]> git.xonotic.org Git - xonotic/xonotic-data.pk3dir.git/commitdiff
Merge branch 'master' into Mario/status_effects_extended
authorMario <mario.mario@y7mail.com>
Wed, 23 Jun 2021 03:17:16 +0000 (13:17 +1000)
committerMario <mario.mario@y7mail.com>
Wed, 23 Jun 2021 03:17:16 +0000 (13:17 +1000)
92 files changed:
.gitlab-ci.yml
balance-mario.cfg
balance-nexuiz25.cfg
balance-overkill.cfg
balance-samual.cfg
balance-xdf.cfg
balance-xonotic.cfg
balance-xpm.cfg
mutators.cfg
qcsrc/client/view.qc
qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc
qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc
qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc
qcsrc/common/items/item.qh
qcsrc/common/items/item/_mod.inc
qcsrc/common/items/item/_mod.qh
qcsrc/common/items/item/jetpack.qh
qcsrc/common/items/item/powerup.qc [deleted file]
qcsrc/common/items/item/powerup.qh [deleted file]
qcsrc/common/monsters/monster/mage.qc
qcsrc/common/monsters/monster/shambler.qc
qcsrc/common/monsters/monster/spider.qc
qcsrc/common/monsters/monster/spider.qh
qcsrc/common/monsters/monster/zombie.qc
qcsrc/common/monsters/sv_monsters.qc
qcsrc/common/mutators/mutator/_mod.inc
qcsrc/common/mutators/mutator/_mod.qh
qcsrc/common/mutators/mutator/buffs/all.inc
qcsrc/common/mutators/mutator/buffs/buffs.qh
qcsrc/common/mutators/mutator/buffs/sv_buffs.qc
qcsrc/common/mutators/mutator/buffs/sv_buffs.qh
qcsrc/common/mutators/mutator/instagib/_mod.inc
qcsrc/common/mutators/mutator/instagib/_mod.qh
qcsrc/common/mutators/mutator/instagib/items.qh
qcsrc/common/mutators/mutator/instagib/sv_instagib.qc
qcsrc/common/mutators/mutator/instagib/sv_instagib.qh
qcsrc/common/mutators/mutator/instagib/sv_items.qc [deleted file]
qcsrc/common/mutators/mutator/instagib/sv_items.qh [deleted file]
qcsrc/common/mutators/mutator/nades/nades.qc
qcsrc/common/mutators/mutator/overkill/okhmg.qc
qcsrc/common/mutators/mutator/powerups/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/cl_powerups.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/cl_powerups.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/_mod.inc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/shield.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/shield.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/speed.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/speed.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/strength.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerup/strength.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerups.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/powerups.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/sv_powerups.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/powerups/sv_powerups.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc
qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc
qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh
qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qc [deleted file]
qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qh [deleted file]
qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qc [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh [new file with mode: 0644]
qcsrc/common/mutators/mutator/status_effects/sv_status_effects.qc
qcsrc/common/mutators/mutator/superspec/sv_superspec.qc
qcsrc/common/mutators/mutator/vampire/sv_vampire.qc
qcsrc/common/vehicles/vehicle/racer.qc
qcsrc/common/weapons/weapon/devastator.qc
qcsrc/common/weapons/weapon/minelayer.qc
qcsrc/common/weapons/weapon/porto.qc
qcsrc/lib/spawnfunc.qh
qcsrc/lib/warpzone/common.qc
qcsrc/server/bot/default/havocbot/havocbot.qc
qcsrc/server/bot/default/havocbot/roles.qc
qcsrc/server/client.qc
qcsrc/server/client.qh
qcsrc/server/compat/quake3.qc
qcsrc/server/compat/wop.qc
qcsrc/server/damage.qc
qcsrc/server/items/items.qc
qcsrc/server/items/items.qh
qcsrc/server/mutators/events.qh
qcsrc/server/player.qc
qcsrc/server/weapons/common.qc
qcsrc/server/weapons/common.qh
qcsrc/server/weapons/weaponsystem.qc
qcsrc/server/world.qc
ruleset-XDF.cfg

index 937914db0b8e48499007d7f50b989ad0c3ee18c4..f11908a114245611e83e38a359a80e59a09c01a4 100644 (file)
@@ -32,7 +32,7 @@ test_sv_game:
     - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints\r
     - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache\r
     - make\r
-    - EXPECT=f48f0950921140f6133c866bcc6e9d79\r
+    - EXPECT=4067e5eaa2042e3930fb375eaeb517fa\r
     - HASH=$(${ENGINE} -noconfig -nohome +timestamps 1 +exec serverbench.cfg\r
       | tee /dev/stderr\r
       | sed -e 's,^\[[^]]*\] ,,'\r
index 3ed931eb64f6b5cdbf12ccc171bbe9674a0b2acd..ad6a0e5c42c40a254c2c8015e3fb7384f6d3b53a 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
 set g_balance_powerup_invincible_takeforce 0.33
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index b89f7caa5b3190af25d79059dd0c3cb923781e06..80716a51b945d771c5f6e1c1f357951b91b8d910 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.2
 set g_balance_powerup_invincible_takeforce 1
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 4
 set g_balance_powerup_strength_time 30
index e3231dd0c3499b8634b479cb75d32d1edb169010..2ab3e5b4c3ff81ec348fd86ba2d6621c11b29b1a 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
 set g_balance_powerup_invincible_takeforce 0.33
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index 3481e191a179d9c1513f4826a8d7d50bb21069a2..24e166ae02d70595b4a0caf28024ac727e2d20c6 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.25 // only 1/4th damage is taken
 set g_balance_powerup_invincible_takeforce 1
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index bb4c0252b3ba67f41ba01b1e62f5001d8fb7ef77..ddea3d6eb6c6379eb5c1ea5343696df56274aaf5 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
 set g_balance_powerup_invincible_takeforce 0.33
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index 823b42a748dafc9c9424146982f99bc310d4c491..ee2da53f12ce6f447189ac8fe091e7415bb08b47 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
 set g_balance_powerup_invincible_takeforce 0.33
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index 8173e38d37cbe69aedade753e9c2ca24de8a8cb4..2eac05cc557e8f9e62a084d034930fc533beec50 100644 (file)
@@ -219,6 +219,11 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i
 set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken
 set g_balance_powerup_invincible_takeforce 0.33
 set g_balance_powerup_invincible_time 30
+set g_balance_powerup_invisibility_alpha 0.15
+set g_balance_powerup_invisibility_time 30
+set g_balance_powerup_speed_attackrate 0.8
+set g_balance_powerup_speed_highspeed 1.5
+set g_balance_powerup_speed_time 30
 set g_balance_powerup_strength_damage 3
 set g_balance_powerup_strength_force 3
 set g_balance_powerup_strength_time 30
index 5420c350f23503b60e12f99ffa0282cd0749c76f..ac965a2e1b313b0b5bedb8369cc53327e152a0bc 100644 (file)
@@ -43,9 +43,7 @@ set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cel
 set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs"
 set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs"
 set g_instagib_invisibility_time 30 "Time of invisibility powerup in seconds."
-set g_instagib_invis_alpha 0.15
 set g_instagib_speed_time 30 "Time of speed powerup in seconds."
-set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup"
 set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib"
 set g_instagib_blaster_keepdamage 0 "allow secondary fire to hurt players"
 set g_instagib_blaster_keepforce 0 "allow secondary fire to push players"
@@ -347,22 +345,12 @@ set g_buffs_disability_slowtime 3 "time in seconds for target disability"
 set g_buffs_disability_speed 0.7 "player speed multiplier while disabled"
 set g_buffs_disability_rate 1.5 "player weapon rate multiplier while disabled"
 set g_buffs_disability_weaponspeed 0.7 "weapon speed multiplier while disabled"
-set g_buffs_speed 1 "speed buff: increased movement/attack/health regeneration speed, carrier takes slightly more damage"
-set g_buffs_speed_time 60 "speed buff carry time"
-set g_buffs_speed_speed 1.7 "player speed multiplier while holding speed buff"
-set g_buffs_speed_rate 0.8 "weapon attack rate multiplier, smaller value means faster rate while holding the speed buff"
-set g_buffs_speed_weaponspeed 1.6 "projectile speed multiplier, higher value means faster projectiles while holding the speed buff"
-set g_buffs_speed_damage_take 1.2 "damage taken multiplier, higher value means more damage taken while holding speed buff"
-set g_buffs_speed_regen 1.2 "regeneration speed multiplier, higher value means faster health regeneration while holding speed buff"
 set g_buffs_vampire 1 "vampire buff: attacks to players and monsters heal the carrier"
 set g_buffs_vampire_time 60 "vampire buff carry time"
 set g_buffs_vampire_damage_steal 0.4 "damage stolen multiplier while holding vampire buff"
 set g_buffs_jump 0 "jump buff: greatly increased jump height"
 set g_buffs_jump_time 60 "jump buff carry time"
 set g_buffs_jump_height 600 "jump height while holding jump buff"
-set g_buffs_invisible 1 "invisible buff: carrier becomes invisible"
-set g_buffs_invisible_time 60 "invisible buff carry time"
-set g_buffs_invisible_alpha 0.15 "player invisibility multiplier while holding invisible buff"
 set g_buffs_inferno 1 "inferno buff: targets damaged by player carrying inferno buff will also receive burning damage"
 set g_buffs_inferno_time 60 "inferno buff carry time"
 set g_buffs_inferno_damagemultiplier 0.3 "multiplier of damage dealt during burn"
index 55d2086923c6ca96d22ce26fe81bb16010a2fd55..6a77db8cc834c718b7607c42a5e3a5f1d71b0b16 100644 (file)
@@ -21,6 +21,7 @@
 #include <common/mapobjects/trigger/viewloc.qh>
 #include <common/minigames/cl_minigames.qh>
 #include <common/minigames/cl_minigames_hud.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/mutators/mutator/waypoints/all.qh>
 #include <common/net_linked.qh>
@@ -1230,9 +1231,13 @@ void View_PostProcessing()
                }
 
                // edge detection postprocess handling done second (used by hud_powerup)
-               float sharpen_intensity = 0, strength_finished = StatusEffects_gettime(STATUSEFFECT_Strength, g_statuseffects), invincible_finished = StatusEffects_gettime(STATUSEFFECT_Shield, g_statuseffects);
-               if (strength_finished - time > 0) { sharpen_intensity += (strength_finished - time); }
-               if (invincible_finished - time > 0) { sharpen_intensity += (invincible_finished - time); }
+               float sharpen_intensity = 0;
+               FOREACH(StatusEffect, it.instanceOfPowerups,
+               {
+                       float powerup_finished = StatusEffects_gettime(it, g_statuseffects) - time;
+                       if(powerup_finished > 0)
+                               sharpen_intensity += powerup_finished;
+               });
 
                sharpen_intensity = bound(0, ((STAT(HEALTH) > 0) ? sharpen_intensity : 0), 5); // Check to see if player is alive (if not, set 0) - also bound to fade out starting at 5 seconds.
 
index 2bf10b33a79174ca52f5583ab37d1509a0e78db0..ba2402f2768229c8576c3399a1084bb99649b02e 100644 (file)
@@ -366,7 +366,7 @@ MUTATOR_HOOKFUNCTION(ca, FilterItem)
        entity item = M_ARGV(0, entity);
 
        if (autocvar_g_powerups <= 0)
-       if (item.flags & FL_POWERUP)
+       if (item.itemdef.instanceOfPowerup)
                return true;
 
        if (autocvar_g_pickup_items <= 0)
index 5323d1f03be000eaaef6583d0f3bdc37beba4838..670b00fa42c0a2b56877c395efa355b535b88259 100644 (file)
@@ -3,6 +3,7 @@
 #include <common/effects/all.qh>
 #include <common/mapobjects/teleporters.qh>
 #include <common/mapobjects/triggers.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/vehicles/all.qh>
 #include <server/command/vote.qh>
 #include <server/client.qh>
@@ -507,7 +508,7 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype)
                {
                        makevectors((player.v_angle.y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle.x, autocvar_g_ctf_throw_angle_max) * '1 0 0'));
 
-                       flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & ITEM_Strength.m_itemid) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
+                       flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((StatusEffects_active(STATUSEFFECT_Strength, player)) ? autocvar_g_ctf_throw_strengthmultiplier : 1)));
                        flag.velocity = W_CalculateProjectileVelocity(player, player.velocity, flag_velocity, false);
                        ctf_Handle_Drop(flag, player, droptype);
                        navigation_dynamicgoal_set(flag, player);
index 1d872bb7fe49afee17410713e72802873d6f64f2..7b47ecec6980f086bfabd7e7d1f7475c3071fdd9 100644 (file)
@@ -4,6 +4,7 @@
 #include <common/monsters/sv_spawn.qh>
 #include <common/monsters/sv_spawner.qh>
 #include <common/monsters/sv_monsters.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 
 #include <server/bot/api.qh>
 #include <server/world.qh>
@@ -212,7 +213,7 @@ void invasion_SpawnChosenMonster(Monster mon)
        if(!monster)
                return;
 
-       monster.spawnshieldtime = time;
+       StatusEffects_remove(STATUSEFFECT_SpawnShield, monster, STATUSEFFECT_REMOVE_NORMAL);
 
        if(spawn_point)
        {
index 8f651ad049bb19fd8d44591af923f9bbbc95357c..9e322c811d86354b1800e3e9dd79ab975382aa12 100644 (file)
@@ -24,11 +24,15 @@ const int IT_RESOURCE                               =  BIT(5); // bitflag to mark this item as a reso
 const int IT_KEY1                                              = BIT(6);
 const int IT_KEY2                                              = BIT(7);
 
+const int IT_BUFF                                              = BIT(8); // unused bit for buff items
+
 // special colorblend meaning in engine
-const int IT_INVISIBILITY                              = BIT(9);
-const int IT_INVINCIBLE                                = BIT(10);
+// legacy bitflags for powerups
+const int IT_INVISIBILITY                              = BIT(9);
+const int IT_INVINCIBLE                                        = BIT(10);
 const int IT_SUPERWEAPON                               = BIT(11); // suit
-const int IT_STRENGTH                                  = BIT(12);
+const int IT_STRENGTH                                  = BIT(12);
+const int IT_SPEED                                             = BIT(13);
 
 // item masks
 const int IT_PICKUPMASK                        = IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS | IT_JETPACK | IT_FUEL_REGEN; // strength and invincible are handled separately
index 2540116430a02457d429a9df6cd3e50ca081865b..4d12af0f0718ed37a4ef9e151c13e926749ca51c 100644 (file)
@@ -4,4 +4,3 @@
 #include <common/items/item/health.qc>
 #include <common/items/item/jetpack.qc>
 #include <common/items/item/pickup.qc>
-#include <common/items/item/powerup.qc>
index 4539e0f35d6567386cb0b97a51a0e266d17f075f..038530b2785d7580a7ecb1c45e0f9834124b0eca 100644 (file)
@@ -4,4 +4,3 @@
 #include <common/items/item/health.qh>
 #include <common/items/item/jetpack.qh>
 #include <common/items/item/pickup.qh>
-#include <common/items/item/powerup.qh>
index 962f16170b8d7268f7d7cd9ff79e6d77f2ed9b68..e9c5627c84dda6cb0799a42836ff9f5bcb79b682 100644 (file)
@@ -5,7 +5,7 @@
 #endif
 
 #include "ammo.qh"
-#include "powerup.qh"
+#include <common/mutators/mutator/powerups/_mod.qh>
 
 #ifndef SVQC
 .int m_itemid;
@@ -27,7 +27,7 @@ void powerup_jetpack_init(Pickup this, entity item)
 CLASS(Jetpack, Powerup)
 ENDCLASS(Jetpack)
 
-REGISTER_ITEM(Jetpack, Powerup) {
+REGISTER_ITEM(Jetpack, Jetpack) {
     this.m_canonical_spawnfunc = "item_jetpack";
 #ifdef GAMEQC
        this.spawnflags = ITEM_FLAG_NORMAL;
diff --git a/qcsrc/common/items/item/powerup.qc b/qcsrc/common/items/item/powerup.qc
deleted file mode 100644 (file)
index 7c7405b..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#include "powerup.qh"
diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh
deleted file mode 100644 (file)
index 76a17af..0000000
+++ /dev/null
@@ -1,101 +0,0 @@
-#pragma once
-
-#ifdef SVQC
-    // For FL_POWERUP
-    #include <common/constants.qh>
-#endif
-
-#include "pickup.qh"
-CLASS(Powerup, Pickup)
-#ifdef SVQC
-    ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
-    ATTRIB(Powerup, m_maxs, vector, '16 16 80');
-    ATTRIB(Powerup, m_botvalue, int, 11000);
-    ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
-    ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
-    ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup));
-#endif
-ENDCLASS(Powerup)
-
-#ifdef GAMEQC
-MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
-SOUND(Strength, Item_Sound("powerup"));
-#endif
-
-#ifdef SVQC
-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)
-{
-    if(!item.strength_finished)
-        item.strength_finished = autocvar_g_balance_powerup_strength_time;
-}
-#endif
-REGISTER_ITEM(Strength, Powerup) {
-    this.m_canonical_spawnfunc = "item_strength";
-#ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_NORMAL;
-    this.m_model            =   MDL_Strength_ITEM;
-    this.m_sound            =   SND_Strength;
-    this.m_glow             =   true;
-    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
-#endif
-    this.netname            =   "strength";
-    this.m_name             =   _("Strength");
-    this.m_icon             =   "strength";
-    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)
-
-#ifdef GAMEQC
-MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Shield, Item_Sound("powerup_shield"));
-#endif
-
-#ifdef SVQC
-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;
-}
-#endif
-REGISTER_ITEM(Shield, Powerup) {
-    this.m_canonical_spawnfunc = "item_shield";
-#ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_NORMAL;
-    this.m_model            =   MDL_Shield_ITEM;
-    this.m_sound            =   SND_Shield;
-    this.m_glow             =   true;
-    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
-#endif
-    this.netname            =   "invincible";
-    this.m_name             =   _("Shield");
-    this.m_icon             =   "shield";
-    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)
-SPAWNFUNC_ITEM(item_invincible, ITEM_Shield)
index 00aa9e6acc88dcd62891d9a3405d9c8e23b2e1d7..74a041b156c26784041019f6ee126c21658f920e 100644 (file)
@@ -83,7 +83,6 @@ void M_Mage_Defend_Shield(entity this);
 
 .entity mage_spike;
 .float mage_shield_delay;
