From: Mario Date: Sat, 29 May 2021 19:55:48 +0000 (+1000) Subject: Numerous enhancements to the new status effects system, split powerups into a dedicat... X-Git-Tag: xonotic-v0.8.5~393^2~8 X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=0ce97ac6e23a4bfa907cd5a25990b4abe90656cf;p=xonotic%2Fxonotic-data.pk3dir.git Numerous enhancements to the new status effects system, split powerups into a dedicated mutator --- diff --git a/balance-mario.cfg b/balance-mario.cfg index 3ed931eb6..eecfccb9c 100644 --- a/balance-mario.cfg +++ b/balance-mario.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/balance-nexuiz25.cfg b/balance-nexuiz25.cfg index b89f7caa5..69b8dd7f7 100644 --- a/balance-nexuiz25.cfg +++ b/balance-nexuiz25.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 4 set g_balance_powerup_strength_time 30 diff --git a/balance-overkill.cfg b/balance-overkill.cfg index e3231dd0c..ca770845f 100644 --- a/balance-overkill.cfg +++ b/balance-overkill.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/balance-samual.cfg b/balance-samual.cfg index 3481e191a..f6023179a 100644 --- a/balance-samual.cfg +++ b/balance-samual.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/balance-xdf.cfg b/balance-xdf.cfg index bb4c0252b..6afac0ba3 100644 --- a/balance-xdf.cfg +++ b/balance-xdf.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/balance-xonotic.cfg b/balance-xonotic.cfg index 823b42a74..a2b53dcc6 100644 --- a/balance-xonotic.cfg +++ b/balance-xonotic.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/balance-xpm.cfg b/balance-xpm.cfg index 8173e38d3..16a5c1aef 100644 --- a/balance-xpm.cfg +++ b/balance-xpm.cfg @@ -219,6 +219,12 @@ 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_speed_weaponspeed 1.5 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 set g_balance_powerup_strength_time 30 diff --git a/mutators.cfg b/mutators.cfg index 5420c350f..ac965a2e1 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -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" diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 9eeb6246d..ae2ac2b4d 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -21,6 +21,7 @@ #include #include #include +#include #include #include #include @@ -1300,9 +1301,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. diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc index d9b1bb985..b8fe21a01 100644 --- a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc +++ b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc @@ -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) diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc index 904d26e8f..2a79d3ef0 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc @@ -3,6 +3,7 @@ #include #include #include +#include #include #include #include @@ -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); diff --git a/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc index 1d872bb7f..7b47ecec6 100644 --- a/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc +++ b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc @@ -4,6 +4,7 @@ #include #include #include +#include #include #include @@ -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) { diff --git a/qcsrc/common/items/item.qh b/qcsrc/common/items/item.qh index 8f651ad04..9e322c811 100644 --- a/qcsrc/common/items/item.qh +++ b/qcsrc/common/items/item.qh @@ -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 diff --git a/qcsrc/common/items/item/_mod.inc b/qcsrc/common/items/item/_mod.inc index 254011643..4d12af0f0 100644 --- a/qcsrc/common/items/item/_mod.inc +++ b/qcsrc/common/items/item/_mod.inc @@ -4,4 +4,3 @@ #include #include #include -#include diff --git a/qcsrc/common/items/item/_mod.qh b/qcsrc/common/items/item/_mod.qh index 4539e0f35..038530b27 100644 --- a/qcsrc/common/items/item/_mod.qh +++ b/qcsrc/common/items/item/_mod.qh @@ -4,4 +4,3 @@ #include #include #include -#include diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh index 962f16170..e9c5627c8 100644 --- a/qcsrc/common/items/item/jetpack.qh +++ b/qcsrc/common/items/item/jetpack.qh @@ -5,7 +5,7 @@ #endif #include "ammo.qh" -#include "powerup.qh" +#include #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 index 7c7405b75..000000000 --- a/qcsrc/common/items/item/powerup.qc +++ /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 index 76a17af25..000000000 --- a/qcsrc/common/items/item/powerup.qh +++ /dev/null @@ -1,101 +0,0 @@ -#pragma once - -#ifdef SVQC - // For FL_POWERUP - #include -#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) diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 00aa9e6ac..74a041b15 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -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; diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index c67d90664..789b7afe4 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -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; diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 73518c1f9..3f3e65d4a 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -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); diff --git a/qcsrc/common/monsters/monster/spider.qh b/qcsrc/common/monsters/monster/spider.qh index 679b14049..ac4770517 100644 --- a/qcsrc/common/monsters/monster/spider.qh +++ b/qcsrc/common/monsters/monster/spider.qh @@ -28,3 +28,15 @@ CLASS(SpiderAttack, PortoLaunch) /* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); ENDCLASS(SpiderAttack) REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); + +#include + +CLASS(Webbed, StatusEffects) + ATTRIB(Webbed, netname, string, "webbed"); + ATTRIB(Webbed, m_name, string, _("Webbed")); + ATTRIB(Webbed, m_icon, string, "buff_disability"); + 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)); diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index 70981fa00..f99474e9e 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -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 diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index b7e4d20a3..e3eaf6ecd 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/qcsrc/common/mutators/mutator/_mod.inc b/qcsrc/common/mutators/mutator/_mod.inc index 8db241e76..c463c429f 100644 --- a/qcsrc/common/mutators/mutator/_mod.inc +++ b/qcsrc/common/mutators/mutator/_mod.inc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/mutators/mutator/_mod.qh b/qcsrc/common/mutators/mutator/_mod.qh index 7c8a809ba..3b4eba7cb 100644 --- a/qcsrc/common/mutators/mutator/_mod.qh +++ b/qcsrc/common/mutators/mutator/_mod.qh @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/common/mutators/mutator/buffs/all.inc b/qcsrc/common/mutators/mutator/buffs/all.inc index 46a96f661..8739c3a40 100644 --- a/qcsrc/common/mutators/mutator/buffs/all.inc +++ b/qcsrc/common/mutators/mutator/buffs/all.inc @@ -3,156 +3,157 @@ 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"); + ATTRIB(Disabled, m_name, string, _("Disabled")); + ATTRIB(Disabled, m_icon, string, "buff_disability"); + 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) diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh index 5b93fa795..e3e4e5edd 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qh @@ -4,6 +4,7 @@ #include #ifdef GAMEQC +#include #include #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 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)); }); } diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index bf680f9a4..7bc6cd712 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -1,7 +1,7 @@ #include "sv_buffs.qh" #include -#include +#include #include #include @@ -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) @@ -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,150 @@ 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); + entity store = IS_PLAYER(actor) ? PS(actor) : actor; + store.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 +669,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 +709,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)) { @@ -569,7 +750,7 @@ 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 && + if(!StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && frag_target != frag_attacker && IS_PLAYER(frag_attacker) && !IS_DEAD(frag_target) && !STAT(FROZEN, frag_target)) @@ -579,26 +760,12 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) } } -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; } @@ -615,28 +782,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; @@ -741,29 +893,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) @@ -771,18 +905,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; @@ -792,10 +919,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; } @@ -803,14 +927,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) { @@ -818,6 +943,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)) @@ -829,181 +955,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) @@ -1016,9 +969,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"); diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh index 1b095c0f2..ea2beb584 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh @@ -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; diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.inc b/qcsrc/common/mutators/mutator/instagib/_mod.inc index 55a67b1e3..2195111f0 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.inc +++ b/qcsrc/common/mutators/mutator/instagib/_mod.inc @@ -1,8 +1,5 @@ // generated file; do not modify #include -#ifdef SVQC - #include -#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.qh b/qcsrc/common/mutators/mutator/instagib/_mod.qh index 9989d8e3f..7097eaf39 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.qh +++ b/qcsrc/common/mutators/mutator/instagib/_mod.qh @@ -1,8 +1,5 @@ // generated file; do not modify #include -#ifdef SVQC - #include -#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh index 4724eda17..9be156bb3 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qh +++ b/qcsrc/common/mutators/mutator/instagib/items.qh @@ -2,7 +2,7 @@ #include #include -#include +#include 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) diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc index 51d65a917..44cd80d02 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -2,6 +2,7 @@ #include #include +#include #include #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(); diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh index c4b9fcc19..4ee7e491f 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh @@ -2,6 +2,7 @@ #include "items.qh" #include +#include // 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 index c944f56c3..000000000 --- a/qcsrc/common/mutators/mutator/instagib/sv_items.qc +++ /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 index 6f70f09be..000000000 --- a/qcsrc/common/mutators/mutator/instagib/sv_items.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 96729df44..04c61a6d7 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -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) { diff --git a/qcsrc/common/mutators/mutator/overkill/okhmg.qc b/qcsrc/common/mutators/mutator/overkill/okhmg.qc index 210b77ec4..6bfafed67 100644 --- a/qcsrc/common/mutators/mutator/overkill/okhmg.qc +++ b/qcsrc/common/mutators/mutator/overkill/okhmg.qc @@ -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 index 000000000..fd926b311 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/_mod.inc @@ -0,0 +1,10 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif + +#include diff --git a/qcsrc/common/mutators/mutator/powerups/_mod.qh b/qcsrc/common/mutators/mutator/powerups/_mod.qh new file mode 100644 index 000000000..8ab7ce65f --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/_mod.qh @@ -0,0 +1,10 @@ +// generated file; do not modify +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif + +#include diff --git a/qcsrc/common/mutators/mutator/powerups/cl_powerups.qc b/qcsrc/common/mutators/mutator/powerups/cl_powerups.qc new file mode 100644 index 000000000..1d6078655 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/cl_powerups.qc @@ -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 index 000000000..85eeb9325 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/cl_powerups.qh @@ -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 index 000000000..42893e87c --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/_mod.inc @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh b/qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh new file mode 100644 index 000000000..7e7c5a2e9 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/_mod.qh @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc new file mode 100644 index 000000000..dda2dcf8f --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc @@ -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 index 000000000..85ac8bd30 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qh @@ -0,0 +1,60 @@ +#pragma once + +#include +#include +#ifdef SVQC + // For FL_POWERUP + #include + #include +#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 index 000000000..230967b2b --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/shield.qc @@ -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 index 000000000..854c80276 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/shield.qh @@ -0,0 +1,57 @@ +#pragma once + +#include +#include +#ifdef SVQC + // For FL_POWERUP + #include + #include +#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 index 000000000..4a2fe2c89 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/speed.qc @@ -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 index 000000000..4f6459263 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/speed.qh @@ -0,0 +1,63 @@ +#pragma once + +#include +#include +#ifdef SVQC + // For FL_POWERUP + #include + #include +#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; +float autocvar_g_balance_powerup_speed_weaponspeed = 1.5; +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 index 000000000..c2f257292 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/strength.qc @@ -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 index 000000000..ce1914966 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerup/strength.qh @@ -0,0 +1,58 @@ +#pragma once + +#include +#include +#ifdef SVQC + // For FL_POWERUP + #include + #include +#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 index 000000000..7fc10c921 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerups.qc @@ -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 index 000000000..3a614e388 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/powerups.qh @@ -0,0 +1,20 @@ +#pragma once + +#include +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 +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 index 000000000..e727d493f --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/sv_powerups.qc @@ -0,0 +1,128 @@ +#include "sv_powerups.qh" + +MUTATOR_HOOKFUNCTION(powerups, W_PlayStrengthSound) +{ + entity player = M_ARGV(0, entity); + entity store = IS_PLAYER(player) ? PS(player) : player; // because non-player entities can fire weapons + + if(StatusEffects_active(STATUSEFFECT_Strength, player) + && ((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_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, WeaponSpeedFactor) +{ + entity player = M_ARGV(1, entity); + + if(StatusEffects_active(STATUSEFFECT_Speed, player)) + M_ARGV(0, float) *= autocvar_g_balance_powerup_speed_weaponspeed; +} + +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 index 000000000..f71025959 --- /dev/null +++ b/qcsrc/common/mutators/mutator/powerups/sv_powerups.qh @@ -0,0 +1,12 @@ +#pragma once + +#include + +#include "powerups.qh" + +int autocvar_g_powerups; + +REGISTER_MUTATOR(powerups, true); + +.float prevstrengthsound; +.float prevstrengthsoundattempt; diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc index d6afab175..2f0c79431 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -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; diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc b/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc index f02099f1d..6fa2ed024 100644 --- a/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.inc @@ -1,3 +1,4 @@ // generated file; do not modify #include -#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh b/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh index c91959bed..feaa8a19d 100644 --- a/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/_mod.qh @@ -1,3 +1,4 @@ // generated file; do not modify #include -#include +#include +#include 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 index 8c031aae4..000000000 --- a/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qc +++ /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 index 62321d7f1..000000000 --- a/qcsrc/common/mutators/mutator/status_effects/status_effect/powerups.qh +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include - -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 index 000000000..6673de650 --- /dev/null +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qc @@ -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 index 000000000..af31e7b34 --- /dev/null +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/spawnshield.qh @@ -0,0 +1,13 @@ +#pragma once + +#include + +CLASS(SpawnShield, StatusEffects) + ATTRIB(SpawnShield, netname, string, "spawnshield"); + ATTRIB(SpawnShield, m_name, string, _("Spawn Shield")); + ATTRIB(SpawnShield, m_icon, string, "shield"); + 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 index 000000000..915bb8aca --- /dev/null +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qc @@ -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 index 000000000..3926bbe62 --- /dev/null +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh @@ -0,0 +1,13 @@ +#pragma once + +#include + +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/sv_status_effects.qc b/qcsrc/common/mutators/mutator/status_effects/sv_status_effects.qc index df97b0589..0ec5d4d5c 100644 --- a/qcsrc/common/mutators/mutator/status_effects/sv_status_effects.qc +++ b/qcsrc/common/mutators/mutator/status_effects/sv_status_effects.qc @@ -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); } diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc index edb61b91c..71bb9c8fc 100644 --- a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc @@ -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); diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc index f4562a99e..e8d1b5977 100644 --- a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -14,7 +14,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 && + if(!StatusEffects_active(STATUSEFFECT_SpawnShield, frag_target) && frag_target != frag_attacker && IS_PLAYER(frag_attacker) && !IS_DEAD(frag_target) && !STAT(FROZEN, frag_target)) diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 59c4c9333..adffbfce0 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -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) { diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index 8ada68244..2337e6273 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -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; diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index 7f93e8fde..f25859e05 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -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; diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index fd33dacde..b161d1054 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -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); diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index db0d83ead..5d35089e5 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -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) \ diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index b5198c08d..66658a390 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -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; diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index fbe14c90f..689f0c179 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc index c000bbdac..87f7bc0d0 100644 --- a/qcsrc/server/bot/default/havocbot/roles.qc +++ b/qcsrc/server/bot/default/havocbot/roles.qc @@ -1,6 +1,7 @@ #include "roles.qh" #include +#include #include #include #include diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index f3e8ca46f..24358fa6e 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -590,9 +590,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; @@ -600,12 +604,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; @@ -1037,10 +1043,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); @@ -1445,14 +1447,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); } } @@ -1464,7 +1464,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); @@ -1477,48 +1477,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)) @@ -1575,10 +1534,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); } diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index ae9dbd357..121df6926 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -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 diff --git a/qcsrc/server/compat/wop.qc b/qcsrc/server/compat/wop.qc index 8a559332d..2a3ad1d64 100644 --- a/qcsrc/server/compat/wop.qc +++ b/qcsrc/server/compat/wop.qc @@ -1,12 +1,12 @@ #include "wop.qh" #include -#include +#include #include #include -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) diff --git a/qcsrc/server/damage.qc b/qcsrc/server/damage.qc index 95e1488fe..560f3e490 100644 --- a/qcsrc/server/damage.qc +++ b/qcsrc/server/damage.qc @@ -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 @@ -614,7 +610,7 @@ void Damage(entity targ, entity inflictor, entity attacker, float damage, int de // No damage modification here // Instead, prepare the victim for his death... SetResourceExplicit(targ, RES_ARMOR, 0); - targ.spawnshieldtime = 0; + StatusEffects_remove(STATUSEFFECT_SpawnShield, targ, STATUSEFFECT_REMOVE_CLEAR); SetResourceExplicit(targ, RES_HEALTH, 0.9); // this is < 1 targ.flags -= targ.flags & FL_GODMODE; damage = 100000; @@ -764,34 +760,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 @@ -853,7 +821,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) diff --git a/qcsrc/server/items/items.qc b/qcsrc/server/items/items.qc index 5cf659cff..8a8e6eca4 100644 --- a/qcsrc/server/items/items.qc +++ b/qcsrc/server/items/items.qc @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -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; @@ -1200,6 +1215,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; @@ -1219,6 +1238,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; @@ -1280,6 +1301,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"); @@ -1362,7 +1385,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 { @@ -1436,7 +1460,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); @@ -1478,6 +1505,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); @@ -1517,9 +1546,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); @@ -1557,8 +1584,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; @@ -1613,8 +1647,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); diff --git a/qcsrc/server/items/items.qh b/qcsrc/server/items/items.qh index 39009fe90..ff2d13843 100644 --- a/qcsrc/server/items/items.qh +++ b/qcsrc/server/items/items.qh @@ -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) diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 35a8f6fd9..1165e7d8c 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -1249,3 +1249,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); diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index 291c9588c..c1ca497ca 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -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 - max(0, autocvar_g_spawnshield_blockdamage); 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)) { diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 3f8d2c285..a00eb120e 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -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) diff --git a/qcsrc/server/weapons/common.qh b/qcsrc/server/weapons/common.qh index 1bf9261f7..779226be3 100644 --- a/qcsrc/server/weapons/common.qh +++ b/qcsrc/server/weapons/common.qh @@ -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); diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 8566b5cdb..200e6a3bd 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -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) diff --git a/qcsrc/server/world.qc b/qcsrc/server/world.qc index 6628b4236..1928d6418 100644 --- a/qcsrc/server/world.qc +++ b/qcsrc/server/world.qc @@ -865,12 +865,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"); } diff --git a/ruleset-XDF.cfg b/ruleset-XDF.cfg index 683a96e02..1bf071d56 100644 --- a/ruleset-XDF.cfg +++ b/ruleset-XDF.cfg @@ -12,7 +12,8 @@ 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 -1 // needed for speed powerup +g_buffs 0 g_start_delay 3 g_use_ammunition 0 g_weapon_stay 1 @@ -20,36 +21,12 @@ teamplay_mode 2 // friendly fire and self damage sv_vote_nospectators 1 timelimit_override 20 -// general buff settings -g_buffs_cooldown_activate 0 -g_buffs_cooldown_respawn 0 -g_buffs_randomize 0 - -// disabled buffs -g_buffs_ammo 0 -g_buffs_resistance 0 -g_buffs_medic 0 -g_buffs_vengeance 0 -g_buffs_bash 0 -g_buffs_disability 0 -g_buffs_vampire 0 -g_buffs_jump 0 -g_buffs_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 +g_balance_powerup_speed_weaponspeed 1 // do not increase projectile speed // game mode settings g_cts_finish_kill_delay 2