]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/items/items.qc
Item Pickup panel
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / items / items.qc
index a87214072efa20179b5cf24b5750370ecba826e8..e68d858a8e72e55d9ad4a156cd0b81400cafbd8e 100644 (file)
@@ -9,8 +9,11 @@
 #include <common/monsters/_mod.qh>
 #include <common/mutators/mutator/buffs/buffs.qh>
 #include <common/mutators/mutator/buffs/sv_buffs.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
+#include <common/net_linked.qh>
 #include <common/notifications/all.qh>
+#include <common/resources/resources.qh>
 #include <common/util.qh>
 #include <common/weapons/_all.qh>
 #include <common/wepent.qh>
@@ -62,9 +65,9 @@ bool ItemSend(entity this, entity to, int sf)
                        LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now");
 
                WriteString(MSG_ENTITY, this.mdl);
+               WriteByte(MSG_ENTITY, bound(0, this.skin, 255));
        }
 
-
        if(sf & ISF_COLORMAP)
        {
                WriteShort(MSG_ENTITY, this.colormap);
@@ -197,7 +200,6 @@ void Item_Respawn(entity this)
 {
        Item_Show(this, 1);
        sound(this, CH_TRIGGER, this.itemdef.m_respawnsound, VOL_BASE, ATTEN_NORM);     // play respawn sound
-       setorigin(this, this.origin);
 
        if (Item_ItemsTime_Allow(this.itemdef) || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
        {
@@ -246,13 +248,13 @@ void Item_RespawnCountdown(entity this)
                                }
                        } while (0);
                        bool mutator_returnvalue = MUTATOR_CALLHOOK(Item_RespawnCountdown, this);
-            if(this.waypointsprite_attached)
-            {
-                GameItem def = this.itemdef;
-                if (Item_ItemsTime_SpectatorOnly(def) && !mutator_returnvalue)
-                    WaypointSprite_UpdateRule(this.waypointsprite_attached, 0, SPRITERULE_SPECTATOR);
-                WaypointSprite_UpdateBuildFinished(this.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
-            }
+                       if(this.waypointsprite_attached)
+                       {
+                               GameItem def = this.itemdef;
+                               if (Item_ItemsTime_SpectatorOnly(def) && !mutator_returnvalue)
+                                       WaypointSprite_UpdateRule(this.waypointsprite_attached, 0, SPRITERULE_SPECTATOR);
+                               WaypointSprite_UpdateBuildFinished(this.waypointsprite_attached, time + ITEM_RESPAWN_TICKS);
+                       }
                }
 
                if(this.waypointsprite_attached)
@@ -446,7 +448,7 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names,
        }
 }
 
-bool Item_GiveAmmoTo(entity item, entity player, int res_type, float ammomax)
+bool Item_GiveAmmoTo(entity item, entity player, Resource res_type, float ammomax)
 {
        float amount = GetResource(item, res_type);
        if (amount == 0)
@@ -474,6 +476,15 @@ bool Item_GiveAmmoTo(entity item, entity player, int res_type, float ammomax)
        return true;
 }
 
