]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/t_items.qc
Update items instantly when they pass through warpzones
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / t_items.qc
index f705d375cfd4a7c67a716342e301f6fc1c1834c0..432bfc36630a1bb69e4ab96b0b4f7caa110ad5ab 100644 (file)
@@ -7,14 +7,14 @@
     #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 "../lib/warpzone/util_server.qh"
 #endif
 
+REGISTER_NET_LINKED(ENT_CLIENT_ITEM)
+
 #ifdef CSQC
 void ItemDraw(entity self)
 {
     if(self.gravity)
     {
-        Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+        Movetype_Physics_MatchServer(self, autocvar_cl_projectiles_sloppy);
         if(self.move_flags & FL_ONGROUND)
         { // For some reason move_avelocity gets set to '0 0 0' here ...
             self.oldorigin = self.origin;
@@ -67,7 +69,7 @@ void ItemDrawSimple(entity this)
 {
     if(self.gravity)
     {
-        Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy);
+        Movetype_Physics_MatchServer(self, autocvar_cl_projectiles_sloppy);
 
         if(self.move_flags & FL_ONGROUND)
             self.gravity = 0;
@@ -94,8 +96,14 @@ void Item_PreDraw()
                self.drawmask = MASK_NORMAL;
 }
 
-void ItemRead(float _IsNew)
+void ItemRemove()
 {SELFPARAM();
+       if (self.mdl)
+               strunzone(self.mdl);
+}
+
+NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
+{
     int sf = ReadByte();
 
     if(sf & ISF_LOCATION)
@@ -166,6 +174,9 @@ void ItemRead(float _IsNew)
         self.drawmask  = MASK_NORMAL;
                self.move_movetype = self.movetype = MOVETYPE_TOSS;
         self.draw       = ItemDraw;
+        //self.move_flags |= FL_ITEM;
+
+        bool use_bigsize = ReadByte();
 
         self.fade_end = ReadShort();
         self.fade_start = ReadShort();
@@ -207,6 +218,8 @@ void ItemRead(float _IsNew)
 
         precache_model(self.mdl);
         _setmodel(self, self.mdl);
+
+        setsize(self, '-16 -16 0', (use_bigsize) ? '16 16 48' : '16 16 32');
     }
 
     if(sf & ISF_COLORMAP)
@@ -240,6 +253,10 @@ void ItemRead(float _IsNew)
         if(self.ItemStatus & ITS_ANIMATE2)
             self.move_avelocity = '0 -90 0';
     }
+
+    self.entremove = ItemRemove;
+
+    return true;
 }
 
 #endif
@@ -252,7 +269,7 @@ bool ItemSend(entity this, entity to, int sf)
        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);
@@ -272,7 +289,8 @@ bool ItemSend(entity this, 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)
@@ -280,6 +298,8 @@ bool ItemSend(entity this, entity to, int sf)
 
        if(sf & ISF_MODEL)
        {
+               Pickup p = this.itemdef;
+               WriteByte(MSG_ENTITY, p.instanceOfPowerup || p.instanceOfHealth || p.instanceOfArmor);
                WriteShort(MSG_ENTITY, self.fade_end);
                WriteShort(MSG_ENTITY, self.fade_start);
 
@@ -303,14 +323,21 @@ bool ItemSend(entity this, entity to, int sf)
        return true;
 }
 
-void ItemUpdate(entity item)
+void ItemUpdate(entity this)
 {
-       item.SendFlags |= ISF_LOCATION;
+       this.oldorigin = this.origin;
+       this.SendFlags |= ISF_LOCATION;
 }
 
-float have_pickup_item(void)
-{SELFPARAM();
-       if(self.flags & FL_POWERUP)
+void UpdateItemAfterTeleport(entity this)
+{
+       if(this.SendEntity3 == ItemSend)
+               ItemUpdate(this);
+}
+
+bool have_pickup_item(entity this)
+{
+       if(this.itemdef.instanceOfPowerup)
        {
                if(autocvar_g_powerups > 0)
                        return true;
@@ -324,7 +351,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;
@@ -377,7 +404,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;
@@ -394,7 +426,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;
@@ -418,10 +450,7 @@ void Item_Think()
 {SELFPARAM();
        self.nextthink = time;
        if(self.origin != self.oldorigin)
-       {
-               self.oldorigin = self.origin;
                ItemUpdate(self);
-       }
 }
 
 bool Item_ItemsTime_SpectatorOnly(GameItem it);
@@ -430,7 +459,7 @@ 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...
@@ -456,7 +485,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)
        {
@@ -473,8 +502,8 @@ void Item_RespawnCountdown (void)
                        MUTATOR_CALLHOOK(Item_RespawnCountdown, string_null, '0 0 0');
                        do {
                                {
-                                       entity wi = get_weaponinfo(self.weapon);
-                                       if (wi.m_id) {
+                                       entity wi = Weapons_from(self.weapon);
+                                       if (wi != WEP_Null) {
                                                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;
@@ -482,7 +511,7 @@ void Item_RespawnCountdown (void)
                                }
                                {
                                        entity ii = self.itemdef;
-                                       if (ii.m_id) {
+                                       if (ii != NULL) {
                                                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;
@@ -513,8 +542,6 @@ void Item_RespawnCountdown (void)
                        WaypointSprite_Ping(self.waypointsprite_attached);
                        //WaypointSprite_UpdateHealth(self.waypointsprite_attached, self.count);
                }
-               else
-                       sound(self, CH_TRIGGER, SND_ITEMRESPAWNCOUNTDOWN, VOL_BASE, ATTEN_NORM);        // play respawn sound
        }
 }
 
@@ -522,10 +549,7 @@ void Item_RespawnThink()
 {SELFPARAM();
        self.nextthink = time;
        if(self.origin != self.oldorigin)
-       {
-               self.oldorigin = self.origin;
                ItemUpdate(self);
-       }
 
        if(time >= self.wait)
                Item_Respawn();
@@ -576,7 +600,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;
@@ -626,10 +650,10 @@ float Item_GiveTo(entity item, entity player)
        // if the player is using their best weapon before items are given, they
        // probably want to switch to an even better weapon after items are given
        if (player.autoswitch)
-       if (player.switchweapon == w_getbestweapon(player))
+       if (PS(player).m_switchweapon == w_getbestweapon(player))
                _switchweapon = true;
 
-       if (!(player.weapons & WepSet_FromWeapon(player.switchweapon)))
+       if (!(player.weapons & WepSet_FromWeapon(PS(player).m_switchweapon)))
                _switchweapon = true;
 
        pickedup |= Item_GiveAmmoTo(item, player, ammo_fuel, g_pickup_fuel_max, ITEM_MODE_FUEL);
@@ -641,17 +665,17 @@ 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))
+                       if(it & WepSet_FromWeapon(Weapons_from(i)))
                        {
                                W_DropEvent(wr_pickup, player, i, item);
                                W_GiveWeapon(player, i);
@@ -692,29 +716,29 @@ 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);
+               W_SwitchWeapon_Force(player, Weapons_from(item.weapon));
                return 1;
        }
 
        if (_switchweapon)
-               if (player.switchweapon != w_getbestweapon(player))
+               if (PS(player).m_switchweapon != w_getbestweapon(player))
                        W_SwitchWeapon_Force(player, w_getbestweapon(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;
                }
        }
@@ -722,33 +746,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;
        }
@@ -757,17 +781,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 ? self.item_pickupsound : self.item_pickupsound_ent.sound_str()), 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")
@@ -780,7 +805,7 @@ void Item_Touch (void)
 
                }
                else
-                       e = self;
+                       e = this;
                Item_ScheduleRespawn(e);
        }
 }