-.float mage_shield_time;
 
 bool M_Mage_Defend_Heal_Check(entity this, entity targ)
 {
@@ -97,7 +96,7 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ)
                return false;
        if(!IS_PLAYER(targ))
                return (IS_MONSTER(targ) && GetResource(targ, RES_HEALTH) < targ.max_health);
-       if(targ.items & ITEM_Shield.m_itemid)
+       if(StatusEffects_active(STATUSEFFECT_Shield, targ))
                return false;
 
        switch(this.skin)
@@ -324,18 +323,11 @@ void M_Mage_Attack_Teleport(entity this, entity targ)
        this.attack_finished_single[0] = time + 0.2;
 }
 
-void M_Mage_Defend_Shield_Remove(entity this)
-{
-       this.effects &= ~(EF_ADDITIVE | EF_BLUE);
-       SetResourceExplicit(this, RES_ARMOR, autocvar_g_monsters_armor_blockpercent);
-}
-
 void M_Mage_Defend_Shield(entity this)
 {
-       this.effects |= (EF_ADDITIVE | EF_BLUE);
+       StatusEffects_apply(STATUSEFFECT_Shield, this, time + autocvar_g_monster_mage_shield_time, 0);
        this.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay);
        SetResourceExplicit(this, RES_ARMOR, autocvar_g_monster_mage_shield_blockpercent);
-       this.mage_shield_time = time + (autocvar_g_monster_mage_shield_time);
        setanim(this, this.anim_shoot, true, true, true);
        this.attack_finished_single[0] = time + 1;
        this.anim_finished = time + 1;
@@ -426,13 +418,8 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor))
     if(random() < 0.5)
         M_Mage_Defend_Heal(actor);
 
-    if(time >= actor.mage_shield_time && GetResource(actor, RES_ARMOR))
-        M_Mage_Defend_Shield_Remove(actor);
-
-    if(actor.enemy)
-    if(GetResource(actor, RES_HEALTH) < actor.max_health)
-    if(time >= actor.mage_shield_delay)
-    if(random() < 0.5)
+    if(actor.enemy && time >= actor.mage_shield_delay && random() < 0.5)
+    if(GetResource(actor, RES_HEALTH) < actor.max_health && !StatusEffects_active(STATUSEFFECT_Shield, actor))
         M_Mage_Defend_Shield(actor);
 
     return true;
index c67d90664267231a8e60df654a811425431d6795..789b7afe496a1a148f2894b80c1c5a1e7cbb22be 100644 (file)
@@ -256,7 +256,7 @@ METHOD(Shambler, mr_setup, bool(Shambler this, entity actor))
 
     setanim(actor, actor.anim_shoot, false, true, true);
     actor.spawn_time = actor.animstate_endtime;
-    actor.spawnshieldtime = actor.spawn_time;
+    StatusEffects_apply(STATUSEFFECT_SpawnShield, actor, actor.spawn_time, 0);
     actor.monster_attackfunc = M_Shambler_Attack;
 
     return true;
index 73518c1f9f2fe841fb1ac5b43454d57eb0a1c5e2..3f3e65d4a3dee63da2cb3d91bdf08871a7146552 100644 (file)
@@ -6,8 +6,6 @@
 
 #ifdef SVQC
 
-.float spider_slowness; // effect time of slowness inflicted by spiders
-
 .float spider_web_delay;
 
 float autocvar_g_monster_spider_attack_web_damagetime;
@@ -26,7 +24,7 @@ MUTATOR_HOOKFUNCTION(spiderweb, PlayerPhysics_UpdateStats)
 {
        entity player = M_ARGV(0, entity);
 
-       if(time < player.spider_slowness)
+       if(StatusEffects_active(STATUSEFFECT_Webbed, player))
                STAT(MOVEVARS_HIGHSPEED, player) *= 0.5;
 }
 
@@ -34,28 +32,13 @@ MUTATOR_HOOKFUNCTION(spiderweb, MonsterMove)
 {
     entity mon = M_ARGV(0, entity);
 
-       if(time < mon.spider_slowness)
+       if(StatusEffects_active(STATUSEFFECT_Webbed, mon))
        {
                M_ARGV(1, float) *= 0.5; // run speed
                M_ARGV(2, float) *= 0.5; // walk speed
        }
 }
 
-MUTATOR_HOOKFUNCTION(spiderweb, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.spider_slowness = 0;
-       return false;
-}
-
-MUTATOR_HOOKFUNCTION(spiderweb, MonsterSpawn)
-{
-    entity mon = M_ARGV(0, entity);
-
-       mon.spider_slowness = 0;
-}
-
 SOUND(SpiderAttack_FIRE, W_Sound("electro_fire"));
 METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, .entity weaponentity, int fire))
 {
@@ -110,7 +93,7 @@ void M_Spider_Attack_Web_Explode(entity this)
 
                FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && it.monsterdef != MON_SPIDER,
                {
-                       it.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime);
+                       StatusEffects_apply(STATUSEFFECT_Webbed, it, time + autocvar_g_monster_spider_attack_web_damagetime, 0);
                });
 
                delete(this);
index 679b140494aa48d7c679b5df85725666b9111936..943833f9909777894290d181bc2b1b39cdae72e5 100644 (file)
@@ -28,3 +28,19 @@ CLASS(SpiderAttack, PortoLaunch)
 /* wepname   */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack"));
 ENDCLASS(SpiderAttack)
 REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack));
+
+#include <common/mutators/mutator/status_effects/all.qh>
+
+CLASS(Webbed, StatusEffects)
+    ATTRIB(Webbed, netname, string, "webbed");
+#if 0
+    // NOTE: status effect name and icon disabled as they are not displayed
+    // re-enable if status effects are given a visual element
+    ATTRIB(Webbed, m_name, string, _("Webbed"));
+    ATTRIB(Webbed, m_icon, string, "buff_disability");
+#endif
+    ATTRIB(Webbed, m_color, vector, '0.94 0.3 1');
+    ATTRIB(Webbed, m_hidden, bool, true);
+    ATTRIB(Webbed, m_lifetime, float, 10);
+ENDCLASS(Webbed)
+REGISTER_STATUSEFFECT(Webbed, NEW(Webbed));
index 70981fa00293ec65e4e4fec6b9d6d83a04a6e302..f99474e9ec2ac293e75b83ac129fd0f6426e3783 100644 (file)
@@ -194,7 +194,7 @@ METHOD(Zombie, mr_setup, bool(Zombie this, entity actor))
 
     actor.monster_loot = ITEM_HealthMedium;
     actor.monster_attackfunc = M_Zombie_Attack;
-    actor.spawnshieldtime = actor.spawn_time;
+    StatusEffects_apply(STATUSEFFECT_SpawnShield, actor, actor.spawn_time, 0);
     actor.respawntime = 0.2;
     actor.damageforcescale = 0.0001; // no push while spawning
 
index b7e4d20a38c1f943087f88e1339bf1049556b2b4..e3eaf6ecd1b5ea88706b1619bf51505026b8f809 100644 (file)
@@ -7,6 +7,7 @@
 #include <common/mapobjects/triggers.qh>
 #include <common/monsters/all.qh>
 #include <common/mutators/mutator/nades/nades.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/physics/movelib.qh>
 #include <common/stats.qh>
 #include <common/teams.qh>
@@ -89,7 +90,6 @@ bool Monster_ValidTarget(entity this, entity targ)
        || (time < game_starttime) // monsters do nothing before match has started
        || (targ.takedamage == DAMAGE_NO)
        || (game_stopped)
-       || (targ.items & IT_INVISIBILITY)
        || (IS_SPEC(targ) || IS_OBSERVER(targ)) // don't attack spectators
        || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || GetResource(targ, RES_HEALTH) <= 0 || GetResource(this, RES_HEALTH) <= 0))
        || (this.monster_follow == targ || targ.monster_follow == this)
@@ -1005,7 +1005,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage
        //if(time < this.pain_finished && deathtype != DEATH_KILL.m_id)
                //return;
 
-       if(time < this.spawnshieldtime && deathtype != DEATH_KILL.m_id)
+       if(StatusEffects_active(STATUSEFFECT_SpawnShield, this) && deathtype != DEATH_KILL.m_id)
                return;
 
        if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL)
@@ -1101,7 +1101,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff)
        bool reverse = false;
        if(trace_fraction != 1.0)
                reverse = true;
-       if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid))
+       if(trace_ent && IS_PLAYER(trace_ent))
                reverse = false;
        if(trace_ent && IS_MONSTER(trace_ent))
                reverse = true;
@@ -1360,6 +1360,14 @@ bool Monster_Spawn(entity this, bool check_appear, Monster mon)
        else
                setmodel(this, mon.m_model);
 
+       if(this.statuseffects && this.statuseffects.owner == this)
+       {
+               StatusEffects_clearall(this.statuseffects);
+               StatusEffects_update(this);
+       }
+       else
+               this.statuseffects = NULL;
+
        this.flags                              = FL_MONSTER;
        this.classname                  = "monster";
        this.takedamage                 = DAMAGE_AIM;
@@ -1378,7 +1386,7 @@ bool Monster_Spawn(entity this, bool check_appear, Monster mon)
        this.use                                = Monster_Use;
        this.solid                              = SOLID_BBOX;
        set_movetype(this, MOVETYPE_WALK);
-       this.spawnshieldtime    = time + autocvar_g_monsters_spawnshieldtime;
+       StatusEffects_apply(STATUSEFFECT_SpawnShield, this, time + autocvar_g_monsters_spawnshieldtime, 0);
        this.enemy                              = NULL;
        this.velocity                   = '0 0 0';
        this.moveto                             = this.origin;
index 8db241e76bf67789506e354e345400612cf06b4b..c463c429f0c0eec1c0c1ffa89487a79158a9ae1a 100644 (file)
@@ -26,6 +26,7 @@
 #include <common/mutators/mutator/overkill/_mod.inc>
 #include <common/mutators/mutator/physical_items/_mod.inc>
 #include <common/mutators/mutator/pinata/_mod.inc>
+#include <common/mutators/mutator/powerups/_mod.inc>
 #include <common/mutators/mutator/random_gravity/_mod.inc>
 #include <common/mutators/mutator/random_items/_mod.inc>
 #include <common/mutators/mutator/rocketflying/_mod.inc>
index 7c8a809ba23d961d03b0286463d5fb8c81b79858..3b4eba7cb65a6a4fa93d97e515d6bb19a7f7b347 100644 (file)
@@ -26,6 +26,7 @@
 #include <common/mutators/mutator/overkill/_mod.qh>
 #include <common/mutators/mutator/physical_items/_mod.qh>
 #include <common/mutators/mutator/pinata/_mod.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/random_gravity/_mod.qh>
 #include <common/mutators/mutator/random_items/_mod.qh>
 #include <common/mutators/mutator/rocketflying/_mod.qh>
index 46a96f6612940e29ab53ee86a6f713e8d91722c0..72f54dcd28cb658a9a99c9ad842c5300e16c2218 100644 (file)
@@ -3,156 +3,161 @@ string Buff_UndeprecateName(string buffname)
     switch(buffname)
     {
         case "ammoregen": return "ammo";
-        case "haste": case "scout": return "speed";
         case "guard": return "resistance";
         case "revival": case "regen": return "medic";
-        case "invis": return "invisible";
         case "jumper": return "jump";
         default: return buffname;
     }
 }
 
-REGISTER_BUFF(AMMO) {
-    this.m_name = _("Ammo");
-    this.netname = "ammo";
-    this.m_icon = "buff_ammo";
-    this.m_skin = 3;
-    this.m_color = '0.76 1 0.1';
-}
+CLASS(AmmoBuff, Buff)
+    ATTRIB(AmmoBuff, m_name, string, _("Ammo"));
+    ATTRIB(AmmoBuff, netname, string, "ammo");
+    ATTRIB(AmmoBuff, m_icon, string, "buff_ammo");
+    ATTRIB(AmmoBuff, m_skin, int, 3);
+    ATTRIB(AmmoBuff, m_color, vector, '0.76 1 0.1');
+ENDCLASS(AmmoBuff)
+REGISTER_BUFF(AMMO, NEW(AmmoBuff));
 BUFF_SPAWNFUNCS(ammo, BUFF_AMMO)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_AMMO)
 
-REGISTER_BUFF(RESISTANCE) {
-    this.m_name = _("Resistance");
-    this.netname = "resistance";
-    this.m_icon = "buff_resistance";
-    this.m_skin = 0;
-    this.m_color = '0.36 1 0.07';
-}
+CLASS(ResistanceBuff, Buff)
+    ATTRIB(ResistanceBuff, m_name, string, _("Resistance"));
+    ATTRIB(ResistanceBuff, netname, string, "resistance");
+    ATTRIB(ResistanceBuff, m_icon, string, "buff_resistance");
+    ATTRIB(ResistanceBuff, m_skin, int, 0);
+    ATTRIB(ResistanceBuff, m_color, vector, '0.36 1 0.07');
+ENDCLASS(ResistanceBuff)
+REGISTER_BUFF(RESISTANCE, NEW(ResistanceBuff));
 BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(guard, BUFF_RESISTANCE)
 
-REGISTER_BUFF(SPEED) {
-    this.m_name = _("Speed");
-    this.netname = "speed";
-    this.m_icon = "buff_speed";
-    this.m_skin = 9;
-    this.m_color = '0.1 1 0.84';
-}
-BUFF_SPAWNFUNCS(speed, BUFF_SPEED)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(haste, BUFF_SPEED)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(scout, BUFF_SPEED)
-
-REGISTER_BUFF(MEDIC) {
-    this.m_name = _("Medic");
-    this.netname = "medic";
-    this.m_icon = "buff_medic";
-    this.m_skin = 1;
-    this.m_color = '1 0.12 0';
-}
+CLASS(MedicBuff, Buff)
+    ATTRIB(MedicBuff, m_name, string, _("Medic"));
+    ATTRIB(MedicBuff, netname, string, "medic");
+    ATTRIB(MedicBuff, m_icon, string, "buff_medic");
+    ATTRIB(MedicBuff, m_skin, int, 1);
+    ATTRIB(MedicBuff, m_color, vector, '1 0.12 0');
+ENDCLASS(MedicBuff)
+REGISTER_BUFF(MEDIC, NEW(MedicBuff));
 BUFF_SPAWNFUNCS(medic, BUFF_MEDIC)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(regen, BUFF_MEDIC)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(revival, BUFF_MEDIC)
 
-REGISTER_BUFF(BASH) {
-    this.m_name = _("Bash");
-    this.netname = "bash";
-    this.m_icon = "buff_bash";
-    this.m_skin = 5;
-    this.m_color = '1 0.39 0';
-}
+CLASS(BashBuff, Buff)
+    ATTRIB(BashBuff, m_name, string, _("Bash"));
+    ATTRIB(BashBuff, netname, string, "bash");
+    ATTRIB(BashBuff, m_icon, string, "buff_bash");
+    ATTRIB(BashBuff, m_skin, int, 5);
+    ATTRIB(BashBuff, m_color, vector, '1 0.39 0');
+ENDCLASS(BashBuff)
+REGISTER_BUFF(BASH, NEW(BashBuff));
 BUFF_SPAWNFUNCS(bash, BUFF_BASH)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(doubler, BUFF_BASH)
 
-REGISTER_BUFF(VAMPIRE) {
-    this.m_name = _("Vampire");
-    this.netname = "vampire";
-    this.m_icon = "buff_vampire";
-    this.m_skin = 2;
-    this.m_color = '1 0 0.24';
-}
+CLASS(VampireBuff, Buff)
+    ATTRIB(VampireBuff, m_name, string, _("Vampire"));
+    ATTRIB(VampireBuff, netname, string, "vampire");
+    ATTRIB(VampireBuff, m_icon, string, "buff_vampire");
+    ATTRIB(VampireBuff, m_skin, int, 2);
+    ATTRIB(VampireBuff, m_color, vector, '1 0 0.24');
+ENDCLASS(VampireBuff)
+REGISTER_BUFF(VAMPIRE, NEW(VampireBuff));
 BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE)
 
-REGISTER_BUFF(DISABILITY) {
-    this.m_name = _("Disability");
-    this.netname = "disability";
-    this.m_icon = "buff_disability";
-    this.m_skin = 7;
-    this.m_color = '0.94 0.3 1';
-}
+CLASS(DisabilityBuff, Buff)
+    ATTRIB(DisabilityBuff, m_name, string, _("Disability"));
+    ATTRIB(DisabilityBuff, netname, string, "disability");
+    ATTRIB(DisabilityBuff, m_icon, string, "buff_disability");
+    ATTRIB(DisabilityBuff, m_skin, int, 7);
+    ATTRIB(DisabilityBuff, m_color, vector, '0.94 0.3 1');
+ENDCLASS(DisabilityBuff)
+REGISTER_BUFF(DISABILITY, NEW(DisabilityBuff));
 BUFF_SPAWNFUNCS(disability, BUFF_DISABILITY)
+// status effect applied to targets by the disability buff
+CLASS(Disabled, StatusEffects)
+    ATTRIB(Disabled, netname, string, "disabled");
+#if 0
+    // NOTE: status effect name and icon disabled as they are not displayed
+    // re-enable if status effects are given a visual element
+    ATTRIB(Disabled, m_name, string, _("Disabled"));
+    ATTRIB(Disabled, m_icon, string, "buff_disability");
+#endif
+    ATTRIB(Disabled, m_color, vector, '0.94 0.3 1');
+    ATTRIB(Disabled, m_hidden, bool, true);
+    ATTRIB(Disabled, m_lifetime, float, 10);
+    ATTRIB(Disabled, disabled_effect_time, float, 0); // TODO: handle this effect client side like EF_FLAME!
+ENDCLASS(Disabled)
+REGISTER_STATUSEFFECT(Disabled, NEW(Disabled));
 
-REGISTER_BUFF(VENGEANCE) {
-    this.m_name = _("Vengeance");
-    this.netname = "vengeance";
-    this.m_icon = "buff_vengeance";
-    this.m_skin = 15;
-    this.m_color = '1 0.23 0.61';
-}
+CLASS(VengeanceBuff, Buff)
+    ATTRIB(VengeanceBuff, m_name, string, _("Vengeance"));
+    ATTRIB(VengeanceBuff, netname, string, "vengeance");
+    ATTRIB(VengeanceBuff, m_icon, string, "buff_vengeance");
+    ATTRIB(VengeanceBuff, m_skin, int, 15);
+    ATTRIB(VengeanceBuff, m_color, vector, '1 0.23 0.61');
+ENDCLASS(VengeanceBuff)
+REGISTER_BUFF(VENGEANCE, NEW(VengeanceBuff));
 BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE)
 
