v3.3.0: adjusted skeleton key locking functionality, different door ID

This commit is contained in:
Evan Debenham
2025-11-19 13:57:14 -05:00
parent 6ae1e33062
commit 77fa4bd400
8 changed files with 72 additions and 23 deletions

View File

@@ -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.

View File

@@ -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) {

View File

@@ -74,7 +74,6 @@ public class SkeletonKey extends Artifact {
public ArrayList<String> actions(Hero hero) {
ArrayList<String> 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;
}

View File

@@ -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");

View File

@@ -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

View File

@@ -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];

View File

@@ -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);

View File

@@ -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;