diff --git a/src/dehacked.c b/src/dehacked.c
index 8334de61a86bd25a60998b0a0c258cd8e06000e7..4bc119a19715f2fc62c02303c7d6fb336c064a28 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -2325,6 +2325,7 @@ static actionpointer_t actionpointers[] =
 	{{A_Boss1Spikeballs},        "A_BOSS1SPIKEBALLS"},
 	{{A_Boss3TakeDamage},        "A_BOSS3TAKEDAMAGE"},
 	{{A_Boss3Path},              "A_BOSS3PATH"},
+	{{A_Boss3ShockThink},        "A_BOSS3SHOCKTHINK"},
 	{{A_LinedefExecute},         "A_LINEDEFEXECUTE"},
 	{{A_PlaySeeSound},           "A_PLAYSEESOUND"},
 	{{A_PlayAttackSound},        "A_PLAYATTACKSOUND"},
@@ -4742,6 +4743,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_BOSSSEBH1",
 	"S_BOSSSEBH2",
 
+	// Boss 3 Shockwave
+
+	"S_SHOCKWAVE1",
+	"S_SHOCKWAVE2",
+
 	// Boss 4
 	"S_EGGMOBILE4_STND",
 	"S_EGGMOBILE4_LATK1",
@@ -7253,7 +7259,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	// Boss 3
 	"MT_EGGMOBILE3",
 	"MT_FAKEMOBILE",
-	"MT_SHOCK",
+	"MT_SHOCKWAVE",
 
 	// Boss 4
 	"MT_EGGMOBILE4",
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 1de20cad7a343a9311458aadbbaa5235e3f92a50..ecd741849a1762522c9894d924fd2e553b92f03a 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -197,6 +197,7 @@ light_t *t_lspr[NUMSPRITES] =
 	&lspr[NOLIGHT],     // SPR_EGGO
 	&lspr[NOLIGHT],     // SPR_SEBH
 	&lspr[NOLIGHT],     // SPR_FAKE
+	&lspr[NOLIGHT],     // SPR_SHCK
 
 	// Boss 4 (Castle Eggman)
 	&lspr[NOLIGHT],     // SPR_EGGP
diff --git a/src/info.c b/src/info.c
index 9da1dcc8c91bd99389d3004912c2e407205e4f15..c20568aee658780017c56e1258bd95cb030e8fb8 100644
--- a/src/info.c
+++ b/src/info.c
@@ -85,6 +85,7 @@ char sprnames[NUMSPRITES + 1][5] =
 	"EGGO", // Boss 3
 	"SEBH", // Boss 3 Junk
 	"FAKE", // Boss 3 Fakemobile
+	"SHCK", // Boss 3 Shockwave
 
 	// Boss 4 (Castle Eggman)
 	"EGGP",
