- wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints
- wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache
- make
- - EXPECT=0a08daa9132d147f533776deda07643e
+ - EXPECT=3fb0c7a99263dd44e026804c12da2fa2
- HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg
| tee /dev/stderr
| grep '^:'
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 15
+set g_random_start_cells 25
+set g_random_start_plasma 25
set g_warmup_start_health 250 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 50 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
set g_start_ammo_cells 0
set g_start_ammo_plasma 0
set g_start_ammo_fuel 0
+set g_random_start_weapons_count 0
+set g_random_start_weapons "machinegun mortar electro crylink vortex hagar devastator"
+set g_random_start_shells 15
+set g_random_start_bullets 80
+set g_random_start_rockets 40
+set g_random_start_cells 30
+set g_random_start_plasma 30
set g_warmup_start_health 100 "starting values when being in warmup-stage"
set g_warmup_start_armor 100 "starting values when being in warmup-stage"
set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage"
#: qcsrc/common/notifications/all.inc:463
#, c-format
-msgid "^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"
+msgid "^BG%s%s^K1 got too close to ^BG%s^K1's rocket%s%s"
msgstr ""
#: qcsrc/common/notifications/all.inc:464
set sv_jumpspeedcap_max "" "upper bound on the baseline velocity of a jump; final velocity will be <= (jumpheight * max + jumpheight)"
set sv_jumpspeedcap_max_disable_on_ramps 0 "disable upper baseline velocity bound on ramps to preserve the old rampjump style"
set sv_track_canjump 0 "track if the player released the jump key between 2 jumps to decide if they are able to jump or not"
+set sv_jumpvelocity_crouch 0 "jump height while crouching, set to 0 to use regular jump height"
set sv_precacheplayermodels 1
set sv_precacheweapons 0
set sv_simple_items 1 "allow or forbid client use of simple items"
set sv_showspectators 1 "Show who's spectating who in the player info panel when client has cl_showspectators on. Shouldn't be used on competitive servers, also disable when watching a suspected cheater"
+
+set sv_damagetext 2 "<= 0: disabled, >= 1: visible to spectators, >= 2: visible to attacker, >= 3: all players see everyone's damage"
set sv_dodging_air_dodging 0
set sv_dodging_wall_dodging 0 "allow dodging off walls"
-set sv_dodging_delay 0.5 "determines how long a player has to wait to be able to dodge again after dodging"
+set sv_dodging_delay 0.6 "determines how long a player has to wait to be able to dodge again after dodging"
set sv_dodging_up_speed 200 "the jump velocity of the dodge"
-set sv_dodging_horiz_speed 400 "the horizontal velocity of the dodge"
-set sv_dodging_horiz_speed_frozen 200 "the horizontal velocity of the dodge while frozen"
+set sv_dodging_horiz_speed_min 200 "the lower bound of current velocity for force scaling"
+set sv_dodging_horiz_speed_max 1000 "the upper bound of current velocity for force scaling"
+set sv_dodging_horiz_force_slowest 400 "the horizontal velocity of the dodge when current velocity is <= sv_dodging_horiz_speed_min, values between min and max are linearly scaled"
+set sv_dodging_horiz_force_fastest 400 "the horizontal velocity of the dodge when current velocity is >= sv_dodging_horiz_speed_max, values between min and max are linearly scaled"
+set sv_dodging_horiz_force_frozen 200 "the horizontal velocity of the dodge while frozen"
set sv_dodging_ramp_time 0.1 "a ramp so that the horizontal part of the dodge is added smoothly (seconds)"
set sv_dodging_height_threshold 10 "the maximum height above ground where to allow dodging"
set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging"
set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent"
set sv_dodging_frozen 0 "allow dodging while frozen"
set sv_dodging_frozen_doubletap 0
-set sv_dodging_maxspeed 450 "maximum speed a player can be moving at before they dodge again"
+set sv_dodging_maxspeed 350 "maximum speed a player can be moving at to use the standard dodging from an (almost) standstill"
+set sv_dodging_air_maxspeed 450 "maximum speed a player can be moving at before they dodge again when air dodging is enabled"
// ===========
set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cell ammo packs"
set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs"
set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs"
+set g_instagib_invisibility_time 30 "Time of ivisibility powerup in seconds."
set g_instagib_invis_alpha 0.15
+set g_instagib_speed_time 30 "Time of speed powerup in seconds."
set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup"
set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib"
set g_instagib_blaster_keepdamage 0 "allow secondary fire to hurt players"
// running guns
// ==============
set g_running_guns 0 "... or wonder, till it drives you mad, what would have followed if you had."
+
+// ==================
+// dynamic handicap
+// ==================
+set g_dynamic_handicap 0 "Whether to enable dynamic handicap."
+set g_dynamic_handicap_scale 0.2 "The scale of the handicap. Larger values mean more penalties for strong players and more buffs for weak players."
+set g_dynamic_handicap_exponent 1 "The exponent used to calculate handicap. 1 means linear scale. Values more than 1 mean stronger non-linear handicap. Values less than 1 mean weaker non-linear handicap"
+set g_dynamic_handicap_min 0 "The minimum value of the handicap."
+set g_dynamic_handicap_max 0 "The maximum value of the handicap."
+
+// ===============
+// kick teamkiller
+// ===============
+set g_kick_teamkiller_rate 0 "Limit for teamkills per minute before the client gets dropped. 0 means that the teamkillers don't get kicked automatically"
+set g_kick_teamkiller_lower_limit 5 "Minimum number of teamkills before the teamkill rate is considered"
+
+// =====================
+// stale-move negation
+// =====================
+set g_smneg 0 "Stale-move negation: penalize repeated use of the same weapon"
+set g_smneg_bonus 1 "Stale-move negation: allow weapons to become stronger than their baseline"
+set g_smneg_bonus_asymptote 4 "Stale-move negation: damage = infinity at this bonus level"
+set g_smneg_cooldown_factor 0.25 "Stale-move negation: penalty cooldown factor"
+
+// ==============
+// random items
+// ==============
+set g_random_items 0 "Whether to enable random items."
+set g_random_loot 0 "Whether to enable random loot."
+exec randomitems-xonotic.cfg
set g_physics_xonotic_airspeedlimit_nonqw 900
set g_physics_xonotic_maxspeed 360
set g_physics_xonotic_jumpvelocity 260
+set g_physics_xonotic_jumpvelocity_crouch 0
set g_physics_xonotic_maxairstrafespeed 100
set g_physics_xonotic_maxairspeed 360
set g_physics_xonotic_airstrafeaccelerate 18
set g_physics_nexuiz_airspeedlimit_nonqw 0
set g_physics_nexuiz_maxspeed 400
set g_physics_nexuiz_jumpvelocity 300 "333 to match xonotic physics"
+set g_physics_nexuiz_jumpvelocity_crouch 0 "333 to match xonotic physics"
set g_physics_nexuiz_maxairstrafespeed 0
set g_physics_nexuiz_maxairspeed 220
set g_physics_nexuiz_airstrafeaccelerate 0
set g_physics_quake_airspeedlimit_nonqw 0
set g_physics_quake_maxspeed 320
set g_physics_quake_jumpvelocity 270
+set g_physics_quake_jumpvelocity_crouch 0
set g_physics_quake_maxairstrafespeed 0
set g_physics_quake_maxairspeed 30
set g_physics_quake_airstrafeaccelerate 0
set g_physics_warsow_airspeedlimit_nonqw 0
set g_physics_warsow_maxspeed 320
set g_physics_warsow_jumpvelocity 280
+set g_physics_warsow_jumpvelocity_crouch 0
set g_physics_warsow_maxairstrafespeed 30
set g_physics_warsow_maxairspeed 320
set g_physics_warsow_airstrafeaccelerate 70
set g_physics_defrag_airspeedlimit_nonqw 0
set g_physics_defrag_maxspeed 320
set g_physics_defrag_jumpvelocity 270
+set g_physics_defrag_jumpvelocity_crouch 0
set g_physics_defrag_maxairstrafespeed 30
set g_physics_defrag_maxairspeed 320
set g_physics_defrag_airstrafeaccelerate 70
set g_physics_quake3_airspeedlimit_nonqw 0
set g_physics_quake3_maxspeed 320
set g_physics_quake3_jumpvelocity 270
+set g_physics_quake3_jumpvelocity_crouch 0
set g_physics_quake3_maxairstrafespeed 0
set g_physics_quake3_maxairspeed 320
set g_physics_quake3_airstrafeaccelerate 0
set g_physics_vecxis_airspeedlimit_nonqw 0
set g_physics_vecxis_maxspeed 400
set g_physics_vecxis_jumpvelocity 300 "333 to match xonotic physics"
+set g_physics_vecxis_jumpvelocity_crouch 0 "333 to match xonotic physics"
set g_physics_vecxis_maxairstrafespeed 0
set g_physics_vecxis_maxairspeed 220
set g_physics_vecxis_airstrafeaccelerate 0
set g_physics_quake2_airspeedlimit_nonqw 0
set g_physics_quake2_maxspeed 300
set g_physics_quake2_jumpvelocity 270
+set g_physics_quake2_jumpvelocity_crouch 0
set g_physics_quake2_maxairstrafespeed 0
set g_physics_quake2_maxairspeed 300
set g_physics_quake2_airstrafeaccelerate 0
set g_physics_bones_airspeedlimit_nonqw 0
set g_physics_bones_maxspeed 320
set g_physics_bones_jumpvelocity 270
+set g_physics_bones_jumpvelocity_crouch 0
set g_physics_bones_maxairstrafespeed 30
set g_physics_bones_maxairspeed 320
set g_physics_bones_airstrafeaccelerate 70
set g_physics_overkill_airspeedlimit_nonqw 900
set g_physics_overkill_maxspeed 400
set g_physics_overkill_jumpvelocity 260
+set g_physics_overkill_jumpvelocity_crouch 0
set g_physics_overkill_maxairstrafespeed 100
set g_physics_overkill_maxairspeed 360
set g_physics_overkill_airstrafeaccelerate 24
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 4
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0 // breaks strafing?
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.65
// actually, what we want is 266.6666 for 180bpm
// but 260 takes same amount of frames and is nicer to mappers
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0 // breaks strafing?
// actually, what we want is 266.6666 for 180bpm
// but 260 takes same amount of frames and is nicer to mappers
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1 // div0 says no! lol
sv_stepheight 26
sv_jumpvelocity 304
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0 // pain in the ass to tweak without screwing up the strafing
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 310
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.35
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.3
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.35
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.35
// actually, what we want is 266.6666 for 180bpm
// but 260 takes same amount of frames and is nicer to mappers
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
// this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
// the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 4
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.8
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.5
edgefriction 1
sv_stepheight 34
sv_jumpvelocity 300
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0.3
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 10
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 10
sv_waterfriction 1
sv_airaccel_sideways_friction 0
edgefriction 1
sv_stepheight 18
sv_jumpvelocity 280
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 10
sv_waterfriction 1
sv_airaccel_sideways_friction 0
// this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
// the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
// actually, what we want is 266.6666 for 180bpm
// but 260 takes same amount of frames and is nicer to mappers
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
// this is smaller than 112 qu, so a 112 qu high corridor (7 of 8 grid units in
// the 16 grid, and the 8th unit used for wall/floor) just lets a player jump!
sv_jumpvelocity 260
+sv_jumpvelocity_crouch 0
sv_wateraccelerate -1
sv_waterfriction -1
sv_airaccel_sideways_friction 0
sv_stepheight 26
// CPMA: 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 4
sv_waterfriction 1
sv_airaccel_sideways_friction 0
sv_stepheight 26
// CPMA: 18
sv_jumpvelocity 270
+sv_jumpvelocity_crouch 0
sv_wateraccelerate 4
sv_waterfriction 1
sv_airaccel_sideways_friction 0
void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time); // TODO: mutator
-void DrawAmmoItem(vector myPos, vector mySize, .int ammoType, bool isCurrent, bool isInfinite)
+void DrawAmmoItem(vector myPos, vector mySize, int ammoType, bool isCurrent, bool isInfinite)
{
TC(bool, isCurrent); TC(bool, isInfinite);
- if(ammoType == ammo_none)
+ if(ammoType == RESOURCE_NONE)
return;
// Initialize variables
int ammo;
if(autocvar__hud_configure)
{
- isCurrent = (ammoType == ammo_rockets); // Rockets always current
+ isCurrent = (ammoType == RESOURCE_ROCKETS); // Rockets always current
ammo = 60;
}
else
{
if(autocvar__hud_configure)
{
- DrawAmmoItem(pos, ammo_size, ammo_rockets, true, false);
+ DrawAmmoItem(pos, ammo_size, RESOURCE_ROCKETS, true, false);
}
else
{
DrawAmmoItem(
pos,
ammo_size,
- wep.ammo_field,
+ wep.ammo_type,
true,
infinite_ammo
);
}
else
{
- .int ammotype;
+ int ammotype;
row = column = 0;
for(i = 0; i < AMMO_COUNT; ++i)
{
- ammotype = GetAmmoFieldFromNum(i);
+ ammotype = GetAmmoTypeFromNum(i);
DrawAmmoItem(
pos + vec2(column * (ammo_size.x + offset.x), row * (ammo_size.y + offset.y)),
ammo_size,
ammotype,
- (wep.ammo_field == ammotype),
+ (wep.ammo_type == ammotype),
infinite_ammo
);
const int acc_decimals = 2;
if(time > physics_update_time)
{
+ discrete_acceleration = acceleration;
// workaround for ftos_decimals returning a negative 0
if(discrete_acceleration > -1 / (10 ** acc_decimals) && discrete_acceleration < 0)
discrete_acceleration = 0;
- discrete_acceleration = acceleration;
discrete_speed = speed;
physics_update_time += autocvar_hud_panel_physics_update_interval;
if(physics_update_time < time)
case "kd": return CTX(_("SCO^k/d"));
case "kdr": return CTX(_("SCO^kdr"));
case "kills": return CTX(_("SCO^kills"));
+ case "teamkills": return CTX(_("SCO^teamkills"));
case "laps": return CTX(_("SCO^laps"));
case "lives": return CTX(_("SCO^lives"));
case "losses": return CTX(_("SCO^losses"));
LOG_INFO(_("^3deaths^7 Number of deaths"));
LOG_INFO(_("^3suicides^7 Number of suicides"));
LOG_INFO(_("^3frags^7 kills - suicides"));
+ LOG_INFO(_("^3teamkills^7 Number of teamkills"));
LOG_INFO(_("^3kd^7 The kill-death ratio"));
LOG_INFO(_("^3dmg^7 The total damage done"));
LOG_INFO(_("^3dmgtaken^7 The total damage taken"));
" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \
" -teams,lms/deaths +ft,tdm/deaths" \
" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \
+" +teams/teamkills"\
" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \
" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \
" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \
}
// draw ammo status bar
- if(!infinite_ammo && autocvar_hud_panel_weapons_ammo && (it.ammo_field != ammo_none))
+ if(!infinite_ammo && autocvar_hud_panel_weapons_ammo && (it.ammo_type != RESOURCE_NONE))
{
float ammo_full;
- a = getstati(GetAmmoStat(it.ammo_field)); // how much ammo do we have?
+ a = getstati(GetAmmoStat(it.ammo_type)); // how much ammo do we have?
if(a > 0)
{
- switch(it.ammo_field)
+ switch (it.ammo_type)
{
- case ammo_shells: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
- case ammo_nails: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
- case ammo_rockets: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
- case ammo_cells: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
- case ammo_plasma: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break;
- case ammo_fuel: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
+ case RESOURCE_SHELLS: ammo_full = autocvar_hud_panel_weapons_ammo_full_shells; break;
+ case RESOURCE_BULLETS: ammo_full = autocvar_hud_panel_weapons_ammo_full_nails; break;
+ case RESOURCE_ROCKETS: ammo_full = autocvar_hud_panel_weapons_ammo_full_rockets; break;
+ case RESOURCE_CELLS: ammo_full = autocvar_hud_panel_weapons_ammo_full_cells; break;
+ case RESOURCE_PLASMA: ammo_full = autocvar_hud_panel_weapons_ammo_full_plasma; break;
+ case RESOURCE_FUEL: ammo_full = autocvar_hud_panel_weapons_ammo_full_fuel; break;
default: ammo_full = 60;
}
// RegisterPlayer(o);
//playerchecker will do this for us later, if it has not already done so
- int sf, lf;
- sf = ReadShort();
- lf = ReadShort();
+ int sf = ReadShort();
+ int lf = ReadShort();
FOREACH(Scores, true, {
- int p = 1 << (i % 16);
+ int p = 1 << (i % 16);
if (sf & p)
{
if (lf & p)
else
o.(scores(it)) = ReadChar();
}
- });
+ });
return = true;
{
make_pure(this);
int i;
- entity o;
this.team = ReadByte();
- o = this.owner = GetTeam(this.team, true); // these team numbers can always be trusted
+ entity o = this.owner = GetTeam(this.team, true); // these team numbers can always be trusted
- int sf, lf;
#if MAX_TEAMSCORE <= 8
- sf = ReadByte();
- lf = ReadByte();
+ int sf = ReadByte();
+ int lf = ReadByte();
#else
- sf = ReadShort();
- lf = ReadShort();
+ int sf = ReadShort();
+ int lf = ReadShort();
#endif
- int p;
- for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)
- if(sf & p)
+ for(i = 0; i < MAX_TEAMSCORE; ++i)
+ if(sf & BIT(i))
{
- if(lf & p)
+ if(lf & BIT(i))
o.(teamscores(i)) = ReadInt24_t();
else
o.(teamscores(i)) = ReadChar();
make_pure(this);
float newspectatee_status;
- int f = ReadByte();
+ int f = ReadByte();
scoreboard_showscores_force = (f & BIT(0));
NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew)
{
make_pure(this);
- int i, j, b, f;
+ int i, j, b, f;
- int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS
+ int nags = ReadByte(); // NAGS NAGS NAGS NAGS NAGS NAGS NADZ NAGS NAGS NAGS
if(!(nags & BIT(2)))
{
for(i = 1; i <= maxclients; i += 8)
{
f = ReadByte();
- for(j = i-1, b = 1; b < 256; b *= 2, ++j)
+ for(j = i-1, b = BIT(0); b < BIT(8); b <<= 1, ++j)
if (!(f & b))
if(playerslots[j])
playerslots[j].ready = 0;
NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew)
{
make_pure(this);
- int i, j, b, f;
-
- int sf = ReadByte();
- if(sf & 1)
- {
- for(j = 0; j < maxclients; ++j)
- if(playerslots[j])
+ int sf = 0;
+ serialize(byte, 0, sf);
+ if (sf & 1) {
+ for (int j = 0; j < maxclients; ++j) {
+ if (playerslots[j]) {
playerslots[j].eliminated = 1;
- for(i = 1; i <= maxclients; i += 8)
- {
- f = ReadByte();
- for(j = i-1, b = 1; b < 256; b *= 2, ++j)
- if (!(f & b))
- if(playerslots[j])
- playerslots[j].eliminated = 0;
+ }
+ }
+ for (int i = 1; i <= maxclients; i += 8) {
+ int f = 0;
+ serialize(byte, 0, f);
+ for (int b = 0; b < 8; ++b) {
+ if (f & BIT(b)) continue;
+ int j = i - 1 + b;
+ if (playerslots[j]) {
+ playerslots[j].eliminated = 0;
+ }
+ }
}
}
return true;
NET_HANDLE(ENT_CLIENT_ACCURACY, bool isnew)
{
make_pure(this);
- int sf = ReadInt24_t();
+ int sf = ReadInt24_t();
if (sf == 0) {
for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w)
weapon_accuracy[w] = -1;
int f = 1;
for (int w = 0; w <= WEP_LAST - WEP_FIRST; ++w) {
if (sf & f) {
- int b = ReadByte();
+ int b = ReadByte();
if (b == 0)
weapon_accuracy[w] = -1;
else if (b == 255)
int i;
if ( mv_num_maps < 24 )
{
- int mask, power;
+ int mask;
if(mv_num_maps < 8)
mask = ReadByte();
else if(mv_num_maps < 16)
else
mask = ReadLong();
- for(i = 0, power = 1; i < mv_num_maps; ++i, power *= 2)
+ for(i = 0; i < mv_num_maps; ++i)
{
- if ( mask & power )
+ if (mask & BIT(i))
mv_flags[i] |= GTV_AVAILABLE;
else
mv_flags[i] &= ~GTV_AVAILABLE;
#include "mutators/events.qh"
#include <common/animdecide.qh>
+#include <common/deathtypes/all.qh>
#include <common/ent_cs.qh>
#include <common/anim.qh>
#include <common/constants.qh>
case 2: // crosshair_color_by_health
{
- float hp = health_stat;
+ vector v = healtharmor_maxdamage(health_stat, STAT(ARMOR), armorblockpercent, DEATH_WEAPON.m_id);
+ float hp = floor(v.x + 1);
//x = red
//y = green
/** True when private information such as origin is available */
.bool m_entcs_private;
+
/** True when origin is available */
+// FIXME: it seems sometimes this is false when observing even though observers should be able to know about all players
+// easily reproducible on heart_v2 or The_Yard with bots - might be because they lack waypoints and bots stand still
+// it has happened in matches with players and no bots but much more rarely
.bool has_origin;
+
/** True when a recent server sent origin has been received */
.bool has_sv_origin;
{
entity item = M_ARGV(0, entity);
- if(item.classname == "droppedweapon")
+ if(Item_IsLoot(item))
if(item.weapon == WEP_NEXBALL.m_id)
return true;
#pragma once
#include "all.qh"
-#include "item/pickup.qh"
CLASS(Inventory, Object)
/** Stores counts of items, the id being the index */
if (!(minorBits & BIT(j))) { \
continue; \
} \
- const GameItem it = Items_from(Inventory_groups_minor * maj + j); \
+ const entity it = Items_from(Inventory_groups_minor * maj + j); \
WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \
} \
} \
#pragma once
-#include <common/t_items.qh>
#ifdef GAMEQC
+#include <common/models/all.qh>
#include <common/sounds/all.qh>
#include <common/sounds/all.inc>
+#include <common/stats.qh>
#endif
const int IT_UNLIMITED_WEAPON_AMMO = BIT(0); // when this bit is set, using a weapon does not reduce ammo. Checkpoints can give this powerup.
#ifdef SVQC
.float strength_finished = _STAT(STRENGTH_FINISHED);
.float invincible_finished = _STAT(INVINCIBLE_FINISHED);
+
+#define SPAWNFUNC_ITEM(name, item) \
+ spawnfunc(name) { StartItem(this, item); }
+
+#else
+
+#define SPAWNFUNC_ITEM(name, item)
+
#endif
+enum
+{
+ ITEM_FLAG_NORMAL = BIT(0), ///< Item is usable during normal gameplay.
+ ITEM_FLAG_INSTAGIB = BIT(1), ///< Item is usable in instagib.
+ ITEM_FLAG_OVERKILL = BIT(2), ///< Item is usable in overkill.
+ ITEM_FLAG_MUTATORBLOCKED = BIT(3)
+};
+
#define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__)
CLASS(GameItem, Object)
ATTRIB(GameItem, m_id, int, 0);
+ /** the canonical spawnfunc name */
+ ATTRIB(GameItem, m_canonical_spawnfunc, string);
+ METHOD(GameItem, m_spawnfunc_hookreplace, GameItem(GameItem this, entity e)) { return this; }
ATTRIB(GameItem, m_name, string);
ATTRIB(GameItem, m_icon, string);
ATTRIB(GameItem, m_color, vector, '1 1 1');
#include "ammo.qh"
+
+#ifdef SVQC
+
+METHOD(Bullets, m_spawnfunc_hookreplace, GameItem(Bullets this, entity e))
+{
+ if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
+ {
+ return ITEM_Shells;
+ }
+ return this;
+}
+
+METHOD(Shells, m_spawnfunc_hookreplace, GameItem(Shells this, entity e))
+{
+ if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
+ {
+ return ITEM_Bullets;
+ }
+ return this;
+}
+
+#endif
#pragma once
#include "pickup.qh"
+#ifdef SVQC
+ #include <common/t_items.qh>
+#endif
+
+.int ammo_none;
+.int ammo_shells;
+.int ammo_nails;
+.int ammo_rockets;
+.int ammo_cells;
+#ifdef SVQC
+.int ammo_plasma = _STAT(PLASMA);
+.int ammo_fuel = _STAT(FUEL);
+#else
+.int ammo_plasma;
+.int ammo_fuel;
+#endif
+
#ifdef SVQC
PROPERTY(float, g_pickup_ammo_anyway);
#endif
#endif
ENDCLASS(Ammo)
-#ifdef SVQC
- #include <common/t_items.qh>
-#endif
#ifdef GAMEQC
MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl"));
item.ammo_nails = g_pickup_nails;
}
#endif
-REGISTER_ITEM(Bullets, Ammo) {
+
+CLASS(Bullets, Ammo)
+ENDCLASS(Bullets)
+
+REGISTER_ITEM(Bullets, Bullets) {
+ this.m_canonical_spawnfunc = "item_bullets";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Bullets_ITEM;
#endif
this.netname = "bullets";
#endif
}
+SPAWNFUNC_ITEM(item_bullets, ITEM_Bullets)
+
#ifdef GAMEQC
MODEL(Cells_ITEM, Item_Model("a_cells.md3"));
#endif
}
#endif
REGISTER_ITEM(Cells, Ammo) {
+ this.m_canonical_spawnfunc = "item_cells";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Cells_ITEM;
#endif
this.netname = "cells";
#endif
}
+SPAWNFUNC_ITEM(item_cells, ITEM_Cells)
+
#ifdef GAMEQC
MODEL(Plasma_ITEM, Item_Model("a_cells.md3"));
#endif
}
#endif
REGISTER_ITEM(Plasma, Ammo) {
+ this.m_canonical_spawnfunc = "item_plasma";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Plasma_ITEM;
#endif
this.netname = "plasma";
#endif
}
+SPAWNFUNC_ITEM(item_plasma, ITEM_Plasma)
+
#ifdef GAMEQC
MODEL(Rockets_ITEM, Item_Model("a_rockets.md3"));
#endif
}
#endif
REGISTER_ITEM(Rockets, Ammo) {
+ this.m_canonical_spawnfunc = "item_rockets";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Rockets_ITEM;
#endif
this.netname = "rockets";
#endif
}
+SPAWNFUNC_ITEM(item_rockets, ITEM_Rockets)
+
#ifdef GAMEQC
MODEL(Shells_ITEM, Item_Model("a_shells.md3"));
#endif
item.ammo_shells = g_pickup_shells;
}
#endif
-REGISTER_ITEM(Shells, Ammo) {
+
+CLASS(Shells, Ammo)
+ENDCLASS(Shells)
+
+REGISTER_ITEM(Shells, Shells) {
+ this.m_canonical_spawnfunc = "item_shells";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Shells_ITEM;
#endif
this.netname = "shells";
this.m_iteminit = ammo_shells_init;
#endif
}
+
+SPAWNFUNC_ITEM(item_shells, ITEM_Shells)
#endif
REGISTER_ITEM(ArmorSmall, Armor) {
+ this.m_canonical_spawnfunc = "item_armor_small";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
this.m_model = MDL_ArmorSmall_ITEM;
this.m_sound = SND_ArmorSmall;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_armor_small, ITEM_ArmorSmall)
+
#ifdef GAMEQC
MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3"));
SOUND(ArmorMedium, Item_Sound("armor10"));
#endif
REGISTER_ITEM(ArmorMedium, Armor) {
+ this.m_canonical_spawnfunc = "item_armor_medium";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
this.m_model = MDL_ArmorMedium_ITEM;
this.m_sound = SND_ArmorMedium;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_armor_medium, ITEM_ArmorMedium)
+
#ifdef GAMEQC
MODEL(ArmorBig_ITEM, Item_Model("item_armor_big.md3"));
SOUND(ArmorBig, Item_Sound("armor17_5"));
#endif
REGISTER_ITEM(ArmorBig, Armor) {
+ this.m_canonical_spawnfunc = "item_armor_big";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
this.m_model = MDL_ArmorBig_ITEM;
this.m_sound = SND_ArmorBig;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_armor_big, ITEM_ArmorBig)
+
#ifdef GAMEQC
MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3"));
SOUND(ArmorMega, Item_Sound("armor25"));
#endif
REGISTER_ITEM(ArmorMega, Armor) {
+ this.m_canonical_spawnfunc = "item_armor_mega";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
this.m_model = MDL_ArmorMega_ITEM;
this.m_sound = SND_ArmorMega;
#endif
this.m_iteminit = item_armormega_init;
#endif
}
+
+SPAWNFUNC_ITEM(item_armor_mega, ITEM_ArmorMega)
#endif
REGISTER_ITEM(HealthSmall, Health) {
+ this.m_canonical_spawnfunc = "item_health_small";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_HealthSmall_ITEM;
this.m_sound = SND_HealthSmall;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_health_small, ITEM_HealthSmall)
+
#ifdef GAMEQC
MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3"));
SOUND(HealthMedium, Item_Sound("mediumhealth"));
#endif
REGISTER_ITEM(HealthMedium, Health) {
+ this.m_canonical_spawnfunc = "item_health_medium";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_HealthMedium_ITEM;
this.m_sound = SND_HealthMedium;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_health_medium, ITEM_HealthMedium)
+
#ifdef GAMEQC
MODEL(HealthBig_ITEM, Item_Model("g_h50.md3"));
SOUND(HealthBig, Item_Sound("mediumhealth"));
#endif
REGISTER_ITEM(HealthBig, Health) {
+ this.m_canonical_spawnfunc = "item_health_big";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_HealthBig_ITEM;
this.m_sound = SND_HealthBig;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_health_big, ITEM_HealthBig)
+
#ifdef GAMEQC
MODEL(HealthMega_ITEM, Item_Model("g_h100.md3"));
SOUND(HealthMega, Item_Sound("megahealth"));
#endif
REGISTER_ITEM(HealthMega, Health) {
+ this.m_canonical_spawnfunc = "item_health_mega";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL;
this.m_model = MDL_HealthMega_ITEM;
this.m_sound = SND_HealthMega;
#endif
this.m_iteminit = item_healthmega_init;
#endif
}
+
+SPAWNFUNC_ITEM(item_health_mega, ITEM_HealthMega)
#include "jetpack.qh"
+
+#ifdef SVQC
+
+METHOD(Jetpack, m_spawnfunc_hookreplace, GameItem(Jetpack this, entity e))
+{
+ if(start_items & ITEM_Jetpack.m_itemid)
+ {
+ return ITEM_JetpackFuel;
+ }
+ return this;
+}
+
+METHOD(JetpackRegen, m_spawnfunc_hookreplace, GameItem(JetpackRegen this, entity e))
+{
+ if (start_items & ITEM_JetpackRegen.m_itemid)
+ {
+ return ITEM_JetpackFuel;
+ }
+ return this;
+}
+
+#endif
item.ammo_fuel = g_pickup_fuel_jetpack;
}
#endif
+
+CLASS(Jetpack, Powerup)
+ENDCLASS(Jetpack)
+
REGISTER_ITEM(Jetpack, Powerup) {
+ this.m_canonical_spawnfunc = "item_jetpack";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Jetpack_ITEM;
this.m_itemid = IT_JETPACK;
#endif
#endif
}
+SPAWNFUNC_ITEM(item_jetpack, ITEM_Jetpack)
+
#ifdef GAMEQC
MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3"));
#endif
}
#endif
REGISTER_ITEM(JetpackFuel, Ammo) {
+ this.m_canonical_spawnfunc = "item_fuel";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_JetpackFuel_ITEM;
#endif
this.netname = "fuel";
#endif
}
+SPAWNFUNC_ITEM(item_fuel, ITEM_JetpackFuel)
+
#ifdef GAMEQC
MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3"));
#endif
-REGISTER_ITEM(JetpackRegen, Powerup) {
+CLASS(JetpackRegen, Powerup)
+ENDCLASS(JetpackRegen)
+
+REGISTER_ITEM(JetpackRegen, JetpackRegen) {
+ this.m_canonical_spawnfunc = "item_fuel_regen";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_JetpackRegen_ITEM;
#endif
this.netname = "fuel_regen";
this.m_pickupevalfunc = ammo_pickupevalfunc;
#endif
}
+
+SPAWNFUNC_ITEM(item_fuel_regen, ITEM_JetpackRegen)
#include "pickup.qh"
+#include <common/items/inventory.qh>
#ifdef SVQC
bool ITEM_HANDLE(Pickup, entity this, entity item, entity player) {
return this.giveTo(this, item, player);
}
+
+METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player))
+{
+ TC(Pickup, this);
+ bool b = Item_GiveTo(item, player);
+ if (b) {
+ LOG_DEBUGF("entity %i picked up %s", player, this.m_name);
+ player.inventory.inv_items[this.m_id]++;
+ Inventory_update(player);
+ }
+ return b;
+}
+
#endif
PROPERTY(float, g_pickup_respawntimejitter_powerup)
#endif
-#include <common/items/inventory.qh>
#include <common/items/item.qh>
-#include <common/t_items.qh>
-
-#ifdef GAMEQC
-#include <common/models/all.qh>
-#include <common/sounds/all.qh>
-#include <common/sounds/all.inc>
-#endif
CLASS(Pickup, GameItem)
#ifdef GAMEQC
ATTRIB(Pickup, m_pickupanyway, float());
ATTRIB(Pickup, m_iteminit, void(entity item));
float Item_GiveTo(entity item, entity player);
- METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player))
- {
- TC(Pickup, this);
- bool b = Item_GiveTo(item, player);
- if (b) {
- LOG_DEBUGF("entity %i picked up %s", player, this.m_name);
- player.inventory.inv_items[this.m_id]++;
- Inventory_update(player);
- }
- return b;
- }
+ METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player));
bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player);
#endif
ENDCLASS(Pickup)
}
#endif
REGISTER_ITEM(Strength, Powerup) {
+ this.m_canonical_spawnfunc = "item_strength";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Strength_ITEM;
this.m_sound = SND_Strength;
this.m_glow = true;
#endif
}
+SPAWNFUNC_ITEM(item_strength, ITEM_Strength)
+
#ifdef GAMEQC
MODEL(Shield_ITEM, Item_Model("g_invincible.md3"));
SOUND(Shield, Item_Sound("powerup_shield"));
}
#endif
REGISTER_ITEM(Shield, Powerup) {
+ this.m_canonical_spawnfunc = "item_shield";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_NORMAL;
this.m_model = MDL_Shield_ITEM;
this.m_sound = SND_Shield;
this.m_glow = true;
this.m_iteminit = powerup_shield_init;
#endif
}
+
+SPAWNFUNC_ITEM(item_shield, ITEM_Shield)
+SPAWNFUNC_ITEM(item_invincible, ITEM_Shield)
return;
vector org = CENTER_OR_VIEWOFS(this);
- entity e = new(droppedweapon); // use weapon handling to remove it on touch
+ entity e = spawn();
+ Item_SetLoot(e, true);
e.spawnfunc_checked = true;
e.monster_loot = this.monster_loot;
e.noalign = true;
StartItem(e, e.monster_loot);
e.gravity = 1;
- set_movetype(e, MOVETYPE_TOSS);
- e.reset = SUB_Remove;
setorigin(e, org);
e.velocity = randomvec() * 175 + '0 0 325';
e.item_spawnshieldtime = time + 0.7;
#include <common/mutators/mutator/damagetext/_mod.inc>
#include <common/mutators/mutator/dodging/_mod.inc>
#include <common/mutators/mutator/doublejump/_mod.inc>
+#include <common/mutators/mutator/dynamic_handicap/_mod.inc>
#include <common/mutators/mutator/globalforces/_mod.inc>
#include <common/mutators/mutator/hook/_mod.inc>
#include <common/mutators/mutator/instagib/_mod.inc>
#include <common/mutators/mutator/invincibleproj/_mod.inc>
#include <common/mutators/mutator/itemstime/_mod.inc>
+#include <common/mutators/mutator/kick_teamkiller/_mod.inc>
#include <common/mutators/mutator/melee_only/_mod.inc>
#include <common/mutators/mutator/midair/_mod.inc>
#include <common/mutators/mutator/multijump/_mod.inc>
#include <common/mutators/mutator/physical_items/_mod.inc>
#include <common/mutators/mutator/pinata/_mod.inc>
#include <common/mutators/mutator/random_gravity/_mod.inc>
+#include <common/mutators/mutator/random_items/_mod.inc>
#include <common/mutators/mutator/rocketflying/_mod.inc>
#include <common/mutators/mutator/rocketminsta/_mod.inc>
#include <common/mutators/mutator/running_guns/_mod.inc>
#include <common/mutators/mutator/sandbox/_mod.inc>
#include <common/mutators/mutator/spawn_near_teammate/_mod.inc>
+#include <common/mutators/mutator/stale_move_negation/_mod.inc>
#include <common/mutators/mutator/superspec/_mod.inc>
#include <common/mutators/mutator/touchexplode/_mod.inc>
#include <common/mutators/mutator/vampire/_mod.inc>
#include <common/mutators/mutator/damagetext/_mod.qh>
#include <common/mutators/mutator/dodging/_mod.qh>
#include <common/mutators/mutator/doublejump/_mod.qh>
+#include <common/mutators/mutator/dynamic_handicap/_mod.qh>
#include <common/mutators/mutator/globalforces/_mod.qh>
#include <common/mutators/mutator/hook/_mod.qh>
#include <common/mutators/mutator/instagib/_mod.qh>
#include <common/mutators/mutator/invincibleproj/_mod.qh>
#include <common/mutators/mutator/itemstime/_mod.qh>
+#include <common/mutators/mutator/kick_teamkiller/_mod.qh>
#include <common/mutators/mutator/melee_only/_mod.qh>
#include <common/mutators/mutator/midair/_mod.qh>
#include <common/mutators/mutator/multijump/_mod.qh>
#include <common/mutators/mutator/physical_items/_mod.qh>
#include <common/mutators/mutator/pinata/_mod.qh>
#include <common/mutators/mutator/random_gravity/_mod.qh>
+#include <common/mutators/mutator/random_items/_mod.qh>
#include <common/mutators/mutator/rocketflying/_mod.qh>
#include <common/mutators/mutator/rocketminsta/_mod.qh>
#include <common/mutators/mutator/running_guns/_mod.qh>
#include <common/mutators/mutator/sandbox/_mod.qh>
#include <common/mutators/mutator/spawn_near_teammate/_mod.qh>
+#include <common/mutators/mutator/stale_move_negation/_mod.qh>
#include <common/mutators/mutator/superspec/_mod.qh>
#include <common/mutators/mutator/touchexplode/_mod.qh>
#include <common/mutators/mutator/vampire/_mod.qh>
switch(ent.classname)
{
case "item_strength":
- case "item_invincible":
+ case "item_shield":
{
entity e = spawn();
buff_SpawnReplacement(e, ent);
}
make_impure(NEW(DamageText, server_entity_index, entcs.origin, false, health, armor, potential_damage, deathtype, friendlyfire));
} else if (autocvar_cl_damagetext_2d && spectatee_status != -1) {
- // never show 2d damagetext when observing
- // on some maps (hearth_v2, The_Yard), sometimes has_origin is false even though observers should know about all players
- // it happens mostly with bots but occasionally also with players
+ // never show 2d damagetext when observing - might be a bug in .has_origin
// screen coords only
vector screen_pos = vec2(vid_conwidth * autocvar_cl_damagetext_2d_pos.x, vid_conheight * autocvar_cl_damagetext_2d_pos.y);
#include "sv_dodging.qh"
+// TODO the CSQC blocks in this sv_ file are currently not compiled but will be when dodging prediction gets enabled
+
#define PHYS_DODGING g_dodging
#define PHYS_DODGING_DELAY autocvar_sv_dodging_delay
#define PHYS_DODGING_DISTANCE_THRESHOLD autocvar_sv_dodging_wall_distance_threshold
-#define PHYS_DODGING_FROZEN_NODOUBLETAP autocvar_sv_dodging_frozen_doubletap
+#define PHYS_DODGING_FROZEN_DOUBLETAP autocvar_sv_dodging_frozen_doubletap
#define PHYS_DODGING_HEIGHT_THRESHOLD autocvar_sv_dodging_height_threshold
-#define PHYS_DODGING_HORIZ_SPEED autocvar_sv_dodging_horiz_speed
-#define PHYS_DODGING_HORIZ_SPEED_FROZEN autocvar_sv_dodging_horiz_speed_frozen
+#define PHYS_DODGING_HORIZ_SPEED_MIN autocvar_sv_dodging_horiz_speed_min
+#define PHYS_DODGING_HORIZ_SPEED_MAX autocvar_sv_dodging_horiz_speed_max
+#define PHYS_DODGING_HORIZ_FORCE_SLOWEST autocvar_sv_dodging_horiz_force_slowest
+#define PHYS_DODGING_HORIZ_FORCE_FASTEST autocvar_sv_dodging_horiz_force_fastest
+#define PHYS_DODGING_HORIZ_FORCE_FROZEN autocvar_sv_dodging_horiz_force_frozen
#define PHYS_DODGING_RAMP_TIME autocvar_sv_dodging_ramp_time
#define PHYS_DODGING_UP_SPEED autocvar_sv_dodging_up_speed
#define PHYS_DODGING_WALL autocvar_sv_dodging_wall_dodging
#define PHYS_DODGING_AIR autocvar_sv_dodging_air_dodging
#define PHYS_DODGING_MAXSPEED autocvar_sv_dodging_maxspeed
+#define PHYS_DODGING_AIR_MAXSPEED autocvar_sv_dodging_air_maxspeed
// we ran out of stats slots! TODO: re-enable this when prediction is available for dodging
#if 0
#define PHYS_DODGING STAT(DODGING, this)
#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this)
#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this)
-#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this)
+#define PHYS_DODGING_FROZEN_DOUBLETAP STAT(DODGING_FROZEN_DOUBLETAP, this)
#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this)
-#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this)
-#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this)
+#define PHYS_DODGING_HORIZ_SPEED_MIN STAT(DODGING_HORIZ_SPEED_MIN, this)
+#define PHYS_DODGING_HORIZ_SPEED_MAX STAT(DODGING_HORIZ_SPEED_MAX, this)
+#define PHYS_DODGING_HORIZ_FORCE_SLOWEST STAT(DODGING_HORIZ_FORCE_SLOWEST, this)
+#define PHYS_DODGING_HORIZ_FORCE_FASTEST STAT(DODGING_HORIZ_FORCE_FASTEST, this)
+#define PHYS_DODGING_HORIZ_FORCE_FROZEN STAT(DODGING_HORIZ_FORCE_FROZEN, this)
#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this)
#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this)
#define PHYS_DODGING_WALL STAT(DODGING_WALL, this)
#define PHYS_DODGING_AIR STAT(DODGING_AIR, this)
#define PHYS_DODGING_MAXSPEED STAT(DODGING_MAXSPEED, this)
+#define PHYS_DODGING_AIR_MAXSPEED STAT(DODGING_AIR_MAXSPEED, this)
#endif
#ifdef CSQC
bool autocvar_sv_dodging_sound;
-// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done..
-.float dodging_action;
-
-// the jump part of the dodge cannot be ramped
-.float dodging_single_action;
-
#include <common/animdecide.qh>
#include <common/physics/player.qh>
// the jump part of the dodge cannot be ramped
.float dodging_single_action;
-
// these are used to store the last key press time for each of the keys..
.float last_FORWARD_KEY_time;
.float last_BACKWARD_KEY_time;
// and to ramp up the dodge acceleration in the physics hook.
.float last_dodging_time;
-// This is the velocity gain to be added over the ramp time.
-// It will decrease from frame to frame during dodging_action = 1
-// until it's 0.
-.float dodging_velocity_gain;
+// the total speed that will be added over the ramp time
+.float dodging_force_total;
+// the part of total yet to be added
+.float dodging_force_remaining;
#ifdef CSQC
.int pressedkeys;
#endif
-// returns true if the player is close to a wall
-bool check_close_to_wall(entity this, float threshold)
-{
- if (PHYS_DODGING_WALL == 0) { return false; }
-
#define X(dir) \
tracebox(this.origin, this.mins, this.maxs, this.origin + threshold * dir, true, this); \
if (trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) \
return true;
+// returns true if the player is close to a wall
+bool is_close_to_wall(entity this, float threshold)
+{
X(v_right);
X(-v_right);
X(v_forward);
X(-v_forward);
-#undef X
return false;
}
-bool check_close_to_ground(entity this, float threshold)
+bool is_close_to_ground(entity this, float threshold)
{
- return IS_ONGROUND(this) ? true : false;
+ if (IS_ONGROUND(this)) return true;
+ X(-v_up); // necessary for dodging down a slope using doubletap (using `+dodge` works anyway)
+
+ return false;
+}
+
+#undef X
+
+float determine_force(entity player) {
+ if (PHYS_FROZEN(player)) return PHYS_DODGING_HORIZ_FORCE_FROZEN;
+
+ float horiz_vel = vlen(vec2(player.velocity));
+ return map_bound_ranges(horiz_vel,
+ PHYS_DODGING_HORIZ_SPEED_MIN, PHYS_DODGING_HORIZ_SPEED_MAX,
+ PHYS_DODGING_HORIZ_FORCE_SLOWEST, PHYS_DODGING_HORIZ_FORCE_FASTEST);
}
bool PM_dodging_checkpressedkeys(entity this)
{
- if(!PHYS_DODGING)
- return false;
-
bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this));
- bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP);
+ bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_DOUBLETAP);
+
+ float tap_direction_x = 0;
+ float tap_direction_y = 0;
+ bool dodge_detected = false;
+ vector mymovement = PHYS_CS(this).movement;
- // first check if the last dodge is far enough back in time so we can dodge again
+ #define X(COND,BTN,RESULT) \
+ if (mymovement_##COND) { \
+ /* is this a state change? */ \
+ if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \
+ tap_direction_##RESULT; \
+ if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) { \
+ dodge_detected = true; \
+ } else if(PHYS_INPUT_BUTTON_DODGE(this)) { \
+ dodge_detected = true; \
+ } \
+ this.last_##BTN##_KEY_time = time; \
+ } \
+ }
+ X(x < 0, BACKWARD, x--);
+ X(x > 0, FORWARD, x++);
+ X(y < 0, LEFT, y--);
+ X(y > 0, RIGHT, y++);
+ #undef X
+
+ if (!dodge_detected) return false;
+
+ // this check has to be after checking keys:
+ // the first key press of the double tap is allowed to be before dodging delay,
+ // only the second has to be after, otherwise +dodge gives an advantage because typical repress time is 0.1 s
+ // or higher which means players using +dodge would be able to do it more often
if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY)
return false;
makevectors(this.angles);
- bool wall_dodge = false;
+ bool can_dodge = (is_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) && (PHYS_DODGING_MAXSPEED == 0 || vdist(this.velocity, <, PHYS_DODGING_MAXSPEED)));
+ bool can_wall_dodge = (PHYS_DODGING_WALL && is_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD));
+ bool can_air_dodge = (PHYS_DODGING_AIR && (PHYS_DODGING_AIR_MAXSPEED == 0 || vdist(this.velocity, <, PHYS_DODGING_AIR_MAXSPEED)));
+ if (!can_dodge && !can_wall_dodge && !can_air_dodge) return false;
- if(!PHYS_DODGING_AIR)
- if(!check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD))
- {
- wall_dodge = check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD);
- if(!wall_dodge) // we're not on the ground, and wall dodging isn't allowed, end it!
- return true;
- }
+ this.last_dodging_time = time;
- if(!wall_dodge && PHYS_DODGING_MAXSPEED && vdist(this.velocity, >, PHYS_DODGING_MAXSPEED))
- return false;
+ this.dodging_action = 1;
+ this.dodging_single_action = 1;
- float tap_direction_x = 0;
- float tap_direction_y = 0;
- bool dodge_detected = false;
- vector mymovement = PHYS_CS(this).movement;
+ this.dodging_force_total = determine_force(this);
+ this.dodging_force_remaining = this.dodging_force_total;
- #define X(COND,BTN,RESULT) \
- if (mymovement_##COND) \
- /* is this a state change? */ \
- if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \
- tap_direction_##RESULT; \
- if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \
- dodge_detected = true; \
- if(PHYS_INPUT_BUTTON_DODGE(this)) \
- dodge_detected = true; \
- this.last_##BTN##_KEY_time = time; \
- }
- X(x < 0, BACKWARD, x--);
- X(x > 0, FORWARD, x++);
- X(y < 0, LEFT, y--);
- X(y > 0, RIGHT, y++);
- #undef X
-
- if (dodge_detected)
- {
- this.last_dodging_time = time;
-
- this.dodging_action = 1;
- this.dodging_single_action = 1;
+ this.dodging_direction.x = tap_direction_x;
+ this.dodging_direction.y = tap_direction_y;
- this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED;
+ // normalize the dodging_direction vector.. (unlike UT99) XD
+ float length = sqrt(this.dodging_direction.x ** 2 + this.dodging_direction.y ** 2);
- this.dodging_direction_x = tap_direction_x;
- this.dodging_direction_y = tap_direction_y;
+ this.dodging_direction.x = this.dodging_direction.x / length;
+ this.dodging_direction.y = this.dodging_direction.y / length;
- // normalize the dodging_direction vector.. (unlike UT99) XD
- float length = this.dodging_direction_x * this.dodging_direction_x
- + this.dodging_direction_y * this.dodging_direction_y;
- length = sqrt(length);
-
- this.dodging_direction_x = this.dodging_direction_x * 1.0 / length;
- this.dodging_direction_y = this.dodging_direction_y * 1.0 / length;
- return true;
- }
- return false;
+ return true;
}
void PM_dodging(entity this)
{
- if (!PHYS_DODGING)
- return;
+ // can't use return value from PM_dodging_checkpressedkeys because they're called from different hooks
+ if (!this.dodging_action) return;
// when swimming or dead, no dodging allowed..
if (this.waterlevel >= WATERLEVEL_SWIMMING || IS_DEAD(this))
{
this.dodging_action = 0;
- this.dodging_direction_x = 0;
- this.dodging_direction_y = 0;
+ this.dodging_direction.x = 0;
+ this.dodging_direction.y = 0;
return;
}
else
makevectors(this.angles);
+ // fraction of the force to apply each frame
// if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code
// will be called ramp_time/frametime times = 2 times. so, we need to
// add 0.5 * the total speed each frame until the dodge action is done..
float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME;
+ // NOTE: depending on cl_netfps the client may (and probably will) send more input frames during each server frame
+ // but common_factor uses server frame rate so players with higher cl_netfps will ramp slightly faster
- // if ramp time is smaller than frametime we get problems ;D
- common_factor = min(common_factor, 1);
-
- float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED;
- float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed);
- new_velocity_gain = max(0, new_velocity_gain);
-
- float velocity_difference = this.dodging_velocity_gain - new_velocity_gain;
-
- // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D
- if (this.dodging_action == 1)
- {
- //disable jump key during dodge accel phase
- if(PHYS_CS(this).movement.z > 0) { PHYS_CS(this).movement_z = 0; }
-
- this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right)
- + ((this.dodging_direction_x * velocity_difference) * v_forward);
-
- this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference;
- }
+ float velocity_increase = min(common_factor * this.dodging_force_total, this.dodging_force_remaining);
+ this.dodging_force_remaining -= velocity_increase;
+ this.velocity += this.dodging_direction.x * velocity_increase * v_forward
+ + this.dodging_direction.y * velocity_increase * v_right;
// the up part of the dodge is a single shot action
if (this.dodging_single_action == 1)
this.dodging_single_action = 0;
}
- // are we done with the dodging ramp yet?
- if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME))
+ if(this.dodging_force_remaining <= 0)
{
// reset state so next dodge can be done correctly
this.dodging_action = 0;
- this.dodging_direction_x = 0;
- this.dodging_direction_y = 0;
+ this.dodging_direction.x = 0;
+ this.dodging_direction.y = 0;
}
}
+#ifdef CSQC
void PM_dodging_GetPressedKeys(entity this)
{
-#ifdef CSQC
- if(!PHYS_DODGING) { return; }
-
PM_dodging_checkpressedkeys(this);
int keys = this.pressedkeys;
keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this));
keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this));
this.pressedkeys = keys;
-#endif
}
+#endif
MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics)
{
- entity player = M_ARGV(0, entity);
+ entity player = M_ARGV(0, entity);
- // print("dodging_PlayerPhysics\n");
+#ifdef CSQC
PM_dodging_GetPressedKeys(player);
+#endif
PM_dodging(player);
}
REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout");
-MUTATOR_HOOKFUNCTION(dodging, PlayerPreThink)
-{
- entity player = M_ARGV(0, entity);
-
- STAT(DODGING_TIMEOUT, player) = CS(player).cvar_cl_dodging_timeout;
-}
-
MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys)
{
entity player = M_ARGV(0, entity);
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qh>
+#endif
--- /dev/null
+#include "sv_dynamic_handicap.qh"
+/// \file
+/// \brief Source file that contains implementation of the Dynamic handicap
+/// mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+//======================= Global variables ====================================
+
+int autocvar_g_dynamic_handicap; ///< Whether to enable dynamic handicap.
+/// \brief The scale of the handicap. Larget values mean more penalties for
+/// strong players and more buffs for weak players.
+float autocvar_g_dynamic_handicap_scale;
+/// \brief The exponent used to calculate handicap. 1 means linear scale. Values
+/// more than 1 mean stronger non-linear handicap. Values less than 1 mean
+/// weaker non-linear handicap.
+float autocvar_g_dynamic_handicap_exponent;
+float autocvar_g_dynamic_handicap_min; ///< The minimum value of the handicap.
+float autocvar_g_dynamic_handicap_max; ///< The maximum value of the handicap.
+
+//====================== Forward declarations =================================
+
+/// \brief Clamps the value of the handicap.
+/// \param[in] handicap Value to clamp.
+/// \return Clamped value.
+float DynamicHandicap_ClampHandicap(float handicap);
+
+//========================= Free functions ====================================
+
+/// \brief Updates the handicap of all players.
+/// \return No return.
+void DynamicHandicap_UpdateHandicap()
+{
+ float total_score = 0;
+ float total_players = 0;
+ FOREACH_CLIENT(IS_PLAYER(it),
+ {
+ total_score += PlayerScore_Get(it, SP_SCORE);
+ ++total_players;
+ });
+ float mean_score = total_score / total_players;
+ FOREACH_CLIENT(true,
+ {
+ float score = PlayerScore_Get(it, SP_SCORE);
+ float handicap = fabs((score - mean_score) *
+ autocvar_g_dynamic_handicap_scale);
+ handicap = handicap ** autocvar_g_dynamic_handicap_exponent;
+ if (score < mean_score)
+ {
+ handicap = -handicap;
+ }
+ if (handicap >= 0)
+ {
+ handicap += 1;
+ }
+ else
+ {
+ handicap = 1 / (fabs(handicap) + 1);
+ }
+ handicap = DynamicHandicap_ClampHandicap(handicap);
+ Handicap_SetForcedHandicap(it, handicap);
+ });
+}
+
+float DynamicHandicap_ClampHandicap(float handicap)
+{
+ if ((autocvar_g_dynamic_handicap_min >= 0) && (handicap <
+ autocvar_g_dynamic_handicap_min))
+ {
+ handicap = autocvar_g_dynamic_handicap_min;
+ }
+ if ((autocvar_g_dynamic_handicap_max > 0) && (handicap >
+ autocvar_g_dynamic_handicap_max))
+ {
+ handicap = autocvar_g_dynamic_handicap_max;
+ }
+ return handicap;
+}
+
+//============================= Hooks ========================================
+
+REGISTER_MUTATOR(dynamic_handicap, autocvar_g_dynamic_handicap);
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, BuildMutatorsString)
+{
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":handicap");
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, BuildMutatorsPrettyString)
+{
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Dynamic handicap");
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, ClientDisconnect)
+{
+ DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, PutClientInServer)
+{
+ DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, MakePlayerObserver)
+{
+ DynamicHandicap_UpdateHandicap();
+}
+
+MUTATOR_HOOKFUNCTION(dynamic_handicap, AddedPlayerScore)
+{
+ if (M_ARGV(0, entity) != SP_SCORE)
+ {
+ return;
+ }
+ DynamicHandicap_UpdateHandicap();
+}
--- /dev/null
+#pragma once
// generated file; do not modify
#include <common/mutators/mutator/instagib/items.qc>
+#ifdef SVQC
+ #include <common/mutators/mutator/instagib/sv_items.qc>
+#endif
#ifdef SVQC
#include <common/mutators/mutator/instagib/sv_instagib.qc>
#endif
}
#endif
REGISTER_ITEM(VaporizerCells, Ammo) {
+ this.m_canonical_spawnfunc = "item_vaporizer_cells";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_INSTAGIB | ITEM_FLAG_MUTATORBLOCKED;
this.m_model = MDL_VaporizerCells_ITEM;
this.m_sound = SND_VaporizerCells;
#endif
- this.netname = "minst_cells";
+ this.netname = "vaporizer_cells";
this.m_name = "Vaporizer Ammo";
this.m_icon = "ammo_supercells";
#ifdef SVQC
#endif
}
+SPAWNFUNC_ITEM(item_vaporizer_cells, ITEM_VaporizerCells)
+SPAWNFUNC_ITEM(item_minst_cells, ITEM_VaporizerCells)
+
#ifdef GAMEQC
MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3"));
SOUND(ExtraLife, Item_Sound("megahealth"));
#endif
REGISTER_ITEM(ExtraLife, Powerup) {
+ this.m_canonical_spawnfunc = "item_extralife";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_INSTAGIB;
this.m_model = MDL_ExtraLife_ITEM;
this.m_sound = SND_ExtraLife;
#endif
- this.netname = "health_mega";
+ this.netname = "extralife";
this.m_name = "Extra life";
this.m_icon = "item_mega_health";
this.m_color = '1 0 0';
this.m_itemid = IT_NAILS;
}
+SPAWNFUNC_ITEM(item_extralife, ITEM_ExtraLife)
+
#ifdef GAMEQC
MODEL(Invisibility_ITEM, Item_Model("g_strength.md3"));
SOUND(Invisibility, Item_Sound("powerup"));
#endif
+#ifdef SVQC
+/// \brief Initializes the invisibility powerup.
+/// \param[in,out] item Item to initialize.
+/// \return No return.
+void powerup_invisibility_init(entity item);
+#endif
+
REGISTER_ITEM(Invisibility, Powerup) {
+ this.m_canonical_spawnfunc = "item_invisibility";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_INSTAGIB;
this.m_model = MDL_Invisibility_ITEM;
this.m_sound = SND_Invisibility;
+ this.m_glow = true;
+ this.m_respawnsound = SND_STRENGTH_RESPAWN;
#endif
- this.netname = "strength";
+ this.netname = "invisibility";
this.m_name = "Invisibility";
this.m_icon = "strength";
this.m_color = '0 0 1';
this.m_waypoint = _("Invisibility");
this.m_waypointblink = 2;
this.m_itemid = IT_STRENGTH;
+#ifdef SVQC
+ this.m_iteminit = powerup_invisibility_init;
+#endif
}
+SPAWNFUNC_ITEM(item_invisibility, ITEM_Invisibility)
+
#ifdef GAMEQC
MODEL(Speed_ITEM, Item_Model("g_invincible.md3"));
SOUND(Speed, Item_Sound("powerup_shield"));
#endif
+#ifdef SVQC
+/// \brief Initializes the speed powerup.
+/// \param[in,out] item Item to initialize.
+/// \return No return.
+void powerup_speed_init(entity item);
+#endif
+
REGISTER_ITEM(Speed, Powerup) {
+ this.m_canonical_spawnfunc = "item_speed";
#ifdef GAMEQC
+ this.spawnflags = ITEM_FLAG_INSTAGIB;
this.m_model = MDL_Speed_ITEM;
this.m_sound = SND_Speed;
+ this.m_glow = true;
+ this.m_respawnsound = SND_SHIELD_RESPAWN;
#endif
- this.netname = "invincible";
+ this.netname = "speed";
this.m_name = "Speed";
this.m_icon = "shield";
this.m_color = '1 0 1';
this.m_waypoint = _("Speed");
this.m_waypointblink = 2;
this.m_itemid = IT_INVINCIBLE;
+#ifdef SVQC
+ this.m_iteminit = powerup_speed_init;
+#endif
}
+
+SPAWNFUNC_ITEM(item_speed, ITEM_Speed)
#include <common/items/_mod.qh>
-REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball);
-
-spawnfunc(item_minst_cells)
+REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball)
{
- if (!g_instagib) { delete(this); return; }
- StartItem(this, ITEM_VaporizerCells);
+ MUTATOR_ONADD
+ {
+ ITEM_VaporizerCells.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+ }
+ MUTATOR_ONROLLBACK_OR_REMOVE
+ {
+ ITEM_VaporizerCells.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+ }
}
void instagib_invisibility(entity this)
void replace_with_insta_cells(entity item)
{
- entity e = spawn();
+ entity e = new(item_vaporizer_cells);
setorigin(e, item.origin);
e.noalign = item.noalign;
e.cnt = item.cnt;
e.team = item.team;
e.spawnfunc_checked = true;
- spawnfunc_item_minst_cells(e);
+ spawnfunc_item_vaporizer_cells(e);
}
MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem)
return true;
}
- if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon")
+ if(item.weapon == WEP_VAPORIZER.m_id && Item_IsLoot(item))
{
SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
return false;
return false;
float cells = GetResourceAmount(item, RESOURCE_CELLS);
- if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells")
+ if(cells > autocvar_g_instagib_ammo_drop && item.classname != "item_vaporizer_cells")
SetResourceAmount(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop);
if(cells && !item.weapon)
if (!autocvar_g_powerups) { return; }
entity ent = M_ARGV(0, entity);
// Can't use .itemdef here
- if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega"))
+ if (!(ent.classname == "item_strength" || ent.classname == "item_shield" || ent.classname == "item_health_mega"))
return;
entity e = spawn();
float r = random();
if (r < 0.3)
+ {
+ e.classname = "item_invisibility";
setthink(e, instagib_invisibility);
+ }
else if (r < 0.6)
+ {
+ e.classname = "item_extralife";
setthink(e, instagib_extralife);
+ }
else
+ {
+ e.classname = "item_speed";
setthink(e, instagib_speed);
+ }
e.nextthink = time + 0.1;
e.spawnflags = ent.spawnflags;
#include "items.qh"
float autocvar_g_instagib_invis_alpha;
+
+void instagib_invisibility(entity this);
+void instagib_extralife(entity this);
+void instagib_speed(entity this);
--- /dev/null
+#include "items.qh"
+
+/// \brief Time of ivisibility powerup in seconds.
+float autocvar_g_instagib_invisibility_time;
+/// \brief Time of speed powerup in seconds.
+float autocvar_g_instagib_speed_time;
+
+void powerup_invisibility_init(entity item)
+{
+ if(!item.strength_finished)
+ {
+ item.strength_finished = autocvar_g_instagib_invisibility_time;
+ }
+}
+
+
+void powerup_speed_init(entity item)
+{
+ if(!item.invincible_finished)
+ {
+ item.invincible_finished = autocvar_g_instagib_speed_time;
+ }
+}
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc>
+#endif
--- /dev/null
+// generated file; do not modify
--- /dev/null
+
+float autocvar_g_kick_teamkiller_rate;
+float autocvar_g_kick_teamkiller_lower_limit;
+
+REGISTER_MUTATOR(kick_teamkiller, (autocvar_g_kick_teamkiller_rate > 0));
+
+MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies)
+{
+ if (!teamplay)
+ {
+ return;
+ }
+ if (warmup_stage)
+ {
+ return;
+ }
+ entity attacker = M_ARGV(1, entity);
+ if (!IS_REAL_CLIENT(attacker))
+ {
+ return;
+ }
+
+ int teamkills = PlayerScore_Get(attacker, SP_TEAMKILLS);
+ // use the players actual playtime
+ float playtime = time - CS(attacker).startplaytime;
+ // rate is in teamkills/minutes, playtime in seconds
+ if (teamkills >= autocvar_g_kick_teamkiller_lower_limit &&
+ teamkills >= autocvar_g_kick_teamkiller_rate*playtime/60.0)
+ {
+ Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_TEAMKILL, attacker.netname);
+ dropclient(attacker);
+ }
+}
#include "sv_new_toys.qh"
+#include "../random_items/sv_random_items.qh"
+
/*
CORE laser vortex lg rl cry gl elec hagar fireb hook
MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace)
{
+ if (autocvar_g_random_items)
+ {
+ // Do not replace weapons when random items are enabled.
+ return;
+ }
entity wep = M_ARGV(0, entity);
entity wepinfo = M_ARGV(1, entity);
string ret_string = M_ARGV(2, string);
SetResourceAmount(this, RESOURCE_FUEL, 0);
if(this.items & IT_UNLIMITED_WEAPON_AMMO)
{
- switch(e.ammo_field)
+ switch (e.ammo_type)
{
- case ammo_shells: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_pickup_shells_max); break;
- case ammo_nails: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_pickup_nails_max); break;
- case ammo_rockets: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_pickup_rockets_max); break;
- case ammo_cells: SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_pickup_cells_max); break;
- case ammo_plasma: SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_pickup_plasma_max); break;
- case ammo_fuel: SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_pickup_fuel_max); break;
+ case RESOURCE_SHELLS: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_pickup_shells_max); break;
+ case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_pickup_nails_max); break;
+ case RESOURCE_ROCKETS: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_pickup_rockets_max); break;
+ case RESOURCE_CELLS: SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_pickup_cells_max); break;
+ case RESOURCE_PLASMA: SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_pickup_plasma_max); break;
+ case RESOURCE_FUEL: SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_pickup_fuel_max); break;
}
}
else
{
- switch(e.ammo_field)
+ switch (e.ammo_type)
{
- case ammo_shells: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammo_shells); break;
- case ammo_nails: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammo_nails); break;
- case ammo_rockets: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammo_rockets); break;
- case ammo_cells: SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammo_cells); break;
- case ammo_plasma: SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammo_plasma); break;
- case ammo_fuel: SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammo_fuel); break;
+ case RESOURCE_SHELLS: SetResourceAmount(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammo_shells); break;
+ case RESOURCE_BULLETS: SetResourceAmount(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammo_nails); break;
+ case RESOURCE_ROCKETS: SetResourceAmount(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammo_rockets); break;
+ case RESOURCE_CELLS: SetResourceAmount(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammo_cells); break;
+ case RESOURCE_PLASMA: SetResourceAmount(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammo_plasma); break;
+ case RESOURCE_FUEL: SetResourceAmount(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammo_fuel); break;
}
}
if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr)
{
- switch(e.ammo_field)
+ switch (e.ammo_type)
{
- case ammo_shells: GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells); break;
- case ammo_nails: GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails); break;
- case ammo_rockets: GiveResource(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammoincr_rockets); break;
- case ammo_cells: GiveResource(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammoincr_cells); break;
- case ammo_plasma: GiveResource(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammoincr_plasma); break;
- case ammo_fuel: GiveResource(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammoincr_fuel); break;
+ case RESOURCE_SHELLS: GiveResource(this, RESOURCE_SHELLS, autocvar_g_balance_nix_ammoincr_shells); break;
+ case RESOURCE_BULLETS: GiveResource(this, RESOURCE_BULLETS, autocvar_g_balance_nix_ammoincr_nails); break;
+ case RESOURCE_ROCKETS: GiveResource(this, RESOURCE_ROCKETS, autocvar_g_balance_nix_ammoincr_rockets); break;
+ case RESOURCE_CELLS: GiveResource(this, RESOURCE_CELLS, autocvar_g_balance_nix_ammoincr_cells); break;
+ case RESOURCE_PLASMA: GiveResource(this, RESOURCE_PLASMA, autocvar_g_balance_nix_ammoincr_plasma); break;
+ case RESOURCE_FUEL: GiveResource(this, RESOURCE_FUEL, autocvar_g_balance_nix_ammoincr_fuel); break;
}
this.nix_nextincr = time + autocvar_g_balance_nix_incrtime;
M_ARGV(3, float) /* damage */ = (M_ARGV(0, entity)).max_health * 0.1;
}
-spawnfunc(weapon_hmg) { weapon_defaultspawnfunc(this, WEP_HMG); }
-
void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire)
{
if (!PHYS_INPUT_BUTTON_ATCK(actor))
METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.ammo_nails >= WEP_CVAR(hmg, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(hmg, ammo);
if(autocvar_g_balance_hmg_reload_ammo)
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.ammo_nails >= WEP_CVAR(hmg, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(hmg, ammo);
if(autocvar_g_balance_hmg_reload_ammo)
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HMG.m_id]) >= WEP_CVAR(hmg, ammo);
#pragma once
+#include <common/weapons/all.qh>
+
CLASS(HeavyMachineGun, Weapon)
-/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails);
+/* spawnfunc */ ATTRIB(HeavyMachineGun, m_canonical_spawnfunc, string, "weapon_hmg");
+/* ammotype */ ATTRIB(HeavyMachineGun, ammo_type, int, RESOURCE_BULLETS);
/* impulse */ ATTRIB(HeavyMachineGun, impulse, int, 3);
/* flags */ ATTRIB(HeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON);
/* rating */ ATTRIB(HeavyMachineGun, bot_pickupbasevalue, float, 10000);
ENDCLASS(HeavyMachineGun)
REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun));
+
+SPAWNFUNC_WEAPON(weapon_hmg, WEP_HMG)
#include "rpc.qh"
#ifdef SVQC
-spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); }
void W_RocketPropelledChainsaw_Explode(entity this, entity directhitentity)
{
METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(rpc, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(rpc, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_RPC.m_id]) >= WEP_CVAR(rpc, ammo);
return ammo_amount;
}
#pragma once
+#include <common/weapons/all.qh>
+
CLASS(RocketPropelledChainsaw, Weapon)
-/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(RocketPropelledChainsaw, m_canonical_spawnfunc, string, "weapon_rpc");
+/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 9);
/* flags */ ATTRIB(RocketPropelledChainsaw, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_SUPERWEAPON);
/* rating */ ATTRIB(RocketPropelledChainsaw, bot_pickupbasevalue, float, 10000);
ENDCLASS(RocketPropelledChainsaw)
REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw));
+
+SPAWNFUNC_WEAPON(weapon_rpc, WEP_RPC)
bool autocvar_g_overkill_itemwaypoints = true;
-bool autocvar_g_overkill_filter_healthmega;
-bool autocvar_g_overkill_filter_armormedium;
-bool autocvar_g_overkill_filter_armorbig;
-bool autocvar_g_overkill_filter_armormega;
-
-.float ok_item;
-
.Weapon ok_lastwep[MAX_WEAPONSLOTS];
-void ok_Initialize();
-
REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill")
{
MUTATOR_ONADD
{
- ok_Initialize();
+ precache_all_playermodels("models/ok_player/*.dpm");
+
+ if (autocvar_g_overkill_filter_healthmega)
+ {
+ ITEM_HealthMega.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+ }
+ if (autocvar_g_overkill_filter_armormedium)
+ {
+ ITEM_ArmorMedium.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+ }
+ if (autocvar_g_overkill_filter_armorbig)
+ {
+ ITEM_ArmorBig.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+ }
+ if (autocvar_g_overkill_filter_armormega)
+ {
+ ITEM_ArmorMega.spawnflags |= ITEM_FLAG_MUTATORBLOCKED;
+ }
+
+ WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+ WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
+
+ WEP_SHOTGUN.mdl = "ok_shotgun";
+ WEP_MACHINEGUN.mdl = "ok_mg";
+ WEP_VORTEX.mdl = "ok_sniper";
}
MUTATOR_ONREMOVE
{
+ ITEM_HealthMega.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+ ITEM_ArmorMedium.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+ ITEM_ArmorBig.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+ ITEM_ArmorMega.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED;
+
WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED;
}
}
void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float);
-spawnfunc(weapon_hmg);
-spawnfunc(weapon_rpc);
MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST)
{
void ok_DropItem(entity this, entity targ)
{
- entity e = new(droppedweapon); // hax
+ entity e = spawn();
e.ok_item = true;
- e.noalign = true;
- e.pickup_anyway = true;
- e.spawnfunc_checked = true;
- spawnfunc_item_armor_small(e);
- if (!wasfreed(e)) { // might have been blocked by a mutator
- set_movetype(e, MOVETYPE_TOSS);
- e.gravity = 1;
- e.reset = SUB_Remove;
- setorigin(e, this.origin + '0 0 32');
- e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500;
- SUB_SetFade(e, time + 5, 1);
- }
+ Item_InitializeLoot(e, "item_armor_small", this.origin + '0 0 32',
+ '0 0 200' + normalize(targ.origin - this.origin) * 500, 5);
}
MUTATOR_HOOKFUNCTION(ok, PlayerDies)
setorigin(wep, ent.origin);
setmodel(wep, MDL_OK_HMG);
wep.ok_item = true;
- wep.noalign = ent.noalign;
+ wep.noalign = Item_ShouldKeepPosition(ent);
wep.cnt = ent.cnt;
wep.team = ent.team;
wep.respawntime = g_pickup_respawntime_superweapon;
wep.nextthink = time + 0.1;
return true;
}
- else if(ent.classname == "item_invincible")
+ else if(ent.classname == "item_shield")
{
entity wep = new(weapon_rpc);
setorigin(wep, ent.origin);
setmodel(wep, MDL_OK_RPC);
wep.ok_item = true;
- wep.noalign = ent.noalign;
+ wep.noalign = Item_ShouldKeepPosition(ent);
wep.cnt = ent.cnt;
wep.team = ent.team;
wep.respawntime = g_pickup_respawntime_superweapon;
if(item.ok_item)
return false;
- switch(item.itemdef)
- {
- case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega;
- case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium;
- case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig;
- case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega;
- }
-
return true;
}
M_ARGV(0, string) = "Overkill";
return true;
}
-
-void ok_Initialize()
-{
- precache_all_playermodels("models/ok_player/*.dpm");
-
- WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
- WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED;
-
- WEP_SHOTGUN.mdl = "ok_shotgun";
- WEP_MACHINEGUN.mdl = "ok_mg";
- WEP_VORTEX.mdl = "ok_sniper";
-}
#pragma once
+
+bool autocvar_g_overkill_filter_healthmega;
+bool autocvar_g_overkill_filter_armormedium;
+bool autocvar_g_overkill_filter_armorbig;
+bool autocvar_g_overkill_filter_armormega;
+
+.float ok_item;
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/random_items/sv_random_items.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/random_items/sv_random_items.qh>
+#endif
--- /dev/null
+#include "sv_random_items.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the random items mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+//============================ Constants ======================================
+
+enum
+{
+ RANDOM_ITEM_TYPE_HEALTH = 1,
+ RANDOM_ITEM_TYPE_ARMOR,
+ RANDOM_ITEM_TYPE_RESOURCE,
+ RANDOM_ITEM_TYPE_WEAPON,
+ RANDOM_ITEM_TYPE_POWERUP
+};
+
+//======================= Global variables ====================================
+
+// Replace cvars
+
+/// \brief Classnames to replace %s with.
+/// string autocvar_g_random_items_replace_%s;
+
+// Map probability cvars
+
+/// \brief Probability of random %s spawning in the map.
+/// float autocvar_g_random_items_%s_probability;
+
+/// \brief Probability of random %s spawning in the map during overkill.
+/// float autocvar_g_random_items_overkill_%s_probability;
+
+// Loot
+
+bool autocvar_g_random_loot; ///< Whether to enable random loot.
+
+float autocvar_g_random_loot_min; ///< Minimum amount of loot items.
+float autocvar_g_random_loot_max; ///< Maximum amount of loot items.
+float autocvar_g_random_loot_time; ///< Amount of time the loot will stay.
+float autocvar_g_random_loot_spread; ///< How far can loot be thrown.
+
+// Loot probability cvars
+
+/// \brief Probability of random %s spawning as loot.
+/// float autocvar_g_random_loot_%s_probability;
+
+/// \brief Probability of random %s spawning as loot during overkill.
+/// float autocvar_g_random_loot_overkill_%s_probability;
+
+/// \brief Holds whether random item is spawning. Used to prevent infinite
+/// recursion.
+bool random_items_is_spawning = false;
+
+//====================== Forward declarations =================================
+
+/// \brief Returns a random classname of the item with specific property.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the item.
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+ .bool item_property);
+
+//=========================== Public API ======================================
+
+string RandomItems_GetRandomItemClassName(string prefix)
+{
+ if (autocvar_g_instagib)
+ {
+ return RandomItems_GetRandomInstagibItemClassName(prefix);
+ }
+ if (expr_evaluate(autocvar_g_overkill))
+ {
+ return RandomItems_GetRandomOverkillItemClassName(prefix);
+ }
+ return RandomItems_GetRandomVanillaItemClassName(prefix);
+}
+
+string RandomItems_GetRandomVanillaItemClassName(string prefix)
+{
+ RandomSelection_Init();
+ string cvar_name = sprintf("g_%s_health_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_HEALTH, cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_armor_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_ARMOR, cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_resource_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_RESOURCE, cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_weapon_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_powerup_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddFloat(RANDOM_ITEM_TYPE_POWERUP, cvar(cvar_name), 1);
+ }
+ int item_type = RandomSelection_chosen_float;
+ switch (item_type)
+ {
+ case RANDOM_ITEM_TYPE_HEALTH:
+ {
+ return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+ instanceOfHealth);
+ }
+ case RANDOM_ITEM_TYPE_ARMOR:
+ {
+ return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+ instanceOfArmor);
+ }
+ case RANDOM_ITEM_TYPE_RESOURCE:
+ {
+ return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+ instanceOfAmmo);
+ }
+ case RANDOM_ITEM_TYPE_WEAPON:
+ {
+ RandomSelection_Init();
+ FOREACH(Weapons, it != WEP_Null &&
+ !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED),
+ {
+ cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.",
+ cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc,
+ cvar(cvar_name), 1);
+ });
+ return RandomSelection_chosen_string;
+ }
+ case RANDOM_ITEM_TYPE_POWERUP:
+ {
+ return RandomItems_GetRandomItemClassNameWithProperty(prefix,
+ instanceOfPowerup);
+ }
+ }
+ return "";
+}
+
+string RandomItems_GetRandomInstagibItemClassName(string prefix)
+{
+ RandomSelection_Init();
+ FOREACH(Items, it.spawnflags & ITEM_FLAG_INSTAGIB,
+ {
+ string cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
+ return RandomSelection_chosen_string;
+}
+
+string RandomItems_GetRandomOverkillItemClassName(string prefix)
+{
+ RandomSelection_Init();
+ FOREACH(Items, (it.spawnflags & ITEM_FLAG_OVERKILL) &&
+ !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED),
+ {
+ string cvar_name = sprintf("g_%s_overkill_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
+ string cvar_name = sprintf("g_%s_overkill_weapon_hmg_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddString("weapon_hmg", cvar(cvar_name), 1);
+ }
+ cvar_name = sprintf("g_%s_overkill_weapon_rpc_probability", prefix);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ }
+ else
+ {
+ RandomSelection_AddString("weapon_rpc", cvar(cvar_name), 1);
+ }
+ return RandomSelection_chosen_string;
+}
+
+//========================= Free functions ====================================
+
+/// \brief Returns list of classnames to replace a map item with.
+/// \param[in] item Item to inspect.
+/// \return List of classnames to replace a map item with.
+string RandomItems_GetItemReplacementClassNames(entity item)
+{
+ string cvar_name = sprintf("g_random_items_replace_%s", item.classname);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ return "";
+ }
+ return cvar_string(cvar_name);
+}
+
+string RandomItems_GetRandomItemClassNameWithProperty(string prefix,
+ .bool item_property)
+{
+ RandomSelection_Init();
+ FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL),
+ {
+ string cvar_name = sprintf("g_%s_%s_probability", prefix,
+ it.m_canonical_spawnfunc);
+ if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS))
+ {
+ LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name);
+ continue;
+ }
+ RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1);
+ });
+ return RandomSelection_chosen_string;
+}
+
+/// \brief Replaces a map item.
+/// \param[in] item Item to replace.
+/// \return Spawned item on success, NULL otherwise.
+entity RandomItems_ReplaceMapItem(entity item)
+{
+ //PrintToChatAll(strcat("Replacing ", item.classname));
+ string new_classnames = RandomItems_GetItemReplacementClassNames(item);
+ if (new_classnames == "")
+ {
+ return NULL;
+ }
+ string new_classname;
+ if (new_classnames == "random")
+ {
+ new_classname = RandomItems_GetRandomItemClassName("random_items");
+ if (new_classname == "")
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ int num_new_classnames = tokenize_console(new_classnames);
+ if (num_new_classnames == 1)
+ {
+ new_classname = new_classnames;
+ }
+ else
+ {
+ int classname_index = floor(random() * num_new_classnames);
+ new_classname = argv(classname_index);
+ }
+ }
+ //PrintToChatAll(strcat("Replacing with ", new_classname));
+ if (new_classname == item.classname)
+ {
+ return NULL;
+ }
+ random_items_is_spawning = true;
+ entity new_item;
+ if (!expr_evaluate(autocvar_g_overkill))
+ {
+ new_item = Item_Create(strzone(new_classname), item.origin,
+ Item_ShouldKeepPosition(item));
+ random_items_is_spawning = false;
+ if (new_item == NULL)
+ {
+ return NULL;
+ }
+ }
+ else
+ {
+ new_item = spawn();
+ new_item.classname = strzone(new_classname);
+ new_item.spawnfunc_checked = true;
+ new_item.noalign = Item_ShouldKeepPosition(item);
+ new_item.ok_item = true;
+ Item_Initialize(new_item, new_classname);
+ random_items_is_spawning = false;
+ if (wasfreed(new_item))
+ {
+ return NULL;
+ }
+ setorigin(new_item, item.origin);
+ }
+ if (item.team)
+ {
+ new_item.team = item.team;
+ }
+ return new_item;
+}
+
+/// \brief Spawns a random loot item.
+/// \param[in] position Position of the item.
+/// \return No return.
+void RandomItems_SpawnLootItem(vector position)
+{
+ string class_name = RandomItems_GetRandomItemClassName("random_loot");
+ if (class_name == "")
+ {
+ return;
+ }
+ vector spread = '0 0 0';
+ spread.z = autocvar_g_random_loot_spread / 2;
+ spread += randomvec() * autocvar_g_random_loot_spread;
+ random_items_is_spawning = true;
+ if (!expr_evaluate(autocvar_g_overkill))
+ {
+ Item_CreateLoot(class_name, position, spread,
+ autocvar_g_random_loot_time);
+ }
+ else
+ {
+ entity item = spawn();
+ item.ok_item = true;
+ item.classname = class_name;
+ Item_InitializeLoot(item, class_name, position, spread,
+ autocvar_g_random_loot_time);
+ }
+ random_items_is_spawning = false;
+}
+
+//============================= Hooks ========================================
+
+REGISTER_MUTATOR(random_items, (autocvar_g_random_items ||
+ autocvar_g_random_loot));
+
+MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsString)
+{
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":random_items");
+}
+
+MUTATOR_HOOKFUNCTION(random_items, BuildMutatorsPrettyString)
+{
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random items");
+}
+
+/// \brief Hook that is called when an item is about to spawn.
+MUTATOR_HOOKFUNCTION(random_items, FilterItem, CBC_ORDER_LAST)
+{
+ //PrintToChatAll("FilterItem");
+ if (!autocvar_g_random_items)
+ {
+ return false;
+ }
+ if (random_items_is_spawning == true)
+ {
+ return false;
+ }
+ entity item = M_ARGV(0, entity);
+ if (Item_IsLoot(item))
+ {
+ return false;
+ }
+ if (RandomItems_ReplaceMapItem(item) == NULL)
+ {
+ return false;
+ }
+ return true;
+}
+
+/// \brief Hook that is called after the player has touched an item.
+MUTATOR_HOOKFUNCTION(random_items, ItemTouched, CBC_ORDER_LAST)
+{
+ //PrintToChatAll("ItemTouched");
+ if (!autocvar_g_random_items)
+ {
+ return;
+ }
+ entity item = M_ARGV(0, entity);
+ if (Item_IsLoot(item))
+ {
+ return;
+ }
+ entity new_item = RandomItems_ReplaceMapItem(item);
+ if (new_item == NULL)
+ {
+ return;
+ }
+ Item_ScheduleRespawn(new_item);
+ delete(item);
+}
+
+/// \brief Hook which is called when the player dies.
+MUTATOR_HOOKFUNCTION(random_items, PlayerDies)
+{
+ //PrintToChatAll("PlayerDies");
+ if (!autocvar_g_random_loot)
+ {
+ return;
+ }
+ entity victim = M_ARGV(2, entity);
+ vector loot_position = victim.origin + '0 0 32';
+ int num_loot_items = floor(autocvar_g_random_loot_min + random() *
+ autocvar_g_random_loot_max);
+ for (int item_index = 0; item_index < num_loot_items; ++item_index)
+ {
+ RandomItems_SpawnLootItem(loot_position);
+ }
+}
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes the random items mutator.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+bool autocvar_g_random_items; ///< Whether to enable random items.
+
+/// \brief Returns a random classname of the item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the item.
+/// \note This function will automatically detect gamemode and use cvars from
+/// that gamemode.
+string RandomItems_GetRandomItemClassName(string prefix);
+
+/// \brief Returns a random classname of the vanilla item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the vanilla item.
+/// \note This includes mutator items that don't change gameplay a lot such as
+/// jetpack and new toys.
+string RandomItems_GetRandomVanillaItemClassName(string prefix);
+
+/// \brief Returns a random classname of the instagib item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the instagib item.
+string RandomItems_GetRandomInstagibItemClassName(string prefix);
+
+/// \brief Returns a random classname of the overkill item.
+/// \param[in] prefix Prefix of the cvars that hold probabilities.
+/// \return Random classname of the overkill item.
+string RandomItems_GetRandomOverkillItemClassName(string prefix);
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qc>
+#endif
--- /dev/null
+// generated file; do not modify
+#ifdef SVQC
+ #include <common/mutators/mutator/stale_move_negation/sv_stale_move_negation.qh>
+#endif
--- /dev/null
+#include "sv_stale_move_negation.qh"
+
+AUTOCVAR(g_smneg, bool, false, "Stale-move negation: penalize repeated use of the same weapon");
+AUTOCVAR(g_smneg_bonus, bool, true, "Stale-move negation: allow weapons to become stronger than their baseline");
+AUTOCVAR(g_smneg_bonus_asymptote, float, 4, "Stale-move negation: damage = infinity at this bonus level");
+AUTOCVAR(g_smneg_cooldown_factor, float, 1 / 4, "Stale-move negation: penalty cooldown factor");
+REGISTER_MUTATOR(mutator_smneg, autocvar_g_smneg);
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, BuildMutatorsString) {
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ":StaleMoveNegation");
+}
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, BuildMutatorsPrettyString) {
+ M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Stale-move negation");
+}
+
+.float x_smneg_weight[Weapons_MAX];
+
+float smneg_multiplier(float weight) {
+ float a = autocvar_g_smneg_bonus_asymptote;
+ float x = max(
+ (!autocvar_g_smneg_bonus ? 0 : (-a + .1)),
+ weight / start_health
+ );
+ float z = (M_PI / 5) * a;
+ float f = (x > 0)
+ ? (atan(z / x) / (M_PI / 2))
+ : (tan(-(x / z)) + 1);
+ return f;
+}
+
+MUTATOR_HOOKFUNCTION(mutator_smneg, Damage_Calculate) {
+ float deathtype = M_ARGV(3, float);
+ Weapon w = DEATH_WEAPONOF(deathtype);
+ if (w == WEP_Null) return;
+
+ entity frag_attacker = M_ARGV(1, entity);
+ entity c = CS(frag_attacker);
+ float weight = c.x_smneg_weight[w.m_id];
+ float f = smneg_multiplier(weight);
+ float frag_damage = M_ARGV(4, float) = f * M_ARGV(4, float);
+ M_ARGV(6, vector) = f * M_ARGV(6, vector); // force
+
+ c.x_smneg_weight[w.m_id] = weight + frag_damage;
+ float restore = frag_damage * autocvar_g_smneg_cooldown_factor;
+ FOREACH(Weapons, it != WEP_Null && it != w, {
+ c.x_smneg_weight[it.m_id] -= restore;
+ });
+}
--- /dev/null
+#pragma once
MSG_INFO_NOTIF(QUIT_DISCONNECT, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 disconnected"), "")
MSG_INFO_NOTIF(QUIT_KICK_IDLING, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was kicked for idling"), "")
MSG_INFO_NOTIF(QUIT_KICK_SPECTATING, N_CONSOLE, 0, 0, "", "", "", _("^F2You were kicked from the server because you are a spectator and spectators aren't allowed at the moment."), "")
+ MSG_INFO_NOTIF(QUIT_KICK_TEAMKILL, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 was kicked for excessive teamkilling"), "")
MSG_INFO_NOTIF(QUIT_SPECTATE, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^F3 is now spectating"), "")
MSG_INFO_NOTIF(RACE_ABANDONED, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^BG has abandoned the race"), "")
MSG_INFO_NOTIF(WEAPON_CRYLINK_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponcrylink", _("^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s"), "")
MSG_INFO_NOTIF(WEAPON_CRYLINK_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponcrylink", _("^BG%s^K1 felt the strong pull of their Crylink%s%s"), "")
MSG_INFO_NOTIF(WEAPON_DEVASTATOR_MURDER_DIRECT, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s"), "")
- MSG_INFO_NOTIF(WEAPON_DEVASTATOR_MURDER_SPLASH, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s"), "")
+ MSG_INFO_NOTIF(WEAPON_DEVASTATOR_MURDER_SPLASH, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrocketlauncher", _("^BG%s%s^K1 got too close to ^BG%s^K1's rocket%s%s"), "")
MSG_INFO_NOTIF(WEAPON_DEVASTATOR_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrocketlauncher", _("^BG%s^K1 blew themself up with their Devastator%s%s"), "")
MSG_INFO_NOTIF(WEAPON_ELECTRO_MURDER_BOLT, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s"), "")
MSG_INFO_NOTIF(WEAPON_ELECTRO_MURDER_COMBO, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponelectro", _("^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s"), "")
_Movetype_CheckWater(this);
this.origin = this.origin + movedt * this.velocity;
this.angles = this.angles + movedt * this.avelocity;
- _Movetype_LinkEdict(this, false);
break;
case MOVETYPE_STEP:
_Movetype_Physics_Step(this, movedt);
case MOVETYPE_PHYSICS:
break;
}
+
+ //_Movetype_CheckVelocity(this);
+
+ _Movetype_LinkEdict(this, true);
+
+ //_Movetype_CheckVelocity(this);
}
void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient) // to be run every move frame
STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate);
STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate);
STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity);
+ STAT(MOVEVARS_JUMPVELOCITY_CROUCH, this) = Physics_ClientOption(this, "jumpvelocity_crouch", autocvar_sv_jumpvelocity_crouch);
STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump);
}
#endif
#endif
bool doublejump = false;
- float mjumpheight = PHYS_JUMPVELOCITY(this);
+ float mjumpheight = ((PHYS_JUMPVELOCITY_CROUCH(this) && IS_DUCKED(this)) ? PHYS_JUMPVELOCITY_CROUCH(this) : PHYS_JUMPVELOCITY(this));
bool track_jump = PHYS_CL_TRACK_CANJUMP(this);
if (MUTATOR_CALLHOOK(PlayerJump, this, mjumpheight, doublejump))
#define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS)
#define PHYS_JUMPVELOCITY(s) STAT(MOVEVARS_JUMPVELOCITY, s)
+#define PHYS_JUMPVELOCITY_CROUCH(s) STAT(MOVEVARS_JUMPVELOCITY_CROUCH, s)
#define PHYS_MAXAIRSPEED(s) STAT(MOVEVARS_MAXAIRSPEED, s)
#define PHYS_MAXAIRSTRAFESPEED(s) STAT(MOVEVARS_MAXAIRSTRAFESPEED, s)
}
}
-// referred to by PS_GR_P_ADDVAL and PS_GR_T_ADDVAL
float PlayerStats_GameReport_Event(string prefix, string event_id, float value)
{
if((prefix == "") || PS_GR_OUT_DB < 0) { return 0; }
void PlayerStats_GameReport_Accuracy(entity p)
{
#define ACCMAC(suffix, field) \
- PS_GR_P_ADDVAL(p, sprintf("acc-%s-%s", it.netname, suffix), CS(p).accuracy.(field[i-1]));
+ PlayerStats_GameReport_Event_Player(p, \
+ sprintf("acc-%s-%s", it.netname, suffix), CS(p).accuracy.(field[i-1]));
FOREACH(Weapons, it != WEP_Null, {
ACCMAC("hit", accuracy_hit)
ACCMAC("fired", accuracy_fired)
// add global info!
if(p.alivetime)
{
- PS_GR_P_ADDVAL(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
+ PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_ALIVETIME, time - p.alivetime);
p.alivetime = 0;
}
db_put(PS_GR_OUT_DB, sprintf("%s:_team", p.playerstats_id), ftos(p.team));
if(stof(db_get(PS_GR_OUT_DB, sprintf("%s:%s", p.playerstats_id, PLAYERSTATS_ALIVETIME))) > 0)
- PS_GR_P_ADDVAL(p, PLAYERSTATS_JOINS, 1);
+ PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_JOINS, 1);
PlayerStats_GameReport_Accuracy(p);
anticheat_report_to_playerstats(p);
if(CS(p).latency_cnt)
{
float latency = (CS(p).latency_sum / CS(p).latency_cnt);
- if(latency) { PS_GR_P_ADDVAL(p, PLAYERSTATS_AVGLATENCY, latency); }
+ if(latency)
+ PlayerStats_GameReport_Event_Player(p, PLAYERSTATS_AVGLATENCY, latency);
}
}
FOREACH_CLIENT(true, {
// add personal score rank
- PS_GR_P_ADDVAL(it, PLAYERSTATS_RANK, it.score_dummyfield);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_RANK, it.score_dummyfield);
// scoreboard data
if(it.scoreboard_pos)
{
// scoreboard is valid!
- PS_GR_P_ADDVAL(it, PLAYERSTATS_SCOREBOARD_VALID, 1);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_SCOREBOARD_VALID, 1);
// add scoreboard position
- PS_GR_P_ADDVAL(it, PLAYERSTATS_SCOREBOARD_POS, it.scoreboard_pos);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_SCOREBOARD_POS, it.scoreboard_pos);
// add scoreboard data
PlayerScore_PlayerStats(it);
// if the match ended normally, add winning info
if(finished)
{
- PS_GR_P_ADDVAL(it, PLAYERSTATS_WINS, it.winning);
- PS_GR_P_ADDVAL(it, PLAYERSTATS_MATCHES, 1);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_WINS, it.winning);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_MATCHES, 1);
}
}
void PlayerStats_GameReport_AddEvent(string event_id);
// call on each event to track, or at player disconnect OR match end for "global stuff"
-#define PS_GR_P_ADDVAL(ent,eventid,val) PlayerStats_GameReport_Event(ent.playerstats_id, eventid, val)
-#define PS_GR_T_ADDVAL(team,eventid,val) PlayerStats_GameReport_Event(sprintf("team#%d", team), eventid, val)
+#define PlayerStats_GameReport_Event_Player(ent, eventid, val) PlayerStats_GameReport_Event(ent.playerstats_id, eventid, val)
+#define PlayerStats_GameReport_Event_Team(team, eventid, val) PlayerStats_GameReport_Event(sprintf("team#%d", team), eventid, val)
float PlayerStats_GameReport_Event(string prefix, string event_id, float value);
void PlayerStats_GameReport_Accuracy(entity p);
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes resource types.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+/// \brief Describes the available resource types.
+enum
+{
+ RESOURCE_NONE, ///< Indicates the lack of resource. Use with caution.
+ RESOURCE_HEALTH, ///< Health.
+ RESOURCE_ARMOR, ///< Armor.
+ RESOURCE_SHELLS, ///< Shells (used by shotgun).
+ RESOURCE_BULLETS, ///< Bullets (used by machinegun, rifle, HMG)
+ RESOURCE_ROCKETS, ///< Rockets (used by mortar, hagar, devastator, etc).
+ RESOURCE_CELLS, ///< Cells (used by electro, crylink, vortex, etc)
+ RESOURCE_PLASMA, ///< Plasma (unused).
+ RESOURCE_FUEL ///< Fuel (used by jetpack).
+};
REGISTER_SP(KILLS);
REGISTER_SP(DEATHS);
REGISTER_SP(SUICIDES);
+REGISTER_SP(TEAMKILLS);
REGISTER_SP(FRAGS);
REGISTER_SP(ELO);
bool autocvar_sv_dodging_frozen;
bool autocvar_sv_dodging_frozen_doubletap;
float autocvar_sv_dodging_height_threshold;
-float autocvar_sv_dodging_horiz_speed;
-float autocvar_sv_dodging_horiz_speed_frozen;
+float autocvar_sv_dodging_horiz_speed_min;
+float autocvar_sv_dodging_horiz_speed_max;
+float autocvar_sv_dodging_horiz_force_slowest;
+float autocvar_sv_dodging_horiz_force_fastest;
+float autocvar_sv_dodging_horiz_force_frozen;
float autocvar_sv_dodging_ramp_time;
float autocvar_sv_dodging_up_speed;
bool autocvar_sv_dodging_wall_dodging;
bool autocvar_sv_dodging_air_dodging;
-float autocvar_sv_dodging_maxspeed = 450;
+float autocvar_sv_dodging_maxspeed;
+float autocvar_sv_dodging_air_maxspeed;
#endif
#if 0
REGISTER_STAT(DODGING, int, g_dodging)
REGISTER_STAT(DODGING_DELAY, float, autocvar_sv_dodging_delay)
REGISTER_STAT(DODGING_DISTANCE_THRESHOLD, float, autocvar_sv_dodging_wall_distance_threshold)
-REGISTER_STAT(DODGING_FROZEN_NO_DOUBLETAP, int, autocvar_sv_dodging_frozen_doubletap)
+REGISTER_STAT(DODGING_FROZEN_DOUBLETAP, int, autocvar_sv_dodging_frozen_doubletap)
REGISTER_STAT(DODGING_HEIGHT_THRESHOLD, float, autocvar_sv_dodging_height_threshold)
-REGISTER_STAT(DODGING_HORIZ_SPEED, float, autocvar_sv_dodging_horiz_speed)
-REGISTER_STAT(DODGING_HORIZ_SPEED_FROZEN, float, autocvar_sv_dodging_horiz_speed_frozen)
+REGISTER_STAT(DODGING_HORIZ_SPEED_MIN, float, autocvar_sv_dodging_horiz_speed_min)
+REGISTER_STAT(DODGING_HORIZ_SPEED_MAX, float, autocvar_sv_dodging_horiz_speed_max)
+REGISTER_STAT(DODGING_HORIZ_FORCE_SLOWEST, float, autocvar_sv_dodging_horiz_force_slowest)
+REGISTER_STAT(DODGING_HORIZ_FORCE_FASTEST, float, autocvar_sv_dodging_horiz_force_fastest)
+REGISTER_STAT(DODGING_HORIZ_FORCE_FROZEN, float, autocvar_sv_dodging_horiz_force_frozen)
REGISTER_STAT(DODGING_RAMP_TIME, float, autocvar_sv_dodging_ramp_time)
REGISTER_STAT(DODGING_UP_SPEED, float, autocvar_sv_dodging_up_speed)
REGISTER_STAT(DODGING_WALL, bool, autocvar_sv_dodging_wall_dodging)
REGISTER_STAT(DODGING_AIR, bool, autocvar_sv_dodging_air_dodging)
REGISTER_STAT(DODGING_MAXSPEED, float, autocvar_sv_dodging_maxspeed)
+REGISTER_STAT(DODGING_AIR_MAXSPEED, float, autocvar_sv_dodging_air_maxspeed)
#endif
/** cvar loopback */
REGISTER_STAT(DODGING_FROZEN, int, autocvar_sv_dodging_frozen)
#ifdef SVQC
#include "physics/movetypes/movetypes.qh"
+float warmup_limit;
#endif
REGISTER_STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, float)
// FIXME: Was 0 on server, 1 on client. Still want that?
REGISTER_STAT(MOVEVARS_ENTGRAVITY, float, (this.gravity) ? this.gravity : 1)
REGISTER_STAT(MOVEVARS_JUMPVELOCITY, float)
+REGISTER_STAT(MOVEVARS_JUMPVELOCITY_CROUCH, float)
REGISTER_STAT(MOVEVARS_MAXAIRSPEED, float)
REGISTER_STAT(MOVEVARS_STEPHEIGHT, float, autocvar_sv_stepheight)
REGISTER_STAT(MOVEVARS_AIRACCEL_QW, float)
Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : ITEM_RESPAWNTIME_INITIAL(e)));
}
+void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names,
+ entity ammo_entity)
+{
+ if (num_weapons == 0)
+ {
+ return;
+ }
+ int num_potential_weapons = tokenize_console(weapon_names);
+ for (int give_attempt = 0; give_attempt < num_weapons; ++give_attempt)
+ {
+ RandomSelection_Init();
+ for (int weapon_index = 0; weapon_index < num_potential_weapons;
+ ++weapon_index)
+ {
+ string weapon = argv(weapon_index);
+ FOREACH(Weapons, it != WEP_Null,
+ {
+ // Finding a weapon which player doesn't have.
+ if (!(receiver.weapons & it.m_wepset) && (it.netname == weapon))
+ {
+ RandomSelection_AddEnt(it, 1, 1);
+ break;
+ }
+ });
+ }
+ if (RandomSelection_chosen_ent == NULL)
+ {
+ return;
+ }
+ receiver.weapons |= RandomSelection_chosen_ent.m_wepset;
+ if (RandomSelection_chosen_ent.ammo_type == RESOURCE_NONE)
+ {
+ continue;
+ }
+ if (GetResourceAmount(receiver,
+ RandomSelection_chosen_ent.ammo_type) != 0)
+ {
+ continue;
+ }
+ GiveResource(receiver, RandomSelection_chosen_ent.ammo_type,
+ GetResourceAmount(ammo_entity,
+ RandomSelection_chosen_ent.ammo_type));
+ }
+}
+
float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammomax)
{
float amount = GetResourceAmount(item, resource_type);
player.superweapons_finished = max(player.superweapons_finished, time) + item.superweapons_finished;
}
-LABEL(skip)
-
// always eat teamed entities
if(item.team)
pickedup = true;
void Item_Touch(entity this, entity toucher)
{
-
// remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky)
- if (this.classname == "droppedweapon")
+ if (Item_IsLoot(this))
{
if (ITEM_TOUCH_NEEDKILL())
{
toucher = M_ARGV(1, entity);
- if (this.classname == "droppedweapon")
+ if (Item_IsExpiring(this))
{
this.strength_finished = max(0, this.strength_finished - time);
this.invincible_finished = max(0, this.invincible_finished - time);
this.superweapons_finished = max(0, this.superweapons_finished - time);
}
- entity it = this.itemdef;
- bool gave = ITEM_HANDLE(Pickup, it, this, toucher);
+ bool gave = ITEM_HANDLE(Pickup, this.itemdef, this, toucher);
if (!gave)
{
- if (this.classname == "droppedweapon")
+ if (Item_IsExpiring(this))
{
// undo what we did above
this.strength_finished += time;
Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1);
_sound (toucher, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM);
- if (this.classname == "droppedweapon")
+ MUTATOR_CALLHOOK(ItemTouched, this, toucher);
+ if (wasfreed(this))
+ {
+ return;
+ }
+
+ if (Item_IsLoot(this))
+ {
delete(this);
- else if (this.spawnshieldtime)
+ return;
+ }
+ if (!this.spawnshieldtime)
{
- entity e;
- if(this.team)
+ return;
+ }
+ entity e;
+ if (this.team)
+ {
+ RandomSelection_Init();
+ IL_EACH(g_items, it.team == this.team,
{
- RandomSelection_Init();
- IL_EACH(g_items, it.team == this.team,
+ if (it.itemdef) // is a registered item
{
- if(it.itemdef) // is a registered item
- {
- Item_Show(it, -1);
- RandomSelection_AddEnt(it, it.cnt, 0);
- }
- });
- e = RandomSelection_chosen_ent;
- Item_Show(e, 1); // reset its state so it is visible (extra sendflags doesn't matter, this happens anyway)
- }
- else
- e = this;
- Item_ScheduleRespawn(e);
+ Item_Show(it, -1);
+ it.scheduledrespawntime = 0;
+ RandomSelection_AddEnt(it, it.cnt, 0);
+ }
+ });
+ e = RandomSelection_chosen_ent;
+ Item_Show(e, 1); // reset its state so it is visible (extra sendflags doesn't matter, this happens anyway)
}
+ else
+ e = this;
+ Item_ScheduleRespawn(e);
}
void Item_Reset(entity this)
Item_Show(this, !this.state);
setorigin(this, this.origin);
- if (this.classname != "droppedweapon")
+ if (Item_IsLoot(this))
{
- setthink(this, Item_Think);
- this.nextthink = time;
-
- if (this.waypointsprite_attached)
- WaypointSprite_Kill(this.waypointsprite_attached);
-
- if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
- Item_ScheduleInitialRespawn(this);
+ return;
+ }
+ setthink(this, Item_Think);
+ this.nextthink = time;
+ if (this.waypointsprite_attached)
+ {
+ WaypointSprite_Kill(this.waypointsprite_attached);
+ }
+ if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially!
+ {
+ Item_ScheduleInitialRespawn(this);
}
}
if(!(player.weapons & (it.m_wepset)))
continue;
- switch(it.ammo_field)
+ switch(it.ammo_type)
{
- case ammo_shells: need_shells = true; break;
- case ammo_nails: need_nails = true; break;
- case ammo_rockets: need_rockets = true; break;
- case ammo_cells: need_cells = true; break;
- case ammo_plasma: need_plasma = true; break;
- case ammo_fuel: need_fuel = true; break;
+ case RESOURCE_SHELLS: need_shells = true; break;
+ case RESOURCE_BULLETS: need_nails = true; break;
+ case RESOURCE_ROCKETS: need_rockets = true; break;
+ case RESOURCE_CELLS: need_cells = true; break;
+ case RESOURCE_PLASMA: need_plasma = true; break;
+ case RESOURCE_FUEL: need_fuel = true; break;
}
});
rating = item.bot_pickupbasevalue;
return;
}
- // is it a dropped weapon?
- if (this.classname == "droppedweapon")
+ if (Item_IsLoot(this))
{
this.reset = SUB_Remove;
- // it's a dropped weapon
set_movetype(this, MOVETYPE_TOSS);
// Savage: remove thrown items after a certain period of time ("garbage collection")
this.takedamage = DAMAGE_YES;
this.event_damage = Item_Damage;
- if(this.strength_finished || this.invincible_finished || this.superweapons_finished)
+ if (Item_IsExpiring(this))
{
// if item is worthless after a timer, have it expire then
this.nextthink = max(this.strength_finished, this.invincible_finished, this.superweapons_finished);
if(def.instanceOfWeaponPickup)
{
- if (this.classname != "droppedweapon") // if dropped, colormap is already set up nicely
+ if (!Item_IsLoot(this)) // if dropped, colormap is already set up nicely
this.colormap = 1024; // color shirt=0 pants=0 grey
else
this.gravity = 1;
void StartItem(entity this, GameItem def)
{
+ def = def.m_spawnfunc_hookreplace(def, this);
+ if (def.spawnflags & ITEM_FLAG_MUTATORBLOCKED)
+ {
+ delete(this);
+ return;
+ }
+ this.classname = def.m_canonical_spawnfunc;
_StartItem(
this,
this.itemdef = def,
}
}
-spawnfunc(item_rockets)
-{
- StartItem(this, ITEM_Rockets);
-}
-
-spawnfunc(item_bullets)
-{
- if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
- (this.classname != "droppedweapon"))
- {
- weaponswapping = true;
- spawnfunc_item_shells(this);
- weaponswapping = false;
- return;
- }
-
- StartItem(this, ITEM_Bullets);
-}
-
-spawnfunc(item_cells)
-{
- StartItem(this, ITEM_Cells);
-}
-
-spawnfunc(item_plasma)
-{
- StartItem(this, ITEM_Plasma);
-}
-
-spawnfunc(item_shells)
-{
- if(!weaponswapping && autocvar_sv_q3acompat_machineshotgunswap &&
- (this.classname != "droppedweapon"))
- {
- weaponswapping = true;
- spawnfunc_item_bullets(this);
- weaponswapping = false;
- return;
- }
-
- StartItem(this, ITEM_Shells);
-}
-
-spawnfunc(item_armor_small)
-{
- StartItem(this, ITEM_ArmorSmall);
-}
-
-spawnfunc(item_armor_medium)
-{
- StartItem(this, ITEM_ArmorMedium);
-}
-
-spawnfunc(item_armor_big)
-{
- StartItem(this, ITEM_ArmorBig);
-}
-
-spawnfunc(item_armor_mega)
-{
- StartItem(this, ITEM_ArmorMega);
-}
-
-spawnfunc(item_health_small)
-{
- StartItem(this, ITEM_HealthSmall);
-}
-
-spawnfunc(item_health_medium)
-{
- StartItem(this, ITEM_HealthMedium);
-}
-
-spawnfunc(item_health_big)
-{
- StartItem(this, ITEM_HealthBig);
-}
-
-spawnfunc(item_health_mega)
-{
- StartItem(this, ITEM_HealthMega);
-}
-
-// support old misnamed entities
-spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); } // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); }
-spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); }
-spawnfunc(item_health1) { spawnfunc_item_health_small(this); }
-spawnfunc(item_health25) { spawnfunc_item_health_medium(this); }
-spawnfunc(item_health_large) { spawnfunc_item_health_big(this); }
-spawnfunc(item_health100) { spawnfunc_item_health_mega(this); }
-
-spawnfunc(item_strength)
-{
- StartItem(this, ITEM_Strength);
-}
-
-spawnfunc(item_invincible)
-{
- StartItem(this, ITEM_Shield);
-}
-
-// compatibility:
-spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength(this);}
-
void target_items_use(entity this, entity actor, entity trigger)
{
- if(actor.classname == "droppedweapon")
+ if(Item_IsLoot(actor))
{
EXACTTRIGGER_TOUCH(this, trigger);
delete(actor);
EXACTTRIGGER_TOUCH(this, trigger);
}
- IL_EACH(g_items, it.enemy == actor && it.classname == "droppedweapon",
+ IL_EACH(g_items, it.enemy == actor && Item_IsLoot(it),
{
delete(it);
});
}
}
-spawnfunc(item_fuel)
-{
- StartItem(this, ITEM_JetpackFuel);
-}
-
-spawnfunc(item_fuel_regen)
-{
- if(start_items & ITEM_JetpackRegen.m_itemid)
- {
- spawnfunc_item_fuel(this);
- return;
- }
- StartItem(this, ITEM_JetpackRegen);
-}
-
-spawnfunc(item_jetpack)
-{
- if(start_items & ITEM_Jetpack.m_itemid)
- {
- spawnfunc_item_fuel(this);
- return;
- }
- StartItem(this, ITEM_Jetpack);
-}
-
float GiveWeapon(entity e, float wpn, float op, float val)
{
WepSet v0, v1;
#pragma once
-#ifdef SVQC
-#include <server/defs.qh>
-#endif
-
const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel
// item networking
#endif
#ifdef SVQC
-spawnfunc(item_strength);
-spawnfunc(item_invincible);
-spawnfunc(item_armor_small);
-spawnfunc(item_shells);
-spawnfunc(item_bullets);
-spawnfunc(item_rockets);
float autocvar_sv_simple_items;
bool ItemSend(entity this, entity to, int sf);
-
bool have_pickup_item(entity this);
const float ITEM_RESPAWN_TICKS = 10;
void Item_ScheduleInitialRespawn(entity e);
+/// \brief Give several random weapons and ammo to the entity.
+/// \param[in,out] receiver Entity to give weapons to.
+/// \param[in] num_weapons Number of weapons to give.
+/// \param[in] weapon_names Names of weapons to give separated by spaces.
+/// \param[in] ammo Entity containing the ammo amount for each possible weapon.
+/// \return No return.
+void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names,
+ entity ammo_entity);
+
float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammomax);
float Item_GiveTo(entity item, entity player);
void func_breakable_init_for_player(entity this, entity player)
{
- if (this.noise1 && this.state == 0 && clienttype(player) == CLIENTTYPE_REAL)
+ if (this.noise1 && this.state == 0 && IS_REAL_CLIENT(player))
{
msg_entity = player;
soundto (MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM);
if(this.spawnflags & DOOR_NOSPLASH)
if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH))
return;
- this.health = this.health - damage;
- if (this.health <= 0)
+ if (this.spawnflags & BUTTON_DONTACCUMULATEDMG)
{
- this.enemy = attacker;
- button_fire(this);
+ if (this.health <= damage)
+ {
+ this.enemy = attacker;
+ button_fire(this);
+ }
+ }
+ else
+ {
+ this.health = this.health - damage;
+ if (this.health <= 0)
+ {
+ this.enemy = attacker;
+ button_fire(this);
+ }
}
}
#pragma once
+
+const int BUTTON_DONTACCUMULATEDMG = 128;
*/
void door_go_down(entity this);
-void door_go_up(entity this);
+void door_go_up(entity this, entity actor, entity trigger);
void door_rotating_go_down(entity this);
void door_rotating_go_up(entity this, entity oth);
if (this.state == STATE_DOWN)
if (this.classname == "door")
{
- door_go_up (this);
+ door_go_up (this, NULL, NULL);
} else
{
door_rotating_go_up(this, blocker);
SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_hit_bottom);
}
-void door_go_up(entity this)
+void door_go_up(entity this, entity actor, entity trigger)
{
if (this.state == STATE_UP)
return; // already going up
string oldmessage;
oldmessage = this.message;
this.message = "";
- SUB_UseTargets(this, NULL, NULL);
+ SUB_UseTargets(this, actor, trigger);
this.message = oldmessage;
}
entity e = this;
do {
if (e.classname == "door") {
- door_go_up(e);
+ door_go_up(e, actor, trigger);
} else {
// if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction
if ((e.spawnflags & 2) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) {
spawnfunc_info_teleport_destination(this);
}
-spawnfunc(target_teleporter)
-{
- spawnfunc_info_teleport_destination(this);
-}
-
#elif defined(CSQC)
void teleport_dest_remove(entity this)
void teleport_findtarget(entity this)
{
+ bool istrigger = (this.solid == SOLID_TRIGGER);
+
int n = 0;
- entity e;
- for(e = NULL; (e = find(e, targetname, this.target)); )
+ for(entity e = NULL; (e = find(e, targetname, this.target)); )
{
++n;
#ifdef SVQC
else if(n == 1)
{
// exactly one dest - bots love that
- this.enemy = find(e, targetname, this.target);
+ this.enemy = find(NULL, targetname, this.target);
}
else
{
}
// now enable touch
- settouch(this, Teleport_Touch);
+ if(istrigger)
+ settouch(this, Teleport_Touch);
#ifdef SVQC
- trigger_teleport_link(this);
+ if(istrigger)
+ trigger_teleport_link(this);
#endif
}
if (!IS_DEAD(toucher))
if (toucher.triggerhealtime < time)
{
- EXACTTRIGGER_TOUCH(this, toucher);
+ bool is_trigger = !boolean(!this.nottargeted && this.targetname != "");
+ if(is_trigger)
+ EXACTTRIGGER_TOUCH(this, toucher);
toucher.triggerhealtime = time + 1;
if (toucher.health < this.max_health)
}
}
+void trigger_heal_use(entity this, entity actor, entity trigger)
+{
+ trigger_heal_touch(this, actor);
+}
+
spawnfunc(trigger_heal)
{
this.active = ACTIVE_ACTIVE;
this.noise = "misc/mediumhealth.wav";
precache_sound(this.noise);
}
+
+spawnfunc(target_heal)
+{
+ this.active = ACTIVE_ACTIVE;
+ this.use = trigger_heal_use;
+ if (!this.health)
+ this.health = 10;
+ if (!this.max_health)
+ this.max_health = 200; //Max health topoff for field
+ if(this.noise == "")
+ this.noise = "misc/mediumhealth.wav";
+ precache_sound(this.noise);
+}
#endif
torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5;
- grav = PHYS_GRAVITY(other);
- if(PHYS_ENTGRAVITY(other))
- grav *= PHYS_ENTGRAVITY(other);
+ grav = PHYS_GRAVITY(tgt);
+ if(PHYS_ENTGRAVITY(tgt))
+ grav *= PHYS_ENTGRAVITY(tgt);
zdist = torg.z - org.z;
sdist = vlen(torg - org - zdist * '0 0 1');
this.use = SUB_UseTargets;
this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully
}
+
+spawnfunc(target_relay) { spawnfunc_trigger_relay(this); }
#endif
}
#endif
-void Teleport_Touch(entity this, entity toucher)
+bool Teleport_Active(entity this, entity player)
{
if (this.active != ACTIVE_ACTIVE)
- return;
+ return false;
#ifdef SVQC
- if (!toucher.teleportable)
- return;
+ if (!player.teleportable)
+ return false;
- if(toucher.vehicle)
- if(!toucher.vehicle.teleportable)
- return;
+ if(player.vehicle)
+ if(!player.vehicle.teleportable)
+ return false;
- if(IS_TURRET(toucher))
- return;
+ if(IS_TURRET(player))
+ return false;
#elif defined(CSQC)
- if(!IS_PLAYER(toucher))
- return;
+ if(!IS_PLAYER(player))
+ return false;
#endif
- if(IS_DEAD(toucher))
- return;
+ if(IS_DEAD(player))
+ return false;
if(this.team)
- if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher)))
- return;
+ if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, player)))
+ return false;
- EXACTTRIGGER_TOUCH(this, toucher);
+ return true;
+}
+
+void Teleport_Touch(entity this, entity toucher)
+{
+ entity player = toucher;
+
+ if(!Teleport_Active(this, player))
+ return;
+
+ EXACTTRIGGER_TOUCH(this, player);
#ifdef SVQC
- if(IS_PLAYER(toucher))
- RemoveGrapplingHooks(toucher);
+ if(IS_PLAYER(player))
+ RemoveGrapplingHooks(player);
#endif
entity e;
- e = Simple_TeleportPlayer(this, toucher);
+ e = Simple_TeleportPlayer(this, player);
#ifdef SVQC
string s = this.target; this.target = string_null;
- SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for trigger too?
+ SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
if (!this.target) this.target = s;
- SUB_UseTargets(e, toucher, toucher);
+ SUB_UseTargets(e, player, player);
#endif
}
+#ifdef SVQC
+void target_teleport_use(entity this, entity actor, entity trigger)
+{
+ entity player = actor;
+
+ if(!Teleport_Active(this, player))
+ return;
+
+ if(IS_PLAYER(player))
+ RemoveGrapplingHooks(player);
+
+ entity e = Simple_TeleportPlayer(this, player);
+
+ string s = this.target; this.target = string_null;
+ SUB_UseTargets(this, player, player); // TODO: should we be using toucher for trigger too?
+ if (!this.target) this.target = s;
+
+ SUB_UseTargets(e, player, player);
+}
+#endif
+
#ifdef SVQC
float trigger_teleport_send(entity this, entity to, float sf)
{
IL_PUSH(g_teleporters, this);
}
+
+spawnfunc(target_teleporter)
+{
+ if(this.target == "")
+ {
+ // actually a destination!
+ spawnfunc_info_teleport_destination(this);
+ return;
+ }
+
+ this.active = ACTIVE_ACTIVE;
+
+ this.use = target_teleport_use;
+
+ if(this.noise != "")
+ FOREACH_WORD(this.noise, true, precache_sound(it));
+
+ InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET);
+}
#elif defined(CSQC)
NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew)
{
e.weapons = result;
}
-string GetAmmoPicture(.int ammotype)
+string GetAmmoPicture(int ammotype)
{
switch (ammotype)
{
- case ammo_shells: return ITEM_Shells.m_icon;
- case ammo_nails: return ITEM_Bullets.m_icon;
- case ammo_rockets: return ITEM_Rockets.m_icon;
- case ammo_cells: return ITEM_Cells.m_icon;
- case ammo_plasma: return ITEM_Plasma.m_icon;
- case ammo_fuel: return ITEM_JetpackFuel.m_icon;
+ case RESOURCE_SHELLS: return ITEM_Shells.m_icon;
+ case RESOURCE_BULLETS: return ITEM_Bullets.m_icon;
+ case RESOURCE_ROCKETS: return ITEM_Rockets.m_icon;
+ case RESOURCE_CELLS: return ITEM_Cells.m_icon;
+ case RESOURCE_PLASMA: return ITEM_Plasma.m_icon;
+ case RESOURCE_FUEL: return ITEM_JetpackFuel.m_icon;
default: return ""; // wtf, no ammo type?
}
}
#ifdef CSQC
- .int GetAmmoFieldFromNum(int i)
+int GetAmmoTypeFromNum(int i)
+{
+ switch (i)
{
- switch (i)
- {
- case 0: return ammo_shells;
- case 1: return ammo_nails;
- case 2: return ammo_rockets;
- case 3: return ammo_cells;
- case 4: return ammo_plasma;
- case 5: return ammo_fuel;
- default: return ammo_none;
- }
+ case 0: return RESOURCE_SHELLS;
+ case 1: return RESOURCE_BULLETS;
+ case 2: return RESOURCE_ROCKETS;
+ case 3: return RESOURCE_CELLS;
+ case 4: return RESOURCE_PLASMA;
+ case 5: return RESOURCE_FUEL;
+ default: return RESOURCE_NONE;
}
+}
- int GetAmmoStat(.int ammotype)
+int GetAmmoStat(int ammotype)
+{
+ switch (ammotype)
{
- switch (ammotype)
- {
- case ammo_shells: return STAT_SHELLS;
- case ammo_nails: return STAT_NAILS;
- case ammo_rockets: return STAT_ROCKETS;
- case ammo_cells: return STAT_CELLS;
- case ammo_plasma: return STAT_PLASMA.m_id;
- case ammo_fuel: return STAT_FUEL.m_id;
- default: return -1;
- }
+ case RESOURCE_SHELLS: return STAT_SHELLS;
+ case RESOURCE_BULLETS: return STAT_NAILS;
+ case RESOURCE_ROCKETS: return STAT_ROCKETS;
+ case RESOURCE_CELLS: return STAT_CELLS;
+ case RESOURCE_PLASMA: return STAT_PLASMA.m_id;
+ case RESOURCE_FUEL: return STAT_FUEL.m_id;
+ default: return -1;
}
+}
#endif
string W_Sound(string w_snd)
#pragma once
+#include <common/resources.qh>
#include <common/items/item/pickup.qh>
#include <common/stats.qh>
/** idle frame */
const int WS_READY = 4;
-#ifdef SVQC
-.int ammo_shells;
-.int ammo_nails;
-.int ammo_rockets;
-.int ammo_cells;
-.int ammo_plasma = _STAT(PLASMA);
-.int ammo_fuel = _STAT(FUEL);
-.int ammo_none;
-#else
-.int ammo_shells;
-.int ammo_nails;
-.int ammo_rockets;
-.int ammo_cells;
-.int ammo_plasma;
-.int ammo_fuel;
-.int ammo_none;
-#endif
-
/** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */
CLASS(Weapon, Object)
ATTRIB(Weapon, m_id, int, 0);
+ /** the canonical spawnfunc name */
+ ATTRIB(Weapon, m_canonical_spawnfunc, string);
+ /** control what happens when this weapon is spawned */
+ METHOD(Weapon, m_spawnfunc_hookreplace, Weapon(Weapon this, entity e)) { return this; }
/** A: WEPSET_id : WEPSET_... */
ATTRIB(Weapon, weapons, WepSet, '0 0 0');
- /** M: ammotype : main ammo field */
- ATTRIB(Weapon, ammo_field, .int, ammo_none);
+ /** M: ammotype : main ammo type */
+ ATTRIB(Weapon, ammo_type, int, RESOURCE_NONE);
/** M: impulse : weapon impulse */
ATTRIB(Weapon, impulse, int, -1);
/** M: flags : WEPSPAWNFLAG_... combined */
}
ENDCLASS(Weapon)
+#ifdef SVQC
+
+void weapon_defaultspawnfunc(entity this, Weapon e);
+#define SPAWNFUNC_WEAPON(name, weapon) \
+ spawnfunc(name) { weapon_defaultspawnfunc(this, weapon); }
+
+#else
+
+#define SPAWNFUNC_WEAPON(name, weapon)
+
+#endif
+
#include <common/items/_mod.qh>
CLASS(WeaponPickup, Pickup)
ATTRIB(WeaponPickup, m_weapon, Weapon);
string W_FixWeaponOrder_ForceComplete(string order);
void W_RandomWeapons(entity e, int n);
-string GetAmmoPicture(.int ammotype);
+string GetAmmoPicture(int ammotype);
#ifdef CSQC
-.int GetAmmoFieldFromNum(int i);
-int GetAmmoStat(.int ammotype);
+int GetAmmoTypeFromNum(int i);
+int GetAmmoStat(int ammotype);
#endif
string W_Sound(string w_snd);
#include "arc.qh"
#ifdef SVQC
-spawnfunc(weapon_arc) { weapon_defaultspawnfunc(this, WEP_ARC); }
bool W_Arc_Beam_Send(entity this, entity to, int sf)
{
if(rootammo)
{
- coefficient = min(coefficient, own.(thiswep.ammo_field) / rootammo);
- own.(thiswep.ammo_field) = max(0, own.(thiswep.ammo_field) - (rootammo * frametime));
+ coefficient = min(coefficient, GetResourceAmount(own, thiswep.ammo_type) / rootammo);
+ SetResourceAmount(own, thiswep.ammo_type, max(0, GetResourceAmount(own, thiswep.ammo_type) - (rootammo * frametime)));
}
}
float heat_speed = burst ? WEP_CVAR(arc, burst_heat) : WEP_CVAR(arc, beam_heat);
}
METHOD(Arc, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- return ((!WEP_CVAR(arc, beam_ammo)) || (actor.(thiswep.ammo_field) > 0));
+ return ((!WEP_CVAR(arc, beam_ammo)) || (GetResourceAmount(actor, thiswep.ammo_type) > 0));
}
METHOD(Arc, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
if(WEP_CVAR(arc, bolt))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(arc, bolt_ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(arc, bolt_ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_ARC.m_id]) >= WEP_CVAR(arc, bolt_ammo);
return ammo_amount;
}
else
return WEP_CVAR(arc, overheat_max) > 0 &&
- ((!WEP_CVAR(arc, burst_ammo)) || (actor.(thiswep.ammo_field) > 0));
+ ((!WEP_CVAR(arc, burst_ammo)) || (GetResourceAmount(actor, thiswep.ammo_type) > 0));
}
METHOD(Arc, wr_killmessage, Notification(entity thiswep))
{
#pragma once
CLASS(Arc, Weapon)
-/* ammotype */ ATTRIB(Arc, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(Arc, m_canonical_spawnfunc, string, "weapon_arc");
+/* ammotype */ ATTRIB(Arc, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(Arc, impulse, int, 3);
/* flags */ ATTRIB(Arc, spawnflags, int, WEP_TYPE_HITSCAN);
/* rating */ ATTRIB(Arc, bot_pickupbasevalue, float, 8000);
ENDCLASS(Arc)
REGISTER_WEAPON(ARC, arc, NEW(Arc));
+SPAWNFUNC_WEAPON(weapon_arc, WEP_ARC)
#ifdef GAMEQC
const float ARC_MAX_SEGMENTS = 20;
#include "blaster.qh"
#ifdef SVQC
-spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); }
-spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); }
void W_Blaster_Touch(entity this, entity toucher)
{
#pragma once
CLASS(Blaster, Weapon)
-/* ammotype */ //ATTRIB(Blaster, ammo_field, .int, ammo_none);
+/* spawnfunc */ ATTRIB(Blaster, m_canonical_spawnfunc, string, "weapon_blaster");
+/* ammotype */ //ATTRIB(Blaster, ammo_type, int, RESOURCE_NONE);
/* impulse */ ATTRIB(Blaster, impulse, int, 1);
/* flags */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0);
ENDCLASS(Blaster)
REGISTER_WEAPON(BLASTER, blaster, NEW(Blaster));
+SPAWNFUNC_WEAPON(weapon_blaster, WEP_BLASTER)
+SPAWNFUNC_WEAPON(weapon_laser, WEP_BLASTER)
+
#ifdef SVQC
.float blaster_damage;
.float blaster_edgedamage;
.float blaster_radius;
.float blaster_force;
.float blaster_lifetime;
+
#endif
#include "crylink.qh"
#ifdef SVQC
-spawnfunc(weapon_crylink) { weapon_defaultspawnfunc(this, WEP_CRYLINK); }
void W_Crylink_CheckLinks(entity e)
{
if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
return true;
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(crylink, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(crylink, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_PRI(crylink, ammo);
return ammo_amount;
}
if(actor.(weaponentity).crylink_lastgroup && actor.(weaponentity).crylink_waitrelease)
return true;
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(crylink, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(crylink, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_CRYLINK.m_id]) >= WEP_CVAR_SEC(crylink, ammo);
return ammo_amount;
}
#pragma once
CLASS(Crylink, Weapon)
-/* ammotype */ ATTRIB(Crylink, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(Crylink, m_canonical_spawnfunc, string, "weapon_crylink");
+/* ammotype */ ATTRIB(Crylink, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(Crylink, impulse, int, 6);
/* flags */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH | WEP_FLAG_CANCLIMB | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Crylink, bot_pickupbasevalue, float, 6000);
ENDCLASS(Crylink)
REGISTER_WEAPON(CRYLINK, crylink, NEW(Crylink));
+SPAWNFUNC_WEAPON(weapon_crylink, WEP_CRYLINK)
+
#ifdef SVQC
.float gravity;
.float crylink_waitrelease;
#include "devastator.qh"
#ifdef SVQC
-spawnfunc(weapon_devastator) { weapon_defaultspawnfunc(this, WEP_DEVASTATOR); }
-spawnfunc(weapon_rocketlauncher) { spawnfunc_weapon_devastator(this); }
.entity lastrocket;
.entity weaponentity = this.weaponentity_fld;
if(this.realowner.(weaponentity).m_weapon == thiswep)
{
- if(this.realowner.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo))
+ if(GetResourceAmount(this.realowner, thiswep.ammo_type) < WEP_CVAR(devastator, ammo))
if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO))
{
this.realowner.cnt = WEP_DEVASTATOR.m_id;
Weapon thiswep = WEP_DEVASTATOR;
if(this.realowner.(weaponentity).m_weapon == thiswep)
{
- if(this.realowner.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo))
+ if(GetResourceAmount(this.realowner, thiswep.ammo_type) < WEP_CVAR(devastator, ammo))
if(!(this.realowner.items & IT_UNLIMITED_WEAPON_AMMO))
{
this.realowner.cnt = WEP_DEVASTATOR.m_id;
ammo_amount = false;
if(WEP_CVAR(devastator, reload_ammo))
{
- if(actor.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo) && actor.(weaponentity).(weapon_load[WEP_DEVASTATOR.m_id]) < WEP_CVAR(devastator, ammo))
+ if(GetResourceAmount(actor, thiswep.ammo_type) < WEP_CVAR(devastator, ammo) && actor.(weaponentity).(weapon_load[WEP_DEVASTATOR.m_id]) < WEP_CVAR(devastator, ammo))
ammo_amount = true;
}
- else if(actor.(thiswep.ammo_field) < WEP_CVAR(devastator, ammo))
+ else if(GetResourceAmount(actor, thiswep.ammo_type) < WEP_CVAR(devastator, ammo))
ammo_amount = true;
return !ammo_amount;
}
#if 0
if(actor.rl_release == 0)
{
- LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE", actor.rl_release, actor.(thiswep.ammo_field), WEP_CVAR(devastator, ammo));
+ LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: TRUE", actor.rl_release, GetResourceAmount(actor, thiswep.ammo_type), WEP_CVAR(devastator, ammo));
return true;
}
else
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(devastator, ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(devastator, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo);
- LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s", actor.rl_release, actor.(thiswep.ammo_field), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE"));
+ LOG_INFOF("W_Devastator(WR_CHECKAMMO1): %d, %.2f, %d: %s", actor.rl_release, GetResourceAmount(actor, thiswep.ammo_type), WEP_CVAR(devastator, ammo), (ammo_amount ? "TRUE" : "FALSE"));
return ammo_amount;
}
#else
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(devastator, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(devastator, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_DEVASTATOR.m_id]) >= WEP_CVAR(devastator, ammo);
return ammo_amount;
#endif
#pragma once
CLASS(Devastator, Weapon)
-/* ammotype */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(Devastator, m_canonical_spawnfunc, string, "weapon_devastator");
+/* ammotype */ ATTRIB(Devastator, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(Devastator, impulse, int, 9);
/* flags */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Devastator, bot_pickupbasevalue, float, 8000);
ENDCLASS(Devastator)
REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator));
+SPAWNFUNC_WEAPON(weapon_devastator, WEP_DEVASTATOR)
+SPAWNFUNC_WEAPON(weapon_rocketlauncher, WEP_DEVASTATOR)
+
#ifdef SVQC
.float rl_release;
.float rl_detonate_later;
#include "electro.qh"
#ifdef SVQC
-spawnfunc(weapon_electro) { weapon_defaultspawnfunc(this, WEP_ELECTRO); }
void W_Electro_TriggerCombo(vector org, float rad, entity own)
{
}
METHOD(Electro, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(electro, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(electro, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_ELECTRO.m_id]) >= WEP_CVAR_PRI(electro, ammo);
return ammo_amount;
}
float ammo_amount;
if(WEP_CVAR(electro, combo_safeammocheck)) // true if you can fire at least one secondary blob AND one primary shot after it, otherwise false.
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_ELECTRO.m_id]) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo);
}
else
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(electro, ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(electro, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_ELECTRO.m_id]) >= WEP_CVAR_SEC(electro, ammo);
}
return ammo_amount;
#pragma once
CLASS(Electro, Weapon)
-/* ammotype */ ATTRIB(Electro, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(Electro, m_canonical_spawnfunc, string, "weapon_electro");
+/* ammotype */ ATTRIB(Electro, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(Electro, impulse, int, 5);
/* flags */ ATTRIB(Electro, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Electro, bot_pickupbasevalue, float, 5000);
ENDCLASS(Electro)
REGISTER_WEAPON(ELECTRO, electro, NEW(Electro));
+SPAWNFUNC_WEAPON(weapon_electro, WEP_ELECTRO)
#ifdef SVQC
.float electro_count;
#include "fireball.qh"
#ifdef SVQC
-spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); }
void W_Fireball_Explode(entity this, entity directhitentity)
{
#pragma once
CLASS(Fireball, Weapon)
-/* ammotype */ //ATTRIB(Fireball, ammo_field, .int, ammo_none);
+/* spawnfunc */ ATTRIB(Fireball, m_canonical_spawnfunc, string, "weapon_fireball");
+/* ammotype */ //ATTRIB(Fireball, ammo_type, int, RESOURCE_NONE);
/* impulse */ ATTRIB(Fireball, impulse, int, 9);
/* flags */ ATTRIB(Fireball, spawnflags, int, WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Fireball, bot_pickupbasevalue, float, 5000);
ENDCLASS(Fireball)
REGISTER_WEAPON(FIREBALL, fireball, NEW(Fireball));
+SPAWNFUNC_WEAPON(weapon_fireball, WEP_FIREBALL)
+
#ifdef SVQC
.float bot_primary_fireballmooth; // whatever a mooth is
.vector fireball_impactvec;
#include "hagar.qh"
#ifdef SVQC
-spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); }
// NO bounce protection, as bounces are limited!
else if(autocvar_g_balance_hagar_reload_ammo)
enough_ammo = actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
else
- enough_ammo = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
+ enough_ammo = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(hagar, ammo);
bool stopped = loaded || !enough_ammo;
}
METHOD(Hagar, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(hagar, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(hagar, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_PRI(hagar, ammo);
return ammo_amount;
}
METHOD(Hagar, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(hagar, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(hagar, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo);
return ammo_amount;
}
#pragma once
CLASS(Hagar, Weapon)
-/* ammotype */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(Hagar, m_canonical_spawnfunc, string, "weapon_hagar");
+/* ammotype */ ATTRIB(Hagar, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(Hagar, impulse, int, 8);
/* flags */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Hagar, bot_pickupbasevalue, float, 6000);
ENDCLASS(Hagar)
REGISTER_WEAPON(HAGAR, hagar, NEW(Hagar));
+
+SPAWNFUNC_WEAPON(weapon_hagar, WEP_HAGAR)
#include "hlac.qh"
#ifdef SVQC
-spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); }
void W_HLAC_Touch(entity this, entity toucher)
{
}
METHOD(HLAC, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(hlac, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(hlac, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HLAC.m_id]) >= WEP_CVAR_PRI(hlac, ammo);
return ammo_amount;
}
METHOD(HLAC, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(hlac, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(hlac, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_HLAC.m_id]) >= WEP_CVAR_SEC(hlac, ammo);
return ammo_amount;
}
#pragma once
CLASS(HLAC, Weapon)
-/* ammotype */ ATTRIB(HLAC, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(HLAC, m_canonical_spawnfunc, string, "weapon_hlac");
+/* ammotype */ ATTRIB(HLAC, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(HLAC, impulse, int, 6);
/* flags */ ATTRIB(HLAC, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(HLAC, bot_pickupbasevalue, float, 4000);
ENDCLASS(HLAC)
REGISTER_WEAPON(HLAC, hlac, NEW(HLAC));
+
+SPAWNFUNC_WEAPON(weapon_hlac, WEP_HLAC)
#ifdef SVQC
-spawnfunc(weapon_hook) { weapon_defaultspawnfunc(this, WEP_HOOK); }
-
void W_Hook_ExplodeThink(entity this)
{
float dt, dmg_remaining_next, f;
#pragma once
CLASS(Hook, Weapon)
-/* ammotype */ ATTRIB(Hook, ammo_field, .int, ammo_fuel);
+/* spawnfunc */ ATTRIB(Hook, m_canonical_spawnfunc, string, "weapon_hook");
+/* ammotype */ ATTRIB(Hook, ammo_type, int, RESOURCE_FUEL);
/* impulse */ ATTRIB(Hook, impulse, int, 0);
/* flags */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Hook, bot_pickupbasevalue, float, 0);
ENDCLASS(Hook)
REGISTER_WEAPON(HOOK, hook, NEW(Hook));
+SPAWNFUNC_WEAPON(weapon_hook, WEP_HOOK)
+
CLASS(OffhandHook, OffhandWeapon)
#ifdef SVQC
METHOD(OffhandHook, offhand_think, void(OffhandHook this, entity actor, bool key_pressed))
#ifdef SVQC
-spawnfunc(weapon_machinegun)
+METHOD(MachineGun, m_spawnfunc_hookreplace, Weapon(MachineGun this, entity e))
{
- if(autocvar_sv_q3acompat_machineshotgunswap)
- if(this.classname != "droppedweapon")
+ if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
{
- weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
- return;
+ return WEP_SHOCKWAVE;
}
- weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
+ return this;
}
-spawnfunc(weapon_uzi) { spawnfunc_weapon_machinegun(this); }
void W_MachineGun_MuzzleFlash_Think(entity this)
{
{
float ammo_amount;
if(WEP_CVAR(machinegun, mode) == 1)
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, sustained_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, sustained_ammo);
else
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
if(WEP_CVAR(machinegun, reload_ammo))
{
{
float ammo_amount;
if(WEP_CVAR(machinegun, mode) == 1)
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, burst_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, burst_ammo);
else
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(machinegun, first_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(machinegun, first_ammo);
if(WEP_CVAR(machinegun, reload_ammo))
{
#pragma once
CLASS(MachineGun, Weapon)
-/* ammotype */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails);
+/* spawnfunc */ ATTRIB(MachineGun, m_canonical_spawnfunc, string, "weapon_machinegun");
+/* ammotype */ ATTRIB(MachineGun, ammo_type, int, RESOURCE_BULLETS);
/* impulse */ ATTRIB(MachineGun, impulse, int, 3);
/* flags */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
/* rating */ ATTRIB(MachineGun, bot_pickupbasevalue, float, 7000);
ENDCLASS(MachineGun)
REGISTER_WEAPON(MACHINEGUN, machinegun, NEW(MachineGun));
+
+SPAWNFUNC_WEAPON(weapon_machinegun, WEP_MACHINEGUN)
+SPAWNFUNC_WEAPON(weapon_uzi, WEP_MACHINEGUN)
#include "minelayer.qh"
#ifdef SVQC
-spawnfunc(weapon_minelayer) { weapon_defaultspawnfunc(this, WEP_MINE_LAYER); }
void W_MineLayer_Stick(entity this, entity to)
{
if(autocvar_g_balance_minelayer_reload_ammo && actor.(weaponentity).clip_load < WEP_CVAR(minelayer, ammo)) // forced reload
{
// not if we're holding the minelayer without enough ammo, but can detonate existing mines
- if(!(W_MineLayer_PlacedMines(actor, weaponentity, false) && actor.(thiswep.ammo_field) < WEP_CVAR(minelayer, ammo))) {
+ if(!(W_MineLayer_PlacedMines(actor, weaponentity, false) && GetResourceAmount(actor, thiswep.ammo_type) < WEP_CVAR(minelayer, ammo))) {
thiswep.wr_reload(thiswep, actor, weaponentity);
}
}
// actually do // don't switch while placing a mine
//if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_MINE_LAYER)
//{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(minelayer, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(minelayer, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_MINE_LAYER.m_id]) >= WEP_CVAR(minelayer, ammo);
return ammo_amount;
//}
#pragma once
CLASS(MineLayer, Weapon)
-/* ammotype */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(MineLayer, m_canonical_spawnfunc, string, "weapon_minelayer");
+/* ammotype */ ATTRIB(MineLayer, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(MineLayer, impulse, int, 4);
/* flags */ ATTRIB(MineLayer, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(MineLayer, bot_pickupbasevalue, float, 7000);
ENDCLASS(MineLayer)
REGISTER_WEAPON(MINE_LAYER, minelayer, NEW(MineLayer));
+SPAWNFUNC_WEAPON(weapon_minelayer, WEP_MINE_LAYER)
+
#ifdef SVQC
void W_MineLayer_Think(entity this);
.float minelayer_detonate, mine_explodeanyway;
#ifdef SVQC
-spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); }
-spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); }
-
void W_Mortar_Grenade_Explode(entity this, entity directhitentity)
{
if(directhitentity.takedamage == DAMAGE_AIM)
}
METHOD(Mortar, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(mortar, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(mortar, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_MORTAR.m_id]) >= WEP_CVAR_PRI(mortar, ammo);
return ammo_amount;
}
METHOD(Mortar, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(mortar, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(mortar, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_MORTAR.m_id]) >= WEP_CVAR_SEC(mortar, ammo);
return ammo_amount;
}
#pragma once
CLASS(Mortar, Weapon)
-/* ammotype */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(Mortar, m_canonical_spawnfunc, string, "weapon_mortar");
+/* ammotype */ ATTRIB(Mortar, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(Mortar, impulse, int, 4);
/* flags */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Mortar, bot_pickupbasevalue, float, 7000);
ENDCLASS(Mortar)
REGISTER_WEAPON(MORTAR, mortar, NEW(Mortar));
+SPAWNFUNC_WEAPON(weapon_mortar, WEP_MORTAR)
+SPAWNFUNC_WEAPON(weapon_grenadelauncher, WEP_MORTAR)
#ifdef SVQC
.float gl_detonate_later;
#ifdef SVQC
#include <common/triggers/trigger/jumppads.qh>
-spawnfunc(weapon_porto) { weapon_defaultspawnfunc(this, WEP_PORTO); }
-
REGISTER_MUTATOR(porto_ticker, true);
MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) {
FOREACH_CLIENT(IS_PLAYER(it), it.porto_forbidden = max(0, it.porto_forbidden - 1));
#pragma once
CLASS(PortoLaunch, Weapon)
-/* ammotype */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none);
+/* spawnfunc */ ATTRIB(PortoLaunch, m_canonical_spawnfunc, string, "weapon_porto");
+/* ammotype */ ATTRIB(PortoLaunch, ammo_type, int, RESOURCE_NONE);
/* impulse */ ATTRIB(PortoLaunch, impulse, int, 0);
/* flags */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0);
ENDCLASS(PortoLaunch)
REGISTER_WEAPON(PORTO, porto, NEW(PortoLaunch));
+SPAWNFUNC_WEAPON(weapon_porto, WEP_PORTO)
+
#ifdef SVQC
.entity porto_current;
.vector porto_v_angle; // holds "held" view angles
#include "rifle.qh"
#ifdef SVQC
-spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); }
-spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); }
-spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); }
void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor)
{
}
METHOD(Rifle, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(rifle, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(rifle, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_RIFLE.m_id]) >= WEP_CVAR_PRI(rifle, ammo);
return ammo_amount;
}
METHOD(Rifle, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(rifle, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(rifle, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_RIFLE.m_id]) >= WEP_CVAR_SEC(rifle, ammo);
return ammo_amount;
}
#pragma once
CLASS(Rifle, Weapon)
-/* ammotype */ ATTRIB(Rifle, ammo_field, .int, ammo_nails);
+/* spawnfunc */ ATTRIB(Rifle, m_canonical_spawnfunc, string, "weapon_rifle");
+/* ammotype */ ATTRIB(Rifle, ammo_type, int, RESOURCE_BULLETS);
/* impulse */ ATTRIB(Rifle, impulse, int, 7);
/* flags */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS);
/* rating */ ATTRIB(Rifle, bot_pickupbasevalue, float, 7000);
ENDCLASS(Rifle)
REGISTER_WEAPON(RIFLE, rifle, NEW(Rifle));
+SPAWNFUNC_WEAPON(weapon_rifle, WEP_RIFLE)
+SPAWNFUNC_WEAPON(weapon_campingrifle, WEP_RIFLE)
+SPAWNFUNC_WEAPON(weapon_sniperrifle, WEP_RIFLE)
#ifdef SVQC
.float rifle_accumulator;
#include "seeker.qh"
#ifdef SVQC
-spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); }
// ============================
// Begin: Missile functions, these are general functions to be manipulated by other code
Weapon thiswep = WEP_SEEKER;
.entity weaponentity = this.weaponentity_fld;
- if((!(this.realowner.items & IT_UNLIMITED_AMMO) && this.realowner.(thiswep.ammo_field) < WEP_CVAR(seeker, missile_ammo)) || (this.cnt <= -1) || (IS_DEAD(this.realowner)) || (this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER))
+ if((!(this.realowner.items & IT_UNLIMITED_AMMO) && GetResourceAmount(this.realowner, thiswep.ammo_type) < WEP_CVAR(seeker, missile_ammo)) || (this.cnt <= -1) || (IS_DEAD(this.realowner)) || (this.realowner.(weaponentity).m_switchweapon != WEP_SEEKER))
{
delete(this);
return;
float ammo_amount;
if(WEP_CVAR(seeker, type) == 1)
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(seeker, missile_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, missile_ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SEEKER.m_id]) >= WEP_CVAR(seeker, missile_ammo);
}
else
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(seeker, tag_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, tag_ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SEEKER.m_id]) >= WEP_CVAR(seeker, tag_ammo);
}
return ammo_amount;
float ammo_amount;
if(WEP_CVAR(seeker, type) == 1)
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(seeker, tag_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, tag_ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SEEKER.m_id]) >= WEP_CVAR(seeker, tag_ammo);
}
else
{
- ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR(seeker, flac_ammo);
+ ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR(seeker, flac_ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SEEKER.m_id]) >= WEP_CVAR(seeker, flac_ammo);
}
return ammo_amount;
#pragma once
CLASS(Seeker, Weapon)
-/* ammotype */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets);
+/* spawnfunc */ ATTRIB(Seeker, m_canonical_spawnfunc, string, "weapon_seeker");
+/* ammotype */ ATTRIB(Seeker, ammo_type, int, RESOURCE_ROCKETS);
/* impulse */ ATTRIB(Seeker, impulse, int, 8);
/* flags */ ATTRIB(Seeker, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH);
/* rating */ ATTRIB(Seeker, bot_pickupbasevalue, float, 5000);
ENDCLASS(Seeker)
REGISTER_WEAPON(SEEKER, seeker, NEW(Seeker));
+SPAWNFUNC_WEAPON(weapon_seeker, WEP_SEEKER)
+
#ifdef SVQC
.entity tag_target, wps_tag_tracker;
.float tag_time;
REGISTER_NET_TEMP(TE_CSQC_SHOCKWAVEPARTICLE)
#ifdef SVQC
-spawnfunc(weapon_shockwave)
+METHOD(Shockwave, m_spawnfunc_hookreplace, Weapon(Shockwave this, entity e))
{
//if(autocvar_sv_q3acompat_machineshockwaveswap) // WEAPONTODO
- if(autocvar_sv_q3acompat_machineshotgunswap)
- if(this.classname != "droppedweapon")
+ if (autocvar_sv_q3acompat_machineshotgunswap && !Item_IsLoot(e))
{
- weapon_defaultspawnfunc(this, WEP_MACHINEGUN);
- return;
+ return WEP_MACHINEGUN;
}
- weapon_defaultspawnfunc(this, WEP_SHOCKWAVE);
+ return this;
}
const float MAX_SHOCKWAVE_HITS = 10;
if(autocvar_g_antilag == 0 || noantilag)
lag = 0; // only do hitscan, but no antilag
if(lag)
- {
- FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag));
- IL_EACH(g_monsters, it != actor,
- {
- antilag_takeback(it, it, time - lag);
- });
- }
+ antilag_takeback_all(actor, lag);
while(head)
{
}
if(lag)
- {
- FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it)));
- IL_EACH(g_monsters, it != actor,
- {
- antilag_restore(it, it);
- });
- }
+ antilag_restore_all(actor);
}
METHOD(Shockwave, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
#pragma once
CLASS(Shockwave, Weapon)
-/* ammotype */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none);
+/* spawnfunc */ ATTRIB(Shockwave, m_canonical_spawnfunc, string, "weapon_shockwave");
+/* ammotype */ //ATTRIB(Shockwave, ammo_type, int, RESOURCE_NONE);
/* impulse */ ATTRIB(Shockwave, impulse, int, 2);
/* flags */ ATTRIB(Shockwave, spawnflags, int, WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_TYPE_MELEE_SEC);
/* rating */ ATTRIB(Shockwave, bot_pickupbasevalue, float, 3000);
ENDCLASS(Shockwave)
REGISTER_WEAPON(SHOCKWAVE, shockwave, NEW(Shockwave));
+SPAWNFUNC_WEAPON(weapon_shockwave, WEP_SHOCKWAVE)
#ifdef CSQC
void Net_ReadShockwaveParticle();
#include "shotgun.qh"
#ifdef SVQC
-spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); }
void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary)
{
}
if(actor.(weaponentity).clip_load >= 0) // we are not currently reloading
if(WEP_CVAR(shotgun, secondary) == 1)
- if(((fire & 1) && actor.(thiswep.ammo_field) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2))
+ if(((fire & 1) && GetResourceAmount(actor, thiswep.ammo_type) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2))
if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire)))
{
// attempt forcing playback of the anim by switching to another anim (that we never play) here...
}
METHOD(Shotgun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(shotgun, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(shotgun, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SHOTGUN.m_id]) >= WEP_CVAR_PRI(shotgun, ammo);
return ammo_amount;
}
case 1: return true; // melee does not use ammo
case 2: // secondary triple shot
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(shotgun, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(shotgun, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_SHOTGUN.m_id]) >= WEP_CVAR_PRI(shotgun, ammo);
return ammo_amount;
}
#pragma once
CLASS(Shotgun, Weapon)
-/* ammotype */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells);
+/* spawnfunc */ ATTRIB(Shotgun, m_canonical_spawnfunc, string, "weapon_shotgun");
+/* ammotype */ ATTRIB(Shotgun, ammo_type, int, RESOURCE_SHELLS);
/* impulse */ ATTRIB(Shotgun, impulse, int, 2);
/* flags */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_TYPE_MELEE_SEC);
/* rating */ ATTRIB(Shotgun, bot_pickupbasevalue, float, 6000);
ENDCLASS(Shotgun)
REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun));
+
+SPAWNFUNC_WEAPON(weapon_shotgun, WEP_SHOTGUN)
.float tuba_lastnotes_cnt; // over
.vector tuba_lastnotes[MAX_TUBANOTES];
-spawnfunc(weapon_tuba) { weapon_defaultspawnfunc(this, WEP_TUBA); }
-
bool W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo)
{
float i, j, mmin, mmax, nolength;
#pragma once
CLASS(Tuba, Weapon)
+/* spawnfunc */ ATTRIB(Tuba, m_canonical_spawnfunc, string, "weapon_tuba");
/* impulse */ ATTRIB(Tuba, impulse, int, 1);
/* flags */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Tuba, bot_pickupbasevalue, float, 2000);
ENDCLASS(Tuba)
REGISTER_WEAPON(TUBA, tuba, NEW(Tuba));
+SPAWNFUNC_WEAPON(weapon_tuba, WEP_TUBA)
+
#ifdef CSQC
entityclass(Tuba);
class(Tuba) .int note;
#endif
#ifdef SVQC
-spawnfunc(weapon_vaporizer) { weapon_defaultspawnfunc(this, WEP_VAPORIZER); }
-spawnfunc(weapon_minstanex) { spawnfunc_weapon_vaporizer(this); }
void W_RocketMinsta_Explosion(entity actor, vector loc)
{
METHOD(Vaporizer, wr_aim, void(entity thiswep, entity actor, .entity weaponentity))
{
- if(actor.(thiswep.ammo_field) > 0)
+ if(GetResourceAmount(actor, thiswep.ammo_type) > 0)
PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 1, false);
else
PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, WEP_CVAR_SEC(vaporizer, speed), 0, WEP_CVAR_SEC(vaporizer, lifetime), false); // WEAPONTODO: replace with proper vaporizer cvars
METHOD(Vaporizer, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo));
- float ammo_amount = actor.(thiswep.ammo_field) >= vaporizer_ammo;
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= vaporizer_ammo;
ammo_amount += actor.(weaponentity).(weapon_load[WEP_VAPORIZER.m_id]) >= vaporizer_ammo;
return ammo_amount;
}
{
if(!WEP_CVAR_SEC(vaporizer, ammo))
return true;
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(vaporizer, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(vaporizer, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_VAPORIZER.m_id]) >= WEP_CVAR_SEC(vaporizer, ammo);
return ammo_amount;
}
#pragma once
CLASS(Vaporizer, Weapon)
-/* ammotype */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(Vaporizer, m_canonical_spawnfunc, string, "weapon_vaporizer");
+/* ammotype */ ATTRIB(Vaporizer, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(Vaporizer, impulse, int, 7);
/* flags */ ATTRIB(Vaporizer, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Vaporizer, bot_pickupbasevalue, float, 10000);
ENDCLASS(Vaporizer)
REGISTER_WEAPON(VAPORIZER, vaporizer, NEW(Vaporizer));
+SPAWNFUNC_WEAPON(weapon_vaporizer, WEP_VAPORIZER)
+SPAWNFUNC_WEAPON(weapon_minstanex, WEP_VAPORIZER)
#ifdef SVQC
.float vaporizer_lasthit;
#endif
#ifdef SVQC
-spawnfunc(weapon_vortex) { weapon_defaultspawnfunc(this, WEP_VORTEX); }
-spawnfunc(weapon_nex) { spawnfunc_weapon_vortex(this); }
REGISTER_MUTATOR(vortex_charge, true);
}
else
{
- dt = min(dt, (actor.(thiswep.ammo_field) - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
+ dt = min(dt, (GetResourceAmount(actor, thiswep.ammo_type) - WEP_CVAR_PRI(vortex, ammo)) / WEP_CVAR_SEC(vortex, ammo));
dt = max(0, dt);
if(dt > 0)
{
- actor.(thiswep.ammo_field) = max(WEP_CVAR_SEC(vortex, ammo), actor.(thiswep.ammo_field) - WEP_CVAR_SEC(vortex, ammo) * dt);
+ SetResourceAmount(actor, thiswep.ammo_type, max(WEP_CVAR_SEC(vortex, ammo), GetResourceAmount(actor, thiswep.ammo_type) - WEP_CVAR_SEC(vortex, ammo) * dt));
}
}
}
}
METHOD(Vortex, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity))
{
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_PRI(vortex, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(vortex, ammo);
ammo_amount += (autocvar_g_balance_vortex_reload_ammo && actor.(weaponentity).(weapon_load[WEP_VORTEX.m_id]) >= WEP_CVAR_PRI(vortex, ammo));
return ammo_amount;
}
if(WEP_CVAR(vortex, secondary))
{
// don't allow charging if we don't have enough ammo
- float ammo_amount = actor.(thiswep.ammo_field) >= WEP_CVAR_SEC(vortex, ammo);
+ float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(vortex, ammo);
ammo_amount += actor.(weaponentity).(weapon_load[WEP_VORTEX.m_id]) >= WEP_CVAR_SEC(vortex, ammo);
return ammo_amount;
}
#pragma once
CLASS(Vortex, Weapon)
-/* ammotype */ ATTRIB(Vortex, ammo_field, .int, ammo_cells);
+/* spawnfunc */ ATTRIB(Vortex, m_canonical_spawnfunc, string, "weapon_vortex");
+/* ammotype */ ATTRIB(Vortex, ammo_type, int, RESOURCE_CELLS);
/* impulse */ ATTRIB(Vortex, impulse, int, 7);
/* flags */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_NODUAL);
/* rating */ ATTRIB(Vortex, bot_pickupbasevalue, float, 8000);
ENDCLASS(Vortex)
REGISTER_WEAPON(VORTEX, vortex, NEW(Vortex));
+SPAWNFUNC_WEAPON(weapon_vortex, WEP_VORTEX)
+SPAWNFUNC_WEAPON(weapon_nex, WEP_VORTEX)
#ifdef SVQC
-
.float vortex_lasthit;
#endif
|| CS(this).impulse == 15
|| CS(this).impulse == 18
|| (CS(this).impulse >= 200 && CS(this).impulse <= 209)
- ) { this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); } else if (CS(this).impulse == 11) {
+ ) {
+ this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5);
+ } else if (CS(this).impulse == 11) {
this.spectatorspeed = maxspeed_mod;
} else if (CS(this).impulse == 12
|| CS(this).impulse == 16
void SV_OnEntityPreSpawnFunction()
{
ENGINE_EVENT();
- if (_SV_OnEntityPreSpawnFunction) _SV_OnEntityPreSpawnFunction(this);
+ __spawnfunc_expecting = true;
+ __spawnfunc_expect = this;
}
#define SV_OnEntityPreSpawnFunction _SV_OnEntityPreSpawnFunction
#include "sort.qh"
#include "yenc.qh"
+// netcode mismatch and not sure what caused it? developer_csqcentities 1
+
.string netname;
.int m_id;
.bool(entity this, entity sender, bool isNew) m_read;
#define NET_HANDLE(id, param) bool Net_Handle_##id(entity this, entity sender, param)
+#define NET_GUARD(id) \
+ bool Net_Handle_##id##_guard(entity this, entity sender, bool isNew) { \
+ bool valid = false; \
+ serialize_marker(to, valid); \
+ if (!valid) LOG_FATALF("Last message not fully parsed: %s", _net_prevmsgstr); \
+ _net_prevmsgstr = #id; \
+ return Net_Handle_##id(this, sender, isNew); \
+ }
#ifdef CSQC
+string _net_prevmsgstr;
#define REGISTER_NET_TEMP(id) \
NET_HANDLE(id, bool); \
- REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) \
- { \
+ NET_GUARD(id); \
+ REGISTER(TempEntities, NET, id, m_id, new_pure(net_temp_packet)) { \
this.netname = #id; \
- this.m_read = Net_Handle_##id; \
+ this.m_read = Net_Handle_##id##_guard; \
}
#else
#define REGISTER_NET_TEMP(id) \
this.sourceLoc = __FILE__ ":" STR(__LINE__); \
if (!this) isnew = true; \
} \
+ NET_GUARD(id); \
REGISTER(LinkedEntities, NET, id, m_id, new_pure(net_linked_packet)) \
{ \
this.netname = #id; \
- this.m_read = Net_Handle_##id; \
+ this.m_read = Net_Handle_##id##_guard; \
}
#else
#define REGISTER_NET_LINKED(id) \
MACRO_BEGIN { \
if (NET_##id##_istemp) WriteByte(to, SVC_TEMPENTITY); \
WriteByte(to, NET_##id.m_id); \
+ bool _net_valid = false; serialize_marker(to, _net_valid); \
} MACRO_END
#endif
#define stream_writing(stream) false
#endif
-#define serialize(T, stream, ...) serialize_##T(stream, __VA_ARGS__)
+#define serialize(T, stream, ...) \
+MACRO_BEGIN \
+ noref Stream _stream = stream; \
+ serialize_##T(_stream, __VA_ARGS__); \
+MACRO_END
#if defined(SVQC)
#define serialize_byte(stream, this) \
#endif
#define serialize_vector(stream, this) \
- MACRO_BEGIN \
+MACRO_BEGIN \
vector _v = this; \
serialize_float(stream, _v.x); \
serialize_float(stream, _v.y); \
serialize_float(stream, _v.z); \
this = _v; \
- MACRO_END
+MACRO_END
+
+#define serialize_marker(stream, this) \
+MACRO_BEGIN \
+ if (NDEBUG) { \
+ this = true; \
+ } else { \
+ int _de = 0xDE, _ad = 0xAD, _be = 0xBE, _ef = 0xEF; \
+ serialize_byte(stream, _de); \
+ serialize_byte(stream, _ad); \
+ serialize_byte(stream, _be); \
+ serialize_byte(stream, _ef); \
+ this = (_de == 0xDE && _ad == 0xAD && _be == 0xBE && _ef == 0xEF); \
+ } \
+MACRO_END
// serialization: old
#define EVAL_REGISTER_REGISTRY(...) __VA_ARGS__
#define REGISTER_REGISTRY_1(id) REGISTER_REGISTRY_2(id, #id)
#define REGISTER_REGISTRY_2(id, str) \
- ACCUMULATE_FUNCTION(__static_init, Register##id) \
+ ACCUMULATE_FUNCTION(__static_init_1, Register##id) \
CLASS(id##Registry, Object) \
ATTRIB(id##Registry, m_name, string, str); \
ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first); \
#define SPAWNFUNC_INTERNAL_FIELDS(X) \
X(string, classname, "spawnfunc") \
+ X(string, target, string_null) \
+ X(string, target2, string_null) \
+ X(string, target3, string_null) \
+ X(string, target4, string_null) \
X(string, targetname, string_null) \
/**/
g_map_entities = IL_NEW(); \
IL_EACH(g_spawn_queue, true, __spawnfunc_spawn(it)); \
MACRO_END
-
+#ifdef SVQC
+ void _SV_OnEntityPreSpawnFunction(entity this);
+#endif
void __spawnfunc_spawn(entity prototype)
{
entity e = new(clone);
#define X(T, fld, def) { e.fld = e.__spawnfunc_##fld; e.__spawnfunc_##fld = def; }
SPAWNFUNC_INTERNAL_FIELDS(X);
#undef X
+#ifdef SVQC
+ _SV_OnEntityPreSpawnFunction(e);
+ if (wasfreed(e)) {
+ return;
+ }
+#endif
e.__spawnfunc_constructor(e);
}
+ noref bool __spawnfunc_first;
+
#define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION)
#define spawnfunc_2(id, whitelist) \
void __spawnfunc_##id(entity this); \
[[accumulate]] void spawnfunc_##id(entity this) \
{ \
+ if (!__spawnfunc_first) { \
+ __spawnfunc_first = true; \
+ static_init_early(); \
+ } \
bool dospawn = true; \
if (__spawnfunc_expecting > 1) { __spawnfunc_expecting = false; } \
else if (__spawnfunc_expecting) { \
#pragma once
-void __static_init() {}
-#define static_init() CALL_ACCUMULATED_FUNCTION(__static_init)
-void __static_init_late() {}
-#define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_late)
-void __static_init_precache() {}
-#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_precache)
-void __shutdown() {}
-#define shutdownhooks() CALL_ACCUMULATED_FUNCTION(__shutdown)
-
#define GETTIME_REALTIME 1
#ifdef MENUQC
float(int tmr) _gettime = #67;
LOG_TRACEF("[%f] %s", rt - g_starttime, s);
}
-#define _STATIC_INIT(where, func) \
+#define _STATIC_INIT(func, where) \
[[accumulate]] void _static_##func() { profile(#func); } \
ACCUMULATE_FUNCTION(where, _static_##func) \
void _static_##func()
-#define STATIC_INIT(func) _STATIC_INIT(__static_init, func)
-#define STATIC_INIT_LATE(func) _STATIC_INIT(__static_init_late, func##_late)
-#define PRECACHE(func) _STATIC_INIT(__static_init_precache, func##_precache)
-#define SHUTDOWN(func) _STATIC_INIT(__shutdown, func##_shutdown)
+/** before worldspawn */
+#define STATIC_INIT_EARLY(func) _STATIC_INIT(func##_0, __static_init_0)
+#define static_init_early() CALL_ACCUMULATED_FUNCTION(__static_init_0)
+void __static_init_0() {}
+
+/** during worldspawn */
+#define STATIC_INIT(func) _STATIC_INIT(func##_1, __static_init_1)
+#define static_init() CALL_ACCUMULATED_FUNCTION(__static_init_1)
+void __static_init_1() {}
+
+/** directly after STATIC_INIT */
+#define STATIC_INIT_LATE(func) _STATIC_INIT(func##_2, __static_init_2)
+#define static_init_late() CALL_ACCUMULATED_FUNCTION(__static_init_2)
+void __static_init_2() {}
+
+/** directly after STATIC_INIT_LATE */
+#define PRECACHE(func) _STATIC_INIT(func##_3, __static_init_3)
+#define static_init_precache() CALL_ACCUMULATED_FUNCTION(__static_init_3)
+void __static_init_3() {}
+
+/* other map entities spawn now */
+
+/** before shutdown */
+#define SHUTDOWN(func) _STATIC_INIT(func##_shutdown, __shutdown)
+#define shutdownhooks() CALL_ACCUMULATED_FUNCTION( __shutdown)
+void __shutdown() {}
return l;
}
+//List of Unicode spaces: https://www.cs.tut.fi/~jkorpela/chars/spaces.html
ERASEABLE
bool isInvisibleString(string s)
{
case 192: // charmap space
if (!utf8) break;
return false;
- case 160: // space in unicode fonts
case 0xE000 + 192: // utf8 charmap space
+ case 0x00A0: // NO-BREAK SPACE
+ //case 0x1680: // OGHAM SPACE MARK
+ case 0x180E: // MONGOLIAN VOWEL SEPARATOR
+ case 0x2000: // EN QUAD
+ case 0x2001: // EM QUAD
+ case 0x2002: // EN SPACE
+ case 0x2003: // EM SPACE
+ case 0x2004: // THREE-PER-EM SPACE
+ case 0x2005: // FOUR-PER-EM SPACE
+ case 0x2006: // SIX-PER-EM SPACE
+ case 0x2007: // FIGURE SPACE
+ case 0x2008: // PUNCTUATION SPACE
+ case 0x2009: // THIN SPACE
+ case 0x200A: // HAIR SPACE
+ case 0x200B: // ZERO WIDTH SPACE
+ case 0x202F: // NARROW NO-BREAK SPACE
+ case 0x205F: // MEDIUM MATHEMATICAL SPACE
+ case 0x3000: // IDEOGRAPHIC SPACE
+ case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
if (utf8) break;
default:
return false;
vector m1 = e.maxs;
e.mins = '0 0 0';
e.maxs = '0 0 0';
- WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins.x = m0.x;
- WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs.x = m1.x;
- WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins.y = m0.y;
- WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs.y = m1.y;
- WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins.z = m0.z;
- WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs.z = m1.z;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m0.x); e.mins_x = m0.x;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eX * m1.x); e.maxs_x = m1.x;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m0.y); e.mins_y = m0.y;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eY * m1.y); e.maxs_y = m1.y;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m0.z); e.mins_z = m0.z;
+ WarpZoneLib_MoveOutOfSolid_Expand(e, eZ * m1.z); e.maxs_z = m1.z;
setorigin(e, e.origin);
tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_WORLDONLY, e);
break;
}
me.encryptLabel.setText(me.encryptLabel, me.currentServerEncrypt);
+ setZonedTooltip(me.encryptLabel, _("Use the `crypto_aeslevel` cvar to change your preferences"), string_null);
s = crypto_getidfp(me.currentServerCName);
if (!s) { s = _("N/A"); }
#include <server/g_models.qc>
#include <server/g_subs.qc>
#include <server/g_world.qc>
+#include <server/handicap.qc>
#include <server/impulse.qc>
#include <server/ipban.qc>
#include <server/item_key.qc>
+#include <server/items.qc>
#include <server/mapvoting.qc>
#include <server/matrix.qc>
#include <server/miscfunctions.qc>
#include <server/g_models.qh>
#include <server/g_subs.qh>
#include <server/g_world.qh>
+#include <server/handicap.qh>
#include <server/impulse.qh>
#include <server/ipban.qh>
#include <server/item_key.qh>
+#include <server/items.qh>
#include <server/mapvoting.qh>
#include <server/matrix.qh>
#include <server/miscfunctions.qh>
}
void anticheat_report_to_playerstats(entity this) {
- PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime);
+ PlayerStats_GameReport_Event_Player(this,
+ strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime);
#define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \
- PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, name), f)
+ PlayerStats_GameReport_Event_Player(this, strcat(PLAYERSTATS_ANTICHEAT, name), f)
ANTICHEATS(ANTICHEAT_REPORT_ONE);
#undef ANTICHEAT_REPORT_ONE
}
}
store.antilag_index = ANTILAG_MAX_ORIGINS - 1; // next one is 0
}
+
+// TODO: use a single intrusive list across all antilagged entities
+void antilag_takeback_all(entity ignore, float lag)
+{
+ FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_takeback(it, CS(it), time - lag));
+ IL_EACH(g_monsters, it != ignore,
+ {
+ antilag_takeback(it, it, time - lag);
+ });
+ IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+ {
+ antilag_takeback(it, it, time - lag);
+ });
+}
+
+void antilag_restore_all(entity ignore)
+{
+ FOREACH_CLIENT(IS_PLAYER(it) && it != ignore, antilag_restore(it, CS(it)));
+ IL_EACH(g_monsters, it != ignore,
+ {
+ antilag_restore(it, it);
+ });
+ IL_EACH(g_projectiles, it != ignore && it.classname == "nade",
+ {
+ antilag_restore(it, it);
+ });
+}
void antilag_restore(entity e, entity store);
void antilag_clear(entity e, entity store);
+void antilag_takeback_all(entity ignore, float lag);
+void antilag_restore_all(entity ignore);
+
.float antilag_debug;
#define ANTILAG_LATENCY(e) min(0.4, CS(e).ping * 0.001)
float autocvar_g_balance_selfdamagepercent;
bool autocvar_g_balance_teams;
bool autocvar_g_balance_teams_prevent_imbalance;
-float autocvar_g_balance_teams_scorefactor;
+//float autocvar_g_balance_teams_scorefactor;
float autocvar_g_ballistics_density_corpse;
float autocvar_g_ballistics_density_player;
float autocvar_g_ballistics_mindistance;
float autocvar_sv_jumpspeedcap_max_disable_on_ramps;
string autocvar_sv_jumpspeedcap_min;
float autocvar_sv_jumpvelocity;
+float autocvar_sv_jumpvelocity_crouch;
bool autocvar_sv_logscores_bots;
bool autocvar_sv_logscores_console;
bool autocvar_sv_logscores_file;
#include <server/defs.qh>
#include <server/miscfunctions.qh>
+#include <server/items.qh>
#include "havocbot.qh"
#include "../cvars.qh"
continue;
// Check if the item can be picked up safely
- if(it.classname == "droppedweapon")
+ if(Item_IsLoot(it))
{
if(!IS_ONGROUND(it))
continue;
#include "spawnpoints.qh"
#include "resources.qh"
#include "g_damage.qh"
+#include "handicap.qh"
#include "g_hook.qh"
#include "command/common.qh"
#include "cheats.qh"
bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this);
PlayerState_detach(this);
- if (IS_PLAYER(this) && this.health >= 1) {
- // despawn effect
- Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+ if (IS_PLAYER(this))
+ {
+ if(this.health >= 1)
+ {
+ // despawn effect
+ Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1);
+ }
+
+ // was a player, recount votes and ready status
+ if(IS_REAL_CLIENT(this))
+ {
+ if (vote_called) { VoteCount(false); }
+ ReadyCount();
+ }
}
{
if (this.alivetime)
{
if (!warmup_stage)
- PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+ PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
this.alivetime = 0;
}
if (mutator_returnvalue) {
// mutator prevents resetting teams+score
} else {
+ int oldteam = this.team;
this.team = -1; // move this as it is needed to log the player spectating in eventlog
+ MUTATOR_CALLHOOK(Player_ChangedTeam, this, oldteam, this.team);
this.frags = FRAGS_SPECTATOR;
PlayerScore_Clear(this); // clear scores when needed
}
this.flags |= FL_NOTARGET;
this.takedamage = DAMAGE_AIM;
this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT;
- this.dmg = 2; // WTF
if (warmup_stage) {
this.ammo_shells = warmup_start_ammo_shells;
this.health = start_health;
this.armorvalue = start_armorvalue;
this.weapons = start_weapons;
+ GiveRandomWeapons(this, random_start_weapons_count,
+ autocvar_g_random_start_weapons, random_start_ammo);
}
SetSpectatee_status(this, 0);
PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false;
+ // player was spectator
if (CS(this).killcount == FRAGS_SPECTATOR) {
PlayerScore_Clear(this);
CS(this).killcount = 0;
+ CS(this).startplaytime = time;
}
for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot)
.float clientkill_nexttime;
void ClientKill_Now_TeamChange(entity this)
{
- if(CS(this).killindicator_teamchange == -1)
+ if(this.killindicator_teamchange == -1)
{
JoinBestTeam( this, false, true );
}
- else if(CS(this).killindicator_teamchange == -2)
+ else if(this.killindicator_teamchange == -2)
{
if(blockSpectators)
Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime);
PutObserverInServer(this);
}
else
- SV_ChangeTeam(this, CS(this).killindicator_teamchange - 1);
- CS(this).killindicator_teamchange = 0;
+ SV_ChangeTeam(this, this.killindicator_teamchange - 1);
+ this.killindicator_teamchange = 0;
}
void ClientKill_Now(entity this)
if(this.vehicle)
{
vehicles_exit(this.vehicle, VHEF_RELEASE);
- if(!CS(this).killindicator_teamchange)
+ if(!this.killindicator_teamchange)
{
this.vehicle_health = -1;
Damage(this, this, this, 1 , DEATH_KILL.m_id, this.origin, '0 0 0');
this.killindicator = NULL;
- if(CS(this).killindicator_teamchange)
+ if(this.killindicator_teamchange)
ClientKill_Now_TeamChange(this);
- if(!IS_SPEC(this) && !IS_OBSERVER(this))
+ if (!IS_SPEC(this) && !IS_OBSERVER(this) && MUTATOR_CALLHOOK(ClientKill_Now, this) == false)
+ {
Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0');
+ }
// now I am sure the player IS dead
}
return;
killtime = M_ARGV(1, float);
- CS(this).killindicator_teamchange = targetteam;
+ this.killindicator_teamchange = targetteam;
if(!this.killindicator)
{
CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects
- CS(this).netname_previous = strzone(this.netname);
-
if(teamplay && IS_PLAYER(this))
Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname);
else
it.init_for_player(it, this);
});
+ Handicap_Initialize(this);
+
MUTATOR_CALLHOOK(ClientConnect, this);
if (IS_REAL_CLIENT(this))
// WORKAROUND: only use dropclient in server frames (frametime set).
// Never use it in cl_movement frames (frametime zero).
checkSpectatorBlock(this);
- }
+ }
zoomstate_set = false;
// Check for nameless players
- if (isInvisibleString(this.netname)) {
- this.netname = strzone(sprintf("Player#%d", this.playerid));
- // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
- }
- if (this.netname != CS(this).netname_previous) {
- if (autocvar_sv_eventlog) {
+ if (this.netname == "" || this.netname != CS(this).netname_previous)
+ {
+ bool assume_unchanged = (CS(this).netname_previous == "");
+ if (isInvisibleString(this.netname))
+ {
+ this.netname = strzone(sprintf("Player#%d", this.playerid));
+ assume_unchanged = false;
+ // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe?
+ }
+ if (!assume_unchanged && autocvar_sv_eventlog)
GameLogEcho(strcat(":name:", ftos(this.playerid), ":", playername(this, false)));
- }
if (CS(this).netname_previous) strunzone(CS(this).netname_previous);
CS(this).netname_previous = strzone(this.netname);
}
if(this.air_finished < time)
PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND);
this.air_finished = time + autocvar_g_balance_contents_drowndelay;
- this.dmg = 2;
}
else if (this.air_finished < time)
{ // drown!
ATTRIB(Client, parm_idlesince, int, this.parm_idlesince);
ATTRIB(Client, muted, bool, this.muted);
- ATTRIB(Client, killindicator_teamchange, int, this.killindicator_teamchange);
ATTRIB(Client, idlekick_lasttimeleft, float, this.idlekick_lasttimeleft);
ATTRIB(Client, pm_frametime, float, this.pm_frametime);
ATTRIB(Client, pressedkeys, int, this.pressedkeys);
ATTRIB(Client, motd_actived_time, float, this.motd_actived_time);
ATTRIB(Client, jointime, float, this.jointime);
ATTRIB(Client, spectatortime, float, this.spectatortime);
+ ATTRIB(Client, startplaytime, float, this.startplaytime);
ATTRIB(Client, version_nagtime, float, this.version_nagtime);
ATTRIB(Client, netname_previous, string, this.netname_previous);
ATTRIB(Client, allowed_timeouts, int, this.allowed_timeouts);
return false;
}
-float c1, c2, c3, c4;
-
void play_countdown(entity this, float finished, Sound samp);
float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit);
{
case CMD_REQUEST_COMMAND:
{
- if (argv(1) != "")
+ if (argv(1) == "")
{
- if (IS_CLIENT(caller))
+ return;
+ }
+ if (!IS_CLIENT(caller))
+ {
+ return;
+ }
+ if (!teamplay)
+ {
+ sprint(caller, "^7selectteam can only be used in teamgames\n");
+ return;
+ }
+ if (caller.team_forced > 0)
+ {
+ sprint(caller, "^7selectteam can not be used as your team is forced\n");
+ return;
+ }
+ if (lockteams)
+ {
+ sprint(caller, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");
+ return;
+ }
+ float selection;
+ switch (argv(1))
+ {
+ case "red":
{
- if (teamplay)
- {
- if (caller.team_forced <= 0)
- {
- if (!lockteams)
- {
- float selection;
-
- switch (argv(1))
- {
- case "red": selection = NUM_TEAM_1;
- break;
- case "blue": selection = NUM_TEAM_2;
- break;
- case "yellow": selection = NUM_TEAM_3;
- break;
- case "pink": selection = NUM_TEAM_4;
- break;
- case "auto": selection = (-1);
- break;
-
- default: selection = 0;
- break;
- }
-
- if (selection)
- {
- if (caller.team == selection && selection != -1 && !IS_DEAD(caller))
- {
- sprint(caller, "^7You already are on that team.\n");
- }
- else if (CS(caller).wasplayer && autocvar_g_changeteam_banned)
- {
- sprint(caller, "^1You cannot change team, forbidden by the server.\n");
- }
- else
- {
- if (autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
- {
- CheckAllowedTeams(caller);
- GetTeamCounts(caller);
- if (!TeamSmallerEqThanTeam(Team_TeamToNumber(selection), Team_TeamToNumber(caller.team), caller))
- {
- Send_Notification(NOTIF_ONE, caller, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
- return;
- }
- }
- ClientKill_TeamChange(caller, selection);
- }
- if(!IS_PLAYER(caller))
- caller.team_selected = true; // avoids asking again for team selection on join
- }
- }
- else
- {
- sprint(caller, "^7The game has already begun, you must wait until the next map to be able to join a team.\n");
- }
- }
- else
- {
- sprint(caller, "^7selectteam can not be used as your team is forced\n");
- }
- }
- else
- {
- sprint(caller, "^7selectteam can only be used in teamgames\n");
- }
+ selection = NUM_TEAM_1;
+ break;
+ }
+ case "blue":
+ {
+ selection = NUM_TEAM_2;
+ break;
+ }
+ case "yellow":
+ {
+ selection = NUM_TEAM_3;
+ break;
+ }
+ case "pink":
+ {
+ selection = NUM_TEAM_4;
+ break;
}
+ case "auto":
+ {
+ selection = (-1);
+ break;
+ }
+ default:
+ {
+ return;
+ }
+ }
+ if (caller.team == selection && selection != -1 && !IS_DEAD(caller))
+ {
+ sprint(caller, "^7You already are on that team.\n");
+ return;
+ }
+ if (CS(caller).wasplayer && autocvar_g_changeteam_banned)
+ {
+ sprint(caller, "^1You cannot change team, forbidden by the server.\n");
return;
}
+ if ((selection != -1) && autocvar_g_balance_teams &&
+ autocvar_g_balance_teams_prevent_imbalance)
+ {
+ CheckAllowedTeams(caller);
+ GetTeamCounts(caller);
+ if ((BIT(Team_TeamToNumber(selection) - 1) & FindBestTeams(caller, false)) == 0)
+ {
+ Send_Notification(NOTIF_ONE, caller, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
+ return;
+ }
+ }
+ ClientKill_TeamChange(caller, selection);
+ if (!IS_PLAYER(caller))
+ {
+ caller.team_selected = true; // avoids asking again for team selection on join
+ }
+ return;
}
-
default:
sprint(caller, "Incorrect parameters for ^2selectteam^7\n");
case CMD_REQUEST_USAGE:
// If so, lets continue and finally move the player
client.team_forced = 0;
- MoveToTeam(client, team_id, 6);
- successful = strcat(successful, (successful ? ", " : ""), playername(client, false));
- LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ") has been moved to the ", Team_ColoredFullName(team_id), "^7.");
+ if (MoveToTeam(client, team_id, 6))
+ {
+ successful = strcat(successful, (successful ? ", " : ""), playername(client, false));
+ LOG_INFO("Player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ") has been moved to the ", Team_ColoredFullName(team_id), "^7.");
+ }
+ else
+ {
+ LOG_INFO("Unable to move player ", ftos(GetFilteredNumber(t)), " (", playername(client, false), ")");
+ }
continue;
}
else
{
for (i = 1; i <= maxclients; i += 8)
{
- for (f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
- if (!IS_REAL_CLIENT(e) || e.ready) f |= b;
+ for (f = 0, e = edict_num(i), b = BIT(0); b < BIT(8); b <<= 1, e = nextent(e))
+ if (!IS_REAL_CLIENT(e) || e.ready)
+ f |= b;
WriteByte(MSG_ENTITY, f);
}
}
FOREACH_CLIENT(IS_PLAYER(it), {
it.alivetime = 0;
CS(it).killcount = 0;
- PS_GR_P_ADDVAL(it, PLAYERSTATS_ALIVETIME, -PS_GR_P_ADDVAL(it, PLAYERSTATS_ALIVETIME, 0));
+ float val = PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, 0);
+ PlayerStats_GameReport_Event_Player(it, PLAYERSTATS_ALIVETIME, -val);
});
restart_mapalreadyrestarted = false; // reset this var, needed when cvar sv_ready_restart_repeatable is in use
int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc)
{
- string first_command;
+ string first_command = argv(startpos);
+ int missing_chars = argv_start_index(startpos);
- first_command = argv(startpos);
-
- /*printf("VoteCommand_parse(): Command: '%s', Length: %f.\n",
- substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos)),
- strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos)))
- );*/
-
- if (
- (autocvar_sv_vote_limit > 0)
- &&
- (strlen(substring(vote_command, argv_start_index(startpos), strlen(vote_command) - argv_start_index(startpos))) > autocvar_sv_vote_limit)
- ) return 0;
+ if (autocvar_sv_vote_limit > 0 && strlen(vote_command) > autocvar_sv_vote_limit)
+ return 0;
if (!VoteCommand_checkinlist(first_command, vote_list)) return 0;
if (accepted > 0)
{
- string reason = ((argc > next_token) ? substring(vote_command, argv_start_index(next_token), strlen(vote_command) - argv_start_index(next_token)) : "No reason provided");
- string command_arguments;
+ string reason = "No reason provided";
+ if(argc > next_token)
+ reason = substring(vote_command, argv_start_index(next_token) - missing_chars, -1);
- if (first_command == "kickban") command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
- else command_arguments = reason;
+ string command_arguments = reason;
+ if (first_command == "kickban")
+ command_arguments = strcat(ftos(autocvar_g_ban_default_bantime), " ", ftos(autocvar_g_ban_default_masksize), " ~");
vote_parsed_command = strcat(first_command, " # ", ftos(etof(victim)), " ", command_arguments);
- vote_parsed_display = strcat("^1", vote_command, " (^7", victim.netname, "^1): ", reason);
+ vote_parsed_display = sprintf("^1%s #%d ^7%s^1 %s", first_command, etof(victim), victim.netname, reason);
}
else { print_to(caller, strcat("vcall: ", GetClientErrorString(accepted, argv(startpos + 1)), ".\n")); return 0; }
if(parse_error == 0)
print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
}
-
else // everything went okay, continue with calling the vote
{
vote_caller = caller; // remember who called the vote
}
FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; });
- if (tmp_playercount > 1) Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL); // don't announce a "vote now" sound if player is alone
+ if (tmp_playercount > 1)
+ Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL);
bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote for ", vote_called_display, "\n");
- if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
+ if (autocvar_sv_eventlog)
+ GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
Nagger_VoteChanged();
VoteCount(true); // needed if you are the only one
}
int parse_error;
vote_command = VoteCommand_extractcommand(vote_command, 3, argc);
- if (!caller.vote_master) { print_to(caller, "^1You do not have vote master privelages."); }
+ if (!caller.vote_master)
+ print_to(caller, "^1You do not have vote master privileges.");
else if (!VoteCommand_checknasty(vote_command))
{
print_to(caller, "^1Syntax error in command, see 'vhelp' for more info.");
if(parse_error == 0)
print_to(caller, "^1This command is not acceptable, see 'vhelp' for more info.");
}
-
else // everything went okay, proceed with command
{
localcmd(strcat(vote_parsed_command, "\n"));
print_to(caller, strcat("Executing command '", vote_parsed_display, "' on server."));
bprint("\{1}^2* ^3", GetCallerName(caller), "^2 used their ^3master^2 status to do \"^2", vote_parsed_display, "^2\".\n");
- if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display)); }
+ if (autocvar_sv_eventlog)
+ GameLogEcho(strcat(":vote:vdo:", ftos(caller.playerid), ":", vote_parsed_display));
+ }
return;
}
{
print_to(caller, strcat("Rejected vote master login from ", GetCallerName(caller)));
}
-
else // everything went okay, proceed with giving this player master privilages
{
caller.vote_master = true;
print_to(caller, strcat("Accepted vote master login from ", GetCallerName(caller)));
bprint("\{1}^2* ^3", GetCallerName(caller), "^2 logged in as ^3master^2\n");
- if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid))); }
+ if (autocvar_sv_eventlog)
+ GameLogEcho(strcat(":vote:vlogin:", ftos(caller.playerid)));
+ }
return;
}
{
print_to(caller, "^1You can not call a vote while a timeout is active.");
}
-
else // everything went okay, continue with creating vote
{
vote_caller = caller;
caller.vote_waittime = time + autocvar_sv_vote_wait;
bprint("\{1}^2* ^3", OriginalCallerName(), "^2 calls a vote to become ^3master^2.\n");
- if (autocvar_sv_eventlog) GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
+ if (autocvar_sv_eventlog)
+ GameLogEcho(strcat(":vote:vcall:", ftos(vote_caller.playerid), ":", vote_called_display));
Nagger_VoteChanged();
VoteCount(true); // needed if you are the only one
}
print_to(caller, "^1You rejected the vote.");
caller.vote_selection = VOTE_SELECT_REJECT;
msg_entity = caller;
- if (!autocvar_sv_vote_singlecount) VoteCount(false); }
+ if (!autocvar_sv_vote_singlecount)
+ VoteCount(false);
+ }
return;
}
{
print_to(caller, "^1You have already voted.");
}
-
else // everything went okay, continue changing vote
{
print_to(caller, "^1You accepted the vote.");
caller.vote_selection = VOTE_SELECT_ACCEPT;
msg_entity = caller;
- if (!autocvar_sv_vote_singlecount) VoteCount(false); }
+ if (!autocvar_sv_vote_singlecount)
+ VoteCount(false);
+ }
return;
}
#include <server/miscfunctions.qh>
#include <common/weapons/_all.qh>
-spawnfunc(weapon_electro);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_machinegun);
-spawnfunc(item_bullets);
-spawnfunc(item_armor_mega);
-spawnfunc(item_health_mega);
-spawnfunc(item_health_medium);
-
//***********************
//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons
//***********************
-spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);}
-spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);}
-spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);}
+SPAWNFUNC_WEAPON(weapon_nailgun, WEP_ELECTRO)
+SPAWNFUNC_WEAPON(weapon_supernailgun, WEP_HAGAR)
+SPAWNFUNC_WEAPON(weapon_supershotgun, WEP_MACHINEGUN)
-spawnfunc(item_spikes) {spawnfunc_item_bullets(this);}
+SPAWNFUNC_ITEM(item_spikes, ITEM_Bullets)
//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);} // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
-spawnfunc(item_armor2) {spawnfunc_item_armor_mega(this);}
-spawnfunc(item_armorInv) {spawnfunc_item_armor_mega(this);} // TODO: make sure we actually want this
-spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);}
-
-//spawnfunc_item_spikes
-//spawnfunc_item_health
-
-
-
+SPAWNFUNC_ITEM(item_armor2, ITEM_ArmorMega)
+SPAWNFUNC_ITEM(item_armorInv, ITEM_ArmorMega) // TODO: make sure we actually want this
+spawnfunc(item_health) {if (this.spawnflags & 2) StartItem(this, ITEM_HealthMega);else StartItem(this, ITEM_HealthMedium);}
#include "quake2.qh"
-spawnfunc(item_armor_medium);
-
-spawnfunc(item_invincible);
-
+#include <common/items/_mod.qh>
//***********************
//QUAKE 2 ENTITIES - So people can play quake2 maps with the xonotic weapons
//***********************
-spawnfunc(item_armor_jacket) {spawnfunc_item_armor_medium(this);}
+SPAWNFUNC_ITEM(item_armor_jacket, ITEM_ArmorMedium)
-spawnfunc(item_invulnerability) {spawnfunc_item_invincible(this);}
+SPAWNFUNC_ITEM(item_invulnerability, ITEM_Shield)
// rest of the quake 2 entities are handled by q1 and q3 compat
#include <server/defs.qh>
#include <server/miscfunctions.qh>
+#include <server/items.qh>
#include <common/weapons/_all.qh>
-spawnfunc(weapon_crylink);
-spawnfunc(weapon_electro);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_hook);
-spawnfunc(weapon_machinegun);
-spawnfunc(weapon_vortex);
-spawnfunc(weapon_minelayer);
-
spawnfunc(target_items);
-spawnfunc(item_bullets);
-spawnfunc(item_cells);
-spawnfunc(item_rockets);
-spawnfunc(item_shells);
-
-spawnfunc(item_strength);
-
-spawnfunc(item_armor_big);
-spawnfunc(item_armor_mega);
-spawnfunc(item_armor_small);
-
-spawnfunc(item_health_medium);
-spawnfunc(item_health_mega);
-
//***********************
//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons
//***********************
// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG
// SG -> SG
-spawnfunc(ammo_shells) { spawnfunc_item_shells(this); }
+SPAWNFUNC_ITEM(ammo_shells, ITEM_Shells)
// MG -> MG
-spawnfunc(ammo_bullets) { spawnfunc_item_bullets(this); }
+SPAWNFUNC_ITEM(ammo_bullets, ITEM_Bullets)
// GL -> Mortar
-spawnfunc(ammo_grenades) { spawnfunc_item_rockets(this); }
+SPAWNFUNC_ITEM(ammo_grenades, ITEM_Rockets)
// Mines -> Rockets
-spawnfunc(weapon_prox_launcher) { spawnfunc_weapon_minelayer(this); }
-spawnfunc(ammo_mines) { spawnfunc_item_rockets(this); }
+SPAWNFUNC_WEAPON(weapon_prox_launcher, WEP_MINE_LAYER)
+SPAWNFUNC_ITEM(ammo_mines, ITEM_Rockets)
// LG -> Lightning
-spawnfunc(weapon_lightning) { spawnfunc_weapon_electro(this); }
-spawnfunc(ammo_lightning) { spawnfunc_item_cells(this); }
+SPAWNFUNC_WEAPON(weapon_lightning, WEP_ELECTRO)
+SPAWNFUNC_ITEM(ammo_lightning, ITEM_Cells)
// Plasma -> Hagar
-spawnfunc(weapon_plasmagun) { spawnfunc_weapon_hagar(this); }
-spawnfunc(ammo_cells) { spawnfunc_item_rockets(this); }
+SPAWNFUNC_WEAPON(weapon_plasmagun, WEP_HAGAR)
+SPAWNFUNC_ITEM(ammo_cells, ITEM_Rockets)
// Rail -> Vortex
-spawnfunc(weapon_railgun) { spawnfunc_weapon_vortex(this); }
-spawnfunc(ammo_slugs) { spawnfunc_item_cells(this); }
+SPAWNFUNC_WEAPON(weapon_railgun, WEP_VORTEX)
+SPAWNFUNC_ITEM(ammo_slugs, ITEM_Cells)
// BFG -> Crylink
-spawnfunc(weapon_bfg) { spawnfunc_weapon_crylink(this); }
-spawnfunc(ammo_bfg) { spawnfunc_item_cells(this); }
+SPAWNFUNC_WEAPON(weapon_bfg, WEP_CRYLINK)
+SPAWNFUNC_ITEM(ammo_bfg, ITEM_Cells)
// grappling hook -> hook
-spawnfunc(weapon_grapplinghook) { spawnfunc_weapon_hook(this); }
+SPAWNFUNC_WEAPON(weapon_grapplinghook, WEP_HOOK)
// RL -> RL
-spawnfunc(ammo_rockets) { spawnfunc_item_rockets(this); }
+SPAWNFUNC_ITEM(ammo_rockets, ITEM_Rockets)
// Armor
-spawnfunc(item_armor_body) { spawnfunc_item_armor_mega(this); }
-spawnfunc(item_armor_combat) { spawnfunc_item_armor_big(this); }
-spawnfunc(item_armor_shard) { spawnfunc_item_armor_small(this); }
-spawnfunc(item_enviro) { spawnfunc_item_invincible(this); }
+SPAWNFUNC_ITEM(item_armor_body, ITEM_ArmorMega)
+SPAWNFUNC_ITEM(item_armor_combat, ITEM_ArmorBig)
+SPAWNFUNC_ITEM(item_armor_shard, ITEM_ArmorSmall)
+SPAWNFUNC_ITEM(item_enviro, ITEM_Shield)
// medkit -> armor (we have no holdables)
-spawnfunc(holdable_medkit) { spawnfunc_item_armor_mega(this); }
+SPAWNFUNC_ITEM(holdable_medkit, ITEM_ArmorMega)
// doubler -> strength
-spawnfunc(item_doubler) { spawnfunc_item_strength(this); }
+SPAWNFUNC_ITEM(item_doubler, ITEM_Strength)
.float wait;
.float delay;
{
IL_EACH(g_items, it.targetname == this.target,
{
- if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") {
+ if (it.classname == "weapon_devastator") {
this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo);
this.netname = cons(this.netname, "devastator");
}
- else if (it.classname == "weapon_lightning") {
+ else if (it.classname == "weapon_vortex") {
+ this.ammo_cells += it.count * WEP_CVAR_PRI(vortex, ammo); // WEAPONTODO
+ this.netname = cons(this.netname, "vortex");
+ }
+ else if (it.classname == "weapon_electro") {
this.ammo_cells += it.count * WEP_CVAR_PRI(electro, ammo); // WEAPONTODO
this.netname = cons(this.netname, "electro");
}
- else if (it.classname == "weapon_plasmagun") {
+ else if (it.classname == "weapon_hagar") {
this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO
this.netname = cons(this.netname, "hagar");
}
- else if (it.classname == "weapon_bfg") {
+ else if (it.classname == "weapon_crylink") {
this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo);
this.netname = cons(this.netname, "crylink");
}
- else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") {
+ else if (it.classname == "weapon_mortar") {
this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO
this.netname = cons(this.netname, "mortar");
}
- else if (it.classname == "item_armor_body")
+ else if (it.classname == "item_armor_mega")
this.armorvalue = 100;
else if (it.classname == "item_health_mega")
this.health = 200;
#include <server/defs.qh>
#include <server/miscfunctions.qh>
+#include <server/items.qh>
#include <common/weapons/_all.qh>
-// #include <server/mutators/gamemode.qh>
-
-spawnfunc(weapon_arc);
-spawnfunc(weapon_crylink);
-spawnfunc(weapon_electro);
-spawnfunc(weapon_mortar);
-spawnfunc(weapon_hagar);
-spawnfunc(weapon_machinegun);
-spawnfunc(weapon_devastator);
-spawnfunc(weapon_shotgun);
-spawnfunc(weapon_vortex);
-
-spawnfunc(item_armor_big);
-spawnfunc(item_armor_mega);
-spawnfunc(item_armor_small);
-
-spawnfunc(item_bullets);
-spawnfunc(item_cells);
-spawnfunc(item_quad);
-spawnfunc(item_rockets);
-spawnfunc(item_shells);
-
-spawnfunc(item_jetpack);
spawnfunc(item_haste);
-spawnfunc(item_health_medium);
-spawnfunc(item_health_mega);
spawnfunc(item_invis);
//***********************
-//WORD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
+//WORLD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons
//***********************
//spawnfunc(item_revival) /* handled by buffs mutator */
//spawnfunc(item_jumper) /* handled by buffs mutator */
-spawnfunc(weapon_punchy) { spawnfunc_weapon_arc(this); }
-spawnfunc(weapon_nipper) { spawnfunc_weapon_machinegun(this); }
-spawnfunc(weapon_pumper) { spawnfunc_weapon_shotgun(this); }
-spawnfunc(weapon_boaster) { spawnfunc_weapon_electro(this); }
-spawnfunc(weapon_splasher) { spawnfunc_weapon_vortex(this); }
-spawnfunc(weapon_bubbleg) { spawnfunc_weapon_hagar(this); }
-spawnfunc(weapon_balloony) { spawnfunc_weapon_mortar(this); }
-spawnfunc(weapon_betty) { spawnfunc_weapon_devastator(this); }
-spawnfunc(weapon_imperius) { spawnfunc_weapon_crylink(this); }
-
-spawnfunc(ammo_pumper) { spawnfunc_item_shells(this); }
-spawnfunc(ammo_nipper) { spawnfunc_item_bullets(this); }
-spawnfunc(ammo_balloony) { spawnfunc_item_rockets(this); }
-spawnfunc(ammo_bubbleg) { spawnfunc_item_rockets(this); }
-spawnfunc(ammo_boaster) { spawnfunc_item_cells(this); }
-spawnfunc(ammo_betty) { spawnfunc_item_rockets(this); }
-spawnfunc(ammo_imperius) { spawnfunc_item_cells(this); }
-
-spawnfunc(item_padpower) { spawnfunc_item_quad(this); }
-spawnfunc(item_climber) { spawnfunc_item_invincible(this); }
+SPAWNFUNC_WEAPON(weapon_punchy, WEP_ARC)
+SPAWNFUNC_WEAPON(weapon_nipper, WEP_MACHINEGUN)
+SPAWNFUNC_WEAPON(weapon_pumper, WEP_SHOTGUN)
+SPAWNFUNC_WEAPON(weapon_boaster, WEP_ELECTRO)
+SPAWNFUNC_WEAPON(weapon_splasher, WEP_VORTEX)
+SPAWNFUNC_WEAPON(weapon_bubbleg, WEP_HAGAR)
+SPAWNFUNC_WEAPON(weapon_balloony, WEP_MORTAR)
+SPAWNFUNC_WEAPON(weapon_betty, WEP_DEVASTATOR)
+SPAWNFUNC_WEAPON(weapon_imperius, WEP_CRYLINK)
+
+SPAWNFUNC_ITEM(ammo_pumper, ITEM_Shells)
+SPAWNFUNC_ITEM(ammo_nipper, ITEM_Bullets)
+SPAWNFUNC_ITEM(ammo_balloony, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_bubbleg, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_boaster, ITEM_Cells)
+SPAWNFUNC_ITEM(ammo_betty, ITEM_Rockets)
+SPAWNFUNC_ITEM(ammo_imperius, ITEM_Cells)
+
+SPAWNFUNC_ITEM(item_padpower, ITEM_Strength)
+SPAWNFUNC_ITEM(item_climber, ITEM_Shield)
spawnfunc(item_speedy) { spawnfunc_item_haste(this); }
spawnfunc(item_visionless) { spawnfunc_item_invis(this); }
-spawnfunc(item_armor_padshield) { spawnfunc_item_armor_mega(this); }
+SPAWNFUNC_ITEM(item_armor_padshield, ITEM_ArmorMega)
-spawnfunc(holdable_floater) { spawnfunc_item_jetpack(this); }
+SPAWNFUNC_ITEM(holdable_floater, ITEM_Jetpack)
#pragma once
-float warmup_limit;
#include <common/weapons/_all.qh>
#include <common/stats.qh>
float game_completion_ratio; // 0 at start, 1 near end
.float winning;
-.float jointime; // time of joining
+.float jointime; // time of connecting
+.float startplaytime; // time of switching from spectator to player
.float alivetime; // time of being alive
.float motd_actived_time; // used for both motd and campaign_message
.float stat_respawn_time = _STAT(RESPAWN_TIME); // shows respawn time, and is negative when awaiting respawn
+.int killindicator_teamchange;
+
void PlayerUseKey(entity this);
USING(spawn_evalfunc_t, vector(entity this, entity player, entity spot, vector current));
else
{
// teamkill
- GameRules_scoring_add(attacker, KILLS, -1); // or maybe add a teamkills field?
+ GameRules_scoring_add(attacker, TEAMKILLS, 1);
}
}
else
{
// regular frag
GameRules_scoring_add(attacker, KILLS, 1);
- if(targ.playerid)
- PS_GR_P_ADDVAL(attacker, sprintf("kills-%d", targ.playerid), 1);
+ if(!warmup_stage && targ.playerid)
+ PlayerStats_GameReport_Event_Player(attacker, sprintf("kills-%d", targ.playerid), 1);
}
GameRules_scoring_add(targ, DEATHS, 1);
string s1, string s2, string s3,
float f1, float f2, float f3)
{
- if(DEATH_ISSPECIAL(deathtype))
+ if(!DEATH_ISSPECIAL(deathtype))
{
- entity deathent = Deathtypes_from(deathtype - DT_FIRST);
- if (!deathent) { backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n"); return; }
+ backtrace("Obituary_SpecialDeath called without a special deathtype?\n");
+ return;
+ }
- if(g_cts && deathtype == DEATH_KILL.m_id)
- return; // TODO: somehow put this in CTS gamemode file!
+ entity deathent = Deathtypes_from(deathtype - DT_FIRST);
+ if (!deathent)
+ {
+ backtrace("Obituary_SpecialDeath: Could not find deathtype entity!\n");
+ return;
+ }
- if(murder)
- {
- if(deathent.death_msgmurder)
- {
- Send_Notification_WOCOVA(
- NOTIF_ONE,
- notif_target,
- MSG_MULTI,
- deathent.death_msgmurder,
- s1, s2, s3, "",
- f1, f2, f3, 0
- );
- Send_Notification_WOCOVA(
- NOTIF_ALL_EXCEPT,
- notif_target,
- MSG_INFO,
- deathent.death_msgmurder.nent_msginfo,
- s1, s2, s3, "",
- f1, f2, f3, 0
- );
- }
- }
- else
- {
- if(deathent.death_msgself)
- {
- Send_Notification_WOCOVA(
- NOTIF_ONE,
- notif_target,
- MSG_MULTI,
- deathent.death_msgself,
- s1, s2, s3, "",
- f1, f2, f3, 0
- );
- Send_Notification_WOCOVA(
- NOTIF_ALL_EXCEPT,
- notif_target,
- MSG_INFO,
- deathent.death_msgself.nent_msginfo,
- s1, s2, s3, "",
- f1, f2, f3, 0
- );
- }
- }
+ if(g_cts && deathtype == DEATH_KILL.m_id)
+ return; // TODO: somehow put this in CTS gamemode file!
+
+ Notification death_message = (murder) ? deathent.death_msgmurder : deathent.death_msgself;
+ if(death_message)
+ {
+ Send_Notification_WOCOVA(
+ NOTIF_ONE,
+ notif_target,
+ MSG_MULTI,
+ death_message,
+ s1, s2, s3, "",
+ f1, f2, f3, 0
+ );
+ Send_Notification_WOCOVA(
+ NOTIF_ALL_EXCEPT,
+ notif_target,
+ MSG_INFO,
+ death_message.nent_msginfo,
+ s1, s2, s3, "",
+ f1, f2, f3, 0
+ );
}
- else { backtrace("Obituary_SpecialDeath called without a special deathtype?\n"); return; }
}
float Obituary_WeaponDeath(
float f1, float f2)
{
Weapon death_weapon = DEATH_WEAPONOF(deathtype);
- if (death_weapon != WEP_Null)
- {
- w_deathtype = deathtype;
- Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
- w_deathtype = false;
+ if (death_weapon == WEP_Null)
+ return false;
- if (death_message)
- {
- Send_Notification_WOCOVA(
- NOTIF_ONE,
- notif_target,
- MSG_MULTI,
- death_message,
- s1, s2, s3, "",
- f1, f2, 0, 0
- );
- // send the info part to everyone
- Send_Notification_WOCOVA(
- NOTIF_ALL_EXCEPT,
- notif_target,
- MSG_INFO,
- death_message.nent_msginfo,
- s1, s2, s3, "",
- f1, f2, 0, 0
- );
- }
- else
- {
- LOG_TRACEF(
- "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %d!\n",
- deathtype,
- death_weapon
- );
- }
+ w_deathtype = deathtype;
+ Notification death_message = ((murder) ? death_weapon.wr_killmessage(death_weapon) : death_weapon.wr_suicidemessage(death_weapon));
+ w_deathtype = false;
- return true;
+ if (death_message)
+ {
+ Send_Notification_WOCOVA(
+ NOTIF_ONE,
+ notif_target,
+ MSG_MULTI,
+ death_message,
+ s1, s2, s3, "",
+ f1, f2, 0, 0
+ );
+ // send the info part to everyone
+ Send_Notification_WOCOVA(
+ NOTIF_ALL_EXCEPT,
+ notif_target,
+ MSG_INFO,
+ death_message.nent_msginfo,
+ s1, s2, s3, "",
+ f1, f2, 0, 0
+ );
}
- return false;
+ else
+ {
+ LOG_TRACEF(
+ "Obituary_WeaponDeath(): ^1Deathtype ^7(%d)^1 has no notification for weapon %d!\n",
+ deathtype,
+ death_weapon
+ );
+ }
+
+ return true;
}
bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target)
attacker.killsound += 1;
+ // TODO: improve SPREE_ITEM and KILL_SPREE_LIST
+ // these 2 macros are spread over multiple files
#define SPREE_ITEM(counta,countb,center,normal,gentle) \
case counta: \
{ \
Send_Notification(NOTIF_ONE, attacker, MSG_ANNCE, ANNCE_KILLSTREAK_##countb); \
- PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+ if (!warmup_stage)\
+ {\
+ PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_##counta, 1); \
+ }\
break; \
}
switch(CS(attacker).killcount)
}
#undef SPREE_ITEM
- if(!checkrules_firstblood)
+ if(!warmup_stage && !checkrules_firstblood)
{
checkrules_firstblood = true;
notif_firstblood = true; // modify the current messages so that they too show firstblood information
- PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
- PS_GR_P_ADDVAL(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
+ PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD, 1);
+ PlayerStats_GameReport_Event_Player(targ, PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM, 1);
// tell spree_inf and spree_cen that this is a first-blood and first-victim event
kill_count_to_attacker = -1;
if(GameRules_scoring_add(targ, SCORE, 0) == -5)
{
Send_Notification(NOTIF_ONE, targ, MSG_ANNCE, ANNCE_ACHIEVEMENT_BOTLIKE);
- PS_GR_P_ADDVAL(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+ if (!warmup_stage)
+ {
+ PlayerStats_GameReport_Event_Player(attacker, PLAYERSTATS_ACHIEVEMENT_BOTLIKE, 1);
+ }
}
}
source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE;
if (lag)
- {
- // take players back into the past
- FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag));
- IL_EACH(g_monsters, it != forent,
- {
- antilag_takeback(it, it, time - lag);
- });
- }
+ antilag_takeback_all(forent, lag);
// do the trace
if(wz)
// restore players to current positions
if (lag)
- {
- FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it)));
- IL_EACH(g_monsters, it != forent,
- {
- antilag_restore(it, it);
- });
- }
+ antilag_restore_all(forent);
// restore shooter solid type
if(source)
e = new(info_player_deathmatch); // safeguard against player joining
- this.classname = "worldspawn"; // safeguard against various stuff ;)
+ // assign reflectively to avoid "assignment to world" warning
+ for (int i = 0, n = numentityfields(); i < n; ++i) {
+ string k = entityfieldname(i);
+ if (k == "classname") {
+ // safeguard against various stuff ;)
+ putentityfieldstring(i, this, "worldspawn");
+ break;
+ }
+ }
// needs to be done so early because of the constants they create
static_init();
MapInfo_Shutdown();
}
+STATIC_INIT_EARLY(maxclients)
+{
+ maxclients = 0;
+ for (entity head = nextent(NULL); head; head = nextent(head)) {
+ ++maxclients;
+ }
+}
+
void Map_MarkAsRecent(string m);
float world_already_spawned;
void Nagger_Init();
cvar_changes_init(); // do this very early now so it REALLY matches the server config
- maxclients = 0;
- for (entity head = nextent(NULL); head; head = nextent(head))
- {
- ++maxclients;
- }
-
// needs to be done so early because of the constants they create
static_init();
{
antilag_record(it, it, altime);
});
+ IL_EACH(g_projectiles, it.classname == "nade",
+ {
+ antilag_record(it, it, altime);
+ });
systems_update();
IL_ENDFRAME();
}
--- /dev/null
+#include "handicap.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the handicap system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+#include <common/state.qh>
+#include "client.qh"
+
+.float m_handicap; ///< Holds the handicap value.
+
+void Handicap_Initialize(entity player)
+{
+ CS(player).m_handicap = 1;
+}
+
+float Handicap_GetVoluntaryHandicap(entity player)
+{
+ return bound(1.0, CS(player).cvar_cl_handicap, 10.0);
+}
+
+float Handicap_GetForcedHandicap(entity player)
+{
+ return CS(player).m_handicap;
+}
+
+void Handicap_SetForcedHandicap(entity player, float value)
+{
+ if (value <= 0)
+ {
+ error("Handicap_SetForcedHandicap: Invalid handicap value.");
+ }
+ CS(player).m_handicap = value;
+}
+
+float Handicap_GetTotalHandicap(entity player)
+{
+ return Handicap_GetForcedHandicap(player) * Handicap_GetVoluntaryHandicap(
+ player);
+}
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes the handicap system.
+/// \author Lyberta
+/// \copyright GNU GPLv2 or any later version.
+
+// Handicap is used to make the game harder for strong players and easier for
+// weak players. Values greater than 1 make the game harder and values less than
+// 1 make the game easier. Right now handicap only affects damage. There are 2
+// types of handicap: voluntary and forced. Voluntary handicap can be set via
+// cl_handicap cvar. For obvious reasons, it can't be less than 1. Forced
+// handicap can be set by server mutators. The total handicap is the product of
+// voluntary and forced handicap.
+
+/// \brief Initializes handicap to its default value.
+/// \param[in,out] player Player to initialize.
+/// \return No return.
+void Handicap_Initialize(entity player);
+
+/// \brief Returns the voluntary handicap of the player.
+/// \param[in] player Player to check.
+/// \return Voluntary handicap of the player.
+float Handicap_GetVoluntaryHandicap(entity player);
+
+/// \brief Returns the forced handicap of the player.
+/// \param[in] player Player to check.
+/// \return Forced handicap of the player.
+float Handicap_GetForcedHandicap(entity player);
+
+/// \brief Sets the forced handicap of the player.
+/// \param[in] player Player to alter.
+/// \param[in] value Handicap value to set.
+/// \return No return.
+void Handicap_SetForcedHandicap(entity player, float value);
+
+/// \brief Returns the total handicap of the player.
+/// \param[in] player Player to check.
+/// \return Total handicap of the player.
+float Handicap_GetTotalHandicap(entity player);
--- /dev/null
+#include "items.qh"
+
+/// \file
+/// \brief Source file that contains implementation of the functions related to
+/// game items.
+/// \copyright GNU GPLv2 or any later version.
+
+#include "g_subs.qh"
+#include <common/weapons/all.qh>
+
+.bool m_isloot; ///< Holds whether item is loot.
+/// \brief Holds whether strength, shield or superweapon timers expire while
+/// this item is on the ground.
+.bool m_isexpiring;
+
+entity Item_Create(string class_name, vector position, bool no_align)
+{
+ entity item = spawn();
+ item.classname = class_name;
+ item.spawnfunc_checked = true;
+ setorigin(item, position);
+ item.noalign = no_align;
+ Item_Initialize(item, class_name);
+ if (wasfreed(item))
+ {
+ return NULL;
+ }
+ return item;
+}
+
+void Item_Initialize(entity item, string class_name)
+{
+ FOREACH(Weapons, it.m_canonical_spawnfunc == class_name,
+ {
+ weapon_defaultspawnfunc(item, it);
+ return;
+ });
+ FOREACH(Items, it.m_canonical_spawnfunc == class_name,
+ {
+ StartItem(item, it);
+ return;
+ });
+ LOG_FATALF("Item_Initialize: Invalid classname: %s", class_name);
+}
+
+entity Item_CreateLoot(string class_name, vector position, vector vel,
+ float time_to_live)
+{
+ entity item = spawn();
+ if (!Item_InitializeLoot(item, class_name, position, vel, time_to_live))
+ {
+ return NULL;
+ }
+ return item;
+}
+
+bool Item_InitializeLoot(entity item, string class_name, vector position,
+ vector vel, float time_to_live)
+{
+ item.classname = class_name;
+ Item_SetLoot(item, true);
+ item.noalign = true;
+ setorigin(item, position);
+ item.pickup_anyway = true;
+ item.spawnfunc_checked = true;
+ Item_Initialize(item, class_name);
+ if (wasfreed(item))
+ {
+ return false;
+ }
+ item.gravity = 1;
+ item.velocity = vel;
+ SUB_SetFade(item, time + time_to_live, 1);
+ return true;
+}
+
+bool Item_IsLoot(entity item)
+{
+ return item.m_isloot;
+}
+
+void Item_SetLoot(entity item, bool loot)
+{
+ item.m_isloot = loot;
+}
+
+bool Item_ShouldKeepPosition(entity item)
+{
+ return item.noalign || (item.spawnflags & 1);
+}
+
+bool Item_IsExpiring(entity item)
+{
+ return item.m_isexpiring;
+}
+
+void Item_SetExpiring(entity item, bool expiring)
+{
+ item.m_isexpiring = expiring;
+}
+
+// Compatibility spawn functions
+
+// FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard
+SPAWNFUNC_ITEM(item_armor1, ITEM_ArmorSmall)
+
+SPAWNFUNC_ITEM(item_armor25, ITEM_ArmorMega)
+
+SPAWNFUNC_ITEM(item_armor_large, ITEM_ArmorMega)
+
+SPAWNFUNC_ITEM(item_health1, ITEM_HealthSmall)
+
+SPAWNFUNC_ITEM(item_health25, ITEM_HealthMedium)
+
+SPAWNFUNC_ITEM(item_health_large, ITEM_HealthBig)
+
+SPAWNFUNC_ITEM(item_health100, ITEM_HealthMega)
+
+SPAWNFUNC_ITEM(item_quad, ITEM_Strength)
--- /dev/null
+#pragma once
+
+/// \file
+/// \brief Header file that describes the functions related to game items.
+/// \copyright GNU GPLv2 or any later version.
+
+/// \brief Creates a new item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] no_align True if item should be placed directly at specified
+/// position, false to let it drop to the ground.
+/// \return Item on success, NULL otherwise.
+entity Item_Create(string class_name, vector position, bool no_align);
+
+/// \brief Initializes the item according to classname.
+/// \param[in,out] item Item to initialize.
+/// \param[in] class_name Class name to use.
+/// \return No return.
+/// \nore This function is useful if you want to set some item properties before
+/// initialization.
+void Item_Initialize(entity item, string class_name);
+
+/// \brief Creates a loot item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] velocity of the item.
+/// \param[in] time_to_live Amount of time after which the item will disappear.
+/// \return Item on success, NULL otherwise.
+entity Item_CreateLoot(string class_name, vector position, vector vel,
+ float time_to_live);
+
+/// \brief Initializes the loot item.
+/// \param[in] class_name Class name of the item.
+/// \param[in] position Position of the item.
+/// \param[in] velocity of the item.
+/// \param[in] time_to_live Amount of time after which the item will disappear.
+/// \return True on success, false otherwise.
+/// \nore This function is useful if you want to set some item properties before
+/// initialization.
+bool Item_InitializeLoot(entity item, string class_name, vector position,
+ vector vel, float time_to_live);
+
+/// \brief Returns whether the item is loot.
+/// \param[in] item Item to check.
+/// \return True if the item is loot, false otherwise.
+bool Item_IsLoot(entity item);
+
+/// \brief Sets the item loot status.
+/// \param[in,out] item Item to adjust.
+/// \param[in] loot Whether item is loot.
+/// \return No return.
+void Item_SetLoot(entity item, bool loot);
+
+/// \brief Returns whether item should keep its position or be dropped to the
+/// ground.
+/// \param[in] item Item to check.
+/// \return True if item should keep its position or false if it should be
+/// dropped to the ground.
+bool Item_ShouldKeepPosition(entity item);
+
+/// \brief Returns whether the item is expiring (i.e. its strength, shield and
+/// superweapon timers expire while it is on the ground).
+/// \param[in] item Item to check.
+/// \return True if the item is expiring, false otherwise.
+bool Item_IsExpiring(entity item);
+
+/// \brief Sets the item expiring status (i.e. whether its strength, shield
+/// and superweapon timers expire while it is on the ground).
+/// \param[in,out] item Item to adjust.
+/// \param[in] expiring Whether item is expiring.
+/// \return No return.
+void Item_SetExpiring(entity item, bool expiring);
#include "ipban.qh"
#include "mutators/_mod.qh"
#include "../common/t_items.qh"
+#include "resources.qh"
+#include "items.qh"
#include "weapons/accuracy.qh"
#include "weapons/csqcprojectile.qh"
#include "weapons/selection.qh"
string AmmoNameFromWeaponentity(entity wpn)
{
string ammoitems = "batteries";
- switch((wpn.m_weapon).ammo_field)
+ switch ((wpn.m_weapon).ammo_type)
{
- case ammo_shells: ammoitems = ITEM_Shells.m_name; break;
- case ammo_nails: ammoitems = ITEM_Bullets.m_name; break;
- case ammo_rockets: ammoitems = ITEM_Rockets.m_name; break;
- case ammo_cells: ammoitems = ITEM_Cells.m_name; break;
- case ammo_plasma: ammoitems = ITEM_Plasma.m_name; break;
- case ammo_fuel: ammoitems = ITEM_JetpackFuel.m_name; break;
+ case RESOURCE_SHELLS: ammoitems = ITEM_Shells.m_name; break;
+ case RESOURCE_BULLETS: ammoitems = ITEM_Bullets.m_name; break;
+ case RESOURCE_ROCKETS: ammoitems = ITEM_Rockets.m_name; break;
+ case RESOURCE_CELLS: ammoitems = ITEM_Cells.m_name; break;
+ case RESOURCE_PLASMA: ammoitems = ITEM_Plasma.m_name; break;
+ case RESOURCE_FUEL: ammoitems = ITEM_JetpackFuel.m_name; break;
}
return ammoitems;
}
start_ammo_rockets = 0;
start_ammo_cells = 0;
start_ammo_plasma = 0;
+ if (random_start_ammo == NULL)
+ {
+ random_start_ammo = spawn();
+ }
start_health = cvar("g_balance_health_start");
start_armorvalue = cvar("g_balance_armor_start");
start_ammo_cells = cvar("g_start_ammo_cells");
start_ammo_plasma = cvar("g_start_ammo_plasma");
start_ammo_fuel = cvar("g_start_ammo_fuel");
+ random_start_weapons_count = cvar("g_random_start_weapons_count");
+ SetResourceAmount(random_start_ammo, RESOURCE_SHELLS, cvar(
+ "g_random_start_shells"));
+ SetResourceAmount(random_start_ammo, RESOURCE_BULLETS, cvar(
+ "g_random_start_bullets"));
+ SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS,
+ cvar("g_random_start_rockets"));
+ SetResourceAmount(random_start_ammo, RESOURCE_CELLS, cvar(
+ "g_random_start_cells"));
+ SetResourceAmount(random_start_ammo, RESOURCE_PLASMA, cvar(
+ "g_random_start_plasma"));
}
if (warmup_stage)
start_ammo_cells = max(0, start_ammo_cells);
start_ammo_plasma = max(0, start_ammo_plasma);
start_ammo_fuel = max(0, start_ammo_fuel);
+ SetResourceAmount(random_start_ammo, RESOURCE_SHELLS, max(0,
+ GetResourceAmount(random_start_ammo, RESOURCE_SHELLS)));
+ SetResourceAmount(random_start_ammo, RESOURCE_BULLETS, max(0,
+ GetResourceAmount(random_start_ammo, RESOURCE_BULLETS)));
+ SetResourceAmount(random_start_ammo, RESOURCE_ROCKETS, max(0,
+ GetResourceAmount(random_start_ammo, RESOURCE_ROCKETS)));
+ SetResourceAmount(random_start_ammo, RESOURCE_CELLS, max(0,
+ GetResourceAmount(random_start_ammo, RESOURCE_CELLS)));
+ SetResourceAmount(random_start_ammo, RESOURCE_PLASMA, max(0,
+ GetResourceAmount(random_start_ammo, RESOURCE_PLASMA)));
warmup_start_ammo_shells = max(0, warmup_start_ammo_shells);
warmup_start_ammo_nails = max(0, warmup_start_ammo_nails);
.float(entity) isEliminated;
bool EliminatedPlayers_SendEntity(entity this, entity to, float sendflags)
{
- float i, f, b;
- entity e;
- WriteHeader(MSG_ENTITY, ENT_CLIENT_ELIMINATEDPLAYERS);
- WriteByte(MSG_ENTITY, sendflags);
-
- if(sendflags & 1)
- {
- for(i = 1; i <= maxclients; i += 8)
- {
- for(f = 0, e = edict_num(i), b = 1; b < 256; b *= 2, e = nextent(e))
- {
- if(eliminatedPlayers.isEliminated(e))
- f |= b;
+ Stream out = MSG_ENTITY;
+ WriteHeader(out, ENT_CLIENT_ELIMINATEDPLAYERS);
+ serialize(byte, out, sendflags);
+ if (sendflags & 1) {
+ for (int i = 1; i <= maxclients; i += 8) {
+ int f = 0;
+ entity e = edict_num(i);
+ for (int b = 0; b < 8; ++b, e = nextent(e)) {
+ if (eliminatedPlayers.isEliminated(e)) {
+ f |= BIT(b);
+ }
}
- WriteByte(MSG_ENTITY, f);
+ serialize(byte, out, f);
}
}
-
return true;
}
return false;
if(e.iscreature)
return true;
+ if (Item_IsLoot(e))
+ {
+ return true;
+ }
switch(e.classname)
{
case "body":
- case "droppedweapon":
return true;
case "bullet": // antilagged bullets can't hit this either
return false;
float start_ammo_cells;
float start_ammo_plasma;
float start_ammo_fuel;
+/// \brief Number of random start weapons to give to players.
+int random_start_weapons_count;
+/// \brief Holds a list of possible random start weapons.
+string autocvar_g_random_start_weapons;
+/// \brief Entity that contains amount of ammo to give with random start
+/// weapons.
+entity random_start_ammo;
float start_health;
float start_armorvalue;
WepSet warmup_start_weapons;
/**/
MUTATOR_HOOKABLE(CheckAllowedTeams, EV_CheckAllowedTeams);
+/** return true to manually override team counts */
+MUTATOR_HOOKABLE(GetTeamCounts, EV_NO_ARGS);
+
+/** allow overriding of team counts */
+#define EV_GetTeamCount(i, o) \
+ /** team to count */ i(float, MUTATOR_ARGV_0_float) \
+ /** player to ignore */ i(entity, MUTATOR_ARGV_1_entity) \
+ /** number of players in a team */ i(float, MUTATOR_ARGV_2_float) \
+ /**/ o(float, MUTATOR_ARGV_2_float) \
+ /** number of bots in a team */ i(float, MUTATOR_ARGV_3_float) \
+ /**/ o(float, MUTATOR_ARGV_3_float) \
+ /** lowest scoring human in a team */ i(entity, MUTATOR_ARGV_4_entity) \
+ /**/ o(entity, MUTATOR_ARGV_4_entity) \
+ /** lowest scoring bot in a team */ i(entity, MUTATOR_ARGV_5_entity) \
+ /**/ o(entity, MUTATOR_ARGV_5_entity) \
+ /**/
+MUTATOR_HOOKABLE(GetTeamCount, EV_GetTeamCount);
+
+/** allows overriding best teams */
+#define EV_FindBestTeams(i, o) \
+ /** player checked */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** bitmask of teams */ o(float, MUTATOR_ARGV_1_float) \
+ /**/
+MUTATOR_HOOKABLE(FindBestTeams, EV_FindBestTeams);
+
/** copies variables for spectating "spectatee" to "this" */
#define EV_SpectateCopy(i, o) \
/** spectatee */ i(entity, MUTATOR_ARGV_0_entity) \
MUT_ITEMTOUCH_PICKUP // return this flag to have the item "picked up" and taken even after mutator handled it
};
+/** called after the item has been touched. */
+#define EV_ItemTouched(i, o) \
+ /** item */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** toucher */ i(entity, MUTATOR_ARGV_1_entity) \
+ /**/
+MUTATOR_HOOKABLE(ItemTouched, EV_ItemTouched);
+
/** Called when the amount of entity resources changes. Can be used to override
resource limit. */
#define EV_GetResourceLimit(i, o) \
/**/
MUTATOR_HOOKABLE(ClientKill, EV_ClientKill);
+/** called when player is about to be killed during kill command or changing teams */
+#define EV_ClientKill_Now(i, o) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(ClientKill_Now, EV_ClientKill_Now);
+
#define EV_FixClientCvars(i, o) \
/** player */ i(entity, MUTATOR_ARGV_0_entity) \
/**/
/**/
MUTATOR_HOOKABLE(AddPlayerScore, EV_AddPlayerScore);
+#define EV_AddedPlayerScore(i, o) \
+ /** score field */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** score */ i(float, MUTATOR_ARGV_1_float) \
+ /** player */ i(entity, MUTATOR_ARGV_2_entity) \
+ /**/
+MUTATOR_HOOKABLE(AddedPlayerScore, EV_AddPlayerScore);
+
#define EV_GetPlayerStatus(i, o) \
/** player */ i(entity, MUTATOR_ARGV_0_entity) \
/**/
/**/
MUTATOR_HOOKABLE(MonsterModel, EV_MonsterModel);
-/**/
+/**
+ * Called before player changes their team. Return true to block team change.
+ */
#define EV_Player_ChangeTeam(i, o) \
/** player */ i(entity, MUTATOR_ARGV_0_entity) \
/** current team */ i(float, MUTATOR_ARGV_1_float) \
/**/
MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam);
+/**
+ * Called after player has changed their team.
+ */
+#define EV_Player_ChangedTeam(i, o) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /** old team */ i(float, MUTATOR_ARGV_1_float) \
+ /** current team */ i(float, MUTATOR_ARGV_2_float) \
+ /**/
+MUTATOR_HOOKABLE(Player_ChangedTeam, EV_Player_ChangedTeam);
+
+/**
+ * Called when player is about to be killed when changing teams. Return true to block killing.
+ */
+#define EV_Player_ChangeTeamKill(i, o) \
+ /** player */ i(entity, MUTATOR_ARGV_0_entity) \
+ /**/
+MUTATOR_HOOKABLE(Player_ChangeTeamKill, EV_Player_ChangeTeamKill);
+
/**/
#define EV_URI_GetCallback(i, o) \
/** id */ i(float, MUTATOR_ARGV_0_float) \
ca_LastPlayerForTeam_Notify(frag_target);
if (!allowed_to_spawn)
- frag_target.respawn_flags = RESPAWN_SILENT;
+ {
+ frag_target.respawn_flags = RESPAWN_SILENT;
+ // prevent unwanted sudden rejoin as spectator and move of spectator camera
+ frag_target.respawn_time = time + 2;
+ }
if (!warmup_stage)
eliminatedPlayers.SendFlags |= 1;
if(IS_BOT_CLIENT(frag_target))
if (!IS_DEAD(player))
ca_LastPlayerForTeam_Notify(player);
- if (CS(player).killindicator_teamchange == -2) // player wants to spectate
+ if (player.killindicator_teamchange == -2) // player wants to spectate
player.caplayer = 0;
if (player.caplayer)
player.frags = FRAGS_LMS_LOSER;
#include "gamemode_cts.qh"
-#include <server/race.qh>
#include <server/race.qh>
+#include <server/items.qh>
float autocvar_g_cts_finish_kill_delay;
bool autocvar_g_cts_selfdamage;
{
entity item = M_ARGV(0, entity);
- if(item.classname == "droppedweapon")
+ if (Item_IsLoot(item))
+ {
return true;
+ }
}
MUTATOR_HOOKFUNCTION(cts, Damage_Calculate)
#include "gamemode_lms.qh"
-#include <common/mutators/mutator/instagib/items.qc>
+#include <common/mutators/mutator/instagib/items.qh>
#include <server/campaign.qh>
#include <server/command/_mod.qh>
#include "bot/api.qh"
#include "cheats.qh"
#include "g_damage.qh"
+#include "handicap.qh"
#include "g_subs.qh"
#include "miscfunctions.qh"
#include "portals.qh"
if(!DEATH_ISSPECIAL(deathtype))
{
- damage *= bound(1.0, CS(this).cvar_cl_handicap, 10.0);
- if(this != attacker && IS_PLAYER(attacker))
- damage /= bound(1.0, CS(attacker).cvar_cl_handicap, 10.0);
+ damage *= Handicap_GetTotalHandicap(this);
+ if (this != attacker && IS_PLAYER(attacker))
+ {
+ damage /= Handicap_GetTotalHandicap(attacker);
+ }
}
if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1)
if(this.alivetime)
{
- PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
+ PlayerStats_GameReport_Event_Player(this, PLAYERSTATS_ALIVETIME, time - this.alivetime);
this.alivetime = 0;
}
{
delete(this.killindicator);
this.killindicator = NULL;
- if(CS(this).killindicator_teamchange)
+ if(this.killindicator_teamchange)
defer_ClientKill_Now_TeamChange = true;
if(this.classname == "body")
if(w != WEP_Null && accuracy_isgooddamage(attacker, this))
CS(attacker).accuracy.(accuracy_frags[w.m_id-1]) += 1;
+ this.respawn_time = 0;
MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage);
damage = M_ARGV(4, float);
excess = max(0, damage - take - save);
if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body"))
return;
+ if (!this.respawn_time) // can be set in the mutator hook PlayerDies
+ calculate_player_respawn_time(this);
+
// when we get here, player actually dies
Unfreeze(this); // remove any icy remains
STAT(MOVEVARS_SPECIALCOMMAND, this) = false; // sweet release
- // when to allow respawn
- calculate_player_respawn_time(this);
-
this.death_time = time;
if (random() < 0.5)
animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true);
}
}
-void MoveToTeam(entity client, int team_colour, int type)
+bool MoveToTeam(entity client, int team_colour, int type)
{
int lockteams_backup = lockteams; // backup any team lock
lockteams = 0; // disable locked teams
TeamchangeFrags(client); // move the players frags
- SetPlayerColors(client, team_colour - 1); // set the players colour
+ if (!SetPlayerTeamSimple(client, team_colour))
+ {
+ return false;
+ }
Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player
lockteams = lockteams_backup; // restore the team lock
LogTeamchange(client.playerid, client.team, type);
+ return true;
}
/** print(), but only print if the server is not local */
void ClientKill_Now_TeamChange(entity this);
-void MoveToTeam(entity client, float team_colour, float type);
+/// \brief Moves player to the specified team.
+/// \param[in,out] client Client to move.
+/// \param[in] team_colour Color of the team.
+/// \param[in] type ???
+/// \return True on success, false otherwise.
+bool MoveToTeam(entity client, float team_colour, float type);
void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force);
#include "resources.qh"
+
/// \file
/// \brief Source file that contains implementation of the resource system.
/// \author Lyberta
#pragma once
+
/// \file
/// \brief Header file that describes the resource system.
/// \author Lyberta
/// \copyright GNU GPLv2 or any later version.
+#include <common/resources.qh>
+
/// \brief Unconditional maximum amount of resources the entity can have.
const int RESOURCE_AMOUNT_HARD_LIMIT = 999;
-/// \brief Describes the available resource types.
-enum
-{
- RESOURCE_HEALTH = 1, ///< Health.
- RESOURCE_ARMOR, ///< Armor.
- RESOURCE_SHELLS, ///< Shells (used by shotgun).
- RESOURCE_BULLETS, ///< Bullets (used by machinegun and rifle)
- RESOURCE_ROCKETS, ///< Rockets (used by mortar, hagar, devastator, etc).
- RESOURCE_CELLS, ///< Cells (used by electro, crylink, vortex, etc)
- RESOURCE_PLASMA, ///< Plasma (unused).
- RESOURCE_FUEL ///< Fuel (used by jetpack).
-};
-
// ============================ Public API ====================================
/// \brief Returns the maximum amount of the given resource.
bool TeamScore_SendEntity(entity this, entity to, float sendflags)
{
- float i, p, longflags;
+ float i, longflags;
WriteHeader(MSG_ENTITY, ENT_CLIENT_TEAMSCORES);
int t = this.team - 1;
WriteByte(MSG_ENTITY, t);
longflags = 0;
- for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)
+ for(i = 0; i < MAX_TEAMSCORE; ++i)
if(this.(teamscores(i)) > 127 || this.(teamscores(i)) <= -128)
- longflags |= p;
+ longflags |= BIT(i);
#if MAX_TEAMSCORE <= 8
WriteByte(MSG_ENTITY, sendflags);
WriteShort(MSG_ENTITY, sendflags);
WriteShort(MSG_ENTITY, longflags);
#endif
- for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2)
- if(sendflags & p)
+ for(i = 0; i < MAX_TEAMSCORE; ++i)
+ if(sendflags & BIT(i))
{
- if(longflags & p)
+ if(longflags & BIT(i))
WriteInt24_t(MSG_ENTITY, this.(teamscores(i)));
else
WriteChar(MSG_ENTITY, this.(teamscores(i)));
PlayerStats_GameReport_AddTeam(t);
}
-float TeamScore_AddToTeam(float t, float scorefield, float score)
+float TeamScore_AddToTeam(int t, float scorefield, float score)
{
entity s;
if(scores_label(scorefield) != "")
s.SendFlags |= (2 ** (scorefield.m_id % 16));
if(!warmup_stage)
- PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
- return (s.(scores(scorefield)) += score);
+ PlayerStats_GameReport_Event_Player(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score);
+ s.(scores(scorefield)) += score;
+ MUTATOR_CALLHOOK(AddedPlayerScore, scorefield, score, player);
+ return s.(scores(scorefield));
}
float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score)
{
entity s = CS(p).scorekeeper;
FOREACH(Scores, true, {
- if(s.(scores(it)) != 0)
- if(scores_label(it) != "")
- PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
- });
+ if(s.(scores(it)) != 0 && scores_label(it) != "")
+ PlayerStats_GameReport_Event_Player(s.owner,
+ strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it)));
+ });
}
void PlayerScore_TeamStats()
if(!sk)
continue;
for(i = 0; i < MAX_TEAMSCORE; ++i)
- if(sk.(teamscores(i)) != 0)
- if(teamscores_label(i) != "")
- // the +1 is important here!
- PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
+ if(sk.(teamscores(i)) != 0 && teamscores_label(i) != "")
+ // the +1 is important here!
+ PlayerStats_GameReport_Event_Team(t+1,
+ strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i)));
}
}
* NEVER call this if team has not been set yet!
* Returns the new score.
*/
-float TeamScore_AddToTeam(float t, float scorefield, float score);
+float TeamScore_AddToTeam(int t, float scorefield, float score);
/**
* Returns a value indicating the team score (and higher is better).
#include "client.qh"
#include "scores.qh"
#include <common/gamemodes/rules.qh>
+#include "teamplay.qh"
int ScoreRules_teams;
ScoreInfo_SetLabel_PlayerScore(SP_DEATHS, "deaths", SFL_LOWER_IS_BETTER);
if (!INDEPENDENT_PLAYERS)
+ {
ScoreInfo_SetLabel_PlayerScore(SP_SUICIDES, "suicides", SFL_LOWER_IS_BETTER);
+ ScoreInfo_SetLabel_PlayerScore(SP_TEAMKILLS, "teamkills", SFL_LOWER_IS_BETTER);
+ }
if(score_enabled)
ScoreInfo_SetLabel_PlayerScore(SP_SCORE, "score", sprio);
this.dmgtime = 0;
}
this.air_finished = time + 12;
- this.dmg = 2;
}
}
void SV_OnEntityPreSpawnFunction(entity this)
{
- __spawnfunc_expecting = true;
- __spawnfunc_expect = this;
if (this)
if (this.gametypefilter != "")
if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter))
return;
LABEL(cleanup)
builtin_remove(this);
- __spawnfunc_expecting = false;
}
void WarpZone_PostInitialize_Callback()
int done = 0; for (int i = 0, n = numentityfields(); i < n; ++i) {
string k = entityfieldname(i); vector v = (k == "mins") ? mi_min : (k == "maxs") ? mi_max : '0 0 0';
if (v) {
- putentityfieldstring(i, world, sprintf("%d %d %d", v));
+ putentityfieldstring(i, world, sprintf("%v", v));
if (++done == 2) break;
}
}
#endif
}
-void SetPlayerColors(entity pl, float _color)
+void SetPlayerColors(entity player, float _color)
{
- /*string s;
- s = ftos(cl);
- stuffcmd(pl, strcat("color ", s, " ", s, "\n") );
- pl.team = cl + 1;
- //pl.clientcolors = pl.clientcolors - (pl.clientcolors & 15) + cl;
- pl.clientcolors = 16*cl + cl;*/
-
- float pants, shirt;
- pants = _color & 0x0F;
- shirt = _color & 0xF0;
-
-
- if(teamplay) {
- setcolor(pl, 16*pants + pants);
- } else {
- setcolor(pl, shirt + pants);
+ float pants = _color & 0x0F;
+ float shirt = _color & 0xF0;
+ if (teamplay)
+ {
+ setcolor(player, 16 * pants + pants);
+ }
+ else
+ {
+ setcolor(player, shirt + pants);
}
}
-void SetPlayerTeam(entity pl, float t, float s, float noprint)
+void KillPlayerForTeamChange(entity player)
{
- float _color;
-
- if(t == 4)
- _color = NUM_TEAM_4 - 1;
- else if(t == 3)
- _color = NUM_TEAM_3 - 1;
- else if(t == 2)
- _color = NUM_TEAM_2 - 1;
- else
- _color = NUM_TEAM_1 - 1;
-
- SetPlayerColors(pl,_color);
-
- if(t != s) {
- LogTeamchange(pl.playerid, pl.team, 3); // log manual team join
+ if (IS_DEAD(player))
+ {
+ return;
+ }
+ if (MUTATOR_CALLHOOK(Player_ChangeTeamKill, player) == true)
+ {
+ return;
+ }
+ Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, player.origin,
+ '0 0 0');
+}
- if(!noprint)
- bprint(playername(pl, false), "^7 has changed from ", Team_NumberToColoredFullName(s), "^7 to ", Team_NumberToColoredFullName(t), "\n");
+bool SetPlayerTeamSimple(entity player, int team_num)
+{
+ if (player.team == team_num)
+ {
+ // This is important when players join the game and one of their color
+ // matches the team color while other doesn't. For example [BOT]Lion.
+ SetPlayerColors(player, team_num - 1);
+ return true;
}
+ if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, Team_TeamToNumber(
+ player.team), Team_TeamToNumber(team_num)) == true)
+ {
+ // Mutator has blocked team change.
+ return false;
+ }
+ int old_team = player.team;
+ SetPlayerColors(player, team_num - 1);
+ MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_team, player.team);
+ return true;
+}
+bool SetPlayerTeam(entity player, int destination_team, int source_team,
+ bool no_print)
+{
+ int team_num = Team_NumberToTeam(destination_team);
+ if (!SetPlayerTeamSimple(player, team_num))
+ {
+ return false;
+ }
+ LogTeamchange(player.playerid, player.team, 3); // log manual team join
+ if (no_print)
+ {
+ return true;
+ }
+ bprint(playername(player, false), "^7 has changed from ", Team_NumberToColoredFullName(source_team), "^7 to ", Team_NumberToColoredFullName(destination_team), "\n");
+ return true;
}
// set c1...c4 to show what teams are allowed
-void CheckAllowedTeams (entity for_whom)
+void CheckAllowedTeams(entity for_whom)
{
int teams_mask = 0;
c1 = c2 = c3 = c4 = -1;
- cb1 = cb2 = cb3 = cb4 = 0;
+ num_bots_team1 = num_bots_team2 = num_bots_team3 = num_bots_team4 = 0;
string teament_name = string_null;
// teams that are allowed will now have their player counts stored in c1...c4
void GetTeamCounts(entity ignore)
{
- float value, bvalue;
- // now count how many players are on each team already
-
- // FIXME: also find and memorize the lowest-scoring bot on each team (in case players must be shuffled around)
- // also remember the lowest-scoring player
-
- FOREACH_CLIENT(true, {
- float t;
- if(IS_PLAYER(it) || it.caplayer)
- t = it.team;
- else if(it.team_forced > 0)
- t = it.team_forced; // reserve the spot
- else
- continue;
- if(it != ignore)// && it.netname != "")
+ if (MUTATOR_CALLHOOK(GetTeamCounts) == true)
+ {
+ if (c1 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_1, ignore, c1,
+ num_bots_team1, lowest_human_team1, lowest_bot_team1);
+ c1 = M_ARGV(2, float);
+ num_bots_team1 = M_ARGV(3, float);
+ lowest_human_team1 = M_ARGV(4, entity);
+ lowest_bot_team1 = M_ARGV(5, entity);
+ }
+ if (c2 >= 0)
{
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_2, ignore, c2,
+ num_bots_team2, lowest_human_team2, lowest_bot_team2);
+ c2 = M_ARGV(2, float);
+ num_bots_team2 = M_ARGV(3, float);
+ lowest_human_team2 = M_ARGV(4, entity);
+ lowest_bot_team2 = M_ARGV(5, entity);
+ }
+ if (c3 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_3, ignore, c3,
+ num_bots_team3, lowest_human_team3, lowest_bot_team3);
+ c3 = M_ARGV(2, float);
+ num_bots_team3 = M_ARGV(3, float);
+ lowest_human_team3 = M_ARGV(4, entity);
+ lowest_bot_team3 = M_ARGV(5, entity);
+ }
+ if (c4 >= 0)
+ {
+ MUTATOR_CALLHOOK(GetTeamCount, NUM_TEAM_4, ignore,
+ c4, num_bots_team4, lowest_human_team4, lowest_bot_team4);
+ c4 = M_ARGV(2, float);
+ num_bots_team4 = M_ARGV(3, float);
+ lowest_human_team4 = M_ARGV(4, entity);
+ lowest_bot_team4 = M_ARGV(5, entity);
+ }
+ }
+ else
+ {
+ float value, bvalue;
+ // now count how many players are on each team already
+ float lowest_human_score1 = FLOAT_MAX;
+ float lowest_bot_score1 = FLOAT_MAX;
+ float lowest_human_score2 = FLOAT_MAX;
+ float lowest_bot_score2 = FLOAT_MAX;
+ float lowest_human_score3 = FLOAT_MAX;
+ float lowest_bot_score3 = FLOAT_MAX;
+ float lowest_human_score4 = FLOAT_MAX;
+ float lowest_bot_score4 = FLOAT_MAX;
+ FOREACH_CLIENT(true,
+ {
+ float t;
+ if (IS_PLAYER(it) || it.caplayer)
+ {
+ t = it.team;
+ }
+ else if (it.team_forced > 0)
+ {
+ t = it.team_forced; // reserve the spot
+ }
+ else
+ {
+ continue;
+ }
+ if (it == ignore)
+ {
+ continue;
+ }
value = PlayerValue(it);
- if(IS_BOT_CLIENT(it))
+ if (IS_BOT_CLIENT(it))
+ {
bvalue = value;
+ }
else
+ {
bvalue = 0;
- if(t == NUM_TEAM_1)
+ }
+ if (value == 0)
{
- if(c1 >= 0)
- {
- c1 = c1 + value;
- cb1 = cb1 + bvalue;
- }
+ continue;
}
- else if(t == NUM_TEAM_2)
+ switch (t)
{
- if(c2 >= 0)
+ case NUM_TEAM_1:
{
- c2 = c2 + value;
- cb2 = cb2 + bvalue;
+ if (c1 < 0)
+ {
+ break;
+ }
+ c1 += value;
+ num_bots_team1 += bvalue;
+ float temp_score = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (temp_score < lowest_human_score1)
+ {
+ lowest_human_team1 = it;
+ lowest_human_score1 = temp_score;
+ }
+ break;
+ }
+ if (temp_score < lowest_bot_score1)
+ {
+ lowest_bot_team1 = it;
+ lowest_bot_score1 = temp_score;
+ }
+ break;
}
- }
- else if(t == NUM_TEAM_3)
- {
- if(c3 >= 0)
+ case NUM_TEAM_2:
{
- c3 = c3 + value;
- cb3 = cb3 + bvalue;
+ if (c2 < 0)
+ {
+ break;
+ }
+ c2 += value;
+ num_bots_team2 += bvalue;
+ float temp_score = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (temp_score < lowest_human_score2)
+ {
+ lowest_human_team2 = it;
+ lowest_human_score2 = temp_score;
+ }
+ break;
+ }
+ if (temp_score < lowest_bot_score2)
+ {
+ lowest_bot_team2 = it;
+ lowest_bot_score2 = temp_score;
+ }
+ break;
}
- }
- else if(t == NUM_TEAM_4)
- {
- if(c4 >= 0)
+ case NUM_TEAM_3:
+ {
+ if (c3 < 0)
+ {
+ break;
+ }
+ c3 += value;
+ num_bots_team3 += bvalue;
+ float temp_score = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (temp_score < lowest_human_score3)
+ {
+ lowest_human_team3 = it;
+ lowest_human_score3 = temp_score;
+ }
+ break;
+ }
+ if (temp_score < lowest_bot_score3)
+ {
+ lowest_bot_team3 = it;
+ lowest_bot_score3 = temp_score;
+ }
+ break;
+ }
+ case NUM_TEAM_4:
{
- c4 = c4 + value;
- cb4 = cb4 + bvalue;
+ if (c4 < 0)
+ {
+ break;
+ }
+ c4 += value;
+ num_bots_team4 += bvalue;
+ float temp_score = PlayerScore_Get(it, SP_SCORE);
+ if (!bvalue)
+ {
+ if (temp_score < lowest_human_score4)
+ {
+ lowest_human_team4 = it;
+ lowest_human_score4 = temp_score;
+ }
+ break;
+ }
+ if (temp_score < lowest_bot_score4)
+ {
+ lowest_bot_team4 = it;
+ lowest_bot_score4 = temp_score;
+ }
+ break;
}
}
- }
- });
+ });
+ }
// if the player who has a forced team has not joined yet, reserve the spot
if(autocvar_g_campaign)
{
switch(autocvar_g_campaign_forceteam)
{
- case 1: if(c1 == cb1) ++c1; break;
- case 2: if(c2 == cb2) ++c2; break;
- case 3: if(c3 == cb3) ++c3; break;
- case 4: if(c4 == cb4) ++c4; break;
+ case 1: if(c1 == num_bots_team1) ++c1; break;
+ case 2: if(c2 == num_bots_team2) ++c2; break;
+ case 3: if(c3 == num_bots_team3) ++c3; break;
+ case 4: if(c4 == num_bots_team4) ++c4; break;
}
}
}
-float TeamSmallerEqThanTeam(float ta, float tb, entity e)
+bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player,
+ bool use_score)
{
+ if (team_a == team_b)
+ {
+ return false;
+ }
// we assume that CheckAllowedTeams and GetTeamCounts have already been called
- float f;
- float ca = -1, cb = -1, cba = 0, cbb = 0, sa = 0, sb = 0;
-
- switch(ta)
+ int num_players_team_a = -1, num_players_team_b = -1;
+ int num_bots_team_a = 0, num_bots_team_b = 0;
+ float score_team_a = 0, score_team_b = 0;
+ switch (team_a)
{
- case 1: ca = c1; cba = cb1; sa = team1_score; break;
- case 2: ca = c2; cba = cb2; sa = team2_score; break;
- case 3: ca = c3; cba = cb3; sa = team3_score; break;
- case 4: ca = c4; cba = cb4; sa = team4_score; break;
+ case 1:
+ {
+ num_players_team_a = c1;
+ num_bots_team_a = num_bots_team1;
+ score_team_a = team1_score;
+ break;
+ }
+ case 2:
+ {
+ num_players_team_a = c2;
+ num_bots_team_a = num_bots_team2;
+ score_team_a = team2_score;
+ break;
+ }
+ case 3:
+ {
+ num_players_team_a = c3;
+ num_bots_team_a = num_bots_team3;
+ score_team_a = team3_score;
+ break;
+ }
+ case 4:
+ {
+ num_players_team_a = c4;
+ num_bots_team_a = num_bots_team4;
+ score_team_a = team4_score;
+ break;
+ }
}
- switch(tb)
+ switch (team_b)
{
- case 1: cb = c1; cbb = cb1; sb = team1_score; break;
- case 2: cb = c2; cbb = cb2; sb = team2_score; break;
- case 3: cb = c3; cbb = cb3; sb = team3_score; break;
- case 4: cb = c4; cbb = cb4; sb = team4_score; break;
+ case 1:
+ {
+ num_players_team_b = c1;
+ num_bots_team_b = num_bots_team1;
+ score_team_b = team1_score;
+ break;
+ }
+ case 2:
+ {
+ num_players_team_b = c2;
+ num_bots_team_b = num_bots_team2;
+ score_team_b = team2_score;
+ break;
+ }
+ case 3:
+ {
+ num_players_team_b = c3;
+ num_bots_team_b = num_bots_team3;
+ score_team_b = team3_score;
+ break;
+ }
+ case 4:
+ {
+ num_players_team_b = c4;
+ num_bots_team_b = num_bots_team4;
+ score_team_b = team4_score;
+ break;
+ }
}
-
// invalid
- if(ca < 0 || cb < 0)
+ if (num_players_team_a < 0 || num_players_team_b < 0)
+ {
return false;
-
- // equal
- if(ta == tb)
+ }
+ if (IS_REAL_CLIENT(player) && bots_would_leave)
+ {
+ num_players_team_a -= num_bots_team_a;
+ num_players_team_b -= num_bots_team_b;
+ }
+ if (!use_score)
+ {
+ return num_players_team_a < num_players_team_b;
+ }
+ if (num_players_team_a < num_players_team_b)
+ {
return true;
+ }
+ if (num_players_team_a > num_players_team_b)
+ {
+ return false;
+ }
+ return score_team_a < score_team_b;
+}
- if(IS_REAL_CLIENT(e))
+bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score)
+{
+ if (team_a == team_b)
+ {
+ return true;
+ }
+ // we assume that CheckAllowedTeams and GetTeamCounts have already been called
+ int num_players_team_a = -1, num_players_team_b = -1;
+ int num_bots_team_a = 0, num_bots_team_b = 0;
+ float score_team_a = 0, score_team_b = 0;
+ switch (team_a)
{
- if(bots_would_leave)
+ case 1:
+ {
+ num_players_team_a = c1;
+ num_bots_team_a = num_bots_team1;
+ score_team_a = team1_score;
+ break;
+ }
+ case 2:
+ {
+ num_players_team_a = c2;
+ num_bots_team_a = num_bots_team2;
+ score_team_a = team2_score;
+ break;
+ }
+ case 3:
{
- ca -= cba * 0.999;
- cb -= cbb * 0.999;
+ num_players_team_a = c3;
+ num_bots_team_a = num_bots_team3;
+ score_team_a = team3_score;
+ break;
+ }
+ case 4:
+ {
+ num_players_team_a = c4;
+ num_bots_team_a = num_bots_team4;
+ score_team_a = team4_score;
+ break;
}
}
+ switch (team_b)
+ {
+ case 1:
+ {
+ num_players_team_b = c1;
+ num_bots_team_b = num_bots_team1;
+ score_team_b = team1_score;
+ break;
+ }
+ case 2:
+ {
+ num_players_team_b = c2;
+ num_bots_team_b = num_bots_team2;
+ score_team_b = team2_score;
+ break;
+ }
+ case 3:
+ {
+ num_players_team_b = c3;
+ num_bots_team_b = num_bots_team3;
+ score_team_b = team3_score;
+ break;
+ }
+ case 4:
+ {
+ num_players_team_b = c4;
+ num_bots_team_b = num_bots_team4;
+ score_team_b = team4_score;
+ break;
+ }
+ }
+ // invalid
+ if (num_players_team_a < 0 || num_players_team_b < 0)
+ return false;
- // keep teams alive (teams of size 0 always count as smaller, ignoring score)
- if(ca < 1)
- if(cb >= 1)
- return true;
- if(ca >= 1)
- if(cb < 1)
- return false;
-
- // first, normalize
- f = max(ca, cb, 1);
- ca /= f;
- cb /= f;
- f = max(sa, sb, 1);
- sa /= f;
- sb /= f;
-
- // the more we're at the end of the match, the more take scores into account
- f = bound(0, game_completion_ratio * autocvar_g_balance_teams_scorefactor, 1);
- ca += (sa - ca) * f;
- cb += (sb - cb) * f;
+ if (IS_REAL_CLIENT(player) && bots_would_leave)
+ {
+ num_players_team_a -= num_bots_team_a;
+ num_players_team_b -= num_bots_team_b;
+ }
+ if (!use_score)
+ {
+ return num_players_team_a == num_players_team_b;
+ }
+ if (num_players_team_a != num_players_team_b)
+ {
+ return false;
+ }
+ return score_team_a == score_team_b;
+}
- return ca <= cb;
+int FindBestTeams(entity player, bool use_score)
+{
+ if (MUTATOR_CALLHOOK(FindBestTeams, player) == true)
+ {
+ return M_ARGV(1, float);
+ }
+ int team_bits = 0;
+ int previous_team = 0;
+ if (c1 >= 0)
+ {
+ team_bits = BIT(0);
+ previous_team = 1;
+ }
+ if (c2 >= 0)
+ {
+ if (previous_team == 0)
+ {
+ team_bits = BIT(1);
+ previous_team = 2;
+ }
+ else if (IsTeamSmallerThanTeam(2, previous_team, player, use_score))
+ {
+ team_bits = BIT(1);
+ previous_team = 2;
+ }
+ else if (IsTeamEqualToTeam(2, previous_team, player, use_score))
+ {
+ team_bits |= BIT(1);
+ previous_team = 2;
+ }
+ }
+ if (c3 >= 0)
+ {
+ if (previous_team == 0)
+ {
+ team_bits = BIT(2);
+ previous_team = 3;
+ }
+ else if (IsTeamSmallerThanTeam(3, previous_team, player, use_score))
+ {
+ team_bits = BIT(2);
+ previous_team = 3;
+ }
+ else if (IsTeamEqualToTeam(3, previous_team, player, use_score))
+ {
+ team_bits |= BIT(2);
+ previous_team = 3;
+ }
+ }
+ if (c4 >= 0)
+ {
+ if (previous_team == 0)
+ {
+ team_bits = BIT(3);
+ }
+ else if (IsTeamSmallerThanTeam(4, previous_team, player, use_score))
+ {
+ team_bits = BIT(3);
+ }
+ else if (IsTeamEqualToTeam(4, previous_team, player, use_score))
+ {
+ team_bits |= BIT(3);
+ }
+ }
+ return team_bits;
}
// returns # of smallest team (1, 2, 3, 4)
// NOTE: Assumes CheckAllowedTeams has already been called!
-float FindSmallestTeam(entity pl, float ignore_pl)
+int FindSmallestTeam(entity player, float ignore_player)
{
- int totalteams = 0;
- int t = 1; // initialize with a random team?
- if(c4 >= 0) t = 4;
- if(c3 >= 0) t = 3;
- if(c2 >= 0) t = 2;
- if(c1 >= 0) t = 1;
-
- // find out what teams are available
- //CheckAllowedTeams();
-
- // make sure there are at least 2 teams to join
- if(c1 >= 0)
- totalteams = totalteams + 1;
- if(c2 >= 0)
- totalteams = totalteams + 1;
- if(c3 >= 0)
- totalteams = totalteams + 1;
- if(c4 >= 0)
- totalteams = totalteams + 1;
-
- if((autocvar_bot_vs_human || pl.team_forced > 0) && totalteams == 1)
- totalteams += 1;
-
- if(totalteams <= 1)
+ // count how many players are in each team
+ if (ignore_player)
{
- if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl))
- return 1; // special case for campaign and player joining
- else if(totalteams == 1) // single team
- LOG_TRACEF("Only 1 team available for %s, you may need to fix your map", MapInfo_Type_ToString(MapInfo_CurrentGametype()));
- else // no teams, major no no
- error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+ GetTeamCounts(player);
}
-
- // count how many players are in each team
- if(ignore_pl)
- GetTeamCounts(pl);
else
+ {
GetTeamCounts(NULL);
-
+ }
+ int team_bits = FindBestTeams(player, true);
+ if (team_bits == 0)
+ {
+ error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype())));
+ }
RandomSelection_Init();
-
- if(TeamSmallerEqThanTeam(1, t, pl))
- t = 1;
- if(TeamSmallerEqThanTeam(2, t, pl))
- t = 2;
- if(TeamSmallerEqThanTeam(3, t, pl))
- t = 3;
- if(TeamSmallerEqThanTeam(4, t, pl))
- t = 4;
-
- // now t is the minimum, or A minimum!
- if(t == 1 || TeamSmallerEqThanTeam(1, t, pl))
+ if ((team_bits & BIT(0)) != 0)
+ {
RandomSelection_AddFloat(1, 1, 1);
- if(t == 2 || TeamSmallerEqThanTeam(2, t, pl))
+ }
+ if ((team_bits & BIT(1)) != 0)
+ {
RandomSelection_AddFloat(2, 1, 1);
- if(t == 3 || TeamSmallerEqThanTeam(3, t, pl))
+ }
+ if ((team_bits & BIT(2)) != 0)
+ {
RandomSelection_AddFloat(3, 1, 1);
- if(t == 4 || TeamSmallerEqThanTeam(4, t, pl))
+ }
+ if ((team_bits & BIT(3)) != 0)
+ {
RandomSelection_AddFloat(4, 1, 1);
-
+ }
return RandomSelection_chosen_float;
}
-int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam)
+int JoinBestTeam(entity this, bool only_return_best, bool force_best_team)
{
- float smallest, selectedteam;
-
// don't join a team if we're not playing a team game
- if(!teamplay)
+ if (!teamplay)
+ {
return 0;
+ }
// find out what teams are available
CheckAllowedTeams(this);
// if we don't care what team he ends up on, put him on whatever team he entered as.
// if he's not on a valid team, then let other code put him on the smallest team
- if(!forcebestteam)
+ if (!force_best_team)
{
+ int selected_team;
if( c1 >= 0 && this.team == NUM_TEAM_1)
- selectedteam = this.team;
+ selected_team = this.team;
else if(c2 >= 0 && this.team == NUM_TEAM_2)
- selectedteam = this.team;
+ selected_team = this.team;
else if(c3 >= 0 && this.team == NUM_TEAM_3)
- selectedteam = this.team;
+ selected_team = this.team;
else if(c4 >= 0 && this.team == NUM_TEAM_4)
- selectedteam = this.team;
+ selected_team = this.team;
else
- selectedteam = -1;
+ selected_team = -1;
- if(selectedteam > 0)
+ if (selected_team > 0)
{
- if(!only_return_best)
+ if (!only_return_best)
{
- SetPlayerColors(this, selectedteam - 1);
+ SetPlayerTeamSimple(this, selected_team);
// when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped
// when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected
LogTeamchange(this.playerid, this.team, 99);
}
- return selectedteam;
+ return selected_team;
}
// otherwise end up on the smallest team (handled below)
}
- smallest = FindSmallestTeam(this, true);
-
- if(!only_return_best && !this.bot_forced_team)
+ int best_team = FindSmallestTeam(this, true);
+ if (only_return_best || this.bot_forced_team)
{
- TeamchangeFrags(this);
- if(smallest == 1)
- {
- SetPlayerColors(this, NUM_TEAM_1 - 1);
- }
- else if(smallest == 2)
- {
- SetPlayerColors(this, NUM_TEAM_2 - 1);
- }
- else if(smallest == 3)
- {
- SetPlayerColors(this, NUM_TEAM_3 - 1);
- }
- else if(smallest == 4)
- {
- SetPlayerColors(this, NUM_TEAM_4 - 1);
- }
- else
- {
- error("smallest team: invalid team\n");
- }
-
- LogTeamchange(this.playerid, this.team, 2); // log auto join
-
- if(!IS_DEAD(this))
- Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
+ return best_team;
}
-
- return smallest;
+ best_team = Team_NumberToTeam(best_team);
+ if (best_team == -1)
+ {
+ error("JoinBestTeam: invalid team\n");
+ }
+ int old_team = Team_TeamToNumber(this.team);
+ TeamchangeFrags(this);
+ SetPlayerTeamSimple(this, best_team);
+ LogTeamchange(this.playerid, this.team, 2); // log auto join
+ if (!IS_BOT_CLIENT(this))
+ {
+ AutoBalanceBots(old_team, Team_TeamToNumber(best_team));
+ }
+ KillPlayerForTeamChange(this);
+ return best_team;
}
-//void() ctf_playerchanged;
void SV_ChangeTeam(entity this, float _color)
{
- float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount;
+ float source_color, destination_color, source_team, destination_team;
// in normal deathmatch we can just apply the color and we're done
if(!teamplay)
if(!teamplay)
return;
- scolor = this.clientcolors & 0x0F;
- dcolor = _color & 0x0F;
-
- if(scolor == NUM_TEAM_1 - 1)
- steam = 1;
- else if(scolor == NUM_TEAM_2 - 1)
- steam = 2;
- else if(scolor == NUM_TEAM_3 - 1)
- steam = 3;
- else // if(scolor == NUM_TEAM_4 - 1)
- steam = 4;
- if(dcolor == NUM_TEAM_1 - 1)
- dteam = 1;
- else if(dcolor == NUM_TEAM_2 - 1)
- dteam = 2;
- else if(dcolor == NUM_TEAM_3 - 1)
- dteam = 3;
- else // if(dcolor == NUM_TEAM_4 - 1)
- dteam = 4;
+ source_color = this.clientcolors & 0x0F;
+ destination_color = _color & 0x0F;
+
+ source_team = Team_TeamToNumber(source_color + 1);
+ destination_team = Team_TeamToNumber(destination_color + 1);
+
+ if (destination_team == -1)
+ {
+ return;
+ }
CheckAllowedTeams(this);
- if(dteam == 1 && c1 < 0) dteam = 4;
- if(dteam == 4 && c4 < 0) dteam = 3;
- if(dteam == 3 && c3 < 0) dteam = 2;
- if(dteam == 2 && c2 < 0) dteam = 1;
+ if (destination_team == 1 && c1 < 0) destination_team = 4;
+ if (destination_team == 4 && c4 < 0) destination_team = 3;
+ if (destination_team == 3 && c3 < 0) destination_team = 2;
+ if (destination_team == 2 && c2 < 0) destination_team = 1;
// not changing teams
- if(scolor == dcolor)
+ if (source_color == destination_color)
{
- //bprint("same team change\n");
- SetPlayerTeam(this, dteam, steam, true);
+ SetPlayerTeam(this, destination_team, source_team, true);
return;
}
}
// autocvar_g_balance_teams_prevent_imbalance only makes sense if autocvar_g_balance_teams is on, as it makes the team selection dialog pointless
- if(autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
+ if (autocvar_g_balance_teams && autocvar_g_balance_teams_prevent_imbalance)
{
GetTeamCounts(this);
- if(!TeamSmallerEqThanTeam(dteam, steam, this))
+ if ((BIT(destination_team - 1) & FindBestTeams(this, false)) == 0)
{
Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM);
return;
}
}
-
-// bprint("allow change teams from ", ftos(steam), " to ", ftos(dteam), "\n");
-
- if(IS_PLAYER(this) && steam != dteam)
+ if(IS_PLAYER(this) && source_team != destination_team)
{
// reduce frags during a team change
TeamchangeFrags(this);
}
-
- MUTATOR_CALLHOOK(Player_ChangeTeam, this, steam, dteam);
-
- SetPlayerTeam(this, dteam, steam, !IS_CLIENT(this));
-
- if(IS_PLAYER(this) && steam != dteam)
+ if (!SetPlayerTeam(this, destination_team, source_team, !IS_CLIENT(this)))
{
- // kill player when changing teams
- if(!IS_DEAD(this))
- Damage(this, this, this, 100000, DEATH_TEAMCHANGE.m_id, this.origin, '0 0 0');
+ return;
}
+ AutoBalanceBots(source_team, destination_team);
+ if (!IS_PLAYER(this) || (source_team == destination_team))
+ {
+ return;
+ }
+ KillPlayerForTeamChange(this);
}
-void ShufflePlayerOutOfTeam (float source_team)
+void AutoBalanceBots(int source_team, int destination_team)
{
- float smallestteam, smallestteam_count, steam;
- float lowest_bot_score, lowest_player_score;
- entity lowest_bot, lowest_player, selected;
-
- smallestteam = 0;
- smallestteam_count = 999999999;
-
- if(c1 >= 0 && c1 < smallestteam_count)
- {
- smallestteam = 1;
- smallestteam_count = c1;
- }
- if(c2 >= 0 && c2 < smallestteam_count)
+ if ((source_team == -1) || (destination_team == -1))
{
- smallestteam = 2;
- smallestteam_count = c2;
- }
- if(c3 >= 0 && c3 < smallestteam_count)
- {
- smallestteam = 3;
- smallestteam_count = c3;
- }
- if(c4 >= 0 && c4 < smallestteam_count)
- {
- smallestteam = 4;
- smallestteam_count = c4;
+ return;
}
-
- if(!smallestteam)
+ if (!autocvar_g_balance_teams ||
+ !autocvar_g_balance_teams_prevent_imbalance)
{
- bprint("warning: no smallest team\n");
return;
}
-
- if(source_team == 1)
- steam = NUM_TEAM_1;
- else if(source_team == 2)
- steam = NUM_TEAM_2;
- else if(source_team == 3)
- steam = NUM_TEAM_3;
- else // if(source_team == 4)
- steam = NUM_TEAM_4;
-
- lowest_bot = NULL;
- lowest_bot_score = 999999999;
- lowest_player = NULL;
- lowest_player_score = 999999999;
-
- // find the lowest-scoring player & bot of that team
- FOREACH_CLIENT(IS_PLAYER(it) && it.team == steam, {
- if(it.isbot)
+ int num_players_source_team = 0;
+ int num_players_destination_team = 0;
+ entity lowest_bot_destination_team = NULL;
+ switch (source_team)
+ {
+ case 1:
{
- if(it.totalfrags < lowest_bot_score)
- {
- lowest_bot = it;
- lowest_bot_score = it.totalfrags;
- }
+ num_players_source_team = c1;
+ break;
}
- else
+ case 2:
{
- if(it.totalfrags < lowest_player_score)
- {
- lowest_player = it;
- lowest_player_score = it.totalfrags;
- }
+ num_players_source_team = c2;
+ break;
+ }
+ case 3:
+ {
+ num_players_source_team = c3;
+ break;
+ }
+ case 4:
+ {
+ num_players_source_team = c4;
+ break;
}
- });
-
- // prefers to move a bot...
- if(lowest_bot != NULL)
- selected = lowest_bot;
- // but it will move a player if it has to
- else
- selected = lowest_player;
- // don't do anything if it couldn't find anyone
- if(!selected)
- {
- bprint("warning: couldn't find a player to move from team\n");
- return;
- }
-
- // smallest team gains a member
- if(smallestteam == 1)
- {
- c1 = c1 + 1;
- }
- else if(smallestteam == 2)
- {
- c2 = c2 + 1;
- }
- else if(smallestteam == 3)
- {
- c3 = c3 + 1;
- }
- else if(smallestteam == 4)
- {
- c4 = c4 + 1;
- }
- else
- {
- bprint("warning: destination team invalid\n");
- return;
- }
- // source team loses a member
- if(source_team == 1)
- {
- c1 = c1 + 1;
- }
- else if(source_team == 2)
- {
- c2 = c2 + 2;
- }
- else if(source_team == 3)
- {
- c3 = c3 + 3;
}
- else if(source_team == 4)
+ switch (destination_team)
{
- c4 = c4 + 4;
+ case 1:
+ {
+ num_players_destination_team = c1;
+ lowest_bot_destination_team = lowest_bot_team1;
+ break;
+ }
+ case 2:
+ {
+ num_players_destination_team = c2;
+ lowest_bot_destination_team = lowest_bot_team2;
+ break;
+ }
+ case 3:
+ {
+ num_players_destination_team = c3;
+ lowest_bot_destination_team = lowest_bot_team3;
+ break;
+ }
+ case 4:
+ {
+ num_players_destination_team = c4;
+ lowest_bot_destination_team = lowest_bot_team4;
+ break;
+ }
}
- else
+ if ((num_players_destination_team <= num_players_source_team) ||
+ (lowest_bot_destination_team == NULL))
{
- bprint("warning: source team invalid\n");
return;
}
-
- // move the player to the new team
- TeamchangeFrags(selected);
- SetPlayerTeam(selected, smallestteam, source_team, false);
-
- if(!IS_DEAD(selected))
- Damage(selected, selected, selected, 100000, DEATH_AUTOTEAMCHANGE.m_id, selected.origin, '0 0 0');
- Send_Notification(NOTIF_ONE, selected, MSG_CENTER, CENTER_DEATH_SELF_AUTOTEAMCHANGE, selected.team);
+ SetPlayerTeamSimple(lowest_bot_destination_team,
+ Team_NumberToTeam(source_team));
+ KillPlayerForTeamChange(lowest_bot_destination_team);
}
string cache_mutatormsg;
string cache_lastmutatormsg;
-// client counts for each team
-//float c1, c2, c3, c4;
-// # of bots on those teams
-float cb1, cb2, cb3, cb4;
+// The following variables are used for balancing. They are not updated
+// automatically. You need to call CheckAllowedTeams and GetTeamCounts to get
+// proper values.
+
+// These four have 2 different states. If they are equal to -1, it means that
+// the player can't join the team. Zero or positive value means that player can
+// join the team and means the number of players on that team.
+float c1;
+float c2;
+float c3;
+float c4;
+float num_bots_team1; ///< Number of bots in the first team.
+float num_bots_team2; ///< Number of bots in the second team.
+float num_bots_team3; ///< Number of bots in the third team.
+float num_bots_team4; ///< Number of bots in the fourth team.
+entity lowest_human_team1; ///< Human with the lowest score in the first team.
+entity lowest_human_team2; ///< Human with the lowest score in the second team.
+entity lowest_human_team3; ///< Human with the lowest score in the third team.
+entity lowest_human_team4; ///< Human with the lowest score in the fourth team.
+entity lowest_bot_team1; ///< Bot with the lowest score in the first team.
+entity lowest_bot_team2; ///< Bot with the lowest score in the second team.
+entity lowest_bot_team3; ///< Bot with the lowest score in the third team.
+entity lowest_bot_team4; ///< Bot with the lowest score in the fourth team.
int redowned, blueowned, yellowowned, pinkowned;
string getwelcomemessage(entity this);
-void SetPlayerColors(entity pl, float _color);
+void SetPlayerColors(entity player, float _color);
-void SetPlayerTeam(entity pl, float t, float s, float noprint);
+/// \brief Kills player as a result of team change.
+/// \param[in,out] player Player to kill.
+/// \return No return.
+void KillPlayerForTeamChange(entity player);
+
+/// \brief Sets the team of the player.
+/// \param[in,out] player Player to adjust.
+/// \param[in] team_num Team number to set. See TEAM_NUM constants.
+/// \return True if team switch was successful, false otherwise.
+bool SetPlayerTeamSimple(entity player, int team_num);
+
+/// \brief Sets the team of the player.
+/// \param[in,out] player Player to adjust.
+/// \param[in] destination_team Team to set.
+/// \param[in] source_team Previous team of the player.
+/// \param[in] no_print Whether to print this event to players' console.
+/// \return True if team switch was successful, false otherwise.
+bool SetPlayerTeam(entity player, int destination_team, int source_team,
+ bool no_print);
// set c1...c4 to show what teams are allowed
-void CheckAllowedTeams (entity for_whom);
+void CheckAllowedTeams(entity for_whom);
float PlayerValue(entity p);
// teams that are allowed will now have their player counts stored in c1...c4
void GetTeamCounts(entity ignore);
-float TeamSmallerEqThanTeam(float ta, float tb, entity e);
+/// \brief Returns whether one team is smaller than the other.
+/// \param[in] team_a First team.
+/// \param[in] team_b Second team.
+/// \param[in] player Player to check.
+/// \param[in] use_score Whether to take into account team scores.
+/// \return True if first team is smaller than the second one, false otherwise.
+/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have
+/// been called.
+bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player,
+ bool use_score);
+
+/// \brief Returns whether one team is equal to the other.
+/// \param[in] team_a First team.
+/// \param[in] team_b Second team.
+/// \param[in] player Player to check.
+/// \param[in] use_score Whether to take into account team scores.
+/// \return True if first team is equal to the second one, false otherwise.
+/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have
+/// been called.
+bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score);
+
+/// \brief Returns the bitmask of the best teams for the player to join.
+/// \param[in] player Player to check.
+/// \param[in] use_score Whether to take into account team scores.
+/// \return Bitmask of the best teams for the player to join.
+/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have
+/// been called.
+int FindBestTeams(entity player, bool use_score);
// returns # of smallest team (1, 2, 3, 4)
// NOTE: Assumes CheckAllowedTeams has already been called!
-float FindSmallestTeam(entity pl, float ignore_pl);
-
-int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam);
+int FindSmallestTeam(entity player, float ignore_player);
-//void() ctf_playerchanged;
+int JoinBestTeam(entity this, bool only_return_best, bool force_best_team);
-void ShufflePlayerOutOfTeam (float source_team);
+/// \brief Auto balances bots in teams after the player has changed team.
+/// \param[in] source_team Previous team of the player (1, 2, 3, 4).
+/// \param[in] destination_team Current team of the player (1, 2, 3, 4).
+/// \return No return.
+/// \note This function assumes that CheckAllowedTeams and GetTeamCounts have
+/// been called.
+void AutoBalanceBots(int source_team, int destination_team);
void setcolor(entity this, int clr);
} \
} MACRO_END
-#define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), body)
+#define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), LAMBDA(body))
// using the "inside out" version of knuth-fisher-yates shuffle
// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle
#include "weaponsystem.qh"
#include <common/t_items.qh>
+#include <server/items.qh>
#include <common/constants.qh>
#include <common/net_linked.qh>
#include <common/util.qh>
if (!autocvar_g_showweaponspawns) return;
IL_EACH(g_items, it.weapon == this.m_id && (!it.team || (it.ItemStatus & ITS_AVAILABLE)),
{
- if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2)
+ if (Item_IsLoot(it) && (autocvar_g_showweaponspawns < 2))
+ {
continue;
+ }
entity wp = WaypointSprite_Spawn(
WP_Weapon,
-2, 0,
#include "spawning.qh"
#include "weaponsystem.qh"
+#include "../resources.qh"
#include "../mutators/_mod.qh"
#include <common/t_items.qh>
+#include <server/items.qh>
#include <common/weapons/_all.qh>
+.bool m_isreplaced; ///< Holds whether the weapon has been replaced.
+
string W_Apply_Weaponreplace(string in)
{
string out = "";
void weapon_defaultspawnfunc(entity this, Weapon e)
{
Weapon wpn = e;
- if (this.classname != "droppedweapon" && this.classname != "replacedweapon")
+ e = wpn = wpn.m_spawnfunc_hookreplace(wpn, this);
+ this.classname = wpn.m_canonical_spawnfunc;
+ if (!Item_IsLoot(this) && !this.m_isreplaced)
{
if (e.spawnflags & WEP_FLAG_MUTATORBLOCKED)
{
{
entity replacement = spawn();
copyentity(this, replacement);
- replacement.classname = "replacedweapon";
+ replacement.m_isreplaced = true;
weapon_defaultspawnfunc(replacement, it);
break;
}
this.superweapons_finished = autocvar_g_balance_superweapons_time;
// if we don't already have ammo, give us some ammo
- if (!this.(wpn.ammo_field))
+ if ((wpn.ammo_type != RESOURCE_NONE) && !GetResourceAmount(this, wpn.ammo_type))
{
- switch (wpn.ammo_field)
+ switch (wpn.ammo_type)
{
- case ammo_shells: this.ammo_shells = cvar("g_pickup_shells_weapon"); break;
- case ammo_nails: this.ammo_nails = cvar("g_pickup_nails_weapon"); break;
- case ammo_rockets: this.ammo_rockets = cvar("g_pickup_rockets_weapon"); break;
- case ammo_cells: this.ammo_cells = cvar("g_pickup_cells_weapon"); break;
- case ammo_plasma: this.ammo_plasma = cvar("g_pickup_plasma_weapon"); break;
- case ammo_fuel: this.ammo_fuel = cvar("g_pickup_fuel_weapon"); break;
+ case RESOURCE_SHELLS: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_shells_weapon")); break;
+ case RESOURCE_BULLETS: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_nails_weapon")); break;
+ case RESOURCE_ROCKETS: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_rockets_weapon")); break;
+ case RESOURCE_CELLS: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_cells_weapon")); break;
+ case RESOURCE_PLASMA: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_plasma_weapon")); break;
+ case RESOURCE_FUEL: SetResourceAmount(this, wpn.ammo_type, cvar("g_pickup_fuel_weapon")); break;
}
}
#include "throwing.qh"
#include "weaponsystem.qh"
+#include "../resources.qh"
+#include "../items.qh"
#include "../mutators/_mod.qh"
#include <common/t_items.qh>
#include "../g_damage.qh"
float thisammo;
string s;
Weapon info = Weapons_from(wpn);
- var .int ammotype = info.ammo_field;
-
- entity wep = new(droppedweapon);
+ int ammotype = info.ammo_type;
+ entity wep = spawn();
+ Item_SetLoot(wep, true);
setorigin(wep, org);
wep.velocity = velo;
wep.owner = wep.enemy = own;
if(WepSet_FromWeapon(Weapons_from(wpn)) & WEPSET_SUPERWEAPONS)
{
+ Item_SetExpiring(wep, true);
if(own.items & IT_UNLIMITED_SUPERWEAPONS)
{
wep.superweapons_finished = time + autocvar_g_balance_superweapons_time;
wep.pickup_anyway = true; // these are ALWAYS pickable
//wa = W_AmmoItemCode(wpn);
- if(ammotype == ammo_none)
+ if(ammotype == RESOURCE_NONE)
{
return "";
}
int i = own.(weaponentity).m_weapon.m_id;
if(own.(weaponentity).(weapon_load[i]) > 0)
{
- own.(ammotype) += own.(weaponentity).(weapon_load[i]);
+ GiveResource(own, ammotype, own.(weaponentity).(weapon_load[i]));
own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
}
-
- wep.(ammotype) = 0;
+ SetResourceAmount(wep, ammotype, 0);
}
else if(doreduce)
{
int i = own.(weaponentity).m_weapon.m_id;
if(own.(weaponentity).(weapon_load[i]) > 0)
{
- own.(ammotype) += own.(weaponentity).(weapon_load[i]);
+ GiveResource(own, ammotype, own.(weaponentity).(weapon_load[i]));
own.(weaponentity).(weapon_load[i]) = -1; // schedule the weapon for reloading
}
- thisammo = min(own.(ammotype), wep.(ammotype));
- wep.(ammotype) = thisammo;
- own.(ammotype) -= thisammo;
+ float ownderammo = GetResourceAmount(own, ammotype);
+ thisammo = min(ownderammo, GetResourceAmount(wep, ammotype));
+ SetResourceAmount(wep, ammotype, thisammo);
+ SetResourceAmount(own, ammotype, ownderammo - thisammo);
- switch(ammotype)
+ switch (ammotype)
{
- case ammo_shells: s = sprintf("%s and %d shells", s, thisammo); break;
- case ammo_nails: s = sprintf("%s and %d nails", s, thisammo); break;
- case ammo_rockets: s = sprintf("%s and %d rockets", s, thisammo); break;
- case ammo_cells: s = sprintf("%s and %d cells", s, thisammo); break;
- case ammo_plasma: s = sprintf("%s and %d plasma", s, thisammo); break;
- case ammo_fuel: s = sprintf("%s and %d fuel", s, thisammo); break;
+ case RESOURCE_SHELLS: s = sprintf("%s and %d shells", s, thisammo); break;
+ case RESOURCE_BULLETS: s = sprintf("%s and %d nails", s, thisammo); break;
+ case RESOURCE_ROCKETS: s = sprintf("%s and %d rockets", s, thisammo); break;
+ case RESOURCE_CELLS: s = sprintf("%s and %d cells", s, thisammo); break;
+ case RESOURCE_PLASMA: s = sprintf("%s and %d plasma", s, thisammo); break;
+ case RESOURCE_FUEL: s = sprintf("%s and %d fuel", s, thisammo); break;
}
s = substring(s, 5, -1);
// start weapons that take no ammo can't be dropped (this prevents dropping the laser, as long as it continues to use no ammo)
if(start_items & IT_UNLIMITED_WEAPON_AMMO)
return false;
- if((Weapons_from(w)).ammo_field == ammo_none)
+ if((Weapons_from(w)).ammo_type == RESOURCE_NONE)
return false;
}
return true;
if(autocvar_g_antilag == 0 || noantilag)
lag = 0; // only do hitscan, but no antilag
if(lag)
- {
- FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_takeback(it, CS(it), time - lag));
- IL_EACH(g_monsters, it != this,
- {
- antilag_takeback(it, it, time - lag);
- });
- }
+ antilag_takeback_all(this, lag);
// change shooter to SOLID_BBOX so the shot can hit corpses
int oldsolid = this.dphitcontentsmask;
}
if(lag)
- {
- FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_restore(it, CS(it)));
- IL_EACH(g_monsters, it != this,
- {
- antilag_restore(it, it);
- });
- }
+ antilag_restore_all(this);
// restore shooter solid type
if(this)
#include "../command/common.qh"
#include "../mutators/_mod.qh"
#include "../round_handler.qh"
+#include "../resources.qh"
#include <common/t_items.qh>
#include <common/animdecide.qh>
#include <common/constants.qh>
w_ent.clip_load -= ammo_use;
w_ent.(weapon_load[w_ent.m_weapon.m_id]) = w_ent.clip_load;
}
- else if (wep.ammo_field != ammo_none)
+ else if (wep.ammo_type != RESOURCE_NONE)
{
- actor.(wep.ammo_field) -= ammo_use;
- if (actor.(wep.ammo_field) < 0)
+ float ammo = GetResourceAmount(actor, wep.ammo_type);
+ if (ammo < ammo_use)
{
backtrace(sprintf(
"W_DecreaseAmmo(%.2f): '%s' subtracted too much %s from '%s', resulting with '%.2f' left... "
"Please notify Samual immediately with a copy of this backtrace!\n",
ammo_use,
wep.netname,
- GetAmmoPicture(wep.ammo_field),
+ GetAmmoPicture(wep.ammo_type),
actor.netname,
- actor.(wep.ammo_field)
+ ammo
));
}
+ SetResourceAmount(actor, wep.ammo_type, ammo - ammo_use);
}
}
w_ent.clip_load = w_ent.old_clip_load; // restore the ammo counter, in case we still had ammo in the weapon before reloading
// if the gun uses no ammo, max out weapon load, else decrease ammo as we increase weapon load
- if (!w_ent.reload_ammo_min || (actor.items & IT_UNLIMITED_WEAPON_AMMO) || wpn.ammo_field == ammo_none)
+ if (!w_ent.reload_ammo_min || (actor.items & IT_UNLIMITED_WEAPON_AMMO) || wpn.ammo_type == RESOURCE_NONE)
{
w_ent.clip_load = w_ent.reload_ammo_amount;
}
else
{
// make sure we don't add more ammo than we have
- float load = min(w_ent.reload_ammo_amount - w_ent.clip_load, actor.(wpn.ammo_field));
+ float ammo = GetResourceAmount(actor, wpn.ammo_type);
+ float load = min(w_ent.reload_ammo_amount - w_ent.clip_load, ammo);
w_ent.clip_load += load;
- actor.(wpn.ammo_field) -= load;
+ SetResourceAmount(actor, wpn.ammo_type, ammo - load);
}
w_ent.(weapon_load[w_ent.m_weapon.m_id]) = w_ent.clip_load;
if (this.clip_load >= this.reload_ammo_amount) return;
// no ammo, so nothing to load
- if (e.ammo_field != ammo_none)
+ if (e.ammo_type != RESOURCE_NONE)
{
- if (!actor.(e.ammo_field) && this.reload_ammo_min)
+ if (!GetResourceAmount(actor, e.ammo_type) && this.reload_ammo_min)
{
if (!(actor.items & IT_UNLIMITED_WEAPON_AMMO))
{
#include <server/miscfunctions.qh>
float internalteam;
-float weaponswapping;
entity weapon_dropevent_item;
..entity weaponentity_fld;
function genmod() {
# use context to work around cmake issue #12619
CTX="${PWD#$ROOT}/"
- oldHashC=$(hash ${MOD}.inc)
- oldTimeC=$(stat -c "%Y" ${MOD}.inc)
- oldHashH=$(hash ${MOD}.qh)
- oldTimeH=$(stat -c "%Y" ${MOD}.qh)
+ if [ -f ${MOD}.inc ]; then
+ oldHashC=$(hash ${MOD}.inc)
+ oldTimeC=$(stat -c "%Y" ${MOD}.inc)
+ fi
+ if [ -f ${MOD}.qh ]; then
+ oldHashH=$(hash ${MOD}.qh)
+ oldTimeH=$(stat -c "%Y" ${MOD}.qh)
+ fi
echo '// generated file; do not modify' > ${MOD}.inc
echo '// generated file; do not modify' > ${MOD}.qh
for f in $(ls | sort -k 1,1 -t .); do
--- /dev/null
+// Random items mutator config
+
+// Map items
+
+set g_random_items_replace_item_health_small "random" "Classnames to replace small health with."
+set g_random_items_replace_item_health_medium "random" "Classnames to replace medium health with."
+set g_random_items_replace_item_health_big "random" "Classnames to replace big health with."
+set g_random_items_replace_item_health_mega "random" "Classnames to replace mega health with."
+set g_random_items_replace_item_armor_small "random" "Classnames to replace small armor with."
+set g_random_items_replace_item_armor_medium "random" "Classnames to replace medium armor with."
+set g_random_items_replace_item_armor_big "random" "Classnames to replace big armor with."
+set g_random_items_replace_item_armor_mega "random" "Classnames to replace mega armor with."
+set g_random_items_replace_item_shells "random" "Classnames to replace shells with."
+set g_random_items_replace_item_bullets "random" "Classnames to replace bullets with."
+set g_random_items_replace_item_rockets "random" "Classnames to replace rockets with."
+set g_random_items_replace_item_cells "random" "Classnames to replace cells with."
+set g_random_items_replace_item_plasma "random" "Classnames to replace plasma with."
+set g_random_items_replace_item_fuel "random" "Classnames to replace fuel with."
+set g_random_items_replace_weapon_blaster "random" "Classnames to replace blaster with."
+set g_random_items_replace_weapon_shotgun "random" "Classnames to replace shotgun with."
+set g_random_items_replace_weapon_machinegun "random" "Classnames to replace machinegun with."
+set g_random_items_replace_weapon_mortar "random" "Classnames to replace mortar with."
+set g_random_items_replace_weapon_electro "random" "Classnames to replace electro with."
+set g_random_items_replace_weapon_crylink "random" "Classnames to replace crylink with."
+set g_random_items_replace_weapon_vortex "random" "Classnames to replace vortex with."
+set g_random_items_replace_weapon_hagar "random" "Classnames to replace hagar with."
+set g_random_items_replace_weapon_devastator "random" "Classnames to replace devastator with."
+set g_random_items_replace_weapon_shockwave "random" "Classnames to replace shockwave with."
+set g_random_items_replace_weapon_arc "random" "Classnames to replace arc with."
+set g_random_items_replace_weapon_hook "random" "Classnames to replace hook with."
+set g_random_items_replace_weapon_tuba "random" "Classnames to replace tuba with."
+set g_random_items_replace_weapon_porto "random" "Classnames to replace port-o-launch with."
+set g_random_items_replace_weapon_fireball "random" "Classnames to replace fireball with."
+set g_random_items_replace_weapon_minelayer "random" "Classnames to replace mine layer with."
+set g_random_items_replace_weapon_hlac "random" "Classnames to replace HLAC with."
+set g_random_items_replace_weapon_rifle "random" "Classnames to replace rifle with."
+set g_random_items_replace_weapon_seeker "random" "Classnames to replace TAG seeker with."
+set g_random_items_replace_weapon_vaporizer "random" "Classnames to replace vaporizer with."
+set g_random_items_replace_weapon_hmg "random" "Classnames to replace HMG with."
+set g_random_items_replace_weapon_rpc "random" "Classnames to replace RPC with."
+set g_random_items_replace_item_strength "random" "Classnames to replace strength with."
+set g_random_items_replace_item_shield "random" "Classnames to replace shield with."
+set g_random_items_replace_item_fuel_regen "random" "Classnames to replace fuel regeneration with."
+set g_random_items_replace_item_jetpack "random" "Classnames to replace jetpack with."
+set g_random_items_replace_item_vaporizer_cells "random" "Classnames to replace vaporizer cells with."
+set g_random_items_replace_item_invisibility "random" "Classnames to replace invisibility with."
+set g_random_items_replace_item_extralife "random" "Classnames to replace extra life with."
+set g_random_items_replace_item_speed "random" "Classnames to replace speed with."
+set g_random_items_health_probability 1 "Probability of random health items spawning in the map."
+set g_random_items_armor_probability 1 "Probability of random armor items spawning in the map."
+set g_random_items_resource_probability 1 "Probability of random resource items spawning in the map."
+set g_random_items_weapon_probability 1 "Probability of random weapons spawning in the map."
+set g_random_items_powerup_probability 0.15 "Probability of random powerups spawning in the map."
+set g_random_items_item_health_small_probability 10 "Probability of random small health spawning in the map."
+set g_random_items_item_health_medium_probability 4 "Probability of random medium health spawning in the map."
+set g_random_items_item_health_big_probability 2 "Probability of random big health spawning in the map."
+set g_random_items_item_health_mega_probability 1 "Probability of random mega health spawning in the map."
+set g_random_items_item_armor_small_probability 10 "Probability of random small armor spawning in the map."
+set g_random_items_item_armor_medium_probability 4 "Probability of random medium armor spawning in the map."
+set g_random_items_item_armor_big_probability 2 "Probability of random big armor spawning in the map."
+set g_random_items_item_armor_mega_probability 1 "Probability of random mega armor spawning in the map."
+set g_random_items_item_shells_probability 1 "Probability of random shells spawning in the map."
+set g_random_items_item_bullets_probability 1 "Probability of random bullets spawning in the map."
+set g_random_items_item_rockets_probability 1 "Probability of random rockets spawning in the map."
+set g_random_items_item_cells_probability 1 "Probability of random cells spawning in the map."
+set g_random_items_item_plasma_probability 0 "Probability of random plasma spawning in the map."
+set g_random_items_item_fuel_probability 0 "Probability of random fuel spawning in the map."
+set g_random_items_weapon_blaster_probability 0 "Probability of random blaster spawning in the map."
+set g_random_items_weapon_shotgun_probability 0 "Probability of random shotgun spawning in the map."
+set g_random_items_weapon_machinegun_probability 1 "Probability of random machinegun spawning in the map."
+set g_random_items_weapon_mortar_probability 1 "Probability of random mortar spawning in the map."
+set g_random_items_weapon_electro_probability 1 "Probability of random electro spawning in the map."
+set g_random_items_weapon_crylink_probability 1 "Probability of random crylink spawning in the map."
+set g_random_items_weapon_vortex_probability 1 "Probability of random vortex spawning in the map."
+set g_random_items_weapon_hagar_probability 1 "Probability of random hagar spawning in the map."
+set g_random_items_weapon_devastator_probability 1 "Probability of random devastator spawning in the map."
+set g_random_items_weapon_shockwave_probability 0 "Probability of random shockwave spawning in the map."
+set g_random_items_weapon_arc_probability 0 "Probability of random arc spawning in the map."
+set g_random_items_weapon_hook_probability 0 "Probability of random hook spawning in the map."
+set g_random_items_weapon_tuba_probability 0 "Probability of random tuba spawning in the map."
+set g_random_items_weapon_porto_probability 0 "Probability of random port-o-launch spawning in the map."
+set g_random_items_weapon_fireball_probability 0 "Probability of random fireball spawning in the map."
+set g_random_items_weapon_minelayer_probability 0 "Probability of random mine layer spawning in the map."
+set g_random_items_weapon_hlac_probability 0 "Probability of random HLAC spawning in the map."
+set g_random_items_weapon_rifle_probability 0 "Probability of random rifle spawning in the map."
+set g_random_items_weapon_seeker_probability 0 "Probability of random TAG seeker spawning in the map."
+set g_random_items_weapon_vaporizer_probability 0 "Probability of random vaporizer spawning in the map."
+set g_random_items_item_strength_probability 1 "Probability of random strength spawning in the map."
+set g_random_items_item_shield_probability 1 "Probability of random shield spawning in the map."
+set g_random_items_item_fuel_regen_probability 0 "Probability of random fuel regeneration spawning in the map."
+set g_random_items_item_jetpack_probability 0 "Probability of random jetpack spawning in the map."
+set g_random_items_item_vaporizer_cells_probability 20 "Probability of random vaporizer cells spawning in the map."
+set g_random_items_item_invisibility_probability 1 "Probability of random invisibility spawning in the map."
+set g_random_items_item_extralife_probability 1 "Probability of random extra life spawning in the map."
+set g_random_items_item_speed_probability 1 "Probability of random speed spawning in the map."
+set g_random_items_overkill_item_health_mega_probability 1 "Probability of random mega health spawning in the map during overkill."
+set g_random_items_overkill_item_armor_small_probability 10 "Probability of random small armor spawning in the map during overkill."
+set g_random_items_overkill_item_armor_medium_probability 4 "Probability of random medium armor spawning in the map during overkill."
+set g_random_items_overkill_item_armor_big_probability 2 "Probability of random big armor spawning in the map during overkill."
+set g_random_items_overkill_item_armor_mega_probability 1 "Probability of random mega armor spawning in the map during overkill."
+set g_random_items_overkill_weapon_hmg_probability 0.5 "Probability of random HMG spawning in the map during overkill."
+set g_random_items_overkill_weapon_rpc_probability 0.5 "Probability of random RPC spawning in the map during overkill."
+
+// Loot
+
+set g_random_loot_min 0 "Minimum amount of loot items."
+set g_random_loot_max 4 "Maximum amount of loot items."
+set g_random_loot_time 10 "Amount of time the loot will stay in seconds."
+set g_random_loot_spread 200 "How far can loot be thrown."
+set g_random_loot_health_probability 1 "Probability of random health items spawning as loot."
+set g_random_loot_armor_probability 1 "Probability of random armor items spawning as loot."
+set g_random_loot_resource_probability 1 "Probability of random ammo items spawning as loot."
+set g_random_loot_weapon_probability 1 "Probability of random weapons spawning as loot."
+set g_random_loot_powerup_probability 0.2 "Probability of random powerups spawning as loot."
+set g_random_loot_item_health_small_probability 4 "Probability of random small health spawning as loot."
+set g_random_loot_item_health_medium_probability 3 "Probability of random medium health spawning as loot."
+set g_random_loot_item_health_big_probability 2 "Probability of random big health spawning as loot."
+set g_random_loot_item_health_mega_probability 1 "Probability of random mega health spawning as loot."
+set g_random_loot_item_armor_small_probability 4 "Probability of random small armor spawning as loot."
+set g_random_loot_item_armor_medium_probability 3 "Probability of random medium armor spawning as loot."
+set g_random_loot_item_armor_big_probability 2 "Probability of random big armor spawning as loot."
+set g_random_loot_item_armor_mega_probability 1 "Probability of random mega armor spawning as loot."
+set g_random_loot_item_shells_probability 1 "Probability of random shells spawning as loot."
+set g_random_loot_item_bullets_probability 1 "Probability of random bullets spawning as loot."
+set g_random_loot_item_rockets_probability 1 "Probability of random rockets spawning as loot."
+set g_random_loot_item_cells_probability 1 "Probability of random cells spawning as loot."
+set g_random_loot_item_plasma_probability 0 "Probability of random plasma spawning as loot."
+set g_random_loot_item_fuel_probability 0 "Probability of random fuel spawning as loot."
+set g_random_loot_weapon_blaster_probability 0 "Probability of random blaster spawning as loot."
+set g_random_loot_weapon_shotgun_probability 0 "Probability of random shotgun spawning as loot."
+set g_random_loot_weapon_machinegun_probability 1 "Probability of random machinegun spawning as loot."
+set g_random_loot_weapon_mortar_probability 1 "Probability of random mortar spawning as loot."
+set g_random_loot_weapon_electro_probability 1 "Probability of random electro spawning as loot."
+set g_random_loot_weapon_crylink_probability 1 "Probability of random crylink spawning as loot."
+set g_random_loot_weapon_vortex_probability 1 "Probability of random vortex spawning as loot."
+set g_random_loot_weapon_hagar_probability 1 "Probability of random hagar spawning as loot."
+set g_random_loot_weapon_devastator_probability 1 "Probability of random devastator spawning as loot."
+set g_random_loot_weapon_shockwave_probability 0 "Probability of random shockwave spawning as loot."
+set g_random_loot_weapon_arc_probability 0 "Probability of random arc spawning as loot."
+set g_random_loot_weapon_hook_probability 0 "Probability of random hook spawning as loot."
+set g_random_loot_weapon_tuba_probability 0 "Probability of random tuba spawning as loot."
+set g_random_loot_weapon_porto_probability 0 "Probability of random port-o-launch spawning as loot."
+set g_random_loot_weapon_fireball_probability 0 "Probability of random fireball spawning as loot."
+set g_random_loot_weapon_minelayer_probability 0 "Probability of random mine layer spawning as loot."
+set g_random_loot_weapon_hlac_probability 0 "Probability of random HLAC spawning as loot."
+set g_random_loot_weapon_rifle_probability 0 "Probability of random rifle spawning as loot."
+set g_random_loot_weapon_seeker_probability 0 "Probability of random TAG seeker spawning as loot."
+set g_random_loot_weapon_vaporizer_probability 0 "Probability of random vaporizer spawning as loot."
+set g_random_loot_item_strength_probability 1 "Probability of random strength spawning as loot."
+set g_random_loot_item_shield_probability 1 "Probability of random shield spawning as loot."
+set g_random_loot_item_fuel_regen_probability 0 "Probability of random fuel regeneration spawning as loot."
+set g_random_loot_item_jetpack_probability 0 "Probability of random jetpack spawning as loot."
+set g_random_loot_item_vaporizer_cells_probability 20 "Probability of random vaporizer cells spawning as loot."
+set g_random_loot_item_invisibility_probability 1 "Probability of random invisibility spawning as loot."
+set g_random_loot_item_extralife_probability 1 "Probability of random extra life spawning as loot."
+set g_random_loot_item_speed_probability 1 "Probability of random speed spawning as loot."
+set g_random_loot_overkill_item_health_mega_probability 1 "Probability of random mega health spawning as loot during overkill."
+set g_random_loot_overkill_item_armor_small_probability 10 "Probability of random small armor spawning as loot during overkill."
+set g_random_loot_overkill_item_armor_medium_probability 4 "Probability of random medium armor spawning as loot during overkill."
+set g_random_loot_overkill_item_armor_big_probability 2 "Probability of random big armor spawning as loot during overkill."
+set g_random_loot_overkill_item_armor_mega_probability 1 "Probability of random mega armor spawning as loot during overkill."
+set g_random_loot_overkill_weapon_hmg_probability 1 "Probability of random HMG spawning as loot during overkill."
+set g_random_loot_overkill_weapon_rpc_probability 1 "Probability of random RPC spawning as loot during overkill."
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_01"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_02"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_03"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_04"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_05"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_06"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_07"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_08"
blendfunc add
{
dpnoshadow
deformVertexes autosprite
+ nopicmip
{
map "models/ok_nade_counter/ok_nade_counter_09"
blendfunc add