From 6dfef794a49a03f024f133ed0b61c805c6e660f0 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sun, 12 Feb 2017 01:28:12 +0000
Subject: [PATCH 1/5] Metal Sonic's bounce attack is much nicer. Also, a bunch
 of other minor tweaks.

---
 src/info.c   |  6 ++---
 src/p_mobj.c | 72 ++++++++++++++++++++++++++++++++--------------------
 2 files changed, 48 insertions(+), 30 deletions(-)

diff --git a/src/info.c b/src/info.c
index fdea617c1..705cb17d8 100644
--- a/src/info.c
+++ b/src/info.c
@@ -1372,11 +1372,11 @@ state_t states[NUMSTATES] =
 	{SPR_METL, 13, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_BLOCK
 	{SPR_METL, 13, 40, {NULL},         0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE
 	{SPR_METL, 14, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_GATHER
-	{SPR_METL,  9, -1, {NULL},         0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH
+	{SPR_METL, 15, -1, {NULL},         0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH
 	{SPR_METL, 14, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_BOUNCE
 	{SPR_METL, 13, -1, {NULL},         0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT
 	{SPR_METL, 11, 40, {A_Pain},       0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN
-	{SPR_METL,  0, -1, {A_BossDeath},  0, 0, S_NULL},             // S_METALSONIC_DEATH
+	{SPR_METL, 11, -1, {A_BossDeath},  0, 0, S_NULL},             // S_METALSONIC_DEATH
 	{SPR_METL,  3,  4, {NULL},         0, 0, S_METALSONIC_FLEE2}, // S_METALSONIC_FLEE1
 	{SPR_METL,  4,  4, {A_BossScream}, 0, 0, S_METALSONIC_FLEE3}, // S_METALSONIC_FLEE2
 	{SPR_METL,  5,  4, {NULL},         0, 0, S_METALSONIC_FLEE4}, // S_METALSONIC_FLEE3
@@ -4961,7 +4961,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		16*FRACUNIT,        // radius
 		48*FRACUNIT,        // height
 		0,                  // display offset
-		sfx_mspogo,         // mass
+		sfx_s3k5a,          // mass
 		3,                  // damage
 		sfx_mswarp,         // activesound
 		MF_NOGRAVITY|MF_BOSS|MF_SLIDEME, // flags
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 39682a7ef..aaae5efd0 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -5641,6 +5641,15 @@ static void P_Boss7Thinker(mobj_t *mobj)
 
 }
 
+#define vectorise mobj->movedir = ANGLE_11hh - FixedAngle(FixedMul(AngleFixed(ANGLE_11hh), FixedDiv((mobj->info->spawnhealth - mobj->health)<<FRACBITS, (mobj->info->spawnhealth-1)<<FRACBITS)));\
+				if (P_RandomChance(FRACUNIT/2))\
+					mobj->movedir = InvAngle(mobj->movedir);\
+				mobj->threshold = 6 + (FixedMul(24<<FRACBITS, FixedDiv((mobj->info->spawnhealth - mobj->health)<<FRACBITS, (mobj->info->spawnhealth-1)<<FRACBITS))>>FRACBITS);\
+				if (mobj->info->activesound)\
+					S_StartSound(mobj, mobj->info->activesound);\
+				if (mobj->info->painchance)\
+					P_SetMobjState(mobj, mobj->info->painchance)
+
 // Metal Sonic battle boss
 // You CAN put multiple Metal Sonics in a single map
 // because I am a totally competent programmer who can do shit right.
@@ -5745,7 +5754,8 @@ static void P_Boss9Thinker(mobj_t *mobj)
 			}
 			if (spawner) {
 				mobj_t *missile = P_SpawnMissile(spawner, mobj, MT_MSGATHER);
-				missile->momz = FixedDiv(missile->momz, 7*FRACUNIT/4);
+				if (mobj->health > mobj->info->damage)
+					missile->momz = FixedDiv(missile->momz, 7*FRACUNIT/5);
 				if (dist == 0)
 					missile->fuse = 0;
 				else
@@ -5757,16 +5767,22 @@ static void P_Boss9Thinker(mobj_t *mobj)
 
 		// Pre-threshold reactiontime stuff for attack phases
 		if (mobj->reactiontime && mobj->movecount == 3) {
+			mobj->reactiontime--;
+
 			if (mobj->movedir == 0 || mobj->movedir == 2) { // Pausing between bounces in the pinball phase
 				if (mobj->target->player->powers[pw_tailsfly]) // Trying to escape, eh?
 					mobj->watertop = mobj->target->z + mobj->target->momz*6; // Readjust your aim. >:3
 				else
 					mobj->watertop = mobj->target->floorz + 16*FRACUNIT;
-				if (!(mobj->threshold%4))
+
+				if (!(mobj->threshold%4)) {
 					mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
+					if (!mobj->reactiontime)
+						S_StartSound(mobj, sfx_zoom); // zoom!
+				}
 			}
-			// Pausing between energy ball shots
-			mobj->reactiontime--;
+			// else -- Pausing between energy ball shots
+
 			return;
 		}
 
@@ -5838,20 +5854,28 @@ static void P_Boss9Thinker(mobj_t *mobj)
 					P_InstaThrust(mobj, mobj->angle, 30*FRACUNIT);
 				if (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true)) { // Hit a wall? Find a direction to bounce
 					mobj->threshold--;
-					if (mobj->threshold) {
-						P_SetMobjState(mobj, mobj->state->nextstate);
-						if (mobj->info->mass)
-							S_StartSound(mobj, mobj->info->mass);
-						if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce.
-							mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
-							mobj->reactiontime = TICRATE; // targetting time
-						} else { // No homing, just use P_BounceMove
-							P_BounceMove(mobj);
-							mobj->angle = R_PointToAngle2(0,0,mobj->momx,mobj->momy);
-							mobj->reactiontime = TICRATE/4; // just a pause before you bounce away
-						}
-						mobj->momx = mobj->momy = 0;
+					P_SetMobjState(mobj, mobj->state->nextstate);
+					if (!mobj->threshold) { // failed bounce!
+						S_StartSound(mobj, sfx_mspogo);
+						P_BounceMove(mobj);
+						mobj->angle = R_PointToAngle2(0,0,mobj->momx, mobj->momy);
+						P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true);
+						mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
+						mobj->flags &= ~MF_PAIN;
+						mobj->fuse = 10*TICRATE;
+						mobj->movecount = 0;
+						vectorise;
+					} else if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce.
+						S_StartSound(mobj, sfx_s3k5a);
+						mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);
+						mobj->reactiontime = TICRATE - 5*(mobj->info->damage - mobj->health); // targetting time
+					} else { // No homing, just use P_BounceMove
+						S_StartSound(mobj, sfx_s3kaa); // make the bounces distinct...
+						P_BounceMove(mobj);
+						mobj->angle = R_PointToAngle2(0,0,mobj->momx,mobj->momy);
+						mobj->reactiontime = 1; // TICRATE/4; // just a pause before you bounce away
 					}
+					mobj->momx = mobj->momy = 0;
 				}
 				return;
 			}
@@ -5978,9 +6002,9 @@ static void P_Boss9Thinker(mobj_t *mobj)
 						S_StartSound(mobj, mobj->info->seesound);
 					P_SetMobjState(mobj, mobj->info->seestate);
 					if (mobj->movedir == 2)
-						mobj->threshold = 16; // bounce 16 times
+						mobj->threshold = 12; // bounce 12 times
 					else
-						mobj->threshold = 32; // bounce 32 times
+						mobj->threshold = 24; // bounce 24 times
 					mobj->watertop = mobj->target->floorz + 16*FRACUNIT;
 					P_LinedefExecute(LE_PINCHPHASE, mobj, NULL);
 				} else {
@@ -6063,14 +6087,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
 			if (danger) {
 				// An incoming attack is detected! What should we do?!
 				// Go into vector form!
-				mobj->movedir = ANGLE_11hh - FixedAngle(FixedMul(AngleFixed(ANGLE_11hh), FixedDiv((mobj->info->spawnhealth - mobj->health)<<FRACBITS, (mobj->info->spawnhealth-1)<<FRACBITS)));
-				if (P_RandomChance(FRACUNIT/2))
-					mobj->movedir = InvAngle(mobj->movedir);
-				mobj->threshold = 6 + (FixedMul(24<<FRACBITS, FixedDiv((mobj->info->spawnhealth - mobj->health)<<FRACBITS, (mobj->info->spawnhealth-1)<<FRACBITS))>>FRACBITS);
-				if (mobj->info->activesound)
-					S_StartSound(mobj, mobj->info->activesound);
-				if (mobj->info->painchance)
-					P_SetMobjState(mobj, mobj->info->painchance);
+				vectorise;
 				return;
 			}
 
@@ -6085,6 +6102,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
 		}
 	}
 }
