v0.7.5: initial tengu rework implementation. Needs balance and phase 3

This commit is contained in:
Evan Debenham 2019-09-23 18:10:55 -04:00
parent 96dd6eff82
commit 309b7357b2
14 changed files with 1192 additions and 35 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -124,10 +124,11 @@ public class Assets {
public static final String LOADING_CITY = "loading_city.png"; public static final String LOADING_CITY = "loading_city.png";
public static final String LOADING_HALLS = "loading_halls.png"; public static final String LOADING_HALLS = "loading_halls.png";
public static final String WEAK_FLOOR = "custom_tiles/weak_floor.png"; public static final String WEAK_FLOOR = "custom_tiles/weak_floor.png";
public static final String SEWER_BOSS = "custom_tiles/sewer_boss.png"; public static final String SEWER_BOSS = "custom_tiles/sewer_boss.png";
public static final String PRISON_QUEST = "custom_tiles/prison_quests.png"; public static final String PRISON_QUEST = "custom_tiles/prison_quests.png";
public static final String PRISON_EXIT = "custom_tiles/prison_exit.png"; public static final String PRISON_EXIT_OLD = "custom_tiles/prison_exit_old.png";
public static final String PRISON_EXIT_NEW = "custom_tiles/prison_exit_new.png";
public static final String BUFFS_SMALL = "buffs.png"; public static final String BUFFS_SMALL = "buffs.png";
public static final String BUFFS_LARGE = "large_buffs.png"; public static final String BUFFS_LARGE = "large_buffs.png";

View File

@ -53,7 +53,7 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.HallsLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.LastLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.LastLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.LastShopLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.LastShopLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.NewPrisonBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel;
@ -253,7 +253,7 @@ public class Dungeon {
level = new PrisonLevel(); level = new PrisonLevel();
break; break;
case 10: case 10:
level = new PrisonBossLevel(); level = new NewPrisonBossLevel();
break; break;
case 11: case 11:
case 12: case 12:

View File

@ -102,6 +102,12 @@ public class ShatteredPixelDungeon extends Game {
com.watabou.utils.Bundle.addAlias( com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.levels.rooms.sewerboss.SewerBossEntranceRoom.class, com.shatteredpixel.shatteredpixeldungeon.levels.rooms.sewerboss.SewerBossEntranceRoom.class,
"com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.SewerBossEntranceRoom" ); "com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.SewerBossEntranceRoom" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.levels.OldPrisonBossLevel.class,
"com.shatteredpixel.shatteredpixeldungeon.levels.OldPrisonBossLevel" );
com.watabou.utils.Bundle.addAlias(
com.shatteredpixel.shatteredpixeldungeon.actors.mobs.OldTengu.class,
"com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Tengu" );
} }
@Override @Override

View File

