]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/t_items.qc
s/(void)/()
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / t_items.qc
index 0fbbf2732ed8545401b320a2fc00ec39c74ee1a5..2a594c11f07fdaee8b75b43d68e7726feec232ad 100644 (file)
@@ -3,19 +3,18 @@
 #include "../common/items/all.qc"
 
 #if defined(SVQC)
-    #include "_all.qh"
 
     #include "bot/bot.qh"
     #include "bot/waypoints.qh"
 
-    #include "mutators/mutators_include.qh"
+    #include "mutators/all.qh"
 
     #include "weapons/common.qh"
     #include "weapons/selection.qh"
     #include "weapons/weaponsystem.qh"
 
     #include "../common/constants.qh"
-    #include "../common/deathtypes.qh"
+    #include "../common/deathtypes/all.qh"
     #include "../common/notifications.qh"
        #include "../common/triggers/subs.qh"
     #include "../common/util.qh"
 
     #include "../common/weapons/all.qh"
 
-    #include "../warpzonelib/util_server.qh"
+    #include "../lib/warpzone/util_server.qh"
 #endif
 
 #ifdef CSQC
-void ItemDraw()
-{SELFPARAM();
+void ItemDraw(entity self)
+{
     if(self.gravity)
     {
         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
@@ -64,8 +63,8 @@ void ItemDraw()
     }
 }
 
-void ItemDrawSimple()
-{SELFPARAM();
+void ItemDrawSimple(entity this)
+{
     if(self.gravity)
     {
         Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
@@ -246,14 +245,14 @@ void ItemRead(float _IsNew)
 #endif
 
 #ifdef SVQC
-bool ItemSend(entity to, int sf)
-{SELFPARAM();
+bool ItemSend(entity this, entity to, int sf)
+{
        if(self.gravity)
                sf |= ISF_DROP;
        else
                sf &= ~ISF_DROP;
 
-       WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
+       WriteHeader(MSG_ENTITY, ENT_CLIENT_ITEM);
        WriteByte(MSG_ENTITY, sf);
 
        //WriteByte(MSG_ENTITY, self.cnt);
@@ -273,7 +272,8 @@ bool ItemSend(entity to, int sf)
 
        if(sf & ISF_SIZE)
        {
-               WriteByte(MSG_ENTITY, ((self.flags & FL_POWERUP) || self.health || self.armorvalue));
+               Pickup p = this.itemdef;
+               WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor);
        }
 
        if(sf & ISF_STATUS)
@@ -309,9 +309,9 @@ void ItemUpdate(entity item)
        item.SendFlags |= ISF_LOCATION;
 }
 
-float have_pickup_item(void)
-{SELFPARAM();
-       if(self.flags & FL_POWERUP)
+bool have_pickup_item(entity this)
+{
+       if(this.itemdef.instanceOfPowerup)
        {
                if(autocvar_g_powerups > 0)
                        return true;
@@ -325,7 +325,7 @@ float have_pickup_item(void)
                if(autocvar_g_pickup_items == 0)
                        return false;
                if(g_weaponarena)
-                       if(self.weapons || (self.items & IT_AMMO)) // no item or ammo pickups in weaponarena
+                       if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena
                                return false;
        }
        return true;
@@ -378,7 +378,12 @@ void Item_Show (entity e, float mode)
                e.spawnshieldtime = 1;
                e.ItemStatus &= ~ITS_AVAILABLE;
        }
-       else if((e.flags & FL_WEAPON) && !(e.flags & FL_NO_WEAPON_STAY) && g_weapon_stay)
+       else {
+       entity def = e.itemdef;
+       bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons
+               || e.team // weapon stay isn't supported for teamed weapons
+               ;
+       if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay)
        {
                // make the item translucent and not touchable
                e.model = e.mdl;
@@ -395,7 +400,7 @@ void Item_Show (entity e, float mode)
                e.glowmod = e.colormod;
                e.spawnshieldtime = 1;
                e.ItemStatus &= ~ITS_AVAILABLE;
-       }
+       }}
 
        if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid)
                e.ItemStatus |= ITS_POWERUP;
@@ -431,16 +436,16 @@ float Item_ItemsTime_UpdateTime(entity e, float t);
 void Item_ItemsTime_SetTime(entity e, float t);
 void Item_ItemsTime_SetTimesForAllPlayers();
 
-void Item_Respawn (void)
+void Item_Respawn ()
 {SELFPARAM();
        Item_Show(self, 1);
        // this is ugly...
        if(self.items == ITEM_Strength.m_itemid)
-               sound (self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTEN_NORM);    // play respawn sound
+               sound (self, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTEN_NORM);   // play respawn sound
        else if(self.items == ITEM_Shield.m_itemid)
-               sound (self, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTEN_NORM);      // play respawn sound
+               sound (self, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTEN_NORM);     // play respawn sound
        else
-               sound (self, CH_TRIGGER, "misc/itemrespawn.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
+               sound (self, CH_TRIGGER, SND_ITEMRESPAWN, VOL_BASE, ATTEN_NORM);        // play respawn sound
        setorigin (self, self.origin);
 
     if (Item_ItemsTime_Allow(self.itemdef, self.weapons))
@@ -457,7 +462,7 @@ void Item_Respawn (void)
        Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
 }
 
-void Item_RespawnCountdown (void)
+void Item_RespawnCountdown ()
 {SELFPARAM();
        if(self.count >= ITEM_RESPAWN_TICKS)
        {
@@ -472,15 +477,24 @@ void Item_RespawnCountdown (void)
                if(self.count == 1)
                {
                        MUTATOR_CALLHOOK(Item_RespawnCountdown, string_null, '0 0 0');
-                       int wpextra = 0;
-            entity e = self.itemdef;
-            if (e) wpextra = e.m_id;
-                       if (self.flags & FL_WEAPON) {
-                               entity wi = get_weaponinfo(self.weapon);
-                               if (wi) wpextra = wi.m_id;
-                       }
-            entity wp = WaypointSprite_Spawn(WP_Item, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP);
-            wp.wp_extra = wpextra;
+                       do {
+                               {
+                                       entity wi = get_weaponinfo(self.weapon);
+                                       if (wi.m_id) {
+                                               entity wp = WaypointSprite_Spawn(WP_Weapon, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_Weapon);
+                                               wp.wp_extra = wi.m_id;
+                                               break;
+                                       }
+                               }
+                               {
+                                       entity ii = self.itemdef;
+                                       if (ii.m_id) {
+                                               entity wp = WaypointSprite_Spawn(WP_Item, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_Item);
+                                               wp.wp_extra = ii.m_id;
+                                               break;
+                                       }
+                               }
+                       } while (0);
             if(self.waypointsprite_attached)
             {
                 GameItem def = self.itemdef;
@@ -498,15 +512,13 @@ void Item_RespawnCountdown (void)
                                if(self.waypointsprite_visible_for_player(e))
                                {
                                        msg_entity = e;
-                                       soundto(MSG_ONE, this, CH_TRIGGER, "misc/itemrespawncountdown.wav", VOL_BASE, ATTEN_NORM);      // play respawn sound
+                                       soundto(MSG_ONE, this, CH_TRIGGER, SND(ITEMRESPAWNCOUNTDOWN), VOL_BASE, ATTEN_NORM);    // play respawn sound
                                }
                        setself(this);
 
                        WaypointSprite_Ping(self.waypointsprite_attached);
                        //WaypointSprite_UpdateHealth(self.waypointsprite_attached, self.count);
                }
-               else
-                       sound(self, CH_TRIGGER, "misc/itemrespawncountdown.wav", VOL_BASE, ATTEN_NORM); // play respawn sound
        }
 }
 
@@ -568,7 +580,7 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax
 
        if (item.spawnshieldtime)
        {
-               if ((player.(ammotype) < ammomax) || item.pickup_anyway)
+               if ((player.(ammotype) < ammomax) || item.pickup_anyway > 0)
                {
                        player.(ammotype) = bound(player.(ammotype), ammomax, player.(ammotype) + item.(ammotype));
                        goto YEAH;
@@ -633,19 +645,19 @@ float Item_GiveTo(entity item, entity player)
        pickedup |= Item_GiveAmmoTo(item, player, health, item.max_health, ITEM_MODE_HEALTH);
        pickedup |= Item_GiveAmmoTo(item, player, armorvalue, item.max_armorvalue, ITEM_MODE_ARMOR);
 
-       if (item.flags & FL_WEAPON)
+       if (item.itemdef.instanceOfWeaponPickup)
        {
                WepSet it;
                it = item.weapons;
                it &= ~player.weapons;
 
-               if (it || (item.spawnshieldtime && item.pickup_anyway))
+               if (it || (item.spawnshieldtime && item.pickup_anyway > 0))
                {
                        pickedup = true;
                        for(i = WEP_FIRST; i <= WEP_LAST; ++i)
                        if(it & WepSet_FromWeapon(i))
                        {
-                               W_DropEvent(WR_PICKUP, player, i, item);
+                               W_DropEvent(wr_pickup, player, i, item);
                                W_GiveWeapon(player, i);
                        }
                }
@@ -684,7 +696,7 @@ float Item_GiveTo(entity item, entity player)
                return 0;
 
        // crude hack to enforce switching weapons
-       if(g_cts && (item.flags & FL_WEAPON))
+       if(g_cts && item.itemdef.instanceOfWeaponPickup)
        {
                W_SwitchWeapon_Force(player, item.weapon);
                return 1;
@@ -697,16 +709,16 @@ float Item_GiveTo(entity item, entity player)
        return 1;
 }
 
-void Item_Touch (void)
-{SELFPARAM();
-       entity e, head;
+void Item_Touch()
+{
+       SELFPARAM();
 
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
-       if(self.classname == "droppedweapon")
+       if (this.classname == "droppedweapon")
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
-                       remove(self);
+                       remove(this);
                        return;
                }
        }
@@ -714,33 +726,33 @@ void Item_Touch (void)
        if(!(other.flags & FL_PICKUPITEMS)
        || other.frozen
        || other.deadflag
-       || (self.solid != SOLID_TRIGGER)
-       || (self.owner == other)
-       || (time < self.item_spawnshieldtime)
-       ) { return;}
+       || (this.solid != SOLID_TRIGGER)
+       || (this.owner == other)
+       || (time < this.item_spawnshieldtime)
+       ) { return; }
 
-       switch(MUTATOR_CALLHOOK(ItemTouch, self, other))
+       switch (MUTATOR_CALLHOOK(ItemTouch, this, other))
        {
                case MUT_ITEMTOUCH_RETURN: { return; }
                case MUT_ITEMTOUCH_PICKUP: { goto pickup; }
        }
 
-       if (self.classname == "droppedweapon")
+       if (this.classname == "droppedweapon")
        {
-               self.strength_finished = max(0, self.strength_finished - time);
-               self.invincible_finished = max(0, self.invincible_finished - time);
-               self.superweapons_finished = max(0, self.superweapons_finished - time);
+               this.strength_finished = max(0, this.strength_finished - time);
+               this.invincible_finished = max(0, this.invincible_finished - time);
+               this.superweapons_finished = max(0, this.superweapons_finished - time);
        }
-       entity it = self.itemdef;
-       bool gave = (it && it.instanceOfPickup) ? ITEM_HANDLE(Pickup, it, self, other) : Item_GiveTo(self, other);
+       entity it = this.itemdef;
+       bool gave = ITEM_HANDLE(Pickup, it, this, other);
        if (!gave)
        {
-               if (self.classname == "droppedweapon")
+               if (this.classname == "droppedweapon")
                {
                        // undo what we did above
-                       self.strength_finished += time;
-                       self.invincible_finished += time;
-                       self.superweapons_finished += time;
+                       this.strength_finished += time;
+                       this.invincible_finished += time;
+                       this.superweapons_finished += time;
                }
                return;
        }
@@ -749,17 +761,18 @@ void Item_Touch (void)
 
        other.last_pickup = time;
 
-       Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
-       sound (other, CH_TRIGGER, self.item_pickupsound, VOL_BASE, ATTEN_NORM);
+       Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
+       _sound (other, CH_TRIGGER, (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
 
-       if (self.classname == "droppedweapon")
-               remove (self);
-       else if (self.spawnshieldtime)
+       if (this.classname == "droppedweapon")
+               remove (this);
+       else if (this.spawnshieldtime)
        {
-               if(self.team)
+               entity e;
+               if(this.team)
                {
                        RandomSelection_Init();
-                       for(head = world; (head = findfloat(head, team, self.team)); )
+                       for(entity head = world; (head = findfloat(head, team, this.team)); )
                        {
                                if(head.flags & FL_ITEM)
                                if(head.classname != "item_flag_team" && head.classname != "item_key_team")
@@ -772,28 +785,29 @@ void Item_Touch (void)
 
                }
                else
-                       e = self;
+                       e = this;
                Item_ScheduleRespawn(e);
        }
 }
 
-void Item_Reset()
-{SELFPARAM();
-       Item_Show(self, !self.state);
-       setorigin (self, self.origin);
+void Item_Reset(entity this)
+{
+       Item_Show(this, !this.state);
+       setorigin(this, this.origin);
 
-       if(self.classname != "droppedweapon")
+       if (this.classname != "droppedweapon")
        {
-               self.think = Item_Think;
-               self.nextthink = time;
+               this.think = Item_Think;
+               this.nextthink = time;
 
-               if(self.waypointsprite_attached)
-                       WaypointSprite_Kill(self.waypointsprite_attached);
+               if (this.waypointsprite_attached)
+                       WaypointSprite_Kill(this.waypointsprite_attached);
 
-               if((self.flags & FL_POWERUP) || (self.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
-                       Item_ScheduleInitialRespawn(self);
+               if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
+                       Item_ScheduleInitialRespawn(this);
        }
 }
+void Item_Reset_self() { SELFPARAM(); Item_Reset(this); }
 
 void Item_FindTeam()
 {SELFPARAM();
@@ -825,13 +839,13 @@ void Item_FindTeam()
                        head.effects &= ~EF_NODRAW;
                }
 
-               Item_Reset();
+               Item_Reset(self);
        }
 }
 
 // Savage: used for item garbage-collection
 // TODO: perhaps nice special effect?
-void RemoveItem(void)
+void RemoveItem()
 {SELFPARAM();
        if(wasfreed(self) || !self) { return; }
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
@@ -965,267 +979,238 @@ void Item_Damage(entity inflictor, entity attacker, float damage, int deathtype,
                RemoveItem();
 }
 
-void StartItem (string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
-{SELFPARAM();
-       startitem_failed = false;
-
-       if(self.model == "")
-               self.model = itemmodel;
+void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter)
+{
+       string itemname = def.m_name;
+       Model itemmodel = def.m_model;
+    Sound pickupsound = def.m_sound;
+       float(entity player, entity item) pickupevalfunc = def.m_pickupevalfunc;
+       float pickupbasevalue = def.m_botvalue;
+       int itemflags = def.m_itemflags;
 
-       if(self.model == "")
-    {
-        error(strcat("^1Tried to spawn ", itemname, " with no model!\n"));
-        return;
-    }
+       startitem_failed = false;
 
-       if(self.item_pickupsound == "")
-               self.item_pickupsound = pickupsound;
+       this.item_model_ent = itemmodel;
+    this.item_pickupsound_ent = pickupsound;
 
-       if(!self.respawntime) // both need to be set
+       if(!this.respawntime) // both need to be set
        {
-               self.respawntime = defaultrespawntime;
-               self.respawntimejitter = defaultrespawntimejitter;
+               this.respawntime = defaultrespawntime;
+               this.respawntimejitter = defaultrespawntimejitter;
        }
 
-       self.items = itemid;
-       self.weapon = weaponid;
+       int itemid = def.m_itemid;
+       this.items = itemid;
+       int weaponid = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0;
+       this.weapon = weaponid;
 
-       if(!self.fade_end)
+       if(!this.fade_end)
        {
-               self.fade_start = autocvar_g_items_mindist;
-               self.fade_end = autocvar_g_items_maxdist;
+               this.fade_start = autocvar_g_items_mindist;
+               this.fade_end = autocvar_g_items_maxdist;
        }
 
        if(weaponid)
-               self.weapons = WepSet_FromWeapon(weaponid);
+               this.weapons = WepSet_FromWeapon(weaponid);
 
-       self.flags = FL_ITEM | itemflags;
+       this.flags = FL_ITEM | itemflags;
 
-       if(MUTATOR_CALLHOOK(FilterItem)) // error means we do not want the item
+       if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item
        {
                startitem_failed = true;
-               remove(self);
+               remove(this);
                return;
        }
 
        // is it a dropped weapon?
-       if (self.classname == "droppedweapon")
+       if (this.classname == "droppedweapon")
        {
-               self.reset = SUB_Remove;
+               this.reset = SUB_Remove;
                // it's a dropped weapon
-               self.movetype = MOVETYPE_TOSS;
+               this.movetype = MOVETYPE_TOSS;
 
                // Savage: remove thrown items after a certain period of time ("garbage collection")
-               self.think = RemoveItem;
-               self.nextthink = time + 20;
+               this.think = RemoveItem;
+               this.nextthink = time + 20;
 
-               self.takedamage = DAMAGE_YES;
-               self.event_damage = Item_Damage;
+               this.takedamage = DAMAGE_YES;
+               this.event_damage = Item_Damage;
 
-               if(self.strength_finished || self.invincible_finished || self.superweapons_finished)
-               /*
-               if(self.items == 0)
-               if(!(self.weapons & ~WEPSET_SUPERWEAPONS)) // only superweapons
-               if(self.ammo_nails == 0)
-               if(self.ammo_cells == 0)
-               if(self.ammo_rockets == 0)
-               if(self.ammo_shells == 0)
-               if(self.ammo_fuel == 0)
-               if(self.health == 0)
-               if(self.armorvalue == 0)
-               */
+               if(this.strength_finished || this.invincible_finished || this.superweapons_finished)
                {
                        // if item is worthless after a timer, have it expire then
-                       self.nextthink = max(self.strength_finished, self.invincible_finished, self.superweapons_finished);
+                       this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
                }
 
                // don't drop if in a NODROP zone (such as lava)
-               traceline(self.origin, self.origin, MOVE_NORMAL, self);
+               traceline(this.origin, this.origin, MOVE_NORMAL, this);
                if (trace_dpstartcontents & DPCONTENTS_NODROP)
                {
                        startitem_failed = true;
-                       remove(self);
+                       remove(this);
                        return;
                }
        }
        else
        {
-               if(!have_pickup_item())
+               if(!have_pickup_item(this))
                {
                        startitem_failed = true;
-                       remove (self);
+                       remove (this);
                        return;
                }
 
-               if(self.angles != '0 0 0')
-                       self.SendFlags |= ISF_ANGLES;
+               if(this.angles != '0 0 0')
+                       this.SendFlags |= ISF_ANGLES;
 
-               self.reset = Item_Reset;
+               this.reset = Item_Reset_self;
                // it's a level item
-               if(self.spawnflags & 1)
-                       self.noalign = 1;
-               if (self.noalign)
-                       self.movetype = MOVETYPE_NONE;
+               if(this.spawnflags & 1)
+                       this.noalign = 1;
+               if (this.noalign > 0)
+                       this.movetype = MOVETYPE_NONE;
                else
-                       self.movetype = MOVETYPE_TOSS;
+                       this.movetype = MOVETYPE_TOSS;
                // do item filtering according to game mode and other things
-               if (!self.noalign)
+               if (this.noalign <= 0)
                {
                        // first nudge it off the floor a little bit to avoid math errors
-                       setorigin(self, self.origin + '0 0 1');
+                       setorigin(this, this.origin + '0 0 1');
                        // set item size before we spawn a spawnfunc_waypoint
-                       if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
-                               setsize (self, '-16 -16 0', '16 16 48');
-                       else
-                               setsize (self, '-16 -16 0', '16 16 32');
-                       self.SendFlags |= ISF_SIZE;
+                       setsize(this, def.m_mins, def.m_maxs);
+                       this.SendFlags |= ISF_SIZE;
                        // note droptofloor returns false if stuck/or would fall too far
-                       droptofloor();
-                       waypoint_spawnforitem(self);
+                       if (!this.noalign)
+                               WITH(entity, self, this, droptofloor());
+                       waypoint_spawnforitem(this);
                }
 
                /*
                 * can't do it that way, as it would break maps
                 * TODO make a target_give like entity another way, that perhaps has
                 * the weapon name in a key
-               if(self.targetname)
+               if(this.targetname)
                {
                        // target_give not yet supported; maybe later
-                       print("removed targeted ", self.classname, "\n");
+                       print("removed targeted ", this.classname, "\n");
                        startitem_failed = true;
-                       remove (self);
+                       remove (this);
                        return;
                }
                */
 
                if(autocvar_spawn_debug >= 2)
                {
-                       entity otheritem;
-                       for(otheritem = findradius(self.origin, 3); otheritem; otheritem = otheritem.chain)
+                       for(entity otheritem = findradius(this.origin, 3); otheritem; otheritem = otheritem.chain)
                        {
                            // why not flags & fl_item?
                                if(otheritem.is_item)
                                {
-                                       LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(self.origin));
+                                       LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
                                        LOG_TRACE(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
                                        error("Mapper sucks.");
                                }
                        }
-                       self.is_item = true;
+                       this.is_item = true;
                }
 
                weaponsInMap |= WepSet_FromWeapon(weaponid);
 
-               precache_model (self.model);
-               precache_sound (self.item_pickupsound);
-
-               precache_sound ("misc/itemrespawncountdown.wav");
-               if(itemid == ITEM_Strength.m_itemid)
-                       precache_sound ("misc/strength_respawn.wav");
-               else if(itemid == ITEM_Shield.m_itemid)
-                       precache_sound ("misc/shield_respawn.wav");
-               else
-                       precache_sound ("misc/itemrespawn.wav");
+               precache_model(this.model);
+               precache_sound(this.item_pickupsound);
 
-               if((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)))
-                       self.target = "###item###"; // for finding the nearest item using find()
+               if (   def.instanceOfPowerup
+                       || def.instanceOfWeaponPickup
+                       || (def.instanceOfHealth && def != ITEM_HealthSmall)
+                       || (def.instanceOfArmor && def != ITEM_ArmorSmall)
+                       || (itemid & (IT_KEY1 | IT_KEY2))
+               ) this.target = "###item###"; // for finding the nearest item using find()
 
-               Item_ItemsTime_SetTime(self, 0);
+               Item_ItemsTime_SetTime(this, 0);
        }
 
-       self.bot_pickup = true;
-       self.bot_pickupevalfunc = pickupevalfunc;
-       self.bot_pickupbasevalue = pickupbasevalue;
-       self.mdl = self.model;
-       self.netname = itemname;
-       self.touch = Item_Touch;
-       setmodel(self, MDL_Null); // precision set below
-       //self.effects |= EF_LOWPRECISION;
+       this.bot_pickup = true;
+       this.bot_pickupevalfunc = pickupevalfunc;
+       this.bot_pickupbasevalue = pickupbasevalue;
+       this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str());
+       this.netname = itemname;
+       this.touch = Item_Touch;
+       setmodel(this, MDL_Null); // precision set below
+       //this.effects |= EF_LOWPRECISION;
 
-       if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
-       {
-               self.pos1 = '-16 -16 0';
-               self.pos2 = '16 16 48';
-       }
-       else
-       {
-               self.pos1 = '-16 -16 0';
-               self.pos2 = '16 16 32';
-       }
-       setsize (self, self.pos1, self.pos2);
+       setsize (this, this.pos1 =  def.m_mins, this.pos2 = def.m_maxs);
 
-       self.SendFlags |= ISF_SIZE;
+       this.SendFlags |= ISF_SIZE;
 
-       if(itemflags & FL_POWERUP)
-               self.ItemStatus |= ITS_ANIMATE1;
-
-       if(self.armorvalue || self.health)
-               self.ItemStatus |= ITS_ANIMATE2;
+       if (!(this.spawnflags & 1024)) {
+               if(def.instanceOfPowerup)
+                       this.ItemStatus |= ITS_ANIMATE1;
+       
+               if(this.armorvalue || this.health)
+                       this.ItemStatus |= ITS_ANIMATE2;
+       }
 
-       if(itemflags & FL_WEAPON)
+       if(def.instanceOfWeaponPickup)
        {
-               if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
-                       self.colormap = 1024; // color shirt=0 pants=0 grey
+               if (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely
+                       this.colormap = 1024; // color shirt=0 pants=0 grey
                else
-                       self.gravity = 1;
-
-               self.ItemStatus |= ITS_ANIMATE1;
-               self.ItemStatus |= ISF_COLORMAP;
+                       this.gravity = 1;
+               if (!(this.spawnflags & 1024))
+                       this.ItemStatus |= ITS_ANIMATE1;
+               this.ItemStatus |= ISF_COLORMAP;
        }
 
-       self.state = 0;
-       if(self.team) // broken, no idea why.
+       this.state = 0;
+       if(this.team) // broken, no idea why.
        {
-               if(!self.cnt)
-                       self.cnt = 1; // item probability weight
+               if(!this.cnt)
+                       this.cnt = 1; // item probability weight
 
-               self.effects |= EF_NODRAW; // marker for item team search
-               InitializeEntity(self, Item_FindTeam, INITPRIO_FINDTARGET);
+               this.effects |= EF_NODRAW; // marker for item team search
+               InitializeEntity(this, Item_FindTeam, INITPRIO_FINDTARGET);
        }
        else
-               Item_Reset();
+               Item_Reset(this);
 
-       Net_LinkEntity(self, !((itemflags & FL_POWERUP) || self.health || self.armorvalue), 0, ItemSend);
+       Net_LinkEntity(this, !(def.instanceOfPowerup || def.instanceOfHealth || def.instanceOfArmor), 0, ItemSend);
 
        // call this hook after everything else has been done
-       if(MUTATOR_CALLHOOK(Item_Spawn, self))
+       if (MUTATOR_CALLHOOK(Item_Spawn, this))
        {
                startitem_failed = true;
-               remove(self);
+               remove(this);
                return;
        }
 }
 
-string Item_Model(string item_mdl)
+void StartItem(entity this, GameItem def)
 {
-       string output = strcat("models/items/", item_mdl);
-       MUTATOR_CALLHOOK(ItemModel, item_mdl, output);
-       return strzone(item_model_output);
-}
-
-void StartItemA (entity a)
-{SELFPARAM();
-    self.itemdef = a;
-    StartItem(Item_Model(a.m_model), a.m_sound, a.m_respawntime(), a.m_respawntimejitter(), a.m_name, a.m_itemid, 0, a.m_itemflags, a.m_pickupevalfunc, a.m_botvalue);
+    _StartItem(
+       this,
+       this.itemdef = def,
+       def.m_respawntime(), // defaultrespawntime
+       def.m_respawntimejitter() // defaultrespawntimejitter
+       );
 }
 
-void spawnfunc_item_rockets()
-{SELFPARAM();
+spawnfunc(item_rockets)
+{
        if(!self.ammo_rockets)
                self.ammo_rockets = g_pickup_rockets;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-    StartItemA (ITEM_Rockets);
+    StartItem(this, ITEM_Rockets);
 }
 
-void spawnfunc_item_bullets()
-{SELFPARAM();
+spawnfunc(item_bullets)
+{
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
        {
                weaponswapping = true;
-               spawnfunc_item_shells();
+               spawnfunc_item_shells(this);
                weaponswapping = false;
                return;
        }
@@ -1234,35 +1219,35 @@ void spawnfunc_item_bullets()
                self.ammo_nails = g_pickup_nails;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-    StartItemA (ITEM_Bullets);
+    StartItem(this, ITEM_Bullets);
 }
 
-void spawnfunc_item_cells()
-{SELFPARAM();
+spawnfunc(item_cells)
+{
        if(!self.ammo_cells)
                self.ammo_cells = g_pickup_cells;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-       StartItemA (ITEM_Cells);
+       StartItem(this, ITEM_Cells);
 }
 
-void spawnfunc_item_plasma()
-{SELFPARAM();
+spawnfunc(item_plasma)
+{
        if(!self.ammo_plasma)
                self.ammo_plasma = g_pickup_plasma;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-       StartItemA (ITEM_Plasma);
+       StartItem(this, ITEM_Plasma);
 }
 
-void spawnfunc_item_shells()
-{SELFPARAM();
+spawnfunc(item_shells)
+{
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
        if(self.classname != "droppedweapon")
        {
                weaponswapping = true;
-               spawnfunc_item_bullets();
+               spawnfunc_item_bullets(this);
                weaponswapping = false;
                return;
        }
@@ -1271,121 +1256,120 @@ void spawnfunc_item_shells()
                self.ammo_shells = g_pickup_shells;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-       StartItemA (ITEM_Shells);
+       StartItem(this, ITEM_Shells);
 }
 
-void spawnfunc_item_armor_small()
-{SELFPARAM();
+spawnfunc(item_armor_small)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorsmall;
        if(!self.max_armorvalue)
                self.max_armorvalue = g_pickup_armorsmall_max;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_armorsmall_anyway;
-       StartItemA (ITEM_ArmorSmall);
+       StartItem(this, ITEM_ArmorSmall);
 }
 
-void spawnfunc_item_armor_medium()
-{SELFPARAM();
+spawnfunc(item_armor_medium)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armormedium;
        if(!self.max_armorvalue)
                self.max_armorvalue = g_pickup_armormedium_max;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_armormedium_anyway;
-       StartItemA (ITEM_ArmorMedium);
+       StartItem(this, ITEM_ArmorMedium);
 }
 
-void spawnfunc_item_armor_big()
-{SELFPARAM();
+spawnfunc(item_armor_big)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorbig;
        if(!self.max_armorvalue)
                self.max_armorvalue = g_pickup_armorbig_max;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_armorbig_anyway;
-       StartItemA (ITEM_ArmorLarge);
+       StartItem(this, ITEM_ArmorLarge);
 }
 
-void spawnfunc_item_armor_large()
-{SELFPARAM();
+spawnfunc(item_armor_large)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorlarge;
        if(!self.max_armorvalue)
                self.max_armorvalue = g_pickup_armorlarge_max;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_armorlarge_anyway;
-       StartItemA (ITEM_ArmorMega);
+       StartItem(this, ITEM_ArmorMega);
 }
 
-void spawnfunc_item_health_small()
-{SELFPARAM();
+spawnfunc(item_health_small)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthsmall_max;
        if(!self.health)
                self.health = g_pickup_healthsmall;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_healthsmall_anyway;
-       StartItemA (ITEM_HealthSmall);
+       StartItem(this, ITEM_HealthSmall);
 }
 
-void spawnfunc_item_health_medium()
-{SELFPARAM();
+spawnfunc(item_health_medium)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthmedium_max;
        if(!self.health)
                self.health = g_pickup_healthmedium;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_healthmedium_anyway;
-    StartItemA (ITEM_HealthMedium);
+    StartItem(this, ITEM_HealthMedium);
 }
 
-void spawnfunc_item_health_large()
-{SELFPARAM();
+spawnfunc(item_health_large)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthlarge_max;
        if(!self.health)
                self.health = g_pickup_healthlarge;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_healthlarge_anyway;
-       StartItemA (ITEM_HealthLarge);
+       StartItem(this, ITEM_HealthLarge);
 }
 
-void spawnfunc_item_health_mega()
-{SELFPARAM();
+spawnfunc(item_health_mega)
+{
     if(!self.max_health)
         self.max_health = g_pickup_healthmega_max;
     if(!self.health)
         self.health = g_pickup_healthmega;
     if(!self.pickup_anyway)
         self.pickup_anyway = g_pickup_healthmega_anyway;
-    StartItemA (ITEM_HealthMega);
+    StartItem(this, ITEM_HealthMega);
 }
 
 // support old misnamed entities
-void spawnfunc_item_armor1() { spawnfunc_item_armor_small(); }  // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-void spawnfunc_item_armor25() { spawnfunc_item_armor_large(); }
-void spawnfunc_item_health1() { spawnfunc_item_health_small(); }
-void spawnfunc_item_health25() { spawnfunc_item_health_medium(); }
-void spawnfunc_item_health100() { spawnfunc_item_health_mega(); }
+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_large(this); }
+spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
+spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
+spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
 
-void spawnfunc_item_strength()
-{SELFPARAM();
-               precache_sound("weapons/strength_fire.wav");
+spawnfunc(item_strength)
+{
                if(!self.strength_finished)
                        self.strength_finished = autocvar_g_balance_powerup_strength_time;
-               StartItemA (ITEM_Strength);
+               StartItem(this, ITEM_Strength);
 }
 
-void spawnfunc_item_invincible()
-{SELFPARAM();
+spawnfunc(item_invincible)
+{
                if(!self.invincible_finished)
                        self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
-               StartItemA (ITEM_Shield);
+               StartItem(this, ITEM_Shield);
 }
 
 // compatibility:
-void spawnfunc_item_quad() {SELFPARAM(); self.classname = "item_strength";spawnfunc_item_strength();}
+spawnfunc(item_quad) { self.classname = "item_strength";spawnfunc_item_strength(this);}
 
 void target_items_use()
 {SELFPARAM();
@@ -1411,8 +1395,8 @@ void target_items_use()
                centerprint(activator, self.message);
 }
 
-void spawnfunc_target_items (void)
-{SELFPARAM();
+spawnfunc(target_items)
+{
        float n, i, j;
        entity e;
        string s;
@@ -1425,13 +1409,6 @@ void spawnfunc_target_items (void)
        if(!self.superweapons_finished)
                self.superweapons_finished = autocvar_g_balance_superweapons_time;
 
-       precache_sound("misc/itempickup.wav");
-       precache_sound("misc/megahealth.wav");
-       precache_sound("misc/armor25.wav");
-       precache_sound("misc/powerup.wav");
-       precache_sound("misc/poweroff.wav");
-       precache_sound(W_Sound("weaponpickup"));
-
        n = tokenize_console(self.netname);
        if(argv(0) == "give")
        {
@@ -1458,8 +1435,10 @@ void spawnfunc_target_items (void)
                                        if(s == e.netname)
                                        {
                                                self.weapons |= WepSet_FromWeapon(j);
-                                               if(self.spawnflags == 0 || self.spawnflags == 2)
-                                                       WEP_ACTION(e.weapon, WR_INIT);
+                                               if(self.spawnflags == 0 || self.spawnflags == 2) {
+                                                       Weapon w = get_weaponinfo(e.weapon);
+                                                       w.wr_init(w);
+                                               }
                                                break;
                                        }
                                }
@@ -1529,42 +1508,43 @@ void spawnfunc_target_items (void)
                        e = get_weaponinfo(j);
                        if(argv(i) == e.netname)
                        {
-                               WEP_ACTION(e.weapon, WR_INIT);
+                               Weapon w = get_weaponinfo(e.weapon);
+                               w.wr_init(w);
                                break;
                        }
                }
        }
 }
 
-void spawnfunc_item_fuel(void)
-{SELFPARAM();
+spawnfunc(item_fuel)
+{
        if(!self.ammo_fuel)
                self.ammo_fuel = g_pickup_fuel;
        if(!self.pickup_anyway)
                self.pickup_anyway = g_pickup_ammo_anyway;
-       StartItemA (ITEM_JetpackFuel);
+       StartItem(this, ITEM_JetpackFuel);
 }
 
-void spawnfunc_item_fuel_regen(void)
+spawnfunc(item_fuel_regen)
 {
        if(start_items & ITEM_JetpackRegen.m_itemid)
        {
-               spawnfunc_item_fuel();
+               spawnfunc_item_fuel(this);
                return;
        }
-       StartItemA (ITEM_JetpackRegen);
+       StartItem(this, ITEM_JetpackRegen);
 }
 
-void spawnfunc_item_jetpack(void)
-{SELFPARAM();
+spawnfunc(item_jetpack)
+{
        if(!self.ammo_fuel)
                self.ammo_fuel = g_pickup_fuel_jetpack;
        if(start_items & ITEM_Jetpack.m_itemid)
        {
-               spawnfunc_item_fuel();
+               spawnfunc_item_fuel(this);
                return;
        }
-       StartItemA (ITEM_Jetpack);
+       StartItem(this, ITEM_Jetpack);
 }
 
 float GiveWeapon(entity e, float wpn, float op, float val)
@@ -1660,12 +1640,12 @@ void GiveSound(entity e, float v0, float v1, float t, string snd_incr, string sn
        if(v1 <= v0 - t)
        {
                if(snd_decr != "")
-                       sound (e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM);
+                       _sound (e, CH_TRIGGER, snd_decr, VOL_BASE, ATTEN_NORM);
        }
        else if(v0 >= v0 + t)
        {
                if(snd_incr != "")
-                       sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTEN_NORM);
+                       _sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTEN_NORM);
        }
 }
 
@@ -1831,31 +1811,33 @@ float GiveItems(entity e, float beginarg, float endarg)
                op = OP_SET;
        }
 
-       POSTGIVE_BIT(e, items, ITEM_JetpackRegen.m_itemid, "misc/itempickup.wav", string_null);
-       POSTGIVE_BIT(e, items, IT_UNLIMITED_SUPERWEAPONS, "misc/powerup.wav", "misc/poweroff.wav");
-       POSTGIVE_BIT(e, items, IT_UNLIMITED_WEAPON_AMMO, "misc/powerup.wav", "misc/poweroff.wav");
-       POSTGIVE_BIT(e, items, ITEM_Jetpack.m_itemid, "misc/itempickup.wav", string_null);
+       POSTGIVE_BIT(e, items, ITEM_JetpackRegen.m_itemid, SND(ITEMPICKUP), string_null);
+       POSTGIVE_BIT(e, items, IT_UNLIMITED_SUPERWEAPONS, SND(POWERUP), SND(POWEROFF));
+       POSTGIVE_BIT(e, items, IT_UNLIMITED_WEAPON_AMMO, SND(POWERUP), SND(POWEROFF));
+       POSTGIVE_BIT(e, items, ITEM_Jetpack.m_itemid, SND(ITEMPICKUP), string_null);
        for(j = WEP_FIRST; j <= WEP_LAST; ++j)
        {
                wi = get_weaponinfo(j);
                if(wi.weapon)
                {
-                       POSTGIVE_WEAPON(e, j, W_Sound("weaponpickup"), string_null);
+                       POSTGIVE_WEAPON(e, j, SND(WEAPONPICKUP), string_null);
                        if (!(save_weapons & WepSet_FromWeapon(j)))
-                               if(e.weapons & WepSet_FromWeapon(j))
-                                       WEP_ACTION(wi.weapon, WR_INIT);
+                               if(e.weapons & WepSet_FromWeapon(j)) {
+                                       Weapon w = get_weaponinfo(wi.weapon);
+                                       w.wr_init(w);
+                               }
                }
        }
-       POSTGIVE_VALUE(e, strength_finished, 1, "misc/powerup.wav", "misc/poweroff.wav");
-       POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", "misc/poweroff.wav");
-       POSTGIVE_VALUE(e, ammo_nails, 0, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE(e, ammo_cells, 0, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE(e, ammo_plasma, 0, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE(e, ammo_shells, 0, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE(e, ammo_rockets, 0, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, "misc/itempickup.wav", string_null);
-       POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/armor25.wav", string_null);
-       POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, "misc/megahealth.wav", string_null);
+       POSTGIVE_VALUE(e, strength_finished, 1, SND(POWERUP), SND(POWEROFF));
+       POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", SND(POWEROFF));
+       POSTGIVE_VALUE(e, ammo_nails, 0, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE(e, ammo_cells, 0, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE(e, ammo_plasma, 0, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE(e, ammo_shells, 0, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE(e, ammo_rockets, 0, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND(ITEMPICKUP), string_null);
+       POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND(ARMOR25), string_null);
+       POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND(MEGAHEALTH), string_null);
 
        if(e.superweapons_finished <= 0)
                if(self.weapons & WEPSET_SUPERWEAPONS)