-REGISTER_BUFF(JUMP) {
-    this.m_name = _("Jump");
-    this.netname = "jump";
-    this.m_icon = "buff_jump";
-    this.m_skin = 10;
-    this.m_color = '0.24 0.78 1';
-}
+CLASS(JumpBuff, Buff)
+    ATTRIB(JumpBuff, m_name, string, _("Jump"));
+    ATTRIB(JumpBuff, netname, string, "jump");
+    ATTRIB(JumpBuff, m_icon, string, "buff_jump");
+    ATTRIB(JumpBuff, m_skin, int, 10);
+    ATTRIB(JumpBuff, m_color, vector, '0.24 0.78 1');
+ENDCLASS(JumpBuff)
+REGISTER_BUFF(JUMP, NEW(JumpBuff));
 BUFF_SPAWNFUNCS(jump, BUFF_JUMP)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(jumper, BUFF_JUMP)
 
-REGISTER_BUFF(INVISIBLE) {
-    this.m_name = _("Invisible");
-    this.netname = "invisible";
-    this.m_icon = "buff_invisible";
-    this.m_skin = 12;
-    this.m_color = '0.5 0.5 1';
-}
-BUFF_SPAWNFUNCS(invisible, BUFF_INVISIBLE)
-BUFF_SPAWNFUNC_Q3TA_COMPAT(invis, BUFF_INVISIBLE)
-
-REGISTER_BUFF(INFERNO) {
-    this.m_name = _("Inferno");
-    this.netname = "inferno";
-    this.m_icon = "buff_inferno";
-    this.m_skin = 16;
-    this.m_color = '1 0.62 0';
-}
+CLASS(InfernoBuff, Buff)
+    ATTRIB(InfernoBuff, m_name, string, _("Inferno"));
+    ATTRIB(InfernoBuff, netname, string, "inferno");
+    ATTRIB(InfernoBuff, m_icon, string, "buff_inferno");
+    ATTRIB(InfernoBuff, m_skin, int, 16);
+    ATTRIB(InfernoBuff, m_color, vector, '1 0.62 0');
+ENDCLASS(InfernoBuff)
+REGISTER_BUFF(INFERNO, NEW(InfernoBuff));
 BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO)
 
-REGISTER_BUFF(SWAPPER) {
-    this.m_name = _("Swapper");
-    this.netname = "swapper";
-    this.m_icon = "buff_swapper";
-    this.m_skin = 17;
-    this.m_color = '0.63 0.36 1';
-}
+CLASS(SwapperBuff, Buff)
+    ATTRIB(SwapperBuff, m_name, string, _("Swapper"));
+    ATTRIB(SwapperBuff, netname, string, "swapper");
+    ATTRIB(SwapperBuff, m_icon, string, "buff_swapper");
+    ATTRIB(SwapperBuff, m_skin, int, 17);
+    ATTRIB(SwapperBuff, m_color, vector, '0.63 0.36 1');
+ENDCLASS(SwapperBuff)
+REGISTER_BUFF(SWAPPER, NEW(SwapperBuff));
 BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER)
 
-REGISTER_BUFF(MAGNET) {
-    this.m_name = _("Magnet");
-    this.netname = "magnet";
-    this.m_icon = "buff_magnet";
-    this.m_skin = 18;
-    this.m_color = '1 0.95 0.18';
-}
+CLASS(MagnetBuff, Buff)
+    ATTRIB(MagnetBuff, m_name, string, _("Magnet"));
+    ATTRIB(MagnetBuff, netname, string, "magnet");
+    ATTRIB(MagnetBuff, m_icon, string, "buff_magnet");
+    ATTRIB(MagnetBuff, m_skin, int, 18);
+    ATTRIB(MagnetBuff, m_color, vector, '1 0.95 0.18');
+ENDCLASS(MagnetBuff)
+REGISTER_BUFF(MAGNET, NEW(MagnetBuff));
 BUFF_SPAWNFUNCS(magnet, BUFF_MAGNET)
 
-REGISTER_BUFF(LUCK) {
-    this.m_name = _("Luck");
-    this.netname = "luck";
-    this.m_icon = "buff_luck";
-    this.m_skin = 19;
-    this.m_color = '1 0.23 0.44';
-}
+CLASS(LuckBuff, Buff)
+    ATTRIB(LuckBuff, m_name, string, _("Luck"));
+    ATTRIB(LuckBuff, netname, string, "luck");
+    ATTRIB(LuckBuff, m_icon, string, "buff_luck");
+    ATTRIB(LuckBuff, m_skin, int, 19);
+    ATTRIB(LuckBuff, m_color, vector, '1 0.23 0.44');
+ENDCLASS(LuckBuff)
+REGISTER_BUFF(LUCK, NEW(LuckBuff));
 BUFF_SPAWNFUNCS(luck, BUFF_LUCK)
 
-REGISTER_BUFF(FLIGHT) {
-    this.m_name = _("Flight");
-    this.netname = "flight";
-    this.m_icon = "buff_flight";
-    this.m_skin = 11;
-    this.m_color = '0.23 0.44 1';
-}
+CLASS(FlightBuff, Buff)
+    ATTRIB(FlightBuff, m_name, string, _("Flight"));
+    ATTRIB(FlightBuff, netname, string, "flight");
+    ATTRIB(FlightBuff, m_icon, string, "buff_flight");
+    ATTRIB(FlightBuff, m_skin, int, 11);
+    ATTRIB(FlightBuff, m_color, vector, '0.23 0.44 1');
+ENDCLASS(FlightBuff)
+REGISTER_BUFF(FLIGHT, NEW(FlightBuff));
 BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT)
 BUFF_SPAWNFUNC_Q3TA_COMPAT(flight, BUFF_FLIGHT)
index 5b93fa79595a78fab9e741b81ce1408873d65031..e3e4e5edd3113132574116fca1736efc21e737ef 100644 (file)
@@ -4,6 +4,7 @@
 #include <common/util.qh>
 
 #ifdef GAMEQC
+#include <common/items/item.qh>
 #include <common/mutators/mutator/waypoints/all.qh>
 #endif
 
@@ -12,13 +13,14 @@ REGISTER_WAYPOINT(Buff, _("Buff"), "", '1 0.5 0', 1);
 REGISTER_RADARICON(Buff, 1);
 #endif
 
-#define REGISTER_BUFF(id) \
-    REGISTER(StatusEffect, BUFF_##id, m_id, NEW(Buff))
+#define REGISTER_BUFF(id, inst) \
+    REGISTER(StatusEffect, BUFF_##id, m_id, inst)
 
 #include <common/mutators/mutator/status_effects/_mod.qh>
 CLASS(Buff, StatusEffects)
-       /** bit index */
-       ATTRIB(Buff, m_itemid, int, 0);
+#ifdef GAMEQC
+       ATTRIB(Buff, m_itemid, int, IT_BUFF);
+#endif
        ATTRIB(Buff, netname, string, "buff");
        ATTRIB(Buff, m_icon, string, "buff");
        ATTRIB(Buff, m_color, vector, '1 1 1');
@@ -37,7 +39,6 @@ ENDCLASS(Buff)
 
 STATIC_INIT(REGISTER_BUFFS) {
        FOREACH(StatusEffect, it.instanceOfBuff, {
-               it.m_itemid = BIT(it.m_id - 1);
                it.m_sprite = strzone(strcat("buff-", it.netname));
        });
 }
index a176e512f31e744b9a8f5390e30d03c518294248..11176f48f3846271812ef8174358dc7940cbed1c 100644 (file)
@@ -1,7 +1,7 @@
 #include "sv_buffs.qh"
 
 #include <common/mapobjects/target/music.qh>
-#include <common/mutators/mutator/instagib/_mod.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/gamemodes/_mod.qh>
 #include <server/items/items.qh>
 
@@ -9,6 +9,10 @@ bool buffs_BuffModel_Customize(entity this, entity client)
 {
        entity player = WaypointSprite_getviewentity(client);
        entity myowner = this.owner;
+       entity heldbuff = buff_FirstFromFlags(myowner);
+
+       if(!heldbuff)
+               return false;
 
        if(myowner.alpha <= 0.5 && DIFF_TEAM(player, myowner) && myowner.alpha != 0)
                return false;
@@ -25,11 +29,49 @@ bool buffs_BuffModel_Customize(entity this, entity client)
        else
        {
                this.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
-               this.alpha = 1;
+               this.alpha = myowner.alpha;
        }
        return true;
 }
 
+void buffs_BuffModel_Think(entity this)
+{
+       this.nextthink = time;
+       entity player = this.owner;
+       if(player.alpha < 0 || player.buff_model != this)
+       {
+               if(player) // remnant from ChatBubbleThink, same question... WHY?!
+                       player.buff_model = NULL;
+               delete(this);
+               return;
+       }
+
+       entity heldbuff = buff_FirstFromFlags(player);
+
+       if(!heldbuff)
+       {
+               this.effects = EF_NODRAW;
+               return;
+       }
+
+       this.color = heldbuff.m_color;
+       this.glowmod = heldbuff.m_color;
+       this.skin = heldbuff.m_skin;
+
+       this.effects = player.effects;
+       this.effects |= EF_LOWPRECISION;
+       this.effects = this.effects & EFMASK_CHEAP; // eat performance
+
+       this.alpha = player.alpha;
+}
+
+void buffs_BuffModel_Remove(entity player)
+{
+       if(player.buff_model)
+               delete(player.buff_model);
+       player.buff_model = NULL;
+}
+
 void buffs_BuffModel_Spawn(entity player)
 {
        player.buff_model = new(buff_model);
@@ -38,23 +80,22 @@ void buffs_BuffModel_Spawn(entity player)
        setattachment(player.buff_model, player, "");
        setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1));
        player.buff_model.owner = player;
+       player.buff_model.exteriormodeltoclient = player;
        player.buff_model.scale = 0.7;
        player.buff_model.pflags = PFLAGS_FULLDYNAMIC;
        player.buff_model.light_lev = 200;
+       setthink(player.buff_model, buffs_BuffModel_Think);
+       player.buff_model.nextthink = time;
        setcefc(player.buff_model, buffs_BuffModel_Customize);
 }
 
-void buffs_BuffModel_Remove(entity player)
+void buffs_BuffModel_Update(entity this)
 {
-       if(player.buff_model)
-               delete(player.buff_model);
-       player.buff_model = NULL;
-}
-
-vector buff_GlowColor(entity buff)
-{
-       //if(buff.team) { return Team_ColorRGB(buff.team); }
-       return buff.m_color;
+       if (this.alpha < 0)
+               return;
+       // spawn a buff model entity if needed
+       if (!this.buff_model)
+               buffs_BuffModel_Spawn(this);
 }
 
 void buff_Effect(entity player, string eff)
@@ -167,7 +208,7 @@ void buff_Touch(entity this, entity toucher)
        || (STAT(FROZEN, toucher))
        || (toucher.vehicle)
        || (!this.buffdef) // TODO: error out or maybe reset type if this occurs?
-       || (time < PS(toucher).buff_shield)
+       || (time < toucher.buff_shield)
        )
        {
                // can't touch this
@@ -269,7 +310,7 @@ void buff_Think(entity this)
        {
                entity buff = this.buffdef;
                this.color = buff.m_color;
-               this.glowmod = buff_GlowColor(buff);
+               this.glowmod = buff.m_color;
                this.skin = buff.m_skin;
 
                setmodel(this, MDL_BUFF);
@@ -413,7 +454,7 @@ void buff_Init(entity this)
        setcefc(this, buff_Customize);
        //this.gravity = 100;
        this.color = buff.m_color;
-       this.glowmod = buff_GlowColor(this);
+       this.glowmod = buff.m_color;
        buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + max(0, game_starttime - time));
        this.buff_active = !this.buff_activetime;
        this.pflags = PFLAGS_FULLDYNAMIC;
@@ -467,6 +508,149 @@ float buff_Inferno_CalculateTime(float damg, float offset_x, float offset_y, flo
        return offset_y + (intersect_y - offset_y) * logn(((damg - offset_x) * ((base - 1) / intersect_x)) + 1, base);
 }
 
+METHOD(Buff, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    if(IS_PLAYER(actor))
+       actor.effects |= EF_NOSHADOW; // does not play well with buff icon
+    SUPER(Buff).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Buff, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+       bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+       if(wasactive)
+       {
+               int buffid = this.m_id;
+               if(removal_type == STATUSEFFECT_REMOVE_TIMEOUT)
+               {
+                       Send_Notification(NOTIF_ONE, actor, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
+                       sound(actor, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
+               }
+               else if(removal_type == STATUSEFFECT_REMOVE_NORMAL && !IS_INDEPENDENT_PLAYER(actor))
+                       Send_Notification(NOTIF_ALL_EXCEPT, actor, MSG_INFO, INFO_ITEM_BUFF_LOST, actor.netname, buffid);
+               actor.buff_shield = time + max(0, autocvar_g_buffs_pickup_delay); // always put in a delay, even if small
+       }
+       if(IS_PLAYER(actor))
+               actor.effects &= ~EF_NOSHADOW;
+       SUPER(Buff).m_remove(this, actor, removal_type);
+}
+
+METHOD(Disabled, m_tick, void(StatusEffects this, entity actor))
+{
+       if(time >= actor.disabled_effect_time)
+       {
+               Send_Effect(EFFECT_SMOKING, actor.origin + ((actor.mins + actor.maxs) * 0.5), '0 0 0', 1);
+               actor.disabled_effect_time = time + 0.5;
+       }
+       SUPER(Disabled).m_tick(this, actor);
+}
+METHOD(Disabled, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+       actor.disabled_effect_time = 0;
+       SUPER(Disabled).m_remove(this, actor, removal_type);
+}
+
+METHOD(AmmoBuff, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive)
+    {
+        actor.buff_ammo_prev_infitems = (actor.items & IT_UNLIMITED_AMMO);
+        actor.items |= IT_UNLIMITED_AMMO;
+        for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(!actor.(weaponentity))
+                               continue;
+                       if(actor.(weaponentity).clip_load)
+                               actor.(weaponentity).buff_ammo_prev_clipload = actor.(weaponentity).clip_load;
+                       if(actor.(weaponentity).clip_size)
+                               actor.(weaponentity).clip_load = actor.(weaponentity).(weapon_load[actor.(weaponentity).m_switchweapon.m_id]) = actor.(weaponentity).clip_size;
+               }
+    }
+    SUPER(AmmoBuff).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(AmmoBuff, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+       bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+       if(wasactive)
+       {
+               actor.items = BITSET(actor.items, IT_UNLIMITED_AMMO, actor.buff_ammo_prev_infitems);
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(!actor.(weaponentity))
+                               continue;
+                       if(actor.(weaponentity).buff_ammo_prev_clipload)
+                       {
+                               actor.(weaponentity).clip_load = actor.(weaponentity).buff_ammo_prev_clipload;
+                               actor.(weaponentity).buff_ammo_prev_clipload = 0;
+                       }
+               }
+       }
+       actor.buff_ammo_prev_infitems = 0;
+       SUPER(AmmoBuff).m_remove(this, actor, removal_type);
+}
+METHOD(AmmoBuff, m_tick, void(StatusEffects this, entity actor))
+{
+       if(IS_PLAYER(actor))
+       {
+               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
+               {
+                       .entity weaponentity = weaponentities[slot];
+                       if(actor.(weaponentity).clip_size)
+                               actor.(weaponentity).clip_load = actor.(weaponentity).(weapon_load[actor.(weaponentity).m_switchweapon.m_id]) = actor.(weaponentity).clip_size;
+               }
+       }
+
+       SUPER(AmmoBuff).m_tick(this, actor);
+}
+
+
+METHOD(FlightBuff, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive)
+    {
+        actor.buff_flight_oldgravity = actor.gravity;
+               if(!actor.gravity)
+                       actor.gravity = 1;
+    }
+    SUPER(FlightBuff).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(FlightBuff, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+       bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+       if(wasactive)
+       {
+               actor.gravity = ((actor.trigger_gravity_check) ? actor.trigger_gravity_check.enemy.gravity : actor.buff_flight_oldgravity);
+       }
+       actor.buff_flight_oldgravity = 0;
+       SUPER(FlightBuff).m_remove(this, actor, removal_type);
+}
+
+METHOD(MagnetBuff, m_tick, void(StatusEffects this, entity actor))
+{
+       if(IS_PLAYER(actor))
+       {
+               vector pickup_size;
+               IL_EACH(g_items, it.itemdef,
+               {
+                       if(it.buffdef)
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
+                       else
+                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
+
+                       if(boxesoverlap(actor.absmin - pickup_size, actor.absmax + pickup_size, it.absmin, it.absmax))
+                       {
+                               if(gettouch(it))
+                                       gettouch(it)(it, actor);
+                       }
+               });
+       }
+
+       SUPER(MagnetBuff).m_tick(this, actor);
+}
+
 // mutator hooks
 MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
 {
@@ -484,10 +668,6 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
                frag_damage = bound(0, frag_damage - reduced, frag_damage);
        }
 
-       if(StatusEffects_active(BUFF_SPEED, frag_target))
-       if(frag_target != frag_attacker)
-               frag_damage *= autocvar_g_buffs_speed_damage_take;
-
        if(StatusEffects_active(BUFF_MEDIC, frag_target))
        if((GetResource(frag_target, RES_HEALTH) - frag_damage) <= 0)
        if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
@@ -528,7 +708,7 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate)
 
        if(StatusEffects_active(BUFF_DISABILITY, frag_attacker))
        if(frag_target != frag_attacker)
-               frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime;
+               StatusEffects_apply(STATUSEFFECT_Disabled, frag_target, time + autocvar_g_buffs_disability_slowtime, 0);
 
        if(StatusEffects_active(BUFF_INFERNO, frag_target))
        {
@@ -570,33 +750,19 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor)
                return;
        float health_take = bound(0, M_ARGV(4, float), GetResource(frag_target, RES_HEALTH));
 