@@ -798,7 +823,7 @@ void Item_Reset(entity this)
                if (this.waypointsprite_attached)
                        WaypointSprite_Kill(this.waypointsprite_attached);
 
-               if ((this.flags & FL_POWERUP) || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
+               if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
                        Item_ScheduleInitialRespawn(this);
        }
 }
@@ -840,7 +865,7 @@ void Item_FindTeam()
 
 // 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);
@@ -912,9 +937,9 @@ float commodity_pickupevalfunc(entity player, entity item)
        // Detect needed ammo
        for(i = WEP_FIRST; i <= WEP_LAST ; ++i)
        {
-               wi = get_weaponinfo(i);
+               wi = Weapons_from(i);
 
-               if (!(player.weapons & WepSet_FromWeapon(i)))
+               if (!(player.weapons & (wi.m_wepset)))
                        continue;
 
                if(wi.items & ITEM_Shells.m_itemid)
@@ -974,21 +999,19 @@ void Item_Damage(entity inflictor, entity attacker, float damage, int deathtype,
                RemoveItem();
 }
 
-void _StartItem(entity this, string itemmodel, string pickupsound, float defaultrespawntime, float defaultrespawntimejitter, string itemname, float itemid, float weaponid, float itemflags, float(entity player, entity item) pickupevalfunc, float pickupbasevalue)
+void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter)
 {
-       startitem_failed = false;
+       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(this.model == "")
-               this.model = itemmodel;
-
-       if(this.model == "")
-    {
-        error(strcat("^1Tried to spawn ", itemname, " with no model!\n"));
-        return;
-    }
+       startitem_failed = false;
 
-       if(this.item_pickupsound == "")
-               this.item_pickupsound = pickupsound;
+       this.item_model_ent = itemmodel;
+    this.item_pickupsound_ent = pickupsound;
 
        if(!this.respawntime) // both need to be set
        {
@@ -996,7 +1019,9 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
                this.respawntimejitter = defaultrespawntimejitter;
        }
 
+       int itemid = def.m_itemid;
        this.items = itemid;
+       int weaponid = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0;
        this.weapon = weaponid;
 
        if(!this.fade_end)
@@ -1006,7 +1031,7 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
        }
 
        if(weaponid)
-               this.weapons = WepSet_FromWeapon(weaponid);
+               this.weapons = WepSet_FromWeapon(Weapons_from(weaponid));
 
        this.flags = FL_ITEM | itemflags;
 
@@ -1032,17 +1057,6 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
                this.event_damage = Item_Damage;
 
                if(this.strength_finished || this.invincible_finished || this.superweapons_finished)
