From 432c06969131d17de86d9c89386a8921d23804fd Mon Sep 17 00:00:00 2001
From: Evan Debenham <Evan@ShatteredPixel.com>
Date: Thu, 26 Mar 2020 14:57:25 -0400
Subject: [PATCH] v0.8.0: New Minor Features: - The game now flashes red when
 the hero is seriously injured, this replaces the old screen shake when
 injured effect - Yog, ripper demons, and DM-300 are now less punishing to
 slower builds - Yog's fist summons are now based on the levelgen seed -
 velvet pouch can now hold goo blobs and metal shards - wand of disintegration
 now breaks webs -

---
 .../shatteredpixeldungeon/actors/Char.java    |  8 --
 .../actors/hero/Hero.java                     | 11 +++
 .../actors/mobs/NewDM300.java                 |  8 +-
 .../actors/mobs/RipperDemon.java              |  4 +-
 .../actors/mobs/YogDzewa.java                 | 80 ++++++++++---------
 .../items/bags/VelvetPouch.java               |  5 +-
 .../items/wands/WandOfDisintegration.java     | 12 ++-
 7 files changed, 73 insertions(+), 55 deletions(-)

diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java
index 7854e9cc8..6d616cafc 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/Char.java
@@ -281,14 +281,6 @@ public abstract class Char extends Actor {
 				return true;
 			}
 
-			//TODO: consider revisiting this and shaking in more cases.
-			float shake = 0f;
-			if (enemy == Dungeon.hero)
-				shake = effectiveDamage / (enemy.HT / 4);
-
-			if (shake > 1f)
-				Camera.main.shake( GameMath.gate( 1, shake, 5), 0.3f );
-
 			enemy.damage( effectiveDamage, this );
 
 			if (buff(FireImbue.class) != null)
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java
index 20f8f1c88..ed28e1f10 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/hero/Hero.java
@@ -1045,7 +1045,18 @@ public class Hero extends Char {
 			dmg -= AntiMagic.drRoll(belongings.armor.buffedLvl());
 		}
 
+		int preHP = HP + shielding();
 		super.damage( dmg, src );
+		int effectiveDamage = preHP - (HP + shielding());
+
+		//flash red when hit for 1/4 of your remaining HP or higher.
+		// Intensity increases the more injured the player is.
+		if (preHP > 0 && effectiveDamage >= preHP/4f){
+			//08%/11%/16%/33% intensity at
+			//75%/50%/25%/00% health
+			float divisor = 3 + 12*((HP + shielding()) / (float)(HT + shielding()));
+			GameScene.flash( (int)(0xFF/divisor) << 16 );
+		}
 	}
 	
 	public void checkVisibleMobs() {
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java
index c7aa70498..b4328663a 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/NewDM300.java
@@ -63,6 +63,7 @@ import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
 import com.watabou.noosa.Camera;
 import com.watabou.noosa.audio.Sample;
 import com.watabou.utils.Bundle;
+import com.watabou.utils.GameMath;
 import com.watabou.utils.PathFinder;
 import com.watabou.utils.Random;
 import com.watabou.utils.RectF;
@@ -393,10 +394,11 @@ public class NewDM300 extends Mob {
 					pos++;
 					continue;
 				}
-				if (!Dungeon.level.solid[pos] && pos != safeCell && Random.Int(Dungeon.level.distance(rockCenter, pos)) == 0) {
-					GameScene.add(Blob.seed(pos, 1, FallingRocks.class));
-				}
 				//add rock cell to pos, if it is not solid, and isn't the safecell
+				if (!Dungeon.level.solid[pos] && pos != safeCell && Random.Int(Dungeon.level.distance(rockCenter, pos)) == 0) {
+					//don't want to overly punish players with slow move or attack speed
+					GameScene.add(Blob.seed(pos, (int)GameMath.gate(TICK, (float)Math.ceil(target.cooldown()), 3*TICK), FallingRocks.class));
+				}
 				pos++;
 			}
 		}
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java
index f26642d89..f38388a19 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/RipperDemon.java
@@ -37,6 +37,7 @@ import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
 import com.watabou.noosa.audio.Sample;
 import com.watabou.utils.Bundle;
 import com.watabou.utils.Callback;
+import com.watabou.utils.GameMath;
 import com.watabou.utils.PathFinder;
 import com.watabou.utils.Random;
 
