From abd9a645fea1415ae2d3d6c690576c97d54904d0 Mon Sep 17 00:00:00 2001
From: Lyberta <lyberta@lyberta.net>
Date: Sat, 30 Sep 2017 08:34:57 +0300
Subject: [PATCH] Refactoring item system.

---
 .../mutators/mutator/instagib/sv_instagib.qc  |  11 +-
 .../mutators/mutator/instagib/sv_instagib.qh  |   5 +
 .../mutator/random_items/sv_random_items.qc   |  30 ++-
 qcsrc/common/t_items.qc                       | 128 ----------
 qcsrc/common/t_items.qh                       |   7 -
 qcsrc/common/weapons/weapon/arc.qh            |   1 +
 qcsrc/common/weapons/weapon/blaster.qh        |   2 +
 qcsrc/common/weapons/weapon/crylink.qh        |   1 +
 qcsrc/common/weapons/weapon/devastator.qh     |   1 +
 qcsrc/common/weapons/weapon/electro.qh        |   1 +
 qcsrc/common/weapons/weapon/fireball.qh       |   1 +
 qcsrc/common/weapons/weapon/hagar.qh          |   4 +
 qcsrc/common/weapons/weapon/hlac.qh           |   4 +
 qcsrc/common/weapons/weapon/hook.qh           |   1 +
 qcsrc/common/weapons/weapon/machinegun.qh     |   4 +
 qcsrc/common/weapons/weapon/minelayer.qh      |   1 +
 qcsrc/common/weapons/weapon/mortar.qh         |   1 +
 qcsrc/common/weapons/weapon/porto.qh          |   1 +
 qcsrc/common/weapons/weapon/rifle.qh          |   1 +
 qcsrc/common/weapons/weapon/seeker.qh         |   1 +
 qcsrc/common/weapons/weapon/shockwave.qh      |   4 +
 qcsrc/common/weapons/weapon/shotgun.qh        |   4 +
 qcsrc/common/weapons/weapon/tuba.qh           |   4 +
 qcsrc/common/weapons/weapon/vaporizer.qh      |   1 +
 qcsrc/common/weapons/weapon/vortex.qh         |   2 +-
 qcsrc/server/items.qc                         | 229 +++++++++++++++---
 qcsrc/server/items.qh                         |  25 ++
 27 files changed, 298 insertions(+), 177 deletions(-)

diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
index 1561dc10db..a2735b5651 100644
--- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
+++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
@@ -377,7 +377,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
 
 void replace_with_insta_cells(entity item)
 {
-	entity e = spawn();
+	entity e = new(item_minst_cells);
 	setorigin(e, item.origin);
 	e.noalign = item.noalign;
 	e.cnt = item.cnt;
@@ -515,11 +515,20 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
 
 	float r = random();
 	if (r < 0.3)
+	{
+		e.classname = "item_invisibility";
 		setthink(e, instagib_invisibility);
+	}
 	else if (r < 0.6)
+	{
+		e.classname = "item_extralife";
 		setthink(e, instagib_extralife);
+	}
 	else
+	{
+		e.classname = "item_speed";
 		setthink(e, instagib_speed);
+	}
 
 	e.nextthink = time + 0.1;
 	e.spawnflags = ent.spawnflags;
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
index 4c6d20b129..78037e8034 100644
--- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
+++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
@@ -3,3 +3,8 @@
 #include "items.qh"
 
 float autocvar_g_instagib_invis_alpha;
+
+spawnfunc(item_minst_cells);
+void instagib_invisibility(entity this);
+void instagib_extralife(entity this);
+void instagib_speed(entity this);
diff --git a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc
index 89918c7268..38958cdaff 100644
--- a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc
+++ b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc
@@ -159,6 +159,11 @@ bool autocvar_g_random_items_replace_item_fuel_regen;
 /// \brief Whether to randomly replace jetpack.
 bool autocvar_g_random_items_replace_item_jetpack;
 
+bool autocvar_g_random_items_replace_item_minst_cells;
+bool autocvar_g_random_items_replace_item_invisibility;
+bool autocvar_g_random_items_replace_item_extralife;
+bool autocvar_g_random_items_replace_item_speed;
+
 // Map probability cvars
 
 /// \brief Probability of random health items spawning in the map.
@@ -253,6 +258,11 @@ float autocvar_g_random_items_fuel_regen_probability;
 /// \brief Probability of random jetpack spawning in the map.
 float autocvar_g_random_items_jetpack_probability;
 
+//float autocvar_g_random_items_minst_cells_probability;
+//float autocvar_g_random_items_invisibility_probability;
+//float autocvar_g_random_items_extralife_probability;
+//float autocvar_g_random_items_speed_probability;
+
 // Loot
 
 bool autocvar_g_random_loot; ///< Whether to enable random loot.
@@ -531,6 +541,22 @@ bool RandomItems_ShouldReplaceItem(entity item)
 		{
 			return autocvar_g_random_items_replace_item_jetpack;
 		}
+		case "item_minst_cells":
+		{
+			return autocvar_g_random_items_replace_item_minst_cells;
+		}
+		case "item_invisibility":
+		{
+			return autocvar_g_random_items_replace_item_invisibility;
+		}
+		case "item_extralife":
+		{
+			return autocvar_g_random_items_replace_item_extralife;
+		}
+		case "item_speed":
+		{
+			return autocvar_g_random_items_replace_item_speed;
+		}
 		case "replacedweapon":
 		{
 			switch (item.weapon)
@@ -1211,12 +1237,12 @@ MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
 }
 
 /// \brief Hook that is called when an item is about to spawn.
-MUTATOR_HOOKFUNCTION(random_items, FilterItem)
+MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
 {
 	//PrintToChatAll("FilterItem");
 	if (!autocvar_g_random_items)
 	{
-		return;
+		return false;
 	}
 	if (random_items_is_spawning == true)
 	{
diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc
index 7d16e6c69e..e6fa5ddfdb 100644
--- a/qcsrc/common/t_items.qc
+++ b/qcsrc/common/t_items.qc
@@ -1443,110 +1443,7 @@ void setItemGroupCount()
 	}
 }
 
-spawnfunc(item_rockets)
-{
-    StartItem(this, ITEM_Rockets);
-}
-
-spawnfunc(item_bullets)
-{
-	if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
-	   (this.classname != "droppedweapon"))
-	{
-		weaponswapping = true;
-		spawnfunc_item_shells(this);
-		weaponswapping = false;
-		return;
-	}
-
-    StartItem(this, ITEM_Bullets);
-}
-
-spawnfunc(item_cells)
-{
-	StartItem(this, ITEM_Cells);
-}
-
-spawnfunc(item_plasma)
-{
-	StartItem(this, ITEM_Plasma);
-}
-
-spawnfunc(item_shells)
-{
-	if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
-	   (this.classname != "droppedweapon"))
-	{
-		weaponswapping = true;
-		spawnfunc_item_bullets(this);
-		weaponswapping = false;
-		return;
-	}
-
-	StartItem(this, ITEM_Shells);
-}
-
-spawnfunc(item_armor_small)
-{
-	StartItem(this, ITEM_ArmorSmall);
-}
-
-spawnfunc(item_armor_medium)
-{
-	StartItem(this, ITEM_ArmorMedium);
-}
-
-spawnfunc(item_armor_big)
-{
-	StartItem(this, ITEM_ArmorBig);
-}
-
-spawnfunc(item_armor_mega)
-{
-	StartItem(this, ITEM_ArmorMega);
-}
-
-spawnfunc(item_health_small)
-{
-	StartItem(this, ITEM_HealthSmall);
-}
-
-spawnfunc(item_health_medium)
-{
-    StartItem(this, ITEM_HealthMedium);
-}
-
-spawnfunc(item_health_big)
-{
-	StartItem(this, ITEM_HealthBig);
-}
-
-spawnfunc(item_health_mega)
-{
-    StartItem(this, ITEM_HealthMega);
-}
-
 // support old misnamed entities
-spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); }
-spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); }
-spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
-spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
-spawnfunc(item_health_large) { spawnfunc_item_health_big(this); }
-spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
-
-spawnfunc(item_strength)
-{
-	StartItem(this, ITEM_Strength);
-}
-
-spawnfunc(item_invincible)
-{
-	StartItem(this, ITEM_Shield);
-}
-
-// compatibility:
-spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength(this);}
 
 void target_items_use(entity this, entity actor, entity trigger)
 {
@@ -1687,31 +1584,6 @@ spawnfunc(target_items)
 	}
 }
 