+void Item_NotifyWeapon(entity player, int wep)
+{
+       FOREACH_CLIENT(IS_REAL_CLIENT(it) && (it == player || (IS_SPEC(it) && it.enemy == player)), {
+               msg_entity = it;
+               WriteHeader(MSG_ONE, TE_CSQC_WEAPONPICKUP);
+               WriteByte(MSG_ONE, wep);
+       });
+}
+
 bool Item_GiveTo(entity item, entity player)
 {
        // if nothing happens to player, just return without taking the item
@@ -482,7 +493,7 @@ bool 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(CS_CVAR(player).autoswitch)
+       if(CS_CVAR(player).cvar_cl_autoswitch)
        {
                for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
@@ -508,15 +519,18 @@ bool Item_GiveTo(entity item, entity player)
        pickedup |= Item_GiveAmmoTo(item, player, RES_FUEL, g_pickup_fuel_max);
        if (item.itemdef.instanceOfWeaponPickup)
        {
-               WepSet w;
+               WepSet w, wp;
                w = STAT(WEAPONS, item);
-               w &= ~STAT(WEAPONS, player);
+               wp = w & ~STAT(WEAPONS, player);
 
-               if (w || (item.spawnshieldtime && item.pickup_anyway > 0))
+               if (wp || (item.spawnshieldtime && item.pickup_anyway > 0))
                {
                        pickedup = true;
                        FOREACH(Weapons, it != WEP_Null, {
                                if(w & (it.m_wepset))
+                                       Item_NotifyWeapon(player, it.m_id);
+
+                               if(wp & (it.m_wepset))
                                {
                                        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                                        {
@@ -550,12 +564,42 @@ bool Item_GiveTo(entity item, entity player)
        if (item.strength_finished)
        {
                pickedup = true;
-               StatusEffects_apply(STATUSEFFECT_Strength, player, max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time) + item.strength_finished, 0);
+               float t = max(StatusEffects_gettime(STATUSEFFECT_Strength, player), time);
+               if (autocvar_g_powerups_stack)
+                       t += item.strength_finished;
+               else
+                       t = max(t, time + item.strength_finished);
+               StatusEffects_apply(STATUSEFFECT_Strength, player, t, 0);
        }
        if (item.invincible_finished)
        {
                pickedup = true;
-               StatusEffects_apply(STATUSEFFECT_Shield, player, max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time) + item.invincible_finished, 0);
+               float t = max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time);
+               if (autocvar_g_powerups_stack)
+                       t += item.invincible_finished;
+               else
+                       t = max(t, time + item.invincible_finished);
+               StatusEffects_apply(STATUSEFFECT_Shield, player, t, 0);
+       }
+       if (item.speed_finished)
+       {
+               pickedup = true;
+               float t = max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time);
+               if (autocvar_g_powerups_stack)
+                       t += item.speed_finished;
+               else
+                       t = max(t, time + item.speed_finished);
+               StatusEffects_apply(STATUSEFFECT_Speed, player, t, 0);
+       }
+       if (item.invisibility_finished)
+       {
+               pickedup = true;
+               float t = max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time);
+               if (autocvar_g_powerups_stack)
+                       t += item.invisibility_finished;
+               else
+                       t = max(t, time + item.invisibility_finished);
+               StatusEffects_apply(STATUSEFFECT_Invisibility, player, t, 0);
        }
        if (item.superweapons_finished)
        {
@@ -603,7 +647,7 @@ void Item_Touch(entity this, entity toucher)
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
-                       delete(this);
+                       RemoveItem(this);
                        return;
                }
        }
@@ -628,6 +672,8 @@ void Item_Touch(entity this, entity toucher)
        {
                this.strength_finished = max(0, this.strength_finished - time);
                this.invincible_finished = max(0, this.invincible_finished - time);
+               this.speed_finished = max(0, this.speed_finished - time);
+               this.invisibility_finished = max(0, this.invisibility_finished - time);
                this.superweapons_finished = max(0, this.superweapons_finished - time);
        }
        bool gave = ITEM_HANDLE(Pickup, this.itemdef, this, toucher);
@@ -638,6 +684,8 @@ void Item_Touch(entity this, entity toucher)
                        // undo what we did above
                        this.strength_finished += time;
                        this.invincible_finished += time;
+                       this.speed_finished += time;
+                       this.invisibility_finished += time;
                        this.superweapons_finished += time;
                }
                return;
@@ -692,7 +740,6 @@ LABEL(pickup)
 void Item_Reset(entity this)
 {
        Item_Show(this, !this.state);
-       setorigin(this, this.origin);
 
        if (Item_IsLoot(this))
        {
@@ -712,45 +759,48 @@ void Item_Reset(entity this)
 
 void Item_FindTeam(entity this)
 {
-       entity e;
+       if(!(this.effects & EF_NOGUNBOB)) // marker for item team search
+               return;
 
-       if(this.effects & EF_NODRAW)
+       LOG_TRACE("Initializing item team ", ftos(this.team));
+       RandomSelection_Init();
+       IL_EACH(g_items, it.team == this.team,
        {
-               // marker for item team search
-               LOG_TRACE("Initializing item team ", ftos(this.team));
-               RandomSelection_Init();
-               IL_EACH(g_items, it.team == this.team,
-               {
-                       if(it.itemdef) // is a registered item
-                               RandomSelection_AddEnt(it, it.cnt, 0);
-               });
+               if(it.itemdef) // is a registered item
+                       RandomSelection_AddEnt(it, it.cnt, 0);
+       });
 
-               e = RandomSelection_chosen_ent;
-               if (!e)
-                       return;
+       entity e = RandomSelection_chosen_ent;
+       if (!e)
+               return;
 
-               IL_EACH(g_items, it.team == this.team,
+       IL_EACH(g_items, it.team == this.team,
+       {
+               if(it.itemdef) // is a registered item
                {
-                       if(it.itemdef) // is a registered item
+                       if(it != e)
                        {
-                               if(it != e)
-                               {
-                                       // make it non-spawned
-                                       Item_Show(it, -1);
-                                       it.state = 1; // state 1 = initially hidden item, apparently
-                               }
-                               else
-                                       Item_Reset(it);
-                               it.effects &= ~EF_NODRAW;
+                               Item_Show(it, -1); // make it non-spawned
+                               if (it.waypointsprite_attached)
+                                       WaypointSprite_Kill(it.waypointsprite_attached);
+                               it.nextthink = 0; // disable any scheduled powerup spawn
                        }
-               });
-       }
+                       else
+                               Item_Reset(it);
+
+                       // leave 'this' marked so Item_FindTeam() works when called again via this.reset
+                       if(it != this)
+                               it.effects &= ~EF_NOGUNBOB;
+               }
+       });
 }
 
 // Savage: used for item garbage-collection
 void RemoveItem(entity this)
 {
        if(wasfreed(this) || !this) { return; }
+       if(this.waypointsprite_attached)
+               WaypointSprite_Kill(this.waypointsprite_attached);
        Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
        delete(this);
 }
@@ -784,24 +834,21 @@ float weapon_pickupevalfunc(entity player, entity item)
 
 float ammo_pickupevalfunc(entity player, entity item)
 {
-       bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false;
+       entity item_resource = NULL; // pointer to the resource that may be associated with the given item
        entity wpn = NULL;
        float c = 0;
        float rating = 0;
 
-       // Detect needed ammo
+       // detect needed ammo
        if(item.itemdef.instanceOfWeaponPickup)
        {
-               entity ammo = NULL;
-               if(GetResource(item, RES_SHELLS))       { need_shells  = true; ammo = ITEM_Shells;      }
-               else if(GetResource(item, RES_BULLETS))   { need_nails   = true; ammo = ITEM_Bullets;     }
-               else if(GetResource(item, RES_ROCKETS)) { need_rockets = true; ammo = ITEM_Rockets;     }
-               else if(GetResource(item, RES_CELLS))   { need_cells   = true; ammo = ITEM_Cells;       }
-               else if(GetResource(item, RES_PLASMA))  { need_plasma  = true; ammo = ITEM_Plasma;      }
-               else if(GetResource(item, RES_FUEL))    { need_fuel    = true; ammo = ITEM_JetpackFuel; }
-
+               entity res = item.itemdef.m_weapon.ammo_type;
+               entity ammo = (res != RES_NONE) ? GetAmmoItem(res) : NULL;
                if(!ammo)
                        return 0;
+               if(res != RES_NONE && GetResource(item, res))
+                       item_resource = res;
+
                wpn = item;
                rating = ammo.m_botvalue;
        }
@@ -810,15 +857,13 @@ float ammo_pickupevalfunc(entity player, entity item)
                FOREACH(Weapons, it != WEP_Null, {
                        if(!(STAT(WEAPONS, player) & (it.m_wepset)))
                                continue;
+                       if(it.ammo_type == RES_NONE)
+                               continue;
 
-                       switch(it.ammo_type)
+                       if(GetResource(item, it.ammo_type))
                        {
-                               case RES_SHELLS:  need_shells  = true; break;
-                               case RES_BULLETS: need_nails   = true; break;
-                               case RES_ROCKETS: need_rockets = true; break;
-                               case RES_CELLS:   need_cells   = true; break;
-                               case RES_PLASMA:  need_plasma  = true; break;
-                               case RES_FUEL:    need_fuel    = true; break;
+                               item_resource = it.ammo_type;
+                               break;
                        }
                });
                rating = item.bot_pickupbasevalue;
@@ -826,23 +871,8 @@ float ammo_pickupevalfunc(entity player, entity item)
 
        float noammorating = 0.5;
 
-       if ((need_shells) && GetResource(item, RES_SHELLS) && (GetResource(player, RES_SHELLS) < g_pickup_shells_max))
-               c = GetResource(item, RES_SHELLS) / max(noammorating, GetResource(player, RES_SHELLS));
-
-       if ((need_nails) && GetResource(item, RES_BULLETS) && (GetResource(player, RES_BULLETS) < g_pickup_nails_max))
-               c = GetResource(item, RES_BULLETS) / max(noammorating, GetResource(player, RES_BULLETS));
-
-       if ((need_rockets) && GetResource(item, RES_ROCKETS) && (GetResource(player, RES_ROCKETS) < g_pickup_rockets_max))
-               c = GetResource(item, RES_ROCKETS) / max(noammorating, GetResource(player, RES_ROCKETS));
-
-       if ((need_cells) && GetResource(item, RES_CELLS) && (GetResource(player, RES_CELLS) < g_pickup_cells_max))
-               c = GetResource(item, RES_CELLS) / max(noammorating, GetResource(player, RES_CELLS));
-
-       if ((need_plasma) && GetResource(item, RES_PLASMA) && (GetResource(player, RES_PLASMA) < g_pickup_plasma_max))
-               c = GetResource(item, RES_PLASMA) / max(noammorating, GetResource(player, RES_PLASMA));
-
-       if ((need_fuel) && GetResource(item, RES_FUEL) && (GetResource(player, RES_FUEL) < g_pickup_fuel_max))
-               c = GetResource(item, RES_FUEL) / max(noammorating, GetResource(player, RES_FUEL));
+       if(item_resource && (GetResource(player, item_resource) < GetResourceLimit(player, item_resource)))
+               c = GetResource(item, item_resource) / max(noammorating, GetResource(player, item_resource));
 
        rating *= min(c, 2);
        if(wpn)
@@ -939,14 +969,21 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        precache_model(this.model);
        precache_sound(this.item_pickupsound);
 
+       if(q3compat && !this.team)
+       {
+               string t = GetField_fullspawndata(this, "team", false);
+               // bones_was_here: this hack is cheaper than changing to a .string strcmp()
+               if(t) this.team = crc16(false, t);
+       }
+
        if (Item_IsLoot(this))
        {
-               this.reset = SUB_Remove;
+               this.reset = RemoveItem;
                set_movetype(this, MOVETYPE_TOSS);
 
                // Savage: remove thrown items after a certain period of time ("garbage collection")
                setthink(this, RemoveItem);
-               this.nextthink = time + 20;
+               this.nextthink = time + autocvar_g_items_dropped_lifetime;
 
                this.takedamage = DAMAGE_YES;
                this.event_damage = Item_Damage;
@@ -981,7 +1018,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                if(this.angles != '0 0 0')
                        this.SendFlags |= ISF_ANGLES;
 
-               this.reset = Item_Reset;
+               this.reset = this.team ? Item_FindTeam : Item_Reset;
                // it's a level item
                if(this.spawnflags & 1)
                        this.noalign = 1;
@@ -1022,19 +1059,19 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
                if(autocvar_spawn_debug >= 2)
                {
-            // why not flags & fl_item?
-                   FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
-                LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
-                LOG_TRACE(" vs ", it.netname, vtos(it.origin));
-                error("Mapper sucks.");
-            });
+                       // why not flags & fl_item?
+                       FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, {
+                               LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin));
+                               LOG_TRACE(" vs ", it.netname, vtos(it.origin));
+                               error("Mapper sucks.");
+                       });
                        this.is_item = true;
                }
 
                weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid));
 