-               /*
-               if(this.items == 0)
-               if(!(this.weapons & ~WEPSET_SUPERWEAPONS)) // only superweapons
-               if(this.ammo_nails == 0)
-               if(this.ammo_cells == 0)
-               if(this.ammo_rockets == 0)
-               if(this.ammo_shells == 0)
-               if(this.ammo_fuel == 0)
-               if(this.health == 0)
-               if(this.armorvalue == 0)
-               */
                {
                        // if item is worthless after a timer, have it expire then
                        this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
@@ -1059,7 +1073,7 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
        }
        else
        {
-               if(!have_pickup_item())
+               if(!have_pickup_item(this))
                {
                        startitem_failed = true;
                        remove (this);
@@ -1069,27 +1083,25 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
                if(this.angles != '0 0 0')
                        this.SendFlags |= ISF_ANGLES;
 
-               this.reset = Item_Reset_self;
+               this.reset = Item_Reset;
                // it's a level item
                if(this.spawnflags & 1)
                        this.noalign = 1;
-               if (this.noalign)
+               if (this.noalign > 0)
                        this.movetype = MOVETYPE_NONE;
                else
                        this.movetype = MOVETYPE_TOSS;
                // do item filtering according to game mode and other things
-               if (!this.noalign)
+               if (this.noalign <= 0)
                {
                        // first nudge it off the floor a little bit to avoid math errors
                        setorigin(this, this.origin + '0 0 1');
                        // set item size before we spawn a spawnfunc_waypoint
-                       if((itemflags & FL_POWERUP) || this.health || this.armorvalue)
-                               setsize (this, '-16 -16 0', '16 16 48');
-                       else
-                               setsize (this, '-16 -16 0', '16 16 32');
+                       setsize(this, def.m_mins, def.m_maxs);
                        this.SendFlags |= ISF_SIZE;
                        // note droptofloor returns false if stuck/or would fall too far
-                       WITH(entity, self, this, droptofloor());
+                       if (!this.noalign)
+                               WITH(entity, self, this, droptofloor());
                        waypoint_spawnforitem(this);
                }
 
@@ -1122,13 +1134,17 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
                        this.is_item = true;
                }
 
-               weaponsInMap |= WepSet_FromWeapon(weaponid);
+               weaponsInMap |= WepSet_FromWeapon(Weapons_from(weaponid));
 
                precache_model(this.model);
                precache_sound(this.item_pickupsound);
 
-               if ((itemflags & (FL_POWERUP | FL_WEAPON)) || (itemid & (IT_HEALTH | IT_ARMOR | IT_KEY1 | IT_KEY2)))
-                       this.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(this, 0);
        }
@@ -1136,40 +1152,32 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
        this.bot_pickup = true;
        this.bot_pickupevalfunc = pickupevalfunc;
        this.bot_pickupbasevalue = pickupbasevalue;
-       this.mdl = this.model;
+       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) || this.health || this.armorvalue)
-       {
-               this.pos1 = '-16 -16 0';
-               this.pos2 = '16 16 48';
-       }
-       else
-       {
-               this.pos1 = '-16 -16 0';
-               this.pos2 = '16 16 32';
-       }
-       setsize (this, this.pos1, this.pos2);
+       setsize (this, this.pos1 =  def.m_mins, this.pos2 = def.m_maxs);
 
        this.SendFlags |= ISF_SIZE;
 
-       if(itemflags & FL_POWERUP)
-               this.ItemStatus |= ITS_ANIMATE1;
-
-       if(this.armorvalue || this.health)
-               this.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 (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely
                        this.colormap = 1024; // color shirt=0 pants=0 grey
                else
                        this.gravity = 1;
-
-               this.ItemStatus |= ITS_ANIMATE1;
+               if (!(this.spawnflags & 1024))
+                       this.ItemStatus |= ITS_ANIMATE1;
                this.ItemStatus |= ISF_COLORMAP;
        }
 
@@ -1185,7 +1193,7 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
        else
                Item_Reset(this);
 
-       Net_LinkEntity(this, !((itemflags & FL_POWERUP) || this.health || this.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, this))
@@ -1196,30 +1204,22 @@ void _StartItem(entity this, string itemmodel, string pickupsound, float default
        }
 }
 
-void StartItem(entity this, entity a)
+void StartItem(entity this, GameItem def)
 {
-    this.itemdef = a;
     _StartItem(
        this,
-       strzone(a.m_model.model_str()), // itemmodel
-       a.m_sound, // pickupsound
-       a.m_respawntime(), // defaultrespawntime
-       a.m_respawntimejitter(), // defaultrespawntimejitter
-       a.m_name, // itemname
-       a.m_itemid, // itemid
-       0, // weaponid
-       a.m_itemflags, // itemflags
-       a.m_pickupevalfunc, // pickupevalfunc
-       a.m_botvalue // pickupbasevalue
+       this.itemdef = def,
+       def.m_respawntime(), // defaultrespawntime
+       def.m_respawntimejitter() // defaultrespawntimejitter
        );
 }
 
 spawnfunc(item_rockets)
 {
-       if(!self.ammo_rockets)
-               self.ammo_rockets = g_pickup_rockets;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_rockets)
+               this.ammo_rockets = g_pickup_rockets;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
     StartItem(this, ITEM_Rockets);
 }
 