+#undef vectorise
 
 //
 // P_GetClosestAxis

From 5f3cfc67294d006e29a8665a93dc7db7c61b3a60 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sun, 12 Feb 2017 14:16:52 +0000
Subject: [PATCH 2/5] Some cleanup.

---
 src/p_mobj.c | 10 +++++-----
 1 file changed, 5 insertions(+), 5 deletions(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index aaae5efd0..65be4d78d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -5858,11 +5858,10 @@ static void P_Boss9Thinker(mobj_t *mobj)
 					if (!mobj->threshold) { // failed bounce!
 						S_StartSound(mobj, sfx_mspogo);
 						P_BounceMove(mobj);
-						mobj->angle = R_PointToAngle2(0,0,mobj->momx, mobj->momy);
-						P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true);
+						mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
 						mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
 						mobj->flags &= ~MF_PAIN;
-						mobj->fuse = 10*TICRATE;
+						mobj->fuse = 0;
 						mobj->movecount = 0;
 						vectorise;
 					} else if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce.
@@ -5884,8 +5883,9 @@ static void P_Boss9Thinker(mobj_t *mobj)
 			mobj->angle += mobj->movedir;
 			P_InstaThrust(mobj, mobj->angle, -speed);
 			while (!P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true) && tries++ < 16) {
-				mobj->angle += mobj->movedir;
-				P_InstaThrust(mobj, mobj->angle, -speed);
+				S_StartSound(mobj, sfx_mspogo);
+				P_BounceMove(mobj);
+				mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
 			}
 			mobj->momx = mobj->momy = 0;
 			mobj->threshold--;

