]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/items/items.qc
Merge branch 'Goodspeed/Naitlee/ChineseLocalizationUpdate' into 'master'
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / items / items.qc
index f2056cbc25c19d40bdd8e705c7d8ad6b86fadcf5..5e900a603977c0b12cc1eb6d834dd545aa699e6e 100644 (file)
@@ -12,6 +12,7 @@
 #include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/notifications/all.qh>
+#include <common/resources/resources.qh>
 #include <common/util.qh>
 #include <common/weapons/_all.qh>
 #include <common/wepent.qh>
@@ -63,9 +64,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);
@@ -198,7 +199,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))
        {
@@ -247,13 +247,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)
@@ -447,7 +447,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)
@@ -483,7 +483,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)
                {
@@ -551,22 +551,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;
-               StatusEffects_apply(STATUSEFFECT_Speed, player, max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time) + item.speed_finished, 0);
+               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;
-               StatusEffects_apply(STATUSEFFECT_Invisibility, player, max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time) + item.invisibility_finished, 0);
+               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)
        {
@@ -614,7 +634,7 @@ void Item_Touch(entity this, entity toucher)
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
-                       delete(this);
+                       RemoveItem(this);
                        return;
                }
        }
@@ -707,7 +727,6 @@ LABEL(pickup)
 void Item_Reset(entity this)
 {
        Item_Show(this, !this.state);
-       setorigin(this, this.origin);
 
        if (Item_IsLoot(this))
        {
@@ -727,45 +746,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);
 }
@@ -954,14 +976,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;
@@ -996,7 +1025,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;
@@ -1066,11 +1095,15 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        this.bot_pickupevalfunc = pickupevalfunc;
        this.bot_pickupbasevalue = pickupbasevalue;
        this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str());
-       this.netname = (def.m_weapon) ? def.m_weapon.netname : def.netname;
+       this.netname = itemname;
        settouch(this, Item_Touch);
        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;
@@ -1085,6 +1118,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)
        {
@@ -1101,7 +1136,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
@@ -1127,7 +1162,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;
@@ -1410,12 +1445,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);
        }
 }
 
@@ -1426,7 +1461,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;
@@ -1488,7 +1523,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)
                {