-       if (time >= frag_target.spawnshieldtime && frag_target != frag_attacker
+       if (!StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && frag_target != frag_attacker
                && IS_PLAYER(frag_attacker) && !IS_DEAD(frag_target) && !STAT(FROZEN, frag_target))
        {
                GiveResource(frag_attacker, RES_HEALTH, autocvar_g_buffs_vampire_damage_steal * health_take);
        }
 }
 
-MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn)
-{
-       entity player = M_ARGV(0, entity);
-
-       buffs_BuffModel_Remove(player);
-       player.oldbuffs = NULL;
-       // reset timers here to prevent them continuing after re-spawn
-       player.buff_disability_time = 0;
-       player.buff_disability_effect_time = 0;
-}
-
 MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics_UpdateStats)
 {
        entity player = M_ARGV(0, entity);
        // these automatically reset, no need to worry
 
-       if(StatusEffects_active(BUFF_SPEED, player))
-               STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_speed_speed;
-
-       if(time < player.buff_disability_time)
+       if(StatusEffects_active(STATUSEFFECT_Disabled, player))
                STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_buffs_disability_speed;
 }
 
@@ -613,28 +779,13 @@ MUTATOR_HOOKFUNCTION(buffs, MonsterMove)
 {
        entity mon = M_ARGV(0, entity);
 
-       if(time < mon.buff_disability_time)
+       if(StatusEffects_active(STATUSEFFECT_Disabled, mon))
        {
                M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed
                M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed
        }
 }
 
-MUTATOR_HOOKFUNCTION(buffs, PlayerDies)
-{
-       entity frag_target = M_ARGV(2, entity);
-
-       entity heldbuff = buff_FirstFromFlags(frag_target);
-       if(heldbuff)
-       {
-               int buffid = heldbuff.m_id;
-               if(!IS_INDEPENDENT_PLAYER(frag_target))
-                       Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid);
-
-               buffs_BuffModel_Remove(frag_target);
-       }
-}
-
 MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
 {
        if(MUTATOR_RETURNVALUE || game_stopped || !autocvar_g_buffs_drop) return;
@@ -650,7 +801,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST)
                        Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
 
                buff_RemoveAll(player, STATUSEFFECT_REMOVE_NORMAL);
-               PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay);
+               player.buff_shield = time + max(0, autocvar_g_buffs_pickup_delay);
                sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
                return true;
        }
@@ -739,29 +890,11 @@ bool buffs_RemovePlayer(entity player)
 {
        buffs_BuffModel_Remove(player);
 
-       // also reset timers here to prevent them continuing after spectating
-       player.buff_disability_time = 0;
-       player.buff_disability_effect_time = 0;
-
        return false;
 }
 MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
 MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); }
 
-MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint)
-{
-       entity wp = M_ARGV(0, entity);
-       entity player = M_ARGV(1, entity);
-
-       entity e = WaypointSprite_getviewentity(player);
-
-       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
-       // but only apply this to real players, not to spectators
-       if((wp.owner.flags & FL_CLIENT) && (e == player) && StatusEffects_active(BUFF_INVISIBLE, wp.owner))
-       if(DIFF_TEAM(wp.owner, e))
-               return true;
-}
-
 MUTATOR_HOOKFUNCTION(buffs, FilterItem)
 {
        if(autocvar_g_buffs < 0)
@@ -769,18 +902,11 @@ MUTATOR_HOOKFUNCTION(buffs, FilterItem)
 
        entity item = M_ARGV(0, entity);
 
-       if(autocvar_g_buffs_replace_powerups)
+       if(autocvar_g_buffs_replace_powerups && item.itemdef.instanceOfPowerup)
        {
-               switch(item.classname)
-               {
-                       case "item_strength":
-                       case "item_shield":
-                       {
-                               entity e = spawn();
-                               buff_SpawnReplacement(e, item);
-                               return true;
-                       }
-               }
+               entity e = spawn();
+               buff_SpawnReplacement(e, item);
+               return true;
        }
 
        return false;
@@ -790,10 +916,7 @@ MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor)
 {
        entity player = M_ARGV(1, entity);
 
-       if(StatusEffects_active(BUFF_SPEED, player))
-               M_ARGV(0, float) *= autocvar_g_buffs_speed_rate;
-
-       if(time < player.buff_disability_time)
+       if(StatusEffects_active(STATUSEFFECT_Disabled, player))
                M_ARGV(0, float) *= autocvar_g_buffs_disability_rate;
 }
 
@@ -801,14 +924,15 @@ MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor)
 {
        entity player = M_ARGV(1, entity);
 
-       if(StatusEffects_active(BUFF_SPEED, player))
-               M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed;
-
-       if(time < player.buff_disability_time)
+       if(StatusEffects_active(STATUSEFFECT_Disabled, player))
                M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed;
 }
 
-.bool buff_flight_crouchheld;
+MUTATOR_HOOKFUNCTION(buffs, Freeze)
+{
+       entity targ = M_ARGV(0, entity);
+       buff_RemoveAll(targ, STATUSEFFECT_REMOVE_NORMAL);
+}
 
 MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 {
@@ -816,6 +940,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
 
        if(game_stopped || IS_DEAD(player) || !IS_PLAYER(player)) return;
 
+       // NOTE: this is kept here to ensure crouches are picked up each player movement frame
        if(StatusEffects_active(BUFF_FLIGHT, player))
        {
                if(!PHYS_INPUT_BUTTON_CROUCH(player))
@@ -827,181 +952,8 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink)
                }
        }
 
-       if(time < player.buff_disability_time)
-       if(time >= player.buff_disability_effect_time)
-       {
-               Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1);
-               player.buff_disability_effect_time = time + 0.5;
-       }
-
-       // handle buff lost status
-       // 1: notify everyone else
-       // 2: notify carrier as well
-       int buff_lost = 0;
-
-       entity heldbuff = buff_FirstFromFlags(player);
-       float bufftime = StatusEffects_gettime(heldbuff, player);
-       if(heldbuff && bufftime && time >= bufftime)
-               buff_lost = 2;
-
-       if(STAT(FROZEN, player)) { buff_lost = 1; }
-
-       if(buff_lost && heldbuff)
-       {
-               int buffid = heldbuff.m_id;
-               if(buff_lost == 2)
-               {
-                       Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message?
-                       sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM);
-               }
-               else if(!IS_INDEPENDENT_PLAYER(player))
-                       Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid);
-               buff_RemoveAll(player, STATUSEFFECT_REMOVE_TIMEOUT); // TODO: remove only the currently active buff?
-               heldbuff = NULL;
-               PS(player).buff_shield = time + max(0, autocvar_g_buffs_pickup_delay); // always put in a delay, even if small
-       }
-
-       if(StatusEffects_active(BUFF_MAGNET, player))
-       {
-               vector pickup_size;
-               IL_EACH(g_items, it.itemdef,
-               {
-                       if(it.buffdef)
-                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff;
-                       else
-                               pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item;
-
-                       if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax))
-                       {
-                               if(gettouch(it))
-                                       gettouch(it)(it, player);
-                       }
-               });
-       }
-
-       if(StatusEffects_active(BUFF_AMMO, player))
-       {
-               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-               {
-                       .entity weaponentity = weaponentities[slot];
-                       if(player.(weaponentity).clip_size)
-                               player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
-               }
-       }
-
-       if(!player.vehicle && StatusEffects_active(BUFF_INVISIBLE, player) && player.oldbuffs == BUFF_INVISIBLE)
-               player.alpha = ((autocvar_g_buffs_invisible_alpha) ? autocvar_g_buffs_invisible_alpha : -1); // powerups reset alpha, so we must enforce this (TODO)
-
-#define BUFF_ONADD(b) if ( (heldbuff == (b)) && (player.oldbuffs != (b)))
-#define BUFF_ONREM(b) if ( (heldbuff != (b)) && (player.oldbuffs == (b)))
-
-       if(heldbuff != player.oldbuffs)
-       {
-               bufftime = heldbuff ? heldbuff.m_time(heldbuff) : 0;
-               if(StatusEffects_gettime(heldbuff, player) <= time) // if the player still has a buff countdown, don't reset it!
-               {
-                       player.statuseffects.statuseffect_time[heldbuff.m_id] = (bufftime) ? time + bufftime : 0;
-                       StatusEffects_update(player);
-               }
-
-               BUFF_ONADD(BUFF_AMMO)
-               {
-                       player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_AMMO);
-                       player.items |= IT_UNLIMITED_AMMO;
-
-                       if(StatusEffects_active(BUFF_AMMO, player))
-                       {
-                               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                               {
-                                       .entity weaponentity = weaponentities[slot];
-                                       if(player.(weaponentity).clip_load)
-                                               player.(weaponentity).buff_ammo_prev_clipload = player.(weaponentity).clip_load;
-                                       if(player.(weaponentity).clip_size)
-                                               player.(weaponentity).clip_load = player.(weaponentity).(weapon_load[player.(weaponentity).m_switchweapon.m_id]) = player.(weaponentity).clip_size;
-                               }
-                       }
-               }
-
-               BUFF_ONREM(BUFF_AMMO)
-               {
-                       if(player.buff_ammo_prev_infitems)
-                               player.items |= IT_UNLIMITED_AMMO;
-                       else
-                               player.items &= ~IT_UNLIMITED_AMMO;
-
-                       if(StatusEffects_active(BUFF_AMMO, player))
-                       {
-                               for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
-                               {
-                                       .entity weaponentity = weaponentities[slot];
-                                       if(player.(weaponentity).buff_ammo_prev_clipload)
-                                               player.(weaponentity).clip_load = player.(weaponentity).buff_ammo_prev_clipload;
-                               }
-                       }
-               }
-
-               BUFF_ONADD(BUFF_INVISIBLE)
-               {
-                       if(StatusEffects_active(STATUSEFFECT_Strength, player) && MUTATOR_IS_ENABLED(mutator_instagib))
-                               player.buff_invisible_prev_alpha = default_player_alpha; // we don't want to save the powerup's alpha, as player may lose the powerup while holding the buff
-                       else
-                               player.buff_invisible_prev_alpha = player.alpha;
-                       if(!player.vehicle)
-                               player.alpha = autocvar_g_buffs_invisible_alpha;
-               }
-
-               BUFF_ONREM(BUFF_INVISIBLE)
-               {
-                       if(!player.vehicle)
-                       {
-                               if(StatusEffects_active(STATUSEFFECT_Strength, player) && MUTATOR_IS_ENABLED(mutator_instagib))
-                                       player.alpha = autocvar_g_instagib_invis_alpha;
-                               else
-                                       player.alpha = player.buff_invisible_prev_alpha;
-                       }
-               }
-
-               BUFF_ONADD(BUFF_FLIGHT)
-               {
-                       player.buff_flight_oldgravity = player.gravity;
-                       if(!player.gravity)
-                               player.gravity = 1;
-               }
-
-               BUFF_ONREM(BUFF_FLIGHT)
-                       player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity);
-
-               player.oldbuffs = heldbuff;
-               if(heldbuff)
-               {
-                       if(!player.buff_model)
-                               buffs_BuffModel_Spawn(player);
-
-                       player.buff_model.color = heldbuff.m_color;
-                       player.buff_model.glowmod = buff_GlowColor(heldbuff);
-                       player.buff_model.skin = heldbuff.m_skin;
-
-                       player.effects |= EF_NOSHADOW;
-               }
-               else
-               {
-                       buffs_BuffModel_Remove(player);
-
-                       player.effects &= ~(EF_NOSHADOW);
-               }
-       }
-
-       if(player.buff_model)
-       {
-               player.buff_model.effects = player.effects;
-               player.buff_model.effects |= EF_LOWPRECISION;
-               player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance
-
-               player.buff_model.alpha = player.alpha;
-       }
-
-#undef BUFF_ONADD
-#undef BUFF_ONREM
+       if(IS_PLAYER(player))
+               buffs_BuffModel_Update(player);
 }
 
 MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
@@ -1014,9 +966,6 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerRegen)
                M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod
                M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod
        }
-
-       if(StatusEffects_active(BUFF_SPEED, player))
-               M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod
 }
 
 REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace");
index 1b095c0f2c50db47956e4e85ec6034648af1ea04..ea2beb584df8f7902ad236d0822029da6e94c24b 100644 (file)
@@ -43,13 +43,7 @@ float autocvar_g_buffs_disability_slowtime;
 float autocvar_g_buffs_disability_speed;
 float autocvar_g_buffs_disability_rate;
 float autocvar_g_buffs_disability_weaponspeed;
-float autocvar_g_buffs_speed_speed;
-float autocvar_g_buffs_speed_rate;
-float autocvar_g_buffs_speed_weaponspeed;
-float autocvar_g_buffs_speed_damage_take;
-float autocvar_g_buffs_speed_regen;
 float autocvar_g_buffs_vampire_damage_steal;
-float autocvar_g_buffs_invisible_alpha;
 float autocvar_g_buffs_jump_height;
 float autocvar_g_buffs_inferno_burntime_factor;
 float autocvar_g_buffs_inferno_burntime_min_time;
@@ -65,13 +59,9 @@ float autocvar_g_buffs_luck_damagemultiplier = 3;
 // ammo
 .float buff_ammo_prev_infitems;
 .int buff_ammo_prev_clipload;
-// invisible
-.float buff_invisible_prev_alpha;
-// disability
-.float buff_disability_time;
-.float buff_disability_effect_time;
 // flight
 .float buff_flight_oldgravity;
+.bool buff_flight_crouchheld;
 // common buff variables
 .float buff_effect_delay;
 
index 55a67b1e38a95abcd62c87af38cd29425a308510..2195111f0f269e2903f767e3badf17644e0aa27d 100644 (file)
@@ -1,8 +1,5 @@
 // generated file; do not modify
 #include <common/mutators/mutator/instagib/items.qc>
-#ifdef SVQC
-    #include <common/mutators/mutator/instagib/sv_items.qc>
-#endif
 #ifdef SVQC
     #include <common/mutators/mutator/instagib/sv_instagib.qc>
 #endif
index 9989d8e3f3313193763276e3652ea16ed013c6e2..7097eaf390dac022c2840f7db4d9dc6ee7028368 100644 (file)
@@ -1,8 +1,5 @@
 // generated file; do not modify
 #include <common/mutators/mutator/instagib/items.qh>
-#ifdef SVQC
-    #include <common/mutators/mutator/instagib/sv_items.qh>
-#endif
 #ifdef SVQC
     #include <common/mutators/mutator/instagib/sv_instagib.qh>
 #endif
index 4724eda1735f5c37c80989854c4fe2ce0c2c71bc..9be156bb30da9e518e7a2a6026fbe439a23759e3 100644 (file)
@@ -2,7 +2,7 @@
 
 #include <common/items/_mod.qh>
 #include <common/items/item/ammo.qh>
-#include <common/items/item/powerup.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 
 float instagib_respawntime_ammo = 45;
 float instagib_respawntimejitter_ammo = 0;
@@ -67,77 +67,3 @@ REGISTER_ITEM(ExtraLife, Powerup) {
 }
 
 SPAWNFUNC_ITEM(item_extralife, ITEM_ExtraLife)
-
-#ifdef GAMEQC
-MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
-SOUND(Invisibility, Item_Sound("powerup"));
-#endif
-
-#ifdef SVQC
-/// \brief Initializes the invisibility powerup.
-/// \param[in,out] item Item to initialize.
-/// \return No return.
-void powerup_invisibility_init(Pickup this, entity item);
-#endif
-
-REGISTER_ITEM(Invisibility, Powerup) {
-    this.m_canonical_spawnfunc = "item_invisibility";
-#ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
-    this.m_model            =   MDL_Invisibility_ITEM;
-    this.m_sound            =   SND_Invisibility;
-    this.m_glow             =   true;
-    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
-#endif
-    this.netname            =   "invisibility";
-    this.m_name             =   _("Invisibility");
-    this.m_icon             =   "strength";
-    this.m_color            =   '0 0 1';
-    this.m_waypoint         =   _("Invisibility");
-    this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_STRENGTH;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_invisibility_init;
-#endif
-}
-
-SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
-
-#ifdef GAMEQC
-MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
-SOUND(Speed, Item_Sound("powerup_shield"));
-#endif
-
-#ifdef SVQC
-/// \brief Initializes the speed powerup.
-/// \param[in,out] item Item to initialize.
-/// \return No return.
-void powerup_speed_init(Pickup this, entity item);
-#endif
-
-REGISTER_ITEM(Speed, Powerup) {
-    this.m_canonical_spawnfunc = "item_speed";
-#ifdef GAMEQC
-       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED;
-    this.m_model            =   MDL_Speed_ITEM;
-    this.m_sound            =   SND_Speed;
-    this.m_glow             =   true;
-    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
-#endif
-    this.netname            =   "speed";
-    this.m_name             =   _("Speed");
-    this.m_icon             =   "shield";
-    this.m_color            =   '1 0 1';
-    this.m_waypoint         =   _("Speed");
-    this.m_waypointblink    =   2;
-#ifdef GAMEQC
-    this.m_itemid           =   IT_INVINCIBLE;
-#endif
-#ifdef SVQC
-    this.m_iteminit         =   powerup_speed_init;
-#endif
-}
-
-SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
index 51d65a917ece56e5368d32e86e7c97be2273c85d..44cd80d023d11f96d0c8ef81b3a0fe54b8ff1591 100644 (file)
@@ -2,6 +2,7 @@
 
 #include <server/client.qh>
 #include <common/items/_mod.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include "../random_items/sv_random_items.qh"
 
@@ -15,11 +16,10 @@ bool autocvar_g_instagib_ammo_convert_cells;
 bool autocvar_g_instagib_ammo_convert_rockets;
 bool autocvar_g_instagib_ammo_convert_shells;
 bool autocvar_g_instagib_ammo_convert_bullets;
-float autocvar_g_instagib_speed_highspeed;
 
 void instagib_invisibility(entity this)
 {
-       this.strength_finished = autocvar_g_instagib_invisibility_time;
+       this.invisibility_finished = autocvar_g_instagib_invisibility_time;
        StartItem(this, ITEM_Invisibility);
 }
 
@@ -30,7 +30,7 @@ void instagib_extralife(entity this)
 
 void instagib_speed(entity this)
 {
-       this.invincible_finished = autocvar_g_instagib_speed_time;
+       this.speed_finished = autocvar_g_instagib_speed_time;
        StartItem(this, ITEM_Speed);
 }
 
@@ -138,14 +138,6 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn)
                mon.skin = 1;
 }
 
-MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack)
-{
-       entity targ = M_ARGV(1, entity);
-
-       if (targ.items & ITEM_Invisibility.m_itemid)
-               return true;
-}
-
 MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver)
 {
        entity player = M_ARGV(0, entity);
@@ -178,70 +170,6 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups)
-{
-       entity player = M_ARGV(0, entity);
-
-       player.effects |= EF_FULLBRIGHT;
-
-       if (player.items & ITEM_Invisibility.m_itemid)
-       {
-               play_countdown(player, StatusEffects_gettime(STATUSEFFECT_Strength, player), SND_POWEROFF);
-               if (time > StatusEffects_gettime(STATUSEFFECT_Strength, player))
-               {
-                       if(!player.vehicle) // already reset upon exit
-                       {
-                               player.alpha = default_player_alpha;
-                               player.exteriorweaponentity.alpha = default_weapon_alpha;
-                       }
-                       player.items &= ~ITEM_Invisibility.m_itemid;
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
-               }
-       }
-       else
-       {
-               if (time < StatusEffects_gettime(STATUSEFFECT_Strength, player))
-               {
-                       if(!player.vehicle) // incase the player is given powerups while inside a vehicle
-                       {
-                               player.alpha = autocvar_g_instagib_invis_alpha;
-                               player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha;
-                       }
-                       player.items |= ITEM_Invisibility.m_itemid;
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname);
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
-               }
-       }
-
-       if (player.items & ITEM_Speed.m_itemid)
-       {
-               play_countdown(player, StatusEffects_gettime(STATUSEFFECT_Shield, player), SND_POWEROFF);
-               if (time > StatusEffects_gettime(STATUSEFFECT_Shield, player))
-               {
-                       player.items &= ~ITEM_Speed.m_itemid;
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED);
-               }
-       }
-       else
-       {
-               if (time < StatusEffects_gettime(STATUSEFFECT_Shield, player))
-               {
-                       player.items |= ITEM_Speed.m_itemid;
-                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname);
-                       Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED);
-               }
-       }
-}
-
-MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics_UpdateStats)
-{
-       entity player = M_ARGV(0, entity);
-       // these automatically reset, no need to worry
-
-       if(player.items & ITEM_Speed.m_itemid)
-               STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_instagib_speed_highspeed;
-}
-
 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor)
 {
        M_ARGV(4, float) = M_ARGV(7, float); // take = damage
@@ -426,7 +354,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
                return true;
        }
 
-       if(item.flags & FL_POWERUP)
+       if(item.itemdef.instanceOfPowerup)
                return false;
 
        float cells = GetResource(item, RES_CELLS);
@@ -439,20 +367,6 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
        return true;
 }
 
-MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint)
-{
-       entity wp = M_ARGV(0, entity);
-       entity player = M_ARGV(1, entity);
-
-       entity e = WaypointSprite_getviewentity(player);
-
-       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
-       // but only apply this to real players, not to spectators
-       if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player))
-       if(DIFF_TEAM(wp.owner, e))
-               return true;
-}
-
 MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies)
 {
        float frag_deathtype = M_ARGV(3, float);
@@ -502,7 +416,7 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn)
        if (!autocvar_g_powerups) { return; }
        entity ent = M_ARGV(0, entity);
        // Can't use .itemdef here
-       if (!(ent.classname == "item_strength" || ent.classname == "item_shield" || ent.classname == "item_health_mega"))
+       if (!(ent.classname == "item_strength" || ent.classname == "item_shield" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
                return;
 
        entity e = spawn();
index c4b9fcc19330b5c537041ea746e380e0689c65bc..4ee7e491fb36ea0f0993118888ff2c302019919c 100644 (file)
@@ -2,6 +2,7 @@
 
 #include "items.qh"
 #include <common/gamemodes/_mod.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 
 // TODO: make this its own mutator (somehow)!
 float autocvar_g_rm;
@@ -24,9 +25,13 @@ float autocvar_g_rm_laser_radius;
 float autocvar_g_rm_laser_force;
 
 bool autocvar_g_instagib;
-float autocvar_g_instagib_invis_alpha;
 int autocvar_g_instagib_extralives;
 
+/// \brief Time of invisibility powerup in seconds.
+float autocvar_g_instagib_invisibility_time;
+/// \brief Time of speed powerup in seconds.
+float autocvar_g_instagib_speed_time;
+
 void instagib_invisibility(entity this);
 void instagib_extralife(entity this);
 void instagib_speed(entity this);
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_items.qc b/qcsrc/common/mutators/mutator/instagib/sv_items.qc
deleted file mode 100644 (file)
index c944f56..0000000
+++ /dev/null
@@ -1,25 +0,0 @@
-#include "sv_items.qh"
-
-#include "items.qh"
-
-/// \brief Time of ivisibility powerup in seconds.
-float autocvar_g_instagib_invisibility_time;
-/// \brief Time of speed powerup in seconds.
-float autocvar_g_instagib_speed_time;
-
-void powerup_invisibility_init(Pickup this, entity item)
-{
-       if(!item.strength_finished)
-       {
-               item.strength_finished = autocvar_g_instagib_invisibility_time;
-       }
-}
-
-
-void powerup_speed_init(Pickup this, entity item)
-{
-       if(!item.invincible_finished)
-       {
-               item.invincible_finished = autocvar_g_instagib_speed_time;
-       }
-}
diff --git a/qcsrc/common/mutators/mutator/instagib/sv_items.qh b/qcsrc/common/mutators/mutator/instagib/sv_items.qh
deleted file mode 100644 (file)
index 6f70f09..0000000
+++ /dev/null
@@ -1 +0,0 @@
-#pragma once
index 96729df449a078cdbe6746d93d5697a680d38ad7..04c61a6d73c5e8cf23c135e780372cb892d6a74e 100644 (file)
@@ -1140,7 +1140,7 @@ void nade_prime(entity this)
        int ntype;
        string pntype = this.pokenade_type;
 
-       if((this.items & ITEM_Strength.m_itemid) && autocvar_g_nades_bonus_onstrength)
+       if(StatusEffects_active(STATUSEFFECT_Strength, this) && autocvar_g_nades_bonus_onstrength)
                ntype = STAT(NADE_BONUS_TYPE, this);
        else if (STAT(NADE_BONUS, this) >= 1)
        {
index 210b77ec4afdef8804de3e0bb71cf2d579421700..6bfafed67f9cab6b360f435ae3e15474148bf3fb 100644 (file)
@@ -18,7 +18,7 @@ void W_OverkillHeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity
                return;
        }
 
-       if((!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !(actor.items & IT_UNLIMITED_AMMO)) || (!(actor.items & IT_SUPERWEAPON) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS)))
+       if((!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !(actor.items & IT_UNLIMITED_AMMO)) || (!StatusEffects_active(STATUSEFFECT_Superweapons, actor) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS)))
        {
                W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity);
                w_ready(thiswep, actor, weaponentity, fire);
