From f0fdfb48b93576093bea2bc490b000a8ca4e3a2e Mon Sep 17 00:00:00 2001
From: UnreallyHard <UnreallyHard@gmail.com>
Date: Sat, 24 Feb 2024 01:33:29 +0700
Subject: [PATCH] Added Multiplayer Things Spawn Type option

Option is available for all netgame types
Added Multiplayer Things Spawn Type to setup menu
Added Cmd Line Parameter mpspawntype
Added netgame functionality
Added mobjtype_weapons array is_weapon function
Closes https://github.com/fabiangreffrath/crispy-doom/issues/681
---
 src/doom/d_main.c       | 16 +++++++++++++++
 src/doom/d_net.c        |  2 ++
 src/doom/doomstat.h     |  4 ++--
 src/doom/p_mobj.c       | 43 ++++++++++++++++++++++++++++++++++++++++-
 src/net_defs.h          | 13 ++++++++++++-
 src/net_structrw.c      |  4 +++-
 src/setup/multiplayer.c | 34 ++++++++++++++++++++++++++++++++
 7 files changed, 111 insertions(+), 5 deletions(-)

diff --git a/src/doom/d_main.c b/src/doom/d_main.c
index 736a1fee1f..8cfa332a50 100644
--- a/src/doom/d_main.c
+++ b/src/doom/d_main.c
@@ -108,6 +108,7 @@ boolean         nomonsters;	// checkparm of -nomonsters
 boolean         respawnparm;	// checkparm of -respawn
 boolean         fastparm;	// checkparm of -fast
 boolean         coop_spawns = false;	// [crispy] checkparm of -coop_spawns
+int             mp_things_spawn_type; // [crispy] checkparm of -mpspawntype
 
 
 
@@ -1577,6 +1578,21 @@ void D_DoomMain (void)
     if (M_CheckParm ("-dm3"))
 	deathmatch = 3;
 
+    //! 
+    // @arg <n>
+    // @category net
+    // [crispy]
+    // Types of multiplayer things to be spawned in a netgame
+    //
+
+    p = M_CheckParmWithArgs("-mpspawntype", 1);
+    mp_things_spawn_type = atoi(myargv[p+1]);
+
+    if (mp_things_spawn_type > MP_THINGS_SPAWN_TYPES_NUM || mp_things_spawn_type < 0)
+    {
+        mp_things_spawn_type = 0;
+    }
+    
     if (devparm)
 	DEH_printf(D_DEVSTR);
     
diff --git a/src/doom/d_net.c b/src/doom/d_net.c
index 4da8004aa3..8cba767554 100644
--- a/src/doom/d_net.c
+++ b/src/doom/d_net.c
@@ -119,6 +119,7 @@ static void LoadGameSettings(net_gamesettings_t *settings)
     respawnparm = settings->respawn_monsters;
     timelimit = settings->timelimit;
     consoleplayer = settings->consoleplayer;