From 3feac5bc90a1fc9a14c8e91131a4f6138af15098 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Mon, 13 Feb 2017 18:04:08 +0000
Subject: [PATCH 3/5] bad bounce
 https://gfycat.com/DeliciousReliableGentoopenguin

---
 src/dehacked.c |  2 +-
 src/info.c     |  4 ++--
 src/info.h     |  2 +-
 src/p_mobj.c   | 23 ++++++++++++++++++++---
 4 files changed, 24 insertions(+), 7 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index c71c55ac1..748a3dc69 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -4772,11 +4772,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_METALSONIC_FLOAT",
 	"S_METALSONIC_VECTOR",
 	"S_METALSONIC_STUN",
-	"S_METALSONIC_BLOCK",
 	"S_METALSONIC_RAISE",
 	"S_METALSONIC_GATHER",
 	"S_METALSONIC_DASH",
 	"S_METALSONIC_BOUNCE",
+	"S_METALSONIC_BADBOUNCE",
 	"S_METALSONIC_SHOOT",
 	"S_METALSONIC_PAIN",
 	"S_METALSONIC_DEATH",
diff --git a/src/info.c b/src/info.c
index 705cb17d8..6b3019781 100644
--- a/src/info.c
+++ b/src/info.c
@@ -1369,11 +1369,11 @@ state_t states[NUMSTATES] =
 	{SPR_METL,  4, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_FLOAT
 	{SPR_METL, 12, -1, {NULL},         0, 0, S_METALSONIC_STUN},  // S_METALSONIC_VECTOR
 	{SPR_METL,  0, -1, {NULL},         0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_STUN
-	{SPR_METL, 13, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_BLOCK
 	{SPR_METL, 13, 40, {NULL},         0, 0, S_METALSONIC_GATHER},// S_METALSONIC_RAISE
 	{SPR_METL, 14, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_GATHER
 	{SPR_METL, 15, -1, {NULL},         0, 0, S_METALSONIC_BOUNCE},// S_METALSONIC_DASH
 	{SPR_METL, 14, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_BOUNCE
+	{SPR_METL, 16, -1, {NULL},         0, 0, S_NULL},             // S_METALSONIC_BADBOUNCE
 	{SPR_METL, 13, -1, {NULL},         0, 0, S_METALSONIC_GATHER},// S_METALSONIC_SHOOT
 	{SPR_METL, 11, 40, {A_Pain},       0, 0, S_METALSONIC_FLOAT}, // S_METALSONIC_PAIN
 	{SPR_METL, 11, -1, {A_BossDeath},  0, 0, S_NULL},             // S_METALSONIC_DEATH
@@ -4952,7 +4952,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_METALSONIC_PAIN,  // painstate
 		S_METALSONIC_VECTOR,// painchance
 		sfx_dmpain,         // painsound
-		S_METALSONIC_BLOCK, // meleestate
+		S_METALSONIC_BADBOUNCE, // meleestate
 		S_METALSONIC_SHOOT, // missilestate
 		S_METALSONIC_DEATH, // deathstate
 		S_METALSONIC_FLEE1, // xdeathstate
diff --git a/src/info.h b/src/info.h
index 1a6c14a70..0cca7896e 100644
--- a/src/info.h
+++ b/src/info.h
@@ -1575,11 +1575,11 @@ typedef enum state
 	S_METALSONIC_FLOAT,
 	S_METALSONIC_VECTOR,
 	S_METALSONIC_STUN,
-	S_METALSONIC_BLOCK,
 	S_METALSONIC_RAISE,
 	S_METALSONIC_GATHER,
 	S_METALSONIC_DASH,
 	S_METALSONIC_BOUNCE,
+	S_METALSONIC_BADBOUNCE,
 	S_METALSONIC_SHOOT,
 	S_METALSONIC_PAIN,
 	S_METALSONIC_DEATH,
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 65be4d78d..fb7ee5a43 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -5694,6 +5694,19 @@ static void P_Boss9Thinker(mobj_t *mobj)
 	if (mobj->health <= 0)
 		return;
 
+	if ((statenum_t)(mobj->state-states) == mobj->info->meleestate)
+	{
+		P_InstaThrust(mobj, mobj->angle, -4*FRACUNIT);
+		P_TryMove(mobj, mobj->x+mobj->momx, mobj->y+mobj->momy, true);
+		mobj->momz -= gravity;
+		if (mobj->z < mobj->watertop)
+		{
+			mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
+			P_SetMobjState(mobj, mobj->info->spawnstate);
+		}
+		return;
+	}
+
 	if ((!mobj->target || !(mobj->target->flags & MF_SHOOTABLE)))
 	{
 		P_BossTargetPlayer(mobj, false);
@@ -5711,6 +5724,10 @@ static void P_Boss9Thinker(mobj_t *mobj)
 		}
 		else if (!mobj->fuse)
 			mobj->fuse = 10*TICRATE;
+
+		// reset to flying so everything gets properly re-initialised
+		mobj->threshold = 0;
+		mobj->movecount = 0;
 	}
 
 	// AI goes here.
@@ -5859,11 +5876,11 @@ static void P_Boss9Thinker(mobj_t *mobj)
 						S_StartSound(mobj, sfx_mspogo);
 						P_BounceMove(mobj);
 						mobj->angle = R_PointToAngle2(mobj->momx, mobj->momy,0,0);
-						mobj->watertop = mobj->target->floorz + 32*FRACUNIT;
+						mobj->momz = 4*FRACUNIT;
 						mobj->flags &= ~MF_PAIN;
-						mobj->fuse = 0;
+						mobj->fuse = 10*TICRATE;
 						mobj->movecount = 0;
-						vectorise;
+						P_SetMobjState(mobj, mobj->info->meleestate);
 					} else if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce.
 						S_StartSound(mobj, sfx_s3k5a);
 						mobj->angle = R_PointToAngle2(mobj->x, mobj->y, mobj->target->x + mobj->target->momx*4, mobj->target->y + mobj->target->momy*4);

From a04ae45a93397fcd4d90726159b482ddacd1f4e6 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Mon, 13 Feb 2017 18:53:16 +0000
Subject: [PATCH 4/5] MORE CUES

---
 src/p_mobj.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index fb7ee5a43..33bcf0ce4 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -5880,6 +5880,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
 						mobj->flags &= ~MF_PAIN;
 						mobj->fuse = 10*TICRATE;
 						mobj->movecount = 0;
+						P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_CYBRAKDEMON_VILE_EXPLOSION);
 						P_SetMobjState(mobj, mobj->info->meleestate);
 					} else if (!(mobj->threshold%4)) { // We've decided to lock onto the player this bounce.
 						S_StartSound(mobj, sfx_s3k5a);

From 758bec6963226d0645da9dc01fc6b48333899e35 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Tue, 14 Feb 2017 02:07:08 +0000
Subject: [PATCH 5/5] Fixed a bunch of awkwardness where Metal wouldn't reset
 himself properly when his target died.

---
 src/p_mobj.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index 33bcf0ce4..0edc2c484 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -5709,25 +5709,30 @@ static void P_Boss9Thinker(mobj_t *mobj)
 
 	if ((!mobj->target || !(mobj->target->flags & MF_SHOOTABLE)))
 	{
+		if (mobj->tracer)
+			P_RemoveMobj(mobj->tracer);
 		P_BossTargetPlayer(mobj, false);
 		if (mobj->target && (!P_IsObjectOnGround(mobj->target) || mobj->target->player->pflags & PF_SPINNING))
 			P_SetTarget(&mobj->target, NULL); // Wait for them to hit the ground first
 		if (!mobj->target) // Still no target, aww.
 		{
 			// Reset the boss.
+			if (mobj->tracer)
+				P_RemoveMobj(mobj->tracer);
 			P_SetMobjState(mobj, mobj->info->spawnstate);
 			mobj->fuse = 0;
 			mobj->momx = FixedDiv(mobj->momx, FRACUNIT + (FRACUNIT>>2));
 			mobj->momy = FixedDiv(mobj->momy, FRACUNIT + (FRACUNIT>>2));
 			mobj->momz = FixedDiv(mobj->momz, FRACUNIT + (FRACUNIT>>2));
+			mobj->watertop = mobj->floorz + 32*FRACUNIT;
+			mobj->momz = (mobj->watertop - mobj->z)>>3;
+			mobj->threshold = 0;
+			mobj->movecount = 0;
+			mobj->flags = mobj->info->flags;
 			return;
 		}
 		else if (!mobj->fuse)
 			mobj->fuse = 10*TICRATE;
-
-		// reset to flying so everything gets properly re-initialised
-		mobj->threshold = 0;
-		mobj->movecount = 0;
 	}
 
 	// AI goes here.