diff --git a/src/dehacked.c b/src/dehacked.c
index 0cb7330d0d40306cf425ef5724c7d71c12710cc8..a826723069e4956567879cb7ee9eac252d6f859a 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -2421,6 +2421,7 @@ static actionpointer_t actionpointers[] =
 	{{A_SnapperThinker},         "A_SNAPPERTHINKER"},
 	{{A_SaloonDoorSpawn},        "A_SALOONDOORSPAWN"},
 	{{A_MinecartSparkThink},     "A_MINECARTSPARKTHINK"},
+	{{A_ModuloToState},          "A_MODULOTOSTATE"},
 	{{NULL},                     "NONE"},
 
 	// This NULL entry must be the last in the list
@@ -7136,19 +7137,6 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SPRK1",
 	"S_SPRK2",
 	"S_SPRK3",
-	"S_SPRK4",
-	"S_SPRK5",
-	"S_SPRK6",
-	"S_SPRK7",
-	"S_SPRK8",
-	"S_SPRK9",
-	"S_SPRK10",
-	"S_SPRK11",
-	"S_SPRK12",
-	"S_SPRK13",
-	"S_SPRK14",
-	"S_SPRK15",
-	"S_SPRK16",
 
 	// Robot Explosion
 	"S_XPLD_FLICKY",
diff --git a/src/info.c b/src/info.c
index d35be7b5814d4e5ed7c09de6ed8c6ce5f346c894..9aed563c7a4316ae707c4ddbb82194372977f6f8 100644
--- a/src/info.c
+++ b/src/info.c
@@ -3833,22 +3833,9 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, 105, {A_Scream}, 0, 0, S_NULL}, // S_CRUMBLE2
 
 	// Spark
-	{SPR_SPRK, FF_TRANS40  , 1, {NULL}, 0, 0, S_SPRK2},  // S_SPRK1
-	{SPR_SPRK, FF_TRANS50|1, 1, {NULL}, 0, 0, S_SPRK3},  // S_SPRK2
-	{SPR_SPRK, FF_TRANS50|2, 1, {NULL}, 0, 0, S_SPRK4},  // S_SPRK3
-	{SPR_SPRK, FF_TRANS50|3, 1, {NULL}, 0, 0, S_SPRK5},  // S_SPRK4
-	{SPR_SPRK, FF_TRANS60  , 1, {NULL}, 0, 0, S_SPRK6},  // S_SPRK5
-	{SPR_SPRK, FF_TRANS60|1, 1, {NULL}, 0, 0, S_SPRK7},  // S_SPRK6
-	{SPR_SPRK, FF_TRANS60|2, 1, {NULL}, 0, 0, S_SPRK8},  // S_SPRK7
-	{SPR_SPRK, FF_TRANS70|3, 1, {NULL}, 0, 0, S_SPRK9},  // S_SPRK8
-	{SPR_SPRK, FF_TRANS70  , 1, {NULL}, 0, 0, S_SPRK10}, // S_SPRK9
-	{SPR_SPRK, FF_TRANS70|1, 1, {NULL}, 0, 0, S_SPRK11}, // S_SPRK10
-	{SPR_SPRK, FF_TRANS80|2, 1, {NULL}, 0, 0, S_SPRK12}, // S_SPRK11
-	{SPR_SPRK, FF_TRANS80|3, 1, {NULL}, 0, 0, S_SPRK13}, // S_SPRK12
-	{SPR_SPRK, FF_TRANS80  , 1, {NULL}, 0, 0, S_SPRK14}, // S_SPRK13
-	{SPR_SPRK, FF_TRANS90|1, 1, {NULL}, 0, 0, S_SPRK15}, // S_SPRK14
-	{SPR_SPRK, FF_TRANS90|2, 1, {NULL}, 0, 0, S_SPRK16}, // S_SPRK15
-	{SPR_SPRK, FF_TRANS90|3, 1, {NULL}, 0, 0, S_NULL},   // S_SPRK16
+	{SPR_NULL, 0, 1, {A_ModuloToState}, 2, S_SPRK2, S_SPRK3},  // S_SPRK1
+	{SPR_SPRK, FF_TRANS20|FF_ANIMATE|0, 18, {NULL}, 8, 2, S_NULL},  // S_SPRK2
+	{SPR_SPRK, FF_TRANS20|FF_ANIMATE|9, 18, {NULL}, 8, 2, S_NULL},  // S_SPRK3
 
 	// Robot Explosion
 	{SPR_BOM1, 0, 0, {A_FlickySpawn}, 0, 0, S_XPLD1}, // S_XPLD_FLICKY