@@ -1306,6 +1307,11 @@ state_t states[NUMSTATES] =
 	{SPR_SEBH, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH1
 	{SPR_SEBH, 1, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSEBH2
 
+	// Boss 3 Shockwave
+
+	{SPR_SHCK,   FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, 8, {A_Boss3ShockThink}, 4, 2, S_SHOCKWAVE2}, // S_SHOCKWAVE1
+	{SPR_SHCK, 3|FF_FULLBRIGHT|FF_PAPERSPRITE|FF_ANIMATE, 8, {A_Boss3ShockThink}, 4, 2, S_SHOCKWAVE1}, // S_SHOCKWAVE2
+
 	// Boss 4
 	{SPR_EGGP, 0, -1, {NULL},           0,          0, S_NULL},              // S_EGGMOBILE4_STND
 	{SPR_EGGP, 1,  3, {NULL},           0,          0, S_EGGMOBILE4_LATK2},  // S_EGGMOBILE4_LATK1
@@ -5461,30 +5467,30 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL              // raisestate
 	},
 
-	{           // MT_SHOCK
+	{           // MT_SHOCKWAVE
 		-1,             // doomednum
-		S_THUNDERCOIN_SPARK, // spawnstate
+		S_SHOCKWAVE1,   // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_None,       // seesound
+		sfx_s3k5e,      // seesound
 		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
-		0,              // painchance
+		8*TICRATE,      // painchance
 		sfx_None,       // painsound
 		S_NULL,         // meleestate
 		S_NULL,         // missilestate
 		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
 		sfx_None,       // deathsound
-		10*FRACUNIT,    // speed
-		16*FRACUNIT,    // radius
-		35*FRACUNIT,    // height
+		12*FRACUNIT,    // speed
+		48*FRACUNIT,    // radius
+		8*FRACUNIT,     // height
 		0,              // display offset
 		DMG_ELECTRIC|(sfx_buzz2<<8), // mass
-		20,             // damage
+		3,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY, // flags
+		MF_NOBLOCKMAP|MF_MISSILE|MF_PAIN|MF_NOGRAVITY|MF_PAPERCOLLISION, // flags
 		S_NULL          // raisestate
 	},
 
diff --git a/src/info.h b/src/info.h
index 74e4b87a26576a3bc1f68dcd2efbc4507b635488..3ff3c3715895cd9b072d7c64ea7dce8032ecfad6 100644
--- a/src/info.h
+++ b/src/info.h
@@ -138,6 +138,7 @@ void A_SetReactionTime();
 void A_Boss1Spikeballs();
 void A_Boss3TakeDamage();
 void A_Boss3Path();
+void A_Boss3ShockThink();
 void A_LinedefExecute();
 void A_PlaySeeSound();
 void A_PlayAttackSound();
@@ -331,6 +332,7 @@ typedef enum sprite
 	SPR_EGGO, // Boss 3
 	SPR_SEBH, // Boss 3 Junk
 	SPR_FAKE, // Boss 3 Fakemobile
+	SPR_SHCK, // Boss 3 Shockwave
 
 	// Boss 4 (Castle Eggman)
 	SPR_EGGP,
@@ -1453,6 +1455,10 @@ typedef enum state
 	S_BOSSSEBH1,
 	S_BOSSSEBH2,
 
+	// Boss 3 Shockwave
+	S_SHOCKWAVE1,
+	S_SHOCKWAVE2,
+
 	// Boss 4
 	S_EGGMOBILE4_STND,
 	S_EGGMOBILE4_LATK1,
@@ -3986,7 +3992,7 @@ typedef enum mobj_type
 	// Boss 3
 	MT_EGGMOBILE3,
 	MT_FAKEMOBILE,
-	MT_SHOCK,
+	MT_SHOCKWAVE,
 
 	// Boss 4
 	MT_EGGMOBILE4,
@@ -4195,7 +4201,7 @@ typedef enum mobj_type
 	MT_SEAWEED, // DSZ Seaweed
 	MT_WATERDRIP, // Dripping Water source
 	MT_WATERDROP, // Water drop from dripping water
-	MT_CORAL1, // Coral 
+	MT_CORAL1, // Coral
 	MT_CORAL2,
 	MT_CORAL3,
 	MT_CORAL4,
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 4dd077ce59286786884f23462ed308a18e43c53f..bc43d0fa2f9f6ab1dc91035aa07e00404ac34991 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -170,6 +170,7 @@ void A_SetReactionTime(mobj_t *actor);
 void A_Boss1Spikeballs(mobj_t *actor);
 void A_Boss3TakeDamage(mobj_t *actor);
 void A_Boss3Path(mobj_t *actor);
+void A_Boss3ShockThink(mobj_t *actor);
 void A_LinedefExecute(mobj_t *actor);
 void A_PlaySeeSound(mobj_t *actor);
 void A_PlayAttackSound(mobj_t *actor);
@@ -8079,6 +8080,57 @@ void A_Boss3Path(mobj_t *actor)
 	}
 }
 