-spawnfunc(item_fuel)
-{
-	StartItem(this, ITEM_JetpackFuel);
-}
-
-spawnfunc(item_fuel_regen)
-{
-	if(start_items & ITEM_JetpackRegen.m_itemid)
-	{
-		spawnfunc_item_fuel(this);
-		return;
-	}
-	StartItem(this, ITEM_JetpackRegen);
-}
-
-spawnfunc(item_jetpack)
-{
-	if(start_items & ITEM_Jetpack.m_itemid)
-	{
-		spawnfunc_item_fuel(this);
-		return;
-	}
-	StartItem(this, ITEM_Jetpack);
-}
-
 float GiveWeapon(entity e, float wpn, float op, float val)
 {
 	WepSet v0, v1;
diff --git a/qcsrc/common/t_items.qh b/qcsrc/common/t_items.qh
index 80385971ab..7eb863e326 100644
--- a/qcsrc/common/t_items.qh
+++ b/qcsrc/common/t_items.qh
@@ -52,17 +52,10 @@ void ItemDrawSimple(entity this);
 
 #endif
 #ifdef SVQC
-spawnfunc(item_strength);
-spawnfunc(item_invincible);
-spawnfunc(item_armor_small);
-spawnfunc(item_shells);
-spawnfunc(item_bullets);
-spawnfunc(item_rockets);
 
 float autocvar_sv_simple_items;
 bool ItemSend(entity this, entity to, int sf);
 
-
 bool have_pickup_item(entity this);
 
 const float ITEM_RESPAWN_TICKS = 10;
diff --git a/qcsrc/common/weapons/weapon/arc.qh b/qcsrc/common/weapons/weapon/arc.qh
index abd9933116..ed2d0a800e 100644
--- a/qcsrc/common/weapons/weapon/arc.qh
+++ b/qcsrc/common/weapons/weapon/arc.qh
@@ -112,6 +112,7 @@ const int ARC_SF_LOCALMASK =   ARC_SF_START | ARC_SF_WANTDIR | ARC_SF_BEAMDIR;
 .float arc_cooldown; // (dropped arc/player) cooling speed
 .float arc_heat_percent = _STAT(ARC_HEAT);
 .float arc_smoke_sound;
+spawnfunc(weapon_arc);
 #endif
 #ifdef CSQC
 
diff --git a/qcsrc/common/weapons/weapon/blaster.qh b/qcsrc/common/weapons/weapon/blaster.qh
index 972dcd003f..40e453511d 100644
--- a/qcsrc/common/weapons/weapon/blaster.qh
+++ b/qcsrc/common/weapons/weapon/blaster.qh
@@ -50,4 +50,6 @@ REGISTER_WEAPON(BLASTER, blaster, NEW(Blaster));
 .float blaster_radius;
 .float blaster_force;
 .float blaster_lifetime;
+
+spawnfunc(weapon_blaster);
 #endif
diff --git a/qcsrc/common/weapons/weapon/crylink.qh b/qcsrc/common/weapons/weapon/crylink.qh
index e686cfa94c..be6afd82c5 100644
--- a/qcsrc/common/weapons/weapon/crylink.qh
+++ b/qcsrc/common/weapons/weapon/crylink.qh
@@ -68,4 +68,5 @@ REGISTER_WEAPON(CRYLINK, crylink, NEW(Crylink));
 
 .entity queuenext;
 .entity queueprev;
+spawnfunc(weapon_crylink);
 #endif
diff --git a/qcsrc/common/weapons/weapon/devastator.qh b/qcsrc/common/weapons/weapon/devastator.qh
index 9c41975117..ebfde371c6 100644
--- a/qcsrc/common/weapons/weapon/devastator.qh
+++ b/qcsrc/common/weapons/weapon/devastator.qh
@@ -65,4 +65,5 @@ REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator));
 #ifdef SVQC
 .float rl_release;
 .float rl_detonate_later;
+spawnfunc(weapon_devastator);
 #endif
diff --git a/qcsrc/common/weapons/weapon/electro.qh b/qcsrc/common/weapons/weapon/electro.qh
index 07c967c49b..d881908278 100644
--- a/qcsrc/common/weapons/weapon/electro.qh
+++ b/qcsrc/common/weapons/weapon/electro.qh
@@ -70,5 +70,6 @@ REGISTER_WEAPON(ELECTRO, electro, NEW(Electro));
 #ifdef SVQC
 .float electro_count;
 .float electro_secondarytime;
