REGISTER_STAT(NADES_SMALL, int, autocvar_g_nades_nade_small)
#ifdef GAMEQC
+
REPLICATE(cvar_cl_nade_type, int, "cl_nade_type");
REPLICATE(cvar_cl_pokenade_type, string, "cl_pokenade_type");
#ifdef CSQC
REGISTER_MUTATOR(cl_nades, true);
-MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay)
-{
- // TODO: make a common orb state!
- if (STAT(HEALING_ORB) > time)
- {
- M_ARGV(0, vector) = NADE_TYPE_HEAL.m_color;
- M_ARGV(1, float) = STAT(HEALING_ORB_ALPHA);
- return true;
- }
- if (STAT(ENTRAP_ORB) > time)
- {
- M_ARGV(0, vector) = NADE_TYPE_ENTRAP.m_color;
- M_ARGV(1, float) = STAT(ENTRAP_ORB_ALPHA);
- return true;
- }
- if (STAT(VEIL_ORB) > time)
- {
- M_ARGV(0, vector) = NADE_TYPE_VEIL.m_color;
- M_ARGV(1, float) = STAT(VEIL_ORB_ALPHA);
- return true;
- }
- return false;
-}
MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile)
{
entity proj = M_ARGV(0, entity);
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\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;
#include <common/monsters/sv_spawn.qh>
#include <common/monsters/sv_monsters.qh>
-REGISTER_MUTATOR(nades, autocvar_g_nades);
-
.float nade_time_primed;
.float nade_lifetime;
if(d < dist)
{
e.fireball_impactvec = p;
- RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e));
+ RandomSelection_AddEnt(e, 1 / (1 + d), !StatusEffects_active(STATUSEFFECT_Burning, e));
}
}
if(RandomSelection_chosen_ent)
#endif
}
- if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) )
+ if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) )
{
- entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher;
- STAT(ENTRAP_ORB, show_tint) = time + 0.1;
-
- float tint_alpha = 0.75;
- if(SAME_TEAM(toucher, this.realowner))
- tint_alpha = 0.45;
- STAT(ENTRAP_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime;
+ entity show_tint = (IS_VEHICLE(toucher) && toucher.owner) ? toucher.owner : toucher;
+ show_tint.nade_entrap_time = time + 0.1;
}
}
}
}
-
- if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) )
- {
- entity show_red = (IS_VEHICLE(toucher)) ? toucher.owner : toucher;
- STAT(HEALING_ORB, show_red) = time+0.1;
- STAT(HEALING_ORB_ALPHA, show_red) = 0.75 * (this.ltime - time) / this.orb_lifetime;
- }
}
void nade_heal_boom(entity this)
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;
void nade_veil_touch(entity this, entity toucher)
{
- if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) )
+ if ( IS_REAL_CLIENT(toucher) || (IS_VEHICLE(toucher) && toucher.owner) )
{
- entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher;
+ 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(!STAT(VEIL_ORB, show_tint))
+ if(!show_tint.nade_veil_time)
{
toucher.nade_veil_prevalpha = toucher.alpha;
toucher.alpha = -1;
}
}
- STAT(VEIL_ORB, show_tint) = time + 0.1;
- STAT(VEIL_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime;
+ show_tint.nade_veil_time = time + 0.1;
}
}
nade_blast = false;
break;
case NADE_TYPE_MONSTER:
+ if(!autocvar_g_monsters)
+ {
+ expef = EFFECT_NADE_EXPLODE(this.realowner.team);
+ break; // fall back to a standard nade explosion
+ }
case NADE_TYPE_SPAWN:
nade_blast = false;
switch(this.realowner.team)
int ntype;
string pntype = this.pokenade_type;
- if((this.items & ITEM_Strength.m_itemid) && autocvar_g_nades_bonus_onstrength)
+ if(StatusEffects_active(STATUSEFFECT_Strength, this) && autocvar_g_nades_bonus_onstrength)
ntype = STAT(NADE_BONUS_TYPE, this);
else if (STAT(NADE_BONUS, this) >= 1)
{
}
ENDCLASS(NadeOffhand)
NadeOffhand OFFHAND_NADE;
-STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); }
+REGISTER_MUTATOR(nades, autocvar_g_nades)
+{
+ MUTATOR_ONADD
+ {
+ OFFHAND_NADE = NEW(NadeOffhand);
+ }
+ return 0;
+}
MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST)
{
}
}
-#ifdef IS_REVIVING
- #undef IS_REVIVING
+#ifdef IN_REVIVING_RANGE
+ #undef IN_REVIVING_RANGE
#endif
// returns true if player is reviving it
-#define IS_REVIVING(player, it, revive_extra_size) \
- (it != player && !STAT(FROZEN, it) && !IS_DEAD(it) && SAME_TEAM(it, player) \
+#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)
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);
+ 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_BONUS, player) = STAT(NADE_BONUS_SCORE, player) = 0;
}
- if(STAT(VEIL_ORB, player) && STAT(VEIL_ORB, player) <= time)
+ if(player.nade_veil_time && player.nade_veil_time <= time)
{
- STAT(VEIL_ORB, player) = 0;
+ player.nade_veil_time = 0;
if(player.vehicle)
player.vehicle.alpha = player.vehicle.nade_veil_prevalpha;
else
}
}
- if (frametime && IS_PLAYER(player))
- {
- int n = 0;
+ if (!(frametime && IS_PLAYER(player)))
+ return true;
- IntrusiveList reviving_players = NULL;
+ entity revivers_last = NULL;
+ entity revivers_first = NULL;
- if(player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout)
- n = -1;
- else if (STAT(FROZEN, player) == FROZEN_TEMP_DYING)
+ 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)
{
- vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
- n = 0;
- FOREACH_CLIENT(IS_PLAYER(it) && IS_REVIVING(player, it, revive_extra_size), {
- if (!reviving_players)
- reviving_players = IL_NEW();
- IL_PUSH(reviving_players, it);
- ++n;
- });
+ if ((STAT(FROZEN, player) == FROZEN_TEMP_DYING))
+ continue;
+ if (!IN_REVIVING_RANGE(player, it, revive_extra_size))
+ continue;
+ player_is_reviving = true;
+ break;
}
- 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);
- SetResource(player, RES_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * start_health));
+ 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(STAT(REVIVE_PROGRESS, player) >= 1)
- {
- Unfreeze(player, false);
+ 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));
- entity first = IL_FIRST(reviving_players);
- Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, first.netname);
- Send_Notification(NOTIF_ONE, first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname);
- }
+ if(STAT(REVIVE_PROGRESS, player) >= 1)
+ {
+ Unfreeze(player, false);
- IL_EACH(reviving_players, true, {
- STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
- });
+ 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);
}
- if (reviving_players)
- IL_DELETE(reviving_players);
+
+ for(entity it = revivers_first; it; it = it.chain)
+ STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player);
}
}
entity player = M_ARGV(0, entity);
// these automatically reset, no need to worry
- if(STAT(ENTRAP_ORB, player) > time)
+ if(player.nade_entrap_time > time)
STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nades_entrap_speed;
}
{
entity mon = M_ARGV(0, entity);
- if (STAT(ENTRAP_ORB, mon) > time)
+ 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 (STAT(VEIL_ORB, mon) && STAT(VEIL_ORB, mon) <= time)
+ if (mon.nade_veil_time && mon.nade_veil_time <= time)
{
mon.alpha = mon.nade_veil_prevalpha;
- STAT(VEIL_ORB, mon) = 0;
+ mon.nade_veil_time = 0;
}
}
client.pokenade_type = spectatee.pokenade_type;
STAT(NADE_BONUS, client) = STAT(NADE_BONUS, spectatee);
STAT(NADE_BONUS_SCORE, client) = STAT(NADE_BONUS_SCORE, spectatee);
- STAT(HEALING_ORB, client) = STAT(HEALING_ORB, spectatee);
- STAT(HEALING_ORB_ALPHA, client) = STAT(HEALING_ORB_ALPHA, spectatee);
- STAT(ENTRAP_ORB, client) = STAT(ENTRAP_ORB, spectatee);
- STAT(ENTRAP_ORB_ALPHA, client) = STAT(ENTRAP_ORB_ALPHA, spectatee);
- STAT(VEIL_ORB, client) = STAT(VEIL_ORB, spectatee);
- STAT(VEIL_ORB_ALPHA, client) = STAT(VEIL_ORB_ALPHA, spectatee);
}
-MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString)
+MUTATOR_HOOKFUNCTION(nades, BuildMutatorsPrettyString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades");
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Nades");
}
-MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString)
+MUTATOR_HOOKFUNCTION(nades, BuildMutatorsString)
{
- M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3nades^8 are enabled, press 'g' (dropweapon) to use them\n");
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Nades");
}
#endif