@@ -209,7 +210,8 @@ public class RipperDemon extends Mob {
 					if (b.collisionPos == targetPos){
 						//get ready to leap
 						leapPos = targetPos;
-						spend(TICK);
+						//don't want to overly punish players with slow move or attack speed
+						spend(GameMath.gate(TICK, enemy.cooldown(), 3*TICK));
 						if (Dungeon.level.heroFOV[pos]){
 							GLog.w(Messages.get(RipperDemon.this, "leap"));
 							sprite.parent.addToBack(new TargetedCell(leapPos, 0xFF0000));
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java
index d0f44bfb9..e17acfbca 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/actors/mobs/YogDzewa.java
@@ -51,6 +51,7 @@ import com.shatteredpixel.shatteredpixeldungeon.tiles.DungeonTilemap;
 import com.shatteredpixel.shatteredpixeldungeon.ui.BossHealthBar;
 import com.shatteredpixel.shatteredpixeldungeon.utils.GLog;
 import com.watabou.utils.Bundle;
+import com.watabou.utils.GameMath;
 import com.watabou.utils.PathFinder;
 import com.watabou.utils.Random;
 import com.watabou.utils.Reflection;
@@ -87,10 +88,12 @@ public class YogDzewa extends Mob {
 
 	private ArrayList<Class> fistSummons = new ArrayList<>();
 	{
-		fistSummons.add(Random.Int(2) == 0 ? YogFist.Burning.class : YogFist.Soiled.class);
-		fistSummons.add(Random.Int(2) == 0 ? YogFist.Rotting.class : YogFist.Rusted.class);
-		fistSummons.add(Random.Int(2) == 0 ? YogFist.Bright.class : YogFist.Dark.class);
-		Random.shuffle(fistSummons);
+		Random.pushGenerator(Dungeon.seedCurDepth());
+			fistSummons.add(Random.Int(2) == 0 ? YogFist.Burning.class : YogFist.Soiled.class);
+			fistSummons.add(Random.Int(2) == 0 ? YogFist.Rotting.class : YogFist.Rusted.class);
+			fistSummons.add(Random.Int(2) == 0 ? YogFist.Bright.class : YogFist.Dark.class);
+			Random.shuffle(fistSummons);
+		Random.popGenerator();
 	}
 
 	private static final int SUMMON_DECK_SIZE = 4;
@@ -130,38 +133,41 @@ public class YogDzewa extends Mob {
 
 			boolean terrainAffected = false;
 			HashSet<Char> affected = new HashSet<>();
-			for (int i : targetedCells){
-				Ballistica b = new Ballistica(pos, i, Ballistica.WONT_STOP);
-				//shoot beams
-				sprite.parent.add(new Beam.DeathRay(sprite.center(), DungeonTilemap.raisedTileCenterToWorld(b.collisionPos)));
-				for (int p : b.path){
-					Char ch = Actor.findChar(p);
-					if (ch != null && ch.alignment != alignment){
-						affected.add(ch);
-					}
-					if (Dungeon.level.flamable[p]){
-						Dungeon.level.destroy( p );
-						GameScene.updateMap( p );
-						terrainAffected = true;
+			//delay fire on a rooted hero
+			if (!Dungeon.hero.rooted) {
+				for (int i : targetedCells) {
+					Ballistica b = new Ballistica(pos, i, Ballistica.WONT_STOP);
+					//shoot beams
+					sprite.parent.add(new Beam.DeathRay(sprite.center(), DungeonTilemap.raisedTileCenterToWorld(b.collisionPos)));
+					for (int p : b.path) {
+						Char ch = Actor.findChar(p);
+						if (ch != null && ch.alignment != alignment) {
+							affected.add(ch);
+						}
+						if (Dungeon.level.flamable[p]) {
+							Dungeon.level.destroy(p);
+							GameScene.updateMap(p);
+							terrainAffected = true;
+						}
 					}
 				}
-			}
-			if (terrainAffected){
-				Dungeon.observe();
-			}
-			for (Char ch : affected){
-				ch.damage(Random.NormalIntRange(20, 40), new Eye.DeathGaze());
+				if (terrainAffected) {
+					Dungeon.observe();
+				}
+				for (Char ch : affected) {
+					ch.damage(Random.NormalIntRange(20, 40), new Eye.DeathGaze());
 
-				if (Dungeon.level.heroFOV[pos]) {
-					ch.sprite.flash();
-					CellEmitter.center( pos ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
-				}
-				if (!ch.isAlive() && ch == Dungeon.hero) {
-					Dungeon.fail( getClass() );
-					GLog.n( Messages.get(Char.class, "kill", name()) );
+					if (Dungeon.level.heroFOV[pos]) {
+						ch.sprite.flash();
+						CellEmitter.center(pos).burst(PurpleParticle.BURST, Random.IntRange(1, 2));
+					}
+					if (!ch.isAlive() && ch == Dungeon.hero) {
+						Dungeon.fail(getClass());
+						GLog.n(Messages.get(Char.class, "kill", name()));
+					}
 				}
+				targetedCells.clear();
 			}
-			targetedCells.clear();
 
 			if (abilityCooldown <= 0){
 
@@ -200,18 +206,15 @@ public class YogDzewa extends Mob {
 					}
 				}
 
-				//wait extra time to let a crippled/rooted hero evade
-				if (Dungeon.hero.buff(Cripple.class) != null){
-					spend(TICK);
-				} else if (Dungeon.hero.buff(Roots.class) != null){
-					spend(Dungeon.hero.buff(Roots.class).cooldown());
-				}
-
+				//don't want to overly punish players with slow move or attack speed
+				spend(GameMath.gate(TICK, Dungeon.hero.cooldown(), 3*TICK));
 				Dungeon.hero.interrupt();
 
 				abilityCooldown += Random.NormalFloat(MIN_ABILITY_CD, MAX_ABILITY_CD);
 				abilityCooldown -= phase;
 
+			} else {
+				spend(TICK);
 			}
 
 			while (summonCooldown <= 0){
@@ -257,7 +260,6 @@ public class YogDzewa extends Mob {
 			summonCooldown = 3;
 		}
 
-		spend(TICK);
 		return true;
 	}
 
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/bags/VelvetPouch.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/bags/VelvetPouch.java
index ca45af8c6..c955e8c66 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/bags/VelvetPouch.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/bags/VelvetPouch.java
@@ -22,6 +22,8 @@
 package com.shatteredpixel.shatteredpixeldungeon.items.bags;
 
 import com.shatteredpixel.shatteredpixeldungeon.items.Item;
+import com.shatteredpixel.shatteredpixeldungeon.items.quest.GooBlob;
+import com.shatteredpixel.shatteredpixeldungeon.items.quest.MetalShard;
 import com.shatteredpixel.shatteredpixeldungeon.items.stones.Runestone;
 import com.shatteredpixel.shatteredpixeldungeon.plants.Plant;
 import com.shatteredpixel.shatteredpixeldungeon.sprites.ItemSpriteSheet;
@@ -36,7 +38,8 @@ public class VelvetPouch extends Bag {
 	
 	@Override
 	public boolean grab( Item item ) {
-		return item instanceof Plant.Seed || item instanceof Runestone;
+		return item instanceof Plant.Seed || item instanceof Runestone
+				|| item instanceof GooBlob || item instanceof MetalShard;
 	}
 	
 	@Override
diff --git a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfDisintegration.java b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfDisintegration.java
index c488c1e7e..b4b954391 100644
--- a/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfDisintegration.java
+++ b/core/src/main/java/com/shatteredpixel/shatteredpixeldungeon/items/wands/WandOfDisintegration.java
@@ -24,6 +24,8 @@ package com.shatteredpixel.shatteredpixeldungeon.items.wands;
 import com.shatteredpixel.shatteredpixeldungeon.Dungeon;
 import com.shatteredpixel.shatteredpixeldungeon.actors.Actor;
 import com.shatteredpixel.shatteredpixeldungeon.actors.Char;
+import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Blob;
+import com.shatteredpixel.shatteredpixeldungeon.actors.blobs.Web;
 import com.shatteredpixel.shatteredpixeldungeon.effects.Beam;
 import com.shatteredpixel.shatteredpixeldungeon.effects.CellEmitter;
 import com.shatteredpixel.shatteredpixeldungeon.effects.particles.PurpleParticle;
@@ -65,6 +67,8 @@ public class WandOfDisintegration extends DamageWand {
 		
 		ArrayList<Char> chars = new ArrayList<>();
 
+		Blob web = Dungeon.level.blobs.get(Web.class);
+
 		int terrainPassed = 2, terrainBonus = 0;
 		for (int c : beam.subPath(1, maxDistance)) {
 			
@@ -79,6 +83,11 @@ public class WandOfDisintegration extends DamageWand {
 				chars.add( ch );
 			}
 
+			if (Dungeon.level.solid[c]) {
+				terrainPassed++;
+				if (web != null) web.clear(c);
+			}
+
 			if (Dungeon.level.flamable[c]) {
 
 				Dungeon.level.destroy( c );
@@ -86,9 +95,6 @@ public class WandOfDisintegration extends DamageWand {
 				terrainAffected = true;
 				
 			}
-
-			if (Dungeon.level.solid[c])
-				terrainPassed++;
 			
 			CellEmitter.center( c ).burst( PurpleParticle.BURST, Random.IntRange( 1, 2 ) );
 		}