+spawnfunc(weapon_electro);
 void W_Electro_ExplodeCombo(entity this);
 #endif
diff --git a/qcsrc/common/weapons/weapon/fireball.qh b/qcsrc/common/weapons/weapon/fireball.qh
index e973d28845..cd52d2a70c 100644
--- a/qcsrc/common/weapons/weapon/fireball.qh
+++ b/qcsrc/common/weapons/weapon/fireball.qh
@@ -57,4 +57,5 @@ REGISTER_WEAPON(FIREBALL, fireball, NEW(Fireball));
 .float bot_primary_fireballmooth; // whatever a mooth is
 .vector fireball_impactvec;
 .float fireball_primarytime;
+spawnfunc(weapon_fireball);
 #endif
diff --git a/qcsrc/common/weapons/weapon/hagar.qh b/qcsrc/common/weapons/weapon/hagar.qh
index 24c700cc84..c5661f1e66 100644
--- a/qcsrc/common/weapons/weapon/hagar.qh
+++ b/qcsrc/common/weapons/weapon/hagar.qh
@@ -56,3 +56,7 @@ CLASS(Hagar, Weapon)
 
 ENDCLASS(Hagar)
 REGISTER_WEAPON(HAGAR, hagar, NEW(Hagar));
+
+#ifdef SVQC
+spawnfunc(weapon_hagar);
+#endif
diff --git a/qcsrc/common/weapons/weapon/hlac.qh b/qcsrc/common/weapons/weapon/hlac.qh
index 4664e54d96..3ab29907e0 100644
--- a/qcsrc/common/weapons/weapon/hlac.qh
+++ b/qcsrc/common/weapons/weapon/hlac.qh
@@ -48,3 +48,7 @@ CLASS(HLAC, Weapon)
 
 ENDCLASS(HLAC)
 REGISTER_WEAPON(HLAC, hlac, NEW(HLAC));
