]> de.git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
items: replace broken loot think implementation
authorbones_was_here <bones_was_here@xonotic.au>
Thu, 15 Jun 2023 20:36:11 +0000 (06:36 +1000)
committerbones_was_here <bones_was_here@xonotic.au>
Thu, 15 Jun 2023 20:36:11 +0000 (06:36 +1000)
Implements a slow update rate for loot items, just enough to correct occasional prediction errors.
Includes SVQC prerequisites for loot item despawn effects

qcsrc/common/items/item.qh
qcsrc/server/items/items.qc
qcsrc/server/weapons/throwing.qc
qcsrc/server/weapons/throwing.qh

index 350421465459bdd57312511b2cd935c565327a1d..2de498aaf48f6a8c2ca2f6e58699f22d6ce95b85 100644 (file)
@@ -57,6 +57,13 @@ const int ITS_AVAILABLE         = BIT(3);
 const int ITS_ALLOWFB           = BIT(4);
 const int ITS_ALLOWSI           = BIT(5);
 const int ITS_GLOW              = BIT(6);
+const int ITS_EXPIRING          = BIT(7);
+
+// enough to notice it's about to despawn and circle jump to grab it
+const float IT_DESPAWNFX_TIME = 1.5;
+
+// 2hz probably enough to correct a desync caused by serious lag
+const float IT_UPDATE_INTERVAL = 0.5;
 
 .float fade_start;
 .float fade_end;
index 57720ef9aff2e29fe8912ba276d50bb379e58600..5214d12ef6ff138f3f9c1acbc65ae83f33015b92 100644 (file)
@@ -188,9 +188,40 @@ void Item_Show(entity e, int mode)
 
 void Item_Think(entity this)
 {
-       this.nextthink = time;
-       if(this.origin != this.oldorigin)
+       if (Item_IsLoot(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
+               else
+               {
+                       // despawning soon, start effects
+                       this.ItemStatus |= ITS_EXPIRING;
+                       this.SendFlags |= ISF_STATUS;
+                       if (time < this.wait - IT_UPDATE_INTERVAL)
+                               this.nextthink = time + IT_UPDATE_INTERVAL;
+                       else
+                       {
+                               setthink(this, RemoveItem);
+                               this.nextthink = this.wait;
+                       }
+               }
+
+               // enable pickup by the player who threw it
+               this.owner = NULL;
+
+               // send slow updates even if the item didn't move
+               // recovers prediction desyncs where server thinks item stopped, client thinks it didn't
                ItemUpdate(this);
+       }
+       else
+       {
+               // bones_was_here: TODO: predict movers, enable client prediction of items with a groundentity,
+               // and then send those less often too (and not all on the same frame)
+               this.nextthink = time;
+
+               if(this.origin != this.oldorigin)
+                       ItemUpdate(this);
+       }
 }
 
 bool Item_ItemsTime_SpectatorOnly(GameItem it);
@@ -986,9 +1017,9 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                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 + autocvar_g_items_dropped_lifetime;
+               setthink(this, Item_Think);
+               this.nextthink = time + IT_UPDATE_INTERVAL;
+               this.wait = time + autocvar_g_items_dropped_lifetime;
 
                this.takedamage = DAMAGE_YES;
                this.event_damage = Item_Damage;
index 01c6ac0d200634347c27d88ba2631322b8fb7c66..a979b7b0c65f9689e33be33270414fca26d37ba7 100644 (file)
 #include <server/weapons/weaponsystem.qh>
 #include <server/world.qh>
 
-void thrown_wep_think(entity this)
-{
-       this.nextthink = time;
-       if(this.oldorigin != this.origin)
-       {
-               this.SendFlags |= ISF_LOCATION;
-               this.oldorigin = this.origin;
-       }
-       this.owner = NULL;
-       float timeleft = this.savenextthink - time;
-       if(timeleft > 1)
-               SUB_SetFade(this, this.savenextthink - 1, 1);
-       else if(timeleft > 0)
-               SUB_SetFade(this, time, timeleft);
-       else
-               SUB_VanishOrRemove(this);
-}
-
 // returns amount of ammo used, or -1 for failure, or 0 for no ammo count
 float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity)
 {
@@ -91,9 +73,7 @@ float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector
        weapon_defaultspawnfunc(wep, info);
        if(startitem_failed)
                return -1;
-       setthink(wep, thrown_wep_think);
-       wep.savenextthink = wep.nextthink;
-       wep.nextthink = min(wep.nextthink, time + 0.5);
+
        wep.pickup_anyway = true; // these are ALWAYS pickable
 
        //wa = W_AmmoItemCode(wpn);
index b3c7df645e26c247e6332b23987b7b09ae683fd4..afbc51851632648b89cf01c0ff3a3b1c9d2a63ec 100644 (file)
@@ -5,9 +5,6 @@
 
 bool autocvar_g_weapon_throwable;
 
-.float savenextthink;
-void thrown_wep_think(entity this);
-
 // returns amount of ammo used, or -1 for failure, or 0 for no ammo count
 float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity);