v0.8.1: design improvements to Tengu:

- Removed phase 2 entirely, replaced with a brief pause before phase 3 (now phase 2)
- Increased the intensity of traps in phase 1 to compensate
- Fixed a bug that could let high damage builds skip some Tengu jumps
- Reduced Tengu's accuracy a bit in melee range to help melee characters
This commit is contained in:
Evan Debenham 2020-05-09 19:27:59 -04:00
parent cf793e3b0d
commit b369cf28ea
2 changed files with 39 additions and 119 deletions

View File

@ -106,7 +106,11 @@ public class NewTengu extends Mob {
@Override @Override
public int attackSkill( Char target ) { public int attackSkill( Char target ) {
return 18; if (Dungeon.level.adjacent(pos, target.pos)){
return 12;
} else {
return 18;
}
} }
@Override @Override
@ -138,7 +142,7 @@ public class NewTengu extends Mob {
//tengu cannot be hit through multiple brackets at a time //tengu cannot be hit through multiple brackets at a time
if ((beforeHitHP/hpBracket - HP/hpBracket) >= 2){ if ((beforeHitHP/hpBracket - HP/hpBracket) >= 2){
HP = hpBracket * ((beforeHitHP/hpBracket)-1); HP = hpBracket * ((beforeHitHP/hpBracket)-1) + 1;
} }
LockedFloor lock = Dungeon.hero.buff(LockedFloor.class); LockedFloor lock = Dungeon.hero.buff(LockedFloor.class);
@ -239,7 +243,7 @@ public class NewTengu extends Mob {
do { do {
newPos = ((NewPrisonBossLevel)Dungeon.level).randomTenguCellPos(); newPos = ((NewPrisonBossLevel)Dungeon.level).randomTenguCellPos();
} while ( (level.distance(newPos, enemy.pos) < 3 || Actor.findChar(newPos) != null)); } while ( (level.trueDistance(newPos, enemy.pos) <= 4 || Actor.findChar(newPos) != null));
if (level.heroFOV[pos]) CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 ); if (level.heroFOV[pos]) CellEmitter.get( pos ).burst( Speck.factory( Speck.WOOL ), 6 );

View File

@ -45,7 +45,6 @@ import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.plants.Plant; import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap; import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap;
import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
import com.shatteredpixel.shatteredpixeldungeon.ui.TargetHealthIndicator; import com.shatteredpixel.shatteredpixeldungeon.ui.TargetHealthIndicator;
import com.shatteredpixel.shatteredpixeldungeon.utils.BArray; import com.shatteredpixel.shatteredpixeldungeon.utils.BArray;
import com.watabou.noosa.Camera; import com.watabou.noosa.Camera;
@ -75,7 +74,8 @@ public class NewPrisonBossLevel extends Level {
public enum State { public enum State {
START, START,
FIGHT_START, FIGHT_START,
TRAP_MAZES, TRAP_MAZES, //pre-0.8.1 saves
FIGHT_PAUSE,
FIGHT_ARENA, FIGHT_ARENA,
WON WON
} }
@ -117,7 +117,7 @@ public class NewPrisonBossLevel extends Level {
state = bundle.getEnum( STATE, State.class ); state = bundle.getEnum( STATE, State.class );
//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.TRAP_MAZES) { if (state == State.START || state == State.TRAP_MAZES || state == State.FIGHT_PAUSE) {
tengu = (NewTengu)bundle.get( TENGU ); tengu = (NewTengu)bundle.get( TENGU );
} else { } else {
for (Mob mob : mobs){ for (Mob mob : mobs){
@ -190,62 +190,25 @@ public class NewPrisonBossLevel extends Level {
Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.LOCKED_DOOR); Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.LOCKED_DOOR);
drop(new IronKey(10), randomPrisonCellPos());
for (Point p : startTorches){ for (Point p : startTorches){
Painter.set(this, p, Terrain.WALL_DECO); Painter.set(this, p, Terrain.WALL_DECO);
} }
} }
private static final Rect mazeHallway = new Rect(9, 6, 12, 24); private void setMapPause(){
private static final Rect[] mazeCells = new Rect[]{ new Rect(1, 9, 10, 16), new Rect(11, 9, 20, 16), setMapStart();
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; 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, tenguCell.left+4, tenguCell.top, Terrain.DOOR);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+2, Terrain.LOCKED_DOOR); Painter.fill(this, startCells[1].left, startCells[1].top+3, 1, 7, Terrain.EMPTY);
Painter.set(this, mazeHallway.left+1, mazeHallway.top+4, Terrain.LOCKED_DOOR); Painter.fill(this, startCells[1].left+2, startCells[1].top+2, 3, 10, Terrain.EMPTY);
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){ Painter.fill(this, entranceRoom, Terrain.WALL);
drop(new IronKey(10), pointToCell(p)); Painter.set(this, startHallway.left+1, startHallway.top, Terrain.EMPTY);
} Painter.set(this, startHallway.left+1, startHallway.top+1, Terrain.DOOR);
for (Point p : mazeTorches){
Painter.set(this, p, Terrain.WALL_DECO);
}
} }
private static final Rect arena = new Rect(3, 1, 18, 16); private static final Rect arena = new Rect(3, 1, 18, 16);
@ -269,7 +232,7 @@ public class NewPrisonBossLevel extends Level {
private static final Point endStart = new Point( startHallway.left+2, startHallway.top+2); 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+6); private static final Point levelExit = new Point( endStart.x+12, endStart.y+6);
private static final int[] endMap = new int[]{ private static final int[] endMap = new int[]{
W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, W, D, 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, W, W, W, W, W, W, W, W, W, W,
W, e, e, e, e, e, e, e, e, 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, W, W,
@ -432,7 +395,7 @@ public class NewPrisonBossLevel extends Level {
clearEntities( tenguCell ); //clear anything not in tengu's cell clearEntities( tenguCell ); //clear anything not in tengu's cell
setMapMazes(); setMapPause();
cleanMapState(); cleanMapState();
Actor.remove(tengu); Actor.remove(tengu);
@ -443,10 +406,11 @@ public class NewPrisonBossLevel extends Level {
GameScene.flash(0xFFFFFF); GameScene.flash(0xFFFFFF);
Sample.INSTANCE.play(Assets.Sounds.BLAST); Sample.INSTANCE.play(Assets.Sounds.BLAST);
state = State.TRAP_MAZES; state = State.FIGHT_PAUSE;
break; break;
case TRAP_MAZES: case TRAP_MAZES: //for pre-0.8.1 saves
case FIGHT_PAUSE:
Dungeon.hero.interrupt(); Dungeon.hero.interrupt();
@ -531,63 +495,10 @@ public class NewPrisonBossLevel extends Level {
progress(); progress();
} }
break; break;
case TRAP_MAZES: case TRAP_MAZES: //pre-0.8.1
case FIGHT_PAUSE:
for (int i = 0; i < mazeCellDoors.length; i++){ if (cellToPoint(ch.pos).y <= startHallway.top+1){
if (ch.pos == pointToCell(mazeCellDoors[i]) && !triggered[i]){
triggered[i] = true;
Maze.allowDiagonals = true;
boolean[][] maze;
boolean validMaze;
do {
maze = Maze.generate(mazeCells[i], map, width(), Terrain.WALL);
//prevents a maze that is just a straight line from the door
validMaze = false;
for (int x = 1; x < maze.length-1; x++){
if (maze[x][3]){
int cell = mazeCells[i].left+x + width()*(mazeCells[i].top+3);
if (heaps.get(cell) == null) {
validMaze = true;
break;
}
}
}
} while (!validMaze);
for (int x = 1; x < maze.length-1; x++) {
for (int y = 1; y < maze[0].length-1; y++) {
if (maze[x][y]){
int cell = mazeCells[i].left+x + width()*(mazeCells[i].top+y);
if (heaps.get(cell) == null
&& Blob.volumeAt(cell, StormCloud.class) == 0
&& Blob.volumeAt(cell, Regrowth.class) <= 9){
Level.set( cell, Terrain.SECRET_TRAP );
setTrap(new TenguDartTrap().hide(), cell);
CellEmitter.get(cell).burst(Speck.factory(Speck.LIGHT), 2);
}
}
}
}
FadingTraps f = new FadingTraps();
f.setCoveringArea(mazeCells[i]);
f.fadeDelay = 2f;
GameScene.add(f, false);
customTiles.add(f);
Sample.INSTANCE.play(Assets.Sounds.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(); progress();
} }
break; break;
@ -610,6 +521,7 @@ public class NewPrisonBossLevel extends Level {
if (item != null) { if (item != null) {
drop( item, randomRespawnCell( null ) ).setHauntedIfCursed().type = Heap.Type.REMAINS; drop( item, randomRespawnCell( null ) ).setHauntedIfCursed().type = Heap.Type.REMAINS;
} }
drop(new IronKey(10), randomPrisonCellPos());
} }
private int randomPrisonCellPos(){ private int randomPrisonCellPos(){
@ -648,7 +560,10 @@ public class NewPrisonBossLevel extends Level {
trapsPatch = Patch.generate(7, 7, fill, 0, false); trapsPatch = Patch.generate(7, 7, fill, 0, false);
PathFinder.buildDistanceMap(tenguPos, BArray.not(trapsPatch, null)); PathFinder.buildDistanceMap(tenguPos, BArray.not(trapsPatch, null));
} while (PathFinder.distance[heroPos] > 6); //note that the effective range of fill is 40%-90%
//so distance to tengu starts at 3-4 tiles and scales up to 7-8 as fill increases
} while (PathFinder.distance[heroPos] > Math.ceil(1 + 7*fill)
|| PathFinder.distance[heroPos] < Math.ceil(7*fill));
PathFinder.setMapSize(width(), height()); PathFinder.setMapSize(width(), height());
@ -658,7 +573,8 @@ public class NewPrisonBossLevel extends Level {
int y = i / 7; int y = i / 7;
int cell = x+tenguCell.left+1 + (y+tenguCell.top+1)*width(); int cell = x+tenguCell.left+1 + (y+tenguCell.top+1)*width();
if (Blob.volumeAt(cell, StormCloud.class) == 0 if (Blob.volumeAt(cell, StormCloud.class) == 0
&& Blob.volumeAt(cell, Regrowth.class) <= 9) { && Blob.volumeAt(cell, Regrowth.class) <= 9
&& Actor.findChar(cell) != tengu) {
Level.set(cell, Terrain.SECRET_TRAP); Level.set(cell, Terrain.SECRET_TRAP);
setTrap(new TenguDartTrap().hide(), cell); setTrap(new TenguDartTrap().hide(), cell);
CellEmitter.get(cell).burst(Speck.factory(Speck.LIGHT), 2); CellEmitter.get(cell).burst(Speck.factory(Speck.LIGHT), 2);