@ -0,0 +1,315 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2019 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.actors.mobs;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Badges;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.LockedFloor;
import com.shatteredpixel.shatteredpixeldungeon.actors.hero.HeroSubClass;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.TomeOfMastery;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.LloydsBeacon;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.NewPrisonBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.sprites.TenguSprite;
import com.shatteredpixel.shatteredpixeldungeon.ui.BossHealthBar;
import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle;
import com.watabou.utils.Random;
//TODO currently has attack/defence stats for testing, need to add those
public class NewTengu extends Mob {
{
spriteClass = TenguSprite.class;
HP = HT = 160;
EXP = 20;
defenseSkill = 0;
HUNTING = new Hunting();
flying = true; //doesn't literally fly, but he is fleet-of-foot enough to avoid hazards
properties.add(Property.BOSS);
}
@Override
protected void onAdd() {
//when he's removed and re-added to the fight, his time is always set to now.
spend(-cooldown());
super.onAdd();
}
@Override
public int damageRoll() {
return Random.NormalIntRange( 0, 0 );
}
@Override
public int attackSkill( Char target ) {
return 20;
}
@Override
public int drRoll() {
return Random.NormalIntRange(0, 0);
}
@Override
public void damage(int dmg, Object src) {
NewPrisonBossLevel.State state = ((NewPrisonBossLevel)Dungeon.level).state();
int hpBracket;
if (state == NewPrisonBossLevel.State.FIGHT_START){
hpBracket = 20;
} else {
hpBracket = 20;
}
int beforeHitHP = HP;
super.damage(dmg, src);
dmg = beforeHitHP - HP;
//tengu cannot be hit through multiple brackets at a time
if ((beforeHitHP/hpBracket - HP/hpBracket) >= 2){
HP = hpBracket * ((beforeHitHP/hpBracket)-1);
}
LockedFloor lock = Dungeon.hero.buff(LockedFloor.class);
if (lock != null) {
int multiple = state == NewPrisonBossLevel.State.FIGHT_START ? 1 : 4;
lock.addTime(dmg*multiple);
}
//phase 2 of the fight is over
if (HP == 0 && state == NewPrisonBossLevel.State.FIGHT_ARENA) {
//let full attack action complete first
Actor.add(new Actor() {
{
actPriority = VFX_PRIO;
}
@Override
protected boolean act() {
Actor.remove(this);
((NewPrisonBossLevel)Dungeon.level).progress();
return true;
}
});
return;
}
//phase 1 of the fight is over
if (state == NewPrisonBossLevel.State.FIGHT_START && HP <= HT/2){
HP = (HT/2)-1;
yell(Messages.get(this, "interesting"));
((NewPrisonBossLevel)Dungeon.level).progress();
BossHealthBar.bleed(true);
//if tengu has lost a certain amount of hp, jump
} else if (beforeHitHP / hpBracket != HP / hpBracket) {
jump();
}
}
@Override
public boolean isAlive() {
return HP > 0 || Dungeon.level.mobs.contains(this); //Tengu has special death rules, see prisonbosslevel.progress()
}
@Override
public void die( Object cause ) {
if (Dungeon.hero.subClass == HeroSubClass.NONE) {
Dungeon.level.drop( new TomeOfMastery(), pos ).sprite.drop();
}
GameScene.bossSlain();
super.die( cause );
Badges.validateBossSlain();
LloydsBeacon beacon = Dungeon.hero.belongings.getItem(LloydsBeacon.class);
if (beacon != null) {
beacon.upgrade();
}
yell( Messages.get(this, "defeated") );
}
@Override
protected boolean canAttack( Char enemy ) {
return new Ballistica( pos, enemy.pos, Ballistica.PROJECTILE).collisionPos == enemy.pos;
}
//tengu's attack is always visible
@Override
protected boolean doAttack(Char enemy) {
sprite.attack( enemy.pos );
spend( attackDelay() );
return false;
}
private void jump() {
//in case tengu hasn't had a chance to act yet
if (fieldOfView == null || fieldOfView.length != Dungeon.level.length()){
fieldOfView = new boolean[Dungeon.level.length()];
Dungeon.level.updateFieldOfView( this, fieldOfView );
}
if (enemy == null) enemy = chooseEnemy();
if (enemy == null) return;
int newPos;
if (Dungeon.level instanceof NewPrisonBossLevel){
NewPrisonBossLevel level = (NewPrisonBossLevel) Dungeon.level;
//if we're in phase 1, want to warp around within the room
if (level.state() == NewPrisonBossLevel.State.FIGHT_START) {
level.cleanTenguCell();
do {
newPos = ((NewPrisonBossLevel)Dungeon.level).randomTenguCellPos();
} while ( (level.distance(newPos, enemy.pos) <= 2 || Actor.findChar(newPos) != null));
if (level.heroFOV[pos]) CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 );
sprite.move( pos, newPos );
move( newPos );
if (level.heroFOV[newPos]) CellEmitter.get( newPos ).burst( Speck.factory( Speck.WOOL ), 6 );
Sample.INSTANCE.play( Assets.SND_PUFF );
spend( 1 / speed() );
float fill = 0.9f - 0.5f*((HP-80)/80f);
level.placeTrapsInTenguCell(fill);
return;
//otherwise.. TODO!
} else {
do {
newPos = Random.Int(level.length());
} while (
level.solid[newPos] ||
level.distance(newPos, enemy.pos) < 6 ||
Actor.findChar(newPos) != null);
if (level.heroFOV[pos]) CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 );
sprite.move( pos, newPos );
move( newPos );
if (level.heroFOV[newPos]) CellEmitter.get( newPos ).burst( Speck.factory( Speck.WOOL ), 6 );
Sample.INSTANCE.play( Assets.SND_PUFF );
spend( 1 / speed() );
}
//if we're on another type of level
} else {
Level level = Dungeon.level;
newPos = level.randomRespawnCell();
if (level.heroFOV[pos]) CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 );
sprite.move( pos, newPos );
move( newPos );
if (level.heroFOV[newPos]) CellEmitter.get( newPos ).burst( Speck.factory( Speck.WOOL ), 6 );
Sample.INSTANCE.play( Assets.SND_PUFF );
spend( 1 / speed() );
}
}
@Override
public void notice() {
super.notice();
if (!BossHealthBar.isAssigned()) {
BossHealthBar.assignBoss(this);
if (HP <= HT/2) BossHealthBar.bleed(true);
if (HP == HT) {
yell(Messages.get(this, "notice_mine", Dungeon.hero.givenName()));
for (Char ch : Actor.chars()){
if (ch instanceof DriedRose.GhostHero){
GLog.n("\n");
((DriedRose.GhostHero) ch).sayBoss();
}
}
} else {
yell(Messages.get(this, "notice_face", Dungeon.hero.givenName()));
}
}
}
@Override
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
BossHealthBar.assignBoss(this);
if (HP <= HT/2) BossHealthBar.bleed(true);
}
//tengu is always hunting
private class Hunting extends Mob.Hunting{
@Override
public boolean act(boolean enemyInFOV, boolean justAlerted) {
enemySeen = enemyInFOV;
if (enemyInFOV && !isCharmedBy( enemy ) && canAttack( enemy )) {
return doAttack( enemy );
} else {
if (enemyInFOV) {
target = enemy.pos;
} else {
chooseEnemy();
if (enemy != null) {
target = enemy.pos;
}
}
spend( TICK );
return true;
}
}
}
}

View File

