v3.3.0: implemented initial new imp room

This commit is contained in:
Evan Debenham
2025-10-30 13:08:45 -04:00
parent 43328191cd
commit 447aba7b1e
7 changed files with 255 additions and 48 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

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

View File

@@ -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<Room> spawn( ArrayList<Room> 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 ) {

View File

@@ -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<Room> initRooms() {
return Imp.Quest.spawn(super.initRooms());
}
@Override

View File

@@ -184,8 +184,8 @@ public abstract class Level implements Bundlable {
public HashMap<Class<? extends Blob>,Blob> blobs;
public SparseArray<Plant> plants;
public SparseArray<Trap> traps;
public HashSet<CustomTilemap> customTiles;
public HashSet<CustomTilemap> customWalls;
public ArrayList<CustomTilemap> customTiles;
public ArrayList<CustomTilemap> customWalls;
protected ArrayList<Item> 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 );

View File

@@ -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 <http://www.gnu.org/licenses/>
*/
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;
}
}
}

View File

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