]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/blobdiff - qcsrc/server/items/items.qc
items: only draw the removal effect when appropriate
[xonotic/xonotic-data.pk3dir.git] / qcsrc / server / items / items.qc
index 0f764c8627fca5aa90d8867f9937e508d353ba3e..829418497dff6483dd53816dd2451714b6624f60 100644 (file)
@@ -184,7 +184,7 @@ void Item_Show(entity e, int mode)
 
 void Item_Think(entity this)
 {
-       if (Item_IsLoot(this))
+       if (ITEM_IS_LOOT(this))
        {
                if (time < this.wait - IT_DESPAWNFX_TIME)
                        this.nextthink = min(time + IT_UPDATE_INTERVAL, this.wait - IT_DESPAWNFX_TIME); // ensuring full time for effects
@@ -202,8 +202,13 @@ void Item_Think(entity this)
                        }
                }
 
-               // enable pickup by the player who threw it
-               this.owner = NULL;
+               if (this.itemdef.instanceOfPowerup)
+                       powerups_DropItem_Think(this);
+
+               // caution: kludge FIXME (with sv_legacy_bbox_expand)
+               // this works around prediction errors caused by bbox discrepancy between SVQC and CSQC
+               if (this.velocity == '0 0 0' && IS_ONGROUND(this))
+                       this.gravity = 0; // don't send ISF_DROP anymore
 
                // send slow updates even if the item didn't move
                // recovers prediction desyncs where server thinks item stopped, client thinks it didn't
@@ -670,10 +675,11 @@ bool Item_GiveTo(entity item, entity player)
 void Item_Touch(entity this, entity toucher)
 {
        // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
-       if (Item_IsLoot(this))
+       if (ITEM_IS_LOOT(this))
        {
                if (ITEM_TOUCH_NEEDKILL())
                {
+                       this.SendFlags |= ISF_REMOVEFX;
                        RemoveItem(this);
                        return;
                }
@@ -695,7 +701,7 @@ void Item_Touch(entity this, entity toucher)
 
        toucher = M_ARGV(1, entity);
 
-       if (Item_IsExpiring(this))
+       if (ITEM_IS_EXPIRING(this))
        {
                this.strength_finished = max(0, this.strength_finished - time);
                this.invincible_finished = max(0, this.invincible_finished - time);
@@ -706,7 +712,7 @@ void Item_Touch(entity this, entity toucher)
        bool gave = ITEM_HANDLE(Pickup, this.itemdef, this, toucher);
        if (!gave)
        {
-               if (Item_IsExpiring(this))
+               if (ITEM_IS_EXPIRING(this))
                {
                        // undo what we did above
                        this.strength_finished += time;
@@ -735,9 +741,10 @@ LABEL(pickup)
                return;
        }
 
-       if (Item_IsLoot(this))
+       if (ITEM_IS_LOOT(this))
        {
-               delete(this);
+               this.SendFlags |= ISF_REMOVEFX;
+               RemoveItem(this);
                return;
        }
        if (!this.spawnshieldtime)
@@ -769,7 +776,7 @@ void Item_Reset(entity this)
 {
        Item_Show(this, !this.state);
 
-       if (Item_IsLoot(this))
+       if (ITEM_IS_LOOT(this))
        {
                return;
        }
@@ -827,7 +834,7 @@ void Item_CopyFields(entity this, entity to)
 {
        setorigin(to, this.origin);
        to.spawnflags = this.spawnflags;
-       to.noalign = Item_ShouldKeepPosition(this);
+       to.noalign = ITEM_SHOULD_KEEP_POSITION(this);
        to.cnt = this.cnt;
        to.team = this.team;
        to.spawnfunc_checked = true;
@@ -842,7 +849,16 @@ void RemoveItem(entity this)
        if(wasfreed(this) || !this) { return; }
        if(this.waypointsprite_attached)
                WaypointSprite_Kill(this.waypointsprite_attached);
-       delete(this);
+
+       if (this.SendFlags & ISF_REMOVEFX)
+       {
+               // delay removal until ISF_REMOVEFX has been sent
+               setthink(this, RemoveItem);
+               this.nextthink = time + 2 * autocvar_sys_ticrate; // micro optimisation: next frame will be too soon
+               this.solid = SOLID_NOT; // untouchable
+       }
+       else
+               delete(this);
 }
 
 // pickup evaluation functions
@@ -1004,7 +1020,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        // set item size before we spawn a waypoint or droptofloor or MoveOutOfSolid
        setsize (this, this.pos1 = def.m_mins, this.pos2 = def.m_maxs);
 
-       if (Item_IsLoot(this))
+       if (ITEM_IS_LOOT(this))
        {
                this.reset = RemoveItem;
 
@@ -1015,13 +1031,16 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                this.nextthink = time + IT_UPDATE_INTERVAL;
                this.wait = time + autocvar_g_items_dropped_lifetime;
 
+               this.owner = NULL; // anyone can pick this up, including the player who threw it
+               this.item_spawnshieldtime = time + 0.5; // but not straight away
+
                this.takedamage = DAMAGE_YES;
                this.event_damage = Item_Damage;
                // enable this to have thrown items burn in lava
                //this.damagedbycontents = true;
                //IL_PUSH(g_damagedbycontents, this);
 
-               if (Item_IsExpiring(this))
+               if (ITEM_IS_EXPIRING(this))
                {
                        // if item is worthless after a timer, have it expire then
                        this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
@@ -1038,6 +1057,8 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        }
        else
        {
+               this.reset = Item_Reset;
+
                // must be done after def.m_iteminit() as that may set ITEM_FLAG_MUTATORBLOCKED
                if(!have_pickup_item(this))
                {
@@ -1148,7 +1169,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
 
        if(def.instanceOfWeaponPickup)
        {
-               if (!Item_IsLoot(this)) // if dropped, colormap is already set up nicely
+               if (!ITEM_IS_LOOT(this)) // if dropped, colormap is already set up nicely
                        this.colormap = 1024; // color shirt=0 pants=0 grey
                if (!(this.spawnflags & 1024))
                        this.ItemStatus |= ITS_ANIMATE1;
@@ -1166,10 +1187,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                this.reset = Item_FindTeam;
        }
        else
-       {
-               this.reset = Item_Reset;
                Item_Reset(this);
-       }
 
        Net_LinkEntity(this, !(def.instanceOfPowerup || def.instanceOfHealth || def.instanceOfArmor), 0, ItemSend);
 
@@ -1204,7 +1222,7 @@ int group_count = 1;
 
 void setItemGroup(entity this)
 {
-       if(!IS_SMALL(this.itemdef) || Item_IsLoot(this))
+       if(!IS_SMALL(this.itemdef) || ITEM_IS_LOOT(this))
                return;
 
        FOREACH_ENTITY_RADIUS(this.origin, 120, (it != this) && IS_SMALL(it.itemdef),
@@ -1249,7 +1267,7 @@ void setItemGroupCount()
 
 void target_items_use(entity this, entity actor, entity trigger)
 {
-       if(Item_IsLoot(actor))
+       if(ITEM_IS_LOOT(actor))
        {
                EXACTTRIGGER_TOUCH(this, trigger);
                delete(actor);
@@ -1264,7 +1282,7 @@ void target_items_use(entity this, entity actor, entity trigger)
                EXACTTRIGGER_TOUCH(this, trigger);
        }
 
-       IL_EACH(g_items, it.enemy == actor && Item_IsLoot(it),
+       IL_EACH(g_items, it.enemy == actor && ITEM_IS_LOOT(it),
        {
                delete(it);
        });