]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'bones_was_here/powerups' into 'master'
authorMario <mario.mario@y7mail.com>
Wed, 28 Jul 2021 10:44:08 +0000 (10:44 +0000)
committerMario <mario.mario@y7mail.com>
Wed, 28 Jul 2021 10:44:08 +0000 (10:44 +0000)
Enable powerup versions of speed and invis, with temporary use of buff models, and XDF support

Closes #2612

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

qcsrc/client/items/items.qc
qcsrc/common/items/item/pickup.qh
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/common/mutators/mutator/powerups/sv_powerups.qh
qcsrc/common/notifications/all.inc
qcsrc/server/items/items.qc
ruleset-XDF.cfg
xonotic-server.cfg

index 064be587e78fc46087e2d4eabad563d84f25885c..f94d36cf6712f2b1022f1940de5ec41d6d60930f 100644 (file)
@@ -229,6 +229,8 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew)
         precache_model(this.mdl);
         _setmodel(this, this.mdl);
 
+       this.skin = ReadByte();
+
         setsize(this, '-16 -16 0', '16 16 48');
     }
 
index 7e31994075c244277201a4d02799825c89346f44..d21923e258dc196ef15e80209e3fbfb230b9de63 100644 (file)
@@ -22,6 +22,8 @@ PROPERTY(float, g_pickup_respawntimejitter_powerup)
 CLASS(Pickup, GameItem)
 #ifdef GAMEQC
     ATTRIB(Pickup, m_model, Model);
+    ATTRIB(Pickup, m_skin, int);
+    ATTRIB(Pickup, m_color, vector);
     ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP);
 #endif
     ATTRIB(Pickup, netname, string);
index 85ac8bd309f1dc4120589040922a9b3485502e2a..8120b25457aa643f876583d5b1bc32c5350a463a 100644 (file)
@@ -9,26 +9,36 @@
 #endif
 
 #ifdef GAMEQC
-MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
+//MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); // TODO: new model required
 SOUND(Invisibility, Item_Sound("powerup"));
 #endif
 
 #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)
 {
     if(!item.invisibility_finished)
-        item.invisibility_finished = autocvar_g_balance_powerup_invisibility_time;
+        item.invisibility_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invisibility_time;
 }
 #endif
 REGISTER_ITEM(Invisibility, Powerup) {
     this.m_canonical_spawnfunc = "item_invisibility";
+#ifdef SVQC
+    if(autocvar_g_powerups_invisibility)
+        this.spawnflags = ITEM_FLAG_NORMAL;
+    else
+        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+    this.m_iteminit         =   powerup_invisibility_init;
+#endif
 #ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
-    this.m_model            =   MDL_Invisibility_ITEM;
+    this.m_itemid           =   IT_INVISIBILITY;
+    this.m_model            =   MDL_BUFF; // TODO: MDL_Invisibility_ITEM when new model available
+    this.m_skin             =   12;
     this.m_sound            =   SND_Invisibility;
     this.m_glow             =   true;
     this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
@@ -39,12 +49,6 @@ REGISTER_ITEM(Invisibility, Powerup) {
     this.m_color            =   '0.5 0.5 1';
     this.m_waypoint         =   _("Invisibility");
     this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_INVISIBILITY;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_invisibility_init;
-#endif
 }
 
 SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
index 854c802769fb83f38c062759d024a738956a1b85..10900e86a89c7ec5f95dfb55693c24543b5cf6a6 100644 (file)
@@ -14,19 +14,28 @@ SOUND(Shield, Item_Sound("powerup_shield"));
 #endif
 
 #ifdef SVQC
+bool autocvar_g_powerups_shield = 1;
 float autocvar_g_balance_powerup_invincible_takedamage;
 float autocvar_g_balance_powerup_invincible_takeforce = 0.33;
 float autocvar_g_balance_powerup_invincible_time;
 void powerup_shield_init(Pickup this, entity item)
 {
     if(!item.invincible_finished)
-        item.invincible_finished = autocvar_g_balance_powerup_invincible_time;
+        item.invincible_finished = (item.count) ? item.count : autocvar_g_balance_powerup_invincible_time;
 }
 #endif
 REGISTER_ITEM(Shield, Powerup) {
     this.m_canonical_spawnfunc = "item_shield";
+#ifdef SVQC
+    if(autocvar_g_powerups_shield)
+        this.spawnflags = ITEM_FLAG_NORMAL;
+    else
+        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+    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;
     this.m_glow             =   true;
@@ -38,12 +47,6 @@ REGISTER_ITEM(Shield, Powerup) {
     this.m_color            =   '1 0 1';
     this.m_waypoint         =   _("Shield");
     this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_INVINCIBLE;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_shield_init;
-#endif
 }
 
 SPAWNFUNC_ITEM(item_shield, ITEM_Shield)
index 3a55a787a33bf46fad10fb169c1187a83e7fcf74..1f56bd0ad8ea68e2b66c83335208bb6db35ff1f6 100644 (file)
@@ -9,27 +9,37 @@
 #endif
 
 #ifdef GAMEQC
-MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
+//MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); // TODO: new model required
 SOUND(Speed, Item_Sound("powerup_shield"));
 #endif
 
 #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)
 {
     if(!item.speed_finished)
-        item.speed_finished = autocvar_g_balance_powerup_speed_time;
+        item.speed_finished = (item.count) ? item.count : autocvar_g_balance_powerup_speed_time;
 }
 #endif
 REGISTER_ITEM(Speed, Powerup) {
     this.m_canonical_spawnfunc = "item_speed";
+#ifdef SVQC
+    if(autocvar_g_powerups_speed)
+        this.spawnflags = ITEM_FLAG_NORMAL;
+    else
+        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+    this.m_iteminit         =   powerup_speed_init;
+#endif
 #ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
-    this.m_model            =   MDL_Speed_ITEM;
+    this.m_itemid           =   IT_SPEED;
+    this.m_model            =   MDL_BUFF; // TODO: MDL_Speed_ITEM when new model available
+    this.m_skin             =   9;
     this.m_sound            =   SND_Speed;
     this.m_glow             =   true;
     this.m_respawnsound     =   SND_SHIELD_RESPAWN;
@@ -40,12 +50,6 @@ REGISTER_ITEM(Speed, Powerup) {
     this.m_color            =   '0.1 1 0.84';
     this.m_waypoint         =   _("Speed");
     this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_SPEED;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_speed_init;
-#endif
 }
 
 SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
index ce1914966bd7f15ae0a2878301199a1abe22e314..9392a6a2d7c9755147633758befbe9252b5405f5 100644 (file)
@@ -14,6 +14,7 @@ SOUND(Strength, Item_Sound("powerup"));
 #endif
 
 #ifdef SVQC
+bool autocvar_g_powerups_strength = 1;
 float autocvar_g_balance_powerup_strength_damage;
 float autocvar_g_balance_powerup_strength_force;
 float autocvar_g_balance_powerup_strength_selfdamage;
@@ -22,13 +23,21 @@ float autocvar_g_balance_powerup_strength_time;
 void powerup_strength_init(Pickup this, entity item)
 {
     if(!item.strength_finished)
-        item.strength_finished = autocvar_g_balance_powerup_strength_time;
+        item.strength_finished = (item.count) ? item.count : autocvar_g_balance_powerup_strength_time;
 }
 #endif
 REGISTER_ITEM(Strength, Powerup) {
     this.m_canonical_spawnfunc = "item_strength";
+#ifdef SVQC
+    if(autocvar_g_powerups_strength)
+        this.spawnflags = ITEM_FLAG_NORMAL;
+    else
+        this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
+
+    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;
     this.m_glow             =   true;
@@ -40,12 +49,6 @@ REGISTER_ITEM(Strength, Powerup) {
     this.m_color            =   '0 0 1';
     this.m_waypoint         =   _("Strength");
     this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_STRENGTH;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_strength_init;
-#endif
 }
 
 SPAWNFUNC_ITEM(item_strength, ITEM_Strength)
index f71025959c67d2c7eaa2a36708b2d6969d6f85b0..b514385381c8a8bfb38d274dcf1c6f9dc36596be 100644 (file)
@@ -5,8 +5,12 @@
 #include "powerups.qh"
 
 int autocvar_g_powerups;
+bool autocvar_g_powerups_stack;
 
 REGISTER_MUTATOR(powerups, true);
 
 .float prevstrengthsound;
 .float prevstrengthsoundattempt;
+
+// q3compat
+.float count;
index 514440dbbd356cd584603607ef934f9f14e8ada9..f1b2af8e600164799b48c0922cd20db778d36fcb 100644 (file)
@@ -402,10 +402,10 @@ string multiteam_info_sprintf(string input, string teamname) { return ((input !=
     MULTITEAM_INFO(ONSLAUGHT_GENDESTROYED,                  N_CONSOLE,  0, 0, "", "",           "",     _("^TC^TT^BG generator has been destroyed"), "", GENERATOR)
     MULTITEAM_INFO(ONSLAUGHT_GENDESTROYED_OVERTIME,         N_CONSOLE,  0, 0, "", "",           "",     _("^TC^TT^BG generator spontaneously combusted due to overtime!"), "", GENERATOR)
 
-    MSG_INFO_NOTIF(POWERUP_INVISIBILITY,                    N_CONSOLE,  1, 0, "s1", "s1",       "strength",     _("^BG%s^K1 picked up Invisibility"), "")
-    MSG_INFO_NOTIF(POWERUP_SHIELD,                          N_CONSOLE,  1, 0, "s1", "s1",       "shield",       _("^BG%s^K1 picked up Shield"), "")
-    MSG_INFO_NOTIF(POWERUP_SPEED,                           N_CONSOLE,  1, 0, "s1", "s1",       "shield",       _("^BG%s^K1 picked up Speed"), "")
-    MSG_INFO_NOTIF(POWERUP_STRENGTH,                        N_CONSOLE,  1, 0, "s1", "s1",       "strength",     _("^BG%s^K1 picked up Strength"), "")
+    MSG_INFO_NOTIF(POWERUP_INVISIBILITY,                    N_CONSOLE,  1, 0, "s1", "s1",       "buff_invisible", _("^BG%s^K1 picked up Invisibility"), "")
+    MSG_INFO_NOTIF(POWERUP_SHIELD,                          N_CONSOLE,  1, 0, "s1", "s1",       "shield",         _("^BG%s^K1 picked up Shield"), "")
+    MSG_INFO_NOTIF(POWERUP_SPEED,                           N_CONSOLE,  1, 0, "s1", "s1",       "buff_speed",     _("^BG%s^K1 picked up Speed"), "")
+    MSG_INFO_NOTIF(POWERUP_STRENGTH,                        N_CONSOLE,  1, 0, "s1", "s1",       "strength",       _("^BG%s^K1 picked up Strength"), "")
 
     MSG_INFO_NOTIF(QUIT_DISCONNECT,                         N_CHATCON,  1, 0, "s1", "",         "",             _("^BG%s^F3 disconnected"), "")
     MSG_INFO_NOTIF(QUIT_KICK_IDLING,                        N_CHATCON,  1, 1, "s1 f1", "",      "",             _("^BG%s^F3 was kicked after idling for %s seconds"), "")
index 610c864b121e30b52e449e99a4eb3ab4a1c90505..db9b3fece9609190313bf71a0693cf062ff8212d 100644 (file)
@@ -63,9 +63,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);
@@ -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)
        {
@@ -1071,6 +1091,10 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default
        setmodel(this, MDL_Null); // precision set below
        //this.effects |= EF_LOWPRECISION;
 
+       // support skinned models for powerups
+       this.skin = def.m_skin;
+       this.glowmod = def.m_color;
+
        setsize (this, this.pos1 =  def.m_mins, this.pos2 = def.m_maxs);
 
        this.SendFlags |= ISF_SIZE;
index fccb89576e053b5945ffbfbe0030980e902cca62..170c38d79a03e5c0cd06d639d5628ab424d341c9 100644 (file)
@@ -12,7 +12,11 @@ g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no
 g_balance_kill_antispam 0
 g_forced_respawn 1
 // g_playerclip_collisions 0 // do not check playerclips
-g_powerups 0 // no powerups until the speed powerup has its own model
+// Powerups including speed enabled by default.
+g_powerups_strength 0
+g_powerups_shield 0
+g_powerups_invisibility 0
+g_buffs 0
 g_start_delay 3
 g_use_ammunition 0
 g_weapon_stay 1
@@ -20,30 +24,10 @@ teamplay_mode 2 // friendly fire and self damage
 sv_vote_nospectators 1
 timelimit_override 20
 
-// general buff settings
-g_buffs_cooldown_activate 0
-g_buffs_cooldown_respawn 0
-g_buffs_randomize 0
-
-// disabled buffs
-g_buffs_ammo 0
-g_buffs_resistance 0
-g_buffs_medic 0
-g_buffs_vengeance 0
-g_buffs_bash 0
-g_buffs_disability 0
-g_buffs_vampire 0
-g_buffs_jump 0
-g_buffs_inferno 0
-g_buffs_swapper 0
-g_buffs_magnet 0
-g_buffs_luck 0
-g_buffs_flight 0
-
 // speed powerup (q3 haste replacement)
 g_movement_highspeed_q3_compat 1
-g_balance_powerup_speed_time 30
-g_balance_powerup_speed_highspeed 1.3  // q3 haste lasts 30 seconds
+g_balance_powerup_speed_time 30 // q3 haste lasts 30 seconds unless count field set otherwise
+g_balance_powerup_speed_highspeed 1.3
 g_balance_powerup_speed_attackrate 0.7692307692   // 1/1.3
 
 // game mode settings
index 09cb0e85672d579c1731b64f888628359ce471ee..39323f0dc6fba66ed95c10acde291b1e4c40db1c 100644 (file)
@@ -197,7 +197,12 @@ set g_shootfromcenter 0 "weapon gets moved to the center, shots still come from
 set g_shootfromfixedorigin "" "if set to a string like 0 y z, the gun is moved to the given y and z coordinates. If set to a string like x y z, the whole shot origin is used"
 set g_weapon_stay 0 "1: ghost weapons can be picked up but give no ammo, thrown guns have ammo 2: ghost weapons can be picked up and refill ammo to one pickup size, thrown guns have no ammo (to prevent infinite ammo abuse)"
 set g_weapon_throwable 1 "if set to 1, weapons can be dropped"
-set g_powerups -1 "if set to 0 the strength and shield (invincibility) will not spawn on the map, if 1 they will spawn in all game modes, -1 is game mode default"
+set g_powerups -1 "if set to 0 no powerups will spawn, if 1 they will spawn in all game modes, -1 is game mode default"
+set g_powerups_stack 0 "enables stacking of powerup timers when picking up a powerup you already have; otherwise timer is reset to the time granted by the item, if greater than the time you currently have"
+set g_powerups_strength 1 "allow strength powerups to spawn"
+set g_powerups_shield 1 "allow shield powerups to spawn"
+set g_powerups_speed 1 "allow speed powerups to spawn"
+set g_powerups_invisibility 1 "allow invisibility powerups to spawn"
 set g_use_ammunition 1 "if set to 0 all weapons have unlimited ammo"
 set g_pickup_items -1 "if set to 0 all items (health, armor, ammo, weapons...) are removed from the map, if 1 they are forced to spawn"
 set g_pickup_respawntime_scaling_reciprocal 0 "Multiply respawn time by `reciprocal / (p + offset) + linear` where `p` is the current number of players, takes effect with 2 or more players present, `reciprocal` (with `offset` and `linear` set to 0) can be used to achieve a constant number of items spawned *per player*"