]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/t_items.qc
Merge branch 'TimePath/bot_api' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / t_items.qc
index 8213994144ce63aa3b625d4be12efd86d5f7d10c..964b05e1bd2f69099745e91c212191d8f356b209 100644 (file)
@@ -5,11 +5,6 @@
 #if defined(SVQC)
     #include "_all.qh"
 
-    #include "waypointsprites.qh"
-
-    #include "bot/bot.qh"
-    #include "bot/waypoints.qh"
-
     #include "mutators/mutators_include.qh"
 
     #include "weapons/common.qh"
@@ -30,7 +25,7 @@
 #endif
 
 #ifdef CSQC
-void ItemDraw()
+void ItemDraw(entity self)
 {
     if(self.gravity)
     {
@@ -66,7 +61,7 @@ void ItemDraw()
     }
 }
 
-void ItemDrawSimple()
+void ItemDrawSimple(entity this)
 {
     if(self.gravity)
     {
@@ -77,8 +72,28 @@ void ItemDrawSimple()
     }
 }
 
+void Item_PreDraw()
+{SELFPARAM();
+       vector org;
+       float alph;
+       org = getpropertyvec(VF_ORIGIN);
+       if(!checkpvs(org, self)) // this makes sense as long as we don't support recursive warpzones
+               alph = 0;
+       else if(self.fade_start)
+               alph = bound(0, (self.fade_end - vlen(org - self.origin - 0.5 * (self.mins + self.maxs))) / (self.fade_end - self.fade_start), 1);
+       else
+               alph = 1;
+       //printf("%v <-> %v\n", view_origin, self.origin + 0.5 * (self.mins + self.maxs));
+       if(self.ItemStatus & ITS_AVAILABLE)
+               self.alpha = alph;
+       if(alph <= 0)
+               self.drawmask = 0;
+       else
+               self.drawmask = MASK_NORMAL;
+}
+
 void ItemRead(float _IsNew)
-{
+{SELFPARAM();
     int sf = ReadByte();
 
     if(sf & ISF_LOCATION)
@@ -92,21 +107,16 @@ void ItemRead(float _IsNew)
 
     if(sf & ISF_ANGLES)
     {
-        self.angles_x = ReadCoord();
-        self.angles_y = ReadCoord();
-        self.angles_z = ReadCoord();
+        self.angles_x = ReadAngle();
+        self.angles_y = ReadAngle();
+        self.angles_z = ReadAngle();
         self.move_angles = self.angles;
     }
 
     if(sf & ISF_SIZE)
     {
-        self.mins_x = ReadCoord();
-        self.mins_y = ReadCoord();
-        self.mins_z = ReadCoord();
-        self.maxs_x = ReadCoord();
-        self.maxs_y = ReadCoord();
-        self.maxs_z = ReadCoord();
-        setsize(self, self.mins, self.maxs);
+        float use_bigsize = ReadByte();
+        setsize(self, '-16 -16 0', (use_bigsize) ? '16 16 48' : '16 16 32');
     }
 
     if(sf & ISF_STATUS) // need to read/write status frist so model can handle simple, fb etc.
@@ -152,9 +162,14 @@ void ItemRead(float _IsNew)
     if(sf & ISF_MODEL)
     {
         self.drawmask  = MASK_NORMAL;
-        self.movetype  = MOVETYPE_TOSS;
+               self.move_movetype = self.movetype = MOVETYPE_TOSS;
         self.draw       = ItemDraw;
 
+        self.fade_end = ReadShort();
+        self.fade_start = ReadShort();
+        if(self.fade_start && !autocvar_cl_items_nofade)
+               self.predraw = Item_PreDraw;
+
         if(self.mdl)
             strunzone(self.mdl);
 
@@ -166,8 +181,6 @@ void ItemRead(float _IsNew)
             string _fn2 = substring(_fn, 0 , strlen(_fn) -4);
             self.draw = ItemDrawSimple;
 
-
-
             if(fexists(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix)))
                 self.mdl = strzone(sprintf("%s%s.md3", _fn2, autocvar_cl_simpleitems_postfix));
             else if(fexists(sprintf("%s%s.dpm", _fn2, autocvar_cl_simpleitems_postfix)))
@@ -179,7 +192,7 @@ void ItemRead(float _IsNew)
             else
             {
                 self.draw = ItemDraw;
-                dprint("Simple item requested for ", _fn, " but no model exists for it\n");
+                LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it\n");
             }
         }
 
@@ -188,10 +201,10 @@ void ItemRead(float _IsNew)
 
 
         if(self.mdl == "")
-            dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, " tell tZork aboute this!\n");
+            LOG_TRACE("^1WARNING!^7 self.mdl is unset for item ", self.classname, " tell tZork aboute this!\n");
 
         precache_model(self.mdl);