+
+#ifdef SVQC
+spawnfunc(weapon_hlac);
+#endif
diff --git a/qcsrc/common/weapons/weapon/hook.qh b/qcsrc/common/weapons/weapon/hook.qh
index 4988323fda..d028323ff1 100644
--- a/qcsrc/common/weapons/weapon/hook.qh
+++ b/qcsrc/common/weapons/weapon/hook.qh
@@ -73,4 +73,5 @@ OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(Offhand
 .float hook_refire;
 .float hook_time_hooked;
 .float hook_time_fueldecrease;
+spawnfunc(weapon_hook);
 #endif
diff --git a/qcsrc/common/weapons/weapon/machinegun.qh b/qcsrc/common/weapons/weapon/machinegun.qh
index 2f0974971e..183a736c25 100644
--- a/qcsrc/common/weapons/weapon/machinegun.qh
+++ b/qcsrc/common/weapons/weapon/machinegun.qh
@@ -54,3 +54,7 @@ CLASS(MachineGun, Weapon)
 
 ENDCLASS(MachineGun)
 REGISTER_WEAPON(MACHINEGUN, machinegun, NEW(MachineGun));
+
+#ifdef SVQC
+spawnfunc(weapon_machinegun);
+#endif
diff --git a/qcsrc/common/weapons/weapon/minelayer.qh b/qcsrc/common/weapons/weapon/minelayer.qh
index f113e6439e..7369d8c675 100644
--- a/qcsrc/common/weapons/weapon/minelayer.qh
+++ b/qcsrc/common/weapons/weapon/minelayer.qh
@@ -58,4 +58,5 @@ void W_MineLayer_Think(entity this);
 .float minelayer_detonate, mine_explodeanyway;
 .float mine_time;
 .vector mine_orientation;
+spawnfunc(weapon_minelayer);
 #endif
diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh
index 4fc5ec9ad2..be9cdbb1f3 100644
--- a/qcsrc/common/weapons/weapon/mortar.qh
+++ b/qcsrc/common/weapons/weapon/mortar.qh
@@ -57,4 +57,5 @@ REGISTER_WEAPON(MORTAR, mortar, NEW(Mortar));
 #ifdef SVQC
 .float gl_detonate_later;
 .float gl_bouncecnt;
+spawnfunc(weapon_mortar);
 #endif
diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh
index b46e479aa9..6af9e3b92f 100644
--- a/qcsrc/common/weapons/weapon/porto.qh
+++ b/qcsrc/common/weapons/weapon/porto.qh
@@ -41,4 +41,5 @@ REGISTER_WEAPON(PORTO, porto, NEW(PortoLaunch));
 .float porto_v_angle_held;
 .vector right_vector;
 .float porto_forbidden;
+spawnfunc(weapon_porto);
 #endif
diff --git a/qcsrc/common/weapons/weapon/rifle.qh b/qcsrc/common/weapons/weapon/rifle.qh
index b1c01b86ff..0340d9fdb1 100644
--- a/qcsrc/common/weapons/weapon/rifle.qh
+++ b/qcsrc/common/weapons/weapon/rifle.qh
@@ -50,4 +50,5 @@ REGISTER_WEAPON(RIFLE, rifle, NEW(Rifle));
 
 #ifdef SVQC
 .float rifle_accumulator;
+spawnfunc(weapon_rifle);
 #endif
diff --git a/qcsrc/common/weapons/weapon/seeker.qh b/qcsrc/common/weapons/weapon/seeker.qh
index 443d0843d0..40ba463ec7 100644
--- a/qcsrc/common/weapons/weapon/seeker.qh
+++ b/qcsrc/common/weapons/weapon/seeker.qh
@@ -85,6 +85,7 @@ REGISTER_WEAPON(SEEKER, seeker, NEW(Seeker));
 #ifdef SVQC
 .entity tag_target, wps_tag_tracker;
 .float tag_time;
+spawnfunc(weapon_seeker);
 
 IntrusiveList g_seeker_trackers;
 STATIC_INIT(g_seeker_trackers) { g_seeker_trackers = IL_NEW(); }
diff --git a/qcsrc/common/weapons/weapon/shockwave.qh b/qcsrc/common/weapons/weapon/shockwave.qh
index 89685376da..d382890684 100644
--- a/qcsrc/common/weapons/weapon/shockwave.qh
+++ b/qcsrc/common/weapons/weapon/shockwave.qh
@@ -84,3 +84,7 @@ void Net_ReadShockwaveParticle();
 .float sw_spread_min;
 .float sw_time;
 #endif
+
+#ifdef SVQC
+spawnfunc(weapon_shockwave);
+#endif
diff --git a/qcsrc/common/weapons/weapon/shotgun.qh b/qcsrc/common/weapons/weapon/shotgun.qh
index cd646a768f..f4afbc070c 100644
--- a/qcsrc/common/weapons/weapon/shotgun.qh
+++ b/qcsrc/common/weapons/weapon/shotgun.qh
@@ -52,3 +52,7 @@ CLASS(Shotgun, Weapon)
 
 ENDCLASS(Shotgun)
 REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun));
+
+#ifdef SVQC
+spawnfunc(weapon_shotgun);
+#endif
diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh
index 714a2b8b58..8e5f3b8c0d 100644
--- a/qcsrc/common/weapons/weapon/tuba.qh
+++ b/qcsrc/common/weapons/weapon/tuba.qh
@@ -48,3 +48,7 @@ class(Tuba) .float tuba_volume;
 class(Tuba) .float tuba_volume_initial;
 class(Tuba) .int tuba_instrument;
 #endif
+
+#ifdef SVQC
+spawnfunc(weapon_tuba);
+#endif
diff --git a/qcsrc/common/weapons/weapon/vaporizer.qh b/qcsrc/common/weapons/weapon/vaporizer.qh
index 0c5c19200a..874f318633 100644
--- a/qcsrc/common/weapons/weapon/vaporizer.qh
+++ b/qcsrc/common/weapons/weapon/vaporizer.qh
@@ -59,4 +59,5 @@ REGISTER_WEAPON(VAPORIZER, vaporizer, NEW(Vaporizer));
 .float rm_force;
 .float rm_damage;
 .float rm_edmg;
+spawnfunc(weapon_vaporizer);
 #endif
