From cde8bc67f6332573624f1f71fb6b0a89a38e4364 Mon Sep 17 00:00:00 2001
From: Evan Debenham <Evan@ShatteredPixel.com>
Date: Tue, 18 Feb 2020 11:56:13 -0500
Subject: [PATCH] v0.8.0: Fixed the following: - Dwarf King's fight not
 starting if his entrance door was burned down - Dwarf King rarely teleporting
 into walls - Dwarf King being movable in his 2nd phase - Dwarf King 2nd phase
 getting stalled of minions are polymorphed or corrupted - Rare freeze errors
 when Dwarf King dies - Various minior errors caused by floor 21 now being a
 demon halls floor - Necromancers playing vfx/sfx when they are not visible -
 Mimics rarely spawning ontop of eachother

---
 .../actors/mobs/DwarfKing.java                | 24 ++++++++++++-------
 .../actors/mobs/Ghoul.java                    | 23 ++++++++++--------
 .../actors/mobs/Necromancer.java              | 12 ++++++----
 .../levels/NewCityBossLevel.java              |  2 +-
 .../levels/rooms/special/TreasuryRoom.java    |  2 +-
 .../scenes/InterlevelScene.java               |  4 ++--
 .../messages/actors/actors.properties         |  2 +-
 7 files changed, 42 insertions(+), 27 deletions(-)

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
index 4a324a310..23b9d563c 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/DwarfKing.java
@@ -128,6 +128,8 @@ public class DwarfKing extends Mob {
 		summonCooldown = bundle.getFloat( SUMMON_CD );
 		abilityCooldown = bundle.getFloat( ABILITY_CD );
 		lastAbility = bundle.getInt( LAST_ABILITY );
+
+		if (phase == 2) properties.add(Property.IMMOVABLE);
 	}
 
 	@Override