-        setmodel(self, self.mdl);
+        _setmodel(self, self.mdl);
     }
 
     if(sf & ISF_COLORMAP)
@@ -230,65 +243,62 @@ void ItemRead(float _IsNew)
 #endif
 
 #ifdef SVQC
-bool ItemSend(entity to, int sf)
+bool ItemSend(entity this, entity to, int sf)
 {
-    if(self.gravity)
-        sf |= ISF_DROP;
-    else
-        sf &= ~ISF_DROP;
+       if(self.gravity)
+               sf |= ISF_DROP;
+       else
+               sf &= ~ISF_DROP;
 
        WriteByte(MSG_ENTITY, ENT_CLIENT_ITEM);
        WriteByte(MSG_ENTITY, sf);
 
        //WriteByte(MSG_ENTITY, self.cnt);
-    if(sf & ISF_LOCATION)
-    {
-        WriteCoord(MSG_ENTITY, self.origin.x);
-        WriteCoord(MSG_ENTITY, self.origin.y);
-        WriteCoord(MSG_ENTITY, self.origin.z);
-    }
+       if(sf & ISF_LOCATION)
+       {
+               WriteCoord(MSG_ENTITY, self.origin.x);
+               WriteCoord(MSG_ENTITY, self.origin.y);
+               WriteCoord(MSG_ENTITY, self.origin.z);
+       }
 
-    if(sf & ISF_ANGLES)
-    {
-        WriteCoord(MSG_ENTITY, self.angles.x);
-        WriteCoord(MSG_ENTITY, self.angles.y);
-        WriteCoord(MSG_ENTITY, self.angles.z);
-    }
+       if(sf & ISF_ANGLES)
+       {
+               WriteAngle(MSG_ENTITY, self.angles_x);
+               WriteAngle(MSG_ENTITY, self.angles_y);
+               WriteAngle(MSG_ENTITY, self.angles_z);
+       }
 
-    if(sf & ISF_SIZE)
-    {
-        WriteCoord(MSG_ENTITY, self.mins.x);
-        WriteCoord(MSG_ENTITY, self.mins.y);
-        WriteCoord(MSG_ENTITY, self.mins.z);
-        WriteCoord(MSG_ENTITY, self.maxs.x);
-        WriteCoord(MSG_ENTITY, self.maxs.y);
-        WriteCoord(MSG_ENTITY, self.maxs.z);
-    }
+       if(sf & ISF_SIZE)
+       {
+               WriteByte(MSG_ENTITY, ((self.flags & FL_POWERUP) || self.health || self.armorvalue));
+       }
 
-    if(sf & ISF_STATUS)
-        WriteByte(MSG_ENTITY, self.ItemStatus);
+       if(sf & ISF_STATUS)
+               WriteByte(MSG_ENTITY, self.ItemStatus);
 
-    if(sf & ISF_MODEL)
-    {
+       if(sf & ISF_MODEL)
+       {
+               WriteShort(MSG_ENTITY, self.fade_end);
+               WriteShort(MSG_ENTITY, self.fade_start);
 
-        if(self.mdl == "")
-            dprint("^1WARNING!^7 self.mdl is unset for item ", self.classname, "exspect a crash just aboute now\n");
+               if(self.mdl == "")
+                       LOG_TRACE("^1WARNING!^7 self.mdl is unset for item ", self.classname, "exspect a crash just aboute now\n");
 
-        WriteString(MSG_ENTITY, self.mdl);
-    }
+               WriteString(MSG_ENTITY, self.mdl);
+       }
 
 
-    if(sf & ISF_COLORMAP)
-        WriteShort(MSG_ENTITY, self.colormap);
+       if(sf & ISF_COLORMAP)
+               WriteShort(MSG_ENTITY, self.colormap);
 
-    if(sf & ISF_DROP)
-    {
-        WriteCoord(MSG_ENTITY, self.velocity.x);
-        WriteCoord(MSG_ENTITY, self.velocity.y);
-        WriteCoord(MSG_ENTITY, self.velocity.z);
-    }
+       if(sf & ISF_DROP)
+       {
+               WriteCoord(MSG_ENTITY, self.velocity.x);
+               WriteCoord(MSG_ENTITY, self.velocity.y);
+               WriteCoord(MSG_ENTITY, self.velocity.z);
+       }
 
-    return true;
+       return true;
 }
 
 void ItemUpdate(entity item)