diff --git a/qcsrc/common/weapons/weapon/vortex.qh b/qcsrc/common/weapons/weapon/vortex.qh
index 5a41b90d8e..3a4ab52a54 100644
--- a/qcsrc/common/weapons/weapon/vortex.qh
+++ b/qcsrc/common/weapons/weapon/vortex.qh
@@ -61,6 +61,6 @@ REGISTER_WEAPON(VORTEX, vortex, NEW(Vortex));
 
 
 #ifdef SVQC
-
 .float vortex_lasthit;
+spawnfunc(weapon_vortex);
 #endif
diff --git a/qcsrc/server/items.qc b/qcsrc/server/items.qc
index eb0d996660..56c59d2631 100644
--- a/qcsrc/server/items.qc
+++ b/qcsrc/server/items.qc
@@ -6,6 +6,7 @@
 /// \copyright GNU GPLv2 or any later version.
 
 #include <common/t_items.qh>
+#include <common/mutators/mutator/instagib/sv_instagib.qh>
 
 .bool m_isloot; ///< Holds whether item is loot.
 
@@ -15,202 +16,222 @@ void Item_Initialize(entity item, string class_name)
 	{
 		case "item_health_small":
 		{
-			StartItem(item, ITEM_HealthSmall);
+			spawnfunc_item_health_small(item);
 			return;
 		}
 		case "item_health_medium":
 		{
-			StartItem(item, ITEM_HealthMedium);
+			spawnfunc_item_health_medium(item);
 			return;
 		}
 		case "item_health_big":
 		case "item_health_large":
 		{
-			StartItem(item, ITEM_HealthBig);
+			spawnfunc_item_health_big(item);
 			return;
 		}
 		case "item_health_mega":
 		{
-			StartItem(item, ITEM_HealthMega);
+			spawnfunc_item_health_mega(item);
 			return;
 		}
 		case "item_armor_small":
 		{
-			StartItem(item, ITEM_ArmorSmall);
+			spawnfunc_item_armor_small(item);
 			return;
 		}
 		case "item_armor_medium":
 		{
-			StartItem(item, ITEM_ArmorMedium);
+			spawnfunc_item_armor_medium(item);
 			return;
 		}
 		case "item_armor_big":
 		case "item_armor_large":
 		{
-			StartItem(item, ITEM_ArmorBig);
+			spawnfunc_item_armor_big(item);
 			return;
 		}
 		case "item_armor_mega":
 		{
-			StartItem(item, ITEM_ArmorMega);
+			spawnfunc_item_armor_mega(item);
 			return;
 		}
 		case "item_shells":
 		{
-			StartItem(item, ITEM_Shells);
+			spawnfunc_item_shells(item);
 			return;
 		}
 		case "item_bullets":
 		{
-			StartItem(item, ITEM_Bullets);
+			spawnfunc_item_bullets(item);
 			return;
 		}
 		case "item_rockets":
 		{
-			StartItem(item, ITEM_Rockets);
+			spawnfunc_item_rockets(item);
 			return;
 		}
 		case "item_cells":
 		{
-			StartItem(item, ITEM_Cells);
+			spawnfunc_item_cells(item);
 			return;
 		}
 		case "item_plasma":
 		{
-			StartItem(item, ITEM_Plasma);
+			spawnfunc_item_plasma(item);
 			return;
 		}
 		case "item_fuel":
 		{
-			StartItem(item, ITEM_JetpackFuel);
+			spawnfunc_item_fuel(item);
 			return;
 		}
 		case "weapon_blaster":
 		case "weapon_laser":
 		{
-			weapon_defaultspawnfunc(item, WEP_BLASTER);
+			spawnfunc_weapon_blaster(item);
 			return;
 		}
 		case "weapon_shotgun":
 		{
-			weapon_defaultspawnfunc(item, WEP_SHOTGUN);
+			spawnfunc_weapon_shotgun(item);
 			return;
 		}
 		case "weapon_machinegun":
 		case "weapon_uzi":
 		{
-			weapon_defaultspawnfunc(item, WEP_MACHINEGUN);
+			spawnfunc_weapon_machinegun(item);
 			return;
 		}
 		case "weapon_mortar":
 		case "weapon_grenadelauncher":
 		{
-			weapon_defaultspawnfunc(item, WEP_MORTAR);
+			spawnfunc_weapon_mortar(item);
 			return;
 		}
 		case "weapon_electro":
 		{
-			weapon_defaultspawnfunc(item, WEP_ELECTRO);
+			spawnfunc_weapon_electro(item);
 			return;
 		}
 		case "weapon_crylink":
 		{
-			weapon_defaultspawnfunc(item, WEP_CRYLINK);
+			spawnfunc_weapon_crylink(item);
 			return;
 		}
 		case "weapon_vortex":
 		case "weapon_nex":
 		{
-			weapon_defaultspawnfunc(item, WEP_VORTEX);
+			spawnfunc_weapon_vortex(item);
 			return;
 		}
 		case "weapon_hagar":
 		{
-			weapon_defaultspawnfunc(item, WEP_HAGAR);
+			spawnfunc_weapon_hagar(item);
 			return;
 		}
 		case "weapon_devastator":
 		case "weapon_rocketlauncher":
 		{
-			weapon_defaultspawnfunc(item, WEP_DEVASTATOR);
+			spawnfunc_weapon_devastator(item);
 			return;
 		}
 		case "weapon_shockwave":
 		{
-			weapon_defaultspawnfunc(item, WEP_SHOCKWAVE);
+			spawnfunc_weapon_shockwave(item);
 			return;
 		}
 		case "weapon_arc":
 		{
-			weapon_defaultspawnfunc(item, WEP_ARC);
+			spawnfunc_weapon_arc(item);
 			return;
 		}
 		case "weapon_hook":
 		{
-			weapon_defaultspawnfunc(item, WEP_HOOK);
+			spawnfunc_weapon_hook(item);
 			return;
 		}
 		case "weapon_tuba":
 		{
-			weapon_defaultspawnfunc(item, WEP_TUBA);
+			spawnfunc_weapon_tuba(item);
 			return;
 		}
 		case "weapon_porto":
 		{
-			weapon_defaultspawnfunc(item, WEP_PORTO);
+			spawnfunc_weapon_porto(item);
 			return;
 		}
 		case "weapon_fireball":
 		{
-			weapon_defaultspawnfunc(item, WEP_FIREBALL);
+			spawnfunc_weapon_fireball(item);
 			return;
 		}
 		case "weapon_minelayer":
 		{
-			weapon_defaultspawnfunc(item, WEP_MINE_LAYER);
+			spawnfunc_weapon_minelayer(item);
 			return;
 		}
 		case "weapon_hlac":
 		{
-			weapon_defaultspawnfunc(item, WEP_HLAC);
+			spawnfunc_weapon_hlac(item);
 			return;
 		}
 		case "weapon_rifle":
 		case "weapon_campingrifle":
 		case "weapon_sniperrifle":
 		{
-			weapon_defaultspawnfunc(item, WEP_RIFLE);
+			spawnfunc_weapon_rifle(item);
 			return;
 		}
 		case "weapon_seeker":
 		{
-			weapon_defaultspawnfunc(item, WEP_SEEKER);
+			spawnfunc_weapon_seeker(item);
 			return;
 		}
 		case "weapon_vaporizer":
 		case "weapon_minstanex":
 		{
-			weapon_defaultspawnfunc(item, WEP_VAPORIZER);
+			spawnfunc_weapon_vaporizer(item);
 			return;
 		}
 		case "item_strength":
 		{
-			StartItem(item, ITEM_Strength);
+			spawnfunc_item_strength(item);
 			return;
 		}
 		case "item_invincible":
 		{
-			StartItem(item, ITEM_Shield);
+			spawnfunc_item_invincible(item);
 			return;
 		}
 		case "item_fuel_regen":
 		{
-			StartItem(item, ITEM_JetpackRegen);
+			spawnfunc_item_fuel_regen(item);
 			return;
 		}
 		case "item_jetpack":
 		{
-			StartItem(item, ITEM_Jetpack);
+			spawnfunc_item_jetpack(item);
+			return;
+		}
+		case "item_minst_cells":
+		{
+			spawnfunc_item_minst_cells(item);
+			return;
+		}
+		case "item_invisibility":
+		{
+			instagib_invisibility(item);
+			return;
+		}
+		case "item_extralife":
+		{
+			instagib_extralife(item);
+			return;
+		}
+		case "item_speed":
+		{
+			instagib_speed(item);
 			return;
 		}
 	}
