diff --git a/core/src/main/assets/messages/items/items.properties b/core/src/main/assets/messages/items/items.properties index 3f06122e5..59bbcd568 100644 --- a/core/src/main/assets/messages/items/items.properties +++ b/core/src/main/assets/messages/items/items.properties @@ -426,15 +426,17 @@ items.artifacts.sandalsofnature.desc_seeds=You have fed the footwear %d seeds. items.artifacts.skeletonkey.name=skeleton key items.artifacts.skeletonkey.ac_insert=INSERT -items.artifacts.skeletonkey.no_charge=Your key does not have enough charge. items.artifacts.skeletonkey.cursed=You can't use a cursed skeleton key. items.artifacts.skeletonkey.prompt=Choose a target items.artifacts.skeletonkey.invalid_target=There's nothing to lock or unlock there. +items.artifacts.skeletonkey.iron_charges=Opening a regular lock requires 1 charge. items.artifacts.skeletonkey.gold_charges=Opening a gold lock requires 2 charges. items.artifacts.skeletonkey.lock_charges=Locking a door requires 2 charges. items.artifacts.skeletonkey.wall_charges=Creating a temporary wall requires 2 charges. -items.artifacts.skeletonkey.crystal_charges=Opening a crystal lock requires 4 charges. +items.artifacts.skeletonkey.crystal_charges=Opening a crystal lock requires 5 charges. items.artifacts.skeletonkey.wont_open=The key refuses to fit into this lock for some reason. +items.artifacts.skeletonkey.locked_with_key=That door was locked by your skeleton key. +items.artifacts.skeletonkey.force_lock=The lock has weakened without your skeleton key. You manage to force it open. items.artifacts.skeletonkey.desc=An important-looking key with a head shaped like a skull. Its teeth seem to be different every time you look at them. items.artifacts.skeletonkey.desc_worn=This magical key seems to gain power as you explore the dungeon and open more locks.\n\nThe key can be inserted into any of the dungeon's locks to either open or close them. This includes most special locks and doors that were never locked previously. The key can even be used on empty space in a cardinal or diagonal direction to 'lock' the air in front of you, creating a temporary solid wall. Locking will push away enemies if there is space to do so. items.artifacts.skeletonkey.desc_cursed=The cursed key seems to be actively avoiding fitting into any lock, and is even making it difficult (but not impossible) for you to use your other keys. diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java index 1533dfacd..1a758c98b 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java @@ -1213,6 +1213,17 @@ public class Hero extends Char { && Notes.keyCount(new IronKey(Dungeon.depth)) > 0) { hasKey = true; + + } else if (door == Terrain.HERO_LKD_DR){ + + if (belongings.getItem(SkeletonKey.class) != null + && !belongings.getItem(SkeletonKey.class).cursed){ + GLog.i(Messages.get(SkeletonKey.class, "locked_with_key")); + ready(); + return false; + } else { + hasKey = true; + } } else if (door == Terrain.CRYSTAL_DOOR && Notes.keyCount(new CrystalKey(Dungeon.depth)) > 0) { @@ -1922,7 +1933,10 @@ public class Hero extends Char { curAction = new HeroAction.OpenChest( cell ); } - } else if (Dungeon.level.map[cell] == Terrain.LOCKED_DOOR || Dungeon.level.map[cell] == Terrain.CRYSTAL_DOOR || Dungeon.level.map[cell] == Terrain.LOCKED_EXIT) { + } else if (Dungeon.level.map[cell] == Terrain.LOCKED_DOOR + || Dungeon.level.map[cell] == Terrain.HERO_LKD_DR + || Dungeon.level.map[cell] == Terrain.CRYSTAL_DOOR + || Dungeon.level.map[cell] == Terrain.LOCKED_EXIT) { curAction = new HeroAction.Unlock( cell ); @@ -2349,6 +2363,10 @@ public class Hero extends Char { skele.keyUsed(new IronKey(Dungeon.depth)); } } + } else if (door == Terrain.HERO_LKD_DR) { + hasKey = true; + Level.set(doorCell, Terrain.DOOR); + GLog.i( Messages.get(SkeletonKey.class, "force_lock")); } else if (door == Terrain.CRYSTAL_DOOR) { hasKey = Notes.remove(new CrystalKey(Dungeon.depth)); if (hasKey) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/SkeletonKey.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/SkeletonKey.java index c21b25aca..1dee972c1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/SkeletonKey.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/artifacts/SkeletonKey.java @@ -74,7 +74,6 @@ public class SkeletonKey extends Artifact { public ArrayList actions(Hero hero) { ArrayList actions = super.actions(hero); if (isEquipped(hero) - && charge > 0 && hero.buff(MagicImmune.class) == null && !cursed) { actions.add(AC_INSERT); @@ -95,9 +94,6 @@ public class SkeletonKey extends Artifact { if (!isEquipped( hero )) { GLog.i( Messages.get(Artifact.class, "need_to_equip") ); - } else if (charge < 1) { - GLog.i( Messages.get(this, "no_charge") ); - } else if (cursed) { GLog.w( Messages.get(this, "cursed") ); @@ -130,6 +126,10 @@ public class SkeletonKey extends Artifact { GLog.w(Messages.get(SkeletonKey.class, "wont_open")); return; } + if (charge < 1){ + GLog.i( Messages.get(this, "iron_charge") ); + return; + } Sample.INSTANCE.play(Assets.Sounds.UNLOCK); curUser.sprite.operate(target, new Callback() { @Override @@ -144,10 +144,25 @@ public class SkeletonKey extends Artifact { curUser.busy(); return; + } else if (Dungeon.level.map[target] == Terrain.HERO_LKD_DR) { + + Sample.INSTANCE.play(Assets.Sounds.UNLOCK); + curUser.sprite.operate(target, new Callback() { + @Override + public void call() { + Level.set(target, Terrain.DOOR); + GameScene.updateMap(target); + //no charge cost + curUser.spendAndNext(Actor.TICK); + curUser.sprite.idle(); + } + }); + curUser.busy(); + return; } else if (Dungeon.level.map[target] == Terrain.CRYSTAL_DOOR) { - if (charge < 4) { - GLog.w(Messages.get(SkeletonKey.class, "crystal_charges")); + if (charge < 5) { + GLog.i(Messages.get(SkeletonKey.class, "crystal_charges")); return; } Sample.INSTANCE.play(Assets.Sounds.UNLOCK); @@ -156,7 +171,7 @@ public class SkeletonKey extends Artifact { public void call() { Level.set(target, Terrain.EMPTY); GameScene.updateMap(target); - charge -= 4; + charge -= 5; Sample.INSTANCE.play(Assets.Sounds.TELEPORT); CellEmitter.get( target ).start( Speck.factory( Speck.DISCOVER ), 0.025f, 20 ); curUser.spendAndNext(Actor.TICK); @@ -168,7 +183,7 @@ public class SkeletonKey extends Artifact { } else if (Dungeon.level.map[target] == Terrain.DOOR || Dungeon.level.map[target] == Terrain.OPEN_DOOR){ if (charge < 2) { - GLog.w(Messages.get(SkeletonKey.class, "lock_charges")); + GLog.i(Messages.get(SkeletonKey.class, "lock_charges")); return; } @@ -185,7 +200,7 @@ public class SkeletonKey extends Artifact { curUser.sprite.operate(target, new Callback() { @Override public void call() { - Level.set(target, Terrain.LOCKED_DOOR); + Level.set(target, Terrain.HERO_LKD_DR); GameScene.updateMap(target); charge -= 2; curUser.spendAndNext(Actor.TICK); @@ -197,7 +212,7 @@ public class SkeletonKey extends Artifact { } else if (Dungeon.level.heaps.get(target) != null && Dungeon.level.heaps.get(target).type == Heap.Type.LOCKED_CHEST){ if (charge < 2) { - GLog.w(Messages.get(SkeletonKey.class, "gold_charges")); + GLog.i(Messages.get(SkeletonKey.class, "gold_charges")); return; } Sample.INSTANCE.play(Assets.Sounds.UNLOCK); @@ -214,8 +229,8 @@ public class SkeletonKey extends Artifact { return; } else if (Dungeon.level.heaps.get(target) != null && Dungeon.level.heaps.get(target).type == Heap.Type.CRYSTAL_CHEST){ - if (charge < 4) { - GLog.w(Messages.get(SkeletonKey.class, "crystal_charges")); + if (charge < 5) { + GLog.i(Messages.get(SkeletonKey.class, "crystal_charges")); return; } Sample.INSTANCE.play(Assets.Sounds.UNLOCK); @@ -223,7 +238,7 @@ public class SkeletonKey extends Artifact { @Override public void call() { Dungeon.level.heaps.get(target).open(curUser); - charge -= 4; + charge -= 5; curUser.spendAndNext(Actor.TICK); curUser.sprite.idle(); } @@ -235,7 +250,7 @@ public class SkeletonKey extends Artifact { } if (charge < 2){ - GLog.w(Messages.get(SkeletonKey.class, "wall_charges")); + GLog.i(Messages.get(SkeletonKey.class, "wall_charges")); return; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java index cbc0460c7..4a90091c0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -1568,6 +1568,7 @@ public abstract class Level implements Bundlable { case Terrain.FURROWED_GRASS: return Messages.get(Level.class, "furrowed_grass_name"); case Terrain.LOCKED_DOOR: + case Terrain.HERO_LKD_DR: return Messages.get(Level.class, "locked_door_name"); case Terrain.CRYSTAL_DOOR: return Messages.get(Level.class, "crystal_door_name"); @@ -1618,6 +1619,7 @@ public abstract class Level implements Bundlable { case Terrain.FURROWED_GRASS: return Messages.get(Level.class, "high_grass_desc"); case Terrain.LOCKED_DOOR: + case Terrain.HERO_LKD_DR: return Messages.get(Level.class, "locked_door_desc"); case Terrain.CRYSTAL_DOOR: return Messages.get(Level.class, "crystal_door_desc"); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java index e0e6bb100..a3013b3f3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/RegularLevel.java @@ -833,7 +833,7 @@ public abstract class RegularLevel extends Level { } } - //it contains a barricade, locked door, or hidden door + //it contains a barricade, locked door (player locked doors are fine though), or hidden door for (int i = 0; i < length; i++){ if (map[i] == Terrain.BARRICADE || map[i] == Terrain.LOCKED_DOOR || map[i] == Terrain.SECRET_DOOR){ //we use adjacent cells to find the room this is connected to diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Terrain.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Terrain.java index b5477326f..a11651e4c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Terrain.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Terrain.java @@ -35,6 +35,7 @@ public class Terrain { public static final int EXIT = 8; public static final int EMBERS = 9; public static final int LOCKED_DOOR = 10; + public static final int HERO_LKD_DR = 38; //a door that was locked by the skeleton key public static final int CRYSTAL_DOOR = 31; public static final int PEDESTAL = 11; public static final int WALL_DECO = 12; @@ -92,6 +93,7 @@ public class Terrain { flags[EXIT] = PASSABLE; flags[EMBERS] = PASSABLE; flags[LOCKED_DOOR] = LOS_BLOCKING | SOLID; + flags[HERO_LKD_DR] = flags[LOCKED_DOOR]; flags[CRYSTAL_DOOR] = SOLID; flags[PEDESTAL] = PASSABLE; flags[WALL_DECO] = flags[WALL]; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonTileSheet.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonTileSheet.java index 5508e38ec..1fc2b3056 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonTileSheet.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonTileSheet.java @@ -112,6 +112,7 @@ public class DungeonTileSheet { chasmStitcheable.put( Terrain.DOOR, CHASM_WALL ); chasmStitcheable.put( Terrain.OPEN_DOOR, CHASM_WALL ); chasmStitcheable.put( Terrain.LOCKED_DOOR, CHASM_WALL ); + chasmStitcheable.put( Terrain.HERO_LKD_DR, CHASM_WALL ); chasmStitcheable.put( Terrain.SECRET_DOOR, CHASM_WALL ); chasmStitcheable.put( Terrain.WALL_DECO, CHASM_WALL ); @@ -146,7 +147,7 @@ public class DungeonTileSheet { Terrain.TRAP, Terrain.INACTIVE_TRAP, Terrain.EMPTY_DECO, Terrain.CUSTOM_DECO, Terrain.WELL, Terrain.STATUE, Terrain.REGION_DECO, Terrain.ALCHEMY, Terrain.CUSTOM_DECO_EMPTY, Terrain.MINE_CRYSTAL, Terrain.MINE_BOULDER, - Terrain.DOOR, Terrain.OPEN_DOOR, Terrain.LOCKED_DOOR, Terrain.CRYSTAL_DOOR + Terrain.DOOR, Terrain.OPEN_DOOR, Terrain.LOCKED_DOOR, Terrain.HERO_LKD_DR, Terrain.CRYSTAL_DOOR )); public static boolean waterStitcheable(int tile){ @@ -277,12 +278,13 @@ public class DungeonTileSheet { else if (tile == Terrain.DOOR) return DungeonTileSheet.RAISED_DOOR; else if (tile == Terrain.OPEN_DOOR) return DungeonTileSheet.RAISED_DOOR_OPEN; else if (tile == Terrain.LOCKED_DOOR) return DungeonTileSheet.RAISED_DOOR_LOCKED; - else if (tile == Terrain.CRYSTAL_DOOR) return DungeonTileSheet.RAISED_DOOR_CRYSTAL; + else if (tile == Terrain.HERO_LKD_DR) return DungeonTileSheet.RAISED_DOOR_LOCKED; + else if (tile == Terrain.CRYSTAL_DOOR) return DungeonTileSheet.RAISED_DOOR_CRYSTAL; else return -1; } private static int[] doorTiles = new int[]{ - Terrain.DOOR, Terrain.LOCKED_DOOR, Terrain.CRYSTAL_DOOR, Terrain.OPEN_DOOR + Terrain.DOOR, Terrain.LOCKED_DOOR, Terrain.HERO_LKD_DR, Terrain.CRYSTAL_DOOR, Terrain.OPEN_DOOR }; public static boolean doorTile(int tile){ @@ -355,6 +357,7 @@ public class DungeonTileSheet { if (tile == Terrain.OPEN_DOOR) visual = DOOR_SIDEWAYS_OVERHANG; else if (tile == Terrain.DOOR) visual = DOOR_SIDEWAYS_OVERHANG_CLOSED; else if (tile == Terrain.LOCKED_DOOR) visual = DOOR_SIDEWAYS_OVERHANG_LOCKED; + else if (tile == Terrain.HERO_LKD_DR) visual = DOOR_SIDEWAYS_OVERHANG_LOCKED; else if (tile == Terrain.CRYSTAL_DOOR) visual = DOOR_SIDEWAYS_OVERHANG_CRYSTAL; //TODO currently this line on triggers on mining floors, do we want to make it universal? else if (Dungeon.branch == 1 && below == Terrain.WALL_DECO) visual = WALL_OVERHANG_DECO; @@ -441,6 +444,7 @@ public class DungeonTileSheet { directFlatVisuals.put(Terrain.DOOR, FLAT_DOOR); directFlatVisuals.put(Terrain.OPEN_DOOR, FLAT_DOOR_OPEN); directFlatVisuals.put(Terrain.LOCKED_DOOR, FLAT_DOOR_LOCKED); + directFlatVisuals.put(Terrain.HERO_LKD_DR, FLAT_DOOR_LOCKED); directFlatVisuals.put(Terrain.CRYSTAL_DOOR, FLAT_DOOR_CRYSTAL); directFlatVisuals.put(Terrain.WALL_DECO, FLAT_WALL_DECO); directFlatVisuals.put(Terrain.BOOKSHELF, FLAT_BOOKSHELF); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonWallsTilemap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonWallsTilemap.java index 716269e7f..91e1c695e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonWallsTilemap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/DungeonWallsTilemap.java @@ -46,7 +46,9 @@ public class DungeonWallsTilemap extends DungeonTilemap { if (map[pos + mapWidth] == Terrain.DOOR){ return DungeonTileSheet.DOOR_SIDEWAYS; - } else if (map[pos + mapWidth] == Terrain.LOCKED_DOOR){ + } else if (map[pos + mapWidth] == Terrain.LOCKED_DOOR) { + return DungeonTileSheet.DOOR_SIDEWAYS_LOCKED; + } else if (map[pos + mapWidth] == Terrain.HERO_LKD_DR){ return DungeonTileSheet.DOOR_SIDEWAYS_LOCKED; } else if (map[pos + mapWidth] == Terrain.CRYSTAL_DOOR){ return DungeonTileSheet.DOOR_SIDEWAYS_CRYSTAL; @@ -82,7 +84,11 @@ public class DungeonWallsTilemap extends DungeonTilemap { pos % mapWidth != 0 ? map[pos - 1 + mapWidth] : -1 ); - } else if (Dungeon.level.insideMap(pos) && (map[pos+mapWidth] == Terrain.DOOR || map[pos+mapWidth] == Terrain.LOCKED_DOOR) ) { + } else if (Dungeon.level.insideMap(pos) && map[pos+mapWidth] == Terrain.DOOR ) { + return DungeonTileSheet.DOOR_OVERHANG; + } else if (Dungeon.level.insideMap(pos) && map[pos+mapWidth] == Terrain.LOCKED_DOOR ) { + return DungeonTileSheet.DOOR_OVERHANG; + } else if (Dungeon.level.insideMap(pos) && map[pos+mapWidth] == Terrain.HERO_LKD_DR ) { return DungeonTileSheet.DOOR_OVERHANG; } else if (Dungeon.level.insideMap(pos) && map[pos+mapWidth] == Terrain.OPEN_DOOR ) { return DungeonTileSheet.DOOR_OVERHANG_OPEN;