@@ -300,7 +302,7 @@ public class DwarfKing extends Mob {
 
 				for (int i : PathFinder.NEIGHBOURS8){
 					if (Actor.findChar(pos+i) == null
-							&& !Dungeon.level.solid[pos+1]
+							&& !Dungeon.level.solid[pos+i]
 							&& Dungeon.level.trueDistance(pos+i, enemy.pos) > bestDist){
 						bestPos = pos+i;
 						bestDist = Dungeon.level.trueDistance(pos+i, enemy.pos);
@@ -371,6 +373,7 @@ public class DwarfKing extends Mob {
 				HP = 50;
 				sprite.showStatus(CharSprite.POSITIVE, Messages.get(this, "immune"));
 				ScrollOfTeleportation.appear(this, NewCityBossLevel.throne);
+				properties.add(Property.IMMOVABLE);
 				phase = 2;
 				summonsMade = 0;
 				sprite.idle();
@@ -385,6 +388,7 @@ public class DwarfKing extends Mob {
 				}
 			}
 		} else if (phase == 2 && shielding() == 0) {
+			properties.remove(Property.IMMOVABLE);
 			phase = 3;
 			summonsMade = 3; //opens with a monk/warlock
 			sprite.centerEmitter().start( Speck.factory( Speck.SCREAM ), 0.4f, 2 );
@@ -416,15 +420,10 @@ public class DwarfKing extends Mob {
 
 		Badges.validateBossSlain();
 
-		super.die( cause );
-
 		Dungeon.level.unseal();
-		Badges.validateBossSlain();
 
-		for (Mob m : Dungeon.level.mobs.toArray(new Mob[0])){
-			if (m instanceof Ghoul || m instanceof Monk || m instanceof Warlock){
-				m.die(null);
-			}
+		for (Mob m : getSubjects()){
+			m.die(null);
 		}
 
 		LloydsBeacon beacon = Dungeon.hero.belongings.getItem(LloydsBeacon.class);
@@ -545,6 +544,15 @@ public class DwarfKing extends Mob {
 
 	public static class KingDamager extends Buff {
 
+		@Override
+		public boolean act() {
+			if (target.alignment != Alignment.ENEMY){
+				detach();
+			}
+			spend( TICK );
+			return true;
+		}
+
 		@Override
 		public void detach() {
 			super.detach();
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java
index a838265ec..e91bfe1fa 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Ghoul.java
@@ -138,11 +138,14 @@ public class Ghoul extends Mob {
 		return super.act();
 	}
 
+	private boolean beingLifeLinked = false;
+
 	@Override
 	public void die(Object cause) {
 		if (cause != Chasm.class && cause != GhoulLifeLink.class){
 			Ghoul nearby = GhoulLifeLink.searchForHost(this);
 			if (nearby != null){
+				beingLifeLinked = true;
 				Actor.remove(this);
 				Dungeon.level.mobs.remove( this );
 				timesDowned++;
@@ -150,25 +153,25 @@ public class Ghoul extends Mob {
 				((GhoulSprite)sprite).crumple();
 				HP = Math.round(HT/10f);
 				GLog.i(Messages.get(this, "collapse"));
+				beingLifeLinked = false;
 				return;
 			}
 		}
 
-		Buff b = buff(Corruption.class);
-		if (b != null) b.detach();
-		b = buff(DwarfKing.KingDamager.class);
-		if (b != null) b.detach();
-
 		super.die(cause);
 	}
 
 	@Override
 	protected synchronized void onRemove() {
-		for (Buff buff : buffs()) {
-			//corruption and king damager are preserved when removed
-			//instead they are detached when the ghoul actually dies
-			if (!(buff instanceof Corruption) && !(buff instanceof DwarfKing.KingDamager))
-				buff.detach();
+		if (beingLifeLinked) {
+			for (Buff buff : buffs()) {
+				//corruption and king damager are preserved when removed via life link
+				if (!(buff instanceof Corruption) && !(buff instanceof DwarfKing.KingDamager)) {
+					buff.detach();
+				}
+			}
+		} else {
+			super.onRemove();
 		}
 	}
 
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java
index 80dd37fad..62405e07e 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/Necromancer.java
@@ -160,16 +160,20 @@ public class Necromancer extends Mob {
 		
 		//heal skeleton first
 		if (mySkeleton.HP < mySkeleton.HT){
-			
-			sprite.parent.add(new Beam.HealthRay(sprite.center(), mySkeleton.sprite.center()));
+
+			if (sprite.visible || mySkeleton.sprite.visible) {
+				sprite.parent.add(new Beam.HealthRay(sprite.center(), mySkeleton.sprite.center()));
+			}
 			
 			mySkeleton.HP = Math.min(mySkeleton.HP + 5, mySkeleton.HT);
 			mySkeleton.sprite.emitter().burst( Speck.factory( Speck.HEALING ), 1 );
 			
 			//otherwise give it adrenaline
 		} else if (mySkeleton.buff(Adrenaline.class) == null) {
-			
-			sprite.parent.add(new Beam.HealthRay(sprite.center(), mySkeleton.sprite.center()));
+
+			if (sprite.visible || mySkeleton.sprite.visible) {
+				sprite.parent.add(new Beam.HealthRay(sprite.center(), mySkeleton.sprite.center()));
+			}
 			
 			Buff.affect(mySkeleton, Adrenaline.class, 3f);
 		}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java
index 09f77132e..fa3f84351 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/NewCityBossLevel.java
@@ -259,7 +259,7 @@ public class NewCityBossLevel extends Level {
 
 		super.occupyCell( ch );
 
-		if (map[bottomDoor] == Terrain.DOOR && map[topDoor] == Terrain.LOCKED_DOOR
+		if (map[bottomDoor] != Terrain.LOCKED_DOOR && map[topDoor] == Terrain.LOCKED_DOOR
 				&& ch.pos < bottomDoor && ch == Dungeon.hero) {
 
 			seal();
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/TreasuryRoom.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/TreasuryRoom.java
index 4066fa8ef..68a5ca953 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/TreasuryRoom.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/levels/rooms/special/TreasuryRoom.java
@@ -47,7 +47,7 @@ public class TreasuryRoom extends SpecialRoom {
 			int pos;
 			do {
 				pos = level.pointToCell(random());
-			} while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null);
+			} while (level.map[pos] != Terrain.EMPTY || level.heaps.get( pos ) != null || level.findMob(pos) != null);
 			if (heapType == Heap.Type.CHEST && Dungeon.depth > 1 && Random.Int( 5 ) == 0){
 				level.mobs.add(Mimic.spawnAt(pos, new Gold().random()));
 			} else {
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java
index 4badc332e..34bad14a2 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/scenes/InterlevelScene.java
@@ -112,7 +112,7 @@ public class InterlevelScene extends PixelScene {
 					if (!(Statistics.deepestFloor < loadingDepth)) {
 						fadeTime = FAST_FADE;
 					} else if (loadingDepth == 6 || loadingDepth == 11
-							|| loadingDepth == 16 || loadingDepth == 22) {
+							|| loadingDepth == 16 || loadingDepth == 21) {
 						fadeTime = SLOW_FADE;
 					}
 				}
@@ -135,7 +135,7 @@ public class InterlevelScene extends PixelScene {
 		if (loadingDepth <= 5)          loadingAsset = Assets.LOADING_SEWERS;
 		else if (loadingDepth <= 10)    loadingAsset = Assets.LOADING_PRISON;
 		else if (loadingDepth <= 15)    loadingAsset = Assets.LOADING_CAVES;
-		else if (loadingDepth <= 21)    loadingAsset = Assets.LOADING_CITY;
+		else if (loadingDepth <= 20)    loadingAsset = Assets.LOADING_CITY;
 		else if (loadingDepth <= 25)    loadingAsset = Assets.LOADING_HALLS;
 		else                            loadingAsset = Assets.SHADOW;
 		
diff --git a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
index 78c6306f3..9fd29b70b 100644
--- a/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
+++ b/core/src/main/resources/com/shatteredpixel/shatteredpixeldungeon/messages/actors/actors.properties
@@ -530,7 +530,7 @@ actors.mobs.dwarfking.wave_3=Useless! KILL THEM NOW!
 actors.mobs.dwarfking.enraged=You cannot kill me %s. I. AM. IMMORTAL!
 actors.mobs.dwarfking.losing=No! You can't do this... you have no idea what lies below...
 actors.mobs.dwarfking.defeated=You've... Doomed us all...
-actors.mobs.dwarfking.desc=Many years ago one of the highest wizards in the dwarven court uncovered secrets which gave him tremendous power over life and death. He soon put this power to use, subjugating and corrupting his peers, his king, and eventually every dwarven citizen. Now he is king, ruler over a legion of undead subjects.\n\nThe King of Dwarves is an aggressive foe, who will attempt to overwhelm his enemies with his horde of undead minions. Careful positioning is key to getting the upper hand against him.
+actors.mobs.dwarfking.desc=Many years ago one of the highest wizards in the dwarven court uncovered secrets which gave him tremendous power over life and death. He soon put this power to use, subjugating and corrupting his peers, his king, and eventually every dwarven citizen. Now he is king, ruler over a legion of undead subjects.\n\nThe King of Dwarves is an aggressive foe, who will attempt to surround and overwhelm his enemies with his horde of undead minions.
 
 actors.mobs.elemental$fire.name=fire elemental
 actors.mobs.elemental$fire.desc=Elementals are chaotic creatures that are often created when powerful occult magic isn't properly controlled. Elementals have minimal intelligence, and are usually associated with a particular type of magic.\n\nFire elementals are a common type of elemental which deals damage with fiery magic. They will set their target ablaze with melee attacks, and can occasionally shoot bolts of fire as well.