]> 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 99c0165fd3a1b51061979bf4ff3fb384ab3bcdd2..e68d858a8e72e55d9ad4a156cd0b81400cafbd8e 100644 (file)
@@ -11,6 +11,7 @@
 #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>
@@ -199,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))
        {
@@ -248,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)
@@ -476,6 +476,15 @@ bool Item_GiveAmmoTo(entity item, entity player, Resource res_type, float ammoma
        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
@@ -510,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)
                                        {
@@ -635,7 +647,7 @@ void Item_Touch(entity this, entity toucher)
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
-                       delete(this);
+                       RemoveItem(this);
                        return;
                }
        }
@@ -728,7 +740,6 @@ LABEL(pickup)
 void Item_Reset(entity this)
 {
        Item_Show(this, !this.state);
-       setorigin(this, this.origin);
 
        if (Item_IsLoot(this))
        {
@@ -748,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);
 }
@@ -820,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;
        }
@@ -846,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;
@@ -862,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)
@@ -975,9 +969,16 @@ 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")
@@ -1017,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;
@@ -1093,7 +1094,8 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        //this.effects |= EF_LOWPRECISION;
 
        // support skinned models for powerups
-       this.skin = def.m_skin;
+       if(!this.skin)
+               this.skin = def.m_skin;
 
        setsize (this, this.pos1 =  def.m_mins, this.pos2 = def.m_maxs);
 
@@ -1127,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
@@ -1153,7 +1155,7 @@ void StartItem(entity this, GameItem def)
        if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
        {
                delete(this);
-               return;
+               return; // TODO does not set startitem_failed
        }
 
        this.classname = def.m_canonical_spawnfunc;
@@ -1279,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;
@@ -1289,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)
@@ -1355,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;
                });
@@ -1654,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;
                                });