+    mp_things_spawn_type = settings->mp_things_spawn_type; // [crispy]
 
     if (lowres_turn)
     {
@@ -150,6 +151,7 @@ static void SaveGameSettings(net_gamesettings_t *settings)
     settings->fast_monsters = fastparm;
     settings->respawn_monsters = respawnparm;
     settings->timelimit = timelimit;
+    settings->mp_things_spawn_type = mp_things_spawn_type; // [crispy]
 
     settings->lowres_turn = (M_ParmExists("-record")
                          && !M_ParmExists("-longtics"))
diff --git a/src/doom/doomstat.h b/src/doom/doomstat.h
index 262e6c4e59..80089ec264 100644
--- a/src/doom/doomstat.h
+++ b/src/doom/doomstat.h
@@ -48,7 +48,7 @@ extern  boolean	nomonsters;	// checkparm of -nomonsters
 extern  boolean	respawnparm;	// checkparm of -respawn
 extern  boolean	fastparm;	// checkparm of -fast
 extern  boolean	coop_spawns;	// [crispy] checkparm of -coop_spawns
-
+extern  int     mp_things_spawn_type; // [crispy] checkparm of -mpspawntype
 extern  boolean	devparm;	// DEBUG: launched with -devparm
 
 
@@ -103,7 +103,7 @@ extern  boolean         respawnmonsters;
 // Netgame? Only true if >1 player.
 extern  boolean	netgame;
 
-// 0=Cooperative; 1=Deathmatch; 2=Altdeath
+// 0=Cooperative; 1=Deathmatch; 2=Altdeath; 3=dm3;
 extern int deathmatch;
 
 // -------------------------
diff --git a/src/doom/p_mobj.c b/src/doom/p_mobj.c
index e3348d3398..74a61b0b11 100644
--- a/src/doom/p_mobj.c
+++ b/src/doom/p_mobj.c
@@ -41,6 +41,27 @@
 void G_PlayerReborn (int player);
 void P_SpawnMapThing (mapthing_t*	mthing);
 
+// [crispy] mobjtype weapons
+const mobjtype_t mobjtype_weapons[] = {
+    MT_MISC25,
+    MT_CHAINGUN,
+    MT_MISC26,
+    MT_MISC27,
+    MT_MISC28,
+    MT_SHOTGUN,
+    MT_SUPERSHOTGUN,
+};
+
+#define MOBJTYPE_WEAPONS_COUNT (sizeof(mobjtype_weapons) / sizeof(mobjtype_t))
+
+boolean is_weapon(mobjtype_t value) {
+    for (int i = 0; i < MOBJTYPE_WEAPONS_COUNT; ++i) {
+        if (value == mobjtype_weapons[i]) {
+            return true;
+        }
+    }
+    return false;
+}
 
 //
 // P_SetMobjState
@@ -1006,7 +1027,13 @@ void P_SpawnMapThing (mapthing_t* mthing)
     // check for appropriate skill level
     if (!coop_spawns && !netgame && (mthing->options & 16) )
 	return;
-		
+
+    // [crispy] Don't spawn mp-only things in the netgame
+    if (netgame && (mthing->options & 16) && mp_things_spawn_type == MP_THINGS_SPAWN_NONE)
+    {
+        return;
+    }
+
     if (gameskill == sk_baby)
 	bit = 1;
     else if (gameskill == sk_nightmare)
@@ -1057,6 +1084,20 @@ void P_SpawnMapThing (mapthing_t* mthing)
 	return;
     }
     
+    if (netgame && (mthing->options & 16))
+    {
+        // [crispy] Don't spawn any mp-only things except monsters in the netgame
+        if (mp_things_spawn_type == MP_THINGS_SPAWN_ONLY_MONSTERS && !(i == MT_SKULL || (mobjinfo[i].flags & MF_COUNTKILL)))
+        {
+            return;
+        }
+        // [crispy] Don't spawn mp-only weapons in the netgame
+        if (mp_things_spawn_type == MP_THINGS_SPAWN_ALL_BUT_WEAPONS && is_weapon(i))
+        {
+            return;
+        }
+    }
+
     // spawn it
     x = mthing->x << FRACBITS;
     y = mthing->y << FRACBITS;
diff --git a/src/net_defs.h b/src/net_defs.h
index ab852c08ee..47fa5249df 100644
--- a/src/net_defs.h
+++ b/src/net_defs.h
@@ -169,6 +169,16 @@ typedef enum
     NET_MASTER_PACKET_TYPE_NAT_HOLE_PUNCH_ALL,
 } net_master_packet_type_t;
 
+typedef enum // [crispy]
+{
+    MP_THINGS_SPAWN_ALL,
+    MP_THINGS_SPAWN_ALL_BUT_WEAPONS,
+    MP_THINGS_SPAWN_ONLY_MONSTERS,
+    MP_THINGS_SPAWN_NONE,
+
+    MP_THINGS_SPAWN_TYPES_NUM,
+} net_mp_things_spawn_t;
+
 // Settings specified when the client connects to the server.
 
 typedef struct
@@ -210,7 +220,8 @@ typedef struct
 
     int num_players;
     int consoleplayer;
-
+    int mp_things_spawn_type; // [crispy]
+    
     // Hexen player classes:
 
     int player_classes[NET_MAXPLAYERS];