-               if (   def.instanceOfPowerup
-                       || def.instanceOfWeaponPickup
+               if (        def.instanceOfPowerup
+                       ||  def.instanceOfWeaponPickup
                        || (def.instanceOfHealth && def != ITEM_HealthSmall)
                        || (def.instanceOfArmor && def != ITEM_ArmorSmall)
                        || (itemid & (IT_KEY1 | IT_KEY2))
@@ -1056,6 +1093,10 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        setmodel(this, MDL_Null); // precision set below
        //this.effects |= EF_LOWPRECISION;
 
+       // support skinned models for powerups
+       if(!this.skin)
+               this.skin = def.m_skin;
+
        setsize (this, this.pos1 =  def.m_mins, this.pos2 = def.m_maxs);
 
        this.SendFlags |= ISF_SIZE;
@@ -1070,6 +1111,8 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
        if(Item_IsLoot(this))
                this.gravity = 1;
+       else
+               this.glowmod = def.m_color;
 
        if(def.instanceOfWeaponPickup)
        {
@@ -1086,7 +1129,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                if(!this.cnt)
                        this.cnt = 1; // item probability weight
 
-               this.effects |= EF_NODRAW; // marker for item team search
+               this.effects |= EF_NOGUNBOB; // marker for item team search
                InitializeEntity(this, Item_FindTeam, INITPRIO_FINDTARGET);
        }
        else
@@ -1107,18 +1150,21 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
 void StartItem(entity this, GameItem def)
 {
-    def = def.m_spawnfunc_hookreplace(def, this);
-    if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
-    {
-        delete(this);
-        return;
-    }
-    this.classname = def.m_canonical_spawnfunc;
-    _StartItem(
-       this,
-       this.itemdef = def,
-       def.m_respawntime(), // defaultrespawntime
-       def.m_respawntimejitter() // defaultrespawntimejitter
+       def = def.m_spawnfunc_hookreplace(def, this);
+
+       if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
+       {
+               delete(this);
+               return; // TODO does not set startitem_failed
+       }
+
+       this.classname = def.m_canonical_spawnfunc;
+
+       _StartItem(
+               this,
+               this.itemdef = def,
+               def.m_respawntime(), // defaultrespawntime
+               def.m_respawntimejitter() // defaultrespawntimejitter
        );
 }
 
@@ -1203,6 +1249,10 @@ spawnfunc(target_items)
                this.strength_finished = autocvar_g_balance_powerup_strength_time;
        if(!this.invincible_finished)
                this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
+       if(!this.speed_finished)
+               this.speed_finished = autocvar_g_balance_powerup_speed_time;
+       if(!this.invisibility_finished)
+               this.invisibility_finished = autocvar_g_balance_powerup_invisibility_time;
        if(!this.superweapons_finished)
                this.superweapons_finished = autocvar_g_balance_superweapons_time;
 
@@ -1222,6 +1272,8 @@ spawnfunc(target_items)
                        else if(argv(j) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS;
                        else if(argv(j) == "strength")               this.items |= ITEM_Strength.m_itemid;
                        else if(argv(j) == "invincible")             this.items |= ITEM_Shield.m_itemid;
+                       else if(argv(j) == "speed")                  this.items |= ITEM_Speed.m_itemid;
+                       else if(argv(j) == "invisibility")           this.items |= ITEM_Invisibility.m_itemid;
                        else if(argv(j) == "superweapons")           this.items |= IT_SUPERWEAPON;
                        else if(argv(j) == "jetpack")                this.items |= ITEM_Jetpack.m_itemid;
                        else if(argv(j) == "fuel_regen")             this.items |= ITEM_JetpackRegen.m_itemid;
@@ -1229,7 +1281,7 @@ spawnfunc(target_items)
                        {
                                FOREACH(StatusEffect, it.instanceOfBuff,
                                {
-                                       string s = Buff_UndeprecateName(argv(j));
+                                       string s = Buff_CompatName(argv(j));
                                        if(s == it.netname)
                                        {
                                                this.buffdef = it;
@@ -1239,8 +1291,8 @@ spawnfunc(target_items)
                                        }
                                });
                                FOREACH(Weapons, it != WEP_Null, {
-                                       string s = W_UndeprecateName(argv(j));
-                                       if(s == it.netname)
+                                       string s = argv(j);
+                                       if(s == it.netname || s == it.m_deprecated_netname)
                                        {
                                                STAT(WEAPONS, this) |= (it.m_wepset);
                                                if(this.spawnflags == 0 || this.spawnflags == 2)
@@ -1283,6 +1335,8 @@ spawnfunc(target_items)
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
                str = sprintf("%s %s%d %s", str, valueprefix, this.strength_finished * boolean(this.items & ITEM_Strength.m_itemid), "strength");
                str = sprintf("%s %s%d %s", str, valueprefix, this.invincible_finished * boolean(this.items & ITEM_Shield.m_itemid), "invincible");
+               str = sprintf("%s %s%d %s", str, valueprefix, this.invisibility_finished * boolean(this.items & ITEM_Invisibility.m_itemid), "invisibility");
+               str = sprintf("%s %s%d %s", str, valueprefix, this.speed_finished * boolean(this.items & ITEM_Speed.m_itemid), "speed");
                str = sprintf("%s %s%d %s", str, valueprefix, this.superweapons_finished * boolean(this.items & IT_SUPERWEAPON), "superweapons");
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & ITEM_Jetpack.m_itemid), "jetpack");
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & ITEM_JetpackRegen.m_itemid), "fuel_regen");
@@ -1303,7 +1357,8 @@ spawnfunc(target_items)
        n = tokenize_console(this.netname);
        for(int j = 0; j < n; ++j)
        {
-               FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(argv(j)) == it.netname, {
+               string cmd = argv(j);
+               FOREACH(Weapons, it != WEP_Null && (cmd == it.netname || cmd == it.m_deprecated_netname), {
                        it.wr_init(it);
                        break;
                });
@@ -1365,7 +1420,8 @@ bool GiveBuff(entity e, Buff thebuff, int op, int val)
        }
        if(new_buff_time <= 0)
        {
-               StatusEffects_remove(thebuff, e, STATUSEFFECT_REMOVE_TIMEOUT);
+               if(had_buff) // only trigger removal mechanics if there is an effect to remove!
+                       StatusEffects_remove(thebuff, e, STATUSEFFECT_REMOVE_NORMAL);
        }
        else
        {
@@ -1383,12 +1439,12 @@ void GiveSound(entity e, float v0, float v1, float t, Sound snd_incr, Sound snd_
        if(v1 <= v0 - t)
        {
                if(snd_decr != NULL)
-                       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 != NULL)
-                       sound (e, CH_TRIGGER, snd_incr, VOL_BASE, ATTEN_NORM);
+                       sound(e, ((snd_incr == SND_POWERUP) ? CH_TRIGGER_SINGLE : CH_TRIGGER), snd_incr, VOL_BASE, ATTEN_NORM);
        }
 }
 
@@ -1399,7 +1455,7 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa
        else if(v0 > v1)
                e.(regenfield) = max(e.(regenfield), time + regentime);
 }
-bool GiveResourceValue(entity e, int res_type, int op, int val)
+bool GiveResourceValue(entity e, Resource res_type, int op, int val)
 {
        int v0 = GetResource(e, res_type);
        float new_val = 0;
@@ -1439,7 +1495,10 @@ bool GiveStatusEffect(entity e, StatusEffects this, int op, float val)
                        break;
        }
        if(new_eff_time <= 0)
-               StatusEffects_remove(this, e, STATUSEFFECT_REMOVE_TIMEOUT);
+       {
+               if(had_eff) // only trigger removal mechanics if there is an effect to remove!
+                       StatusEffects_remove(this, e, STATUSEFFECT_REMOVE_NORMAL);
+       }
        else
                StatusEffects_apply(this, e, new_eff_time, 0);
        bool have_eff = StatusEffects_active(this, e);
@@ -1458,7 +1517,7 @@ float GiveItems(entity e, float beginarg, float endarg)
 
        int _switchweapon = 0;
 
-       if(CS_CVAR(e).autoswitch)
+       if(CS_CVAR(e).cvar_cl_autoswitch)
        {
                for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
                {
@@ -1481,6 +1540,8 @@ float GiveItems(entity e, float beginarg, float endarg)
        PREGIVE_WEAPONS(e);
        PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength);
        PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield);
+       PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Speed);
+       PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Invisibility);
        //PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Superweapons);
        PREGIVE_RESOURCE(e, RES_BULLETS);
        PREGIVE_RESOURCE(e, RES_CELLS);
@@ -1520,9 +1581,7 @@ float GiveItems(entity e, float beginarg, float endarg)
                                continue;
                        case "ALL":
                                got += GiveBit(e, items, ITEM_JetpackRegen.m_itemid, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Strength, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Shield, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Superweapons, op, val);
+                               FOREACH(StatusEffect, it.instanceOfPowerups, got += GiveStatusEffect(e, it, op, val));
                                got += GiveBit(e, items, IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS, op, val);
                        case "all":
                                got += GiveBit(e, items, ITEM_Jetpack.m_itemid, op, val);
@@ -1560,8 +1619,15 @@ float GiveItems(entity e, float beginarg, float endarg)
                                got += GiveStatusEffect(e, STATUSEFFECT_Strength, op, val);
                                break;
                        case "invincible":
+                       case "shield":
                                got += GiveStatusEffect(e, STATUSEFFECT_Shield, op, val);
                                break;
+                       case "speed":
+                               got += GiveStatusEffect(e, STATUSEFFECT_Speed, op, val);
+                               break;
+                       case "invisibility":
+                               got += GiveStatusEffect(e, STATUSEFFECT_Invisibility, op, val);
+                               break;
                        case "superweapons":
                                got += GiveStatusEffect(e, STATUSEFFECT_Superweapons, op, val);
                                break;
@@ -1591,12 +1657,12 @@ float GiveItems(entity e, float beginarg, float endarg)
                                got += GiveResourceValue(e, RES_FUEL, op, val);
                                break;
                        default:
-                               FOREACH(StatusEffect, it.instanceOfBuff && buff_Available(it) && Buff_UndeprecateName(cmd) == it.netname,
+                               FOREACH(StatusEffect, it.instanceOfBuff && buff_Available(it) && Buff_CompatName(cmd) == it.netname,
                                {
                                        got += GiveBuff(e, it, op, val);
                                        break;
                                });
-                               FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(cmd) == it.netname, {
+                               FOREACH(Weapons, it != WEP_Null && (cmd == it.netname || cmd == it.m_deprecated_netname), {
                     got += GiveWeapon(e, it.m_id, op, val);
                     break;
                                });
@@ -1616,8 +1682,10 @@ float GiveItems(entity e, float beginarg, float endarg)
                        if(STAT(WEAPONS, e) & (it.m_wepset))
                                it.wr_init(it);
        });
-       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength, 1, SND_POWERUP, SND_POWEROFF);
-       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield, 1, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Speed, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Invisibility, SND_POWERUP, SND_POWEROFF);
        POSTGIVE_RESOURCE(e, RES_BULLETS, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_RESOURCE(e, RES_CELLS, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_RESOURCE(e, RES_PLASMA, 0, SND_ITEMPICKUP, SND_Null);