From 447aba7b1ed43c81597119e5ab296e9795f38cf6 Mon Sep 17 00:00:00 2001 From: Evan Debenham Date: Thu, 30 Oct 2025 13:08:45 -0400 Subject: [PATCH] v3.3.0: implemented initial new imp room --- .../environment/custom_tiles/city_quest.png | Bin 0 -> 1490 bytes .../shatteredpixeldungeon/Assets.java | 1 + .../actors/mobs/npcs/Imp.java | 39 ++-- .../levels/CityLevel.java | 48 ++++- .../shatteredpixeldungeon/levels/Level.java | 12 +- .../levels/rooms/quest/AmbitiousImpRoom.java | 192 ++++++++++++++++++ .../sprites/ImpSprite.java | 11 - 7 files changed, 255 insertions(+), 48 deletions(-) create mode 100644 core/src/main/assets/environment/custom_tiles/city_quest.png create mode 100644 core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/quest/AmbitiousImpRoom.java diff --git a/core/src/main/assets/environment/custom_tiles/city_quest.png b/core/src/main/assets/environment/custom_tiles/city_quest.png new file mode 100644 index 0000000000000000000000000000000000000000..c0742932e1b3f5c64b1212328c6c7b522e7d4bf8 GIT binary patch literal 1490 zcmV;@1ugoCP)C0000;P)t-s0000w zK~X(KR5?CPF*HCoJWDAoHZ(UwFf%_hHbO2jJu)>xEigMTGCrGyd4q6iV^md5|DHoQ zL^v-vFDNe|93dAI7XSbNgV$@>00001bW%=J06^y0W&i*MI7vi7RCocb(1C_6F&G5l zRHgraX6L^D`(9y>V+R8HfYLyZH&kXsbSAqVrzL7!B*m`BX_8cMA-L;t8d6E27*8Xm&kL6M`y90lOZjNhYXNlF6>eX;f5{lq9+b&cdmpC-lq4m>?EB}suI^92_i@^Ep}SRQ|6EU=`^j(T zcdt*6(_EN+-`&jW=XtLD%X(pTUVFxAvi#@Oind$FLEuBV62?U9Y`dHP|I-y9=MtR6 zX5C(nUv%soh8bB*smo-<7or)bX%-d`N`a5Z`*2c%*#DW))`g6CoPmg-UMfgNsoLL{ zb@T_P`hQnkwH?H3Qfe;X;l8nO#vjD~U#LeZzshXH6ZE8ix#&xD_W^Z#dVg@L{~NIt zN;Tq1vo9_bLoJ~9iG+emz;FG*^?JYd`hS^GQ}^l+D`L~^f4fi&O)AIQS+I>#Ir@W` z-Cghcf03G|d1ycN@-_RX3&kKL)C#b^q3r#^nf_mdRV3A(w0zC}5fsgODHyj4+P=V84F3w*7wUf8hH++l~?xBEV-XKa%j-DbO7d{XwjM zkAk(o1M7|w6e7S^C_h9;AR++q{r6!0;5dNmL;vq2G6QML69L}m7X%{04E+1|0MQ>L z`o9YJ!AwA?0=&;J2vh|A@V}RX!0Jy>5dFYZn+R~*4H2PTHhv%j;QGuD)DB<>uq(pS zzTXdkyaFb`z6f#_P~G@}Yes|U2P$+0On`k6bQ@yy1J`Tf+W3KK>I&==U=)Z3Fh@U- zi|7Za1NgcfjIX81xfT6Df`Y9dKx&I9a7=@x$>|yWKng(g1JnVC0>?C1>WGj`IeH3>m*B+z4}u@Ks4>SQ2(%H&;q}Z99IZe40TfB=d=LO25K){DqyPjz04C}F zn_&3`E!H?6NJa1iI3%?<;ot^*F&{WofAj-&oNMMy4{pHd2aW-FJ|6&^5WwgOFCi?p z0S#^R1F499z;+>wp74r9vt0plOX`HB=tSj!&|=~Hj#1G@vF=m)@b z)%w&OwdD~*U$)VLjVs`j|37ekKJI?)d<#uIpBqX+>mfsJ6z+>+{?Bz}WzBTj4SgjWvO-ffD^c8_a$y z{1RaZY~AtLy7G^HKfrt^zBBFCxUMJu;qM0q(W spawn( ArrayList rooms ) { if (!spawned && Dungeon.depth > 16 && Random.Int( 20 - Dungeon.depth ) == 0) { - Imp npc = new Imp(); - int tries = 30; - do { - npc.pos = level.randomRespawnCell( npc ); - tries--; - } while ( - npc.pos == -1 || - //visibility issues on these tiles, try to avoid them - (tries > 0 && level.map[ npc.pos ] == Terrain.EMPTY_SP) || - level.heaps.get( npc.pos ) != null || - level.traps.get( npc.pos) != null || - level.findMob( npc.pos ) != null || - //don't place the imp against solid terrain - !level.passable[npc.pos + PathFinder.CIRCLE4[0]] || !level.passable[npc.pos + PathFinder.CIRCLE4[1]] || - !level.passable[npc.pos + PathFinder.CIRCLE4[2]] || !level.passable[npc.pos + PathFinder.CIRCLE4[3]]); - level.mobs.add( npc ); - + rooms.add(new AmbitiousImpRoom()); spawned = true; - //imp always spawns on an empty tile, for better visibility - Level.set( npc.pos, Terrain.EMPTY, level); - //always assigns monks on floor 17, golems on floor 19, and 50/50 between either on 18 switch (Dungeon.depth){ case 17: default: @@ -255,6 +236,12 @@ public class Imp extends NPC { reward.upgrade( 2 ); reward.cursed = true; } + + return rooms; + } + + public static boolean given(){ + return given; } public static void process( Mob mob ) { diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java index ec10196aa..08e44a1c0 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityLevel.java @@ -24,10 +24,13 @@ package com.shatteredpixel.shatteredpixeldungeon.levels; import com.shatteredpixel.shatteredpixeldungeon.Assets; import com.shatteredpixel.shatteredpixeldungeon.Dungeon; import com.shatteredpixel.shatteredpixeldungeon.Statistics; +import com.shatteredpixel.shatteredpixeldungeon.actors.hero.Hero; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp; import com.shatteredpixel.shatteredpixeldungeon.effects.particles.ElmoParticle; +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.Room; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.BlazingTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.CorrosionTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.CursingTrap; @@ -46,14 +49,21 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.traps.SummoningTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.WarpingTrap; import com.shatteredpixel.shatteredpixeldungeon.levels.traps.WeakeningTrap; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.sprites.ImpSprite; import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap; +import com.shatteredpixel.shatteredpixeldungeon.windows.WndOptions; +import com.watabou.noosa.Game; import com.watabou.noosa.Group; import com.watabou.noosa.audio.Music; import com.watabou.noosa.particles.Emitter; import com.watabou.noosa.particles.PixelParticle; +import com.watabou.utils.Callback; import com.watabou.utils.PointF; import com.watabou.utils.Random; +import java.util.ArrayList; + public class CityLevel extends RegularLevel { { @@ -122,12 +132,40 @@ public class CityLevel extends RegularLevel { 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1 }; } - + @Override - protected void createMobs() { - Imp.Quest.spawn( this ); - - super.createMobs(); + public boolean activateTransition(Hero hero, LevelTransition transition) { + if (transition.type == LevelTransition.Type.BRANCH_EXIT) { + + if (Imp.Quest.given()){ + Game.runOnRenderThread(new Callback() { + @Override + public void call() { + GameScene.show( new WndOptions( new ImpSprite(), + Messages.titleCase(Messages.get(Imp.class, "name")), + "Want to go down?", + "yes", + "no"){ + @Override + protected void onSelect(int index) { + if (index == 0){ + CityLevel.super.activateTransition(hero, transition); + } + } + } ); + } + }); + } + return false; + + } else { + return super.activateTransition(hero, transition); + } + } + + @Override + protected ArrayList initRooms() { + return Imp.Quest.spawn(super.initRooms()); } @Override 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 e250ac72e..cbc0460c7 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/Level.java @@ -184,8 +184,8 @@ public abstract class Level implements Bundlable { public HashMap,Blob> blobs; public SparseArray plants; public SparseArray traps; - public HashSet customTiles; - public HashSet customWalls; + public ArrayList customTiles; + public ArrayList customWalls; protected ArrayList itemsToSpawn = new ArrayList<>(); @@ -302,8 +302,8 @@ public abstract class Level implements Bundlable { blobs = new HashMap<>(); plants = new SparseArray<>(); traps = new SparseArray<>(); - customTiles = new HashSet<>(); - customWalls = new HashSet<>(); + customTiles = new ArrayList<>(); + customWalls = new ArrayList<>(); } while (!build()); @@ -375,8 +375,8 @@ public abstract class Level implements Bundlable { blobs = new HashMap<>(); plants = new SparseArray<>(); traps = new SparseArray<>(); - customTiles = new HashSet<>(); - customWalls = new HashSet<>(); + customTiles = new ArrayList<>(); + customWalls = new ArrayList<>(); map = bundle.getIntArray( MAP ); diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/quest/AmbitiousImpRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/quest/AmbitiousImpRoom.java new file mode 100644 index 000000000..e99a19394 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/quest/AmbitiousImpRoom.java @@ -0,0 +1,192 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2025 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.rooms.quest; + +import com.shatteredpixel.shatteredpixeldungeon.Assets; +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp; +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.special.SpecialRoom; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap; +import com.watabou.noosa.Game; +import com.watabou.noosa.NoosaScript; +import com.watabou.noosa.TextureFilm; +import com.watabou.noosa.Tilemap; +import com.watabou.utils.Point; +import com.watabou.utils.Random; + +public class AmbitiousImpRoom extends SpecialRoom { + + @Override + public int maxWidth() { return 9; } + public int minWidth() { return 9; } + public int maxHeight() { return 9; } + public int minHeight() { return 9; } + + @Override + public void paint(Level level) { + + Painter.fill( level, this, Terrain.WALL ); + Painter.fill( level, this, 1, Terrain.EMPTY ); + + Point c = center(); + + Painter.set(level, c.x-2, c.y-2, Terrain.REGION_DECO); + Painter.set(level, c.x+2, c.y-2, Terrain.REGION_DECO); + Painter.set(level, c.x-2, c.y+2, Terrain.REGION_DECO); + Painter.set(level, c.x+2, c.y+2, Terrain.REGION_DECO); + + Painter.set(level, c.x-3, c.y-3, Terrain.WALL_DECO); + Painter.set(level, c.x+3, c.y-3, Terrain.WALL_DECO); + Painter.set(level, c.x-3, c.y+3, Terrain.WALL_DECO); + Painter.set(level, c.x+3, c.y+3, Terrain.WALL_DECO); + + Door entrance = entrance(); + Imp npc = new Imp(); + npc.pos = level.pointToCell(c); + + //TODO we have imp in front for now, do we want to put him in the back? + if (entrance.x == left || entrance.x == right){ + npc.pos += Random.IntRange(-1, 1)*level.width(); + npc.pos += entrance.x == left ? -2 : 2; + } else if (entrance.y == top || entrance.y == bottom){ + npc.pos += Random.IntRange(-1, 1); + npc.pos += level.width() * (entrance.y == top ? -2 : 2); + } + level.mobs.add( npc ); + + Painter.drawInside(level, this, entrance, 1, Terrain.EMPTY); + entrance.set( Door.Type.REGULAR ); //TODO maybe lock? + + //TODO finalize quest entrance visuals + QuestEntrance vis = new QuestEntrance(); + vis.pos(c.x - 2, c.y - 2); + level.customTiles.add(vis); + + EntranceBarrier vis2 = new EntranceBarrier(); + vis2.pos(c.x - 1, c.y - 1); + level.customTiles.add(vis2); + + int entrancePos = level.pointToCell(c); + + 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); + + } + + @Override + public boolean canPlaceCharacter(Point p, Level l) { + return false; + } + + @Override + public boolean canPlaceItem(Point p, Level l) { + return false; + } + + @Override + public boolean canPlaceTrap(Point p) { + return false; + } + + @Override + public boolean canPlaceGrass(Point p) { + return false; + } + + @Override + public boolean canPlaceWater(Point p) { + return false; + } + + public static class QuestEntrance extends CustomTilemap { + + { + texture = Assets.Environment.CITY_QUEST; + + tileW = tileH = 5; + } + + final int TEX_WIDTH = 128; + + @Override + public Tilemap create() { + Tilemap v = super.create(); + v.map(mapSimpleImage(0, 0, TEX_WIDTH), 5); + return v; + } + + @Override + public String name(int tileX, int tileY) { + return Messages.get(this, "name"); + } + + @Override + public String desc(int tileX, int tileY) { + return Messages.get(this, "desc"); + } + + } + + public static class EntranceBarrier extends CustomTilemap { + { + texture = Assets.Environment.CITY_QUEST; + + tileW = tileH = 3; + } + + final int TEX_WIDTH = 128; + + @Override + public Tilemap create() { + //largely a copy of super method, so that we can change alpha on update + if (vis != null && vis.alive) vis.killAndErase(); + vis = new Tilemap(texture, new TextureFilm( texture, SIZE, SIZE )){ + @Override + protected NoosaScript script() { + //allow lighting for custom tilemaps + return NoosaScript.get(); + } + + @Override + public void update() { + alpha(0.3f + 0.3f*(float)Math.sin(Game.timeTotal)); + super.update(); + } + }; + vis.x = tileX*SIZE; + vis.y = tileY*SIZE; + vis.map(mapSimpleImage(5, 1, TEX_WIDTH), 3); + return vis; + } + + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ImpSprite.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ImpSprite.java index 48dd07a56..6ebcf3cba 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ImpSprite.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/sprites/ImpSprite.java @@ -22,8 +22,6 @@ package com.shatteredpixel.shatteredpixeldungeon.sprites; import com.shatteredpixel.shatteredpixeldungeon.Assets; -import com.shatteredpixel.shatteredpixeldungeon.actors.Char; -import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp; import com.shatteredpixel.shatteredpixeldungeon.effects.Speck; import com.watabou.noosa.TextureFilm; @@ -50,15 +48,6 @@ public class ImpSprite extends MobSprite { play( idle ); } - @Override - public void link( Char ch ) { - super.link( ch ); - - if (ch instanceof Imp) { - alpha( 0.5f ); - } - } - @Override public void onComplete( Animation anim ) { if (anim == die) {