diff --git a/src/net_structrw.c b/src/net_structrw.c
index 2dbd2740a1..878990249f 100644
--- a/src/net_structrw.c
+++ b/src/net_structrw.c
@@ -81,6 +81,7 @@ void NET_WriteSettings(net_packet_t *packet, net_gamesettings_t *settings)
     NET_WriteInt8(packet, settings->random);
     NET_WriteInt8(packet, settings->num_players);
     NET_WriteInt8(packet, settings->consoleplayer);
+    NET_WriteInt8(packet, settings->mp_things_spawn_type); // [crispy]
 
     for (i = 0; i < settings->num_players; ++i)
     {
@@ -109,7 +110,8 @@ boolean NET_ReadSettings(net_packet_t *packet, net_gamesettings_t *settings)
            && NET_ReadSInt8(packet, (signed int *) &settings->loadgame)
            && NET_ReadInt8(packet, (unsigned int *) &settings->random)
            && NET_ReadInt8(packet, (unsigned int *) &settings->num_players)
-           && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer);
+           && NET_ReadSInt8(packet, (signed int *) &settings->consoleplayer)
+           && NET_ReadSInt8(packet, (signed int *) &settings->mp_things_spawn_type); // [crispy]
 
     if (!success)
     {
diff --git a/src/setup/multiplayer.c b/src/setup/multiplayer.c
index 24517519db..d8af870e43 100644
--- a/src/setup/multiplayer.c
+++ b/src/setup/multiplayer.c
@@ -133,6 +133,7 @@ static int deathmatch = 0;
 static int strife_altdeath = 0;
 static int fast = 0;
 static int respawn = 0;
+static int mp_things_spawn_type = 0;  // [crispy]
 static int udpport = 2342;
 static int timer = 0;
 static int privateserver = 0;
@@ -282,6 +283,11 @@ static void StartGame(int multiplayer)
         {
             AddCmdLineParameter(exec, "-privateserver");
         }
+
+        if (mp_things_spawn_type) // [crispy]
+        {   
+            AddCmdLineParameter(exec, "-mpspawntype %i", mp_things_spawn_type);
+        }
     }
 
     AddWADs(exec);
@@ -708,6 +714,25 @@ static txt_dropdown_list_t *GameTypeDropdown(void)
     }
 }
 
+static void MultiplayerFlags(void) // [crispy]
+{
+    txt_window_t *window;
+
+    // Build the window
+    window = TXT_NewWindow("Multiplayer Flags");
+    TXT_SetColumnWidths(window, 40);
+    TXT_SetWindowPosition(window, TXT_HORIZ_CENTER, TXT_VERT_TOP, TXT_SCREEN_W / 2, 3);
+
+    TXT_AddWidgets(window,
+        TXT_NewSeparator("Multiplayer Things Spawn Type"),
+        TXT_NewRadioButton("All", &mp_things_spawn_type, MP_THINGS_SPAWN_ALL),
+        TXT_NewRadioButton("All except weapons", &mp_things_spawn_type, MP_THINGS_SPAWN_ALL_BUT_WEAPONS),
+        TXT_NewRadioButton("Only monsters", &mp_things_spawn_type, MP_THINGS_SPAWN_ONLY_MONSTERS),
+        TXT_NewRadioButton("None", &mp_things_spawn_type, MP_THINGS_SPAWN_NONE),
+        NULL
+    );
+}
+
 // "Start game" menu.  This is used for the start server window
 // and the single player warp menu.  The parameters specify
 // the window title and whether to display multiplayer options.
@@ -769,6 +794,15 @@ static void StartGameMenu(const char *window_title, int multiplayer)
                                TXT_NewLabel("minutes"),
                                NULL),
                NULL);
+        if (gamemission == doom)  // [crispy] Multiplayer Flags
+        {
+            TXT_AddWidgets(window,
+                TXT_NewLabel("Flags"),
+                TXT_NewButton2("Set",
+                    (TxtWidgetSignalFunc) MultiplayerFlags, NULL),
+                NULL
+            );
+        }
     }
 
     TXT_AddWidgets(window,