@@ -1227,7 +1227,7 @@ spawnfunc(item_bullets)
 {
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(self.classname != "droppedweapon")
+       if(this.classname != "droppedweapon")
        {
                weaponswapping = true;
                spawnfunc_item_shells(this);
@@ -1235,28 +1235,28 @@ spawnfunc(item_bullets)
                return;
        }
 
-       if(!self.ammo_nails)
-               self.ammo_nails = g_pickup_nails;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_nails)
+               this.ammo_nails = g_pickup_nails;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
     StartItem(this, ITEM_Bullets);
 }
 
 spawnfunc(item_cells)
 {
-       if(!self.ammo_cells)
-               self.ammo_cells = g_pickup_cells;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_cells)
+               this.ammo_cells = g_pickup_cells;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
        StartItem(this, ITEM_Cells);
 }
 
 spawnfunc(item_plasma)
 {
-       if(!self.ammo_plasma)
-               self.ammo_plasma = g_pickup_plasma;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_plasma)
+               this.ammo_plasma = g_pickup_plasma;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
        StartItem(this, ITEM_Plasma);
 }
 
@@ -1264,7 +1264,7 @@ spawnfunc(item_shells)
 {
        if(!weaponswapping)
        if(autocvar_sv_q3acompat_machineshotgunswap)
-       if(self.classname != "droppedweapon")
+       if(this.classname != "droppedweapon")
        {
                weaponswapping = true;
                spawnfunc_item_bullets(this);
@@ -1272,98 +1272,98 @@ spawnfunc(item_shells)
                return;
        }
 
-       if(!self.ammo_shells)
-               self.ammo_shells = g_pickup_shells;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_shells)
+               this.ammo_shells = g_pickup_shells;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
        StartItem(this, ITEM_Shells);
 }
 
 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;
+       if(!this.armorvalue)
+               this.armorvalue = g_pickup_armorsmall;
+       if(!this.max_armorvalue)
+               this.max_armorvalue = g_pickup_armorsmall_max;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_armorsmall_anyway;
        StartItem(this, ITEM_ArmorSmall);
 }
 
 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;
+       if(!this.armorvalue)
+               this.armorvalue = g_pickup_armormedium;
+       if(!this.max_armorvalue)
+               this.max_armorvalue = g_pickup_armormedium_max;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_armormedium_anyway;
        StartItem(this, ITEM_ArmorMedium);
 }
 
 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;
+       if(!this.armorvalue)
+               this.armorvalue = g_pickup_armorbig;
+       if(!this.max_armorvalue)
+               this.max_armorvalue = g_pickup_armorbig_max;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_armorbig_anyway;
        StartItem(this, ITEM_ArmorLarge);
 }
 
 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;
+       if(!this.armorvalue)
+               this.armorvalue = g_pickup_armorlarge;
+       if(!this.max_armorvalue)
+               this.max_armorvalue = g_pickup_armorlarge_max;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_armorlarge_anyway;
        StartItem(this, ITEM_ArmorMega);
 }
 
 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;
+       if(!this.max_health)
+               this.max_health = g_pickup_healthsmall_max;
+       if(!this.health)
+               this.health = g_pickup_healthsmall;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_healthsmall_anyway;
        StartItem(this, ITEM_HealthSmall);
 }
 
 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;
+       if(!this.max_health)
+               this.max_health = g_pickup_healthmedium_max;
+       if(!this.health)
+               this.health = g_pickup_healthmedium;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_healthmedium_anyway;
     StartItem(this, ITEM_HealthMedium);
 }
 
 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;
+       if(!this.max_health)
+               this.max_health = g_pickup_healthlarge_max;
+       if(!this.health)
+               this.health = g_pickup_healthlarge;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_healthlarge_anyway;
        StartItem(this, ITEM_HealthLarge);
 }
 
 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;
