From: otta8634 Date: Sat, 25 Jan 2025 19:28:26 +0000 (+0800) Subject: Merge branch 'master' into TimePath/guide X-Git-Url: https://git.rm.cloudns.org/?a=commitdiff_plain;h=40ffe038cbb3f02ef119ef32ff268244f41b2c51;p=xonotic%2Fxonotic-data.pk3dir.git Merge branch 'master' into TimePath/guide --- 40ffe038cbb3f02ef119ef32ff268244f41b2c51 diff --cc qcsrc/common/items/item/ammo.qh index 8397f1fcca,8397f1fcca..537161ba2d --- a/qcsrc/common/items/item/ammo.qh +++ b/qcsrc/common/items/item/ammo.qh @@@ -52,6 -52,6 +52,7 @@@ CLASS(Shells, Ammo /* refname */ ATTRIB(Shells, netname, string, "shells"); /* itemname */ ATTRIB(Shells, m_name, string, _("Shells")); /* icon */ ATTRIB(Shells, m_icon, string, "ammo_shells"); ++/* color */ ATTRIB(Shells, m_color, vector, '0.604 0.647 0.671'); #ifdef SVQC /* botvalue */ ATTRIB(Shells, m_botvalue, int, 1000); /* itemid */ ATTRIB(Shells, m_itemid, int, IT_RESOURCE); @@@ -86,6 -86,6 +87,7 @@@ CLASS(Bullets, Ammo /* refname */ ATTRIB(Bullets, netname, string, "bullets"); /* itemname */ ATTRIB(Bullets, m_name, string, _("Bullets")); /* icon */ ATTRIB(Bullets, m_icon, string, "ammo_bullets"); ++/* color */ ATTRIB(Bullets, m_color, vector, '0.678 0.941 0.522'); #ifdef SVQC /* botvalue */ ATTRIB(Bullets, m_botvalue, int, 1500); /* itemid */ ATTRIB(Bullets, m_itemid, int, IT_RESOURCE); @@@ -120,6 -120,6 +122,7 @@@ CLASS(Rockets, Ammo /* refname */ ATTRIB(Rockets, netname, string, "rockets"); /* itename */ ATTRIB(Rockets, m_name, string, _("Rockets")); /* icon */ ATTRIB(Rockets, m_icon, string, "ammo_rockets"); ++/* color */ ATTRIB(Rockets, m_color, vector, '0.918 0.686 0.525'); #ifdef SVQC /* botvalue */ ATTRIB(Rockets, m_botvalue, int, 1500); /* itemid */ ATTRIB(Rockets, m_itemid, int, IT_RESOURCE); @@@ -154,6 -154,6 +157,7 @@@ CLASS(Cells, Ammo /* refname */ ATTRIB(Cells, netname, string, "cells"); /* itemname */ ATTRIB(Cells, m_name, string, _("Cells")); /* icon */ ATTRIB(Cells, m_icon, string, "ammo_cells"); ++/* color */ ATTRIB(Cells, m_color, vector, '0.545 0.882 0.969'); #ifdef SVQC /* botvalue */ ATTRIB(Cells, m_botvalue, int, 1500); /* itemid */ ATTRIB(Cells, m_itemid, int, IT_RESOURCE); diff --cc qcsrc/common/mutators/mutator/buffs/buff/ammo.qh index 0000000000,ee654c8ad9..fdb96d892f mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/ammo.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/ammo.qh @@@ -1,0 -1,20 +1,29 @@@ + #pragma once + + #include + + #ifdef SVQC + .int buff_ammo_prev_infitems; + .int buff_ammo_prev_clipload; + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(AmmoBuff, describe, string(AmmoBuff this)) ++ { ++ TC(AmmoBuff, this); ++ return sprintf(_("The %s gives you infinite ammo until the buff expires, so you don't need to worry about running out of ammo\n\n" ++ "It also removes the need to reload any weapons that require reloading"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(AmmoBuff) + + REGISTER_BUFF(AMMO, NEW(AmmoBuff)); + BUFF_SPAWNFUNCS(ammo, BUFF_AMMO) + BUFF_SPAWNFUNC_Q3COMPAT(item_ammoregen, BUFF_AMMO) diff --cc qcsrc/common/mutators/mutator/buffs/buff/bash.qh index 0000000000,ba306eec59..25a7210856 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/bash.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/bash.qh @@@ -1,0 -1,23 +1,32 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_bash_force; + float autocvar_g_buffs_bash_force_self; + + vector buff_Bash_TargetCalculateForce(vector frag_force, entity frag_target, entity frag_attacker); + vector buff_Bash_AttackerCalculateForce(vector frag_force, entity frag_target, entity frag_attacker); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(BashBuff, describe, string(BashBuff this)) ++ { ++ TC(BashBuff, this); ++ return sprintf(_("The %s increases knockback force you deal, and makes you immune to knockback while the buff is active\n\n" ++ "It also slightly increases knockback you deal to yourself"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(BashBuff) + + REGISTER_BUFF(BASH, NEW(BashBuff)); + BUFF_SPAWNFUNCS(bash, BUFF_BASH) + BUFF_SPAWNFUNC_Q3COMPAT(item_scout, BUFF_BASH) diff --cc qcsrc/common/mutators/mutator/buffs/buff/disability.qh index 0000000000,26d3c446b8..ea81ad5cb3 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/disability.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/disability.qh @@@ -1,0 -1,23 +1,32 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_disability_slowtime; + float autocvar_g_buffs_disability_speed; + float autocvar_g_buffs_disability_rate; + float autocvar_g_buffs_disability_weaponspeed; + + void buff_Disability_ApplyStunned(entity frag_target, entity frag_attacker); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(DisabilityBuff, describe, string(DisabilityBuff this)) ++ { ++ TC(DisabilityBuff, this); ++ return sprintf(_("The %s causes your enemies and monsters to slow down for a few seconds when you attack them, while the buff is active\n\n" ++ "This is particularly useful against speedy players, especially in %s"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff")), COLORED_NAME(MAPINFO_TYPE_CTF)); ++ } ++#endif + ENDCLASS(DisabilityBuff) + + REGISTER_BUFF(DISABILITY, NEW(DisabilityBuff)); + BUFF_SPAWNFUNCS(disability, BUFF_DISABILITY) diff --cc qcsrc/common/mutators/mutator/buffs/buff/flight.qh index 0000000000,90903a8227..27dda96916 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/flight.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/flight.qh @@@ -1,0 -1,20 +1,28 @@@ + #pragma once + + #include + + #ifdef SVQC + .float buff_flight_oldgravity; + .bool buff_flight_crouchheld; + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(FlightBuff, describe, string(FlightBuff this)) ++ { ++ TC(FlightBuff, this); ++ return sprintf(_("While you have the %s, you can crouch while midair to switch your gravity, allowing flight"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(FlightBuff) + + REGISTER_BUFF(FLIGHT, NEW(FlightBuff)); + BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT) + BUFF_SPAWNFUNC_Q3COMPAT(item_flight, BUFF_FLIGHT) diff --cc qcsrc/common/mutators/mutator/buffs/buff/inferno.qh index 0000000000,57148e01ed..ec6141e2d2 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/inferno.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/inferno.qh @@@ -1,0 -1,26 +1,34 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_inferno_burntime_factor; + float autocvar_g_buffs_inferno_burntime_min_time; + float autocvar_g_buffs_inferno_burntime_target_damage; + float autocvar_g_buffs_inferno_burntime_target_time; + float autocvar_g_buffs_inferno_damagemultiplier; + + float buff_Inferno_CalculateTime(float dmg); + float buff_Inferno_CalculateDamage(float frag_damage); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(InfernoBuff, describe, string(InfernoBuff this)) ++ { ++ TC(InfernoBuff, this); ++ return sprintf(_("The %s sets any enemies or monsters you attack alight, dealing burn damage to them for several seconds until the buff expires"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(InfernoBuff) + + REGISTER_BUFF(INFERNO, NEW(InfernoBuff)); + BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO) + BUFF_SPAWNFUNC_Q3COMPAT(item_doubler, BUFF_INFERNO) diff --cc qcsrc/common/mutators/mutator/buffs/buff/jump.qh index 0000000000,7163f542ce..f635c938b0 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/jump.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/jump.qh @@@ -1,0 -1,21 +1,29 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_jump_height; + + float buff_Jump_ChangeDamage(float frag_damage, float frag_deathtype); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(JumpBuff, describe, string(JumpBuff this)) ++ { ++ TC(JumpBuff, this); ++ return sprintf(_("The %s greatly increases your jump height, while the buff is active\n\nIt also makes you immune to fall damage"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(JumpBuff) + + REGISTER_BUFF(JUMP, NEW(JumpBuff)); + BUFF_SPAWNFUNCS(jump, BUFF_JUMP) + BUFF_SPAWNFUNC_Q3COMPAT(item_jumper, BUFF_JUMP) diff --cc qcsrc/common/mutators/mutator/buffs/buff/luck.qh index 0000000000,1fd1226d19..cd7f9148ec mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/luck.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/luck.qh @@@ -1,0 -1,21 +1,29 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_luck_chance = 0.15; + float autocvar_g_buffs_luck_damagemultiplier = 3; + + float buff_Luck_CalculateDamage(float frag_damage); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(LuckBuff, describe, string(LuckBuff this)) ++ { ++ TC(LuckBuff, this); ++ return sprintf(_("While you have the %s, each attack has a chance of being a critical hit with greatly increased damage"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(LuckBuff) + + REGISTER_BUFF(LUCK, NEW(LuckBuff)); + BUFF_SPAWNFUNCS(luck, BUFF_LUCK) diff --cc qcsrc/common/mutators/mutator/buffs/buff/magnet.qh index 0000000000,1b04615c0b..dd1739becc mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/magnet.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/magnet.qh @@@ -1,0 -1,19 +1,27 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_magnet_range_item; + float autocvar_g_buffs_magnet_range_buff = 200; + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(MagnetBuff, describe, string(MagnetBuff this)) ++ { ++ TC(MagnetBuff, this); ++ return sprintf(_("The %s greatly increases your item pickup range, collecting nearby items for you while the buff is active"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(MagnetBuff) + + REGISTER_BUFF(MAGNET, NEW(MagnetBuff)); + BUFF_SPAWNFUNCS(magnet, BUFF_MAGNET) diff --cc qcsrc/common/mutators/mutator/buffs/buff/medic.qh index 0000000000,c3021c1189..eaad97e66d mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/medic.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/medic.qh @@@ -1,0 -1,26 +1,35 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_medic_survive_chance; + float autocvar_g_buffs_medic_survive_health; + float autocvar_g_buffs_medic_rot; + float autocvar_g_buffs_medic_max; + float autocvar_g_buffs_medic_regen; + + float buff_Medic_CalculateSurviveDamage(float frag_damage, float health); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(MedicBuff, describe, string(MedicBuff this)) ++ { ++ TC(MedicBuff, this); ++ return sprintf(_("The %s increases health regeneration speed, increases the maximum health you can have before health starts to rot, and reduces health rot speed until the buff expires\n\n" ++ "It also gives you a chance to survive a fatal hit, with a small amount of health left over"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(MedicBuff) + + REGISTER_BUFF(MEDIC, NEW(MedicBuff)); + BUFF_SPAWNFUNCS(medic, BUFF_MEDIC) + BUFF_SPAWNFUNC_Q3COMPAT(item_regen, BUFF_MEDIC) + BUFF_SPAWNFUNC_Q3COMPAT(item_revival, BUFF_MEDIC) diff --cc qcsrc/common/mutators/mutator/buffs/buff/resistance.qh index 0000000000,d351a9453f..d888a22afd mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/resistance.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/resistance.qh @@@ -1,0 -1,21 +1,30 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_resistance_blockpercent; + + float buff_Resistance_CalculateDamage(float frag_damage); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(ResistanceBuff, describe, string(ResistanceBuff this)) ++ { ++ TC(ResistanceBuff, this); ++ return sprintf(_("The %s greatly reduces your damage taken while the buff is active\n\n" ++ "This also impacts the damage you deal to yourself"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(ResistanceBuff) + + REGISTER_BUFF(RESISTANCE, NEW(ResistanceBuff)); + BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE) + BUFF_SPAWNFUNC_Q3COMPAT(item_guard, BUFF_RESISTANCE) diff --cc qcsrc/common/mutators/mutator/buffs/buff/swapper.qh index 0000000000,6b795ac84e..c08d7a9c60 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/swapper.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/swapper.qh @@@ -1,0 -1,19 +1,28 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_swapper_range; + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(SwapperBuff, describe, string(SwapperBuff this)) ++ { ++ TC(SwapperBuff, this); ++ return sprintf(_("The %s allows you to press the ^3drop weapon^7 bind to switch places with a nearby enemy once\n\n" ++ "A common usage of this buff is to jump over the map's void, then swap with an enemy, to cause them to fall into the void"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(SwapperBuff) + + REGISTER_BUFF(SWAPPER, NEW(SwapperBuff)); + BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER) + BUFF_SPAWNFUNC_Q3COMPAT(holdable_teleporter, BUFF_SWAPPER) diff --cc qcsrc/common/mutators/mutator/buffs/buff/vampire.qh index 0000000000,1311733379..0b09fa994c mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/vampire.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/vampire.qh @@@ -1,0 -1,19 +1,27 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_vampire_damage_steal; + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(VampireBuff, describe, string(VampireBuff this)) ++ { ++ TC(VampireBuff, this); ++ return sprintf(_("The %s converts some of the damage you deal to enemies and monsters into health for yourself, until the buff expires"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(VampireBuff) + + REGISTER_BUFF(VAMPIRE, NEW(VampireBuff)); + BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE) + BUFF_SPAWNFUNC_Q3COMPAT(holdable_invulnerability, BUFF_VAMPIRE) diff --cc qcsrc/common/mutators/mutator/buffs/buff/vengeance.qh index 0000000000,5391821068..3400589827 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/buffs/buff/vengeance.qh +++ b/qcsrc/common/mutators/mutator/buffs/buff/vengeance.qh @@@ -1,0 -1,22 +1,30 @@@ + #pragma once + + #include + + #ifdef SVQC + float autocvar_g_buffs_vengeance_damage_multiplier; + + void buff_Vengeance_DelayedDamage(entity this); + float buff_Vengeance_CalculateDamage(float frag_damage); + #endif // SVQC + + 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'); ++#ifdef MENUQC ++ METHOD(VengeanceBuff, describe, string(VengeanceBuff this)) ++ { ++ TC(VengeanceBuff, this); ++ return sprintf(_("The %s reciprocates a portion of the damage enemies deal to you onto them, until the buff expires"), ++ COLORED_NAME_WITH_CONCAT(this, _("Buff"))); ++ } ++#endif + ENDCLASS(VengeanceBuff) + + REGISTER_BUFF(VENGEANCE, NEW(VengeanceBuff)); + BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE) + BUFF_SPAWNFUNC_Q3COMPAT(holdable_kamikaze, BUFF_VENGEANCE) diff --cc qcsrc/common/mutators/mutator/nades/nade/ammo.qh index 0000000000,6797826f8b..62b83aef68 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/ammo.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/ammo.qh @@@ -1,0 -1,24 +1,34 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_ammo; + float autocvar_g_nades_ammo_time; + float autocvar_g_nades_ammo_rate; + float autocvar_g_nades_ammo_friend; + float autocvar_g_nades_ammo_foe; + + .float nade_ammo_time; + + void nade_ammo_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_AMMO = 88; + const int PROJECTILE_NADE_AMMO_BURN = 89; + + CLASS(AmmoNade, Nade) + ATTRIB(AmmoNade, m_color, vector, '0.66 0.33 0'); + ATTRIB(AmmoNade, m_name, string, _("Ammo grenade")); + ATTRIB(AmmoNade, m_icon, string, "nade_ammo"); ++#ifdef MENUQC ++ METHOD(AmmoNade, describe, string(AmmoNade this)) ++ { ++ TC(AmmoNade, this); ++ return sprintf(_("The %s detonates after a short delay, temporarily creating an orb around the point where it detonated for several seconds. " ++ "If your team members enter the orb they will recover ammo, and if enemies enter the sphere they will lose ammo\n\n" ++ "This does not impact weapon magazines, so it won't reload your weapons for you"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(AmmoNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/darkness.qh index 0000000000,e638aa1754..df37118ae1 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/darkness.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/darkness.qh @@@ -1,0 -1,21 +1,29 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_darkness; + bool autocvar_g_nades_darkness_explode; + bool autocvar_g_nades_darkness_teamcheck; + float autocvar_g_nades_darkness_time; + + void nade_darkness_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_DARKNESS = 90; + const int PROJECTILE_NADE_DARKNESS_BURN = 91; + + CLASS(DarknessNade, Nade) + ATTRIB(DarknessNade, m_color, vector, '0.23 0 0.23'); + ATTRIB(DarknessNade, m_name, string, _("Darkness grenade")); + ATTRIB(DarknessNade, m_icon, string, "nade_darkness"); ++#ifdef MENUQC ++ METHOD(DarknessNade, describe, string(DarknessNade this)) ++ { ++ TC(DarknessNade, this); ++ return sprintf(_("The %s detonates after a short delay, creating a dark field which temporarily blinds enemies who enter it"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(DarknessNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/entrap.qh index 0000000000,e89c64fbac..cedea9c135 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/entrap.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/entrap.qh @@@ -1,0 -1,24 +1,33 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_entrap; + float autocvar_g_nades_entrap_strength = 0.01; + float autocvar_g_nades_entrap_speed = 0.5; + float autocvar_g_nades_entrap_radius = 500; + float autocvar_g_nades_entrap_time = 10; + + .float nade_entrap_time; + + void nade_entrap_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_ENTRAP = 84; + const int PROJECTILE_NADE_ENTRAP_BURN = 85; + + CLASS(EntrapNade, Nade) + ATTRIB(EntrapNade, m_color, vector, '0.15 0.85 0'); + ATTRIB(EntrapNade, m_name, string, _("Entrap grenade")); + ATTRIB(EntrapNade, m_icon, string, "nade_entrap"); ++#ifdef MENUQC ++ METHOD(EntrapNade, describe, string(EntrapNade this)) ++ { ++ TC(EntrapNade, this); ++ return sprintf(_("The %s detonates after a short delay, temporarily creating an orb around the point where it detonated for several seconds. " ++ "Players and projectiles that enter the sphere will be slowed down, including yourself"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(EntrapNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/heal.qh index 0000000000,49b6f9edb5..e1d60a2db4 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/heal.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/heal.qh @@@ -1,0 -1,22 +1,31 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_heal = true; + float autocvar_g_nades_heal_time; + float autocvar_g_nades_heal_rate; + float autocvar_g_nades_heal_friend; + float autocvar_g_nades_heal_foe; + + void nade_heal_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_HEAL = 80; + const int PROJECTILE_NADE_HEAL_BURN = 81; + + CLASS(HealNade, Nade) + ATTRIB(HealNade, m_color, vector, '1 0 0'); + ATTRIB(HealNade, m_name, string, _("Heal grenade")); + ATTRIB(HealNade, m_icon, string, "nade_heal"); ++#ifdef MENUQC ++ METHOD(HealNade, describe, string(HealNade this)) ++ { ++ TC(HealNade, this); ++ return sprintf(_("The %s detonates after a short delay, temporarily creating a healing orb around the point where it detonated for several seconds. " ++ "If your team members enter the orb they will recover health, and if enemies enter the sphere they will be harmed"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(HealNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/ice.qh index 0000000000,2ac7dd2b1e..8e710205e2 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/ice.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/ice.qh @@@ -1,0 -1,22 +1,31 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_ice = true; + float autocvar_g_nades_ice_freeze_time; + float autocvar_g_nades_ice_health; + bool autocvar_g_nades_ice_explode; + bool autocvar_g_nades_ice_teamcheck; + + void nade_ice_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_ICE = 76; + const int PROJECTILE_NADE_ICE_BURN = 77; + + CLASS(IceNade, Nade) + ATTRIB(IceNade, m_color, vector, '0 0.5 2'); + ATTRIB(IceNade, m_name, string, _("Ice grenade")); + ATTRIB(IceNade, m_icon, string, "nade_ice"); ++#ifdef MENUQC ++ METHOD(IceNade, describe, string(IceNade this)) ++ { ++ TC(IceNade, this); ++ return sprintf(_("The %s detonates after a short delay, freezing any enemies who walk within the explosion radius for a few seconds after the explosion. " ++ "While frozen, enemies are progressively dealt damage"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(IceNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/monster.qh index 0000000000,464b34b552..8c6a9613ff mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/monster.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/monster.qh @@@ -1,0 -1,21 +1,29 @@@ + #pragma once + + #include + + #ifdef SVQC + // TODO: rename these to `monster` + bool autocvar_g_nades_pokenade; + string autocvar_g_nades_pokenade_monster_type; + float autocvar_g_nades_pokenade_monster_lifetime; + + void nade_monster_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_MONSTER = 82; + const int PROJECTILE_NADE_MONSTER_BURN = 83; + + CLASS(MonsterNade, Nade) + ATTRIB(MonsterNade, m_color, vector, '0.25 0.75 0'); + ATTRIB(MonsterNade, m_name, string, _("Monster grenade")); + ATTRIB(MonsterNade, m_icon, string, "nade_monster"); ++#ifdef MENUQC ++ METHOD(MonsterNade, describe, string(MonsterNade this)) ++ { ++ TC(MonsterNade, this); ++ return sprintf(_("The %s explodes after a short delay, spawning one of four monster types"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(MonsterNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/napalm.qh index 0000000000,26f6cc8176..47c57d20b4 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/napalm.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/napalm.qh @@@ -1,0 -1,33 +1,42 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_napalm; + int autocvar_g_nades_napalm_ball_count; + float autocvar_g_nades_napalm_ball_spread; + float autocvar_g_nades_napalm_ball_damage; + float autocvar_g_nades_napalm_ball_damageforcescale; + float autocvar_g_nades_napalm_ball_lifetime; + float autocvar_g_nades_napalm_ball_radius; + bool autocvar_g_nades_napalm_blast; + float autocvar_g_nades_napalm_fountain_lifetime; + float autocvar_g_nades_napalm_fountain_delay; + float autocvar_g_nades_napalm_fountain_radius; + float autocvar_g_nades_napalm_fountain_damage; + float autocvar_g_nades_napalm_fountain_edgedamage; + float autocvar_g_nades_napalm_burntime; + bool autocvar_g_nades_napalm_selfdamage; + + void nade_napalm_boom(entity this); + #endif // SVQC + + const int PROJECTILE_NADE_NAPALM = 73; + const int PROJECTILE_NADE_NAPALM_BURN = 74; + const int PROJECTILE_NAPALM_FOUNTAIN = 75; + + CLASS(NapalmNade, Nade) + ATTRIB(NapalmNade, m_color, vector, '2 0.5 0'); + ATTRIB(NapalmNade, m_name, string, _("Napalm grenade")); + ATTRIB(NapalmNade, m_icon, string, "nade_napalm"); ++#ifdef MENUQC ++ METHOD(NapalmNade, describe, string(NapalmNade this)) ++ { ++ TC(NapalmNade, this); ++ return sprintf(_("The %s explodes after a short delay, spreading fiery napalm globs around in the fountain. " ++ "The napalm fire balls burn for a while, and damage players who get too close"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(NapalmNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/normal.qh index 0000000000,24716f0763..2136824f23 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/normal.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/normal.qh @@@ -1,0 -1,13 +1,22 @@@ + #pragma once + + #include + + #ifdef SVQC + void normal_nade_boom(entity this); + #endif // SVQC + + CLASS(NormalNade, Nade) + ATTRIB(NormalNade, m_color, vector, '1 1 1'); + ATTRIB(NormalNade, m_name, string, _("Grenade")); + ATTRIB(NormalNade, m_icon, string, "nade_normal"); ++#ifdef MENUQC ++ METHOD(NormalNade, describe, string(NormalNade this)) ++ { ++ TC(NormalNade, this); ++ return sprintf(_("The (normal) %s explodes after a short delay, dealing damage to nearby players\n\n" ++ "Make sure you remember to throw it with ^3drop weapon^7, or else it will blow up in your hands!"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(NormalNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/spawn.qh index 0000000000,0b97773e3e..66a2166bde mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/spawn.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/spawn.qh @@@ -1,0 -1,19 +1,28 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_spawn = true; + + void nade_spawn_boom(entity this); + void nade_spawn_SetSpawnHealth(entity player); + bool nade_spawn_DestroyDamage(entity this, entity attacker); + #endif // SVQC + + const int PROJECTILE_NADE_SPAWN = 79; + + CLASS(SpawnNade, Nade) + ATTRIB(SpawnNade, m_color, vector, '1 0.9 0'); + ATTRIB(SpawnNade, m_name, string, _("Spawn grenade")); + ATTRIB(SpawnNade, m_icon, string, "nade_spawn"); ++#ifdef MENUQC ++ METHOD(SpawnNade, describe, string(SpawnNade this)) ++ { ++ TC(SpawnNade, this); ++ return sprintf(_("The %s detonates after a short delay, temporarily setting your spawn point to where it detonated. " ++ "It is useful for cases where you want to go back to some point on the map after you die"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(SpawnNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/translocate.qh index 0000000000,0ad11a103a..1c6710fe94 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/translocate.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/translocate.qh @@@ -1,0 -1,19 +1,27 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_translocate = true; + float autocvar_g_nades_translocate_destroy_damage = 25; + + void nade_translocate_boom(entity this); + bool nade_translocate_DestroyDamage(entity this, entity attacker); + #endif // SVQC + + const int PROJECTILE_NADE_TRANSLOCATE = 78; + + CLASS(TranslocateNade, Nade) + ATTRIB(TranslocateNade, m_color, vector, '1 0 1'); + ATTRIB(TranslocateNade, m_name, string, _("Translocate grenade")); + ATTRIB(TranslocateNade, m_icon, string, "nade_translocate"); ++#ifdef MENUQC ++ METHOD(TranslocateNade, describe, string(TranslocateNade this)) ++ { ++ TC(TranslocateNade, this); ++ return sprintf(_("The %s detonates after a short delay, teleporting you to where it detonated"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(TranslocateNade) diff --cc qcsrc/common/mutators/mutator/nades/nade/veil.qh index 0000000000,09169da513..87c92aa592 mode 000000,100644..100644 --- a/qcsrc/common/mutators/mutator/nades/nade/veil.qh +++ b/qcsrc/common/mutators/mutator/nades/nade/veil.qh @@@ -1,0 -1,25 +1,34 @@@ + #pragma once + + #include + + #ifdef SVQC + bool autocvar_g_nades_veil; + float autocvar_g_nades_veil_time = 8; + float autocvar_g_nades_veil_radius = 300; + + .float nade_veil_time; + .float nade_veil_prevalpha; + + void nade_veil_boom(entity this); + void nade_veil_Apply(entity player); + #endif // SVQC + + const int PROJECTILE_NADE_VEIL = 86; + const int PROJECTILE_NADE_VEIL_BURN = 87; + + CLASS(VeilNade, Nade) + ATTRIB(VeilNade, m_color, vector, '0.65 0.85 0.65'); + ATTRIB(VeilNade, m_name, string, _("Veil grenade")); + ATTRIB(VeilNade, m_icon, string, "nade_veil"); + ATTRIB(VeilNade, m_alpha, float, 0.45); ++#ifdef MENUQC ++ METHOD(VeilNade, describe, string(VeilNade this)) ++ { ++ TC(VeilNade, this); ++ return sprintf(_("The Veil grenade detonates after a short delay, temporarily creating an orb around the point where it detonated for several seconds. " ++ "Players inside the orb will be invisible to those outside it"), ++ COLORED_NAME(this)); ++ } ++#endif + ENDCLASS(VeilNade) diff --cc qcsrc/common/mutators/mutator/nades/nades.qc index 39e6af99f9,7a6b7f08b2..78a38d1aca --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@@ -17,1805 -16,25 +16,41 @@@ REPLICATE(cvar_cl_pokenade_type, string entity Nade_TrailEffect(int proj, int nade_team) { - switch (proj) - { - case PROJECTILE_NADE: return EFFECT_NADE_TRAIL(nade_team); - case PROJECTILE_NADE_BURN: return EFFECT_NADE_TRAIL_BURN(nade_team); - } - - FOREACH(Nades, true, { - for (int j = 0; j < 2; ++j) - { - if (it.m_projectile[j] == proj) - { - string trail = it.m_trail[j].eent_eff_name; - if (trail) return it.m_trail[j]; - break; - } - } - }); - - return EFFECT_Null; - } - - #endif - - #ifdef CSQC - #include - #include - - bool darkness_fadealpha; - - void HUD_DarkBlinking() - { - vector bottomright = vec2(vid_conwidth, vid_conheight); - drawfill('0 0 0', bottomright, NADE_TYPE_DARKNESS.m_color, darkness_fadealpha, DRAWFLAG_NORMAL); - } - - REGISTER_MUTATOR(cl_nades, true); - MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay) - { - if (STAT(NADE_DARKNESS_TIME) > time) - { - if (!darkness_fadealpha) - sound(csqcplayer, CH_PAIN, SND_BLIND, VOL_BASE, ATTEN_NORM); - darkness_fadealpha = min(0.986, darkness_fadealpha + frametime * 7); - } - else if (darkness_fadealpha > 0) - darkness_fadealpha = max(0, darkness_fadealpha - frametime * 7); - - if (darkness_fadealpha > 0) - { - HUD_DarkBlinking(); - M_ARGV(1, float) = 0; // alpha_multipl 0, don't draw normal overlay - return true; - } - return false; - } - - MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile) - { - entity proj = M_ARGV(0, entity); - - if (proj.cnt == PROJECTILE_NAPALM_FOUNTAIN) - { - proj.modelindex = 0; - proj.traileffect = EFFECT_FIREBALL.m_id; - return true; - } - if (Nade_FromProjectile(proj.cnt) != NADE_TYPE_Null) - { - setmodel(proj, MDL_PROJECTILE_NADE); - entity trail = Nade_TrailEffect(proj.cnt, proj.team); - if (trail.eent_eff_name) proj.traileffect = trail.m_id; - return true; - } - } - MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile) - { - if (!mut_is_active(MUT_NADES)) return; - - entity proj = M_ARGV(0, entity); - - if (proj.cnt == PROJECTILE_NAPALM_FOUNTAIN) - { - loopsound(proj, CH_SHOTS_SINGLE, SND_FIREBALL_FLY2, VOL_BASE, ATTEN_NORM); - proj.mins = '-16 -16 -16'; - proj.maxs = '16 16 16'; - } - - entity nade_type = Nade_FromProjectile(proj.cnt); - if (nade_type == NADE_TYPE_Null) return; - - if(STAT(NADES_SMALL)) - { - proj.mins = '-8 -8 -8'; - proj.maxs = '8 8 8'; - } - else - { - proj.mins = '-16 -16 -16'; - proj.maxs = '16 16 16'; - } - proj.colormod = nade_type.m_color; - set_movetype(proj, MOVETYPE_BOUNCE); - settouch(proj, func_null); - proj.scale = 1.5; - proj.avelocity = randomvec() * 720; - proj.alphamod = nade_type.m_alpha; - - if (nade_type == NADE_TYPE_TRANSLOCATE || nade_type == NADE_TYPE_SPAWN) - proj.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - else - proj.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - } - - MUTATOR_HOOKFUNCTION(cl_nades, BuildGameplayTipsString) - { - if (mut_is_active(MUT_NADES)) - { - string key = getcommandkey(_("drop weapon / throw nade"), "dropweapon"); - M_ARGV(0, string) = strcat(M_ARGV(0, string), - "\n", sprintf(_("^3nades^8 are enabled, press ^3%s^8 to use them"), key), "\n"); - } - } - - bool Projectile_isnade(int p) - { - return Nade_FromProjectile(p) != NADE_TYPE_Null; - } - void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time) - { - float bonusNades = STAT(NADE_BONUS); - float bonusProgress = STAT(NADE_BONUS_SCORE); - float bonusType = STAT(NADE_BONUS_TYPE); - Nade def = REGISTRY_GET(Nades, max(1, bonusType)); - string nadeIcon = def.m_icon; // use the Normal Nade icon for the random nade, and draw it as rainbow - vector nadeColor; - if(bonusType) - nadeColor = def.m_color; - else - { - nadeColor.x = time % (M_PI * 2); - nadeColor.y = 1; - nadeColor.z = 1; - nadeColor = hsv_to_rgb(nadeColor); - } - - vector iconPos, textPos; - - if(autocvar_hud_panel_ammo_iconalign) - { - iconPos = myPos + eX * 2 * mySize.y; - textPos = myPos; - } - else - { - iconPos = myPos; - textPos = myPos + eX * mySize.y; - } - - if(bonusNades > 0 || bonusProgress > 0) - { - DrawNadeProgressBar(myPos, mySize, bonusProgress, nadeColor); - - if(autocvar_hud_panel_ammo_text) - drawstring_aspect(textPos, ftos(bonusNades), vec2((2/3) * mySize.x, mySize.y), '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - - if(draw_expanding) - drawpic_aspect_skin_expanding(iconPos, nadeIcon, '1 1 0' * mySize.y, (bonusType ? '1 1 1' : nadeColor), panel_fg_alpha, DRAWFLAG_NORMAL, expand_time); - - drawpic_aspect_skin(iconPos, nadeIcon, '1 1 0' * mySize.y, (bonusType ? '1 1 1' : nadeColor), panel_fg_alpha, DRAWFLAG_NORMAL); - } - } - #endif - - #ifdef SVQC - - #include - #include - #include - #include - - .float nade_time_primed; - .float nade_lifetime; - - .entity nade_spawnloc; - - vector nades_PlayerColor(entity this, bool isPants) - { - if(teamplay) - return Team_ColorRGB(this.team); - - // logic copied from Scoreboard_GetName - int col = (this.colormap >= 1024) ? this.colormap - 1024 : this.clientcolors; - return (isPants) ? colormapPaletteColor(col % 16, true) : colormapPaletteColor(floor(col / 16), false); - } - - void nade_timer_think(entity this) - { - this.skin = 8 - (this.owner.wait - time) / (this.owner.nade_lifetime / 10); - this.nextthink = time; - if(!this.owner || wasfreed(this.owner)) - delete(this); - } - - void nade_burn_spawn(entity _nade) - { - CSQCProjectile(_nade, true, REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, _nade)).m_projectile[1], true); - } - - void nade_spawn(entity _nade) - { - entity timer = new(nade_timer); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, _nade, ""); - timer.colormap = _nade.colormap; - timer.glowmod = _nade.glowmod; - setthink(timer, nade_timer_think); - timer.nextthink = time; - timer.wait = _nade.wait; - timer.owner = _nade; - timer.skin = 10; - - _nade.effects |= EF_LOWPRECISION; - - CSQCProjectile(_nade, true, REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, _nade)).m_projectile[0], true); - } - - void normal_nade_boom(entity this) - { - RadiusDamage(this, this.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, this, NULL, autocvar_g_nades_nade_force, this.projectiledeathtype, DMG_NOWEP, this.enemy); - Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, - autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this); - } - - void napalm_damage(entity this, float dist, float damage, float edgedamage, float burntime) - { - entity e; - float d; - vector p; - - if ( damage < 0 ) - return; - - RandomSelection_Init(); - for(e = WarpZone_FindRadius(this.origin, dist, true); e; e = e.chain) - if(e.takedamage == DAMAGE_AIM) - if(this.realowner != e || autocvar_g_nades_napalm_selfdamage) - if(!IS_PLAYER(e) || !this.realowner || DIFF_TEAM(e, this)) - if(!STAT(FROZEN, e)) - { - p = e.origin; - p.x += e.mins.x + random() * (e.maxs.x - e.mins.x); - p.y += e.mins.y + random() * (e.maxs.y - e.mins.y); - p.z += e.mins.z + random() * (e.maxs.z - e.mins.z); - d = vlen(WarpZone_UnTransformOrigin(e, this.origin) - p); - if(d < dist) - { - e.fireball_impactvec = p; - RandomSelection_AddEnt(e, 1 / (1 + d), !StatusEffects_active(STATUSEFFECT_Burning, e)); - } - } - if(RandomSelection_chosen_ent) - { - d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, this.origin) - RandomSelection_chosen_ent.fireball_impactvec); - d = damage + (edgedamage - damage) * (d / dist); - Fire_AddDamage(RandomSelection_chosen_ent, this.realowner, d * burntime, burntime, this.projectiledeathtype); - //trailparticles(this, particleeffectnum(EFFECT_FIREBALL_LASER), this.origin, RandomSelection_chosen_ent.fireball_impactvec); - Send_Effect(EFFECT_FIREBALL_LASER, this.origin, RandomSelection_chosen_ent.fireball_impactvec - this.origin, 1); - } - } - - - void napalm_ball_think(entity this) - { - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - delete(this); - return; - } - - if(time > this.pushltime) - { - delete(this); - return; - } - - vector midpoint = ((this.absmin + this.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - this.velocity = this.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { this.velocity_z = 200; } - } - - this.angles = vectoangles(this.velocity); - - napalm_damage(this, autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage, - autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime); - - this.nextthink = time + 0.1; - } - - - void nade_napalm_ball(entity this) - { - entity proj; - vector kick; - - spamsound(this, CH_SHOTS, SND_FIREBALL_FIRE, VOL_BASE, ATTEN_NORM); - - proj = new(grenade); - proj.owner = this.owner; - proj.realowner = this.realowner; - proj.team = this.owner.team; - proj.bot_dodge = true; - proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; - set_movetype(proj, MOVETYPE_BOUNCE); - proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - PROJECTILE_MAKETRIGGER(proj); - setmodel(proj, MDL_Null); - proj.scale = 1;//0.5; - setsize(proj, '-4 -4 -4', '4 4 4'); - setorigin(proj, this.origin); - setthink(proj, napalm_ball_think); - proj.nextthink = time; - proj.damageforcescale = autocvar_g_nades_napalm_ball_damageforcescale; - proj.effects = EF_LOWPRECISION | EF_FLAME; - - kick.x =(random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.y = (random() - 0.5) * 2 * autocvar_g_nades_napalm_ball_spread; - kick.z = (random()/2+0.5) * autocvar_g_nades_napalm_ball_spread; - proj.velocity = kick; - - proj.pushltime = time + autocvar_g_nades_napalm_ball_lifetime; - - proj.angles = vectoangles(proj.velocity); - proj.flags = FL_PROJECTILE; - IL_PUSH(g_projectiles, proj); - IL_PUSH(g_bot_dodge, proj); - proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; - - //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); - } - - - void napalm_fountain_think(entity this) - { - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - delete(this); - return; - } - - if(time >= this.ltime) - { - delete(this); - return; - } - - vector midpoint = ((this.absmin + this.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - this.velocity = this.velocity * 0.5; - - if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER) - { this.velocity_z = 200; } - - UpdateCSQCProjectile(this); - } - - napalm_damage(this, autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage, - autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime); - - this.nextthink = time + 0.1; - if(time >= this.nade_special_time) - { - this.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay; - nade_napalm_ball(this); - } - } - - void nade_napalm_boom(entity this) - { - for (int c = 0; c < autocvar_g_nades_napalm_ball_count; ++c) - nade_napalm_ball(this); - - entity fountain = new(nade_napalm_fountain); - fountain.owner = this.owner; - fountain.realowner = this.realowner; - fountain.origin = this.origin; - fountain.flags = FL_PROJECTILE; - IL_PUSH(g_projectiles, fountain); - IL_PUSH(g_bot_dodge, fountain); - setorigin(fountain, fountain.origin); - setthink(fountain, napalm_fountain_think); - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; - fountain.pushltime = fountain.ltime; - fountain.team = this.team; - set_movetype(fountain, MOVETYPE_TOSS); - fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; - fountain.bot_dodge = true; - fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; - fountain.nade_special_time = time; - setsize(fountain, '-16 -16 -16', '16 16 16'); - CSQCProjectile(fountain, true, PROJECTILE_NAPALM_FOUNTAIN, true); - } - - void nade_ice_freeze(entity freezefield, entity frost_target, float freezetime) - { - frost_target.frozen_by = freezefield.realowner; - Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); - Freeze(frost_target, 1 / freezetime, FROZEN_TEMP_DYING, false); - - Drop_Special_Items(frost_target); - } - - void nade_ice_think(entity this) - { - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - delete(this); - return; - } - - if(time >= this.ltime) - { - if ( autocvar_g_nades_ice_explode ) - { - vector expcol_min = nades_PlayerColor(this.realowner, false); - vector expcol_max = nades_PlayerColor(this.realowner, true); - entity expef = EFFECT_NADE_EXPLODE_NEUTRAL; - Send_Effect_Except(expef, this.origin + '0 0 1', '0 0 0', 1, expcol_min, expcol_max, NULL); - sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - normal_nade_boom(this); - } - delete(this); - return; - } - - - this.nextthink = time + 0.1; - - // gaussian - float randomr; - randomr = random(); - randomr = exp(-5 * randomr * randomr) * autocvar_g_nades_nade_radius; - float randomw; - randomw = random() * (2 * M_PI); - vector randomp; - randomp.x = randomr * cos(randomw); - randomp.y = randomr * sin(randomw); - randomp.z = 1; - Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, this.origin + randomp, '0 0 0', 1); - - if(time >= this.nade_special_time) - { - this.nade_special_time = time + 0.7; - - Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1); - Send_Effect(EFFECT_ICEFIELD, this.origin, '0 0 0', 1); - } - - - float current_freeze_time = this.ltime - time - 0.1; - - FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage - && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_freeze_time > 0 - && (!it.revival_time || ((time - it.revival_time) >= 1.5)) && !STAT(FROZEN, it), - { - switch (autocvar_g_nades_ice_teamcheck) - { - case 0: break; // affect everyone - default: - case 2: if(SAME_TEAM(it, this.realowner)) continue; // don't affect teammates - // fall through (check case 1 condition too) - case 1: if(it == this.realowner) continue; // don't affect the player who threw the nade - } - nade_ice_freeze(this, it, current_freeze_time); - }); - } - - void nade_ice_boom(entity this) - { - entity fountain = new(nade_ice_fountain); - fountain.owner = this.owner; - fountain.realowner = this.realowner; - fountain.origin = this.origin; - setorigin(fountain, fountain.origin); - setthink(fountain, nade_ice_think); - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_ice_freeze_time; - fountain.pushltime = fountain.wait = fountain.ltime; - fountain.team = this.team; - set_movetype(fountain, MOVETYPE_TOSS); - fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; - fountain.bot_dodge = false; - setsize(fountain, '-16 -16 -16', '16 16 16'); - fountain.nade_special_time = time + 0.3; - fountain.angles = this.angles; - - if ( autocvar_g_nades_ice_explode ) - { - setmodel(fountain, MDL_PROJECTILE_GRENADE); - entity timer = new(nade_timer); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, fountain, ""); - timer.colormap = this.colormap; - timer.glowmod = this.glowmod; - setthink(timer, nade_timer_think); - timer.nextthink = time; - timer.wait = fountain.ltime; - timer.owner = fountain; - timer.skin = 10; - } - else - setmodel(fountain, MDL_Null); - } - - void nade_translocate_boom(entity this) - { - if(this.realowner.vehicle) - return; - - setsize(this, PL_MIN_CONST-'16 16 16', PL_MAX_CONST+'16 16 16'); - - if(!move_out_of_solid(this)) - { - sprint(this.realowner, "^1Couldn't move the translocator out of solid! origin: ", vtos(this.origin), "\n"); - return; - } - - vector locout = this.origin + '0 0 1' * (1 - this.realowner.mins.z - 24); - tracebox(locout, this.realowner.mins, this.realowner.maxs, locout, MOVE_NOMONSTERS, this.realowner); - locout = trace_endpos; - - makevectors(this.realowner.angles); - - MUTATOR_CALLHOOK(PortalTeleport, this.realowner); - - TeleportPlayer(this, this.realowner, locout, this.realowner.angles, v_forward * vlen(this.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); - } - - void nade_spawn_boom(entity this) - { - entity player = this.realowner; - entity spawnloc = new(nade_spawn_loc); - setorigin(spawnloc, this.origin); - setsize(spawnloc, player.mins, player.maxs); - set_movetype(spawnloc, MOVETYPE_NONE); - spawnloc.solid = SOLID_NOT; - spawnloc.drawonlytoclient = player; - spawnloc.effects = EF_STARDUST; - spawnloc.cnt = autocvar_g_nades_spawn_count; - - if(player.nade_spawnloc) - delete(player.nade_spawnloc); - - player.nade_spawnloc = spawnloc; - } - - void nades_orb_think(entity this) - { - if(time >= this.ltime) - { - delete(this); - return; - } - - this.nextthink = time; - - if(time >= this.nade_special_time) - { - this.nade_special_time = time+0.25; - this.nade_show_particles = 1; - } - else - this.nade_show_particles = 0; - } - - entity nades_spawn_orb(entity own, entity realown, vector org, float orb_ltime, float orb_rad) - { - // NOTE: this function merely places an orb - // you must add a custom touch function to the returned entity if desired - // also set .colormod if you wish to have it colorized - entity orb = new(nades_spawn_orb); - orb.owner = own; - orb.realowner = realown; - setorigin(orb, org); - - orb.orb_lifetime = orb_ltime; // required for timers - orb.ltime = time + orb.orb_lifetime; - orb.bot_dodge = false; - orb.team = realown.team; - orb.solid = SOLID_TRIGGER; - - setmodel(orb, MDL_NADE_ORB); - orb.skin = 1; - orb.orb_radius = orb_rad; // required for fading - vector size = '1 1 1' * orb.orb_radius / 2; - setsize(orb, -size, size); - - Net_LinkEntity(orb, true, 0, orb_send); - orb.SendFlags |= 1; - - setthink(orb, nades_orb_think); - orb.nextthink = time; - - return orb; - } - - void nade_entrap_touch(entity this, entity toucher) - { - if(DIFF_TEAM(toucher, this.realowner)) // TODO: what if realowner changes team or disconnects? - { - if (!isPushable(toucher)) - return; - - float pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > 0.15) pushdeltatime = 0; - toucher.lastpushtime = time; - if(!pushdeltatime) return; - - // div0: ticrate independent, 1 = identity (not 20) - toucher.velocity = toucher.velocity * (autocvar_g_nades_entrap_strength ** pushdeltatime); - - #ifdef SVQC - UpdateCSQCProjectile(toucher); - #endif - } - - if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) - { - entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; - show_tint.nade_entrap_time = time + 0.1; - } - } - - void nade_entrap_boom(entity this) - { - entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_entrap_time, autocvar_g_nades_entrap_radius); - - settouch(orb, nade_entrap_touch); - orb.colormod = NADE_TYPE_ENTRAP.m_color; - } - - void nade_heal_touch(entity this, entity toucher) - { - float maxhealth; - float health_factor; - - if(IS_PLAYER(toucher) || IS_MONSTER(toucher) || IS_VEHICLE(toucher)) - if(!IS_DEAD(toucher)) - if(!STAT(FROZEN, toucher)) - { - health_factor = autocvar_g_nades_heal_rate*frametime/2; - if ( toucher != this.realowner ) - health_factor *= (SAME_TEAM(toucher,this)) ? autocvar_g_nades_heal_friend : autocvar_g_nades_heal_foe; - - if ( health_factor > 0 ) - { - maxhealth = (IS_MONSTER(toucher)) ? toucher.max_health : g_pickup_healthmega_max; - float hp = GetResource(toucher, RES_HEALTH); - if (hp < maxhealth) - { - if (this.nade_show_particles) - Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1); - - GiveResourceWithLimit(toucher, RES_HEALTH, health_factor, maxhealth); - } - } - else if ( health_factor < 0 ) - Damage(toucher,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,DMG_NOWEP,toucher.origin,'0 0 0'); - } - } - - void nade_heal_boom(entity this) - { - entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_heal_time, autocvar_g_nades_nade_radius); - - settouch(orb, nade_heal_touch); - orb.colormod = '1 0 0'; - } - - void nade_monster_boom(entity this) - { - if(!autocvar_g_monsters) - return; - entity e = spawn(); - e.noalign = true; // don't drop to floor - e = spawnmonster(e, this.pokenade_type, MON_Null, this.realowner, this.realowner, this.origin, false, false, 1); - if(!e) - return; // monster failed to be spawned - - if(autocvar_g_nades_pokenade_monster_lifetime > 0) - e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; - e.monster_skill = MONSTER_SKILL_INSANE; - } - - void nade_veil_touch(entity this, entity toucher) - { - if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) - { - entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; - - float tint_alpha = 0.75; - if(SAME_TEAM(toucher, this.realowner)) - { - tint_alpha = 0.45; - if(!show_tint.nade_veil_time) - { - toucher.nade_veil_prevalpha = toucher.alpha; - toucher.alpha = -1; - } - } - show_tint.nade_veil_time = time + 0.1; - } - } - - void nade_veil_boom(entity this) - { - entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_veil_time, autocvar_g_nades_veil_radius); - - settouch(orb, nade_veil_touch); - orb.colormod = NADE_TYPE_VEIL.m_color; - } - - void nade_ammo_touch(entity this, entity toucher) - { - float maxammo = 999; - float ammo_factor; - float amshells = GetResource(toucher, RES_SHELLS); - float ambullets = GetResource(toucher, RES_BULLETS); - float amrockets = GetResource(toucher, RES_ROCKETS); - float amcells = GetResource(toucher, RES_CELLS); - if(IS_PLAYER(toucher) || IS_MONSTER(toucher)) - if(!IS_DEAD(toucher)) - if(!STAT(FROZEN, toucher)) - { - ammo_factor = autocvar_g_nades_ammo_rate*frametime/2; - if ( toucher != this.realowner ) - ammo_factor *= (SAME_TEAM(toucher, this)) ? autocvar_g_nades_ammo_friend : autocvar_g_nades_ammo_foe; - - #define CHECK_AMMO_RESOURCE_LIMIT(amresource, res_resource) \ - if (amresource < maxammo) \ - GiveResourceWithLimit(toucher, res_resource, ammo_factor, maxammo); - - #define DROP_AMMO_RESOURCE(amresource, res_resource) \ - if (amresource > 0) \ - SetResource(toucher, res_resource, amresource + ammo_factor); - - if ( ammo_factor > 0 ) - { - CHECK_AMMO_RESOURCE_LIMIT(amshells, RES_SHELLS); - CHECK_AMMO_RESOURCE_LIMIT(ambullets, RES_BULLETS); - CHECK_AMMO_RESOURCE_LIMIT(amrockets, RES_ROCKETS); - CHECK_AMMO_RESOURCE_LIMIT(amcells, RES_CELLS); - - if (this.nade_show_particles) - Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1); - } - else if ( ammo_factor < 0 ) - { - //Foe drops ammo points - DROP_AMMO_RESOURCE(amshells, RES_SHELLS); - DROP_AMMO_RESOURCE(ambullets, RES_BULLETS); - DROP_AMMO_RESOURCE(amrockets, RES_ROCKETS); - DROP_AMMO_RESOURCE(amcells, RES_CELLS); - - return; - } - } - #undef CHECK_AMMO_RESOURCE_LIMIT - #undef DROP_AMMO_RESOURCE - - if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) ) - { - entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher; - show_tint.nade_ammo_time = time + 0.1; - } - } - - void nade_ammo_boom(entity this) - { - entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_ammo_time, autocvar_g_nades_nade_radius); - - settouch(orb, nade_ammo_touch); - orb.colormod = '0.66 0.33 0'; - } - - void nade_darkness_think(entity this) - { - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - { - delete(this); - return; - } - - if(time >= this.ltime) - { - if ( autocvar_g_nades_darkness_explode ) - { - vector expcol_min = nades_PlayerColor(this.realowner, false); - vector expcol_max = nades_PlayerColor(this.realowner, true); - entity expef = EFFECT_NADE_EXPLODE_NEUTRAL; - Send_Effect_Except(expef, this.origin + '0 0 1', '0 0 0', 1, expcol_min, expcol_max, NULL); - sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - normal_nade_boom(this); - } - else - Send_Effect(EFFECT_SPAWN_PURPLE, this.origin + '0 0 1', '0 0 0', 1); - - delete(this); - return; - } - - this.nextthink = time + 0.1; - - // gaussian - float randomr; - randomr = random(); - randomr = exp(-5 * randomr * randomr) * autocvar_g_nades_nade_radius; - float randomw; - randomw = random() * (2 * M_PI); - vector randomp; - randomp.x = randomr * cos(randomw); - randomp.y = randomr * sin(randomw); - randomp.z = 1; - Send_Effect(EFFECT_DARKFIELD, this.origin + randomp, '0 0 0', 1); - - if(time >= this.nade_special_time) - { - this.nade_special_time = time + 0.7; - Send_Effect(EFFECT_DARKFIELD, this.origin, '0 0 0', 1); - } - - - float current_dark_time = this.ltime - time - 0.1; - - FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage - && !IS_DEAD(it) && GetResource(it, RES_HEALTH) > 0 && current_dark_time > 0 && IS_REAL_CLIENT(it), - { - switch (autocvar_g_nades_darkness_teamcheck) - { - case 0: break; // affect everyone - default: - case 2: if(SAME_TEAM(it, this.realowner)) continue; // don't affect teammates - // fall through (check case 1 condition too) - case 1: if(it == this.realowner) continue; // don't affect the player who threw the nade - } - STAT(NADE_DARKNESS_TIME, it) = time + 0.1; - }); - } - - void nade_darkness_boom(entity this) - { - entity fountain = new(nade_darkness_fountain); - fountain.owner = this.owner; - fountain.realowner = this.realowner; - fountain.origin = this.origin; - setorigin(fountain, fountain.origin); - setthink(fountain, nade_darkness_think); - fountain.nextthink = time; - fountain.ltime = time + autocvar_g_nades_darkness_time; - fountain.pushltime = fountain.wait = fountain.ltime; - fountain.team = this.team; - set_movetype(fountain, MOVETYPE_TOSS); - fountain.projectiledeathtype = DEATH_NADE.m_id; - fountain.bot_dodge = false; - setsize(fountain, '-16 -16 -16', '16 16 16'); - fountain.nade_special_time = time + 0.3; - fountain.angles = this.angles; - - if ( autocvar_g_nades_darkness_explode ) - { - setmodel(fountain, MDL_PROJECTILE_GRENADE); - entity timer = new(nade_timer); - setmodel(timer, MDL_NADE_TIMER); - setattachment(timer, fountain, ""); - timer.colormap = this.colormap; - timer.glowmod = this.glowmod; - setthink(timer, nade_timer_think); - timer.nextthink = time; - timer.wait = fountain.ltime; - timer.owner = fountain; - timer.skin = 10; - } - else - setmodel(fountain, MDL_Null); - } - - void nade_boom(entity this) - { - entity expef = NULL; - bool nade_blast = true; - vector expcol_min = '0 0 0', expcol_max = '0 0 0'; - - #define SET_NADE_EFFECT(nade_type, blast, exp_effect, exp_color_min, exp_color_max) \ - case nade_type: \ - nade_blast = blast; \ - expef = exp_effect; \ - expcol_min = exp_color_min; \ - expcol_max = exp_color_max; \ - break - - switch ( REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)) ) - { - SET_NADE_EFFECT(NADE_TYPE_NAPALM, autocvar_g_nades_napalm_blast, EFFECT_EXPLOSION_MEDIUM, '0 0 0', '0 0 0'); - SET_NADE_EFFECT(NADE_TYPE_ICE, false, EFFECT_ELECTRO_COMBO, '0 0 0', '0 0 0'); - SET_NADE_EFFECT(NADE_TYPE_TRANSLOCATE, false, NULL, '0 0 0', '0 0 0'); - SET_NADE_EFFECT(NADE_TYPE_MONSTER, true, (!autocvar_g_monsters) ? EFFECT_NADE_EXPLODE_NEUTRAL : NULL, nades_PlayerColor(this.realowner, false), nades_PlayerColor(this.realowner, true)); - SET_NADE_EFFECT(NADE_TYPE_SPAWN, false, EFFECT_SPAWN_NEUTRAL, nades_PlayerColor(this.realowner, false), nades_PlayerColor(this.realowner, true)); - SET_NADE_EFFECT(NADE_TYPE_HEAL, false, EFFECT_SPAWN_NEUTRAL, '1 0 0', '1 0 0'); - SET_NADE_EFFECT(NADE_TYPE_ENTRAP, false, EFFECT_SPAWN_NEUTRAL, '1 1 0', '1 1 0'); - SET_NADE_EFFECT(NADE_TYPE_VEIL, false, EFFECT_SPAWN_NEUTRAL, '0 0 0', '0 0 0'); - SET_NADE_EFFECT(NADE_TYPE_AMMO, false, EFFECT_SPAWN_NEUTRAL, '0.66 0.33 0', '0.66 0.33 0'); - SET_NADE_EFFECT(NADE_TYPE_DARKNESS, false, EFFECT_EXPLOSION_MEDIUM, '0 0 0', '0 0 0'); - SET_NADE_EFFECT(NADE_TYPE_NORMAL, true, EFFECT_NADE_EXPLODE_NEUTRAL, nades_PlayerColor(this.realowner, false), nades_PlayerColor(this.realowner, true)); - default: expef = EFFECT_NADE_EXPLODE_NEUTRAL; expcol_min = nades_PlayerColor(this.realowner, false); expcol_max = nades_PlayerColor(this.realowner, true); break; - } - #undef SET_NADE_EFFECT - - if(expef) - Send_Effect_Except(expef, findbetterlocation(this.origin, 8), '0 0 0', 1, expcol_min, expcol_max, NULL); - - sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); - sound(this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - - this.event_damage = func_null; // prevent somehow calling damage in the next call - - if(nade_blast) - normal_nade_boom(this); - - if(this.takedamage) - switch ( REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)) ) - { - case NADE_TYPE_NAPALM: nade_napalm_boom(this); break; - case NADE_TYPE_ICE: nade_ice_boom(this); break; - case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(this); break; - case NADE_TYPE_SPAWN: nade_spawn_boom(this); break; - case NADE_TYPE_HEAL: nade_heal_boom(this); break; - case NADE_TYPE_MONSTER: nade_monster_boom(this); break; - case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break; - case NADE_TYPE_VEIL: nade_veil_boom(this); break; - case NADE_TYPE_AMMO: nade_ammo_boom(this); break; - case NADE_TYPE_DARKNESS: nade_darkness_boom(this); break; - } - - IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, - { - RemoveHook(it); - }); - - delete(this); - } - - void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, string pntype); - void nade_pickup(entity this, entity thenade) - { - spawn_held_nade(this, thenade.realowner, autocvar_g_nades_pickup_time, STAT(NADE_BONUS_TYPE, thenade), thenade.pokenade_type); - - // set refire so player can't even - this.nade_refire = time + autocvar_g_nades_nade_refire; - STAT(NADE_TIMER, this) = 0; - - if(this.nade) - this.nade.nade_time_primed = thenade.nade_time_primed; - } - - bool CanThrowNade(entity this); - void nade_touch(entity this, entity toucher) - { - if(toucher) - UpdateCSQCProjectile(this); - - if(toucher == this.realowner) - return; // no this impacts - - if(autocvar_g_nades_pickup) - if(time >= this.spawnshieldtime) - if(!toucher.nade && GetResource(this, RES_HEALTH) == this.max_health) // no boosted shot pickups, thank you very much - if(CanThrowNade(toucher)) // prevent some obvious things, like dead players - if(IS_REAL_CLIENT(toucher)) // above checks for IS_PLAYER, don't need to do it here - { - nade_pickup(toucher, this); - sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - delete(this); - return; - } - /*float is_weapclip = 0; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NODRAW) - if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NONSOLID)) - if (!(trace_dphitcontents & DPCONTENTS_OPAQUE)) - is_weapclip = 1;*/ - if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) - { - IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, - { - RemoveHook(it); - }); - delete(this); - return; - } - - PROJECTILE_TOUCH(this, toucher); - - //setsize(this, '-2 -2 -2', '2 2 2'); - //UpdateCSQCProjectile(this); - if(GetResource(this, RES_HEALTH) == this.max_health) - { - spamsound(this, CH_SHOTS, SND_GRENADE_BOUNCE_RANDOM(), VOL_BASE, ATTEN_NORM); - return; - } - - this.enemy = toucher; - nade_boom(this); - } - - void nade_beep(entity this) - { - sound(this, CH_SHOTS_SINGLE, SND_NADE_BEEP, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - setthink(this, nade_boom); - this.nextthink = max(this.wait, time); - } - - void nade_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) - { - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - this.takedamage = DAMAGE_NO; - nade_boom(this); - return; - } - - if(STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_TRANSLOCATE.m_id || STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_SPAWN.m_id) - return; - - if (MUTATOR_CALLHOOK(Nade_Damage, this, DEATH_WEAPONOF(deathtype), force, damage)) {} - else if(DEATH_ISWEAPON(deathtype, WEP_BLASTER)) - { - force *= 1.5; - damage = 0; - } - else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) || DEATH_ISWEAPON(deathtype, WEP_OVERKILL_NEX)) - { - force *= 6; - damage = this.max_health * 0.55; - } - else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN) || DEATH_ISWEAPON(deathtype, WEP_OVERKILL_MACHINEGUN)) - damage = this.max_health * 0.1; - else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) || DEATH_ISWEAPON(deathtype, WEP_OVERKILL_SHOTGUN)) // WEAPONTODO - { - if(!(deathtype & HITTYPE_SECONDARY)) - damage = this.max_health * 1.15; - } - - // melee slaps - entity death_weapon = DEATH_WEAPONOF(deathtype); - if(((deathtype & HITTYPE_SECONDARY) ? (death_weapon.spawnflags & WEP_TYPE_MELEE_SEC) : (death_weapon.spawnflags & WEP_TYPE_MELEE_PRI))) - { - damage = this.max_health * 0.1; - force *= 10; - } - - this.velocity += force; - UpdateCSQCProjectile(this); - - if(damage <= 0 || ((IS_ONGROUND(this)) && IS_PLAYER(attacker))) - return; - - float hp = GetResource(this, RES_HEALTH); - if(hp == this.max_health) - { - sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - this.nextthink = max(time + this.nade_lifetime, time); - setthink(this, nade_beep); - } - - hp -= damage; - SetResource(this, RES_HEALTH, hp); - - if(STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_TRANSLOCATE.m_id && STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_SPAWN.m_id) - if(STAT(NADE_BONUS_TYPE, this) != NADE_TYPE_HEAL.m_id || IS_PLAYER(attacker)) - this.realowner = attacker; - - if(hp <= 0) - { - if(autocvar_g_nades_spawn_destroy_damage > 0 && STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_SPAWN.m_id) - Damage(this.realowner, attacker, attacker, autocvar_g_nades_spawn_destroy_damage, DEATH_TOUCHEXPLODE.m_id, DMG_NOWEP, this.realowner.origin, '0 0 0'); - - if(autocvar_g_nades_translocate_destroy_damage > 0 && STAT(NADE_BONUS_TYPE, this) == NADE_TYPE_TRANSLOCATE.m_id) - { - Damage(this.realowner, attacker, attacker, autocvar_g_nades_translocate_destroy_damage, DEATH_TOUCHEXPLODE.m_id, DMG_NOWEP, this.realowner.origin, '0 0 0'); - W_PrepareExplosionByDamage(this, this.realowner, nade_boom); // Don't change the owner - - return; - } - - W_PrepareExplosionByDamage(this, attacker, nade_boom); - } - else - nade_burn_spawn(this); - } - - void toss_nade(entity e, bool set_owner, vector _velocity, float _time) - { - if(e.nade == NULL) - return; - - entity _nade = e.nade; - e.nade = NULL; - - if(e.fake_nade) - delete(e.fake_nade); - e.fake_nade = NULL; - - Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_NADES); - - makevectors(e.v_angle); - - // NOTE: always throw from first weapon entity? - W_SetupShot(e, _nade.weaponentity_fld, false, false, SND_Null, CH_WEAPON_A, 0, DEATH_NADE.m_id); - - vector offset = (v_forward * autocvar_g_nades_throw_offset.x) - + (v_right * autocvar_g_nades_throw_offset.y) - + (v_up * autocvar_g_nades_throw_offset.z); - - setorigin(_nade, w_shotorg + offset); - //setmodel(_nade, MDL_PROJECTILE_NADE); - //setattachment(_nade, NULL, ""); - PROJECTILE_MAKETRIGGER(_nade); - if(STAT(NADES_SMALL, e)) - setsize(_nade, '-8 -8 -8', '8 8 8'); - else - setsize(_nade, '-16 -16 -16', '16 16 16'); - set_movetype(_nade, MOVETYPE_BOUNCE); - - tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, MOVE_NOMONSTERS, _nade); - if (trace_startsolid) - setorigin(_nade, e.origin); - - if(e.v_angle.x >= 70 && e.v_angle.x <= 110 && PHYS_INPUT_BUTTON_CROUCH(e)) - _nade.velocity = '0 0 100'; - else if(autocvar_g_nades_nade_newton_style == 1) - _nade.velocity = e.velocity + _velocity; - else if(autocvar_g_nades_nade_newton_style == 2) - _nade.velocity = _velocity; - else - _nade.velocity = W_CalculateProjectileVelocity(e, e.velocity, _velocity, true); - - if(set_owner) - _nade.realowner = e; - - settouch(_nade, nade_touch); - _nade.spawnshieldtime = time + 0.1; // prevent instantly picking up again - SetResource(_nade, RES_HEALTH, autocvar_g_nades_nade_health); - _nade.max_health = GetResource(_nade, RES_HEALTH); - _nade.takedamage = DAMAGE_AIM; - _nade.event_damage = nade_damage; - setcefc(_nade, func_null); - _nade.exteriormodeltoclient = NULL; - _nade.traileffectnum = 0; - _nade.teleportable = true; - _nade.pushable = true; - _nade.gravity = 1; - _nade.missile_flags = MIF_SPLASH | MIF_ARC; - _nade.damagedbycontents = true; - IL_PUSH(g_damagedbycontents, _nade); - _nade.angles = vectoangles(_nade.velocity); - _nade.flags = FL_PROJECTILE; - IL_PUSH(g_projectiles, _nade); - IL_PUSH(g_bot_dodge, _nade); - _nade.projectiledeathtype = DEATH_NADE.m_id; - _nade.toss_time = time; - _nade.solid = SOLID_CORPSE; //((STAT(NADE_BONUS_TYPE, _nade) == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); - - if(STAT(NADE_BONUS_TYPE, _nade) == NADE_TYPE_TRANSLOCATE.m_id || STAT(NADE_BONUS_TYPE, _nade) == NADE_TYPE_SPAWN.m_id) - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - else - _nade.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - - nade_spawn(_nade); - - if(_time) - { - setthink(_nade, nade_boom); - _nade.nextthink = _time; - } - - e.nade_refire = time + autocvar_g_nades_nade_refire; - STAT(NADE_TIMER, e) = 0; - } - - void nades_GiveBonus(entity player, float score) - { - if (autocvar_g_nades) - if (autocvar_g_nades_bonus) - if (IS_REAL_CLIENT(player)) - if (IS_PLAYER(player) && STAT(NADE_BONUS, player) < autocvar_g_nades_bonus_max) - if (!STAT(FROZEN, player)) - if (!IS_DEAD(player)) - { - if ( STAT(NADE_BONUS_SCORE, player) < 1 ) - STAT(NADE_BONUS_SCORE, player) += score/autocvar_g_nades_bonus_score_max; - - if ( STAT(NADE_BONUS_SCORE, player) >= 1 ) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); - play2(player, SND(NADE_BONUS)); - STAT(NADE_BONUS, player)++; - STAT(NADE_BONUS_SCORE, player) -= 1; - } - } - } - - /** Remove all bonus nades from a player */ - void nades_RemoveBonus(entity player) - { - STAT(NADE_BONUS, player) = STAT(NADE_BONUS_SCORE, player) = 0; - } - - MUTATOR_HOOKFUNCTION(nades, PutClientInServer) - { - entity player = M_ARGV(0, entity); - - nades_RemoveBonus(player); - } - - bool nade_customize(entity this, entity client) - { - //if(IS_SPEC(client)) { return false; } - if(client == this.exteriormodeltoclient || (IS_SPEC(client) && client.enemy == this.exteriormodeltoclient)) - { - // somewhat hide the model, but keep the glow - //this.effects = 0; - if(this.traileffectnum) - this.traileffectnum = 0; - this.alpha = -1; - } - else - { - //this.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - if(!this.traileffectnum) - { - entity nade = REGISTRY_GET(Nades, STAT(NADE_BONUS_TYPE, this)); - this.traileffectnum = _particleeffectnum(Nade_TrailEffect(nade.m_projectile[0], this.team).eent_eff_name); - } - this.alpha = 1; - } - - return true; - } - - void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, string pntype) - { - entity n = new(nade), fn = new(fake_nade); - - n.pokenade_type = pntype; - - if(ntype == 0) // random nade - ntype = floor(random() * (REGISTRY_COUNT(Nades) - 1)) + 1; - Nade def = REGISTRY_GET(Nades, ntype); - if(def == NADE_TYPE_Null) - def = NADE_TYPE_NORMAL; - - STAT(NADE_BONUS_TYPE, n) = def.m_id; - - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - - setmodel(n, MDL_PROJECTILE_NADE); - //setattachment(n, player, "bip01 l hand"); - n.exteriormodeltoclient = player; - setcefc(n, nade_customize); - n.traileffectnum = _particleeffectnum(Nade_TrailEffect(def.m_projectile[0], player.team).eent_eff_name); - n.colormod = def.m_color; - n.realowner = nowner; - n.colormap = player.colormap; - n.glowmod = player.glowmod; - n.wait = time + max(0, ntime); - n.nade_time_primed = time; - setthink(n, nade_beep); - n.nextthink = max(n.wait - 3, time); - n.projectiledeathtype = DEATH_NADE.m_id; - n.weaponentity_fld = weaponentity; - n.nade_lifetime = ntime; - n.alpha = def.m_alpha; - - setmodel(fn, MDL_NADE_VIEW); - //setattachment(fn, player.(weaponentity), ""); - fn.viewmodelforclient = player; - fn.realowner = fn.owner = player; - fn.colormod = def.m_color; - fn.colormap = player.colormap; - fn.glowmod = player.glowmod; - setthink(fn, SUB_Remove); - fn.nextthink = n.wait; - fn.weaponentity_fld = weaponentity; - fn.alpha = def.m_alpha; - - player.nade = n; - player.fake_nade = fn; - } - - void nade_prime(entity this) - { - if(autocvar_g_nades_bonus_only && !STAT(NADE_BONUS, this)) - return; // only allow bonus nades - - // TODO: handle old nade if it exists? - if(this.nade) - delete(this.nade); - this.nade = NULL; - - if(this.fake_nade) - delete(this.fake_nade); - this.fake_nade = NULL; - - int ntype; - string pntype = this.pokenade_type; - - if(StatusEffects_active(STATUSEFFECT_Strength, this) && autocvar_g_nades_bonus_onstrength) - ntype = STAT(NADE_BONUS_TYPE, this); - else if (STAT(NADE_BONUS, this) >= 1) - { - ntype = STAT(NADE_BONUS_TYPE, this); - pntype = this.pokenade_type; - STAT(NADE_BONUS, this) -= 1; - } - else - { - ntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_nade_type : autocvar_g_nades_nade_type); - pntype = ((autocvar_g_nades_client_select) ? CS_CVAR(this).cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type); - } - - spawn_held_nade(this, this, autocvar_g_nades_nade_lifetime, ntype, pntype); - } - - bool CanThrowNade(entity this) - { - return !(this.vehicle || !autocvar_g_nades || IS_DEAD(this) || !IS_PLAYER(this) || weaponLocked(this)); - } - - .bool nade_altbutton; - - void nades_CheckThrow(entity this) - { - if(!CanThrowNade(this)) - return; - - entity held_nade = this.nade; - if (!held_nade) - { - this.nade_altbutton = true; - if(time > this.nade_refire) - { - nade_prime(this); - this.nade_refire = time + autocvar_g_nades_nade_refire; - } - } - else - { - this.nade_altbutton = false; - if (time >= held_nade.nade_time_primed + 1) { - makevectors(this.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - vector dir = (v_forward * 0.75 + v_up * 0.2 + v_right * 0.05); - dir = W_CalculateSpread(dir, autocvar_g_nades_spread, autocvar_g_weaponspreadfactor, autocvar_g_projectiles_spread_style); - toss_nade(this, true, dir * _force, 0); - } - } - } - - void nades_Clear(entity player) - { - if(player.nade) - delete(player.nade); - if(player.fake_nade) - delete(player.fake_nade); - - player.nade = player.fake_nade = NULL; - STAT(NADE_TIMER, player) = 0; - } - - int nades_CheckTypes(entity player, int cl_ntype) - { - // TODO check what happens without this patch - #define CL_NADE_TYPE_CHECK(nade_ent, nade_cvar) \ - case nade_ent.m_id: if (nade_cvar) return cl_ntype - - switch (cl_ntype) - { - case 0: return 0; // random nade - CL_NADE_TYPE_CHECK(NADE_TYPE_NAPALM, autocvar_g_nades_napalm); - CL_NADE_TYPE_CHECK(NADE_TYPE_ICE, autocvar_g_nades_ice); - CL_NADE_TYPE_CHECK(NADE_TYPE_TRANSLOCATE, autocvar_g_nades_translocate); - CL_NADE_TYPE_CHECK(NADE_TYPE_SPAWN, autocvar_g_nades_spawn); - CL_NADE_TYPE_CHECK(NADE_TYPE_HEAL, autocvar_g_nades_heal); - CL_NADE_TYPE_CHECK(NADE_TYPE_MONSTER, autocvar_g_nades_pokenade); - CL_NADE_TYPE_CHECK(NADE_TYPE_ENTRAP, autocvar_g_nades_entrap); - CL_NADE_TYPE_CHECK(NADE_TYPE_VEIL, autocvar_g_nades_veil); - CL_NADE_TYPE_CHECK(NADE_TYPE_AMMO, autocvar_g_nades_ammo); - CL_NADE_TYPE_CHECK(NADE_TYPE_DARKNESS, autocvar_g_nades_darkness); - } - return NADE_TYPE_NORMAL.m_id; // default to NADE_TYPE_NORMAL for unknown nade types - #undef CL_NADE_TYPE_CHECK - } - - MUTATOR_HOOKFUNCTION(nades, VehicleEnter) - { - entity player = M_ARGV(0, entity); - - if(player.nade) - toss_nade(player, true, '0 0 100', max(player.nade.wait, time + 0.05)); - } - - CLASS(NadeOffhand, OffhandWeapon) - METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) - { - entity held_nade = player.nade; - - if (!CanThrowNade(player)) return; - if (!(time > player.nade_refire)) return; - if (key_pressed) { - if (!held_nade) { - nade_prime(player); - held_nade = player.nade; - } - } else if (time >= held_nade.nade_time_primed + 1) { - if (held_nade) { - makevectors(player.v_angle); - float _force = time - held_nade.nade_time_primed; - _force /= autocvar_g_nades_nade_lifetime; - _force = autocvar_g_nades_nade_minforce + (_force * (autocvar_g_nades_nade_maxforce - autocvar_g_nades_nade_minforce)); - vector dir = (v_forward * 0.7 + v_up * 0.2 + v_right * 0.1); - dir = W_CalculateSpread(dir, autocvar_g_nades_spread, autocvar_g_weaponspreadfactor, autocvar_g_projectiles_spread_style); - toss_nade(player, false, dir * _force, 0); - } - } - } - ENDCLASS(NadeOffhand) - NadeOffhand OFFHAND_NADE; - REGISTER_MUTATOR(nades, autocvar_g_nades) - { - MUTATOR_ONADD - { - OFFHAND_NADE = NEW(NadeOffhand); - } - return 0; - } - - MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) - { - entity player = M_ARGV(0, entity); - - if (player.offhand != OFFHAND_NADE || (STAT(WEAPONS, player) & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { - nades_CheckThrow(player); - return true; - } - } - - #ifdef IN_REVIVING_RANGE - #undef IN_REVIVING_RANGE - #endif - - // returns true if player is reviving it - #define IN_REVIVING_RANGE(player, it, revive_extra_size) \ - (it != player && !IS_DEAD(it) && SAME_TEAM(it, player) \ - && boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax)) - - MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) - { - entity player = M_ARGV(0, entity); - - if (!IS_PLAYER(player)) { return; } - - if (player.nade && (player.offhand != OFFHAND_NADE || (STAT(WEAPONS, player) & WEPSET(HOOK)))) - OFFHAND_NADE.offhand_think(OFFHAND_NADE, player, player.nade_altbutton); - - entity held_nade = player.nade; - if (held_nade) - { - STAT(NADE_TIMER, player) = bound(0, (time - held_nade.nade_time_primed) / held_nade.nade_lifetime, 1); - // LOG_TRACEF("%d %d", STAT(NADE_TIMER, player), time - held_nade.nade_time_primed); - makevectors(player.angles); - held_nade.velocity = player.velocity; - setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); - held_nade.angles_y = player.angles.y; - - if (time + 0.1 >= held_nade.wait) - { - toss_nade(player, false, '0 0 0', time + 0.05); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_THROW); - } - } - - if(IS_PLAYER(player)) - { - if ( autocvar_g_nades_bonus && autocvar_g_nades ) - { - entity key; - float key_count = 0; - FOR_EACH_KH_KEY(key) if(key.owner == player) { ++key_count; } - - float time_score; - if(GameRules_scoring_is_vip(player)) - time_score = autocvar_g_nades_bonus_score_time_flagcarrier; - else - time_score = autocvar_g_nades_bonus_score_time; - - if(key_count) - time_score = autocvar_g_nades_bonus_score_time_flagcarrier * key_count; // multiply by the number of keys the player is holding - - if(autocvar_g_nades_bonus_client_select) - { - STAT(NADE_BONUS_TYPE, player) = nades_CheckTypes(player, CS_CVAR(player).cvar_cl_nade_type); - player.pokenade_type = CS_CVAR(player).cvar_cl_pokenade_type; - } - else - { - STAT(NADE_BONUS_TYPE, player) = autocvar_g_nades_bonus_type; - player.pokenade_type = autocvar_g_nades_pokenade_monster_type; - } - - STAT(NADE_BONUS_TYPE, player) = bound(0, STAT(NADE_BONUS_TYPE, player), REGISTRY_COUNT(Nades)); - - if(STAT(NADE_BONUS_SCORE, player) >= 0 && autocvar_g_nades_bonus_score_max) - nades_GiveBonus(player, time_score / autocvar_g_nades_bonus_score_max); - } - else - { - STAT(NADE_BONUS, player) = STAT(NADE_BONUS_SCORE, player) = 0; - } - - if(player.nade_veil_time && player.nade_veil_time <= time) - { - player.nade_veil_time = 0; - if(player.vehicle) - player.vehicle.alpha = player.vehicle.nade_veil_prevalpha; - else - player.alpha = player.nade_veil_prevalpha; - } - } - - if (!(frametime && IS_PLAYER(player))) - return true; - - entity revivers_last = NULL; - entity revivers_first = NULL; - - bool player_is_reviving = false; - int n = 0; - vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; - FOREACH_CLIENT(IS_PLAYER(it) && IN_REVIVING_RANGE(player, it, revive_extra_size), { - // check if player is reviving anyone - if (STAT(FROZEN, it) == FROZEN_TEMP_DYING) - { - if ((STAT(FROZEN, player) == FROZEN_TEMP_DYING)) - continue; - if (!IN_REVIVING_RANGE(player, it, revive_extra_size)) - continue; - player_is_reviving = true; - break; - } - - if (!(STAT(FROZEN, player) == FROZEN_TEMP_DYING)) - continue; // both player and it are NOT frozen - if (revivers_last) - revivers_last.chain = it; - revivers_last = it; - if (!revivers_first) - revivers_first = it; - ++n; - }); - if (revivers_last) - revivers_last.chain = NULL; - - if (!n) // no teammate nearby - { - // freezetag already resets revive progress - if (!g_freezetag && !STAT(FROZEN, player) && !player_is_reviving) - STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody - } - else if (n > 0 && STAT(FROZEN, player) == FROZEN_TEMP_DYING) // OK, there is at least one teammate reviving us - { - STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); - // undo what PlayerPreThink did - STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * player.revive_speed, 1); - SetResource(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * start_health)); - - if(STAT(REVIVE_PROGRESS, player) >= 1) - { - Unfreeze(player, false); - - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, revivers_first.netname); - Send_Notification(NOTIF_ONE, revivers_first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); - } - - for(entity it = revivers_first; it; it = it.chain) - STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player); - } - } - - MUTATOR_HOOKFUNCTION(nades, PlayerPhysics_UpdateStats) - { - entity player = M_ARGV(0, entity); - // these automatically reset, no need to worry - - if(player.nade_entrap_time > time) - STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nades_entrap_speed; - } - - MUTATOR_HOOKFUNCTION(nades, MonsterMove) - { - entity mon = M_ARGV(0, entity); - - if (mon.nade_entrap_time > time) - { - M_ARGV(1, float) *= autocvar_g_nades_entrap_speed; // run speed - M_ARGV(2, float) *= autocvar_g_nades_entrap_speed; // walk speed - } - - if (mon.nade_veil_time && mon.nade_veil_time <= time) - { - mon.alpha = mon.nade_veil_prevalpha; - mon.nade_veil_time = 0; - } - } - - MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) - { - entity player = M_ARGV(0, entity); - - if (StatusEffects_active(STATUSEFFECT_SpawnShield, player)) - player.nade_refire = StatusEffects_gettime(STATUSEFFECT_SpawnShield, player); - else - player.nade_refire = time; - - if (!autocvar_g_nades_onspawn) - player.nade_refire += autocvar_g_nades_nade_refire; - - if(autocvar_g_nades_bonus_client_select) - STAT(NADE_BONUS_TYPE, player) = CS_CVAR(player).cvar_cl_nade_type; - - STAT(NADE_TIMER, player) = 0; - - if (!player.offhand) player.offhand = OFFHAND_NADE; - - if(player.nade_spawnloc) + switch (proj) { - setorigin(player, player.nade_spawnloc.origin); - player.nade_spawnloc.cnt -= 1; - - if(player.nade_spawnloc.cnt <= 0) - { - delete(player.nade_spawnloc); - player.nade_spawnloc = NULL; - } - - if(autocvar_g_nades_spawn_health_respawn > 0) - SetResource(player, RES_HEALTH, autocvar_g_nades_spawn_health_respawn); + case PROJECTILE_NADE: return EFFECT_NADE_TRAIL(nade_team); + case PROJECTILE_NADE_BURN: return EFFECT_NADE_TRAIL_BURN(nade_team); } - } - - MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) - { - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - if(frag_target.nade) - if(!STAT(FROZEN, frag_target) || !autocvar_g_freezetag_revive_nade) - toss_nade(frag_target, true, '0 0 100', max(frag_target.nade.wait, time + 0.05)); - - if(IS_PLAYER(frag_attacker)) - { - float killcount_bonus = ((CS(frag_attacker).killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * CS(frag_attacker).killcount, autocvar_g_nades_bonus_score_medium) - : autocvar_g_nades_bonus_score_minor); - if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) - nades_RemoveBonus(frag_attacker); - else if(GameRules_scoring_is_vip(frag_target)) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium); - else if(autocvar_g_nades_bonus_score_spree && CS(frag_attacker).killcount > 1) + FOREACH(Nades, true, { + for (int j = 0; j < 2; ++j) { - #define SPREE_ITEM(counta,countb,center,normal,gentle) \ - case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; } - switch(CS(frag_attacker).killcount) + if (it.m_projectile[j] == proj) { - KILL_SPREE_LIST - default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break; + string trail = it.m_trail[j].eent_eff_name; + if (trail) return it.m_trail[j]; + break; } - #undef SPREE_ITEM } - else - nades_GiveBonus(frag_attacker, killcount_bonus); - } - - nades_RemoveBonus(frag_target); - } - - MUTATOR_HOOKFUNCTION(nades, Damage_Calculate) - { - entity frag_inflictor = M_ARGV(0, entity); - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - - if(autocvar_g_freezetag_revive_nade && STAT(FROZEN, frag_target) && frag_attacker == frag_target && frag_deathtype == DEATH_NADE.m_id) - if(time - frag_inflictor.toss_time <= 0.1) - { - Unfreeze(frag_target, false); - SetResource(frag_target, RES_HEALTH, autocvar_g_freezetag_revive_nade_health); - Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); - M_ARGV(4, float) = 0; - M_ARGV(6, vector) = '0 0 0'; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname); - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF); - } - } - - MUTATOR_HOOKFUNCTION(nades, MonsterDies) - { - entity frag_target = M_ARGV(0, entity); - entity frag_attacker = M_ARGV(1, entity); - - if(IS_PLAYER(frag_attacker)) - if(DIFF_TEAM(frag_attacker, frag_target)) - if(!(frag_target.spawnflags & MONSTERFLAG_SPAWNED)) - nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); - } - - MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) - { - entity frag_target = M_ARGV(0, entity); - - if(frag_target.nade) - toss_nade(frag_target, true, '0 0 0', time + 0.05); - } - - void nades_RemovePlayer(entity this) - { - nades_Clear(this); - nades_RemoveBonus(this); - } - - MUTATOR_HOOKFUNCTION(nades, MakePlayerObserver) { entity player = M_ARGV(0, entity); nades_RemovePlayer(player); } - MUTATOR_HOOKFUNCTION(nades, ClientDisconnect) { entity player = M_ARGV(0, entity); nades_RemovePlayer(player); } - MUTATOR_HOOKFUNCTION(nades, reset_map_global) - { - FOREACH_CLIENT(IS_PLAYER(it), - { - nades_RemovePlayer(it); }); - } - - MUTATOR_HOOKFUNCTION(nades, SpectateCopy) - { - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - STAT(NADE_TIMER, client) = STAT(NADE_TIMER, spectatee); - STAT(NADE_BONUS_TYPE, client) = STAT(NADE_BONUS_TYPE, spectatee); - client.pokenade_type = spectatee.pokenade_type; - STAT(NADE_BONUS, client) = STAT(NADE_BONUS, spectatee); - STAT(NADE_BONUS_SCORE, client) = STAT(NADE_BONUS_SCORE, spectatee); + return EFFECT_Null; } - MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString) - { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Nades"); - } - - MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString) - { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades"); - } - - #endif + #endif // GAMEQC +#ifdef MENUQC +#include "../hook/hook.qh" +#include "../offhand_blaster/offhand_blaster.qh" +#include "../overkill/overkill.qh" + +METHOD(MutatorNades, describe, string(MutatorNades this)) +{ + TC(MutatorNades, this); + return sprintf(_("The %s mutator gives all players offhand nades that can be primed then thrown by pressing ^3drop weapon^7 twice. " + "If the %s and %s mutators are disabled, they can also be thrown by holding then releasing ^3+hook^7\n\n" + "There are multiple types of nades that can be selected, and many options available regarding this mutator, " + "so they may act a little differently depending on the server you play on\n\n" + "This mutator is enabled by default in %s"), + COLORED_NAME(this), COLORED_NAME(MUTATOR_offhand_blaster), COLORED_NAME(MUTATOR_hook), COLORED_NAME(MUTATOR_ok)); +} - #endif ++#endif // MENUQC diff --cc qcsrc/common/mutators/mutator/nades/nades.qh index 7b6fe39c9d,b328d2ed79..280d7510d6 --- a/qcsrc/common/mutators/mutator/nades/nades.qh +++ b/qcsrc/common/mutators/mutator/nades/nades.qh @@@ -125,24 -16,28 +16,38 @@@ CLASS(Nade, Object ATTRIB(Nade, m_alpha, float, 1); ATTRIBARRAY(Nade, m_projectile, int, 2); ATTRIBARRAY(Nade, m_trail, entity, 2); - METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) { - returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon)); + METHOD(Nade, display, void(Nade this, void(string name, string icon) returns)) { + TC(Nade, this); + returns(this.m_name, this.m_icon ? sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon) : string_null); } +#ifdef MENUQC + METHOD(Nade, describe, string(Nade this)) { + TC(Nade, this); + return SUPER(Nade).describe(this); + } +#endif ENDCLASS(Nade) -REGISTER_NADE(Null, NEW(Nade)); +CLASS(NullNade, Nade) + ATTRIB(NullNade, m_hidden, bool, true); +ENDCLASS(NullNade) +REGISTER(Nades, NADE_TYPE, Null, m_id, NEW(NullNade)); REGISTRY_DEFINE_GET(Nades, NADE_TYPE_Null) + // use slots 70-100 + const int PROJECTILE_NADE = 71; + const int PROJECTILE_NADE_BURN = 72; + // the rest of these are defined in their individual files like nade/napalm.qh + + #ifdef GAMEQC + #include "effects.inc" + + #define NADE_PROJECTILE(i, projectile, trail) MACRO_BEGIN \ + this.m_projectile[i] = projectile; \ + this.m_trail[i] = trail; \ + MACRO_END + #endif // GAMEQC + Nade Nade_FromProjectile(int proj) { FOREACH(Nades, true, { @@@ -164,60 -55,3 +65,13 @@@ REPLICATE_INIT(int, cvar_cl_nade_type); REPLICATE_INIT(string, cvar_cl_pokenade_type); #endif + - #ifdef SVQC - - .entity nade; - .entity fake_nade; - .float nade_refire; - .float nade_special_time; - .string pokenade_type; - .entity nade_damage_target; - .int cvar_cl_nade_type; - .string cvar_cl_pokenade_type; - .float toss_time; - .float nade_show_particles; - .float nade_veil_time; - .float nade_veil_prevalpha; - .float nade_entrap_time; - .float nade_ammo_time; - .float nade_darkness_time; - - bool orb_send(entity this, entity to, int sf); - - // Remove nades that are being thrown - void nades_Clear(entity player); - - // Give a bonus grenade to a player - void nades_GiveBonus(entity player, float score); - - /** - * called to adjust nade damage and force on hit - */ - #define EV_Nade_Damage(i, o) \ - /** nade */ i(entity, MUTATOR_ARGV_0_entity) \ - /** weapon */ i(entity, MUTATOR_ARGV_1_entity) \ - /** force */ i(vector, MUTATOR_ARGV_2_vector) \ - /**/ o(vector, MUTATOR_ARGV_2_vector) \ - /** damage */ i(float, MUTATOR_ARGV_3_float) \ - /**/ o(float, MUTATOR_ARGV_3_float) \ - /**/ - MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); - - #endif - - #ifdef CSQC - bool Projectile_isnade(int proj); // TODO: remove - - void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time); // TODO: mutator - #endif - +#ifdef MENUQC +#include + +CLASS(MutatorNades, Mutator) + ATTRIB(MutatorNades, message, string, _("Nades")); +ENDCLASS(MutatorNades) + +REGISTER_MUTATOR(nades, true, MutatorNades); +#endif diff --cc qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc index ed583e34ca,f16e8457a7..f9f3934842 --- a/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc +++ b/qcsrc/common/mutators/mutator/powerups/powerup/invisibility.qc @@@ -41,16 -41,4 +41,16 @@@ METHOD(InvisibilityStatusEffect, m_tick } SUPER(InvisibilityStatusEffect).m_tick(this, actor); } --#endif ++#endif // SVQC +#ifdef MENUQC +#include "../../instagib/instagib.qh" + +METHOD(InvisibilityItem, describe, string(InvisibilityItem this)) +{ + TC(InvisibilityItem, this); + return sprintf(_("The %s powerup increases your translucency while the powerup is active, making it more difficult for enemies to see you. " + "This powerup is often present in %s\n\n" + "Since it is a powerup, it will drop if you die while holding it"), + COLORED_NAME(this), COLORED_NAME(MUTATOR_mutator_instagib)); +} - #endif ++#endif // MENUQC diff --cc qcsrc/common/mutators/mutator/powerups/powerup/shield.qc index bfbc414334,5130e69559..3ce3670d02 --- a/qcsrc/common/mutators/mutator/powerups/powerup/shield.qc +++ b/qcsrc/common/mutators/mutator/powerups/powerup/shield.qc @@@ -31,9 -31,9 +31,9 @@@ METHOD(ShieldStatusEffect, m_tick, void actor.effects |= (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT); SUPER(ShieldStatusEffect).m_tick(this, actor); } --#endif ++#endif // SVQC #ifdef CSQC - METHOD(ShieldStatusEffect, m_active, bool(StatusEffects this, entity actor)) + METHOD(ShieldStatusEffect, m_active, bool(StatusEffect this, entity actor)) { if(autocvar__hud_configure) return true; @@@ -47,14 -47,4 +47,14 @@@ METHOD(ShieldStatusEffect, m_tick, void 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 ++#endif // CSQC +#ifdef MENUQC +METHOD(ShieldItem, describe, string(ShieldItem this)) +{ + TC(ShieldItem, this); + return sprintf(_("The %s powerup greatly decreases the damage you take until the powerup expires, having a compounding effect with armor. " + "It also makes you more resistant to knockback\n\n" + "Since it is a powerup, it will drop if you die while holding it"), + COLORED_NAME(this)); +} - #endif ++#endif // MENUQC diff --cc qcsrc/common/mutators/mutator/powerups/powerup/speed.qc index 5e871367d3,887a145919..736f35a332 --- a/qcsrc/common/mutators/mutator/powerups/powerup/speed.qc +++ b/qcsrc/common/mutators/mutator/powerups/powerup/speed.qc @@@ -29,14 -29,4 +29,14 @@@ METHOD(SpeedStatusEffect, m_tick, void( play_countdown(actor, StatusEffects_gettime(this, actor), SND_POWEROFF); SUPER(SpeedStatusEffect).m_tick(this, actor); } --#endif ++#endif // SVQC +#ifdef MENUQC +METHOD(SpeedItem, describe, string(SpeedItem this)) +{ + TC(SpeedItem, this); + return sprintf(_("The %s powerup increases your movement speed, attack speed, and health regeneration speed while the powerup is active. " + "However, it also makes you a bit more vulnerable to incoming attacks\n\n" + "Since it is a powerup, it will drop if you die while holding it"), + COLORED_NAME(this)); +} - #endif ++#endif // MENUQC diff --cc qcsrc/common/mutators/mutator/powerups/powerup/strength.qc index be82b2b89b,c520363fe6..45ec36ddff --- a/qcsrc/common/mutators/mutator/powerups/powerup/strength.qc +++ b/qcsrc/common/mutators/mutator/powerups/powerup/strength.qc @@@ -31,9 -31,9 +31,9 @@@ METHOD(StrengthStatusEffect, m_tick, vo actor.effects |= (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT); SUPER(StrengthStatusEffect).m_tick(this, actor); } --#endif ++#endif // SVQC #ifdef CSQC - METHOD(StrengthStatusEffect, m_active, bool(StatusEffects this, entity actor)) + METHOD(StrengthStatusEffect, m_active, bool(StatusEffect this, entity actor)) { if(autocvar__hud_configure) return true; @@@ -47,15 -47,4 +47,15 @@@ METHOD(StrengthStatusEffect, m_tick, vo 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 ++#endif // CSQC +#ifdef MENUQC +METHOD(StrengthItem, describe, string(StrengthItem this)) +{ + TC(StrengthItem, this); + return sprintf(_("The %s powerup greatly increases the damage you deal, until the powerup expires. " + "It also increases the knockback that your attacks deal\n\n" + "The damage and knockback you deal to yourself also increases but by a smaller amount\n\n" + "Since it is a powerup, it will drop if you die while holding it"), + COLORED_NAME(this)); +} - #endif ++#endif // MENUQC diff --cc qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh index 6269db3e4a,e6a2288585..b752d4229c --- a/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh +++ b/qcsrc/common/mutators/mutator/status_effects/status_effect/superweapons.qh @@@ -2,15 -2,12 +2,15 @@@ #include - CLASS(Superweapons, StatusEffects) - ATTRIB(Superweapons, netname, string, "superweapons"); - ATTRIB(Superweapons, m_name, string, _("Superweapons")); - ATTRIB(Superweapons, m_icon, string, "superweapons"); + CLASS(Superweapon, StatusEffect) + ATTRIB(Superweapon, netname, string, "superweapons"); + ATTRIB(Superweapon, m_name, string, _("Superweapons")); + ATTRIB(Superweapon, m_icon, string, "superweapons"); #ifdef GAMEQC - ATTRIB(Superweapons, m_sound_rm, Sound, SND_POWEROFF); + ATTRIB(Superweapon, m_sound_rm, Sound, SND_POWEROFF); #endif +#ifdef MENUQC - ATTRIB(Superweapons, m_hidden, bool, true); ++ ATTRIB(Superweapon, m_hidden, bool, true); +#endif - ENDCLASS(Superweapons) - REGISTER_STATUSEFFECT(Superweapons, NEW(Superweapons)); + ENDCLASS(Superweapon) + REGISTER_STATUSEFFECT(Superweapon, NEW(Superweapon)); diff --cc qcsrc/menu/xonotic/guide/guide.qh index 5da247225f,0000000000..f1307db177 mode 100644,000000..100644 --- a/qcsrc/menu/xonotic/guide/guide.qh +++ b/qcsrc/menu/xonotic/guide/guide.qh @@@ -1,142 -1,0 +1,142 @@@ +#pragma once + +#include +#include +#include +#include + +#define TOPICS(X) \ + X(NEW(FreetextSource), _("Guide"), "gametype_tdm") \ + X(NEW(GametypeSource), _("Gametypes"), "gametype_dm") \ + X(NEW(WeaponSource), _("Weapons"), "gametype_duel") \ + X(NEW(ItemSource), _("Items"), "gametype_kh") \ + X(NEW(PowerupSource), _("Powerups"), "gametype_dom") \ + X(NEW(BuffSource), _("Buffs"), "gametype_ka") \ + X(NEW(NadeSource), _("Nades"), "gametype_ft") \ + X(NEW(MonsterSource), _("Monsters"), "gametype_lms") \ + X(NEW(VehicleSource), _("Vehicles"), "gametype_rc") \ + X(NEW(TurretSource), _("Turrets"), "gametype_as") \ + X(NEW(MutatorSource), _("Mutators"), "gametype_nb") \ + /*X(NEW(MapSource), _("Maps"), "gametype_ctf")*/ \ + /*if (gamestatus & GAME_DEVELOPER) X(NEW(DebugSource), _("Debug"), "gametype_ons")*/ \ + /**/ +CLASS(TopicSource, DataSource) + METHOD(TopicSource, getEntry, entity(TopicSource this, int i, void(string, string) returns)); + METHOD(TopicSource, reload, int(TopicSource this, string filter)); +ENDCLASS(TopicSource) + +/* +CLASS(DebugSource, DataSource) + .entity nextdebug; + string DebugSource_activeFilter = ""; + METHOD(DebugSource, getEntry, entity(DebugSource this, int i, void(string, string) returns)); + METHOD(DebugSource, reload, int(DebugSource this, string filter)); +ENDCLASS(DebugSource) +*/ + +.bool m_hidden; +.bool instanceOfAmmo; +.bool instanceOfHealth; +.bool instanceOfArmor; +.bool instanceOfPowerup; + +#define _REGISTRY_SOURCE_FILL(arr_name, register_arr, cond1, cond2, cond3, num_conds, filter_cond) \ +if (num_conds >= 1) \ + FOREACH(register_arr, !it.m_hidden && (cond1) && (filter_cond), { \ + AL_sete(arr_name##_MENU, arr_name##_MENU_COUNT, it); \ + ++arr_name##_MENU_COUNT; \ + }); \ +if (num_conds >= 2) \ + FOREACH(register_arr, !it.m_hidden && (cond2) && (filter_cond), { \ + AL_sete(arr_name##_MENU, arr_name##_MENU_COUNT, it); \ + ++arr_name##_MENU_COUNT; \ + }); \ +if (num_conds >= 3) \ + FOREACH(register_arr, !it.m_hidden && (cond3) && (filter_cond), { \ + AL_sete(arr_name##_MENU, arr_name##_MENU_COUNT, it); \ + ++arr_name##_MENU_COUNT; \ + }); + +#define _REGISTRY_SOURCE(id, arr_name, register_arr, cond1, cond2, cond3, num_conds) \ +ArrayList arr_name##_MENU; \ +int arr_name##_MENU_COUNT; \ +STATIC_INIT_LATE(arr_name##_MENU) \ +{ \ + AL_NEW(arr_name##_MENU, REGISTRY_MAX(register_arr), NULL, e); \ + _REGISTRY_SOURCE_FILL(arr_name, register_arr, cond1, cond2, cond3, num_conds, true); \ +} \ +CLASS(id, DataSource) \ + METHOD(id, getEntry, entity(id this, int i, void(string, string) returns)) { \ + entity e = AL_gete(arr_name##_MENU, i); \ + if (returns) \ + e.display(e, returns); \ + return e; \ + } \ + METHOD(id, reload, int(id this, string filter)) { \ + arr_name##_MENU_COUNT = 0; \ + _REGISTRY_SOURCE_FILL(arr_name, register_arr, cond1, cond2, cond3, num_conds, \ + (filter == "" ? true : (strstrofs(strtolower(it.message ? it.message : it.m_name), strtolower(filter), 0) >= 0))); \ + return arr_name##_MENU_COUNT; \ + } \ +ENDCLASS(id) + +#define REGISTRY_SOURCE(...) EVAL(OVERLOAD(REGISTRY_SOURCE, __VA_ARGS__)) +#define REGISTRY_SOURCE_3(id, arr_name, register_arr) _REGISTRY_SOURCE(id, arr_name, register_arr, true, false, false, 1) +#define REGISTRY_SOURCE_4(id, arr_name, register_arr, cond1) _REGISTRY_SOURCE(id, arr_name, register_arr, cond1, false, false, 1) +#define REGISTRY_SOURCE_5(id, arr_name, register_arr, cond1, cond2) _REGISTRY_SOURCE(id, arr_name, register_arr, cond1, cond2, false, 2) +#define REGISTRY_SOURCE_6(id, arr_name, register_arr, cond1, cond2, cond3) _REGISTRY_SOURCE(id, arr_name, register_arr, cond1, cond2, cond3, 3) + +REGISTRY_SOURCE(FreetextSource, Guide, GuidePages) +// The descriptions for these are in menu/xonotic/guide/pages.qh + +#include +REGISTRY_SOURCE(GametypeSource, Gametypes, Gametypes) +// The descriptions for these are in common/gamemodes/gamemode/*/*.qc + +#include - REGISTRY_SOURCE(BuffSource, Buffs, StatusEffect, it.instanceOfBuff) ++REGISTRY_SOURCE(BuffSource, Buffs, StatusEffects, it.instanceOfBuff) +// The descriptions for these are in common/mutators/mutator/buffs/all.inc + +#include +REGISTRY_SOURCE(PowerupSource, Powerups, Items, it.instanceOfPowerup) +// The descriptions for these are in common/mutators/mutator/powerups/powerup/*.qc and common/items/item/jetpack.qc + +#include +REGISTRY_SOURCE(ItemSource, Items, Items, + !it.instanceOfPowerup && it.instanceOfAmmo, + !it.instanceOfPowerup && it.instanceOfHealth, + !it.instanceOfPowerup && it.instanceOfArmor) +// The descriptions for these are in common/items/item/*.qc and common/mutators/mutator/instagib/items.qc + +#include +REGISTRY_SOURCE(NadeSource, Nades, Nades) +// The descriptions for these are in common/mutators/mutator/nades/all.inc + +#include +REGISTRY_SOURCE(WeaponSource, Weapons, Weapons) +// The descriptions for these are in common/weapons/weapon/*.qc and common/mutators/mutator/overkill/ok*.qc (weapon files) + +#include +REGISTRY_SOURCE(MonsterSource, Monsters, Monsters) +// The descriptions for these are in common/monsters/monster/*.qc + +#include +REGISTRY_SOURCE(VehicleSource, Vehicles, Vehicles) +// The descriptions for these are in common/vehicles/vehicle/*.qc + +#include +REGISTRY_SOURCE(TurretSource, Turrets, Turrets) +// The descriptions for these are in common/turrets/turret/*.qc + +#include +REGISTRY_SOURCE(MutatorSource, Mutators, Mutators) +// The descriptions for these are in common/mutators/mutator/*.qc (not the cl_* or sv_* files) + +#if 0 +CLASS(MapSource, DataSource) + METHOD(MapSource, getEntry, entity(MapSource this, int i, void(string, string) returns)); + METHOD(MapSource, indexOf, int(MapSource this, string s)); + METHOD(MapSource, reload, int(MapSource this, string s)); + METHOD(MapSource, destroy, void(MapSource)); +ENDCLASS(MapSource) +#endif