]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'bones_was_here/svqc_itemfixes' into 'master'
authorterencehill <piuntn@gmail.com>
Sun, 23 Jul 2023 08:17:47 +0000 (08:17 +0000)
committerterencehill <piuntn@gmail.com>
Sun, 23 Jul 2023 08:17:47 +0000 (08:17 +0000)
Items fixes and refactoring

Closes #2857

See merge request xonotic/xonotic-data.pk3dir!1222

qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh
qcsrc/common/mutators/mutator/powerups/powerup/shield.qh
qcsrc/common/mutators/mutator/powerups/powerup/speed.qh
qcsrc/common/mutators/mutator/powerups/powerup/strength.qh
qcsrc/server/items/items.qc
qcsrc/server/items/items.qh
qcsrc/server/weapons/spawning.qc

index 15856ad6a834de0187328aafbf8cf0297321abde..12d4316f4ea7a8974b0ab0a1ea64e2b0bbc7319b 100644 (file)
@@ -279,25 +279,15 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena)
 
 void instagib_replace_item_with(entity this, GameItem def)
 {
-       entity new_item = NULL;
+       entity new_item = spawn();
        switch (def)
        {
                case ITEM_Invisibility:
-                       new_item = new(item_invisibility);
                        new_item.invisibility_finished = autocvar_g_instagib_invisibility_time;
                        break;
                case ITEM_Speed:
-                       new_item = new(item_speed);
                        new_item.speed_finished = autocvar_g_instagib_speed_time;
                        break;
-               case ITEM_ExtraLife:
-                       new_item = new(item_extralife);
-                       break;
-               case ITEM_VaporizerCells:
-                       new_item = new(item_vaporizer_cells);
-                       break;
-               default:
-                       error("Unhandled replacement item.");
        }
        Item_CopyFields(this, new_item);
        StartItem(new_item, def);
@@ -329,49 +319,43 @@ void instagib_replace_item_with_random_powerup(entity item)
 MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
 {
        entity item = M_ARGV(0, entity);
-       entity def = item.itemdef;
-       if(def == ITEM_Strength || def == ITEM_Shield || def == ITEM_HealthMega || def == ITEM_ArmorMega)
-       {
-               if(autocvar_g_powerups)
-                       instagib_replace_item_with_random_powerup(item);
-               return true;
-       }
 
-       if(def == ITEM_Cells)
-       {
-               if(autocvar_g_instagib_ammo_convert_cells)
-                       instagib_replace_item_with(item, ITEM_VaporizerCells);
-               return true;
-       }
-       else if(def == ITEM_Rockets)
-       {
-               if(autocvar_g_instagib_ammo_convert_rockets)
-                       instagib_replace_item_with(item, ITEM_VaporizerCells);
-               return true;
-       }
-       else if(def == ITEM_Shells)
+       switch (item.itemdef)
        {
-               if(autocvar_g_instagib_ammo_convert_shells)
-                       instagib_replace_item_with(item, ITEM_VaporizerCells);
-               return true;
-       }
-       else if(def == ITEM_Bullets)
-       {
-               if(autocvar_g_instagib_ammo_convert_bullets)
-                       instagib_replace_item_with(item, ITEM_VaporizerCells);
-               return true;
+               case ITEM_Strength: case ITEM_Shield: case ITEM_HealthMega: case ITEM_ArmorMega:
+                       if(autocvar_g_powerups)
+                               instagib_replace_item_with_random_powerup(item);
+                       return true;
+               case ITEM_Cells:
+                       if(autocvar_g_instagib_ammo_convert_cells)
+                               instagib_replace_item_with(item, ITEM_VaporizerCells);
+                       return true;
+               case ITEM_Rockets:
+                       if(autocvar_g_instagib_ammo_convert_rockets)
+                               instagib_replace_item_with(item, ITEM_VaporizerCells);
+                       return true;
+               case ITEM_Shells:
+                       if(autocvar_g_instagib_ammo_convert_shells)
+                               instagib_replace_item_with(item, ITEM_VaporizerCells);
+                       return true;
+               case ITEM_Bullets:
+                       if(autocvar_g_instagib_ammo_convert_bullets)
+                               instagib_replace_item_with(item, ITEM_VaporizerCells);
+                       return true;
        }
 
-       if(item.weapon == WEP_VAPORIZER.m_id && ITEM_IS_LOOT(item))
+       switch (item.weapon)
        {
-               SetResource(item, RES_CELLS, autocvar_g_instagib_ammo_drop);
-               return false;
-       }
-
-       if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id)
-       {
-               instagib_replace_item_with(item, ITEM_VaporizerCells);
-               return true;
+               case WEP_VAPORIZER.m_id:
+                       if (ITEM_IS_LOOT(item))
+                       {
+                               SetResource(item, RES_CELLS, autocvar_g_instagib_ammo_drop);
+                               return false;
+                       }
+                       break;
+               case WEP_DEVASTATOR.m_id: case WEP_VORTEX.m_id:
+                       instagib_replace_item_with(item, ITEM_VaporizerCells);
+                       return true;
        }
 
        if(item.itemdef.instanceOfPowerup)
index 53264eeb2e2ccd69f72de9d5a1fa2768c7fbd85a..bf8d8fb10a903820e9ad9e546c444c05b9274c19 100644 (file)
@@ -16,15 +16,13 @@ SOUND(Invisibility, Item_Sound("powerup"));
 #ifdef SVQC
 .float invisibility_finished;
 
-bool autocvar_g_powerups_invisibility = 1;
-float autocvar_g_balance_powerup_invisibility_alpha = 0.15;
-float autocvar_g_balance_powerup_invisibility_time = 30;
-void powerup_invisibility_init(Pickup this, entity item)
+bool autocvar_g_powerups_invisibility;
+float autocvar_g_balance_powerup_invisibility_alpha;
+float autocvar_g_balance_powerup_invisibility_time;
+void powerup_invisibility_init(Pickup def, entity item)
 {
-    if(autocvar_g_powerups_invisibility)
-        this.spawnflags = ITEM_FLAG_NORMAL;
-    else
-        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+    if(!autocvar_g_powerups || !autocvar_g_powerups_invisibility)
+        def.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
 
     if(!item.invisibility_finished)
         item.invisibility_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invisibility_time;
@@ -36,6 +34,7 @@ REGISTER_ITEM(Invisibility, Powerup) {
     this.m_iteminit         =   powerup_invisibility_init;
 #endif
 #ifdef GAMEQC
+    this.spawnflags         =   ITEM_FLAG_NORMAL;
     this.m_itemid           =   IT_INVISIBILITY;
     this.m_model            =   MDL_BUFF; // TODO: MDL_Invisibility_ITEM when new model available
     this.m_skin             =   12;
index d665fb894d37c77eccfe563ed892cb78f4f73183..de2f5dc29a40b544cce37631070c358460b731fb 100644 (file)
@@ -14,16 +14,14 @@ SOUND(Shield, Item_Sound("powerup_shield"));
 #endif
 
 #ifdef SVQC
-bool autocvar_g_powerups_shield = 1;
+bool autocvar_g_powerups_shield;
 float autocvar_g_balance_powerup_invincible_takedamage;
-float autocvar_g_balance_powerup_invincible_takeforce = 0.33;
+float autocvar_g_balance_powerup_invincible_takeforce;
 float autocvar_g_balance_powerup_invincible_time;
-void powerup_shield_init(Pickup this, entity item)
+void powerup_shield_init(Pickup def, entity item)
 {
-    if(autocvar_g_powerups_shield)
-        this.spawnflags = ITEM_FLAG_NORMAL;
-    else
-        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+    if(!autocvar_g_powerups || !autocvar_g_powerups_shield)
+        def.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
 
     if(!item.invincible_finished)
         item.invincible_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invincible_time;
@@ -35,6 +33,7 @@ REGISTER_ITEM(Shield, Powerup) {
     this.m_iteminit         =   powerup_shield_init;
 #endif
 #ifdef GAMEQC
+    this.spawnflags         =   ITEM_FLAG_NORMAL;
     this.m_itemid           =   IT_INVINCIBLE;
     this.m_model            =   MDL_Shield_ITEM;
     this.m_sound            =   SND_Shield;
index 4bda28e35917ffeb02904adad7210048948e0a25..c816fec53a7ffc8f3fc6d48c2dad5bbe853c1f11 100644 (file)
@@ -16,16 +16,14 @@ SOUND(Speed, Item_Sound("powerup_shield"));
 #ifdef SVQC
 .float speed_finished;
 
-bool autocvar_g_powerups_speed = 1;
-float autocvar_g_balance_powerup_speed_attackrate = 0.8;
-float autocvar_g_balance_powerup_speed_highspeed = 1.5;
-float autocvar_g_balance_powerup_speed_time = 30;
-void powerup_speed_init(Pickup this, entity item)
+bool autocvar_g_powerups_speed;
+float autocvar_g_balance_powerup_speed_attackrate;
+float autocvar_g_balance_powerup_speed_highspeed;
+float autocvar_g_balance_powerup_speed_time;
+void powerup_speed_init(Pickup def, entity item)
 {
-    if(autocvar_g_powerups_speed)
-        this.spawnflags = ITEM_FLAG_NORMAL;
-    else
-        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+    if(!autocvar_g_powerups || !autocvar_g_powerups_speed)
+        def.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
 
     if(!item.speed_finished)
         item.speed_finished = (item.count) ? item.count : autocvar_g_balance_powerup_speed_time;
@@ -37,6 +35,7 @@ REGISTER_ITEM(Speed, Powerup) {
     this.m_iteminit         =   powerup_speed_init;
 #endif
 #ifdef GAMEQC
+    this.spawnflags         =   ITEM_FLAG_NORMAL;
     this.m_itemid           =   IT_SPEED;
     this.m_model            =   MDL_BUFF; // TODO: MDL_Speed_ITEM when new model available
     this.m_skin             =   9;
index aea3bcf2f31c74190afddef9749186b428f411f2..79ef229f1cbbf751b5b0a39ac5b76ab7f6fa70d4 100644 (file)
@@ -14,18 +14,16 @@ SOUND(Strength, Item_Sound("powerup"));
 #endif
 
 #ifdef SVQC
-bool autocvar_g_powerups_strength = 1;
+bool autocvar_g_powerups_strength;
 float autocvar_g_balance_powerup_strength_damage;
 float autocvar_g_balance_powerup_strength_force;
 float autocvar_g_balance_powerup_strength_selfdamage;
 float autocvar_g_balance_powerup_strength_selfforce;
 float autocvar_g_balance_powerup_strength_time;
-void powerup_strength_init(Pickup this, entity item)
+void powerup_strength_init(Pickup def, entity item)
 {
-    if(autocvar_g_powerups_strength)
-        this.spawnflags = ITEM_FLAG_NORMAL;
-    else
-        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+    if(!autocvar_g_powerups || !autocvar_g_powerups_strength)
+        def.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
 
     if(!item.strength_finished)
         item.strength_finished = (item.count) ? item.count : autocvar_g_balance_powerup_strength_time;
@@ -37,6 +35,7 @@ REGISTER_ITEM(Strength, Powerup) {
     this.m_iteminit         =   powerup_strength_init;
 #endif
 #ifdef GAMEQC
+    this.spawnflags         =   ITEM_FLAG_NORMAL;
     this.m_itemid           =   IT_STRENGTH;
     this.m_model            =   MDL_Strength_ITEM;
     this.m_sound            =   SND_Strength;
index 829418497dff6483dd53816dd2451714b6624f60..fe677d7f190e095e5be5a1fe776d2e552c70158e 100644 (file)
@@ -100,14 +100,7 @@ bool have_pickup_item(entity this)
        if (this.itemdef.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
                return false;
 
-       if(this.itemdef.instanceOfPowerup)
-       {
-               if(autocvar_g_powerups > 0)
-                       return true;
-               if(autocvar_g_powerups == 0)
-                       return false;
-       }
-       else
+       if(!this.itemdef.instanceOfPowerup)
        {
                if(autocvar_g_pickup_items > 0)
                        return true;
@@ -972,49 +965,56 @@ void item_use(entity this, entity actor, entity trigger)
        gettouch(this)(this, actor);
 }
 
-// if defaultrespawntime is 0 get respawntime from the item definition
-// if defaultrespawntimejitter is 0 get respawntimejitter from the item definition
-void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter)
+void StartItem(entity this, entity def)
 {
-       string itemname = def.m_name;
-       float(entity player, entity item) pickupevalfunc = def.m_pickupevalfunc;
-       float pickupbasevalue = def.m_botvalue;
-
-       startitem_failed = false;
+       if (def.m_spawnfunc_hookreplace)
+               def = def.m_spawnfunc_hookreplace(def, this);
+       this.itemdef = def;
+       if (def.m_canonical_spawnfunc != "") // FIXME why do weapons set itemdef to an entity that doesn't have this?
+               this.classname = def.m_canonical_spawnfunc;
 
-       this.item_model_ent = def.m_model;
-       this.item_pickupsound_ent = def.m_sound;
-       if (!this.item_pickupsound)
-               this.item_pickupsound = Sound_fixpath(this.item_pickupsound_ent);
+       startitem_failed = true; // early return means failure
 
+       // some mutators check for resources set by m_iteminit in FilterItem
        if(def.m_iteminit)
                def.m_iteminit(def, this);
 
+       // also checked by some mutators in FilterItem
+       this.items = def.m_itemid;
+       this.weapon = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0;
+       if(this.weapon)
+               STAT(WEAPONS, this) = WepSet_FromWeapon(REGISTRY_GET(Weapons, this.weapon));
+       this.flags = FL_ITEM | def.m_itemflags;
+
+       // FilterItem may change any field of a specific instance of an item, but
+       // it must not change any itemdef field (would cause mutators to break other mutators),
+       // and must not convert items into different ones (StartItem could be refactored to support that).
+       if(MUTATOR_CALLHOOK(FilterItem, this))
+       {
+               delete(this);
+               return;
+       }
+
+       if (!this.item_model_ent)
+               this.item_model_ent = def.m_model;
+
+       if (!this.item_pickupsound_ent)
+               this.item_pickupsound_ent = def.m_sound;
+       if (!this.item_pickupsound && this.item_pickupsound_ent)
+               this.item_pickupsound = Sound_fixpath(this.item_pickupsound_ent);
+       if (this.item_pickupsound == "")
+               LOG_WARNF("No pickup sound set for a %s", this.classname);
+
        if(!this.pickup_anyway && def.m_pickupanyway)
                this.pickup_anyway = def.m_pickupanyway();
 
-       int itemid = def.m_itemid;
-       this.items = itemid;
-       int weaponid = def.instanceOfWeaponPickup ? def.m_weapon.m_id : 0;
-       this.weapon = weaponid;
-
        // bones_was_here TODO: implement sv_cullentities_dist and replace g_items_maxdist with it
        if(!this.fade_end)
                this.fade_end = autocvar_g_items_maxdist;
 
-       if(weaponid)
-               STAT(WEAPONS, this) = WepSet_FromWeapon(REGISTRY_GET(Weapons, weaponid));
-
-       this.flags = FL_ITEM | def.m_itemflags;
+       // bones_was_here TODO: can we do this after we're sure the entity won't be deleted?
        IL_PUSH(g_items, this);
 
-       if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item
-       {
-               startitem_failed = true;
-               delete(this);
-               return;
-       }
-
        this.mdl = this.model ? this.model : strzone(this.item_model_ent.model_str());
        setmodel(this, MDL_Null); // precision set below
        // set item size before we spawn a waypoint or droptofloor or MoveOutOfSolid
@@ -1050,7 +1050,6 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                traceline(this.origin, this.origin, MOVE_NORMAL, this);
                if (trace_dpstartcontents & DPCONTENTS_NODROP)
                {
-                       startitem_failed = true;
                        delete(this);
                        return;
                }
@@ -1062,7 +1061,6 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                // must be done after def.m_iteminit() as that may set ITEM_FLAG_MUTATORBLOCKED
                if(!have_pickup_item(this))
                {
-                       startitem_failed = true;
                        delete(this);
                        return;
                }
@@ -1070,8 +1068,15 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                // must be done before Item_Reset() and after MUTATORBLOCKED check (blocked items may have null func ptrs)
                if(!this.respawntime) // both need to be set
                {
-                       this.respawntime = defaultrespawntime ? defaultrespawntime : def.m_respawntime();
-                       this.respawntimejitter = defaultrespawntimejitter ? defaultrespawntimejitter : def.m_respawntimejitter();
+                       if (def.m_respawntime)
+                               this.respawntime = def.m_respawntime();
+                       else
+                               LOG_WARNF("Default respawntime for a %s is unavailable from its itemdef", this.classname);
+
+                       if (def.m_respawntimejitter)
+                               this.respawntimejitter = def.m_respawntimejitter();
+                       else
+                               LOG_WARNF("Default respawntimejitter for a %s is unavailable from its itemdef", this.classname);
                }
 
                if(this.angles != '0 0 0')
@@ -1110,7 +1115,6 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                {
                        // target_give not yet supported; maybe later
                        print("removed targeted ", this.classname, "\n");
-                       startitem_failed = true;
                        delete(this);
                        return;
                }
@@ -1123,20 +1127,20 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
                {
                        // 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("XXX Found duplicated item: ", def.m_name, 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));
+               weaponsInMap |= WepSet_FromWeapon(REGISTRY_GET(Weapons, this.weapon));
 
                if (        def.instanceOfPowerup
                        ||  def.instanceOfWeaponPickup
                        || (def.instanceOfHealth && def != ITEM_HealthSmall)
                        || (def.instanceOfArmor && def != ITEM_ArmorSmall)
-                       || (itemid & (IT_KEY1 | IT_KEY2))
+                       || (def.m_itemid & (IT_KEY1 | IT_KEY2))
                )
                {
                        if(!this.target || this.target == "")
@@ -1149,9 +1153,9 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        }
 
        this.bot_pickup = true;
-       this.bot_pickupevalfunc = pickupevalfunc;
-       this.bot_pickupbasevalue = pickupbasevalue;
-       this.netname = itemname;
+       this.bot_pickupevalfunc = def.m_pickupevalfunc;
+       this.bot_pickupbasevalue = def.m_botvalue;
+       this.netname = def.m_name;
        settouch(this, Item_Touch);
        //this.effects |= EF_LOWPRECISION;
 
@@ -1194,7 +1198,6 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        // call this hook after everything else has been done
        if (MUTATOR_CALLHOOK(Item_Spawn, this))
        {
-               startitem_failed = true;
                delete(this);
                return;
        }
@@ -1205,16 +1208,8 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        precache_sound(this.item_pickupsound);
 
        setItemGroup(this);
-}
 
-void StartItem(entity this, GameItem def)
-{
-       def = def.m_spawnfunc_hookreplace(def, this);
-
-       this.classname = def.m_canonical_spawnfunc;
-
-       this.itemdef = def;
-       _StartItem(this, this.itemdef, 0, 0);
+       startitem_failed = false;
 }
 
 #define IS_SMALL(def) ((def.instanceOfHealth && def == ITEM_HealthSmall) || (def.instanceOfArmor && def == ITEM_ArmorSmall))
index a38765c276838037bb7844810476fe6b07d70224..7bfb973537be1f46ea13b2897b6da11864b7a258 100644 (file)
@@ -11,11 +11,8 @@ int autocvar_g_pickup_items;
 bool autocvar_g_nodepthtestitems;
 #define autocvar_g_weapon_stay cvar("g_weapon_stay")
 
-void StartItem(entity this, entity a);
-.int item_group;
-.int item_group_count;
-
 float autocvar_sv_simple_items;
+
 bool ItemSend(entity this, entity to, int sf);
 
 const float ITEM_RESPAWN_TICKS = 10;
@@ -92,8 +89,10 @@ float healtharmor_pickupevalfunc(entity player, entity item);
 
 .bool is_item;
 .entity itemdef;
-void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter);
+void StartItem(entity this, entity def);
 
+.int item_group;
+.int item_group_count;
 void setItemGroup(entity this);
 void setItemGroupCount();
 
index 3dd040b97132c52db28b70e0ed5d444a97dc3868..e2a28f254ade99e3704d5b3880a113f8c3132b51 100644 (file)
@@ -155,9 +155,7 @@ void weapon_defaultspawnfunc(entity this, Weapon wpn)
        else
                this.glowmod = colormapPaletteColor(this.owner.clientcolors & 0x0F, true);
 
-       GameItem def = wpn.m_pickup;
-       this.itemdef = def;
-       _StartItem(this, this.itemdef, this.respawntime, this.respawntimejitter);
+       StartItem(this, wpn.m_pickup);
 
        #if 0 // WEAPONTODO
        if (this.modelindex) { // don't precache if this was removed