+    if(!this.max_health)
+        this.max_health = g_pickup_healthmega_max;
+    if(!this.health)
+        this.health = g_pickup_healthmega;
+    if(!this.pickup_anyway)
+        this.pickup_anyway = g_pickup_healthmega_anyway;
     StartItem(this, ITEM_HealthMega);
 }
 
@@ -1376,20 +1376,20 @@ spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
 
 spawnfunc(item_strength)
 {
-               if(!self.strength_finished)
-                       self.strength_finished = autocvar_g_balance_powerup_strength_time;
+               if(!this.strength_finished)
+                       this.strength_finished = autocvar_g_balance_powerup_strength_time;
                StartItem(this, ITEM_Strength);
 }
 
 spawnfunc(item_invincible)
 {
-               if(!self.invincible_finished)
-                       self.invincible_finished = autocvar_g_balance_powerup_invincible_time;
+               if(!this.invincible_finished)
+                       this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
                StartItem(this, ITEM_Shield);
 }
 
 // compatibility:
-spawnfunc(item_quad) { self.classname = "item_strength";spawnfunc_item_strength(this);}
+spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength(this);}
 
 void target_items_use()
 {SELFPARAM();
@@ -1450,14 +1450,13 @@ spawnfunc(target_items)
                        {
                                for(j = WEP_FIRST; j <= WEP_LAST; ++j)
                                {
-                                       e = get_weaponinfo(j);
+                                       e = Weapons_from(j);
                                        s = W_UndeprecateName(argv(i));
                                        if(s == e.netname)
                                        {
-                                               self.weapons |= WepSet_FromWeapon(j);
+                                               self.weapons |= (e.m_wepset);
                                                if(self.spawnflags == 0 || self.spawnflags == 2) {
-                                                       Weapon w = get_weaponinfo(e.weapon);
-                                                       w.wr_init(w);
+                                                       e.wr_init(e);
                                                }
                                                break;
                                        }
@@ -1495,13 +1494,13 @@ spawnfunc(target_items)
                }
 
                self.netname = "";
-               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_WEAPON_AMMO), "unlimited_weapon_ammo");
-               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
-               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.strength_finished * !!(self.items & ITEM_Strength.m_itemid), "strength");
-               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.invincible_finished * !!(self.items & ITEM_Shield.m_itemid), "invincible");
-               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.superweapons_finished * !!(self.items & IT_SUPERWEAPON), "superweapons");
-               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & ITEM_Jetpack.m_itemid), "jetpack");
-               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.items & ITEM_JetpackRegen.m_itemid), "fuel_regen");
+               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, boolean(self.items & IT_UNLIMITED_WEAPON_AMMO), "unlimited_weapon_ammo");
+               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, boolean(self.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
+               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.strength_finished * boolean(self.items & ITEM_Strength.m_itemid), "strength");
+               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.invincible_finished * boolean(self.items & ITEM_Shield.m_itemid), "invincible");
+               self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, self.superweapons_finished * boolean(self.items & IT_SUPERWEAPON), "superweapons");
+               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, boolean(self.items & ITEM_Jetpack.m_itemid), "jetpack");
+               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, boolean(self.items & ITEM_JetpackRegen.m_itemid), "fuel_regen");
                if(self.ammo_shells != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_shells), "shells");
                if(self.ammo_nails != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_nails), "nails");
                if(self.ammo_rockets != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.ammo_rockets), "rockets");
@@ -1512,9 +1511,9 @@ spawnfunc(target_items)
                if(self.armorvalue != 0) self.netname = sprintf("%s %s%d %s", self.netname, valueprefix, max(0, self.health), "armor");
                for(j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
-                       e = get_weaponinfo(j);
-                       if(e.weapon)
-                               self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.weapons & WepSet_FromWeapon(j)), e.netname);
+                       e = Weapons_from(j);
+                       if(e == WEP_Null) continue;
+                       self.netname = sprintf("%s %s%d %s", self.netname, itemprefix, !!(self.weapons & (e.m_wepset)), e.netname);
                }
        }
        self.netname = strzone(self.netname);
