alias sv_hook_gameend
+ // =====================
+ // gametype vote hooks
+ // =====================
+ // these are called when the mode is switched via gametype vote screen, earlier than gamestart hooks (useful for enabling per-gamemode mutators)
+ alias sv_vote_gametype_hook_all
+ alias sv_vote_gametype_hook_as
+ alias sv_vote_gametype_hook_ca
+ alias sv_vote_gametype_hook_ctf
+ alias sv_vote_gametype_hook_cts
+ alias sv_vote_gametype_hook_dm
+ alias sv_vote_gametype_hook_dom
+ alias sv_vote_gametype_hook_ft
+ alias sv_vote_gametype_hook_inv
+ alias sv_vote_gametype_hook_ka
+ alias sv_vote_gametype_hook_kh
+ alias sv_vote_gametype_hook_lms
+ alias sv_vote_gametype_hook_nb
+ alias sv_vote_gametype_hook_ons
+ alias sv_vote_gametype_hook_rc
+ alias sv_vote_gametype_hook_tdm
+
+
// ===========
// leadlimit
// ===========
seta g_race_laps_limit -1 "Race laps limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
seta g_nexball_goallimit -1 "Nexball goal limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
seta g_nexball_goalleadlimit -1 "Nexball goal lead limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
- seta g_invasion_round_limit -1 "Invasion round limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
+ seta g_invasion_point_limit -1 "Invasion point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)"
// =================================
set g_freezetag_revive_speed 0.4 "Speed for reviving a frozen teammate"
set g_freezetag_revive_clearspeed 1.6 "Speed at which reviving progress gets lost when out of range"
set g_freezetag_revive_extra_size 100 "Distance in qu that you can stand from a frozen teammate to keep reviving him"
+set g_freezetag_revive_nade 1 "Enable reviving from own nade explosion"
+set g_freezetag_revive_nade_health 40 "Amount of health player has if they revived from their own nade explosion"
set g_freezetag_revive_falldamage 0 "Enable reviving from this amount of fall damage"
set g_freezetag_revive_falldamage_health 40 "Amount of health player has if they revived from falling"
set g_freezetag_round_timelimit 180 "round time limit in seconds"
+set g_freezetag_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn"
set g_freezetag_frozen_force 0.6 "How much to multiply the force on a frozen player with"
set g_freezetag_frozen_maxtime 60 "frozen players will be automatically unfrozen after this time in seconds"
seta g_freezetag_teams_override 0
set g_invasion_warmup 10 "time between waves to prepare for battle"
set g_invasion_monster_count 10 "number of monsters on first wave (increments)"
set g_invasion_zombies_only 0 "only spawn zombies"
- set g_invasion_spawn_delay 2 "spawn more monsters after this delay"
+ set g_invasion_spawn_delay 0.25
+ set g_invasion_spawnpoint_spawn_delay 0.5
+ set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)"
+ set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode"
// =======
-// nades
+// Nades
// =======
set g_nades 0 "enable off-hand grenades"
set g_nades_spawn 1 "give nades right away when player spawns rather than delaying entire refire"
+set g_nades_client_select 0 "allow client side selection of nade type"
set g_nades_nade_lifetime 3.5
set g_nades_nade_minforce 400
set g_nades_nade_maxforce 2000
set g_nades_nade_radius 300
set g_nades_nade_force 650
set g_nades_nade_newton_style 0
+set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+
+seta cl_nade_timer 1 "show a visual timer for nades, 1 = only circle, 2 = circle with text"
+seta cl_nade_type 3
+seta cl_pokenade_type "zombie"
+
+// ------------
+// Nade bonus
+// ------------
+//
+// How the nade bonus system works:
+// Each player has a score counter that is increased by some actions (eg: capping, fragging...)
+// Once this counter reaches its maximum, the player will receive a bonus grenade and the score counter resets
+// If the player dies all the bonus nades will be lost and the score counter resets
+// If g_nades_bonus_score_time is not zero, this score will increase or decrease over time
+//
+set g_nades_bonus 0 "Enable bonus grenades"
+set g_nades_bonus_client_select 0 "Allow client side selection of bonus nade type"
+set g_nades_bonus_type 2 "Type of the bonus grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade"
+set g_nades_bonus_onstrength 1 "Always give bonus grenades to players that have the strength powerup"
+set g_nades_bonus_max 3 "Maximum number of bonus grenades"
+// Bonus score
+set g_nades_bonus_score_max 120 "Score value that will give a bonus nade"
+set g_nades_bonus_score_minor 5 "Score given for minor actions (pickups, regular frags etc.)"
+set g_nades_bonus_score_low 20 "Score given for frags and unfreezes"
+set g_nades_bonus_score_medium 30 "Score given for flag returns and flag carrier kills"
+set g_nades_bonus_score_high 60 "Score given for flag captures"
+set g_nades_bonus_score_spree 40 "Score given every spree of this many frags"
+set g_nades_bonus_score_time -1 "Bonus nade score given per second (negative to have the score decay)"
+set g_nades_bonus_score_time_flagcarrier 2 "Bonus nade score given per second as flag carrier (negative to have the score decay)"
+
+// Napalm (2)
+set g_nades_napalm_blast 1 "Whether the napalm grenades also give damage with the usual grenade explosion"
+set g_nades_napalm_burntime 0.5 "Time that the fire from napalm will stick to the player"
+set g_nades_napalm_selfdamage 1 "Whether the player that tossed the nade can be harmed by its fire"
+// Napalm fireballs
+set g_nades_napalm_ball_count 6 "Number of fireballs emitted during the explosion"
+set g_nades_napalm_ball_spread 500 "Maximum force which the fireballs will have on explosion"
+set g_nades_napalm_ball_damageforcescale 4
+set g_nades_napalm_ball_damage 40
+set g_nades_napalm_ball_lifetime 7
+set g_nades_napalm_ball_radius 100 "Distance from the fireball within which you may get burned"
+// Napalm Fire fountain
+set g_nades_napalm_fountain_lifetime 3 "Time period during which extra fire mines are ejected"
+set g_nades_napalm_fountain_delay 0.5 "Delay between emissions by the fountain"
+set g_nades_napalm_fountain_damage 50 "Damage caused by the center of the fountain"
+set g_nades_napalm_fountain_edgedamage 20 "Damage caused by the edge of the fountain"
+set g_nades_napalm_fountain_radius 130
+
+// Ice (3)
+set g_nades_ice_freeze_time 3 "How long the ice field will last"
+set g_nades_ice_health 0 "How much health the player will have after being unfrozen"
+set g_nades_ice_explode 0 "Whether the ice nade should explode again once the ice field dissipated"
+set g_nades_ice_teamcheck 0 "Don't freeze teammates"
+
+// Spawn (5)
+set g_nades_spawn_count 3 "Number of times player will spawn at their spawn nade explosion location"
+
+// Heal (6)
+set g_nades_heal_time 5 "How long the heling field will last"
+set g_nades_heal_rate 30 "Health given per second"
+set g_nades_heal_friend 1 "Multiplier of health given to team mates"
+set g_nades_heal_foe -2 "Multiplier of health given to enemies"
+
+// Pokenade (7)
+set g_nades_pokenade_monster_lifetime 20 "How long pokenade monster will survive"
+set g_nades_pokenade_monster_type "zombie" "Monster to spawn"
// ============
set g_campcheck_damage 100
set g_campcheck_distance 1800
+
+ // =======
+ // buffs
+ // =======
+ set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another"
+ set g_buffs 0 "enable buffs (requires buff items or powerups)"
+ set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item"
+ set g_buffs_randomize 1 "randomize buff type when player drops buff"
+ set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touched after this time in seconds"
+ set g_buffs_random_location 0 "randomize buff location on start and when reset"
+ set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up"
+ set g_buffs_spawn_count 5 "how many buffs to spawn on the map if none exist already"
+ set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs"
+ set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated"
+ set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading"
+ set g_buffs_ammo 1 "ammo buff: infinite ammunition"
+ set g_buffs_resistance 1 "resistance buff: greatly reduces damage taken"
+ set g_buffs_resistance_blockpercent 0.7 "damage reduction multiplier, higher values mean less damage"
+ set g_buffs_medic 1 "medic buff: increased regeneration speed, extra health, chance to survive a fatal attack"
+ set g_buffs_medic_survive_chance 0.6 "multiplier chance of player surviving a fatal hit"
+ set g_buffs_medic_survive_health 5 "amount of health player survives with after taking a fatal hit"
+ set g_buffs_medic_rot 0.7 "health rot rate multiplier"
+ set g_buffs_medic_max 1.5 "stable health medic limit multiplier"
+ set g_buffs_medic_regen 1.7 "health medic rate multiplier"
+ set g_buffs_vengeance 1 "vengeance buff: attackers also take damage"
+ set g_buffs_vengeance_damage_multiplier 0.6 "amount of damage dealt the attacker takes when hitting a target with vengeance"
+ set g_buffs_bash 1 "bash buff: increased knockback force and immunity to knockback"
+ set g_buffs_bash_force 2 "bash force multiplier"
+ set g_buffs_bash_force_self 1.2 "bash self force multiplier"
+ set g_buffs_disability 1 "disability buff: attacks to players and monsters deal slowness (decreased movement/attack speed) for a few seconds"
+ set g_buffs_disability_time 3 "time in seconds for target disability"
+ set g_buffs_disability_speed 0.5 "player speed multiplier while disabled"
+ set g_buffs_disability_rate 1.7 "player weapon rate multiplier while disabled"
+ set g_buffs_speed 1 "speed buff: increased movement/attack/health regeneration speed, carrier takes slightly more damage"
+ set g_buffs_speed_speed 1.7 "player speed multiplier while holding speed buff"
+ set g_buffs_speed_rate 0.8 "player weapon rate multiplier while holding speed buff"
+ set g_buffs_speed_damage_take 1.2 "damage taken multiplier while holding speed buff"
+ set g_buffs_speed_regen 1.2 "regeneration speed multiplier while holding speed buff"
+ set g_buffs_vampire 1 "vampire buff: attacks to players and monsters heal the carrier"
+ set g_buffs_vampire_damage_steal 0.6 "damage stolen multiplier while holding vampire buff"
+ set g_buffs_jump 1 "jump buff: greatly increased jump height"
+ set g_buffs_jump_height 600 "jump height while holding jump buff"
+ set g_buffs_flight 1 "flight buff: greatly decreased gravity"
+ set g_buffs_flight_gravity 0.3 "player gravity multiplier while holding flight buff"
+ set g_buffs_invisible 1 "invisible buff: carrier becomes invisible"
+ set g_buffs_invisible_alpha 0.4 "player invisibility multiplier while holding invisible buff"
+
registercvar("hud_usecsqc", "1");
registercvar("scoreboard_columns", "default");
+ registercvar("cl_nade_type", "3");
+ registercvar("cl_pokenade_type", "zombie");
+
gametype = 0;
// hud_fields uses strunzone on the titles!
CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
CALL_ACCUMULATED_FUNCTION(RegisterHUD_Panels);
+ CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
WaypointSprite_Load();
float autocvar_hud_panel_notify_flip;
float autocvar_hud_panel_notify_fontsize;
float autocvar_hud_panel_notify_time;
+ float autocvar_hud_panel_notify_icon_aspect;
float autocvar_hud_panel_physics;
float autocvar_hud_panel_physics_acceleration_progressbar_mode;
float autocvar_hud_panel_physics_acceleration_progressbar_scale;
float autocvar_hud_panel_powerups_flip;
float autocvar_hud_panel_powerups_iconalign;
float autocvar_hud_panel_powerups_progressbar;
+ float autocvar_hud_panel_buffs;
+ //float autocvar_hud_panel_buffs_iconalign;
string autocvar_hud_panel_powerups_progressbar_shield;
string autocvar_hud_panel_powerups_progressbar_strength;
string autocvar_hud_panel_powerups_progressbar_superweapons;
float autocvar_cl_deathglow;
float autocvar_developer_csqcentities;
float autocvar_g_jetpack_attenuation;
+float autocvar_cl_nade_timer;
}
}
+void DrawNadeScoreBar(vector myPos, vector mySize, vector color)
+{
+
+ HUD_Panel_DrawProgressBar(
+ myPos + eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x,
+ mySize - eX * autocvar_hud_panel_ammo_progressbar_xoffset * mySize_x,
+ autocvar_hud_panel_ammo_progressbar_name,
+ getstatf(STAT_NADE_BONUS_SCORE), 0, 0, color,
+ autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL);
+
+}
+
+void DrawAmmoNades(vector myPos, vector mySize, float draw_expanding, float expand_time)
+{
+ float theAlpha = 1, a, b;
+ vector nade_color, picpos, numpos;
+
+ nade_color = Nade_Color(getstati(STAT_NADE_BONUS_TYPE));
+
+ a = getstatf(STAT_NADE_BONUS);
+ b = getstatf(STAT_NADE_BONUS_SCORE);
+
+ if(autocvar_hud_panel_ammo_iconalign)
+ {
+ numpos = myPos;
+ picpos = myPos + eX * 2 * mySize_y;
+ }
+ else
+ {
+ numpos = myPos + eX * mySize_y;
+ picpos = myPos;
+ }
+
+ DrawNadeScoreBar(myPos, mySize, nade_color);
+
+ if(b > 0 || a > 0)
+ {
+ if(autocvar_hud_panel_ammo_text)
+ drawstring_aspect(numpos, ftos(a), eX * (2/3) * mySize_x + eY * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+
+ if(draw_expanding)
+ drawpic_aspect_skin_expanding(picpos, "nade_nbg", '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, expand_time);
+
+ drawpic_aspect_skin(picpos, "nade_bg" , '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+ drawpic_aspect_skin(picpos, "nade_nbg" , '1 1 0' * mySize_y, nade_color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL);
+ }
+}
+
void DrawAmmoItem(vector myPos, vector mySize, float itemcode, float currently_selected, float infinite_ammo)
{
float a;
drawpic_aspect_skin(picpos, GetAmmoPicture(itemcode), '1 1 0' * mySize_y, '0 0 0', panel_fg_alpha * theAlpha * 0.5, DRAWFLAG_NORMAL);
}
+float nade_prevstatus;
+float nade_prevframe;
+float nade_statuschange_time;
void HUD_Ammo(void)
{
if(hud != HUD_NORMAL) return;
mySize -= '2 2 0' * panel_bg_padding;
}
- const float AMMO_COUNT = 4;
float rows = 0, columns, row, column;
+ float nade_cnt = getstatf(STAT_NADE_BONUS), nade_score = getstatf(STAT_NADE_BONUS_SCORE);
+ float draw_nades = (nade_cnt > 0 || nade_score > 0), nade_statuschange_elapsedtime;
+ float total_ammo_count;
+
vector ammo_size;
+ float AMMO_COUNT = 4;
if (autocvar_hud_panel_ammo_onlycurrent)
- ammo_size = mySize;
+ total_ammo_count = 1;
else
+ total_ammo_count = AMMO_COUNT - 1; // fuel
+
+ if(draw_nades)
{
- rows = mySize_y/mySize_x;
- rows = bound(1, floor((sqrt(4 * (3/1) * rows * AMMO_COUNT + rows * rows) + rows + 0.5) / 2), AMMO_COUNT);
- // ^^^ ammo item aspect goes here
+ ++total_ammo_count;
+ if (nade_cnt != nade_prevframe)
+ {
+ nade_statuschange_time = time;
+ nade_prevstatus = nade_prevframe;
+ nade_prevframe = nade_cnt;
+ }
+ }
+ else
+ nade_prevstatus = nade_prevframe = nade_statuschange_time = 0;
- columns = ceil(AMMO_COUNT/rows);
+ rows = mySize_y/mySize_x;
+ rows = bound(1, floor((sqrt(4 * (3/1) * rows * (total_ammo_count) + rows * rows) + rows + 0.5) / 2), (total_ammo_count));
+ // ^^^ ammo item aspect goes here
- ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
- }
+ columns = ceil((total_ammo_count)/rows);
+
+ ammo_size = eX * mySize_x*(1/columns) + eY * mySize_y*(1/rows);
+
local vector offset = '0 0 0'; // fteqcc sucks
float newSize;
float i, stat_items, currently_selected, infinite_ammo;
infinite_ammo = FALSE;
+
+ row = column = 0;
+
if (autocvar_hud_panel_ammo_onlycurrent)
{
if(autocvar__hud_configure)
}
}
}
+
+ ++row;
+ if(row >= rows)
+ {
+ row = 0;
+ column = column + 1;
+ }
}
else
{
stat_items = getstati(STAT_ITEMS, 0, 24);
if (stat_items & IT_UNLIMITED_WEAPON_AMMO)
infinite_ammo = TRUE;
- row = column = 0;
for (i = 0; i < AMMO_COUNT; ++i) {
currently_selected = stat_items & GetAmmoItemCode(i);
DrawAmmoItem(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, i, currently_selected, infinite_ammo);
}
}
+ if (draw_nades)
+ {
+ nade_statuschange_elapsedtime = time - nade_statuschange_time;
+
+ float f = bound(0, nade_statuschange_elapsedtime*2, 1);
+
+ DrawAmmoNades(pos + eX * column * (ammo_size_x + offset_x) + eY * row * (ammo_size_y + offset_y), ammo_size, nade_prevstatus < nade_cnt && nade_cnt != 0 && f < 1, f);
+ }
+
draw_endBoldFont();
}
void HUD_Notify_Push(string icon, string attacker, string victim)
{
- if(icon != "")
- {
- --kn_index;
- if (kn_index == -1) { kn_index = KN_MAX_ENTRIES-1; }
- notify_times[kn_index] = time;
+ if (icon == "")
+ return;
+
+ ++notify_count;
+ --notify_index;
+
+ if (notify_index == -1)
+ notify_index = NOTIFY_MAX_ENTRIES-1;
+
+ // Free old strings
+ if (notify_attackers[notify_index])
+ strunzone(notify_attackers[notify_index]);
- // icon
- if(notify_icon[kn_index]) { strunzone(notify_icon[kn_index]); }
- notify_icon[kn_index] = strzone(icon);
+ if (notify_victims[notify_index])
+ strunzone(notify_victims[notify_index]);
- // attacker
- if(notify_attackers[kn_index]) { strunzone(notify_attackers[kn_index]); }
- notify_attackers[kn_index] = strzone(attacker);
+ if (notify_icons[notify_index])
+ strunzone(notify_icons[notify_index]);
- // victim
- if(notify_victims[kn_index]) { strunzone(notify_victims[kn_index]); }
- notify_victims[kn_index] = strzone(victim);
+ // Allocate new strings
+ if (victim != "")
+ {
+ notify_attackers[notify_index] = strzone(attacker);
+ notify_victims[notify_index] = strzone(victim);
}
+ else
+ {
+ // In case of a notification without a victim, the attacker
+ // is displayed on the victim's side. Instead of special
+ // treatment later on, we can simply switch them here.
+ notify_attackers[notify_index] = string_null;
+ notify_victims[notify_index] = strzone(attacker);
+ }
+
+ notify_icons[notify_index] = strzone(icon);
+ notify_times[notify_index] = time;
}
void HUD_Notify(void)
{
- if(!autocvar__hud_configure)
- {
- if(!autocvar_hud_panel_notify) return;
- }
+ if (!autocvar__hud_configure)
+ if (!autocvar_hud_panel_notify)
+ return;
HUD_Panel_UpdateCvars();
- vector pos, mySize;
- pos = panel_pos;
- mySize = panel_size;
-
HUD_Panel_DrawBg(1);
- if(panel_bg_padding)
+
+ if (!autocvar__hud_configure)
+ if (notify_count == 0)
+ return;
+
+ vector pos, size;
+ pos = panel_pos;
+ size = panel_size;
+
+ if (panel_bg_padding)
{
- pos += '1 1 0' * panel_bg_padding;
- mySize -= '2 2 0' * panel_bg_padding;
+ pos += '1 1 0' * panel_bg_padding;
+ size -= '2 2 0' * panel_bg_padding;
}
- float entries, height;
- entries = bound(1, floor(KN_MAX_ENTRIES * mySize_y/mySize_x), KN_MAX_ENTRIES);
- height = mySize_y/entries;
+ float fade_start = max(0, autocvar_hud_panel_notify_time);
+ float fade_time = max(0, autocvar_hud_panel_notify_fadetime);
+ float icon_aspect = max(1, autocvar_hud_panel_notify_icon_aspect);
- vector fontsize;
- float fontheight = height * autocvar_hud_panel_notify_fontsize;
- fontsize = '0.5 0.5 0' * fontheight;
+ float entry_count = bound(1, floor(NOTIFY_MAX_ENTRIES * size_y / size_x), NOTIFY_MAX_ENTRIES);
+ float entry_height = size_y / entry_count;
- float a;
- float when;
- when = autocvar_hud_panel_notify_time;
- float fadetime;
- fadetime = autocvar_hud_panel_notify_fadetime;
+ float panel_width_half = size_x * 0.5;
+ float icon_width_half = entry_height * icon_aspect / 2;
+ float name_maxwidth = panel_width_half - icon_width_half - size_x * NOTIFY_ICON_MARGIN;
- vector pos_attacker, pos_victim, pos_icon;
- float width_attacker;
+ vector font_size = '0.5 0.5 0' * entry_height * autocvar_hud_panel_notify_fontsize;
+ vector icon_size = (eX * icon_aspect + eY) * entry_height;
+ vector icon_left = eX * (panel_width_half - icon_width_half);
+ vector attacker_right = eX * name_maxwidth;
+ vector victim_left = eX * (size_x - name_maxwidth);
+
+ vector attacker_pos, victim_pos, icon_pos;
string attacker, victim, icon;
+ float i, j, count, step, limit, alpha;
- float i, j, step, limit;
- if(autocvar_hud_panel_notify_flip) //order items from the top down
+ if (autocvar_hud_panel_notify_flip)
{
+ // Order items from the top down
i = 0;
step = +1;
- limit = entries;
+ limit = entry_count;
}
- else //order items from the bottom up
+ else
{
- i = entries - 1;
+ // Order items from the bottom up
+ i = entry_count - 1;
step = -1;
limit = -1;
}
- for(j = kn_index; i != limit; i += step, ++j)
+ for (j = notify_index, count = 0; i != limit; i += step, ++j, ++count)
{
if(autocvar__hud_configure)
{
- if (step == +1)
- a = i;
- else // inverse order
- a = entries - 1 - i;
- attacker = textShortenToWidth(sprintf(_("Player %d"), a+1), 0.48 * mySize_x - height, fontsize, stringwidth_colors);
- victim = textShortenToWidth(sprintf(_("Player %d"), a+2), 0.48 * mySize_x - height, fontsize, stringwidth_colors);
- icon = strcat("weapon", get_weaponinfo(WEP_FIRST + mod(floor(a*2.4), WEP_LAST)).netname);
- a = bound(0, (when - a) / 4, 1);
- goto hud_config_notifyprint;
+ attacker = sprintf(_("Player %d"), count + 1);
+ victim = sprintf(_("Player %d"), count + 2);
+ icon = strcat("weapon", get_weaponinfo(min(WEP_FIRST + count * 2, WEP_LAST)).netname);
+ alpha = bound(0, 1.2 - count / entry_count, 1);
}
else
{
- if (j == KN_MAX_ENTRIES)
+ if (j == NOTIFY_MAX_ENTRIES)
j = 0;
- if(notify_times[j] + when > time)
- a = 1;
- else if(fadetime)
+ if (notify_times[j] + fade_start > time)
+ alpha = 1;
+ else if (fade_time != 0)
{
- a = bound(0, (notify_times[j] + when + fadetime - time) / fadetime, 1);
- if(!a)
- {
+ alpha = bound(0, (notify_times[j] + fade_start + fade_time - time) / fade_time, 1);
+ if (alpha == 0)
break;
- }
}
else
- {
break;
- }
attacker = notify_attackers[j];
victim = notify_victims[j];
- icon = notify_icon[j];
+ icon = notify_icons[j];
}
- //type = notify_deathtype[j];
- //w = DEATH_WEAPONOF(type);
-
- if(icon != "")
+ if (icon != "" && victim != "")
{
- if((attacker != "") && (victim == ""))
- {
- // Y [used by] X
- attacker = textShortenToWidth(attacker, 0.73 * mySize_x - height, fontsize, stringwidth_colors);
- pos_attacker = pos + eX * (0.27 * mySize_x + height) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
- pos_icon = pos + eX * 0.25 * mySize_x - eX * height + eY * i * height;
+ vector name_top = eY * (i * entry_height + 0.5 * (entry_height - font_size_y));
- drawpic_aspect_skin(pos_icon, icon, '2 1 0' * height, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring(pos_attacker, attacker, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- }
- else if((attacker != "") && (victim != ""))
+ icon_pos = pos + icon_left + eY * i * entry_height;
+ drawpic_aspect_skin(icon_pos, icon, icon_size, '1 1 1', panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+
+ victim = textShortenToWidth(victim, name_maxwidth, font_size, stringwidth_colors);
+ victim_pos = pos + victim_left + name_top;
+ drawcolorcodedstring(victim_pos, victim, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
+
+ if (attacker != "")
{
- // X [did action to] Y
- attacker = textShortenToWidth(attacker, 0.48 * mySize_x - height, fontsize, stringwidth_colors);
- victim = textShortenToWidth(victim, 0.48 * mySize_x - height, fontsize, stringwidth_colors);
- :hud_config_notifyprint
- width_attacker = stringwidth(attacker, TRUE, fontsize);
- pos_attacker = pos + eX * (0.48 * mySize_x - height - width_attacker) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
- pos_victim = pos + eX * (0.52 * mySize_x + height) + eY * ((0.5 * fontsize_y + i * height) + (0.5 * (height - fontheight)));
- pos_icon = pos + eX * 0.5 * mySize_x - eX * height + eY * i * height;
-
- drawpic_aspect_skin(pos_icon, icon, '2 1 0' * height, '1 1 1', panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring(pos_attacker, attacker, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
- drawcolorcodedstring(pos_victim, victim, fontsize, panel_fg_alpha * a, DRAWFLAG_NORMAL);
+ attacker = textShortenToWidth(attacker, name_maxwidth, font_size, stringwidth_colors);
+ attacker_pos = pos + attacker_right - eX * stringwidth(attacker, TRUE, font_size) + name_top;
+ drawcolorcodedstring(attacker_pos, attacker, font_size, panel_fg_alpha * alpha, DRAWFLAG_NORMAL);
}
}
}
+
+ notify_count = count;
}
// Timer (#5)
}
}
+ // Buffs (#18)
+ //
+ void HUD_Buffs(void)
+ {
+ float buffs = getstati(STAT_BUFFS, 0, 24);
+ if(!autocvar__hud_configure)
+ {
+ if(!autocvar_hud_panel_buffs) return;
+ if(spectatee_status == -1) return;
+ if(getstati(STAT_HEALTH) <= 0) return;
+ if(!buffs) return;
+ }
+ else
+ {
+ buffs = Buff_Type_first.items; // force first buff
+ }
+
+ float b = 0; // counter to tell other functions that we have buffs
+ entity e;
+ string s = "";
+ for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+ {
+ ++b;
+ string o = strcat(rgb_to_hexcolor(Buff_Color(e.items)), Buff_PrettyName(e.items));
+ if(s == "")
+ s = o;
+ else
+ s = strcat(s, " ", o);
+ }
+
+ HUD_Panel_UpdateCvars();
+
+ draw_beginBoldFont();
+
+ vector pos, mySize;
+ pos = panel_pos;
+ mySize = panel_size;
+
+ HUD_Panel_DrawBg(bound(0, b, 1));
+ if(panel_bg_padding)
+ {
+ pos += '1 1 0' * panel_bg_padding;
+ mySize -= '2 2 0' * panel_bg_padding;
+ }
+
+ //float panel_ar = mySize_x/mySize_y;
+ //float is_vertical = (panel_ar < 1);
+ //float buff_iconalign = autocvar_hud_panel_buffs_iconalign;
+ vector buff_offset = '0 0 0';
+
+ for(e = Buff_Type_first; e; e = e.enemy) if(buffs & e.items)
+ {
+ //DrawNumIcon(pos + buff_offset, mySize, shield, "shield", is_vertical, buff_iconalign, '1 1 1', 1);
+ drawcolorcodedstring_aspect(pos + buff_offset, s, mySize, panel_fg_alpha * 0.5, DRAWFLAG_NORMAL);
+ }
+
+ draw_endBoldFont();
+ }
+
+
/*
==================
Main HUD system
Defs.qc
../dpdefs/keycodes.qc
../common/constants.qh
+ ../common/stats.qh
../warpzonelib/anglestransform.qh
../warpzonelib/mathlib.qh
../common/teams.qh
../common/util.qh
+../common/nades.qh
+ ../common/buffs.qh
../common/test.qh
../common/counting.qh
../common/items.qh
../common/monsters/monsters.qc
+../common/nades.qc
+ ../common/buffs.qc
../warpzonelib/anglestransform.qc
../warpzonelib/mathlib.qc
}
string spritelookuptext(string s)
{
+ if(substring(s, 0, 5) == "buff-") { return Buff_PrettyName(Buff_Type_FromSprite(s)); }
switch(s)
{
case "as-push": return _("Push");
case "item-shield": return _("Shield");
case "item-fuelregen": return _("Fuel regen");
case "item-jetpack": return _("Jet Pack");
- case "freezetag_frozen": return _("Frozen!");
+ case "frozen": return _("Frozen!");
case "tagged-target": return _("Tagged");
case "vehicle": return _("Vehicle");
default: return s;
const float ENT_CLIENT_AUXILIARYXHAIR = 50;
const float ENT_CLIENT_VEHICLE = 60;
+const float ENT_CLIENT_HEALING_ORB = 80;
+
const float SPRITERULE_DEFAULT = 0;
const float SPRITERULE_TEAMPLAY = 1;
///////////////////////////
// csqc communication stuff
- const float STAT_KH_KEYS = 32;
- const float STAT_CTF_STATE = 33;
- const float STAT_WEAPONS = 35;
- const float STAT_SWITCHWEAPON = 36;
- const float STAT_GAMESTARTTIME = 37;
- const float STAT_STRENGTH_FINISHED = 38;
- const float STAT_INVINCIBLE_FINISHED = 39;
- const float STAT_PRESSED_KEYS = 42;
- const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
- const float STAT_FUEL = 44;
- const float STAT_NB_METERSTART = 45;
- const float STAT_SHOTORG = 46; // compressShotOrigin
- const float STAT_LEADLIMIT = 47;
- const float STAT_WEAPON_CLIPLOAD = 48;
- const float STAT_WEAPON_CLIPSIZE = 49;
- const float STAT_NEX_CHARGE = 50;
- const float STAT_LAST_PICKUP = 51;
- const float STAT_HUD = 52;
- const float STAT_NEX_CHARGEPOOL = 53;
- const float STAT_HIT_TIME = 54;
- const float STAT_TYPEHIT_TIME = 55;
- const float STAT_LAYED_MINES = 56;
- const float STAT_HAGAR_LOAD = 57;
- const float STAT_SWITCHINGWEAPON = 58;
- const float STAT_SUPERWEAPONS_FINISHED = 59;
-
- const float STAT_VEHICLESTAT_HEALTH = 60;
- const float STAT_VEHICLESTAT_SHIELD = 61;
- const float STAT_VEHICLESTAT_ENERGY = 62;
- const float STAT_VEHICLESTAT_AMMO1 = 63;
- const float STAT_VEHICLESTAT_RELOAD1 = 64;
- const float STAT_VEHICLESTAT_AMMO2 = 65;
- const float STAT_VEHICLESTAT_RELOAD2 = 66;
-
- const float STAT_NADE_TIMER = 69;
-
- const float STAT_SECRETS_TOTAL = 70;
- const float STAT_SECRETS_FOUND = 71;
-
- const float STAT_RESPAWN_TIME = 72;
- const float STAT_ROUNDSTARTTIME = 73;
-
- const float STAT_WEAPONS2 = 74;
- const float STAT_WEAPONS3 = 75;
-
- const float STAT_MONSTERS_TOTAL = 76;
- const float STAT_MONSTERS_KILLED = 77;
-
- const float STAT_NADE_BONUS = 80;
- const float STAT_NADE_BONUS_TYPE = 81;
- const float STAT_NADE_BONUS_SCORE = 82;
- const float STAT_HEALING_ORB = 83;
- const float STAT_HEALING_ORB_ALPHA = 84;
-
- // mod stats (1xx)
- const float STAT_REDALIVE = 100;
- const float STAT_BLUEALIVE = 101;
- const float STAT_YELLOWALIVE = 102;
- const float STAT_PINKALIVE = 103;
-
- // freeze tag
- const float STAT_FROZEN = 104;
- const float STAT_REVIVE_PROGRESS = 105;
-
- // domination
- const float STAT_DOM_TOTAL_PPS = 100;
- const float STAT_DOM_PPS_RED = 101;
- const float STAT_DOM_PPS_BLUE = 102;
- const float STAT_DOM_PPS_PINK = 103;
- const float STAT_DOM_PPS_YELLOW = 104;
-
- //const float STAT_SPIDERBOT_AIM 53 // compressShotOrigin
- //const float STAT_SPIDERBOT_TARGET 54 // compressShotOrigin
-
- // see DP source, quakedef.h
- const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
- const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
- const float STAT_MOVEVARS_MAXSPEED = 244;
- const float STAT_MOVEVARS_AIRACCEL_QW = 254;
-
const float CTF_STATE_ATTACK = 1;
const float CTF_STATE_DEFEND = 2;
const float CTF_STATE_COMMANDER = 3;
// moved that here so the client knows the max.
// # of maps, I'll use arrays for them :P
- #define MAPVOTE_COUNT 10
+ #define MAPVOTE_COUNT 30
/**
* Lower scores are better (e.g. suicides)
#define SP_SCORE 3
// game mode specific indices are not in common/, but in server/scores_rules.qc!
- #ifdef COMPAT_XON010_CHANNELS
- const float CH_INFO = 0; // only on world and csqc
- const float CH_TRIGGER = 0; // only on players; compat: FALSELY CONTROLLED BY "Info"
- const float CH_WEAPON_A = 1; // only on players and entities
- const float CH_WEAPON_SINGLE = 5; // only on players and entities
- const float CH_VOICE = 2; // only on players
- const float CH_BGM_SINGLE = 2; // only on csqc; compat: FALSELY CONTROLLED BY "Voice"
- const float CH_AMBIENT = 2; // only on csqc; compat: FALSELY CONTROLLED BY "Voice"
- const float CH_TRIGGER_SINGLE = 3; // only on players, entities, csqc
- const float CH_SHOTS = 4; // only on players, entities, csqc
- const float CH_SHOTS_SINGLE = 4; // only on players, entities, csqc
- const float CH_WEAPON_B = 5; // only on players and entities
- const float CH_PAIN = 6; // only on players and csqc
- const float CH_PAIN_SINGLE = 6; // only on players and csqc
- const float CH_PLAYER = 7; // only on players and entities
- const float CH_TUBA = 5; // only on csqc
- #else
const float CH_INFO = 0;
const float CH_TRIGGER = -3;
const float CH_WEAPON_A = -1;
const float CH_PAIN = -6;
const float CH_PAIN_SINGLE = 6;
const float CH_PLAYER = -7;
- const float CH_TUBA = 5;
- #endif
+ const float CH_PLAYER_SINGLE = 7;
+ const float CH_TUBA_SINGLE = 5;
const float ATTEN_NONE = 0;
const float ATTEN_MIN = 0.015625;
const float PROJECTILE_MAGE_SPIKE = 32;
const float PROJECTILE_SHAMBLER_LIGHTNING = 33;
-const float PROJECTILE_NADE_RED = 50;
-const float PROJECTILE_NADE_RED_BURN = 51;
-const float PROJECTILE_NADE_BLUE = 52;
-const float PROJECTILE_NADE_BLUE_BURN = 53;
-const float PROJECTILE_NADE_YELLOW = 54;
-const float PROJECTILE_NADE_YELLOW_BURN = 55;
-const float PROJECTILE_NADE_PINK = 56;
-const float PROJECTILE_NADE_PINK_BURN = 57;
-const float PROJECTILE_NADE = 58;
-const float PROJECTILE_NADE_BURN = 59;
-
const float SPECIES_HUMAN = 0;
const float SPECIES_ROBOT_SOLID = 1;
const float SPECIES_ALIEN = 2;
#define URI_GET_UPDATENOTIFICATION 33
#define URI_GET_URLLIB 128
#define URI_GET_URLLIB_END 191
+
+ // gametype votes
+ #define GTV_AVAILABLE 0
+ // for later use in per-map gametype filtering
+ #define GTV_FORBIDDEN 2
#define DEATHTYPES \
DEATHTYPE(DEATH_AUTOTEAMCHANGE, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, DEATH_SPECIAL_START) \
+ DEATHTYPE(DEATH_BUFF_VENGEANCE, NO_MSG, DEATH_MURDER_VENGEANCE, NORMAL_POS) \
DEATHTYPE(DEATH_CAMP, DEATH_SELF_CAMP, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_CHEAT, DEATH_SELF_CHEAT, DEATH_MURDER_CHEAT, NORMAL_POS) \
DEATHTYPE(DEATH_CUSTOM, DEATH_SELF_CUSTOM, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_MONSTER_WYVERN, DEATH_SELF_MON_WYVERN, DEATH_MURDER_MONSTER, NORMAL_POS) \
DEATHTYPE(DEATH_MONSTER_ZOMBIE_JUMP, DEATH_SELF_MON_ZOMBIE_JUMP, DEATH_MURDER_MONSTER, NORMAL_POS) \
DEATHTYPE(DEATH_MONSTER_ZOMBIE_MELEE, DEATH_SELF_MON_ZOMBIE_MELEE, DEATH_MURDER_MONSTER, DEATH_MONSTER_LAST) \
- DEATHTYPE(DEATH_NADE, DEATH_SELF_NADE, DEATH_MURDER_NADE, NORMAL_POS) \
+ DEATHTYPE(DEATH_NADE, DEATH_SELF_NADE, DEATH_MURDER_NADE, NORMAL_POS) \
+ DEATHTYPE(DEATH_NADE_NAPALM, DEATH_SELF_NADE_NAPALM, DEATH_MURDER_NADE_NAPALM, NORMAL_POS) \
+ DEATHTYPE(DEATH_NADE_ICE, DEATH_SELF_NADE_ICE, DEATH_MURDER_NADE_ICE, NORMAL_POS) \
+ DEATHTYPE(DEATH_NADE_ICE_FREEZE, DEATH_SELF_NADE_ICE_FREEZE, DEATH_MURDER_NADE_ICE_FREEZE, NORMAL_POS) \
+ DEATHTYPE(DEATH_NADE_HEAL, DEATH_SELF_NADE_HEAL, DEATH_MURDER_NADE_HEAL, NORMAL_POS) \
DEATHTYPE(DEATH_NOAMMO, DEATH_SELF_NOAMMO, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_ROT, DEATH_SELF_ROT, NO_MSG, NORMAL_POS) \
DEATHTYPE(DEATH_SHOOTING_STAR, DEATH_SELF_SHOOTING_STAR, DEATH_MURDER_SHOOTING_STAR, NORMAL_POS) \
return FALSE;
if(DIFF_TEAM(e, self) && e != self.monster_owner)
return FALSE;
- if(e.freezetag_frozen)
+ if(e.frozen)
return FALSE;
if(!IS_PLAYER(e))
return ((e.flags & FL_MONSTER) && e.health < e.max_health);
{
pointparticles(particleeffectnum("healing_fx"), head.origin, '0 0 0', 1);
head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health);
- WaypointSprite_UpdateHealth(head.sprite, head.health);
+ if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE))
+ WaypointSprite_UpdateHealth(head.sprite, head.health);
}
}
{
self.classname = "monster_mage";
- self.monster_spawnfunc = spawnfunc_monster_mage;
-
- if(Monster_CheckAppearFlags(self))
- return;
-
- if(!monster_initialize(MON_MAGE, FALSE)) { remove(self); return; }
+ if(!monster_initialize(MON_MAGE)) { remove(self); return; }
}
// compatibility with old spawns
}
case MR_PRECACHE:
{
- precache_model ("models/monsters/mage.dpm");
+ precache_model("models/monsters/mage.dpm");
precache_sound ("weapons/grenade_impact.wav");
precache_sound ("weapons/tagexp1.wav");
return TRUE;
{
case MR_PRECACHE:
{
- precache_model ("models/monsters/mage.dpm");
return TRUE;
}
}
// =========================
- void monster_item_spawn()
- {
- if(self.monster_loot)
- self.monster_loot();
-
- self.gravity = 1;
- self.reset = SUB_Remove;
- self.noalign = TRUE;
- self.velocity = randomvec() * 175 + '0 0 325';
- self.classname = "droppedweapon"; // hax
- self.item_spawnshieldtime = time + 0.7;
-
- SUB_SetFade(self, time + autocvar_g_monsters_drop_time, 1);
- }
-
void monster_dropitem()
{
if(!self.candrop || !self.monster_loot)
return;
vector org = self.origin + ((self.mins + self.maxs) * 0.5);
- entity e = spawn();
-
- setorigin(e, org);
+ entity e = spawn(), oldself = self;
e.monster_loot = self.monster_loot;
MUTATOR_CALLHOOK(MonsterDropItem);
e = other;
- if(e)
+ if(e && e.monster_loot)
{
- e.think = monster_item_spawn;
- e.nextthink = time + 0.3;
+ self = e;
+ e.noalign = TRUE;
+ e.monster_loot();
+ e.gravity = 1;
+ e.movetype = MOVETYPE_TOSS;
+ e.reset = SUB_Remove;
+ setorigin(e, org);
+ e.velocity = randomvec() * 175 + '0 0 325';
+ e.item_spawnshieldtime = time + 0.7;
+ e.classname = "droppedweapon"; // use weapon handling to remove it on touch
+ SUB_SetFade(e, time + autocvar_g_monsters_drop_time, 1);
+ self = oldself;
}
}
if(targ == ent)
return FALSE; // don't attack ourselves
- traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
+ //traceline(ent.origin, targ.origin, MOVE_NORMAL, ent);
- if(trace_ent != targ)
- return FALSE;
+ //if(trace_ent != targ)
+ //return FALSE;
if(targ.vehicle_flags & VHF_ISVEHICLE)
if(!((get_monsterinfo(ent.monsterid)).spawnflags & MON_FLAG_RANGED))
if(time < game_starttime)
return FALSE; // monsters do nothing before the match has started
- if(vlen(targ.origin - ent.origin) >= ent.target_range)
- return FALSE; // enemy is too far away
-
if(targ.takedamage == DAMAGE_NO)
return FALSE; // enemy can't be damaged
if(SAME_TEAM(targ, ent))
return FALSE; // enemy is on our team
- if (targ.freezetag_frozen)
+ if (targ.frozen)
return FALSE; // ignore frozen
- if(autocvar_g_monsters_target_infront || ent.spawnflags & MONSTERFLAG_INFRONT)
+ if(autocvar_g_monsters_target_infront || (ent.spawnflags & MONSTERFLAG_INFRONT))
if(ent.enemy != targ)
{
float dot;
entity head, closest_target = world;
head = findradius(ent.origin, ent.target_range);
+ //head = WarpZone_FindRadius(ent.origin, ent.target_range, TRUE);
while(head) // find the closest acceptable target to pass to
{
{
// if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc)
vector head_center = CENTER_OR_VIEWOFS(head);
+ //vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head));
vector ent_center = CENTER_OR_VIEWOFS(ent);
- //if(ctf_CheckPassDirection(head_center, ent_center, ent.v_angle, head.WarpZone_findradius_nearest))
+ traceline(ent_center, head_center, MOVE_NORMAL, ent);
+
+ if(trace_ent == head)
if(closest_target)
{
vector closest_target_center = CENTER_OR_VIEWOFS(closest_target);
+ //vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target));
if(vlen(ent_center - head_center) < vlen(ent_center - closest_target_center))
{ closest_target = head; }
}
void MonsterSound(.string samplefield, float sound_delay, float delaytoo, float chan)
{
- if(delaytoo && time < self.msound_delay)
+ if(!autocvar_g_monsters_sounds) { return; }
+
+ if(delaytoo)
+ if(time < self.msound_delay)
return; // too early
GlobalSound(self.samplefield, chan, VOICETYPE_PLAYERSOUND);
return TRUE;
}
+ float monster_initialize(float mon_id);
+ void monster_respawn()
+ {
+ // is this function really needed?
+ monster_initialize(self.monsterid);
+ }
+
void Monster_Fade ()
{
if(Monster_CanRespawn(self))
{
self.spawnflags |= MONSTERFLAG_RESPAWNED;
- self.think = self.monster_spawnfunc;
+ self.think = monster_respawn;
self.nextthink = time + self.respawntime;
- self.ltime = 0;
+ self.monster_lifetime = 0;
self.deadflag = DEAD_RESPAWNING;
if(self.spawnflags & MONSTER_RESPAWN_DEATHPOINT)
{
// number of monsters spawned with mobspawn command
totalspawned -= 1;
- if(IS_CLIENT(self.realowner))
- self.realowner.monstercount -= 1;
-
SUB_SetFade(self, time + 3, 1);
}
}
// enemy is always preferred target
if(self.enemy)
{
- makevectors(self.angles);
+ vector targ_origin = ((self.enemy.absmin + self.enemy.absmax) * 0.5);
+ targ_origin = WarpZone_RefSys_TransformOrigin(self.enemy, self, targ_origin); // origin of target as seen by the monster (us)
+ WarpZone_TraceLine(self.origin, targ_origin, MOVE_NOMONSTERS, self);
+
+ if((self.enemy == world)
+ || (self.enemy.deadflag != DEAD_NO || self.enemy.health < 1)
- || (self.enemy.freezetag_frozen)
++ || (self.enemy.frozen)
+ || (self.enemy.flags & FL_NOTARGET)
+ || (self.enemy.alpha < 0.5)
+ || (self.enemy.takedamage == DAMAGE_NO)
+ || (vlen(self.origin - targ_origin) > self.target_range)
+ || ((trace_fraction < 1) && (trace_ent != self.enemy)))
+ //|| (time > self.ctf_droptime + autocvar_g_ctf_pass_timelimit)) // TODO: chase timelimit?
+ {
+ self.enemy = world;
+ self.pass_distance = 0;
+ }
+
+ if(self.enemy)
+ {
+ /*WarpZone_TrailParticles(world, particleeffectnum("red_pass"), self.origin, targ_origin);
+ print("Trace origin: ", vtos(targ_origin), "\n");
+ print("Target origin: ", vtos(self.enemy.origin), "\n");
+ print("My origin: ", vtos(self.origin), "\n"); */
+
+ self.monster_movestate = MONSTER_MOVE_ENEMY;
+ self.last_trace = time + 1.2;
+ return targ_origin;
+ }
+
+ /*makevectors(self.angles);
self.monster_movestate = MONSTER_MOVE_ENEMY;
self.last_trace = time + 1.2;
- return self.enemy.origin;
+ return self.enemy.origin; */
}
switch(self.monster_moveflags)
{
vector pos;
self.monster_movestate = MONSTER_MOVE_WANDER;
- self.last_trace = time + 2;
-
- self.angles_y = rint(random() * 500);
- makevectors(self.angles);
- pos = self.origin + v_forward * 600;
-
- if(self.flags & FL_FLY || self.flags & FL_SWIM)
- if(self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
- {
- pos_z = random() * 200;
- if(random() >= 0.5)
- pos_z *= -1;
- }
if(targ)
{
self.last_trace = time + 0.5;
pos = targ.origin;
}
+ else
+ {
+ self.last_trace = time + self.wander_delay;
+
+ self.angles_y = rint(random() * 500);
+ makevectors(self.angles);
+ pos = self.origin + v_forward * self.wander_distance;
+
+ if(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM))
+ {
+ pos_z = random() * 200;
+ if(random() >= 0.5)
+ pos_z *= -1;
+ }
+ }
return pos;
}
}
}
+ void monster_CalculateVelocity(entity mon, vector to, vector from, float turnrate, float movespeed)
+ {
+ float current_distance = vlen((('1 0 0' * to_x) + ('0 1 0' * to_y)) - (('1 0 0' * from_x) + ('0 1 0' * from_y))); // for the sake of this check, exclude Z axis
+ float initial_height = 0; //min(50, (targ_distance * tanh(20)));
+ float current_height = (initial_height * min(1, (current_distance / self.pass_distance)));
+ //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n");
+
+ vector targpos;
+ if(current_height) // make sure we can actually do this arcing path
+ {
+ targpos = (to + ('0 0 1' * current_height));
+ WarpZone_TraceLine(mon.origin, targpos, MOVE_NOMONSTERS, mon);
+ if(trace_fraction < 1)
+ {
+ //print("normal arc line failed, trying to find new pos...");
+ WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, mon);
+ targpos = (trace_endpos + '0 0 -10');
+ WarpZone_TraceLine(mon.origin, targpos, MOVE_NOMONSTERS, mon);
+ if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ }
+ /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */
+ }
+ }
+ else { targpos = to; }
+
+ //mon.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y));
+
+ vector desired_direction = normalize(targpos - from);
+ if(turnrate) { mon.velocity = (normalize(normalize(mon.velocity) + (desired_direction * 50)) * movespeed); }
+ else { mon.velocity = (desired_direction * movespeed); }
+
+ //mon.steerto = steerlib_attract2(targpos, 0.5, 500, 0.95);
+ //mon.angles = vectoangles(mon.velocity);
+ }
+
void monster_move(float runspeed, float walkspeed, float stopspeed, float manim_run, float manim_walk, float manim_idle)
{
- fixedmakevectors(self.angles);
+ //fixedmakevectors(self.angles);
if(self.target2)
self.goalentity = find(world, targetname, self.target2);
entity targ;
+ if(self.frozen == 2)
+ {
+ self.revive_progress = bound(0, self.revive_progress + self.ticrate * self.revive_speed, 1);
+ self.health = max(1, self.revive_progress * self.max_health);
+ self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+ movelib_beak_simple(stopspeed);
+ self.frame = manim_idle;
+
+ self.enemy = world;
+ self.nextthink = time + self.ticrate;
+
+ if(self.revive_progress >= 1)
+ Unfreeze(self);
+
+ return;
+ }
+ else if(self.frozen == 3)
+ {
+ self.revive_progress = bound(0, self.revive_progress - self.ticrate * self.revive_speed, 1);
+ self.health = max(0, autocvar_g_nades_ice_health + (self.max_health-autocvar_g_nades_ice_health) * self.revive_progress );
+
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+
+ movelib_beak_simple(stopspeed);
+ self.frame = manim_idle;
+
+ self.enemy = world;
+ self.nextthink = time + self.ticrate;
+
+ if(self.health < 1)
+ {
+ Unfreeze(self);
+ self.health = 0;
+ self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+ }
+
+ else if ( self.revive_progress <= 0 )
+ Unfreeze(self);
+
+ return;
+ }
+
if(self.flags & FL_SWIM)
{
if(self.waterlevel < WATERLEVEL_WETFEET)
monster_speed_run = runspeed;
monster_speed_walk = walkspeed;
- if(MUTATOR_CALLHOOK(MonsterMove) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
+ if(MUTATOR_CALLHOOK(MonsterMove) || gameover || self.draggedby != world || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || time < game_starttime || (autocvar_g_campaign && !campaign_bots_may_start) || time < self.spawn_time)
{
runspeed = walkspeed = 0;
if(time >= self.spawn_time)
if(DIFF_TEAM(self.monster_owner, self))
self.monster_owner = world;
- if(self.enemy && self.enemy.health < 1)
- self.enemy = world; // enough!
-
if(time >= self.last_enemycheck)
{
- if(!monster_isvalidtarget(self.enemy, self))
- self.enemy = world;
-
if(!self.enemy)
{
self.enemy = FindTarget(self);
if(self.enemy)
+ {
+ WarpZone_RefSys_Copy(self.enemy, self);
+ WarpZone_RefSys_AddInverse(self.enemy, self); // wz1^-1 ... wzn^-1 receiver
+ self.moveto = WarpZone_RefSys_TransformOrigin(self.enemy, self, (0.5 * (self.enemy.absmin + self.enemy.absmax)));
+
+ self.pass_distance = vlen((('1 0 0' * self.enemy.origin_x) + ('0 1 0' * self.enemy.origin_y)) - (('1 0 0' * self.origin_x) + ('0 1 0' * self.origin_y)));
MonsterSound(monstersound_sight, 0, FALSE, CH_VOICE);
+ }
}
- self.last_enemycheck = time + 0.5;
+ self.last_enemycheck = time + 1; // check for enemies every second
}
if(self.state == MONSTER_STATE_ATTACK_MELEE && time >= self.attack_finished_single)
if(!self.enemy)
MonsterSound(monstersound_idle, 7, TRUE, CH_VOICE);
- if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
- self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
if(self.state == MONSTER_STATE_ATTACK_LEAP && (self.flags & FL_ONGROUND))
{
self.state = 0;
self.touch = MonsterTouch;
}
- //self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
-
- float turny = 0;
- vector real_angle = vectoangles(self.steerto) - self.angles;
-
- if(self.state != MONSTER_STATE_ATTACK_LEAP && self.state != MONSTER_STATE_ATTACK_MELEE)
- turny = 20;
-
- if(self.flags & FL_SWIM)
- turny = vlen(self.angles - self.moveto);
-
- if(turny)
- {
- turny = bound(turny * -1, shortangle_f(real_angle_y, self.angles_y), turny);
- self.angles_y += turny;
- }
-
if(self.state == MONSTER_STATE_ATTACK_MELEE)
self.moveto = self.origin;
if(self.enemy && self.enemy.vehicle)
runspeed = 0;
- if(((self.flags & FL_FLY) || (self.flags & FL_SWIM)) && self.spawnflags & MONSTERFLAG_FLY_VERTICAL)
- v_forward = normalize(self.moveto - self.origin);
- else
+ if(!(((self.flags & FL_FLY) && (self.spawnflags & MONSTERFLAG_FLY_VERTICAL)) || (self.flags & FL_SWIM)))
+ //v_forward = normalize(self.moveto - self.origin);
+ //else
self.moveto_z = self.origin_z;
if(vlen(self.origin - self.moveto) > 64)
{
- if(self.flags & FL_FLY || self.flags & FL_SWIM)
+ if((self.flags & FL_ONGROUND) || ((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
+ monster_CalculateVelocity(self, self.moveto, self.origin, TRUE, ((self.enemy) ? runspeed : walkspeed));
+
+ /*&if(self.flags & FL_FLY || self.flags & FL_SWIM)
movelib_move_simple(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
else
- movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6);
+ movelib_move_simple_gravity(v_forward, ((self.enemy) ? runspeed : walkspeed), 0.6); */
if(time > self.pain_finished)
if(time > self.attack_finished_single)
if (vlen(self.velocity) <= 30)
self.frame = manim_idle;
}
+
+ self.steerto = steerlib_attract2(self.moveto, 0.5, 500, 0.95);
+
+ vector real_angle = vectoangles(self.steerto) - self.angles;
+ float turny = 25;
+ if(self.state == MONSTER_STATE_ATTACK_MELEE)
+ turny = 0;
+ if(turny)
+ {
+ turny = bound(turny * -1, shortangle_f(real_angle_y, self.angles_y), turny);
+ self.angles_y += turny;
+ }
monster_checkattack(self, self.enemy);
}
if(mon.weaponentity)
remove(mon.weaponentity);
+ if(mon.iceblock)
+ remove(mon.iceblock);
+
WaypointSprite_Kill(mon.sprite);
remove(mon);
CSQCMODEL_AUTOUPDATE();
- if(self.ltime != 0)
- if(time >= self.ltime)
+ if(self.monster_lifetime != 0)
+ if(time >= self.monster_lifetime)
{
Monster_Fade();
return;
void Monster_Appear()
{
self.enemy = activator;
- self.spawnflags &= ~MONSTERFLAG_APPEAR;
- self.monster_spawnfunc();
+ self.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop
+ monster_initialize(self.monsterid);
}
- float Monster_CheckAppearFlags(entity ent)
+ float Monster_CheckAppearFlags(entity ent, float monster_id)
{
if(!(ent.spawnflags & MONSTERFLAG_APPEAR))
return FALSE;
ent.think = func_null;
+ ent.monsterid = monster_id; // set so this monster is properly registered (otherwise, normal initialization is used)
ent.nextthink = 0;
ent.use = Monster_Appear;
ent.flags = FL_MONSTER; // set so this monster can get butchered
setorigin(self, self.pos1);
self.angles = self.pos2;
+ Unfreeze(self); // remove any icy remains
+
self.health = self.max_health;
self.velocity = '0 0 0';
self.enemy = world;
// number of monsters spawned with mobspawn command
totalspawned -= 1;
- if(IS_CLIENT(self.realowner))
- self.realowner.monstercount -= 1;
-
self.think = SUB_Remove;
self.nextthink = time + 0.1;
+ self.event_damage = func_null;
}
}
{
self.think = monster_dead_think;
self.nextthink = time;
- self.ltime = time + 5;
+ self.monster_lifetime = time + 5;
+ if(self.frozen)
+ {
+ Unfreeze(self); // remove any icy remains
+ self.health = 0; // reset by Unfreeze
+ }
+
monster_dropitem();
MonsterSound(monstersound_death, 0, FALSE, CH_VOICE);
{
// number of monsters spawned with mobspawn command
totalspawned -= 1;
-
- if(IS_CLIENT(self.realowner))
- self.realowner.monstercount -= 1;
}
if(self.candrop && self.weapon)
W_ThrowNewWeapon(self, self.weapon, 0, self.origin, randomvec() * 150 + '0 0 325');
- self.event_damage = monsters_corpse_damage;
+ self.event_damage = ((gibbed) ? func_null : monsters_corpse_damage);
self.solid = SOLID_CORPSE;
self.takedamage = DAMAGE_AIM;
self.deadflag = DEAD_DEAD;
self.state = 0;
self.attack_finished_single = 0;
- if(!(self.flags & FL_FLY))
+ if(!((self.flags & FL_FLY) || (self.flags & FL_SWIM)))
self.velocity = '0 0 0';
MON_ACTION(self.monsterid, MR_DEATH);
void monsters_damage (entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
+ if(self.frozen && deathtype != DEATH_KILL && deathtype != DEATH_NADE_ICE_FREEZE)
+ return;
+
+ if((self.spawnflags & MONSTERFLAG_INVINCIBLE) && deathtype != DEATH_KILL)
+ return;
+
if(time < self.pain_finished && deathtype != DEATH_KILL)
return;
if(time < self.spawnshieldtime && deathtype != DEATH_KILL)
return;
+ if(deathtype == DEATH_FALL && self.draggedby != world)
+ return;
+
vector v;
float take, save;
}
}
- void monster_setupcolors()
+ void monster_setupcolors(entity mon)
{
- if(IS_PLAYER(self.monster_owner))
- self.colormap = self.monster_owner.colormap;
- else if(teamplay && self.team)
- self.colormap = 1024 + (self.team - 1) * 17;
+ if(IS_PLAYER(mon.monster_owner))
+ mon.colormap = mon.monster_owner.colormap;
+ else if(teamplay && mon.team)
+ mon.colormap = 1024 + (mon.team - 1) * 17;
else
{
- if(self.monster_skill <= MONSTER_SKILL_EASY)
- self.colormap = 1029;
- else if(self.monster_skill <= MONSTER_SKILL_MEDIUM)
- self.colormap = 1027;
- else if(self.monster_skill <= MONSTER_SKILL_HARD)
- self.colormap = 1038;
- else if(self.monster_skill <= MONSTER_SKILL_INSANE)
- self.colormap = 1028;
- else if(self.monster_skill <= MONSTER_SKILL_NIGHTMARE)
- self.colormap = 1032;
+ if(mon.monster_skill <= MONSTER_SKILL_EASY)
+ mon.colormap = 1029;
+ else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM)
+ mon.colormap = 1027;
+ else if(mon.monster_skill <= MONSTER_SKILL_HARD)
+ mon.colormap = 1038;
+ else if(mon.monster_skill <= MONSTER_SKILL_INSANE)
+ mon.colormap = 1028;
+ else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE)
+ mon.colormap = 1032;
else
- self.colormap = 1024;
+ mon.colormap = 1024;
+ }
+ }
+
+ void monster_changeteam(entity ent, float newteam)
+ {
+ if(!teamplay) { return; }
+
+ ent.team = newteam;
+ ent.monster_attack = TRUE; // new team, activate attacking
+ monster_setupcolors(ent);
+
+ if(ent.sprite)
+ {
+ WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0'));
+
+ ent.sprite.team = newteam;
+ ent.sprite.SendFlags |= 1;
}
}
self.think = monster_think;
self.nextthink = self.ticrate;
- if(self.ltime)
- if(time >= self.ltime)
+ if(self.monster_lifetime)
+ if(time >= self.monster_lifetime)
{
Damage(self, self, self, self.health + self.max_health, DEATH_KILL, self.origin, self.origin);
return;
if(!self.attack_range)
self.attack_range = autocvar_g_monsters_attack_range;
+ if(!self.wander_delay) { self.wander_delay = 2; }
+ if(!self.wander_distance) { self.wander_distance = 600; }
+
precache_monstersounds();
UpdateMonsterSounds();
MonsterSound(monstersound_spawn, 0, FALSE, CH_VOICE);
WaypointSprite_Spawn(M_NAME(self.monsterid), 0, 1024, self, '0 0 1' * (self.maxs_z + 15), world, self.team, self, sprite, TRUE, RADARICON_DANGER, ((self.team) ? Team_ColorRGB(self.team) : '1 0 0'));
- WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
- WaypointSprite_UpdateHealth(self.sprite, self.health);
+ if(!(self.spawnflags & MONSTERFLAG_INVINCIBLE))
+ {
+ WaypointSprite_UpdateMaxHealth(self.sprite, self.max_health);
+ WaypointSprite_UpdateHealth(self.sprite, self.health);
+ }
self.think = monster_think;
self.nextthink = time + self.ticrate;
return TRUE;
}
- float monster_initialize(float mon_id, float nodrop)
+ float monster_initialize(float mon_id)
{
- if(!autocvar_g_monsters)
- return FALSE;
+ if(!autocvar_g_monsters) { return FALSE; }
+ if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) { MON_ACTION(mon_id, MR_PRECACHE); }
+ if(Monster_CheckAppearFlags(self, mon_id)) { return TRUE; } // return true so the monster isn't removed
entity mon = get_monsterinfo(mon_id);
self.team = 0;
if(!(self.spawnflags & MONSTERFLAG_SPAWNED)) // naturally spawned monster
- if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
+ if(!(self.spawnflags & MONSTERFLAG_RESPAWNED)) // don't count re-spawning monsters either
monsters_total += 1;
setmodel(self, mon.model);
- setsize(self, mon.mins, mon.maxs);
+ //setsize(self, mon.mins, mon.maxs);
self.flags = FL_MONSTER;
self.takedamage = DAMAGE_AIM;
self.bot_attack = TRUE;
self.candrop = TRUE;
self.view_ofs = '0 0 1' * (self.maxs_z * 0.5);
self.oldtarget2 = self.target2;
+ self.pass_distance = 0;
self.deadflag = DEAD_NO;
- self.scale = 1;
- self.noalign = nodrop;
+ self.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM));
self.spawn_time = time;
self.spider_slowness = 0;
self.gravity = 1;
self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP;
+ if(!self.scale)
+ self.scale = 1;
+
+ if(autocvar_g_monsters_edit)
+ self.grab = 1; // owner may carry their monster
+
if(autocvar_g_fullbrightplayers)
self.effects |= EF_FULLBRIGHT;
}
if(mon.spawnflags & MONSTER_SIZE_BROKEN)
- self.scale = 1.3;
+ if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
+ self.scale *= 1.3;
+
+ setsize(self, mon.mins * self.scale, mon.maxs * self.scale);
if(!self.ticrate)
self.ticrate = autocvar_g_monsters_think_delay;
return FALSE;
if(!(self.spawnflags & MONSTERFLAG_RESPAWNED))
- monster_setupcolors();
+ monster_setupcolors(self);
CSQCMODEL_AUTOINIT();
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_FIRE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s"), _("^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_LAVA, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_lava", _("^BG%s%s^K1 was cooked by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_MONSTER, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was pushed infront of a monster by ^BG%s^K1%s%s"), "") \
- MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_nade", _("^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_NAPALM, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_nade_napalm", _("^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s"), _("^BG%s%s^K1 got too close to a napalm explosion%s%s")) \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_nade_ice", _("^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_ICE_FREEZE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_nade_ice", _("^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_NADE_HEAL, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_nade_heal", _("^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SHOOTING_STAR, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_shootingstar", _("^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SLIME, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_slime", _("^BG%s%s^K1 was slimed by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_SWAMP, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_slime", _("^BG%s%s^K1 was preserved by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_DEATH, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_GUN, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VH_WAKI_ROCKET, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VENGEANCE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_death", _("^BG%s%s^K1 was destroyed by the vengeful ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_MURDER_VOID, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "notify_void", _("^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_AUTOTEAMCHANGE, 2, 1, "s1 s2loc death_team", "", "", _("^BG%s^K1 was moved into the %s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_BETRAYAL, 2, 1, "s1 s2loc spree_lost", "s1", "notify_teamkill_red", _("^BG%s^K1 became enemies with the Lord of Teamplay%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_FIRE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 became a bit too crispy%s%s"), _("^BG%s^K1 felt a little hot%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_GENERIC, 2, 1, "s1 s2loc spree_lost", "s1", "notify_selfkill", _("^BG%s^K1 died%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_LAVA, 2, 1, "s1 s2loc spree_lost", "s1", "notify_lava", _("^BG%s^K1 turned into hot slag%s%s"), _("^BG%s^K1 found a hot place%s%s")) \
- MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_MAGE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was exploded by a Mage%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_CLAW, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1's innards became outwards by a Shambler%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_SHAMBLER_SMASH, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was smashed by a Shambler%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_WYVERN, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was fireballed by a Wyvern%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 joins the Zombies%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_MON_ZOMBIE_MELEE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 was given kung fu lessons by a Zombie%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_nade", _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_NAPALM, 2, 1, "s1 s2loc spree_lost", "s1", "notify_nade_napalm", _("^BG%s^K1 was burned to death by their own Napalm Nade%s%s"), _("^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s")) \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_nade_ice", _("^BG%s^K1 mastered the art of self-nading%s%s"), "") \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_ICE_FREEZE, 2, 1, "s1 s2loc spree_lost", "s1", "notify_nade_ice", _("^BG%s^K1 was frozen to death by their own Ice Nade%s%s"), _("^BG%s^K1 felt a little chilly%s%s")) \
+ MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NADE_HEAL, 2, 1, "s1 s2loc spree_lost", "s1", "notify_nade_heal", _("^BG%s^K1's Healing Nade didn't quite heal them%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_NOAMMO, 2, 1, "s1 s2loc spree_lost", "s1", "notify_outofammo", _("^BG%s^K1 died%s%s. What's the point of living without ammo?"), _("^BG%s^K1 ran out of ammo%s%s")) \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_ROT, 2, 1, "s1 s2loc spree_lost", "s1", "notify_death", _("^BG%s^K1 rotted away%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_DEATH_SELF_SHOOTING_STAR, 2, 1, "s1 s2loc spree_lost", "s1", "notify_shootingstar", _("^BG%s^K1 became a shooting star%s%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_FREEZE, 2, 0, "s1 s2", "", "", _("^BG%s^K1 was frozen by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED, 2, 0, "s1 s2", "", "", _("^BG%s^K3 was revived by ^BG%s"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_FALL, 1, 0, "s1", "", "", _("^BG%s^K3 was revived by falling"), "") \
+ MSG_INFO_NOTIF(1, INFO_FREEZETAG_REVIVED_NADE, 1, 0, "s1", "", "", _("^BG%s^K3 was revived by their Nade explosion"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_AUTO_REVIVED, 1, 1, "s1 f1", "", "", _("^BG%s^K3 was automatically revived after %s second(s)"), "") \
MULTITEAM_INFO(1, INFO_ROUND_TEAM_WIN_, 4, 0, 0, "", "", "", _("^TC^TT^BG team wins the round"), "") \
MSG_INFO_NOTIF(1, INFO_ROUND_PLAYER_WIN, 1, 0, "s1", "", "", _("^BG%s^BG wins the round"), "") \
MSG_INFO_NOTIF(1, INFO_ROUND_OVER, 0, 0, "", "", "", _("^BGRound over, there's no winner"), "") \
MSG_INFO_NOTIF(1, INFO_FREEZETAG_SELF, 1, 0, "s1", "", "", _("^BG%s^K1 froze themself"), "") \
MSG_INFO_NOTIF(1, INFO_GODMODE_OFF, 0, 1, "f1", "", "", _("^BGGodmode saved you %s units of damage, cheater!"), "") \
+ MSG_INFO_NOTIF(1, INFO_ITEM_BUFF, 1, 1, "s1 item_buffname", "", "", _("^BG%s^BG got the %s^BG Buff!"), "") \
+ MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_LOST, 1, 1, "s1 item_buffname", "", "", _("^BG%s^BG lost the %s^BG Buff!"), "") \
+ MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_DROP, 0, 1, "item_buffname", "", "", _("^BGYou dropped the %s^BG Buff!"), "") \
+ MSG_INFO_NOTIF(1, INFO_ITEM_BUFF_GOT, 0, 1, "item_buffname", "", "", _("^BGYou got the %s^BG Buff!"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DONTHAVE, 0, 1, "item_wepname", "", "", _("^BGYou do not have the ^F1%s"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_DROP, 1, 1, "item_wepname item_wepammo", "", "", _("^BGYou dropped the ^F1%s^BG%s"), "") \
MSG_INFO_NOTIF(0, INFO_ITEM_WEAPON_GOT, 0, 1, "item_wepname", "", "", _("^BGYou got the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE, 1, 4, "spree_cen s1 frag_stats", NO_CPID, "0 0", _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE, 1, 2, "spree_cen s1 frag_ping", NO_CPID, "0 0", _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s")) \
MSG_CENTER_NOTIF(1, CENTER_NADE_THROW, 0, 0, "", CPID_NADES, "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_NADE_BONUS, 0, 0, "", CPID_NADES, "0 0", _("^F2You got a ^K1BONUS GRENADE^F2!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_AUTOTEAMCHANGE, 0, 1, "death_team", NO_CPID, "0 0", _("^BGYou have been moved into a different team\nYou are now on: %s"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_BETRAYAL, 0, 0, "", NO_CPID, "0 0", _("^K1Don't shoot your team mates!"), _("^K1Don't go against your team mates!")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_CAMP, 0, 0, "", NO_CPID, "0 0", _("^K1Die camper!"), _("^K1Reconsider your tactics, camper!")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_LAVA, 0, 0, "", NO_CPID, "0 0", _("^K1You couldn't stand the heat!"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_MONSTER, 0, 0, "", NO_CPID, "0 0", _("^K1You were killed by a monster!"), _("^K1You need to watch out for monsters!")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE, 0, 0, "", NO_CPID, "0 0", _("^K1You forgot to put the pin back in!"), _("^K1Tastes like chicken!")) \
+ MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_NAPALM, 0, 0, "", NO_CPID, "0 0", _("^K1Hanging around a napalm explosion is bad!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_ICE_FREEZE, 0, 0, "", NO_CPID, "0 0", _("^K1You got a little bit too cold!"), _("^K1You felt a little chilly!")) \
+ MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NADE_HEAL, 0, 0, "", NO_CPID, "0 0", _("^K1Your Healing Nade is a bit defective"), "") \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_NOAMMO, 0, 0, "", NO_CPID, "0 0", _("^K1You were killed for running out of ammo..."), _("^K1You are respawning for running out of ammo...")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_ROT, 0, 0, "", NO_CPID, "0 0", _("^K1You grew too old without taking your medicine"), _("^K1You need to preserve your health")) \
MSG_CENTER_NOTIF(1, CENTER_DEATH_SELF_SHOOTING_STAR, 0, 0, "", NO_CPID, "0 0", _("^K1You became a shooting star!"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FREEZE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You froze ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_FROZEN, 1, 0, "s1", NO_CPID, "0 0", _("^K1You were frozen by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE, 1, 0, "s1", NO_CPID, "0 0", _("^K3You revived ^BG%s"), "") \
- MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_FALL, 0, 0, "", NO_CPID, "0 0", _("^K3You revived yourself"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVE_SELF, 0, 0, "", NO_CPID, "0 0", _("^K3You revived yourself"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_REVIVED, 1, 0, "s1", NO_CPID, "0 0", _("^K3You were revived by ^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_AUTO_REVIVED, 0, 1, "f1", NO_CPID, "0 0", _("^K3You were automatically revived after %s second(s)"), "") \
MULTITEAM_CENTER(1, CENTER_ROUND_TEAM_WIN_, 4, 0, 0, "", CPID_ROUND, "0 0", _("^TC^TT^BG team wins the round"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SELF, 0, 0, "", NO_CPID, "0 0", _("^K1You froze yourself"), "") \
MSG_CENTER_NOTIF(1, CENTER_FREEZETAG_SPAWN_LATE, 0, 0, "", NO_CPID, "0 0", _("^K1Round already started, you spawn as frozen"), "") \
MSG_CENTER_NOTIF(1, CENTER_INVASION_SUPERMONSTER, 1, 0, "s1", NO_CPID, "0 0", _("^K1A %s has arrived!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_DROP, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG Buff!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_ITEM_BUFF_GOT, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG Buff!"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DONTHAVE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_DROP, 1, 1, "item_wepname item_wepammo", CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_ITEM_WEAPON_GOT, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") \
MSG_CENTER_NOTIF(1, CENTER_JOIN_PREVENT, 0, 0, "", CPID_PREVENT_JOIN, "0 0", _("^K1You may not join the game at this time.\nThe player limit reached maximum capacity."), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_DROPPED, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has dropped the ball!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_PICKUP, 1, 0, "s1", CPID_KEEPAWAY, "0 0", _("^BG%s^BG has picked up the ball!"), "") \
+ MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_PICKUP_SELF, 0, 0, "", CPID_KEEPAWAY, "0 0", _("^BGYou picked up the ball"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEEPAWAY_WARN, 0, 0, "", CPID_KEEPAWAY_WARN, "0 0", _("^BGKilling people while you don't have the ball gives no points!"), "") \
MSG_CENTER_NOTIF(1, CENTER_KEYHUNT_HELP, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in your team's hands!\nHelp the key carriers to meet!"), "") \
MULTITEAM_CENTER(1, CENTER_KEYHUNT_INTERFERE_, 4, 0, 0, "", CPID_KEYHUNT, "0 0", _("^BGAll keys are in ^TC^TT team^BG's hands!\nInterfere ^F4NOW^BG!"), "") \
MSG_MULTI_NOTIF(1, DEATH_MURDER_LAVA, NO_MSG, INFO_DEATH_MURDER_LAVA, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_MONSTER, NO_MSG, INFO_DEATH_MURDER_MONSTER, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE, NO_MSG, INFO_DEATH_MURDER_NADE, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_NAPALM, NO_MSG, INFO_DEATH_MURDER_NADE_NAPALM, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE, NO_MSG, INFO_DEATH_MURDER_NADE_ICE, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_ICE_FREEZE, NO_MSG, INFO_DEATH_MURDER_NADE_ICE_FREEZE, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_NADE_HEAL, NO_MSG, INFO_DEATH_MURDER_NADE_HEAL, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_SHOOTING_STAR, NO_MSG, INFO_DEATH_MURDER_SHOOTING_STAR, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_SLIME, NO_MSG, INFO_DEATH_MURDER_SLIME, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_SWAMP, NO_MSG, INFO_DEATH_MURDER_SWAMP, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_DEATH, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_GUN, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_GUN, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_MURDER_VH_WAKI_ROCKET, NO_MSG) \
+ MSG_MULTI_NOTIF(1, DEATH_MURDER_VENGEANCE, NO_MSG, INFO_DEATH_MURDER_VENGEANCE, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_MURDER_VOID, NO_MSG, INFO_DEATH_MURDER_VOID, NO_MSG) \
MSG_MULTI_NOTIF(1, DEATH_SELF_AUTOTEAMCHANGE, NO_MSG, INFO_DEATH_SELF_AUTOTEAMCHANGE, CENTER_DEATH_SELF_AUTOTEAMCHANGE) \
MSG_MULTI_NOTIF(1, DEATH_SELF_BETRAYAL, NO_MSG, INFO_DEATH_SELF_BETRAYAL, CENTER_DEATH_SELF_BETRAYAL) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_JUMP, NO_MSG, INFO_DEATH_SELF_MON_ZOMBIE_JUMP, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_MON_ZOMBIE_MELEE, NO_MSG, INFO_DEATH_SELF_MON_ZOMBIE_MELEE, CENTER_DEATH_SELF_MONSTER) \
MSG_MULTI_NOTIF(1, DEATH_SELF_NADE, NO_MSG, INFO_DEATH_SELF_NADE, CENTER_DEATH_SELF_NADE) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_NAPALM, NO_MSG, INFO_DEATH_SELF_NADE_NAPALM, CENTER_DEATH_SELF_NADE_NAPALM) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE, NO_MSG, INFO_DEATH_SELF_NADE_ICE, CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_ICE_FREEZE, NO_MSG, INFO_DEATH_SELF_NADE_ICE_FREEZE, CENTER_DEATH_SELF_NADE_ICE_FREEZE) \
+ MSG_MULTI_NOTIF(1, DEATH_SELF_NADE_HEAL, NO_MSG, INFO_DEATH_SELF_NADE_HEAL, CENTER_DEATH_SELF_NADE_HEAL) \
MSG_MULTI_NOTIF(1, DEATH_SELF_NOAMMO, NO_MSG, INFO_DEATH_SELF_NOAMMO, CENTER_DEATH_SELF_NOAMMO) \
MSG_MULTI_NOTIF(1, DEATH_SELF_ROT, NO_MSG, INFO_DEATH_SELF_ROT, CENTER_DEATH_SELF_ROT) \
MSG_MULTI_NOTIF(1, DEATH_SELF_SHOOTING_STAR, NO_MSG, INFO_DEATH_SELF_SHOOTING_STAR, CENTER_DEATH_SELF_SHOOTING_STAR) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_DEATH, NO_MSG, INFO_DEATH_SELF_VH_WAKI_DEATH, CENTER_DEATH_SELF_VH_WAKI_DEATH) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VH_WAKI_ROCKET, NO_MSG, INFO_DEATH_SELF_VH_WAKI_ROCKET, CENTER_DEATH_SELF_VH_WAKI_ROCKET) \
MSG_MULTI_NOTIF(1, DEATH_SELF_VOID, NO_MSG, INFO_DEATH_SELF_VOID, CENTER_DEATH_SELF_VOID) \
+ MSG_MULTI_NOTIF(1, ITEM_BUFF_DROP, NO_MSG, INFO_ITEM_BUFF_DROP, CENTER_ITEM_BUFF_DROP) \
+ MSG_MULTI_NOTIF(1, ITEM_BUFF_GOT, NO_MSG, INFO_ITEM_BUFF_GOT, CENTER_ITEM_BUFF_GOT) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_DONTHAVE, NO_MSG, INFO_ITEM_WEAPON_DONTHAVE, CENTER_ITEM_WEAPON_DONTHAVE) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_DROP, NO_MSG, INFO_ITEM_WEAPON_DROP, CENTER_ITEM_WEAPON_DROP) \
MSG_MULTI_NOTIF(1, ITEM_WEAPON_GOT, NO_MSG, INFO_ITEM_WEAPON_GOT, CENTER_ITEM_WEAPON_GOT) \
item_wepname: return full name of a weapon from weaponid
item_wepammo: ammo display for weapon from string
item_centime: amount of time to display weapon message in centerprint
+ item_buffname: return full name of a buff from buffid
death_team: show the full name of the team a player is switching from
*/
ARG_CASE(ARG_CS_SV, "spree_end", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-1, "", "", f1) : "")) \
ARG_CASE(ARG_CS_SV, "spree_lost", (autocvar_notification_show_sprees ? notif_arg_spree_inf(-2, "", "", f1) : "")) \
ARG_CASE(ARG_CS_SV, "item_wepname", W_Name(f1)) \
+ ARG_CASE(ARG_CS_SV, "item_buffname", sprintf("%s%s", rgb_to_hexcolor(Buff_Color(f1)), Buff_PrettyName(f1))) \
ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \
ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \
ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \
--- /dev/null
-// 69 empty?
+ // Full list of all stat constants, icnluded in a single location for easy reference
+ // 255 is the current limit (MAX_CL_STATS - 1), engine will need to be modified if you wish to add more stats
+
+ const float MAX_CL_STATS = 256;
+ const float STAT_HEALTH = 0;
+ // 1 empty?
+ const float STAT_WEAPON = 2;
+ const float STAT_AMMO = 3;
+ const float STAT_ARMOR = 4;
+ const float STAT_WEAPONFRAME = 5;
+ const float STAT_SHELLS = 6;
+ const float STAT_NAILS = 7;
+ const float STAT_ROCKETS = 8;
+ const float STAT_CELLS = 9;
+ const float STAT_ACTIVEWEAPON = 10;
+ const float STAT_TOTALSECRETS = 11;
+ const float STAT_TOTALMONSTERS = 12;
+ const float STAT_SECRETS = 13;
+ const float STAT_MONSTERS = 14;
+ const float STAT_ITEMS = 15;
+ const float STAT_VIEWHEIGHT = 16;
+ // 17 empty?
+ // 18 empty?
+ // 19 empty?
+ // 20 empty?
+ const float STAT_VIEWZOOM = 21;
+ // 22 empty?
+ // 23 empty?
+ // 24 empty?
+ // 25 empty?
+ // 26 empty?
+ // 27 empty?
+ // 28 empty?
+ // 29 empty?
+ // 30 empty?
+ // 31 empty?
+ const float STAT_KH_KEYS = 32;
+ const float STAT_CTF_STATE = 33;
+ // 34 empty?
+ const float STAT_WEAPONS = 35;
+ const float STAT_SWITCHWEAPON = 36;
+ const float STAT_GAMESTARTTIME = 37;
+ const float STAT_STRENGTH_FINISHED = 38;
+ const float STAT_INVINCIBLE_FINISHED = 39;
+ // 40 empty?
+ // 41 empty?
+ const float STAT_PRESSED_KEYS = 42;
+ const float STAT_ALLOW_OLDNEXBEAM = 43; // this stat could later contain some other bits of info, like, more server-side particle config
+ const float STAT_FUEL = 44;
+ const float STAT_NB_METERSTART = 45;
+ const float STAT_SHOTORG = 46; // compressShotOrigin
+ const float STAT_LEADLIMIT = 47;
+ const float STAT_WEAPON_CLIPLOAD = 48;
+ const float STAT_WEAPON_CLIPSIZE = 49;
+ const float STAT_NEX_CHARGE = 50;
+ const float STAT_LAST_PICKUP = 51;
+ const float STAT_HUD = 52;
+ const float STAT_NEX_CHARGEPOOL = 53;
+ const float STAT_HIT_TIME = 54;
+ const float STAT_TYPEHIT_TIME = 55;
+ const float STAT_LAYED_MINES = 56;
+ const float STAT_HAGAR_LOAD = 57;
+ const float STAT_SWITCHINGWEAPON = 58;
+ const float STAT_SUPERWEAPONS_FINISHED = 59;
+ const float STAT_VEHICLESTAT_HEALTH = 60;
+ const float STAT_VEHICLESTAT_SHIELD = 61;
+ const float STAT_VEHICLESTAT_ENERGY = 62;
+ const float STAT_VEHICLESTAT_AMMO1 = 63;
+ const float STAT_VEHICLESTAT_RELOAD1 = 64;
+ const float STAT_VEHICLESTAT_AMMO2 = 65;
+ const float STAT_VEHICLESTAT_RELOAD2 = 66;
+ const float STAT_VEHICLESTAT_W2MODE = 67;
+ // 68 empty?
-// 79 empty?
-// 80 empty?
++const float STAT_NADE_TIMER = 69;
+ const float STAT_SECRETS_TOTAL = 70;
+ const float STAT_SECRETS_FOUND = 71;
+ const float STAT_RESPAWN_TIME = 72;
+ const float STAT_ROUNDSTARTTIME = 73;
+ const float STAT_WEAPONS2 = 74;
+ const float STAT_WEAPONS3 = 75;
+ const float STAT_MONSTERS_TOTAL = 76;
+ const float STAT_MONSTERS_KILLED = 77;
+ const float STAT_BUFFS = 78;
++const float STAT_NADE_BONUS = 79;
++const float STAT_NADE_BONUS_TYPE = 80;
++const float STAT_NADE_BONUS_SCORE = 81;
++const float STAT_HEALING_ORB = 82;
++const float STAT_HEALING_ORB_ALPHA = 83;
++// 84 empty?
++// 85 empty?
+ // 86 empty?
+ // 87 empty?
+ // 88 empty?
+ // 89 empty?
+ // 90 empty?
+ // 91 empty?
+ // 92 empty?
+ // 93 empty?
+ // 94 empty?
+ // 95 empty?
+ // 96 empty?
+ // 97 empty?
+ // 98 empty?
+ // 99 empty?
+
+
+ /* The following stats change depending on the gamemode, so can share the same ID */
+ // IDs 100 to 104 reserved for gamemodes
+
+ // freeze tag, clan arena, jailbreak
+ const float STAT_REDALIVE = 100;
+ const float STAT_BLUEALIVE = 101;
+ const float STAT_YELLOWALIVE = 102;
+ const float STAT_PINKALIVE = 103;
+
+ // domination
+ const float STAT_DOM_TOTAL_PPS = 100;
+ const float STAT_DOM_PPS_RED = 101;
+ const float STAT_DOM_PPS_BLUE = 102;
+ const float STAT_DOM_PPS_YELLOW = 103;
+ const float STAT_DOM_PPS_PINK = 104;
+
+ // vip
+ const float STAT_VIP = 100;
+ const float STAT_VIP_RED = 101;
+ const float STAT_VIP_BLUE = 102;
+ const float STAT_VIP_YELLOW = 103;
+ const float STAT_VIP_PINK = 104;
+
+ // key hunt
+ const float STAT_KH_REDKEY_TEAM = 100;
+ const float STAT_KH_BLUEKEY_TEAM = 101;
+ const float STAT_KH_YELLOWKEY_TEAM = 102;
+ const float STAT_KH_PINKKEY_TEAM = 103;
+
+ /* Gamemode-specific stats end here */
+
+
+ const float STAT_FROZEN = 105;
+ const float STAT_REVIVE_PROGRESS = 106;
+ // 107 empty?
+ // 108 empty?
+ // 109 empty?
+ // 110 empty?
+ // 111 empty?
+ // 112 empty?
+ // 113 empty?
+ // 114 empty?
+ // 115 empty?
+ // 116 empty?
+ // 117 empty?
+ // 118 empty?
+ // 119 empty?
+ // 120 empty?
+ // 121 empty?
+ // 122 empty?
+ // 123 empty?
+ // 124 empty?
+ // 125 empty?
+ // 126 empty?
+ // 127 empty?
+ // 128 empty?
+ // 129 empty?
+ // 130 empty?
+ // 131 empty?
+ // 132 empty?
+ // 133 empty?
+ // 134 empty?
+ // 135 empty?
+ // 136 empty?
+ // 137 empty?
+ // 138 empty?
+ // 139 empty?
+ // 140 empty?
+ // 141 empty?
+ // 142 empty?
+ // 143 empty?
+ // 144 empty?
+ // 145 empty?
+ // 146 empty?
+ // 147 empty?
+ // 148 empty?
+ // 149 empty?
+ // 150 empty?
+ // 151 empty?
+ // 152 empty?
+ // 153 empty?
+ // 154 empty?
+ // 155 empty?
+ // 156 empty?
+ // 157 empty?
+ // 158 empty?
+ // 159 empty?
+ // 160 empty?
+ // 161 empty?
+ // 162 empty?
+ // 162 empty?
+ // 163 empty?
+ // 164 empty?
+ // 165 empty?
+ // 166 empty?
+ // 167 empty?
+ // 168 empty?
+ // 169 empty?
+ // 170 empty?
+ // 171 empty?
+ // 172 empty?
+ // 173 empty?
+ // 174 empty?
+ // 175 empty?
+ // 176 empty?
+ // 177 empty?
+ // 178 empty?
+ // 179 empty?
+ // 180 empty?
+ // 181 empty?
+ // 182 empty?
+ // 183 empty?
+ // 184 empty?
+ // 185 empty?
+ // 186 empty?
+ // 187 empty?
+ // 188 empty?
+ // 189 empty?
+ // 190 empty?
+ // 191 empty?
+ // 192 empty?
+ // 193 empty?
+ // 194 empty?
+ // 195 empty?
+ // 196 empty?
+ // 197 empty?
+ // 198 empty?
+ // 199 empty?
+ // 200 empty?
+ // 201 empty?
+ // 202 empty?
+ // 203 empty?
+ // 204 empty?
+ // 205 empty?
+ // 206 empty?
+ // 207 empty?
+ // 208 empty?
+ // 209 empty?
+ // 210 empty?
+ // 211 empty?
+ // 212 empty?
+ // 213 empty?
+ // 214 empty?
+ // 215 empty?
+ // 216 empty?
+ // 217 empty?
+ // 218 empty?
+ // 219 empty?
+ const float STAT_MOVEVARS_AIRACCEL_QW_STRETCHFACTOR = 220;
+ const float STAT_MOVEVARS_AIRCONTROL_PENALTY = 221;
+ const float STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW = 222;
+ const float STAT_MOVEVARS_AIRSTRAFEACCEL_QW = 223;
+ const float STAT_MOVEVARS_AIRCONTROL_POWER = 224;
+ const float STAT_MOVEFLAGS = 225;
+ const float STAT_MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL = 226;
+ const float STAT_MOVEVARS_WARSOWBUNNY_ACCEL = 227;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TOPSPEED = 228;
+ const float STAT_MOVEVARS_WARSOWBUNNY_TURNACCEL = 229;
+ const float STAT_MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO = 230;
+ const float STAT_MOVEVARS_AIRSTOPACCELERATE = 231;
+ const float STAT_MOVEVARS_AIRSTRAFEACCELERATE = 232;
+ const float STAT_MOVEVARS_MAXAIRSTRAFESPEED = 233;
+ const float STAT_MOVEVARS_AIRCONTROL = 234;
+ const float STAT_FRAGLIMIT = 235;
+ const float STAT_TIMELIMIT = 236;
+ const float STAT_MOVEVARS_WALLFRICTION = 237;
+ const float STAT_MOVEVARS_FRICTION = 238;
+ const float STAT_MOVEVARS_WATERFRICTION = 239;
+ const float STAT_MOVEVARS_TICRATE = 240;
+ const float STAT_MOVEVARS_TIMESCALE = 241;
+ const float STAT_MOVEVARS_GRAVITY = 242;
+ const float STAT_MOVEVARS_STOPSPEED = 243;
+ const float STAT_MOVEVARS_MAXSPEED = 244;
+ const float STAT_MOVEVARS_SPECTATORMAXSPEED = 245;
+ const float STAT_MOVEVARS_ACCELERATE = 246;
+ const float STAT_MOVEVARS_AIRACCELERATE = 247;
+ const float STAT_MOVEVARS_WATERACCELERATE = 248;
+ const float STAT_MOVEVARS_ENTGRAVITY = 249;
+ const float STAT_MOVEVARS_JUMPVELOCITY = 250;
+ const float STAT_MOVEVARS_EDGEFRICTION = 251;
+ const float STAT_MOVEVARS_MAXAIRSPEED = 252;
+ const float STAT_MOVEVARS_STEPHEIGHT = 253;
+ const float STAT_MOVEVARS_AIRACCEL_QW = 254;
+ const float STAT_MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION = 255;
float autocvar_g_balance_grapplehook_speed_pull;
float autocvar_g_balance_grapplehook_stretch;
float autocvar_g_balance_grapplehook_damagedbycontents;
+ float autocvar_g_balance_grapplehook_refire;
float autocvar_g_balance_grenadelauncher_bouncefactor;
float autocvar_g_balance_grenadelauncher_bouncestop;
float autocvar_g_balance_grenadelauncher_primary_ammo;
string autocvar_g_forced_team_pink;
string autocvar_g_forced_team_red;
string autocvar_g_forced_team_yellow;
+float autocvar_g_freezetag_frozen_damage_trigger;
float autocvar_g_freezetag_frozen_force;
float autocvar_g_freezetag_frozen_maxtime;
float autocvar_g_freezetag_revive_falldamage;
float autocvar_g_freezetag_revive_falldamage_health;
+float autocvar_g_freezetag_revive_nade;
+float autocvar_g_freezetag_revive_nade_health;
float autocvar_g_freezetag_point_leadlimit;
float autocvar_g_freezetag_point_limit;
float autocvar_g_freezetag_revive_extra_size;
float autocvar_sv_dodging_wall_distance_threshold;
float autocvar_sv_dodging_wall_dodging;
float autocvar_sv_dodging_frozen;
+float autocvar_sv_dodging_frozen_doubletap;
float autocvar_sv_doublejump;
float autocvar_sv_eventlog;
float autocvar_sv_eventlog_console;
float autocvar_sv_vote_call;
float autocvar_sv_vote_change;
string autocvar_sv_vote_commands;
+ float autocvar_sv_vote_gametype;
+ float autocvar_sv_vote_gametype_timeout;
+ string autocvar_sv_vote_gametype_options;
+ float autocvar_sv_vote_gametype_keeptwotime;
+ float autocvar_sv_vote_gametype_default_current;
float autocvar_sv_vote_limit;
float autocvar_sv_vote_majority_factor;
float autocvar_sv_vote_majority_factor_of_voted;
float autocvar_g_physical_items_reset;
float autocvar_g_monsters;
float autocvar_g_monsters_edit;
+ float autocvar_g_monsters_sounds;
float autocvar_g_monsters_think_delay;
float autocvar_g_monsters_max;
float autocvar_g_monsters_max_perplayer;
float autocvar_g_touchexplode_edgedamage;
float autocvar_g_touchexplode_force;
float autocvar_g_invasion_round_timelimit;
- #define autocvar_g_invasion_round_limit cvar("g_invasion_round_limit")
+ float autocvar_g_invasion_teams;
+ float autocvar_g_invasion_team_spawns;
+ float autocvar_g_invasion_spawnpoint_spawn_delay;
+ #define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit")
float autocvar_g_invasion_warmup;
float autocvar_g_invasion_monster_count;
float autocvar_g_invasion_zombies_only;
float autocvar_g_random_gravity_delay;
float autocvar_g_nades;
float autocvar_g_nades_spawn;
+float autocvar_g_nades_spawn_count;
+float autocvar_g_nades_client_select;
float autocvar_g_nades_nade_lifetime;
float autocvar_g_nades_nade_minforce;
float autocvar_g_nades_nade_maxforce;
float autocvar_g_nades_nade_radius;
float autocvar_g_nades_nade_force;
float autocvar_g_nades_nade_newton_style;
+float 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;
+float 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;
+float autocvar_g_nades_napalm_selfdamage;
+float autocvar_g_nades_nade_type;
+float autocvar_g_nades_bonus_type;
+float autocvar_g_nades_bonus;
+float autocvar_g_nades_bonus_onstrength;
+float autocvar_g_nades_bonus_client_select;
+float autocvar_g_nades_bonus_max;
+float autocvar_g_nades_bonus_score_max;
+float autocvar_g_nades_bonus_score_time;
+float autocvar_g_nades_bonus_score_time_flagcarrier;
+float autocvar_g_nades_bonus_score_minor;
+float autocvar_g_nades_bonus_score_low;
+float autocvar_g_nades_bonus_score_high;
+float autocvar_g_nades_bonus_score_medium;
+float autocvar_g_nades_bonus_score_spree;
+float autocvar_g_nades_ice_freeze_time;
+float autocvar_g_nades_ice_health;
+float autocvar_g_nades_ice_explode;
+float autocvar_g_nades_ice_teamcheck;
+float autocvar_g_nades_heal_time;
+float autocvar_g_nades_heal_rate;
+float autocvar_g_nades_heal_friend;
+float autocvar_g_nades_heal_foe;
+string autocvar_g_nades_pokenade_monster_type;
+//float autocvar_g_nades_pokenade_monster_lifetime;
float autocvar_g_campcheck_damage;
float autocvar_g_campcheck_distance;
float autocvar_g_campcheck_interval;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health;
float autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath;
+ float autocvar_g_buffs_waypoint_distance;
+ float autocvar_g_buffs_randomize;
+ float autocvar_g_buffs_random_lifetime;
+ float autocvar_g_buffs_random_location;
+ float autocvar_g_buffs_random_location_attempts;
+ float autocvar_g_buffs_spawn_count;
+ float autocvar_g_buffs_replace_powerups;
+ float autocvar_g_buffs_cooldown_activate;
+ float autocvar_g_buffs_cooldown_respawn;
+ float autocvar_g_buffs_resistance_blockpercent;
+ 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 autocvar_g_buffs_vengeance_damage_multiplier;
+ float autocvar_g_buffs_bash_force;
+ float autocvar_g_buffs_bash_force_self;
+ float autocvar_g_buffs_disability_time;
+ float autocvar_g_buffs_disability_speed;
+ float autocvar_g_buffs_disability_rate;
+ float autocvar_g_buffs_speed_speed;
+ float autocvar_g_buffs_speed_rate;
+ float autocvar_g_buffs_speed_damage_take;
+ float autocvar_g_buffs_speed_regen;
+ float autocvar_g_buffs_vampire_damage_steal;
+ float autocvar_g_buffs_invisible_alpha;
+ float autocvar_g_buffs_flight_gravity;
+ float autocvar_g_buffs_jump_height;
+
- void race_send_recordtime(float msg);
- void race_SendRankings(float pos, float prevpos, float del, float msg);
-
void send_CSQC_teamnagger() {
WriteByte(MSG_BROADCAST, SVC_TEMPENTITY);
WriteByte(MSG_BROADCAST, TE_CSQC_TEAMNAGGER);
{
entity spot;
self.hud = HUD_NORMAL;
- race_PreSpawnObserver();
spot = SelectSpawnPoint (TRUE);
if(!spot)
WriteEntity(MSG_ONE, self);
}
- if((g_race && g_race_qualifying) || g_cts)
- {
- if(PlayerScore_Add(self, SP_RACE_FASTEST, 0))
- self.frags = FRAGS_LMS_LOSER;
- else
- self.frags = FRAGS_SPECTATOR;
- }
- else
- self.frags = FRAGS_SPECTATOR;
+ self.frags = FRAGS_SPECTATOR;
MUTATOR_CALLHOOK(MakePlayerObserver);
Portal_ClearAll(self);
+ Unfreeze(self);
+
if(self.alivetime)
{
if(!warmup_stage)
self.angles_z = 0;
self.fixangle = TRUE;
self.crouch = FALSE;
+ self.revival_time = 0;
setorigin (self, (spot.origin + PL_VIEW_OFS)); // offset it so that the spectator spawns higher off the ground, looks better this way
self.prevorigin = self.origin;
if(teamplay)
{
string s;
- s = Team_ColorName_Lower(self.team);
+ s = Static_Team_ColorName_Lower(self.team);
if(s != "neutral")
{
defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s));
if(self.team < 0)
JoinBestTeam(self, FALSE, TRUE);
- race_PreSpawn();
-
spot = SelectSpawnPoint (FALSE);
if(!spot)
{
self.punchvector = '0 0 0';
self.oldvelocity = self.velocity;
self.fire_endtime = -1;
+ self.revival_time = 0;
entity spawnevent = spawn();
spawnevent.owner = self;
Net_LinkEntity(spawnevent, FALSE, 0.5, SpawnEvent_Send);
+ // Cut off any still running player sounds.
+ stopsound(self, CH_PLAYER_SINGLE);
+
self.model = "";
FixPlayermodel();
self.drawonlytoclient = world;
self.speedrunning = FALSE;
- race_PostSpawn(spot);
-
//stuffcmd(self, "chase_active 0");
//stuffcmd(self, "set viewsize $tmpviewsize \n");
activator = world;
self = oldself;
+ Unfreeze(self);
+
spawn_spot = spot;
MUTATOR_CALLHOOK(PlayerSpawn);
{
if(gameover) return;
if(self.player_blocked) return;
- if(self.freezetag_frozen) return;
+ if(self.frozen) return;
ClientKill_TeamChange(0);
}
anticheat_init();
- race_PreSpawnObserver();
-
// identify the right forced team
if(autocvar_g_campaign)
{
else
self.hitplotfh = -1;
- if(g_race || g_cts) {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
-
- msg_entity = self;
- race_send_recordtime(MSG_ONE);
- race_send_speedaward(MSG_ONE);
-
- speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed")));
- speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp")));
- race_send_speedaward_alltimebest(MSG_ONE);
-
- float i;
- for (i = 1; i <= RANKINGS_CNT; ++i) {
- race_SendRankings(i, 0, 0, MSG_ONE);
- }
- }
- else if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca) // teamnagger is currently bad for ca
+ if(autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1)) && !g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts
send_CSQC_teamnagger();
CheatInitClient();
Portal_ClearAll(self);
+ Unfreeze(self);
+
RemoveGrapplingHook(self);
// Here, everything has been done that requires this player to be a client.
void player_regen (void)
{
+ float max_mod, regen_mod, rot_mod, limit_mod;
+ max_mod = regen_mod = rot_mod = limit_mod = 1;
+ regen_mod_max = max_mod;
+ regen_mod_regen = regen_mod;
+ regen_mod_rot = rot_mod;
+ regen_mod_limit = limit_mod;
if(!MUTATOR_CALLHOOK(PlayerRegen))
+ if(!self.frozen)
{
- float minh, mina, maxh, maxa, limith, limita, max_mod, regen_mod, rot_mod, limit_mod;
+ float minh, mina, maxh, maxa, limith, limita;
maxh = autocvar_g_balance_health_rotstable;
maxa = autocvar_g_balance_armor_rotstable;
minh = autocvar_g_balance_health_regenstable;
mina = autocvar_g_balance_armor_regenstable;
limith = autocvar_g_balance_health_limit;
limita = autocvar_g_balance_armor_limit;
-
- max_mod = regen_mod = rot_mod = limit_mod = 1;
+
+ max_mod = regen_mod_max;
+ regen_mod = regen_mod_regen;
+ rot_mod = regen_mod_rot;
+ limit_mod = regen_mod_limit;
maxh = maxh * max_mod;
minh = minh * max_mod;
self.dmg_inflictor = spectatee.dmg_inflictor;
self.v_angle = spectatee.v_angle;
self.angles = spectatee.v_angle;
+ self.frozen = spectatee.frozen;
+ self.revive_progress = spectatee.revive_progress;
if(!self.BUTTON_USE)
self.fixangle = TRUE;
setorigin(self, spectatee.origin);
if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (self.wasplayer && autocvar_g_changeteam_banned) || self.team_forced > 0)
{
self.classname = "player";
+ nades_RemoveBonus(self);
if(autocvar_g_campaign || autocvar_g_balance_teams)
{ JoinBestTeam(self, FALSE, TRUE); }
return;
#endif
+ if(self.frozen == 2)
+ {
+ self.revive_progress = bound(0, self.revive_progress + frametime * self.revive_speed, 1);
+ self.health = max(1, self.revive_progress * start_health);
+ self.iceblock.alpha = bound(0.2, 1 - self.revive_progress, 1);
+
+ if(self.revive_progress >= 1)
+ Unfreeze(self);
+ }
+ else if(self.frozen == 3)
+ {
+ self.revive_progress = bound(0, self.revive_progress - frametime * self.revive_speed, 1);
+ self.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * self.revive_progress );
+
+ if(self.health < 1)
+ {
+ if(self.vehicle)
+ vehicles_exit(VHEF_RELESE);
+ self.event_damage(self, self.frozen_by, 1, DEATH_NADE_ICE_FREEZE, self.origin, '0 0 0');
+ }
+ else if ( self.revive_progress <= 0 )
+ Unfreeze(self);
+ }
+
MUTATOR_CALLHOOK(PlayerPreThink);
if(!self.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button
do_crouch = 0;
if(self.vehicle)
do_crouch = 0;
- if(self.freezetag_frozen)
+ if(self.frozen)
do_crouch = 0;
if(self.weapon == WEP_SHOTGUN && self.weaponentity.wframe == WFRAME_FIRE2 && time < self.weapon_nextthink)
do_crouch = 0;
if(self.spectatee_status != oldspectatee_status)
{
ClientData_Touch(self);
- if(g_race || g_cts)
- race_InitSpectator();
}
if(self.teamkill_soundtime)
playerdemo_write();
- if((g_cts || g_race) && self.cvar_cl_allow_uidtracking == 1 && self.cvar_cl_allow_uid2name == 1)
- {
- if (!self.stored_netname)
- self.stored_netname = strzone(uid2name(self.crypto_idfp));
- if(self.stored_netname != self.netname)
- {
- db_put(ServerProgsDB, strcat("/uid2name/", self.crypto_idfp), self.netname);
- strunzone(self.stored_netname);
- self.stored_netname = strzone(self.netname);
- }
- }
-
- /*
- if(g_race)
- dprintf("%f %.6f\n", time, race_GetFractionalLapCount(self));
- */
-
CSQCMODEL_AUTOUPDATE();
}
*/
void PlayerJump (void)
{
+ if(self.frozen)
+ return; // no jumping in freezetag when frozen
+
+ if(self.player_blocked)
+ return; // no jumping while blocked
+
float doublejump = FALSE;
+ float mjumpheight = autocvar_sv_jumpvelocity;
player_multijump = doublejump;
+ player_jumpheight = mjumpheight;
if(MUTATOR_CALLHOOK(PlayerJump))
return;
doublejump = player_multijump;
-
- float mjumpheight;
+ mjumpheight = player_jumpheight;
if (autocvar_sv_doublejump)
{
}
}
- mjumpheight = autocvar_sv_jumpvelocity;
if (self.waterlevel >= WATERLEVEL_SWIMMING)
{
self.velocity_z = self.stat_sv_maxspeed * 0.7;
self.stat_sv_airspeedlimit_nonqw *= 0.5;
}
+ if(self.frozen)
+ {
+ if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
+ {
+ self.movement_x = bound(-5, self.movement_x, 5);
+ self.movement_y = bound(-5, self.movement_y, 5);
+ self.movement_z = bound(-5, self.movement_z, 5);
+ }
+ else
+ self.movement = '0 0 0';
+ self.disableclientprediction = 1;
+
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ { self.velocity_z = 200; }
+ }
+ }
+
MUTATOR_CALLHOOK(PlayerPhysics);
if(self.player_blocked)
PM_Accelerate(wishdir, wishspeed, wishspeed, autocvar_sv_accelerate*maxspd_mod, 1, 0, 0, 0);
}
}
- else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.freezetag_frozen)
+ else if ((self.items & IT_JETPACK) && self.BUTTON_HOOK && (!autocvar_g_jetpack_fuel || self.ammo_fuel >= 0.01 || self.items & IT_UNLIMITED_WEAPON_AMMO) && !self.frozen)
{
//makevectors(self.v_angle_y * '0 1 0');
makevectors(self.v_angle);
}
}
- if((g_cts || g_race) && !IS_OBSERVER(self)) {
- if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed) {
+ if((g_cts || g_race) && !IS_OBSERVER(self))
+ {
+ if(vlen(self.velocity - self.velocity_z * '0 0 1') > speedaward_speed)
+ {
speedaward_speed = vlen(self.velocity - self.velocity_z * '0 0 1');
speedaward_holder = self.netname;
speedaward_uid = self.crypto_idfp;
speedaward_lastupdate = time;
}
- if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
+ if(speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1)
+ {
+ string rr = (g_cts) ? CTS_RECORD : RACE_RECORD;
race_send_speedaward(MSG_ALL);
speedaward_lastsent = speedaward_speed;
- if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") {
+ if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "")
+ {
speedaward_alltimebest = speedaward_speed;
speedaward_alltimebest_holder = speedaward_holder;
speedaward_alltimebest_uid = speedaward_uid;
else
deadbits = ANIMSTATE_DEAD2;
float animbits = deadbits;
- if(self.freezetag_frozen)
+ if(self.frozen)
animbits |= ANIMSTATE_FROZEN;
if(self.crouch)
animbits |= ANIMSTATE_DUCK;
else
self.respawn_countdown = -1; // do not count down
- if(g_cts || autocvar_g_forced_respawn)
+ if(autocvar_g_forced_respawn)
self.respawn_flags = self.respawn_flags | RESPAWN_FORCE;
}
// print an obituary message
Obituary (attacker, inflictor, self, deathtype);
- race_PreDie();
// increment frag counter for used weapon type
float w;
Portal_ClearAllLater(self);
- if(IS_REAL_CLIENT(self))
- {
- self.fixangle = TRUE;
- //msg_entity = self;
- //WriteByte (MSG_ONE, SVC_SETANGLE);
- //WriteAngle (MSG_ONE, self.v_angle_x);
- //WriteAngle (MSG_ONE, self.v_angle_y);
- //WriteAngle (MSG_ONE, 80);
- }
+ self.fixangle = TRUE;
if(defer_ClientKill_Now_TeamChange)
ClientKill_Now_TeamChange(); // can turn player into spectator
// when we get here, player actually dies
+ Unfreeze(self); // remove any icy remains
+ self.health = 0; // Unfreeze resets health, so we need to set it back
+
// clear waypoints
WaypointSprite_PlayerDead();
// throw a weapon
// 0 = reject
// -1 = fake accept
{
- string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr;
+ string msgstr, colorstr, cmsgstr, namestr, fullmsgstr, sourcemsgstr, fullcmsgstr, sourcecmsgstr, colorprefix;
float flood;
var .float flood_field;
entity head;
else
namestr = source.netname;
+ if(strdecolorize(namestr) == namestr)
+ colorprefix = "^3";
+ else
+ colorprefix = "^7";
+
if(msgin != "")
{
if(privatesay)
{
- msgstr = strcat("\{1}\{13}* ^3", namestr, "^3 tells you: ^7");
+ msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7");
privatemsgprefixlen = strlen(msgstr);
msgstr = strcat(msgstr, msgin);
- cmsgstr = strcat(colorstr, "^3", namestr, "^3 tells you:\n^7", msgin);
+ cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin);
if(autocvar_g_chat_teamcolors)
privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7");
else
}
else if(teamsay)
{
- msgstr = strcat("\{1}\{13}", colorstr, "(^3", namestr, colorstr, ") ^7", msgin);
- cmsgstr = strcat(colorstr, "(^3", namestr, colorstr, ")\n^7", msgin);
+ msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin);
+ cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin);
}
else
{
- msgstr = strcat("\{1}", namestr, "^7: ", msgin);
+ msgstr = strcat("\{1}", colorprefix, namestr, "^7: ", msgin);
cmsgstr = "";
}
msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint
return 0;
if (g_weaponarena)
return 0;
- if (g_cts)
- return 0;
if (g_nexball && w == WEP_GRENADE_LAUNCHER)
return 0;
if(w == 0)
w = self.weapon;
if (w == 0)
return; // just in case
+ if(self.frozen)
+ return;
if(MUTATOR_CALLHOOK(ForbidThrowCurrentWeapon))
return;
if(!autocvar_g_weapon_throwable)
return 1;
if(self.player_blocked)
return 1;
- if(self.freezetag_frozen)
+ if(self.frozen)
return 1;
return 0;
}
{
entity e;
string tospawn;
- float moveflag;
+ float moveflag, monstercount = 0;
moveflag = (argv(2) ? stof(argv(2)) : 1); // follow owner if not defined
tospawn = strtolower(argv(1));
return;
}
+ FOR_EACH_MONSTER(e)
+ {
+ if(e.realowner == self)
+ ++monstercount;
+ }
+
if(autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { sprint(self, "Monster spawning is disabled.\n"); return; }
else if(!IS_PLAYER(self)) { sprint(self, "You can't spawn monsters while spectating.\n"); return; }
else if(MUTATOR_CALLHOOK(AllowMobSpawning)) { sprint(self, "Monster spawning is currently disabled by a mutator.\n"); return; }
else if(!autocvar_g_monsters) { Send_Notification(NOTIF_ONE, self, MSG_INFO, INFO_MONSTERS_DISABLED); return; }
else if(self.vehicle) { sprint(self, "You can't spawn monsters while driving a vehicle.\n"); return; }
- else if(self.freezetag_frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
+ else if(self.frozen) { sprint(self, "You can't spawn monsters while frozen.\n"); return; }
else if(autocvar_g_campaign) { sprint(self, "You can't spawn monsters in campaign mode.\n"); return; }
else if(self.deadflag != DEAD_NO) { sprint(self, "You can't spawn monsters while dead.\n"); return; }
- else if(self.monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
+ else if(monstercount >= autocvar_g_monsters_max_perplayer) { sprint(self, "You have spawned too many monsters, kill some before trying to spawn any more.\n"); return; }
else if(totalspawned >= autocvar_g_monsters_max) { sprint(self, "The global maximum monster count has been reached, kill some before trying to spawn any more.\n"); return; }
else if(tospawn != "")
{
}
}
- if(found)
+ if(found || tospawn == "random")
{
- self.monstercount += 1;
totalspawned += 1;
makevectors(self.v_angle);
WarpZone_TraceBox (CENTER_OR_VIEWOFS(self), PL_MIN, PL_MAX, CENTER_OR_VIEWOFS(self) + v_forward * 150, TRUE, self);
//WarpZone_TraceLine(self.origin + self.view_ofs, self.origin + self.view_ofs + v_forward * 150, MOVE_NORMAL, self);
- e = spawnmonster(tospawn, 0, self, self, trace_endpos, FALSE, moveflag);
+ e = spawnmonster(tospawn, 0, self, self, trace_endpos, FALSE, FALSE, moveflag);
sprint(self, strcat("Spawned ", e.monster_name, "\n"));
{
if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
{
+ CheckAllowedTeams(self);
GetTeamCounts(self);
- if(!TeamSmallerEqThanTeam(selection, self.team, self))
+ if(!TeamSmallerEqThanTeam(Team_TeamToNumber(selection), Team_TeamToNumber(self.team), self))
{
sprint(self, "Cannot change to a larger/better/shinier team\n");
return;
float g_warmup_limit;
float g_warmup_allguns;
float g_warmup_allow_timeout;
- float g_race_qualifying;
float warmup_stage;
float g_pickup_respawntime_weapon;
float g_pickup_respawntime_superweapon;
.float player_blocked;
-.float freezetag_frozen;
+.float frozen; // for freeze attacks
+.float revive_progress;
+.float revival_time; // time at which player was last revived
+.float revive_speed; // NOTE: multiplier (anything above 1 is instaheal)
+.entity iceblock;
+.entity frozen_by; // for ice fields
.entity muzzle_flash;
.float misc_bulletcounter; // replaces uzi & hlac bullet counter.
if(targ.killcount) { targ.killcount = 0; }
}
+void Ice_Think()
+{
+ if(!self.owner.frozen || self.owner.iceblock != self)
+ {
+ remove(self);
+ return;
+ }
+ setorigin(self, self.owner.origin - '0 0 16');
+ self.nextthink = time;
+}
+
+void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint)
+{
+ if(!IS_PLAYER(targ) && !(targ.flags & FL_MONSTER)) // only specified entities can be freezed
+ return;
+
+ if(targ.frozen)
+ return;
+
+ float targ_maxhealth = ((targ.flags & FL_MONSTER) ? targ.max_health : start_health);
+
+ targ.frozen = frozen_type;
+ targ.revive_progress = ((frozen_type == 3) ? 1 : 0);
+ targ.health = ((frozen_type == 3) ? targ_maxhealth : 1);
+ targ.revive_speed = freeze_time;
+
+ entity ice, head;
+ ice = spawn();
+ ice.owner = targ;
+ ice.classname = "ice";
+ ice.scale = targ.scale;
+ ice.think = Ice_Think;
+ ice.nextthink = time;
+ ice.frame = floor(random() * 21); // ice model has 20 different looking frames
+ setmodel(ice, "models/ice/ice.md3");
+ ice.alpha = 1;
+ ice.colormod = Team_ColorRGB(targ.team);
+ ice.glowmod = ice.colormod;
+ targ.iceblock = ice;
+ targ.revival_time = 0;
+
+ entity oldself;
+ oldself = self;
+ self = ice;
+ Ice_Think();
+ self = oldself;
+
+ RemoveGrapplingHook(targ);
+
+ FOR_EACH_PLAYER(head)
+ if(head.hook.aiment == targ)
+ RemoveGrapplingHook(head);
+
+ // add waypoint
+ if(show_waypoint)
+ WaypointSprite_Spawn("frozen", 0, 0, targ, '0 0 64', world, targ.team, targ, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
+}
+
+void Unfreeze (entity targ)
+{
+ if(targ.frozen && targ.frozen != 3) // only reset health if target was frozen
+ targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health);
+
+ entity head;
+ targ.frozen = 0;
+ targ.revive_progress = 0;
+ targ.revival_time = time;
+
+ WaypointSprite_Kill(targ.waypointsprite_attached);
+
+ FOR_EACH_PLAYER(head)
+ if(head.hook.aiment == targ)
+ RemoveGrapplingHook(head);
+
+ // remove the ice block
+ if(targ.iceblock)
+ remove(targ.iceblock);
+ targ.iceblock = world;
+}
+
// these are updated by each Damage call for use in button triggering and such
entity damage_targ;
entity damage_inflictor;
mirrordamage = frag_mirrordamage;
force = frag_force;
- if (!g_minstagib)
+ if(targ.frozen)
+ if(deathtype != DEATH_HURTTRIGGER && deathtype != DEATH_TEAMCHANGE && deathtype != DEATH_AUTOTEAMCHANGE)
+ {
+ if(autocvar_g_freezetag_revive_falldamage > 0)
+ if(deathtype == DEATH_FALL)
+ if(damage >= autocvar_g_freezetag_revive_falldamage)
+ {
+ Unfreeze(targ);
+ targ.health = autocvar_g_freezetag_revive_falldamage_health;
+ pointparticles(particleeffectnum("iceorglass"), targ.origin, '0 0 0', 3);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname);
+ Send_Notification(NOTIF_ONE, targ, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+ }
+
+ damage = 0;
+ force *= autocvar_g_freezetag_frozen_force;
+ }
+
+ if(targ.frozen && deathtype == DEATH_HURTTRIGGER && !autocvar_g_freezetag_frozen_damage_trigger)
+ {
+ pointparticles(particleeffectnum("teleport"), targ.origin, '0 0 0', 1);
+
+ entity oldself = self;
+ self = targ;
+ entity spot = SelectSpawnPoint (FALSE);
+
+ if(spot)
+ {
+ damage = 0;
+ self.deadflag = DEAD_NO;
+
+ self.angles = spot.angles;
+
+ self.effects = 0;
+ self.effects |= EF_TELEPORT_BIT;
+
+ self.angles_z = 0; // never spawn tilted even if the spot says to
+ self.fixangle = TRUE; // turn this way immediately
+ self.velocity = '0 0 0';
+ self.avelocity = '0 0 0';
+ self.punchangle = '0 0 0';
+ self.punchvector = '0 0 0';
+ self.oldvelocity = self.velocity;
+
+ self.spawnorigin = spot.origin;
+ setorigin (self, spot.origin + '0 0 1' * (1 - self.mins_z - 24));
+ // don't reset back to last position, even if new position is stuck in solid
+ self.oldorigin = self.origin;
+ self.prevorigin = self.origin;
+
+ pointparticles(particleeffectnum("teleport"), self.origin, '0 0 0', 1);
+ }
+
+ self = oldself;
+ }
+
+ if(!g_minstagib)
{
// apply strength multiplier
if (attacker.items & IT_STRENGTH)
}
if (targ == attacker)
- {
- if(g_cts && !autocvar_g_cts_selfdamage)
- damage = 0;
- else
- damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
- }
+ damage = damage * autocvar_g_balance_selfdamagepercent; // Partial damage if the attacker hits himself
// count the damage
if(attacker)
e.fire_endtime = 0;
// ice stops fire
- if(e.freezetag_frozen)
+ if(e.frozen)
e.fire_endtime = 0;
t = min(frametime, e.fire_endtime - time);
e.fire_hitsound = TRUE;
if (!IS_INDEPENDENT_PLAYER(e))
- if (!e.frozen)
- if(!e.freezetag_frozen)
++ if(!e.frozen)
FOR_EACH_PLAYER(other) if(e != other)
{
if(IS_PLAYER(other))
string redirection_target;
float world_initialized;
- string GetMapname();
string GetGametype();
void GotoNextMap(float reinit);
void ShuffleMaplist();
// private
BADCVAR("developer");
BADCVAR("log_dest_udp");
- BADCVAR("log_file");
BADCVAR("net_address");
BADCVAR("net_address_ipv6");
BADCVAR("port");
BADPREFIX("g_playerstats_");
BADPREFIX("g_respawn_ghosts");
BADPREFIX("g_voice_flood_");
+ BADPREFIX("log_file");
BADPREFIX("rcon_");
BADPREFIX("sv_allowdownloads");
BADPREFIX("sv_autodemo");
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+ CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
MapInfo_Enumerate();
MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0);
CALL_ACCUMULATED_FUNCTION(RegisterGametypes);
CALL_ACCUMULATED_FUNCTION(RegisterNotifications);
CALL_ACCUMULATED_FUNCTION(RegisterDeathtypes);
+ CALL_ACCUMULATED_FUNCTION(RegisterBuffs);
ServerProgsDB = db_load(strcat("server.db", autocvar_sessionid));
addstat(STAT_HAGAR_LOAD, AS_INT, hagar_load);
+ // freeze attacks
+ addstat(STAT_FROZEN, AS_INT, frozen);
+ addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, revive_progress);
+
// g_movementspeed hack
addstat(STAT_MOVEVARS_AIRSPEEDLIMIT_NONQW, AS_FLOAT, stat_sv_airspeedlimit_nonqw);
addstat(STAT_MOVEVARS_MAXSPEED, AS_FLOAT, stat_sv_maxspeed);
return MapInfo_Type_ToString(MapInfo_LoadedGametype);
}
- string getmapname_stored;
string GetMapname()
{
return mapname;
return TRUE;
}
if(autocvar_nextmap != "")
- if(MapInfo_CheckMap(autocvar_nextmap))
+ {
+ string m;
+ m = GameTypeVote_MapInfo_FixName(autocvar_nextmap);
+ cvar_set("nextmap",m);
+
+ if(!m || gametypevote)
+ return FALSE;
+ if(autocvar_sv_vote_gametype)
{
- Map_Goto_SetStr(autocvar_nextmap);
+ Map_Goto_SetStr(m);
+ return FALSE;
+ }
+
+ if(MapInfo_CheckMap(m))
+ {
+ Map_Goto_SetStr(m);
Map_Goto(reinit);
alreadychangedlevel = TRUE;
return TRUE;
}
+ }
if(!reinit && autocvar_lastlevel)
{
cvar_settemp_restore();
============
*/
.float autoscreenshot;
- void() MapVote_Start;
- void() MapVote_Think;
- float mapvote_initialized;
void IntermissionThink()
{
FixIntermissionClient(self);
// - for this timelimit_overtime needs to be >0 of course
// - also check the winning condition calculated in the previous frame and only add normal overtime
// again, if at the point at which timelimit would be extended again, still no winner was found
- if (!autocvar_g_campaign && (checkrules_overtimesadded >= 0) && (checkrules_overtimesadded < autocvar_timelimit_overtimes) && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying))
+ if (!autocvar_g_campaign && (checkrules_overtimesadded >= 0) && (checkrules_overtimesadded < autocvar_timelimit_overtimes || autocvar_timelimit_overtimes < 0) && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying))
{
return 1; // need to call InitiateOvertime later
}
SetDefaultAlpha();
- /*
- MapVote_Think should now do that part
- if (intermission_running)
- if (time >= intermission_exittime + 60)
- {
- if(!DoNextMapOverride())
- GotoNextMap();
- return;
- }
- */
-
if (gameover) // someone else quit the game already
{
if(player_count == 0) // Nobody there? Then let's go to the next map
}
}
- float mapvote_nextthink;
- float mapvote_initialized;
- float mapvote_keeptwotime;
- float mapvote_timeout;
- string mapvote_message;
- #define MAPVOTE_SCREENSHOT_DIRS_COUNT 4
- string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT];
- float mapvote_screenshot_dirs_count;
-
- float mapvote_count;
- float mapvote_count_real;
- string mapvote_maps[MAPVOTE_COUNT];
- float mapvote_maps_screenshot_dir[MAPVOTE_COUNT];
- string mapvote_maps_pakfile[MAPVOTE_COUNT];
- float mapvote_maps_suggested[MAPVOTE_COUNT];
- string mapvote_suggestions[MAPVOTE_COUNT];
- float mapvote_suggestion_ptr;
- float mapvote_voters;
- float mapvote_selections[MAPVOTE_COUNT];
- float mapvote_run;
- float mapvote_detail;
- float mapvote_abstain;
- .float mapvote;
-
- void MapVote_ClearAllVotes()
- {
- FOR_EACH_CLIENT(other)
- other.mapvote = 0;
- }
-
- string MapVote_Suggest(string m)
+ string GotoMap(string m)
{
- float i;
- if(m == "")
- return "That's not how to use this command.";
- if(!autocvar_g_maplist_votable_suggestions)
- return "Suggestions are not accepted on this server.";
- if(mapvote_initialized)
- return "Can't suggest - voting is already in progress!";
- m = MapInfo_FixName(m);
+ m = GameTypeVote_MapInfo_FixName(m);
if (!m)
return "The map you suggested is not available on this server.";
- if(!autocvar_g_maplist_votable_suggestions_override_mostrecent)
- if(Map_IsRecent(m))
- return "This server does not allow for recent maps to be played again. Please be patient for some rounds.";
-
+ if (!autocvar_sv_vote_gametype)
if(!MapInfo_CheckMap(m))
return "The map you suggested does not support the current game mode.";
- for(i = 0; i < mapvote_suggestion_ptr; ++i)
- if(mapvote_suggestions[i] == m)
- return "This map was already suggested.";
- if(mapvote_suggestion_ptr >= MAPVOTE_COUNT)
- {
- i = floor(random() * mapvote_suggestion_ptr);
- }
- else
- {
- i = mapvote_suggestion_ptr;
- mapvote_suggestion_ptr += 1;
- }
- if(mapvote_suggestions[i] != "")
- strunzone(mapvote_suggestions[i]);
- mapvote_suggestions[i] = strzone(m);
- if(autocvar_sv_eventlog)
- GameLogEcho(strcat(":vote:suggested:", m, ":", ftos(self.playerid)));
- return strcat("Suggestion of ", m, " accepted.");
- }
-
- void MapVote_AddVotable(string nextMap, float isSuggestion)
- {
- float j, i, o;
- string pakfile, mapfile;
-
- if(nextMap == "")
- return;
- for(j = 0; j < mapvote_count; ++j)
- if(mapvote_maps[j] == nextMap)
- return;
- // suggestions might be no longer valid/allowed after gametype switch!
- if(isSuggestion)
- if(!MapInfo_CheckMap(nextMap))
- return;
- mapvote_maps[mapvote_count] = strzone(nextMap);
- mapvote_maps_suggested[mapvote_count] = isSuggestion;
-
- pakfile = string_null;
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- {
- mapfile = strcat(mapvote_screenshot_dirs[i], "/", mapvote_maps[i]);
- pakfile = whichpack(strcat(mapfile, ".tga"));
- if(pakfile == "")
- pakfile = whichpack(strcat(mapfile, ".jpg"));
- if(pakfile == "")
- pakfile = whichpack(strcat(mapfile, ".png"));
- if(pakfile != "")
- break;
- }
- if(i >= mapvote_screenshot_dirs_count)
- i = 0; // FIXME maybe network this error case, as that means there is no mapshot on the server?
- for(o = strstr(pakfile, "/", 0)+1; o > 0; o = strstr(pakfile, "/", 0)+1)
- pakfile = substring(pakfile, o, -1);
-
- mapvote_maps_screenshot_dir[mapvote_count] = i;
- mapvote_maps_pakfile[mapvote_count] = strzone(pakfile);
-
- mapvote_count += 1;
- }
-
- void MapVote_Spawn();
- void MapVote_Init()
- {
- float i;
- float nmax, smax;
-
- MapVote_ClearAllVotes();
-
- mapvote_count = 0;
- mapvote_detail = !autocvar_g_maplist_votable_nodetail;
- mapvote_abstain = autocvar_g_maplist_votable_abstain;
-
- if(mapvote_abstain)
- nmax = min(MAPVOTE_COUNT - 1, autocvar_g_maplist_votable);
- else
- nmax = min(MAPVOTE_COUNT, autocvar_g_maplist_votable);
- smax = min3(nmax, autocvar_g_maplist_votable_suggestions, mapvote_suggestion_ptr);
-
- // we need this for AddVotable, as that cycles through the screenshot dirs
- mapvote_screenshot_dirs_count = tokenize_console(autocvar_g_maplist_votable_screenshot_dir);
- if(mapvote_screenshot_dirs_count == 0)
- mapvote_screenshot_dirs_count = tokenize_console("maps levelshots");
- mapvote_screenshot_dirs_count = min(mapvote_screenshot_dirs_count, MAPVOTE_SCREENSHOT_DIRS_COUNT);
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- mapvote_screenshot_dirs[i] = strzone(argv(i));
-
- if(mapvote_suggestion_ptr)
- for(i = 0; i < 100 && mapvote_count < smax; ++i)
- MapVote_AddVotable(mapvote_suggestions[floor(random() * mapvote_suggestion_ptr)], TRUE);
-
- for(i = 0; i < 100 && mapvote_count < nmax; ++i)
- MapVote_AddVotable(GetNextMap(), FALSE);
-
- if(mapvote_count == 0)
- {
- bprint( "Maplist contains no single playable map! Resetting it to default map list.\n" );
- cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags()));
- if(autocvar_g_maplist_shuffle)
- ShuffleMaplist();
- localcmd("\nmenu_cmd sync\n");
- for(i = 0; i < 100 && mapvote_count < nmax; ++i)
- MapVote_AddVotable(GetNextMap(), FALSE);
- }
-
- mapvote_count_real = mapvote_count;
- if(mapvote_abstain)
- MapVote_AddVotable("don't care", 0);
-
- //dprint("mapvote count is ", ftos(mapvote_count), "\n");
-
- mapvote_keeptwotime = time + autocvar_g_maplist_votable_keeptwotime;
- mapvote_timeout = time + autocvar_g_maplist_votable_timeout;
- if(mapvote_count_real < 3 || mapvote_keeptwotime <= time)
- mapvote_keeptwotime = 0;
- mapvote_message = "Choose a map and press its key!";
-
- MapVote_Spawn();
- }
-
- void MapVote_SendPicture(float id)
- {
- msg_entity = self;
- WriteByte(MSG_ONE, SVC_TEMPENTITY);
- WriteByte(MSG_ONE, TE_CSQC_PICTURE);
- WriteByte(MSG_ONE, id);
- WritePicture(MSG_ONE, strcat(mapvote_screenshot_dirs[mapvote_maps_screenshot_dir[id]], "/", mapvote_maps[id]), 3072);
- }
-
- float MapVote_GetMapMask()
- {
- float mask, i, power;
- mask = 0;
- for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2)
- if(mapvote_maps[i] != "")
- mask |= power;
- return mask;
- }
-
- entity mapvote_ent;
- float MapVote_SendEntity(entity to, float sf)
- {
- float i;
-
- if(sf & 1)
- sf &= ~2; // if we send 1, we don't need to also send 2
-
- WriteByte(MSG_ENTITY, ENT_CLIENT_MAPVOTE);
- WriteByte(MSG_ENTITY, sf);
-
- if(sf & 1)
- {
- // flag 1 == initialization
- for(i = 0; i < mapvote_screenshot_dirs_count; ++i)
- WriteString(MSG_ENTITY, mapvote_screenshot_dirs[i]);
- WriteString(MSG_ENTITY, "");
- WriteByte(MSG_ENTITY, mapvote_count);
- WriteByte(MSG_ENTITY, mapvote_abstain);
- WriteByte(MSG_ENTITY, mapvote_detail);
- WriteCoord(MSG_ENTITY, mapvote_timeout);
- if(mapvote_count <= 8)
- WriteByte(MSG_ENTITY, MapVote_GetMapMask());
- else
- WriteShort(MSG_ENTITY, MapVote_GetMapMask());
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- if(mapvote_abstain && i == mapvote_count - 1)
- {
- WriteString(MSG_ENTITY, ""); // abstain needs no text
- WriteString(MSG_ENTITY, ""); // abstain needs no pack
- WriteByte(MSG_ENTITY, 0); // abstain needs no screenshot dir
- }
- else
- {
- WriteString(MSG_ENTITY, mapvote_maps[i]);
- WriteString(MSG_ENTITY, mapvote_maps_pakfile[i]);
- WriteByte(MSG_ENTITY, mapvote_maps_screenshot_dir[i]);
- }
- }
- }
-
- if(sf & 2)
- {
- // flag 2 == update of mask
- if(mapvote_count <= 8)
- WriteByte(MSG_ENTITY, MapVote_GetMapMask());
- else
- WriteShort(MSG_ENTITY, MapVote_GetMapMask());
- }
-
- if(sf & 4)
- {
- if(mapvote_detail)
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- WriteByte(MSG_ENTITY, mapvote_selections[i]);
-
- WriteByte(MSG_ENTITY, to.mapvote);
- }
-
- return TRUE;
- }
-
- void MapVote_Spawn()
- {
- Net_LinkEntity(mapvote_ent = spawn(), FALSE, 0, MapVote_SendEntity);
- }
-
- void MapVote_TouchMask()
- {
- mapvote_ent.SendFlags |= 2;
- }
-
- void MapVote_TouchVotes(entity voter)
- {
- mapvote_ent.SendFlags |= 4;
- }
-
- float MapVote_Finished(float mappos)
- {
- string result;
- float i;
- float didntvote;
-
- if(autocvar_sv_eventlog)
- {
- result = strcat(":vote:finished:", mapvote_maps[mappos]);
- result = strcat(result, ":", ftos(mapvote_selections[mappos]), "::");
- didntvote = mapvote_voters;
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- didntvote -= mapvote_selections[i];
- if(i != mappos)
- {
- result = strcat(result, ":", mapvote_maps[i]);
- result = strcat(result, ":", ftos(mapvote_selections[i]));
- }
- }
- result = strcat(result, ":didn't vote:", ftos(didntvote));
-
- GameLogEcho(result);
- if(mapvote_maps_suggested[mappos])
- GameLogEcho(strcat(":vote:suggestion_accepted:", mapvote_maps[mappos]));
- }
-
- FOR_EACH_REALCLIENT(other)
- FixClientCvars(other);
-
- Map_Goto_SetStr(mapvote_maps[mappos]);
- Map_Goto(0);
- alreadychangedlevel = TRUE;
- return TRUE;
- }
- void MapVote_CheckRules_1()
- {
- float i;
-
- for(i = 0; i < mapvote_count; ++i) if(mapvote_maps[i] != "")
- {
- //dprint("Map ", ftos(i), ": "); dprint(mapvote_maps[i], "\n");
- mapvote_selections[i] = 0;
- }
-
- mapvote_voters = 0;
- FOR_EACH_REALCLIENT(other)
- {
- ++mapvote_voters;
- if(other.mapvote)
- {
- i = other.mapvote - 1;
- //dprint("Player ", other.netname, " vote = ", ftos(other.mapvote - 1), "\n");
- mapvote_selections[i] = mapvote_selections[i] + 1;
- }
- }
- }
-
- float MapVote_CheckRules_2()
- {
- float i;
- float firstPlace, secondPlace;
- float firstPlaceVotes, secondPlaceVotes;
- float mapvote_voters_real;
- string result;
-
- if(mapvote_count_real == 1)
- return MapVote_Finished(0);
-
- mapvote_voters_real = mapvote_voters;
- if(mapvote_abstain)
- mapvote_voters_real -= mapvote_selections[mapvote_count - 1];
-
- RandomSelection_Init();
- for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
- RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
- firstPlace = RandomSelection_chosen_float;
- firstPlaceVotes = RandomSelection_best_priority;
- //dprint("First place: ", ftos(firstPlace), "\n");
- //dprint("First place votes: ", ftos(firstPlaceVotes), "\n");
-
- RandomSelection_Init();
- for(i = 0; i < mapvote_count_real; ++i) if(mapvote_maps[i] != "")
- if(i != firstPlace)
- RandomSelection_Add(world, i, string_null, 1, mapvote_selections[i]);
- secondPlace = RandomSelection_chosen_float;
- secondPlaceVotes = RandomSelection_best_priority;
- //dprint("Second place: ", ftos(secondPlace), "\n");
- //dprint("Second place votes: ", ftos(secondPlaceVotes), "\n");
-
- if(firstPlace == -1)
- error("No first place in map vote... WTF?");
-
- if(secondPlace == -1 || time > mapvote_timeout || (mapvote_voters_real - firstPlaceVotes) < firstPlaceVotes)
- return MapVote_Finished(firstPlace);
-
- if(mapvote_keeptwotime)
- if(time > mapvote_keeptwotime || (mapvote_voters_real - firstPlaceVotes - secondPlaceVotes) < secondPlaceVotes)
- {
- float didntvote;
- MapVote_TouchMask();
- mapvote_message = "Now decide between the TOP TWO!";
- mapvote_keeptwotime = 0;
- result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]);
- result = strcat(result, ":", ftos(firstPlaceVotes));
- result = strcat(result, ":", mapvote_maps[secondPlace]);
- result = strcat(result, ":", ftos(secondPlaceVotes), "::");
- didntvote = mapvote_voters;
- for(i = 0; i < mapvote_count; ++i)
- if(mapvote_maps[i] != "")
- {
- didntvote -= mapvote_selections[i];
- if(i != firstPlace)
- if(i != secondPlace)
- {
- result = strcat(result, ":", mapvote_maps[i]);
- result = strcat(result, ":", ftos(mapvote_selections[i]));
- if(i < mapvote_count_real)
- {
- strunzone(mapvote_maps[i]);
- mapvote_maps[i] = "";
- strunzone(mapvote_maps_pakfile[i]);
- mapvote_maps_pakfile[i] = "";
- }
- }
- }
- result = strcat(result, ":didn't vote:", ftos(didntvote));
- if(autocvar_sv_eventlog)
- GameLogEcho(result);
- }
-
- return FALSE;
- }
- void MapVote_Tick()
- {
- float keeptwo;
- float totalvotes;
-
- keeptwo = mapvote_keeptwotime;
- MapVote_CheckRules_1(); // count
- if(MapVote_CheckRules_2()) // decide
- return;
-
- totalvotes = 0;
- FOR_EACH_REALCLIENT(other)
- {
- // hide scoreboard again
- if(other.health != 2342)
- {
- other.health = 2342;
- other.impulse = 0;
- if(IS_REAL_CLIENT(other))
- {
- msg_entity = other;
- WriteByte(MSG_ONE, SVC_FINALE);
- WriteString(MSG_ONE, "");
- }
- }
-
- // clear possibly invalid votes
- if(mapvote_maps[other.mapvote - 1] == "")
- other.mapvote = 0;
- // use impulses as new vote
- if(other.impulse >= 1 && other.impulse <= mapvote_count)
- if(mapvote_maps[other.impulse - 1] != "")
- {
- other.mapvote = other.impulse;
- MapVote_TouchVotes(other);
- }
- other.impulse = 0;
-
- if(other.mapvote)
- ++totalvotes;
- }
-
- MapVote_CheckRules_1(); // just count
- }
- void MapVote_Start()
- {
- if(mapvote_run)
- return;
-
- // wait for stats to be sent first
- if(!playerstats_waitforme)
- return;
-
- MapInfo_Enumerate();
- if(MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
- mapvote_run = TRUE;
- }
- void MapVote_Think()
- {
- if(!mapvote_run)
- return;
-
- if(alreadychangedlevel)
- return;
-
- if(time < mapvote_nextthink)
- return;
- //dprint("tick\n");
-
- mapvote_nextthink = time + 0.5;
-
- if(!mapvote_initialized)
- {
- if(autocvar_rescan_pending == 1)
- {
- cvar_set("rescan_pending", "2");
- localcmd("fs_rescan\nrescan_pending 3\n");
- return;
- }
- else if(autocvar_rescan_pending == 2)
- {
- return;
- }
- else if(autocvar_rescan_pending == 3)
- {
- // now build missing mapinfo files
- if(!MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 1))
- return;
-
- // we're done, start the timer
- cvar_set("rescan_pending", "0");
- }
-
- mapvote_initialized = TRUE;
- if(DoNextMapOverride(0))
- return;
- if(!autocvar_g_maplist_votable || player_count <= 0)
- {
- GotoNextMap(0);
- return;
- }
- MapVote_Init();
- }
-
- MapVote_Tick();
- }
-
- string GotoMap(string m)
- {
- if(!MapInfo_CheckMap(m))
- return "The map you chose is not available on this server.";
cvar_set("nextmap", m);
cvar_set("timelimit", "-1");
if(mapvote_initialized || alreadychangedlevel)
void EndFrame()
{
+ anticheat_endframe();
+
float altime;
FOR_EACH_REALCLIENT(self)
{
float sv_taunt;
string GetGametype(); // g_world.qc
+ void mutators_add(); // mutators.qc
void readlevelcvars(void)
{
// load mutators
- #define CHECK_MUTATOR_ADD(mut_cvar,mut_name,dependence) \
- { if(cvar(mut_cvar) && dependence) { MUTATOR_ADD(mut_name); } }
-
- CHECK_MUTATOR_ADD("g_dodging", mutator_dodging, 1);
- CHECK_MUTATOR_ADD("g_spawn_near_teammate", mutator_spawn_near_teammate, teamplay);
- CHECK_MUTATOR_ADD("g_physical_items", mutator_physical_items, 1);
- CHECK_MUTATOR_ADD("g_touchexplode", mutator_touchexplode, 1);
- CHECK_MUTATOR_ADD("g_minstagib", mutator_minstagib, 1);
- CHECK_MUTATOR_ADD("g_invincible_projectiles", mutator_invincibleprojectiles, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_new_toys", mutator_new_toys, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_nix", mutator_nix, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_rocket_flying", mutator_rocketflying, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_vampire", mutator_vampire, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_superspectate", mutator_superspec, 1);
- CHECK_MUTATOR_ADD("g_pinata", mutator_pinata, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_midair", mutator_midair, 1);
- CHECK_MUTATOR_ADD("g_bloodloss", mutator_bloodloss, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_random_gravity", mutator_random_gravity, 1);
- CHECK_MUTATOR_ADD("g_multijump", mutator_multijump, 1);
- CHECK_MUTATOR_ADD("g_melee_only", mutator_melee_only, !cvar("g_minstagib"));
- CHECK_MUTATOR_ADD("g_nades", mutator_nades, 1);
- CHECK_MUTATOR_ADD("g_sandbox", sandbox, 1);
- CHECK_MUTATOR_ADD("g_campcheck", mutator_campcheck, 1);
-
- #undef CHECK_MUTATOR_ADD
+ mutators_add();
if(cvar("sv_allow_fullbright"))
serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT;
sv_clones = cvar("sv_clones");
sv_foginterval = cvar("sv_foginterval");
g_cloaked = cvar("g_cloaked");
- if(g_cts)
- g_cloaked = 1; // always enable cloak in CTS
g_footsteps = cvar("g_footsteps");
g_grappling_hook = cvar("g_grappling_hook");
g_jetpack = cvar("g_jetpack");
return TRUE;
}
- #ifdef COMPAT_XON010_CHANNELS
- void(entity e, float chan, string samp, float vol, float atten) builtin_sound = #8;
- void sound(entity e, float chan, string samp, float vol, float atten)
- {
- if (!sound_allowed(MSG_BROADCAST, e))
- return;
- builtin_sound(e, chan, samp, vol, atten);
- }
- #else
#undef sound
void sound(entity e, float chan, string samp, float vol, float atten)
{
return;
sound7(e, chan, samp, vol, atten, 0, 0);
}
- #endif
void soundtoat(float dest, entity e, vector o, float chan, string samp, float vol, float atten)
{
{
// gamemode related things
precache_model ("models/misc/chatbubble.spr");
+ precache_model("models/ice/ice.md3");
#ifdef TTURRETS_ENABLED
if (autocvar_g_turrets)
return s;
}
- float race_readTime(string map, float pos)
- {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
-
- return stof(db_get(ServerProgsDB, strcat(map, rr, "time", ftos(pos))));
- }
-
- string race_readUID(string map, float pos)
- {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
-
- return db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos)));
- }
-
- float race_readPos(string map, float t) {
- float i;
- for (i = 1; i <= RANKINGS_CNT; ++i)
- if (race_readTime(map, i) == 0 || race_readTime(map, i) > t)
- return i;
-
- return 0; // pos is zero if unranked
- }
-
- void race_writeTime(string map, float t, string myuid)
- {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
-
- float newpos;
- newpos = race_readPos(map, t);
-
- float i, prevpos = 0;
- for(i = 1; i <= RANKINGS_CNT; ++i)
- {
- if(race_readUID(map, i) == myuid)
- prevpos = i;
- }
- if (prevpos) { // player improved his existing record, only have to iterate on ranks between new and old recs
- for (i = prevpos; i > newpos; --i) {
- db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
- db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
- }
- } else { // player has no ranked record yet
- for (i = RANKINGS_CNT; i > newpos; --i) {
- db_put(ServerProgsDB, strcat(map, rr, "time", ftos(i)), ftos(race_readTime(map, i - 1)));
- db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(i)), race_readUID(map, i - 1));
- }
- }
-
- // store new time itself
- db_put(ServerProgsDB, strcat(map, rr, "time", ftos(newpos)), ftos(t));
- db_put(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(newpos)), myuid);
- }
-
- string race_readName(string map, float pos)
- {
- string rr;
- if(g_cts)
- rr = CTS_RECORD;
- else
- rr = RACE_RECORD;
-
- return uid2name(db_get(ServerProgsDB, strcat(map, rr, "crypto_idfp", ftos(pos))));
- }
-
float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, float badsurfaceflags, float attempts, float maxaboveground, float minviewdistance)
{
float m, i;
if (!player) { return; } // without someone to give the reward to, we can't possibly cap
+ nades_GiveBonus(player, autocvar_g_nades_bonus_score_high );
+
// messages and sounds
Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_ENT_2(enemy_flag, CENTER_CTF_CAPTURE_));
ctf_CaptureRecord(enemy_flag, player);
{
PlayerTeamScore_AddScore(player, autocvar_g_ctf_score_return); // reward for return
PlayerScore_Add(player, SP_CTF_RETURNS, 1); // add to count of returns
+
+ nades_GiveBonus(player,autocvar_g_nades_bonus_score_medium);
}
TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it
// scoring
PlayerScore_Add(player, SP_CTF_PICKUPS, 1);
+ nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor);
switch(pickuptype)
{
case PICKUP_BASE:
if(self.mins != FLAG_MIN || self.maxs != FLAG_MAX) { // reset the flag boundaries in case it got squished
dprint("wtf the flag got squashed?\n");
tracebox(self.origin, FLAG_MIN, FLAG_MAX, self.origin, MOVE_NOMONSTERS, self);
- if(!trace_startsolid) // can we resize it without getting stuck?
+ if(!trace_startsolid || self.noalign) // can we resize it without getting stuck?
setsize(self, FLAG_MIN, FLAG_MAX); }
switch(self.ctf_status) // reset flag angles in case warpzones adjust it
}
// special touch behaviors
- if(toucher.vehicle_flags & VHF_ISVEHICLE)
+ if(toucher.frozen) { return; }
+ else if(toucher.vehicle_flags & VHF_ISVEHICLE)
{
- if(autocvar_g_ctf_allow_vehicle_touch)
+ if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner)
toucher = toucher.owner; // the player is actually the vehicle owner, not other
else
return; // do nothing
MUTATOR_HOOK(VehicleEnter, ctf_VehicleEnter, CBC_ORDER_ANY);
MUTATOR_HOOK(VehicleExit, ctf_VehicleExit, CBC_ORDER_ANY);
MUTATOR_HOOK(AbortSpeedrun, ctf_AbortSpeedrun, CBC_ORDER_ANY);
- MUTATOR_HOOK(HavocBot_ChooseRule, ctf_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRole, ctf_BotRoles, CBC_ORDER_ANY);
MUTATOR_ONADD
{
.float freezetag_frozen_time;
.float freezetag_frozen_timeout;
.float freezetag_revive_progress;
-.entity freezetag_ice;
#define ICE_MAX_ALPHA 1
#define ICE_MIN_ALPHA 0.1
float freezetag_teams;
{
entity e;
total_players = redalive = bluealive = yellowalive = pinkalive = 0;
- FOR_EACH_PLAYER(e) {
- if(e.team == NUM_TEAM_1 && e.health >= 1)
- {
- ++total_players;
- if (!e.freezetag_frozen) ++redalive;
- }
- else if(e.team == NUM_TEAM_2 && e.health >= 1)
- {
- ++total_players;
- if (!e.freezetag_frozen) ++bluealive;
- }
- else if(e.team == NUM_TEAM_3 && e.health >= 1)
- {
- ++total_players;
- if (!e.freezetag_frozen) ++yellowalive;
- }
- else if(e.team == NUM_TEAM_4 && e.health >= 1)
+ FOR_EACH_PLAYER(e)
+ {
+ switch(e.team)
{
- ++total_players;
- if (!e.freezetag_frozen) ++pinkalive;
+ case NUM_TEAM_1: ++total_players; if(e.health >= 1 && e.frozen != 1) ++redalive; break;
+ case NUM_TEAM_2: ++total_players; if(e.health >= 1 && e.frozen != 1) ++bluealive; break;
+ case NUM_TEAM_3: ++total_players; if(e.health >= 1 && e.frozen != 1) ++yellowalive; break;
+ case NUM_TEAM_4: ++total_players; if(e.health >= 1 && e.frozen != 1) ++pinkalive; break;
}
}
- FOR_EACH_REALCLIENT(e) {
+ FOR_EACH_REALCLIENT(e)
+ {
e.redalive_stat = redalive;
e.bluealive_stat = bluealive;
e.yellowalive_stat = yellowalive;
FOR_EACH_PLAYER(e)
{
e.freezetag_frozen_timeout = 0;
- e.freezetag_revive_progress = 0;
+ nades_Clear(e);
}
round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
return 1;
FOR_EACH_PLAYER(e)
{
e.freezetag_frozen_timeout = 0;
- e.freezetag_revive_progress = 0;
+ nades_Clear(e);
}
round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit);
return 1;
}
-// this is needed to allow the player to turn his view around (fixangle can't
-// be used to freeze his view, as that also changes the angles), while not
-// turning that ice object with the player
-void freezetag_Ice_Think()
-{
- setorigin(self, self.owner.origin - '0 0 16');
- self.nextthink = time;
-}
-
void freezetag_Add_Score(entity attacker)
{
if(attacker == self)
void freezetag_Freeze(entity attacker)
{
- if(self.freezetag_frozen)
+ if(self.frozen)
return;
- self.freezetag_frozen = 1;
- self.freezetag_frozen_time = time;
- self.freezetag_revive_progress = 0;
- self.health = 1;
+
if(autocvar_g_freezetag_frozen_maxtime > 0)
self.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime;
- freezetag_count_alive_players();
+ Freeze(self, 0, 1, TRUE);
- entity ice;
- ice = spawn();
- ice.owner = self;
- ice.classname = "freezetag_ice";
- ice.think = freezetag_Ice_Think;
- ice.nextthink = time;
- ice.frame = floor(random() * 21); // ice model has 20 different looking frames
- ice.alpha = ICE_MAX_ALPHA;
- ice.colormod = Team_ColorRGB(self.team);
- ice.glowmod = ice.colormod;
- setmodel(ice, "models/ice/ice.md3");
-
- self.freezetag_ice = ice;
-
- RemoveGrapplingHook(self);
-
- // add waypoint
- WaypointSprite_Spawn("freezetag_frozen", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attached, TRUE, RADARICON_WAYPOINT, '0.25 0.90 1');
+ freezetag_count_alive_players();
freezetag_Add_Score(attacker);
}
void freezetag_Unfreeze(entity attacker)
{
- self.freezetag_frozen = 0;
self.freezetag_frozen_time = 0;
self.freezetag_frozen_timeout = 0;
- self.freezetag_revive_progress = 0;
- remove(self.freezetag_ice);
- self.freezetag_ice = world;
-
- if(self.waypointsprite_attached)
- WaypointSprite_Kill(self.waypointsprite_attached);
+ Unfreeze(self);
}
{
if ((head != self) && (head.team == self.team))
{
- if (head.freezetag_frozen)
+ if (head.frozen == 1)
{
distance = vlen(head.origin - org);
if (distance > sradius)
unfrozen = 0;
FOR_EACH_PLAYER(head)
{
- if ((head.team == self.team) && (!head.freezetag_frozen))
+ if ((head.team == self.team) && (head.frozen != 1))
unfrozen++;
}
// If only one left on team or if role has timed out then start trying to free players.
- if (((unfrozen == 0) && (!self.freezetag_frozen)) || (time > self.havocbot_role_timeout))
+ if (((unfrozen == 0) && (!self.frozen)) || (time > self.havocbot_role_timeout))
{
dprint("changing role to freeing\n");
self.havocbot_role = havocbot_role_ft_freeing;
if(round_handler_IsActive())
if(round_handler_CountdownRunning())
{
- if(self.freezetag_frozen)
+ if(self.frozen)
freezetag_Unfreeze(world);
freezetag_count_alive_players();
return 1; // let the player die so that he can respawn whenever he wants
|| frag_deathtype == DEATH_TEAMCHANGE || frag_deathtype == DEATH_AUTOTEAMCHANGE)
{
// let the player die, he will be automatically frozen when he respawns
- if(!self.freezetag_frozen)
+ if(self.frozen != 1)
{
freezetag_Add_Score(frag_attacker);
freezetag_count_alive_players();
}
else
freezetag_Unfreeze(world); // remove ice
+ self.health = 0; // Unfreeze resets health
self.freezetag_frozen_timeout = -2; // freeze on respawn
return 1;
}
- if(self.freezetag_frozen)
+ if(self.frozen)
return 1;
freezetag_Freeze(frag_attacker);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname);
}
- frag_target.health = 1; // "respawn" the player :P
-
return 1;
}
FOR_EACH_PLAYER(self)
{
self.killcount = 0;
- if (self.freezetag_frozen)
- freezetag_Unfreeze(world);
self.freezetag_frozen_timeout = -1;
PutClientInServer();
self.freezetag_frozen_timeout = 0;
if(gameover)
return 1;
- if(self.freezetag_frozen)
+ if(self.frozen == 1)
{
// keep health = 1
self.pauseregen_finished = time + autocvar_g_balance_pause_health_regen;
entity o;
o = world;
- if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
- self.freezetag_ice.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
+ //if(self.frozen)
+ //if(self.freezetag_frozen_timeout > 0 && time < self.freezetag_frozen_timeout)
+ //self.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (self.freezetag_frozen_timeout - time) / (self.freezetag_frozen_timeout - self.freezetag_frozen_time);
if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
n = -1;
{
vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
n = 0;
- FOR_EACH_PLAYER(other) if(self != other)
+ FOR_EACH_PLAYER(other)
+ if(self != other)
+ if(other.frozen == 0)
+ if(other.deadflag == DEAD_NO)
+ if(SAME_TEAM(other, self))
+ if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
{
- if(other.freezetag_frozen == 0)
- {
- if(other.team == self.team)
- {
- if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
- {
- if(!o)
- o = other;
- if(self.freezetag_frozen)
- other.reviving = TRUE;
- ++n;
- }
- }
- }
+ if(!o)
+ o = other;
+ if(self.frozen == 1)
+ other.reviving = TRUE;
+ ++n;
}
}
- if(n && self.freezetag_frozen) // OK, there is at least one teammate reviving us
+ if(n && self.frozen == 1) // OK, there is at least one teammate reviving us
{
- self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
- if(warmup_stage)
- self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
- else
- self.health = max(1, self.freezetag_revive_progress * start_health);
+ self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+ self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
- if(self.freezetag_revive_progress >= 1)
+ if(self.revive_progress >= 1)
{
freezetag_Unfreeze(self);
freezetag_count_alive_players();
{
PlayerScore_Add(other, SP_FREEZETAG_REVIVALS, +1);
PlayerScore_Add(other, SP_SCORE, +1);
+
+ nades_GiveBonus(other,autocvar_g_nades_bonus_score_low);
}
}
{
if(other.reviving)
{
- other.freezetag_revive_progress = self.freezetag_revive_progress;
+ other.revive_progress = self.revive_progress;
other.reviving = FALSE;
}
}
}
- else if(!n && self.freezetag_frozen) // only if no teammate is nearby will we reset
- {
- self.freezetag_revive_progress = bound(0, self.freezetag_revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
- if(warmup_stage)
- self.health = max(1, self.freezetag_revive_progress * warmup_start_health);
- else
- self.health = max(1, self.freezetag_revive_progress * start_health);
- }
- else if(!n)
+ else if(!n && self.frozen == 1) // only if no teammate is nearby will we reset
{
- self.freezetag_revive_progress = 0; // thawing nobody
+ self.revive_progress = bound(0, self.revive_progress - frametime * autocvar_g_freezetag_revive_clearspeed, 1);
+ self.health = max(1, self.revive_progress * ((warmup_stage) ? warmup_start_health : start_health));
}
-
- return 1;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_PlayerPhysics)
-{
- if(self.freezetag_frozen)
+ else if(!n && !self.frozen)
{
- if(autocvar_sv_dodging_frozen && IS_REAL_CLIENT(self))
- {
- self.movement_x = bound(-5, self.movement_x, 5);
- self.movement_y = bound(-5, self.movement_y, 5);
- self.movement_z = bound(-5, self.movement_z, 5);
- }
- else
- self.movement = '0 0 0';
-
- self.disableclientprediction = 1;
+ self.revive_progress = 0; // thawing nobody
}
- return 1;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_PlayerDamage_Calculate)
-{
- if(frag_target.freezetag_frozen && frag_deathtype != DEATH_HURTTRIGGER)
- {
- if(autocvar_g_freezetag_revive_falldamage > 0)
- if(frag_deathtype == DEATH_FALL)
- if(frag_damage >= autocvar_g_freezetag_revive_falldamage)
- {
- freezetag_Unfreeze(frag_target);
- frag_target.health = autocvar_g_freezetag_revive_falldamage_health;
- pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
- Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, frag_target.netname);
- Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_FALL);
- }
- frag_damage = 0;
- frag_force = frag_force * autocvar_g_freezetag_frozen_force;
- }
return 1;
}
-MUTATOR_HOOKFUNCTION(freezetag_PlayerJump)
-{
- if(self.freezetag_frozen)
- return TRUE; // no jumping in freezetag when frozen
-
- return FALSE;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_ForbidThrowCurrentWeapon)
-{
- if (self.freezetag_frozen)
- return 1;
- return 0;
-}
-
-MUTATOR_HOOKFUNCTION(freezetag_ItemTouch)
-{
- if (other.freezetag_frozen)
- return MUT_ITEMTOUCH_RETURN;
- return MUT_ITEMTOUCH_CONTINUE;
-}
-
MUTATOR_HOOKFUNCTION(freezetag_BotRoles)
{
if (!self.deadflag)
return TRUE;
}
-MUTATOR_HOOKFUNCTION(freezetag_SpectateCopy)
-{
- self.freezetag_frozen = other.freezetag_frozen;
- self.freezetag_revive_progress = other.freezetag_revive_progress;
- return 0;
-}
-
MUTATOR_HOOKFUNCTION(freezetag_GetTeamCount)
{
ret_float = freezetag_teams;
return 0;
}
-MUTATOR_HOOKFUNCTION(freezetag_VehicleTouch)
-{
- if(other.freezetag_frozen)
- return TRUE;
-
- return FALSE;
-}
-
void freezetag_Initialize()
{
- precache_model("models/ice/ice.md3");
-
freezetag_teams = autocvar_g_freezetag_teams_override;
if(freezetag_teams < 2)
freezetag_teams = autocvar_g_freezetag_teams;
addstat(STAT_BLUEALIVE, AS_INT, bluealive_stat);
addstat(STAT_YELLOWALIVE, AS_INT, yellowalive_stat);
addstat(STAT_PINKALIVE, AS_INT, pinkalive_stat);
-
- addstat(STAT_FROZEN, AS_INT, freezetag_frozen);
- addstat(STAT_REVIVE_PROGRESS, AS_FLOAT, freezetag_revive_progress);
}
MUTATOR_DEFINITION(gamemode_freezetag)
MUTATOR_HOOK(reset_map_players, freezetag_reset_map_players, CBC_ORDER_ANY);
MUTATOR_HOOK(GiveFragsForKill, freezetag_GiveFragsForKill, CBC_ORDER_FIRST);
MUTATOR_HOOK(PlayerPreThink, freezetag_PlayerPreThink, CBC_ORDER_FIRST);
- MUTATOR_HOOK(HavocBot_ChooseRule, freezetag_BotRoles, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerPhysics, freezetag_PlayerPhysics, CBC_ORDER_FIRST);
- MUTATOR_HOOK(PlayerDamage_Calculate, freezetag_PlayerDamage_Calculate, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerJump, freezetag_PlayerJump, CBC_ORDER_ANY);
- MUTATOR_HOOK(ForbidThrowCurrentWeapon, freezetag_ForbidThrowCurrentWeapon, CBC_ORDER_ANY);
- MUTATOR_HOOK(ItemTouch, freezetag_ItemTouch, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRole, freezetag_BotRoles, CBC_ORDER_ANY);
- MUTATOR_HOOK(SpectateCopy, freezetag_SpectateCopy, CBC_ORDER_ANY);
MUTATOR_HOOK(GetTeamCount, freezetag_GetTeamCount, CBC_ORDER_EXCLUSIVE);
- MUTATOR_HOOK(VehicleTouch, freezetag_VehicleTouch, CBC_ORDER_ANY);
MUTATOR_ONADD
{
if(gameover) { return; }
vector oldballorigin = self.origin;
- if(MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
+ if(!MoveToRandomMapLocation(self, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256))
{
- makevectors(self.angles);
- self.movetype = MOVETYPE_BOUNCE;
- self.velocity = '0 0 200';
- self.angles = '0 0 0';
- self.effects = autocvar_g_keepawayball_effects;
- self.think = ka_RespawnBall;
- self.nextthink = time + autocvar_g_keepawayball_respawntime;
+ entity spot = SelectSpawnPoint(TRUE);
+ setorigin(self, spot.origin);
+ self.angles = spot.angles;
+ }
- pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1);
- pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1);
+ makevectors(self.angles);
+ self.movetype = MOVETYPE_BOUNCE;
+ self.velocity = '0 0 200';
+ self.angles = '0 0 0';
+ self.effects = autocvar_g_keepawayball_effects;
+ self.think = ka_RespawnBall;
+ self.nextthink = time + autocvar_g_keepawayball_respawntime;
- WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAGCARRIER, '0 1 1');
- WaypointSprite_Ping(self.waypointsprite_attachedforcarrier);
+ pointparticles(particleeffectnum("electro_combo"), oldballorigin, '0 0 0', 1);
+ pointparticles(particleeffectnum("electro_combo"), self.origin, '0 0 0', 1);
- sound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
- }
- else
- {
- ka_RespawnBall(); // finding a location failed, retry
- }
+ WaypointSprite_Spawn("ka-ball", 0, 0, self, '0 0 64', world, self.team, self, waypointsprite_attachedforcarrier, FALSE, RADARICON_FLAGCARRIER, '0 1 1');
+ WaypointSprite_Ping(self.waypointsprite_attachedforcarrier);
+
+ sound(self, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
}
void ka_TimeScoring()
return;
}
if(other.deadflag != DEAD_NO) { return; }
+ if(other.frozen) { return; }
if (!IS_PLAYER(other))
{ // The ball just touched an object, most likely the world
pointparticles(particleeffectnum("kaball_sparks"), self.origin, '0 0 0', 1);
// messages and sounds
ka_EventLog("pickup", other);
Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_KEEPAWAY_PICKUP, other.netname);
- Send_Notification(NOTIF_ALL, world, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, other.netname);
+ Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, other.netname);
+ Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF);
sound(self.owner, CH_TRIGGER, "keepaway/pickedup.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
// scoring
MUTATOR_HOOK(PlayerDamage_Calculate, ka_PlayerDamage, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPowerups, ka_PlayerPowerups, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerUseKey, ka_PlayerUseKey, CBC_ORDER_ANY);
- MUTATOR_HOOK(HavocBot_ChooseRule, ka_BotRoles, CBC_ORDER_ANY);
+ MUTATOR_HOOK(HavocBot_ChooseRole, ka_BotRoles, CBC_ORDER_ANY);
MUTATOR_ONADD
{
--- /dev/null
- || (other.freezetag_frozen)
+ float buffs_BuffModel_Customize()
+ {
+ entity player, myowner;
+ float same_team;
+
+ player = WaypointSprite_getviewentity(other);
+ myowner = self.owner;
+ same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner));
+
+ if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0)
+ return FALSE;
+
+ if(player == myowner || (IS_SPEC(other) && other.enemy == myowner))
+ {
+ // somewhat hide the model, but keep the glow
+ self.effects = 0;
+ self.alpha = -1;
+ }
+ else
+ {
+ self.effects = EF_FULLBRIGHT | EF_LOWPRECISION;
+ self.alpha = 1;
+ }
+ return TRUE;
+ }
+
+ // buff item
+ float buff_Waypoint_visible_for_player(entity plr)
+ {
+ if(!self.owner.buff_active && !self.owner.buff_activetime)
+ return FALSE;
+
+ if(plr.buffs)
+ {
+ if(plr.cvar_cl_buffs_autoreplace)
+ {
+ if(plr.buffs == self.owner.buffs)
+ return FALSE;
+ }
+ else
+ return FALSE;
+ }
+
+ return WaypointSprite_visible_for_player(plr);
+ }
+
+ void buff_Waypoint_Spawn(entity e)
+ {
+ WaypointSprite_Spawn(Buff_Sprite(e.buffs), 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs_z, world, e.team, e, buff_waypoint, TRUE, RADARICON_POWERUP, e.glowmod);
+ WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_POWERUP, e.glowmod);
+ e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player;
+ }
+
+ void buff_SetCooldown(float cd)
+ {
+ cd = max(0, cd);
+
+ if(!self.buff_waypoint)
+ buff_Waypoint_Spawn(self);
+
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + cd);
+ self.buff_activetime = cd;
+ self.buff_active = !cd;
+ }
+
+ void buff_Respawn(entity ent)
+ {
+ if(gameover) { return; }
+
+ vector oldbufforigin = ent.origin;
+
+ if(!MoveToRandomMapLocation(ent, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256))
+ {
+ entity spot = SelectSpawnPoint(TRUE);
+ setorigin(ent, ((spot.origin + '0 0 200') + (randomvec() * 300)));
+ ent.angles = spot.angles;
+ }
+
+ tracebox(ent.origin, ent.mins * 1.5, self.maxs * 1.5, ent.origin, MOVE_NOMONSTERS, ent);
+
+ setorigin(ent, trace_endpos); // attempt to unstick
+
+ ent.movetype = MOVETYPE_TOSS;
+
+ makevectors(ent.angles);
+ ent.velocity = '0 0 200';
+ ent.angles = '0 0 0';
+ if(autocvar_g_buffs_random_lifetime > 0)
+ ent.lifetime = time + autocvar_g_buffs_random_lifetime;
+
+ pointparticles(particleeffectnum("electro_combo"), oldbufforigin + ((ent.mins + ent.maxs) * 0.5), '0 0 0', 1);
+ pointparticles(particleeffectnum("electro_combo"), CENTER_OR_VIEWOFS(ent), '0 0 0', 1);
+
+ WaypointSprite_Ping(ent.buff_waypoint);
+
+ sound(ent, CH_TRIGGER, "keepaway/respawn.wav", VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere)
+ }
+
+ void buff_Touch()
+ {
+ if(gameover) { return; }
+
+ if(ITEM_TOUCH_NEEDKILL())
+ {
+ buff_Respawn(self);
+ return;
+ }
+
+ if((self.team && DIFF_TEAM(other, self))
- if(!self.owner || self.owner.freezetag_frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
++ || (other.frozen)
+ || (other.vehicle)
+ || (!IS_PLAYER(other))
+ || (!self.buff_active)
+ )
+ {
+ // can't touch this
+ return;
+ }
+
+ if(other.buffs)
+ {
+ if(other.cvar_cl_buffs_autoreplace && other.buffs != self.buffs)
+ {
+ //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs);
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, other.buffs);
+
+ other.buffs = 0;
+ //sound(other, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+ }
+ else { return; } // do nothing
+ }
+
+ self.owner = other;
+ self.buff_active = FALSE;
+ self.lifetime = 0;
+
+ Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, self.buffs);
+ Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, self.buffs);
+
+ pointparticles(particleeffectnum("item_pickup"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ sound(other, CH_TRIGGER, "misc/shield_respawn.wav", VOL_BASE, ATTN_NORM);
+ other.buffs |= (self.buffs);
+ }
+
+ float buff_Available(float buffid)
+ {
+ if(buffid == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only"))))
+ return FALSE;
+
+ if(buffid == BUFF_VAMPIRE && cvar("g_vampire"))
+ return FALSE;
+
+ if(!cvar(strcat("g_buffs_", Buff_Name(buffid))))
+ return FALSE;
+
+ return TRUE;
+ }
+
+ void buff_NewType(entity ent, float cb)
+ {
+ entity e;
+ RandomSelection_Init();
+ for(e = Buff_Type_first; e; e = e.enemy)
+ if(buff_Available(e.items))
+ {
+ RandomSelection_Add(world, e.items, string_null, 1, 1 / e.count); // if it's already been chosen, give it a lower priority
+ e.count += 1;
+ }
+ ent.buffs = RandomSelection_chosen_float;
+ }
+
+ void buff_Think()
+ {
+ if(self.buffs != self.oldbuffs)
+ {
+ self.color = Buff_Color(self.buffs);
+ self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color);
+ self.skin = Buff_Skin(self.buffs);
+
+ setmodel(self, "models/relics/relic.md3");
+
+ if(self.buff_waypoint)
+ {
+ //WaypointSprite_Disown(self.buff_waypoint, 1);
+ WaypointSprite_Kill(self.buff_waypoint);
+ buff_Waypoint_Spawn(self);
+ if(self.buff_activetime)
+ WaypointSprite_UpdateBuildFinished(self.buff_waypoint, time + self.buff_activetime - frametime);
+ }
+
+ self.oldbuffs = self.buffs;
+ }
+
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ if(!self.buff_activetime_updated)
+ {
+ buff_SetCooldown(self.buff_activetime);
+ self.buff_activetime_updated = TRUE;
+ }
+
+ if(!self.buff_active && !self.buff_activetime)
- if(!frag_target.freezetag_frozen)
++ if(!self.owner || self.owner.frozen || self.owner.deadflag != DEAD_NO || !self.owner.iscreature || !(self.owner.buffs & self.buffs))
+ {
+ buff_SetCooldown(autocvar_g_buffs_cooldown_respawn + frametime);
+ self.owner = world;
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+ buff_Respawn(self);
+ }
+
+ if(self.buff_activetime)
+ if(!gameover)
+ if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime)
+ {
+ self.buff_activetime = max(0, self.buff_activetime - frametime);
+
+ if(!self.buff_activetime)
+ {
+ self.buff_active = TRUE;
+ sound(self, CH_TRIGGER, "misc/strength_respawn.wav", VOL_BASE, ATTN_NORM);
+ pointparticles(particleeffectnum("item_respawn"), CENTER_OR_VIEWOFS(self), '0 0 0', 1);
+ }
+ }
+
+ if(!self.buff_active)
+ {
+ self.alpha = 0.3;
+ self.effects &= ~(EF_FULLBRIGHT);
+ self.pflags = 0;
+ }
+ else
+ {
+ self.alpha = 1;
+ self.effects |= EF_FULLBRIGHT;
+ self.light_lev = 220 + 36 * sin(time);
+ self.pflags = PFLAGS_FULLDYNAMIC;
+
+ if(self.team && !self.buff_waypoint)
+ buff_Waypoint_Spawn(self);
+
+ if(self.lifetime)
+ if(time >= self.lifetime)
+ buff_Respawn(self);
+ }
+
+ self.nextthink = time;
+ //self.angles_y = time * 110.1;
+ }
+
+ void buff_Waypoint_Reset()
+ {
+ WaypointSprite_Kill(self.buff_waypoint);
+
+ if(self.buff_activetime) { buff_Waypoint_Spawn(self); }
+ }
+
+ void buff_Reset()
+ {
+ if(autocvar_g_buffs_randomize)
+ buff_NewType(self, self.buffs);
+ self.owner = world;
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate);
+ buff_Waypoint_Reset();
+ self.buff_activetime_updated = FALSE;
+
+ if(autocvar_g_buffs_random_location || (self.spawnflags & 1))
+ buff_Respawn(self);
+ }
+
+ void buff_Init(entity ent)
+ {
+ if(!cvar("g_buffs")) { remove(self); return; }
+
+ if(!teamplay && self.team) { self.team = 0; }
+
+ entity oldself = self;
+ self = ent;
+ if(!self.buffs || buff_Available(self.buffs))
+ buff_NewType(self, 0);
+
+ self.classname = "item_buff";
+ self.solid = SOLID_TRIGGER;
+ self.flags = FL_ITEM;
+ self.think = buff_Think;
+ self.touch = buff_Touch;
+ self.reset = buff_Reset;
+ self.nextthink = time + 0.1;
+ self.gravity = 1;
+ self.movetype = MOVETYPE_TOSS;
+ self.scale = 1;
+ self.skin = Buff_Skin(self.buffs);
+ self.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW;
+ self.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY;
+ //self.gravity = 100;
+ self.color = Buff_Color(self.buffs);
+ self.glowmod = ((self.team) ? Team_ColorRGB(self.team) + '0.1 0.1 0.1' : self.color);
+ buff_SetCooldown(autocvar_g_buffs_cooldown_activate + game_starttime);
+ self.buff_active = !self.buff_activetime;
+ self.pflags = PFLAGS_FULLDYNAMIC;
+
+ if(self.noalign)
+ self.movetype = MOVETYPE_NONE; // reset by random location
+
+ setmodel(self, "models/relics/relic.md3");
+ setsize(self, BUFF_MIN, BUFF_MAX);
+
+ if(cvar("g_buffs_random_location") || (self.spawnflags & 1))
+ buff_Respawn(self);
+
+ self = oldself;
+ }
+
+ void buff_Init_Compat(entity ent, float replacement)
+ {
+ if(ent.spawnflags & 2)
+ ent.team = NUM_TEAM_1;
+ else if(ent.spawnflags & 4)
+ ent.team = NUM_TEAM_2;
+
+ ent.buffs = replacement;
+
+ buff_Init(ent);
+ }
+
+ void buff_SpawnReplacement(entity ent, entity old)
+ {
+ setorigin(ent, old.origin);
+ ent.angles = old.angles;
+ ent.noalign = old.noalign;
+
+ buff_Init(ent);
+ }
+
+ // mutator hooks
+ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_SplitHealthArmor)
+ {
+ if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return FALSE; } // oh no you don't
+
+ if(frag_target.buffs & BUFF_RESISTANCE)
+ {
+ vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage);
+ damage_take = v_x;
+ damage_save = v_y;
+ }
+
+ return FALSE;
+ }
+
+ void buff_Vengeance_DelayedDamage()
+ {
+ if(self.enemy)
+ Damage(self.enemy, self.owner, self.owner, self.dmg, DEATH_BUFF_VENGEANCE, self.enemy.origin, '0 0 0');
+
+ remove(self);
+ return;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerDamage_Calculate)
+ {
+ if(frag_deathtype == DEATH_BUFF_VENGEANCE) { return FALSE; } // oh no you don't
+
+ if(frag_target.buffs & BUFF_SPEED)
+ if(frag_target != frag_attacker)
+ frag_damage *= autocvar_g_buffs_speed_damage_take;
+
+ if(frag_target.buffs & BUFF_MEDIC)
+ if((frag_target.health - frag_damage) <= 0)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ if(frag_attacker)
+ if(random() <= autocvar_g_buffs_medic_survive_chance)
+ if(frag_target.health - autocvar_g_buffs_medic_survive_health > 0) // not if the final result would be less than 0, medic must get health
+ frag_damage = frag_target.health - autocvar_g_buffs_medic_survive_health;
+
+ if(frag_target.buffs & BUFF_VENGEANCE)
+ if(frag_attacker)
+ if(frag_attacker != frag_target)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ {
+ entity dmgent = spawn();
+
+ dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier;
+ dmgent.enemy = frag_attacker;
+ dmgent.owner = frag_target;
+ dmgent.think = buff_Vengeance_DelayedDamage;
+ dmgent.nextthink = time + 0.1;
+ }
+
+ if(frag_target.buffs & BUFF_BASH)
+ if(frag_attacker != frag_target)
+ if(vlen(frag_force))
+ frag_force = '0 0 0';
+
+ if(frag_attacker.buffs & BUFF_BASH)
+ if(vlen(frag_force))
+ if(frag_attacker == frag_target)
+ frag_force *= autocvar_g_buffs_bash_force_self;
+ else
+ frag_force *= autocvar_g_buffs_bash_force;
+
+ if(frag_attacker.buffs & BUFF_DISABILITY)
+ if(frag_target != frag_attacker)
+ frag_target.buff_disability_time = time + autocvar_g_buffs_disability_time;
+
+ if(frag_attacker.buffs & BUFF_MEDIC)
+ if(SAME_TEAM(frag_attacker, frag_target))
+ if(frag_attacker != frag_target)
+ {
+ frag_target.health = min(g_pickup_healthmega_max, frag_target.health + frag_damage);
+ frag_damage = 0;
+ }
+
+ // this... is ridiculous (TODO: fix!)
+ if(frag_attacker.buffs & BUFF_VAMPIRE)
+ if(!frag_target.vehicle)
+ if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype))
+ if(frag_target.deadflag == DEAD_NO)
+ if(IS_PLAYER(frag_target) || (frag_target.flags & FL_MONSTER))
+ if(frag_attacker != frag_target)
- if(self.freezetag_frozen)
++ if(!frag_target.frozen)
+ if(frag_target.takedamage)
+ if(DIFF_TEAM(frag_attacker, frag_target))
+ frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max);
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerSpawn)
+ {
+ self.buffs = 0;
+ // reset timers here to prevent them continuing after re-spawn
+ self.buff_disability_time = 0;
+ self.buff_disability_effect_time = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerPhysics)
+ {
+ if(self.buffs & BUFF_SPEED)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed;
+ }
+
+ if(time < self.buff_disability_time)
+ {
+ self.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed;
+ self.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerJump)
+ {
+ if(self.buffs & BUFF_JUMP)
+ player_jumpheight = autocvar_g_buffs_jump_height;
+ self.stat_jumpheight = player_jumpheight;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_MonsterMove)
+ {
+ if(time < self.buff_disability_time)
+ {
+ monster_speed_walk *= autocvar_g_buffs_disability_speed;
+ monster_speed_run *= autocvar_g_buffs_disability_speed;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerDies)
+ {
+ if(self.buffs)
+ {
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+ self.buffs = 0;
+
+ if(self.buff_model)
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+ }
+ }
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerUseKey)
+ {
+ if(MUTATOR_RETURNVALUE || gameover) { return FALSE; }
+ if(self.buffs)
+ {
+ Send_Notification(NOTIF_ONE, self, MSG_MULTI, ITEM_BUFF_DROP, self.buffs);
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+
+ self.buffs = 0;
+ sound(self, CH_TRIGGER, "relics/relic_effect.wav", VOL_BASE, ATTN_NORM);
+ return TRUE;
+ }
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_RemovePlayer)
+ {
+ if(self.buff_model)
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+ }
+
+ // also reset timers here to prevent them continuing after spectating
+ self.buff_disability_time = 0;
+ self.buff_disability_effect_time = 0;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_CustomizeWaypoint)
+ {
+ entity e = WaypointSprite_getviewentity(other);
+
+ // if you have the invisibility powerup, sprites ALWAYS are restricted to your team
+ // but only apply this to real players, not to spectators
+ if((self.owner.flags & FL_CLIENT) && (self.owner.buffs & BUFF_INVISIBLE) && (e == other))
+ if(DIFF_TEAM(self.owner, e))
+ return TRUE;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_OnEntityPreSpawn)
+ {
+ if(autocvar_g_buffs_replace_powerups)
+ switch(self.classname)
+ {
+ case "item_strength":
+ case "item_invincible":
+ {
+ entity e = spawn();
+ buff_SpawnReplacement(e, self);
+ return TRUE;
+ }
+ }
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_WeaponRate)
+ {
+ if(self.buffs & BUFF_SPEED)
+ weapon_rate *= autocvar_g_buffs_speed_rate;
+
+ if(time < self.buff_disability_time)
+ weapon_rate *= autocvar_g_buffs_disability_rate;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerThink)
+ {
+ if(gameover || self.deadflag != DEAD_NO) { return FALSE; }
+
+ if(time < self.buff_disability_time)
+ if(time >= self.buff_disability_effect_time)
+ {
+ pointparticles(particleeffectnum("smoking"), self.origin + ((self.mins + self.maxs) * 0.5), '0 0 0', 1);
+ self.buff_disability_effect_time = time + 0.5;
+ }
+
++ if(self.frozen)
+ {
+ if(self.buffs)
+ {
+ Send_Notification(NOTIF_ALL_EXCEPT, self, MSG_INFO, INFO_ITEM_BUFF_LOST, self.netname, self.buffs);
+ self.buffs = 0;
+ }
+ }
+
+ if((self.buffs & BUFF_INVISIBLE) && (self.oldbuffs & BUFF_INVISIBLE))
+ if(self.alpha != autocvar_g_buffs_invisible_alpha)
+ self.alpha = autocvar_g_buffs_invisible_alpha;
+
+ if(self.buffs != self.oldbuffs)
+ {
+ if(self.oldbuffs & BUFF_AMMO)
+ {
+ if(self.buff_ammo_prev_infitems)
+ self.items |= IT_UNLIMITED_WEAPON_AMMO;
+ else
+ self.items &= ~IT_UNLIMITED_WEAPON_AMMO;
+ }
+ else if(self.buffs & BUFF_AMMO)
+ {
+ self.buff_ammo_prev_infitems = (self.items & IT_UNLIMITED_WEAPON_AMMO);
+ self.items |= IT_UNLIMITED_WEAPON_AMMO;
+ if(!self.ammo_shells) { self.ammo_shells = 20; }
+ if(!self.ammo_cells) { self.ammo_cells = 20; }
+ if(!self.ammo_rockets) { self.ammo_rockets = 20; }
+ if(!self.ammo_nails) { self.ammo_nails = 20; }
+ if(!self.ammo_fuel) { self.ammo_fuel = 20; }
+ }
+
+ if(self.oldbuffs & BUFF_INVISIBLE)
+ {
+ if(time < self.strength_finished && g_minstagib)
+ self.alpha = autocvar_g_minstagib_invis_alpha;
+ else
+ self.alpha = self.buff_invisible_prev_alpha;
+ }
+ else if(self.buffs & BUFF_INVISIBLE)
+ {
+ if(time < self.strength_finished && g_minstagib)
+ self.buff_invisible_prev_alpha = default_player_alpha;
+ else
+ self.buff_invisible_prev_alpha = self.alpha;
+ self.alpha = autocvar_g_buffs_invisible_alpha;
+ }
+
+ if(self.oldbuffs & BUFF_FLIGHT)
+ self.gravity = self.buff_flight_prev_gravity;
+ else if(self.buffs & BUFF_FLIGHT)
+ {
+ self.buff_flight_prev_gravity = self.gravity;
+ self.gravity = autocvar_g_buffs_flight_gravity;
+ }
+
+ self.oldbuffs = self.buffs;
+ if(self.buffs)
+ {
+ if(!self.buff_model)
+ {
+ self.buff_model = spawn();
+ setmodel(self.buff_model, "models/relics/relic.md3");
+ setsize(self.buff_model, '0 0 -40', '0 0 40');
+ setattachment(self.buff_model, self, "");
+ setorigin(self.buff_model, '0 0 1' * (self.buff_model.maxs_z * 1));
+ self.buff_model.owner = self;
+ self.buff_model.scale = 0.7;
+ self.buff_model.pflags = PFLAGS_FULLDYNAMIC;
+ self.buff_model.light_lev = 200;
+ self.buff_model.customizeentityforclient = buffs_BuffModel_Customize;
+ }
+ self.buff_model.color = Buff_Color(self.buffs);
+ self.buff_model.glowmod = ((self.buff_model.team) ? Team_ColorRGB(self.buff_model.team) + '0.1 0.1 0.1' : self.buff_model.color);
+ self.buff_model.skin = Buff_Skin(self.buffs);
+
+ self.effects |= EF_NOSHADOW;
+ }
+ else
+ {
+ remove(self.buff_model);
+ self.buff_model = world;
+
+ self.effects &= ~(EF_NOSHADOW);
+ }
+ }
+
+ if(self.buff_model)
+ {
+ self.buff_model.effects = self.effects;
+ self.buff_model.effects |= EF_LOWPRECISION;
+ self.buff_model.effects = self.buff_model.effects & EFMASK_CHEAP; // eat performance
+
+ self.buff_model.alpha = self.alpha;
+ }
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_SpectateCopy)
+ {
+ self.buffs = other.buffs;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_VehicleEnter)
+ {
+ vh_vehicle.buffs = vh_player.buffs;
+ vh_player.buffs = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_VehicleExit)
+ {
+ vh_player.buffs = vh_vehicle.buffs;
+ vh_vehicle.buffs = 0;
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_PlayerRegen)
+ {
+ if(self.buffs & BUFF_MEDIC)
+ {
+ regen_mod_rot = autocvar_g_buffs_medic_rot;
+ regen_mod_limit = regen_mod_max = autocvar_g_buffs_medic_max;
+ regen_mod_regen = autocvar_g_buffs_medic_regen;
+ }
+
+ if(self.buffs & BUFF_SPEED)
+ regen_mod_regen = autocvar_g_buffs_speed_regen;
+
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_GetCvars)
+ {
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_buffs_autoreplace, "cl_buffs_autoreplace");
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsString)
+ {
+ ret_string = strcat(ret_string, ":Buffs");
+ return FALSE;
+ }
+
+ MUTATOR_HOOKFUNCTION(buffs_BuildMutatorsPrettyString)
+ {
+ ret_string = strcat(ret_string, ", Buffs");
+ return FALSE;
+ }
+
+ void buffs_DelayedInit()
+ {
+ if(autocvar_g_buffs_spawn_count > 0)
+ if(find(world, classname, "item_buff") == world)
+ {
+ float i;
+ for(i = 0; i < autocvar_g_buffs_spawn_count; ++i)
+ {
+ entity e = spawn();
+ e.spawnflags |= 1; // always randomize
+ e.velocity = randomvec() * 250; // this gets reset anyway if random location works
+ buff_Init(e);
+ }
+ }
+ }
+
+ void buffs_Initialize()
+ {
+ precache_model("models/relics/relic.md3");
+ precache_sound("misc/strength_respawn.wav");
+ precache_sound("misc/shield_respawn.wav");
+ precache_sound("relics/relic_effect.wav");
+ precache_sound("weapons/rocket_impact.wav");
+ precache_sound("keepaway/respawn.wav");
+
+ addstat(STAT_BUFFS, AS_INT, buffs);
+ addstat(STAT_MOVEVARS_JUMPVELOCITY, AS_FLOAT, stat_jumpheight);
+
+ InitializeEntity(world, buffs_DelayedInit, INITPRIO_FINDTARGET);
+ }
+
+ MUTATOR_DEFINITION(mutator_buffs)
+ {
+ MUTATOR_HOOK(PlayerDamage_SplitHealthArmor, buffs_PlayerDamage_SplitHealthArmor, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDamage_Calculate, buffs_PlayerDamage_Calculate, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerSpawn, buffs_PlayerSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPhysics, buffs_PlayerPhysics, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerJump, buffs_PlayerJump, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MonsterMove, buffs_MonsterMove, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, buffs_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(VehicleEnter, buffs_VehicleEnter, CBC_ORDER_ANY);
+ MUTATOR_HOOK(VehicleExit, buffs_VehicleExit, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerRegen, buffs_PlayerRegen, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, buffs_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerUseKey, buffs_PlayerUseKey, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MakePlayerObserver, buffs_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(ClientDisconnect, buffs_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(OnEntityPreSpawn, buffs_OnEntityPreSpawn, CBC_ORDER_ANY);
+ MUTATOR_HOOK(CustomizeWaypoint, buffs_CustomizeWaypoint, CBC_ORDER_ANY);
+ MUTATOR_HOOK(WeaponRateFactor, buffs_WeaponRate, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerPreThink, buffs_PlayerThink, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetCvars, buffs_GetCvars, CBC_ORDER_ANY);
+ MUTATOR_HOOK(BuildMutatorsString, buffs_BuildMutatorsString, CBC_ORDER_ANY);
+ MUTATOR_HOOK(BuildMutatorsPrettyString, buffs_BuildMutatorsPrettyString, CBC_ORDER_ANY);
+
+ MUTATOR_ONADD
+ {
+ buffs_Initialize();
+ }
+
+ return FALSE;
+ }
+.entity nade_spawnloc;
+
void nade_timer_think()
{
self.skin = 8 - (self.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10);
self.nextthink = time;
if(!self.owner || wasfreed(self.owner))
remove(self);
-
}
void nade_burn_spawn(entity _nade)
{
- float p;
-
- switch(_nade.realowner.team)
- {
- case NUM_TEAM_1: p = PROJECTILE_NADE_RED_BURN; break;
- case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE_BURN; break;
- case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW_BURN; break;
- case NUM_TEAM_4: p = PROJECTILE_NADE_PINK_BURN; break;
- default: p = PROJECTILE_NADE_BURN; break;
- }
-
- CSQCProjectile(_nade, TRUE, p, TRUE);
+ CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, TRUE), TRUE);
}
void nade_spawn(entity _nade)
{
- float p;
entity timer = spawn();
setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
setattachment(timer, _nade, "");
timer.owner = _nade;
timer.skin = 10;
- switch(_nade.realowner.team)
+ _nade.effects |= EF_LOWPRECISION;
+
+ CSQCProjectile(_nade, TRUE, Nade_ProjectileFromID(_nade.nade_type, FALSE), TRUE);
+}
+
+void napalm_damage(float dist, float damage, float edgedamage, float burntime)
+{
+ entity e;
+ float d;
+ vector p;
+
+ if ( damage < 0 )
+ return;
+
+ RandomSelection_Init();
+ for(e = WarpZone_FindRadius(self.origin, dist, TRUE); e; e = e.chain)
+ if(e.takedamage == DAMAGE_AIM)
+ if(self.realowner != e || autocvar_g_nades_napalm_selfdamage)
+ if(!IS_PLAYER(e) || !self.realowner || DIFF_TEAM(e, self))
+ if(!e.frozen)
+ {
+ 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, self.origin) - p);
+ if(d < dist)
+ {
+ e.fireball_impactvec = p;
+ RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e));
+ }
+ }
+ if(RandomSelection_chosen_ent)
{
- case NUM_TEAM_1: p = PROJECTILE_NADE_RED; break;
- case NUM_TEAM_2: p = PROJECTILE_NADE_BLUE; break;
- case NUM_TEAM_3: p = PROJECTILE_NADE_YELLOW; break;
- case NUM_TEAM_4: p = PROJECTILE_NADE_PINK; break;
- default: p = PROJECTILE_NADE; break;
+ d = vlen(WarpZone_UnTransformOrigin(RandomSelection_chosen_ent, self.origin) - RandomSelection_chosen_ent.fireball_impactvec);
+ d = damage + (edgedamage - damage) * (d / dist);
+ Fire_AddDamage(RandomSelection_chosen_ent, self.realowner, d * burntime, burntime, self.projectiledeathtype | HITTYPE_BOUNCE);
+ //trailparticles(self, particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec);
+ pointparticles(particleeffectnum("fireball_laser"), self.origin, RandomSelection_chosen_ent.fireball_impactvec - self.origin, 1);
}
+}
- CSQCProjectile(_nade, TRUE, p, TRUE);
- entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, 1);
+void napalm_ball_think()
+{
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time > self.pushltime)
+ {
+ remove(self);
+ return;
+ }
+
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ { self.velocity_z = 200; }
+ }
+
+ self.angles = vectoangles(self.velocity);
+
+ napalm_damage(autocvar_g_nades_napalm_ball_radius,autocvar_g_nades_napalm_ball_damage,
+ autocvar_g_nades_napalm_ball_damage,autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+}
+
+
+void nade_napalm_ball()
+{
+ entity proj;
+ vector kick;
+
+ spamsound(self, CH_SHOTS, "weapons/fireball_fire.wav", VOL_BASE, ATTEN_NORM);
+
+ proj = spawn ();
+ proj.owner = self.owner;
+ proj.realowner = self.realowner;
+ proj.team = self.owner.team;
+ proj.classname = "grenade";
+ proj.bot_dodge = TRUE;
+ proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage;
+ proj.movetype = MOVETYPE_BOUNCE;
+ proj.projectiledeathtype = DEATH_NADE_NAPALM;
+ PROJECTILE_MAKETRIGGER(proj);
+ setmodel(proj, "null");
+ proj.scale = 1;//0.5;
+ setsize(proj, '-4 -4 -4', '4 4 4');
+ setorigin(proj, self.origin);
+ proj.think = 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;
+ proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC;
+
+ //CSQCProjectile(proj, TRUE, PROJECTILE_NAPALM_FIRE, TRUE);
+}
+
+
+void napalm_fountain_think()
+{
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ vector midpoint = ((self.absmin + self.absmax) * 0.5);
+ if(pointcontents(midpoint) == CONTENT_WATER)
+ {
+ self.velocity = self.velocity * 0.5;
+
+ if(pointcontents(midpoint + '0 0 16') == CONTENT_WATER)
+ { self.velocity_z = 200; }
+
+ UpdateCSQCProjectile(self);
+ }
+
+ napalm_damage(autocvar_g_nades_napalm_fountain_radius, autocvar_g_nades_napalm_fountain_damage,
+ autocvar_g_nades_napalm_fountain_edgedamage, autocvar_g_nades_napalm_burntime);
+
+ self.nextthink = time + 0.1;
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time + autocvar_g_nades_napalm_fountain_delay;
+ nade_napalm_ball();
+ }
+}
+
+void nade_napalm_boom()
+{
+ entity fountain;
+ local float c;
+ for (c = 0; c < autocvar_g_nades_napalm_ball_count; c ++)
+ nade_napalm_ball();
+
+
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = napalm_fountain_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime;
+ fountain.pushltime = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_NAPALM;
+ 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 freeze_time)
+{
+ frost_target.frozen_by = freezefield.realowner;
+ pointparticles(particleeffectnum("electro_impact"), frost_target.origin, '0 0 0', 1);
+ Freeze(frost_target, 1/freeze_time, 3, FALSE);
+ if(frost_target.ballcarried)
+ if(g_keepaway) { ka_DropEvent(frost_target); }
+ else { DropBall(frost_target.ballcarried, frost_target.origin, frost_target.velocity);}
+ if(frost_target.flagcarried) { ctf_Handle_Throw(frost_target, world, DROP_THROW); }
+ if(frost_target.nade) { toss_nade(frost_target, '0 0 0', time + 0.05); }
+
+ kh_Key_DropAll(frost_target, FALSE);
+}
+
+void nade_ice_think()
+{
+
+ if(round_handler_IsActive())
+ if(!round_handler_IsRoundStarted())
+ {
+ remove(self);
+ return;
+ }
+
+ if(time >= self.ltime)
+ {
+ if ( autocvar_g_nades_ice_explode )
+ {
+ string expef;
+ switch(self.realowner.team)
+ {
+ case NUM_TEAM_1: expef = "nade_red_explode"; break;
+ case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+ case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+ case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+ default: expef = "nade_neutral_explode"; break;
+ }
+ pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
+ sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
+
+ RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+ Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+ autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ }
+ remove(self);
+ return;
+ }
+
+
+ self.nextthink = time+0.1;
+
+ // gaussian
+ float randomr;
+ randomr = random();
+ randomr = exp(-5*randomr*randomr)*autocvar_g_nades_nade_radius;
+ float randomw;
+ randomw = random()*M_PI*2;
+ vector randomp;
+ randomp_x = randomr*cos(randomw);
+ randomp_y = randomr*sin(randomw);
+ randomp_z = 1;
+ pointparticles(particleeffectnum("electro_muzzleflash"), self.origin + randomp, '0 0 0', 1);
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.7;
+
+
+ pointparticles(particleeffectnum("electro_impact"), self.origin, '0 0 0', 1);
+ pointparticles(particleeffectnum("icefield"), self.origin, '0 0 0', 1);
+ }
+
+
+ float current_freeze_time = self.ltime - time - 0.1;
+
+ entity e;
+ for(e = findradius(self.origin, autocvar_g_nades_nade_radius); e; e = e.chain)
+ if(e != self)
+ if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, self.realowner) || e == self.realowner))
+ if(e.takedamage && e.deadflag == DEAD_NO)
+ if(e.health > 0)
+ if(!e.revival_time || ((time - e.revival_time) >= 1.5))
+ if(!e.frozen)
+ if(current_freeze_time > 0)
+ nade_ice_freeze(self, e, current_freeze_time);
+}
+
+void nade_ice_boom()
+{
+ entity fountain;
+ fountain = spawn();
+ fountain.owner = self.owner;
+ fountain.realowner = self.realowner;
+ fountain.origin = self.origin;
+ setorigin(fountain, fountain.origin);
+ fountain.think = nade_ice_think;
+ fountain.nextthink = time;
+ fountain.ltime = time + autocvar_g_nades_ice_freeze_time;
+ fountain.pushltime = fountain.wait = fountain.ltime;
+ fountain.team = self.team;
+ fountain.movetype = MOVETYPE_TOSS;
+ fountain.projectiledeathtype = DEATH_NADE_ICE;
+ fountain.bot_dodge = FALSE;
+ setsize(fountain, '-16 -16 -16', '16 16 16');
+ fountain.nade_special_time = time+0.3;
+ fountain.angles = self.angles;
+
+ if ( autocvar_g_nades_ice_explode )
+ {
+ setmodel(fountain, "models/grenademodel.md3");
+ entity timer = spawn();
+ setmodel(timer, "models/ok_nade_counter/ok_nade_counter.md3");
+ setattachment(timer, fountain, "");
+ timer.classname = "nade_timer";
+ timer.colormap = self.colormap;
+ timer.glowmod = self.glowmod;
+ timer.think = nade_timer_think;
+ timer.nextthink = time;
+ timer.wait = fountain.ltime;
+ timer.owner = fountain;
+ timer.skin = 10;
+ }
+ else
+ setmodel(fountain, "null");
+}
+
+void nade_translocate_boom()
+{
+ if(self.realowner.vehicle)
+ return;
+
+ vector locout = self.origin + '0 0 1' * (1 - self.realowner.mins_z - 24);
+
+ makevectors(self.realowner.angles);
+
+ entity oldself = self;
+ self = self.realowner;
+ MUTATOR_CALLHOOK(PortalTeleport);
+ self.realowner = self;
+ self = oldself;
+
+ TeleportPlayer(self, self.realowner, locout, self.realowner.mangle, v_forward * vlen(self.realowner.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER);
+}
+
+void nade_spawn_boom()
+{
+ entity spawnloc = spawn();
+ setorigin(spawnloc, self.origin);
+ setsize(spawnloc, self.realowner.mins, self.realowner.maxs);
+ spawnloc.movetype = MOVETYPE_NONE;
+ spawnloc.solid = SOLID_NOT;
+ spawnloc.drawonlytoclient = self.realowner;
+ spawnloc.effects = EF_STARDUST;
+ spawnloc.cnt = autocvar_g_nades_spawn_count;
+
+ if(self.realowner.nade_spawnloc)
+ {
+ remove(self.realowner.nade_spawnloc);
+ self.realowner.nade_spawnloc = world;
+ }
+
+ self.realowner.nade_spawnloc = spawnloc;
+}
+
+void nade_heal_think()
+{
+ if(time >= self.ltime)
+ {
+ remove(self);
+ return;
+ }
+
+ self.nextthink = time;
+
+ if(time >= self.nade_special_time)
+ {
+ self.nade_special_time = time+0.25;
+ self.nade_show_particles = 1;
+ }
+ else
+ self.nade_show_particles = 0;
+}
+
+void nade_heal_touch()
+{
+ float maxhealth;
+ float health_factor;
+ if(IS_PLAYER(other) || (other.flags & FL_MONSTER))
+ if(other.deadflag == DEAD_NO)
+ if(!other.frozen)
+ {
+ health_factor = autocvar_g_nades_heal_rate*frametime/2;
+ if ( other != self.realowner )
+ {
+ if ( SAME_TEAM(other,self) )
+ health_factor *= autocvar_g_nades_heal_friend;
+ else
+ health_factor *= autocvar_g_nades_heal_foe;
+ }
+ if ( health_factor > 0 )
+ {
+ maxhealth = (other.flags & FL_MONSTER) ? other.max_health : g_pickup_healthmega_max;
+ if ( other.health < maxhealth )
+ {
+ if ( self.nade_show_particles )
+ pointparticles(particleeffectnum("healing_fx"), other.origin, '0 0 0', 1);
+ other.health = min(other.health+health_factor, maxhealth);
+ }
+ other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot);
+ }
+ else if ( health_factor < 0 )
+ {
+ Damage(other,self,self.realowner,-health_factor,DEATH_NADE_HEAL,other.origin,'0 0 0');
+ }
+
+ }
+
+ if ( IS_REAL_CLIENT(other) || (other.vehicle_flags & VHF_ISVEHICLE) )
+ {
+ entity show_red = (other.vehicle_flags & VHF_ISVEHICLE) ? other.owner : other;
+ show_red.stat_healing_orb = time+0.1;
+ show_red.stat_healing_orb_alpha = 0.75 * (self.ltime - time) / self.healer_lifetime;
+ }
+}
+
+void nade_heal_boom()
+{
+ entity healer;
+ healer = spawn();
+ healer.owner = self.owner;
+ healer.realowner = self.realowner;
+ setorigin(healer, self.origin);
+ healer.healer_lifetime = autocvar_g_nades_heal_time; // save the cvar
+ healer.ltime = time + healer.healer_lifetime;
+ healer.team = self.realowner.team;
+ healer.bot_dodge = FALSE;
+ healer.solid = SOLID_TRIGGER;
+ healer.touch = nade_heal_touch;
+
+ setmodel(healer, "models/ctf/shield.md3");
+ healer.healer_radius = autocvar_g_nades_nade_radius;
+ vector size = '1 1 1' * healer.healer_radius / 2;
+ setsize(healer,-size,size);
+
+ Net_LinkEntity(healer, TRUE, 0, healer_send);
+
+ healer.think = nade_heal_think;
+ healer.nextthink = time;
+ healer.SendFlags |= 1;
+}
+
+void nade_monster_boom()
+{
++ entity e = spawnmonster(self.pokenade_type, 0, self.realowner, self.realowner, self.origin, FALSE, FALSE, 1);
+
+ //e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime;
+ e.monster_skill = MONSTER_SKILL_INSANE;
}
void nade_boom()
{
string expef;
+ float nade_blast = 1;
- switch(self.realowner.team)
+ switch ( self.nade_type )
{
- case NUM_TEAM_1: expef = "nade_red_explode"; break;
- case NUM_TEAM_2: expef = "nade_blue_explode"; break;
- case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
- case NUM_TEAM_4: expef = "nade_pink_explode"; break;
- default: expef = "nade_explode"; break;
+ case NADE_TYPE_NAPALM:
+ nade_blast = autocvar_g_nades_napalm_blast;
+ expef = "explosion_medium";
+ break;
+ case NADE_TYPE_ICE:
+ nade_blast = 0;
+ expef = "electro_combo"; // hookbomb_explode electro_combo bigplasma_impact
+ break;
+ case NADE_TYPE_TRANSLOCATE:
+ nade_blast = 0;
+ expef = "";
+ break;
+ case NADE_TYPE_MONSTER:
+ case NADE_TYPE_SPAWN:
+ nade_blast = 0;
+ switch(self.realowner.team)
+ {
+ case NUM_TEAM_1: expef = "spawn_event_red"; break;
+ case NUM_TEAM_2: expef = "spawn_event_blue"; break;
+ case NUM_TEAM_3: expef = "spawn_event_yellow"; break;
+ case NUM_TEAM_4: expef = "spawn_event_pink"; break;
+ default: expef = "spawn_event_neutral"; break;
+ }
+ break;
+ case NADE_TYPE_HEAL:
+ nade_blast = 0;
+ expef = "spawn_event_red";
+ break;
+
+ default:
+ case NADE_TYPE_NORMAL:
+ switch(self.realowner.team)
+ {
+ case NUM_TEAM_1: expef = "nade_red_explode"; break;
+ case NUM_TEAM_2: expef = "nade_blue_explode"; break;
+ case NUM_TEAM_3: expef = "nade_yellow_explode"; break;
+ case NUM_TEAM_4: expef = "nade_pink_explode"; break;
+ default: expef = "nade_neutral_explode"; break;
+ }
}
- sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
- sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
pointparticles(particleeffectnum(expef), self.origin + '0 0 1', '0 0 0', 1);
- Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ sound(self, CH_SHOTS_SINGLE, "misc/null.wav", VOL_BASE, ATTEN_NORM);
+ sound(self, CH_SHOTS, "weapons/rocket_impact.wav", VOL_BASE, ATTEN_NORM);
self.takedamage = DAMAGE_NO;
- RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
+
+ if(nade_blast)
+ {
+ RadiusDamage(self, self.realowner, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage,
autocvar_g_nades_nade_radius, self, autocvar_g_nades_nade_force, self.projectiledeathtype, self.enemy);
+ Damage_DamageInfo(self.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, self.projectiledeathtype, 0, self);
+ }
+
+ switch ( self.nade_type )
+ {
+ case NADE_TYPE_NAPALM: nade_napalm_boom(); break;
+ case NADE_TYPE_ICE: nade_ice_boom(); break;
+ case NADE_TYPE_TRANSLOCATE: nade_translocate_boom(); break;
+ case NADE_TYPE_SPAWN: nade_spawn_boom(); break;
+ case NADE_TYPE_HEAL: nade_heal_boom(); break;
+ case NADE_TYPE_MONSTER: nade_monster_boom(); break;
+ }
remove(self);
}
void nade_damage(entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector force)
{
+ if(self.nade_type == NADE_TYPE_TRANSLOCATE || self.nade_type == NADE_TYPE_SPAWN)
+ return;
+
if(DEATH_ISWEAPON(deathtype, WEP_LASER))
return;
if(DEATH_ISWEAPON(deathtype, WEP_UZI))
damage = self.max_health * 0.1;
- if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) && !(deathtype & HITTYPE_SECONDARY))
- damage = self.max_health * 1.1;
-
- if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN) && (deathtype & HITTYPE_SECONDARY))
+ if(DEATH_ISWEAPON(deathtype, WEP_SHOTGUN))
+ if(deathtype & HITTYPE_SECONDARY)
{
damage = self.max_health * 0.1;
- force *= 15;
+ force *= 10;
}
+ else
+ damage = self.max_health * 1.1;
self.velocity += force;
self.think = nade_beep;
}
- self.health -= damage;
- self.realowner = attacker;
+ self.health -= damage;
+
+ if ( self.nade_type != NADE_TYPE_HEAL || IS_PLAYER(attacker) )
+ self.realowner = attacker;
if(self.health <= 0)
W_PrepareExplosionByDamage(attacker, nade_boom);
void toss_nade(entity e, vector _velocity, float _time)
{
+ if(e.nade == world)
+ return;
+
entity _nade = e.nade;
e.nade = world;
Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER_CPID, CPID_NADES);
- //setorigin(_nade, CENTER_OR_VIEWOFS(e) + (v_right * 10) * -1);
setorigin(_nade, w_shotorg + (v_right * 25) * -1);
- setmodel(_nade, "models/weapons/v_ok_grenade.md3");
- setattachment(_nade, world, "");
+ //setmodel(_nade, "models/weapons/v_ok_grenade.md3");
+ //setattachment(_nade, world, "");
PROJECTILE_MAKETRIGGER(_nade);
setsize(_nade, '-16 -16 -16', '16 16 16');
_nade.movetype = MOVETYPE_BOUNCE;
_nade.velocity = _velocity;
else
_nade.velocity = W_CalculateProjectileVelocity(e.velocity, _velocity, TRUE);
-
+
_nade.touch = nade_touch;
_nade.health = autocvar_g_nades_nade_health;
_nade.max_health = _nade.health;
_nade.takedamage = DAMAGE_AIM;
_nade.event_damage = nade_damage;
+ _nade.customizeentityforclient = func_null;
+ _nade.exteriormodeltoclient = world;
+ _nade.traileffectnum = 0;
_nade.teleportable = TRUE;
_nade.pushable = TRUE;
_nade.gravity = 1;
_nade.damagedbycontents = TRUE;
_nade.angles = vectoangles(_nade.velocity);
_nade.flags = FL_PROJECTILE;
+ _nade.projectiledeathtype = DEATH_NADE;
+ _nade.toss_time = time;
+ _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX);
nade_spawn(_nade);
}
e.nade_refire = time + autocvar_g_nades_nade_refire;
+ e.nade_timer = 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) && player.bonus_nades < autocvar_g_nades_bonus_max)
+ if (player.frozen == 0)
+ if (player.deadflag == DEAD_NO)
+ {
+ if ( player.bonus_nade_score < 1 )
+ player.bonus_nade_score += score/autocvar_g_nades_bonus_score_max;
+
+ if ( player.bonus_nade_score >= 1 )
+ {
+ Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS);
+ play2(player,"kh/alarm.wav");
+ player.bonus_nades++;
+ player.bonus_nade_score -= 1;
+ }
+ }
+}
+
+void nades_RemoveBonus(entity player)
+{
+ player.bonus_nades = player.bonus_nade_score = 0;
+}
+
+float nade_customize()
+{
+ //if(IS_SPEC(other)) { return FALSE; }
+ if(other == self.realowner || (IS_SPEC(other) && other.enemy == self.realowner))
+ {
+ // somewhat hide the model, but keep the glow
+ //self.effects = 0;
+ if(self.traileffectnum)
+ self.traileffectnum = 0;
+ self.alpha = -1;
+ }
+ else
+ {
+ //self.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION;
+ if(!self.traileffectnum)
+ self.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(self.nade_type, FALSE), self.team));
+ self.alpha = 1;
+ }
+
+ return TRUE;
}
void nade_prime()
if(self.fake_nade)
remove(self.fake_nade);
- self.nade = spawn();
- setmodel(self.nade, "null");
- setattachment(self.nade, self, "bip01 l hand");
- self.nade.classname = "nade";
- self.nade.realowner = self;
- self.nade.colormap = self.colormap;
- self.nade.glowmod = self.glowmod;
- self.nade.wait = time + autocvar_g_nades_nade_lifetime;
- self.nade.lifetime = time;
- self.nade.think = nade_beep;
- self.nade.nextthink = max(self.nade.wait - 3, time);
- self.nade.projectiledeathtype = DEATH_NADE;
-
- self.fake_nade = spawn();
- setmodel(self.fake_nade, "models/weapons/h_ok_grenade.iqm");
- setattachment(self.fake_nade, self.weaponentity, "");
- self.fake_nade.classname = "fake_nade";
- //self.fake_nade.viewmodelforclient = self;
- self.fake_nade.realowner = self.fake_nade.owner = self;
- self.fake_nade.colormap = self.colormap;
- self.fake_nade.glowmod = self.glowmod;
- self.fake_nade.think = SUB_Remove;
- self.fake_nade.nextthink = self.nade.wait;
+ entity n = spawn(), fn = spawn();
+
+ n.classname = "nade";
+ fn.classname = "fake_nade";
+
+ if(self.items & IT_STRENGTH && autocvar_g_nades_bonus_onstrength)
+ n.nade_type = self.nade_type;
+ else if (self.bonus_nades >= 1)
+ {
+ n.nade_type = self.nade_type;
+ n.pokenade_type = self.pokenade_type;
+ self.bonus_nades -= 1;
+ }
+ else
+ {
+ n.nade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_nade_type : autocvar_g_nades_nade_type);
+ n.pokenade_type = ((autocvar_g_nades_client_select) ? self.cvar_cl_pokenade_type : autocvar_g_nades_pokenade_monster_type);
+ }
+
+ n.nade_type = bound(1, n.nade_type, NADE_TYPE_LAST);
+
+ setmodel(n, "models/weapons/v_ok_grenade.md3");
+ //setattachment(n, self, "bip01 l hand");
+ n.exteriormodeltoclient = self;
+ n.customizeentityforclient = nade_customize;
+ n.traileffectnum = particleeffectnum(Nade_TrailEffect(Nade_ProjectileFromID(n.nade_type, FALSE), self.team));
+ n.colormod = Nade_Color(n.nade_type);
+ n.realowner = self;
+ n.colormap = self.colormap;
+ n.glowmod = self.glowmod;
+ n.wait = time + autocvar_g_nades_nade_lifetime;
+ n.lifetime = time;
+ n.think = nade_beep;
+ n.nextthink = max(n.wait - 3, time);
+ n.projectiledeathtype = DEATH_NADE;
+
+ setmodel(fn, "models/weapons/h_ok_grenade.iqm");
+ setattachment(fn, self.weaponentity, "");
+ fn.realowner = fn.owner = self;
+ fn.colormod = Nade_Color(n.nade_type);
+ fn.colormap = self.colormap;
+ fn.glowmod = self.glowmod;
+ fn.think = SUB_Remove;
+ fn.nextthink = n.wait;
+
+ self.nade = n;
+ self.fake_nade = fn;
}
float CanThrowNade()
}
}
+void nades_Clear(entity player)
+{
+ if(player.nade)
+ remove(player.nade);
+ if(player.fake_nade)
+ remove(player.fake_nade);
+
+ player.nade = player.fake_nade = world;
+ player.nade_timer = 0;
+}
+
+MUTATOR_HOOKFUNCTION(nades_CheckThrow)
+{
+ if(MUTATOR_RETURNVALUE) { nades_CheckThrow(); }
+ return FALSE;
+}
+
MUTATOR_HOOKFUNCTION(nades_VehicleEnter)
{
- if(other.nade)
- toss_nade(other, '0 0 100', max(other.nade.wait, time + 0.05));
+ if(vh_player.nade)
+ toss_nade(vh_player, '0 0 100', max(vh_player.nade.wait, time + 0.05));
return FALSE;
}
MUTATOR_HOOKFUNCTION(nades_PlayerPreThink)
{
- float key_pressed = ((g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK)) ? self.button16 : self.BUTTON_HOOK);
+ if(!IS_PLAYER(self)) { return FALSE; }
+
+ float key_pressed = self.BUTTON_HOOK;
+ float time_score;
+ if(g_grappling_hook || client_hasweapon(self, WEP_HOOK, FALSE, FALSE) || (weaponsInMap & WEPSET_HOOK) || g_jetpack || self.items & IT_JETPACK)
+ key_pressed = self.button16; // if hook/jetpack is enabled, use an alternate key
+
if(self.nade)
- if(self.nade.wait - 0.1 <= time)
- toss_nade(self, '0 0 0', time + 0.05);
+ {
+ self.nade_timer = bound(0, (time - self.nade.lifetime) / autocvar_g_nades_nade_lifetime, 1);
+ //print(sprintf("%d %d\n", self.nade_timer, time - self.nade.lifetime));
+ makevectors(self.angles);
+ self.nade.velocity = self.velocity;
+
+ setorigin(self.nade, self.origin + self.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0);
+ self.nade.angles_y = self.angles_y;
+ }
+
+ if(self.nade)
+ if(self.nade.wait - 0.1 <= time)
+ toss_nade(self, '0 0 0', time + 0.05);
if(CanThrowNade())
if(self.nade_refire < time)
}
}
+ if(IS_PLAYER(self))
+ {
+ if ( autocvar_g_nades_bonus && autocvar_g_nades )
+ {
+ entity key;
+ float key_count = 0;
+ FOR_EACH_KH_KEY(key) if(key.owner == self) { ++key_count; }
+
+ if(self.flagcarried || self.ballcarried) // this player is important
+ 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)
+ {
+ self.nade_type = self.cvar_cl_nade_type;
+ self.pokenade_type = self.cvar_cl_pokenade_type;
+ }
+ else
+ {
+ self.nade_type = autocvar_g_nades_bonus_type;
+ self.pokenade_type = autocvar_g_nades_pokenade_monster_type;
+ }
+
+ self.nade_type = bound(1, self.nade_type, NADE_TYPE_LAST);
+
+ if(self.bonus_nade_score >= 0 && autocvar_g_nades_bonus_score_max)
+ nades_GiveBonus(self, time_score / autocvar_g_nades_bonus_score_max);
+ }
+ else
+ {
+ self.bonus_nades = self.bonus_nade_score = 0;
+ }
+ }
+
+ float n = 0;
+ entity o = world;
+ if(self.freezetag_frozen_timeout > 0 && time >= self.freezetag_frozen_timeout)
+ n = -1;
+ else
+ {
+ vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size;
+ n = 0;
+ FOR_EACH_PLAYER(other) if(self != other)
+ {
+ if(other.deadflag == DEAD_NO)
+ if(other.frozen == 0)
+ if(SAME_TEAM(other, self))
+ if(boxesoverlap(self.absmin - revive_extra_size, self.absmax + revive_extra_size, other.absmin, other.absmax))
+ {
+ if(!o)
+ o = other;
+ if(self.frozen == 1)
+ other.reviving = TRUE;
+ ++n;
+ }
+ }
+ }
+
+ if(n && self.frozen == 3) // OK, there is at least one teammate reviving us
+ {
+ self.revive_progress = bound(0, self.revive_progress + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1);
+ self.health = max(1, self.revive_progress * start_health);
+
+ if(self.revive_progress >= 1)
+ {
+ Unfreeze(self);
+
+ Send_Notification(NOTIF_ONE, self, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname);
+ Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, self.netname);
+ }
+
+ FOR_EACH_PLAYER(other) if(other.reviving)
+ {
+ other.revive_progress = self.revive_progress;
+ other.reviving = FALSE;
+ }
+ }
+
return FALSE;
}
else
self.nade_refire = time + autocvar_g_nades_nade_refire;
+ if(autocvar_g_nades_bonus_client_select)
+ self.nade_type = self.cvar_cl_nade_type;
+
+ self.nade_timer = 0;
+
+ if(self.nade_spawnloc)
+ {
+ setorigin(self, self.nade_spawnloc.origin);
+ self.nade_spawnloc.cnt -= 1;
+
+ if(self.nade_spawnloc.cnt <= 0)
+ {
+ remove(self.nade_spawnloc);
+ self.nade_spawnloc = world;
+ }
+ }
+
return FALSE;
}
MUTATOR_HOOKFUNCTION(nades_PlayerDies)
{
- if(self.nade)
- toss_nade(self, '0 0 100', max(self.nade.wait, time + 0.05));
+ if(frag_target.nade)
+ if(!frag_target.frozen || !autocvar_g_freezetag_revive_nade)
+ toss_nade(frag_target, '0 0 100', max(frag_target.nade.wait, time + 0.05));
+
+ float killcount_bonus = ((frag_attacker.killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * frag_attacker.killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor);
+
+ if(IS_PLAYER(frag_attacker))
+ {
+ if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target)
+ nades_RemoveBonus(frag_attacker);
+ else if(frag_target.flagcarried)
+ nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_medium);
+ else if(autocvar_g_nades_bonus_score_spree && frag_attacker.killcount > 1)
+ {
+ #define SPREE_ITEM(counta,countb,center,normal,gentle) \
+ case counta: { nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_spree); break; }
+ switch(frag_attacker.killcount)
+ {
+ KILL_SPREE_LIST
+ default: nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor); break;
+ }
+ #undef SPREE_ITEM
+ }
+ else
+ nades_GiveBonus(frag_attacker, killcount_bonus);
+ }
+
+ nades_RemoveBonus(frag_target);
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_PlayerDamage)
+{
+ if(frag_target.frozen)
+ if(autocvar_g_freezetag_revive_nade)
+ if(frag_attacker == frag_target)
+ if(frag_deathtype == DEATH_NADE)
+ if(time - frag_inflictor.toss_time <= 0.1)
+ {
+ Unfreeze(frag_target);
+ frag_target.health = autocvar_g_freezetag_revive_nade_health;
+ pointparticles(particleeffectnum("iceorglass"), frag_target.origin, '0 0 0', 3);
+ frag_damage = 0;
+ frag_force = '0 0 0';
+ Send_Notification(NOTIF_ALL, world, MSG_INFO, INFO_FREEZETAG_REVIVED_NADE, frag_target.netname);
+ Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_REVIVE_SELF);
+ }
+
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_MonsterDies)
+{
+ if(IS_PLAYER(frag_attacker))
+ if(DIFF_TEAM(frag_attacker, self))
+ if(!(self.spawnflags & MONSTERFLAG_SPAWNED))
+ nades_GiveBonus(frag_attacker, autocvar_g_nades_bonus_score_minor);
return FALSE;
}
MUTATOR_HOOKFUNCTION(nades_RemovePlayer)
{
- if(self.nade)
- remove(self.nade);
+ nades_Clear(self);
+ nades_RemoveBonus(self);
+ return FALSE;
+}
- if(self.fake_nade)
- remove(self.fake_nade);
+MUTATOR_HOOKFUNCTION(nades_SpectateCopy)
+{
+ self.nade_timer = other.nade_timer;
+ self.nade_type = other.nade_type;
+ self.pokenade_type = other.pokenade_type;
+ self.bonus_nades = other.bonus_nades;
+ self.bonus_nade_score = other.bonus_nade_score;
+ self.stat_healing_orb = other.stat_healing_orb;
+ self.stat_healing_orb_alpha = other.stat_healing_orb_alpha;
+ return FALSE;
+}
+
+MUTATOR_HOOKFUNCTION(nades_GetCvars)
+{
+ GetCvars_handleFloat(get_cvars_s, get_cvars_f, cvar_cl_nade_type, "cl_nade_type");
+ GetCvars_handleString(get_cvars_s, get_cvars_f, cvar_cl_pokenade_type, "cl_pokenade_type");
return FALSE;
}
return FALSE;
}
+void nades_Initialize()
+{
+ addstat(STAT_NADE_TIMER, AS_FLOAT, nade_timer);
+ addstat(STAT_NADE_BONUS, AS_FLOAT, bonus_nades);
+ addstat(STAT_NADE_BONUS_TYPE, AS_INT, nade_type);
+ addstat(STAT_NADE_BONUS_SCORE, AS_FLOAT, bonus_nade_score);
+ addstat(STAT_HEALING_ORB, AS_FLOAT, stat_healing_orb);
+ addstat(STAT_HEALING_ORB_ALPHA, AS_FLOAT, stat_healing_orb_alpha);
+
+ precache_model("models/ok_nade_counter/ok_nade_counter.md3");
+ precache_model("models/weapons/h_ok_grenade.iqm");
+ precache_model("models/weapons/v_ok_grenade.md3");
+ precache_model("models/ctf/shield.md3");
+
+ precache_sound("weapons/rocket_impact.wav");
+ precache_sound("weapons/grenade_bounce1.wav");
+ precache_sound("weapons/grenade_bounce2.wav");
+ precache_sound("weapons/grenade_bounce3.wav");
+ precache_sound("weapons/grenade_bounce4.wav");
+ precache_sound("weapons/grenade_bounce5.wav");
+ precache_sound("weapons/grenade_bounce6.wav");
+ precache_sound("overkill/grenadebip.ogg");
+}
+
MUTATOR_DEFINITION(mutator_nades)
{
+ MUTATOR_HOOK(ForbidThrowCurrentWeapon, nades_CheckThrow, CBC_ORDER_LAST);
MUTATOR_HOOK(VehicleEnter, nades_VehicleEnter, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerPreThink, nades_PlayerPreThink, CBC_ORDER_ANY);
MUTATOR_HOOK(PlayerSpawn, nades_PlayerSpawn, CBC_ORDER_ANY);
- MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_ANY);
+ MUTATOR_HOOK(PlayerDies, nades_PlayerDies, CBC_ORDER_LAST);
+ MUTATOR_HOOK(PlayerDamage_Calculate, nades_PlayerDamage, CBC_ORDER_ANY);
+ MUTATOR_HOOK(MonsterDies, nades_MonsterDies, CBC_ORDER_ANY);
MUTATOR_HOOK(MakePlayerObserver, nades_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(ClientDisconnect, nades_RemovePlayer, CBC_ORDER_ANY);
+ MUTATOR_HOOK(SpectateCopy, nades_SpectateCopy, CBC_ORDER_ANY);
+ MUTATOR_HOOK(GetCvars, nades_GetCvars, CBC_ORDER_ANY);
+ MUTATOR_HOOK(reset_map_global, nades_RemovePlayer, CBC_ORDER_ANY);
MUTATOR_HOOK(BuildMutatorsString, nades_BuildMutatorsString, CBC_ORDER_ANY);
MUTATOR_HOOK(BuildMutatorsPrettyString, nades_BuildMutatorsPrettyString, CBC_ORDER_ANY);
MUTATOR_ONADD
{
- precache_model("models/ok_nade_counter/ok_nade_counter.md3");
-
- precache_model("models/weapons/h_ok_grenade.iqm");
- precache_model("models/weapons/v_ok_grenade.md3");
- precache_sound("weapons/rocket_impact.wav");
- precache_sound("weapons/grenade_bounce1.wav");
- precache_sound("weapons/grenade_bounce2.wav");
- precache_sound("weapons/grenade_bounce3.wav");
- precache_sound("weapons/grenade_bounce4.wav");
- precache_sound("weapons/grenade_bounce5.wav");
- precache_sound("weapons/grenade_bounce6.wav");
- precache_sound("overkill/grenadebip.ogg");
+ nades_Initialize();
}
return FALSE;
MUTATOR_HOOKFUNCTION(msnt_PlayerSpawn)
{
+ // Note: when entering this, fixangle is already set.
if(autocvar_g_spawn_near_teammate_ignore_spawnpoint)
{
if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death)
if(team_mate.msnt_timer < time)
if(SAME_TEAM(self, team_mate))
if(time > team_mate.spawnshieldtime) // spawn shielding
- if(team_mate.freezetag_frozen == 0)
+ if(team_mate.frozen == 0)
if(team_mate != self)
{
tracebox(team_mate.origin, PL_MIN, PL_MAX, team_mate.origin - '0 0 100', MOVE_WORLDONLY, team_mate);
setorigin(self, trace_endpos);
self.angles = team_mate.angles;
self.angles_z = 0; // never spawn tilted even if the spot says to
- self.fixangle = TRUE; // turn this way immediately
team_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
return 0;
}
setorigin(self, best_spot);
self.angles = best_mate.angles;
self.angles_z = 0; // never spawn tilted even if the spot says to
- self.fixangle = TRUE; // turn this way immediately
best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay;
}
}
self.angles = vectoangles(spawn_spot.msnt_lookat.origin - self.origin);
self.angles_x = -self.angles_x;
self.angles_z = 0; // never spawn tilted even if the spot says to
- self.fixangle = TRUE; // turn this way immediately
/*
sprint(self, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n");
sprint(self, "distance: ", vtos(spawn_spot.msnt_lookat.origin - self.origin), "\n");
../warpzonelib/server.qh
../common/constants.qh
+ ../common/stats.qh
../common/teams.qh
../common/util.qh
+../common/nades.qh
+ ../common/buffs.qh
../common/test.qh
../common/counting.qh
../common/items.qh
../common/notifications.qh // must be after autocvars
../common/deathtypes.qh // must be after notifications
- mutators/base.qh
- mutators/mutators.qh
- mutators/gamemode_assault.qh
- mutators/gamemode_ca.qh
- mutators/gamemode_ctf.qh
- mutators/gamemode_domination.qh
- mutators/gamemode_keyhunt.qh // TODO fix this
- mutators/gamemode_keepaway.qh
- mutators/gamemode_nexball.qh
- mutators/gamemode_lms.qh
- mutators/gamemode_invasion.qh
- mutators/mutator_dodging.qh
- mutators/mutator_nades.qh
+ mutators/mutators_include.qh
//// tZork Turrets ////
tturrets/include/turrets_early.qh
spawnpoints.qh
+ mapvoting.qh
+
ipban.qh
race.qh
miscfunctions.qc
+ mutators/mutators.qc
+
waypointsprites.qc
bot/bot.qc
g_world.qc
g_casings.qc
+ mapvoting.qc
+
t_jumppads.qc
t_teleporters.qc
../common/items.qc
+../common/nades.qc
+ ../common/buffs.qc
accuracy.qc
../common/monsters/spawn.qc
- mutators/base.qc
- mutators/gamemode_assault.qc
- mutators/gamemode_ca.qc
- mutators/gamemode_ctf.qc
- mutators/gamemode_domination.qc
- mutators/gamemode_freezetag.qc
- mutators/gamemode_keyhunt.qc
- mutators/gamemode_keepaway.qc
- mutators/gamemode_nexball.qc
- mutators/gamemode_onslaught.qc
- mutators/gamemode_lms.qc
- mutators/gamemode_invasion.qc
- mutators/mutator_invincibleproj.qc
- mutators/mutator_new_toys.qc
- mutators/mutator_nix.qc
- mutators/mutator_dodging.qc
- mutators/mutator_rocketflying.qc
- mutators/mutator_vampire.qc
- mutators/mutator_spawn_near_teammate.qc
- mutators/mutator_physical_items.qc
- mutators/sandbox.qc
- mutators/mutator_superspec.qc
- mutators/mutator_minstagib.qc
- mutators/mutator_touchexplode.qc
- mutators/mutator_pinata.qc
- mutators/mutator_midair.qc
- mutators/mutator_bloodloss.qc
- mutators/mutator_random_gravity.qc
- mutators/mutator_multijump.qc
- mutators/mutator_melee_only.qc
- mutators/mutator_nades.qc
- mutators/mutator_campcheck.qc
+ mutators/mutators_include.qc
../warpzonelib/anglestransform.qc
../warpzonelib/mathlib.qc
if (self.watersound_finished < time)
{
self.watersound_finished = time + 0.5;
- sound (self, CH_PLAYER, "player/lava.wav", VOL_BASE, ATTEN_NORM);
+ sound (self, CH_PLAYER_SINGLE, "player/lava.wav", VOL_BASE, ATTEN_NORM);
}
Damage (self, world, world, autocvar_g_balance_contents_playerdamage_lava * autocvar_g_balance_contents_damagerate * self.waterlevel, DEATH_LAVA, self.origin, '0 0 0');
}
if (self.watersound_finished < time)
{
self.watersound_finished = time + 0.5;
- sound (self, CH_PLAYER, "player/slime.wav", VOL_BASE, ATTEN_NORM);
+ sound (self, CH_PLAYER_SINGLE, "player/slime.wav", VOL_BASE, ATTEN_NORM);
}
Damage (self, world, world, autocvar_g_balance_contents_playerdamage_slime * autocvar_g_balance_contents_damagerate * self.waterlevel, DEATH_SLIME, self.origin, '0 0 0');
}
{
// check for falling damage
float velocity_len = vlen(self.velocity);
- if(!self.hook.state && !(g_cts && !autocvar_g_cts_selfdamage))
+ if(!self.hook.state)
{
dm = vlen(self.oldvelocity) - velocity_len; // dm is now the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage.
if (self.deadflag)
float game_delay_last;
float RedirectionThink();
-entity SelectSpawnPoint (float anypoint);
void StartFrame (void)
{
execute_next_frame();
FOR_EACH_PLAYER(self)
self.porto_forbidden = max(0, self.porto_forbidden - 1);
+ anticheat_startframe();
+
MUTATOR_CALLHOOK(SV_StartFrame);
}
if(g_race)
{
-
if(autocvar_g_race_teams)
{
ActivateTeamplay();
qualifying_override = autocvar_g_race_qualifying_timelimit_override;
fraglimit_override = autocvar_g_race_laps_limit;
leadlimit_override = 0; // currently not supported by race
+
+ MUTATOR_ADD(gamemode_race);
}
if(g_cts)
g_race_qualifying = 1;
fraglimit_override = 0;
leadlimit_override = 0;
+ MUTATOR_ADD(gamemode_cts);
}
if(g_nexball)
if(g_invasion)
{
- timelimit_override = 0; // no timelimit in invasion, round based
- fraglimit_override = autocvar_g_invasion_round_limit;
+ fraglimit_override = autocvar_g_invasion_point_limit;
+ if(autocvar_g_invasion_teams >= 2)
+ {
+ ActivateTeamplay();
+ if(autocvar_g_invasion_team_spawns)
+ have_team_spawns = -1; // request team spawns
+ }
MUTATOR_ADD(gamemode_invasion);
}
else
g_race_qualifying = 0;
}
-
- if(g_invasion)
- {
- inv_maxrounds = cvar("fraglimit");
- cvar_set("fraglimit", "0");
- }
if(g_race || g_cts)
- {
- if(g_race_qualifying)
- independent_players = 1;
-
- ScoreRules_race();
- }
+ if(g_race_qualifying)
+ independent_players = 1;
InitializeEntity(world, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK);
}
if (g_grappling_hook)
s = strcat(s, "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n");
+ if (cvar("g_nades"))
+ s = strcat(s, "\n\n^3nades^8 are enabled, press 'g' to use them\n");
+
if(cache_lastmutatormsg != autocvar_g_mutatormsg)
{
if(cache_lastmutatormsg)
else
{
// cover anything else by treating it like tdm with no teams spawned
- if(g_race)
- dm = race_teams;
- else
- dm = 2;
+ dm = 2;
ret_float = dm;
MUTATOR_CALLHOOK(GetTeamCount);
// a player's mines shall explode if he disconnects or dies
// TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams?
- if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO)
- if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.freezetag_frozen)
++ if(!IS_PLAYER(self.realowner) || self.realowner.deadflag != DEAD_NO || self.realowner.frozen)
{
other = world;
self.projectiledeathtype |= HITTYPE_BOUNCE;
head = findradius(self.origin, autocvar_g_balance_minelayer_proximityradius);
while(head)
{
- if(IS_PLAYER(head) && head.deadflag == DEAD_NO)
- if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.freezetag_frozen)
++ if(IS_PLAYER(head) && head.deadflag == DEAD_NO && !head.frozen)
if(head != self.realowner && DIFF_TEAM(head, self.realowner)) // don't trigger for team mates
if(!self.mine_time)
{