+// Function: A_Boss3ShockThink
+//
+// Description: Inserts new interstitial shockwave objects when the space between others spreads too much.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_Boss3ShockThink(mobj_t *actor)
+{
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_Boss3ShockThink", actor))
+		return;
+#endif
+
+	if (actor->momx || actor->momy)
+		actor->angle = R_PointToAngle2(0, 0, actor->momx, actor->momy) + ANGLE_90;
+
+	if (actor->hnext && !P_MobjWasRemoved(actor->hnext))
+	{
+		mobj_t *snext = actor->hnext;
+		mobj_t *snew;
+		fixed_t x0, y0, x1, y1;
+
+		// Break the link if movements are too different
+		if (FixedHypot(snext->momx - actor->momx, snext->momy - actor->momy) > 12*actor->scale)
+		{
+			P_SetTarget(&actor->hnext, NULL);
+			return;
+		}
+
+		// Check distance between shockwave objects to determine whether interstitial ones should be spawned
+		x0 = actor->x;
+		y0 = actor->y;
+		x1 = snext->x;
+		y1 = snext->y;
+		if (FixedHypot(x1 - x0, y1 - y0) > 2*actor->radius)
+		{
+			snew = P_SpawnMobj((x0 + x1) >> 1, (y0 + y1) >> 1, (actor->z + snext->z) >> 1, actor->type);
+			snew->momx = (actor->momx + snext->momx) >> 1;
+			snew->momy = (actor->momy + snext->momy) >> 1;
+			snew->momz = (actor->momz + snext->momz) >> 1; // is this really needed?
+			snew->angle = (actor->angle + snext->angle) >> 1;
+			P_SetTarget(&snew->target, actor->target);
+			snew->fuse = actor->fuse;
+
+			P_SetTarget(&actor->hnext, snew);
+			P_SetTarget(&snew->hnext, snext);
+		}
+	}
+}
+
 // Function: A_LinedefExecute
 //
 // Description: Object's location is used to set the calling sector. The tag used is var1. Optionally, if var2 is set, the actor's angle (multiplied by var2) is added to the tag number as well.
diff --git a/src/p_mobj.c b/src/p_mobj.c
index ce97024bafb8e6e0a6e41c4b70110e19ebaeede5..043ff2153687170bc7268f27050b323064c26cc4 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -4605,22 +4605,37 @@ static void P_Boss3Thinker(mobj_t *mobj)
 
 			if (!mobj->movefactor) // to firing mode
 			{
-				UINT8 i;
-				angle_t ang = 0;
+				UINT8 i, numtospawn = 24;
+				angle_t ang = 0, interval = FixedAngle((360 << FRACBITS) / numtospawn);
+				mobj_t *shock, *sfirst, *sprev = NULL;
 
 				mobj->movecount = mobj->health+1;
 				mobj->movefactor = -512*FRACUNIT;
 
 				// shock the water!
-				for (i = 0; i < 64; i++)
+				for (i = 0; i < numtospawn; i++)
 				{
-					mobj_t *shock = P_SpawnMobjFromMobj(mobj, 0, 0, 4*FRACUNIT, MT_SHOCK);
+					shock = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_SHOCKWAVE);
 					P_SetTarget(&shock->target, mobj);
-					P_InstaThrust(shock, ang, shock->info->speed);
-					P_CheckMissileSpawn(shock);
-					ang += (ANGLE_MAX/64);
+					shock->fuse = shock->info->painchance;
+
+					if (i % 2 == 0)
+					P_SetMobjState(shock, shock->state->nextstate);
+
+					if (!sprev)
+						sfirst = shock;
+					else
+					{
+						if (i == numtospawn - 1)
+							P_SetTarget(&shock->hnext, sfirst);
+						P_SetTarget(&sprev->hnext, shock);
+					}
+
+					P_Thrust(shock, ang, shock->info->speed);
+					ang += interval;
+					sprev = shock;
 				}
-				S_StartSound(mobj, sfx_fizzle);
+				S_StartSound(mobj, shock->info->seesound);
 
 				// look for a new target
 				P_BossTargetPlayer(mobj, false);
diff --git a/src/sounds.c b/src/sounds.c
index 43225a6157a6b6a9d4f5890505c5fbe8a27435dc..3c8cfa44f2a0fe287ec88d9a1b66263b435ddeac 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -484,7 +484,7 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"s3k5b",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Menu beep"},
   {"s3k5c",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Electric spark"},
   {"s3k5d",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Heavy hit"},
-  {"s3k5e",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Firing laser"},
+  {"s3k5e",  false, 127,  8, -1, NULL, 0,        -1,  -1, LUMPERROR, "Releasing charge"},
   {"s3k5f",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Crusher stomp"},
   {"s3k60",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Accelerating"},
   {"s3k61",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR, "Drilling"},