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 000000000..c0742932e Binary files /dev/null and b/core/src/main/assets/environment/custom_tiles/city_quest.png differ diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java index 00a58af68..d33114085 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java @@ -58,6 +58,7 @@ public class Assets { public static final String PRISON_EXIT = "environment/custom_tiles/prison_exit.png"; public static final String CAVES_QUEST = "environment/custom_tiles/caves_quest.png"; public static final String CAVES_BOSS = "environment/custom_tiles/caves_boss.png"; + public static final String CITY_QUEST = "environment/custom_tiles/city_quest.png"; public static final String CITY_BOSS = "environment/custom_tiles/city_boss.png"; public static final String HALLS_SP = "environment/custom_tiles/halls_special.png"; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Imp.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Imp.java index 3650a8c03..746bd4deb 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Imp.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/npcs/Imp.java @@ -33,9 +33,8 @@ import com.shatteredpixel.shatteredpixeldungeon.items.Generator; import com.shatteredpixel.shatteredpixeldungeon.items.quest.DwarfToken; import com.shatteredpixel.shatteredpixeldungeon.items.rings.Ring; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; -import com.shatteredpixel.shatteredpixeldungeon.levels.CityLevel; -import com.shatteredpixel.shatteredpixeldungeon.levels.Level; -import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.Room; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.quest.AmbitiousImpRoom; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.ImpSprite; @@ -44,9 +43,10 @@ import com.shatteredpixel.shatteredpixeldungeon.windows.WndQuest; import com.watabou.noosa.Game; import com.watabou.utils.Bundle; import com.watabou.utils.Callback; -import com.watabou.utils.PathFinder; import com.watabou.utils.Random; +import java.util.ArrayList; + public class Imp extends NPC { { @@ -208,32 +208,13 @@ public class Imp extends NPC { reward = (Ring)node.get( REWARD ); } } - - public static void spawn( CityLevel level ) { + + public static ArrayList 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) {