diff --git a/qcsrc/common/mutators/mutator/powerups/_mod.inc b/qcsrc/common/mutators/mutator/powerups/_mod.inc
new file mode 100644 (file)
index 0000000..fd926b3
--- /dev/null
@@ -0,0 +1,10 @@
+// generated file; do not modify
+#include <common/mutators/mutator/powerups/powerups.qc>
+#ifdef CSQC
+    #include <common/mutators/mutator/powerups/cl_powerups.qc>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/powerups/sv_powerups.qc>
+#endif
+
+#include <common/mutators/mutator/powerups/powerup/_mod.inc>
diff --git a/qcsrc/common/mutators/mutator/powerups/_mod.qh b/qcsrc/common/mutators/mutator/powerups/_mod.qh
new file mode 100644 (file)
index 0000000..8ab7ce6
--- /dev/null
@@ -0,0 +1,10 @@
+// generated file; do not modify
+#include <common/mutators/mutator/powerups/powerups.qh>
+#ifdef CSQC
+    #include <common/mutators/mutator/powerups/cl_powerups.qh>
+#endif
+#ifdef SVQC
+    #include <common/mutators/mutator/powerups/sv_powerups.qh>
+#endif
+
+#include <common/mutators/mutator/powerups/powerup/_mod.qh>
diff --git a/qcsrc/common/mutators/mutator/powerups/cl_powerups.qc b/qcsrc/common/mutators/mutator/powerups/cl_powerups.qc
new file mode 100644 (file)
index 0000000..1d60786
--- /dev/null
@@ -0,0 +1 @@
+#include "cl_powerups.qh"
diff --git a/qcsrc/common/mutators/mutator/powerups/cl_powerups.qh b/qcsrc/common/mutators/mutator/powerups/cl_powerups.qh
new file mode 100644 (file)
index 0000000..85eeb93
--- /dev/null
@@ -0,0 +1,4 @@
+#pragma once
+
+#include "powerups.qh"
+
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/_mod.inc b/qcsrc/common/mutators/mutator/powerups/powerup/_mod.inc
new file mode 100644 (file)
index 0000000..42893e8
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <common/mutators/mutator/powerups/powerup/invisibility.qc>
+#include <common/mutators/mutator/powerups/powerup/shield.qc>
+#include <common/mutators/mutator/powerups/powerup/speed.qc>
+#include <common/mutators/mutator/powerups/powerup/strength.qc>
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh b/qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh
new file mode 100644 (file)
index 0000000..7e7c5a2
--- /dev/null
@@ -0,0 +1,5 @@
+// generated file; do not modify
+#include <common/mutators/mutator/powerups/powerup/invisibility.qh>
+#include <common/mutators/mutator/powerups/powerup/shield.qh>
+#include <common/mutators/mutator/powerups/powerup/speed.qh>
+#include <common/mutators/mutator/powerups/powerup/strength.qh>
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc
new file mode 100644 (file)
index 0000000..dda2dcf
--- /dev/null
@@ -0,0 +1,44 @@
+#include "invisibility.qh"
+
+#ifdef SVQC
+METHOD(Invisibility, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(removal_type == STATUSEFFECT_REMOVE_TIMEOUT && wasactive && IS_PLAYER(actor))
+    {
+        //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_INVISIBILITY, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY);
+    }
+    if(wasactive)
+        stopsound(actor, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+    if(!actor.vehicle)
+    {
+        actor.alpha = default_player_alpha;
+        if(actor.exteriorweaponentity)
+            actor.exteriorweaponentity.alpha = default_weapon_alpha;
+    }
+    SUPER(Invisibility).m_remove(this, actor, removal_type);
+}
+METHOD(Invisibility, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive && IS_PLAYER(actor))
+    {
+        if(!g_cts)
+            Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERUP_INVISIBILITY);
+    }
+    SUPER(Invisibility).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Invisibility, m_tick, void(StatusEffects this, entity actor))
+{
+    play_countdown(actor, StatusEffects_gettime(this, actor), SND_POWEROFF);
+    if(!actor.vehicle)
+    {
+        actor.alpha = autocvar_g_balance_powerup_invisibility_alpha;
+        if(actor.exteriorweaponentity)
+            actor.exteriorweaponentity.alpha = autocvar_g_balance_powerup_invisibility_alpha;
+    }
+    SUPER(Invisibility).m_tick(this, actor);
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh
new file mode 100644 (file)
index 0000000..85ac8bd
--- /dev/null
@@ -0,0 +1,60 @@
+#pragma once
+
+#include <common/items/all.qh>
+#include <common/mutators/mutator/powerups/powerups.qh>
+#ifdef SVQC
+    // For FL_POWERUP
+    #include <common/constants.qh>
+    #include <server/items/items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
+SOUND(Invisibility, Item_Sound("powerup"));
+#endif
+
+#ifdef SVQC
+.float invisibility_finished;
+
+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;
+}
+#endif
+REGISTER_ITEM(Invisibility, Powerup) {
+    this.m_canonical_spawnfunc = "item_invisibility";
+#ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
+    this.m_model            =   MDL_Invisibility_ITEM;
+    this.m_sound            =   SND_Invisibility;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
+#endif
+    this.netname            =   "invisibility";
+    this.m_name             =   _("Invisibility");
+    this.m_icon             =   "buff_invisible";
+    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)
+// compat
+SPAWNFUNC_ITEM(item_invis, ITEM_Invisibility)
+
+CLASS(Invisibility, Powerups)
+    ATTRIB(Invisibility, netname, string, "invisibility");
+    ATTRIB(Invisibility, m_name, string, _("Invisibility"));
+    ATTRIB(Invisibility, m_color, vector, '0.5 0.5 1');
+    ATTRIB(Invisibility, m_icon, string, "buff_invisible");
+ENDCLASS(Invisibility)
+REGISTER_STATUSEFFECT(Invisibility, NEW(Invisibility));
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/shield.qc b/qcsrc/common/mutators/mutator/powerups/powerup/shield.qc
new file mode 100644 (file)
index 0000000..230967b
--- /dev/null
@@ -0,0 +1,50 @@
+#include "shield.qh"
+
+#ifdef SVQC
+METHOD(Shield, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(removal_type == STATUSEFFECT_REMOVE_TIMEOUT && wasactive && IS_PLAYER(actor))
+    {
+        //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
+    }
+    if(wasactive)
+        stopsound(actor, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+    actor.effects &= ~(EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(Shield).m_remove(this, actor, removal_type);
+}
+METHOD(Shield, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive && IS_PLAYER(actor))
+    {
+        if(!g_cts)
+            Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERUP_SHIELD);
+    }
+    SUPER(Shield).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Shield, m_tick, void(StatusEffects this, entity actor))
+{
+    play_countdown(actor, StatusEffects_gettime(this, actor), SND_POWEROFF);
+    actor.effects |= (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(Shield).m_tick(this, actor);
+}
+#endif
+#ifdef CSQC
+METHOD(Shield, m_active, bool(StatusEffects this, entity actor))
+{
+    if(autocvar__hud_configure)
+        return true;
+    return SUPER(Shield).m_active(this, actor);
+}
+METHOD(Shield, m_tick, void(StatusEffects this, entity actor))
+{
+    if(this.m_hidden)
+        return;
+
+    float currentTime = (autocvar__hud_configure) ? 27 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
+    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_shield_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/shield.qh b/qcsrc/common/mutators/mutator/powerups/powerup/shield.qh
new file mode 100644 (file)
index 0000000..854c802
--- /dev/null
@@ -0,0 +1,57 @@
+#pragma once
+
+#include <common/items/all.qh>
+#include <common/mutators/mutator/powerups/powerups.qh>
+#ifdef SVQC
+    // For FL_POWERUP
+    #include <common/constants.qh>
+    #include <server/items/items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Shield, Item_Sound("powerup_shield"));
+#endif
+
+#ifdef SVQC
+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;
+}
+#endif
+REGISTER_ITEM(Shield, Powerup) {
+    this.m_canonical_spawnfunc = "item_shield";
+#ifdef GAMEQC
+    this.spawnflags = ITEM_FLAG_NORMAL;
+    this.m_model            =   MDL_Shield_ITEM;
+    this.m_sound            =   SND_Shield;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
+#endif
+    this.netname            =   "invincible";
+    this.m_name             =   _("Shield");
+    this.m_icon             =   "shield";
+    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)
+SPAWNFUNC_ITEM(item_invincible, ITEM_Shield)
+
+CLASS(Shield, Powerups)
+    ATTRIB(Shield, netname, string, "shield");
+    ATTRIB(Shield, m_name, string, _("Shield"));
+    ATTRIB(Shield, m_icon, string, "shield");
+ENDCLASS(Shield)
+REGISTER_STATUSEFFECT(Shield, NEW(Shield));
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/speed.qc b/qcsrc/common/mutators/mutator/powerups/powerup/speed.qc
new file mode 100644 (file)
index 0000000..4a2fe2c
--- /dev/null
@@ -0,0 +1,34 @@
+#include "speed.qh"
+
+#ifdef SVQC
+METHOD(Speed, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(removal_type == STATUSEFFECT_REMOVE_TIMEOUT && wasactive && IS_PLAYER(actor))
+    {
+        //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SPEED, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERDOWN_SPEED);
+    }
+    if(wasactive)
+        stopsound(actor, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+    actor.effects &= ~EF_STARDUST;
+    SUPER(Speed).m_remove(this, actor, removal_type);
+}
+METHOD(Speed, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive && IS_PLAYER(actor))
+    {
+        if(!g_cts)
+            Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERUP_SPEED);
+    }
+    SUPER(Speed).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Speed, m_tick, void(StatusEffects this, entity actor))
+{
+    play_countdown(actor, StatusEffects_gettime(this, actor), SND_POWEROFF);
+    actor.effects |= EF_STARDUST;
+    SUPER(Speed).m_tick(this, actor);
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/speed.qh b/qcsrc/common/mutators/mutator/powerups/powerup/speed.qh
new file mode 100644 (file)
index 0000000..3a55a78
--- /dev/null
@@ -0,0 +1,62 @@
+#pragma once
+
+#include <common/items/all.qh>
+#include <common/mutators/mutator/powerups/powerups.qh>
+#ifdef SVQC
+    // For FL_POWERUP
+    #include <common/constants.qh>
+    #include <server/items/items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
+SOUND(Speed, Item_Sound("powerup_shield"));
+#endif
+
+#ifdef SVQC
+.float speed_finished;
+
+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;
+}
+#endif
+REGISTER_ITEM(Speed, Powerup) {
+    this.m_canonical_spawnfunc = "item_speed";
+#ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; // TODO: ITEM_FLAG_NORMAL (once it has a model!)
+    this.m_model            =   MDL_Speed_ITEM;
+    this.m_sound            =   SND_Speed;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_SHIELD_RESPAWN;
+#endif
+    this.netname            =   "speed";
+    this.m_name             =   _("Speed");
+    this.m_icon             =   "buff_speed";
+    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)
+// compat
+SPAWNFUNC_ITEM(item_haste, ITEM_Speed)
+SPAWNFUNC_ITEM(item_scout, ITEM_Speed)
+
+CLASS(Speed, Powerups)
+    ATTRIB(Speed, netname, string, "speed");
+    ATTRIB(Speed, m_name, string, _("Speed"));
+    ATTRIB(Speed, m_color, vector, '0.1 1 0.84');
+    ATTRIB(Speed, m_icon, string, "buff_speed");
+ENDCLASS(Speed)
+REGISTER_STATUSEFFECT(Speed, NEW(Speed));
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/strength.qc b/qcsrc/common/mutators/mutator/powerups/powerup/strength.qc
new file mode 100644 (file)
index 0000000..c2f2572
--- /dev/null
@@ -0,0 +1,50 @@
+#include "strength.qh"
+
+#ifdef SVQC
+METHOD(Strength, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(removal_type == STATUSEFFECT_REMOVE_TIMEOUT && wasactive && IS_PLAYER(actor))
+    {
+        //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
+    }
+    if(wasactive)
+        stopsound(actor, CH_TRIGGER_SINGLE); // get rid of the pickup sound
+    actor.effects &= ~(EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(Strength).m_remove(this, actor, removal_type);
+}
+METHOD(Strength, m_apply, void(StatusEffects this, entity actor, float eff_time, float eff_flags))
+{
+    bool wasactive = (actor.statuseffects && (actor.statuseffects.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_ACTIVE));
+    if(!wasactive && IS_PLAYER(actor))
+    {
+        if(!g_cts)
+            Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, actor.netname);
+        Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_POWERUP_STRENGTH);
+    }
+    SUPER(Strength).m_apply(this, actor, eff_time, eff_flags);
+}
+METHOD(Strength, m_tick, void(StatusEffects this, entity actor))
+{
+    play_countdown(actor, StatusEffects_gettime(this, actor), SND_POWEROFF);
+    actor.effects |= (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(Strength).m_tick(this, actor);
+}
+#endif
+#ifdef CSQC
+METHOD(Strength, m_active, bool(StatusEffects this, entity actor))
+{
+    if(autocvar__hud_configure)
+        return true;
+    return SUPER(Strength).m_active(this, actor);
+}
+METHOD(Strength, m_tick, void(StatusEffects this, entity actor))
+{
+    if(this.m_hidden)
+        return;
+
+    float currentTime = (autocvar__hud_configure) ? 15 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
+    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_strength_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/strength.qh b/qcsrc/common/mutators/mutator/powerups/powerup/strength.qh
new file mode 100644 (file)
index 0000000..ce19149
--- /dev/null
@@ -0,0 +1,58 @@
+#pragma once
+
+#include <common/items/all.qh>
+#include <common/mutators/mutator/powerups/powerups.qh>
+#ifdef SVQC
+    // For FL_POWERUP
+    #include <common/constants.qh>
+    #include <server/items/items.qh>
+#endif
+
+#ifdef GAMEQC
+MODEL(Strength_ITEM, Item_Model("g_strength.md3"));
+SOUND(Strength, Item_Sound("powerup"));
+#endif
+
+#ifdef SVQC
+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)
+{
+    if(!item.strength_finished)
+        item.strength_finished = autocvar_g_balance_powerup_strength_time;
+}
+#endif
+REGISTER_ITEM(Strength, Powerup) {
+    this.m_canonical_spawnfunc = "item_strength";
+#ifdef GAMEQC
+       this.spawnflags = ITEM_FLAG_NORMAL;
+    this.m_model            =   MDL_Strength_ITEM;
+    this.m_sound            =   SND_Strength;
+    this.m_glow             =   true;
+    this.m_respawnsound     =   SND_STRENGTH_RESPAWN;
+#endif
+    this.netname            =   "strength";
+    this.m_name             =   _("Strength");
+    this.m_icon             =   "strength";
+    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)
+
+CLASS(Strength, Powerups)
+    ATTRIB(Strength, netname, string, "strength");
+    ATTRIB(Strength, m_name, string, _("Strength"));
+    ATTRIB(Strength, m_icon, string, "strength");
+ENDCLASS(Strength)
+REGISTER_STATUSEFFECT(Strength, NEW(Strength));
diff --git a/qcsrc/common/mutators/mutator/powerups/powerups.qc b/qcsrc/common/mutators/mutator/powerups/powerups.qc
new file mode 100644 (file)
index 0000000..7fc10c9
--- /dev/null
@@ -0,0 +1,2 @@
+#include "powerups.qh"
+
diff --git a/qcsrc/common/mutators/mutator/powerups/powerups.qh b/qcsrc/common/mutators/mutator/powerups/powerups.qh
new file mode 100644 (file)
index 0000000..3a614e3
--- /dev/null
@@ -0,0 +1,20 @@
+#pragma once
+
+#include <common/items/item/pickup.qh>
+CLASS(Powerup, Pickup)
+#ifdef SVQC
+    ATTRIB(Powerup, m_mins, vector, '-16 -16 0');
+    ATTRIB(Powerup, m_maxs, vector, '16 16 80');
+    ATTRIB(Powerup, m_botvalue, int, 11000);
+    ATTRIB(Powerup, m_itemflags, int, FL_POWERUP);
+    ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup));
+    ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup));
+#endif
+ENDCLASS(Powerup)
+
+#include <common/mutators/mutator/status_effects/all.qh>
+CLASS(Powerups, StatusEffects)
+#ifdef GAMEQC
+    ATTRIB(Powerups, m_sound_rm, Sound, SND_POWEROFF);
+#endif
+ENDCLASS(Powerups)
diff --git a/qcsrc/common/mutators/mutator/powerups/sv_powerups.qc b/qcsrc/common/mutators/mutator/powerups/sv_powerups.qc
new file mode 100644 (file)
index 0000000..2ac0bbc
--- /dev/null
@@ -0,0 +1,119 @@
+#include "sv_powerups.qh"
+
+MUTATOR_HOOKFUNCTION(powerups, W_PlayStrengthSound)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(StatusEffects_active(STATUSEFFECT_Strength, player)
+               && ((time > player.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
+               || (time > player.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
+               {
+                       sound(player, CH_TRIGGER, SND_STRENGTH_FIRE, VOL_BASE, ATTEN_NORM);
+                       player.prevstrengthsound = time;
+               }
+       player.prevstrengthsoundattempt = time;
+}
+
+MUTATOR_HOOKFUNCTION(powerups, LogDeath_AppendItemCodes)
+{
+       entity player = M_ARGV(0, entity);
+
+       if(StatusEffects_active(STATUSEFFECT_Strength, player))
+               M_ARGV(1, string) = strcat(M_ARGV(1, string), "S");
+
+       if(StatusEffects_active(STATUSEFFECT_Shield, player))
+               M_ARGV(1, string) = strcat(M_ARGV(1, string), "I");
+
+       // TODO: item codes for other powerups?
+}
+
+MUTATOR_HOOKFUNCTION(powerups, Damage_Calculate)
+{
+       entity attacker = M_ARGV(1, entity);
+       entity targ = M_ARGV(2, entity);
+
+       // apply strength multiplier
+       if(StatusEffects_active(STATUSEFFECT_Strength, attacker))
+       {
+               if(targ == attacker)
+               {
+                       M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_strength_selfdamage;
+                       M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_strength_selfforce;
+               }
+               else
+               {
+                       M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_strength_damage;
+                       M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_strength_force;
+               }
+       }
+
+       // apply shield multiplier
+       if(StatusEffects_active(STATUSEFFECT_Shield, targ))
+       {
+               M_ARGV(4, float) = M_ARGV(4, float) * autocvar_g_balance_powerup_invincible_takedamage;
+               if (targ != attacker)
+               {
+                       M_ARGV(6, vector) = M_ARGV(6, vector) * autocvar_g_balance_powerup_invincible_takeforce;
+               }
+       }
+}
+
+MUTATOR_HOOKFUNCTION(powerups, CustomizeWaypoint)
+{
+       entity wp = M_ARGV(0, entity);
+       entity player = M_ARGV(1, entity);
+
+       entity e = WaypointSprite_getviewentity(player);
+
+       // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+       // but only apply this to real players, not to spectators
+       if(IS_CLIENT(wp.owner) && (e == player) && DIFF_TEAM(wp.owner, e) && StatusEffects_active(STATUSEFFECT_Invisibility, wp.owner))
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(powerups, MonsterValidTarget)
+{
+       entity targ = M_ARGV(1, entity);
+       return StatusEffects_active(STATUSEFFECT_Invisibility, targ);
+}
+
+MUTATOR_HOOKFUNCTION(powerups, PlayerPhysics_UpdateStats)
+{
+       entity player = M_ARGV(0, entity);
+       // these automatically reset, no need to worry
+
+       if(StatusEffects_active(STATUSEFFECT_Speed, player))
+               STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_balance_powerup_speed_highspeed;
+}
+
+MUTATOR_HOOKFUNCTION(powerups, WeaponRateFactor)
+{
+       entity player = M_ARGV(1, entity);
+
+       if(StatusEffects_active(STATUSEFFECT_Speed, player))
+               M_ARGV(0, float) *= autocvar_g_balance_powerup_speed_attackrate;
+}
+
+MUTATOR_HOOKFUNCTION(powerups, BuildMutatorsPrettyString)
+{
+       if(autocvar_g_powerups == 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ", No powerups");
+       if(autocvar_g_powerups > 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Powerups");
+}
+
+MUTATOR_HOOKFUNCTION(powerups, BotShouldAttack)
+{
+       entity targ = M_ARGV(1, entity);
+
+       if(StatusEffects_active(STATUSEFFECT_Invisibility, targ))
+               return true;
+}
+
+MUTATOR_HOOKFUNCTION(powerups, BuildMutatorsString)
+{
+       if(autocvar_g_powerups == 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ":no_powerups");
+       if(autocvar_g_powerups > 0)
+               M_ARGV(0, string) = strcat(M_ARGV(0, string), ":powerups");
+}
diff --git a/qcsrc/common/mutators/mutator/powerups/sv_powerups.qh b/qcsrc/common/mutators/mutator/powerups/sv_powerups.qh
new file mode 100644 (file)
index 0000000..f710259
--- /dev/null
@@ -0,0 +1,12 @@
+#pragma once
+
+#include <common/mutators/base.qh>
+
+#include "powerups.qh"
+
+int autocvar_g_powerups;
+
+REGISTER_MUTATOR(powerups, true);
+
+.float prevstrengthsound;
+.float prevstrengthsoundattempt;
index d6afab175160405513e6d6203393669a2aaad76b..2f0c794317b15ccbe8c41f85a90381c37208022e 100644 (file)
@@ -96,7 +96,7 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn)
                        if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && GetResource(it, RES_HEALTH) < autocvar_g_balance_health_regenstable) continue;
                        if (IS_DEAD(it)) continue;
                        if (time < it.msnt_timer) continue;
-                       if (time < it.spawnshieldtime) continue;
+                       if (StatusEffects_active(STATUSEFFECT_SpawnShield, it)) continue;
                        if (weaponLocked(it)) continue;
                        if (it == player) continue;
 
index f02099f1d1cbc6682ab751f140b34f276aa0d8a5..6fa2ed024de20c8f200b2dbb0e721824eeba20a1 100644 (file)
@@ -1,3 +1,4 @@
 // generated file; do not modify
 #include <common/mutators/mutator/status_effects/status_effect/burning.qc>
-#include <common/mutators/mutator/status_effects/status_effect/powerups.qc>
+#include <common/mutators/mutator/status_effects/status_effect/spawnshield.qc>
+#include <common/mutators/mutator/status_effects/status_effect/superweapons.qc>
index c91959bed6d40ade7e19e7dfe7980ce5256f222e..feaa8a19d61b812ca1b4e68fcd65e1d0cd35f3b8 100644 (file)
@@ -1,3 +1,4 @@
 // generated file; do not modify
 #include <common/mutators/mutator/status_effects/status_effect/burning.qh>
-#include <common/mutators/mutator/status_effects/status_effect/powerups.qh>
+#include <common/mutators/mutator/status_effects/status_effect/spawnshield.qh>
+#include <common/mutators/mutator/status_effects/status_effect/superweapons.qh>
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qc b/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qc
deleted file mode 100644 (file)
index 8c031aa..0000000
+++ /dev/null
@@ -1,57 +0,0 @@
-#include "powerups.qh"
-
-#ifdef CSQC
-METHOD(Strength, m_active, bool(StatusEffects this, entity actor))
-{
-    if(autocvar__hud_configure)
-        return true;
-    return SUPER(Strength).m_active(this, actor);
-}
-METHOD(Strength, m_tick, void(StatusEffects this, entity actor))
-{
-    if(this.m_hidden)
-        return;
-
-    float currentTime = (autocvar__hud_configure) ? 15 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
-    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_strength_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
-}
-
-METHOD(Superweapons, m_active, bool(StatusEffects this, entity actor))
-{
-    if(autocvar__hud_configure)
-        return true;
-    return SUPER(Superweapons).m_active(this, actor);
-}
-METHOD(Superweapons, m_tick, void(StatusEffects this, entity actor))
-{
-    if(this.m_hidden)
-        return;
-
-    int allItems = STAT(ITEMS);
-
-    // Prevent stuff to show up on mismatch that will be fixed next frame
-    if(!(allItems & IT_SUPERWEAPON) && !autocvar__hud_configure)
-        return;
-
-    if(allItems & IT_UNLIMITED_SUPERWEAPONS)
-        return;
-
-    float currentTime = (autocvar__hud_configure) ? 13 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
-    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_superweapons_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
-}
-
-METHOD(Shield, m_active, bool(StatusEffects this, entity actor))
-{
-    if(autocvar__hud_configure)
-        return true;
-    return SUPER(Shield).m_active(this, actor);
-}
-METHOD(Shield, m_tick, void(StatusEffects this, entity actor))
-{
-    if(this.m_hidden)
-        return;
-
-    float currentTime = (autocvar__hud_configure) ? 27 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
-    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_shield_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
-}
-#endif
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qh b/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qh
deleted file mode 100644 (file)
index 62321d7..0000000
+++ /dev/null
@@ -1,33 +0,0 @@
-#pragma once
-
-#include <common/mutators/mutator/status_effects/all.qh>
-
-CLASS(Powerups, StatusEffects)
-#ifdef GAMEQC
-    ATTRIB(Powerups, m_sound_rm, Sound, SND_POWEROFF);
-#endif
-ENDCLASS(Powerups)
-
-CLASS(Strength, Powerups)
-    ATTRIB(Strength, netname, string, "strength");
-    ATTRIB(Strength, m_name, string, _("Strength"));
-    ATTRIB(Strength, m_icon, string, "strength");
-ENDCLASS(Strength)
-REGISTER_STATUSEFFECT(Strength, NEW(Strength));
-
-CLASS(Shield, Powerups)
-    ATTRIB(Shield, netname, string, "shield");
-    ATTRIB(Shield, m_name, string, _("Shield"));
-    ATTRIB(Shield, m_icon, string, "shield");
-ENDCLASS(Shield)
-REGISTER_STATUSEFFECT(Shield, NEW(Shield));
-
-CLASS(Superweapons, StatusEffects)
-    ATTRIB(Superweapons, netname, string, "superweapons");
-    ATTRIB(Superweapons, m_name, string, _("Superweapons"));
-    ATTRIB(Superweapons, m_icon, string, "superweapons");
-#ifdef GAMEQC
-    ATTRIB(Superweapons, m_sound_rm, Sound, SND_POWEROFF);
-#endif
-ENDCLASS(Superweapons)
-REGISTER_STATUSEFFECT(Superweapons, NEW(Superweapons));
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qc b/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qc
new file mode 100644 (file)
index 0000000..6673de6
--- /dev/null
@@ -0,0 +1,15 @@
+#include "burning.qh"
+
+#ifdef SVQC
+METHOD(SpawnShield, m_remove, void(StatusEffects this, entity actor, int removal_type))
+{
+    actor.effects &= ~(EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(SpawnShield).m_remove(this, actor, removal_type);
+}
+METHOD(SpawnShield, m_tick, void(StatusEffects this, entity actor))
+{
+    if(time >= game_starttime)
+        actor.effects |= (EF_ADDITIVE | EF_FULLBRIGHT);
+    SUPER(SpawnShield).m_tick(this, actor);
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qh b/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qh
new file mode 100644 (file)
index 0000000..14db808
--- /dev/null
@@ -0,0 +1,17 @@
+#pragma once
+
+#include <common/mutators/mutator/status_effects/all.qh>
+
+CLASS(SpawnShield, StatusEffects)
+    ATTRIB(SpawnShield, netname, string, "spawnshield");
+#if 0
+    // NOTE: status effect name and icon disabled as they are not displayed
+    // re-enable if status effects are given a visual element
+    ATTRIB(SpawnShield, m_name, string, _("Spawn Shield"));
+    ATTRIB(SpawnShield, m_icon, string, "shield");
+#endif
+    ATTRIB(SpawnShield, m_color, vector, '0.36 1 0.07');
+    ATTRIB(SpawnShield, m_hidden, bool, true);
+    ATTRIB(SpawnShield, m_lifetime, float, 10);
+ENDCLASS(SpawnShield)
+REGISTER_STATUSEFFECT(SpawnShield, NEW(SpawnShield));
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qc b/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qc
new file mode 100644 (file)
index 0000000..915bb8a
--- /dev/null
@@ -0,0 +1,27 @@
+#include "superweapons.qh"
+
+#ifdef CSQC
+METHOD(Superweapons, m_active, bool(StatusEffects this, entity actor))
+{
+    if(autocvar__hud_configure)
+        return true;
+    return SUPER(Superweapons).m_active(this, actor);
+}
+METHOD(Superweapons, m_tick, void(StatusEffects this, entity actor))
+{
+    if(this.m_hidden)
+        return;
+
+    int allItems = STAT(ITEMS);
+
+    // Prevent stuff to show up on mismatch that will be fixed next frame
+    if(!(allItems & IT_SUPERWEAPON) && !autocvar__hud_configure)
+        return;
+
+    if(allItems & IT_UNLIMITED_SUPERWEAPONS)
+        return;
+
+    float currentTime = (autocvar__hud_configure) ? 13 : bound(0, actor.statuseffect_time[this.m_id] - time, 99);
+    addPowerupItem(this.m_name, this.m_icon, autocvar_hud_progressbar_superweapons_color, currentTime, this.m_lifetime, (actor.statuseffect_flags[this.m_id] & STATUSEFFECT_FLAG_PERSISTENT));
+}
+#endif
diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh b/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh
new file mode 100644 (file)
index 0000000..3926bbe
--- /dev/null
@@ -0,0 +1,13 @@
+#pragma once
+
+#include <common/mutators/mutator/status_effects/all.qh>
+
+CLASS(Superweapons, StatusEffects)
+    ATTRIB(Superweapons, netname, string, "superweapons");
+    ATTRIB(Superweapons, m_name, string, _("Superweapons"));
+    ATTRIB(Superweapons, m_icon, string, "superweapons");
+#ifdef GAMEQC
+    ATTRIB(Superweapons, m_sound_rm, Sound, SND_POWEROFF);
+#endif
+ENDCLASS(Superweapons)
+REGISTER_STATUSEFFECT(Superweapons, NEW(Superweapons));
index df97b0589721b4d321c318d75b8d5abe87b96338..0ec5d4d5c5760ac87fbc44b70b81637bb26a068f 100644 (file)
@@ -50,7 +50,10 @@ METHOD(StatusEffects, m_remove, void(StatusEffects this, entity actor, int remov
 
 MUTATOR_HOOKFUNCTION(status_effects, SV_StartFrame)
 {
+       if(game_stopped)
+               return;
        // TODO: explicitly only loop through entities with a valid statuseffects object
+       // NOTE: due to the way vehicles work currently, this does not function correctly! effect does not tick while inside a vehicle
        IL_EACH(g_damagedbycontents, it.damagedbycontents,
        {
                if (it.move_movetype == MOVETYPE_NOCLIP || !it.statuseffects) continue;
@@ -95,7 +98,7 @@ MUTATOR_HOOKFUNCTION(status_effects, SpectateCopy)
        client.statuseffects = spectatee.statuseffects;
 }
 
-MUTATOR_HOOKFUNCTION(status_effects, PlayerSpawn)
+MUTATOR_HOOKFUNCTION(status_effects, PutClientInServer)
 {
        entity player = M_ARGV(0, entity);
 
@@ -109,8 +112,4 @@ MUTATOR_HOOKFUNCTION(status_effects, PlayerSpawn)
                StatusEffects_clearall(player.statuseffects_store);
                player.statuseffects = NULL;
        }
-
-       // TODO: special hook for when effects are initialized?
-       if(STAT(WEAPONS, player) & WEPSET_SUPERWEAPONS)
-               StatusEffects_apply(STATUSEFFECT_Superweapons, player, time + autocvar_g_balance_superweapons_time, 0);
 }
index edb61b91c253cd41d6b24d1b2f0ec10fe8b5493e..71bb9c8fcc9d7974f8b4ce3ebd6a9b6aa764550a 100644 (file)
@@ -346,6 +346,7 @@ MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand)
 
        if(cmd_name == "followpowerup")
        {
+               // TODO: somehow cheaply loop through all held powerups
                FOREACH_CLIENT(IS_PLAYER(it) && (StatusEffects_active(STATUSEFFECT_Strength, it) || StatusEffects_active(STATUSEFFECT_Shield, it)), { return superspec_Spectate(player, it); });
 
                superspec_msg("", "", player, "No active powerup\n", 1);
index 5b4c33e7bf02052fb48e66e99a4afdbd6f9e6a45..6e7f3adf9b48eb3856a003c9b43e37e39e080c43 100644 (file)
@@ -15,7 +15,7 @@ MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor)
        float armor_take = bound(0, M_ARGV(5, float), GetResource(frag_target, RES_ARMOR));
        float damage_take = (autocvar_g_vampire_use_total_damage) ? health_take + armor_take : health_take;
 
-       if (time >= frag_target.spawnshieldtime && frag_target != frag_attacker
+       if (!StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && frag_target != frag_attacker
                && IS_PLAYER(frag_attacker) && !IS_DEAD(frag_target) && !STAT(FROZEN, frag_target))
        {
                GiveResource(frag_attacker, RES_HEALTH, autocvar_g_vampire_factor * damage_take);
index 59c4c9333d25beebc2ee48bcfb31b62ea1862cda..adffbfce002414ebbac5ab7bffea06cb5d5592d4 100644 (file)
@@ -265,6 +265,7 @@ bool racer_frame(entity this, float dt)
                }
 
 #ifdef SVQC
+               // TODO: move these to the client side where they belong
                // NOTE: reusing .invincible_finished here as delay counter for the smoke effect
                if(vehic.invincible_finished < time)
                {
index 8ada68244d4de7fd70caca90c82b672296c3a579..2337e6273370acf237605d172c0433d7ee965a8f 100644 (file)
@@ -394,7 +394,7 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor, .entity weaponenti
         });
         float desirabledamage;
         desirabledamage = enemydamage;
-        if(StatusEffects_active(STATUSEFFECT_Shield, actor) && time > actor.spawnshieldtime)
+        if(StatusEffects_active(STATUSEFFECT_Shield, actor) && !StatusEffects_active(STATUSEFFECT_SpawnShield, actor))
             desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
         if(teamplay && actor.team)
             desirabledamage = desirabledamage - teamdamage;
index 7f93e8fde376402c653684e1a91a93509a99a89d..f25859e0532983b1b1fbf7c9605f8f902fcde27d 100644 (file)
@@ -379,7 +379,7 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor, .entity weaponentit
 
         float desirabledamage;
         desirabledamage = enemydamage;
-        if(StatusEffects_active(STATUSEFFECT_Shield, actor) && time > actor.spawnshieldtime)
+        if(StatusEffects_active(STATUSEFFECT_Shield, actor) && !StatusEffects_active(STATUSEFFECT_SpawnShield, actor))
             desirabledamage = desirabledamage - selfdamage * autocvar_g_balance_selfdamagepercent;
         if(teamplay && actor.team)
             desirabledamage = desirabledamage - teamdamage;
index fd33dacded56547632bda86e12359ae6187df06f..b161d1054d8e1f1b6d608d173196072a8aca11e8 100644 (file)
@@ -306,7 +306,8 @@ void W_Porto_Attack(Weapon thiswep, entity actor, .entity weaponentity, float ty
        setthink(gren, W_Porto_Think);
        settouch(gren, W_Porto_Touch);
 
-       if(actor.items & ITEM_Strength.m_itemid)
+       // TODO: handle as mutator effect
+       if(StatusEffects_active(STATUSEFFECT_Strength, actor))
                W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed) * autocvar_g_balance_powerup_strength_force, 0);
        else
                W_SetupProjVelocity_Basic(gren, WEP_CVAR_BOTH(porto, (type <= 0), speed), 0);
index db0d83ead7d2ff2ab26db36a57b03d373633f81b..5d35089e5ab1daf83c1441691f5a3538d63c4555 100644 (file)
@@ -149,6 +149,7 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, height) \
                FIELD_SCALAR(fld, impulse) \
                FIELD_SCALAR(fld, invincible_finished) \
+               FIELD_SCALAR(fld, invisibility_finished) \
                FIELD_SCALAR(fld, item_pickupsound) \
                FIELD_SCALAR(fld, killtarget) \
                FIELD_SCALAR(fld, lerpfrac) \
@@ -181,6 +182,7 @@ noref bool require_spawnfunc_prefix;
                FIELD_SCALAR(fld, phase) \
                FIELD_SCALAR(fld, platmovetype) \
                FIELD_SCALAR(fld, race_place) \
+               FIELD_SCALAR(fld, speed_finished) \
                FIELD_SCALAR(fld, strength_finished) \
                FIELD_SCALAR(fld, radius) \
                FIELD_SCALAR(fld, respawntimestart) \
index b5198c08d1bc5b7db57e1d293ec5eb7d80cbf76e..66658a3901d1dd7837e5883675a328c26e35b927 100644 (file)
@@ -585,6 +585,7 @@ bool WarpZoneLib_BadEntity(entity e)
                case "spawnfunc":
                case "weaponchild":
                case "chatbubbleentity":
+               case "buff_model":
                //case "net_linked": // actually some real entities are linked without classname, fail
                case "":
                        return true;
index fbe14c90f74e5d9754361ba65fe07d04051f868c..689f0c179ffcfc3ea65155ec04153bddb6ce1716 100644 (file)
@@ -7,6 +7,7 @@
 #include <common/mapobjects/teleporters.qh>
 #include <common/mapobjects/trigger/hurt.qh>
 #include <common/mapobjects/trigger/jumppads.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/net_linked.qh>
 #include <common/physics/player.qh>
index c000bbdacfe0363b4f0d0dbbe3a17f1b120496f6..87f7bc0d00f0584b2c70f356fb18eea830b6a926 100644 (file)
@@ -1,6 +1,7 @@
 #include "roles.qh"
 
 #include <common/stats.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/weapons/_all.qh>
 #include <server/bot/default/bot.qh>
index 20ee5c2518589ced5b7d7ed1441f63e832b6b886..a9758146f5dddfaca618b7ddc6b6a624fa7da1ef 100644 (file)
@@ -591,9 +591,13 @@ void PutPlayerInServer(entity this)
 
        PS(this).dual_weapons = '0 0 0';
 
+       if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)
+               StatusEffects_apply(STATUSEFFECT_Superweapons, this, time + autocvar_g_balance_superweapons_time, 0);
+
        this.items = start_items;
 
-       this.spawnshieldtime = time + autocvar_g_spawnshieldtime;
+       float shieldtime = time + autocvar_g_spawnshieldtime;
+
        this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn;
        this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn;
        this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn;
@@ -601,12 +605,14 @@ void PutPlayerInServer(entity this)
        if (!sv_ready_restart_after_countdown && time < game_starttime)
        {
                float f = game_starttime - time;
-               this.spawnshieldtime += f;
+               shieldtime += f;
                this.pauserotarmor_finished += f;
                this.pauserothealth_finished += f;
                this.pauseregen_finished += f;
        }
 
+       StatusEffects_apply(STATUSEFFECT_SpawnShield, this, shieldtime, 0);
+
        this.damageforcescale = autocvar_g_player_damageforcescale;
        this.death_time = 0;
        this.respawn_flags = 0;
@@ -1038,10 +1044,6 @@ string getwelcomemessage(entity this)
                modifications = strcat(modifications, ", Weapons stay");
        if(autocvar_g_jetpack)
                modifications = strcat(modifications, ", Jet pack");
-       if(autocvar_g_powerups == 0)
-               modifications = strcat(modifications, ", No powerups");
-       if(autocvar_g_powerups > 0)
-               modifications = strcat(modifications, ", Powerups");
        modifications = substring(modifications, 2, strlen(modifications) - 2);
 
        string versionmessage = GetClientVersionMessage(this);
@@ -1444,14 +1446,12 @@ void play_countdown(entity this, float finished, Sound samp)
 
 void player_powerups_remove_all(entity this)
 {
-       if (this.items & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON))
+       if (this.items & IT_SUPERWEAPON)
        {
                // don't play the poweroff sound when the game restarts or the player disconnects
                if (time > game_starttime + 1 && IS_CLIENT(this))
                        sound(this, CH_INFO, SND_POWEROFF, VOL_BASE, ATTEN_NORM);
                stopsound(this, CH_TRIGGER_SINGLE); // get rid of the pickup sound
-               this.items &= ~ITEM_Strength.m_itemid;
-               this.items &= ~ITEM_Shield.m_itemid;
                this.items -= (this.items & IT_SUPERWEAPON);
        }
 }
@@ -1463,7 +1463,7 @@ void player_powerups(entity this)
        else
                this.modelflags &= ~MF_ROCKET;
 
-       this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_NODEPTHTEST);
+       this.effects &= ~EF_NODEPTHTEST;
 
        if (IS_DEAD(this))
                player_powerups_remove_all(this);
@@ -1476,48 +1476,7 @@ void player_powerups(entity this)
 
        if (!MUTATOR_IS_ENABLED(mutator_instagib))
        {
-               if (this.items & ITEM_Strength.m_itemid)
-               {
-                       play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Strength, this), SND_POWEROFF);
-                       this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > StatusEffects_gettime(STATUSEFFECT_Strength, this))
-                       {
-                               this.items = this.items - (this.items & ITEM_Strength.m_itemid);
-                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH);
-                       }
-               }
-               else
-               {
-                       if (time < StatusEffects_gettime(STATUSEFFECT_Strength, this))
-                       {
-                               this.items = this.items | ITEM_Strength.m_itemid;
-                               if(!g_cts)
-                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH);
-                       }
-               }
-               if (this.items & ITEM_Shield.m_itemid)
-               {
-                       play_countdown(this, StatusEffects_gettime(STATUSEFFECT_Shield, this), SND_POWEROFF);
-                       this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT);
-                       if (time > StatusEffects_gettime(STATUSEFFECT_Shield, this))
-                       {
-                               this.items = this.items - (this.items & ITEM_Shield.m_itemid);
-                               //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD);
-                       }
-               }
-               else
-               {
-                       if (time < StatusEffects_gettime(STATUSEFFECT_Shield, this))
-                       {
-                               this.items = this.items | ITEM_Shield.m_itemid;
-                               if(!g_cts)
-                                       Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname);
-                               Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD);
-                       }
-               }
+               // NOTE: superweapons are a special case and as such are handled here instead of the status effects system
                if (this.items & IT_SUPERWEAPON)
                {
                        if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS))