@@ -297,7 +307,7 @@ void ItemUpdate(entity item)
 }
 
 float have_pickup_item(void)
-{
+{SELFPARAM();
        if(self.flags & FL_POWERUP)
        {
                if(autocvar_g_powerups > 0)
@@ -385,25 +395,25 @@ void Item_Show (entity e, float mode)
        }
 
        if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid)
-           e.ItemStatus |= ITS_POWERUP;
+               e.ItemStatus |= ITS_POWERUP;
 
        if (autocvar_g_nodepthtestitems)
                e.effects |= EF_NODEPTHTEST;
 
 
-    if (autocvar_g_fullbrightitems)
+       if (autocvar_g_fullbrightitems)
                e.ItemStatus |= ITS_ALLOWFB;
 
        if (autocvar_sv_simple_items)
-        e.ItemStatus |= ITS_ALLOWSI;
+               e.ItemStatus |= ITS_ALLOWSI;
 
        // relink entity (because solid may have changed)
        setorigin(e, e.origin);
-    e.SendFlags |= ISF_STATUS;
+       e.SendFlags |= ISF_STATUS;
 }
 
 void Item_Think()
-{
+{SELFPARAM();
        self.nextthink = time;
        if(self.origin != self.oldorigin)
        {
@@ -412,21 +422,22 @@ void Item_Think()
        }
 }
 
+bool Item_ItemsTime_SpectatorOnly(GameItem it);
 bool Item_ItemsTime_Allow(GameItem it, WepSet _weapons);
 float Item_ItemsTime_UpdateTime(entity e, float t);
 void Item_ItemsTime_SetTime(entity e, float t);
 void Item_ItemsTime_SetTimesForAllPlayers();
 
 void Item_Respawn (void)
-{
+{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))
@@ -439,12 +450,12 @@ void Item_Respawn (void)
        self.think = Item_Think;
        self.nextthink = time;
 
-       //Send_Effect("item_respawn", self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
-       Send_Effect("item_respawn", CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+       //Send_Effect(EFFECT_ITEM_RESPAWN, self.origin + self.mins_z * '0 0 1' + '0 0 48', '0 0 0', 1);
+       Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
 }
 
 void Item_RespawnCountdown (void)
-{
+{SELFPARAM();
        if(self.count >= ITEM_RESPAWN_TICKS)
        {
                if(self.waypointsprite_attached)
@@ -457,65 +468,56 @@ void Item_RespawnCountdown (void)
                self.count += 1;
                if(self.count == 1)
                {
-                       string name = string_null;
-                       vector rgb = '1 0 1';
-            entity e = self.itemdef;
-            if (e) {
-                name = e.m_waypoint;
-                rgb = e.m_color;
-            }
-                       MUTATOR_CALLHOOK(Item_RespawnCountdown, name, rgb);
-                       name = item_name;
-                       rgb = item_color;
-                       if(self.flags & FL_WEAPON)
-                       {
-                               entity wi = get_weaponinfo(self.weapon);
-                               if(wi)
+                       MUTATOR_CALLHOOK(Item_RespawnCountdown, string_null, '0 0 0');
+                       do {
                                {
-                                       name = wi.wpmodel;
-                                       rgb = '1 0 0';
+                                       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;
+                                       }
                                }
-                       }
-                       if(name)
-                       {
-                               WaypointSprite_Spawn(name, 0, 0, self, '0 0 64', world, 0, self, waypointsprite_attached, true, RADARICON_POWERUP, rgb);
-                               if(self.waypointsprite_attached)
                                {
-                                       if (self.items == IT_HEALTH || self.items == IT_ARMOR)
-                                               WaypointSprite_UpdateRule(self.waypointsprite_attached, 0, SPRITERULE_SPECTATOR);
-                                       WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
+                                       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;
+                                       }
                                }
-                       }
-                       else
-                       {
-                               print("Unknown powerup-marked item is wanting to respawn\n");
-                               localcmd(sprintf("prvm_edict server %d\n", num_for_edict(self)));
-                       }
+                       } while (0);
+            if(self.waypointsprite_attached)
+            {
+                GameItem def = self.itemdef;
+                if (Item_ItemsTime_SpectatorOnly(def))
+                    WaypointSprite_UpdateRule(self.waypointsprite_attached, 0, SPRITERULE_SPECTATOR);
+                WaypointSprite_UpdateBuildFinished(self.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
+            }
                }
 
                if(self.waypointsprite_attached)
                {
+                       setself(self.waypointsprite_attached);
                        entity e;
-                       entity it = self;
-                       self = self.waypointsprite_attached;
                        FOR_EACH_REALCLIENT(e)
                                if(self.waypointsprite_visible_for_player(e))
                                {
                                        msg_entity = e;
-                                       soundto(MSG_ONE, it, 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
                                }
-                       self = it;
+                       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
+                       sound(self, CH_TRIGGER, SND_ITEMRESPAWNCOUNTDOWN, VOL_BASE, ATTEN_NORM);        // play respawn sound
        }
 }
 
 void Item_RespawnThink()
-{
+{SELFPARAM();
        self.nextthink = time;
        if(self.origin != self.oldorigin)
        {
@@ -649,7 +651,7 @@ float Item_GiveTo(entity item, entity player)
                        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);
                        }
                }
@@ -687,6 +689,13 @@ float Item_GiveTo(entity item, entity player)
        if (!pickedup)
                return 0;
 
+       // crude hack to enforce switching weapons
+       if(g_cts && (item.flags & FL_WEAPON))
+       {
+               W_SwitchWeapon_Force(player, item.weapon);
+               return 1;
+       }
+
        if (_switchweapon)
                if (player.switchweapon != w_getbestweapon(player))
                        W_SwitchWeapon_Force(player, w_getbestweapon(player));
@@ -695,7 +704,7 @@ float Item_GiveTo(entity item, entity player)
 }
 
 void Item_Touch (void)
-{
+{SELFPARAM();
        entity e, head;
 
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
@@ -708,18 +717,13 @@ void Item_Touch (void)
                }
        }
 
-       if (!IS_PLAYER(other))
-               return;
-       if (other.frozen)
-               return;
-       if (other.deadflag)
-               return;
-       if (self.solid != SOLID_TRIGGER)
-               return;
-       if (self.owner == other)
-               return;
-       if (time < self.item_spawnshieldtime)
-               return;
+       if(!(other.flags & FL_PICKUPITEMS)
+       || other.frozen
+       || other.deadflag
+       || (self.solid != SOLID_TRIGGER)
+       || (self.owner == other)
+       || (time < self.item_spawnshieldtime)
+       ) { return;}
 
        switch(MUTATOR_CALLHOOK(ItemTouch, self, other))
        {
@@ -751,8 +755,8 @@ void Item_Touch (void)
 
        other.last_pickup = time;
 
-       Send_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(self), '0 0 0', 1);
+       _sound (other, CH_TRIGGER, (self.item_pickupsound ? self.item_pickupsound : self.item_pickupsound_ent.sound_str()), VOL_BASE, ATTEN_NORM);
 
        if (self.classname == "droppedweapon")
                remove (self);
@@ -764,6 +768,7 @@ void Item_Touch (void)
                        for(head = world; (head = findfloat(head, team, self.team)); )
                        {
                                if(head.flags & FL_ITEM)
+                               if(head.classname != "item_flag_team" && head.classname != "item_key_team")
                                {
                                        Item_Show(head, -1);
                                        RandomSelection_Add(head, 0, string_null, head.cnt, 0);
@@ -779,7 +784,7 @@ void Item_Touch (void)
 }
 
 void Item_Reset()
-{
+{SELFPARAM();
        Item_Show(self, !self.state);
        setorigin (self, self.origin);
 
@@ -797,21 +802,25 @@ void Item_Reset()
 }
 
 void Item_FindTeam()
-{
+{SELFPARAM();
        entity head, e;
 
        if(self.effects & EF_NODRAW)
        {
                // marker for item team search
-               dprint("Initializing item team ", ftos(self.team), "\n");
+               LOG_TRACE("Initializing item team ", ftos(self.team), "\n");
                RandomSelection_Init();
-               for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
+               for(head = world; (head = findfloat(head, team, self.team)); )
+               if(head.flags & FL_ITEM)
+               if(head.classname != "item_flag_team" && head.classname != "item_key_team")
                        RandomSelection_Add(head, 0, string_null, head.cnt, 0);
                e = RandomSelection_chosen_ent;
                e.state = 0;
                Item_Show(e, 1);
 
-               for(head = world; (head = findfloat(head, team, self.team)); ) if(head.flags & FL_ITEM)
+               for(head = world; (head = findfloat(head, team, self.team)); )
+               if(head.flags & FL_ITEM)
+               if(head.classname != "item_flag_team" && head.classname != "item_key_team")
                {
                        if(head != e)
                        {
@@ -829,8 +838,9 @@ void Item_FindTeam()
 // Savage: used for item garbage-collection
 // TODO: perhaps nice special effect?
 void RemoveItem(void)
-{
-       Send_Effect("item_pickup", CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+{SELFPARAM();
+       if(wasfreed(self) || !self) { return; }
+       Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(self), '0 0 0', 1);
        remove(self);
 }
 
@@ -962,7 +972,7 @@ void Item_Damage(entity inflictor, entity attacker, float damage, int deathtype,
 }
 
 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 == "")
@@ -986,6 +996,12 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
        self.items = itemid;
        self.weapon = weaponid;
 
+       if(!self.fade_end)
+       {
+               self.fade_start = autocvar_g_items_mindist;
+               self.fade_end = autocvar_g_items_maxdist;
+       }
+
        if(weaponid)
                self.weapons = WepSet_FromWeapon(weaponid);
 
@@ -1047,6 +1063,9 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                        return;
                }
 
+               if(self.angles != '0 0 0')
+                       self.SendFlags |= ISF_ANGLES;
+
                self.reset = Item_Reset;
                // it's a level item
                if(self.spawnflags & 1)
@@ -1065,7 +1084,7 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                                setsize (self, '-16 -16 0', '16 16 48');
                        else
                                setsize (self, '-16 -16 0', '16 16 32');
-
+                       self.SendFlags |= ISF_SIZE;
                        // note droptofloor returns false if stuck/or would fall too far
                        droptofloor();
                        waypoint_spawnforitem(self);
@@ -1093,8 +1112,8 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                            // why not flags & fl_item?
                                if(otheritem.is_item)
                                {
-                                       dprint("XXX Found duplicated item: ", itemname, vtos(self.origin));
-                                       dprint(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
+                                       LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(self.origin));
+                                       LOG_TRACE(" vs ", otheritem.netname, vtos(otheritem.origin), "\n");
                                        error("Mapper sucks.");
                                }
                        }
@@ -1106,14 +1125,6 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
                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");
-
                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()
 
@@ -1126,33 +1137,35 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
        self.mdl = self.model;
        self.netname = itemname;
        self.touch = Item_Touch;
-       setmodel(self, "null"); // precision set below
+       setmodel(self, MDL_Null); // precision set below
        //self.effects |= EF_LOWPRECISION;
 
        if((itemflags & FL_POWERUP) || self.health || self.armorvalue)
-    {
-        self.pos1 = '-16 -16 0';
-        self.pos2 = '16 16 48';
-    }
+       {
+               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);
+       {
+               self.pos1 = '-16 -16 0';
+               self.pos2 = '16 16 32';
+       }
+       setsize (self, self.pos1, self.pos2);
 
-    if(itemflags & FL_POWERUP)
-        self.ItemStatus |= ITS_ANIMATE1;
+       self.SendFlags |= ISF_SIZE;
+
+       if(itemflags & FL_POWERUP)
+               self.ItemStatus |= ITS_ANIMATE1;
 
        if(self.armorvalue || self.health)
-        self.ItemStatus |= ITS_ANIMATE2;
+               self.ItemStatus |= ITS_ANIMATE2;
 
        if(itemflags & FL_WEAPON)
        {
                if (self.classname != "droppedweapon") // if dropped, colormap is already set up nicely
-            self.colormap = 1024; // color shirt=0 pants=0 grey
-        else
-            self.gravity = 1;
+                       self.colormap = 1024; // color shirt=0 pants=0 grey
+               else
+                       self.gravity = 1;
 
                self.ItemStatus |= ITS_ANIMATE1;
                self.ItemStatus |= ISF_COLORMAP;
@@ -1170,11 +1183,7 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
        else
                Item_Reset();
 
-    Net_LinkEntity(self, false, 0, ItemSend);
-
-       self.SendFlags |= ISF_SIZE;
-       if(self.angles)
-               self.SendFlags |= ISF_ANGLES;
+       Net_LinkEntity(self, !((itemflags & FL_POWERUP) || self.health || self.armorvalue), 0, ItemSend);
 
        // call this hook after everything else has been done
        if(MUTATOR_CALLHOOK(Item_Spawn, self))
@@ -1185,20 +1194,14 @@ void StartItem (string itemmodel, string pickupsound, float defaultrespawntime,
        }
 }
 
-string Item_Model(string item_mdl)
-{
-       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(strzone(a.m_model.model_str()), 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);
 }
 
-void spawnfunc_item_rockets (void) {
+spawnfunc(item_rockets)
+{
        if(!self.ammo_rockets)
                self.ammo_rockets = g_pickup_rockets;
        if(!self.pickup_anyway)
@@ -1206,13 +1209,14 @@ void spawnfunc_item_rockets (void) {
     StartItemA (ITEM_Rockets);
 }
 
-void spawnfunc_item_bullets (void) {
+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;
        }
@@ -1224,7 +1228,8 @@ void spawnfunc_item_bullets (void) {
     StartItemA (ITEM_Bullets);
 }
 
-void spawnfunc_item_cells (void) {
+spawnfunc(item_cells)
+{
        if(!self.ammo_cells)
                self.ammo_cells = g_pickup_cells;
        if(!self.pickup_anyway)
@@ -1232,7 +1237,7 @@ void spawnfunc_item_cells (void) {
        StartItemA (ITEM_Cells);
 }
 
-void spawnfunc_item_plasma()
+spawnfunc(item_plasma)
 {
        if(!self.ammo_plasma)
                self.ammo_plasma = g_pickup_plasma;
@@ -1241,13 +1246,14 @@ void spawnfunc_item_plasma()
        StartItemA (ITEM_Plasma);
 }
 
-void spawnfunc_item_shells (void) {
+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;
        }
@@ -1259,7 +1265,8 @@ void spawnfunc_item_shells (void) {
        StartItemA (ITEM_Shells);
 }
 
-void spawnfunc_item_armor_small (void) {
+spawnfunc(item_armor_small)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorsmall;
        if(!self.max_armorvalue)
@@ -1269,7 +1276,8 @@ void spawnfunc_item_armor_small (void) {
        StartItemA (ITEM_ArmorSmall);
 }
 
-void spawnfunc_item_armor_medium (void) {
+spawnfunc(item_armor_medium)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armormedium;
        if(!self.max_armorvalue)
@@ -1279,7 +1287,8 @@ void spawnfunc_item_armor_medium (void) {
        StartItemA (ITEM_ArmorMedium);
 }
 
-void spawnfunc_item_armor_big (void) {
+spawnfunc(item_armor_big)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorbig;
        if(!self.max_armorvalue)
@@ -1289,7 +1298,8 @@ void spawnfunc_item_armor_big (void) {
        StartItemA (ITEM_ArmorLarge);
 }
 
-void spawnfunc_item_armor_large (void) {
+spawnfunc(item_armor_large)
+{
        if(!self.armorvalue)
                self.armorvalue = g_pickup_armorlarge;
        if(!self.max_armorvalue)
@@ -1299,7 +1309,8 @@ void spawnfunc_item_armor_large (void) {
        StartItemA (ITEM_ArmorMega);
 }
 
-void spawnfunc_item_health_small (void) {
+spawnfunc(item_health_small)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthsmall_max;
        if(!self.health)
@@ -1309,7 +1320,8 @@ void spawnfunc_item_health_small (void) {
        StartItemA (ITEM_HealthSmall);
 }
 
-void spawnfunc_item_health_medium (void) {
+spawnfunc(item_health_medium)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthmedium_max;
        if(!self.health)
@@ -1319,7 +1331,8 @@ void spawnfunc_item_health_medium (void) {
     StartItemA (ITEM_HealthMedium);
 }
 
-void spawnfunc_item_health_large (void) {
+spawnfunc(item_health_large)
+{
        if(!self.max_health)
                self.max_health = g_pickup_healthlarge_max;
        if(!self.health)
@@ -1329,7 +1342,8 @@ void spawnfunc_item_health_large (void) {
        StartItemA (ITEM_HealthLarge);
 }
 
-void spawnfunc_item_health_mega (void) {
+spawnfunc(item_health_mega)
+{
     if(!self.max_health)
         self.max_health = g_pickup_healthmega_max;
     if(!self.health)
@@ -1340,30 +1354,31 @@ void spawnfunc_item_health_mega (void) {
 }
 
 // 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(); }
-
-void spawnfunc_item_strength (void) {
-               precache_sound("weapons/strength_fire.wav");
+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); }
+
+spawnfunc(item_strength)
+{
                if(!self.strength_finished)
                        self.strength_finished = autocvar_g_balance_powerup_strength_time;
                StartItemA (ITEM_Strength);
 }
 
-void spawnfunc_item_invincible (void) {
+spawnfunc(item_invincible)
+{
                if(!self.invincible_finished)
                        self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
                StartItemA (ITEM_Shield);
 }
 
 // compatibility:
-void spawnfunc_item_quad (void) {self.classname = "item_strength";spawnfunc_item_strength();}
+spawnfunc(item_quad) { self.classname = "item_strength";spawnfunc_item_strength(this);}
 
-void target_items_use (void)
-{
+void target_items_use()
+{SELFPARAM();
        if(activator.classname == "droppedweapon")
        {
                EXACTTRIGGER_TOUCH;
@@ -1386,7 +1401,7 @@ void target_items_use (void)
                centerprint(activator, self.message);
 }
 
-void spawnfunc_target_items (void)
+spawnfunc(target_items)
 {
        float n, i, j;
        entity e;
@@ -1400,13 +1415,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")
        {
@@ -1433,13 +1441,15 @@ 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;
                                        }
                                }
                                if(j > WEP_LAST)
-                                       print("target_items: invalid item ", argv(i), "\n");
+                                       LOG_INFO("target_items: invalid item ", argv(i), "\n");
                        }
                }
 
@@ -1504,14 +1514,15 @@ 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)
+spawnfunc(item_fuel)
 {
        if(!self.ammo_fuel)
                self.ammo_fuel = g_pickup_fuel;
@@ -1520,23 +1531,23 @@ void spawnfunc_item_fuel(void)
        StartItemA (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);
 }
 
-void spawnfunc_item_jetpack(void)
+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);
@@ -1635,12 +1646,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);
        }
 }
 
@@ -1652,7 +1663,7 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa
                e.(regenfield) = max(e.(regenfield), time + regentime);
 }
 float GiveItems(entity e, float beginarg, float endarg)
-{
+{SELFPARAM();
        float got, i, j, val, op;
        float _switchweapon;
        entity wi;
@@ -1799,38 +1810,40 @@ float GiveItems(entity e, float beginarg, float endarg)
                                        }
                                }
                                if(j > WEP_LAST)
-                                       print("give: invalid item ", cmd, "\n");
+                                       LOG_INFO("give: invalid item ", cmd, "\n");
                                break;
                }
                val = 999;
                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)