diff --git a/src/info.h b/src/info.h
index 45c1d79b27077a930dd4b50885f3c2ab3ae9954d..bb27ac3e3b299681495fbd4f8bfad07324be61e9 100644
--- a/src/info.h
+++ b/src/info.h
@@ -265,6 +265,7 @@ void A_SnapperSpawn();
 void A_SnapperThinker();
 void A_SaloonDoorSpawn();
 void A_MinecartSparkThink();
+void A_ModuloToState();
 
 // ratio of states to sprites to mobj types is roughly 6 : 1 : 1
 #define NUMMOBJFREESLOTS 512
@@ -3893,19 +3894,6 @@ typedef enum state
 	S_SPRK1,
 	S_SPRK2,
 	S_SPRK3,
-	S_SPRK4,
-	S_SPRK5,
-	S_SPRK6,
-	S_SPRK7,
-	S_SPRK8,
-	S_SPRK9,
-	S_SPRK10,
-	S_SPRK11,
-	S_SPRK12,
-	S_SPRK13,
-	S_SPRK14,
-	S_SPRK15,
-	S_SPRK16,
 
 	// Robot Explosion
 	S_XPLD_FLICKY,
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 18d6f20c0e9fd75748c6ff4bf654036512141082..4f046fe06e2b032043b6b2f20ec5af32f53e2807 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -37,6 +37,7 @@ boolean LUA_CallAction(const char *action, mobj_t *actor);
 player_t *stplyr;
 INT32 var1;
 INT32 var2;
+INT32 modulothing;
 
 //
 // P_NewChaseDir related LUT.
@@ -294,6 +295,8 @@ void A_SnapperSpawn(mobj_t *actor);
 void A_SnapperThinker(mobj_t *actor);
 void A_SaloonDoorSpawn(mobj_t *actor);
 void A_MinecartSparkThink(mobj_t *actor);
+void A_ModuloToState(mobj_t *actor);
+
 //for p_enemy.c
 
 //
@@ -13677,4 +13680,25 @@ void A_MinecartSparkThink(mobj_t *actor)
 		P_SetScale(trail, trail->scale/4);
 		trail->destscale = trail->scale;
 	}
+}
+
+// Function: A_ModuloToState
+//
+// Description: Modulo operation to state
+//
+// var1 = Modulo
+// var2 = State
+//
+void A_ModuloToState(mobj_t *actor)
+{
+	INT32 locvar1 = var1;
+	INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+	if (LUA_CallAction("A_ModuloToState", actor))
+		return;
+#endif
+
+	if ((modulothing % locvar1 == 0))
+		P_SetMobjState(actor, (locvar2));
+	modulothing++;
 }
\ No newline at end of file
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 77791f928165ac188ca5d6e3c952dbc39791dc56..a9d5244b044ad58840238d753e2e70184e2fc585 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -464,6 +464,8 @@ void P_SetScale(mobj_t *mobj, fixed_t newscale);
 void P_XYMovement(mobj_t *mo);
 void P_EmeraldManager(void);
 
+extern INT32 modulothing;
+
 #define MAXHUNTEMERALDS 64
 extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
 extern INT32 numhuntemeralds;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 24b68b97120ec81dec179beef0babe26ab473fb4..0bfb81f4e0ceaf4419501c30df4bd9b7e2f3ff5c 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -3999,6 +3999,7 @@ static void P_NetArchiveMisc(void)
 	WRITEINT32(save_p, sstimer);
 	WRITEUINT32(save_p, bluescore);
 	WRITEUINT32(save_p, redscore);
+	WRITEINT32(save_p, modulothing);
 
 	WRITEINT16(save_p, autobalance);
 	WRITEINT16(save_p, teamscramble);
@@ -4077,6 +4078,7 @@ static inline boolean P_NetUnArchiveMisc(void)
 	sstimer = READINT32(save_p);
 	bluescore = READUINT32(save_p);
 	redscore = READUINT32(save_p);
+	modulothing = READINT32(save_p);
 
 	autobalance = READINT16(save_p);
 	teamscramble = READINT16(save_p);
diff --git a/src/p_setup.c b/src/p_setup.c
index c2872a836f32f30f78379f71bf77d6c79989dd25..03e3e51ff7f7f7854bbc2a61ddc76f4fe72d3baf 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -2168,6 +2168,7 @@ static void P_LevelInitStuff(void)
 
 	localaiming = 0;
 	localaiming2 = 0;
+	modulothing = 0;
 
 	// special stage tokens, emeralds, and ring total
 	tokenbits = 0;