@@ -257,3 +278,135 @@ void Item_SetLoot(entity item, bool loot)
 {
 	item.m_isloot = loot;
 }
+
+spawnfunc(item_health_small)
+{
+	StartItem(this, ITEM_HealthSmall);
+}
+
+spawnfunc(item_health_medium)
+{
+	StartItem(this, ITEM_HealthMedium);
+}
+
+spawnfunc(item_health_big)
+{
+	StartItem(this, ITEM_HealthBig);
+}
+
+spawnfunc(item_health_mega)
+{
+	StartItem(this, ITEM_HealthMega);
+}
+
+spawnfunc(item_armor_small)
+{
+	StartItem(this, ITEM_ArmorSmall);
+}
+
+spawnfunc(item_armor_medium)
+{
+	StartItem(this, ITEM_ArmorMedium);
+}
+
+spawnfunc(item_armor_big)
+{
+	StartItem(this, ITEM_ArmorBig);
+}
+
+spawnfunc(item_armor_mega)
+{
+	StartItem(this, ITEM_ArmorMega);
+}
+
+spawnfunc(item_shells)
+{
+	if (!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
+		(this.classname != "droppedweapon"))
+	{
+		weaponswapping = true;
+		spawnfunc_item_bullets(this);
+		weaponswapping = false;
+		return;
+	}
+	StartItem(this, ITEM_Shells);
+}
+
+spawnfunc(item_bullets)
+{
+	if (!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
+		(this.classname != "droppedweapon"))
+	{
+		weaponswapping = true;
+		spawnfunc_item_shells(this);
+		weaponswapping = false;
+		return;
+	}
+	StartItem(this, ITEM_Bullets);
+}
+
+spawnfunc(item_rockets)
+{
+	StartItem(this, ITEM_Rockets);
+}
+
+spawnfunc(item_cells)
+{
+	StartItem(this, ITEM_Cells);
+}
+
+spawnfunc(item_plasma)
+{
+	StartItem(this, ITEM_Plasma);
+}
+
+spawnfunc(item_fuel)
+{
+	StartItem(this, ITEM_JetpackFuel);
+}
+
+spawnfunc(item_strength)
+{
+	StartItem(this, ITEM_Strength);
+}
+
+spawnfunc(item_invincible)
+{
+	StartItem(this, ITEM_Shield);
+}
+
+spawnfunc(item_fuel_regen)
+{
+	if (start_items & ITEM_JetpackRegen.m_itemid)
+	{
+		spawnfunc_item_fuel(this);
+		return;
+	}
+	StartItem(this, ITEM_JetpackRegen);
+}
+
+spawnfunc(item_jetpack)
+{
+	if(start_items & ITEM_Jetpack.m_itemid)
+	{
+		spawnfunc_item_fuel(this);
+		return;
+	}
+	StartItem(this, ITEM_Jetpack);
+}
+
+// Compatibility spawn functions
+
+spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
+spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); }
+spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); }
+spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
+spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
+spawnfunc(item_health_large) { spawnfunc_item_health_big(this); }
+spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
+
+spawnfunc(item_quad)
+{
+	this.classname = "item_strength";
+	spawnfunc_item_strength(this);
+}
diff --git a/qcsrc/server/items.qh b/qcsrc/server/items.qh
index 67a2411768..bb2af40fbb 100644
--- a/qcsrc/server/items.qh
+++ b/qcsrc/server/items.qh
@@ -40,3 +40,28 @@ bool Item_IsLoot(entity item);
 /// \param[in] loot Whether item is loot.
 /// \return No return.
 void Item_SetLoot(entity item, bool loot);
+
+// Item spawn functions.
+// If a function is declared like this:
+// spawnfunc(foo);
+// You need to call it like this:
+// spawnfunc_foo(item);
+
+spawnfunc(item_health_small);
+spawnfunc(item_health_medium);
+spawnfunc(item_health_big);
+spawnfunc(item_health_mega);
+spawnfunc(item_armor_small);
+spawnfunc(item_armor_medium);
+spawnfunc(item_armor_big);
+spawnfunc(item_armor_mega);
+spawnfunc(item_shells);
+spawnfunc(item_bullets);
+spawnfunc(item_rockets);
+spawnfunc(item_cells);
+spawnfunc(item_plasma);
+spawnfunc(item_fuel);
+spawnfunc(item_strength);
+spawnfunc(item_invincible);
+spawnfunc(item_fuel_regen);
+spawnfunc(item_jetpack);
-- 
2.39.5