diff --git a/core/src/main/assets/sounds/mine.mp3 b/core/src/main/assets/sounds/mine.mp3 new file mode 100644 index 000000000..5586120af Binary files /dev/null and b/core/src/main/assets/sounds/mine.mp3 differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java index 079ced5fb..85a6cd087 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java @@ -206,6 +206,7 @@ public class Assets { public static final String CHAINS = "sounds/chains.mp3"; public static final String SCAN = "sounds/scan.mp3"; public static final String SHEEP = "sounds/sheep.mp3"; + public static final String MINE = "sounds/mine.mp3"; public static final String[] all = new String[]{ CLICK, BADGE, GOLD, @@ -218,7 +219,7 @@ public class Assets { DESCEND, EAT, READ, LULLABY, DRINK, SHATTER, ZAP, LIGHTNING, LEVELUP, DEATH, CHALLENGE, CURSED, TRAP, EVOKE, TOMB, ALERT, MELD, BOSS, BLAST, PLANT, RAY, BEACON, TELEPORT, CHARMS, MASTERY, PUFF, ROCKS, BURNING, FALLING, GHOST, SECRET, BONES, - BEE, DEGRADE, MIMIC, DEBUFF, CHARGEUP, GAS, CHAINS, SCAN, SHEEP + BEE, DEGRADE, MIMIC, DEBUFF, CHARGEUP, GAS, CHAINS, SCAN, SHEEP, MINE }; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java index 2903c7cba..a871ad1c6 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java @@ -58,6 +58,7 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.HallsBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.HallsLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.LastLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.MiningLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel; @@ -338,6 +339,17 @@ public class Dungeon { default: level = new DeadEndLevel(); } + } else if (branch == 1) { + switch (depth) { + case 11: + case 12: + case 13: + case 14: + level = new MiningLevel(); + break; + default: + level = new DeadEndLevel(); + } } else { level = new DeadEndLevel(); } 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 8f96a0841..0fbbdb0ff 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 @@ -108,6 +108,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfExperience import com.shatteredpixel.shatteredpixeldungeon.items.potions.PotionOfHealing; import com.shatteredpixel.shatteredpixeldungeon.items.potions.elixirs.ElixirOfMight; import com.shatteredpixel.shatteredpixeldungeon.items.potions.exotic.PotionOfDivineInspiration; +import com.shatteredpixel.shatteredpixeldungeon.items.quest.DarkGold; +import com.shatteredpixel.shatteredpixeldungeon.items.quest.Pickaxe; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfAccuracy; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfEvasion; import com.shatteredpixel.shatteredpixeldungeon.items.rings.RingOfForce; @@ -133,6 +135,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.weapon.missiles.MissileWea import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.MiningLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.levels.features.Chasm; import com.shatteredpixel.shatteredpixeldungeon.levels.features.LevelTransition; @@ -804,7 +807,10 @@ public class Hero extends Char { } else if (curAction instanceof HeroAction.Unlock) { actResult = actUnlock((HeroAction.Unlock) curAction); - } else if (curAction instanceof HeroAction.LvlTransition) { + } else if (curAction instanceof HeroAction.Mine) { + actResult = actMine( (HeroAction.Mine)curAction ); + + }else if (curAction instanceof HeroAction.LvlTransition) { actResult = actTransition( (HeroAction.LvlTransition)curAction ); } else if (curAction instanceof HeroAction.Attack) { @@ -1132,6 +1138,50 @@ public class Hero extends Char { return false; } } + + public boolean actMine(HeroAction.Mine action){ + if (Dungeon.level.adjacent(pos, action.dst) + && (Dungeon.level.map[action.dst] == Terrain.WALL || Dungeon.level.map[action.dst] == Terrain.WALL_DECO) + && Dungeon.level.insideMap(action.dst)){ + sprite.attack(action.dst, new Callback() { + @Override + public void call() { + + if (Dungeon.level.map[action.dst] == Terrain.WALL_DECO){ + DarkGold gold = new DarkGold(); + if (gold.doPickUp( Dungeon.hero )) { + GLog.i( Messages.capitalize(Messages.get(Dungeon.hero, "you_now_have", gold.name())) ); + } else { + Dungeon.level.drop( gold, pos ).sprite.drop(); + } + CellEmitter.center( action.dst ).burst( Speck.factory( Speck.STAR ), 7 ); + Sample.INSTANCE.play( Assets.Sounds.EVOKE ); + } else { + CellEmitter.get( action.dst ).burst( Speck.factory( Speck.ROCK ), 2 ); + Sample.INSTANCE.play( Assets.Sounds.MINE ); + } + + PixelScene.shake(0.5f, 0.5f); + + Level.set( action.dst, Terrain.EMPTY_DECO ); + for (int i : PathFinder.NEIGHBOURS9) { + Dungeon.level.discoverable[action.dst + i] = true; + } + for (int i : PathFinder.NEIGHBOURS9) { + GameScene.updateMap( action.dst+i ); + } + + Dungeon.observe(); + + spendAndNext(TICK); + ready(); + } + }); + return false; + } + ready(); + return false; + } private boolean actTransition(HeroAction.LvlTransition action ) { int stairs = action.dst; @@ -1613,6 +1663,13 @@ public class Hero extends Char { curAction = new HeroAction.Attack( ch ); } + //TODO perhaps only trigger this if hero is already adjacent? reducing mistaps + } else if (Dungeon.level instanceof MiningLevel && + belongings.getItem(Pickaxe.class) != null && + (Dungeon.level.map[cell] == Terrain.WALL || Dungeon.level.map[cell] == Terrain.WALL_DECO)){ + + curAction = new HeroAction.Mine( cell ); + } else if (heap != null //moving to an item doesn't auto-pickup when enemies are near... && (visibleEnemies.size() == 0 || cell == pos || diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroAction.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroAction.java index 6d43af0af..c775b74ce 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroAction.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/HeroAction.java @@ -69,6 +69,12 @@ public class HeroAction { this.dst = stairs; } } + + public static class Mine extends HeroAction { + public Mine( int wall ) { + this.dst = wall; + } + } public static class Alchemy extends HeroAction { public Alchemy( int pot ) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/MiningLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/MiningLevel.java new file mode 100644 index 000000000..79e0abdd0 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/MiningLevel.java @@ -0,0 +1,105 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2023 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 + */ + +package com.shatteredpixel.shatteredpixeldungeon.levels; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Bones; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.Actor; +import com.shatteredpixel.shatteredpixeldungeon.actors.Char; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +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.levels.rooms.standard.CaveRoom; + +public class MiningLevel extends Level { + + { + color1 = 0x534f3e; + color2 = 0xb9d661; + } + + @Override + public String tilesTex() { + return Assets.Environment.TILES_CAVES; + } + + @Override + public String waterTex() { + return Assets.Environment.WATER_CAVES; + } + + @Override + protected boolean build() { + + //a few niceties are needed here before putting this out, things like water, short grass + // tile deco, and a pause to hunger/regen + + setSize(32, 32); + + CaveRoom c = new CaveRoom(); + c.set(1, 1, 31, 31); + c.paint(this); + + Painter.fill(this, 15, 15, 3, 3, Terrain.EMPTY); + int entrance = 16 * width() + 16; + + transitions.add(new LevelTransition(this, + entrance, + LevelTransition.Type.BRANCH_ENTRANCE, + Dungeon.depth, + 0, + LevelTransition.Type.BRANCH_EXIT)); + + map[entrance] = Terrain.ENTRANCE; + + return true; + } + + @Override + public Mob createMob() { + return null; + } + + @Override + protected void createMobs() { + } + + public Actor addRespawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + drop( item, entrance()-width() ).setHauntedIfCursed().type = Heap.Type.REMAINS; + } + } + + @Override + public int randomRespawnCell( Char ch ) { + return entrance()-width(); + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/BlacksmithRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/BlacksmithRoom.java index 7b41b18be..2a60e8b4e 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/BlacksmithRoom.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/BlacksmithRoom.java @@ -21,10 +21,12 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Blacksmith; import com.shatteredpixel.shatteredpixeldungeon.items.Generator; 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.traps.BurningTrap; import com.watabou.utils.Point; @@ -72,6 +74,22 @@ public class BlacksmithRoom extends StandardRoom { } while (level.heaps.get( npc.pos ) != null); level.mobs.add( npc ); + // TODO need to add some better visuals here (even just a simple custom asset) + Random.pushGenerator(Dungeon.seedCurDepth()+1); + int entrancePos; + do { + entrancePos = level.pointToCell(random( 2 )); + } while (level.heaps.get( npc.pos ) != null || entrancePos == npc.pos); + Random.popGenerator(); + + level.transitions.add(new LevelTransition(level, + entrancePos, + LevelTransition.Type.BRANCH_EXIT, + Dungeon.depth, + Dungeon.branch+1, + LevelTransition.Type.BRANCH_ENTRANCE)); + Painter.set(level, entrancePos, Terrain.EXIT); + for(Point p : getPoints()) { int cell = level.pointToCell(p); if (level.map[cell] == Terrain.TRAP){ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java index 53b4f0b06..a513397d2 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/GameScene.java @@ -60,6 +60,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportat import com.shatteredpixel.shatteredpixeldungeon.journal.Document; import com.shatteredpixel.shatteredpixeldungeon.journal.Journal; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; +import com.shatteredpixel.shatteredpixeldungeon.levels.MiningLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.RegularLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; @@ -428,6 +429,9 @@ public class GameScene extends PixelScene { break; } } + if (Dungeon.level instanceof MiningLevel){ + add(new WndStory(Messages.get(this, "blacksmith_quest_window_title") + ":\n\n" + Messages.get(this, "blacksmith_quest_window")).setDelays(0.6f, 1.4f)); + } if (Dungeon.hero.isAlive()) { Badges.validateNoKilling(); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/WallBlockingTilemap.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/WallBlockingTilemap.java index 75b334a52..0a998a2f3 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/WallBlockingTilemap.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/tiles/WallBlockingTilemap.java @@ -24,6 +24,7 @@ package com.shatteredpixel.shatteredpixeldungeon.tiles; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.levels.HallsBossLevel; +import com.shatteredpixel.shatteredpixeldungeon.levels.MiningLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.watabou.noosa.TextureFilm; import com.watabou.noosa.Tilemap; @@ -107,9 +108,10 @@ public class WallBlockingTilemap extends Tilemap { //- none of the remaining 5 neighbour cells are both not a wall and visible //if all 3 above are wall we can shortcut and just clear the cell - //unless one or more is a shelf, then we have to just block none + //unless one or more is a shelf, or we can mine, then we have to just block none if (wall(cell - 1 - mapWidth) && wall(cell - mapWidth) && wall(cell + 1 - mapWidth)){ - if (shelf(cell - 1 - mapWidth) || shelf(cell - mapWidth) || shelf(cell + 1 - mapWidth)){ + if (shelf(cell - 1 - mapWidth) || shelf(cell - mapWidth) + || shelf(cell + 1 - mapWidth) || Dungeon.level instanceof MiningLevel){ curr = BLOCK_NONE; } else { curr = CLEARED;