v1.3.0: Overhauled entrance/exit logic to account for branches

This commit is contained in:
Evan Debenham
2022-05-20 13:50:27 -04:00
parent 5cc8734dc2
commit 654bce1f65
31 changed files with 494 additions and 226 deletions
@@ -61,6 +61,7 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.SewerLevel;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.secret.SecretRoom;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SpecialRoom;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@@ -218,7 +219,7 @@ public class Dungeon {
quickslot.reset();
QuickSlotButton.reset();
depth = 0;
depth = 1;
branch = 0;
gold = 0;
@@ -252,8 +253,7 @@ public class Dungeon {
Dungeon.level = null;
Actor.clear();
depth++;
if (depth > Statistics.deepestFloor) {
Statistics.deepestFloor = depth;
@@ -337,7 +337,7 @@ public class Dungeon {
Actor.clear();
level.reset();
switchLevel( level, level.entrance );
switchLevel( level, level.entrance() );
}
public static long seedCurDepth(){
@@ -374,9 +374,12 @@ public class Dungeon {
public static void switchLevel( final Level level, int pos ) {
if (pos == -2){
pos = level.exit;
} else if (pos < 0 || pos >= level.length() || (!level.passable[pos] && !level.avoid[pos])){
pos = level.entrance;
LevelTransition t = level.getTransition(LevelTransition.Type.REGULAR_EXIT);
if (t != null) pos = t.cell();
}
if (pos < 0 || pos >= level.length() || (!level.passable[pos] && !level.avoid[pos])){
pos = level.getTransition(null).cell();
}
PathFinder.setMapSize(level.width(), level.height());
@@ -30,7 +30,6 @@ import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon;
import com.shatteredpixel.shatteredpixeldungeon.Statistics;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Alchemy;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AdrenalineSurge;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.Amok;
import com.shatteredpixel.shatteredpixeldungeon.actors.buffs.AnkhInvulnerability;
@@ -59,7 +58,6 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.hero.abilities.warrior.En
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Monk;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Snake;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.NPC;
import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
import com.shatteredpixel.shatteredpixeldungeon.effects.CheckedCell;
import com.shatteredpixel.shatteredpixeldungeon.effects.Speck;
@@ -120,6 +118,7 @@ import com.shatteredpixel.shatteredpixeldungeon.journal.Notes;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.ShadowCaster;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@@ -152,7 +151,6 @@ import com.watabou.utils.Random;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
public class Hero extends Char {
@@ -709,11 +707,8 @@ public class Hero extends Char {
} else if (curAction instanceof HeroAction.Unlock) {
actResult = actUnlock((HeroAction.Unlock) curAction);
} else if (curAction instanceof HeroAction.Descend) {
actResult = actDescend( (HeroAction.Descend)curAction );
} else if (curAction instanceof HeroAction.Ascend) {
actResult = actAscend( (HeroAction.Ascend)curAction );
} else if (curAction instanceof HeroAction.LvlTransition) {
actResult = actTransition( (HeroAction.LvlTransition)curAction );
} else if (curAction instanceof HeroAction.Attack) {
actResult = actAttack( (HeroAction.Attack)curAction );
@@ -751,7 +746,7 @@ public class Hero extends Char {
public void interrupt() {
if (isAlive() && curAction != null &&
((curAction instanceof HeroAction.Move && curAction.dst != pos) ||
(curAction instanceof HeroAction.Ascend || curAction instanceof HeroAction.Descend))) {
(curAction instanceof HeroAction.LvlTransition))) {
lastAction = curAction;
}
curAction = null;
@@ -1006,53 +1001,17 @@ public class Hero extends Char {
}
}
private boolean actDescend( HeroAction.Descend action ) {
private boolean actTransition(HeroAction.LvlTransition action ) {
int stairs = action.dst;
LevelTransition transition = Dungeon.level.getTransition(stairs);
if (rooted) {
Camera.main.shake(1, 1f);
ready();
return false;
//there can be multiple exit tiles, so descend on any of them
//TODO this is slightly brittle, it assumes there are no disjointed sets of exit tiles
} else if ((Dungeon.level.map[pos] == Terrain.EXIT || Dungeon.level.map[pos] == Terrain.UNLOCKED_EXIT)) {
curAction = null;
} else if (transition != null && transition.inside(pos)) {
TimekeepersHourglass.timeFreeze timeFreeze = buff(TimekeepersHourglass.timeFreeze.class);
if (timeFreeze != null) timeFreeze.disarmPressedTraps();
Swiftthistle.TimeBubble timeBubble = buff(Swiftthistle.TimeBubble.class);
if (timeBubble != null) timeBubble.disarmPressedTraps();
InterlevelScene.mode = InterlevelScene.Mode.DESCEND;
Game.switchScene( InterlevelScene.class );
return false;
} else if (getCloser( stairs )) {
return true;
} else {
ready();
return false;
}
}
private boolean actAscend( HeroAction.Ascend action ) {
int stairs = action.dst;
if (rooted){
Camera.main.shake( 1, 1f );
ready();
return false;
//there can be multiple entrance tiles, so descend on any of them
//TODO this is slightly brittle, it assumes there are no disjointed sets of entrance tiles
} else if (Dungeon.level.map[pos] == Terrain.ENTRANCE) {
if (Dungeon.depth == 1) {
if (transition.type == LevelTransition.Type.SURFACE){
if (belongings.getItem( Amulet.class ) == null) {
Game.runOnRenderThread(new Callback() {
@Override
@@ -1068,9 +1027,9 @@ public class Hero extends Char {
Dungeon.deleteGame( GamesInProgress.curSlot, true );
Game.switchScene( SurfaceScene.class );
}
} else {
curAction = null;
TimekeepersHourglass.timeFreeze timeFreeze = buff(TimekeepersHourglass.timeFreeze.class);
@@ -1078,8 +1037,15 @@ public class Hero extends Char {
Swiftthistle.TimeBubble timeBubble = buff(Swiftthistle.TimeBubble.class);
if (timeBubble != null) timeBubble.disarmPressedTraps();
InterlevelScene.mode = InterlevelScene.Mode.ASCEND;
Game.switchScene( InterlevelScene.class );
InterlevelScene.curTransition = transition;
//TODO probably want to make this more flexible when more types exist
if (transition.type == LevelTransition.Type.REGULAR_EXIT) {
InterlevelScene.mode = InterlevelScene.Mode.DESCEND;
} else {
InterlevelScene.mode = InterlevelScene.Mode.ASCEND;
}
Game.switchScene(InterlevelScene.class);
}
return false;
@@ -1486,16 +1452,13 @@ public class Hero extends Char {
curAction = new HeroAction.Unlock( cell );
} else if ((cell == Dungeon.level.exit || Dungeon.level.map[cell] == Terrain.EXIT || Dungeon.level.map[cell] == Terrain.UNLOCKED_EXIT)
&& Dungeon.depth < 26) {
} else if (Dungeon.level.getTransition(cell) != null
&& !Dungeon.level.locked
&& (Dungeon.depth < 26 || Dungeon.level.getTransition(cell).type == LevelTransition.Type.REGULAR_ENTRANCE) ) {
curAction = new HeroAction.LvlTransition( cell );
curAction = new HeroAction.Descend( cell );
} else if (cell == Dungeon.level.entrance || Dungeon.level.map[cell] == Terrain.ENTRANCE) {
curAction = new HeroAction.Ascend( cell );
} else {
} else {
if (!Dungeon.level.visited[cell] && !Dungeon.level.mapped[cell]
&& Dungeon.level.traps.get(cell) != null && Dungeon.level.traps.get(cell).visible) {
@@ -64,14 +64,8 @@ public class HeroAction {
}
}
public static class Descend extends HeroAction {
public Descend( int stairs ) {
this.dst = stairs;
}
}
public static class Ascend extends HeroAction {
public Ascend( int stairs ) {
public static class LvlTransition extends HeroAction {
public LvlTransition(int stairs ) {
this.dst = stairs;
}
}
@@ -110,7 +110,7 @@ public class WarpBeacon extends ArmorAbility {
armor.charge -= chargeNeeded;
armor.updateQuickslot();
if (tracker.depth == Dungeon.depth){
if (tracker.depth == Dungeon.depth && tracker.branch == Dungeon.branch){
Char existing = Actor.findChar(tracker.pos);
ScrollOfTeleportation.appear(hero, tracker.pos);
@@ -172,6 +172,7 @@ public class WarpBeacon extends ArmorAbility {
InterlevelScene.mode = InterlevelScene.Mode.RETURN;
InterlevelScene.returnDepth = tracker.depth;
InterlevelScene.returnBranch = tracker.branch;
InterlevelScene.returnPos = tracker.pos;
Game.switchScene( InterlevelScene.class );
}
@@ -203,6 +204,7 @@ public class WarpBeacon extends ArmorAbility {
WarpBeaconTracker tracker = new WarpBeaconTracker();
tracker.pos = target;
tracker.depth = Dungeon.depth;
tracker.branch = Dungeon.branch;
tracker.attachTo(hero);
hero.sprite.operate(target);
@@ -220,6 +222,7 @@ public class WarpBeacon extends ArmorAbility {
int pos;
int depth;
int branch;
Emitter e;
@@ -234,12 +237,14 @@ public class WarpBeacon extends ArmorAbility {
public static final String POS = "pos";
public static final String DEPTH = "depth";
public static final String BRANCH = "branch";
@Override
public void storeInBundle(Bundle bundle) {
super.storeInBundle(bundle);
bundle.put(POS, pos);
bundle.put(DEPTH, depth);
bundle.put(BRANCH, branch);
}
@Override
@@ -247,6 +252,7 @@ public class WarpBeacon extends ArmorAbility {
super.restoreFromBundle(bundle);
pos = bundle.getInt(POS);
depth = bundle.getInt(DEPTH);
branch = bundle.getInt(BRANCH);
}
}
@@ -376,9 +376,9 @@ public class YogDzewa extends Mob {
addFist((YogFist)Reflection.newInstance(challengeSummons.remove(0)));
}
CellEmitter.get(Dungeon.level.exit-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit).burst(ShadowParticle.UP, 100);
CellEmitter.get(Dungeon.level.exit+1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit()-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit()).burst(ShadowParticle.UP, 100);
CellEmitter.get(Dungeon.level.exit()+1).burst(ShadowParticle.UP, 25);
if (abilityCooldown < 5) abilityCooldown = 5;
if (summonCooldown < 5) summonCooldown = 5;
@@ -391,16 +391,16 @@ public class YogDzewa extends Mob {
}
public void addFist(YogFist fist){
fist.pos = Dungeon.level.exit;
fist.pos = Dungeon.level.exit();
CellEmitter.get(Dungeon.level.exit-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit).burst(ShadowParticle.UP, 100);
CellEmitter.get(Dungeon.level.exit+1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit()-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(Dungeon.level.exit()).burst(ShadowParticle.UP, 100);
CellEmitter.get(Dungeon.level.exit()+1).burst(ShadowParticle.UP, 25);
if (abilityCooldown < 5) abilityCooldown = 5;
if (summonCooldown < 5) summonCooldown = 5;
int targetPos = Dungeon.level.exit + Dungeon.level.width();
int targetPos = Dungeon.level.exit() + Dungeon.level.width();
if (!Dungeon.isChallenged(Challenges.STRONGER_BOSSES)
&& Actor.findChar(targetPos) == null){
@@ -414,7 +414,7 @@ public class YogDzewa extends Mob {
}
GameScene.add(fist, 4);
Actor.addDelayed( new Pushing( fist, Dungeon.level.exit, fist.pos ), -1 );
Actor.addDelayed( new Pushing( fist, Dungeon.level.exit(), fist.pos ), -1 );
}
public void updateVisibility( Level level ){
@@ -105,7 +105,7 @@ public abstract class YogFist extends Mob {
private boolean invulnWarned = false;
protected boolean isNearYog(){
int yogPos = Dungeon.level.exit + 3*Dungeon.level.width();
int yogPos = Dungeon.level.exit() + 3*Dungeon.level.width();
return Dungeon.level.distance(pos, yogPos) <= 4;
}
@@ -339,7 +339,7 @@ public abstract class YogFist extends Mob {
}
private boolean canSpreadGrass(int cell){
int yogPos = Dungeon.level.exit + Dungeon.level.width()*3;
int yogPos = Dungeon.level.exit() + Dungeon.level.width()*3;
return Dungeon.level.distance(cell, yogPos) > 4 && !Dungeon.level.solid[cell]
&& !(Dungeon.level.map[cell] == Terrain.FURROWED_GRASS || Dungeon.level.map[cell] == Terrain.HIGH_GRASS);
}
@@ -499,7 +499,7 @@ public abstract class YogFist extends Mob {
} while (Dungeon.level.heroFOV[i]
|| Dungeon.level.solid[i]
|| Actor.findChar(i) != null
|| PathFinder.getStep(i, Dungeon.level.exit, Dungeon.level.passable) == -1);
|| PathFinder.getStep(i, Dungeon.level.exit(), Dungeon.level.passable) == -1);
ScrollOfTeleportation.appear(this, i);
state = WANDERING;
GameScene.flash(0x80FFFFFF);
@@ -570,7 +570,7 @@ public abstract class YogFist extends Mob {
} while (Dungeon.level.heroFOV[i]
|| Dungeon.level.solid[i]
|| Actor.findChar(i) != null
|| PathFinder.getStep(i, Dungeon.level.exit, Dungeon.level.passable) == -1);
|| PathFinder.getStep(i, Dungeon.level.exit(), Dungeon.level.passable) == -1);
ScrollOfTeleportation.appear(this, i);
state = WANDERING;
GameScene.flash(0, false);
@@ -84,18 +84,18 @@ public class RatKing extends NPC {
@Override
protected boolean act() {
if (Dungeon.depth < 5){
if (pos == Dungeon.level.exit){
if (pos == Dungeon.level.exit()){
destroy();
sprite.killAndErase();
} else {
target = Dungeon.level.exit;
target = Dungeon.level.exit();
}
} else if (Dungeon.depth > 5){
if (pos == Dungeon.level.entrance){
if (pos == Dungeon.level.entrance()){
destroy();
sprite.killAndErase();
} else {
target = Dungeon.level.entrance;
target = Dungeon.level.entrance();
}
}
return super.act();
@@ -285,7 +285,7 @@ public class Wandmaker extends NPC {
do {
validPos = true;
npc.pos = level.pointToCell(room.random());
if (npc.pos == level.entrance){
if (npc.pos == level.entrance()){
validPos = false;
}
for (Point door : room.connected.values()){
@@ -57,6 +57,7 @@ public class ScrollOfPassage extends ExoticScroll {
InterlevelScene.mode = InterlevelScene.Mode.RETURN;
InterlevelScene.returnDepth = Math.max(1, (Dungeon.depth - 1 - (Dungeon.depth-2)%5));
InterlevelScene.returnBranch = 0;
InterlevelScene.returnPos = -1;
Game.switchScene( InterlevelScene.class );
}
@@ -51,6 +51,7 @@ public class BeaconOfReturning extends Spell {
}
public int returnDepth = -1;
public int returnBranch = 0;
public int returnPos;
@Override
@@ -94,6 +95,7 @@ public class BeaconOfReturning extends Spell {
private void setBeacon(Hero hero ){
returnDepth = Dungeon.depth;
returnBranch = Dungeon.branch;
returnPos = hero.pos;
hero.spend( 1f );
@@ -120,9 +122,9 @@ public class BeaconOfReturning extends Spell {
}
}
if (returnDepth == Dungeon.depth) {
if (returnDepth == Dungeon.depth && returnBranch == Dungeon.branch) {
if (!Dungeon.level.passable[returnPos] && !Dungeon.level.avoid[returnPos]){
returnPos = Dungeon.level.entrance;
returnPos = Dungeon.level.entrance();
}
ScrollOfTeleportation.appear( hero, returnPos );
for(Mob m : Dungeon.level.mobs){
@@ -149,6 +151,7 @@ public class BeaconOfReturning extends Spell {
InterlevelScene.mode = InterlevelScene.Mode.RETURN;
InterlevelScene.returnDepth = returnDepth;
InterlevelScene.returnBranch = returnBranch;
InterlevelScene.returnPos = returnPos;
Game.switchScene( InterlevelScene.class );
}
@@ -309,6 +309,7 @@ public class CursedWand {
InterlevelScene.mode = InterlevelScene.Mode.RETURN;
InterlevelScene.returnDepth = depth;
InterlevelScene.returnBranch = 0;
InterlevelScene.returnPos = -1;
Game.switchScene(InterlevelScene.class);
@@ -331,8 +331,8 @@ public class WandOfRegrowth extends Wand {
ArrayList<Integer> candidates = new ArrayList<>();
for (int i : PathFinder.NEIGHBOURS8){
if (Dungeon.level.passable[pos+i]
&& pos+i != Dungeon.level.entrance
&& pos+i != Dungeon.level.exit){
&& pos+i != Dungeon.level.entrance()
&& pos+i != Dungeon.level.exit()){
candidates.add(pos+i);
}
}
@@ -371,8 +371,8 @@ public class WandOfRegrowth extends Wand {
ArrayList<Integer> candidates = new ArrayList<>();
for (int i : PathFinder.NEIGHBOURS8){
if (Dungeon.level.passable[pos+i]
&& pos+i != Dungeon.level.entrance
&& pos+i != Dungeon.level.exit){
&& pos+i != Dungeon.level.entrance()
&& pos+i != Dungeon.level.exit()){
candidates.add(pos+i);
}
}
@@ -40,6 +40,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.particles.BlastParticle;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.SparkParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.CavesPainter;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
@@ -151,7 +152,10 @@ public class CavesBossLevel extends Level {
Painter.fill(this, 16, 5, 1, 6, Terrain.EMPTY_SP);
Painter.fill(this, 15, 0, 3, 3, Terrain.EXIT);
exit = 16 + 2*width();
int exitCell = 16 + 2*width();
LevelTransition exit = new LevelTransition(this, exitCell, LevelTransition.Type.REGULAR_EXIT);
exit.set(14, 0, 18, 2);
transitions.add(exit);
CustomTilemap customVisuals = new CityEntrance();
customVisuals.setRect(0, 0, width(), 11);
@@ -173,6 +177,13 @@ public class CavesBossLevel extends Level {
public void restoreFromBundle(Bundle bundle) {
super.restoreFromBundle(bundle);
//pre-1.3.0 saves, modifies exit transition with custom size
if (bundle.contains("exit")){
LevelTransition exit = getTransition(LevelTransition.Type.REGULAR_EXIT);
exit.set(14, 0, 18, 2);
transitions.add(exit);
}
for (CustomTilemap c : customTiles){
if (c instanceof ArenaVisuals){
customArenaVisuals = (ArenaVisuals) c;
@@ -201,7 +212,7 @@ public class CavesBossLevel extends Level {
int pos;
do {
pos = randomRespawnCell(null);
} while (pos == entrance);
} while (pos == entrance());
drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS;
}
}
@@ -209,12 +220,12 @@ public class CavesBossLevel extends Level {
@Override
public int randomRespawnCell( Char ch ) {
//this check is mainly here for DM-300, to prevent an infinite loop
if (Char.hasProp(ch, Char.Property.LARGE) && map[entrance] != Terrain.ENTRANCE){
if (Char.hasProp(ch, Char.Property.LARGE) && map[entrance()] != Terrain.ENTRANCE){
return -1;
}
int cell;
do {
cell = entrance + PathFinder.NEIGHBOURS8[Random.Int(8)];
cell = entrance() + PathFinder.NEIGHBOURS8[Random.Int(8)];
} while (!passable[cell]
|| (Char.hasProp(ch, Char.Property.LARGE) && !openSpace[cell])
|| Actor.findChar(cell) != null);
@@ -252,6 +263,7 @@ public class CavesBossLevel extends Level {
public void seal() {
super.seal();
int entrance = entrance();
set( entrance, Terrain.WALL );
Heap heap = Dungeon.level.heaps.get( entrance );
@@ -302,7 +314,7 @@ public class CavesBossLevel extends Level {
blobs.get(PylonEnergy.class).fullyClear();
set( entrance, Terrain.ENTRANCE );
set( entrance(), Terrain.ENTRANCE );
int i = 14 + 13*width();
for (int j = 0; j < 5; j++){
set( i+j, Terrain.EMPTY );
@@ -480,7 +492,7 @@ public class CavesBossLevel extends Level {
};
private void buildEntrance(){
entrance = 16 + 25*width();
int entrance = 16 + 25*width();
//entrance area
int NW = entrance - 7 - 7*width();
@@ -502,6 +514,7 @@ public class CavesBossLevel extends Level {
}
Painter.set(this, entrance, Terrain.ENTRANCE);
transitions.add(new LevelTransition(this, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
}
private static short[] corner1 = {
@@ -31,6 +31,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.CityPainter;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.ImpShopRoom;
@@ -119,6 +120,12 @@ public class CityBossLevel extends Level {
@Override
public void restoreFromBundle( Bundle bundle ) {
super.restoreFromBundle( bundle );
//pre-1.3.0 saves, modifies exit transition with custom size
if (bundle.contains("exit")){
LevelTransition exit = getTransition(LevelTransition.Type.REGULAR_EXIT);
exit.set(end.left+4, end.top+4, end.left+4+6, end.top+4+4);
transitions.add(exit);
}
impShop = (ImpShopRoom) bundle.get( IMP_SHOP );
if (map[topDoor] != Terrain.LOCKED_DOOR && Imp.Quest.isCompleted() && !impShop.shopSpawned()){
spawnShop();
@@ -147,8 +154,9 @@ public class CityBossLevel extends Level {
Painter.set(this, c.x, entry.top, Terrain.DOOR);
entrance = c.x + (c.y+2)*width();
int entrance = c.x + (c.y+2)*width();
Painter.set(this, entrance, Terrain.ENTRANCE);
transitions.add(new LevelTransition(this, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
//DK's throne room
Painter.fillDiamond(this, arena, 1, Terrain.EMPTY);
@@ -173,7 +181,11 @@ public class CityBossLevel extends Level {
Painter.fill(this, end, Terrain.CHASM);
Painter.fill(this, end.left+4, end.top+5, 7, 18, Terrain.EMPTY);
Painter.fill(this, end.left+4, end.top+5, 7, 4, Terrain.EXIT);
exit = end.left+7 + (end.top+8)*width();
int exitCell = end.left+7 + (end.top+8)*width();
LevelTransition exit = new LevelTransition(this, exitCell, LevelTransition.Type.REGULAR_EXIT);
exit.set(end.left+4, end.top+4, end.left+4+6, end.top+4+4);
transitions.add(exit);
impShop = new ImpShopRoom();
impShop.set(end.left+3, end.top+12, end.left+11, end.top+20);
@@ -255,7 +267,7 @@ public class CityBossLevel extends Level {
int pos;
do {
pos = randomRespawnCell(null);
} while (pos == entrance);
} while (pos == entrance());
drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS;
}
}
@@ -264,7 +276,7 @@ public class CityBossLevel extends Level {
public int randomRespawnCell( Char ch ) {
int cell;
do {
cell = entrance + PathFinder.NEIGHBOURS8[Random.Int(8)];
cell = entrance() + PathFinder.NEIGHBOURS8[Random.Int(8)];
} while (!passable[cell]
|| (Char.hasProp(ch, Char.Property.LARGE) && !openSpace[cell])
|| Actor.findChar(cell) != null);
@@ -25,6 +25,7 @@ import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
public class DeadEndLevel extends Level {
@@ -64,11 +65,10 @@ public class DeadEndLevel extends Level {
Terrain.WATER;
}
entrance = SIZE * width() + SIZE / 2 + 1;
int entrance = SIZE * width() + SIZE / 2 + 1;
transitions.add(new LevelTransition(this, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
map[entrance] = Terrain.ENTRANCE;
exit = 0;
return true;
}
@@ -91,7 +91,7 @@ public class DeadEndLevel extends Level {
@Override
public int randomRespawnCell( Char ch ) {
return entrance-width();
return entrance()-width();
}
}
@@ -33,6 +33,7 @@ import com.shatteredpixel.shatteredpixeldungeon.effects.particles.FlameParticle;
import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ShadowParticle;
import com.shatteredpixel.shatteredpixeldungeon.items.Heap;
import com.shatteredpixel.shatteredpixeldungeon.items.Item;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene;
@@ -68,7 +69,7 @@ public class HallsBossLevel extends Level {
if (locked){
Music.INSTANCE.play(Assets.Music.HALLS_BOSS, true);
//if exit isn't unlocked
} else if (map[exit] != Terrain.EXIT){
} else if (map[exit()] != Terrain.EXIT){
Music.INSTANCE.end();
} else {
Music.INSTANCE.playTracks(
@@ -112,7 +113,8 @@ public class HallsBossLevel extends Level {
Painter.fill(this, 4 + i * 5, top, 5, bottom - top + 1, Terrain.EMPTY);
if (i == 2) {
entrance = (6 + i * 5) + (bottom - 1) * width();
int entrance = (6 + i * 5) + (bottom - 1) * width();
transitions.add(new LevelTransition(this, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
}
}
@@ -124,7 +126,7 @@ public class HallsBossLevel extends Level {
}
}
map[entrance] = Terrain.ENTRANCE;
map[entrance()] = Terrain.ENTRANCE;
Painter.fill(this, ROOM_LEFT-1, ROOM_TOP-1, 11, 11, Terrain.EMPTY );
@@ -149,7 +151,12 @@ public class HallsBossLevel extends Level {
Painter.fill(this, ROOM_LEFT+3, ROOM_TOP+2, 3, 4, Terrain.EMPTY );
exit = width/2 + ((ROOM_TOP+1) * width);
int exitCell = width/2 + ((ROOM_TOP+1) * width);
LevelTransition exit = new LevelTransition(this, exitCell, LevelTransition.Type.REGULAR_EXIT);
exit.top--;
exit.left--;
exit.right++;
transitions.add(exit);
CustomTilemap vis = new CenterPieceVisuals();
vis.pos(ROOM_LEFT, ROOM_TOP+1);
@@ -165,7 +172,7 @@ public class HallsBossLevel extends Level {
}
//ensures a path to the exit exists
return (PathFinder.getStep(entrance, exit, passable) != -1);
return (PathFinder.getStep(entrance(), exit(), passable) != -1);
}
@Override
@@ -183,14 +190,14 @@ public class HallsBossLevel extends Level {
int pos;
do {
pos = randomRespawnCell(null);
} while (pos == entrance);
} while (pos == entrance());
drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS;
}
}
@Override
public int randomRespawnCell( Char ch ) {
int pos = entrance;
int pos = entrance();
int cell;
do {
cell = pos + PathFinder.NEIGHBOURS8[Random.Int(8)];
@@ -204,8 +211,8 @@ public class HallsBossLevel extends Level {
public void occupyCell( Char ch ) {
super.occupyCell( ch );
if (map[entrance] == Terrain.ENTRANCE && map[exit] != Terrain.EXIT
&& ch == Dungeon.hero && Dungeon.level.distance(ch.pos, entrance) >= 2) {
if (map[entrance()] == Terrain.ENTRANCE && map[exit()] != Terrain.EXIT
&& ch == Dungeon.hero && Dungeon.level.distance(ch.pos, entrance()) >= 2) {
seal();
}
}
@@ -213,6 +220,7 @@ public class HallsBossLevel extends Level {
@Override
public void seal() {
super.seal();
int entrance = entrance();
set( entrance, Terrain.EMPTY_SP );
GameScene.updateMap( entrance );
CellEmitter.get( entrance ).start( FlameParticle.FACTORY, 0.1f, 10 );
@@ -220,22 +228,22 @@ public class HallsBossLevel extends Level {
Dungeon.observe();
YogDzewa boss = new YogDzewa();
boss.pos = exit + width*3;
boss.pos = exit() + width*3;
GameScene.add( boss );
}
@Override
public void unseal() {
super.unseal();
set( entrance, Terrain.ENTRANCE );
GameScene.updateMap( entrance );
set( entrance(), Terrain.ENTRANCE );
GameScene.updateMap( entrance() );
set( exit, Terrain.EXIT );
GameScene.updateMap( exit );
set( exit(), Terrain.EXIT );
GameScene.updateMap( exit() );
CellEmitter.get(exit-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(exit).burst(ShadowParticle.UP, 100);
CellEmitter.get(exit+1).burst(ShadowParticle.UP, 25);
CellEmitter.get(exit()-1).burst(ShadowParticle.UP, 25);
CellEmitter.get(exit()).burst(ShadowParticle.UP, 100);
CellEmitter.get(exit()+1).burst(ShadowParticle.UP, 25);
for( CustomTilemap t : customTiles){
if (t instanceof CenterPieceVisuals){
((CenterPieceVisuals) t).updateState();
@@ -336,7 +344,7 @@ public class HallsBossLevel extends Level {
private void updateState(){
if (vis != null){
int[] data = map.clone();
if (Dungeon.level.map[Dungeon.level.exit] == Terrain.EXIT) {
if (Dungeon.level.map[Dungeon.level.exit()] == Terrain.EXIT) {
data[4] = 19;
data[12] = data[14] = 31;
}
@@ -375,7 +383,7 @@ public class HallsBossLevel extends Level {
private void updateState(){
if (vis != null){
int[] data = map.clone();
if (Dungeon.level.map[Dungeon.level.exit] == Terrain.EXIT) {
if (Dungeon.level.map[Dungeon.level.exit()] == Terrain.EXIT) {
data[3] = 1;
data[4] = 0;
data[5] = 2;
@@ -28,6 +28,7 @@ import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob;
import com.shatteredpixel.shatteredpixeldungeon.items.Amulet;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap;
@@ -89,6 +90,9 @@ public class LastLevel extends Level {
}
private static final int ROOM_TOP = 10;
private static final int WIDTH = 16;
private static final int MID = WIDTH/2;
public static int AMULET_POS = 12*WIDTH + MID;
@Override
protected boolean build() {
@@ -103,15 +107,18 @@ public class LastLevel extends Level {
Painter.fill( this, MID - 2, height - 3, 5, 1, Terrain.EMPTY);
Painter.fill( this, MID - 3, height - 2, 7, 1, Terrain.EMPTY);
entrance = (height-ROOM_TOP) * width() + MID;
int entrance = (height-ROOM_TOP) * width() + MID;
Painter.fill(this, 0, height - ROOM_TOP, width, 2, Terrain.WALL);
map[entrance] = Terrain.ENTRANCE;
map[entrance+width] = Terrain.ENTRANCE;
LevelTransition entry = new LevelTransition(this, entrance, LevelTransition.Type.REGULAR_ENTRANCE);
entry.left--;
entry.right++;
entry.bottom += 2;
transitions.add(entry);
Painter.fill(this, 0, height - ROOM_TOP + 2, width, 8, Terrain.EMPTY);
Painter.fill(this, MID-1, height - ROOM_TOP + 2, 3, 1, Terrain.ENTRANCE);
exit = 12*(width()) + MID;
for (int i=0; i < length(); i++) {
if (map[i] == Terrain.EMPTY && Random.Int( 5 ) == 0) {
map[i] = Terrain.EMPTY_DECO;
@@ -154,14 +161,14 @@ public class LastLevel extends Level {
@Override
protected void createItems() {
drop( new Amulet(), exit );
drop( new Amulet(), AMULET_POS );
}
@Override
public int randomRespawnCell( Char ch ) {
int cell;
do {
cell = entrance + PathFinder.NEIGHBOURS8[Random.Int(8)];
cell = entrance() + PathFinder.NEIGHBOURS8[Random.Int(8)];
} while (!passable[cell]
|| (Char.hasProp(ch, Char.Property.LARGE) && !openSpace[cell])
|| Actor.findChar(cell) != null);
@@ -209,6 +216,9 @@ public class LastLevel extends Level {
@Override
public void restoreFromBundle(Bundle bundle) {
//pre-1.3.0 saves, deletes unneeded exit
if (bundle.contains("exit")) bundle.remove("exit");
super.restoreFromBundle(bundle);
for (int i=0; i < length(); i++) {
int flags = Terrain.flags[map[i]];
@@ -250,7 +260,7 @@ public class LastLevel extends Level {
public Tilemap create() {
Tilemap v = super.create();
int candlesStart = Dungeon.level.exit - 3 - 3*Dungeon.level.width();
int candlesStart = AMULET_POS - 3 - 3*Dungeon.level.width();
int cell = tileX + tileY * Dungeon.level.width();
int[] map = Dungeon.level.map;
@@ -121,7 +121,7 @@ public class LastShopLevel extends RegularLevel {
int pos;
do {
pos = pointToCell(roomEntrance.random());
} while (pos == entrance);
} while (pos == entrance());
drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS;
}
}
@@ -69,6 +69,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.HeavyBoome
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Door;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.HighGrass;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
import com.shatteredpixel.shatteredpixeldungeon.mechanics.ShadowCaster;
@@ -143,6 +144,8 @@ public abstract class Level implements Bundlable {
public int entrance;
public int exit;
public ArrayList<LevelTransition> transitions;
//when a boss level has become locked.
public boolean locked = false;
@@ -167,8 +170,7 @@ public abstract class Level implements Bundlable {
private static final String MAP = "map";
private static final String VISITED = "visited";
private static final String MAPPED = "mapped";
private static final String ENTRANCE = "entrance";
private static final String EXIT = "exit";
private static final String TRANSITIONS = "transitions";
private static final String LOCKED = "locked";
private static final String HEAPS = "heaps";
private static final String PLANTS = "plants";
@@ -249,6 +251,8 @@ public abstract class Level implements Bundlable {
do {
width = height = length = 0;
transitions = new ArrayList<>();
mobs = new HashSet<>();
heaps = new SparseArray<>();
blobs = new HashMap<>();
@@ -334,9 +338,24 @@ public abstract class Level implements Bundlable {
visited = bundle.getBooleanArray( VISITED );
mapped = bundle.getBooleanArray( MAPPED );
entrance = bundle.getInt( ENTRANCE );
exit = bundle.getInt( EXIT );
transitions = new ArrayList<>();
if (bundle.contains(TRANSITIONS)){
for (Bundlable b : bundle.getCollection( TRANSITIONS )){
transitions.add((LevelTransition) b);
}
//pre-1.3.0 saves, converts old entrance/exit to new transitions
} else {
if (bundle.contains("entrance")){
transitions.add(new LevelTransition(
this,
bundle.getInt("entrance"),
Dungeon.depth == 1 ? LevelTransition.Type.SURFACE : LevelTransition.Type.REGULAR_ENTRANCE));
}
if (bundle.contains("exit")){
transitions.add(new LevelTransition(this, bundle.getInt("exit"), LevelTransition.Type.REGULAR_EXIT));
}
}
locked = bundle.getBoolean( LOCKED );
@@ -412,8 +431,7 @@ public abstract class Level implements Bundlable {
bundle.put( MAP, map );
bundle.put( VISITED, visited );
bundle.put( MAPPED, mapped );
bundle.put( ENTRANCE, entrance );
bundle.put( EXIT, exit );
bundle.put( TRANSITIONS, transitions );
bundle.put( LOCKED, locked );
bundle.put( HEAPS, heaps.valueList() );
bundle.put( PLANTS, plants.valueList() );
@@ -471,6 +489,44 @@ public abstract class Level implements Bundlable {
abstract protected void createItems();
public int entrance(){
LevelTransition l = getTransition(null);
if (l != null){
return l.cell();
}
return 0;
}
public int exit(){
LevelTransition l = getTransition(LevelTransition.Type.REGULAR_EXIT);
if (l != null){
return l.cell();
}
return 0;
}
public LevelTransition getTransition(LevelTransition.Type type){
for (LevelTransition transition : transitions){
//if we don't specify a type, prefer to return any entrance
if (type == null &&
(transition.type == LevelTransition.Type.REGULAR_ENTRANCE || transition.type == LevelTransition.Type.SURFACE)){
return transition;
} else if (transition.type == type){
return transition;
}
}
return (type == null && !transitions.isEmpty() ? transitions.get(0) : null);
}
public LevelTransition getTransition(int cell){
for (LevelTransition transition : transitions){
if (transition.inside(cell)){
return transition;
}
}
return null;
}
public void seal(){
if (!locked) {
locked = true;
@@ -39,6 +39,7 @@ 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.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.TenguDartTrap;
import com.shatteredpixel.shatteredpixeldungeon.levels.traps.Trap;
@@ -132,6 +133,20 @@ public class PrisonBossLevel extends Level {
public void restoreFromBundle( Bundle bundle ) {
super.restoreFromBundle(bundle);
state = bundle.getEnum( STATE, State.class );
//pre-1.3.0 saves, recreates custom exit and entrance transitions
if (bundle.contains("entrance")){
transitions.clear();
if (state == State.START || state == State.WON){
transitions.add(new LevelTransition(this, ENTRANCE_POS, LevelTransition.Type.REGULAR_ENTRANCE));
}
if (state == State.WON){
LevelTransition exit = new LevelTransition(this, pointToCell(levelExit), LevelTransition.Type.REGULAR_EXIT);
exit.right+=2;
exit.bottom+=3;
transitions.add(exit);
}
}
//in some states tengu won't be in the world, in others he will be.
if (state == State.START || state == State.FIGHT_PAUSE) {
@@ -177,15 +192,14 @@ public class PrisonBossLevel extends Level {
new Point(8, 23), new Point(12, 23)};
private void setMapStart(){
entrance = ENTRANCE_POS;
exit = 0;
transitions.add(new LevelTransition(this, ENTRANCE_POS, LevelTransition.Type.REGULAR_ENTRANCE));
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.set(this, ENTRANCE_POS, Terrain.ENTRANCE);
Painter.fill(this, startHallway, Terrain.WALL);
Painter.fill(this, startHallway, 1, Terrain.EMPTY);
@@ -217,8 +231,7 @@ public class PrisonBossLevel extends Level {
private void setMapPause(){
setMapStart();
exit = entrance = 0;
transitions.clear();
Painter.set(this, tenguCell.left+4, tenguCell.top, Terrain.DOOR);
@@ -234,8 +247,8 @@ public class PrisonBossLevel extends Level {
private static final Rect arena = new Rect(3, 1, 18, 16);
private void setMapArena(){
exit = entrance = 0;
transitions.clear();
Painter.fill(this, 0, 0, 32, 32, Terrain.WALL);
Painter.fill(this, arena, Terrain.WALL);
@@ -250,7 +263,7 @@ public class PrisonBossLevel extends Level {
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+6);
private static final Point levelExit = new Point( endStart.x+11, endStart.y+6);
private static final int[] endMap = new int[]{
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,
@@ -308,8 +321,11 @@ public class PrisonBossLevel extends Level {
i += 14;
cell += width();
}
exit = pointToCell(levelExit);
LevelTransition exit = new LevelTransition(this, pointToCell(levelExit), LevelTransition.Type.REGULAR_EXIT);
exit.right+=2;
exit.bottom+=3;
transitions.add(exit);
}
//keep track of removed items as the level is changed. Dump them back into the level at the end.
@@ -224,7 +224,7 @@ public abstract class RegularLevel extends Level {
do {
mob.pos = pointToCell(roomToSpawn.random());
tries--;
} while (tries >= 0 && (findMob(mob.pos) != null || !passable[mob.pos] || solid[mob.pos] || mob.pos == exit
} while (tries >= 0 && (findMob(mob.pos) != null || !passable[mob.pos] || solid[mob.pos] || mob.pos == exit()
|| (!openSpace[mob.pos] && mob.properties().contains(Char.Property.LARGE))));
if (tries >= 0) {
@@ -239,7 +239,7 @@ public abstract class RegularLevel extends Level {
do {
mob.pos = pointToCell(roomToSpawn.random());
tries--;
} while (tries >= 0 && (findMob(mob.pos) != null || !passable[mob.pos] || solid[mob.pos] || mob.pos == exit
} while (tries >= 0 && (findMob(mob.pos) != null || !passable[mob.pos] || solid[mob.pos] || mob.pos == exit()
|| (!openSpace[mob.pos] && mob.properties().contains(Char.Property.LARGE))));
if (tries >= 0) {
@@ -283,7 +283,7 @@ public abstract class RegularLevel extends Level {
&& !solid[cell]
&& (!Char.hasProp(ch, Char.Property.LARGE) || openSpace[cell])
&& room.canPlaceCharacter(cellToPoint(cell), this)
&& cell != exit) {
&& cell != exit()) {
return cell;
}
@@ -530,7 +530,7 @@ public abstract class RegularLevel extends Level {
if (room != roomEntrance) {
int pos = pointToCell(room.random());
if (passable[pos] && !solid[pos]
&& pos != exit
&& pos != exit()
&& heaps.get(pos) == null
&& findMob(pos) == null) {
@@ -56,8 +56,6 @@ public class SewerBossLevel extends SewerLevel {
color2 = 0x59994a;
}
private int stairs = 0;
@Override
public void playLevelMusic() {
if (locked){
@@ -147,7 +145,7 @@ public class SewerBossLevel extends SewerLevel {
int pos;
do {
pos = pointToCell(roomEntrance.random());
} while (pos == entrance || solid[pos]);
} while (pos == entrance() || solid[pos]);
drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS;
}
}
@@ -157,7 +155,7 @@ public class SewerBossLevel extends SewerLevel {
int pos;
do {
pos = pointToCell(roomEntrance.random());
} while (pos == entrance
} while (pos == entrance()
|| !passable[pos]
|| (Char.hasProp(ch, Char.Property.LARGE) && !openSpace[pos])
|| Actor.findChar(pos) != null);
@@ -166,16 +164,13 @@ public class SewerBossLevel extends SewerLevel {
public void seal() {
if (entrance != 0) {
if (!locked) {
super.seal();
set( entrance, Terrain.WATER );
GameScene.updateMap( entrance );
GameScene.ripple( entrance );
stairs = entrance;
entrance = 0;
set( entrance(), Terrain.WATER );
GameScene.updateMap( entrance() );
GameScene.ripple( entrance() );
Game.runOnRenderThread(new Callback() {
@Override
@@ -187,15 +182,12 @@ public class SewerBossLevel extends SewerLevel {
}
public void unseal() {
if (stairs != 0) {
if (locked) {
super.unseal();
entrance = stairs;
stairs = 0;
set( entrance, Terrain.ENTRANCE );
GameScene.updateMap( entrance );
set( entrance(), Terrain.ENTRANCE );
GameScene.updateMap( entrance() );
Game.runOnRenderThread(new Callback() {
@Override
@@ -209,23 +201,19 @@ public class SewerBossLevel extends SewerLevel {
@Override
public Group addVisuals() {
super.addVisuals();
if (map[exit-1] != Terrain.WALL_DECO) visuals.add(new PrisonLevel.Torch(exit-1));
if (map[exit+1] != Terrain.WALL_DECO) visuals.add(new PrisonLevel.Torch(exit+1));
if (map[exit()-1] != Terrain.WALL_DECO) visuals.add(new PrisonLevel.Torch(exit()-1));
if (map[exit()+1] != Terrain.WALL_DECO) visuals.add(new PrisonLevel.Torch(exit()+1));
return visuals;
}
private static final String STAIRS = "stairs";
@Override
public void storeInBundle( Bundle bundle ) {
super.storeInBundle( bundle );
bundle.put( STAIRS, stairs );
}
@Override
public void restoreFromBundle( Bundle bundle ) {
//pre-1.3.0 saves
if (bundle.getInt("stairs") != 0){
bundle.put("entrance", bundle.getInt("stairs"));
bundle.remove("stairs");
}
super.restoreFromBundle( bundle );
stairs = bundle.getInt( STAIRS );
roomExit = roomEntrance;
}
}
@@ -0,0 +1,154 @@
/*
* Pixel Dungeon
* Copyright (C) 2012-2015 Oleg Dolya
*
* Shattered Pixel Dungeon
* Copyright (C) 2014-2022 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.features;
import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.watabou.utils.Bundlable;
import com.watabou.utils.Bundle;
import com.watabou.utils.Point;
import com.watabou.utils.Random;
import com.watabou.utils.Rect;
public class LevelTransition extends Rect implements Bundlable {
public enum Type {
SURFACE,
REGULAR_ENTRANCE,
REGULAR_EXIT;
}
public Type type;
public int destDepth;
public int destBranch;
public Type destType;
public int centerCell;
//for bundling
public LevelTransition(){
super();
}
public LevelTransition(Level level, int cell, Type type, int destDepth, int destBranch, Type destType){
centerCell = cell;
Point p = level.cellToPoint(cell);
set(p.x, p.y, p.x, p.y);
this.type = type;
this.destDepth = destDepth;
this.destBranch = destBranch;
this.destType = destType;
}
//gives default values for common transition types
public LevelTransition(Level level, int cell, Type type){
centerCell = cell;
Point p = level.cellToPoint(cell);
set(p.x, p.y, p.x, p.y);
this.type = type;
switch (type){
case REGULAR_ENTRANCE: default:
destDepth = Dungeon.depth-1;
destBranch = Dungeon.branch;
destType = Type.REGULAR_EXIT;
break;
case REGULAR_EXIT:
destDepth = Dungeon.depth+1;
destBranch = Dungeon.branch;
destType = Type.REGULAR_ENTRANCE;
break;
case SURFACE:
destDepth = 0;
destBranch = 0;
destType = null;
break;
}
}
//note that the center cell isn't always the actual center.
// It is important when game logic needs to pick a specific cell for some action
// e.g. where to place the hero
public int cell(){
return centerCell;
}
//Transitions are inclusive to their right and bottom sides
@Override
public int width() {
return super.width()+1;
}
@Override
public int height() {
return super.height()+1;
}
@Override
public boolean inside(Point p) {
return p.x >= left && p.x <= right && p.y >= top && p.y <= bottom;
}
public boolean inside(int cell){
return inside(new Point(Dungeon.level.cellToPoint(cell)));
}
public Point center() {
return new Point(
(left + right) / 2 + (((right - left) % 2) == 1 ? Random.Int( 2 ) : 0),
(top + bottom) / 2 + (((bottom - top) % 2) == 1 ? Random.Int( 2 ) : 0) );
}
public static final String TYPE = "type";
public static final String DEST_DEPTH = "dest_depth";
public static final String DEST_BRANCH = "dest_branch";
public static final String DEST_TYPE = "dest_type";
@Override
public void storeInBundle(Bundle bundle) {
bundle.put( "left", left );
bundle.put( "top", top );
bundle.put( "right", right );
bundle.put( "bottom", bottom );
bundle.put( "center", centerCell );
bundle.put(TYPE, type);
bundle.put(DEST_DEPTH, destDepth);
bundle.put(DEST_BRANCH, destBranch);
bundle.put(DEST_TYPE, destType);
}
@Override
public void restoreFromBundle(Bundle bundle) {
left = bundle.getInt( "left" );
top = bundle.getInt( "top" );
right = bundle.getInt( "right" );
bottom = bundle.getInt( "bottom" );
centerCell = bundle.getInt( "center" );
type = bundle.getEnum(TYPE, Type.class);
destDepth = bundle.getInt(DEST_DEPTH);
destBranch = bundle.getInt(DEST_BRANCH);
if (bundle.contains(DEST_TYPE)) destType = bundle.getEnum(DEST_TYPE, Type.class);
}
}
@@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.sewerboss;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.EntranceRoom;
@@ -46,12 +47,14 @@ public class SewerBossEntranceRoom extends EntranceRoom {
Painter.fill( level, left+1, top+1, width()-2, 1, Terrain.WALL_DECO);
Painter.fill( level, left+1, top+2, width()-2, 1, Terrain.WATER);
int entrance;
do {
level.entrance = level.pointToCell(random(3));
} while (level.findMob(level.entrance) != null);
Painter.set( level, level.entrance, Terrain.ENTRANCE );
entrance = level.pointToCell(random(3));
} while (level.findMob(entrance) != null);
Painter.set( level, entrance, Terrain.ENTRANCE );
level.transitions.add(new LevelTransition(level, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
for (Room.Door door : connected.values()) {
door.set( Room.Door.Type.REGULAR );
@@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.sewerboss;
import com.shatteredpixel.shatteredpixeldungeon.Assets;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.ExitRoom;
@@ -58,8 +59,13 @@ public class SewerBossExitRoom extends ExitRoom {
Painter.fill( level, c.x-1, c.y-1, 3, 2, Terrain.WALL );
Painter.fill( level, c.x-1, c.y+1, 3, 1, Terrain.EMPTY_SP );
level.exit = level.pointToCell(c);
Painter.set( level, level.exit, Terrain.LOCKED_EXIT );
int exitCell = level.pointToCell(c);
Painter.set( level, exitCell, Terrain.LOCKED_EXIT );
LevelTransition exit = new LevelTransition(level, exitCell, LevelTransition.Type.REGULAR_EXIT);
exit.top--;
exit.left--;
exit.right++;
level.transitions.add(exit);
CustomTilemap vis = new SewerExit();
vis.pos(c.x-1, c.y);
@@ -27,6 +27,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.journal.Guidebook;
import com.shatteredpixel.shatteredpixeldungeon.journal.Document;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.watabou.utils.Point;
@@ -58,10 +59,17 @@ public class EntranceRoom extends StandardRoom {
door.set( Room.Door.Type.REGULAR );
}
int entrance;
do {
level.entrance = level.pointToCell(random(2));
} while (level.findMob(level.entrance) != null);
Painter.set( level, level.entrance, Terrain.ENTRANCE );
entrance = level.pointToCell(random(2));
} while (level.findMob(entrance) != null);
Painter.set( level, entrance, Terrain.ENTRANCE );
if (Dungeon.depth == 1){
level.transitions.add(new LevelTransition(level, entrance, LevelTransition.Type.SURFACE));
} else {
level.transitions.add(new LevelTransition(level, entrance, LevelTransition.Type.REGULAR_ENTRANCE));
}
//use a separate generator here so meta progression doesn't affect levelgen
Random.pushGenerator();
@@ -73,7 +81,7 @@ public class EntranceRoom extends StandardRoom {
//can't be on bottom row of tiles
pos = level.pointToCell(new Point( Random.IntRange( left + 1, right - 1 ),
Random.IntRange( top + 1, bottom - 2 )));
} while (pos == level.entrance || level.findMob(level.entrance) != null);
} while (pos == level.entrance() || level.findMob(level.entrance()) != null);
level.drop( new Guidebook(), pos );
}
@@ -84,7 +92,7 @@ public class EntranceRoom extends StandardRoom {
//can't be on bottom row of tiles
pos = level.pointToCell(new Point( Random.IntRange( left + 1, right - 1 ),
Random.IntRange( top + 1, bottom - 2 )));
} while (pos == level.entrance || level.findMob(level.entrance) != null);
} while (pos == level.entrance() || level.findMob(level.entrance()) != null);
GuidePage p = new GuidePage();
p.page(Document.GUIDE_SEARCHING);
level.drop( p, pos );
@@ -23,6 +23,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room;
import com.watabou.utils.Point;
@@ -48,13 +49,14 @@ public class ExitRoom extends StandardRoom {
door.set( Room.Door.Type.REGULAR );
}
level.exit = level.pointToCell(random( 2 ));
Painter.set( level, level.exit, Terrain.EXIT );
int exit = level.pointToCell(random( 2 ));
Painter.set( level, exit, Terrain.EXIT );
level.transitions.add(new LevelTransition(level, exit, LevelTransition.Type.REGULAR_EXIT));
}
@Override
public boolean canPlaceCharacter(Point p, Level l) {
return super.canPlaceCharacter(p, l) && l.pointToCell(p) != l.exit;
return super.canPlaceCharacter(p, l) && l.pointToCell(p) != l.exit();
}
@Override
@@ -66,6 +66,7 @@ public class Fadeleaf extends Plant {
InterlevelScene.mode = InterlevelScene.Mode.RETURN;
InterlevelScene.returnDepth = Math.max(1, (Dungeon.depth - 1));
InterlevelScene.returnBranch = 0;
InterlevelScene.returnPos = -2;
Game.switchScene( InterlevelScene.class );
@@ -35,6 +35,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.LostBackpack;
import com.shatteredpixel.shatteredpixeldungeon.levels.Level;
import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm;
import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition;
import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.SpecialRoom;
import com.shatteredpixel.shatteredpixeldungeon.messages.Messages;
import com.shatteredpixel.shatteredpixeldungeon.services.updates.Updates;
@@ -75,8 +76,10 @@ public class InterlevelScene extends PixelScene {
DESCEND, ASCEND, CONTINUE, RESURRECT, RETURN, FALL, RESET, NONE
}
public static Mode mode;
public static LevelTransition curTransition = null;
public static int returnDepth;
public static int returnBranch;
public static int returnPos;
public static boolean noStory = false;
@@ -123,7 +126,7 @@ public class InterlevelScene extends PixelScene {
loadingDepth = 1;
fadeTime = SLOW_FADE;
} else {
loadingDepth = Dungeon.depth+1;
loadingDepth = curTransition.destDepth;
if (!(Statistics.deepestFloor < loadingDepth)) {
fadeTime = FAST_FADE;
} else if (loadingDepth == 6 || loadingDepth == 11
@@ -139,7 +142,7 @@ public class InterlevelScene extends PixelScene {
break;
case ASCEND:
fadeTime = FAST_FADE;
loadingDepth = Dungeon.depth-1;
loadingDepth = curTransition.destDepth;
scrollSpeed = -5;
break;
case RETURN:
@@ -376,21 +379,31 @@ public class InterlevelScene extends PixelScene {
noStory = false;
}
GameLog.wipe();
Level level = Dungeon.newLevel();
Dungeon.switchLevel( level, -1 );
} else {
Mob.holdAllies( Dungeon.level );
Dungeon.saveAll();
Level level;
Dungeon.depth = curTransition.destDepth;
Dungeon.branch = curTransition.destBranch;
//TODO this is brittle atm, assumes we're always going down in depth 1 at a time
if (curTransition.destDepth > Statistics.deepestFloor) {
level = Dungeon.newLevel();
} else {
level = Dungeon.loadLevel( GamesInProgress.curSlot );
}
LevelTransition destTransition = level.getTransition(curTransition.destType);
curTransition = null;
Dungeon.switchLevel( level, destTransition.cell() );
}
Level level;
if (Dungeon.depth >= Statistics.deepestFloor) {
level = Dungeon.newLevel();
} else {
Dungeon.depth++;
level = Dungeon.loadLevel( GamesInProgress.curSlot );
}
Dungeon.switchLevel( level, level.entrance );
}
//TODO atm falling always just increments depth by 1, do we eventually want to roll it into the transition system?
private void fall() throws IOException {
Mob.holdAllies( Dungeon.level );
@@ -413,9 +426,13 @@ public class InterlevelScene extends PixelScene {
Mob.holdAllies( Dungeon.level );
Dungeon.saveAll();
Dungeon.depth--;
Dungeon.depth = curTransition.destDepth;
Dungeon.branch = curTransition.destBranch;
Level level = Dungeon.loadLevel( GamesInProgress.curSlot );
Dungeon.switchLevel( level, level.exit );
LevelTransition destTransition = level.getTransition(curTransition.destType);
curTransition = null;
Dungeon.switchLevel( level, destTransition.cell() );
}
private void returnTo() throws IOException {
@@ -424,6 +441,7 @@ public class InterlevelScene extends PixelScene {
Dungeon.saveAll();
Dungeon.depth = returnDepth;
Dungeon.branch = returnBranch;
Level level = Dungeon.loadLevel( GamesInProgress.curSlot );
Dungeon.switchLevel( level, returnPos );
}
@@ -453,7 +471,6 @@ public class InterlevelScene extends PixelScene {
ArrayList<Item> preservedItems = Dungeon.level.getItemsToPreserveFromSealedResurrect();
Dungeon.hero.resurrect();
Dungeon.depth--;
level = Dungeon.newLevel();
Dungeon.hero.pos = level.randomRespawnCell(Dungeon.hero);
@@ -495,9 +512,8 @@ public class InterlevelScene extends PixelScene {
SpecialRoom.resetPitRoom(Dungeon.depth+1);
Dungeon.depth--;
Level level = Dungeon.newLevel();
Dungeon.switchLevel( level, level.entrance );
Dungeon.switchLevel( level, level.entrance() );
}
@Override
@@ -115,7 +115,7 @@ public class StatusPane extends Component {
talentBlink = 0;
compass = new Compass( Statistics.amuletObtained ? Dungeon.level.entrance : Dungeon.level.exit );
compass = new Compass( Statistics.amuletObtained ? Dungeon.level.entrance() : Dungeon.level.exit() );
add( compass );
if (large) rawShielding = new Image(asset, 0, 112, 128, 9);