@@ -1525,11 +1524,10 @@ spawnfunc(target_items)
        {
                for(j = WEP_FIRST; j <= WEP_LAST; ++j)
                {
-                       e = get_weaponinfo(j);
+                       e = Weapons_from(j);
                        if(argv(i) == e.netname)
                        {
-                               Weapon w = get_weaponinfo(e.weapon);
-                               w.wr_init(w);
+                               e.wr_init(e);
                                break;
                        }
                }
@@ -1538,10 +1536,10 @@ spawnfunc(target_items)
 
 spawnfunc(item_fuel)
 {
-       if(!self.ammo_fuel)
-               self.ammo_fuel = g_pickup_fuel;
-       if(!self.pickup_anyway)
-               self.pickup_anyway = g_pickup_ammo_anyway;
+       if(!this.ammo_fuel)
+               this.ammo_fuel = g_pickup_fuel;
+       if(!this.pickup_anyway)
+               this.pickup_anyway = g_pickup_ammo_anyway;
        StartItem(this, ITEM_JetpackFuel);
 }
 
@@ -1557,8 +1555,8 @@ spawnfunc(item_fuel_regen)
 
 spawnfunc(item_jetpack)
 {
-       if(!self.ammo_fuel)
-               self.ammo_fuel = g_pickup_fuel_jetpack;
+       if(!this.ammo_fuel)
+               this.ammo_fuel = g_pickup_fuel_jetpack;
        if(start_items & ITEM_Jetpack.m_itemid)
        {
                spawnfunc_item_fuel(this);
@@ -1570,86 +1568,31 @@ spawnfunc(item_jetpack)
 float GiveWeapon(entity e, float wpn, float op, float val)
 {
        WepSet v0, v1;
-       v0 = (e.weapons & WepSet_FromWeapon(wpn));
+       WepSet s = WepSet_FromWeapon(Weapons_from(wpn));
+       v0 = (e.weapons & s);
        switch(op)
        {
                case OP_SET:
                        if(val > 0)
-                               e.weapons |= WepSet_FromWeapon(wpn);
+                               e.weapons |= s;
                        else
-                               e.weapons &= ~WepSet_FromWeapon(wpn);
+                               e.weapons &= ~s;
                        break;
                case OP_MIN:
                case OP_PLUS:
                        if(val > 0)
-                               e.weapons |= WepSet_FromWeapon(wpn);
+                               e.weapons |= s;
                        break;
                case OP_MAX:
                        if(val <= 0)
-                               e.weapons &= ~WepSet_FromWeapon(wpn);
+                               e.weapons &= ~s;
                        break;
                case OP_MINUS:
                        if(val > 0)
-                               e.weapons &= ~WepSet_FromWeapon(wpn);
+                               e.weapons &= ~s;
                        break;
        }
-       v1 = (e.weapons & WepSet_FromWeapon(wpn));
-       return (v0 != v1);
-}
-
-float GiveBit(entity e, .float fld, float bit, float op, float val)
-{
-       float v0, v1;
-       v0 = (e.(fld) & bit);
-       switch(op)
-       {
-               case OP_SET:
-                       if(val > 0)
-                               e.(fld) |= bit;
-                       else
-                               e.(fld) &= ~bit;
-                       break;
-               case OP_MIN:
-               case OP_PLUS:
-                       if(val > 0)
-                               e.(fld) |= bit;
-                       break;
-               case OP_MAX:
-                       if(val <= 0)
-                               e.(fld) &= ~bit;
-                       break;
-               case OP_MINUS:
-                       if(val > 0)
-                               e.(fld) &= ~bit;
-                       break;
-       }
-       v1 = (e.(fld) & bit);
-       return (v0 != v1);
-}
-
-float GiveValue(entity e, .float fld, float op, float val)
-{
-       float v0, v1;
-       v0 = e.(fld);
-       switch(op)
-       {
-               case OP_SET:
-                       e.(fld) = val;
-                       break;
-               case OP_MIN:
-                       e.(fld) = max(e.(fld), val); // min 100 cells = at least 100 cells
-                       break;
-               case OP_MAX:
-                       e.(fld) = min(e.(fld), val);
-                       break;
-               case OP_PLUS:
-                       e.(fld) += val;
-                       break;
-               case OP_MINUS:
-                       e.(fld) -= val;
-                       break;
-       }
-       v1 = e.(fld);
+       v1 = (e.weapons & s);
        return (v0 != v1);
 }
 
@@ -1690,7 +1633,7 @@ float GiveItems(entity e, float beginarg, float endarg)
 
        _switchweapon = false;
        if (e.autoswitch)
-               if (e.switchweapon == w_getbestweapon(e))
+               if (PS(e).m_switchweapon == w_getbestweapon(e))
                        _switchweapon = true;
 
        e.strength_finished = max(0, e.strength_finished - time);
@@ -1751,10 +1694,10 @@ float GiveItems(entity e, float beginarg, float endarg)
                        case "allweapons":
                                for(j = WEP_FIRST; j <= WEP_LAST; ++j)
                                {
-                                       wi = get_weaponinfo(j);
-                                       if(wi.weapon)
-                                               if (!(wi.spawnflags & WEP_FLAG_MUTATORBLOCKED))
-                                                       got += GiveWeapon(e, j, op, val);
+                                       wi = Weapons_from(j);
+                                       if(wi == WEP_Null) continue;
+                                       if (!(wi.spawnflags & WEP_FLAG_MUTATORBLOCKED))
+                                               got += GiveWeapon(e, j, op, val);
                                }
                        case "allammo":
                                got += GiveValue(e, ammo_cells, op, val);
@@ -1816,7 +1759,7 @@ float GiveItems(entity e, float beginarg, float endarg)
                        default:
                                for(j = WEP_FIRST; j <= WEP_LAST; ++j)
                                {
-                                       wi = get_weaponinfo(j);
+                                       wi = Weapons_from(j);
                                        if(cmd == wi.netname)
                                        {
                                                got += GiveWeapon(e, j, op, val);
@@ -1837,16 +1780,13 @@ float GiveItems(entity e, float beginarg, float endarg)
        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, SND(WEAPONPICKUP), string_null);
-                       if (!(save_weapons & WepSet_FromWeapon(j)))
-                               if(e.weapons & WepSet_FromWeapon(j)) {
-                                       Weapon w = get_weaponinfo(wi.weapon);
-                                       w.wr_init(w);
-                               }
-               }
+               wi = Weapons_from(j);
+               if (wi == WEP_Null) continue;
+               POSTGIVE_WEAPON(e, Weapons_from(j), SND(WEAPONPICKUP), string_null);
+               if (!(save_weapons & (wi.m_wepset)))
+                       if(e.weapons & (wi.m_wepset)) {
+                               wi.wr_init(wi);
+                       }
        }
        POSTGIVE_VALUE(e, strength_finished, 1, SND(POWERUP), SND(POWEROFF));
        POSTGIVE_VALUE(e, invincible_finished, 1, "misc/powerup_shield.wav", SND(POWEROFF));
@@ -1876,7 +1816,7 @@ float GiveItems(entity e, float beginarg, float endarg)
        else
                e.superweapons_finished += time;
 
-       if (!(e.weapons & WepSet_FromWeapon(e.switchweapon)))
+       if (!(e.weapons & WepSet_FromWeapon(PS(e).m_switchweapon)))
                _switchweapon = true;
        if(_switchweapon)
                W_SwitchWeapon_Force(e, w_getbestweapon(e));