diff --git a/SPD-classes/src/main/java/com/watabou/utils/Rect.java b/SPD-classes/src/main/java/com/watabou/utils/Rect.java index a0c4b4281..628bec56c 100644 --- a/SPD-classes/src/main/java/com/watabou/utils/Rect.java +++ b/SPD-classes/src/main/java/com/watabou/utils/Rect.java @@ -133,6 +133,12 @@ public class Rect { public boolean inside( Point p ) { return p.x >= left && p.x < right && p.y >= top && p.y < bottom; } + + public Point center() { + return new Point( + (left + right) / 2 + (((right - left) % 2) == 0 ? Random.Int( 2 ) : 0), + (top + bottom) / 2 + (((bottom - top) % 2) == 0 ? Random.Int( 2 ) : 0) ); + } public Rect shrink( int d ) { return new Rect( left + d, top + d, right - d, bottom - d ); diff --git a/core/src/main/assets/custom_tiles/city_boss.png b/core/src/main/assets/custom_tiles/city_boss.png new file mode 100644 index 000000000..733063d9f Binary files /dev/null and b/core/src/main/assets/custom_tiles/city_boss.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 6eed43e6f..2c7d7ae19 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Assets.java @@ -135,6 +135,7 @@ public class Assets { public static final String PRISON_EXIT_OLD = "custom_tiles/prison_exit_old.png"; public static final String PRISON_EXIT_NEW = "custom_tiles/prison_exit_new.png"; public static final String CAVES_BOSS = "custom_tiles/caves_boss.png"; + public static final String CITY_BOSS = "custom_tiles/city_boss.png"; public static final String BUFFS_SMALL = "buffs.png"; public static final String BUFFS_LARGE = "large_buffs.png"; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java index ff2b4a8ed..f424a6b3c 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/Dungeon.java @@ -44,7 +44,6 @@ import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfUpgrade; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.SpiritBow; import com.shatteredpixel.shatteredpixeldungeon.journal.Notes; import com.shatteredpixel.shatteredpixeldungeon.levels.CavesLevel; -import com.shatteredpixel.shatteredpixeldungeon.levels.CityBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.CityLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.DeadEndLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.HallsBossLevel; @@ -53,6 +52,7 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.LastLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.LastShopLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.NewCavesBossLevel; +import com.shatteredpixel.shatteredpixeldungeon.levels.NewCityBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.NewPrisonBossLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.PrisonLevel; import com.shatteredpixel.shatteredpixeldungeon.levels.SewerBossLevel; @@ -272,10 +272,22 @@ public class Dungeon { level = new CityLevel(); break; case 20: - level = new CityBossLevel(); + level = new NewCityBossLevel(); break; case 21: - level = new LastShopLevel(); + //logic for old city boss levels, need to spawn a shop on floor 21 + try { + Bundle bundle = FileUtils.bundleFromFile(GamesInProgress.depthFile(GamesInProgress.curSlot, 20)); + Class cls = bundle.getBundle(LEVEL).getClass("__className"); + if (cls == NewCityBossLevel.class) { + level = new HallsLevel(); + } else { + level = new LastShopLevel(); + } + } catch (Exception e) { + ShatteredPixelDungeon.reportException(e); + level = new HallsLevel(); + } break; case 22: case 23: diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java index 0527e6ff8..f01e79b9a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/ShatteredPixelDungeon.java @@ -132,6 +132,9 @@ public class ShatteredPixelDungeon extends Game { com.watabou.utils.Bundle.addAlias( com.shatteredpixel.shatteredpixeldungeon.levels.OldCavesBossLevel.class, "com.shatteredpixel.shatteredpixeldungeon.levels.CavesBossLevel" ); + com.watabou.utils.Bundle.addAlias( + com.shatteredpixel.shatteredpixeldungeon.levels.OldCityBossLevel.class, + "com.shatteredpixel.shatteredpixeldungeon.levels.CityBossLevel" ); } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java new file mode 100644 index 000000000..aa822eabc --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java @@ -0,0 +1,39 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2020 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.actors.mobs; + + +import com.shatteredpixel.shatteredpixeldungeon.Dungeon; + +//TODO currently just regular DK but with no summoning ability +public class DwarfKing extends King { + + protected boolean canTryToSummon() { + return false; + } + + @Override + public void die(Object cause) { + super.die(cause); + Dungeon.level.unseal(); + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/King.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/King.java index 2752ecb0c..9a899ae70 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/King.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/King.java @@ -43,7 +43,7 @@ import com.shatteredpixel.shatteredpixeldungeon.items.keys.SkeletonKey; import com.shatteredpixel.shatteredpixeldungeon.items.scrolls.ScrollOfTeleportation; import com.shatteredpixel.shatteredpixeldungeon.items.wands.WandOfDisintegration; import com.shatteredpixel.shatteredpixeldungeon.items.weapon.enchantments.Grim; -import com.shatteredpixel.shatteredpixeldungeon.levels.CityBossLevel; +import com.shatteredpixel.shatteredpixeldungeon.levels.OldCityBossLevel; import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.shatteredpixel.shatteredpixeldungeon.sprites.KingSprite; @@ -62,9 +62,9 @@ public class King extends Mob { { spriteClass = KingSprite.class; - HP = HT = 300; + HP = HT = 3; EXP = 40; - defenseSkill = 25; + defenseSkill = 0; Undead.count = 0; @@ -91,7 +91,7 @@ public class King extends Mob { @Override public int damageRoll() { - return Random.NormalIntRange( 25, 40 ); + return Random.NormalIntRange( 0, 0 ); } @Override @@ -101,26 +101,26 @@ public class King extends Mob { @Override public int drRoll() { - return Random.NormalIntRange(0, 14); + return Random.NormalIntRange(0, 0); } @Override protected boolean getCloser( int target ) { return canTryToSummon() ? - super.getCloser( ((CityBossLevel)Dungeon.level).pedestal( nextPedestal ) ) : + super.getCloser( ((OldCityBossLevel)Dungeon.level).pedestal( nextPedestal ) ) : super.getCloser( target ); } @Override protected boolean canAttack( Char enemy ) { return canTryToSummon() ? - pos == ((CityBossLevel)Dungeon.level).pedestal( nextPedestal ) : + pos == ((OldCityBossLevel)Dungeon.level).pedestal( nextPedestal ) : Dungeon.level.adjacent( pos, enemy.pos ); } - - private boolean canTryToSummon() { + + protected boolean canTryToSummon() { if (paralysed <= 0 && Undead.count < maxArmySize()) { - Char ch = Actor.findChar( ((CityBossLevel)Dungeon.level).pedestal( nextPedestal ) ); + Char ch = Actor.findChar( ((OldCityBossLevel)Dungeon.level).pedestal( nextPedestal ) ); return ch == this || ch == null; } else { return false; @@ -129,11 +129,11 @@ public class King extends Mob { @Override protected boolean act() { - if (canTryToSummon() && pos == ((CityBossLevel)Dungeon.level).pedestal( nextPedestal )) { + if (canTryToSummon() && pos == ((OldCityBossLevel)Dungeon.level).pedestal( nextPedestal )) { summon(); return true; } else { - if (enemy != null && Actor.findChar( ((CityBossLevel)Dungeon.level).pedestal( nextPedestal ) ) == enemy) { + if (enemy != null && canTryToSummon() && Actor.findChar( ((OldCityBossLevel)Dungeon.level).pedestal( nextPedestal ) ) == enemy) { nextPedestal = !nextPedestal; } return super.act(); @@ -176,7 +176,7 @@ public class King extends Mob { } } - private int maxArmySize() { + protected int maxArmySize() { return 1 + MAX_ARMY_SIZE * (HT - HP) / HT; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java new file mode 100644 index 000000000..fc3062fc3 --- /dev/null +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java @@ -0,0 +1,530 @@ +/* + * Pixel Dungeon + * Copyright (C) 2012-2015 Oleg Dolya + * + * Shattered Pixel Dungeon + * Copyright (C) 2014-2020 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.DwarfKing; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; +import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp; +import com.shatteredpixel.shatteredpixeldungeon.items.Heap; +import com.shatteredpixel.shatteredpixeldungeon.items.Item; +import com.shatteredpixel.shatteredpixeldungeon.levels.painters.CityPainter; +import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter; +import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard.ImpShopRoom; +import com.shatteredpixel.shatteredpixeldungeon.messages.Messages; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; +import com.shatteredpixel.shatteredpixeldungeon.tiles.CustomTilemap; +import com.watabou.noosa.Group; +import com.watabou.noosa.Tilemap; +import com.watabou.noosa.tweeners.AlphaTweener; +import com.watabou.utils.Bundle; +import com.watabou.utils.PathFinder; +import com.watabou.utils.Point; +import com.watabou.utils.Random; +import com.watabou.utils.Rect; + +public class NewCityBossLevel extends Level { + + { + color1 = 0x4b6636; + color2 = 0xf2f2f2; + } + + private static final Rect entry = new Rect(1, 37, 14, 48); + private static final Rect arena = new Rect(1, 25, 14, 38); + private static final Rect end = new Rect(0, 0, 15, 22); + + private static final int bottomDoor = 7 + (arena.bottom-1)*15; + private static final int topDoor = 7 + arena.top*15; + + private ImpShopRoom impShop; + + @Override + public String tilesTex() { + return Assets.TILES_CITY; + } + + @Override + public String waterTex() { + return Assets.WATER_CITY; + } + + private static final String IMP_SHOP = "imp_shop"; + + @Override + public void storeInBundle( Bundle bundle ) { + super.storeInBundle( bundle ); + bundle.put( IMP_SHOP, impShop ); + } + + @Override + public void restoreFromBundle( Bundle bundle ) { + super.restoreFromBundle( bundle ); + impShop = (ImpShopRoom) bundle.get( IMP_SHOP ); + if (map[topDoor] != Terrain.LOCKED_DOOR) impShop.onLevelLoad(this); + } + + @Override + protected boolean build() { + + setSize(15, 48); + + //entrance room + Painter.fill(this, entry, Terrain.WALL); + Painter.fill(this, entry, 1, Terrain.BOOKSHELF); + Painter.fill(this, entry, 2, Terrain.EMPTY); + + Painter.fill(this, entry.left+3, entry.top+3, 1, 5, Terrain.BOOKSHELF); + Painter.fill(this, entry.right-4, entry.top+3, 1, 5, Terrain.BOOKSHELF); + + Point c = entry.center(); + + Painter.fill(this, c.x-1, c.y-2, 3, 1, Terrain.STATUE); + Painter.fill(this, c.x-1, c.y, 3, 1, Terrain.STATUE); + Painter.fill(this, c.x-1, c.y+2, 3, 1, Terrain.STATUE); + Painter.fill(this, c.x, entry.top+1, 1, 6, Terrain.EMPTY_SP); + + Painter.set(this, c.x, entry.top, Terrain.DOOR); + + entrance = c.x + (c.y+2)*width(); + Painter.set(this, entrance, Terrain.ENTRANCE); + + //DK's throne room + Painter.fillDiamond(this, arena, 1, Terrain.EMPTY); + + Painter.fill(this, arena, 5, Terrain.EMPTY_SP); + Painter.fill(this, arena, 6, Terrain.STATUE_SP); + + c = arena.center(); + Painter.set(this, c.x-3, c.y, Terrain.STATUE); + Painter.set(this, c.x-4, c.y, Terrain.STATUE); + Painter.set(this, c.x+3, c.y, Terrain.STATUE); + Painter.set(this, c.x+4, c.y, Terrain.STATUE); + + Painter.set(this, c.x-3, c.y-3, Terrain.PEDESTAL); + Painter.set(this, c.x+3, c.y-3, Terrain.PEDESTAL); + Painter.set(this, c.x+3, c.y+3, Terrain.PEDESTAL); + Painter.set(this, c.x-3, c.y+3, Terrain.PEDESTAL); + + Painter.set(this, c.x, arena.top, Terrain.LOCKED_DOOR); + + //exit hallway + Painter.fill(this, end, Terrain.CHASM); + Painter.fill(this, end.left+4, end.top+5, 7, 18, Terrain.EMPTY); + Painter.fill(this, end.left+4, end.top+5, 7, 3, Terrain.EXIT); + exit = end.left+7 + (end.top+7)*width(); + + impShop = new ImpShopRoom(); + impShop.set(end.left+3, end.top+12, end.left+11, end.top+20); + if (impShop.itemCount() > (7*7)){ + impShop.bottom += 2; + } + Painter.set(this, impShop.center(), Terrain.PEDESTAL); + + Painter.set(this, impShop.left+2, impShop.top, Terrain.STATUE); + Painter.set(this, impShop.left+6, impShop.top, Terrain.STATUE); + + Painter.fill(this, end.left+5, end.bottom+1, 5, 1, Terrain.EMPTY); + Painter.fill(this, end.left+6, end.bottom+2, 3, 1, Terrain.EMPTY); + + new CityPainter().paint(this, null); + + //pillars last, no deco on these + Painter.fill(this, end.left+1, end.top+2, 2, 2, Terrain.WALL); + Painter.fill(this, end.left+1, end.top+7, 2, 2, Terrain.WALL); + Painter.fill(this, end.left+1, end.top+12, 2, 2, Terrain.WALL); + Painter.fill(this, end.left+1, end.top+17, 2, 2, Terrain.WALL); + + Painter.fill(this, end.right-3, end.top+2, 2, 2, Terrain.WALL); + Painter.fill(this, end.right-3, end.top+7, 2, 2, Terrain.WALL); + Painter.fill(this, end.right-3, end.top+12, 2, 2, Terrain.WALL); + Painter.fill(this, end.right-3, end.top+17, 2, 2, Terrain.WALL); + + CustomTilemap customVisuals = new CustomGroundVisuals(); + customVisuals.setRect(0, 0, width(), height()); + customTiles.add(customVisuals); + + customVisuals = new CustomWallVisuals(); + customVisuals.setRect(0, 0, width(), height()); + customWalls.add(customVisuals); + + return true; + } + + @Override + protected void createMobs() { + } + + public Actor respawner() { + return null; + } + + @Override + protected void createItems() { + Item item = Bones.get(); + if (item != null) { + int pos; + do { + pos = randomRespawnCell(null); + } while (pos == entrance); + drop( item, pos ).setHauntedIfCursed().type = Heap.Type.REMAINS; + } + } + + @Override + public int randomRespawnCell( Char ch ) { + int cell; + do { + cell = entrance + PathFinder.NEIGHBOURS8[Random.Int(8)]; + } while (!passable[cell] + || (Char.hasProp(ch, Char.Property.LARGE) && !openSpace[cell]) + || Actor.findChar(cell) != null); + return cell; + } + + @Override + public void occupyCell( Char ch ) { + + super.occupyCell( ch ); + + if (map[bottomDoor] == Terrain.DOOR && map[topDoor] == Terrain.LOCKED_DOOR + && ch.pos < bottomDoor && ch == Dungeon.hero) { + + seal(); + + } + } + + @Override + public void seal() { + super.seal(); + + for (Mob m : mobs){ + //bring the first ally with you + if (m.alignment == Char.Alignment.ALLY && !m.properties().contains(Char.Property.IMMOVABLE)){ + m.pos = Dungeon.hero.pos + (Random.Int(2) == 0 ? +1 : -1); + m.sprite.place(m.pos); + break; + } + } + + DwarfKing boss = new DwarfKing(); + boss.state = boss.WANDERING; + boss.pos = pointToCell(arena.center()); + GameScene.add( boss ); + + if (heroFOV[boss.pos]) { + boss.notice(); + boss.sprite.alpha( 0 ); + boss.sprite.parent.add( new AlphaTweener( boss.sprite, 1, 0.1f ) ); + } + + set( bottomDoor, Terrain.LOCKED_DOOR ); + GameScene.updateMap( bottomDoor ); + Dungeon.observe(); + } + + @Override + public void unseal() { + super.unseal(); + + set( bottomDoor, Terrain.DOOR ); + GameScene.updateMap( bottomDoor ); + + set( topDoor, Terrain.DOOR ); + GameScene.updateMap( topDoor ); + + if (Imp.Quest.isCompleted()) { + impShop.spawnShop(this); + } + Dungeon.observe(); + } + + @Override + public String tileName( int tile ) { + switch (tile) { + case Terrain.WATER: + return Messages.get(CityLevel.class, "water_name"); + case Terrain.HIGH_GRASS: + return Messages.get(CityLevel.class, "high_grass_name"); + default: + return super.tileName( tile ); + } + } + + @Override + public String tileDesc(int tile) { + switch (tile) { + case Terrain.ENTRANCE: + return Messages.get(CityLevel.class, "entrance_desc"); + case Terrain.EXIT: + return Messages.get(CityLevel.class, "exit_desc"); + case Terrain.WALL_DECO: + case Terrain.EMPTY_DECO: + return Messages.get(CityLevel.class, "deco_desc"); + case Terrain.EMPTY_SP: + return Messages.get(CityLevel.class, "sp_desc"); + case Terrain.STATUE: + case Terrain.STATUE_SP: + return Messages.get(CityLevel.class, "statue_desc"); + case Terrain.BOOKSHELF: + return Messages.get(CityLevel.class, "bookshelf_desc"); + default: + return super.tileDesc( tile ); + } + } + + @Override + public Group addVisuals( ) { + super.addVisuals(); + CityLevel.addCityVisuals(this, visuals); + return visuals; + } + + //TODO need to change text for some of these tiles + public static class CustomGroundVisuals extends CustomTilemap { + + { + texture = Assets.CITY_BOSS; + tileW = 15; + tileH = 48; + } + + private static final int STAIR_ROWS = 8; + + @Override + public Tilemap create() { + Tilemap v = super.create(); + int[] data = new int[tileW*tileH]; + + int[] map = Dungeon.level.map; + + int stairsTop = -1; + + //upper part of the level, mostly demon halls tiles + for (int i = tileW; i < tileW*22; i++){ + + if (map[i] == Terrain.EXIT && stairsTop == -1){ + stairsTop = i - tileW; + } + + //pillars + if (map[i] == Terrain.WALL && map[i-tileW] == Terrain.CHASM){ + data[i] = 13*8 + 6; + data[++i] = 13*8 + 7; + } else if (map[i] == Terrain.WALL && map[i-tileW] == Terrain.WALL){ + data[i] = 14*8 + 6; + data[++i] = 14*8 + 7; + } else if (i > tileW && map[i] == Terrain.CHASM && map[i-tileW] == Terrain.WALL) { + data[i] = 15*8 + 6; + data[++i] = 15*8 + 7; + + //imp's pedestal + } else if (map[i] == Terrain.PEDESTAL) { + data[i] = 12*8 + 5; + + //skull piles + } else if (map[i] == Terrain.STATUE) { + data[i] = 13*8 + 5; + + //ground tiles + } else if (map[i] == Terrain.EMPTY || map[i] == Terrain.EMPTY_DECO){ + + //final ground stiching with city tiles + if (i/tileW == 21){ + data[i] = 11*8 + 0; + data[++i] = 11*8 + 1; + data[++i] = 11*8 + 2; + data[++i] = 11*8 + 3; + data[++i] = 11*8 + 4; + data[++i] = 11*8 + 5; + data[++i] = 11*8 + 6; + } else { + + //regular ground tiles + if (map[i - 1] == Terrain.CHASM) { + data[i] = 12 * 8 + 1; + } else if (map[i + 1] == Terrain.CHASM) { + data[i] = 12 * 8 + 3; + } else if (map[i] == Terrain.EMPTY_DECO) { + data[i] = 12 * 8 + 4; + } else { + data[i] = 12 * 8 + 2; + } + } + + //otherwise no tile here + } else { + data[i] = -1; + } + } + + //custom for stairs + int[] rowData = null; + for (int i = 0; i < STAIR_ROWS; i++){ + if (i == 0){ + rowData = new int[]{-1, -1, 7*8+2, 7*8+3, 7*8+4, -1, -1}; + } else if (i == 1){ + rowData = new int[]{8*8+0, 8*8+1, 8*8+2, 8*8+3, 8*8+4, 8*8+5, 8*8+6}; + } else if (i < STAIR_ROWS-2){ + rowData = new int[]{9*8+0, 8*8+3, 8*8+3, 8*8+3, 8*8+3, 8*8+3, 9*8+6}; + } else if (i == STAIR_ROWS-2){ + rowData = new int[]{9*8+0, 9*8+1, 9*8+2, 9*8+3, 9*8+4, 9*8+5, 9*8+6}; + } else { + rowData = new int[]{10*8+0, 10*8+1, 10*8+2, 10*8+3, 10*8+4, 10*8+5, 10*8+6}; + } + for (int j = 0; j < rowData.length; j++){ + data[stairsTop+j] = rowData[j]; + } + stairsTop += tileW; + } + + //lower part: statues, pedestals, and carpets + for (int i = tileW*22; i < tileW * tileH; i++){ + + //pedestal spawners + if (map[i] == Terrain.PEDESTAL){ + data[i] = 13*8 + 4; + + //statues that should face left instead of right + } else if (map[i] == Terrain.STATUE && i%tileW > 7) { + data[i] = 15 * 8 + 4; + + //carpet tiles + } else if (map[i] == Terrain.EMPTY_SP) { + //top row of DK's throne + if (map[i + 1] == Terrain.EMPTY_SP && map[i + tileW] == Terrain.EMPTY_SP) { + data[i] = 13 * 8 + 1; + data[++i] = 13 * 8 + 2; + data[++i] = 13 * 8 + 3; + + //mid row of DK's throne + }else if (map[i + 1] == Terrain.STATUE_SP) { + data[i] = 14 * 8 + 1; + data[++i] = 15 * 8 + 5; + data[++i] = 14 * 8 + 3; + + //bottom row of DK's throne + } else if (map[i+1] == Terrain.EMPTY_SP && map[i-tileW] == Terrain.EMPTY_SP){ + data[i] = 15*8 + 1; + data[++i] = 15*8 + 2; + data[++i] = 15*8 + 3; + + //otherwise entrance carpet + } else if (map[i-tileW] != Terrain.EMPTY_SP){ + data[i] = 13*8 + 0; + } else if (map[i+tileW] != Terrain.EMPTY_SP){ + data[i] = 15*8 + 0; + } else { + data[i] = 14*8 + 0; + } + + //otherwise no tile here + } else { + data[i] = -1; + } + } + + v.map( data, tileW ); + return v; + } + } + + public static class CustomWallVisuals extends CustomTilemap { + { + texture = Assets.CITY_BOSS; + tileW = 15; + tileH = 48; + } + + @Override + public Tilemap create() { + Tilemap v = super.create(); + int[] data = new int[tileW*tileH]; + + int[] map = Dungeon.level.map; + + int stairsTop = -1; + + //upper part of the level, mostly demon halls tiles + for (int i = tileW; i < tileW*21; i++) { + + if (map[i] == Terrain.EXIT && stairsTop == -1){ + stairsTop = i - tileW; + } + + //pillars + if (map[i] == Terrain.CHASM && map[i+tileW] == Terrain.WALL) { + data[i] = 12*8 + 6; + data[++i] = 12*8 + 7; + } else if (map[i] == Terrain.WALL && map[i-tileW] == Terrain.CHASM) { + data[i] = 13*8 + 6; + data[++i] = 13*8 + 7; + + //otherwise no tile here + } else { + data[i] = -1; + } + } + + //custom shadow (and skull tops) for stairs + //TODO this doesn't look so great. Should try baking some of the shadow into the stairs themselves + for (int i = 0; i < CustomGroundVisuals.STAIR_ROWS; i++){ + if (i == CustomGroundVisuals.STAIR_ROWS-1){ + data[stairsTop] = i*8 + 0; + data[stairsTop+1] = i*8 + 1; + data[stairsTop+2] = data[stairsTop+3] = data[stairsTop+4] = -1; + data[stairsTop+5] = i*8 + 5; + data[stairsTop+6] = i*8 + 6; + } else { + for (int j = 0; j < 7; j++) { + data[stairsTop + j] = i*8 + j; + } + } + stairsTop += tileW; + } + + //lower part. Just need to handle statue tiles here + for (int i = tileW*21; i < tileW * tileH; i++){ + + //DK's throne + if (map[i] == Terrain.STATUE_SP){ + data[i-tileW] = 14*8 + 5; + + //Statues that need to face left instead of right + } else if (map[i] == Terrain.STATUE && i%tileW > 7){ + data[i-tileW] = 14*8 + 4; + } + + //always no tile here (as the above statements are modifying previous tiles) + data[i] = -1; + } + + v.map( data, tileW ); + return v; + } + } +} diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/OldCityBossLevel.java similarity index 99% rename from core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityBossLevel.java rename to core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/OldCityBossLevel.java index 3b29e77b3..aef0a808a 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/CityBossLevel.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/OldCityBossLevel.java @@ -41,7 +41,7 @@ import com.watabou.utils.Bundle; import com.watabou.utils.PathFinder; import com.watabou.utils.Random; -public class CityBossLevel extends Level { +public class OldCityBossLevel extends Level { { color1 = 0x4b6636; diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/painters/CityPainter.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/painters/CityPainter.java index 44b06e34d..d8c1f21d1 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/painters/CityPainter.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/painters/CityPainter.java @@ -46,7 +46,7 @@ public class CityPainter extends RegularPainter { } else if (map[i] == Terrain.WALL && !DungeonTileSheet.wallStitcheable(map[i + w]) - && Random.Int( 22 - Dungeon.depth ) == 0) { + && Random.Int( 21 - Dungeon.depth ) == 0) { map[i] = Terrain.WALL_DECO; } } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/ShopRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/ShopRoom.java index 796355b22..86bf81193 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/ShopRoom.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/ShopRoom.java @@ -68,14 +68,17 @@ public class ShopRoom extends SpecialRoom { @Override public int minWidth() { - if (itemsToSpawn == null) itemsToSpawn = generateItems(); - return Math.max(7, (int)(Math.sqrt(itemsToSpawn.size())+3)); + return Math.max(7, (int)(Math.sqrt(itemCount())+3)); } @Override public int minHeight() { + return Math.max(7, (int)(Math.sqrt(itemCount())+3)); + } + + public int itemCount(){ if (itemsToSpawn == null) itemsToSpawn = generateItems(); - return Math.max(7, (int)(Math.sqrt(itemsToSpawn.size())+3)); + return itemsToSpawn.size(); } public void paint( Level level ) { @@ -105,8 +108,9 @@ public class ShopRoom extends SpecialRoom { protected void placeItems( Level level ){ - if (itemsToSpawn == null) + if (itemsToSpawn == null){ itemsToSpawn = generateItems(); + } Point itemPlacement = new Point(entrance()); if (itemPlacement.y == top){ @@ -167,8 +171,8 @@ public class ShopRoom extends SpecialRoom { itemsToSpawn.add( Generator.random(Generator.misTiers[3]).quantity(2).identify() ); itemsToSpawn.add( new ScaleArmor().identify() ); break; - - case 21: + + case 20: case 21: w = (MeleeWeapon) Generator.random(Generator.wepTiers[4]); itemsToSpawn.add( Generator.random(Generator.misTiers[4]).quantity(2).identify() ); itemsToSpawn.add( new PlateArmor().identify() ); @@ -178,6 +182,7 @@ public class ShopRoom extends SpecialRoom { break; } w.enchant(null); + w.cursed = false; w.level(0); w.identify(); itemsToSpawn.add(w); @@ -236,7 +241,7 @@ public class ShopRoom extends SpecialRoom { bags = (int)Math.ceil(( 5-hourglass.sandBags) * 0.25f ); break; case 16: bags = (int)Math.ceil(( 5-hourglass.sandBags) * 0.50f ); break; - case 21: + case 20: case 21: bags = (int)Math.ceil(( 5-hourglass.sandBags) * 0.80f ); break; } diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/ImpShopRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/ImpShopRoom.java index 6d9b7326d..7dc910740 100644 --- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/ImpShopRoom.java +++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/standard/ImpShopRoom.java @@ -21,6 +21,7 @@ package com.shatteredpixel.shatteredpixeldungeon.levels.rooms.standard; +import com.shatteredpixel.shatteredpixeldungeon.ShatteredPixelDungeon; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.Mob; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.Imp; import com.shatteredpixel.shatteredpixeldungeon.actors.mobs.npcs.ImpShopkeeper; @@ -28,6 +29,7 @@ import com.shatteredpixel.shatteredpixeldungeon.levels.Level; import com.shatteredpixel.shatteredpixeldungeon.levels.Terrain; import com.shatteredpixel.shatteredpixeldungeon.levels.painters.Painter; import com.shatteredpixel.shatteredpixeldungeon.levels.rooms.special.ShopRoom; +import com.shatteredpixel.shatteredpixeldungeon.scenes.GameScene; import com.watabou.utils.Bundle; //shops probably shouldn't extend special room, because of cases like this. @@ -62,9 +64,7 @@ public class ImpShopRoom extends ShopRoom { } if (Imp.Quest.isCompleted()){ - impSpawned = true; - placeItems(level); - placeShopkeeper(level); + spawnShop(level); } else { impSpawned = false; } @@ -78,7 +78,11 @@ public class ImpShopRoom extends ShopRoom { Mob shopkeeper = new ImpShopkeeper(); shopkeeper.pos = pos; - level.mobs.add( shopkeeper ); + if (ShatteredPixelDungeon.scene() instanceof GameScene) { + GameScene.add(shopkeeper); + } else { + level.mobs.add(shopkeeper); + } } @@ -88,9 +92,10 @@ public class ImpShopRoom extends ShopRoom { return connected.isEmpty() ? new Door(left, top+2) : super.entrance(); } - private void spawnShop(Level level){ + public void spawnShop(Level level){ impSpawned = true; - super.paint(level); + placeItems(level); + placeShopkeeper(level); } private static final String IMP = "imp_spawned"; @@ -112,9 +117,7 @@ public class ImpShopRoom extends ShopRoom { super.onLevelLoad(level); if (Imp.Quest.isCompleted() && !impSpawned){ - impSpawned = true; - placeItems(level); - placeShopkeeper(level); + spawnShop(level); } } }