@ -35,7 +35,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.DriedRose;
import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.LloydsBeacon; import com.shatteredpixel.shatteredpixeldungeon.items.artifacts.LloydsBeacon;
import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfMagicMapping;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.OldPrisonBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.GrippingTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.GrippingTrap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica; import com.shatteredpixel.shatteredpixeldungeon.mechanics.Ballistica;
@ -48,7 +48,8 @@ import com.watabou.noosa.audio.Sample;
import com.watabou.utils.Bundle; import com.watabou.utils.Bundle;
import com.watabou.utils.Random; import com.watabou.utils.Random;
public class Tengu extends Mob { //Exists to support pre-0.7.5 saves
public class OldTengu extends Mob {
{ {
spriteClass = TenguSprite.class; spriteClass = TenguSprite.class;
@ -89,10 +90,10 @@ public class Tengu extends Mob {
@Override @Override
public void damage(int dmg, Object src) { public void damage(int dmg, Object src) {
PrisonBossLevel.State state = ((PrisonBossLevel)Dungeon.level).state(); OldPrisonBossLevel.State state = ((OldPrisonBossLevel)Dungeon.level).state();
int hpBracket; int hpBracket;
if (state == PrisonBossLevel.State.FIGHT_START){ if (state == OldPrisonBossLevel.State.FIGHT_START){
hpBracket = 12; hpBracket = 12;
} else { } else {
hpBracket = 20; hpBracket = 20;
@ -104,12 +105,12 @@ public class Tengu extends Mob {
LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); LockedFloor lock = Dungeon.hero.buff(LockedFloor.class);
if (lock != null) { if (lock != null) {
int multiple = state == PrisonBossLevel.State.FIGHT_START ? 1 : 4; int multiple = state == OldPrisonBossLevel.State.FIGHT_START ? 1 : 4;
lock.addTime(dmg*multiple); lock.addTime(dmg*multiple);
} }
//phase 2 of the fight is over //phase 2 of the fight is over
if (HP == 0 && state == PrisonBossLevel.State.FIGHT_ARENA) { if (HP == 0 && state == OldPrisonBossLevel.State.FIGHT_ARENA) {
//let full attack action complete first //let full attack action complete first
Actor.add(new Actor() { Actor.add(new Actor() {
@ -120,7 +121,7 @@ public class Tengu extends Mob {
@Override @Override
protected boolean act() { protected boolean act() {
Actor.remove(this); Actor.remove(this);
((PrisonBossLevel)Dungeon.level).progress(); ((OldPrisonBossLevel)Dungeon.level).progress();
return true; return true;
} }
}); });
@ -128,10 +129,10 @@ public class Tengu extends Mob {
} }
//phase 1 of the fight is over //phase 1 of the fight is over
if (state == PrisonBossLevel.State.FIGHT_START && HP <= HT/2){ if (state == OldPrisonBossLevel.State.FIGHT_START && HP <= HT/2){
HP = (HT/2)-1; HP = (HT/2)-1;
yell(Messages.get(this, "interesting")); yell(Messages.get(this, "interesting"));
((PrisonBossLevel)Dungeon.level).progress(); ((OldPrisonBossLevel)Dungeon.level).progress();
BossHealthBar.bleed(true); BossHealthBar.bleed(true);
//if tengu has lost a certain amount of hp, jump //if tengu has lost a certain amount of hp, jump
@ -193,7 +194,7 @@ public class Tengu extends Mob {
int newPos; int newPos;
//if we're in phase 1, want to warp around within the room //if we're in phase 1, want to warp around within the room
if (((PrisonBossLevel)Dungeon.level).state() == PrisonBossLevel.State.FIGHT_START) { if (((OldPrisonBossLevel)Dungeon.level).state() == OldPrisonBossLevel.State.FIGHT_START) {
//place new traps //place new traps
int tries; int tries;

View File

@ -0,0 +1,768 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2019 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.levels;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.Bones;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.NewTengu;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey;
import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.HeavyBoomerang;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Maze;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.TenguDartTrap;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.ui.TargetHealthIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import com.watabou.noosa.Camera;
import com.watabou.noosa.Group;
import com.watabou.noosa.Tilemap;
import com.watabou.noosa.audio.Sample;
import com.watabou.noosa.tweeners.AlphaTweener;
import com.watabou.utils.Bundlable;
import com.watabou.utils.Bundle;
import com.watabou.utils.PathFinder;
import com.watabou.utils.Point;
import com.watabou.utils.Random;
import com.watabou.utils.Rect;
import java.util.ArrayList;
public class NewPrisonBossLevel extends Level {
{
color1 = 0x6a723d;
color2 = 0x88924c;
//the player should be able to see all of Tengu's arena
viewDistance = 12;
}
public enum State {
START,
FIGHT_START,
TRAP_MAZES,
FIGHT_ARENA,
WON
}
private State state;
private NewTengu tengu;
public State state(){
return state;
}
@Override
public String tilesTex() {
return Assets.TILES_PRISON;
}
@Override
public String waterTex() {
return Assets.WATER_PRISON;
}
private static final String STATE = "state";
private static final String TENGU = "tengu";
private static final String STORED_ITEMS = "storeditems";
private static final String TRIGGERED = "triggered";
@Override
public void storeInBundle( Bundle bundle ) {
super.storeInBundle(bundle);
bundle.put( STATE, state );
bundle.put( TENGU, tengu );
bundle.put( STORED_ITEMS, storedItems);
bundle.put(TRIGGERED, triggered );
}
@Override
public void restoreFromBundle( Bundle bundle ) {
super.restoreFromBundle(bundle);
state = bundle.getEnum( STATE, State.class );
//in some states tengu won't be in the world, in others he will be.
if (state == State.START || state == State.TRAP_MAZES) {
tengu = (NewTengu)bundle.get( TENGU );
} else {
for (Mob mob : mobs){
if (mob instanceof NewTengu) {
tengu = (NewTengu) mob;
break;
}
}
}
for (Bundlable item : bundle.getCollection(STORED_ITEMS)){
storedItems.add( (Item)item );
}
triggered = bundle.getBooleanArray(TRIGGERED);
}
@Override
protected boolean build() {
setSize(32, 32);
state = State.START;
setMapStart();
return true;
}
private static final int ENTRANCE_POS = 10 + 4*32;
private static final Rect entranceRoom = new Rect(8, 2, 13, 8);
private static final Rect startHallway = new Rect(9, 7, 12, 24);
private static final Rect[] startCells = new Rect[]{ new Rect(5, 9, 10, 16), new Rect(11, 9, 16, 16),
new Rect(5, 15, 10, 22), new Rect(11, 15, 16, 22)};
private static final Rect tenguCell = new Rect(6, 23, 15, 32);
private static final Point tenguCellCenter = new Point(10, 27);
private static final Point tenguCellDoor = new Point(10, 23);
private static final Point[] startTorches = new Point[]{ new Point(10, 2),
new Point(7, 9), new Point(13, 9),
new Point(7, 15), new Point(13, 15),
new Point(8, 23), new Point(12, 23)};
private void setMapStart(){
entrance = ENTRANCE_POS;
exit = 0;
Painter.fill(this, 0, 0, 32, 32, Terrain.WALL);
//Start
Painter.fill(this, entranceRoom, Terrain.WALL);
Painter.fill(this, entranceRoom, 1, Terrain.EMPTY);
Painter.set(this, entrance, Terrain.ENTRANCE);
Painter.fill(this, startHallway, Terrain.WALL);
Painter.fill(this, startHallway, 1, Terrain.EMPTY);
Painter.set(this, startHallway.left+1, startHallway.top, Terrain.DOOR);
for (Rect r : startCells){
Painter.fill(this, r, Terrain.WALL);
Painter.fill(this, r, 1, Terrain.EMPTY);
}
Painter.set(this, startHallway.left, startHallway.top+5, Terrain.DOOR);
Painter.set(this, startHallway.right-1, startHallway.top+5, Terrain.DOOR);
Painter.set(this, startHallway.left, startHallway.top+11, Terrain.DOOR);
Painter.set(this, startHallway.right-1, startHallway.top+11, Terrain.DOOR);
Painter.fill(this, tenguCell, Terrain.WALL);
Painter.fill(this, tenguCell, 1, Terrain.EMPTY);
Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.LOCKED_DOOR);
drop(new IronKey(10), randomPrisonCellPos());
for (Point p : startTorches){
Painter.set(this, p, Terrain.WALL_DECO);
}
}
private static final Rect mazeHallway = new Rect(9, 6, 12, 24);
private static final Rect[] mazeCells = new Rect[]{ new Rect(1, 9, 10, 16), new Rect(11, 9, 20, 16),
new Rect(3, 15, 10, 22), new Rect(11, 15, 18, 22)};
private static final Point[] mazeKeySpawns = new Point[]{new Point(mazeCells[0].left+1, mazeCells[0].top+3),
new Point(mazeCells[1].right-2, mazeCells[1].top+3),
new Point(mazeCells[2].left+1, mazeCells[2].top+3),
new Point(mazeCells[3].right-2, mazeCells[3].top+3)};
private static final Point[] mazeCellDoors = new Point[]{new Point(mazeCells[0].right-1, mazeCells[0].top+3),
new Point(mazeCells[1].left, mazeCells[1].top+3),
new Point(mazeCells[2].right-1, mazeCells[2].top+3),
new Point(mazeCells[3].left, mazeCells[3].top+3)};
private static final Point[] mazeTorches = new Point[]{ new Point(5, 9), new Point(15, 9),
new Point(6, 15), new Point(14, 15),
new Point(8, 23), new Point(12, 23)};
private void setMapMazes(){
exit = entrance = 0;
Painter.fill(this, 0, 0, 32, 32, Terrain.WALL);
Painter.fill(this, mazeHallway, Terrain.WALL);
Painter.fill(this, mazeHallway, 1, Terrain.EMPTY);
for (Rect r : mazeCells){
Painter.fill(this, r, Terrain.WALL);
Painter.fill(this, r, 1, Terrain.EMPTY);
}
for (Point p : mazeCellDoors){
Painter.set(this, p, Terrain.DOOR);
}
Painter.fill(this, tenguCell, Terrain.WALL);
Painter.fill(this, tenguCell, 1, Terrain.EMPTY);
Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.DOOR);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+2, Terrain.LOCKED_DOOR);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+4, Terrain.LOCKED_DOOR);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+8, Terrain.LOCKED_DOOR);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+10, Terrain.LOCKED_DOOR);
for (Point p : mazeKeySpawns){
drop(new IronKey(10), pointToCell(p));
}
for (Point p : mazeTorches){
Painter.set(this, p, Terrain.WALL_DECO);
}
}
private static final Rect arena = new Rect(3, 1, 18, 16);
private void setMapArena(){
exit = entrance = 0;
Painter.fill(this, 0, 0, 32, 32, Terrain.WALL);
Painter.fill(this, arena, Terrain.WALL);
Painter.fillEllipse(this, arena, 1, Terrain.EMPTY);
}
private static int W = Terrain.WALL;
private static int D = Terrain.WALL_DECO;
private static int e = Terrain.EMPTY;
private static int C = Terrain.CHASM;
private static final Point endStart = new Point( startHallway.left+2, startHallway.top+2);
private static final Point levelExit = new Point( endStart.x+12, endStart.y+8);
private static final int[] endMap = new int[]{
W, W, W, W, W, W, W, W, W, W, W, W, W, W,
W, e, e, e, W, W, W, W, W, W, W, W, W, W,
W, e, e, e, e, e, e, e, e, W, W, W, W, W,
e, e, e, e, e, e, e, e, e, e, e, e, W, W,
e, e, e, e, e, e, e, e, e, e, e, e, e, W,
e, e, e, e, C, C, C, C, C, C, e, e, e, W,
e, W, C, C, C, C, C, C, C, C, C, e, e, W,
e, e, e, e, C, C, C, C, C, C, C, C, e, W,
e, e, e, e, e, e, e, C, C, C, C, C, e, W,
e, e, e, e, e, e, e, W, W, W, C, C, C, W,
e, e, e, e, e, e, W, W, W, W, C, C, C, W,
W, e, e, e, e, W, W, W, W, W, W, C, C, W,
W, W, W, W, W, W, W, W, W, W, W, C, C, W,
W, W, W, W, W, W, W, W, W, W, W, C, C, W,
W, D, W, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
e, e, e, W, W, W, W, W, W, W, W, C, C, W,
W, W, W, W, W, W, W, W, W, W, W, C, C, W
};
private void setMapEnd(){
Painter.fill(this, 0, 0, 32, 32, Terrain.WALL);
setMapStart();
for (Heap h : heaps.valueList()){
if (h.peek() instanceof IronKey){
h.destroy();
}
}
Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.DOOR);
int cell = pointToCell(endStart);
int i = 0;
while (cell < length()){
System.arraycopy(endMap, i, map, cell, 14);
i += 14;
cell += width();
}
exit = pointToCell(levelExit);
Painter.set(this, exit, Terrain.EXIT);
}
//keep track of removed items as the level is changed. Dump them back into the level at the end.
private ArrayList<Item> storedItems = new ArrayList<>();
private void clearEntities(Rect safeArea){
for (Heap heap : heaps.valueList()){
if (safeArea == null || !safeArea.inside(cellToPoint(heap.pos))){
storedItems.addAll(heap.items);
heap.destroy();
}
}
for (HeavyBoomerang.CircleBack b : Dungeon.hero.buffs(HeavyBoomerang.CircleBack.class)){
if (safeArea == null || !safeArea.inside(cellToPoint(b.returnPos()))){
storedItems.add(b.cancel());
}
}
for (Mob mob : Dungeon.level.mobs.toArray(new Mob[0])){
if (mob != tengu && (safeArea == null || !safeArea.inside(cellToPoint(mob.pos)))){
mob.destroy();
if (mob.sprite != null)
mob.sprite.killAndErase();
}
}
for (Plant plant : plants.valueList()){
if (safeArea == null || !safeArea.inside(cellToPoint(plant.pos))){
plants.remove(plant.pos);
}
}
}
private void cleanMapState(){
buildFlagMaps();
cleanWalls();
BArray.setFalse(visited);
BArray.setFalse(mapped);
for (Blob blob: blobs.values()){
blob.fullyClear();
}
addVisuals(); //this also resets existing visuals
traps.clear();
GameScene.resetMap();
Dungeon.observe();
}
@Override
public Group addVisuals() {
super.addVisuals();
PrisonLevel.addPrisonVisuals(this, visuals);
return visuals;
}
public void progress(){
switch (state){
case START:
//if something is occupying Tengu's space, wait and do nothing.
if (Actor.findChar(pointToCell(tenguCellCenter)) != null){
return;
}
seal();
set(pointToCell(tenguCellDoor), Terrain.LOCKED_DOOR);
GameScene.updateMap(pointToCell(tenguCellDoor));
for (Mob m : mobs){
//bring the first ally with you
if (m.alignment == Char.Alignment.ALLY && !m.properties().contains(Char.Property.IMMOVABLE)){
m.pos = pointToCell(tenguCellDoor); //they should immediately walk out of the door
m.sprite.place(m.pos);
break;
}
}
tengu.state = tengu.HUNTING;
tengu.pos = pointToCell(tenguCellCenter); //in the middle of the fight room
GameScene.add( tengu );
tengu.notice();
state = State.FIGHT_START;
break;
case FIGHT_START:
clearEntities( tenguCell ); //clear anything not in tengu's cell
Actor.remove(tengu);
mobs.remove(tengu);
TargetHealthIndicator.instance.target(null);
tengu.sprite.kill();
setMapMazes();
cleanMapState();
GameScene.flash(0xFFFFFF);
Sample.INSTANCE.play(Assets.SND_BLAST);
state = State.TRAP_MAZES;
break;
case TRAP_MAZES:
Dungeon.hero.interrupt();
clearEntities( arena ); //clear anything not in the arena
setMapArena();
cleanMapState();
tengu.state = tengu.HUNTING;
do {
tengu.pos = Random.Int(length());
} while (solid[tengu.pos] || distance(tengu.pos, Dungeon.hero.pos) < 6);
GameScene.add(tengu);
tengu.notice();
GameScene.flash(0xFFFFFF);
Sample.INSTANCE.play(Assets.SND_BLAST);
state = State.FIGHT_ARENA;
break;
case FIGHT_ARENA:
unseal();
CustomTilemap vis = new exitVisual();
vis.pos(11, 9);
customTiles.add(vis);
GameScene.add(vis, false);
vis = new exitVisualWalls();
vis.pos(12, 9);
customWalls.add(vis);
GameScene.add(vis, true);
Dungeon.hero.interrupt();
Dungeon.hero.pos = tenguCell.left+4 + (tenguCell.top+2)*width();
Dungeon.hero.sprite.interruptMotion();
Dungeon.hero.sprite.place(Dungeon.hero.pos);
Camera.main.snapTo(Dungeon.hero.sprite.center());
tengu.pos = pointToCell(tenguCellCenter);
tengu.sprite.place(tengu.pos);
//remove all mobs, but preserve allies
ArrayList<Mob> allies = new ArrayList<>();
for(Mob m : mobs.toArray(new Mob[0])){
if (m.alignment == Char.Alignment.ALLY && !m.properties().contains(Char.Property.IMMOVABLE)){
allies.add(m);
mobs.remove(m);
}
}
setMapEnd();
cleanMapState();
for (Mob m : allies){
do{
m.pos = randomTenguCellPos();
} while (findMob(m.pos) != null);
if (m.sprite != null) m.sprite.place(m.pos);
mobs.add(m);
}
tengu.die(Dungeon.hero);
clearEntities(tenguCell);
for (Item item : storedItems) {
drop(item, randomTenguCellPos());
}
GameScene.flash(0xFFFFFF);
Sample.INSTANCE.play(Assets.SND_BLAST);
state = State.WON;
break;
}
}
private boolean[] triggered = new boolean[]{false, false, false, false};
@Override
public void occupyCell(Char ch) {
super.occupyCell(ch);
if (ch == Dungeon.hero){
switch (state){
case START:
if (cellToPoint(ch.pos).y > tenguCell.top){
progress();
}
break;
case TRAP_MAZES:
for (int i = 0; i < mazeCellDoors.length; i++){
if (ch.pos == pointToCell(mazeCellDoors[i]) && !triggered[i]){
triggered[i] = true;
Maze.allowDiagonals = true;
boolean[][] maze = Maze.generate(mazeCells[i], map, width(), Terrain.WALL);
for (int x = 1; x < maze.length-1; x++) {
for (int y = 1; y < maze[0].length-1; y++) {
if (maze[x][y] && !(x == 1 && y == 3)){
int cell = mazeCells[i].left+x + width()*(mazeCells[i].top+y);
if (heaps.get(cell) == null){
setTrap(new TenguDartTrap().hide(), cell);
Painter.set(this, cell, Terrain.SECRET_TRAP);
CellEmitter.get(cell).burst(Speck.factory(Speck.LIGHT), 2);
}
}
}
}
FadingTraps f = new FadingTraps();
f.setCoveringArea(mazeCells[i]);
GameScene.add(f, false);
f.startFade(2.5f);
Sample.INSTANCE.play(Assets.SND_TELEPORT);
int roomCenter = (mazeCells[i].left + mazeCells[i].right)/2 +
(mazeCells[i].top + mazeCells[i].bottom)/2 * width();
Camera.main.panTo(DungeonTilemap.tileCenterToWorld(roomCenter), 5f);
Dungeon.hero.interrupt();
}
}
if (cellToPoint(ch.pos).y <= mazeHallway.top+2){
progress();
}
break;
}
}
}
@Override
protected void createMobs() {
tengu = new NewTengu(); //We want to keep track of tengu independently of other mobs, he's not always in the level.
}
public Actor respawner() {
return null;
}
@Override
protected void createItems() {
Item item = Bones.get();
if (item != null) {
drop( item, randomRespawnCell() ).setHauntedIfCursed(1f).type = Heap.Type.REMAINS;
}
}
private int randomPrisonCellPos(){
Rect room = startCells[Random.Int(startCells.length)];
return Random.IntRange(room.left+1, room.right-2)
+ width()*Random.IntRange(room.top+1, room.bottom-2);
}
public int randomTenguCellPos(){
return Random.IntRange(tenguCell.left+1, tenguCell.right-2)
+ width()*Random.IntRange(tenguCell.top+1, tenguCell.bottom-2);
}
public void cleanTenguCell(){
traps.clear();
Painter.fill(this, tenguCell, 1, Terrain.EMPTY);
buildFlagMaps();
}
public void placeTrapsInTenguCell(float fill){
Point tenguPoint = cellToPoint(tengu.pos);
Point heroPoint = cellToPoint(Dungeon.hero.pos);
PathFinder.setMapSize(7, 7);
int tenguPos = tenguPoint.x-(tenguCell.left+1) + (tenguPoint.y-(tenguCell.top+1))*7;
int heroPos = heroPoint.x-(tenguCell.left+1) + (heroPoint.y-(tenguCell.top+1))*7;
boolean[] trapsPatch;
do {
trapsPatch = Patch.generate(7, 7, fill, 0, false);
PathFinder.buildDistanceMap(tenguPos, BArray.not(trapsPatch, null));
} while (PathFinder.distance[heroPos] > 6);
PathFinder.setMapSize(width(), height());
for (int i = 0; i < trapsPatch.length; i++){
if (trapsPatch[i]) {
int x = i % 7;
int y = i / 7;
int cell = x+tenguCell.left+1 + (y+tenguCell.top+1)*width();
Level.set(cell, Terrain.SECRET_TRAP);
setTrap(new TenguDartTrap().hide(), cell);
CellEmitter.get(cell).burst(Speck.factory(Speck.LIGHT), 2);
} else {
}
}
GameScene.updateMap();
FadingTraps t = new FadingTraps();
t.setCoveringArea(tenguCell);
GameScene.add(t, false);
t.startFade( 2.5f );
}
@Override
public int randomRespawnCell() {
int pos = ENTRANCE_POS; //random cell adjacent to the entrance.
int cell;
do {
cell = pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
} while (!passable[cell] || Actor.findChar(cell) != null);
return cell;
}
@Override
public String tileName( int tile ) {
switch (tile) {
case Terrain.WATER:
return Messages.get(PrisonLevel.class, "water_name");
default:
return super.tileName( tile );
}
}
@Override
public String tileDesc(int tile) {
switch (tile) {
case Terrain.EMPTY_DECO:
return Messages.get(PrisonLevel.class, "empty_deco_desc");
case Terrain.BOOKSHELF:
return Messages.get(PrisonLevel.class, "bookshelf_desc");
default:
return super.tileDesc( tile );
}
}
private class FadingTraps extends CustomTilemap {
{
texture = Assets.TERRAIN_FEATURES;
}
Rect area;
public void setCoveringArea(Rect area){
tileX = area.left;
tileY = area.top;
tileH = area.bottom - area.top;
tileW = area.right - area.left;
this.area = area;
}
@Override
public Tilemap create() {
Tilemap v = super.create();
int[] data = new int[tileW*tileH];
int cell;
Trap t;
int i = 0;
for (int y = tileY; y < tileY + tileH; y++){
cell = tileX + y*width();
for (int x = tileX; x < tileX + tileW; x++){
t = traps.get(cell);
if (t != null){
data[i] = t.color + t.shape*16;
} else {
data[i] = -1;
}
cell++;
i++;
}
}
v.map( data, tileW );
return v;
}
public void startFade( float duration ){
if (vis == null || vis.parent == null){
return;
}
vis.parent.add(new AlphaTweener(vis, 0f, duration){
@Override
protected void onComplete() {
super.onComplete();
vis.killAndErase();
killAndErase();
vis = null;
}
});
}
}
public static class exitVisual extends CustomTilemap {
{
texture = Assets.PRISON_EXIT_NEW;
tileW = 13;
tileH = 24;
}
final int TEX_WIDTH = 512;
@Override
public Tilemap create() {
Tilemap v = super.create();
v.map(mapSimpleImage(0, 0, TEX_WIDTH), tileW);
return v;
}
}
public static class exitVisualWalls extends CustomTilemap {
{
texture = Assets.PRISON_EXIT_NEW;
tileW = 13;
tileH = 24;
}
final int TEX_WIDTH = 512;
@Override
public Tilemap create() {
Tilemap v = super.create();
v.map(mapSimpleImage(13, 0, TEX_WIDTH), tileW);
return v;
}
}
}

View File

@ -29,7 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char; import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob; import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Tengu; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.OldTengu;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap; import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item; import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey; import com.shatteredpixel.shatteredpixeldungeon.items.keys.IronKey;
@ -55,7 +55,8 @@ import com.watabou.utils.Random;
import java.util.ArrayList; import java.util.ArrayList;
public class PrisonBossLevel extends Level { //Exists to support pre-0.7.5 saves
public class OldPrisonBossLevel extends Level {
{ {
color1 = 0x6a723d; color1 = 0x6a723d;
@ -74,7 +75,7 @@ public class PrisonBossLevel extends Level {
private static final int ARENA_DOOR = 5+25*32; private static final int ARENA_DOOR = 5+25*32;
private State state; private State state;
private Tengu tengu; private OldTengu tengu;
public State state(){ public State state(){
return state; return state;
@ -112,11 +113,11 @@ public class PrisonBossLevel extends Level {
//in some states tengu won't be in the world, in others he will be. //in some states tengu won't be in the world, in others he will be.
if (state == State.START || state == State.MAZE) { if (state == State.START || state == State.MAZE) {
tengu = (Tengu)bundle.get( TENGU ); tengu = (OldTengu)bundle.get( TENGU );
} else { } else {
for (Mob mob : mobs){ for (Mob mob : mobs){
if (mob instanceof Tengu) { if (mob instanceof OldTengu) {
tengu = (Tengu) mob; tengu = (OldTengu) mob;
break; break;
} }
} }
@ -148,7 +149,7 @@ public class PrisonBossLevel extends Level {
@Override @Override
protected void createMobs() { protected void createMobs() {
tengu = new Tengu(); //We want to keep track of tengu independently of other mobs, he's not always in the level. tengu = new OldTengu(); //We want to keep track of tengu independently of other mobs, he's not always in the level.
} }
public Actor respawner() { public Actor respawner() {
@ -280,7 +281,7 @@ public class PrisonBossLevel extends Level {
} }
private void clearEntities(Room safeArea){ private void clearEntities(Room safeArea){
for (Heap heap : heaps.values()){ for (Heap heap : heaps.valueList()){
if (safeArea == null || !safeArea.inside(cellToPoint(heap.pos))){ if (safeArea == null || !safeArea.inside(cellToPoint(heap.pos))){
storedItems.addAll(heap.items); storedItems.addAll(heap.items);
heap.destroy(); heap.destroy();
@ -300,7 +301,7 @@ public class PrisonBossLevel extends Level {
mob.sprite.killAndErase(); mob.sprite.killAndErase();
} }
} }
for (Plant plant : plants.values()){ for (Plant plant : plants.valueList()){
if (safeArea == null || !safeArea.inside(cellToPoint(plant.pos))){ if (safeArea == null || !safeArea.inside(cellToPoint(plant.pos))){
plants.remove(plant.pos); plants.remove(plant.pos);
} }
@ -613,7 +614,7 @@ public class PrisonBossLevel extends Level {
public static class exitVisual extends CustomTilemap { public static class exitVisual extends CustomTilemap {
{ {
texture = Assets.PRISON_EXIT; texture = Assets.PRISON_EXIT_OLD;
tileW = 12; tileW = 12;
tileH = 14; tileH = 14;
@ -656,7 +657,7 @@ public class PrisonBossLevel extends Level {
public static class exitVisualWalls extends CustomTilemap { public static class exitVisualWalls extends CustomTilemap {
{ {
texture = Assets.PRISON_EXIT; texture = Assets.PRISON_EXIT_OLD;
tileW = 12; tileW = 12;
tileH = 14; tileH = 14;

View File

@ -48,15 +48,23 @@ public class PoisonDartTrap extends Trap {
return 8 + Math.round(2*Dungeon.depth / 3f); return 8 + Math.round(2*Dungeon.depth / 3f);
} }
protected boolean canTarget( Char ch ){
return true;
}
@Override @Override
public void activate() { public void activate() {
Char target = Actor.findChar(pos); Char target = Actor.findChar(pos);
if (target != null && !canTarget(target)){
target = null;
}
//find the closest char that can be aimed at //find the closest char that can be aimed at
if (target == null){ if (target == null){
for (Char ch : Actor.chars()){ for (Char ch : Actor.chars()){
Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE); Ballistica bolt = new Ballistica(pos, ch.pos, Ballistica.PROJECTILE);
if (bolt.collisionPos == ch.pos && if (canTarget(ch) && bolt.collisionPos == ch.pos &&
(target == null || Dungeon.level.trueDistance(pos, ch.pos) < Dungeon.level.trueDistance(pos, target.pos))){ (target == null || Dungeon.level.trueDistance(pos, ch.pos) < Dungeon.level.trueDistance(pos, target.pos))){
target = ch; target = ch;
} }

View File

@ -0,0 +1,43 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2019 Evan Debenham
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>
*/
package com.shatteredpixel.shatteredpixeldungeon.levels.traps;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.NewTengu;
public class TenguDartTrap extends PoisonDartTrap {
{
canBeHidden = true;
canBeSearched = false;
}
@Override
protected int poisonAmount() {
return 9; //21 damage total
}
@Override
protected boolean canTarget(Char ch) {
return !(ch instanceof NewTengu);
}
}

View File

@ -47,6 +47,10 @@ public final class ShadowCaster {
} }
public static void castShadow( int x, int y, boolean[] fieldOfView, boolean[] blocking, int distance ) { public static void castShadow( int x, int y, boolean[] fieldOfView, boolean[] blocking, int distance ) {
if (distance >= MAX_DISTANCE){
distance = MAX_DISTANCE;
}
BArray.setFalse(fieldOfView); BArray.setFalse(fieldOfView);

View File

@ -582,13 +582,21 @@ actors.mobs.swarm.name=swarm of flies
actors.mobs.swarm.def_verb=evaded actors.mobs.swarm.def_verb=evaded
actors.mobs.swarm.desc=The deadly swarm of flies buzzes angrily. Every non-magical attack will split it into two smaller but equally dangerous swarms. actors.mobs.swarm.desc=The deadly swarm of flies buzzes angrily. Every non-magical attack will split it into two smaller but equally dangerous swarms.
actors.mobs.tengu.name=Tengu actors.mobs.newtengu.name=Tengu
actors.mobs.tengu.notice_mine=You're mine, %s! actors.mobs.newtengu.notice_mine=You're mine, %s!
actors.mobs.tengu.notice_face=Face me, %s! actors.mobs.newtengu.notice_face=Face me, %s!
actors.mobs.tengu.interesting=Let's make this interesting... actors.mobs.newtengu.interesting=Let's make this interesting...
actors.mobs.tengu.defeated=Free at last... actors.mobs.newtengu.defeated=Free at last...
actors.mobs.tengu.rankings_desc=Assassinated by the Tengu actors.mobs.newtengu.rankings_desc=Assassinated by the Tengu
actors.mobs.tengu.desc=A famous and enigmatic assassin, named for the mask grafted to his face.\n\nTengu is held down with large clasps on his wrists and knees, though he seems to have gotten rid of his chains long ago.\n\nHe will try to use traps, deceptive magic, and precise attacks to eliminate the only thing stopping his escape: you. actors.mobs.newtengu.desc=A famous and enigmatic assassin, named for the mask grafted to his face.\n\nTengu is held down with large clasps on his wrists and knees, though he seems to have gotten rid of his chains long ago.\n\nHe will try to use traps, deceptive magic, and precise attacks to eliminate the only thing stopping his escape: you.
actors.mobs.oldtengu.name=Tengu
actors.mobs.oldtengu.notice_mine=You're mine, %s!
actors.mobs.oldtengu.notice_face=Face me, %s!
actors.mobs.oldtengu.interesting=Let's make this interesting...
actors.mobs.oldtengu.defeated=Free at last...
actors.mobs.oldtengu.rankings_desc=Assassinated by the Tengu
actors.mobs.oldtengu.desc=A famous and enigmatic assassin, named for the mask grafted to his face.\n\nTengu is held down with large clasps on his wrists and knees, though he seems to have gotten rid of his chains long ago.\n\nHe will try to use traps, deceptive magic, and precise attacks to eliminate the only thing stopping his escape: you.
actors.mobs.thief.name=crazy thief actors.mobs.thief.name=crazy thief
actors.mobs.thief.stole=The thief stole %s from you! actors.mobs.thief.stole=The thief stole %s from you!

View File

@ -104,6 +104,8 @@ levels.traps.summoningtrap.desc=Triggering this trap will summon a number of thi
levels.traps.teleportationtrap.name=teleportation trap levels.traps.teleportationtrap.name=teleportation trap
levels.traps.teleportationtrap.desc=Whatever triggers this trap will be teleported to some other location on this floor. levels.traps.teleportationtrap.desc=Whatever triggers this trap will be teleported to some other location on this floor.
levels.traps.tengudarttrap.desc=Tengu has clearly been preparing for a fight. This trap will activate a hidden dart blower which will shoot a poison dart at the nearest thing that isn't Tengu.\n\nThe trap is so well made that the trigger mechanism is impossible to detect without magical aid. However the trap is visible for a moment when it is being set.
levels.traps.toxictrap.name=toxic gas trap levels.traps.toxictrap.name=toxic gas trap
levels.traps.toxictrap.desc=Triggering this trap will set a cloud of toxic gas loose within the surrounding area. levels.traps.toxictrap.desc=Triggering this trap will set a cloud of toxic gas loose within the surrounding area.