@@ -1574,10 +1533,6 @@ void player_powerups(entity this)
        if(autocvar_g_fullbrightplayers)
                this.effects = this.effects | EF_FULLBRIGHT;
 
-       if (time >= game_starttime)
-       if (time < this.spawnshieldtime)
-               this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT);
-
        MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev);
 }
 
index 31d65f948608c1ba8e394b11970f73bd3c4f1412..3199d155a9c853c17d82cf67b5c26c3d7b467fe1 100644 (file)
@@ -275,9 +275,6 @@ CLASS(Player, Client)
     ATTRIB(Player, dual_weapons, vector, this.dual_weapons); // TODO: actually WepSet!
     ATTRIB(Player, itemkeys, int, this.itemkeys);
     ATTRIB(Player, ballistics_density, float, this.ballistics_density);
-    ATTRIB(Player, prevstrengthsound, float, this.prevstrengthsound);
-    ATTRIB(Player, prevstrengthsoundattempt, float, this.prevstrengthsoundattempt);
-    ATTRIB(Player, buff_shield, float, this.buff_shield);
 
     INIT(Player) {
         this.classname = STR_PLAYER;
index ae9dbd35760faf0e834f184d5f64d16fcac65d82..121df69265166023ff4188cf1cd6810183d184aa 100644 (file)
@@ -6,6 +6,7 @@
 #include <common/mapobjects/triggers.qh>
 #include <common/mutators/mutator/buffs/buffs.qh>
 #include <common/mutators/mutator/buffs/sv_buffs.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/notifications/all.qh>
 #include <common/stats.qh>
@@ -132,8 +133,10 @@ void target_init_use(entity this, entity actor, entity trigger)
 
        if (!(this.spawnflags & 8))
        {
-               StatusEffects_remove(STATUSEFFECT_Strength, actor, STATUSEFFECT_REMOVE_NORMAL);
-               StatusEffects_remove(STATUSEFFECT_Shield, actor, STATUSEFFECT_REMOVE_NORMAL);
+               FOREACH(StatusEffect, it.instanceOfPowerups,
+               {
+                       it.m_remove(it, actor, STATUSEFFECT_REMOVE_NORMAL);
+               });
                entity heldbuff = buff_FirstFromFlags(actor);
                if(heldbuff) // TODO: make a dropbuffs function to handle this
                {
@@ -251,12 +254,12 @@ spawnfunc(target_fragsFilter)
 
 //spawnfunc(item_flight)       /* handled by buffs mutator */
 //spawnfunc(item_doubler)        /* handled by buffs mutator */
-//spawnfunc(item_haste)        /* handled by buffs mutator */
+//spawnfunc(item_haste)        /* handled by powerups mutator */
 //spawnfunc(item_health)       /* handled in t_quake.qc */
 //spawnfunc(item_health_large) /* handled in items.qc */
 //spawnfunc(item_health_small) /* handled in items.qc */
 //spawnfunc(item_health_mega)  /* handled in items.qc */
-//spawnfunc(item_invis)        /* handled by buffs mutator */
+//spawnfunc(item_invis)        /* handled by powerups mutator */
 //spawnfunc(item_regen)        /* handled by buffs mutator */
 
 // CTF spawnfuncs handled in mutators/gamemode_ctf.qc now
index 8a559332da191c00f71ae203f80885f8a089e9f9..2a3ad1d64fe0ee3d791adf37b76dbdd068ddafa4 100644 (file)
@@ -1,12 +1,12 @@
 #include "wop.qh"
 
 #include <common/stats.qh>
-#include <common/weapons/_all.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/weapons/_all.qh>
 #include <server/items/spawning.qh>
 
-spawnfunc(item_haste);
-spawnfunc(item_invis);
+spawnfunc(item_speed);
+spawnfunc(item_invisibility);
 
 //***********************
 //WORLD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
@@ -35,8 +35,8 @@ SPAWNFUNC_ITEM(ammo_imperius, ITEM_Cells)
 
 SPAWNFUNC_ITEM(item_padpower, ITEM_Strength)
 SPAWNFUNC_ITEM(item_climber, ITEM_Shield)
-spawnfunc(item_speedy)         { spawnfunc_item_haste(this);                   }
-spawnfunc(item_visionless)     { spawnfunc_item_invis(this);                   }
+spawnfunc(item_speedy)         { spawnfunc_item_speed(this);                   }
+spawnfunc(item_visionless)     { spawnfunc_item_invisibility(this);                    }
 SPAWNFUNC_ITEM(item_armor_padshield, ITEM_ArmorMega)
 
 SPAWNFUNC_ITEM(holdable_floater, ITEM_Jetpack)
index 7ce674f5187b29e000a77150132c1035c90d6dcc..d480c229b9b244024d6efae2e81eccb30657657e 100644 (file)
@@ -95,10 +95,6 @@ string AppendItemcodes(string s, entity player)
                if(w != 0 || slot == 0)
                        s = strcat(s, ftos(w));
        }
-       if(StatusEffects_active(STATUSEFFECT_Strength, player))
-               s = strcat(s, "S");
-       if(StatusEffects_active(STATUSEFFECT_Shield, player))
-               s = strcat(s, "I");
        if(PHYS_INPUT_BUTTON_CHAT(player))
                s = strcat(s, "T");
        // TODO: include these codes as a flag on the item itself
@@ -618,7 +614,7 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de
                        SetResourceExplicit(targ, RES_ARMOR, 0);
                        SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1
                }
-               targ.spawnshieldtime = 0;
+               StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR);
                targ.flags -= targ.flags & FL_GODMODE;
                damage = 100000;
        }
@@ -767,34 +763,6 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de
                        }
                }
 
-               if(!MUTATOR_IS_ENABLED(mutator_instagib))
-               {
-                       // apply strength multiplier
-                       if (attacker.items & ITEM_Strength.m_itemid)
-                       {
-                               if(targ == attacker)
-                               {
-                                       damage = damage * autocvar_g_balance_powerup_strength_selfdamage;
-                                       force = force * autocvar_g_balance_powerup_strength_selfforce;
-                               }
-                               else
-                               {
-                                       damage = damage * autocvar_g_balance_powerup_strength_damage;
-                                       force = force * autocvar_g_balance_powerup_strength_force;
-                               }
-                       }
-
-                       // apply invincibility multiplier
-                       if (targ.items & ITEM_Shield.m_itemid)
-                       {
-                               damage = damage * autocvar_g_balance_powerup_invincible_takedamage;
-                               if (targ != attacker)
-                               {
-                                       force = force * autocvar_g_balance_powerup_invincible_takeforce;
-                               }
-                       }
-               }
-
                if (targ == attacker)
                        damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
 
@@ -856,7 +824,7 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de
        // apply push
        if (targ.damageforcescale)
        if (force)
-       if (!IS_PLAYER(targ) || time >= targ.spawnshieldtime || targ == attacker)
+       if (!IS_PLAYER(targ) || !StatusEffects_active(STATUSEFFECT_SpawnShield, targ) || targ == attacker)
        {
                vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor);
                if(targ.move_movetype == MOVETYPE_PHYSICS)
index a87214072efa20179b5cf24b5750370ecba826e8..610c864b121e30b52e449e99a4eb3ab4a1c90505 100644 (file)
@@ -9,6 +9,7 @@
 #include <common/monsters/_mod.qh>
 #include <common/mutators/mutator/buffs/buffs.qh>
 #include <common/mutators/mutator/buffs/sv_buffs.qh>
+#include <common/mutators/mutator/powerups/_mod.qh>
 #include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/notifications/all.qh>
 #include <common/util.qh>
@@ -557,6 +558,16 @@ bool Item_GiveTo(entity item, entity player)
                pickedup = true;
                StatusEffects_apply(STATUSEFFECT_Shield, player, max(StatusEffects_gettime(STATUSEFFECT_Shield, player), time) + item.invincible_finished, 0);
        }
+       if (item.speed_finished)
+       {
+               pickedup = true;
+               StatusEffects_apply(STATUSEFFECT_Speed, player, max(StatusEffects_gettime(STATUSEFFECT_Speed, player), time) + item.speed_finished, 0);
+       }
+       if (item.invisibility_finished)
+       {
+               pickedup = true;
+               StatusEffects_apply(STATUSEFFECT_Invisibility, player, max(StatusEffects_gettime(STATUSEFFECT_Invisibility, player), time) + item.invisibility_finished, 0);
+       }
        if (item.superweapons_finished)
        {
                pickedup = true;
@@ -628,6 +639,8 @@ void Item_Touch(entity this, entity toucher)
        {
                this.strength_finished = max(0, this.strength_finished - time);
                this.invincible_finished = max(0, this.invincible_finished - time);
+               this.speed_finished = max(0, this.speed_finished - time);
+               this.invisibility_finished = max(0, this.invisibility_finished - time);
                this.superweapons_finished = max(0, this.superweapons_finished - time);
        }
        bool gave = ITEM_HANDLE(Pickup, this.itemdef, this, toucher);
@@ -638,6 +651,8 @@ void Item_Touch(entity this, entity toucher)
                        // undo what we did above
                        this.strength_finished += time;
                        this.invincible_finished += time;
+                       this.speed_finished += time;
+                       this.invisibility_finished += time;
                        this.superweapons_finished += time;
                }
                return;
@@ -1203,6 +1218,10 @@ spawnfunc(target_items)
                this.strength_finished = autocvar_g_balance_powerup_strength_time;
        if(!this.invincible_finished)
                this.invincible_finished = autocvar_g_balance_powerup_invincible_time;
+       if(!this.speed_finished)
+               this.speed_finished = autocvar_g_balance_powerup_speed_time;
+       if(!this.invisibility_finished)
+               this.invisibility_finished = autocvar_g_balance_powerup_invisibility_time;
        if(!this.superweapons_finished)
                this.superweapons_finished = autocvar_g_balance_superweapons_time;
 
@@ -1222,6 +1241,8 @@ spawnfunc(target_items)
                        else if(argv(j) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS;
                        else if(argv(j) == "strength")               this.items |= ITEM_Strength.m_itemid;
                        else if(argv(j) == "invincible")             this.items |= ITEM_Shield.m_itemid;
+                       else if(argv(j) == "speed")                  this.items |= ITEM_Speed.m_itemid;
+                       else if(argv(j) == "invisibility")           this.items |= ITEM_Invisibility.m_itemid;
                        else if(argv(j) == "superweapons")           this.items |= IT_SUPERWEAPON;
                        else if(argv(j) == "jetpack")                this.items |= ITEM_Jetpack.m_itemid;
                        else if(argv(j) == "fuel_regen")             this.items |= ITEM_JetpackRegen.m_itemid;
@@ -1283,6 +1304,8 @@ spawnfunc(target_items)
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & IT_UNLIMITED_SUPERWEAPONS), "unlimited_superweapons");
                str = sprintf("%s %s%d %s", str, valueprefix, this.strength_finished * boolean(this.items & ITEM_Strength.m_itemid), "strength");
                str = sprintf("%s %s%d %s", str, valueprefix, this.invincible_finished * boolean(this.items & ITEM_Shield.m_itemid), "invincible");
+               str = sprintf("%s %s%d %s", str, valueprefix, this.invisibility_finished * boolean(this.items & ITEM_Invisibility.m_itemid), "invisibility");
+               str = sprintf("%s %s%d %s", str, valueprefix, this.speed_finished * boolean(this.items & ITEM_Speed.m_itemid), "speed");
                str = sprintf("%s %s%d %s", str, valueprefix, this.superweapons_finished * boolean(this.items & IT_SUPERWEAPON), "superweapons");
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & ITEM_Jetpack.m_itemid), "jetpack");
                str = sprintf("%s %s%d %s", str, itemprefix, boolean(this.items & ITEM_JetpackRegen.m_itemid), "fuel_regen");
@@ -1365,7 +1388,8 @@ bool GiveBuff(entity e, Buff thebuff, int op, int val)
        }
        if(new_buff_time <= 0)
        {
-               StatusEffects_remove(thebuff, e, STATUSEFFECT_REMOVE_TIMEOUT);
+               if(had_buff) // only trigger removal mechanics if there is an effect to remove!
+                       StatusEffects_remove(thebuff, e, STATUSEFFECT_REMOVE_NORMAL);
        }
        else
        {
@@ -1439,7 +1463,10 @@ bool GiveStatusEffect(entity e, StatusEffects this, int op, float val)
                        break;
        }
        if(new_eff_time <= 0)
-               StatusEffects_remove(this, e, STATUSEFFECT_REMOVE_TIMEOUT);
+       {
+               if(had_eff) // only trigger removal mechanics if there is an effect to remove!
+                       StatusEffects_remove(this, e, STATUSEFFECT_REMOVE_NORMAL);
+       }
        else
                StatusEffects_apply(this, e, new_eff_time, 0);
        bool have_eff = StatusEffects_active(this, e);
@@ -1481,6 +1508,8 @@ float GiveItems(entity e, float beginarg, float endarg)
        PREGIVE_WEAPONS(e);
        PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength);
        PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield);
+       PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Speed);
+       PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Invisibility);
        //PREGIVE_STATUSEFFECT(e, STATUSEFFECT_Superweapons);
        PREGIVE_RESOURCE(e, RES_BULLETS);
        PREGIVE_RESOURCE(e, RES_CELLS);
@@ -1520,9 +1549,7 @@ float GiveItems(entity e, float beginarg, float endarg)
                                continue;
                        case "ALL":
                                got += GiveBit(e, items, ITEM_JetpackRegen.m_itemid, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Strength, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Shield, op, val);
-                               got += GiveStatusEffect(e, STATUSEFFECT_Superweapons, op, val);
+                               FOREACH(StatusEffect, it.instanceOfPowerups, got += GiveStatusEffect(e, it, op, val));
                                got += GiveBit(e, items, IT_UNLIMITED_AMMO | IT_UNLIMITED_SUPERWEAPONS, op, val);
                        case "all":
                                got += GiveBit(e, items, ITEM_Jetpack.m_itemid, op, val);
@@ -1560,8 +1587,15 @@ float GiveItems(entity e, float beginarg, float endarg)
                                got += GiveStatusEffect(e, STATUSEFFECT_Strength, op, val);
                                break;
                        case "invincible":
+                       case "shield":
                                got += GiveStatusEffect(e, STATUSEFFECT_Shield, op, val);
                                break;
+                       case "speed":
+                               got += GiveStatusEffect(e, STATUSEFFECT_Speed, op, val);
+                               break;
+                       case "invisibility":
+                               got += GiveStatusEffect(e, STATUSEFFECT_Invisibility, op, val);
+                               break;
                        case "superweapons":
                                got += GiveStatusEffect(e, STATUSEFFECT_Superweapons, op, val);
                                break;
@@ -1616,8 +1650,10 @@ float GiveItems(entity e, float beginarg, float endarg)
                        if(STAT(WEAPONS, e) & (it.m_wepset))
                                it.wr_init(it);
        });
-       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength, 1, SND_POWERUP, SND_POWEROFF);
-       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield, 1, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Strength, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Shield, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Speed, SND_POWERUP, SND_POWEROFF);
+       POSTGIVE_STATUSEFFECT(e, STATUSEFFECT_Invisibility, SND_POWERUP, SND_POWEROFF);
        POSTGIVE_RESOURCE(e, RES_BULLETS, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_RESOURCE(e, RES_CELLS, 0, SND_ITEMPICKUP, SND_Null);
        POSTGIVE_RESOURCE(e, RES_PLASMA, 0, SND_ITEMPICKUP, SND_Null);
index 39009fe90f3e62b8c7b0251f302ff2845b583d58..ff2d138437658ed1e941f2c06dc5f0b10eb15a33 100644 (file)
@@ -4,7 +4,6 @@
 
 float autocvar_g_balance_superweapons_time;
 bool autocvar_g_fullbrightitems;
-int autocvar_g_powerups;
 float autocvar_g_items_mindist;
 float autocvar_g_items_maxdist;
 int autocvar_g_pickup_items;
@@ -109,11 +108,11 @@ spawnfunc(target_items);
 
 #define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = STAT(WEAPONS, e)
 #define PREGIVE(e,f) float save_##f; save_##f = (e).f
-#define PREGIVE_STATUSEFFECT(e,f) float save_##f = StatusEffects_gettime((f), (e))
+#define PREGIVE_STATUSEFFECT(e,f) bool save_##f = StatusEffects_active(f, (e))
 #define PREGIVE_RESOURCE(e,f) float save_##f = GetResource((e), (f))
 #define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(STAT(WEAPONS, e) & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr)
 #define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr)
-#define POSTGIVE_STATUSEFFECT(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, StatusEffects_gettime((f), (e)), t, snd_incr, snd_decr)
+#define POSTGIVE_STATUSEFFECT(e,f,snd_incr,snd_decr) GiveSound((e), save_##f, StatusEffects_active(f, (e)), 0, snd_incr, snd_decr)
 #define POSTGIVE_RESOURCE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, GetResource((e), (f)), t, snd_incr, snd_decr)
 #define POSTGIVE_RES_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e),save_##f,GetResource((e),(f)),rotfield,rottime,regenfield,regentime);GiveSound((e),save_##f,GetResource((e),(f)),t,snd_incr,snd_decr)
 #define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr)
index c4521e824f3a7a9d57ce58df35494fcf3dbb8fa2..f33fc4598e0f785f04a5138b27ebcab5b835125b 100644 (file)
@@ -1250,3 +1250,9 @@ MUTATOR_HOOKABLE(LogDeath_AppendItemCodes, EV_LogDeath_AppendItemCodes);
     /**/                            o(bool, MUTATOR_ARGV_0_bool) \
     /**/
 MUTATOR_HOOKABLE(AllowRocketJumping, EV_AllowRocketJumping);
+
+/** Called when weapons are performing their attack, useful for applying bonus attack sounds */
+#define EV_W_PlayStrengthSound(i, o) \
+    /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+    /**/
+MUTATOR_HOOKABLE(W_PlayStrengthSound, EV_W_PlayStrengthSound);
index 916aa8a286355bcb187e38bbf8ad4acafda103b7..ad3ba94e4a21d18c9aa7c293a1c68cfa9a04edeb 100644 (file)
@@ -12,6 +12,7 @@
 #include <common/mapobjects/subs.qh>
 #include <common/mapobjects/teleporters.qh>
 #include <common/minigames/sv_minigames.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/mutators/mutator/waypoints/waypointsprites.qh>
 #include <common/physics/player.qh>
 #include <common/playerstats.qh>
@@ -248,7 +249,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
                        if (!ITEM_DAMAGE_NEEDKILL(deathtype))
                                damage = 0;
                }
-               else if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
+               else if (StatusEffects_active(STATUSEFFECT_SpawnShield, this) && autocvar_g_spawnshield_blockdamage < 1)
                        damage *= 1 - bound(0, autocvar_g_spawnshield_blockdamage, 1);
 
                if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears
@@ -333,7 +334,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage,
        if (take > 100)
                Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker);
 
-       if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1)
+       if (!StatusEffects_active(STATUSEFFECT_SpawnShield, this) || autocvar_g_spawnshield_blockdamage < 1)
        {
                if (!(this.flags & FL_GODMODE))
                {
index 3f8d2c28546391e58f42b5cc73bb16759b0c7e82..a00eb120ee8d9fa213e8e8fea68f4241220642bb 100644 (file)
@@ -43,16 +43,7 @@ void W_GiveWeapon(entity e, int wep)
 
 void W_PlayStrengthSound(entity player)
 {
-       entity store = IS_PLAYER(player) ? PS(player) : player; // because non-player entities can fire, but can they have items? TODO
-
-       if((player.items & ITEM_Strength.m_itemid)
-               && ((time > store.prevstrengthsound + autocvar_sv_strengthsound_antispam_time) // prevent insane sound spam
-               || (time > store.prevstrengthsoundattempt + autocvar_sv_strengthsound_antispam_refire_threshold)))
-               {
-                       sound(player, CH_TRIGGER, SND_STRENGTH_FIRE, VOL_BASE, ATTEN_NORM);
-                       store.prevstrengthsound = time;
-               }
-               store.prevstrengthsoundattempt = time;
+       MUTATOR_CALLHOOK(W_PlayStrengthSound, player);
 }
 
 float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception)
index 1bf9261f79458f0d57f4d766f16ccf61bede7856..779226be3dfc98ded7bb21b56042e9ac54265b40 100644 (file)
@@ -7,8 +7,6 @@ float autocvar_sv_strengthsound_antispam_time;
 
 bool W_DualWielding(entity player);
 void W_GiveWeapon (entity e, float wep);
-.float prevstrengthsound;
-.float prevstrengthsoundattempt;
 void W_PlayStrengthSound(entity player);
 float W_CheckProjectileDamage(entity inflictor, entity projowner, int deathtype, float exception);
 void W_PrepareExplosionByDamage(entity this, entity attacker, void(entity this) explode);
index 8566b5cdb408615271369f64c2978eafb7d3b1d5..200e6a3bd24c5ca66efbf05141750cebc5c1796e 100644 (file)
@@ -5,6 +5,7 @@
 #include <common/items/_mod.qh>
 #include <common/mapobjects/platforms.qh>
 #include <common/monsters/_mod.qh>
+#include <common/mutators/mutator/status_effects/_mod.qh>
 #include <common/net_linked.qh>
 #include <common/notifications/all.qh>
 #include <common/state.qh>
@@ -301,7 +302,8 @@ void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary,
        if (this == NULL) return;
        this.state = WS_INUSE;
 
-       actor.spawnshieldtime = min(actor.spawnshieldtime, time);  // kill spawn shield when you fire
+       if(StatusEffects_active(STATUSEFFECT_SpawnShield, actor)) // given this is performed often, perform a lighter check first
+               StatusEffects_remove(STATUSEFFECT_SpawnShield, actor, STATUSEFFECT_REMOVE_CLEAR); // kill spawn shield when you fire
 
        // if the weapon hasn't been firing continuously, reset the timer
        if (attacktime >= 0)
index 99e30265f49e9eb24217dfd6b150154afa7d65d8..2e494450edcfd422f9647caabe982d8e9271468b 100644 (file)
@@ -868,12 +868,6 @@ spawnfunc(worldspawn)
                if(autocvar_g_norecoil)
                        s = strcat(s, ":norecoil");
 
-               // TODO to mutator system
-               if(autocvar_g_powerups == 0)
-                       s = strcat(s, ":no_powerups");
-               if(autocvar_g_powerups > 0)
-                       s = strcat(s, ":powerups");
-
                GameLogEcho(s);
                GameLogEcho(":gameinfo:end");
        }
index 683a96e02cf9ec442f1a48f3f3658d9b1e8b57e0..fccb89576e053b5945ffbfbe0030980e902cca62 100644 (file)
@@ -12,7 +12,7 @@ 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
+g_powerups 0 // no powerups until the speed powerup has its own model
 g_start_delay 3
 g_use_ammunition 0
 g_weapon_stay 1
@@ -34,22 +34,17 @@ g_buffs_bash 0
 g_buffs_disability 0
 g_buffs_vampire 0
 g_buffs_jump 0
-g_buffs_invisible 0
 g_buffs_inferno 0
 g_buffs_swapper 0
 g_buffs_magnet 0
 g_buffs_luck 0
 g_buffs_flight 0
 
-// speed buff (q3 haste replacement)
+// speed powerup (q3 haste replacement)
 g_movement_highspeed_q3_compat 1
-g_buffs_speed 1
-g_buffs_speed_time 30
-g_buffs_speed_speed 1.3  // q3 haste lasts 30 seconds
-g_buffs_speed_rate 0.7692307692   // 1/1.3
-g_buffs_speed_weaponspeed 1  // do not increase projectile speed
-g_buffs_speed_damage_take 1
-g_buffs_speed_regen 1.2
+g_balance_powerup_speed_time 30
+g_balance_powerup_speed_highspeed 1.3  // q3 haste lasts 30 seconds
+g_balance_powerup_speed_attackrate 0.7692307692   // 1/1.3
 
 // game mode settings
 g_cts_finish_kill_delay 2