From: terencehill Date: Thu, 1 Nov 2018 14:25:19 +0000 (+0100) Subject: Merge branch 'master' into martin-t/dmgtext X-Git-Tag: xonotic-v0.8.5~1729^2~1 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=62d736d8c3a51baf5fa3a4265e39a2b773704a91;hp=59845bcb06720efc1f30ffbf550f7e04a4c51fcd Merge branch 'master' into martin-t/dmgtext --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 6a84214343..291bf499f3 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ test_compilation_units: test_sv_game: stage: test script: - - git clone --depth=1 --branch=master https://gitlab.com/xonotic/darkplaces.git darkplaces + - git clone --depth=1 --branch=div0-stable https://gitlab.com/xonotic/darkplaces.git darkplaces - cd darkplaces && make sv-debug -j $(nproc) && export ENGINE="$PWD/darkplaces-dedicated -xonotic" - cd .. @@ -29,7 +29,7 @@ test_sv_game: - 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=eb10d49149a894afd1c3e8af610dc98f + - EXPECT=78686205fc72d91c106a3022d0d8b083 - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg | tee /dev/stderr | grep '^:' @@ -44,7 +44,7 @@ test_sv_game: test_sv_unit: stage: test script: - - git clone --depth=1 --branch=master https://gitlab.com/xonotic/darkplaces.git darkplaces + - git clone --depth=1 --branch=div0-stable https://gitlab.com/xonotic/darkplaces.git darkplaces - cd darkplaces && make sv-debug -j $(nproc) && export ENGINE="$PWD/darkplaces-dedicated -xonotic" - cd .. diff --git a/.tx/merge-base b/.tx/merge-base index 946c98fc60..4d49f61166 100644 --- a/.tx/merge-base +++ b/.tx/merge-base @@ -1 +1 @@ -Fri Mar 2 07:24:40 CET 2018 +Tue Oct 30 07:24:07 CET 2018 diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a66f9fd2e..f732e5b7af 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,12 +125,12 @@ copy(progs) copy(menu) function(pack prog) - add_custom_target(${prog}.pk3 + add_custom_target(${prog}.pk3 ALL DEPENDS ${prog}-${GIT_DESC}.pk3 ) add_custom_command(OUTPUT ${prog}-${GIT_DESC}.pk3 DEPENDS ${prog} - COMMAND ${CMAKE_COMMAND} -E echo "http://xonotic.org" > "${prog}-${GIT_DESC}.txt" + COMMAND ${CMAKE_COMMAND} -E echo "https://xonotic.org" > "${prog}-${GIT_DESC}.txt" COMMAND ${CMAKE_COMMAND} -E copy "$/${prog}.dat" "${prog}-${GIT_DESC}.dat" COMMAND ${CMAKE_COMMAND} -E copy "$/${prog}.lno" "${prog}-${GIT_DESC}.lno" COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${prog}-${GIT_DESC}.pk3" --format=zip diff --git a/_hud_common.cfg b/_hud_common.cfg index 622e581d3b..75e8eb8d6f 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -93,7 +93,7 @@ seta hud_panel_engineinfo_framecounter_exponentialmovingaverage_new_weight 0.1 " seta hud_panel_engineinfo_framecounter_exponentialmovingaverage_instantupdate_change_threshold 0.5 "threshold for fps change when to update instantly, to make big fps changes update faster" seta hud_panel_physics_acceleration_movingaverage 1 "use an averaging method for calculating acceleration instead of the real value" -seta hud_panel_physics_update_interval 0.0666 "how often (in seconds) numeric values get updated on screen" +seta hud_panel_physics_update_interval 0.016 "how often (in seconds) numeric values get updated on screen" seta hud_panel_physics_speed_unit "1" "speed unit (1 = qu/s, 2 = m/s, 3 = km/h, 4 = mph, 5 = knots)" seta hud_panel_itemstime_progressbar_maxtime "30" "when left time is at least this amount, the status bar is full" @@ -113,7 +113,10 @@ seta hud_panel_scoreboard_maxheight 0.6 "max height of the scoreboard; a few pla seta hud_panel_scoreboard_others_showscore 1 "show scores of players listed in the last row when the scoreboard reaches the max height" seta hud_panel_scoreboard_spectators_showping 1 "show ping of spectators" seta hud_panel_scoreboard_spectators_aligned 0 "align spectators in columns" -seta hud_panel_scoreboard_minwidth 0.4 "minimum width of the scoreboard" +seta hud_panel_scoreboard_minwidth 0.6 "minimum width of the scoreboard" + +seta hud_panel_scoreboard_accuracy_showdelay 2 "how long to delay displaying accuracy below the scoreboard if it's too far down" +seta hud_panel_scoreboard_accuracy_showdelay_minpos 0.75 "delay displaying the accuracy panel only if its position is lower than this percentage of the screen height from the top" // hud panel aliases alias quickmenu "cl_cmd hud quickmenu ${* ?}" @@ -156,11 +159,11 @@ seta hud_damage_pain_threshold_lower_health 50 "at which health we start lowerin seta hud_damage_pain_threshold_pulsating_min 0.6 "minimum value when calculating the pulse: max(pulsating_min, fabs(sin(PI * time / period))" seta hud_damage_pain_threshold_pulsating_period 0.8 "one pulse every X seconds" -seta hud_powerup 0 "power of the sharpen effect when owning the shield or strength powerups, default is 0.5" +seta hud_powerup 0 "power of the sharpen effect when owning the shield or strength powerups" seta hud_postprocessing 1 "enables the ability for effects such as hud_damage_blur and hud_contents to apply a postprocessing method upon the screen - enabling this disables manual editing of the postprocess cvars" -seta hud_postprocessing_maxbluralpha 0 "maximum alpha which the blur postprocess can be, default is 0.5" -seta hud_postprocessing_maxblurradius 8 "maximum radius which the blur postprocess can be, default is 8" +seta hud_postprocessing_maxbluralpha 0 "maximum alpha which the blur postprocess can be" +seta hud_postprocessing_maxblurradius 8 "maximum radius which the blur postprocess can be" seta hud_contents 1 "an improved version of gl_polyblend for liquids such as water/lava/slime, draw a filler when inside the liquid" seta hud_contents_blur 10 "Use postprocessing to blur the screen when you are inside a liquid. Higher values = more blur" diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index 9a1654f83a..30e5a8bee9 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -62,7 +62,7 @@ seta hud_panel_weapons_label "" "1 = show number of weapon, 2 = show bound key o seta hud_panel_weapons_label_scale "" "scale of the weapon text label" seta hud_panel_weapons_accuracy "" "show accuracy color as the weapon icon background; colors can be configured with accuracy_color* cvars" seta hud_panel_weapons_ammo "" "show ammo as a status bar" -seta hud_panel_weapons_onlyowned "" "show only owned weapons" +seta hud_panel_weapons_onlyowned "" "show only owned weapons, set it to 2 to show only the held weapon" seta hud_panel_weapons_noncurrent_alpha "" "alpha of noncurrent weapons" seta hud_panel_weapons_noncurrent_scale "" "scale of noncurrent weapons, relative to the current weapon" seta hud_panel_weapons_selection_radius "" "number of weapons that get partially highlighted on each side of the currently selected weapon" diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg index 57d5d745df..45a75349be 100644 --- a/bal-wep-mario.cfg +++ b/bal-wep-mario.cfg @@ -1,6 +1,6 @@ // {{{ #1: Blaster set g_balance_blaster_primary_animtime 0.2 -set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_damage 20 set g_balance_blaster_primary_delay 0 set g_balance_blaster_primary_edgedamage 12.5 set g_balance_blaster_primary_force 300 @@ -75,7 +75,7 @@ set g_balance_machinegun_burst_speed 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 -set g_balance_machinegun_first_force 5 +set g_balance_machinegun_first_force 3 set g_balance_machinegun_first_refire 0.125 set g_balance_machinegun_first_spread 0.03 set g_balance_machinegun_mode 1 @@ -87,12 +87,12 @@ set g_balance_machinegun_spread_max 0.05 set g_balance_machinegun_spread_min 0.02 set g_balance_machinegun_sustained_ammo 1 set g_balance_machinegun_sustained_damage 10 -set g_balance_machinegun_sustained_force 5 +set g_balance_machinegun_sustained_force 3 set g_balance_machinegun_sustained_refire 0.1 set g_balance_machinegun_sustained_spread 0.03 set g_balance_machinegun_switchdelay_drop 0.2 set g_balance_machinegun_switchdelay_raise 0.2 -set g_balance_machinegun_weaponreplace "" +set g_balance_machinegun_weaponreplace "arc" set g_balance_machinegun_weaponstart 0 set g_balance_machinegun_weaponstartoverride -1 set g_balance_machinegun_weaponthrowable 1 @@ -219,7 +219,7 @@ set g_balance_electro_secondary_speed 1000 set g_balance_electro_secondary_speed_up 200 set g_balance_electro_secondary_speed_z 0 set g_balance_electro_secondary_spread 0 -set g_balance_electro_secondary_stick 1 +set g_balance_electro_secondary_stick 0 set g_balance_electro_secondary_touchexplode 1 set g_balance_electro_switchdelay_drop 0.2 set g_balance_electro_switchdelay_raise 0.2 @@ -251,18 +251,18 @@ set g_balance_crylink_primary_other_lifetime 5 set g_balance_crylink_primary_radius 80 set g_balance_crylink_primary_refire 0.7 set g_balance_crylink_primary_shots 6 -set g_balance_crylink_primary_speed 2000 +set g_balance_crylink_primary_speed 4000 set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 set g_balance_crylink_secondary_damage 10 set g_balance_crylink_secondary_edgedamage 5 -set g_balance_crylink_secondary_force -250 +set g_balance_crylink_secondary_force -200 set g_balance_crylink_secondary_joindelay 0 set g_balance_crylink_secondary_joinexplode 0 set g_balance_crylink_secondary_joinexplode_damage 0 @@ -270,17 +270,17 @@ set g_balance_crylink_secondary_joinexplode_edgedamage 0 set g_balance_crylink_secondary_joinexplode_force 0 set g_balance_crylink_secondary_joinexplode_radius 0 set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_linkexplode 1 +set g_balance_crylink_secondary_linkexplode 0 set g_balance_crylink_secondary_middle_fadetime 5 set g_balance_crylink_secondary_middle_lifetime 5 -set g_balance_crylink_secondary_other_fadetime 5 -set g_balance_crylink_secondary_other_lifetime 5 +set g_balance_crylink_secondary_other_fadetime 2 +set g_balance_crylink_secondary_other_lifetime 2 set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_refire 0.7 +set g_balance_crylink_secondary_refire 0.65 set g_balance_crylink_secondary_shots 5 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 -set g_balance_crylink_secondary_spreadtype 1 +set g_balance_crylink_secondary_speed 7000 +set g_balance_crylink_secondary_spread 0.08 +set g_balance_crylink_secondary_spreadtype 0 set g_balance_crylink_switchdelay_drop 0.2 set g_balance_crylink_switchdelay_raise 0.2 set g_balance_crylink_weaponreplace "" @@ -303,7 +303,8 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 6 set g_balance_vortex_primary_animtime 0.4 -set g_balance_vortex_primary_damage 70 +set g_balance_vortex_primary_armorpierce 0 +set g_balance_vortex_primary_damage 65 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 set g_balance_vortex_primary_damagefalloff_maxdist 0 @@ -315,6 +316,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -371,7 +373,7 @@ set g_balance_hagar_secondary_speed 2000 set g_balance_hagar_secondary_spread 0 set g_balance_hagar_switchdelay_drop 0.2 set g_balance_hagar_switchdelay_raise 0.2 -set g_balance_hagar_weaponreplace "" +set g_balance_hagar_weaponreplace "0" set g_balance_hagar_weaponstart 0 set g_balance_hagar_weaponstartoverride -1 set g_balance_hagar_weaponthrowable 1 @@ -436,6 +438,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage 150 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -456,7 +459,7 @@ set g_balance_vaporizer_switchdelay_raise 0.2 set g_balance_vaporizer_weaponreplace "" set g_balance_vaporizer_weaponstart 0 set g_balance_vaporizer_weaponstartoverride -1 -set g_balance_vaporizer_weaponthrowable 0 +set g_balance_vaporizer_weaponthrowable 1 // }}} // {{{ #13: Grappling Hook set g_balance_hook_primary_ammo 5 @@ -750,10 +753,10 @@ set g_balance_arc_beam_heat 0 set g_balance_arc_burst_heat 5 set g_balance_arc_beam_maxangle 10 set g_balance_arc_beam_nonplayerdamage 80 -set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_range 1500 set g_balance_arc_beam_refire 0.25 set g_balance_arc_beam_returnspeed 8 -set g_balance_arc_beam_tightness 0.5 +set g_balance_arc_beam_tightness 0.6 set g_balance_arc_bolt 1 set g_balance_arc_bolt_ammo 1 set g_balance_arc_bolt_damage 25 @@ -777,44 +780,183 @@ set g_balance_arc_weaponstart 0 set g_balance_arc_weaponstartoverride -1 set g_balance_arc_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 +// }}} +// {{{ Overkill Shotgun +set g_balance_okshotgun_primary_ammo 3 +set g_balance_okshotgun_primary_animtime 0.65 +set g_balance_okshotgun_primary_bot_range 512 +set g_balance_okshotgun_primary_bullets 10 +set g_balance_okshotgun_primary_damage 17 +set g_balance_okshotgun_primary_force 80 +set g_balance_okshotgun_primary_refire 0.75 +set g_balance_okshotgun_primary_solidpenetration 3.8 +set g_balance_okshotgun_primary_spread 0.07 +set g_balance_okshotgun_reload_ammo 24 +set g_balance_okshotgun_reload_time 2 +set g_balance_okshotgun_secondary_animtime 0.2 +set g_balance_okshotgun_secondary_damage 25 +set g_balance_okshotgun_secondary_delay 0 +set g_balance_okshotgun_secondary_edgedamage 12.5 +set g_balance_okshotgun_secondary_force 300 +set g_balance_okshotgun_secondary_lifetime 5 +set g_balance_okshotgun_secondary_radius 70 +set g_balance_okshotgun_secondary_refire 0.7 +set g_balance_okshotgun_secondary_refire_type 1 +set g_balance_okshotgun_secondary_shotangle 0 +set g_balance_okshotgun_secondary_speed 6000 +set g_balance_okshotgun_secondary_spread 0 +set g_balance_okshotgun_switchdelay_drop 0.2 +set g_balance_okshotgun_switchdelay_raise 0.2 +set g_balance_okshotgun_weaponreplace "" +set g_balance_okshotgun_weaponstart 0 +set g_balance_okshotgun_weaponstartoverride -1 +set g_balance_okshotgun_weaponthrowable 1 +// }}} +// {{{ Overkill Machine Gun +set g_balance_okmachinegun_primary_ammo 1 +set g_balance_okmachinegun_primary_damage 25 +set g_balance_okmachinegun_primary_force 5 +set g_balance_okmachinegun_primary_refire 0.1 +set g_balance_okmachinegun_primary_solidpenetration 63 +set g_balance_okmachinegun_primary_spread_add 0.012 +set g_balance_okmachinegun_primary_spread_max 0.05 +set g_balance_okmachinegun_primary_spread_min 0 +set g_balance_okmachinegun_reload_ammo 30 +set g_balance_okmachinegun_reload_time 1.5 +set g_balance_okmachinegun_secondary_animtime 0.2 +set g_balance_okmachinegun_secondary_damage 25 +set g_balance_okmachinegun_secondary_delay 0 +set g_balance_okmachinegun_secondary_edgedamage 12.5 +set g_balance_okmachinegun_secondary_force 300 +set g_balance_okmachinegun_secondary_lifetime 5 +set g_balance_okmachinegun_secondary_radius 70 +set g_balance_okmachinegun_secondary_refire 0.7 +set g_balance_okmachinegun_secondary_refire_type 1 +set g_balance_okmachinegun_secondary_shotangle 0 +set g_balance_okmachinegun_secondary_speed 6000 +set g_balance_okmachinegun_secondary_spread 0 +set g_balance_okmachinegun_switchdelay_drop 0.2 +set g_balance_okmachinegun_switchdelay_raise 0.2 +set g_balance_okmachinegun_weaponreplace "" +set g_balance_okmachinegun_weaponstart 0 +set g_balance_okmachinegun_weaponstartoverride -1 +set g_balance_okmachinegun_weaponthrowable 1 +// }}} +// {{{ Overkill Nex +set g_balance_oknex_charge 0 +set g_balance_oknex_charge_animlimit 0.5 +set g_balance_oknex_charge_limit 1 +set g_balance_oknex_charge_maxspeed 800 +set g_balance_oknex_charge_mindmg 40 +set g_balance_oknex_charge_minspeed 400 +set g_balance_oknex_charge_rate 0.6 +set g_balance_oknex_charge_rot_pause 0 +set g_balance_oknex_charge_rot_rate 0 +set g_balance_oknex_charge_shot_multiplier 0 +set g_balance_oknex_charge_start 0.5 +set g_balance_oknex_charge_velocity_rate 0 +set g_balance_oknex_primary_ammo 10 +set g_balance_oknex_primary_animtime 0.65 +set g_balance_oknex_primary_damage 100 +set g_balance_oknex_primary_damagefalloff_forcehalflife 0 +set g_balance_oknex_primary_damagefalloff_halflife 0 +set g_balance_oknex_primary_damagefalloff_maxdist 0 +set g_balance_oknex_primary_damagefalloff_mindist 0 +set g_balance_oknex_primary_force 500 +set g_balance_oknex_primary_refire 1 +set g_balance_oknex_reload_ammo 50 +set g_balance_oknex_reload_time 2 +set g_balance_oknex_secondary 2 +set g_balance_oknex_secondary_ammo 0 +set g_balance_oknex_secondary_animtime 0.2 +set g_balance_oknex_secondary_chargepool 0 +set g_balance_oknex_secondary_chargepool_pause_regen 1 +set g_balance_oknex_secondary_chargepool_regen 0.15 +set g_balance_oknex_secondary_damage 25 +set g_balance_oknex_secondary_damagefalloff_forcehalflife 0 +set g_balance_oknex_secondary_damagefalloff_halflife 0 +set g_balance_oknex_secondary_damagefalloff_maxdist 0 +set g_balance_oknex_secondary_damagefalloff_mindist 0 +set g_balance_oknex_secondary_force 300 +set g_balance_oknex_secondary_refire 0.7 +set g_balance_oknex_secondary_refire_type 1 +set g_balance_oknex_secondary_delay 0 +set g_balance_oknex_secondary_edgedamage 12.5 +set g_balance_oknex_secondary_lifetime 5 +set g_balance_oknex_secondary_radius 70 +set g_balance_oknex_secondary_shotangle 0 +set g_balance_oknex_secondary_speed 6000 +set g_balance_oknex_secondary_spread 0 +set g_balance_oknex_switchdelay_drop 0.2 +set g_balance_oknex_switchdelay_raise 0.2 +set g_balance_oknex_weaponreplace "" +set g_balance_oknex_weaponstart 0 +set g_balance_oknex_weaponstartoverride -1 +set g_balance_oknex_weaponthrowable 1 // }}} diff --git a/bal-wep-nexuiz25.cfg b/bal-wep-nexuiz25.cfg index f7d912ca77..b1a83000f6 100644 --- a/bal-wep-nexuiz25.cfg +++ b/bal-wep-nexuiz25.cfg @@ -303,6 +303,7 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 5 set g_balance_vortex_primary_animtime 0.3 +set g_balance_vortex_primary_armorpierce 0 set g_balance_vortex_primary_damage 100 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 @@ -315,6 +316,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -436,6 +438,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage -1 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -777,44 +780,70 @@ set g_balance_arc_weaponstart 0 set g_balance_arc_weaponstartoverride -1 set g_balance_arc_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 // }}} diff --git a/bal-wep-overkill-nerfed.cfg b/bal-wep-overkill-nerfed.cfg new file mode 100644 index 0000000000..e4fb022369 --- /dev/null +++ b/bal-wep-overkill-nerfed.cfg @@ -0,0 +1,116 @@ +// This config file is for overkill weapons that were nerfed to have the same +// stats as vanilla weapons, secondary attack uses stats of vanilla blaster. + +// {{{ Overkill Shotgun +set g_balance_okshotgun_primary_ammo 1 +set g_balance_okshotgun_primary_animtime 0.2 +set g_balance_okshotgun_primary_bot_range 512 +set g_balance_okshotgun_primary_bullets 12 +set g_balance_okshotgun_primary_damage 4 +set g_balance_okshotgun_primary_force 15 +set g_balance_okshotgun_primary_refire 0.75 +set g_balance_okshotgun_primary_solidpenetration 3.8 +set g_balance_okshotgun_primary_spread 0.12 +set g_balance_okshotgun_reload_ammo 0 +set g_balance_okshotgun_reload_time 2 +set g_balance_okshotgun_secondary_animtime 0.2 +set g_balance_okshotgun_secondary_damage 20 +set g_balance_okshotgun_secondary_delay 0 +set g_balance_okshotgun_secondary_edgedamage 10 +set g_balance_okshotgun_secondary_force 300 +set g_balance_okshotgun_secondary_lifetime 5 +set g_balance_okshotgun_secondary_radius 60 +set g_balance_okshotgun_secondary_refire 0.7 +set g_balance_okshotgun_secondary_refire_type 0 +set g_balance_okshotgun_secondary_shotangle 0 +set g_balance_okshotgun_secondary_speed 6000 +set g_balance_okshotgun_secondary_spread 0 +set g_balance_okshotgun_switchdelay_drop 0.2 +set g_balance_okshotgun_switchdelay_raise 0.2 +set g_balance_okshotgun_weaponreplace "" +set g_balance_okshotgun_weaponstart 0 +set g_balance_okshotgun_weaponstartoverride -1 +set g_balance_okshotgun_weaponthrowable 1 +// }}} +// {{{ Overkill Machine Gun +set g_balance_okmachinegun_primary_ammo 1 +set g_balance_okmachinegun_primary_damage 10 +set g_balance_okmachinegun_primary_force 3 +set g_balance_okmachinegun_primary_refire 0.1 +set g_balance_okmachinegun_primary_solidpenetration 13.1 +set g_balance_okmachinegun_primary_spread_add 0.012 +set g_balance_okmachinegun_primary_spread_max 0.05 +set g_balance_okmachinegun_primary_spread_min 0.02 +set g_balance_okmachinegun_reload_ammo 60 +set g_balance_okmachinegun_reload_time 2 +set g_balance_okmachinegun_secondary_animtime 0.2 +set g_balance_okmachinegun_secondary_damage 20 +set g_balance_okmachinegun_secondary_delay 0 +set g_balance_okmachinegun_secondary_edgedamage 10 +set g_balance_okmachinegun_secondary_force 300 +set g_balance_okmachinegun_secondary_lifetime 5 +set g_balance_okmachinegun_secondary_radius 60 +set g_balance_okmachinegun_secondary_refire 0.7 +set g_balance_okmachinegun_secondary_refire_type 0 +set g_balance_okmachinegun_secondary_shotangle 0 +set g_balance_okmachinegun_secondary_speed 6000 +set g_balance_okmachinegun_secondary_spread 0 +set g_balance_okmachinegun_switchdelay_drop 0.2 +set g_balance_okmachinegun_switchdelay_raise 0.2 +set g_balance_okmachinegun_weaponreplace "" +set g_balance_okmachinegun_weaponstart 0 +set g_balance_okmachinegun_weaponstartoverride -1 +set g_balance_okmachinegun_weaponthrowable 1 +// }}} +// {{{ Overkill Nex +set g_balance_oknex_charge 1 +set g_balance_oknex_charge_animlimit 0.5 +set g_balance_oknex_charge_limit 1 +set g_balance_oknex_charge_maxspeed 800 +set g_balance_oknex_charge_mindmg 40 +set g_balance_oknex_charge_minspeed 400 +set g_balance_oknex_charge_rate 0.6 +set g_balance_oknex_charge_rot_pause 0 +set g_balance_oknex_charge_rot_rate 0 +set g_balance_oknex_charge_shot_multiplier 0 +set g_balance_oknex_charge_start 0.5 +set g_balance_oknex_charge_velocity_rate 0 +set g_balance_oknex_primary_ammo 6 +set g_balance_oknex_primary_animtime 0.4 +set g_balance_oknex_primary_damage 80 +set g_balance_oknex_primary_damagefalloff_forcehalflife 0 +set g_balance_oknex_primary_damagefalloff_halflife 0 +set g_balance_oknex_primary_damagefalloff_maxdist 0 +set g_balance_oknex_primary_damagefalloff_mindist 0 +set g_balance_oknex_primary_force 400 +set g_balance_oknex_primary_refire 1.5 +set g_balance_oknex_reload_ammo 0 +set g_balance_oknex_reload_time 2 +set g_balance_oknex_secondary 0 +set g_balance_oknex_secondary_ammo 2 +set g_balance_oknex_secondary_animtime 0 +set g_balance_oknex_secondary_chargepool 0 +set g_balance_oknex_secondary_chargepool_pause_regen 1 +set g_balance_oknex_secondary_chargepool_regen 0.15 +set g_balance_oknex_secondary_damage 0 +set g_balance_oknex_secondary_damagefalloff_forcehalflife 0 +set g_balance_oknex_secondary_damagefalloff_halflife 0 +set g_balance_oknex_secondary_damagefalloff_maxdist 0 +set g_balance_oknex_secondary_damagefalloff_mindist 0 +set g_balance_oknex_secondary_force 0 +set g_balance_oknex_secondary_refire 0 +set g_balance_oknex_secondary_refire_type 0 +set g_balance_oknex_secondary_delay 0 +set g_balance_oknex_secondary_edgedamage 10 +set g_balance_oknex_secondary_lifetime 5 +set g_balance_oknex_secondary_radius 60 +set g_balance_oknex_secondary_shotangle 0 +set g_balance_oknex_secondary_speed 6000 +set g_balance_oknex_secondary_spread 0 +set g_balance_oknex_switchdelay_drop 0.2 +set g_balance_oknex_switchdelay_raise 0.2 +set g_balance_oknex_weaponreplace "" +set g_balance_oknex_weaponstart 0 +set g_balance_oknex_weaponstartoverride -1 +set g_balance_oknex_weaponthrowable 1 +// }}} diff --git a/bal-wep-overkill.cfg b/bal-wep-overkill.cfg deleted file mode 100644 index 9cfffed10b..0000000000 --- a/bal-wep-overkill.cfg +++ /dev/null @@ -1,820 +0,0 @@ -// {{{ #1: Blaster -set g_balance_blaster_primary_animtime 0.2 -set g_balance_blaster_primary_damage 20 -set g_balance_blaster_primary_delay 0 -set g_balance_blaster_primary_edgedamage 10 -set g_balance_blaster_primary_force 300 -set g_balance_blaster_primary_force_zscale 1.25 -set g_balance_blaster_primary_lifetime 5 -set g_balance_blaster_primary_radius 60 -set g_balance_blaster_primary_refire 0.7 -set g_balance_blaster_primary_shotangle 0 -set g_balance_blaster_primary_speed 6000 -set g_balance_blaster_primary_spread 0 -set g_balance_blaster_secondary 0 -set g_balance_blaster_secondary_animtime 0.2 -set g_balance_blaster_secondary_damage 25 -set g_balance_blaster_secondary_delay 0 -set g_balance_blaster_secondary_edgedamage 12.5 -set g_balance_blaster_secondary_force 300 -set g_balance_blaster_secondary_force_zscale 1.2 -set g_balance_blaster_secondary_lifetime 5 -set g_balance_blaster_secondary_radius 70 -set g_balance_blaster_secondary_refire 0.7 -set g_balance_blaster_secondary_shotangle 0 -set g_balance_blaster_secondary_speed 6000 -set g_balance_blaster_secondary_spread 0 -set g_balance_blaster_switchdelay_drop 0.2 -set g_balance_blaster_switchdelay_raise 0.2 -set g_balance_blaster_weaponreplace "" -set g_balance_blaster_weaponstart 1 -set g_balance_blaster_weaponstartoverride -1 -set g_balance_blaster_weaponthrowable 0 -// }}} -// {{{ #2: Shotgun -set g_balance_shotgun_primary_ammo 3 -set g_balance_shotgun_primary_animtime 0.65 -set g_balance_shotgun_primary_bullets 10 -set g_balance_shotgun_primary_damage 17 -set g_balance_shotgun_primary_force 80 -set g_balance_shotgun_primary_refire 0.75 -set g_balance_shotgun_primary_solidpenetration 3.8 -set g_balance_shotgun_primary_spread 0.07 -set g_balance_shotgun_reload_ammo 24 -set g_balance_shotgun_reload_time 2 -set g_balance_shotgun_secondary 1 -set g_balance_shotgun_secondary_animtime 1.15 -set g_balance_shotgun_secondary_damage 70 -set g_balance_shotgun_secondary_force 200 -set g_balance_shotgun_secondary_melee_delay 0.25 -set g_balance_shotgun_secondary_melee_multihit 1 -set g_balance_shotgun_secondary_melee_no_doubleslap 1 -set g_balance_shotgun_secondary_melee_nonplayerdamage 40 -set g_balance_shotgun_secondary_melee_range 120 -set g_balance_shotgun_secondary_melee_swing_side 120 -set g_balance_shotgun_secondary_melee_swing_up 30 -set g_balance_shotgun_secondary_melee_time 0.15 -set g_balance_shotgun_secondary_melee_traces 10 -set g_balance_shotgun_secondary_refire 1.25 -set g_balance_shotgun_secondary_alt_animtime 0.2 -set g_balance_shotgun_secondary_alt_refire 1.2 -set g_balance_shotgun_switchdelay_drop 0.2 -set g_balance_shotgun_switchdelay_raise 0.2 -set g_balance_shotgun_weaponreplace "" -set g_balance_shotgun_weaponstart 1 -set g_balance_shotgun_weaponstartoverride -1 -set g_balance_shotgun_weaponthrowable 1 -// }}} -// {{{ #3: Machine Gun -set g_balance_machinegun_burst 3 -set g_balance_machinegun_burst_ammo 3 -set g_balance_machinegun_burst_animtime 0.3 -set g_balance_machinegun_burst_refire 0.06 -set g_balance_machinegun_burst_refire2 0.45 -set g_balance_machinegun_burst_speed 0 -set g_balance_machinegun_first 1 -set g_balance_machinegun_first_ammo 1 -set g_balance_machinegun_first_damage 14 -set g_balance_machinegun_first_force 5 -set g_balance_machinegun_first_refire 0.125 -set g_balance_machinegun_first_spread 0.03 -set g_balance_machinegun_mode 1 -set g_balance_machinegun_reload_ammo 30 -set g_balance_machinegun_reload_time 1.5 -set g_balance_machinegun_solidpenetration 63 -set g_balance_machinegun_spread_add 0.012 -set g_balance_machinegun_spread_max 0.05 -set g_balance_machinegun_spread_min 0 -set g_balance_machinegun_sustained_ammo 1 -set g_balance_machinegun_sustained_damage 25 -set g_balance_machinegun_sustained_force 5 -set g_balance_machinegun_sustained_refire 0.1 -set g_balance_machinegun_sustained_spread 0.01 -set g_balance_machinegun_switchdelay_drop 0.2 -set g_balance_machinegun_switchdelay_raise 0.2 -set g_balance_machinegun_weaponreplace "" -set g_balance_machinegun_weaponstart 0 -set g_balance_machinegun_weaponstartoverride -1 -set g_balance_machinegun_weaponthrowable 1 -// }}} -// {{{ #4: Mortar -set g_balance_mortar_bouncefactor 0.5 -set g_balance_mortar_bouncestop 0.075 -set g_balance_mortar_primary_ammo 2 -set g_balance_mortar_primary_animtime 0.3 -set g_balance_mortar_primary_damage 55 -set g_balance_mortar_primary_damageforcescale 0 -set g_balance_mortar_primary_edgedamage 25 -set g_balance_mortar_primary_force 250 -set g_balance_mortar_primary_health 15 -set g_balance_mortar_primary_lifetime 5 -set g_balance_mortar_primary_lifetime_stick 0 -set g_balance_mortar_primary_radius 120 -set g_balance_mortar_primary_refire 0.8 -set g_balance_mortar_primary_remote_minbouncecnt 0 -set g_balance_mortar_primary_speed 1900 -set g_balance_mortar_primary_speed_up 225 -set g_balance_mortar_primary_speed_z 0 -set g_balance_mortar_primary_spread 0 -set g_balance_mortar_primary_type 0 -set g_balance_mortar_reload_ammo 0 -set g_balance_mortar_reload_time 2 -set g_balance_mortar_secondary_ammo 2 -set g_balance_mortar_secondary_animtime 0.3 -set g_balance_mortar_secondary_damage 55 -set g_balance_mortar_secondary_damageforcescale 4 -set g_balance_mortar_secondary_edgedamage 30 -set g_balance_mortar_secondary_force 250 -set g_balance_mortar_secondary_health 30 -set g_balance_mortar_secondary_lifetime 5 -set g_balance_mortar_secondary_lifetime_bounce 0.5 -set g_balance_mortar_secondary_lifetime_stick 0 -set g_balance_mortar_secondary_radius 120 -set g_balance_mortar_secondary_refire 0.7 -set g_balance_mortar_secondary_remote_detonateprimary 0 -set g_balance_mortar_secondary_speed 1400 -set g_balance_mortar_secondary_speed_up 150 -set g_balance_mortar_secondary_speed_z 0 -set g_balance_mortar_secondary_spread 0 -set g_balance_mortar_secondary_type 1 -set g_balance_mortar_switchdelay_drop 0.2 -set g_balance_mortar_switchdelay_raise 0.2 -set g_balance_mortar_weaponreplace "" -set g_balance_mortar_weaponstart 0 -set g_balance_mortar_weaponstartoverride -1 -set g_balance_mortar_weaponthrowable 1 -// }}} -// {{{ #5: Mine Layer (MUTATOR WEAPON) -set g_balance_minelayer_ammo 4 -set g_balance_minelayer_animtime 0.4 -set g_balance_minelayer_damage 40 -set g_balance_minelayer_damageforcescale 0 -set g_balance_minelayer_detonatedelay -1 -set g_balance_minelayer_edgedamage 20 -set g_balance_minelayer_force 250 -set g_balance_minelayer_health 15 -set g_balance_minelayer_lifetime 10 -set g_balance_minelayer_lifetime_countdown 0.5 -set g_balance_minelayer_limit 3 -set g_balance_minelayer_protection 0 -set g_balance_minelayer_proximityradius 150 -set g_balance_minelayer_radius 175 -set g_balance_minelayer_refire 1.5 -set g_balance_minelayer_reload_ammo 0 -set g_balance_minelayer_reload_time 2 -set g_balance_minelayer_remote_damage 45 -set g_balance_minelayer_remote_edgedamage 40 -set g_balance_minelayer_remote_force 300 -set g_balance_minelayer_remote_radius 200 -set g_balance_minelayer_speed 1000 -set g_balance_minelayer_switchdelay_drop 0.2 -set g_balance_minelayer_switchdelay_raise 0.2 -set g_balance_minelayer_time 0.5 -set g_balance_minelayer_weaponreplace "" -set g_balance_minelayer_weaponstart 0 -set g_balance_minelayer_weaponstartoverride -1 -set g_balance_minelayer_weaponthrowable 1 -// }}} -// {{{ #6: Electro -set g_balance_electro_combo_comboradius 300 -set g_balance_electro_combo_comboradius_thruwall 200 -set g_balance_electro_combo_damage 50 -set g_balance_electro_combo_edgedamage 25 -set g_balance_electro_combo_force 120 -set g_balance_electro_combo_radius 150 -set g_balance_electro_combo_safeammocheck 1 -set g_balance_electro_combo_speed 2000 -set g_balance_electro_primary_ammo 4 -set g_balance_electro_primary_animtime 0.3 -set g_balance_electro_primary_comboradius 300 -set g_balance_electro_primary_damage 40 -set g_balance_electro_primary_edgedamage 20 -set g_balance_electro_primary_force 200 -set g_balance_electro_primary_lifetime 5 -set g_balance_electro_primary_midaircombo_explode 1 -set g_balance_electro_primary_midaircombo_interval 0.1 -set g_balance_electro_primary_midaircombo_radius 0 -set g_balance_electro_primary_radius 100 -set g_balance_electro_primary_refire 0.6 -set g_balance_electro_primary_speed 2500 -set g_balance_electro_primary_spread 0 -set g_balance_electro_reload_ammo 0 -set g_balance_electro_reload_time 2 -set g_balance_electro_secondary_ammo 2 -set g_balance_electro_secondary_animtime 0.2 -set g_balance_electro_secondary_bouncefactor 0.3 -set g_balance_electro_secondary_bouncestop 0.05 -set g_balance_electro_secondary_count 3 -set g_balance_electro_secondary_damage 30 -set g_balance_electro_secondary_damagedbycontents 1 -set g_balance_electro_secondary_damageforcescale 4 -set g_balance_electro_secondary_edgedamage 15 -set g_balance_electro_secondary_force 50 -set g_balance_electro_secondary_health 5 -set g_balance_electro_secondary_lifetime 4 -set g_balance_electro_secondary_radius 150 -set g_balance_electro_secondary_refire 0.2 -set g_balance_electro_secondary_refire2 1.6 -set g_balance_electro_secondary_speed 1000 -set g_balance_electro_secondary_speed_up 200 -set g_balance_electro_secondary_speed_z 0 -set g_balance_electro_secondary_spread 0 -set g_balance_electro_secondary_stick 0 -set g_balance_electro_secondary_touchexplode 1 -set g_balance_electro_switchdelay_drop 0.2 -set g_balance_electro_switchdelay_raise 0.2 -set g_balance_electro_weaponreplace "" -set g_balance_electro_weaponstart 0 -set g_balance_electro_weaponstartoverride -1 -set g_balance_electro_weaponthrowable 1 -// }}} -// {{{ #7: Crylink -set g_balance_crylink_primary_ammo 3 -set g_balance_crylink_primary_animtime 0.3 -set g_balance_crylink_primary_bouncedamagefactor 0.5 -set g_balance_crylink_primary_bounces 1 -set g_balance_crylink_primary_damage 10 -set g_balance_crylink_primary_edgedamage 5 -set g_balance_crylink_primary_force -50 -set g_balance_crylink_primary_joindelay 0.1 -set g_balance_crylink_primary_joinexplode 1 -set g_balance_crylink_primary_joinexplode_damage 0 -set g_balance_crylink_primary_joinexplode_edgedamage 0 -set g_balance_crylink_primary_joinexplode_force 0 -set g_balance_crylink_primary_joinexplode_radius 0 -set g_balance_crylink_primary_joinspread 0.2 -set g_balance_crylink_primary_linkexplode 0 -set g_balance_crylink_primary_middle_fadetime 5 -set g_balance_crylink_primary_middle_lifetime 5 -set g_balance_crylink_primary_other_fadetime 5 -set g_balance_crylink_primary_other_lifetime 5 -set g_balance_crylink_primary_radius 80 -set g_balance_crylink_primary_refire 0.7 -set g_balance_crylink_primary_shots 6 -set g_balance_crylink_primary_speed 2000 -set g_balance_crylink_primary_spread 0.08 -set g_balance_crylink_reload_ammo 0 -set g_balance_crylink_reload_time 2 -set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 -set g_balance_crylink_secondary_animtime 0.2 -set g_balance_crylink_secondary_bouncedamagefactor 0.5 -set g_balance_crylink_secondary_bounces 0 -set g_balance_crylink_secondary_damage 8 -set g_balance_crylink_secondary_edgedamage 4 -set g_balance_crylink_secondary_force -200 -set g_balance_crylink_secondary_joindelay 0 -set g_balance_crylink_secondary_joinexplode 0 -set g_balance_crylink_secondary_joinexplode_damage 0 -set g_balance_crylink_secondary_joinexplode_edgedamage 0 -set g_balance_crylink_secondary_joinexplode_force 0 -set g_balance_crylink_secondary_joinexplode_radius 0 -set g_balance_crylink_secondary_joinspread 0 -set g_balance_crylink_secondary_linkexplode 1 -set g_balance_crylink_secondary_middle_fadetime 5 -set g_balance_crylink_secondary_middle_lifetime 5 -set g_balance_crylink_secondary_other_fadetime 5 -set g_balance_crylink_secondary_other_lifetime 5 -set g_balance_crylink_secondary_radius 100 -set g_balance_crylink_secondary_refire 0.7 -set g_balance_crylink_secondary_shots 5 -set g_balance_crylink_secondary_speed 3000 -set g_balance_crylink_secondary_spread 0.01 -set g_balance_crylink_secondary_spreadtype 1 -set g_balance_crylink_switchdelay_drop 0.2 -set g_balance_crylink_switchdelay_raise 0.2 -set g_balance_crylink_weaponreplace "" -set g_balance_crylink_weaponstart 0 -set g_balance_crylink_weaponstartoverride -1 -set g_balance_crylink_weaponthrowable 1 -// }}} -// {{{ #8: Vortex -set g_balance_vortex_charge 0 -set g_balance_vortex_charge_animlimit 0.5 -set g_balance_vortex_charge_limit 1 -set g_balance_vortex_charge_maxspeed 800 -set g_balance_vortex_charge_mindmg 40 -set g_balance_vortex_charge_minspeed 400 -set g_balance_vortex_charge_rate 0.6 -set g_balance_vortex_charge_rot_pause 0 -set g_balance_vortex_charge_rot_rate 0 -set g_balance_vortex_charge_shot_multiplier 0 -set g_balance_vortex_charge_start 0.5 -set g_balance_vortex_charge_velocity_rate 0 -set g_balance_vortex_primary_ammo 10 -set g_balance_vortex_primary_animtime 0.65 -set g_balance_vortex_primary_damage 100 -set g_balance_vortex_primary_damagefalloff_forcehalflife 0 -set g_balance_vortex_primary_damagefalloff_halflife 0 -set g_balance_vortex_primary_damagefalloff_maxdist 0 -set g_balance_vortex_primary_damagefalloff_mindist 0 -set g_balance_vortex_primary_force 500 -set g_balance_vortex_primary_refire 1 -set g_balance_vortex_reload_ammo 50 -set g_balance_vortex_reload_time 2 -set g_balance_vortex_secondary 1 -set g_balance_vortex_secondary_ammo 2 -set g_balance_vortex_secondary_animtime 0 -set g_balance_vortex_secondary_chargepool 0 -set g_balance_vortex_secondary_chargepool_pause_regen 1 -set g_balance_vortex_secondary_chargepool_regen 0.15 -set g_balance_vortex_secondary_damage 0 -set g_balance_vortex_secondary_damagefalloff_forcehalflife 0 -set g_balance_vortex_secondary_damagefalloff_halflife 0 -set g_balance_vortex_secondary_damagefalloff_maxdist 0 -set g_balance_vortex_secondary_damagefalloff_mindist 0 -set g_balance_vortex_secondary_force 0 -set g_balance_vortex_secondary_refire 0 -set g_balance_vortex_switchdelay_drop 0.2 -set g_balance_vortex_switchdelay_raise 0.2 -set g_balance_vortex_weaponreplace "" -set g_balance_vortex_weaponstart 0 -set g_balance_vortex_weaponstartoverride -1 -set g_balance_vortex_weaponthrowable 1 -// }}} -// {{{ #9: Hagar -set g_balance_hagar_primary_ammo 1 -set g_balance_hagar_primary_damage 25 -set g_balance_hagar_primary_damageforcescale 0 -set g_balance_hagar_primary_edgedamage 12.5 -set g_balance_hagar_primary_force 100 -set g_balance_hagar_primary_health 15 -set g_balance_hagar_primary_lifetime 5 -set g_balance_hagar_primary_radius 65 -set g_balance_hagar_primary_refire 0.16667 -set g_balance_hagar_primary_speed 2200 -set g_balance_hagar_primary_spread 0 -set g_balance_hagar_reload_ammo 0 -set g_balance_hagar_reload_time 2 -set g_balance_hagar_secondary 1 -set g_balance_hagar_secondary_ammo 1 -set g_balance_hagar_secondary_damage 35 -set g_balance_hagar_secondary_damageforcescale 0 -set g_balance_hagar_secondary_edgedamage 17.5 -set g_balance_hagar_secondary_force 75 -set g_balance_hagar_secondary_health 15 -set g_balance_hagar_secondary_lifetime_min 10 -set g_balance_hagar_secondary_lifetime_rand 0 -set g_balance_hagar_secondary_load 1 -set g_balance_hagar_secondary_load_abort 1 -set g_balance_hagar_secondary_load_animtime 0.2 -set g_balance_hagar_secondary_load_hold 4 -set g_balance_hagar_secondary_load_linkexplode 0 -set g_balance_hagar_secondary_load_max 4 -set g_balance_hagar_secondary_load_releasedeath 0 -set g_balance_hagar_secondary_load_speed 0.5 -set g_balance_hagar_secondary_load_spread 0.075 -set g_balance_hagar_secondary_load_spread_bias 0.5 -set g_balance_hagar_secondary_radius 80 -set g_balance_hagar_secondary_refire 0.5 -set g_balance_hagar_secondary_speed 2000 -set g_balance_hagar_secondary_spread 0 -set g_balance_hagar_switchdelay_drop 0.2 -set g_balance_hagar_switchdelay_raise 0.2 -set g_balance_hagar_weaponreplace "" -set g_balance_hagar_weaponstart 0 -set g_balance_hagar_weaponstartoverride -1 -set g_balance_hagar_weaponthrowable 1 -// }}} -// {{{ #10: Devastator -set g_balance_devastator_ammo 4 -set g_balance_devastator_animtime 0.4 -set g_balance_devastator_damage 80 -set g_balance_devastator_damageforcescale 1 -set g_balance_devastator_detonatedelay 0.02 -set g_balance_devastator_edgedamage 40 -set g_balance_devastator_force 400 -set g_balance_devastator_guidedelay 0.2 -set g_balance_devastator_guidegoal 512 -set g_balance_devastator_guiderate 90 -set g_balance_devastator_guideratedelay 0.01 -set g_balance_devastator_guidestop 0 -set g_balance_devastator_health 30 -set g_balance_devastator_lifetime 10 -set g_balance_devastator_radius 110 -set g_balance_devastator_refire 1.1 -set g_balance_devastator_reload_ammo 0 -set g_balance_devastator_reload_time 2 -set g_balance_devastator_remote_damage 70 -set g_balance_devastator_remote_edgedamage 35 -set g_balance_devastator_remote_force 300 -set g_balance_devastator_remote_jump_damage 70 -set g_balance_devastator_remote_jump_force 450 -set g_balance_devastator_remote_jump_radius 0 -set g_balance_devastator_remote_jump_velocity_z_add 0 -set g_balance_devastator_remote_jump_velocity_z_max 1500 -set g_balance_devastator_remote_jump_velocity_z_min 400 -set g_balance_devastator_remote_radius 110 -set g_balance_devastator_speed 1300 -set g_balance_devastator_speedaccel 1300 -set g_balance_devastator_speedstart 1000 -set g_balance_devastator_switchdelay_drop 0.2 -set g_balance_devastator_switchdelay_raise 0.2 -set g_balance_devastator_weaponreplace "" -set g_balance_devastator_weaponstart 0 -set g_balance_devastator_weaponstartoverride -1 -set g_balance_devastator_weaponthrowable 1 -// }}} -// {{{ #11: Port-O-Launch -set g_balance_porto_primary_animtime 0.3 -set g_balance_porto_primary_lifetime 5 -set g_balance_porto_primary_refire 1.5 -set g_balance_porto_primary_speed 1000 -set g_balance_porto_secondary 1 -set g_balance_porto_secondary_animtime 0.3 -set g_balance_porto_secondary_lifetime 5 -set g_balance_porto_secondary_refire 1.5 -set g_balance_porto_secondary_speed 1000 -set g_balance_porto_switchdelay_drop 0.2 -set g_balance_porto_switchdelay_raise 0.2 -set g_balance_porto_weaponreplace "" -set g_balance_porto_weaponstart 0 -set g_balance_porto_weaponstartoverride -1 -set g_balance_porto_weaponthrowable 1 -// }}} -// {{{ #12: Vaporizer -set g_balance_vaporizer_primary_ammo 10 -set g_balance_vaporizer_primary_animtime 0.3 -set g_balance_vaporizer_primary_damage 150 -set g_balance_vaporizer_primary_refire 1 -set g_balance_vaporizer_reload_ammo 0 -set g_balance_vaporizer_reload_time 0 -set g_balance_vaporizer_secondary_ammo 0 -set g_balance_vaporizer_secondary_animtime 0.2 -set g_balance_vaporizer_secondary_damage 25 -set g_balance_vaporizer_secondary_delay 0 -set g_balance_vaporizer_secondary_edgedamage 12.5 -set g_balance_vaporizer_secondary_force 300 -set g_balance_vaporizer_secondary_lifetime 5 -set g_balance_vaporizer_secondary_radius 70 -set g_balance_vaporizer_secondary_refire 0.7 -set g_balance_vaporizer_secondary_shotangle 0 -set g_balance_vaporizer_secondary_speed 6000 -set g_balance_vaporizer_secondary_spread 0 -set g_balance_vaporizer_switchdelay_drop 0.2 -set g_balance_vaporizer_switchdelay_raise 0.2 -set g_balance_vaporizer_weaponreplace "" -set g_balance_vaporizer_weaponstart 0 -set g_balance_vaporizer_weaponstartoverride -1 -set g_balance_vaporizer_weaponthrowable 1 -// }}} -// {{{ #13: Grappling Hook -set g_balance_hook_primary_ammo 5 -set g_balance_hook_primary_animtime 0.3 -set g_balance_hook_primary_hooked_ammo 5 -set g_balance_hook_primary_hooked_time_free 2 -set g_balance_hook_primary_hooked_time_max 0 -set g_balance_hook_primary_refire 0.2 -set g_balance_hook_secondary_animtime 0.3 -set g_balance_hook_secondary_damage 25 -set g_balance_hook_secondary_damageforcescale 0 -set g_balance_hook_secondary_duration 1.5 -set g_balance_hook_secondary_edgedamage 5 -set g_balance_hook_secondary_force -2000 -set g_balance_hook_secondary_gravity 5 -set g_balance_hook_secondary_health 15 -set g_balance_hook_secondary_lifetime 5 -set g_balance_hook_secondary_power 3 -set g_balance_hook_secondary_radius 500 -set g_balance_hook_secondary_refire 3 -set g_balance_hook_secondary_speed 0 -set g_balance_hook_switchdelay_drop 0.2 -set g_balance_hook_switchdelay_raise 0.2 -set g_balance_hook_weaponreplace "" -set g_balance_hook_weaponstart 0 -set g_balance_hook_weaponstartoverride -1 -set g_balance_hook_weaponthrowable 1 -// }}} -// {{{ #14: Heavy Laser Assault Cannon (MUTATOR WEAPON) -set g_balance_hlac_primary_ammo 1 -set g_balance_hlac_primary_animtime 0.4 -set g_balance_hlac_primary_damage 18 -set g_balance_hlac_primary_edgedamage 9 -set g_balance_hlac_primary_force 90 -set g_balance_hlac_primary_lifetime 5 -set g_balance_hlac_primary_radius 70 -set g_balance_hlac_primary_refire 0.15 -set g_balance_hlac_primary_speed 9000 -set g_balance_hlac_primary_spread_add 0.0045 -set g_balance_hlac_primary_spread_crouchmod 0.25 -set g_balance_hlac_primary_spread_max 0.25 -set g_balance_hlac_primary_spread_min 0.01 -set g_balance_hlac_reload_ammo 0 -set g_balance_hlac_reload_time 2 -set g_balance_hlac_secondary 1 -set g_balance_hlac_secondary_ammo 10 -set g_balance_hlac_secondary_animtime 0.3 -set g_balance_hlac_secondary_damage 15 -set g_balance_hlac_secondary_edgedamage 7.5 -set g_balance_hlac_secondary_force 90 -set g_balance_hlac_secondary_lifetime 5 -set g_balance_hlac_secondary_radius 70 -set g_balance_hlac_secondary_refire 1 -set g_balance_hlac_secondary_shots 6 -set g_balance_hlac_secondary_speed 9000 -set g_balance_hlac_secondary_spread 0.15 -set g_balance_hlac_secondary_spread_crouchmod 0.5 -set g_balance_hlac_switchdelay_drop 0.2 -set g_balance_hlac_switchdelay_raise 0.2 -set g_balance_hlac_weaponreplace "" -set g_balance_hlac_weaponstart 0 -set g_balance_hlac_weaponstartoverride -1 -set g_balance_hlac_weaponthrowable 1 -// }}} -// {{{ #15: @!#%'n Tuba -set g_balance_tuba_animtime 0.05 -set g_balance_tuba_attenuation 0.5 -set g_balance_tuba_damage 5 -set g_balance_tuba_edgedamage 0 -set g_balance_tuba_fadetime 0.25 -set g_balance_tuba_force 40 -set g_balance_tuba_pitchstep 6 -set g_balance_tuba_radius 200 -set g_balance_tuba_refire 0.05 -set g_balance_tuba_switchdelay_drop 0.2 -set g_balance_tuba_switchdelay_raise 0.2 -set g_balance_tuba_volume 1 -set g_balance_tuba_weaponreplace "" -set g_balance_tuba_weaponstart 0 -set g_balance_tuba_weaponstartoverride -1 -set g_balance_tuba_weaponthrowable 1 -// }}} -// {{{ #16: Rifle (MUTATOR WEAPON) -set g_balance_rifle_bursttime 0 -set g_balance_rifle_primary_ammo 10 -set g_balance_rifle_primary_animtime 0.4 -set g_balance_rifle_primary_bullethail 0 -set g_balance_rifle_primary_burstcost 0 -set g_balance_rifle_primary_damage 80 -set g_balance_rifle_primary_force 100 -set g_balance_rifle_primary_refire 1.2 -set g_balance_rifle_primary_shots 1 -set g_balance_rifle_primary_solidpenetration 62.2 -set g_balance_rifle_primary_spread 0 -set g_balance_rifle_primary_tracer 1 -set g_balance_rifle_reload_ammo 80 -set g_balance_rifle_reload_time 2 -set g_balance_rifle_secondary 1 -set g_balance_rifle_secondary_ammo 10 -set g_balance_rifle_secondary_animtime 0.3 -set g_balance_rifle_secondary_bullethail 0 -set g_balance_rifle_secondary_burstcost 0 -set g_balance_rifle_secondary_damage 20 -set g_balance_rifle_secondary_force 50 -set g_balance_rifle_secondary_refire 0.9 -set g_balance_rifle_secondary_reload 0 -set g_balance_rifle_secondary_shots 4 -set g_balance_rifle_secondary_solidpenetration 15.5 -set g_balance_rifle_secondary_spread 0.04 -set g_balance_rifle_secondary_tracer 0 -set g_balance_rifle_switchdelay_drop 0.2 -set g_balance_rifle_switchdelay_raise 0.2 -set g_balance_rifle_weaponreplace "" -set g_balance_rifle_weaponstart 0 -set g_balance_rifle_weaponstartoverride -1 -set g_balance_rifle_weaponthrowable 1 -// }}} -// {{{ #17: Fireball -set g_balance_fireball_primary_animtime 0.4 -set g_balance_fireball_primary_bfgdamage 100 -set g_balance_fireball_primary_bfgforce 0 -set g_balance_fireball_primary_bfgradius 1000 -set g_balance_fireball_primary_damage 200 -set g_balance_fireball_primary_damageforcescale 0 -set g_balance_fireball_primary_edgedamage 50 -set g_balance_fireball_primary_force 600 -set g_balance_fireball_primary_health 0 -set g_balance_fireball_primary_laserburntime 0.5 -set g_balance_fireball_primary_laserdamage 80 -set g_balance_fireball_primary_laseredgedamage 20 -set g_balance_fireball_primary_laserradius 256 -set g_balance_fireball_primary_lifetime 15 -set g_balance_fireball_primary_radius 200 -set g_balance_fireball_primary_refire 2 -set g_balance_fireball_primary_refire2 0 -set g_balance_fireball_primary_speed 1200 -set g_balance_fireball_primary_spread 0 -set g_balance_fireball_secondary_animtime 0.3 -set g_balance_fireball_secondary_damage 40 -set g_balance_fireball_secondary_damageforcescale 4 -set g_balance_fireball_secondary_damagetime 5 -set g_balance_fireball_secondary_laserburntime 0.5 -set g_balance_fireball_secondary_laserdamage 50 -set g_balance_fireball_secondary_laseredgedamage 20 -set g_balance_fireball_secondary_laserradius 110 -set g_balance_fireball_secondary_lifetime 7 -set g_balance_fireball_secondary_refire 1.5 -set g_balance_fireball_secondary_speed 900 -set g_balance_fireball_secondary_speed_up 100 -set g_balance_fireball_secondary_speed_z 0 -set g_balance_fireball_secondary_spread 0 -set g_balance_fireball_switchdelay_drop 0.2 -set g_balance_fireball_switchdelay_raise 0.2 -set g_balance_fireball_weaponreplace "" -set g_balance_fireball_weaponstart 0 -set g_balance_fireball_weaponstartoverride -1 -set g_balance_fireball_weaponthrowable 0 -// }}} -// {{{ #18: T.A.G. Seeker (MUTATOR WEAPON) -set g_balance_seeker_flac_ammo 1 -set g_balance_seeker_flac_animtime 0.1 -set g_balance_seeker_flac_damage 15 -set g_balance_seeker_flac_edgedamage 10 -set g_balance_seeker_flac_force 50 -set g_balance_seeker_flac_lifetime 0.1 -set g_balance_seeker_flac_lifetime_rand 0.05 -set g_balance_seeker_flac_radius 100 -set g_balance_seeker_flac_refire 0.1 -set g_balance_seeker_flac_speed 3000 -set g_balance_seeker_flac_speed_up 1000 -set g_balance_seeker_flac_speed_z 0 -set g_balance_seeker_flac_spread 0.4 -set g_balance_seeker_missile_accel 1400 -set g_balance_seeker_missile_ammo 2 -set g_balance_seeker_missile_animtime 0.2 -set g_balance_seeker_missile_count 3 -set g_balance_seeker_missile_damage 30 -set g_balance_seeker_missile_damageforcescale 4 -set g_balance_seeker_missile_decel 1400 -set g_balance_seeker_missile_delay 0.25 -set g_balance_seeker_missile_edgedamage 10 -set g_balance_seeker_missile_force 150 -set g_balance_seeker_missile_health 5 -set g_balance_seeker_missile_lifetime 15 -set g_balance_seeker_missile_proxy 0 -set g_balance_seeker_missile_proxy_delay 0.2 -set g_balance_seeker_missile_proxy_maxrange 45 -set g_balance_seeker_missile_radius 80 -set g_balance_seeker_missile_refire 0.5 -set g_balance_seeker_missile_smart 1 -set g_balance_seeker_missile_smart_mindist 800 -set g_balance_seeker_missile_smart_trace_max 2500 -set g_balance_seeker_missile_smart_trace_min 1000 -set g_balance_seeker_missile_speed 700 -set g_balance_seeker_missile_speed_max 1300 -set g_balance_seeker_missile_speed_up 300 -set g_balance_seeker_missile_speed_z 0 -set g_balance_seeker_missile_spread 0 -set g_balance_seeker_missile_turnrate 0.65 -set g_balance_seeker_reload_ammo 0 -set g_balance_seeker_reload_time 2 -set g_balance_seeker_switchdelay_drop 0.2 -set g_balance_seeker_switchdelay_raise 0.2 -set g_balance_seeker_tag_ammo 1 -set g_balance_seeker_tag_animtime 0.2 -set g_balance_seeker_tag_damageforcescale 4 -set g_balance_seeker_tag_health 5 -set g_balance_seeker_tag_lifetime 15 -set g_balance_seeker_tag_refire 0.75 -set g_balance_seeker_tag_speed 5000 -set g_balance_seeker_tag_spread 0 -set g_balance_seeker_tag_tracker_lifetime 10 -set g_balance_seeker_type 0 -set g_balance_seeker_weaponreplace "" -set g_balance_seeker_weaponstart 0 -set g_balance_seeker_weaponstartoverride -1 -set g_balance_seeker_weaponthrowable 1 -// }}} -// {{{ #19: Shockwave (MUTATOR WEAPON) -set g_balance_shockwave_blast_animtime 0.3 -set g_balance_shockwave_blast_damage 40 -set g_balance_shockwave_blast_distance 1000 -set g_balance_shockwave_blast_edgedamage 0 -set g_balance_shockwave_blast_force 15 -set g_balance_shockwave_blast_force_forwardbias 50 -set g_balance_shockwave_blast_force_zscale 1 -set g_balance_shockwave_blast_jump_damage 20 -set g_balance_shockwave_blast_jump_edgedamage 0 -set g_balance_shockwave_blast_jump_force 100 -set g_balance_shockwave_blast_jump_force_velocitybias 1 -set g_balance_shockwave_blast_jump_force_zscale 1 -set g_balance_shockwave_blast_jump_multiplier_accuracy 0.5 -set g_balance_shockwave_blast_jump_multiplier_distance 0.5 -set g_balance_shockwave_blast_jump_multiplier_min 0 -set g_balance_shockwave_blast_jump_radius 150 -set g_balance_shockwave_blast_multiplier_accuracy 0.45 -set g_balance_shockwave_blast_multiplier_distance 0.2 -set g_balance_shockwave_blast_multiplier_min 0 -set g_balance_shockwave_blast_refire 0.75 -set g_balance_shockwave_blast_splash_damage 15 -set g_balance_shockwave_blast_splash_edgedamage 0 -set g_balance_shockwave_blast_splash_force 100 -set g_balance_shockwave_blast_splash_force_forwardbias 50 -set g_balance_shockwave_blast_splash_multiplier_accuracy 0.5 -set g_balance_shockwave_blast_splash_multiplier_distance 0.5 -set g_balance_shockwave_blast_splash_multiplier_min 0 -set g_balance_shockwave_blast_splash_radius 70 -set g_balance_shockwave_blast_spread_max 120 -set g_balance_shockwave_blast_spread_min 25 -set g_balance_shockwave_melee_animtime 1.3 -set g_balance_shockwave_melee_damage 80 -set g_balance_shockwave_melee_delay 0.25 -set g_balance_shockwave_melee_force 200 -set g_balance_shockwave_melee_multihit 1 -set g_balance_shockwave_melee_no_doubleslap 1 -set g_balance_shockwave_melee_nonplayerdamage 40 -set g_balance_shockwave_melee_range 120 -set g_balance_shockwave_melee_refire 1.25 -set g_balance_shockwave_melee_swing_side 120 -set g_balance_shockwave_melee_swing_up 30 -set g_balance_shockwave_melee_time 0.15 -set g_balance_shockwave_melee_traces 10 -set g_balance_shockwave_switchdelay_drop 0.2 -set g_balance_shockwave_switchdelay_raise 0.2 -set g_balance_shockwave_weaponreplace "" -set g_balance_shockwave_weaponstart 0 -set g_balance_shockwave_weaponstartoverride -1 -set g_balance_shockwave_weaponthrowable 0 -// }}} -// {{{ #20: Arc -set g_balance_arc_beam_ammo 6 -set g_balance_arc_beam_animtime 0.1 -set g_balance_arc_beam_botaimlifetime 0 -set g_balance_arc_beam_botaimspeed 0 -set g_balance_arc_beam_damage 100 -set g_balance_arc_beam_degreespersegment 1 -set g_balance_arc_beam_distancepersegment 0 -set g_balance_arc_beam_falloff_halflifedist 0 -set g_balance_arc_beam_falloff_maxdist 0 -set g_balance_arc_beam_falloff_mindist 0 -set g_balance_arc_beam_force 600 -set g_balance_arc_beam_healing_amax 0 -set g_balance_arc_beam_healing_aps 50 -set g_balance_arc_beam_healing_hmax 150 -set g_balance_arc_beam_healing_hps 50 -set g_balance_arc_cooldown 2.5 -set g_balance_arc_cooldown_release 0 -set g_balance_arc_overheat_max 5 -set g_balance_arc_overheat_min 3 -set g_balance_arc_beam_heat 0 -set g_balance_arc_burst_heat 5 -set g_balance_arc_beam_maxangle 10 -set g_balance_arc_beam_nonplayerdamage 80 -set g_balance_arc_beam_range 1000 -set g_balance_arc_beam_refire 0.25 -set g_balance_arc_beam_returnspeed 8 -set g_balance_arc_beam_tightness 0.5 -set g_balance_arc_bolt 0 -set g_balance_arc_bolt_ammo 1 -set g_balance_arc_bolt_damage 25 -set g_balance_arc_bolt_damageforcescale 0 -set g_balance_arc_bolt_edgedamage 12.5 -set g_balance_arc_bolt_force 120 -set g_balance_arc_bolt_health 15 -set g_balance_arc_bolt_lifetime 5 -set g_balance_arc_bolt_radius 65 -set g_balance_arc_bolt_refire 0.16667 -set g_balance_arc_bolt_speed 2300 -set g_balance_arc_bolt_spread 0 -set g_balance_arc_burst_ammo 15 -set g_balance_arc_burst_damage 250 -set g_balance_arc_burst_healing_aps 100 -set g_balance_arc_burst_healing_hps 100 -set g_balance_arc_switchdelay_drop 0.2 -set g_balance_arc_switchdelay_raise 0.2 -set g_balance_arc_weaponreplace "" -set g_balance_arc_weaponstart 0 -set g_balance_arc_weaponstartoverride -1 -set g_balance_arc_weaponthrowable 1 -// }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 -// }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 -// }}} diff --git a/bal-wep-samual.cfg b/bal-wep-samual.cfg index b3ec457a8a..1816f3f2e9 100644 --- a/bal-wep-samual.cfg +++ b/bal-wep-samual.cfg @@ -322,7 +322,7 @@ set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 @@ -369,6 +369,7 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 6 set g_balance_vortex_primary_animtime 0.6 +set g_balance_vortex_primary_armorpierce 0 set g_balance_vortex_primary_damage 80 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 @@ -381,6 +382,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -502,6 +504,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage 150 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -775,44 +778,70 @@ set g_balance_shotgun_weaponstart 0 set g_balance_shotgun_weaponstartoverride -1 set g_balance_shotgun_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 // }}} diff --git a/bal-wep-xdf.cfg b/bal-wep-xdf.cfg index cc8936c338..13bdc529a9 100644 --- a/bal-wep-xdf.cfg +++ b/bal-wep-xdf.cfg @@ -256,7 +256,7 @@ set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0 set g_balance_crylink_secondary_bounces 0 @@ -303,6 +303,7 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 6 set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_armorpierce 0 set g_balance_vortex_primary_damage 80 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 @@ -315,6 +316,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -390,7 +392,7 @@ set g_balance_devastator_guiderate 0 set g_balance_devastator_guideratedelay 999 set g_balance_devastator_guidestop 1 set g_balance_devastator_health 30 -set g_balance_devastator_lifetime 100 +set g_balance_devastator_lifetime 20 set g_balance_devastator_radius 110 set g_balance_devastator_refire 0.9 set g_balance_devastator_reload_ammo 0 @@ -436,6 +438,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage 150 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -759,11 +762,11 @@ set g_balance_arc_bolt_ammo 1 set g_balance_arc_bolt_damage 25 set g_balance_arc_bolt_damageforcescale 0 set g_balance_arc_bolt_edgedamage 12.5 -set g_balance_arc_bolt_force 120 +set g_balance_arc_bolt_force 200 set g_balance_arc_bolt_health 15 set g_balance_arc_bolt_lifetime 5 set g_balance_arc_bolt_radius 65 -set g_balance_arc_bolt_refire 0.16667 +set g_balance_arc_bolt_refire 0.033333 set g_balance_arc_bolt_speed 2300 set g_balance_arc_bolt_spread 0 set g_balance_arc_burst_ammo 15 @@ -777,44 +780,70 @@ set g_balance_arc_weaponstart 0 set g_balance_arc_weaponstartoverride -1 set g_balance_arc_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 // }}} diff --git a/bal-wep-xonotic.cfg b/bal-wep-xonotic.cfg index ac5be34f37..5c6ace7abd 100644 --- a/bal-wep-xonotic.cfg +++ b/bal-wep-xonotic.cfg @@ -256,7 +256,7 @@ set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 @@ -303,6 +303,7 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 6 set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_armorpierce 0 set g_balance_vortex_primary_damage 80 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 @@ -315,6 +316,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -436,6 +438,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage 150 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -750,11 +753,11 @@ set g_balance_arc_beam_heat 0 set g_balance_arc_burst_heat 5 set g_balance_arc_beam_maxangle 10 set g_balance_arc_beam_nonplayerdamage 80 -set g_balance_arc_beam_range 1000 +set g_balance_arc_beam_range 1500 set g_balance_arc_beam_refire 0.25 set g_balance_arc_beam_returnspeed 8 -set g_balance_arc_beam_tightness 0.5 -set g_balance_arc_bolt 0 +set g_balance_arc_beam_tightness 0.6 +set g_balance_arc_bolt 1 set g_balance_arc_bolt_ammo 1 set g_balance_arc_bolt_damage 25 set g_balance_arc_bolt_damageforcescale 0 @@ -777,44 +780,183 @@ set g_balance_arc_weaponstart 0 set g_balance_arc_weaponstartoverride -1 set g_balance_arc_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 +// }}} +// {{{ Overkill Shotgun +set g_balance_okshotgun_primary_ammo 3 +set g_balance_okshotgun_primary_animtime 0.65 +set g_balance_okshotgun_primary_bot_range 512 +set g_balance_okshotgun_primary_bullets 10 +set g_balance_okshotgun_primary_damage 17 +set g_balance_okshotgun_primary_force 80 +set g_balance_okshotgun_primary_refire 0.75 +set g_balance_okshotgun_primary_solidpenetration 3.8 +set g_balance_okshotgun_primary_spread 0.07 +set g_balance_okshotgun_reload_ammo 24 +set g_balance_okshotgun_reload_time 2 +set g_balance_okshotgun_secondary_animtime 0.2 +set g_balance_okshotgun_secondary_damage 25 +set g_balance_okshotgun_secondary_delay 0 +set g_balance_okshotgun_secondary_edgedamage 12.5 +set g_balance_okshotgun_secondary_force 300 +set g_balance_okshotgun_secondary_lifetime 5 +set g_balance_okshotgun_secondary_radius 70 +set g_balance_okshotgun_secondary_refire 0.7 +set g_balance_okshotgun_secondary_refire_type 1 +set g_balance_okshotgun_secondary_shotangle 0 +set g_balance_okshotgun_secondary_speed 6000 +set g_balance_okshotgun_secondary_spread 0 +set g_balance_okshotgun_switchdelay_drop 0.2 +set g_balance_okshotgun_switchdelay_raise 0.2 +set g_balance_okshotgun_weaponreplace "" +set g_balance_okshotgun_weaponstart 0 +set g_balance_okshotgun_weaponstartoverride -1 +set g_balance_okshotgun_weaponthrowable 1 +// }}} +// {{{ Overkill Machine Gun +set g_balance_okmachinegun_primary_ammo 1 +set g_balance_okmachinegun_primary_damage 25 +set g_balance_okmachinegun_primary_force 5 +set g_balance_okmachinegun_primary_refire 0.1 +set g_balance_okmachinegun_primary_solidpenetration 63 +set g_balance_okmachinegun_primary_spread_add 0.012 +set g_balance_okmachinegun_primary_spread_max 0.05 +set g_balance_okmachinegun_primary_spread_min 0 +set g_balance_okmachinegun_reload_ammo 30 +set g_balance_okmachinegun_reload_time 1.5 +set g_balance_okmachinegun_secondary_animtime 0.2 +set g_balance_okmachinegun_secondary_damage 25 +set g_balance_okmachinegun_secondary_delay 0 +set g_balance_okmachinegun_secondary_edgedamage 12.5 +set g_balance_okmachinegun_secondary_force 300 +set g_balance_okmachinegun_secondary_lifetime 5 +set g_balance_okmachinegun_secondary_radius 70 +set g_balance_okmachinegun_secondary_refire 0.7 +set g_balance_okmachinegun_secondary_refire_type 1 +set g_balance_okmachinegun_secondary_shotangle 0 +set g_balance_okmachinegun_secondary_speed 6000 +set g_balance_okmachinegun_secondary_spread 0 +set g_balance_okmachinegun_switchdelay_drop 0.2 +set g_balance_okmachinegun_switchdelay_raise 0.2 +set g_balance_okmachinegun_weaponreplace "" +set g_balance_okmachinegun_weaponstart 0 +set g_balance_okmachinegun_weaponstartoverride -1 +set g_balance_okmachinegun_weaponthrowable 1 +// }}} +// {{{ Overkill Nex +set g_balance_oknex_charge 0 +set g_balance_oknex_charge_animlimit 0.5 +set g_balance_oknex_charge_limit 1 +set g_balance_oknex_charge_maxspeed 800 +set g_balance_oknex_charge_mindmg 40 +set g_balance_oknex_charge_minspeed 400 +set g_balance_oknex_charge_rate 0.6 +set g_balance_oknex_charge_rot_pause 0 +set g_balance_oknex_charge_rot_rate 0 +set g_balance_oknex_charge_shot_multiplier 0 +set g_balance_oknex_charge_start 0.5 +set g_balance_oknex_charge_velocity_rate 0 +set g_balance_oknex_primary_ammo 10 +set g_balance_oknex_primary_animtime 0.65 +set g_balance_oknex_primary_damage 100 +set g_balance_oknex_primary_damagefalloff_forcehalflife 0 +set g_balance_oknex_primary_damagefalloff_halflife 0 +set g_balance_oknex_primary_damagefalloff_maxdist 0 +set g_balance_oknex_primary_damagefalloff_mindist 0 +set g_balance_oknex_primary_force 500 +set g_balance_oknex_primary_refire 1 +set g_balance_oknex_reload_ammo 50 +set g_balance_oknex_reload_time 2 +set g_balance_oknex_secondary 2 +set g_balance_oknex_secondary_ammo 0 +set g_balance_oknex_secondary_animtime 0.2 +set g_balance_oknex_secondary_chargepool 0 +set g_balance_oknex_secondary_chargepool_pause_regen 1 +set g_balance_oknex_secondary_chargepool_regen 0.15 +set g_balance_oknex_secondary_damage 25 +set g_balance_oknex_secondary_damagefalloff_forcehalflife 0 +set g_balance_oknex_secondary_damagefalloff_halflife 0 +set g_balance_oknex_secondary_damagefalloff_maxdist 0 +set g_balance_oknex_secondary_damagefalloff_mindist 0 +set g_balance_oknex_secondary_force 300 +set g_balance_oknex_secondary_refire 0.7 +set g_balance_oknex_secondary_refire_type 1 +set g_balance_oknex_secondary_delay 0 +set g_balance_oknex_secondary_edgedamage 12.5 +set g_balance_oknex_secondary_lifetime 5 +set g_balance_oknex_secondary_radius 70 +set g_balance_oknex_secondary_shotangle 0 +set g_balance_oknex_secondary_speed 6000 +set g_balance_oknex_secondary_spread 0 +set g_balance_oknex_switchdelay_drop 0.2 +set g_balance_oknex_switchdelay_raise 0.2 +set g_balance_oknex_weaponreplace "" +set g_balance_oknex_weaponstart 0 +set g_balance_oknex_weaponstartoverride -1 +set g_balance_oknex_weaponthrowable 1 // }}} diff --git a/bal-wep-xpm.cfg b/bal-wep-xpm.cfg index ac5be34f37..a5438d1b7c 100644 --- a/bal-wep-xpm.cfg +++ b/bal-wep-xpm.cfg @@ -256,7 +256,7 @@ set g_balance_crylink_primary_spread 0.08 set g_balance_crylink_reload_ammo 0 set g_balance_crylink_reload_time 2 set g_balance_crylink_secondary 1 -set g_balance_crylink_secondary_ammo 2 +set g_balance_crylink_secondary_ammo 3 set g_balance_crylink_secondary_animtime 0.2 set g_balance_crylink_secondary_bouncedamagefactor 0.5 set g_balance_crylink_secondary_bounces 0 @@ -303,6 +303,7 @@ set g_balance_vortex_charge_start 0.5 set g_balance_vortex_charge_velocity_rate 0 set g_balance_vortex_primary_ammo 6 set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_armorpierce 0 set g_balance_vortex_primary_damage 80 set g_balance_vortex_primary_damagefalloff_forcehalflife 0 set g_balance_vortex_primary_damagefalloff_halflife 0 @@ -315,6 +316,7 @@ set g_balance_vortex_reload_time 2 set g_balance_vortex_secondary 0 set g_balance_vortex_secondary_ammo 2 set g_balance_vortex_secondary_animtime 0 +set g_balance_vortex_secondary_armorpierce 0 set g_balance_vortex_secondary_chargepool 0 set g_balance_vortex_secondary_chargepool_pause_regen 1 set g_balance_vortex_secondary_chargepool_regen 0.15 @@ -436,6 +438,7 @@ set g_balance_porto_weaponthrowable 1 set g_balance_vaporizer_primary_ammo 10 set g_balance_vaporizer_primary_animtime 0.3 set g_balance_vaporizer_primary_damage 150 +set g_balance_vaporizer_primary_force 800 set g_balance_vaporizer_primary_refire 1 set g_balance_vaporizer_reload_ammo 0 set g_balance_vaporizer_reload_time 0 @@ -777,44 +780,70 @@ set g_balance_arc_weaponstart 0 set g_balance_arc_weaponstartoverride -1 set g_balance_arc_weaponthrowable 1 // }}} -// {{{ #21: Heavy Machine Gun -set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 30 -set g_balance_hmg_force 10 -set g_balance_hmg_refire 0.05 -set g_balance_hmg_reload_ammo 120 -set g_balance_hmg_reload_time 1 -set g_balance_hmg_solidpenetration 32 -set g_balance_hmg_spread_add 0.005 -set g_balance_hmg_spread_max 0.06 -set g_balance_hmg_spread_min 0.01 -set g_balance_hmg_switchdelay_drop 0.2 -set g_balance_hmg_switchdelay_raise 0.2 -set g_balance_hmg_weaponreplace "" -set g_balance_hmg_weaponstart 0 -set g_balance_hmg_weaponstartoverride 0 -set g_balance_hmg_weaponthrowable 0 +// {{{ #21: Overkill Heavy Machine Gun +set g_balance_okhmg_primary_ammo 1 +set g_balance_okhmg_primary_damage 30 +set g_balance_okhmg_primary_force 10 +set g_balance_okhmg_primary_refire 0.05 +set g_balance_okhmg_primary_solidpenetration 32 +set g_balance_okhmg_primary_spread_add 0.005 +set g_balance_okhmg_primary_spread_max 0.06 +set g_balance_okhmg_primary_spread_min 0.01 +set g_balance_okhmg_reload_ammo 120 +set g_balance_okhmg_reload_time 1 +set g_balance_okhmg_secondary_ammo 0 +set g_balance_okhmg_secondary_animtime 0.2 +set g_balance_okhmg_secondary_damage 25 +set g_balance_okhmg_secondary_delay 0 +set g_balance_okhmg_secondary_edgedamage 12.5 +set g_balance_okhmg_secondary_force 300 +set g_balance_okhmg_secondary_lifetime 5 +set g_balance_okhmg_secondary_radius 70 +set g_balance_okhmg_secondary_refire 0.7 +set g_balance_okhmg_secondary_refire_type 1 +set g_balance_okhmg_secondary_shotangle 0 +set g_balance_okhmg_secondary_speed 6000 +set g_balance_okhmg_secondary_spread 0 +set g_balance_okhmg_switchdelay_drop 0.2 +set g_balance_okhmg_switchdelay_raise 0.2 +set g_balance_okhmg_weaponreplace "" +set g_balance_okhmg_weaponstart 0 +set g_balance_okhmg_weaponstartoverride 0 +set g_balance_okhmg_weaponthrowable 0 // }}} -// {{{ #22: Rocket Propelled Chainsaw -set g_balance_rpc_ammo 10 -set g_balance_rpc_animtime 1 -set g_balance_rpc_damage 150 -set g_balance_rpc_damage2 500 -set g_balance_rpc_damageforcescale 2 -set g_balance_rpc_edgedamage 50 -set g_balance_rpc_force 400 -set g_balance_rpc_health 25 -set g_balance_rpc_lifetime 30 -set g_balance_rpc_radius 300 -set g_balance_rpc_refire 1 -set g_balance_rpc_reload_ammo 10 -set g_balance_rpc_reload_time 1 -set g_balance_rpc_speed 2500 -set g_balance_rpc_speedaccel 5000 -set g_balance_rpc_switchdelay_drop 0.2 -set g_balance_rpc_switchdelay_raise 0.2 -set g_balance_rpc_weaponreplace "" -set g_balance_rpc_weaponstart 0 -set g_balance_rpc_weaponstartoverride 0 -set g_balance_rpc_weaponthrowable 0 +// {{{ #22: Overkill Rocket Propelled Chainsaw +set g_balance_okrpc_primary_ammo 10 +set g_balance_okrpc_primary_animtime 1 +set g_balance_okrpc_primary_damage 150 +set g_balance_okrpc_primary_damage2 500 +set g_balance_okrpc_primary_damageforcescale 2 +set g_balance_okrpc_primary_edgedamage 50 +set g_balance_okrpc_primary_force 400 +set g_balance_okrpc_primary_health 25 +set g_balance_okrpc_primary_lifetime 30 +set g_balance_okrpc_primary_radius 300 +set g_balance_okrpc_primary_refire 1 +set g_balance_okrpc_primary_speed 2500 +set g_balance_okrpc_primary_speedaccel 5000 +set g_balance_okrpc_reload_ammo 10 +set g_balance_okrpc_reload_time 1 +set g_balance_okrpc_secondary_ammo 0 +set g_balance_okrpc_secondary_animtime 0.2 +set g_balance_okrpc_secondary_damage 25 +set g_balance_okrpc_secondary_delay 0 +set g_balance_okrpc_secondary_edgedamage 12.5 +set g_balance_okrpc_secondary_force 300 +set g_balance_okrpc_secondary_lifetime 5 +set g_balance_okrpc_secondary_radius 70 +set g_balance_okrpc_secondary_refire 0.7 +set g_balance_okrpc_secondary_refire_type 1 +set g_balance_okrpc_secondary_shotangle 0 +set g_balance_okrpc_secondary_speed 6000 +set g_balance_okrpc_secondary_spread 0 +set g_balance_okrpc_switchdelay_drop 0.2 +set g_balance_okrpc_switchdelay_raise 0.2 +set g_balance_okrpc_weaponreplace "" +set g_balance_okrpc_weaponstart 0 +set g_balance_okrpc_weaponstartoverride 0 +set g_balance_okrpc_weaponthrowable 0 // }}} diff --git a/balance-mario.cfg b/balance-mario.cfg index 4dda8bdc88..1974ad6c81 100644 --- a/balance-mario.cfg +++ b/balance-mario.cfg @@ -101,7 +101,7 @@ set g_pickup_respawntime_powerup 120 set g_pickup_respawntime_weapon 10 set g_pickup_respawntime_superweapon 120 set g_pickup_respawntime_ammo 10 -set g_pickup_respawntime_initial_random 2 +set g_pickup_respawntime_initial_random 1 set g_pickup_respawntimejitter_short 0 set g_pickup_respawntimejitter_medium 0 set g_pickup_respawntimejitter_long 0 @@ -132,7 +132,7 @@ set g_balance_pause_armor_rot_spawn 5 set g_balance_armor_regenstable 100 set g_balance_armor_rotstable 100 set g_balance_armor_limit 200 -set g_balance_armor_blockpercent 0.7 +set g_balance_armor_blockpercent 0.55 set g_balance_fuel_regen 0.1 "fuel regeneration (only applies if the player owns IT_FUEL_REGEN)" set g_balance_fuel_regenlinear 0 set g_balance_pause_fuel_regen 2 // other than this, fuel uses the health regen counter @@ -199,7 +199,7 @@ set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone i // {{{ powerups set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken -set g_balance_powerup_invincible_takeforce 1 +set g_balance_powerup_invincible_takeforce 0.33 set g_balance_powerup_invincible_time 30 set g_balance_powerup_strength_damage 3 set g_balance_powerup_strength_force 3 diff --git a/balance-overkill.cfg b/balance-overkill.cfg index bf2ecc623c..18d862d747 100644 --- a/balance-overkill.cfg +++ b/balance-overkill.cfg @@ -239,5 +239,3 @@ set g_balance_grapplehook_pull_frozen 0 set g_balance_portal_health 200 // these get recharged whenever the portal is used set g_balance_portal_lifetime 15 // these get recharged whenever the portal is used // }}} - -exec bal-wep-overkill.cfg diff --git a/balance-xdf.cfg b/balance-xdf.cfg index 245fa7cc4c..6a1622c2c4 100644 --- a/balance-xdf.cfg +++ b/balance-xdf.cfg @@ -49,8 +49,8 @@ set g_balance_nix_ammoincr_fuel 2 // }}} // {{{ pickup items -set g_pickup_ammo_anyway 1 -set g_pickup_weapons_anyway 1 +set g_pickup_ammo_anyway 0 +set g_pickup_weapons_anyway 0 set g_pickup_shells 15 set g_pickup_shells_weapon 15 set g_pickup_shells_max 60 @@ -72,28 +72,28 @@ set g_pickup_fuel_jetpack 100 set g_pickup_fuel_max 100 set g_pickup_armorsmall 5 set g_pickup_armorsmall_max 200 -set g_pickup_armorsmall_anyway 1 +set g_pickup_armorsmall_anyway 0 set g_pickup_armormedium 25 set g_pickup_armormedium_max 200 -set g_pickup_armormedium_anyway 1 +set g_pickup_armormedium_anyway 0 set g_pickup_armorbig 50 set g_pickup_armorbig_max 200 -set g_pickup_armorbig_anyway 1 +set g_pickup_armorbig_anyway 0 set g_pickup_armormega 100 set g_pickup_armormega_max 200 -set g_pickup_armormega_anyway 1 +set g_pickup_armormega_anyway 0 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 -set g_pickup_healthsmall_anyway 1 +set g_pickup_healthsmall_anyway 0 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 200 -set g_pickup_healthmedium_anyway 1 +set g_pickup_healthmedium_anyway 0 set g_pickup_healthbig 50 set g_pickup_healthbig_max 200 -set g_pickup_healthbig_anyway 1 +set g_pickup_healthbig_anyway 0 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 -set g_pickup_healthmega_anyway 1 +set g_pickup_healthmega_anyway 0 set g_pickup_respawntime_short 0.1 set g_pickup_respawntime_medium 0.1 set g_pickup_respawntime_long 0.1 diff --git a/binds-xonotic.cfg b/binds-xonotic.cfg index e8f4f1a589..611f5da95a 100644 --- a/binds-xonotic.cfg +++ b/binds-xonotic.cfg @@ -4,7 +4,7 @@ bind f5 menu_showteamselect bind f6 team_auto bind f7 menu_showsandboxtools - +bind f8 "quickmenu" bind f9 "cl_cmd hud minigame" // movement @@ -40,6 +40,7 @@ bind MWHEELUP weapnext bind MWHEELDOWN weapprev bind r reload bind BACKSPACE dropweapon +bind k kill bind g dropweapon bind f +use bind v +button8 // drag object diff --git a/check-translations.sh b/check-translations.sh index 5e2ad5a54a..6a55b5ef73 100755 --- a/check-translations.sh +++ b/check-translations.sh @@ -101,11 +101,11 @@ if [ x"$mode" = x"txt" ]; then if [ "$p" -lt 50 ]; then continue fi - item="$l $l \"$l\" 0%" + item="$l \"$l\" \"$l\" 0%" fi printf "%s\n" "$item" | sed -e "s/[0-9][0-9]*%/$p%/" done - } | tr '"' '\t' | sort -k3 | tr '\t' '"' + } | LC_ALL=C sort -t '"' -k4,4 fi if [ x"$mode" = x"po" ]; then diff --git a/cmake/qcc.sh b/cmake/qcc.sh index b7c77fd563..a6038dd591 100755 --- a/cmake/qcc.sh +++ b/cmake/qcc.sh @@ -3,7 +3,16 @@ CPP=${CPP:-cpp} QCC=${QCC:-$PWD/../../gmqcc/gmqcc${CMAKE_EXECUTABLE_SUFFIX}} case $1 in compile) - ${CPP} ${@:3} | sed 's/^#\(line\)\? \([[:digit:]]\+\) "\(.*\)".*/\n#pragma file(\3)\n#pragma line(\2)/g' > $2 + for var in "$@"; do case "$var" in + -I*) + home=${var:2} + break + ;; + esac; done + ${CPP} ${@:3} \ + | sed -E "s|${home}|~|g" \ + | sed -E 's/^#(line)? ([[:digit:]]+) "(.*)".*/'$'\\\n''#pragma file(\3)'$'\\\n''#pragma line(\2)/g' \ + > $2 ;; link) ${QCC} \ diff --git a/commands.cfg b/commands.cfg index 79366e4b70..0f2e568925 100644 --- a/commands.cfg +++ b/commands.cfg @@ -40,6 +40,7 @@ if_client "alias" help "cl_cmd help; cmd help" // networked/server common commands alias cvar_changes "qc_cmd_svcmd cvar_changes ${* ?}" // Prints a list of all changed server cvars alias cvar_purechanges "qc_cmd_svcmd cvar_purechanges ${* ?}" // Prints a list of all changed gameplay cvars +alias editmob "qc_cmd_svcmd editmob ${* ?}" // Modifies a monster or all monsters alias info "qc_cmd_svcmd info ${* ?}" // Request for unique server information set up by admin alias ladder "qc_cmd_svcmd ladder ${* ?}" // Get information about top players if supported alias lsmaps "qc_cmd_svcmd lsmaps ${* ?}" // List maps which can be used with the current game mode @@ -55,16 +56,26 @@ alias who "qc_cmd_svcmd who ${* ?}" // Displa // generic commands (across all programs) alias addtolist "qc_cmd_svmenu addtolist ${* ?}" // Add a string to a cvar +alias bufstr_get "qc_cmd_svmenu bufstr_get ${* ?}" // Examine a string buffer object +alias cvar_localchanges "qc_cmd_svmenu cvar_localchanges ${* ?}" // Print locally changed cvars alias dumpcommands "qc_cmd_svmenu dumpcommands ${* ?}" // Dump all commands on the program to *_cmd_dump.txt -alias dumpnotifs "qc_cmd_svcl dumpnotifs ${* ?}" // Dump all notifications into notifications_dump.txt +alias dumpnotifs "qc_cmd_svmenu dumpnotifs ${* ?}" // Dump all notifications into notifications_dump.txt +alias dumpitems "qc_cmd_svmenu dumpitems ${* ?}" // Dump all items to the console +alias dumpturrets "qc_cmd_svmenu dumpturrets ${* ?}" // Dump all turrets into turrets_dump.txt +alias dumpweapons "qc_cmd_svmenu dumpweapons ${* ?}" // Dump all weapons into weapons_dump.txt +alias find "qc_cmd_svmenu find ${* ?}" // Search through entities for matching classname +alias findat "qc_cmd_svmenu findat ${* ?}" // Search through entities for matching origin alias maplist "qc_cmd_svmenu maplist ${* ?}" // Automatic control of maplist +alias mx "qc_cmd_svmenu mx ${* ?}" // Send a matrix command alias nextframe "qc_cmd_svmenu nextframe ${* ?}" // Execute the given command next frame of this VM alias qc_curl "qc_cmd_svmenu qc_curl ${* ?}" // Queries a URL alias removefromlist "qc_cmd_svmenu removefromlist ${* ?}" // Remove a string from a cvar -alias restartnotifs "qc_cmd_svcl restartnotifs ${* ?}" // Re-initialize all notifications +alias restartnotifs "qc_cmd_svmenu restartnotifs ${* ?}" // Re-initialize all notifications alias rpn "qc_cmd_svmenu rpn ${* ?}" // RPN calculator +alias runtest "qc_cmd_svmenu runtest ${* ?}" // Run unit tests //alias settemp "qc_cmd_svmenu settemp ${* ?}" // Temporarily set a value to a cvar which is restored later //alias settemp_restore "qc_cmd_svmenu settemp_restore ${* ?}" // Restore all cvars set by settemp command +alias version "qc_cmd_svmenu version ${* ?}" // Print the current version // other aliases for common commands alias g_hitplots_add "qc_cmd_svmenu rpn /g_hitplots_individuals g_hitplots_individuals ${1 !} union def" @@ -116,6 +127,7 @@ alias menu_loadmap_prepare "disconnect; wait; g_campaign 0; menu_cmd rpn /_menu_ // ========================================================== // commented out commands are really only intended for internal use alias blurtest "qc_cmd_cl blurtest ${* ?}" // Feature for testing blur postprocessing +alias boxparticles "qc_cmd_cl boxparticles ${* ?}" // Spawn particles manually alias create_scrshot_ent "qc_cmd_cl create_scrshot_ent ${* ?}" // Create an entity at this location for automatic screenshots alias debugmodel "qc_cmd_cl debugmodel ${* ?}" // Spawn a debug model manually //alias handlevote "qc_cmd_cl handlevote ${* ?}" // System to handle selecting a vote or option @@ -123,6 +135,8 @@ alias hud "qc_cmd_cl hud ${* ?}" // Comman alias localprint "qc_cmd_cl localprint ${* ?}" // Create your own centerprint sent to yourself //alias mv_download "qc_cmd_cl mv_download ${* ?}" // Retrieve mapshot picture from the server alias sendcvar "qc_cmd_cl sendcvar ${* ?}" // Send a cvar to the server (like weaponpriority) +alias weapon_find "qc_cmd_cl weapon_find ${* ?}" // Show spawn locations of a weapon + alias exit "quit" // other aliases for local commands @@ -147,21 +161,21 @@ seta cl_autoswitch 1 "automatically switch to newly picked up weapons if they ar // commented out commands are really only intended for internal use, or already have declaration in the engine alias autoswitch "qc_cmd_cmd autoswitch ${* ?}" // Whether or not to switch automatically when getting a better weapon alias clientversion "qc_cmd_cmd clientversion ${* ?}" // Release version of the game -//alias mv_getpicture "qc_cmd_cmd mv_getpicture ${* ?}" // Retrieve mapshot picture from the server alias join "qc_cmd_cmd join ${* ?}" // Become a player in the game +alias minigame "qc_cmd_cmd minigame ${* ?}" // Start a minigame +//alias mv_getpicture "qc_cmd_cmd mv_getpicture ${* ?}" // Retrieve mapshot picture from the server +alias physics "qc_cmd_cmd physics ${* ?}" // Change physics set alias ready "qc_cmd_cmd ready ${* ?}" // Qualify as ready to end warmup stage (or restart server if allowed) -alias reportcvar "qc_cmd_cmd reportcvar ${* ?}" // Old system for sending a client cvar to the server //alias say "qc_cmd_cmd say ${* ?}" // Print a message to chat to all players //alias say_team "qc_cmd_cmd say_team ${* ?}" // Print a message to chat to all team mates alias selectteam "qc_cmd_cmd selectteam ${* ?}" // Attempt to choose a team to join into alias selfstuff "qc_cmd_cmd selfstuff ${* ?}" // Stuffcmd a command to your own client alias sentcvar "qc_cmd_cmd sentcvar ${* ?}" // New system for sending a client cvar to the server -alias editmob "qc_cmd_cmd editmob ${* ?}" // Edit a monster's properties -alias physics "qc_cmd_cmd physics ${* ?}" // Change physics set alias spectate "qc_cmd_cmd spectate ${* ?}" // Become an observer alias suggestmap "qc_cmd_cmd suggestmap ${* ?}" // Suggest a map to the mapvote at match end //alias tell "qc_cmd_cmd tell ${* ?}" // Send a message directly to a player alias voice "qc_cmd_cmd voice ${* ?}" // Send voice message via sound +alias wpeditor "qc_cmd_cmd wpeditor ${* ?}" // Waypoint editor commands // other aliases for client-to-server commands alias autoswitch "set cl_autoswitch ${1 ?} ; cmd autoswitch ${1 ?}" // todo @@ -194,6 +208,7 @@ alias mobbutcher "editmob butcher ${* ?}" alias adminmsg "qc_cmd_sv adminmsg ${* ?}" // Send an admin message to a client directly alias allready "qc_cmd_sv allready ${* ?}" // Restart the server and reset the players alias allspec "qc_cmd_sv allspec ${* ?}" // Force all players to spectate +alias animbench "qc_cmd_sv animbench ${* ?}" // Benchmark model animation (LAGS) alias anticheat "qc_cmd_sv anticheat ${* ?}" // Create an anticheat report for a client alias bbox "qc_cmd_sv bbox ${* ?}" // Print detailed information about world size alias bot_cmd "qc_cmd_sv bot_cmd ${* ?}" // Control and send commands to bots @@ -204,7 +219,6 @@ alias defer_clear_all "qc_cmd_sv defer_clear_all ${* ?}" // Clear alias delrec "qc_cmd_sv delrec ${* ?}" // Delete race time record for a map alias effectindexdump "qc_cmd_sv effectindexdump ${* ?}" // Dump list of effects from code and effectinfo.txt alias extendmatchtime "qc_cmd_sv extendmatchtime ${* ?}" // Increase the timelimit value incrementally -alias find "qc_cmd_sv find ${* ?}" // Search through entities for matching classname alias gametype "qc_cmd_sv gametype ${* ?}" // Simple command to change the active gametype alias gettaginfo "qc_cmd_sv gettaginfo ${* ?}" // Get specific information about a weapon model alias gotomap "qc_cmd_sv gotomap ${* ?}" // Simple command to switch to another map @@ -212,7 +226,6 @@ alias lockteams "qc_cmd_sv lockteams ${* ?}" // Disabl alias make_mapinfo "qc_cmd_sv make_mapinfo ${* ?}" // Automatically rebuild mapinfo files alias moveplayer "qc_cmd_sv moveplayer ${* ?}" // Change the team/status of a player alias nospectators "qc_cmd_sv nospectators ${* ?}" // Automatically remove spectators from a match -alias playerdemo "qc_cmd_sv playerdemo ${* ?}" // Control the ability to save demos of players alias printstats "qc_cmd_sv printstats ${* ?}" // Dump eventlog player stats and other score information alias radarmap "qc_cmd_sv radarmap ${* ?}" // Generate a radar image of the map alias reducematchtime "qc_cmd_sv reducematchtime ${* ?}" // Decrease the timelimit value incrementally @@ -322,7 +335,7 @@ set sv_vote_master_password "" "when set, users can use \"vlogin PASSWORD\" to l set sv_vote_master_playerlimit 2 "Minimum number of players needed for a player to be allowed to vote for master" set sv_vote_no_stops_vote 1 "Allow the vote caller to stop his own vote simply by voting no" set sv_vote_singlecount 0 "set to 1 to count votes once after timeout or to 0 to count with every vote" -set sv_vote_timeout 30 "a vote will timeout after this many seconds" +set sv_vote_timeout 24 "a vote will timeout after this many seconds" set sv_vote_wait 120 "a player can not call a vote again for this many seconds when his vote was not accepted" set sv_vote_stop 15 "a player can not call a vote again for this many seconds when he stopped this vote (e.g. to correct it)" set sv_vote_majority_factor 0.5 "What percentage of the PLAYERS constitute a majority? (Must be at least 0.5, recommended: 0.5)" diff --git a/common.cs.po b/common.cs.po index 5413fe1414..149ddc5c83 100644 --- a/common.cs.po +++ b/common.cs.po @@ -6,22 +6,23 @@ # Martin Taibr , 2017 # Martin Taibr , 2017 # NONE , 2015 -# Tomáš Volavka , 2015 +# Tomáš Volavka , 2015,2018 # Tomáš Volavka , 2015 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-19 23:01+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-05-13 08:23+0000\n" +"Last-Translator: Tomáš Volavka \n" "Language-Team: Czech (http://www.transifex.com/team-xonotic/xonotic/language/" "cs/)\n" "Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n " +"<= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" #: qcsrc/client/hud/hud_config.qc:239 #, c-format @@ -31,7 +32,7 @@ msgstr "^2Úspěšně exportováno do %s! (Pozn.: Uloženo v data/data/)\n" #: qcsrc/client/hud/hud_config.qc:243 #, c-format msgid "^1Couldn't write to %s\n" -msgstr "" +msgstr "^1Nelze zapisovat do %s\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." @@ -59,7 +60,7 @@ msgstr "^1Stiskni ^3%s^1 pro sledování" #: qcsrc/client/hud/panel/infomessages.qc:100 #: qcsrc/menu/xonotic/keybinder.qc:40 msgid "primary fire" -msgstr "" +msgstr "primární střelba" #: qcsrc/client/hud/panel/infomessages.qc:102 #, c-format @@ -69,12 +70,12 @@ msgstr "^1Stiskni ^3%s^1 nebo ^3%s^1 pro dalšího nebo předchozího hráče" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "next weapon" -msgstr "" +msgstr "další zbraň" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "previous weapon" -msgstr "" +msgstr "předchozí zbraň" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format @@ -84,17 +85,17 @@ msgstr "^1Použij ^3%s^1 nebo ^3%s^1 pro změnu rychlosti" #: qcsrc/client/hud/panel/infomessages.qc:108 #, c-format msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" -msgstr "" +msgstr "^1Stskni ^3%s^1 pro sledování, ^3%s^1 pro změnu kamery" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/common/vehicles/cl_vehicles.qc:192 msgid "drop weapon" -msgstr "" +msgstr "odhodit zbraň" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/menu/xonotic/keybinder.qc:41 msgid "secondary fire" -msgstr "" +msgstr "sekundární střelba" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format @@ -123,7 +124,7 @@ msgstr "^1Stiskni ^3%s^1 pro připojení" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 msgid "jump" -msgstr "" +msgstr "skok" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format diff --git a/common.de.po b/common.de.po index f248daf3c8..2423e37825 100644 --- a/common.de.po +++ b/common.de.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Wuzzy , 2016-2017 +# Wuzzy , 2016-2018 # Brot Brot , 2015 # cvcxc , 2013 # divVerent , 2011,2013 @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-23 19:12+0000\n" +"PO-Revision-Date: 2018-07-13 16:04+0000\n" "Last-Translator: Wuzzy \n" "Language-Team: German (http://www.transifex.com/team-xonotic/xonotic/" "language/de/)\n" @@ -48,7 +48,7 @@ msgstr "^1Konnte nicht nach %s schreiben\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." -msgstr "^3Player^7: Dies ist der Chat-Bereich." +msgstr "^3Spieler^7: Dies ist der Chat-Bereich." #: qcsrc/client/hud/panel/engineinfo.qc:69 #, c-format @@ -210,7 +210,7 @@ msgstr "^3Doppelklicke ^7ein Panel für Panel-spezifische Optionen." #: qcsrc/client/hud/panel/infomessages.qc:227 msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" -msgstr "^3STRG^7, um Kollisionstests zu deaktivieren, ^3SHIFT ^7und" +msgstr "^3CTRL^7, um Kollisionstests zu deaktivieren, ^3SHIFT ^7und" #: qcsrc/client/hud/panel/infomessages.qc:228 msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." @@ -222,7 +222,7 @@ msgstr "Persönliche Bestzeit" #: qcsrc/client/hud/panel/modicons.qc:576 msgid "Server best" -msgstr "Server Bestzeit" +msgstr "Server-Bestzeit" #: qcsrc/client/hud/panel/notify.qc:115 qcsrc/client/hud/panel/notify.qc:116 #: qcsrc/client/hud/panel/score.qc:59 @@ -285,7 +285,7 @@ msgstr "freier Gegenstand %x^7 (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item, icon" -msgstr "freier Gegenstand, icon" +msgstr "freier Gegenstand, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item (l:%l^7)" @@ -293,7 +293,7 @@ msgstr "Gegenstand genommen (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item, icon" -msgstr "Gegenstand genommen, icon" +msgstr "Gegenstand genommen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:806 msgid "QMCMD^negative" @@ -309,7 +309,7 @@ msgstr "brauche Hilfe (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:808 msgid "QMCMD^need help, icon" -msgstr "brauche Hilfe, icon" +msgstr "brauche Hilfe, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:809 msgid "QMCMD^enemy seen (l:%y^7)" @@ -317,7 +317,7 @@ msgstr "Gegner gesehen (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:809 msgid "QMCMD^enemy seen, icon" -msgstr "Gegner gesehen, icon" +msgstr "Gegner gesehen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:810 msgid "QMCMD^flag seen (l:%y^7)" @@ -325,7 +325,7 @@ msgstr "Flagge gesehen (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:810 msgid "QMCMD^flag seen, icon" -msgstr "Flagge gesehen, icon" +msgstr "Flagge gesehen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -333,7 +333,7 @@ msgstr "verteidigen (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending, icon" -msgstr "verteidigen, icon" +msgstr "verteidigen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -341,7 +341,7 @@ msgstr "wandernd (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming, icon" -msgstr "wandernd, icon" +msgstr "wandernd, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -349,7 +349,7 @@ msgstr "angreifen (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking, icon" -msgstr "angreifen, Iion" +msgstr "angreifen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier (l:%y^7)" @@ -357,7 +357,7 @@ msgstr "Flaggenträger getötet (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier, icon" -msgstr "Flaggenträger getötet, icon" +msgstr "Flaggenträger getötet, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:815 #, c-format @@ -366,11 +366,11 @@ msgstr "Flagge fallen gelassen (l:%d^7)" #: qcsrc/client/hud/panel/quickmenu.qc:815 msgid "QMCMD^dropped flag, icon" -msgstr "Flagge fallen gelassen, icon" +msgstr "Flagge fallen gelassen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^drop weapon, icon" -msgstr "Waffe wegwerfen, icon" +msgstr "Waffe wegwerfen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" @@ -378,7 +378,7 @@ msgstr "Waffe fallen gelassen %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^drop flag/key, icon" -msgstr "Flagge/Schlüssel fallen gelassen, icon" +msgstr "Flagge/Schlüssel fallen gelassen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^dropped flag/key %w^7 (l:%l^7)" @@ -694,7 +694,7 @@ msgstr "ticks" msgid "" "You can modify the scoreboard using the ^2scoreboard_columns_set command.\n" msgstr "" -"Du kannst die Tabelle mit dem ^2scoreboard_columns_set-Befehl ändern.\n" +"Du kannst die Punktetafel mit dem ^2scoreboard_columns_set-Befehl ändern.\n" #: qcsrc/client/hud/panel/scoreboard.qc:296 msgid "^3|---------------------------------------------------------------|\n" @@ -792,7 +792,7 @@ msgstr "^3capzeit^7 Zeit des schnellsten Captures (CTF)\n" #: qcsrc/client/hud/panel/scoreboard.qc:319 msgid "^3fckills^7 Number of flag carrier kills\n" -msgstr "^3fckills^7 Anzahl der getöteten Flaggen-Träger\n" +msgstr "^3fckills^7 Anzahl der getöteten Flaggenträger\n" #: qcsrc/client/hud/panel/scoreboard.qc:320 msgid "^3returns^7 Number of flag returns\n" @@ -950,7 +950,7 @@ msgstr "Platzierungen" #: qcsrc/client/hud/panel/scoreboard.qc:1519 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 msgid "Scoreboard" -msgstr "Tabelle" +msgstr "Punktetafel" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format @@ -1022,7 +1022,7 @@ msgstr "Du bist tot, warte ^3%s^7 bis zum Respawn" #: qcsrc/client/hud/panel/scoreboard.qc:1707 #, c-format msgid "You are dead, press ^2%s^7 to respawn" -msgstr "Du bist tot, drücke ^2%s^7 um neu zu spawnen" +msgstr "Du bist tot, drücke ^2%s^7, um neu zu spawnen" #: qcsrc/client/hud/panel/vote.qc:24 msgid "^1You must answer before entering hud configure mode\n" @@ -1070,11 +1070,11 @@ msgstr "Keine Munition mehr" #: qcsrc/client/hud/panel/weapons.qc:534 msgid "Don't have" -msgstr "Nicht vorhanden" +msgstr "Hast du nicht" #: qcsrc/client/hud/panel/weapons.qc:538 msgid "Unavailable" -msgstr "Nicht verfügbar" +msgstr "Fehlend" #: qcsrc/client/main.qc:1014 msgid " qu/s" @@ -1384,11 +1384,11 @@ msgstr "Vielleicht klappt es beim nächsten Mal!" #: qcsrc/common/minigames/minigame/bd.qc:1172 msgid "Tubular! Press \"Next Level\" to continue!" -msgstr "Großartig! Drücke „Nächter Level“ zum Fortfahren!" +msgstr "Großartig! Drücke „Nächstes Level“ zum Fortfahren!" #: qcsrc/common/minigames/minigame/bd.qc:1174 msgid "Wicked! Press \"Next Level\" to continue!" -msgstr "Wahnsinn! Klicke \"Nächstes Level\" um weiter zu spielen!" +msgstr "Wahnsinn! Drücke „Nächstes Level“ zum Fortfahren!" #: qcsrc/common/minigames/minigame/bd.qc:1177 msgid "Press the space bar to change your currently selected tile" @@ -1419,7 +1419,7 @@ msgstr "Speichern" #: qcsrc/common/minigames/minigame/pp.qc:438 #: qcsrc/common/minigames/minigame/ttt.qc:319 msgid "Draw" -msgstr "Zeichnen" +msgstr "Unentschieden" #: qcsrc/common/minigames/minigame/c4.qc:378 #: qcsrc/common/minigames/minigame/nmm.qc:601 @@ -1521,7 +1521,7 @@ msgstr "Nächstes Spiel" #: qcsrc/common/minigames/minigame/ps.qc:478 #, c-format msgid "Pieces left: %s" -msgstr "Verbleibende Spielfiguren: %s" +msgstr "Figuren: %s" #: qcsrc/common/minigames/minigame/ps.qc:488 msgid "No more valid moves" @@ -2626,7 +2626,7 @@ msgstr "^BGDu hast ^F1%s^BG%s fallengelassen" #: qcsrc/common/notifications/all.inc:698 #, c-format msgid "^BGYou got the ^F1%s" -msgstr "^BG^F1%s^K1 erhalten" +msgstr "^F1%s^BG erhalten" #: qcsrc/common/notifications/all.inc:385 #: qcsrc/common/notifications/all.inc:699 @@ -2734,12 +2734,12 @@ msgstr "^BGTeam ^TC^TT^BG hielt den Ball zu lange fest" #: qcsrc/common/notifications/all.inc:412 #, c-format msgid "^BG%s^BG captured %s^BG control point" -msgstr "^BG%s^BG hat den Kontrollpunkt von %s^BG erobert" +msgstr "^BG%s^BG hat einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:413 #, c-format msgid "^TC^TT^BG team %s^BG control point has been destroyed by %s" -msgstr "^TC^TT^BGDer Kontrollpunkt vom Team %s^BG wurde von %s zerstört" +msgstr "^TC^TT^BGEin Kontrollpunkt vom Team %s^BG wurde von %s zerstört" #: qcsrc/common/notifications/all.inc:414 msgid "^TC^TT^BG generator has been destroyed" @@ -2759,7 +2759,7 @@ msgstr "^BG%s^K1 hat Unsichtbarkeit aufgesammelt" #: qcsrc/common/notifications/all.inc:418 #, c-format msgid "^BG%s^K1 picked up Shield" -msgstr "^BG%s^K1 hat das Schild aufgenommen" +msgstr "^BG%s^K1 hat den Schild aufgenommen" #: qcsrc/common/notifications/all.inc:419 #, c-format @@ -3596,7 +3596,7 @@ msgstr "^K1Deine Medizin-Granate ist ein wenig defekt" #: qcsrc/common/notifications/all.inc:644 msgid "^K1You are respawning for running out of ammo..." -msgstr "^K1Du wirst wiederbelebt weil du keine Munition mehr hast …" +msgstr "^K1Du wirst wiederbelebt, weil du keine Munition mehr hast …" #: qcsrc/common/notifications/all.inc:644 msgid "^K1You were killed for running out of ammo..." @@ -3913,12 +3913,12 @@ msgstr "^F2Aktive Waffe: ^F1%s" #: qcsrc/common/notifications/all.inc:733 #, c-format msgid "^BGYou captured %s^BG control point" -msgstr "^BGDu hast den Kontrollpunkt von %s^BG erobert" +msgstr "^BGDu hast einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:734 #, c-format msgid "^TC^TT^BG team captured %s^BG control point" -msgstr "Team ^TC^TT^BG hat %ss^BG Kontrollpunkt erobert" +msgstr "Team ^TC^TT^BG hat einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:735 msgid "^BGThis control point currently cannot be captured" @@ -4019,7 +4019,7 @@ msgstr "^F2Ein Schild umgibt dich" #: qcsrc/common/notifications/all.inc:754 msgid "^F2Shield has worn off" -msgstr "^F2Das Schild ist wieder verschwunden" +msgstr "^F2Der Schild ist wieder verschwunden" #: qcsrc/common/notifications/all.inc:756 msgid "^F2You are on speed" @@ -5257,7 +5257,7 @@ msgstr "Schriftgröße:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh:6 msgid "Centerprint Panel" -msgstr "Nachrichten-Panel" +msgstr "Zentralanzeigen-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:15 msgid "Chat entries:" @@ -5355,7 +5355,7 @@ msgstr "Beim Zuschauen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:18 msgid "PNL^Enabled even playing in warmup" -msgstr "Auch in der Aufwärmphase zeigen" +msgstr "Auch in Aufwärmphase zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:29 msgid "Reduced" @@ -5363,7 +5363,7 @@ msgstr "Reduziert" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:32 msgid "Text/icon ratio:" -msgstr "Text/Icon-Verhältnis:" +msgstr "Text-/Icon-Verhältnis:" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:35 msgid "Hide spawned items" @@ -5407,7 +5407,7 @@ msgstr "Eintrags-Ausblendung:" #: qcsrc/menu/xonotic/dialog_hudpanel_notification.qh:6 msgid "Notification Panel" -msgstr "Anzeige-Panel" +msgstr "Nachrichten-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:15 #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 @@ -5421,11 +5421,11 @@ msgstr "Panel anzeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 msgid "Panel enabled even observing" -msgstr "Panel auch beim Zuschauen anzeigen" +msgstr "Auch beim Zuschauen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:18 msgid "Panel enabled only in Race/CTS" -msgstr "Panel nur in Rennen und CTS-Rennen anzeigen" +msgstr "Nur in Rennen und CTS-Rennen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:24 msgid "Status bar" @@ -5536,7 +5536,7 @@ msgstr "Rundenzeit-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:16 msgid "Panel enabled in teamgames" -msgstr "Panel in Team-Spieltypen aktivieren" +msgstr "Panel in Teamspielen aktivieren" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:23 msgid "Radar:" @@ -6317,13 +6317,11 @@ msgstr "Spielmechanik-Mutatoren:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:160 msgid "Enable dodging" -msgstr "" -"Ausweichmodus: Es ist möglich rasch zur Seite zu springen (spezielle " -"Bewegung)" +msgstr "Ausweichmanöver aktivieren" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:167 msgid "All players are almost invisible" -msgstr "Tarnmodus: Alle Spieler sind fast unsichtbar" +msgstr "Alle Spieler sind fast unsichtbar" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:174 msgid "Only possible to inflict damage on your enemy while he's airborne" @@ -6333,15 +6331,15 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 msgid "Damage done to your enemy gets added to your own health" msgstr "" -"Vampirmodus: Der Schaden, dem du anderen Spielern zufügst, wird deiner " -"eigenen Lebensenergie hinzugefügt" +"Der Schaden, dem du anderen Spielern zufügst, wird deiner eigenen " +"Lebensenergie hinzugefügt" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 msgid "" "Amount of health below which your player gets stunned because of blood loss" msgstr "" -"Blutverlust: Aktiviere diesen Modus und stelle den Wert der Lebensenergie, " -"bei der Spieler auf Grund von Blutverlust betäubt wirken, ein" +"Aktiviere diesen Modus und stelle den Wert der Lebensenergie, bei der " +"Spieler auf Grund von Blutverlust betäubt wirken, ein" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:192 msgid "Make things fall to the ground slower, lower value means lower gravity" @@ -6390,10 +6388,9 @@ msgid "" "Selecting a weapon arena will give all players that weapon at spawn as well " "as unlimited ammo, and disable all other weapon pickups." msgstr "" -"Waffen-Arenen: Die Auswahl einer Waffen-Arena führt dazu, dass jeder Spieler " -"mit der gewählten Waffe startet. Diese hat unendlich viel Munition, andere " -"Waffen sind nicht vorhanden – Spezielle Waffen-Arenen: Spieler starten mit " -"allen Waffen und unendlich viel Munition" +"Die Auswahl einer Waffen-Arena führt dazu, dass jeder Spieler mit der " +"gewählten Waffe startet. Diese hat unendlich viel Munition, andere Waffen " +"sind nicht vorhanden." #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 msgid "Most weapons" @@ -6617,7 +6614,7 @@ msgstr "Musikplayer" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:48 msgid "Auto record demos" -msgstr "Wiederholungen automatisch aufzeichnen" +msgstr "Wiederholungen autom. aufzeichnen" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:57 msgid "Timedemo" @@ -6708,7 +6705,7 @@ msgstr "Alle entfernen" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 msgid "Auto screenshot scoreboard" -msgstr "Screenshot der Tabelle automatisch anfertigen" +msgstr "Auto-Screenshot der Punktetafel" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:63 msgid "Open in the viewer" @@ -7419,7 +7416,7 @@ msgstr "Scharf" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:163 msgid "Decals" -msgstr "Einschusslöcher" +msgstr "Dekore" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 msgid "Enable decals (bullet holes and blood) (default: enabled)" @@ -7717,7 +7714,7 @@ msgstr "Dezimalstellen im Respawn-Countdown anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:57 msgid "Show accuracy underneath scoreboard" -msgstr "Trefferquote unter Tabelle anzeigen" +msgstr "Trefferquote unter Punktetafel anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:61 msgid "Waypoints" @@ -7824,7 +7821,7 @@ msgstr "Um den HUD-Editor zu starten, muss ein Spiel gestartet werden." #: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:23 msgid "Do you wish to start a local game to set up the HUD?" -msgstr "Willst du ein lokales Spiel starten, um das HUD zu editieren?" +msgstr "Willst du ein lokales Spiel starten, um das HUD zu bearbeiten?" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:24 msgid "Frag Information" @@ -7840,7 +7837,7 @@ msgstr "Amokläufe nur anzeigen, wenn sie Achievements sind" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:34 msgid "Show spree information in centerprints" -msgstr "Amoklauf-Informationen in Centerprints anzeigen" +msgstr "Amoklauf-Infos in Zentralanzeige zeigen" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:38 msgid "Show spree information in death messages" @@ -7872,7 +7869,7 @@ msgstr "In separater Zeile anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:58 msgid "Add extra frag information to centerprint when available" -msgstr "Zusätzliche Frag-Informationen in Centerprint anzeigen, wenn verfügbar" +msgstr "Zusätzliche Frag-Infos in Zentralanzeige zeigen, wenn verfügbar" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:62 msgid "Add frag location to death messages when available" @@ -7914,7 +7911,7 @@ msgstr "Powerup-Nachrichten" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:89 msgid "Weapon centerprint notifications" -msgstr "Waffen-Centerprint-Nachrichten" +msgstr "Waffen-Zentralanzeigen-Nachrichten" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:92 msgid "Weapon info message notifications" @@ -7995,7 +7992,7 @@ msgstr "Nur in Nicht-Teamspielen" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:60 msgid "Body fading:" -msgstr "Ausblenden von Leichen:" +msgstr "Leichenausblendung:" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:63 msgid "Gibs:" @@ -8290,7 +8287,7 @@ msgstr "Auto-Springen" #: qcsrc/menu/xonotic/dialog_settings_input.qc:99 msgid "Jetpack on jump:" -msgstr "Jetpack aktiveren durch Springen:" +msgstr "Jetpack bei Sprung:" #: qcsrc/menu/xonotic/dialog_settings_input.qc:101 msgid "JPJUMP^Disabled" @@ -8395,7 +8392,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:48 msgid "Server queries/s:" -msgstr "Server Anfragen/Sekunde:" +msgstr "Serveranfragen/s:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 msgid "Downloads:" @@ -8589,11 +8586,12 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:10 msgid "While connected language changes will be applied only to the menu," msgstr "" -"Während du verbunden bist, werden Sprachänderungen nur auf das Menü angewandt" +"Während du verbunden bist, werden Sprachänderungen nur auf das Menü " +"angewandt;" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:12 msgid "full language changes will take effect starting from the next game" -msgstr "Volle Sprachänderungen finden erst nach dem Neustart des Spiels statt" +msgstr "volle Sprachänderungen finden erst nach dem Neustart des Spiels statt." #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 msgid "Disconnect now" @@ -8940,7 +8938,7 @@ msgstr "Einzelspieler" #: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 msgid "Play the singleplayer campaign or instant action matches against bots" msgstr "" -"Spiele die Einzelspieler-Kampagne oder habe Instant-Action Spiele gegen Bots" +"Spiele die Einzelspieler-Kampagne oder spiele ein Schnellspiel gegen Bots" #: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 msgid "Winner" @@ -9492,7 +9490,7 @@ msgstr "" #: qcsrc/menu/xonotic/util.qc:780 msgid "Use default" -msgstr "Standard verwenden" +msgstr "Standard" #: qcsrc/menu/xonotic/util.qc:800 msgid "Team Color:" diff --git a/common.de_CH.po b/common.de_CH.po index d5c87645f5..2c2c96b5da 100644 --- a/common.de_CH.po +++ b/common.de_CH.po @@ -3,7 +3,7 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Wuzzy , 2016-2017 +# Wuzzy , 2016-2018 # Brot Brot , 2015 # cvcxc , 2013 # divVerent , 2011,2013 @@ -24,7 +24,7 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-23 19:12+0000\n" +"PO-Revision-Date: 2018-07-13 16:04+0000\n" "Last-Translator: Wuzzy \n" "Language-Team: German (http://www.transifex.com/team-xonotic/xonotic/" "language/de/)\n" @@ -48,7 +48,7 @@ msgstr "^1Konnte nicht nach %s schreiben\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." -msgstr "^3Player^7: Dies ist der Chat-Bereich." +msgstr "^3Spieler^7: Dies ist der Chat-Bereich." #: qcsrc/client/hud/panel/engineinfo.qc:69 #, c-format @@ -210,7 +210,7 @@ msgstr "^3Doppelklicke ^7ein Panel für Panel-spezifische Optionen." #: qcsrc/client/hud/panel/infomessages.qc:227 msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" -msgstr "^3STRG^7, um Kollisionstests zu deaktivieren, ^3SHIFT ^7und" +msgstr "^3CTRL^7, um Kollisionstests zu deaktivieren, ^3SHIFT ^7und" #: qcsrc/client/hud/panel/infomessages.qc:228 msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." @@ -222,7 +222,7 @@ msgstr "Persönliche Bestzeit" #: qcsrc/client/hud/panel/modicons.qc:576 msgid "Server best" -msgstr "Server Bestzeit" +msgstr "Server-Bestzeit" #: qcsrc/client/hud/panel/notify.qc:115 qcsrc/client/hud/panel/notify.qc:116 #: qcsrc/client/hud/panel/score.qc:59 @@ -285,7 +285,7 @@ msgstr "freier Gegenstand %x^7 (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item, icon" -msgstr "freier Gegenstand, icon" +msgstr "freier Gegenstand, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item (l:%l^7)" @@ -293,7 +293,7 @@ msgstr "Gegenstand genommen (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item, icon" -msgstr "Gegenstand genommen, icon" +msgstr "Gegenstand genommen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:806 msgid "QMCMD^negative" @@ -309,7 +309,7 @@ msgstr "brauche Hilfe (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:808 msgid "QMCMD^need help, icon" -msgstr "brauche Hilfe, icon" +msgstr "brauche Hilfe, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:809 msgid "QMCMD^enemy seen (l:%y^7)" @@ -317,7 +317,7 @@ msgstr "Gegner gesehen (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:809 msgid "QMCMD^enemy seen, icon" -msgstr "Gegner gesehen, icon" +msgstr "Gegner gesehen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:810 msgid "QMCMD^flag seen (l:%y^7)" @@ -325,7 +325,7 @@ msgstr "Flagge gesehen (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:810 msgid "QMCMD^flag seen, icon" -msgstr "Flagge gesehen, icon" +msgstr "Flagge gesehen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -333,7 +333,7 @@ msgstr "verteidigen (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending, icon" -msgstr "verteidigen, icon" +msgstr "verteidigen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -341,7 +341,7 @@ msgstr "wandernd (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming, icon" -msgstr "wandernd, icon" +msgstr "wandernd, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" @@ -349,7 +349,7 @@ msgstr "angreifen (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking, icon" -msgstr "angreifen, Iion" +msgstr "angreifen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier (l:%y^7)" @@ -357,7 +357,7 @@ msgstr "Flaggenträger getötet (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier, icon" -msgstr "Flaggenträger getötet, icon" +msgstr "Flaggenträger getötet, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:815 #, c-format @@ -366,11 +366,11 @@ msgstr "Flagge fallen gelassen (l:%d^7)" #: qcsrc/client/hud/panel/quickmenu.qc:815 msgid "QMCMD^dropped flag, icon" -msgstr "Flagge fallen gelassen, icon" +msgstr "Flagge fallen gelassen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^drop weapon, icon" -msgstr "Waffe wegwerfen, icon" +msgstr "Waffe wegwerfen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" @@ -378,7 +378,7 @@ msgstr "Waffe fallen gelassen %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^drop flag/key, icon" -msgstr "Flagge/Schlüssel fallen gelassen, icon" +msgstr "Flagge/Schlüssel fallen gelassen, Icon" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^dropped flag/key %w^7 (l:%l^7)" @@ -694,7 +694,7 @@ msgstr "ticks" msgid "" "You can modify the scoreboard using the ^2scoreboard_columns_set command.\n" msgstr "" -"Du kannst die Tabelle mit dem ^2scoreboard_columns_set-Befehl ändern.\n" +"Du kannst die Punktetafel mit dem ^2scoreboard_columns_set-Befehl ändern.\n" #: qcsrc/client/hud/panel/scoreboard.qc:296 msgid "^3|---------------------------------------------------------------|\n" @@ -792,7 +792,7 @@ msgstr "^3capzeit^7 Zeit des schnellsten Captures (CTF)\n" #: qcsrc/client/hud/panel/scoreboard.qc:319 msgid "^3fckills^7 Number of flag carrier kills\n" -msgstr "^3fckills^7 Anzahl der getöteten Flaggen-Träger\n" +msgstr "^3fckills^7 Anzahl der getöteten Flaggenträger\n" #: qcsrc/client/hud/panel/scoreboard.qc:320 msgid "^3returns^7 Number of flag returns\n" @@ -950,7 +950,7 @@ msgstr "Platzierungen" #: qcsrc/client/hud/panel/scoreboard.qc:1519 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 msgid "Scoreboard" -msgstr "Tabelle" +msgstr "Punktetafel" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format @@ -1022,7 +1022,7 @@ msgstr "Du bist tot, warte ^3%s^7 bis zum Respawn" #: qcsrc/client/hud/panel/scoreboard.qc:1707 #, c-format msgid "You are dead, press ^2%s^7 to respawn" -msgstr "Du bist tot, drücke ^2%s^7 um neu zu spawnen" +msgstr "Du bist tot, drücke ^2%s^7, um neu zu spawnen" #: qcsrc/client/hud/panel/vote.qc:24 msgid "^1You must answer before entering hud configure mode\n" @@ -1070,11 +1070,11 @@ msgstr "Keine Munition mehr" #: qcsrc/client/hud/panel/weapons.qc:534 msgid "Don't have" -msgstr "Nicht vorhanden" +msgstr "Hast du nicht" #: qcsrc/client/hud/panel/weapons.qc:538 msgid "Unavailable" -msgstr "Nicht verfügbar" +msgstr "Fehlend" #: qcsrc/client/main.qc:1014 msgid " qu/s" @@ -1384,11 +1384,11 @@ msgstr "Vielleicht klappt es beim nächsten Mal!" #: qcsrc/common/minigames/minigame/bd.qc:1172 msgid "Tubular! Press \"Next Level\" to continue!" -msgstr "Grossartig! Drücke „Nächter Level“ zum Fortfahren!" +msgstr "Grossartig! Drücke „Nächstes Level“ zum Fortfahren!" #: qcsrc/common/minigames/minigame/bd.qc:1174 msgid "Wicked! Press \"Next Level\" to continue!" -msgstr "Wahnsinn! Klicke \"Nächstes Level\" um weiter zu spielen!" +msgstr "Wahnsinn! Drücke „Nächstes Level“ zum Fortfahren!" #: qcsrc/common/minigames/minigame/bd.qc:1177 msgid "Press the space bar to change your currently selected tile" @@ -1419,7 +1419,7 @@ msgstr "Speichern" #: qcsrc/common/minigames/minigame/pp.qc:438 #: qcsrc/common/minigames/minigame/ttt.qc:319 msgid "Draw" -msgstr "Zeichnen" +msgstr "Unentschieden" #: qcsrc/common/minigames/minigame/c4.qc:378 #: qcsrc/common/minigames/minigame/nmm.qc:601 @@ -1521,7 +1521,7 @@ msgstr "Nächstes Spiel" #: qcsrc/common/minigames/minigame/ps.qc:478 #, c-format msgid "Pieces left: %s" -msgstr "Verbleibende Spielfiguren: %s" +msgstr "Figuren: %s" #: qcsrc/common/minigames/minigame/ps.qc:488 msgid "No more valid moves" @@ -2626,7 +2626,7 @@ msgstr "^BGDu hast ^F1%s^BG%s fallengelassen" #: qcsrc/common/notifications/all.inc:698 #, c-format msgid "^BGYou got the ^F1%s" -msgstr "^BG^F1%s^K1 erhalten" +msgstr "^F1%s^BG erhalten" #: qcsrc/common/notifications/all.inc:385 #: qcsrc/common/notifications/all.inc:699 @@ -2734,12 +2734,12 @@ msgstr "^BGTeam ^TC^TT^BG hielt den Ball zu lange fest" #: qcsrc/common/notifications/all.inc:412 #, c-format msgid "^BG%s^BG captured %s^BG control point" -msgstr "^BG%s^BG hat den Kontrollpunkt von %s^BG erobert" +msgstr "^BG%s^BG hat einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:413 #, c-format msgid "^TC^TT^BG team %s^BG control point has been destroyed by %s" -msgstr "^TC^TT^BGDer Kontrollpunkt vom Team %s^BG wurde von %s zerstört" +msgstr "^TC^TT^BGEin Kontrollpunkt vom Team %s^BG wurde von %s zerstört" #: qcsrc/common/notifications/all.inc:414 msgid "^TC^TT^BG generator has been destroyed" @@ -2759,7 +2759,7 @@ msgstr "^BG%s^K1 hat Unsichtbarkeit aufgesammelt" #: qcsrc/common/notifications/all.inc:418 #, c-format msgid "^BG%s^K1 picked up Shield" -msgstr "^BG%s^K1 hat das Schild aufgenommen" +msgstr "^BG%s^K1 hat den Schild aufgenommen" #: qcsrc/common/notifications/all.inc:419 #, c-format @@ -3597,7 +3597,7 @@ msgstr "^K1Deine Medizin-Granate ist ein wenig defekt" #: qcsrc/common/notifications/all.inc:644 msgid "^K1You are respawning for running out of ammo..." -msgstr "^K1Du wirst wiederbelebt weil du keine Munition mehr hast …" +msgstr "^K1Du wirst wiederbelebt, weil du keine Munition mehr hast …" #: qcsrc/common/notifications/all.inc:644 msgid "^K1You were killed for running out of ammo..." @@ -3914,12 +3914,12 @@ msgstr "^F2Aktive Waffe: ^F1%s" #: qcsrc/common/notifications/all.inc:733 #, c-format msgid "^BGYou captured %s^BG control point" -msgstr "^BGDu hast den Kontrollpunkt von %s^BG erobert" +msgstr "^BGDu hast einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:734 #, c-format msgid "^TC^TT^BG team captured %s^BG control point" -msgstr "Team ^TC^TT^BG hat %ss^BG Kontrollpunkt erobert" +msgstr "Team ^TC^TT^BG hat einen Kontrollpunkt erobert: %s^BG" #: qcsrc/common/notifications/all.inc:735 msgid "^BGThis control point currently cannot be captured" @@ -4020,7 +4020,7 @@ msgstr "^F2Ein Schild umgibt dich" #: qcsrc/common/notifications/all.inc:754 msgid "^F2Shield has worn off" -msgstr "^F2Das Schild ist wieder verschwunden" +msgstr "^F2Der Schild ist wieder verschwunden" #: qcsrc/common/notifications/all.inc:756 msgid "^F2You are on speed" @@ -5258,7 +5258,7 @@ msgstr "Schriftgrösse:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh:6 msgid "Centerprint Panel" -msgstr "Nachrichten-Panel" +msgstr "Zentralanzeigen-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:15 msgid "Chat entries:" @@ -5356,7 +5356,7 @@ msgstr "Beim Zuschauen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:18 msgid "PNL^Enabled even playing in warmup" -msgstr "Auch in der Aufwärmphase zeigen" +msgstr "Auch in Aufwärmphase zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:29 msgid "Reduced" @@ -5364,7 +5364,7 @@ msgstr "Reduziert" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:32 msgid "Text/icon ratio:" -msgstr "Text/Icon-Verhältnis:" +msgstr "Text-/Icon-Verhältnis:" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:35 msgid "Hide spawned items" @@ -5408,7 +5408,7 @@ msgstr "Eintrags-Ausblendung:" #: qcsrc/menu/xonotic/dialog_hudpanel_notification.qh:6 msgid "Notification Panel" -msgstr "Anzeige-Panel" +msgstr "Nachrichten-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:15 #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 @@ -5422,11 +5422,11 @@ msgstr "Panel anzeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 msgid "Panel enabled even observing" -msgstr "Panel auch beim Zuschauen anzeigen" +msgstr "Auch beim Zuschauen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:18 msgid "Panel enabled only in Race/CTS" -msgstr "Panel nur in Rennen und CTS-Rennen anzeigen" +msgstr "Nur in Rennen und CTS-Rennen zeigen" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:24 msgid "Status bar" @@ -5537,7 +5537,7 @@ msgstr "Rundenzeit-Panel" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:16 msgid "Panel enabled in teamgames" -msgstr "Panel in Team-Spieltypen aktivieren" +msgstr "Panel in Teamspielen aktivieren" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:23 msgid "Radar:" @@ -6318,13 +6318,11 @@ msgstr "Spielmechanik-Mutatoren:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:160 msgid "Enable dodging" -msgstr "" -"Ausweichmodus: Es ist möglich rasch zur Seite zu springen (spezielle " -"Bewegung)" +msgstr "Ausweichmanöver aktivieren" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:167 msgid "All players are almost invisible" -msgstr "Tarnmodus: Alle Spieler sind fast unsichtbar" +msgstr "Alle Spieler sind fast unsichtbar" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:174 msgid "Only possible to inflict damage on your enemy while he's airborne" @@ -6334,15 +6332,15 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 msgid "Damage done to your enemy gets added to your own health" msgstr "" -"Vampirmodus: Der Schaden, dem du anderen Spielern zufügst, wird deiner " -"eigenen Lebensenergie hinzugefügt" +"Der Schaden, dem du anderen Spielern zufügst, wird deiner eigenen " +"Lebensenergie hinzugefügt" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 msgid "" "Amount of health below which your player gets stunned because of blood loss" msgstr "" -"Blutverlust: Aktiviere diesen Modus und stelle den Wert der Lebensenergie, " -"bei der Spieler auf Grund von Blutverlust betäubt wirken, ein" +"Aktiviere diesen Modus und stelle den Wert der Lebensenergie, bei der " +"Spieler auf Grund von Blutverlust betäubt wirken, ein" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:192 msgid "Make things fall to the ground slower, lower value means lower gravity" @@ -6391,10 +6389,9 @@ msgid "" "Selecting a weapon arena will give all players that weapon at spawn as well " "as unlimited ammo, and disable all other weapon pickups." msgstr "" -"Waffen-Arenen: Die Auswahl einer Waffen-Arena führt dazu, dass jeder Spieler " -"mit der gewählten Waffe startet. Diese hat unendlich viel Munition, andere " -"Waffen sind nicht vorhanden – Spezielle Waffen-Arenen: Spieler starten mit " -"allen Waffen und unendlich viel Munition" +"Die Auswahl einer Waffen-Arena führt dazu, dass jeder Spieler mit der " +"gewählten Waffe startet. Diese hat unendlich viel Munition, andere Waffen " +"sind nicht vorhanden." #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 msgid "Most weapons" @@ -6618,7 +6615,7 @@ msgstr "Musikplayer" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:48 msgid "Auto record demos" -msgstr "Wiederholungen automatisch aufzeichnen" +msgstr "Wiederholungen autom. aufzeichnen" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:57 msgid "Timedemo" @@ -6709,7 +6706,7 @@ msgstr "Alle entfernen" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 msgid "Auto screenshot scoreboard" -msgstr "Screenshot der Tabelle automatisch anfertigen" +msgstr "Auto-Screenshot der Punktetafel" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:63 msgid "Open in the viewer" @@ -7420,7 +7417,7 @@ msgstr "Scharf" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:163 msgid "Decals" -msgstr "Einschusslöcher" +msgstr "Dekore" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 msgid "Enable decals (bullet holes and blood) (default: enabled)" @@ -7718,7 +7715,7 @@ msgstr "Dezimalstellen im Respawn-Countdown anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:57 msgid "Show accuracy underneath scoreboard" -msgstr "Trefferquote unter Tabelle anzeigen" +msgstr "Trefferquote unter Punktetafel anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:61 msgid "Waypoints" @@ -7825,7 +7822,7 @@ msgstr "Um den HUD-Editor zu starten, muss ein Spiel gestartet werden." #: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:23 msgid "Do you wish to start a local game to set up the HUD?" -msgstr "Willst du ein lokales Spiel starten, um das HUD zu editieren?" +msgstr "Willst du ein lokales Spiel starten, um das HUD zu bearbeiten?" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:24 msgid "Frag Information" @@ -7841,7 +7838,7 @@ msgstr "Amokläufe nur anzeigen, wenn sie Achievements sind" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:34 msgid "Show spree information in centerprints" -msgstr "Amoklauf-Informationen in Centerprints anzeigen" +msgstr "Amoklauf-Infos in Zentralanzeige zeigen" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:38 msgid "Show spree information in death messages" @@ -7873,7 +7870,7 @@ msgstr "In separater Zeile anzeigen" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:58 msgid "Add extra frag information to centerprint when available" -msgstr "Zusätzliche Frag-Informationen in Centerprint anzeigen, wenn verfügbar" +msgstr "Zusätzliche Frag-Infos in Zentralanzeige zeigen, wenn verfügbar" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:62 msgid "Add frag location to death messages when available" @@ -7915,7 +7912,7 @@ msgstr "Powerup-Nachrichten" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:89 msgid "Weapon centerprint notifications" -msgstr "Waffen-Centerprint-Nachrichten" +msgstr "Waffen-Zentralanzeigen-Nachrichten" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:92 msgid "Weapon info message notifications" @@ -7996,7 +7993,7 @@ msgstr "Nur in Nicht-Teamspielen" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:60 msgid "Body fading:" -msgstr "Ausblenden von Leichen:" +msgstr "Leichenausblendung:" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:63 msgid "Gibs:" @@ -8291,7 +8288,7 @@ msgstr "Auto-Springen" #: qcsrc/menu/xonotic/dialog_settings_input.qc:99 msgid "Jetpack on jump:" -msgstr "Jetpack aktiveren durch Springen:" +msgstr "Jetpack bei Sprung:" #: qcsrc/menu/xonotic/dialog_settings_input.qc:101 msgid "JPJUMP^Disabled" @@ -8396,7 +8393,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:48 msgid "Server queries/s:" -msgstr "Server Anfragen/Sekunde:" +msgstr "Serveranfragen/s:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 msgid "Downloads:" @@ -8590,11 +8587,12 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:10 msgid "While connected language changes will be applied only to the menu," msgstr "" -"Während du verbunden bist, werden Sprachänderungen nur auf das Menü angewandt" +"Während du verbunden bist, werden Sprachänderungen nur auf das Menü " +"angewandt;" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:12 msgid "full language changes will take effect starting from the next game" -msgstr "Volle Sprachänderungen finden erst nach dem Neustart des Spiels statt" +msgstr "volle Sprachänderungen finden erst nach dem Neustart des Spiels statt." #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 msgid "Disconnect now" @@ -8941,7 +8939,7 @@ msgstr "Einzelspieler" #: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 msgid "Play the singleplayer campaign or instant action matches against bots" msgstr "" -"Spiele die Einzelspieler-Kampagne oder habe Instant-Action Spiele gegen Bots" +"Spiele die Einzelspieler-Kampagne oder spiele ein Schnellspiel gegen Bots" #: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 msgid "Winner" @@ -9493,7 +9491,7 @@ msgstr "" #: qcsrc/menu/xonotic/util.qc:780 msgid "Use default" -msgstr "Standard verwenden" +msgstr "Standard" #: qcsrc/menu/xonotic/util.qc:800 msgid "Team Color:" diff --git a/common.el.po b/common.el.po index 81195ec58c..dd308eb5d6 100644 --- a/common.el.po +++ b/common.el.po @@ -7,6 +7,7 @@ # Vindex , 2014 # Konstantinos Mihalenas , 2014 # MasterWord, 2016 +# Mensious Mensious , 2018 # Vindex , 2014 # Γιάννης Ανθυμίδης, 2011-2012 msgid "" @@ -14,8 +15,8 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-19 19:54+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-10-29 12:20+0000\n" +"Last-Translator: Mensious Mensious \n" "Language-Team: Greek (http://www.transifex.com/team-xonotic/xonotic/language/" "el/)\n" "Language: el\n" @@ -70,12 +71,12 @@ msgstr "^1Πατήστε ^3%s^1 ή ^3%s^1 για επόμενο ή προηγο #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "next weapon" -msgstr "" +msgstr "επόμενο όπλο" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "previous weapon" -msgstr "" +msgstr "προηγούμενο όπλο" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format @@ -124,7 +125,7 @@ msgstr "^1Πατήστε ^3%s^1 για σύνδεση" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 msgid "jump" -msgstr "" +msgstr "άλμα" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format @@ -892,7 +893,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:1295 msgid "Map stats:" -msgstr "" +msgstr "Στατιστικά χάρτη:" #: qcsrc/client/hud/panel/scoreboard.qc:1325 msgid "Monsters killed:" diff --git a/common.es.po b/common.es.po index 5025390f48..2d4350a35f 100644 --- a/common.es.po +++ b/common.es.po @@ -15,7 +15,8 @@ # roader_gentoo , 2014 # Rodrigo Mouton Laudin , 2011 # Simon , 2014-2015 -# starfire24680 , 2017 +# starfire24680 , 2018 +# starfire24680 , 2017-2018 # starfire24680 , 2017 # Vitama Piru Leta , 2017 # Ari_tent , 2014 @@ -26,8 +27,8 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-19 19:54+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-05-11 21:48+0000\n" +"Last-Translator: starfire24680 \n" "Language-Team: Spanish (http://www.transifex.com/team-xonotic/xonotic/" "language/es/)\n" "Language: es\n" @@ -48,7 +49,7 @@ msgstr "^1No se pudo escribir a %s\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." -msgstr "^3Jugador^7: Este es el area de chat." +msgstr "^3Jugador^7: Este es el area del chat." #: qcsrc/client/hud/panel/engineinfo.qc:69 #, c-format @@ -97,7 +98,7 @@ msgstr "^1Usa ^3%s^1 o ^3%s^1 para cambiar la velocidad" #: qcsrc/client/hud/panel/infomessages.qc:108 #, c-format msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" -msgstr "^1Pulsa ^3%s^1 para observar y ^3%s^1 para cambiar el modo de cámara." +msgstr "^1Pulsa ^3%s^1 para observar o ^3%s^1 para cambiar el modo de cámara." #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/common/vehicles/cl_vehicles.qc:192 @@ -112,7 +113,7 @@ msgstr "disparo secundario" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format msgid "^1Press ^3%s^1 for gamemode info" -msgstr "^1Presiona ^3%s^1 para información del modo de juego" +msgstr "^1Presiona ^3%s^1 para mostrar información del modo de juego" #: qcsrc/client/hud/panel/infomessages.qc:111 #: qcsrc/menu/xonotic/keybinder.qc:94 @@ -357,7 +358,7 @@ msgstr "QMCMD^asesinado el portador de bandera (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier, icon" -msgstr "" +msgstr "QMCMD^asesinado el portador de la bandera, icono" #: qcsrc/client/hud/panel/quickmenu.qc:815 #, c-format @@ -370,11 +371,11 @@ msgstr "QMCMD^bandera tirada, icono" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^drop weapon, icon" -msgstr "" +msgstr "QMCMD^tirar arma, icono" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" -msgstr "" +msgstr "QMCMD^arma tirada %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^drop flag/key, icon" @@ -523,7 +524,7 @@ msgstr "Linea de salida" #: qcsrc/client/hud/panel/racetimer.qc:63 #: qcsrc/client/hud/panel/racetimer.qc:67 msgid "Finish line" -msgstr "Línea eta" +msgstr "Línea de meta" #: qcsrc/client/hud/panel/racetimer.qc:65 #, c-format @@ -1118,7 +1119,7 @@ msgstr "No importa" #: qcsrc/client/mapvoting.qc:365 msgid "Decide the gametype" -msgstr "Elegir el modo de juego" +msgstr "Elige el modo de juego" #: qcsrc/client/mapvoting.qc:365 msgid "Vote for a map" @@ -1132,7 +1133,7 @@ msgstr "%d segundos restantes" #: qcsrc/client/mapvoting.qc:497 msgid "" "mv_mapdownload: ^3You're not supposed to use this command on your own!\n" -msgstr "mv_mapdownload: ^3¡No deberias usar este comando en tí!\n" +msgstr "mv_mapdownload: ^3¡No deberias usar este comando por tu cuenta!\n" #: qcsrc/client/mapvoting.qc:507 msgid "^1Error:^7 Couldn't find pak index.\n" @@ -1251,7 +1252,7 @@ msgstr "Combate a muerte por equipos" #: qcsrc/common/mapinfo.qh:220 msgid "Capture the Flag" -msgstr "Consigue la bandera" +msgstr "Captura la bandera" #: qcsrc/common/mapinfo.qh:220 msgid "" @@ -1679,7 +1680,7 @@ msgstr "Color:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 msgid "Draw damage numbers for friendly fire" -msgstr "Dibujar números de daño para fuego amigo" +msgstr "Dibujar números de daño para el fuego amigo" #: qcsrc/common/mutators/mutator/instagib/items.qh:56 msgid "Extra life" @@ -1814,7 +1815,7 @@ msgstr "Base rosa" #: qcsrc/common/mutators/mutator/waypoints/all.inc:29 msgid "Return flag here" -msgstr "Devuele la vandera aquí" +msgstr "Devuelva la bandera aquí" #: qcsrc/common/mutators/mutator/waypoints/all.inc:31 #: qcsrc/common/mutators/mutator/waypoints/all.inc:32 @@ -1912,8 +1913,8 @@ msgid "" "^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG" "%s^BG's previous record of ^F2%s^BG seconds" msgstr "" -"^BG%s^BG capturó la bandera ^TC^TT^BG en ^F1%s^BG segundos, rompiendo ^BG" -"%s^BG's el record anterior de ^F2%s^BG segundos" +"^BG%s^BG capturó la bandera ^TC^TT^BG en ^F1%s^BG segundos, batiendo el " +"record anterior de ^BG%s^BG de ^F2%s^BG segundos" #: qcsrc/common/notifications/all.inc:243 #, c-format @@ -1932,7 +1933,7 @@ msgid "" "^BG%s^BG's previous record of ^F1%s^BG seconds" msgstr "" "^BG%s^BG capturó la bandera ^TC^TT^BG en ^F2%s^BG segundos, fallando al " -"romper ^BG%s^BG's el record anterior de ^F1%s^BG segundos" +"batir el record anterior de ^BG%s^BG de ^F1%s^BG segundos" #: qcsrc/common/notifications/all.inc:246 msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" @@ -2085,12 +2086,12 @@ msgstr "^BG%s%s^K1 fue cocinado por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:280 #, c-format msgid "^BG%s%s^K1 was pushed in front of a monster by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 fue empujado a un monstruo por ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 fue empujado hacia un monstruo por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:281 #, c-format msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s" -msgstr "^BG%s%s^K1 fue explotado por ^BG%s^K1 Granda%s%s" +msgstr "^BG%s%s^K1 fue explotado por la granada de ^BG%s^K1 %s%s" #: qcsrc/common/notifications/all.inc:282 #, c-format @@ -2869,7 +2870,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:443 #, c-format msgid "^BG%s^K1 picked up a Superweapon" -msgstr "^BG%s^K1 ha recogido una superarma" +msgstr "^BG%s^K1 ha recogido una Superarma" #: qcsrc/common/notifications/all.inc:445 msgid "^BGYou cannot change to a larger team" @@ -2908,7 +2909,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:452 #, c-format msgid "^F3SVQC Build information: ^F4%s" -msgstr "^F3SVQC Información de compilación ^F4%s" +msgstr "^F3SVQC Información de compilación: ^F4%s" #: qcsrc/common/notifications/all.inc:454 #, c-format @@ -3106,7 +3107,7 @@ msgstr "^BG%s%s^K1 se acercó demasiado a la granada del Mortar de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:490 #, c-format msgid "^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s" -msgstr "^BG%s%s^K1 se comió la granada del mortar de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 se comió la granada del Mortero de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:491 #, c-format @@ -3215,7 +3216,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:510 #, c-format msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s" -msgstr "^BG%s^K1 lastimó sus propios oidos con el @!#%%'n Tuba%s%s" +msgstr "^BG%s^K1 lastimó sus propios oidos con la @!#%%'n Tuba%s%s" #: qcsrc/common/notifications/all.inc:511 #, c-format @@ -4120,7 +4121,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:788 msgid "^F2Intruder detected, disabling shields!" -msgstr "" +msgstr "^F2Intruso detectado, desactivando escudos!" #: qcsrc/common/notifications/all.qh:188 msgid "Notification dump command only works with cl_cmd and sv_cmd.\n" @@ -4533,11 +4534,11 @@ msgstr "Presiona %s" #: qcsrc/common/vehicles/vehicle/bumblebee.qc:950 msgid "No right gunner!" -msgstr "Sin ametralladora derecha!" +msgstr "¡Sin artillero derecho!" #: qcsrc/common/vehicles/vehicle/bumblebee.qc:956 msgid "No left gunner!" -msgstr "Sin ametralladora izquierda!" +msgstr "¡Sin artillero izquierdo!" #: qcsrc/common/vehicles/vehicle/bumblebee.qh:19 msgid "Bumblebee" @@ -5041,7 +5042,7 @@ msgstr "Húngaro" #: qcsrc/menu/xonotic/credits.qc:231 msgid "Irish" -msgstr "" +msgstr "Irlandés" #: qcsrc/menu/xonotic/credits.qc:234 msgid "Italian" @@ -5073,7 +5074,7 @@ msgstr "Ruso" #: qcsrc/menu/xonotic/credits.qc:279 msgid "Scottish Gaelic" -msgstr "" +msgstr "Gaélico escocés" #: qcsrc/menu/xonotic/credits.qc:282 msgid "Serbian" @@ -7390,7 +7391,7 @@ msgstr "Ajustado" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:163 msgid "Decals" -msgstr "Símbolos" +msgstr "Calcomanía" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 msgid "Enable decals (bullet holes and blood) (default: enabled)" diff --git a/common.fr.po b/common.fr.po index 84391e6114..4a3b837d1c 100644 --- a/common.fr.po +++ b/common.fr.po @@ -12,13 +12,13 @@ # RedGuff , 2014 # Yannick Le Guen , 2013 # Hugo Locurcio, 2013 -# Yannick Le Guen , 2013-2017 +# Yannick Le Guen , 2013-2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-14 13:32+0000\n" +"PO-Revision-Date: 2018-07-08 09:28+0000\n" "Last-Translator: Yannick Le Guen \n" "Language-Team: French (http://www.transifex.com/team-xonotic/xonotic/" "language/fr/)\n" @@ -3086,12 +3086,12 @@ msgstr "" #: qcsrc/common/notifications/all.inc:484 #, c-format msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s" -msgstr "^BG%s%s^K1 a été abattu par la Mitrailleuse de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 a été abattu par la Mitraillette de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:485 #, c-format msgid "^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s" -msgstr "^BG%s%s^K1 a été criblé de balles par la Mitrailleuse de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 a été criblé de balles par la Mitraillette de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:486 #: qcsrc/common/notifications/all.inc:790 @@ -4631,7 +4631,7 @@ msgstr "Grappin" #: qcsrc/common/weapons/weapon/machinegun.qc:17 msgid "MachineGun" -msgstr "Mitrailleuse" +msgstr "Mitraillette" #: qcsrc/common/weapons/weapon/minelayer.qc:17 msgid "Mine Layer" diff --git a/common.he.po b/common.he.po index abdf38575f..f158cef5b8 100644 --- a/common.he.po +++ b/common.he.po @@ -3,20 +3,22 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# nad le , 2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-07-05 15:06+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-07-18 12:01+0000\n" +"Last-Translator: nad le \n" "Language-Team: Hebrew (http://www.transifex.com/team-xonotic/xonotic/" "language/he/)\n" "Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % " +"1 == 0) ? 1: (n % 10 == 0 && n % 1 == 0 && n > 10) ? 2 : 3;\n" #: qcsrc/client/hud/hud_config.qc:239 #, c-format @@ -64,12 +66,12 @@ msgstr "" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "next weapon" -msgstr "" +msgstr "נשק הבא" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "previous weapon" -msgstr "" +msgstr "נשק קודם" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format @@ -99,7 +101,7 @@ msgstr "" #: qcsrc/client/hud/panel/infomessages.qc:111 #: qcsrc/menu/xonotic/keybinder.qc:94 msgid "server info" -msgstr "" +msgstr "מידע על השרת" #: qcsrc/client/hud/panel/infomessages.qc:124 msgid "^1Match has already begun" @@ -139,7 +141,7 @@ msgstr "" #: qcsrc/client/hud/panel/infomessages.qc:175 #: qcsrc/menu/xonotic/keybinder.qc:91 msgid "ready" -msgstr "" +msgstr "מוכן" #: qcsrc/client/hud/panel/infomessages.qc:162 #, c-format @@ -171,7 +173,7 @@ msgstr "" #: qcsrc/client/hud/panel/infomessages.qc:199 #: qcsrc/menu/xonotic/keybinder.qc:102 msgid "team menu" -msgstr "" +msgstr "תפריט צוות" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating this player:" @@ -224,7 +226,7 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:636 msgid "Continue..." -msgstr "" +msgstr "המשך..." #: qcsrc/client/hud/panel/quickmenu.qc:794 #: qcsrc/client/hud/panel/quickmenu.qc:798 @@ -499,12 +501,12 @@ msgstr "" #: qcsrc/client/hud/panel/racetimer.qc:61 msgid "Start line" -msgstr "" +msgstr "קו התחלה" #: qcsrc/client/hud/panel/racetimer.qc:63 #: qcsrc/client/hud/panel/racetimer.qc:67 msgid "Finish line" -msgstr "" +msgstr "קו סיום" #: qcsrc/client/hud/panel/racetimer.qc:65 #, c-format @@ -1009,7 +1011,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:16 #: qcsrc/menu/xonotic/dialog_uid2name.qc:15 msgid "Yes" -msgstr "" +msgstr "כן" #: qcsrc/client/hud/panel/vote.qc:127 qcsrc/menu/xonotic/dialog_firstrun.qc:83 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:21 @@ -1019,7 +1021,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:17 #: qcsrc/menu/xonotic/dialog_uid2name.qc:17 msgid "No" -msgstr "" +msgstr "לא" #: qcsrc/client/hud/panel/weapons.qc:530 msgid "Out of ammo" @@ -1031,7 +1033,7 @@ msgstr "" #: qcsrc/client/hud/panel/weapons.qc:538 msgid "Unavailable" -msgstr "" +msgstr "לא זמין" #: qcsrc/client/main.qc:1014 msgid " qu/s" @@ -1077,7 +1079,7 @@ msgstr "" #: qcsrc/client/mapvoting.qc:365 msgid "Vote for a map" -msgstr "" +msgstr "הצבע למפה" #: qcsrc/client/mapvoting.qc:382 #, c-format @@ -1180,11 +1182,11 @@ msgstr "" #: qcsrc/common/mapinfo.qh:126 msgid "Race" -msgstr "" +msgstr "מרוץ" #: qcsrc/common/mapinfo.qh:126 msgid "Race against other players to the finish line" -msgstr "" +msgstr "התחרה נגד שחקנים אחרים לקו הסיום" #: qcsrc/common/mapinfo.qh:160 msgid "Race CTS" @@ -1204,7 +1206,7 @@ msgstr "" #: qcsrc/common/mapinfo.qh:220 msgid "Capture the Flag" -msgstr "" +msgstr "תפוס את הדגל" #: qcsrc/common/mapinfo.qh:220 msgid "" @@ -1307,7 +1309,7 @@ msgstr "" #: qcsrc/common/minigames/cl_minigames_hud.qc:403 msgid "Exit Menu" -msgstr "" +msgstr "צא מהתפריט" #: qcsrc/common/minigames/cl_minigames_hud.qc:415 #: qcsrc/menu/xonotic/dialog_multiplayer.qc:16 @@ -1316,7 +1318,7 @@ msgstr "" #: qcsrc/common/minigames/cl_minigames_hud.qc:418 msgid "Join" -msgstr "" +msgstr "הצטרף" #: qcsrc/common/minigames/cl_minigames_hud.qc:489 msgid "Minigames" @@ -1344,7 +1346,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/bd.qc:1404 msgid "Next Level" -msgstr "" +msgstr "שלב הבא" #: qcsrc/common/minigames/minigame/bd.qc:1405 msgid "Restart" @@ -1357,7 +1359,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/bd.qc:1407 #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:37 msgid "Save" -msgstr "" +msgstr "שמור" #: qcsrc/common/minigames/minigame/c4.qc:373 #: qcsrc/common/minigames/minigame/pp.qc:438 @@ -1413,15 +1415,15 @@ msgstr "" #: qcsrc/common/minigames/minigame/pong.qc:651 msgid "Start Match" -msgstr "" +msgstr "התחל משחק" #: qcsrc/common/minigames/minigame/pong.qc:652 msgid "Add AI player" -msgstr "" +msgstr "הוסף שחקן מחשב" #: qcsrc/common/minigames/minigame/pong.qc:653 msgid "Remove AI player" -msgstr "" +msgstr "הסר שחקן מחשב" #: qcsrc/common/minigames/minigame/pp.qc:443 #: qcsrc/common/minigames/minigame/ttt.qc:324 @@ -1450,7 +1452,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/pp.qc:582 #: qcsrc/common/minigames/minigame/ttt.qc:665 msgid "Next Match" -msgstr "" +msgstr "משחק הבא" #: qcsrc/common/minigames/minigame/ps.qc:478 #, c-format @@ -1463,7 +1465,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/ps.qc:491 msgid "Well done, you win!" -msgstr "" +msgstr "כל הכבוד, ניצחת!" #: qcsrc/common/minigames/minigame/ps.qc:494 msgid "Jump a piece over another to capture it" @@ -1476,7 +1478,7 @@ msgstr "" #: qcsrc/common/monsters/monster/mage.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:18 msgid "Mage" -msgstr "" +msgstr "קוסם" #: qcsrc/common/monsters/monster/mage.qh:29 msgid "Mage spike" @@ -1490,7 +1492,7 @@ msgstr "" #: qcsrc/common/monsters/monster/spider.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:16 msgid "Spider" -msgstr "" +msgstr "עכביש" #: qcsrc/common/monsters/monster/spider.qh:28 msgid "Spider attack" @@ -1508,11 +1510,11 @@ msgstr "" #: qcsrc/common/monsters/monster/zombie.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:15 msgid "Zombie" -msgstr "" +msgstr "זומבי" #: qcsrc/common/mutators/mutator/buffs/all.inc:15 msgid "Ammo" -msgstr "" +msgstr "תחמושת" #: qcsrc/common/mutators/mutator/buffs/all.inc:24 msgid "Resistance" @@ -1521,7 +1523,7 @@ msgstr "" #: qcsrc/common/mutators/mutator/buffs/all.inc:33 #: qcsrc/common/mutators/mutator/instagib/items.qh:94 msgid "Speed" -msgstr "" +msgstr "מהירות" #: qcsrc/common/mutators/mutator/buffs/all.inc:43 msgid "Medic" @@ -1609,7 +1611,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:109 #: qcsrc/menu/xonotic/util.qc:775 msgid "Color:" -msgstr "" +msgstr "צבע:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 msgid "Draw damage numbers for friendly fire" @@ -1653,7 +1655,7 @@ msgstr "" #: qcsrc/common/mutators/mutator/nades/nades.qh:32 msgid "Grenade" -msgstr "" +msgstr "רימון" #: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 msgid "Heavy Machine Gun" @@ -1673,15 +1675,15 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:5 msgid "Here" -msgstr "" +msgstr "כאן" #: qcsrc/common/mutators/mutator/waypoints/all.inc:6 msgid "DANGER" -msgstr "" +msgstr "סכנה" #: qcsrc/common/mutators/mutator/waypoints/all.inc:8 msgid "Frozen!" -msgstr "" +msgstr "קפוא!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:10 msgid "Item" @@ -1775,12 +1777,12 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:39 msgid "Run here" -msgstr "" +msgstr "רוץ לכאן" #: qcsrc/common/mutators/mutator/waypoints/all.inc:45 #: qcsrc/common/mutators/mutator/waypoints/all.inc:48 msgid "Ball" -msgstr "" +msgstr "כדור" #: qcsrc/common/mutators/mutator/waypoints/all.inc:46 msgid "Ball carrier" @@ -1797,19 +1799,19 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:57 msgid "Weapon" -msgstr "" +msgstr "נשק" #: qcsrc/common/mutators/mutator/waypoints/all.inc:59 msgid "Monster" -msgstr "" +msgstr "מפלצת" #: qcsrc/common/mutators/mutator/waypoints/all.inc:61 msgid "Vehicle" -msgstr "" +msgstr "רכב" #: qcsrc/common/mutators/mutator/waypoints/all.inc:62 msgid "Intruder!" -msgstr "" +msgstr "פולש!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:64 msgid "Tagged" @@ -4331,7 +4333,7 @@ msgstr "" #: qcsrc/common/turrets/turret/plasma_weapon.qh:7 msgid "Plasma" -msgstr "" +msgstr "פלזמה" #: qcsrc/common/turrets/turret/tesla.qh:13 #: qcsrc/common/turrets/turret/tesla_weapon.qh:7 @@ -4461,7 +4463,7 @@ msgstr "" #: qcsrc/common/weapons/weapon/shotgun.qc:17 msgid "Shotgun" -msgstr "" +msgstr "שוטגן" #: qcsrc/common/weapons/weapon/tuba.qc:17 #, no-c-format @@ -4711,7 +4713,7 @@ msgstr "" #: qcsrc/menu/command/menu_cmd.qc:79 msgid "Available options:\n" -msgstr "" +msgstr "אפשרויות זמינות:\n" #: qcsrc/menu/command/menu_cmd.qc:128 msgid "Invalid command. For a list of supported commands, try menu_cmd help.\n" @@ -4744,7 +4746,7 @@ msgstr "" #: qcsrc/menu/xonotic/credits.qc:48 msgid "Website" -msgstr "" +msgstr "אתר" #: qcsrc/menu/xonotic/credits.qc:53 msgid "Stats" @@ -4924,7 +4926,7 @@ msgstr "" #: qcsrc/menu/xonotic/cvarlist.qc:93 msgid "private" -msgstr "" +msgstr "פרטי" #: qcsrc/menu/xonotic/cvarlist.qc:95 msgid "engine setting" @@ -4945,7 +4947,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_credits.qh:7 msgid "Credits" -msgstr "" +msgstr "קרדיטים" #: qcsrc/menu/xonotic/dialog_credits.qh:8 msgid "The Xonotic credits" @@ -4961,7 +4963,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_firstrun.qc:45 #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:28 msgid "Name:" -msgstr "" +msgstr "שם:" #: qcsrc/menu/xonotic/dialog_firstrun.qc:53 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:60 @@ -4982,11 +4984,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_firstrun.qc:88 msgid "Save settings" -msgstr "" +msgstr "שמור הגדרות" #: qcsrc/menu/xonotic/dialog_firstrun.qh:6 msgid "Welcome" -msgstr "" +msgstr "ברוך הבא" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:16 msgid "Ammunition display:" @@ -5020,7 +5022,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:33 #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:18 msgid "Left" -msgstr "" +msgstr "שמאל" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:32 #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:32 @@ -5031,7 +5033,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:34 #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:20 msgid "Right" -msgstr "" +msgstr "ימין" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh:6 msgid "Ammo Panel" @@ -5058,7 +5060,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:19 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:71 msgid "Center" -msgstr "" +msgstr "מרכז" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:35 msgid "Font scale:" @@ -5074,7 +5076,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:18 msgid "Chat size:" -msgstr "" +msgstr "גודל צ'אט:" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:22 msgid "Chat lifetime:" @@ -5264,15 +5266,15 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:37 msgid "Speed:" -msgstr "" +msgstr "מהירות:" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:38 msgid "Include vertical speed" -msgstr "" +msgstr "כלול מהירות אנכית" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:49 msgid "Speed unit:" -msgstr "" +msgstr "יחידת מידה למהירות:" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:51 msgid "qu/s" @@ -5296,7 +5298,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:57 msgid "Show" -msgstr "" +msgstr "הצג" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:60 msgid "Top speed" @@ -5418,7 +5420,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:15 msgid "Score:" -msgstr "" +msgstr "ניקוד:" #: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:18 msgid "Rankings:" @@ -5469,7 +5471,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:139 #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:55 msgid "Never" -msgstr "" +msgstr "לעולם לא" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:24 #, c-format @@ -5514,7 +5516,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:54 msgid "Number" -msgstr "" +msgstr "מספר" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:55 msgid "Bind" @@ -5555,14 +5557,14 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:25 #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:35 msgid "Filter:" -msgstr "" +msgstr "מסנן:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:30 #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:49 #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:44 msgid "Refresh" -msgstr "" +msgstr "רענן" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:33 #: qcsrc/menu/xonotic/dialog_settings_user.qc:30 @@ -5580,7 +5582,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:48 #: qcsrc/menu/xonotic/util.qc:767 msgid "Background:" -msgstr "" +msgstr "רקע:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:50 #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:62 @@ -5661,7 +5663,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_monstertools.qc:13 msgid "Monster:" -msgstr "" +msgstr "מפלצת:" #: qcsrc/menu/xonotic/dialog_monstertools.qc:22 #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:20 @@ -5671,7 +5673,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_monstertools.qc:23 #: qcsrc/menu/xonotic/serverlist.qc:268 msgid "Remove" -msgstr "" +msgstr "הסר" #: qcsrc/menu/xonotic/dialog_monstertools.qc:25 msgid "Move target:" @@ -5695,7 +5697,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_monstertools.qc:31 msgid "Colors:" -msgstr "" +msgstr "צבעים:" #: qcsrc/menu/xonotic/dialog_monstertools.qc:33 #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:39 @@ -5708,7 +5710,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer.qc:14 msgid "Servers" -msgstr "" +msgstr "שרתים" #: qcsrc/menu/xonotic/dialog_multiplayer.qc:15 msgid "Find servers to play on" @@ -5742,12 +5744,12 @@ msgstr "" #: qcsrc/menu/xonotic/util.qc:785 qcsrc/menu/xonotic/util.qc:794 #: qcsrc/menu/xonotic/util.qc:802 qcsrc/menu/xonotic/util.qc:814 msgid "Default" -msgstr "" +msgstr "ברירת מחדל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:48 #: qcsrc/menu/xonotic/dialog_settings_misc.qc:64 msgid "Unlimited" -msgstr "" +msgstr "לא מוגבל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:65 #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:66 @@ -5788,7 +5790,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:70 msgid "Lives:" -msgstr "" +msgstr "חיים:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:71 msgid "Laps:" @@ -5804,11 +5806,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:97 msgid "Gametype" -msgstr "" +msgstr "סוג משחק" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:102 msgid "Time limit:" -msgstr "" +msgstr "הגבלת זמן:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:104 msgid "Timelimit in minutes that when hit, will end the match" @@ -5826,7 +5828,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:107 #: qcsrc/menu/xonotic/dialog_settings_audio.qc:159 msgid "1 minute" -msgstr "" +msgstr "דקה אחת" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:124 msgid "TIMLIM^Infinite" @@ -5834,19 +5836,19 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:132 msgid "Teams:" -msgstr "" +msgstr "צוותים:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:135 msgid "2 teams" -msgstr "" +msgstr "2 צוותים" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:136 msgid "3 teams" -msgstr "" +msgstr "3 צוותים" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:137 msgid "4 teams" -msgstr "" +msgstr "4 צוותים" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:140 msgid "Player slots:" @@ -5880,15 +5882,15 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:153 msgid "Beginner" -msgstr "" +msgstr "מתחיל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:154 msgid "You will win" -msgstr "" +msgstr "אתה תנצח" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:155 msgid "You can win" -msgstr "" +msgstr "אתה יכול לנצח" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:156 msgid "You might win" @@ -5896,27 +5898,27 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:157 msgid "Advanced" -msgstr "" +msgstr "מתקדם" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:158 msgid "Expert" -msgstr "" +msgstr "מומחה" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:159 msgid "Pro" -msgstr "" +msgstr "מקצוען" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:160 msgid "Assassin" -msgstr "" +msgstr "מתנקש" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:161 msgid "Unhuman" -msgstr "" +msgstr "לא אנושי" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:162 msgid "Godlike" -msgstr "" +msgstr "דמוי אל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:178 msgid "Mutators..." @@ -5954,7 +5956,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:217 msgid "Add all" -msgstr "" +msgstr "הוסף הכל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:218 msgid "Add every available map to your selection" @@ -5962,7 +5964,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:221 msgid "Remove all" -msgstr "" +msgstr "הסר הכל" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:222 msgid "Remove all the maps from your selection" @@ -5974,20 +5976,20 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:58 msgid "Title:" -msgstr "" +msgstr "כותרת:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:64 msgid "Author:" -msgstr "" +msgstr "מחבר:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:70 msgid "Game types:" -msgstr "" +msgstr "סוגי משחק:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:93 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:296 msgid "Close" -msgstr "" +msgstr "סגור" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:96 msgid "MAP^Play" @@ -6043,7 +6045,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:75 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:282 msgid "No start weapons" -msgstr "" +msgstr "ללא נשקי התחלה" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:77 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:195 @@ -6072,7 +6074,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:89 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:229 msgid "Weapons stay" -msgstr "" +msgstr "נשקים נשארים" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:91 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:184 @@ -6128,7 +6130,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 msgid "Damage done to your enemy gets added to your own health" -msgstr "" +msgstr "נזק שנעשה לאויב שלך מתווסף לחיים שלך" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 msgid "" @@ -6181,11 +6183,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 msgid "Most weapons" -msgstr "" +msgstr "רוב הנשקים" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:260 msgid "All weapons" -msgstr "" +msgstr "כל הנשקים" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:264 msgid "Special arenas:" @@ -6254,11 +6256,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:67 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:223 msgid "Address:" -msgstr "" +msgstr "כתובת:" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:78 msgid "Info..." -msgstr "" +msgstr "מידע..." #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:79 msgid "Show more information about the currently highlighted server" @@ -6267,7 +6269,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:84 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:303 msgid "Join!" -msgstr "" +msgstr "הצטרף!" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:154 #: qcsrc/menu/xonotic/serverlist.qc:1061 @@ -6281,7 +6283,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 msgid "Official" -msgstr "" +msgstr "רשמי" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:169 msgid "N/A (auth library missing, can't connect)" @@ -6293,7 +6295,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:177 msgid "Not supported (can't connect)" -msgstr "" +msgstr "לא נתמך(לא יכול להתחבר)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:179 msgid "Not supported (won't encrypt)" @@ -6329,11 +6331,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:231 msgid "Gametype:" -msgstr "" +msgstr "סוג משחק:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:236 msgid "Map:" -msgstr "" +msgstr "מפה:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:241 msgid "Mod:" @@ -6341,16 +6343,16 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:246 msgid "Version:" -msgstr "" +msgstr "גרסה:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:251 msgid "Settings:" -msgstr "" +msgstr "הגדרות:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:258 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:290 msgid "Players:" -msgstr "" +msgstr "שחקנים:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:263 msgid "Bots:" @@ -6374,7 +6376,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh:7 msgid "Server Information" -msgstr "" +msgstr "מידע על השרת" #: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:25 msgid "Demos" @@ -6411,12 +6413,12 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:15 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:15 msgid "Do you really wish to disconnect now?" -msgstr "" +msgstr "אתה באמת רוצה להתנתק עכשיו?" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh:6 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh:6 msgid "Disconnect" -msgstr "" +msgstr "התנתק" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:13 msgid "Timing a demo will disconnect you from the current match." @@ -6444,7 +6446,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:55 msgid "Random order" -msgstr "" +msgstr "סדר אקראי" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:60 msgid "MUSICPL^Stop" @@ -6484,19 +6486,19 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:139 msgid "Reset" -msgstr "" +msgstr "אפס" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:144 msgid "Previous" -msgstr "" +msgstr "קודם" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:147 msgid "Next" -msgstr "" +msgstr "הבא" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:152 msgid "Slide show" -msgstr "" +msgstr "מצגת שקופיות" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:34 #: qcsrc/menu/xonotic/dialog_settings_audio.qc:21 @@ -6509,7 +6511,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:48 msgid "Name" -msgstr "" +msgstr "שם" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:77 msgid "Model" @@ -6537,38 +6539,38 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:145 msgid "Country" -msgstr "" +msgstr "מדינה" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:159 msgid "Gender:" -msgstr "" +msgstr "מין:" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:161 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:174 msgid "Undisclosed" -msgstr "" +msgstr "לא ידוע" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:162 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:172 msgid "Female" -msgstr "" +msgstr "נקבה" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:163 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:173 msgid "Male" -msgstr "" +msgstr "זכר" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:166 msgid "Gender" -msgstr "" +msgstr "מין" #: qcsrc/menu/xonotic/dialog_quit.qc:11 msgid "Are you sure you want to quit?" -msgstr "" +msgstr "אתה בטוח שברצונך לצאת?" #: qcsrc/menu/xonotic/dialog_quit.qc:15 msgid "Back to work..." -msgstr "" +msgstr "בחזרה לעבודה..." #: qcsrc/menu/xonotic/dialog_quit.qc:17 msgid "I got some more fragging to do!" @@ -6576,7 +6578,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_quit.qh:7 msgid "Quit the game" -msgstr "" +msgstr "צא מהמשחק" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:15 msgid "Model:" @@ -6584,15 +6586,15 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:21 msgid "Remove *" -msgstr "" +msgstr "הסר *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:23 msgid "Copy *" -msgstr "" +msgstr "העתק *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:24 msgid "Paste" -msgstr "" +msgstr "הדבק" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:26 msgid "Bone:" @@ -6692,7 +6694,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:81 msgid "Show help" -msgstr "" +msgstr "הצג עזרה" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:82 msgid "* is the object you are facing" @@ -6708,23 +6710,23 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings.qc:19 msgid "Effects" -msgstr "" +msgstr "אפקטים" #: qcsrc/menu/xonotic/dialog_settings.qc:20 msgid "Audio" -msgstr "" +msgstr "שמע" #: qcsrc/menu/xonotic/dialog_settings.qc:22 msgid "Game" -msgstr "" +msgstr "משחק" #: qcsrc/menu/xonotic/dialog_settings.qc:23 msgid "Input" -msgstr "" +msgstr "קלט" #: qcsrc/menu/xonotic/dialog_settings.qc:24 msgid "User" -msgstr "" +msgstr "משתמש" #: qcsrc/menu/xonotic/dialog_settings.qc:25 #: qcsrc/menu/xonotic/keybinder.qc:105 @@ -6733,11 +6735,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings.qh:6 msgid "Settings" -msgstr "" +msgstr "הגדרות" #: qcsrc/menu/xonotic/dialog_settings.qh:7 msgid "Change the game settings" -msgstr "" +msgstr "שנה את הגדרות המשחק" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:29 msgid "Master:" @@ -6745,7 +6747,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:35 msgid "Music:" -msgstr "" +msgstr "מוזיקה:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:43 msgid "VOL^Ambient:" @@ -6753,7 +6755,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:50 msgid "Info:" -msgstr "" +msgstr "מידע:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:57 msgid "Items:" @@ -6765,7 +6767,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:71 msgid "Player:" -msgstr "" +msgstr "שחקן:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:78 msgid "Shots:" @@ -6773,11 +6775,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:85 msgid "Voice:" -msgstr "" +msgstr "קול:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:93 msgid "Weapons:" -msgstr "" +msgstr "נשקים:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:99 msgid "New style sound attenuation" @@ -6841,7 +6843,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:123 msgid "Stereo" -msgstr "" +msgstr "סטראו" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:124 msgid "2.1" @@ -6899,7 +6901,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:149 msgid "Menu sounds" -msgstr "" +msgstr "צלילי תפריט" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:150 msgid "Play sounds when clicking menu items" @@ -6923,7 +6925,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:160 msgid "5 minutes" -msgstr "" +msgstr "5 דקות" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:161 msgid "WRN^Both" @@ -6939,17 +6941,17 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:168 msgid "Sometimes" -msgstr "" +msgstr "לפעמים" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:169 msgid "Often" -msgstr "" +msgstr "לעתים קרובות" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:170 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:141 #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:57 msgid "Always" -msgstr "" +msgstr "תמיד" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:176 msgid "Debug info about sounds" @@ -7179,7 +7181,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:169 #: qcsrc/menu/xonotic/dialog_settings_effects.qc:253 msgid "Distance:" -msgstr "" +msgstr "מרחק" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:172 msgid "Decals further away than this will not be drawn (default: 300)" @@ -7187,7 +7189,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:176 msgid "Time:" -msgstr "" +msgstr "זמן:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:179 msgid "Time in seconds before decals fade away (default: 2)" @@ -7263,7 +7265,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:212 msgid "Use normal maps" -msgstr "" +msgstr "השתמש במפות רגילות" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:213 msgid "Enable use of directional shading on textures (default: enabled)" @@ -7311,7 +7313,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:239 msgid "Particles" -msgstr "" +msgstr "חלקיקים" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:240 msgid "Spawnpoint effects" @@ -7323,7 +7325,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:246 msgid "Quality:" -msgstr "" +msgstr "איכות:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:249 msgid "" @@ -7354,11 +7356,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:81 #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:97 msgid "Size:" -msgstr "" +msgstr "גודל:" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:64 msgid "By health" -msgstr "" +msgstr "לפי חיים" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:76 msgid "Use rings to indicate weapon status" @@ -7454,7 +7456,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:74 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:124 msgid "Fontsize:" -msgstr "" +msgstr "גודל גופן:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:80 msgid "Edge offset:" @@ -7466,7 +7468,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:94 msgid "Damage" -msgstr "" +msgstr "נזק" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:96 msgid "Overlay:" @@ -7486,11 +7488,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:114 msgid "Show names above players" -msgstr "" +msgstr "הצג שמות מעל שחקנים" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:130 msgid "Max distance:" -msgstr "" +msgstr "מרחק מקסימלי:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:136 msgid "Decolorize:" @@ -7596,7 +7598,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:65 msgid "Gamemode Settings" -msgstr "" +msgstr "אפשרויות מצב משחק" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:67 msgid "Display capture times in Capture The Flag" @@ -7610,7 +7612,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_input.qc:91 #: qcsrc/menu/xonotic/dialog_settings_misc.qc:133 msgid "Other" -msgstr "" +msgstr "אחר" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:78 msgid "Display console messages in the top left corner" @@ -7654,7 +7656,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qh:7 msgid "Messages" -msgstr "" +msgstr "הודעות" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:30 msgid "Items" @@ -7695,7 +7697,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:49 #: qcsrc/menu/xonotic/serverlist.qc:767 msgid "Players" -msgstr "" +msgstr "שחקנים" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:51 msgid "Force player models to mine" @@ -7767,7 +7769,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:51 msgid "3rd person perspective" -msgstr "" +msgstr "נקודת מבט מגוף שלישי" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:55 msgid "Back distance" @@ -7912,7 +7914,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qh:7 #: qcsrc/menu/xonotic/keybinder.qc:43 msgid "Weapons" -msgstr "" +msgstr "נשקים" #: qcsrc/menu/xonotic/dialog_settings_input.qc:33 msgid "Key Bindings" @@ -7924,23 +7926,23 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_input.qc:41 msgid "Edit..." -msgstr "" +msgstr "ערוך..." #: qcsrc/menu/xonotic/dialog_settings_input.qc:47 msgid "Clear" -msgstr "" +msgstr "נקה" #: qcsrc/menu/xonotic/dialog_settings_input.qc:52 msgid "Reset all" -msgstr "" +msgstr "אפס הכל" #: qcsrc/menu/xonotic/dialog_settings_input.qc:57 msgid "Mouse" -msgstr "" +msgstr "עכבר" #: qcsrc/menu/xonotic/dialog_settings_input.qc:59 msgid "Sensitivity:" -msgstr "" +msgstr "רגישות:" #: qcsrc/menu/xonotic/dialog_settings_input.qc:61 msgid "Mouse speed multiplier" @@ -7990,7 +7992,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_input.qc:96 msgid "Automatically repeat jumping if holding jump" -msgstr "" +msgstr "המשך לקפוץ באופן אוטומטי אם מקש קפיצה לחוץ" #: qcsrc/menu/xonotic/dialog_settings_input.qc:99 msgid "Jetpack on jump:" @@ -8012,7 +8014,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_input.qc:114 #: qcsrc/menu/xonotic/dialog_settings_input.qc:119 msgid "Use joystick input" -msgstr "" +msgstr "התשתמש בקלט מג'ויסטיק" #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:31 msgid "Command when pressed:" @@ -8024,7 +8026,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:40 msgid "Cancel" -msgstr "" +msgstr "בטל" #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qh:7 msgid "User defined key bind" @@ -8047,7 +8049,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:27 msgid "Network" -msgstr "" +msgstr "רשת" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:29 msgid "Client UDP port:" @@ -8059,11 +8061,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:34 msgid "Bandwidth:" -msgstr "" +msgstr "רוחב פס:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:36 msgid "Specify your network speed" -msgstr "" +msgstr "צין את מהירות הרשת שלך" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:37 msgid "56k" @@ -8099,7 +8101,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 msgid "Downloads:" -msgstr "" +msgstr "הורדות:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:54 msgid "Maximum number of concurrent HTTP/FTP downloads" @@ -8107,7 +8109,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:56 msgid "Download speed:" -msgstr "" +msgstr "מהירות הורדה:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:69 msgid "Local latency:" @@ -8147,7 +8149,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:102 msgid "Target:" -msgstr "" +msgstr "מטרה:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:104 msgid "TRGT^Disabled" @@ -8167,7 +8169,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:129 msgid "Show frames per second" -msgstr "" +msgstr "הצג פריימים לשנייה" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:130 msgid "Show your rendered frames per second" @@ -8205,7 +8207,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:147 msgid "Enable developer mode" -msgstr "" +msgstr "אפשר מצב מפתח" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:151 msgid "Advanced settings..." @@ -8234,23 +8236,23 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:49 msgid "Type:" -msgstr "" +msgstr "סוג:" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:53 msgid "Value:" -msgstr "" +msgstr "ערך:" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:70 msgid "Description:" -msgstr "" +msgstr "תיאור:" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh:7 msgid "Advanced settings" -msgstr "" +msgstr "אפשרויות מתקדמות" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:11 msgid "Are you sure you want to reset all settings?" -msgstr "" +msgstr "האם אתה בטוח שברצונך לאפס את כל ההגדרות?" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:13 msgid "This will create a backup config in your data directory" @@ -8262,11 +8264,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_user.qc:64 msgid "Text Language" -msgstr "" +msgstr "שפת טקסט" #: qcsrc/menu/xonotic/dialog_settings_user.qc:69 msgid "Set language" -msgstr "" +msgstr "קבע שפה" #: qcsrc/menu/xonotic/dialog_settings_user.qc:74 msgid "Disable gore effects and harsh language" @@ -8288,11 +8290,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 msgid "Disconnect now" -msgstr "" +msgstr "התנתק עכשיו" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:17 msgid "Switch language" -msgstr "" +msgstr "החלף שפה" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh:6 msgid "Warning" @@ -8360,7 +8362,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_video.qc:59 msgid "Full screen" -msgstr "" +msgstr "מסך מלא" #: qcsrc/menu/xonotic/dialog_settings_video.qc:61 msgid "Vertical Synchronization" @@ -8480,7 +8482,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_video.qc:119 msgid "Brightness:" -msgstr "" +msgstr "בהירות:" #: qcsrc/menu/xonotic/dialog_settings_video.qc:121 msgid "Brightness of black (default: 0)" @@ -8488,7 +8490,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_video.qc:123 msgid "Contrast:" -msgstr "" +msgstr "ניגודיות:" #: qcsrc/menu/xonotic/dialog_settings_video.qc:125 msgid "Brightness of white (default: 1)" @@ -8610,7 +8612,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 msgid "Winner" -msgstr "" +msgstr "מנצח" #: qcsrc/menu/xonotic/dialog_teamselect.qc:32 msgid "join 'best' team (auto-select)" @@ -8622,27 +8624,27 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_teamselect.qc:37 msgid "red" -msgstr "" +msgstr "אדום" #: qcsrc/menu/xonotic/dialog_teamselect.qc:38 msgid "blue" -msgstr "" +msgstr "כחול" #: qcsrc/menu/xonotic/dialog_teamselect.qc:39 msgid "yellow" -msgstr "" +msgstr "צהוב" #: qcsrc/menu/xonotic/dialog_teamselect.qc:40 msgid "pink" -msgstr "" +msgstr "ורוד" #: qcsrc/menu/xonotic/dialog_teamselect.qc:43 msgid "spectate" -msgstr "" +msgstr "צפה" #: qcsrc/menu/xonotic/dialog_teamselect.qh:7 msgid "Team Selection" -msgstr "" +msgstr "בחירת צוות" #: qcsrc/menu/xonotic/dialog_uid2name.qc:10 msgid "Allow player statistics to use your nickname?" @@ -8666,7 +8668,7 @@ msgstr "" #: qcsrc/menu/xonotic/keybinder.qc:30 msgid "forward" -msgstr "" +msgstr "קדימה" #: qcsrc/menu/xonotic/keybinder.qc:31 msgid "backpedal" @@ -8738,7 +8740,7 @@ msgstr "" #: qcsrc/menu/xonotic/keybinder.qc:80 msgid "screen shot" -msgstr "" +msgstr "צילום מסך" #: qcsrc/menu/xonotic/keybinder.qc:81 msgid "maximize radar" @@ -8770,11 +8772,11 @@ msgstr "" #: qcsrc/menu/xonotic/keybinder.qc:89 msgid "vote YES" -msgstr "" +msgstr "הצבע כן" #: qcsrc/menu/xonotic/keybinder.qc:90 msgid "vote NO" -msgstr "" +msgstr "הצבע לא" #: qcsrc/menu/xonotic/keybinder.qc:93 msgid "Client" @@ -8786,11 +8788,11 @@ msgstr "" #: qcsrc/menu/xonotic/keybinder.qc:96 msgid "disconnect" -msgstr "" +msgstr "התנתק" #: qcsrc/menu/xonotic/keybinder.qc:97 msgid "quit" -msgstr "" +msgstr "צא" #: qcsrc/menu/xonotic/keybinder.qc:101 msgid "auto-join team" @@ -8856,7 +8858,7 @@ msgstr "" #: qcsrc/menu/xonotic/serverlist.qc:763 msgid "Ping" -msgstr "" +msgstr "פינג" #: qcsrc/menu/xonotic/serverlist.qc:764 msgid "Hostname" @@ -8864,11 +8866,11 @@ msgstr "" #: qcsrc/menu/xonotic/serverlist.qc:765 msgid "Map" -msgstr "" +msgstr "מפה" #: qcsrc/menu/xonotic/serverlist.qc:766 msgid "Type" -msgstr "" +msgstr "סוג" #: qcsrc/menu/xonotic/serverlist.qc:1060 #, c-format @@ -9004,10 +9006,13 @@ msgid "" "texture memory usage, but make the textures appear very blurry. (default: " "good)" msgstr "" +"שנה את החדות של טקסטורות.\n" +"הנמכה תגרום לצמצום יעיל של זיכרון שמשומש על ידי טקסטורות, אבל יגרום " +"לטקסטורות להראות מאוד מטושטשות. (ברירת מחדל: טוב)" #: qcsrc/menu/xonotic/slider_resolution.qc:115 msgid "Screen resolution" -msgstr "" +msgstr "רזולוציית מסך" #: qcsrc/menu/xonotic/slider_sbfadetime.qc:13 msgid "PART^Slow" @@ -9023,51 +9028,51 @@ msgstr "" #: qcsrc/menu/xonotic/statslist.qc:29 msgid "January" -msgstr "" +msgstr "ינואר" #: qcsrc/menu/xonotic/statslist.qc:30 msgid "February" -msgstr "" +msgstr "פברואר" #: qcsrc/menu/xonotic/statslist.qc:31 msgid "March" -msgstr "" +msgstr "מרץ" #: qcsrc/menu/xonotic/statslist.qc:32 msgid "April" -msgstr "" +msgstr "אפריל" #: qcsrc/menu/xonotic/statslist.qc:33 msgid "May" -msgstr "" +msgstr "מאי" #: qcsrc/menu/xonotic/statslist.qc:34 msgid "June" -msgstr "" +msgstr "יוני" #: qcsrc/menu/xonotic/statslist.qc:35 msgid "July" -msgstr "" +msgstr "יולי" #: qcsrc/menu/xonotic/statslist.qc:36 msgid "August" -msgstr "" +msgstr "אוגוסט" #: qcsrc/menu/xonotic/statslist.qc:37 msgid "September" -msgstr "" +msgstr "ספטמבר" #: qcsrc/menu/xonotic/statslist.qc:38 msgid "October" -msgstr "" +msgstr "אוקטובר" #: qcsrc/menu/xonotic/statslist.qc:39 msgid "November" -msgstr "" +msgstr "נובמבר" #: qcsrc/menu/xonotic/statslist.qc:40 msgid "December" -msgstr "" +msgstr "דצמבר" #: qcsrc/menu/xonotic/statslist.qc:96 msgid "Joined:" @@ -9075,7 +9080,7 @@ msgstr "" #: qcsrc/menu/xonotic/statslist.qc:103 msgid "Last_Seen:" -msgstr "" +msgstr "נראה_לאחרונה:" #: qcsrc/menu/xonotic/statslist.qc:110 msgid "Time_Played:" @@ -9144,7 +9149,7 @@ msgstr "" #: qcsrc/menu/xonotic/util.qc:780 msgid "Use default" -msgstr "" +msgstr "השתמש בברירת מחדל" #: qcsrc/menu/xonotic/util.qc:800 msgid "Team Color:" diff --git a/common.hu.po b/common.hu.po index 605d51a6fc..2951cdd5f5 100644 --- a/common.hu.po +++ b/common.hu.po @@ -5,15 +5,17 @@ # Translators: # Ákos RUSZKAI, 2012 # divVerent , 2011 +# MmAaXx500 , 2018 # Peter Ferenczy , 2017 # divVerent , 2011 +# MmAaXx500 , 2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-19 19:55+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-08-26 13:48+0000\n" +"Last-Translator: MmAaXx500 \n" "Language-Team: Hungarian (http://www.transifex.com/team-xonotic/xonotic/" "language/hu/)\n" "Language: hu\n" @@ -380,12 +382,12 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:823 #: qcsrc/client/hud/panel/quickmenu.qc:860 msgid "QMCMD^Settings" -msgstr "" +msgstr "QMCMD^Beállítások" #: qcsrc/client/hud/panel/quickmenu.qc:824 #: qcsrc/client/hud/panel/quickmenu.qc:831 msgid "QMCMD^View/HUD settings" -msgstr "" +msgstr "QMCMD^Nézet/HUD beállítások" #: qcsrc/client/hud/panel/quickmenu.qc:825 msgid "QMCMD^3rd person view" @@ -397,49 +399,49 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:827 msgid "QMCMD^Names above players" -msgstr "" +msgstr "QMCMD^Nevek a játékosok fölött" #: qcsrc/client/hud/panel/quickmenu.qc:828 msgid "QMCMD^Crosshair per weapon" -msgstr "" +msgstr "QMCMD^Célkereszt fegyverenként" #: qcsrc/client/hud/panel/quickmenu.qc:829 msgid "QMCMD^FPS" -msgstr "" +msgstr "QMCMD^FPS" #: qcsrc/client/hud/panel/quickmenu.qc:830 msgid "QMCMD^Net graph" -msgstr "" +msgstr "QMCMD^Hálózati grafikon" #: qcsrc/client/hud/panel/quickmenu.qc:833 #: qcsrc/client/hud/panel/quickmenu.qc:836 msgid "QMCMD^Sound settings" -msgstr "" +msgstr "Hang beállítások" #: qcsrc/client/hud/panel/quickmenu.qc:834 msgid "QMCMD^Hit sound" -msgstr "" +msgstr "QMCMD^Találat hang" #: qcsrc/client/hud/panel/quickmenu.qc:835 msgid "QMCMD^Chat sound" -msgstr "" +msgstr "QMCMD^Chat hang" #: qcsrc/client/hud/panel/quickmenu.qc:840 #: qcsrc/client/hud/panel/quickmenu.qc:844 msgid "QMCMD^Spectator camera" -msgstr "" +msgstr "QMCMD^Szemlélő kamera" #: qcsrc/client/hud/panel/quickmenu.qc:841 msgid "QMCMD^1st person" -msgstr "" +msgstr "QMCMD^Első személy" #: qcsrc/client/hud/panel/quickmenu.qc:842 msgid "QMCMD^3rd person around player" -msgstr "" +msgstr "QMCMD^Harmadik személy a játékos körül" #: qcsrc/client/hud/panel/quickmenu.qc:843 msgid "QMCMD^3rd person behind" -msgstr "" +msgstr "QMCMD^Harmadik személy mögött" #: qcsrc/client/hud/panel/quickmenu.qc:849 #: qcsrc/client/hud/panel/quickmenu.qc:854 @@ -448,11 +450,11 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:850 msgid "QMCMD^Increase speed" -msgstr "" +msgstr "QMCMD^Sebesség növelése" #: qcsrc/client/hud/panel/quickmenu.qc:851 msgid "QMCMD^Decrease speed" -msgstr "" +msgstr "QMCMD^Sebesség csökkentése" #: qcsrc/client/hud/panel/quickmenu.qc:852 msgid "QMCMD^Wall collision off" @@ -464,36 +466,36 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:857 msgid "QMCMD^Fullscreen" -msgstr "" +msgstr "QMCMD^Teljes képernyő" #: qcsrc/client/hud/panel/quickmenu.qc:859 msgid "QMCMD^Translate chat messages" -msgstr "" +msgstr "QMCMD^Chat üzenetek lefordítása" #: qcsrc/client/hud/panel/quickmenu.qc:862 #: qcsrc/client/hud/panel/quickmenu.qc:872 msgid "QMCMD^Call a vote" -msgstr "" +msgstr "QMCMD^Szavazás indítása" #: qcsrc/client/hud/panel/quickmenu.qc:863 msgid "QMCMD^Restart the map" -msgstr "" +msgstr "QMCMD^Játék újraindítása" #: qcsrc/client/hud/panel/quickmenu.qc:864 msgid "QMCMD^End match" -msgstr "" +msgstr "QMCMD^Játék vége" #: qcsrc/client/hud/panel/quickmenu.qc:867 msgid "QMCMD^Reduce match time" -msgstr "" +msgstr "QMCMD^Játékidő csökkentése" #: qcsrc/client/hud/panel/quickmenu.qc:868 msgid "QMCMD^Extend match time" -msgstr "" +msgstr "QMCMD^Játékidő csökkentése" #: qcsrc/client/hud/panel/quickmenu.qc:871 msgid "QMCMD^Shuffle teams" -msgstr "" +msgstr "QMCMD^Csapatok összekeverése" #: qcsrc/client/hud/panel/racetimer.qc:37 #, c-format @@ -561,7 +563,7 @@ msgstr "megsemmisítve" #: qcsrc/client/hud/panel/scoreboard.qc:84 msgid "SCO^damage" -msgstr "" +msgstr "SCO^sérülés" #: qcsrc/client/hud/panel/scoreboard.qc:85 msgid "SCO^dmgtaken" @@ -621,7 +623,7 @@ msgstr "Név" #: qcsrc/client/hud/panel/scoreboard.qc:99 msgid "SCO^sum" -msgstr "" +msgstr "SCO^össz" #: qcsrc/client/hud/panel/scoreboard.qc:100 msgid "SCO^nick" @@ -931,7 +933,7 @@ msgstr "Feldezett titkok:" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Capture time rankings" -msgstr "" +msgstr "Célbaérési idő rangsor" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Rankings" @@ -1002,7 +1004,7 @@ msgstr " amíg valaki ^3%s %s^7 -ig nem vezeti a mezőnyt." #: qcsrc/client/hud/panel/scoreboard.qc:1688 #, c-format msgid "^1Respawning in ^3%s^1..." -msgstr "" +msgstr "^1Respawning: ^3%s^1..." #: qcsrc/client/hud/panel/scoreboard.qc:1698 #, c-format diff --git a/common.kk@Cyrl.po b/common.kk@Cyrl.po index 60d4b36c96..6738cdd3ec 100644 --- a/common.kk@Cyrl.po +++ b/common.kk@Cyrl.po @@ -20,7 +20,7 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" +"Plural-Forms: nplurals=2; plural=(n!=1);\n" #: qcsrc/client/hud/hud_config.qc:239 #, c-format diff --git a/common.ko.po b/common.ko.po index 0407c66068..4d65b27123 100644 --- a/common.ko.po +++ b/common.ko.po @@ -4,9 +4,10 @@ # # Translators: # Jisoo Lim , 2017 -# Kyf Lee (coughingmouse) , 2016 -# Kyf Lee (coughingmouse) , 2016-2017 -# Kyf Lee (coughingmouse) , 2016-2017 +# Kyf Lee , 2016 +# Kyf Lee , 2016-2017 +# Kyf Lee , 2016-2017 +# Kyf Lee , 2016 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" diff --git a/common.kw.po b/common.kw.po index 33fbaf5be4..7562f10ba6 100644 --- a/common.kw.po +++ b/common.kw.po @@ -3,22 +3,23 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: -# Nicky Rowe , 2016 +# Nicky Rowe , 2016,2018 +# Nicky Rowe , 2016 +# Nicky Rowe , 2016 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-22 11:16+0000\n" -"Last-Translator: divVerent \n" +"PO-Revision-Date: 2018-04-28 07:53+0000\n" +"Last-Translator: Nicky Rowe \n" "Language-Team: Cornish (http://www.transifex.com/team-xonotic/xonotic/" "language/kw/)\n" "Language: kw\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=4; plural=(n==1) ? 0 : (n==2) ? 1 : (n == 3) ? 2 : " -"3;\n" +"Plural-Forms: nplurals=3; plural=(n == 1 ? 0 : n == 2 ? 1 : 2);\n" #: qcsrc/client/hud/hud_config.qc:239 #, c-format @@ -56,7 +57,7 @@ msgstr "^1Gweskewgh ^3%s^1 rag mires" #: qcsrc/client/hud/panel/infomessages.qc:100 #: qcsrc/menu/xonotic/keybinder.qc:40 msgid "primary fire" -msgstr "" +msgstr "tenn kensa" #: qcsrc/client/hud/panel/infomessages.qc:102 #, c-format @@ -66,12 +67,12 @@ msgstr "^1Gweskewgh ^3%s^1 po ^3%s^1 rag an gwarier nessa po kyns" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "next weapon" -msgstr "" +msgstr "arv nessa" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "previous weapon" -msgstr "" +msgstr "arv gens" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format @@ -81,17 +82,17 @@ msgstr "^1Usyewgh ^3%s^1 po ^3%s^1 rag chanjya an tooth" #: qcsrc/client/hud/panel/infomessages.qc:108 #, c-format msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" -msgstr "" +msgstr "^1Gweskewgh ^3%s^1 aspia, ^3%s^1 dhe janjya modh an kamera" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/common/vehicles/cl_vehicles.qc:192 msgid "drop weapon" -msgstr "" +msgstr "droppya an arv" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/menu/xonotic/keybinder.qc:41 msgid "secondary fire" -msgstr "" +msgstr "tenn nessa" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format @@ -101,7 +102,7 @@ msgstr "^1Gweskewgh ^3%s^1 rag kedhlow an modh gwari" #: qcsrc/client/hud/panel/infomessages.qc:111 #: qcsrc/menu/xonotic/keybinder.qc:94 msgid "server info" -msgstr "" +msgstr "kedhlow an servyer" #: qcsrc/client/hud/panel/infomessages.qc:124 msgid "^1Match has already begun" @@ -120,7 +121,7 @@ msgstr "^1Gweskewgh ^3%s^1 dhe junya" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 msgid "jump" -msgstr "" +msgstr "lamma" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format @@ -141,7 +142,7 @@ msgstr "%sGweskewgh ^3%s%s rag diwedha an tommheans" #: qcsrc/client/hud/panel/infomessages.qc:175 #: qcsrc/menu/xonotic/keybinder.qc:91 msgid "ready" -msgstr "" +msgstr "parys" #: qcsrc/client/hud/panel/infomessages.qc:162 #, c-format @@ -173,15 +174,15 @@ msgstr " Gweskewgh ^3%s%s rag kompeshe" #: qcsrc/client/hud/panel/infomessages.qc:199 #: qcsrc/menu/xonotic/keybinder.qc:102 msgid "team menu" -msgstr "" +msgstr "rol an para" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating this player:" -msgstr "" +msgstr "^1Owth aspia an gwarier ma:" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating you:" -msgstr "" +msgstr "^1Orth dha aspia:" #: qcsrc/client/hud/panel/infomessages.qc:225 msgid "^7Press ^3ESC ^7to show HUD options." @@ -340,7 +341,7 @@ msgstr "QMCMD^doger an baner ledhys (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier, icon" -msgstr "" +msgstr "QMCMD^a ladhas doger an baner, arwodhik" #: qcsrc/client/hud/panel/quickmenu.qc:815 #, c-format @@ -353,11 +354,11 @@ msgstr "QMCMD^baner droppyes, arwodhik" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^drop weapon, icon" -msgstr "" +msgstr "QMCMD^droppya an arv, arwodhik" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" -msgstr "" +msgstr "QMCMD^a dhroppyas an arv %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^drop flag/key, icon" @@ -555,7 +556,7 @@ msgstr "SCO^distruys" #: qcsrc/client/hud/panel/scoreboard.qc:84 msgid "SCO^damage" -msgstr "" +msgstr "SCO^damach" #: qcsrc/client/hud/panel/scoreboard.qc:85 msgid "SCO^dmgtaken" @@ -655,7 +656,7 @@ msgstr "SCO^teudhys" #: qcsrc/client/hud/panel/scoreboard.qc:109 msgid "SCO^rounds won" -msgstr "" +msgstr "SCO^rondys gwaynyes" #: qcsrc/client/hud/panel/scoreboard.qc:110 msgid "SCO^score" @@ -702,7 +703,7 @@ msgstr "Aswonys yw an henwyn skrifva a sew (nyns yw an kas aswonys):\n" #: qcsrc/client/hud/panel/scoreboard.qc:301 msgid "You can use a ^3|^7 to start the right-aligned fields.\n" -msgstr "" +msgstr "Ty a yll usya ^3|^7 rag dalleth an parkow alinys a-dhyghow.\n" #: qcsrc/client/hud/panel/scoreboard.qc:304 msgid "^3name^7 or ^3nick^7 Name of a player\n" @@ -718,7 +719,7 @@ msgstr "^3pl^7 Fardelligow kellys\n" #: qcsrc/client/hud/panel/scoreboard.qc:307 msgid "^3elo^7 Player ELO\n" -msgstr "" +msgstr "^3elo^7 ELO an gwarier\n" #: qcsrc/client/hud/panel/scoreboard.qc:308 msgid "^3kills^7 Number of kills\n" @@ -851,7 +852,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:335 msgid "^3score^7 Total score\n" -msgstr "" +msgstr "^3score^7 Skor dien\n" #: qcsrc/client/hud/panel/scoreboard.qc:338 msgid "" @@ -928,7 +929,7 @@ msgstr "Kevrinyow diskudhys:" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Capture time rankings" -msgstr "" +msgstr "Renkyow an termyn sesya" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Rankings" @@ -942,12 +943,12 @@ msgstr "Bord an skoryow" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format msgid "Speed award: %d%s ^7(%s^7)" -msgstr "" +msgstr "Powas tooth: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1588 #, c-format msgid "All-time fastest: %d%s ^7(%s^7)" -msgstr "" +msgstr "An kreffa oll-dermyn: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1604 #, c-format @@ -1039,7 +1040,7 @@ msgstr "^1Restra an HUD" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:16 #: qcsrc/menu/xonotic/dialog_uid2name.qc:15 msgid "Yes" -msgstr "" +msgstr "Ya" #: qcsrc/client/hud/panel/vote.qc:127 qcsrc/menu/xonotic/dialog_firstrun.qc:83 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:21 @@ -1049,7 +1050,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:17 #: qcsrc/menu/xonotic/dialog_uid2name.qc:17 msgid "No" -msgstr "" +msgstr "Na" #: qcsrc/client/hud/panel/weapons.qc:530 msgid "Out of ammo" @@ -1139,7 +1140,7 @@ msgstr "Euryer granaden" #: qcsrc/client/view.qc:1385 msgid "Capture progress" -msgstr "" +msgstr "Spedyans ow sesya" #: qcsrc/client/view.qc:1390 msgid "Revival progress" @@ -1160,7 +1161,7 @@ msgstr "Lader pelyow" #: qcsrc/common/items/item/armor.qh:111 msgid "Big armor" -msgstr "" +msgstr "Arvwisk bras" #: qcsrc/common/items/item/armor.qh:147 msgid "Mega armor" @@ -1168,7 +1169,7 @@ msgstr "Arvwisk mega" #: qcsrc/common/items/item/health.qh:111 msgid "Big health" -msgstr "" +msgstr "Yeghes bras" #: qcsrc/common/items/item/health.qh:147 msgid "Mega health" @@ -1308,6 +1309,8 @@ msgid "" "Kill enemies to freeze them, stand next to frozen teammates to revive them; " "freeze all enemies to win" msgstr "" +"Ladh eskerens rag aga rewi, sav ryb dha bara rag aga dasserghi; rew pub " +"eskar rag gwaynya" #: qcsrc/common/mapinfo.qh:446 msgid "Hold the ball to get points for kills" @@ -1514,135 +1517,135 @@ msgstr "Lemmewgh darn a-ugh darn aral rag y sesya" #: qcsrc/common/minigames/minigame/ttt.qc:666 msgid "Single Player" -msgstr "" +msgstr "Unn gwarier" #: qcsrc/common/monsters/monster/mage.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:18 msgid "Mage" -msgstr "" +msgstr "Huder" #: qcsrc/common/monsters/monster/mage.qh:29 msgid "Mage spike" -msgstr "" +msgstr "Spik an huder" #: qcsrc/common/monsters/monster/shambler.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:17 msgid "Shambler" -msgstr "" +msgstr "Shambler" #: qcsrc/common/monsters/monster/spider.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:16 msgid "Spider" -msgstr "" +msgstr "Kevnisen" #: qcsrc/common/monsters/monster/spider.qh:28 msgid "Spider attack" -msgstr "" +msgstr "Omsettyans kevnisen" #: qcsrc/common/monsters/monster/wyvern.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:19 msgid "Wyvern" -msgstr "" +msgstr "Dragon" #: qcsrc/common/monsters/monster/wyvern.qh:28 msgid "Wyvern attack" -msgstr "" +msgstr "Omsettyans dragon" #: qcsrc/common/monsters/monster/zombie.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:15 msgid "Zombie" -msgstr "" +msgstr "Zombi" #: qcsrc/common/mutators/mutator/buffs/all.inc:15 msgid "Ammo" -msgstr "" +msgstr "Daffar ladhva" #: qcsrc/common/mutators/mutator/buffs/all.inc:24 msgid "Resistance" -msgstr "" +msgstr "Defens" #: qcsrc/common/mutators/mutator/buffs/all.inc:33 #: qcsrc/common/mutators/mutator/instagib/items.qh:94 msgid "Speed" -msgstr "" +msgstr "Tooth" #: qcsrc/common/mutators/mutator/buffs/all.inc:43 msgid "Medic" -msgstr "" +msgstr "Medhek" #: qcsrc/common/mutators/mutator/buffs/all.inc:54 msgid "Bash" -msgstr "" +msgstr "Kronkya" #: qcsrc/common/mutators/mutator/buffs/all.inc:62 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:85 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:177 msgid "Vampire" -msgstr "" +msgstr "Vampir" #: qcsrc/common/mutators/mutator/buffs/all.inc:70 msgid "Disability" -msgstr "" +msgstr "Anles" #: qcsrc/common/mutators/mutator/buffs/all.inc:78 msgid "Vengeance" -msgstr "" +msgstr "Venjyans" #: qcsrc/common/mutators/mutator/buffs/all.inc:86 msgid "Jump" -msgstr "" +msgstr "Lamma" #: qcsrc/common/mutators/mutator/buffs/all.inc:95 msgid "Invisible" -msgstr "" +msgstr "Anweladow" #: qcsrc/common/mutators/mutator/buffs/all.inc:104 msgid "Inferno" -msgstr "" +msgstr "Inferno" #: qcsrc/common/mutators/mutator/buffs/all.inc:112 msgid "Swapper" -msgstr "" +msgstr "Keschanj" #: qcsrc/common/mutators/mutator/buffs/all.inc:120 msgid "Magnet" -msgstr "" +msgstr "Tennven" #: qcsrc/common/mutators/mutator/buffs/all.inc:128 msgid "Luck" -msgstr "" +msgstr "Chons" #: qcsrc/common/mutators/mutator/buffs/all.inc:136 msgid "Flight" -msgstr "" +msgstr "Neyj" #: qcsrc/common/mutators/mutator/buffs/buffs.qh:7 msgid "Buff" -msgstr "" +msgstr "Bonus" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:8 msgid "Damage text" -msgstr "" +msgstr "Tekst damach" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:18 msgid "Draw damage numbers" -msgstr "" +msgstr "Delinya niverow damach" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 msgid "Font size minimum:" -msgstr "" +msgstr "Myns font an lyha:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 msgid "Font size maximum:" -msgstr "" +msgstr "Myns font an ughella:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 msgid "Accumulate range:" -msgstr "" +msgstr "Kuntel towlhys:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:35 msgid "Lifetime:" -msgstr "" +msgstr "Termyn bewnans:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:40 #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:50 @@ -1656,11 +1659,11 @@ msgstr "Liw:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 msgid "Draw damage numbers for friendly fire" -msgstr "" +msgstr "Delinya niverow damach rag tenn kowethek" #: qcsrc/common/mutators/mutator/instagib/items.qh:56 msgid "Extra life" -msgstr "" +msgstr "Bewnans keworransel" #: qcsrc/common/mutators/mutator/instagib/items.qh:75 msgid "Invisibility" @@ -1668,51 +1671,51 @@ msgstr "Anweladowder" #: qcsrc/common/mutators/mutator/nades/nades.inc:18 msgid "Napalm grenade" -msgstr "" +msgstr "Grenad napalm" #: qcsrc/common/mutators/mutator/nades/nades.inc:26 msgid "Ice grenade" -msgstr "" +msgstr "Grenad rew" #: qcsrc/common/mutators/mutator/nades/nades.inc:34 msgid "Translocate grenade" -msgstr "" +msgstr "Grenad treustesedha" #: qcsrc/common/mutators/mutator/nades/nades.inc:42 msgid "Spawn grenade" -msgstr "" +msgstr "Grenad dinythi" #: qcsrc/common/mutators/mutator/nades/nades.inc:50 msgid "Heal grenade" -msgstr "" +msgstr "Grenad sawya" #: qcsrc/common/mutators/mutator/nades/nades.inc:58 msgid "Monster grenade" -msgstr "" +msgstr "Grenad euthvil" #: qcsrc/common/mutators/mutator/nades/nades.inc:66 msgid "Entrap grenade" -msgstr "" +msgstr "Grenad maglenna" #: qcsrc/common/mutators/mutator/nades/nades.qh:32 msgid "Grenade" -msgstr "" +msgstr "Grenad" #: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 msgid "Heavy Machine Gun" -msgstr "" +msgstr "Gonn Jynn Poos" #: qcsrc/common/mutators/mutator/overkill/rpc.qh:17 msgid "Rocket Propelled Chainsaw" -msgstr "" +msgstr "Hesken gadon rocket" #: qcsrc/common/mutators/mutator/waypoints/all.inc:3 msgid "Waypoint" -msgstr "" +msgstr "Fordhva" #: qcsrc/common/mutators/mutator/waypoints/all.inc:4 msgid "Help me!" -msgstr "" +msgstr "Gweres vy!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:5 msgid "Here" @@ -1720,7 +1723,7 @@ msgstr "Omma" #: qcsrc/common/mutators/mutator/waypoints/all.inc:6 msgid "DANGER" -msgstr "" +msgstr "PERYL" #: qcsrc/common/mutators/mutator/waypoints/all.inc:8 msgid "Frozen!" @@ -1728,11 +1731,11 @@ msgstr "Rewys!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:10 msgid "Item" -msgstr "" +msgstr "Tra" #: qcsrc/common/mutators/mutator/waypoints/all.inc:12 msgid "Checkpoint" -msgstr "" +msgstr "Checkva" #: qcsrc/common/mutators/mutator/waypoints/all.inc:13 #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 @@ -1747,51 +1750,51 @@ msgstr "Dallethva" #: qcsrc/common/mutators/mutator/waypoints/all.inc:17 msgid "Defend" -msgstr "" +msgstr "Difres" #: qcsrc/common/mutators/mutator/waypoints/all.inc:18 msgid "Destroy" -msgstr "" +msgstr "Distrui" #: qcsrc/common/mutators/mutator/waypoints/all.inc:19 msgid "Push" -msgstr "" +msgstr "Herdhya" #: qcsrc/common/mutators/mutator/waypoints/all.inc:21 msgid "Flag carrier" -msgstr "" +msgstr "Doger an baner" #: qcsrc/common/mutators/mutator/waypoints/all.inc:22 msgid "Enemy carrier" -msgstr "" +msgstr "Doger an anvi" #: qcsrc/common/mutators/mutator/waypoints/all.inc:23 msgid "Dropped flag" -msgstr "" +msgstr "Baner droppyes" #: qcsrc/common/mutators/mutator/waypoints/all.inc:24 msgid "White base" -msgstr "" +msgstr "Selva wynn" #: qcsrc/common/mutators/mutator/waypoints/all.inc:25 msgid "Red base" -msgstr "" +msgstr "Selva rudh" #: qcsrc/common/mutators/mutator/waypoints/all.inc:26 msgid "Blue base" -msgstr "" +msgstr "Selva las" #: qcsrc/common/mutators/mutator/waypoints/all.inc:27 msgid "Yellow base" -msgstr "" +msgstr "Selva velyn" #: qcsrc/common/mutators/mutator/waypoints/all.inc:28 msgid "Pink base" -msgstr "" +msgstr "Selva wynnrudh" #: qcsrc/common/mutators/mutator/waypoints/all.inc:29 msgid "Return flag here" -msgstr "" +msgstr "Daskor an baner arta" #: qcsrc/common/mutators/mutator/waypoints/all.inc:31 #: qcsrc/common/mutators/mutator/waypoints/all.inc:32 @@ -1802,11 +1805,11 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:52 #: qcsrc/common/mutators/mutator/waypoints/all.inc:53 msgid "Control point" -msgstr "" +msgstr "Kontrolva" #: qcsrc/common/mutators/mutator/waypoints/all.inc:37 msgid "Dropped key" -msgstr "" +msgstr "Alhwedh droppyes" #: qcsrc/common/mutators/mutator/waypoints/all.inc:38 #: qcsrc/common/mutators/mutator/waypoints/all.inc:40 @@ -1814,11 +1817,11 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:42 #: qcsrc/common/mutators/mutator/waypoints/all.inc:43 msgid "Key carrier" -msgstr "" +msgstr "Deger an alhwedh" #: qcsrc/common/mutators/mutator/waypoints/all.inc:39 msgid "Run here" -msgstr "" +msgstr "Poon bys yn omma" #: qcsrc/common/mutators/mutator/waypoints/all.inc:45 #: qcsrc/common/mutators/mutator/waypoints/all.inc:48 @@ -1827,54 +1830,55 @@ msgstr "Pel" #: qcsrc/common/mutators/mutator/waypoints/all.inc:46 msgid "Ball carrier" -msgstr "" +msgstr "Deger an bel" #: qcsrc/common/mutators/mutator/waypoints/all.inc:49 msgid "Goal" -msgstr "" +msgstr "Gol" #: qcsrc/common/mutators/mutator/waypoints/all.inc:54 #: qcsrc/common/mutators/mutator/waypoints/all.inc:55 msgid "Generator" -msgstr "" +msgstr "Dinythor" #: qcsrc/common/mutators/mutator/waypoints/all.inc:57 msgid "Weapon" -msgstr "" +msgstr "Arv" #: qcsrc/common/mutators/mutator/waypoints/all.inc:59 msgid "Monster" -msgstr "" +msgstr "Euthvil" #: qcsrc/common/mutators/mutator/waypoints/all.inc:61 msgid "Vehicle" -msgstr "" +msgstr "Karr" #: qcsrc/common/mutators/mutator/waypoints/all.inc:62 msgid "Intruder!" -msgstr "" +msgstr "Kammdremener!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:64 msgid "Tagged" -msgstr "" +msgstr "Taggys" #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:651 #: qcsrc/common/turrets/cl_turrets.qc:120 msgid "Spam" -msgstr "" +msgstr "Spam" #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:655 #, c-format msgid "%s needing help!" -msgstr "" +msgstr "%s ow kovyn gweres!" #: qcsrc/common/net_notice.qc:87 msgid "^1Server notices:" -msgstr "" +msgstr "^1Notyansow an servyer:" #: qcsrc/common/notifications/all.inc:239 msgid "^F4NOTE: ^BGSpectator chat is not sent to players during the match" msgstr "" +"^F4NOTEN: ^BGNyns yw klap an viroryon danvenys dhe warioryon dres an fytt" #: qcsrc/common/notifications/all.inc:241 #, c-format diff --git a/common.ms.po b/common.ms.po new file mode 100644 index 0000000000..f50708e104 --- /dev/null +++ b/common.ms.po @@ -0,0 +1,9172 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Muhammad Nur Hidayat Yasuyoshi , 2018 +msgid "" +msgstr "" +"Project-Id-Version: Xonotic\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-09 00:35+0200\n" +"PO-Revision-Date: 2018-05-29 05:44+0000\n" +"Last-Translator: Muhammad Nur Hidayat Yasuyoshi \n" +"Language-Team: Malay (http://www.transifex.com/team-xonotic/xonotic/language/" +"ms/)\n" +"Language: ms\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=1; plural=0;\n" + +#: qcsrc/client/hud/hud_config.qc:239 +#, c-format +msgid "^2Successfully exported to %s! (Note: It's saved in data/data/)\n" +msgstr "^2Berjaya dieksport ke %s! (Nota: Ia disimpan di data/data/)\n" + +#: qcsrc/client/hud/hud_config.qc:243 +#, c-format +msgid "^1Couldn't write to %s\n" +msgstr "^1Tidak boleh menulis ke %s\n" + +#: qcsrc/client/hud/panel/chat.qc:82 +msgid "^3Player^7: This is the chat area." +msgstr "^3Pemain^7: Ini kawasan sembang." + +#: qcsrc/client/hud/panel/engineinfo.qc:69 +#, c-format +msgid "FPS: %.*f" +msgstr "FPS: %.*f" + +#: qcsrc/client/hud/panel/infomessages.qc:87 +msgid "^1Observing" +msgstr "^1Memerhati" + +#: qcsrc/client/hud/panel/infomessages.qc:89 +#, c-format +msgid "^1Spectating: ^7%s" +msgstr "^1Menonton: ^7%s" + +#: qcsrc/client/hud/panel/infomessages.qc:100 +#, c-format +msgid "^1Press ^3%s^1 to spectate" +msgstr "^1Tekan ^3%s^1 untuk menonton" + +#: qcsrc/client/hud/panel/infomessages.qc:100 +#: qcsrc/menu/xonotic/keybinder.qc:40 +msgid "primary fire" +msgstr "senjata api utama" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#, c-format +msgid "^1Press ^3%s^1 or ^3%s^1 for next or previous player" +msgstr "^1Tekan ^3%s^1 atau ^3%s^1 untuk pemain seterusnya atau sebelumnya" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#: qcsrc/client/hud/panel/infomessages.qc:106 +msgid "next weapon" +msgstr "senjata seterusnya" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#: qcsrc/client/hud/panel/infomessages.qc:106 +msgid "previous weapon" +msgstr "senjata sebelumnya" + +#: qcsrc/client/hud/panel/infomessages.qc:106 +#, c-format +msgid "^1Use ^3%s^1 or ^3%s^1 to change the speed" +msgstr "^1Gunakan ^3%s^1 atau ^3%s^1 untuk mengubah kelajuan" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#, c-format +msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" +msgstr "^1Tekan ^3%s^1 untuk memerhati, ^3%s^1 untuk tukar mod kamera" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#: qcsrc/common/vehicles/cl_vehicles.qc:192 +msgid "drop weapon" +msgstr "jatuhkan senjata" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#: qcsrc/menu/xonotic/keybinder.qc:41 +msgid "secondary fire" +msgstr "senjata api kedua" + +#: qcsrc/client/hud/panel/infomessages.qc:111 +#, c-format +msgid "^1Press ^3%s^1 for gamemode info" +msgstr "^1Tekan ^3%s^1 untuk maklumat mod permainan" + +#: qcsrc/client/hud/panel/infomessages.qc:111 +#: qcsrc/menu/xonotic/keybinder.qc:94 +msgid "server info" +msgstr "maklumat pelayan" + +#: qcsrc/client/hud/panel/infomessages.qc:124 +msgid "^1Match has already begun" +msgstr "^1Perlawanan telah bermula" + +#: qcsrc/client/hud/panel/infomessages.qc:126 +msgid "^1You have no more lives left" +msgstr "^1Anda dah kehabisan nyawa" + +#: qcsrc/client/hud/panel/infomessages.qc:128 +#: qcsrc/client/hud/panel/infomessages.qc:131 +#, c-format +msgid "^1Press ^3%s^1 to join" +msgstr "^1Tekan ^3%s^1 untuk ikut serta" + +#: qcsrc/client/hud/panel/infomessages.qc:128 +#: qcsrc/client/hud/panel/infomessages.qc:131 +msgid "jump" +msgstr "lompat" + +#: qcsrc/client/hud/panel/infomessages.qc:139 +#, c-format +msgid "^1Game starts in ^3%d^1 seconds" +msgstr "^1Permainan bermula dalam ^3%d^1 saat" + +#: qcsrc/client/hud/panel/infomessages.qc:145 +msgid "^2Currently in ^1warmup^2 stage!" +msgstr "^2Kini ni peringkat ^1panaskan badan^2!" + +#: qcsrc/client/hud/panel/infomessages.qc:160 +#, c-format +msgid "%sPress ^3%s%s to end warmup" +msgstr "%sTekan ^3%s%s untuk tamatkan sesi panaskan badan" + +#: qcsrc/client/hud/panel/infomessages.qc:160 +#: qcsrc/client/hud/panel/infomessages.qc:162 +#: qcsrc/client/hud/panel/infomessages.qc:175 +#: qcsrc/menu/xonotic/keybinder.qc:91 +msgid "ready" +msgstr "bersedia" + +#: qcsrc/client/hud/panel/infomessages.qc:162 +#, c-format +msgid "%sPress ^3%s%s once you are ready" +msgstr "%sTekan ^3%s%s apabila anda dah bersedia" + +#: qcsrc/client/hud/panel/infomessages.qc:167 +msgid "^2Waiting for others to ready up to end warmup..." +msgstr "" +"^2Menunggu yang lain untuk bersedia sebelum menamatkan sesi panaskan badan..." + +#: qcsrc/client/hud/panel/infomessages.qc:169 +msgid "^2Waiting for others to ready up..." +msgstr "^2Menunggu yang lain untuk bersedia..." + +#: qcsrc/client/hud/panel/infomessages.qc:175 +#, c-format +msgid "^2Press ^3%s^2 to end warmup" +msgstr "^2Tekan ^3%s^2 untuk tamatkan sesi panaskan badan" + +#: qcsrc/client/hud/panel/infomessages.qc:196 +msgid "Teamnumbers are unbalanced!" +msgstr "Jumlah ahli pasukan tidak seimbang!" + +#: qcsrc/client/hud/panel/infomessages.qc:199 +#, c-format +msgid " Press ^3%s%s to adjust" +msgstr " Tekan ^3%s%s untuk susun" + +#: qcsrc/client/hud/panel/infomessages.qc:199 +#: qcsrc/menu/xonotic/keybinder.qc:102 +msgid "team menu" +msgstr "menu pasukan" + +#: qcsrc/client/hud/panel/infomessages.qc:209 +msgid "^1Spectating this player:" +msgstr "^1Menonton pemain ini:" + +#: qcsrc/client/hud/panel/infomessages.qc:209 +msgid "^1Spectating you:" +msgstr "^1Menonton anda:" + +#: qcsrc/client/hud/panel/infomessages.qc:225 +msgid "^7Press ^3ESC ^7to show HUD options." +msgstr "^7Tekan ^3ESC ^7untuk menunjukkan pilihan HUD." + +#: qcsrc/client/hud/panel/infomessages.qc:226 +msgid "^3Doubleclick ^7a panel for panel-specific options." +msgstr "^3Klik dua kali ^7a panel untuk pilihan khusus panel." + +#: qcsrc/client/hud/panel/infomessages.qc:227 +msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" +msgstr "^3CTRL ^7untuk melumpuhkan percubaan perlanggaran, ^3SHIFT ^7dan" + +#: qcsrc/client/hud/panel/infomessages.qc:228 +msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." +msgstr "^3ALT ^7+ ^3KEKUNCI ANAK PANAH ^7untuk pelarasan halus." + +#: qcsrc/client/hud/panel/modicons.qc:566 +msgid "Personal best" +msgstr "Pencapaian terbaik peribadi" + +#: qcsrc/client/hud/panel/modicons.qc:576 +msgid "Server best" +msgstr "Pencapaian terbaik pelayan" + +#: qcsrc/client/hud/panel/notify.qc:115 qcsrc/client/hud/panel/notify.qc:116 +#: qcsrc/client/hud/panel/score.qc:59 +#, c-format +msgid "Player %d" +msgstr "Pemain %d" + +#: qcsrc/client/hud/panel/quickmenu.qc:603 +#: qcsrc/client/hud/panel/quickmenu.qc:605 +#, c-format +msgid "Submenu%d" +msgstr "Submenu%d" + +#: qcsrc/client/hud/panel/quickmenu.qc:610 +#, c-format +msgid "Command%d" +msgstr "Perintah%d" + +#: qcsrc/client/hud/panel/quickmenu.qc:636 +msgid "Continue..." +msgstr "Teruskan..." + +#: qcsrc/client/hud/panel/quickmenu.qc:794 +#: qcsrc/client/hud/panel/quickmenu.qc:798 +msgid "Chat" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:795 +msgid "QMCMD^:-) / nice one" +msgstr "QMCMD^:-) / hebat" + +#: qcsrc/client/hud/panel/quickmenu.qc:795 +msgid "QMCMD^nice one" +msgstr "QMCMD^hebat" + +#: qcsrc/client/hud/panel/quickmenu.qc:796 +msgid "QMCMD^good game" +msgstr "QMCMD^terbaiklah" + +#: qcsrc/client/hud/panel/quickmenu.qc:797 +msgid "QMCMD^hi / good luck" +msgstr "QMCMD^hai / semoga berjaya" + +#: qcsrc/client/hud/panel/quickmenu.qc:797 +msgid "QMCMD^hi / good luck and have fun" +msgstr "QMCMD^hai / semoga berjaya dan bergembiralah" + +#: qcsrc/client/hud/panel/quickmenu.qc:802 +#: qcsrc/client/hud/panel/quickmenu.qc:818 +msgid "QMCMD^Team chat" +msgstr "QMCMD^Sembang pasukan" + +#: qcsrc/client/hud/panel/quickmenu.qc:803 +msgid "QMCMD^quad soon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:804 +msgid "QMCMD^free item %x^7 (l:%y^7)" +msgstr "QMCMD^item percuma %x^7 (l:%y^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:804 +msgid "QMCMD^free item, icon" +msgstr "QMCMD^item percuma, ikon" + +#: qcsrc/client/hud/panel/quickmenu.qc:805 +msgid "QMCMD^took item (l:%l^7)" +msgstr "QMCMD^mengambil item (l:%l^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:805 +msgid "QMCMD^took item, icon" +msgstr "QMCMD^mengambil item, ikon" + +#: qcsrc/client/hud/panel/quickmenu.qc:806 +msgid "QMCMD^negative" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:807 +msgid "QMCMD^positive" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:808 +msgid "QMCMD^need help (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:808 +msgid "QMCMD^need help, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:809 +msgid "QMCMD^enemy seen (l:%y^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:809 +msgid "QMCMD^enemy seen, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:810 +msgid "QMCMD^flag seen (l:%y^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:810 +msgid "QMCMD^flag seen, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:811 +msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:811 +msgid "QMCMD^defending, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:812 +msgid "QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:812 +msgid "QMCMD^roaming, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:813 +msgid "QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:813 +msgid "QMCMD^attacking, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:814 +msgid "QMCMD^killed flagcarrier (l:%y^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:814 +msgid "QMCMD^killed flagcarrier, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:815 +#, c-format +msgid "QMCMD^dropped flag (l:%d^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:815 +msgid "QMCMD^dropped flag, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:816 +msgid "QMCMD^drop weapon, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:816 +msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:817 +msgid "QMCMD^drop flag/key, icon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:817 +msgid "QMCMD^dropped flag/key %w^7 (l:%l^7)" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:821 +msgid "QMCMD^Send private message to" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:823 +#: qcsrc/client/hud/panel/quickmenu.qc:860 +msgid "QMCMD^Settings" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:824 +#: qcsrc/client/hud/panel/quickmenu.qc:831 +msgid "QMCMD^View/HUD settings" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:825 +msgid "QMCMD^3rd person view" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:826 +msgid "QMCMD^Player models like mine" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:827 +msgid "QMCMD^Names above players" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:828 +msgid "QMCMD^Crosshair per weapon" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:829 +msgid "QMCMD^FPS" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:830 +msgid "QMCMD^Net graph" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:833 +#: qcsrc/client/hud/panel/quickmenu.qc:836 +msgid "QMCMD^Sound settings" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:834 +msgid "QMCMD^Hit sound" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:835 +msgid "QMCMD^Chat sound" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:840 +#: qcsrc/client/hud/panel/quickmenu.qc:844 +msgid "QMCMD^Spectator camera" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:841 +msgid "QMCMD^1st person" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:842 +msgid "QMCMD^3rd person around player" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:843 +msgid "QMCMD^3rd person behind" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:849 +#: qcsrc/client/hud/panel/quickmenu.qc:854 +msgid "QMCMD^Observer camera" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:850 +msgid "QMCMD^Increase speed" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:851 +msgid "QMCMD^Decrease speed" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:852 +msgid "QMCMD^Wall collision off" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:853 +msgid "QMCMD^Wall collision on" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:857 +msgid "QMCMD^Fullscreen" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:859 +msgid "QMCMD^Translate chat messages" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:862 +#: qcsrc/client/hud/panel/quickmenu.qc:872 +msgid "QMCMD^Call a vote" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:863 +msgid "QMCMD^Restart the map" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:864 +msgid "QMCMD^End match" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:867 +msgid "QMCMD^Reduce match time" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:868 +msgid "QMCMD^Extend match time" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:871 +msgid "QMCMD^Shuffle teams" +msgstr "" + +#: qcsrc/client/hud/panel/racetimer.qc:37 +#, c-format +msgid " (-%dL)" +msgstr " (-%dL)" + +#: qcsrc/client/hud/panel/racetimer.qc:42 +#, c-format +msgid " (+%dL)" +msgstr " (+%dL)" + +#: qcsrc/client/hud/panel/racetimer.qc:61 +msgid "Start line" +msgstr "Garisan pemula" + +#: qcsrc/client/hud/panel/racetimer.qc:63 +#: qcsrc/client/hud/panel/racetimer.qc:67 +msgid "Finish line" +msgstr "Garisan penamat" + +#: qcsrc/client/hud/panel/racetimer.qc:65 +#, c-format +msgid "Intermediate %d" +msgstr "" + +#: qcsrc/client/hud/panel/racetimer.qc:132 +msgid "^1Intermediate 1 (+15.42)" +msgstr "" + +#: qcsrc/client/hud/panel/racetimer.qc:135 +#: qcsrc/client/hud/panel/racetimer.qc:177 +#: qcsrc/client/hud/panel/racetimer.qc:227 +#, c-format +msgid "^1PENALTY: %.1f (%s)" +msgstr "" + +#: qcsrc/client/hud/panel/racetimer.qc:229 +#, c-format +msgid "^2PENALTY: %.1f (%s)" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:78 +msgid "SCO^bckills" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:79 +msgid "SCO^bctime" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:80 +msgid "SCO^caps" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:81 +msgid "SCO^captime" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:82 +msgid "SCO^deaths" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:83 +msgid "SCO^destroyed" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:84 +msgid "SCO^damage" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:85 +msgid "SCO^dmgtaken" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:86 +msgid "SCO^drops" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:87 +msgid "SCO^faults" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:88 +msgid "SCO^fckills" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:89 +msgid "SCO^goals" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:90 +msgid "SCO^kckills" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:91 +msgid "SCO^kdratio" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:92 +msgid "SCO^k/d" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:93 +msgid "SCO^kdr" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:94 +msgid "SCO^kills" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:95 +msgid "SCO^laps" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:96 +msgid "SCO^lives" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:97 +msgid "SCO^losses" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:98 +msgid "SCO^name" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:99 +msgid "SCO^sum" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:100 +msgid "SCO^nick" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:101 +msgid "SCO^objectives" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:102 +msgid "SCO^pickups" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:103 +msgid "SCO^ping" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:104 +msgid "SCO^pl" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:105 +msgid "SCO^pushes" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:106 +msgid "SCO^rank" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:107 +msgid "SCO^returns" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:108 +msgid "SCO^revivals" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:109 +msgid "SCO^rounds won" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:110 +msgid "SCO^score" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:111 +msgid "SCO^suicides" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:112 +msgid "SCO^takes" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:113 +msgid "SCO^ticks" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:295 +msgid "" +"You can modify the scoreboard using the ^2scoreboard_columns_set command.\n" +msgstr "" +"Anda boleh mengubah papan markah menggunakan perintah " +"^2scoreboard_columns_set .\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:296 +msgid "^3|---------------------------------------------------------------|\n" +msgstr "^3|---------------------------------------------------------------|\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:297 +msgid "Usage:\n" +msgstr "Kegunaan:\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:298 +msgid "^2scoreboard_columns_set default\n" +msgstr "^2scoreboard_columns_set default\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:299 +msgid "^2scoreboard_columns_set ^7field1 field2 ...\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:300 +msgid "The following field names are recognized (case insensitive):\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:301 +msgid "You can use a ^3|^7 to start the right-aligned fields.\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:304 +msgid "^3name^7 or ^3nick^7 Name of a player\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:305 +msgid "^3ping^7 Ping time\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:306 +msgid "^3pl^7 Packet loss\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:307 +msgid "^3elo^7 Player ELO\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:308 +msgid "^3kills^7 Number of kills\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:309 +msgid "^3deaths^7 Number of deaths\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:310 +msgid "^3suicides^7 Number of suicides\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:311 +msgid "^3frags^7 kills - suicides\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:312 +msgid "^3kd^7 The kill-death ratio\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:313 +msgid "^3dmg^7 The total damage done\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:314 +msgid "^3dmgtaken^7 The total damage taken\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:315 +msgid "^3sum^7 frags - deaths\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:316 +msgid "" +"^3caps^7 How often a flag (CTF) or a key (KeyHunt) was " +"captured\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:317 +msgid "" +"^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a " +"ball (Keepaway) was picked up\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:318 +msgid "^3captime^7 Time of fastest cap (CTF)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:319 +msgid "^3fckills^7 Number of flag carrier kills\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:320 +msgid "^3returns^7 Number of flag returns\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:321 +msgid "^3drops^7 Number of flag drops\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:322 +msgid "^3lives^7 Number of lives (LMS)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:323 +msgid "^3rank^7 Player rank\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:324 +msgid "^3pushes^7 Number of players pushed into void\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:325 +msgid "" +"^3destroyed^7 Number of keys destroyed by pushing them into " +"void\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:326 +msgid "^3kckills^7 Number of keys carrier kills\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:327 +msgid "^3losses^7 Number of times a key was lost\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:328 +msgid "^3laps^7 Number of laps finished (race/cts)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:329 +msgid "^3time^7 Total time raced (race/cts)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:330 +msgid "^3fastest^7 Time of fastest lap (race/cts)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:331 +msgid "^3ticks^7 Number of ticks (DOM)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:332 +msgid "^3takes^7 Number of domination points taken (DOM)\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:333 +msgid "^3bckills^7 Number of ball carrier kills\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:334 +msgid "" +"^3bctime^7 Total amount of time holding the ball in " +"Keepaway\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:335 +msgid "^3score^7 Total score\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:338 +msgid "" +"Before a field you can put a + or - sign, then a comma separated list\n" +"of game types, then a slash, to make the field show up only in these\n" +"or in all but these game types. You can also specify 'all' as a\n" +"field to show all fields available for the current game mode.\n" +"\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:343 +msgid "" +"The special game type names 'teams' and 'noteams' can be used to\n" +"include/exclude ALL teams/noteams game modes.\n" +"\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:346 +msgid "Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:347 +msgid "" +"will display name, ping and pl aligned to the left, and the fields\n" +"right of the vertical bar aligned to the right.\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:349 +msgid "" +"'field3' will only be shown in CTF, and 'field4' will be shown in all\n" +"other gamemodes except DM.\n" +msgstr "" + +#: qcsrc/client/hud/panel/scoreboard.qc:611 +#: qcsrc/client/hud/panel/scoreboard.qc:618 +#: qcsrc/client/hud/panel/scoreboard.qc:670 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:86 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:87 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:203 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:208 +msgid "N/A" +msgstr "Tiada" + +#: qcsrc/client/hud/panel/scoreboard.qc:1156 +#, c-format +msgid "Accuracy stats (average %d%%)" +msgstr "Statistik ketepatan (puratanya %d%%)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1295 +msgid "Map stats:" +msgstr "Statistik peta:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1325 +msgid "Monsters killed:" +msgstr "Raksasa dibunuh:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1332 +msgid "Secrets found:" +msgstr "Rahsia dijumpai:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1354 +msgid "Capture time rankings" +msgstr "Kedudukan masa tangkapan" + +#: qcsrc/client/hud/panel/scoreboard.qc:1354 +msgid "Rankings" +msgstr "Kedudukan" + +#: qcsrc/client/hud/panel/scoreboard.qc:1519 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 +msgid "Scoreboard" +msgstr "Papan markah" + +#: qcsrc/client/hud/panel/scoreboard.qc:1584 +#, c-format +msgid "Speed award: %d%s ^7(%s^7)" +msgstr "Anugerah kelajuan: %d%s ^7(%s^7)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1588 +#, c-format +msgid "All-time fastest: %d%s ^7(%s^7)" +msgstr "Terlaju sepanjang masa: %d%s ^7(%s^7)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1604 +#, c-format +msgid "Spectators" +msgstr "Penonton" + +#: qcsrc/client/hud/panel/scoreboard.qc:1619 +#, c-format +msgid "playing ^3%s^7 on ^2%s^7" +msgstr "bermain ^3%s^7 di ^2%s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1626 +#: qcsrc/client/hud/panel/scoreboard.qc:1631 +#, c-format +msgid " for up to ^1%1.0f minutes^7" +msgstr " sehingga ^1%1.0f minit^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1635 +#: qcsrc/client/hud/panel/scoreboard.qc:1654 +msgid " or" +msgstr "atau" + +#: qcsrc/client/hud/panel/scoreboard.qc:1638 +#: qcsrc/client/hud/panel/scoreboard.qc:1645 +#, c-format +msgid " until ^3%s %s^7" +msgstr " sehingga ^3%s %s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1639 +#: qcsrc/client/hud/panel/scoreboard.qc:1646 +#: qcsrc/client/hud/panel/scoreboard.qc:1658 +#: qcsrc/client/hud/panel/scoreboard.qc:1665 +msgid "SCO^points" +msgstr "SCO^mata" + +#: qcsrc/client/hud/panel/scoreboard.qc:1640 +#: qcsrc/client/hud/panel/scoreboard.qc:1647 +#: qcsrc/client/hud/panel/scoreboard.qc:1659 +#: qcsrc/client/hud/panel/scoreboard.qc:1666 +msgid "SCO^is beaten" +msgstr "SCO^dikalahkan" + +#: qcsrc/client/hud/panel/scoreboard.qc:1657 +#: qcsrc/client/hud/panel/scoreboard.qc:1664 +#, c-format +msgid " until a lead of ^3%s %s^7" +msgstr " sehingga pimpinan ^3%s %s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1688 +#, c-format +msgid "^1Respawning in ^3%s^1..." +msgstr "^1Lahir semula dalam ^3%s^1..." + +#: qcsrc/client/hud/panel/scoreboard.qc:1698 +#, c-format +msgid "You are dead, wait ^3%s^7 before respawning" +msgstr "Anda dah mati, tunggu ^3%s^7 sebelum lahir semula" + +#: qcsrc/client/hud/panel/scoreboard.qc:1707 +#, c-format +msgid "You are dead, press ^2%s^7 to respawn" +msgstr "Anda dah mati, tekan ^2%s^7 untuk lahir semula" + +#: qcsrc/client/hud/panel/vote.qc:24 +msgid "^1You must answer before entering hud configure mode\n" +msgstr "^1Anda mesti jawab sebelum memasuki mod susunan hud\n" + +#: qcsrc/client/hud/panel/vote.qc:29 +msgid "^2Name ^7instead of \"^1Anonymous player^7\" in stats" +msgstr "^2Nama ^7menggantikan \"^1Pemain tidak bernama^7\" dalam statistik" + +#: qcsrc/client/hud/panel/vote.qc:115 +msgid "A vote has been called for:" +msgstr "" + +#: qcsrc/client/hud/panel/vote.qc:117 +msgid "Allow servers to store and display your name?" +msgstr "Benarkan pelayan untuk menyimpan dan memaparkan nama anda?" + +#: qcsrc/client/hud/panel/vote.qc:121 +msgid "^1Configure the HUD" +msgstr "^1Susun HUD" + +#: qcsrc/client/hud/panel/vote.qc:125 qcsrc/menu/xonotic/dialog_firstrun.qc:82 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:18 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:18 +#: qcsrc/menu/xonotic/dialog_quit.qc:14 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:26 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:16 +#: qcsrc/menu/xonotic/dialog_uid2name.qc:15 +msgid "Yes" +msgstr "Ya" + +#: qcsrc/client/hud/panel/vote.qc:127 qcsrc/menu/xonotic/dialog_firstrun.qc:83 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:21 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:21 +#: qcsrc/menu/xonotic/dialog_quit.qc:16 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:29 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:17 +#: qcsrc/menu/xonotic/dialog_uid2name.qc:17 +msgid "No" +msgstr "Tidak" + +#: qcsrc/client/hud/panel/weapons.qc:530 +msgid "Out of ammo" +msgstr "Kehabisan peluru" + +#: qcsrc/client/hud/panel/weapons.qc:534 +msgid "Don't have" +msgstr "Tiada" + +#: qcsrc/client/hud/panel/weapons.qc:538 +msgid "Unavailable" +msgstr "Tak wujud" + +#: qcsrc/client/main.qc:1014 +msgid " qu/s" +msgstr "qu/s" + +#: qcsrc/client/main.qc:1016 +msgid " m/s" +msgstr "m/s" + +#: qcsrc/client/main.qc:1018 +msgid " km/h" +msgstr "km/j" + +#: qcsrc/client/main.qc:1020 +msgid " mph" +msgstr "b/j" + +#: qcsrc/client/main.qc:1022 +msgid " knots" +msgstr "knot" + +#: qcsrc/client/main.qc:1264 +#, c-format +msgid "%s (not bound)" +msgstr "" + +#: qcsrc/client/mapvoting.qc:49 +msgid " (1 vote)" +msgstr "(1 undi)" + +#: qcsrc/client/mapvoting.qc:51 +#, c-format +msgid " (%d votes)" +msgstr " (%d undi)" + +#: qcsrc/client/mapvoting.qc:271 +msgid "Don't care" +msgstr "Tak kisah" + +#: qcsrc/client/mapvoting.qc:365 +msgid "Decide the gametype" +msgstr "Tentukan jenis permainan" + +#: qcsrc/client/mapvoting.qc:365 +msgid "Vote for a map" +msgstr "Undi peta" + +#: qcsrc/client/mapvoting.qc:382 +#, c-format +msgid "%d seconds left" +msgstr "Tinggal %d saat" + +#: qcsrc/client/mapvoting.qc:497 +msgid "" +"mv_mapdownload: ^3You're not supposed to use this command on your own!\n" +msgstr "" +"mv_mapdownload: ^3Anda tidak patut guna perintah ini dengan sendirinya!\n" + +#: qcsrc/client/mapvoting.qc:507 +msgid "^1Error:^7 Couldn't find pak index.\n" +msgstr "" + +#: qcsrc/client/mapvoting.qc:516 +msgid "Requesting preview...\n" +msgstr "Meminta pratonton...\n" + +#: qcsrc/client/miscfunctions.qc:109 +msgid "Trying to remove a team which is not in the teamlist!" +msgstr "" + +#: qcsrc/client/view.qc:1380 +msgid "Nade timer" +msgstr "" + +#: qcsrc/client/view.qc:1385 +msgid "Capture progress" +msgstr "" + +#: qcsrc/client/view.qc:1390 +msgid "Revival progress" +msgstr "" + +#: qcsrc/common/command/generic.qc:157 +msgid "error creating curl handle\n" +msgstr "" + +#: qcsrc/common/command/generic.qc:403 +msgid "Notification restart command only works with cl_cmd and sv_cmd.\n" +msgstr "" + +#: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 +msgid "Ball Stealer" +msgstr "" + +#: qcsrc/common/items/item/armor.qh:111 +msgid "Big armor" +msgstr "" + +#: qcsrc/common/items/item/armor.qh:147 +msgid "Mega armor" +msgstr "" + +#: qcsrc/common/items/item/health.qh:111 +msgid "Big health" +msgstr "" + +#: qcsrc/common/items/item/health.qh:147 +msgid "Mega health" +msgstr "" + +#: qcsrc/common/items/item/jetpack.qh:35 +msgid "Jet Pack" +msgstr "" + +#: qcsrc/common/items/item/jetpack.qh:82 +msgid "Fuel regen" +msgstr "" + +#: qcsrc/common/items/item/powerup.qh:44 +msgid "Strength" +msgstr "" + +#: qcsrc/common/items/item/powerup.qh:76 +msgid "Shield" +msgstr "" + +#: qcsrc/common/mapinfo.qc:639 +#, no-c-format +msgid "@!#%'n Tuba Throwing" +msgstr "" + +#: qcsrc/common/mapinfo.qh:99 +msgid "Deathmatch" +msgstr "Kalah Mati" + +#: qcsrc/common/mapinfo.qh:99 +msgid "Score as many frags as you can" +msgstr "Dominasi kawasan sebanyak mana yang anda boleh" + +#: qcsrc/common/mapinfo.qh:111 +msgid "Last Man Standing" +msgstr "Orang Terakhir Berdiri" + +#: qcsrc/common/mapinfo.qh:111 +msgid "Survive and kill until the enemies have no lives left" +msgstr "Kekal hidup dan bunuh semua musuh sehingga mereka kehabisan nyawa" + +#: qcsrc/common/mapinfo.qh:126 +msgid "Race" +msgstr "Lumba" + +#: qcsrc/common/mapinfo.qh:126 +msgid "Race against other players to the finish line" +msgstr "Berlumba dengan pemain lain ke garisan penamat" + +#: qcsrc/common/mapinfo.qh:160 +msgid "Race CTS" +msgstr "Lumba CTS" + +#: qcsrc/common/mapinfo.qh:160 +msgid "Race for fastest time." +msgstr "Lumba untuk masa terpantas." + +#: qcsrc/common/mapinfo.qh:184 +msgid "Help your team score the most frags against the enemy team" +msgstr "" +"Bantu pasukan anda untuk mendominasi kebanyakan kawasan berbanding pasukan " +"lawan" + +#: qcsrc/common/mapinfo.qh:184 +msgid "Team Deathmatch" +msgstr "Kalah Mati Berpasukan" + +#: qcsrc/common/mapinfo.qh:220 +msgid "Capture the Flag" +msgstr "Curi Bendera" + +#: qcsrc/common/mapinfo.qh:220 +msgid "" +"Find and bring the enemy flag to your base to capture it, defend your base " +"from the other team" +msgstr "" +"Cari dan bawa balik bendera musuh ke tapak anda, pertahankan tapak anda " +"daripada pasukan lain" + +#: qcsrc/common/mapinfo.qh:249 +msgid "Clan Arena" +msgstr "Arena Suku" + +#: qcsrc/common/mapinfo.qh:249 +msgid "Kill all enemy teammates to win the round" +msgstr "Bunuh semua ahli pasukan musuh untuk memenangi pusingan tersebut" + +#: qcsrc/common/mapinfo.qh:287 +msgid "Capture and defend all the control points to win" +msgstr "Ambil alih dan pertahankan semua titik kawalan untuk menang" + +#: qcsrc/common/mapinfo.qh:287 +msgid "Domination" +msgstr "Dominasi" + +#: qcsrc/common/mapinfo.qh:319 +msgid "Gather all the keys to win the round" +msgstr "Kumpul semua kunci untuk memenangi pusingan" + +#: qcsrc/common/mapinfo.qh:319 +msgid "Key Hunt" +msgstr "Pencarian Kunci" + +#: qcsrc/common/mapinfo.qh:353 +msgid "Assault" +msgstr "Serangan" + +#: qcsrc/common/mapinfo.qh:353 +msgid "" +"Destroy obstacles to find and destroy the enemy power core before time runs " +"out" +msgstr "" +"Musnahkan halangan untuk mencari dan memusnahkan teras kuasa musuh sebelum " +"masa tamat" + +#: qcsrc/common/mapinfo.qh:371 +msgid "Capture control points to reach and destroy the enemy generator" +msgstr "" +"Ambil alih titik kawalan untuk sampai ke dan musnahkan penjana kuasa musuh" + +#: qcsrc/common/mapinfo.qh:371 +msgid "Onslaught" +msgstr "Serangan Hebat" + +#: qcsrc/common/mapinfo.qh:387 +msgid "Nexball" +msgstr "Bola Nex" + +#: qcsrc/common/mapinfo.qh:387 +msgid "Shoot and kick the ball into the enemies goal, keep your goal clean" +msgstr "Tembak dan tendang bola ke dalam gol musuh, biar kosong gol anda" + +#: qcsrc/common/mapinfo.qh:408 +msgid "Freeze Tag" +msgstr "Aci Beku" + +#: qcsrc/common/mapinfo.qh:408 +msgid "" +"Kill enemies to freeze them, stand next to frozen teammates to revive them; " +"freeze all enemies to win" +msgstr "" +"Bunuh musuh untuk bekukan mereka, berdiri dekat dengan rakan pasukan beku " +"untuk mencairkan mereka; bekukan semua musuh untuk menang" + +#: qcsrc/common/mapinfo.qh:446 +msgid "Hold the ball to get points for kills" +msgstr "Tangkap bola untuk mendapat mata pembunuhan" + +#: qcsrc/common/mapinfo.qh:446 +msgid "Keepaway" +msgstr "Bola Elak" + +#: qcsrc/common/mapinfo.qh:461 +msgid "Invasion" +msgstr "Serangan" + +#: qcsrc/common/mapinfo.qh:461 +msgid "Survive against waves of monsters" +msgstr "Cuba untuk hidup dengan gelombang kelahiran raksasa" + +#: qcsrc/common/minigames/cl_minigames.qc:383 +msgid "It's your turn" +msgstr "Giliran anda" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:331 +#: qcsrc/menu/xonotic/dialog_quit.qh:6 +msgid "Quit" +msgstr "Berhenti Main" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:336 +msgid "Invite" +msgstr "Ajak Rakan" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:378 +msgid "Current Game" +msgstr "Permainan Semasa" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:403 +msgid "Exit Menu" +msgstr "Keluar Menu" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:415 +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:16 +msgid "Create" +msgstr "Cipta" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:418 +msgid "Join" +msgstr "Sertai" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:489 +msgid "Minigames" +msgstr "Permainan Mini" + +#: qcsrc/common/minigames/minigame/bd.qc:1168 +msgid "Better luck next time!" +msgstr "Cuba lagi akan datang!" + +#: qcsrc/common/minigames/minigame/bd.qc:1172 +msgid "Tubular! Press \"Next Level\" to continue!" +msgstr "Macam tiub! Tekan \"Tahap Seterusnya\" untuk teruskan!" + +#: qcsrc/common/minigames/minigame/bd.qc:1174 +msgid "Wicked! Press \"Next Level\" to continue!" +msgstr "Jahatnya! Tekan \"Tahap Seterusnya\" untuk teruskan!" + +#: qcsrc/common/minigames/minigame/bd.qc:1177 +msgid "Press the space bar to change your currently selected tile" +msgstr "" + +#: qcsrc/common/minigames/minigame/bd.qc:1180 +msgid "Push the boulders onto the targets" +msgstr "" + +#: qcsrc/common/minigames/minigame/bd.qc:1404 +msgid "Next Level" +msgstr "Tahap Seterusnya" + +#: qcsrc/common/minigames/minigame/bd.qc:1405 +msgid "Restart" +msgstr "Mulakan Semula" + +#: qcsrc/common/minigames/minigame/bd.qc:1406 +msgid "Editor" +msgstr "Editor" + +#: qcsrc/common/minigames/minigame/bd.qc:1407 +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:37 +msgid "Save" +msgstr "Simpan" + +#: qcsrc/common/minigames/minigame/c4.qc:373 +#: qcsrc/common/minigames/minigame/pp.qc:438 +#: qcsrc/common/minigames/minigame/ttt.qc:319 +msgid "Draw" +msgstr "Lukis" + +#: qcsrc/common/minigames/minigame/c4.qc:378 +#: qcsrc/common/minigames/minigame/nmm.qc:601 +msgid "You lost the game!" +msgstr "Anda kalah!" + +#: qcsrc/common/minigames/minigame/c4.qc:379 +#: qcsrc/common/minigames/minigame/nmm.qc:602 +msgid "You win!" +msgstr "Anda menang!" + +#: qcsrc/common/minigames/minigame/c4.qc:383 +#: qcsrc/common/minigames/minigame/nmm.qc:606 +#: qcsrc/common/minigames/minigame/pp.qc:455 +#: qcsrc/common/minigames/minigame/ttt.qc:336 +msgid "Wait for your opponent to make their move" +msgstr "" + +#: qcsrc/common/minigames/minigame/c4.qc:386 +#: qcsrc/common/minigames/minigame/nmm.qc:608 +#: qcsrc/common/minigames/minigame/pp.qc:458 +#: qcsrc/common/minigames/minigame/ttt.qc:339 +msgid "Click on the game board to place your piece" +msgstr "Klik pada papan permainan untuk meletakkan kepingan anda" + +#: qcsrc/common/minigames/minigame/nmm.qc:610 +msgid "" +"You can select one of your pieces to move it in one of the surrounding places" +msgstr "" + +#: qcsrc/common/minigames/minigame/nmm.qc:612 +msgid "You can select one of your pieces to move it anywhere on the board" +msgstr "" + +#: qcsrc/common/minigames/minigame/nmm.qc:614 +msgid "You can take one of the opponent's pieces" +msgstr "" + +#: qcsrc/common/minigames/minigame/pong.qc:570 +#: qcsrc/common/minigames/minigame/ttt.qc:299 +msgid "AI" +msgstr "" + +#: qcsrc/common/minigames/minigame/pong.qc:587 +msgid "Press ^1Start Match^7 to start the match with the current players" +msgstr "" + +#: qcsrc/common/minigames/minigame/pong.qc:651 +msgid "Start Match" +msgstr "" + +#: qcsrc/common/minigames/minigame/pong.qc:652 +msgid "Add AI player" +msgstr "" + +#: qcsrc/common/minigames/minigame/pong.qc:653 +msgid "Remove AI player" +msgstr "" + +#: qcsrc/common/minigames/minigame/pp.qc:443 +#: qcsrc/common/minigames/minigame/ttt.qc:324 +msgid "" +"You lost the game!\n" +"Select \"^1Next Match^7\" on the menu for a rematch!" +msgstr "" + +#: qcsrc/common/minigames/minigame/pp.qc:444 +#: qcsrc/common/minigames/minigame/ttt.qc:325 +msgid "" +"You win!\n" +"Select \"^1Next Match^7\" on the menu to start a new match!" +msgstr "" + +#: qcsrc/common/minigames/minigame/pp.qc:450 +#: qcsrc/common/minigames/minigame/ttt.qc:331 +msgid "Select \"^1Next Match^7\" on the menu to start a new match!" +msgstr "" + +#: qcsrc/common/minigames/minigame/pp.qc:451 +#: qcsrc/common/minigames/minigame/ttt.qc:332 +msgid "Wait for your opponent to confirm the rematch" +msgstr "" + +#: qcsrc/common/minigames/minigame/pp.qc:582 +#: qcsrc/common/minigames/minigame/ttt.qc:665 +msgid "Next Match" +msgstr "" + +#: qcsrc/common/minigames/minigame/ps.qc:478 +#, c-format +msgid "Pieces left: %s" +msgstr "" + +#: qcsrc/common/minigames/minigame/ps.qc:488 +msgid "No more valid moves" +msgstr "" + +#: qcsrc/common/minigames/minigame/ps.qc:491 +msgid "Well done, you win!" +msgstr "" + +#: qcsrc/common/minigames/minigame/ps.qc:494 +msgid "Jump a piece over another to capture it" +msgstr "" + +#: qcsrc/common/minigames/minigame/ttt.qc:666 +msgid "Single Player" +msgstr "" + +#: qcsrc/common/monsters/monster/mage.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:18 +msgid "Mage" +msgstr "" + +#: qcsrc/common/monsters/monster/mage.qh:29 +msgid "Mage spike" +msgstr "" + +#: qcsrc/common/monsters/monster/shambler.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:17 +msgid "Shambler" +msgstr "" + +#: qcsrc/common/monsters/monster/spider.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:16 +msgid "Spider" +msgstr "" + +#: qcsrc/common/monsters/monster/spider.qh:28 +msgid "Spider attack" +msgstr "" + +#: qcsrc/common/monsters/monster/wyvern.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:19 +msgid "Wyvern" +msgstr "" + +#: qcsrc/common/monsters/monster/wyvern.qh:28 +msgid "Wyvern attack" +msgstr "" + +#: qcsrc/common/monsters/monster/zombie.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:15 +msgid "Zombie" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:15 +msgid "Ammo" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:24 +msgid "Resistance" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:33 +#: qcsrc/common/mutators/mutator/instagib/items.qh:94 +msgid "Speed" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:43 +msgid "Medic" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:54 +msgid "Bash" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:62 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:85 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:177 +msgid "Vampire" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:70 +msgid "Disability" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:78 +msgid "Vengeance" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:86 +msgid "Jump" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:95 +msgid "Invisible" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:104 +msgid "Inferno" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:112 +msgid "Swapper" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:120 +msgid "Magnet" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:128 +msgid "Luck" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:136 +msgid "Flight" +msgstr "" + +#: qcsrc/common/mutators/mutator/buffs/buffs.qh:7 +msgid "Buff" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:8 +msgid "Damage text" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:18 +msgid "Draw damage numbers" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 +msgid "Font size minimum:" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 +msgid "Font size maximum:" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 +msgid "Accumulate range:" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:35 +msgid "Lifetime:" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:40 +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:50 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:55 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:102 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:60 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:109 +#: qcsrc/menu/xonotic/util.qc:775 +msgid "Color:" +msgstr "" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 +msgid "Draw damage numbers for friendly fire" +msgstr "" + +#: qcsrc/common/mutators/mutator/instagib/items.qh:56 +msgid "Extra life" +msgstr "" + +#: qcsrc/common/mutators/mutator/instagib/items.qh:75 +msgid "Invisibility" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:18 +msgid "Napalm grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:26 +msgid "Ice grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:34 +msgid "Translocate grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:42 +msgid "Spawn grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:50 +msgid "Heal grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:58 +msgid "Monster grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:66 +msgid "Entrap grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/nades/nades.qh:32 +msgid "Grenade" +msgstr "" + +#: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 +msgid "Heavy Machine Gun" +msgstr "" + +#: qcsrc/common/mutators/mutator/overkill/rpc.qh:17 +msgid "Rocket Propelled Chainsaw" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:3 +msgid "Waypoint" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:4 +msgid "Help me!" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:5 +msgid "Here" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:6 +msgid "DANGER" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:8 +msgid "Frozen!" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:10 +msgid "Item" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:12 +msgid "Checkpoint" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:13 +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 +msgid "Finish" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:14 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:15 +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 +msgid "Start" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:17 +msgid "Defend" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:18 +msgid "Destroy" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:19 +msgid "Push" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:21 +msgid "Flag carrier" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:22 +msgid "Enemy carrier" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:23 +msgid "Dropped flag" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:24 +msgid "White base" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:25 +msgid "Red base" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:26 +msgid "Blue base" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:27 +msgid "Yellow base" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:28 +msgid "Pink base" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:29 +msgid "Return flag here" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:31 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:32 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:33 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:34 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:35 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:51 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:52 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:53 +msgid "Control point" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:37 +msgid "Dropped key" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:38 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:40 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:41 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:42 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:43 +msgid "Key carrier" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:39 +msgid "Run here" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:45 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:48 +msgid "Ball" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:46 +msgid "Ball carrier" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:49 +msgid "Goal" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:54 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:55 +msgid "Generator" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:57 +msgid "Weapon" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:59 +msgid "Monster" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:61 +msgid "Vehicle" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:62 +msgid "Intruder!" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:64 +msgid "Tagged" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:651 +#: qcsrc/common/turrets/cl_turrets.qc:120 +msgid "Spam" +msgstr "" + +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:655 +#, c-format +msgid "%s needing help!" +msgstr "" + +#: qcsrc/common/net_notice.qc:87 +msgid "^1Server notices:" +msgstr "" + +#: qcsrc/common/notifications/all.inc:239 +msgid "^F4NOTE: ^BGSpectator chat is not sent to players during the match" +msgstr "" + +#: qcsrc/common/notifications/all.inc:241 +#, c-format +msgid "^BG%s^BG captured the ^TC^TT^BG flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:242 +#, c-format +msgid "" +"^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG" +"%s^BG's previous record of ^F2%s^BG seconds" +msgstr "" + +#: qcsrc/common/notifications/all.inc:243 +#, c-format +msgid "^BG%s^BG captured the flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:244 +#, c-format +msgid "^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds" +msgstr "" + +#: qcsrc/common/notifications/all.inc:245 +#, c-format +msgid "" +"^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break " +"^BG%s^BG's previous record of ^F1%s^BG seconds" +msgstr "" + +#: qcsrc/common/notifications/all.inc:246 +msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" +msgstr "" + +#: qcsrc/common/notifications/all.inc:247 +msgid "^BGThe flag was returned by its owner" +msgstr "" + +#: qcsrc/common/notifications/all.inc:248 +msgid "^BGThe ^TC^TT^BG flag was destroyed and returned to base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:249 +msgid "^BGThe flag was destroyed and returned to base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:250 +msgid "^BGThe ^TC^TT^BG flag was dropped in the base and returned itself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:251 +msgid "^BGThe flag was dropped in the base and returned itself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:252 +msgid "" +"^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to " +"base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:253 +msgid "^BGThe flag fell somewhere it couldn't be reached and returned to base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:254 +#, c-format +msgid "" +"^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned " +"itself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:255 +#, c-format +msgid "" +"^BGThe flag became impatient after ^F1%.2f^BG seconds and returned itself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:256 +msgid "^BGThe ^TC^TT^BG flag has returned to the base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:257 +msgid "^BGThe flag has returned to the base" +msgstr "" + +#: qcsrc/common/notifications/all.inc:258 +#, c-format +msgid "^BG%s^BG lost the ^TC^TT^BG flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:259 +#, c-format +msgid "^BG%s^BG lost the flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:260 +#, c-format +msgid "^BG%s^BG got the ^TC^TT^BG flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:261 +#, c-format +msgid "^BG%s^BG got the flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:262 +#: qcsrc/common/notifications/all.inc:263 +#, c-format +msgid "^BG%s^BG returned the ^TC^TT^BG flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:265 +#: qcsrc/common/notifications/all.inc:553 +#, c-format +msgid "^F2Throwing coin... Result: %s^F2!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:267 +msgid "^BGYou don't have any fuel for the ^F1Jetpack" +msgstr "" + +#: qcsrc/common/notifications/all.inc:269 +msgid "^F2You lack a UID, superspec options will not be saved/restored" +msgstr "" + +#: qcsrc/common/notifications/all.inc:271 +msgid "^F1Round already started, you will join the game in the next round" +msgstr "" + +#: qcsrc/common/notifications/all.inc:272 +msgid "^F2You will spectate in the next round" +msgstr "" + +#: qcsrc/common/notifications/all.inc:274 +#, c-format +msgid "^BG%s%s^K1 was killed by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:274 +#, c-format +msgid "^BG%s%s^K1 was scored against by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:275 +#, c-format +msgid "^BG%s%s^K1 was unfairly eliminated by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:276 +#, c-format +msgid "^BG%s%s^K1 was drowned by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:277 +#, c-format +msgid "^BG%s%s^K1 was grounded by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:278 +#, c-format +msgid "^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:278 +#, c-format +msgid "^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:279 +#, c-format +msgid "^BG%s%s^K1 was cooked by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:280 +#, c-format +msgid "^BG%s%s^K1 was pushed in front of a monster by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:281 +#, c-format +msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:282 +#, c-format +msgid "^BG%s%s^K1 got too close to a napalm explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:282 +#, c-format +msgid "^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:283 +#, c-format +msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:284 +#, c-format +msgid "^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:285 +#, c-format +msgid "^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:286 +#, c-format +msgid "^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:287 +#, c-format +msgid "^BG%s%s^K1 was slimed by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:288 +#, c-format +msgid "^BG%s%s^K1 was preserved by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:289 +#, c-format +msgid "^BG%s%s^K1 tried to occupy ^BG%s^K1's teleport destination space%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:289 +#, c-format +msgid "^BG%s%s^K1 was telefragged by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:290 +#, c-format +msgid "^BG%s%s^K1 died in an accident with ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:291 +#, c-format +msgid "" +"^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Bumblebee exploded%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:292 +#, c-format +msgid "^BG%s%s^K1 saw the pretty lights of ^BG%s^K1's Bumblebee gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:293 +#, c-format +msgid "^BG%s%s^K1 was crushed by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:294 +#, c-format +msgid "^BG%s%s^K1 was cluster bombed by ^BG%s^K1's Raptor%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:295 +#, c-format +msgid "^BG%s%s^K1 couldn't resist ^BG%s^K1's purple blobs%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:296 +#, c-format +msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Raptor exploded%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:297 +#, c-format +msgid "" +"^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Spiderbot exploded%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:298 +#, c-format +msgid "^BG%s%s^K1 got shredded by ^BG%s^K1's Spiderbot%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:299 +#, c-format +msgid "^BG%s%s^K1 was blasted to bits by ^BG%s^K1's Spiderbot%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:300 +#, c-format +msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:301 +#, c-format +msgid "^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:302 +#, c-format +msgid "^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:303 +#, c-format +msgid "^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:305 +#, c-format +msgid "^BG%s^K1 was moved into the %s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:306 +#, c-format +msgid "^BG%s^K1 became enemies with the Lord of Teamplay%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:307 +#, c-format +msgid "^BG%s^K1 thought they found a nice camping ground%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:308 +#, c-format +msgid "^BG%s^K1 unfairly eliminated themself%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:310 +#, c-format +msgid "^BG%s^K1 couldn't catch their breath%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:310 +#, c-format +msgid "^BG%s^K1 was in the water for too long%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:311 +#, c-format +msgid "^BG%s^K1 hit the ground with a bit too much force%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:311 +#, c-format +msgid "^BG%s^K1 hit the ground with a crunch%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:312 +#, c-format +msgid "^BG%s^K1 became a bit too crispy%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:312 +#, c-format +msgid "^BG%s^K1 felt a little hot%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:313 +#, c-format +msgid "^BG%s^K1 died%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:314 +#, c-format +msgid "^BG%s^K1 found a hot place%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:314 +#, c-format +msgid "^BG%s^K1 turned into hot slag%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:315 +#, c-format +msgid "^BG%s^K1 was exploded by a Mage%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:316 +#, c-format +msgid "^BG%s^K1's innards became outwards by a Shambler%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:317 +#, c-format +msgid "^BG%s^K1 was smashed by a Shambler%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:318 +#, c-format +msgid "^BG%s^K1 was zapped to death by a Shambler%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:319 +#, c-format +msgid "^BG%s^K1 was bitten by a Spider%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:320 +#, c-format +msgid "^BG%s^K1 was fireballed by a Wyvern%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:321 +#, c-format +msgid "^BG%s^K1 joins the Zombies%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:322 +#, c-format +msgid "^BG%s^K1 was given kung fu lessons by a Zombie%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:323 +#: qcsrc/common/notifications/all.inc:325 +#, c-format +msgid "^BG%s^K1 mastered the art of self-nading%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:324 +#, c-format +msgid "" +"^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:324 +#, c-format +msgid "^BG%s^K1 was burned to death by their own Napalm Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:326 +#, c-format +msgid "^BG%s^K1 felt a little chilly%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:326 +#, c-format +msgid "^BG%s^K1 was frozen to death by their own Ice Nade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:327 +#, c-format +msgid "^BG%s^K1's Healing Nade didn't quite heal them%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:328 +#, c-format +msgid "^BG%s^K1 died%s%s. What's the point of living without ammo?" +msgstr "" + +#: qcsrc/common/notifications/all.inc:328 +#, c-format +msgid "^BG%s^K1 ran out of ammo%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:329 +#, c-format +msgid "^BG%s^K1 rotted away%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:330 +#, c-format +msgid "^BG%s^K1 became a shooting star%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:331 +#, c-format +msgid "^BG%s^K1 was slimed%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:332 +#, c-format +msgid "^BG%s^K1 couldn't take it anymore%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:333 +#, c-format +msgid "^BG%s^K1 is now preserved for centuries to come%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:334 +#, c-format +msgid "^BG%s^K1 switched to the %s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:335 +#, c-format +msgid "^BG%s^K1 died in an accident%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:336 +#, c-format +msgid "^BG%s^K1 ran into a turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:337 +#, c-format +msgid "^BG%s^K1 was blasted away by an eWheel turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:338 +#, c-format +msgid "^BG%s^K1 got caught up in the FLAC turret fire%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:339 +#, c-format +msgid "^BG%s^K1 was blasted away by a Hellion turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:340 +#, c-format +msgid "^BG%s^K1 could not hide from the Hunter turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:341 +#, c-format +msgid "^BG%s^K1 was riddled full of holes by a Machinegun turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:342 +#, c-format +msgid "^BG%s^K1 got turned into smoldering gibs by an MLRS turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:343 +#, c-format +msgid "^BG%s^K1 was phased out by a turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:344 +#, c-format +msgid "^BG%s^K1 got served some superheated plasma from a turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:345 +#, c-format +msgid "^BG%s^K1 was electrocuted by a Tesla turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:346 +#, c-format +msgid "^BG%s^K1 got served a lead enrichment by a Walker turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:347 +#, c-format +msgid "^BG%s^K1 was impaled by a Walker turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:348 +#, c-format +msgid "^BG%s^K1 was blasted away by a Walker turret%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:349 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:350 +#, c-format +msgid "^BG%s^K1 was crushed by a vehicle%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:351 +#, c-format +msgid "^BG%s^K1 was caught in a Raptor cluster bomb%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:352 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Raptor explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:353 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Spiderbot explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:354 +#, c-format +msgid "^BG%s^K1 was blasted to bits by a Spiderbot rocket%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:355 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Racer explosion%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:356 +#, c-format +msgid "^BG%s^K1 couldn't find shelter from a Racer rocket%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:359 +#, c-format +msgid "^BG%s^K1 was betrayed by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:361 +#, c-format +msgid "^BG%s^BG%s^BG (%s %s every %s seconds)" +msgstr "" + +#: qcsrc/common/notifications/all.inc:363 +#, c-format +msgid "^BG%s^K1 was frozen by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:364 +#, c-format +msgid "^BG%s^K3 was revived by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:365 +#, c-format +msgid "^BG%s^K3 was revived by falling" +msgstr "" + +#: qcsrc/common/notifications/all.inc:366 +#, c-format +msgid "^BG%s^K3 was revived by their Nade explosion" +msgstr "" + +#: qcsrc/common/notifications/all.inc:367 +#, c-format +msgid "^BG%s^K3 was automatically revived after %s second(s)" +msgstr "" + +#: qcsrc/common/notifications/all.inc:368 +#, c-format +msgid "^BG%s^K1 froze themself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:370 +#: qcsrc/common/notifications/all.inc:684 +msgid "^TC^TT^BG team wins the round" +msgstr "" + +#: qcsrc/common/notifications/all.inc:371 +#: qcsrc/common/notifications/all.inc:685 +#, c-format +msgid "^BG%s^BG wins the round" +msgstr "" + +#: qcsrc/common/notifications/all.inc:372 +#: qcsrc/common/notifications/all.inc:548 +msgid "^BGRound tied" +msgstr "" + +#: qcsrc/common/notifications/all.inc:373 +#: qcsrc/common/notifications/all.inc:549 +msgid "^BGRound over, there's no winner" +msgstr "" + +#: qcsrc/common/notifications/all.inc:375 +#, c-format +msgid "^BGGodmode saved you %s units of damage, cheater!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:377 +#, c-format +msgid "^BG%s^BG got the %s^BG buff!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:378 +#, c-format +msgid "^BG%s^BG lost the %s^BG buff!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:379 +#: qcsrc/common/notifications/all.inc:692 +#, c-format +msgid "^BGYou dropped the %s^BG buff!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:380 +#: qcsrc/common/notifications/all.inc:693 +#, c-format +msgid "^BGYou got the %s^BG buff!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:382 +#: qcsrc/common/notifications/all.inc:696 +#, c-format +msgid "^BGYou do not have the ^F1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:383 +#: qcsrc/common/notifications/all.inc:697 +#, c-format +msgid "^BGYou dropped the ^F1%s^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:384 +#: qcsrc/common/notifications/all.inc:698 +#, c-format +msgid "^BGYou got the ^F1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:385 +#: qcsrc/common/notifications/all.inc:699 +#, c-format +msgid "^BGYou don't have enough ammo for the ^F1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:386 +#: qcsrc/common/notifications/all.inc:700 +#, c-format +msgid "^F1%s %s^BG is unable to fire, but its ^F1%s^BG can" +msgstr "" + +#: qcsrc/common/notifications/all.inc:387 +#: qcsrc/common/notifications/all.inc:701 +#, c-format +msgid "^F1%s^BG is ^F4not available^BG on this map" +msgstr "" + +#: qcsrc/common/notifications/all.inc:389 +#, c-format +msgid "^BG%s^BG is connecting..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:390 +#, c-format +msgid "^BG%s^F3 connected" +msgstr "" + +#: qcsrc/common/notifications/all.inc:391 +#, c-format +msgid "^BG%s^F3 connected and joined the ^TC^TT team" +msgstr "" + +#: qcsrc/common/notifications/all.inc:392 +#, c-format +msgid "^BG%s^F3 is now playing" +msgstr "" + +#: qcsrc/common/notifications/all.inc:393 +#, c-format +msgid "^BG%s^F3 is now playing on the ^TC^TT team" +msgstr "" + +#: qcsrc/common/notifications/all.inc:395 +#: qcsrc/common/notifications/all.inc:706 +#, c-format +msgid "^BG%s^BG has dropped the ball!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:396 +#: qcsrc/common/notifications/all.inc:707 +#, c-format +msgid "^BG%s^BG has picked up the ball!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:398 +#, c-format +msgid "^BG%s^BG captured the keys for the ^TC^TT team" +msgstr "" + +#: qcsrc/common/notifications/all.inc:399 +#, c-format +msgid "^BG%s^BG dropped the ^TC^TT Key" +msgstr "" + +#: qcsrc/common/notifications/all.inc:400 +#, c-format +msgid "^BG%s^BG lost the ^TC^TT Key" +msgstr "" + +#: qcsrc/common/notifications/all.inc:401 +#, c-format +msgid "^BG%s^BG pushed %s^BG causing the ^TC^TT Key ^BGdestruction" +msgstr "" + +#: qcsrc/common/notifications/all.inc:402 +#, c-format +msgid "^BG%s^BG destroyed the ^TC^TT Key" +msgstr "" + +#: qcsrc/common/notifications/all.inc:403 +#, c-format +msgid "^BG%s^BG picked up the ^TC^TT Key" +msgstr "" + +#: qcsrc/common/notifications/all.inc:405 +#, c-format +msgid "^BG%s^F3 forfeited" +msgstr "" + +#: qcsrc/common/notifications/all.inc:406 +#, c-format +msgid "^BG%s^F3 has no more lives left" +msgstr "" + +#: qcsrc/common/notifications/all.inc:408 +msgid "^BGMonsters are currently disabled" +msgstr "" + +#: qcsrc/common/notifications/all.inc:410 +msgid "^BGThe ^TC^TT^BG team held the ball for too long" +msgstr "" + +#: qcsrc/common/notifications/all.inc:412 +#, c-format +msgid "^BG%s^BG captured %s^BG control point" +msgstr "" + +#: qcsrc/common/notifications/all.inc:413 +#, c-format +msgid "^TC^TT^BG team %s^BG control point has been destroyed by %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:414 +msgid "^TC^TT^BG generator has been destroyed" +msgstr "" + +#: qcsrc/common/notifications/all.inc:415 +msgid "^TC^TT^BG generator spontaneously combusted due to overtime!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:417 +#, c-format +msgid "^BG%s^K1 picked up Invisibility" +msgstr "" + +#: qcsrc/common/notifications/all.inc:418 +#, c-format +msgid "^BG%s^K1 picked up Shield" +msgstr "" + +#: qcsrc/common/notifications/all.inc:419 +#, c-format +msgid "^BG%s^K1 picked up Speed" +msgstr "" + +#: qcsrc/common/notifications/all.inc:420 +#, c-format +msgid "^BG%s^K1 picked up Strength" +msgstr "" + +#: qcsrc/common/notifications/all.inc:422 +#, c-format +msgid "^BG%s^F3 disconnected" +msgstr "" + +#: qcsrc/common/notifications/all.inc:423 +#, c-format +msgid "^BG%s^F3 was kicked for idling" +msgstr "" + +#: qcsrc/common/notifications/all.inc:424 +msgid "" +"^F2You were kicked from the server because you are a spectator and " +"spectators aren't allowed at the moment." +msgstr "" + +#: qcsrc/common/notifications/all.inc:425 +#, c-format +msgid "^BG%s^F3 is now spectating" +msgstr "" + +#: qcsrc/common/notifications/all.inc:427 +#, c-format +msgid "^BG%s^BG has abandoned the race" +msgstr "" + +#: qcsrc/common/notifications/all.inc:428 +#, c-format +msgid "^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:429 +#, c-format +msgid "^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:430 +#, c-format +msgid "^BG%s^BG has finished the race" +msgstr "" + +#: qcsrc/common/notifications/all.inc:431 +#, c-format +msgid "^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:432 +#, c-format +msgid "^BG%s^BG improved their %s%s^BG place record with %s%s %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:433 +#, c-format +msgid "" +"^BG%s^BG scored a new record with ^F2%s^BG, but unfortunately lacks a UID " +"and will be lost." +msgstr "" + +#: qcsrc/common/notifications/all.inc:434 +#, c-format +msgid "" +"^BG%s^BG scored a new record with ^F2%s^BG, but is anonymous and will be " +"lost." +msgstr "" + +#: qcsrc/common/notifications/all.inc:435 +#, c-format +msgid "^BG%s^BG set the %s%s^BG place record with %s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:437 +#, c-format +msgid "" +"^F4You have been invited by ^BG%s^F4 to join their game of ^F2%s^F4 " +"(^F1%s^F4)" +msgstr "" + +#: qcsrc/common/notifications/all.inc:439 +msgid "^TC^TT ^BGteam scores!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:441 +#, c-format +msgid "" +"^F2You have to become a player within the next %s, otherwise you will be " +"kicked, because spectating isn't allowed at this time!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:443 +#, c-format +msgid "^BG%s^K1 picked up a Superweapon" +msgstr "" + +#: qcsrc/common/notifications/all.inc:445 +msgid "^BGYou cannot change to a larger team" +msgstr "" + +#: qcsrc/common/notifications/all.inc:446 +msgid "^BGYou are not allowed to change teams" +msgstr "" + +#: qcsrc/common/notifications/all.inc:448 +#, c-format +msgid "" +"^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have " +"^F2Xonotic %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:449 +#, c-format +msgid "" +"^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:450 +#, c-format +msgid "" +"^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get " +"the update from ^F3http://www.xonotic.org/^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:452 +#, c-format +msgid "^F3SVQC Build information: ^F4%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:454 +#, c-format +msgid "" +"^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:455 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:456 +#, c-format +msgid "^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:457 +#, c-format +msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Arc bolts%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:458 +#, c-format +msgid "^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:459 +#, c-format +msgid "^BG%s^K1 shot themself to hell with their Blaster%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:460 +#, c-format +msgid "^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:461 +#, c-format +msgid "^BG%s^K1 felt the strong pull of their Crylink%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:462 +#, c-format +msgid "^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:463 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's rocket%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:464 +#, c-format +msgid "^BG%s^K1 blew themself up with their Devastator%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:465 +#, c-format +msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:466 +#, c-format +msgid "^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:467 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:468 +#, c-format +msgid "^BG%s^K1 played with Electro bolts%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:469 +#, c-format +msgid "^BG%s^K1 could not remember where they put their Electro orb%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:470 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:471 +#, c-format +msgid "^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:472 +#, c-format +msgid "^BG%s^K1 should have used a smaller gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:473 +#, c-format +msgid "^BG%s^K1 forgot about their firemine%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:474 +#, c-format +msgid "^BG%s%s^K1 was pummeled by a burst of ^BG%s^K1's Hagar rockets%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:475 +#, c-format +msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Hagar rockets%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:476 +#, c-format +msgid "^BG%s^K1 played with tiny Hagar rockets%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:477 +#, c-format +msgid "^BG%s%s^K1 was cut down with ^BG%s^K1's HLAC%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:478 +#, c-format +msgid "^BG%s^K1 got a little jumpy with their HLAC%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:479 +#, c-format +msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Heavy Machine Gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:480 +#, c-format +msgid "^BG%s%s^K1 was torn to bits by ^BG%s^K1's Heavy Machine Gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:481 +#, c-format +msgid "^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:482 +#, c-format +msgid "" +"^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:483 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:484 +#, c-format +msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:485 +#, c-format +msgid "^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:486 +#: qcsrc/common/notifications/all.inc:790 +#, c-format +msgid "^BGYou cannot place more than ^F2%s^BG mines at a time" +msgstr "" + +#: qcsrc/common/notifications/all.inc:487 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:488 +#, c-format +msgid "^BG%s^K1 forgot about their mine%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:489 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:490 +#, c-format +msgid "^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:491 +#, c-format +msgid "^BG%s^K1 didn't see their own Mortar grenade%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:492 +#, c-format +msgid "^BG%s^K1 blew themself up with their own Mortar%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:493 +#, c-format +msgid "^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:494 +#, c-format +msgid "^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:495 +#, c-format +msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:496 +#, c-format +msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:497 +#, c-format +msgid "^BG%s%s^K1 was sawn in half by ^BG%s^K1's Rocket Propelled Chainsaw%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:498 +#, c-format +msgid "^BG%s%s^K1 almost dodged ^BG%s^K1's Rocket Propelled Chainsaw%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:499 +#, c-format +msgid "^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:500 +#, c-format +msgid "^BG%s^K1 blew themself up with their Rocket Propelled Chainsaw%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:501 +#, c-format +msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:502 +#, c-format +msgid "^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:503 +#, c-format +msgid "^BG%s^K1 played with tiny Seeker rockets%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:504 +#, c-format +msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:505 +#, c-format +msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:506 +#, c-format +msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:507 +#, c-format +msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:508 +#, c-format +msgid "^BG%s^K1 is now thinking with portals%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:509 +#, c-format +msgid "^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:510 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:511 +#, c-format +msgid "^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:512 +#, c-format +msgid "^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:537 +msgid "^F4You are now alone!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:539 +msgid "^BGYou are attacking!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:540 +msgid "^BGYou are defending!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:541 +#, c-format +msgid "^BGObjective destroyed in ^F4%s^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:543 +msgid "^F4Begin!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:544 +msgid "^F4Game starts in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:545 +msgid "^F4Round starts in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:546 +msgid "^F4Round cannot start" +msgstr "" + +#: qcsrc/common/notifications/all.inc:551 +msgid "^F2Don't camp!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:555 +msgid "" +"^BGYou are now free.\n" +"^BGFeel free to ^F2try to capture^BG the flag again\n" +"^BGif you think you will succeed." +msgstr "" + +#: qcsrc/common/notifications/all.inc:556 +msgid "^BGThis flag is currently inactive" +msgstr "" + +#: qcsrc/common/notifications/all.inc:557 +msgid "" +"^BGYou are now ^F1shielded^BG from the flag(s)\n" +"^BGfor ^F2too many unsuccessful attempts^BG to capture.\n" +"^BGMake some defensive scores before trying again." +msgstr "" + +#: qcsrc/common/notifications/all.inc:558 +msgid "^BGYou captured the ^TC^TT^BG flag!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:559 +msgid "^BGYou captured the flag!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:560 +#, c-format +msgid "^BGToo many flag throws! Throwing disabled for %s." +msgstr "" + +#: qcsrc/common/notifications/all.inc:561 +#, c-format +msgid "^BG%s^BG passed the ^TC^TT^BG flag to %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:562 +#, c-format +msgid "^BG%s^BG passed the flag to %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:563 +#, c-format +msgid "^BGYou received the ^TC^TT^BG flag from %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:564 +#, c-format +msgid "^BGYou received the flag from %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:565 +#, c-format +msgid "^BGPress ^F2%s^BG to receive the flag from %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.inc:566 +#, c-format +msgid "^BGRequesting %s^BG to pass you the flag" +msgstr "" + +#: qcsrc/common/notifications/all.inc:567 +#, c-format +msgid "^BGYou passed the ^TC^TT^BG flag to %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:568 +#, c-format +msgid "^BGYou passed the flag to %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:569 +msgid "^BGYou got the ^TC^TT^BG flag!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:570 +msgid "^BGYou got the flag!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:571 +#, c-format +msgid "^BGYou got your %steam^BG's flag, return it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:572 +#, c-format +msgid "^BGYou got the %senemy^BG's flag, return it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:573 +#, c-format +msgid "^BGThe %senemy^BG got your flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:574 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:575 +#, c-format +msgid "^BGThe %senemy^BG got the flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:576 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got the flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:577 +#, c-format +msgid "^BGThe %senemy^BG got their flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:578 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got their flag! Retrieve it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:579 +#, c-format +msgid "^BGYour %steam mate^BG got the ^TC^TT^BG flag! Protect them!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:580 +#, c-format +msgid "^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:581 +#, c-format +msgid "^BGYour %steam mate^BG got the flag! Protect them!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:582 +#, c-format +msgid "^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:583 +msgid "^BGEnemies can now see you on radar!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:584 +msgid "^BGYou returned the ^TC^TT^BG flag!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:585 +msgid "^BGStalemate! Enemies can now see you on radar!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:586 +msgid "^BGStalemate! Flag carriers can now be seen by enemies on radar!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:590 +#, c-format +msgid "^K3%sYou fragged ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:591 +#: qcsrc/common/notifications/all.inc:600 +#: qcsrc/common/notifications/all.inc:609 +#, c-format +msgid "^K3%sYou scored against ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:592 +#, c-format +msgid "^K1%sYou were fragged by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:593 +#: qcsrc/common/notifications/all.inc:602 +#: qcsrc/common/notifications/all.inc:611 +#, c-format +msgid "^K1%sYou were scored against by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:599 +#, c-format +msgid "^K3%sYou burned ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:601 +#, c-format +msgid "^K1%sYou were burned by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:608 +#, c-format +msgid "^K3%sYou froze ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:610 +#, c-format +msgid "^K1%sYou were frozen by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:617 +#, c-format +msgid "^K1%sYou typefragged ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:618 +#, c-format +msgid "^K1%sYou scored against ^BG%s^K1 while they were typing" +msgstr "" + +#: qcsrc/common/notifications/all.inc:619 +#, c-format +msgid "^K1%sYou were typefragged by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:620 +#, c-format +msgid "^K1%sYou were scored against by ^BG%s^K1 while typing" +msgstr "" + +#: qcsrc/common/notifications/all.inc:626 +#, c-format +msgid "^BGPress ^F2%s^BG again to toss the nade!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:627 +msgid "^F2You got a ^K1BONUS GRENADE^F2!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:629 +#, c-format +msgid "" +"^BGYou have been moved into a different team\n" +"You are now on: %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:630 +msgid "^K1Don't go against your team mates!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:630 +msgid "^K1Don't shoot your team mates!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:631 +msgid "^K1Die camper!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:631 +msgid "^K1Reconsider your tactics, camper!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:632 +msgid "^K1You unfairly eliminated yourself!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:633 +#, c-format +msgid "^K1You were %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:634 +msgid "^K1You couldn't catch your breath!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:635 +msgid "^K1You hit the ground with a crunch!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:636 +msgid "^K1You felt a little too hot!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:636 +msgid "^K1You got a little bit too crispy!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:637 +msgid "^K1You killed your own dumb self!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:637 +msgid "^K1You need to be more careful!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:638 +msgid "^K1You couldn't stand the heat!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:639 +msgid "^K1You need to watch out for monsters!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:639 +msgid "^K1You were killed by a monster!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:640 +msgid "^K1Tastes like chicken!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:640 +msgid "^K1You forgot to put the pin back in!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:641 +msgid "^K1Hanging around a napalm explosion is bad!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:642 +msgid "^K1You felt a little chilly!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:642 +msgid "^K1You got a little bit too cold!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:643 +msgid "^K1Your Healing Nade is a bit defective" +msgstr "" + +#: qcsrc/common/notifications/all.inc:644 +msgid "^K1You are respawning for running out of ammo..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:644 +msgid "^K1You were killed for running out of ammo..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:645 +msgid "^K1You grew too old without taking your medicine" +msgstr "" + +#: qcsrc/common/notifications/all.inc:645 +msgid "^K1You need to preserve your health" +msgstr "" + +#: qcsrc/common/notifications/all.inc:646 +msgid "^K1You became a shooting star!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:647 +msgid "^K1You melted away in slime!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:648 +msgid "^K1You committed suicide!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:648 +msgid "^K1You ended it all!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:649 +msgid "^K1You got stuck in a swamp!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:650 +#, c-format +msgid "^BGYou are now on: %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:651 +msgid "^K1You died in an accident!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:652 +msgid "^K1You had an unfortunate run in with a turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:652 +msgid "^K1You were fragged by a turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:653 +msgid "^K1You had an unfortunate run in with an eWheel turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:653 +msgid "^K1You were fragged by an eWheel turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:654 +msgid "^K1You had an unfortunate run in with a Walker turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:654 +msgid "^K1You were fragged by a Walker turret!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:655 +msgid "^K1You got caught in the blast of a Bumblebee explosion!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:656 +msgid "^K1You were crushed by a vehicle!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:657 +msgid "^K1You were caught in a Raptor cluster bomb!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:658 +msgid "^K1You got caught in the blast of a Raptor explosion!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:659 +msgid "^K1You got caught in the blast of a Spiderbot explosion!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:660 +msgid "^K1You were blasted to bits by a Spiderbot rocket!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:661 +msgid "^K1You got caught in the blast of a Racer explosion!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:662 +msgid "^K1You couldn't find shelter from a Racer rocket!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:663 +msgid "^K1Watch your step!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:665 +#, c-format +msgid "^K1Moron! You fragged ^BG%s^K1, a team mate!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:665 +#, c-format +msgid "^K1Moron! You went against ^BG%s^K1, a team mate!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:666 +#, c-format +msgid "^K1You were fragged by ^BG%s^K1, a team mate" +msgstr "" + +#: qcsrc/common/notifications/all.inc:666 +#, c-format +msgid "^K1You were scored against by ^BG%s^K1, a team mate" +msgstr "" + +#: qcsrc/common/notifications/all.inc:668 +msgid "" +"^K1Stop idling!\n" +"^BGDisconnecting in ^COUNT..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:670 +#, c-format +msgid "^BGYou need %s^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:671 +#, c-format +msgid "^BGYou also need %s^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:672 +msgid "^BGDoor unlocked!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:674 +msgid "^F2You picked up some extra lives" +msgstr "" + +#: qcsrc/common/notifications/all.inc:676 +#, c-format +msgid "^K3You revived ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:677 +msgid "^K3You revived yourself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:678 +#, c-format +msgid "^K3You were revived by ^BG%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:679 +#, c-format +msgid "^K3You were automatically revived after %s second(s)" +msgstr "" + +#: qcsrc/common/notifications/all.inc:681 +msgid "^BGThe generator is under attack!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:683 +msgid "^TC^TT^BG team loses the round" +msgstr "" + +#: qcsrc/common/notifications/all.inc:687 +msgid "^K1You froze yourself" +msgstr "" + +#: qcsrc/common/notifications/all.inc:688 +msgid "^K1Round already started, you spawn as frozen" +msgstr "" + +#: qcsrc/common/notifications/all.inc:690 +#, c-format +msgid "^K1A %s has arrived!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:694 +msgid "^BGYou got the ^F1Fuel regenerator" +msgstr "" + +#: qcsrc/common/notifications/all.inc:695 +msgid "^BGYou got the ^F1Jet pack" +msgstr "" + +#: qcsrc/common/notifications/all.inc:703 +msgid "" +"^K1No spawnpoints available!\n" +"Hope your team can fix it..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:704 +msgid "" +"^K1You may not join the game at this time.\n" +"The player limit reached maximum capacity." +msgstr "" + +#: qcsrc/common/notifications/all.inc:708 +msgid "^BGYou picked up the ball" +msgstr "" + +#: qcsrc/common/notifications/all.inc:709 +msgid "^BGKilling people while you don't have the ball gives no points!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:711 +msgid "" +"^BGAll keys are in your team's hands!\n" +"Help the key carriers to meet!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:712 +msgid "" +"^BGAll keys are in ^TC^TT team^BG's hands!\n" +"Interfere ^F4NOW^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:713 +msgid "" +"^BGAll keys are in your team's hands!\n" +"Meet the other key carriers ^F4NOW^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:714 +msgid "^F4Round will start in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:715 +msgid "^BGScanning frequency range..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:716 +msgid "^BGYou are starting with the ^TC^TT Key" +msgstr "" + +#: qcsrc/common/notifications/all.inc:718 +msgid "^BGYou have no lives left, you must wait until the next match" +msgstr "" + +#: qcsrc/common/notifications/all.inc:720 +#, c-format +msgid "" +"^BGWaiting for players to join...\n" +"Need active players for: %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:721 +#, c-format +msgid "^BGWaiting for %s player(s) to join..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:723 +msgid "^BGYour weapon has been downgraded until you find some ammo!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:724 +msgid "^F4^COUNT^BG left to find some ammo!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:725 +msgid "^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:725 +msgid "^BGGet some ammo! ^F4^COUNT^BG left!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:726 +#, c-format +msgid "^F2Extra lives remaining: ^K1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:730 +#, c-format +msgid "" +"^F2^COUNT^BG until weapon change...\n" +"Next weapon: ^F1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:731 +#, c-format +msgid "^F2Active weapon: ^F1%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:733 +#, c-format +msgid "^BGYou captured %s^BG control point" +msgstr "" + +#: qcsrc/common/notifications/all.inc:734 +#, c-format +msgid "^TC^TT^BG team captured %s^BG control point" +msgstr "" + +#: qcsrc/common/notifications/all.inc:735 +msgid "^BGThis control point currently cannot be captured" +msgstr "" + +#: qcsrc/common/notifications/all.inc:736 +msgid "" +"^BGThe enemy generator cannot be destroyed yet\n" +"^F2Capture some control points to unshield it" +msgstr "" + +#: qcsrc/common/notifications/all.inc:737 +msgid "^BGThe ^TCenemy^BG generator is no longer shielded!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:738 +msgid "" +"^K1Your generator is NOT shielded!\n" +"^BGRe-capture control points to shield it!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:739 +#, c-format +msgid "^BGPress ^F2%s^BG to teleport" +msgstr "" + +#: qcsrc/common/notifications/all.inc:740 +#, c-format +msgid "^BGTeleporting disabled for %s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:742 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"Keep fragging until we have a winner!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:742 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"Keep scoring until we have a winner!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:743 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"\n" +"Generators are now decaying.\n" +"The more control points your team holds,\n" +"the faster the enemy generator decays" +msgstr "" + +#: qcsrc/common/notifications/all.inc:744 +#, c-format +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"^BGAdded ^F4%s^BG to the game!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:746 +msgid "^K1In^BG-portal created" +msgstr "" + +#: qcsrc/common/notifications/all.inc:747 +msgid "^F3Out^BG-portal created" +msgstr "" + +#: qcsrc/common/notifications/all.inc:748 +msgid "^F1Portal creation failed" +msgstr "" + +#: qcsrc/common/notifications/all.inc:750 +msgid "^F2Strength infuses your weapons with devastating power" +msgstr "" + +#: qcsrc/common/notifications/all.inc:751 +msgid "^F2Strength has worn off" +msgstr "" + +#: qcsrc/common/notifications/all.inc:753 +msgid "^F2Shield surrounds you" +msgstr "" + +#: qcsrc/common/notifications/all.inc:754 +msgid "^F2Shield has worn off" +msgstr "" + +#: qcsrc/common/notifications/all.inc:756 +msgid "^F2You are on speed" +msgstr "" + +#: qcsrc/common/notifications/all.inc:757 +msgid "^F2Speed has worn off" +msgstr "" + +#: qcsrc/common/notifications/all.inc:759 +msgid "^F2You are invisible" +msgstr "" + +#: qcsrc/common/notifications/all.inc:760 +msgid "^F2Invisibility has worn off" +msgstr "" + +#: qcsrc/common/notifications/all.inc:762 +msgid "^F2The race is over, finish your lap!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:764 +msgid "^BGSecondary fire inflicts no damage!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:766 +msgid "^BGSequence completed!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:767 +msgid "^BGThere are more to go..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:768 +#, c-format +msgid "^BGOnly %s^BG more to go..." +msgstr "" + +#: qcsrc/common/notifications/all.inc:770 +msgid "^F2Superweapons have broken down" +msgstr "" + +#: qcsrc/common/notifications/all.inc:771 +msgid "^F2Superweapons have been lost" +msgstr "" + +#: qcsrc/common/notifications/all.inc:772 +msgid "^F2You now have a superweapon" +msgstr "" + +#: qcsrc/common/notifications/all.inc:774 +msgid "^K1Changing to ^TC^TT^K1 in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:775 +msgid "^K1Changing team in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:776 +msgid "^K1Spectating in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:777 +msgid "^K1Suicide in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:779 +msgid "^F4Timeout begins in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:780 +msgid "^F4Timeout ends in ^COUNT" +msgstr "" + +#: qcsrc/common/notifications/all.inc:782 +msgid "^K1Cannot join given minigame session!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:784 +#, c-format +msgid "^BGPress ^F2%s^BG to enter/exit the vehicle" +msgstr "" + +#: qcsrc/common/notifications/all.inc:785 +#, c-format +msgid "^BGPress ^F2%s^BG to enter the vehicle gunner" +msgstr "" + +#: qcsrc/common/notifications/all.inc:786 +#, c-format +msgid "^BGPress ^F2%s^BG to steal this vehicle" +msgstr "" + +#: qcsrc/common/notifications/all.inc:787 +msgid "" +"^F2The enemy is stealing one of your vehicles!\n" +"^F4Stop them!" +msgstr "" + +#: qcsrc/common/notifications/all.inc:788 +msgid "^F2Intruder detected, disabling shields!" +msgstr "" + +#: qcsrc/common/notifications/all.qh:188 +msgid "Notification dump command only works with cl_cmd and sv_cmd.\n" +msgstr "" + +#: qcsrc/common/notifications/all.qh:399 qcsrc/common/notifications/all.qh:400 +#, c-format +msgid " (near %s)" +msgstr "" + +#: qcsrc/common/notifications/all.qh:407 qcsrc/common/notifications/all.qh:408 +msgid "primary" +msgstr "" + +#: qcsrc/common/notifications/all.qh:407 qcsrc/common/notifications/all.qh:408 +msgid "secondary" +msgstr "" + +#: qcsrc/common/notifications/all.qh:410 +msgid "point" +msgstr "" + +#: qcsrc/common/notifications/all.qh:410 +msgid "points" +msgstr "" + +#: qcsrc/common/notifications/all.qh:419 +msgid "drop flag" +msgstr "" + +#: qcsrc/common/notifications/all.qh:420 +msgid "throw nade" +msgstr "" + +#: qcsrc/common/notifications/all.qh:431 +#, c-format +msgid " with %s" +msgstr "" + +#: qcsrc/common/notifications/all.qh:444 +#, c-format +msgid "%s^K1 made a TRIPLE FRAG! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:444 +#, c-format +msgid "%s^K1 made a TRIPLE SCORE! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:444 +msgid "TRIPLE FRAG! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:445 +#, c-format +msgid "%s^K1 made FIVE SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:445 +#, c-format +msgid "%s^K1 unlocked RAGE! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:445 +msgid "RAGE! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:446 +#, c-format +msgid "%s^K1 made TEN SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:446 +#, c-format +msgid "%s^K1 started a MASSACRE! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:446 +msgid "MASSACRE! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:447 +#, c-format +msgid "%s^K1 executed MAYHEM! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:447 +#, c-format +msgid "%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:447 +msgid "MAYHEM! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:448 +#, c-format +msgid "%s^K1 is a BERSERKER! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:448 +#, c-format +msgid "%s^K1 made TWENTY SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:448 +msgid "BERSERKER! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:449 +#, c-format +msgid "%s^K1 inflicts CARNAGE! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:449 +#, c-format +msgid "%s^K1 made TWENTY FIVE SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:449 +msgid "CARNAGE! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:450 +#, c-format +msgid "%s^K1 made THIRTY SCORES IN A ROW! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:450 +#, c-format +msgid "%s^K1 unleashes ARMAGEDDON! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:450 +msgid "ARMAGEDDON! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:457 +#, c-format +msgid "%s(^F1Bot^BG)" +msgstr "" + +#: qcsrc/common/notifications/all.qh:459 +#, c-format +msgid "%s(Ping ^F1%d^BG)" +msgstr "" + +#: qcsrc/common/notifications/all.qh:466 +#, c-format +msgid "" +"\n" +"(Health ^1%d^BG / Armor ^2%d^BG)%s" +msgstr "" + +#: qcsrc/common/notifications/all.qh:468 +#, c-format +msgid "" +"\n" +"(^F4Dead^BG)%s" +msgstr "" + +#: qcsrc/common/notifications/all.qh:489 qcsrc/common/notifications/all.qh:502 +#, c-format +msgid "%d score spree! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:501 +#, c-format +msgid "%d frag spree! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:514 +msgid "First blood! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:514 +msgid "First score! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:518 +msgid "First casualty! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:518 +msgid "First victim! " +msgstr "" + +#: qcsrc/common/notifications/all.qh:559 +#, c-format +msgid "%s^K1 has %d frags in a row! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:560 +#, c-format +msgid "%s^K1 made %d scores in a row! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:578 +#, c-format +msgid "%s^K1 drew first blood! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:579 +#, c-format +msgid "%s^K1 got the first score! %s^BG" +msgstr "" + +#: qcsrc/common/notifications/all.qh:595 +#, c-format +msgid ", ending their %d frag spree" +msgstr "" + +#: qcsrc/common/notifications/all.qh:596 +#, c-format +msgid ", ending their %d score spree" +msgstr "" + +#: qcsrc/common/notifications/all.qh:610 +#, c-format +msgid ", losing their %d frag spree" +msgstr "" + +#: qcsrc/common/notifications/all.qh:611 +#, c-format +msgid ", losing their %d score spree" +msgstr "" + +#: qcsrc/common/teams.qh:29 +msgid "TEAM^Red" +msgstr "" + +#: qcsrc/common/teams.qh:30 +msgid "TEAM^Blue" +msgstr "" + +#: qcsrc/common/teams.qh:31 +msgid "TEAM^Yellow" +msgstr "" + +#: qcsrc/common/teams.qh:32 +msgid "TEAM^Pink" +msgstr "" + +#: qcsrc/common/teams.qh:33 +msgid "Team" +msgstr "" + +#: qcsrc/common/teams.qh:34 +msgid "Neutral" +msgstr "" + +#: qcsrc/common/teams.qh:37 +msgid "KEY^Red" +msgstr "" + +#: qcsrc/common/teams.qh:38 +msgid "KEY^Blue" +msgstr "" + +#: qcsrc/common/teams.qh:39 +msgid "KEY^Yellow" +msgstr "" + +#: qcsrc/common/teams.qh:40 +msgid "KEY^Pink" +msgstr "" + +#: qcsrc/common/teams.qh:41 +msgid "FLAG^Red" +msgstr "" + +#: qcsrc/common/teams.qh:42 +msgid "FLAG^Blue" +msgstr "" + +#: qcsrc/common/teams.qh:43 +msgid "FLAG^Yellow" +msgstr "" + +#: qcsrc/common/teams.qh:44 +msgid "FLAG^Pink" +msgstr "" + +#: qcsrc/common/teams.qh:45 +msgid "GENERATOR^Red" +msgstr "" + +#: qcsrc/common/teams.qh:46 +msgid "GENERATOR^Blue" +msgstr "" + +#: qcsrc/common/teams.qh:47 +msgid "GENERATOR^Yellow" +msgstr "" + +#: qcsrc/common/teams.qh:48 +msgid "GENERATOR^Pink" +msgstr "" + +#: qcsrc/common/turrets/all.qh:51 +msgid "Turrets dump command only works with sv_cmd.\n" +msgstr "" + +#: qcsrc/common/turrets/cl_turrets.qc:129 +#, c-format +msgid "%s under attack!" +msgstr "" + +#: qcsrc/common/turrets/turret.qh:11 +msgid "Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/ewheel.qh:15 +msgid "eWheel Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/ewheel_weapon.qh:7 +msgid "eWheel" +msgstr "" + +#: qcsrc/common/turrets/turret/flac.qh:13 +msgid "FLAC Cannon" +msgstr "" + +#: qcsrc/common/turrets/turret/flac_weapon.qh:7 +msgid "FLAC" +msgstr "" + +#: qcsrc/common/turrets/turret/fusionreactor.qh:11 +msgid "Fusion Reactor" +msgstr "" + +#: qcsrc/common/turrets/turret/hellion.qh:13 +msgid "Hellion Missile Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/hellion_weapon.qh:7 +msgid "Hellion" +msgstr "" + +#: qcsrc/common/turrets/turret/hk.qh:15 +msgid "Hunter-Killer Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/hk_weapon.qh:7 +msgid "Hunter-Killer" +msgstr "" + +#: qcsrc/common/turrets/turret/machinegun.qh:13 +msgid "Machinegun Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/machinegun_weapon.qh:7 +msgid "Machinegun" +msgstr "" + +#: qcsrc/common/turrets/turret/mlrs.qh:13 +msgid "MLRS Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/mlrs_weapon.qh:7 +msgid "MLRS" +msgstr "" + +#: qcsrc/common/turrets/turret/phaser.qh:13 +msgid "Phaser Cannon" +msgstr "" + +#: qcsrc/common/turrets/turret/phaser_weapon.qh:7 +msgid "Phaser" +msgstr "" + +#: qcsrc/common/turrets/turret/plasma.qh:13 +msgid "Plasma Cannon" +msgstr "" + +#: qcsrc/common/turrets/turret/plasma_dual.qh:7 +msgid "Dual plasma" +msgstr "" + +#: qcsrc/common/turrets/turret/plasma_dual.qh:19 +msgid "Dual Plasma Cannon" +msgstr "" + +#: qcsrc/common/turrets/turret/plasma_weapon.qh:7 +msgid "Plasma" +msgstr "" + +#: qcsrc/common/turrets/turret/tesla.qh:13 +#: qcsrc/common/turrets/turret/tesla_weapon.qh:7 +msgid "Tesla Coil" +msgstr "" + +#: qcsrc/common/turrets/turret/walker.qh:15 +msgid "Walker Turret" +msgstr "" + +#: qcsrc/common/turrets/turret/walker_weapon.qh:7 +msgid "Walker" +msgstr "" + +#: qcsrc/common/vehicles/cl_vehicles.qc:192 +#, c-format +msgid "Press %s" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qc:950 +msgid "No right gunner!" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qc:956 +msgid "No left gunner!" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qh:19 +msgid "Bumblebee" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/racer.qh:19 +msgid "Racer" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/racer_weapon.qh:9 +msgid "Racer cannon" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/raptor.qh:19 +msgid "Raptor" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:9 +msgid "Raptor cannon" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:17 +msgid "Raptor bomb" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:25 +msgid "Raptor flare" +msgstr "" + +#: qcsrc/common/vehicles/vehicle/spiderbot.qh:19 +msgid "Spiderbot" +msgstr "" + +#: qcsrc/common/weapons/all.qh:78 +msgid "Weapons dump command only works with sv_cmd.\n" +msgstr "" + +#: qcsrc/common/weapons/weapon/arc.qc:17 +msgid "Arc" +msgstr "" + +#: qcsrc/common/weapons/weapon/blaster.qc:17 +msgid "Blaster" +msgstr "" + +#: qcsrc/common/weapons/weapon/crylink.qc:17 +msgid "Crylink" +msgstr "" + +#: qcsrc/common/weapons/weapon/devastator.qc:17 +msgid "Devastator" +msgstr "" + +#: qcsrc/common/weapons/weapon/electro.qc:17 +msgid "Electro" +msgstr "" + +#: qcsrc/common/weapons/weapon/fireball.qc:17 +msgid "Fireball" +msgstr "" + +#: qcsrc/common/weapons/weapon/hagar.qc:17 +msgid "Hagar" +msgstr "" + +#: qcsrc/common/weapons/weapon/hlac.qc:17 +msgid "Heavy Laser Assault Cannon" +msgstr "" + +#: qcsrc/common/weapons/weapon/hook.qc:17 +msgid "Grappling Hook" +msgstr "" + +#: qcsrc/common/weapons/weapon/machinegun.qc:17 +msgid "MachineGun" +msgstr "" + +#: qcsrc/common/weapons/weapon/minelayer.qc:17 +msgid "Mine Layer" +msgstr "" + +#: qcsrc/common/weapons/weapon/mortar.qc:17 +msgid "Mortar" +msgstr "" + +#: qcsrc/common/weapons/weapon/porto.qc:17 +msgid "Port-O-Launch" +msgstr "" + +#: qcsrc/common/weapons/weapon/rifle.qc:18 +msgid "Rifle" +msgstr "" + +#: qcsrc/common/weapons/weapon/seeker.qc:17 +msgid "T.A.G. Seeker" +msgstr "" + +#: qcsrc/common/weapons/weapon/shockwave.qc:17 +msgid "Shockwave" +msgstr "" + +#: qcsrc/common/weapons/weapon/shotgun.qc:17 +msgid "Shotgun" +msgstr "" + +#: qcsrc/common/weapons/weapon/tuba.qc:17 +#, no-c-format +msgid "@!#%'n Tuba" +msgstr "" + +#: qcsrc/common/weapons/weapon/vaporizer.qc:18 +msgid "Vaporizer" +msgstr "" + +#: qcsrc/common/weapons/weapon/vortex.qc:18 +msgid "Vortex" +msgstr "" + +#: qcsrc/lib/counting.qh:9 +#, c-format +msgid "CI_DEC^%s years" +msgstr "" + +#: qcsrc/lib/counting.qh:12 +#, c-format +msgid "CI_ZER^%d years" +msgstr "" + +#: qcsrc/lib/counting.qh:13 +#, c-format +msgid "CI_FIR^%d year" +msgstr "" + +#: qcsrc/lib/counting.qh:14 +#, c-format +msgid "CI_SEC^%d years" +msgstr "" + +#: qcsrc/lib/counting.qh:15 +#, c-format +msgid "CI_THI^%d years" +msgstr "" + +#: qcsrc/lib/counting.qh:16 +#, c-format +msgid "CI_MUL^%d years" +msgstr "" + +#: qcsrc/lib/counting.qh:18 +#, c-format +msgid "CI_DEC^%s weeks" +msgstr "" + +#: qcsrc/lib/counting.qh:21 +#, c-format +msgid "CI_ZER^%d weeks" +msgstr "" + +#: qcsrc/lib/counting.qh:22 +#, c-format +msgid "CI_FIR^%d week" +msgstr "" + +#: qcsrc/lib/counting.qh:23 +#, c-format +msgid "CI_SEC^%d weeks" +msgstr "" + +#: qcsrc/lib/counting.qh:24 +#, c-format +msgid "CI_THI^%d weeks" +msgstr "" + +#: qcsrc/lib/counting.qh:25 +#, c-format +msgid "CI_MUL^%d weeks" +msgstr "" + +#: qcsrc/lib/counting.qh:27 +#, c-format +msgid "CI_DEC^%s days" +msgstr "" + +#: qcsrc/lib/counting.qh:30 +#, c-format +msgid "CI_ZER^%d days" +msgstr "" + +#: qcsrc/lib/counting.qh:31 +#, c-format +msgid "CI_FIR^%d day" +msgstr "" + +#: qcsrc/lib/counting.qh:32 +#, c-format +msgid "CI_SEC^%d days" +msgstr "" + +#: qcsrc/lib/counting.qh:33 +#, c-format +msgid "CI_THI^%d days" +msgstr "" + +#: qcsrc/lib/counting.qh:34 +#, c-format +msgid "CI_MUL^%d days" +msgstr "" + +#: qcsrc/lib/counting.qh:36 +#, c-format +msgid "CI_DEC^%s hours" +msgstr "" + +#: qcsrc/lib/counting.qh:39 +#, c-format +msgid "CI_ZER^%d hours" +msgstr "" + +#: qcsrc/lib/counting.qh:40 +#, c-format +msgid "CI_FIR^%d hour" +msgstr "" + +#: qcsrc/lib/counting.qh:41 +#, c-format +msgid "CI_SEC^%d hours" +msgstr "" + +#: qcsrc/lib/counting.qh:42 +#, c-format +msgid "CI_THI^%d hours" +msgstr "" + +#: qcsrc/lib/counting.qh:43 +#, c-format +msgid "CI_MUL^%d hours" +msgstr "" + +#: qcsrc/lib/counting.qh:46 +#, c-format +msgid "CI_DEC^%s minutes" +msgstr "" + +#: qcsrc/lib/counting.qh:49 +#, c-format +msgid "CI_ZER^%d minutes" +msgstr "" + +#: qcsrc/lib/counting.qh:50 +#, c-format +msgid "CI_FIR^%d minute" +msgstr "" + +#: qcsrc/lib/counting.qh:51 +#, c-format +msgid "CI_SEC^%d minutes" +msgstr "" + +#: qcsrc/lib/counting.qh:52 +#, c-format +msgid "CI_THI^%d minutes" +msgstr "" + +#: qcsrc/lib/counting.qh:53 +#, c-format +msgid "CI_MUL^%d minutes" +msgstr "" + +#: qcsrc/lib/counting.qh:55 +#, c-format +msgid "CI_DEC^%s seconds" +msgstr "" + +#: qcsrc/lib/counting.qh:58 +#, c-format +msgid "CI_ZER^%d seconds" +msgstr "" + +#: qcsrc/lib/counting.qh:59 +#, c-format +msgid "CI_FIR^%d second" +msgstr "" + +#: qcsrc/lib/counting.qh:60 +#, c-format +msgid "CI_SEC^%d seconds" +msgstr "" + +#: qcsrc/lib/counting.qh:61 +#, c-format +msgid "CI_THI^%d seconds" +msgstr "" + +#: qcsrc/lib/counting.qh:62 +#, c-format +msgid "CI_MUL^%d seconds" +msgstr "" + +#: qcsrc/lib/counting.qh:79 +#, c-format +msgid "%dst" +msgstr "" + +#: qcsrc/lib/counting.qh:80 +#, c-format +msgid "%dnd" +msgstr "" + +#: qcsrc/lib/counting.qh:81 +#, c-format +msgid "%drd" +msgstr "" + +#: qcsrc/lib/counting.qh:82 qcsrc/lib/counting.qh:85 +#, c-format +msgid "%dth" +msgstr "" + +#: qcsrc/lib/oo.qh:298 +msgid "No description" +msgstr "" + +#: qcsrc/lib/spawnfunc.qh:65 +#, c-format +msgid "" +"Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, " +"please file an issue." +msgstr "" + +#: qcsrc/lib/string.qh:48 +#, c-format +msgid "%d days, %02d:%02d:%02d" +msgstr "" + +#: qcsrc/lib/string.qh:49 +#, c-format +msgid "%02d:%02d:%02d" +msgstr "" + +#: qcsrc/menu/command/menu_cmd.qc:48 +msgid "Usage: menu_cmd command..., where possible commands are:\n" +msgstr "" + +#: qcsrc/menu/command/menu_cmd.qc:49 +msgid " sync - reloads all cvars on the current menu page\n" +msgstr "" + +#: qcsrc/menu/command/menu_cmd.qc:50 +msgid " directmenu ITEM - select a menu item as main item\n" +msgstr "" + +#: qcsrc/menu/command/menu_cmd.qc:79 +msgid "Available options:\n" +msgstr "" + +#: qcsrc/menu/command/menu_cmd.qc:128 +msgid "Invalid command. For a list of supported commands, try menu_cmd help.\n" +msgstr "" + +#: qcsrc/menu/item/listbox.qc:415 +#, c-format +msgid "Item %d" +msgstr "" + +#: qcsrc/menu/item/textslider.qc:11 qcsrc/menu/item/textslider.qc:12 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:37 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:68 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:115 +msgid "Custom" +msgstr "" + +#: qcsrc/menu/xonotic/campaign.qc:241 +#, c-format +msgid "Level %d: %s" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:4 +msgid "Core Team" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:16 +msgid "Extended Team" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:48 +msgid "Website" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:53 +msgid "Stats" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:57 +msgid "Art" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:65 +msgid "Animation" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:69 +msgid "Level Design" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:92 +msgid "Music / Sound FX" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:108 +msgid "Game Code" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:116 +msgid "Marketing / PR" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:122 +msgid "Legal" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:127 +msgid "Game Engine" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:131 +msgid "Engine Additions" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:136 +msgid "Compiler" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:142 +msgid "Other Active Contributors" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:149 +msgid "Translators" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:151 +msgid "Asturian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:156 +msgid "Belarusian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:159 +msgid "Bulgarian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:166 +msgid "Chinese (China)" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:172 +msgid "Chinese (Taiwan)" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:177 +msgid "Cornish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:180 +msgid "Czech" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:185 +msgid "Dutch" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:192 +msgid "English (Australia)" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:197 +msgid "Finnish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:202 +msgid "French" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:210 +msgid "German" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:221 +msgid "Greek" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:227 +msgid "Hungarian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:231 +msgid "Irish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:234 +msgid "Italian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:240 +msgid "Kazakh" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:243 +msgid "Korean" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:247 +msgid "Polish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:255 +msgid "Portuguese" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:261 +msgid "Romanian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:268 +msgid "Russian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:279 +msgid "Scottish Gaelic" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:282 +msgid "Serbian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:288 +msgid "Spanish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:299 +msgid "Swedish" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:303 +msgid "Ukrainian" +msgstr "" + +#: qcsrc/menu/xonotic/credits.qc:310 +msgid "Past Contributors" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:73 +msgid "forced to be saved to config.cfg" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:79 qcsrc/menu/xonotic/cvarlist.qc:89 +msgid "will not be saved" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:84 +msgid "will be saved to config.cfg" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:93 +msgid "private" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:95 +msgid "engine setting" +msgstr "" + +#: qcsrc/menu/xonotic/cvarlist.qc:97 +msgid "read only" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_credits.qc:13 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:38 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:287 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:85 +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:75 +#: qcsrc/menu/xonotic/dialog_singleplayer_winner.qc:14 +msgid "OK" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_credits.qh:7 +msgid "Credits" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_credits.qh:8 +msgid "The Xonotic credits" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:39 +msgid "" +"Welcome to Xonotic, please select your language preference and enter your " +"player name to get started. You can change these options later through the " +"menu system." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:45 +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:28 +msgid "Name:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:53 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:60 +msgid "Name under which you will appear in the game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:69 +msgid "Text language:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:78 +msgid "Allow player statistics to use your nickname at stats.xonotic.org?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:84 +msgid "Undecided" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:88 +msgid "Save settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_firstrun.qh:6 +msgid "Welcome" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:16 +msgid "Ammunition display:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:19 +msgid "Show only current ammo type" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:22 +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:44 +msgid "Noncurrent alpha:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:26 +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:48 +msgid "Noncurrent scale:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:30 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:24 +msgid "Align icon:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:31 +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:30 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:23 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:35 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:21 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:33 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:18 +msgid "Left" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:36 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:26 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:23 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:34 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:20 +msgid "Right" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh:6 +msgid "Ammo Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:17 +msgid "Message duration:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:21 +msgid "Fade time:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:25 +msgid "Flip messages order" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:15 +msgid "Text alignment:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:31 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:19 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:71 +msgid "Center" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:35 +msgid "Font scale:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh:6 +msgid "Centerprint Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:15 +msgid "Chat entries:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:18 +msgid "Chat size:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:22 +msgid "Chat lifetime:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:26 +msgid "Chat beep sound" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qh:6 +msgid "Chat Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc:14 +msgid "Engine info:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc:17 +msgid "Use an averaging algorithm for fps" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh:6 +msgid "Engine Info Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:15 +msgid "Combine health and armor" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:17 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:28 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:15 +msgid "Enable status bar" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:19 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:17 +msgid "Status bar alignment:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:37 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:35 +msgid "Inward" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:29 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:38 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:36 +msgid "Outward" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:30 +msgid "Icon alignment:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:40 +msgid "Flip health and armor positions" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh:6 +msgid "Health/Armor Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc:14 +msgid "Info messages:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc:17 +msgid "Flip align" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh:6 +msgid "Info Messages Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:16 +msgid "PNL^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:17 +msgid "PNL^Enabled spectating" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:18 +msgid "PNL^Enabled even playing in warmup" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:29 +msgid "Reduced" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:32 +msgid "Text/icon ratio:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:35 +msgid "Hide spawned items" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:37 +msgid "Hide big armor and health" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:39 +msgid "Dynamic size" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh:6 +msgid "Items Time Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh:6 +msgid "Mod Icons Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:15 +msgid "Notifications:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:18 +msgid "Also print notifications to the console" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:21 +msgid "Flip notify order" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:24 +msgid "Entry lifetime:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:28 +msgid "Entry fadetime:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qh:6 +msgid "Notification Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:15 +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:15 +msgid "Panel disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:16 +msgid "Panel enabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 +msgid "Panel enabled even observing" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:18 +msgid "Panel enabled only in Race/CTS" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:24 +msgid "Status bar" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:26 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:68 +msgid "Left align" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:27 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:74 +msgid "Right align" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:28 +msgid "Inward align" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:29 +msgid "Outward align" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:33 +msgid "Flip speed/acceleration positions" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:37 +msgid "Speed:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:38 +msgid "Include vertical speed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:49 +msgid "Speed unit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:51 +msgid "qu/s" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:52 +msgid "m/s" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:53 +msgid "km/h" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:54 +msgid "mph" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:55 +msgid "knots" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:57 +msgid "Show" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:60 +msgid "Top speed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:66 +msgid "Acceleration:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:67 +msgid "Include vertical acceleration" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qh:6 +msgid "Physics Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh:6 +msgid "Powerups Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:15 +msgid "Panel enabled when spectating" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:16 +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:17 +msgid "Panel always enabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:23 +msgid "Forced aspect:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh:6 +msgid "Pressed Keys Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh:6 +msgid "Quick Menu Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh:6 +msgid "Race Timer Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:16 +msgid "Panel enabled in teamgames" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:23 +msgid "Radar:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:26 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:68 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:107 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:54 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:87 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:103 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:45 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:67 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:118 +#: qcsrc/menu/xonotic/util.qc:792 +msgid "Alpha:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:30 +msgid "Rotation:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:32 +msgid "Forward" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:33 +msgid "West" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:34 +msgid "South" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:35 +msgid "East" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:36 +msgid "North" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:40 +msgid "Scale:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:44 +msgid "Zoom mode:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:46 +msgid "Zoomed in" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:47 +msgid "Zoomed out" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:48 +msgid "Always zoomed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:49 +msgid "Never zoomed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qh:6 +msgid "Radar Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:15 +msgid "Score:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:18 +msgid "Rankings:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:19 +msgid "Off" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:20 +msgid "And me" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:21 +msgid "Pure" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qh:6 +msgid "Score Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:14 +msgid "Timer:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:17 +msgid "Show elapsed time" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qh:6 +msgid "Timer Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_vote.qc:15 +msgid "Alpha after voting:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_vote.qh:6 +msgid "Vote Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:20 +msgid "Fade out after:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:22 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:167 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:139 +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:55 +msgid "Never" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:24 +#, c-format +msgid "%ds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:28 +msgid "Fade effect:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:31 +msgid "EF^None" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:32 +msgid "Alpha" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:33 +msgid "Slide" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:34 +msgid "EF^Both" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:38 +msgid "Weapon icons:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:41 +msgid "Show only owned weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:52 +msgid "Show weapon ID as:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:53 +msgid "SHOWAS^None" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:54 +msgid "Number" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:55 +msgid "Bind" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:58 +msgid "Weapon ID scale:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:64 +msgid "Show Accuracy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:65 +msgid "Show Ammo" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:68 +msgid "Ammo bar alpha:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:74 +msgid "Ammo bar color:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh:6 +msgid "Weapons Panel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:19 +msgid "HUD skins" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:22 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:196 +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:31 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:42 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:25 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:35 +msgid "Filter:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:30 +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:49 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:44 +msgid "Refresh" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:33 +#: qcsrc/menu/xonotic/dialog_settings_user.qc:30 +msgid "Set skin" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:37 +msgid "Save current skin" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:46 +msgid "Panel background defaults:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:48 +#: qcsrc/menu/xonotic/util.qc:767 +msgid "Background:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:50 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:62 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:77 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:116 +#: qcsrc/menu/xonotic/util.qc:770 qcsrc/menu/xonotic/util.qc:786 +#: qcsrc/menu/xonotic/util.qc:803 +msgid "Disable" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:60 +#: qcsrc/menu/xonotic/util.qc:783 +msgid "Border size:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:75 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:114 +msgid "Team color:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:83 +#: qcsrc/menu/xonotic/util.qc:809 +msgid "Test team color in configure mode" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:86 +#: qcsrc/menu/xonotic/util.qc:812 +msgid "Padding:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:93 +msgid "HUD Dock:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:95 +msgid "DOCK^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:96 +msgid "DOCK^Small" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:97 +msgid "DOCK^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:98 +msgid "DOCK^Large" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:121 +msgid "Grid settings:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:124 +msgid "Snap panels to grid" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:127 +msgid "Grid size:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:129 +msgid "X:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:136 +msgid "Y:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:145 +msgid "Exit setup" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qh:6 +msgid "Panel HUD Setup" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:13 +msgid "Monster:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:22 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:20 +msgid "Spawn" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:23 +#: qcsrc/menu/xonotic/serverlist.qc:268 +msgid "Remove" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:25 +msgid "Move target:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:26 +msgid "Follow" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:27 +msgid "Wander" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:28 +msgid "Spawnpoint" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:29 +msgid "No moving" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:31 +msgid "Colors:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:33 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:39 +msgid "Set skin:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_monstertools.qh:6 +msgid "Monster Tools" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:14 +msgid "Servers" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:15 +msgid "Find servers to play on" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:17 +msgid "Host your own game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:18 +msgid "Media" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:19 +msgid "Profile" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qh:6 +msgid "Multiplayer" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qh:7 +msgid "" +"Play online, against your friends in LAN, view demos or change player " +"settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:46 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:134 +#: qcsrc/menu/xonotic/skinlist.qc:88 qcsrc/menu/xonotic/util.qc:769 +#: qcsrc/menu/xonotic/util.qc:785 qcsrc/menu/xonotic/util.qc:794 +#: qcsrc/menu/xonotic/util.qc:802 qcsrc/menu/xonotic/util.qc:814 +msgid "Default" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:48 +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:64 +msgid "Unlimited" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:66 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:78 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:128 +msgid "Frag limit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:66 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:78 +msgid "The amount of frags needed before the match will end" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:67 +msgid "Capture limit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:67 +msgid "The amount of captures needed before the match will end" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:68 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:73 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:74 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:75 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:76 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:77 +msgid "Point limit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:68 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:77 +msgid "The amount of points needed before the match will end" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:70 +msgid "Lives:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:71 +msgid "Laps:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 +msgid "Goals:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 +msgid "The amount of goals needed before the match will end" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:97 +msgid "Gametype" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:102 +msgid "Time limit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:104 +msgid "Timelimit in minutes that when hit, will end the match" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:105 +#, c-format +msgid "%d minutes" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:106 +msgid "TIMLIM^Default" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:107 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:159 +msgid "1 minute" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:124 +msgid "TIMLIM^Infinite" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:132 +msgid "Teams:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:135 +msgid "2 teams" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:136 +msgid "3 teams" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:137 +msgid "4 teams" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:140 +msgid "Player slots:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:142 +msgid "" +"The maximum amount of players or bots that can be connected to your server " +"at once" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:144 +msgid "Number of bots:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:146 +msgid "Amount of bots on your server" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:148 +msgid "Bot skill:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:151 +msgid "Specify how experienced the bots will be" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:152 +msgid "Botlike" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:153 +msgid "Beginner" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:154 +msgid "You will win" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:155 +msgid "You can win" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:156 +msgid "You might win" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:157 +msgid "Advanced" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:158 +msgid "Expert" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:159 +msgid "Pro" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:160 +msgid "Assassin" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:161 +msgid "Unhuman" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:162 +msgid "Godlike" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:178 +msgid "Mutators..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:179 +msgid "Mutators and weapon arenas" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:188 +msgid "Maplist" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:198 +msgid "" +"Click here or Ctrl-F to provide a keyword to narrow down the map list. Ctrl-" +"Delete to clear; Enter when done." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:207 +msgid "Add shown" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:208 +msgid "Add the maps shown in the list to your selection" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:211 +msgid "Remove shown" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:212 +msgid "Remove the maps shown in the list from your selection" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:217 +msgid "Add all" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:218 +msgid "Add every available map to your selection" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:221 +msgid "Remove all" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:222 +msgid "Remove all the maps from your selection" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:229 +msgid "Start Multiplayer!" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:58 +msgid "Title:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:64 +msgid "Author:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:70 +msgid "Game types:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:93 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:296 +msgid "Close" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:96 +msgid "MAP^Play" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh:7 +msgid "Map Information" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:28 +msgid "All Weapons Arena" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:30 +msgid "Most Weapons Arena" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:49 +#, c-format +msgid "%s Arena" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:61 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:159 +msgid "Dodging" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:63 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:267 +msgid "InstaGib" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:216 +msgid "New Toys" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:67 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:272 +msgid "NIX" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:220 +msgid "Rocket Flying" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:71 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:212 +msgid "Invincible Projectiles" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:75 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:282 +msgid "No start weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:77 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:195 +msgid "Low gravity" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:79 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:166 +msgid "Cloaked" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:81 +msgid "Hook" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:83 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:173 +msgid "Midair" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:87 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:224 +msgid "Piñata" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:89 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:229 +msgid "Weapons stay" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:91 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:184 +msgid "Blood loss" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:93 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:208 +msgid "Jet pack" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:95 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:170 +msgid "Buffs" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:97 +msgid "Overkill" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:99 +msgid "No powerups" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:101 +msgid "Powerups" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:103 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:163 +msgid "Touch explode" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:105 +msgid "MUT^None" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:156 +msgid "Gameplay mutators:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:160 +msgid "Enable dodging" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:167 +msgid "All players are almost invisible" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:174 +msgid "Only possible to inflict damage on your enemy while he's airborne" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 +msgid "Damage done to your enemy gets added to your own health" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 +msgid "" +"Amount of health below which your player gets stunned because of blood loss" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:192 +msgid "Make things fall to the ground slower, lower value means lower gravity" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:201 +msgid "Weapon & item mutators:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:204 +msgid "Grappling hook" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:205 +msgid "Players spawn with the grappling hook" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:209 +msgid "Players spawn with the jetpack" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:225 +msgid "Players will drop all weapons they possessed when they are killed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:230 +msgid "Weapons stay after they are picked up" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:235 +msgid "Regular (no arena)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:237 +msgid "Weapon arenas:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:238 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:256 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:261 +msgid "" +"Selecting a weapon arena will give all players that weapon at spawn as well " +"as unlimited ammo, and disable all other weapon pickups." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 +msgid "Most weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:260 +msgid "All weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:264 +msgid "Special arenas:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:268 +msgid "" +"Players will be given only one weapon, which can instantly kill the opponent " +"with a single shot. If the player runs out of ammo, he will have 10 seconds " +"to find some or if he fails to do so, face death. The secondary fire mode " +"does not inflict any damage but is good for doing trickjumps." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:273 +msgid "" +"No items Xonotic - instead of pickup items, everyone plays with the same " +"weapon. After some time, a countdown will start, after which everyone will " +"switch to another weapon." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:277 +msgid "with blaster" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:278 +msgid "Always carry the blaster as an additional weapon in Nix" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh:9 +msgid "Mutators" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:38 +msgid "SRVS^Categories" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:41 +msgid "SRVS^Empty" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:42 +msgid "Show empty servers" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:46 +msgid "SRVS^Full" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:47 +msgid "Show full servers that have no slots available" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:51 +msgid "Pause" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:52 +msgid "" +"Pause updating the server list to prevent servers from \"jumping around\"" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 +msgid "Reload the server list" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:67 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:223 +msgid "Address:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:78 +msgid "Info..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:79 +msgid "Show more information about the currently highlighted server" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:84 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:303 +msgid "Join!" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:154 +#: qcsrc/menu/xonotic/serverlist.qc:1061 +msgid "MOD^Default" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +#, c-format +msgid "%d modified" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +msgid "Official" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:169 +msgid "N/A (auth library missing, can't connect)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:171 +msgid "N/A (auth library missing)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:177 +msgid "Not supported (can't connect)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:179 +msgid "Not supported (won't encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:183 +msgid "Supported (will encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:185 +msgid "Supported (won't encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:189 +msgid "Requested (will encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:191 +msgid "Requested (won't encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:195 +msgid "Required (can't connect)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:197 +msgid "Required (will encrypt)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:217 +msgid "Hostname:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:231 +msgid "Gametype:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:236 +msgid "Map:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:241 +msgid "Mod:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:246 +msgid "Version:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:251 +msgid "Settings:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:258 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:290 +msgid "Players:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:263 +msgid "Bots:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:268 +msgid "Free slots:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:274 +msgid "Encryption:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:279 +msgid "ID:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:284 +msgid "Key:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh:7 +msgid "Server Information" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:25 +msgid "Demos" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:26 +msgid "Screenshots" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:27 +msgid "Music Player" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:48 +msgid "Auto record demos" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:57 +msgid "Timedemo" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:58 +msgid "Benchmark how fast your computer can run the highlighted demo" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:62 +msgid "DEMO^Play" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:13 +msgid "Playing a demo will disconnect you from the current match." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:15 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:15 +msgid "Do you really wish to disconnect now?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh:6 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh:6 +msgid "Disconnect" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:13 +msgid "Timing a demo will disconnect you from the current match." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:37 +msgid "MUSICPL^Add" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:40 +msgid "MUSICPL^Add all" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:44 +msgid "Set as menu track" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:48 +msgid "Reset default menu track" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:54 +msgid "Playlist:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:55 +msgid "Random order" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:60 +msgid "MUSICPL^Stop" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:63 +msgid "MUSICPL^Play" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:66 +msgid "MUSICPL^Pause" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:69 +msgid "MUSICPL^Prev" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:72 +msgid "MUSICPL^Next" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:76 +msgid "MUSICPL^Remove" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:79 +msgid "MUSICPL^Remove all" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 +msgid "Auto screenshot scoreboard" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:63 +msgid "Open in the viewer" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:139 +msgid "Reset" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:144 +msgid "Previous" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:147 +msgid "Next" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:152 +msgid "Slide show" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:34 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:21 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:37 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:25 +#: qcsrc/menu/xonotic/dialog_settings_user.qc:20 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:21 +msgid "Apply immediately" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:48 +msgid "Name" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:77 +msgid "Model" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:96 +msgid "Glowing color" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:106 +msgid "Detail color" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:121 +msgid "Statistics" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:125 +msgid "Allow player statistics to track your client" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:129 +msgid "Allow player statistics to use your nickname" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:145 +msgid "Country" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:159 +msgid "Gender:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:161 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:174 +msgid "Undisclosed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:162 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:172 +msgid "Female" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:163 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:173 +msgid "Male" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:166 +msgid "Gender" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_quit.qc:11 +msgid "Are you sure you want to quit?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_quit.qc:15 +msgid "Back to work..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_quit.qc:17 +msgid "I got some more fragging to do!" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_quit.qh:7 +msgid "Quit the game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:15 +msgid "Model:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:21 +msgid "Remove *" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:23 +msgid "Copy *" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:24 +msgid "Paste" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:26 +msgid "Bone:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:31 +msgid "Set * as child" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:32 +msgid "Attach to *" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:34 +msgid "Detach from *" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:37 +msgid "Visual object properties for *:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:41 +msgid "Set alpha:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:44 +msgid "Set color main:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:46 +msgid "Set color glow:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:50 +msgid "Set frame:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:54 +msgid "Physical object properties for *:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:56 +msgid "Set material:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:62 +msgid "Set solidity:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:63 +msgid "Non-solid" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:64 +msgid "Solid" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:65 +msgid "Set physics:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:66 +msgid "Static" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:67 +msgid "Movable" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:68 +msgid "Physical" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:70 +msgid "Set scale:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:72 +msgid "Set force:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:76 +msgid "Claim *" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:78 +msgid "* object info" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:79 +msgid "* mesh info" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:80 +msgid "* attachment info" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:81 +msgid "Show help" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:82 +msgid "* is the object you are facing" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qh:6 +msgid "Sandbox Tools" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:18 +msgid "Video" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:19 +msgid "Effects" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:20 +msgid "Audio" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:22 +msgid "Game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:23 +msgid "Input" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:24 +msgid "User" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qc:25 +#: qcsrc/menu/xonotic/keybinder.qc:105 +msgid "Misc" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qh:6 +msgid "Settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings.qh:7 +msgid "Change the game settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:29 +msgid "Master:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:35 +msgid "Music:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:43 +msgid "VOL^Ambient:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:50 +msgid "Info:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:57 +msgid "Items:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:64 +msgid "Pain:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:71 +msgid "Player:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:78 +msgid "Shots:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:85 +msgid "Voice:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:93 +msgid "Weapons:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:99 +msgid "New style sound attenuation" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:102 +msgid "Mute sounds when not active" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:105 +msgid "Frequency:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:107 +msgid "Sound output frequency" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:108 +msgid "8 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:109 +msgid "11.025 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:110 +msgid "16 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:111 +msgid "22.05 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:112 +msgid "24 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:113 +msgid "32 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:114 +msgid "44.1 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:115 +msgid "48 kHz" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:119 +msgid "Channels:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:121 +msgid "Number of channels for the sound output" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:122 +msgid "Mono" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:123 +msgid "Stereo" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:124 +msgid "2.1" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:125 +msgid "4" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:126 +msgid "5" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:127 +msgid "5.1" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:128 +msgid "6.1" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:129 +msgid "7.1" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:134 +msgid "Swap stereo output channels" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:135 +msgid "Swap left/right channels" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:138 +msgid "Headphone friendly mode" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:139 +msgid "" +"Enable spatialization (blend the right and left channel slightly to decrease " +"stereo separation a bit for headphones)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:143 +msgid "Hit indication sound" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:144 +msgid "Play a hit indicator sound when your shot hits an enemy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:147 +msgid "Chat message sound" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:149 +msgid "Menu sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:150 +msgid "Play sounds when clicking menu items" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:151 +msgid "Focus sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:152 +msgid "Play sounds when hovering over menu items too" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:156 +msgid "Time announcer:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:158 +msgid "WRN^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:160 +msgid "5 minutes" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:161 +msgid "WRN^Both" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:164 +msgid "Automatic taunts:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:166 +msgid "Automatically taunt enemies after fragging them" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:168 +msgid "Sometimes" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:169 +msgid "Often" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:170 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:141 +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:57 +msgid "Always" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:176 +msgid "Debug info about sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:41 +msgid "Quality preset:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:45 +msgid "PRE^OMG!" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:48 +msgid "PRE^Low" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:50 +msgid "PRE^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:52 +msgid "PRE^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:54 +msgid "PRE^High" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:56 +msgid "PRE^Ultra" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:60 +msgid "PRE^Ultimate" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:65 +msgid "Geometry detail:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:67 +msgid "Change the smoothness of the curves on the map (default: normal)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:68 +msgid "DET^Lowest" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:69 +msgid "DET^Low" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:70 +msgid "DET^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:71 +msgid "DET^Good" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:72 +msgid "DET^Best" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:73 +msgid "DET^Insane" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:77 +msgid "Player detail:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:79 +msgid "PDET^Low" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:80 +msgid "PDET^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:81 +msgid "PDET^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:82 +msgid "PDET^Good" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:83 +msgid "PDET^Best" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:87 +msgid "Texture resolution:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:91 +msgid "RES^Leet" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:92 +msgid "RES^Lowest" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:93 +msgid "RES^Very low" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:94 +msgid "RES^Low" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:95 +msgid "RES^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:96 +msgid "RES^Good" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:97 +msgid "RES^Best" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:110 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:115 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:120 +msgid "Avoid lossy texture compression" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:131 +msgid "Show surfaces" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:132 +msgid "" +"Disable textures completely for very slow hardware. This gives a huge " +"performance boost, but looks very ugly. (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:135 +msgid "Use lightmaps" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:136 +msgid "" +"Use high resolution lightmaps, which will look pretty but use up some extra " +"video memory (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:138 +msgid "Deluxe mapping" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:139 +msgid "Use per-pixel lighting effects (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:141 +msgid "Gloss" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:142 +msgid "" +"Enable the use of glossmaps on textures supporting it (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:145 +msgid "Offset mapping" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:146 +msgid "" +"Offset mapping effect that will make textures with bumpmaps appear like they " +"\"pop out\" of the flat 2D surface (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:148 +msgid "Relief mapping" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:149 +msgid "" +"Higher quality offset mapping, which also has a huge impact on performance " +"(default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:152 +msgid "Reflections:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:153 +msgid "" +"Reflection and refraction quality, has a huge impact on performance on maps " +"with reflecting surfaces (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:156 +msgid "Resolution of reflections/refractions (default: good)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:157 +msgid "Blurred" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:158 +msgid "REFL^Good" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:159 +msgid "Sharp" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:163 +msgid "Decals" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 +msgid "Enable decals (bullet holes and blood) (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:165 +msgid "Decals on models" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:169 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:253 +msgid "Distance:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:172 +msgid "Decals further away than this will not be drawn (default: 300)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:176 +msgid "Time:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:179 +msgid "Time in seconds before decals fade away (default: 2)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:183 +msgid "Damage effects:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:185 +msgid "DMGFX^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:186 +msgid "Skeletal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:187 +msgid "DMGFX^All" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:191 +msgid "No dynamic lighting" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:192 +msgid "Enable corona flares around certain lights (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:194 +msgid "Fake corona lighting" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:195 +msgid "" +"Enable faster but uglier dynamic lights by rendering bright coronas instead " +"of real dynamic lights (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:198 +msgid "Realtime dynamic lighting" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:199 +msgid "" +"Enable rendering of dynamic lights such as explosions and rocket lights " +"(default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:201 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:207 +msgid "Shadows" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:202 +msgid "Enable rendering of shadows from dynamic lights (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:205 +msgid "Realtime world lighting" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:206 +msgid "" +"Enable rendering of full realtime world lighting on maps that support it. " +"Note that this might have a big impact on performance. (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:208 +msgid "" +"Enable rendering of shadows from realtime world lights (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:212 +msgid "Use normal maps" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:213 +msgid "Enable use of directional shading on textures (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:215 +msgid "Soft shadows" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:219 +msgid "Fade corona according to visibility" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:220 +msgid "Fade coronas according to visibility (default: enabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:224 +msgid "Bloom" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:225 +msgid "" +"Enable bloom effect, which brightens the neighboring pixels of very bright " +"pixels. Has a big impact on performance. (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:226 +msgid "Extra postprocessing effects" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:227 +msgid "" +"Enables special postprocessing effects for when damaged or under water or " +"using a powerup (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:232 +msgid "Motion blur strength - 0.4 recommended" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:233 +msgid "Motion blur:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:239 +msgid "Particles" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:240 +msgid "Spawnpoint effects" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:241 +msgid "Particles effects at all spawn points and whenever a player spawns" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:246 +msgid "Quality:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:249 +msgid "" +"Multiplier for amount of particles. Less means less particles, which in turn " +"gives for better performance (default: 1.0)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:256 +msgid "Particles further away than this will not be drawn (default: 1000)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:31 +msgid "No crosshair" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:33 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:62 +msgid "Per weapon" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:34 +msgid "" +"Set a different crosshair for each weapon, good if you play without weapon " +"models" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:48 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:81 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:97 +msgid "Size:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:64 +msgid "By health" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:76 +msgid "Use rings to indicate weapon status" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:93 +msgid "Enable center crosshair dot" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:111 +msgid "Use normal crosshair color" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:122 +msgid "Smooth effects of crosshairs" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:125 +msgid "Hit testing:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:128 +msgid "" +"None: do not do hit tests for the crosshair; TrueAim: blur the crosshair " +"when there's an obstacle between your gun and the target; Enemies: also " +"enlarge the crosshair when you would hit an enemy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:129 +msgid "HTTST^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:130 +msgid "HTTST^TrueAim" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:131 +msgid "HTTST^Enemies" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:136 +msgid "Blur crosshair if the shot is obstructed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:140 +msgid "Enlarge crosshair if targeting an enemy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:143 +msgid "Animate crosshair when hitting an enemy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:146 +msgid "Animate crosshair when picking up an item" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh:7 +msgid "Crosshair" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:48 +msgid "Fading speed:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:51 +msgid "Enable rows / columns highlighting" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:55 +msgid "Show decimals in respawn countdown" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:57 +msgid "Show accuracy underneath scoreboard" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:61 +msgid "Waypoints" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:63 +msgid "Display waypoint markers for objectives on the map" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:64 +msgid "Show various gametype specific waypoints" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:70 +msgid "Control transparency of the waypoints" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:74 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:124 +msgid "Fontsize:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:80 +msgid "Edge offset:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:89 +msgid "Fade when near the crosshair" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:94 +msgid "Damage" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:96 +msgid "Overlay:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:99 +msgid "Factor:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:104 +msgid "Fade rate:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:112 +msgid "Player Names" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:114 +msgid "Show names above players" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:130 +msgid "Max distance:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:136 +msgid "Decolorize:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:140 +#: qcsrc/menu/xonotic/keybinder.qc:99 +msgid "Teamplay" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:148 +msgid "Only when near crosshair" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:152 +msgid "Display health and armor" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:157 +msgid "Damage overlay:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:160 +msgid "Dynamic HUD" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:161 +msgid "HUD moves around following player's movement" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:163 +msgid "Shake the HUD when hurt" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:167 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh:6 +msgid "Enter HUD editor" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qh:7 +msgid "HUD" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:21 +msgid "In order for the HUD editor to show, you must first be in game." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:23 +msgid "Do you wish to start a local game to set up the HUD?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:24 +msgid "Frag Information" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:26 +msgid "Display information about killing sprees" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:29 +msgid "Only display sprees if they are achievements" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:34 +msgid "Show spree information in centerprints" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:38 +msgid "Show spree information in death messages" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:43 +msgid "Sprees in info messages:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:46 +msgid "SPREES^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:47 +msgid "Target" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:48 +msgid "Attacker" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:49 +msgid "SPREES^Both" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:55 +msgid "Print on a seperate line" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:58 +msgid "Add extra frag information to centerprint when available" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:62 +msgid "Add frag location to death messages when available" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:65 +msgid "Gamemode Settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:67 +msgid "Display capture times in Capture The Flag" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:71 +msgid "Display name of flag stealer in Capture The Flag" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:76 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:91 +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:133 +msgid "Other" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:78 +msgid "Display console messages in the top left corner" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:80 +msgid "Display all info messages in the chatbox" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:82 +msgid "Display player statuses in the chatbox" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:86 +msgid "Powerup notifications" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:89 +msgid "Weapon centerprint notifications" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:92 +msgid "Weapon info message notifications" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:96 +msgid "Announcers" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:98 +msgid "Respawn countdown sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:101 +msgid "Killstreak sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:104 +msgid "Achievement sounds" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qh:7 +msgid "Messages" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:30 +msgid "Items" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:32 +msgid "Use simple 2D images instead of item models" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:34 +msgid "Unavailable alpha:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:37 +msgid "Unavailable color:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:39 +msgid "GHOITEMS^Black" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:40 +msgid "GHOITEMS^Dark" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:41 +msgid "GHOITEMS^Tinted" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:42 +msgid "GHOITEMS^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:43 +msgid "GHOITEMS^Blue" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:49 +#: qcsrc/menu/xonotic/serverlist.qc:767 +msgid "Players" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:51 +msgid "Force player models to mine" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:53 +msgid "Force player colors to mine" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:56 +msgid "In non teamplay modes only" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:60 +msgid "Body fading:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:63 +msgid "Gibs:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:65 +msgid "GIBS^None" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:66 +msgid "GIBS^Few" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:67 +msgid "GIBS^Many" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:68 +msgid "GIBS^Lots" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qh:7 +msgid "Models" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qh:8 +msgid "Customize how players and items are displayed in game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:26 +msgid "1st person perspective" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:30 +msgid "Slide to third person upon death" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:34 +msgid "Smooth the view when landing from a jump" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:38 +msgid "Smooth the view while crouching" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:42 +msgid "View waving while idle" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:46 +msgid "View bobbing while walking around" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:51 +msgid "3rd person perspective" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:55 +msgid "Back distance" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:61 +msgid "Up distance" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:67 +msgid "Allow passing through walls while spectating" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:70 +msgid "Field of view:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:72 +msgid "Field of vision in degrees (default: 100)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:76 +msgid "ZOOM^Zoom factor:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:78 +msgid "How big the zoom factor is when the zoom button is pressed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:81 +msgid "ZOOM^Zoom speed:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:83 +msgid "How fast the view will be zoomed, disable to zoom instantly" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:92 +msgid "ZOOM^Instant" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:96 +msgid "ZOOM^Zoom sensitivity:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:98 +msgid "" +"How zoom changes sensitivity, from 0 (lower sensitivity) to 1 (no " +"sensitivity change)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:101 +msgid "Velocity zoom" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:102 +msgid "Forward movement only" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:106 +msgid "VZOOM^Factor" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:113 +msgid "Display reticle 2D overlay while zooming" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:116 +msgid "Release zoom when you die or respawn" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:120 +msgid "Release zoom when you switch weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qh:7 +#: qcsrc/menu/xonotic/keybinder.qc:76 +msgid "View" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:34 +msgid "Weapon Priority List (* = mutator weapon)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:40 +msgid "Up" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:44 +msgid "Down" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:50 +msgid "Use priority list for weapon cycling" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:51 +msgid "" +"Make use of the list above when cycling through weapons with the mouse wheel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:53 +msgid "Cycle through only usable weapon selections" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:57 +msgid "Auto switch weapons on pickup" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:58 +msgid "" +"Automatically switch to newly picked up weapons if they are better than what " +"you are carrying" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:61 +msgid "Release attack buttons when you switch weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:64 +msgid "Draw 1st person weapon model" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:65 +msgid "Draw the weapon model" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:69 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:72 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:75 +msgid "Position of the weapon model; requires reconnect" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:80 +msgid "Gun model swaying" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:85 +msgid "Gun model bobbing" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qh:7 +#: qcsrc/menu/xonotic/keybinder.qc:43 +msgid "Weapons" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:33 +msgid "Key Bindings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:37 +msgid "Change key..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:41 +msgid "Edit..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:47 +msgid "Clear" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:52 +msgid "Reset all" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:57 +msgid "Mouse" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:59 +msgid "Sensitivity:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:61 +msgid "Mouse speed multiplier" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:63 +msgid "Smooth aiming" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:64 +msgid "Smoothes the mouse movement, but makes aiming slightly less responsive" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:66 +msgid "Invert aiming" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:67 +msgid "Invert mouse movement on the Y-axis" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:69 +msgid "Use system mouse positioning" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:74 +msgid "Enable built in mouse acceleration" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:78 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:82 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:85 +msgid "Disable system mouse acceleration" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:79 +msgid "Make use of DGA mouse input" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:93 +msgid "Pressing \"enter console\" key also closes it" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:94 +msgid "Allow the console toggling bind to also close the console" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:96 +msgid "Automatically repeat jumping if holding jump" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:99 +msgid "Jetpack on jump:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:101 +msgid "JPJUMP^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:102 +msgid "Air only" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:103 +msgid "JPJUMP^All" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:109 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:114 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:119 +msgid "Use joystick input" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:31 +msgid "Command when pressed:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:34 +msgid "Command when released:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:40 +msgid "Cancel" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qh:7 +msgid "User defined key bind" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:11 +#, c-format +msgid "%d fps" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:12 +#, c-format +msgid "%d KB/s" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:13 +#, c-format +msgid "%d MB/s" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:27 +msgid "Network" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:29 +msgid "Client UDP port:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:31 +msgid "Force client to use chosen port unless it is set to 0" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:34 +msgid "Bandwidth:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:36 +msgid "Specify your network speed" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:37 +msgid "56k" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:38 +msgid "ISDN" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:39 +msgid "Slow ADSL" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:40 +msgid "Fast ADSL" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:41 +msgid "Broadband" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:44 +msgid "Input packets/s:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:46 +msgid "How many input packets to send to the server each second" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:48 +msgid "Server queries/s:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 +msgid "Downloads:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:54 +msgid "Maximum number of concurrent HTTP/FTP downloads" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:56 +msgid "Download speed:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:69 +msgid "Local latency:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:73 +msgid "Show netgraph" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:74 +msgid "Show a graph of packet sizes and other information" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:76 +msgid "Client-side movement prediction" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:78 +msgid "Movement error compensation" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:82 +msgid "Use encryption (AES) when available" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:85 +msgid "Framerate" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:87 +msgid "Maximum:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:99 +msgid "MAXFPS^Unlimited" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:102 +msgid "Target:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:104 +msgid "TRGT^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:116 +msgid "Idle limit:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:122 +msgid "IDLFPS^Unlimited" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:126 +msgid "Save processing time for other apps" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:129 +msgid "Show frames per second" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:130 +msgid "Show your rendered frames per second" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:135 +msgid "Menu tooltips:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:137 +msgid "" +"Menu tooltips: disabled, standard or advanced (also shows cvar or console " +"command bound to the menu item)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:138 +msgid "TLTIP^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:139 +msgid "TLTIP^Standard" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:140 +msgid "TLTIP^Advanced" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:143 +msgid "Show current date and time" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:144 +msgid "Show current date and time of day, useful on screenshots" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:147 +msgid "Enable developer mode" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:151 +msgid "Advanced settings..." +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:152 +msgid "Advanced settings where you can tweak every single variable of the game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:157 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qh:6 +msgid "Factory reset" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:31 +msgid "Cvar filter:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:38 +msgid "Modified cvars only" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:45 +msgid "Setting:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:49 +msgid "Type:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:53 +msgid "Value:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:70 +msgid "Description:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh:7 +msgid "Advanced settings" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:11 +msgid "Are you sure you want to reset all settings?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:13 +msgid "This will create a backup config in your data directory" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:25 +msgid "Menu Skins" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:64 +msgid "Text Language" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:69 +msgid "Set language" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:74 +msgid "Disable gore effects and harsh language" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:75 +msgid "" +"Replace blood and gibs with content that does not have any gore effects " +"(default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:10 +msgid "While connected language changes will be applied only to the menu," +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:12 +msgid "full language changes will take effect starting from the next game" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 +msgid "Disconnect now" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:17 +msgid "Switch language" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh:6 +msgid "Warning" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:33 +msgid "Resolution:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:37 +msgid "Font/UI size:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:39 +msgid "SZ^Unreadable" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:40 +msgid "SZ^Tiny" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:41 +msgid "SZ^Little" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:42 +msgid "SZ^Small" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:43 +msgid "SZ^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:44 +msgid "SZ^Large" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:45 +msgid "SZ^Huge" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:46 +msgid "SZ^Gigantic" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:47 +msgid "SZ^Colossal" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:51 +msgid "Color depth:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:53 +msgid "How many bits per pixel (BPP) to render at, 32 is recommended" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:54 +msgid "16bit" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:55 +msgid "32bit" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:59 +msgid "Full screen" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:61 +msgid "Vertical Synchronization" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:62 +msgid "" +"Enable vertical synchronization to prevent tearing, will cap your fps to the " +"screen refresh rate (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:67 +msgid "Flip view horizontally" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:68 +msgid "Poor man's left handed mode (default: off)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:71 +msgid "Anisotropy:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:73 +msgid "Anisotropic filtering quality (default: 1x)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:74 +msgid "ANISO^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:75 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:86 +msgid "2x" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:76 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:87 +msgid "4x" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:77 +msgid "8x" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:78 +msgid "16x" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:81 +msgid "Antialiasing:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:84 +msgid "" +"Enable antialiasing, which smooths the edges of 3D geometry. Note that it " +"might decrease performance by quite a lot (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:85 +msgid "AA^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:92 +msgid "High-quality frame buffer" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:97 +msgid "Depth first:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:99 +msgid "" +"Eliminate overdraw by rendering a depth-only version of the scene before the " +"normal rendering starts (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:100 +msgid "DF^Disabled" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:101 +msgid "DF^World" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:102 +msgid "DF^All" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:105 +msgid "Vertex Buffer Objects (VBOs)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:108 +msgid "VBO^Off" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:109 +msgid "Vertices, some Tris (compatible)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:110 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:114 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:116 +msgid "" +"Make use of Vertex Buffer Objects to store static geometry in video memory " +"for faster rendering (default: Vertex and Triangles)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:113 +msgid "Vertices" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:115 +msgid "Vertices and Triangles" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:119 +msgid "Brightness:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:121 +msgid "Brightness of black (default: 0)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:123 +msgid "Contrast:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:125 +msgid "Brightness of white (default: 1)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:127 +msgid "Gamma:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:130 +msgid "" +"Inverse gamma correction value, a brightness effect that does not affect " +"white or black (default: 1.125)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:133 +msgid "Contrast boost:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:136 +msgid "By how much to multiply the contrast in dark areas (default: 1)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:139 +msgid "Saturation:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:142 +msgid "" +"Saturation adjustment (0 = grayscale, 1 = normal, 2 = oversaturated), " +"requires GLSL color control (default: 1)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:146 +msgid "LIT^Ambient:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:148 +msgid "" +"Ambient lighting, if set too high it tends to make light on maps look dull " +"and flat (default: 4)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:150 +msgid "Intensity:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:152 +msgid "Global rendering brightness (default: 1)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:155 +msgid "Wait for GPU to finish each frame" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:156 +msgid "" +"Make the CPU wait for the GPU to finish each frame, can help with some " +"strange input or video lag on some machines (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:158 +msgid "Use OpenGL 2.0 shaders (GLSL)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:162 +msgid "Use GLSL to handle color control" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:163 +msgid "" +"Enable use of GLSL to apply gamma correction, note that it might decrease " +"performance by a lot (default: disabled)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:168 +msgid "Psycho coloring (easter egg)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:171 +msgid "Trippy vertices (easter egg)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:110 +msgid "Instant action! (random map with bots)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:117 +msgid "???" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:130 +msgid "Campaign Difficulty:" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:131 +msgid "CSKL^Easy" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:132 +msgid "CSKL^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:133 +msgid "CSKL^Hard" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:135 +msgid "Start Singleplayer!" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qh:6 +msgid "Singleplayer" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 +msgid "Play the singleplayer campaign or instant action matches against bots" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 +msgid "Winner" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:32 +msgid "join 'best' team (auto-select)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:33 +msgid "Autoselect team (recommended)" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:37 +msgid "red" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:38 +msgid "blue" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:39 +msgid "yellow" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:40 +msgid "pink" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:43 +msgid "spectate" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_teamselect.qh:7 +msgid "Team Selection" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_uid2name.qc:10 +msgid "Allow player statistics to use your nickname?" +msgstr "" + +#: qcsrc/menu/xonotic/dialog_uid2name.qc:12 +msgid "Answering \"No\" you will appear as \"Anonymous player\"" +msgstr "" + +#: qcsrc/menu/xonotic/gametypelist.qc:86 +msgid "teamplay" +msgstr "" + +#: qcsrc/menu/xonotic/gametypelist.qc:88 +msgid "free for all" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:29 +msgid "Moving" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:30 +msgid "forward" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:31 +msgid "backpedal" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:32 +msgid "strafe left" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:33 +msgid "strafe right" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:34 +msgid "jump / swim" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:35 +msgid "crouch / sink" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:36 +msgid "off-hand hook" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:37 +msgid "jet pack" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:39 +msgid "Attacking" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:44 +msgid "WEAPON^previous" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:45 +msgid "WEAPON^next" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:46 +msgid "WEAPON^previously used" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:47 +msgid "WEAPON^best" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:48 +msgid "reload" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:49 +msgid "drop weapon / throw nade" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:77 +msgid "hold zoom" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:78 +msgid "toggle zoom" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:79 +msgid "show scores" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:80 +msgid "screen shot" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:81 +msgid "maximize radar" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:82 +msgid "3rd person view" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:83 +msgid "enter spectator mode" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:85 +msgid "Communicate" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:86 +msgid "public chat" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:87 qcsrc/menu/xonotic/keybinder.qc:100 +msgid "team chat" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:88 +msgid "show chat history" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:89 +msgid "vote YES" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:90 +msgid "vote NO" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:93 +msgid "Client" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:95 +msgid "enter console" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:96 +msgid "disconnect" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:97 +msgid "quit" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:101 +msgid "auto-join team" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:103 +msgid "drop key / drop flag" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:106 +msgid "quick menu" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:107 +msgid "sandbox menu" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:108 +msgid "drag object" +msgstr "" + +#: qcsrc/menu/xonotic/keybinder.qc:110 +msgid "User defined" +msgstr "" + +#: qcsrc/menu/xonotic/mainwindow.qc:64 qcsrc/menu/xonotic/mainwindow.qc:67 +msgid "Do not press this button again!" +msgstr "" + +#: qcsrc/menu/xonotic/maplist.qc:291 +msgid "" +"Huh? Can't play this (m is NULL). Refiltering so this won't happen again.\n" +msgstr "" + +#: qcsrc/menu/xonotic/maplist.qc:299 +#, c-format +msgid "%s's Xonotic Server" +msgstr "" + +#: qcsrc/menu/xonotic/maplist.qc:304 +msgid "" +"Huh? Can't play this (invalid game type). Refiltering so this won't happen " +"again.\n" +msgstr "" + +#: qcsrc/menu/xonotic/playerlist.qc:100 qcsrc/menu/xonotic/playerlist.qc:110 +msgid "spectator" +msgstr "" + +#: qcsrc/menu/xonotic/playermodel.qc:170 +msgid "" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:273 +msgid "Favorite" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:274 +msgid "" +"Bookmark the currently highlighted server so that it's faster to find in the " +"future" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:763 +msgid "Ping" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:764 +msgid "Hostname" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:765 +msgid "Map" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:766 +msgid "Type" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +#, c-format +msgid "AES level %d" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +msgid "ENC^none" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +msgid "encryption:" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1061 +#, c-format +msgid "mod: %s" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1063 +#, c-format +msgid "modified settings" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1063 +#, c-format +msgid "official settings" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1065 +msgid "stats disabled" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qc:1065 +msgid "stats enabled" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:151 +msgid "SLCAT^Favorites" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:152 +msgid "SLCAT^Recommended" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:153 +msgid "SLCAT^Normal Servers" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:154 +msgid "SLCAT^Servers" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:155 +msgid "SLCAT^Competitive Mode" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:156 +msgid "SLCAT^Modified Servers" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:157 +msgid "SLCAT^Overkill" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:158 +msgid "SLCAT^InstaGib" +msgstr "" + +#: qcsrc/menu/xonotic/serverlist.qh:159 +msgid "SLCAT^Defrag Mode" +msgstr "" + +#: qcsrc/menu/xonotic/skinlist.qc:70 +msgid "" +msgstr "" + +#: qcsrc/menu/xonotic/skinlist.qc:71 +msgid "<AUTHOR>" +msgstr "" + +#: qcsrc/menu/xonotic/slider_decibels.qc:72 +msgid "VOL^MAX" +msgstr "" + +#: qcsrc/menu/xonotic/slider_decibels.qc:74 +msgid "VOL^OFF" +msgstr "" + +#: qcsrc/menu/xonotic/slider_decibels.qc:82 +#, c-format +msgid "%s dB" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:13 +msgid "" +"Multiplier for amount of particles. Less means less particles, which in turn " +"gives for better performance (default: 1)" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:14 +msgid "PART^OMG" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:15 +msgid "PART^Low" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:16 +msgid "PART^Medium" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:17 +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:14 +msgid "PART^Normal" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:18 +msgid "PART^High" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:19 +msgid "PART^Ultra" +msgstr "" + +#: qcsrc/menu/xonotic/slider_particles.qc:20 +msgid "PART^Ultimate" +msgstr "" + +#: qcsrc/menu/xonotic/slider_picmip.qc:13 +msgid "" +"Change the sharpness of the textures. Lowering it will effectively reduce " +"texture memory usage, but make the textures appear very blurry. (default: " +"good)" +msgstr "" + +#: qcsrc/menu/xonotic/slider_resolution.qc:115 +msgid "Screen resolution" +msgstr "" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:13 +msgid "PART^Slow" +msgstr "" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:15 +msgid "PART^Fast" +msgstr "" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:16 +msgid "PART^Instant" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:29 +msgid "January" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:30 +msgid "February" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:31 +msgid "March" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:32 +msgid "April" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:33 +msgid "May" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:34 +msgid "June" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:35 +msgid "July" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:36 +msgid "August" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:37 +msgid "September" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:38 +msgid "October" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:39 +msgid "November" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:40 +msgid "December" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:96 +msgid "Joined:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:103 +msgid "Last_Seen:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:110 +msgid "Time_Played:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:117 +msgid "Favorite_Map:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:201 qcsrc/menu/xonotic/statslist.qc:245 +#, c-format +msgid "%s_Matches:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:208 +#, c-format +msgid "%s_ELO:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:215 +#, c-format +msgid "%s_Rank:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:222 +#, c-format +msgid "%s_Percentile:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:231 +#, c-format +msgid "%s_Favorite_Map:" +msgstr "" + +#: qcsrc/menu/xonotic/statslist.qc:246 +#, c-format +msgid "%d (unranked)" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:417 +#, c-format +msgid "" +"Update can be downloaded at:\n" +"%s\n" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:528 +msgid "Autogenerating mapinfo for newly added maps..." +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:557 +#, c-format +msgid "^1%s TEST BUILD" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:577 +#, c-format +msgid "Update to %s now!" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:662 +msgid "" +"^1ERROR: Texture compression is required but not supported.\n" +"^1Expect visual problems.\n" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:780 +msgid "Use default" +msgstr "" + +#: qcsrc/menu/xonotic/util.qc:800 +msgid "Team Color:" +msgstr "" + +#: qcsrc/menu/xonotic/util.qh:44 +msgid "Enable panel" +msgstr "" + +#~ msgid "QMCMD^Chat" +#~ msgstr "QMCMD^Sembang" diff --git a/common.pl.po b/common.pl.po index f8c5ab3bb4..0b40453da5 100644 --- a/common.pl.po +++ b/common.pl.po @@ -13,15 +13,15 @@ # Kriss Chr <kriss7475@gmail.com>, 2017 # Piotr Kozica <koza91@gmail.com>, 2016 # Rafał Szymański <okavasly@gmail.com>, 2017 -# Robert Wolniak <robert.wolniak@gmail.com>, 2015 +# Robert Wolniak <robert.wolniak@gmail.com>, 2015,2018 # Sertomas, 2014 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-20 00:10+0000\n" -"Last-Translator: Rafał Szymański <okavasly@gmail.com>\n" +"PO-Revision-Date: 2018-04-19 09:01+0000\n" +"Last-Translator: Robert Wolniak <robert.wolniak@gmail.com>\n" "Language-Team: Polish (http://www.transifex.com/team-xonotic/xonotic/" "language/pl/)\n" "Language: pl\n" @@ -1158,7 +1158,7 @@ msgstr "" #: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 msgid "Ball Stealer" -msgstr "" +msgstr "Złodziej Kuli" #: qcsrc/common/items/item/armor.qh:111 msgid "Big armor" @@ -1310,6 +1310,8 @@ msgid "" "Kill enemies to freeze them, stand next to frozen teammates to revive them; " "freeze all enemies to win" msgstr "" +"Zabijaj przeciwników by ich zamrozić i stój obok członków swojej drużyny by " +"ich wskrzesić; aby wygrać, zamroź wszystkich wrogów" #: qcsrc/common/mapinfo.qh:446 msgid "Hold the ball to get points for kills" @@ -1468,6 +1470,8 @@ msgid "" "You lost the game!\n" "Select \"^1Next Match^7\" on the menu for a rematch!" msgstr "" +"Przegrana meczu!\n" +"Wybierz w menu opcję \"^1Następny Mecz^7\" po rewanż!" #: qcsrc/common/minigames/minigame/pp.qc:444 #: qcsrc/common/minigames/minigame/ttt.qc:325 @@ -1475,16 +1479,18 @@ msgid "" "You win!\n" "Select \"^1Next Match^7\" on the menu to start a new match!" msgstr "" +"Wygrana!\n" +"Wybierz w menu opcję \"^1Następny Mecz^7\" by rozpocząć nową grę!" #: qcsrc/common/minigames/minigame/pp.qc:450 #: qcsrc/common/minigames/minigame/ttt.qc:331 msgid "Select \"^1Next Match^7\" on the menu to start a new match!" -msgstr "" +msgstr "Wybierz w menu opcję \"^1Następny Mecz^7\" by rozpocząć nową grę!" #: qcsrc/common/minigames/minigame/pp.qc:451 #: qcsrc/common/minigames/minigame/ttt.qc:332 msgid "Wait for your opponent to confirm the rematch" -msgstr "" +msgstr "Poczekaj aż twój przeciwnik potwierdzi rewanż." #: qcsrc/common/minigames/minigame/pp.qc:582 #: qcsrc/common/minigames/minigame/ttt.qc:665 diff --git a/common.pt.po b/common.pt.po index ec526d5e35..b2b27a40dd 100644 --- a/common.pt.po +++ b/common.pt.po @@ -4,17 +4,18 @@ # # Translators: # Ivan Paulos Tomé <greylica@gmail.com>, 2016 -# Jean Trindade Pereira <jean_trindade2@hotmail.com>, 2015-2017 +# Jean Trindade Pereira <jean_trindade2@hotmail.com>, 2015-2018 # Mirio <opivy@hotmail.de>, 2017 # NotThatPrivate Yes <henriqueferreira2009@gmail.com>, 2015 # Ricardo Manuel da Cruz Coelho da Silva <ricardo.mccs@gmail.com>, 2015 +# Rui <xymarior@yandex.com>, 2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-12-21 15:23+0000\n" -"Last-Translator: Jean Trindade Pereira <jean_trindade2@hotmail.com>\n" +"PO-Revision-Date: 2018-05-02 10:54+0000\n" +"Last-Translator: Rui <xymarior@yandex.com>\n" "Language-Team: Portuguese (http://www.transifex.com/team-xonotic/xonotic/" "language/pt/)\n" "Language: pt\n" @@ -26,16 +27,16 @@ msgstr "" #: qcsrc/client/hud/hud_config.qc:239 #, c-format msgid "^2Successfully exported to %s! (Note: It's saved in data/data/)\n" -msgstr "^2Exportado com sucesso para %s! (Nota: Foi salvo em data/data/)\n" +msgstr "^2Exportado com sucesso para %s! (Nota: foi gravado em data/data/)\n" #: qcsrc/client/hud/hud_config.qc:243 #, c-format msgid "^1Couldn't write to %s\n" -msgstr "^1Não foi possível escrever para %s\n" +msgstr "^1Não foi possível gravar para %s\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." -msgstr "^3Jogador^7: Isto é a área do bate-papo." +msgstr "^3Jogador^7: isto é a área doe conversação." #: qcsrc/client/hud/panel/engineinfo.qc:69 #, c-format @@ -54,7 +55,7 @@ msgstr "^1Assistindo: ^7%s" #: qcsrc/client/hud/panel/infomessages.qc:100 #, c-format msgid "^1Press ^3%s^1 to spectate" -msgstr "^1Aperte ^3%s^1 para assistir" +msgstr "^1Pressionar ^3%s^1 para assistir" #: qcsrc/client/hud/panel/infomessages.qc:100 #: qcsrc/menu/xonotic/keybinder.qc:40 @@ -64,7 +65,7 @@ msgstr "disparo primário" #: qcsrc/client/hud/panel/infomessages.qc:102 #, c-format msgid "^1Press ^3%s^1 or ^3%s^1 for next or previous player" -msgstr "^1Aperte ^3%s^1 ou ^3%s^1 para o jogador seguinte ou anterior" +msgstr "^1Pressionar ^3%s^1 ou ^3%s^1 para o jogador seguinte ou anterior" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 @@ -79,12 +80,13 @@ msgstr "arma anterior" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format msgid "^1Use ^3%s^1 or ^3%s^1 to change the speed" -msgstr "^1Use ^3%s^1 ou ^3%s^1 para alterar a velocidade" +msgstr "^1Usar ^3%s^1 ou ^3%s^1 para alterar a velocidade" #: qcsrc/client/hud/panel/infomessages.qc:108 #, c-format msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" -msgstr "^1Aperte ^3%s^1 para observar e ^3%s^1 para alterar o modo da câmera" +msgstr "" +"^1Pressionar ^3%s^1 para observar e ^3%s^1 para alterar o modo da câmara" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/common/vehicles/cl_vehicles.qc:192 @@ -99,7 +101,7 @@ msgstr "disparo secundário" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format msgid "^1Press ^3%s^1 for gamemode info" -msgstr "^1Aperte ^3%s^1 para ver as informações do modo de jogo" +msgstr "^1Pressionar ^3%s^1 para ver as informações do modo de jogo" #: qcsrc/client/hud/panel/infomessages.qc:111 #: qcsrc/menu/xonotic/keybinder.qc:94 @@ -112,13 +114,13 @@ msgstr "^1A partida já começou" #: qcsrc/client/hud/panel/infomessages.qc:126 msgid "^1You have no more lives left" -msgstr "^1Você não tem mais vidas sobrando" +msgstr "^1Não tens mais vidas" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 #, c-format msgid "^1Press ^3%s^1 to join" -msgstr "^1Aperte ^3%s^1 para entrar no jogo" +msgstr "^1Pressionar ^3%s^1 para entrar no jogo" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 @@ -128,16 +130,16 @@ msgstr "saltar" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format msgid "^1Game starts in ^3%d^1 seconds" -msgstr "^1A partida iniciará em ^3%d^1 segundo(s)" +msgstr "^1O jogo vai começar em ^3%d^1 segundo(s)" #: qcsrc/client/hud/panel/infomessages.qc:145 msgid "^2Currently in ^1warmup^2 stage!" -msgstr "^2Atualmente em fase de ^1aquecimento^2!" +msgstr "^2Neste momento em fase de ^1aquecimento^2!" #: qcsrc/client/hud/panel/infomessages.qc:160 #, c-format msgid "%sPress ^3%s%s to end warmup" -msgstr "%sAperte ^3%s%s para terminar o aquecimento" +msgstr "%sPressionar ^3%s%s para terminar o aquecimento" #: qcsrc/client/hud/panel/infomessages.qc:160 #: qcsrc/client/hud/panel/infomessages.qc:162 @@ -149,58 +151,57 @@ msgstr "pronto" #: qcsrc/client/hud/panel/infomessages.qc:162 #, c-format msgid "%sPress ^3%s%s once you are ready" -msgstr "%sAperte ^3%s%s assim que estiver pronto" +msgstr "%sPressionar ^3%s%s assim que estiveres pronto" #: qcsrc/client/hud/panel/infomessages.qc:167 msgid "^2Waiting for others to ready up to end warmup..." msgstr "" -"^2Esperando que os outros jogadores estejam prontos para acabar o " +"^2Á espera que os outros jogadores estejam prontos para acabar o " "aquecimento..." #: qcsrc/client/hud/panel/infomessages.qc:169 msgid "^2Waiting for others to ready up..." -msgstr "^2Esperando que os outros jogadores estejam prontos..." +msgstr "^2À espera que os outros jogadores estejam prontos..." #: qcsrc/client/hud/panel/infomessages.qc:175 #, c-format msgid "^2Press ^3%s^2 to end warmup" -msgstr "^2Aperte ^3%s^2 para terminar o aquecimento" +msgstr "^2Pressionar ^3%s^2 para terminar o aquecimento" #: qcsrc/client/hud/panel/infomessages.qc:196 msgid "Teamnumbers are unbalanced!" -msgstr "As equipes estão desequilibradas!" +msgstr "As equipas estão desequilibradas!" #: qcsrc/client/hud/panel/infomessages.qc:199 #, c-format msgid " Press ^3%s%s to adjust" -msgstr " Aperte ^3%s%s para ajustar" +msgstr " Pressiona ^3%s%s para ajustar" #: qcsrc/client/hud/panel/infomessages.qc:199 #: qcsrc/menu/xonotic/keybinder.qc:102 msgid "team menu" -msgstr "menu de equipe" +msgstr "menu de equipa" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating this player:" -msgstr "^1Também estão assistindo a este jogador:" +msgstr "^1A assistir este jogador:" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating you:" -msgstr "^1Assistindo você:" +msgstr "^1A assistir a ti:" #: qcsrc/client/hud/panel/infomessages.qc:225 msgid "^7Press ^3ESC ^7to show HUD options." -msgstr "^7Aperte ^3ESC ^7para exibir as opções de HUD." +msgstr "^7Pressionar ^3ESC ^7para mostrar as opções de interface." #: qcsrc/client/hud/panel/infomessages.qc:226 msgid "^3Doubleclick ^7a panel for panel-specific options." msgstr "" -"^3Clique duas vezes ^7em um painel para abrir sua janela de opções " -"específicas." +"^3Clica duas vezes ^7num painel para abrir a janela de opções específicas." #: qcsrc/client/hud/panel/infomessages.qc:227 msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" -msgstr "^3CTRL ^7para desligar teste de colisão, ^3SHIFT ^7e" +msgstr "^3CTRL ^7para desativar o teste de colisão, ^3SHIFT ^7e" #: qcsrc/client/hud/panel/infomessages.qc:228 msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." @@ -224,7 +225,7 @@ msgstr "Jogador %d" #: qcsrc/client/hud/panel/quickmenu.qc:605 #, c-format msgid "Submenu%d" -msgstr "Submenu%d" +msgstr "Sub-menu%d" #: qcsrc/client/hud/panel/quickmenu.qc:610 #, c-format @@ -258,12 +259,12 @@ msgstr "Olá / Boa sorte" #: qcsrc/client/hud/panel/quickmenu.qc:797 msgid "QMCMD^hi / good luck and have fun" -msgstr "Olá / Boa sorte e divirta-se" +msgstr "Olá / Boa sorte e diverte-te" #: qcsrc/client/hud/panel/quickmenu.qc:802 #: qcsrc/client/hud/panel/quickmenu.qc:818 msgid "QMCMD^Team chat" -msgstr "Bate-papo de equipe" +msgstr "Conversação da equipa" #: qcsrc/client/hud/panel/quickmenu.qc:803 msgid "QMCMD^quad soon" @@ -271,19 +272,19 @@ msgstr "Quad em breve" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item %x^7 (l:%y^7)" -msgstr "Item livre %x^7 (l:%y^7)" +msgstr "Item grátis %x^7 (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item, icon" -msgstr "Item livre, ícone" +msgstr "Item grátis, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item (l:%l^7)" -msgstr "Item pego (l:%l^7)" +msgstr "Pegou no item (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item, icon" -msgstr "Item pego, ícone" +msgstr "Pegou no item, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:806 msgid "QMCMD^negative" @@ -319,27 +320,27 @@ msgstr "Bandeira avistada, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" -msgstr "Defendendo (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "A defender (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:811 msgid "QMCMD^defending, icon" -msgstr "Defendendo, ícone" +msgstr "A defender, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" -msgstr "Patrulhando (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "A patrulhar (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:812 msgid "QMCMD^roaming, icon" -msgstr "Patrulhando, ícone" +msgstr "A patrulhar, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" -msgstr "Atacando (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "A atacar (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" #: qcsrc/client/hud/panel/quickmenu.qc:813 msgid "QMCMD^attacking, icon" -msgstr "Atacando, ícone" +msgstr "A atacar, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier (l:%y^7)" @@ -364,7 +365,7 @@ msgstr "Largar arma, ícone" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" -msgstr "Arma solta %w^7 (l:%l^7)" +msgstr "Arma largada %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:817 msgid "QMCMD^drop flag/key, icon" @@ -386,11 +387,11 @@ msgstr "Configurações" #: qcsrc/client/hud/panel/quickmenu.qc:824 #: qcsrc/client/hud/panel/quickmenu.qc:831 msgid "QMCMD^View/HUD settings" -msgstr "Configurações de Exibição/HUD" +msgstr "Configurações de Visualização/Interface" #: qcsrc/client/hud/panel/quickmenu.qc:825 msgid "QMCMD^3rd person view" -msgstr "Visão em 3ª pessoa" +msgstr "Visão na 3ª pessoa" #: qcsrc/client/hud/panel/quickmenu.qc:826 msgid "QMCMD^Player models like mine" @@ -398,7 +399,7 @@ msgstr "Modelos de jogadores como o meu" #: qcsrc/client/hud/panel/quickmenu.qc:827 msgid "QMCMD^Names above players" -msgstr "Nomes sobre jogadores" +msgstr "Nomes por cima dos jogadores" #: qcsrc/client/hud/panel/quickmenu.qc:828 msgid "QMCMD^Crosshair per weapon" @@ -419,16 +420,16 @@ msgstr "Configurações de som" #: qcsrc/client/hud/panel/quickmenu.qc:834 msgid "QMCMD^Hit sound" -msgstr "Som de acerto" +msgstr "Som de acertar" #: qcsrc/client/hud/panel/quickmenu.qc:835 msgid "QMCMD^Chat sound" -msgstr "Som do bate-papo" +msgstr "Som da conversação" #: qcsrc/client/hud/panel/quickmenu.qc:840 #: qcsrc/client/hud/panel/quickmenu.qc:844 msgid "QMCMD^Spectator camera" -msgstr "Câmera de espectador" +msgstr "Câmara de espetador" #: qcsrc/client/hud/panel/quickmenu.qc:841 msgid "QMCMD^1st person" @@ -436,16 +437,16 @@ msgstr "1ª pessoa" #: qcsrc/client/hud/panel/quickmenu.qc:842 msgid "QMCMD^3rd person around player" -msgstr "3ª pessoa em volta do jogador" +msgstr "3ª pessoa à volta do jogador" #: qcsrc/client/hud/panel/quickmenu.qc:843 msgid "QMCMD^3rd person behind" -msgstr "3ª pessoa traseira" +msgstr "3ª pessoa por trás" #: qcsrc/client/hud/panel/quickmenu.qc:849 #: qcsrc/client/hud/panel/quickmenu.qc:854 msgid "QMCMD^Observer camera" -msgstr "Câmera de observador" +msgstr "Câmara de observador" #: qcsrc/client/hud/panel/quickmenu.qc:850 msgid "QMCMD^Increase speed" @@ -465,11 +466,11 @@ msgstr "Colisão com paredes ligada" #: qcsrc/client/hud/panel/quickmenu.qc:857 msgid "QMCMD^Fullscreen" -msgstr "Tela cheia" +msgstr "Ecrã inteiro" #: qcsrc/client/hud/panel/quickmenu.qc:859 msgid "QMCMD^Translate chat messages" -msgstr "Traduzir mensagens do bate-papo" +msgstr "Traduzir mensagens da conversação" #: qcsrc/client/hud/panel/quickmenu.qc:862 #: qcsrc/client/hud/panel/quickmenu.qc:872 @@ -490,11 +491,11 @@ msgstr "Reduzir tempo de partida" #: qcsrc/client/hud/panel/quickmenu.qc:868 msgid "QMCMD^Extend match time" -msgstr "Estender tempo de partida" +msgstr "Aumentar tempo de partida" #: qcsrc/client/hud/panel/quickmenu.qc:871 msgid "QMCMD^Shuffle teams" -msgstr "Misturar as equipes" +msgstr "Misturar as equipas" #: qcsrc/client/hud/panel/racetimer.qc:37 #, c-format @@ -582,7 +583,7 @@ msgstr "pbndvítimas" #: qcsrc/client/hud/panel/scoreboard.qc:89 msgid "SCO^goals" -msgstr "gols" +msgstr "golos" #: qcsrc/client/hud/panel/scoreboard.qc:90 msgid "SCO^kckills" @@ -684,7 +685,8 @@ msgstr "ticks" msgid "" "You can modify the scoreboard using the ^2scoreboard_columns_set command.\n" msgstr "" -"Você pode modificar o placar usando o comando ^2scoreboard_columns_set.\n" +"Podes alterar o placar de pontuações utilizando o comando " +"^2scoreboard_columns_set.\n" #: qcsrc/client/hud/panel/scoreboard.qc:296 msgid "^3|---------------------------------------------------------------|\n" @@ -692,7 +694,7 @@ msgstr "^3|---------------------------------------------------------------|\n" #: qcsrc/client/hud/panel/scoreboard.qc:297 msgid "Usage:\n" -msgstr "Uso:\n" +msgstr "Utilização:\n" #: qcsrc/client/hud/panel/scoreboard.qc:298 msgid "^2scoreboard_columns_set default\n" @@ -705,12 +707,12 @@ msgstr "^2scoreboard_columns_set ^7campo1 campo2...\n" #: qcsrc/client/hud/panel/scoreboard.qc:300 msgid "The following field names are recognized (case insensitive):\n" msgstr "" -"Os seguintes nomes de campos são reconhecidos (maiúsculas/minúsculas não são " -"distintas):\n" +"Os seguintes nomes de campos são reconhecidos (insensível a maiúsculas e " +"minúsculas):\n" #: qcsrc/client/hud/panel/scoreboard.qc:301 msgid "You can use a ^3|^7 to start the right-aligned fields.\n" -msgstr "Você pode usar um ^3|^7 para iniciar os campos alinhados à direita.\n" +msgstr "Podes usar um ^3|^7 para iniciar os campos alinhados à direita.\n" #: qcsrc/client/hud/panel/scoreboard.qc:304 msgid "^3name^7 or ^3nick^7 Name of a player\n" @@ -765,16 +767,16 @@ msgid "" "^3caps^7 How often a flag (CTF) or a key (KeyHunt) was " "captured\n" msgstr "" -"^3capturas^7 Quão frequente uma bandeira (CTF) ou uma chave (KeyHunt) foi " -"capturada\n" +"^3capturas^7 Quão frequente uma bandeira (CTF) ou uma chave (Caça a Chaves) " +"foi capturada\n" #: qcsrc/client/hud/panel/scoreboard.qc:317 msgid "" "^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a " "ball (Keepaway) was picked up\n" msgstr "" -"^3coletas^7. Quão frequente uma bandeira (CTF), uma chave (KeyHunt) ou uma " -"bola (Keepaway) foi coletada\n" +"^3coletas^7. Quão frequente uma bandeira (CTF), uma chave (Caça a Chaves) ou " +"uma bola (Keepaway) foi recolhida\n" #: qcsrc/client/hud/panel/scoreboard.qc:318 msgid "^3captime^7 Time of fastest cap (CTF)\n" @@ -787,11 +789,11 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:320 msgid "^3returns^7 Number of flag returns\n" -msgstr "^3retornos^7 Número de bandeiras retomadas\n" +msgstr "^3retornos^7 Número de bandeiras retornadas\n" #: qcsrc/client/hud/panel/scoreboard.qc:321 msgid "^3drops^7 Number of flag drops\n" -msgstr "^3quedas^7 Número de bandeiras que foram soltas\n" +msgstr "^3quedas^7 Número de bandeiras que foram largadas\n" #: qcsrc/client/hud/panel/scoreboard.qc:322 msgid "^3lives^7 Number of lives (LMS)\n" @@ -862,12 +864,12 @@ msgid "" "field to show all fields available for the current game mode.\n" "\n" msgstr "" -"Antes dos campos de digitação, você pode inserir um sinal de + ou -, e usar " -"uma lista de modos de jogo separada por vírgulas.\n" -"Insira barras para fazer com que os campos sejam mostrados somente nesses " +"Antes dos campos de digitação, podes inserir um sinal de + ou -, e usar uma " +"lista de modos de jogo separada por vírgulas.\n" +"Insere barras para fazer com que os campos sejam mostrados apenas nesses " "modos de jogo ou em todos os modos exceto os especificados.\n" -"Você também pode especificar a palavra 'all' como um campo para mostrar " -"todos os campos disponíveis para o modo de jogo atual.\n" +"Também podes especificar a palavra 'all' como um campo para mostrar todos os " +"campos disponíveis para o modo de jogo atual.\n" "\n" #: qcsrc/client/hud/panel/scoreboard.qc:343 @@ -876,8 +878,8 @@ msgid "" "include/exclude ALL teams/noteams game modes.\n" "\n" msgstr "" -"Os nomes especiais de modos de jogo 'teams' e 'noteams' podem ser usados\n" -"para incluir/excluir TODOS os modos de jogo de equipe/sem equipe\n" +"Podem ser usados os nomes especiais de modos de jogo 'teams' e 'noteams'\n" +"para incluir/excluir TODOS os modos de jogo de equipa/sem equipa\n" #: qcsrc/client/hud/panel/scoreboard.qc:346 msgid "Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n" @@ -889,7 +891,7 @@ msgid "" "will display name, ping and pl aligned to the left, and the fields\n" "right of the vertical bar aligned to the right.\n" msgstr "" -"irá exibir nome, ping e pp alinhados à esquerda e os campos\n" +"irá mostrar o nome, ping e pp alinhados à esquerda e os campos\n" "à direita da barra vertical alinhados à direita.\n" #: qcsrc/client/hud/panel/scoreboard.qc:349 @@ -897,7 +899,7 @@ msgid "" "'field3' will only be shown in CTF, and 'field4' will be shown in all\n" "other gamemodes except DM.\n" msgstr "" -"'field3' só será exibido em CTF e 'field4' será exibido em todos\n" +"'field3' só será mostrado em CTF e 'field4' será mostrado em todos\n" "os outros modos de jogo, exceto DM.\n" #: qcsrc/client/hud/panel/scoreboard.qc:611 @@ -914,7 +916,7 @@ msgstr "N/A" #: qcsrc/client/hud/panel/scoreboard.qc:1156 #, c-format msgid "Accuracy stats (average %d%%)" -msgstr "Estatísticas de precisão (média %d%%)" +msgstr "Estatísticas de pontaria (média %d%%)" #: qcsrc/client/hud/panel/scoreboard.qc:1295 msgid "Map stats:" @@ -930,7 +932,7 @@ msgstr "Segredos encontrados:" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Capture time rankings" -msgstr "Classificações de tempo de capturas" +msgstr "Classificações de tempo de captura" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Rankings" @@ -939,12 +941,12 @@ msgstr "Classificações" #: qcsrc/client/hud/panel/scoreboard.qc:1519 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 msgid "Scoreboard" -msgstr "Placar" +msgstr "Placar de pontuações" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format msgid "Speed award: %d%s ^7(%s^7)" -msgstr "Prêmio de velocidade: %d%s ^7(%s^7)" +msgstr "Prémio de velocidade: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1588 #, c-format @@ -954,18 +956,18 @@ msgstr "O mais rápido de todos: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1604 #, c-format msgid "Spectators" -msgstr "Espectadores" +msgstr "Espetadores" #: qcsrc/client/hud/panel/scoreboard.qc:1619 #, c-format msgid "playing ^3%s^7 on ^2%s^7" -msgstr "jogando ^3%s^7 em ^2%s^7" +msgstr "a jogar ^3%s^7 em ^2%s^7" #: qcsrc/client/hud/panel/scoreboard.qc:1626 #: qcsrc/client/hud/panel/scoreboard.qc:1631 #, c-format msgid " for up to ^1%1.0f minutes^7" -msgstr " por até ^1%1.0f minutos^7" +msgstr " até ^1%1.0f minutos^7" #: qcsrc/client/hud/panel/scoreboard.qc:1635 #: qcsrc/client/hud/panel/scoreboard.qc:1654 @@ -1006,33 +1008,33 @@ msgstr "^1Ressurgindo em ^3%s^1..." #: qcsrc/client/hud/panel/scoreboard.qc:1698 #, c-format msgid "You are dead, wait ^3%s^7 before respawning" -msgstr "Você morreu. Espere ^3%s^7 antes de ressurgir" +msgstr "Morreste. Espera ^3%s^7 antes de ressurgir" #: qcsrc/client/hud/panel/scoreboard.qc:1707 #, c-format msgid "You are dead, press ^2%s^7 to respawn" -msgstr "Você morreu. Aperte ^2%s^7 para ressurgir" +msgstr "Morreste. Pressiona ^2%s^7 para ressurgir" #: qcsrc/client/hud/panel/vote.qc:24 msgid "^1You must answer before entering hud configure mode\n" msgstr "" -"^1Você tem que responder antes de entrar no modo de configuração do HUD\n" +"^1Tens que responder antes de entrar no modo de configuração da interface\n" #: qcsrc/client/hud/panel/vote.qc:29 msgid "^2Name ^7instead of \"^1Anonymous player^7\" in stats" -msgstr "^2Nome ^7em vez de \"^1Jogador anônimo^7\" nas estatísticas" +msgstr "^2Nome ^7em vez de \"^1Jogador anónimo^7\" nas estatísticas" #: qcsrc/client/hud/panel/vote.qc:115 msgid "A vote has been called for:" -msgstr "Uma votação foi iniciada para:" +msgstr "Foi iniciada uma votação para:" #: qcsrc/client/hud/panel/vote.qc:117 msgid "Allow servers to store and display your name?" -msgstr "Permitir que servidores armazenem e mostrem o seu nome?" +msgstr "Permitir que os servidores armazenem e mostrem o teu nome?" #: qcsrc/client/hud/panel/vote.qc:121 msgid "^1Configure the HUD" -msgstr "^1Configurar o HUD" +msgstr "^1Configurar a Interface" #: qcsrc/client/hud/panel/vote.qc:125 qcsrc/menu/xonotic/dialog_firstrun.qc:82 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:18 @@ -1056,7 +1058,7 @@ msgstr "Não" #: qcsrc/client/hud/panel/weapons.qc:530 msgid "Out of ammo" -msgstr "Sem munição" +msgstr "Sem munições" #: qcsrc/client/hud/panel/weapons.qc:534 msgid "Don't have" @@ -1110,7 +1112,7 @@ msgstr "Decidir o modo de jogo" #: qcsrc/client/mapvoting.qc:365 msgid "Vote for a map" -msgstr "Vote em um mapa" +msgstr "Vota num mapa" #: qcsrc/client/mapvoting.qc:382 #, c-format @@ -1120,20 +1122,19 @@ msgstr "Faltam %d segundos" #: qcsrc/client/mapvoting.qc:497 msgid "" "mv_mapdownload: ^3You're not supposed to use this command on your own!\n" -msgstr "mv_mapdownload: ^3Você não pode usar esse comando para si próprio!\n" +msgstr "mv_mapdownload: ^3Não podes usar esse comando para ti próprio!\n" #: qcsrc/client/mapvoting.qc:507 msgid "^1Error:^7 Couldn't find pak index.\n" -msgstr "^1Erro:^7 Não foi possível encontrar o índice do pak.\n" +msgstr "^1Erro:^7 não foi possível encontrar o índice do pak.\n" #: qcsrc/client/mapvoting.qc:516 msgid "Requesting preview...\n" -msgstr "Solicitando previsão...\n" +msgstr "A pedir a pré-visualização...\n" #: qcsrc/client/miscfunctions.qc:109 msgid "Trying to remove a team which is not in the teamlist!" -msgstr "" -"Você está tentando remover uma equipe que não está na lista de equipes!" +msgstr "Estás a tentar remover uma equipa que não está na lista de equipas!" #: qcsrc/client/view.qc:1380 msgid "Nade timer" @@ -1154,7 +1155,7 @@ msgstr "erro ao criar curl handle\n" #: qcsrc/common/command/generic.qc:403 msgid "Notification restart command only works with cl_cmd and sv_cmd.\n" msgstr "" -"Comando de reinício de notificação funciona apenas com cl_cmd e sv_cmd.\n" +"O comando de reinício de notificação funciona apenas com cl_cmd e sv_cmd.\n" #: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 msgid "Ball Stealer" @@ -1195,7 +1196,7 @@ msgstr "Escudo" #: qcsrc/common/mapinfo.qc:639 #, no-c-format msgid "@!#%'n Tuba Throwing" -msgstr "@!#%'n Tuba Throwing" +msgstr "@!#%'n Atirar da Tuba" #: qcsrc/common/mapinfo.qh:99 msgid "Deathmatch" @@ -1203,15 +1204,15 @@ msgstr "Mata-mata" #: qcsrc/common/mapinfo.qh:99 msgid "Score as many frags as you can" -msgstr "Consiga o máximo de execuções que puder" +msgstr "Consegue o máximo de execuções que puderes" #: qcsrc/common/mapinfo.qh:111 msgid "Last Man Standing" -msgstr "Last Man Standing" +msgstr "Último Homem de Pé" #: qcsrc/common/mapinfo.qh:111 msgid "Survive and kill until the enemies have no lives left" -msgstr "Sobreviva e mate até que os inimigos não tenham vidas sobrando" +msgstr "Sobrevive e mata até que os inimigos não tenham mais vidas" #: qcsrc/common/mapinfo.qh:126 msgid "Race" @@ -1219,7 +1220,7 @@ msgstr "Corrida" #: qcsrc/common/mapinfo.qh:126 msgid "Race against other players to the finish line" -msgstr "Corra contra outros jogadores até a linha de chegada" +msgstr "Corra contra os outros jogadores até à linha de chegada" #: qcsrc/common/mapinfo.qh:160 msgid "Race CTS" @@ -1227,111 +1228,113 @@ msgstr "Corrida CTS" #: qcsrc/common/mapinfo.qh:160 msgid "Race for fastest time." -msgstr "Corra pelo melhor tempo." +msgstr "Corre pelo melhor tempo." #: qcsrc/common/mapinfo.qh:184 msgid "Help your team score the most frags against the enemy team" -msgstr "Ajude sua equipe a conseguir mais execuções do que a equipe inimiga" +msgstr "" +"Ajuda a tua equipa a conseguir mais execuções do que a equipa do inimigo" #: qcsrc/common/mapinfo.qh:184 msgid "Team Deathmatch" -msgstr "Mata-mata por Equipe" +msgstr "Mata-mata por Equipa" #: qcsrc/common/mapinfo.qh:220 msgid "Capture the Flag" -msgstr "Capture a Bandeira" +msgstr "Captura a Bandeira" #: qcsrc/common/mapinfo.qh:220 msgid "" "Find and bring the enemy flag to your base to capture it, defend your base " "from the other team" msgstr "" -"Encontre e retorne a bandeira inimiga à sua base para capturá-la e defenda " -"sua base da equipe oponente" +"Encontra e retorna a bandeira inimiga à tua base para capturá-la e defende a " +"tua base da equipa oponente" #: qcsrc/common/mapinfo.qh:249 msgid "Clan Arena" -msgstr "Clan Arena" +msgstr "Clã Arena" #: qcsrc/common/mapinfo.qh:249 msgid "Kill all enemy teammates to win the round" -msgstr "Mate todos os inimigos para vencer a rodada" +msgstr "Mata todos os inimigos para vencer a rodada" #: qcsrc/common/mapinfo.qh:287 msgid "Capture and defend all the control points to win" -msgstr "Capture e defenda todos os pontos de controle para vencer" +msgstr "Captura e defende todos os pontos de controlo para vencer" #: qcsrc/common/mapinfo.qh:287 msgid "Domination" -msgstr "Domination" +msgstr "Dominação" #: qcsrc/common/mapinfo.qh:319 msgid "Gather all the keys to win the round" -msgstr "Colete todas as chaves para vencer a rodada" +msgstr "Recolhe todas as chaves para vencer a rodada" #: qcsrc/common/mapinfo.qh:319 msgid "Key Hunt" -msgstr "Key Hunt" +msgstr "Caça as Chaves" #: qcsrc/common/mapinfo.qh:353 msgid "Assault" -msgstr "Assault" +msgstr "Assalto" #: qcsrc/common/mapinfo.qh:353 msgid "" "Destroy obstacles to find and destroy the enemy power core before time runs " "out" msgstr "" -"Destrua obstáculos para encontrar e destruir o núcleo de poder inimigo antes " +"Destrói obstáculos para encontrar e destruir o núcleo de poder inimigo antes " "que o tempo acabe" #: qcsrc/common/mapinfo.qh:371 msgid "Capture control points to reach and destroy the enemy generator" -msgstr "Capture pontos de controle para alcançar e destruir o gerador inimigo" +msgstr "" +"Captura os pontos de controlo para alcançar e destruir o gerador inimigo" #: qcsrc/common/mapinfo.qh:371 msgid "Onslaught" -msgstr "Onslaught" +msgstr "Massacre" #: qcsrc/common/mapinfo.qh:387 msgid "Nexball" -msgstr "Nexball" +msgstr "Bola Nex" #: qcsrc/common/mapinfo.qh:387 msgid "Shoot and kick the ball into the enemies goal, keep your goal clean" -msgstr "Atire e chute a bola no gol inimigo e mantenha seu gol limpo" +msgstr "Atira e chuta a bola na baliza do inimigo e mantém a tua baliza limpa" #: qcsrc/common/mapinfo.qh:408 msgid "Freeze Tag" -msgstr "Freeze Tag" +msgstr "Congela" #: qcsrc/common/mapinfo.qh:408 msgid "" "Kill enemies to freeze them, stand next to frozen teammates to revive them; " "freeze all enemies to win" msgstr "" -"Mate inimigos para congelá-los. Fique perto de colegas para descongelá-los; " -"congele todos os inimigos para vencer" +"Mata os inimigos para congelá-los. Fica perto de colegas para descongelá-" +"los; congela todos os inimigos para venceres" #: qcsrc/common/mapinfo.qh:446 msgid "Hold the ball to get points for kills" -msgstr "Segure a bola para ganhar pontos por cada jogador aniquilado" +msgstr "Segura a bola para ganhar pontos por cada jogador aniquilado" #: qcsrc/common/mapinfo.qh:446 msgid "Keepaway" -msgstr "Keepaway" +msgstr "" #: qcsrc/common/mapinfo.qh:461 msgid "Invasion" -msgstr "Invasion" +msgstr "Invasão" #: qcsrc/common/mapinfo.qh:461 msgid "Survive against waves of monsters" -msgstr "Sobreviva contra ondas de monstros" +msgstr "Sobrevive contra ondas de monstros" #: qcsrc/common/minigames/cl_minigames.qc:383 msgid "It's your turn" -msgstr "É a sua vez" +msgstr "É a tua vez" #: qcsrc/common/minigames/cl_minigames_hud.qc:331 #: qcsrc/menu/xonotic/dialog_quit.qh:6 @@ -1361,29 +1364,29 @@ msgstr "Entrar" #: qcsrc/common/minigames/cl_minigames_hud.qc:489 msgid "Minigames" -msgstr "Minijogos" +msgstr "Mini-jogos" #: qcsrc/common/minigames/minigame/bd.qc:1168 msgid "Better luck next time!" -msgstr "Mais sorte da próxima vez!" +msgstr "Talvez tenhas mais sorte da próxima vez!" #: qcsrc/common/minigames/minigame/bd.qc:1172 msgid "Tubular! Press \"Next Level\" to continue!" -msgstr "Tubular! Clique em \"Próximo Mapa\" para continuar!" +msgstr "Tubular! Clica em \"Próximo Mapa\" para continuar!" #: qcsrc/common/minigames/minigame/bd.qc:1174 msgid "Wicked! Press \"Next Level\" to continue!" -msgstr "Enfeitiçado! Clique em \"Próximo Mapa\" para continuar!" +msgstr "Enfeitiçado! Clica em \"Próximo Mapa\" para continuar!" #: qcsrc/common/minigames/minigame/bd.qc:1177 msgid "Press the space bar to change your currently selected tile" msgstr "" -"Aperte a barra de espaço para alterar a sua imagem de equipe atualmente " -"selecionada" +"Carrega na tecla de barra de espaços para alterar a tua imagem da equipa " +"atualmente selecionada" #: qcsrc/common/minigames/minigame/bd.qc:1180 msgid "Push the boulders onto the targets" -msgstr "Empurre os pedregulhos em direção aos alvos" +msgstr "Empurra os pedregulhos em direção aos alvos" #: qcsrc/common/minigames/minigame/bd.qc:1404 msgid "Next Level" @@ -1400,7 +1403,7 @@ msgstr "Editor" #: qcsrc/common/minigames/minigame/bd.qc:1407 #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:37 msgid "Save" -msgstr "Salvar" +msgstr "Gravar" #: qcsrc/common/minigames/minigame/c4.qc:373 #: qcsrc/common/minigames/minigame/pp.qc:438 @@ -1411,43 +1414,42 @@ msgstr "Empate" #: qcsrc/common/minigames/minigame/c4.qc:378 #: qcsrc/common/minigames/minigame/nmm.qc:601 msgid "You lost the game!" -msgstr "Você perdeu o jogo!" +msgstr "Perdeste o jogo!" #: qcsrc/common/minigames/minigame/c4.qc:379 #: qcsrc/common/minigames/minigame/nmm.qc:602 msgid "You win!" -msgstr "Você venceu!" +msgstr "Venceste!" #: qcsrc/common/minigames/minigame/c4.qc:383 #: qcsrc/common/minigames/minigame/nmm.qc:606 #: qcsrc/common/minigames/minigame/pp.qc:455 #: qcsrc/common/minigames/minigame/ttt.qc:336 msgid "Wait for your opponent to make their move" -msgstr "Espere o seu oponente terminar a vez dele" +msgstr "Espera que o teu oponente termine a vez dele" #: qcsrc/common/minigames/minigame/c4.qc:386 #: qcsrc/common/minigames/minigame/nmm.qc:608 #: qcsrc/common/minigames/minigame/pp.qc:458 #: qcsrc/common/minigames/minigame/ttt.qc:339 msgid "Click on the game board to place your piece" -msgstr "Clique no tabuleiro de jogo para posicionar sua peça" +msgstr "Clica no tabuleiro de jogo para posicionar a tua peça" #: qcsrc/common/minigames/minigame/nmm.qc:610 msgid "" "You can select one of your pieces to move it in one of the surrounding places" msgstr "" -"Você pode selecionar uma de suas peças para movê-la para um dos lugares ao " -"redor" +"Podes selecionar uma das tuas peças para movê-la para um dos lugares em redor" #: qcsrc/common/minigames/minigame/nmm.qc:612 msgid "You can select one of your pieces to move it anywhere on the board" msgstr "" -"Você pode selecionar uma de suas peças para movê-la para qualquer lugar no " +"Podes selecionar uma das tuas peças para movê-la para qualquer lugar no " "tabuleiro" #: qcsrc/common/minigames/minigame/nmm.qc:614 msgid "You can take one of the opponent's pieces" -msgstr "Você pode tomar uma das peças do seu oponente" +msgstr "Podes pegar numa das peças do teu oponente" #: qcsrc/common/minigames/minigame/pong.qc:570 #: qcsrc/common/minigames/minigame/ttt.qc:299 @@ -1457,7 +1459,7 @@ msgstr "IA" #: qcsrc/common/minigames/minigame/pong.qc:587 msgid "Press ^1Start Match^7 to start the match with the current players" msgstr "" -"Aperte ^1Iniciar Partida^7 para iniciar a partida com os jogadores atuais" +"Pressiona ^1Iniciar Partida^7 para iniciar a partida com os jogadores atuais" #: qcsrc/common/minigames/minigame/pong.qc:651 msgid "Start Match" @@ -1465,11 +1467,11 @@ msgstr "Iniciar Partida" #: qcsrc/common/minigames/minigame/pong.qc:652 msgid "Add AI player" -msgstr "Adicionar bot" +msgstr "Adicionar robô" #: qcsrc/common/minigames/minigame/pong.qc:653 msgid "Remove AI player" -msgstr "Remover bot" +msgstr "Remover robô" #: qcsrc/common/minigames/minigame/pp.qc:443 #: qcsrc/common/minigames/minigame/ttt.qc:324 @@ -1477,8 +1479,8 @@ msgid "" "You lost the game!\n" "Select \"^1Next Match^7\" on the menu for a rematch!" msgstr "" -"Você perdeu o jogo!\n" -"Selecione \"^1Próxima Partida^7\" no menu para uma revanche!" +"Perdeste o jogo!\n" +"Seleciona \"^1Próxima Partida^7\" no menu para te vingares!" #: qcsrc/common/minigames/minigame/pp.qc:444 #: qcsrc/common/minigames/minigame/ttt.qc:325 @@ -1486,19 +1488,19 @@ msgid "" "You win!\n" "Select \"^1Next Match^7\" on the menu to start a new match!" msgstr "" -"Você venceu!\n" -"Selecione \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" +"Venceste!\n" +"Seleciona \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" #: qcsrc/common/minigames/minigame/pp.qc:450 #: qcsrc/common/minigames/minigame/ttt.qc:331 msgid "Select \"^1Next Match^7\" on the menu to start a new match!" msgstr "" -"Selecione \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" +"Seleciona \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" #: qcsrc/common/minigames/minigame/pp.qc:451 #: qcsrc/common/minigames/minigame/ttt.qc:332 msgid "Wait for your opponent to confirm the rematch" -msgstr "Espere o seu oponente confirmar a revanche" +msgstr "Espera que o teu oponente confirme a vingança" #: qcsrc/common/minigames/minigame/pp.qc:582 #: qcsrc/common/minigames/minigame/ttt.qc:665 @@ -1516,11 +1518,11 @@ msgstr "Não há mais movimentos válidos" #: qcsrc/common/minigames/minigame/ps.qc:491 msgid "Well done, you win!" -msgstr "Bom trabalho, você venceu!" +msgstr "Bom trabalho, venceste!" #: qcsrc/common/minigames/minigame/ps.qc:494 msgid "Jump a piece over another to capture it" -msgstr "Faça uma peça saltar sobre outra para capturá-la" +msgstr "Faz com que uma peça salte sobre outra para capturá-la" #: qcsrc/common/minigames/minigame/ttt.qc:666 msgid "Single Player" @@ -1538,7 +1540,7 @@ msgstr "Prego de mago" #: qcsrc/common/monsters/monster/shambler.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:17 msgid "Shambler" -msgstr "Shambler" +msgstr "" #: qcsrc/common/monsters/monster/spider.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:16 @@ -1552,11 +1554,11 @@ msgstr "Ataque da Aranha" #: qcsrc/common/monsters/monster/wyvern.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:19 msgid "Wyvern" -msgstr "Wyvern" +msgstr "" #: qcsrc/common/monsters/monster/wyvern.qh:28 msgid "Wyvern attack" -msgstr "Ataque do Wyvern" +msgstr "" #: qcsrc/common/monsters/monster/zombie.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:15 @@ -1565,7 +1567,7 @@ msgstr "Zumbi" #: qcsrc/common/mutators/mutator/buffs/all.inc:15 msgid "Ammo" -msgstr "Munição" +msgstr "Munições" #: qcsrc/common/mutators/mutator/buffs/all.inc:24 msgid "Resistance" @@ -1616,7 +1618,7 @@ msgstr "Trocador" #: qcsrc/common/mutators/mutator/buffs/all.inc:120 msgid "Magnet" -msgstr "Ímã" +msgstr "Íman" #: qcsrc/common/mutators/mutator/buffs/all.inc:128 msgid "Luck" @@ -1628,7 +1630,7 @@ msgstr "Voo" #: qcsrc/common/mutators/mutator/buffs/buffs.qh:7 msgid "Buff" -msgstr "Bônus" +msgstr "Bónus" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:8 msgid "Damage text" @@ -1636,19 +1638,19 @@ msgstr "Texto de dano" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:18 msgid "Draw damage numbers" -msgstr "Exibir números de dano" +msgstr "Mostrar números de dano" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 msgid "Font size minimum:" -msgstr "Tamanho da fonte mínimo:" +msgstr "Tamanho mínimo da fonte:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 msgid "Font size maximum:" -msgstr "Tamanho da fonte máximo:" +msgstr "Tamanho máximo da fonte:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 msgid "Accumulate range:" -msgstr "Alcance de acúmulo:" +msgstr "Alcance de acumulação:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:35 msgid "Lifetime:" @@ -1666,7 +1668,7 @@ msgstr "Cor:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 msgid "Draw damage numbers for friendly fire" -msgstr "Exibir números de dano para fogo amigo" +msgstr "Mostrar números de dano para fogo amigo" #: qcsrc/common/mutators/mutator/instagib/items.qh:56 msgid "Extra life" @@ -1710,11 +1712,11 @@ msgstr "Granada" #: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 msgid "Heavy Machine Gun" -msgstr "Heavy Machine Gun" +msgstr "Metralhadora Pesada" #: qcsrc/common/mutators/mutator/overkill/rpc.qh:17 msgid "Rocket Propelled Chainsaw" -msgstr "Rocket Propelled Chainsaw" +msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:3 msgid "Waypoint" @@ -1742,7 +1744,7 @@ msgstr "Item" #: qcsrc/common/mutators/mutator/waypoints/all.inc:12 msgid "Checkpoint" -msgstr "Ponto de checagem" +msgstr "Ponto de verificação" #: qcsrc/common/mutators/mutator/waypoints/all.inc:13 #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 @@ -1769,7 +1771,7 @@ msgstr "Empurrar" #: qcsrc/common/mutators/mutator/waypoints/all.inc:21 msgid "Flag carrier" -msgstr "Portador de bandeiras" +msgstr "Portador de bandeira" #: qcsrc/common/mutators/mutator/waypoints/all.inc:22 msgid "Enemy carrier" @@ -1801,7 +1803,7 @@ msgstr "Base rosa" #: qcsrc/common/mutators/mutator/waypoints/all.inc:29 msgid "Return flag here" -msgstr "Traga a bandeira para cá" +msgstr "Traz a bandeira para cá" #: qcsrc/common/mutators/mutator/waypoints/all.inc:31 #: qcsrc/common/mutators/mutator/waypoints/all.inc:32 @@ -1812,7 +1814,7 @@ msgstr "Traga a bandeira para cá" #: qcsrc/common/mutators/mutator/waypoints/all.inc:52 #: qcsrc/common/mutators/mutator/waypoints/all.inc:53 msgid "Control point" -msgstr "Ponto de controle" +msgstr "Ponto de controlo" #: qcsrc/common/mutators/mutator/waypoints/all.inc:37 msgid "Dropped key" @@ -1824,11 +1826,11 @@ msgstr "Chave largada" #: qcsrc/common/mutators/mutator/waypoints/all.inc:42 #: qcsrc/common/mutators/mutator/waypoints/all.inc:43 msgid "Key carrier" -msgstr "Portador de chaves" +msgstr "Portador de chave" #: qcsrc/common/mutators/mutator/waypoints/all.inc:39 msgid "Run here" -msgstr "Corra aqui" +msgstr "Corre para aqui" #: qcsrc/common/mutators/mutator/waypoints/all.inc:45 #: qcsrc/common/mutators/mutator/waypoints/all.inc:48 @@ -1837,11 +1839,11 @@ msgstr "Bola" #: qcsrc/common/mutators/mutator/waypoints/all.inc:46 msgid "Ball carrier" -msgstr "Portador de bolas" +msgstr "Portador de bola" #: qcsrc/common/mutators/mutator/waypoints/all.inc:49 msgid "Goal" -msgstr "Gol" +msgstr "Golo" #: qcsrc/common/mutators/mutator/waypoints/all.inc:54 #: qcsrc/common/mutators/mutator/waypoints/all.inc:55 @@ -1876,7 +1878,7 @@ msgstr "Spam" #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:655 #, c-format msgid "%s needing help!" -msgstr "%s precisando de ajuda!" +msgstr "%s a precisar de ajuda!" #: qcsrc/common/net_notice.qc:87 msgid "^1Server notices:" @@ -1885,7 +1887,7 @@ msgstr "^1Avisos do servidor:" #: qcsrc/common/notifications/all.inc:239 msgid "^F4NOTE: ^BGSpectator chat is not sent to players during the match" msgstr "" -"^F4NOTA: ^BGMensagens no bate-papo de espectador não serão enviadas aos " +"^F4NOTA: ^BGas mensagens na conversação de espetador não serão enviadas aos " "jogadores durante a partida" #: qcsrc/common/notifications/all.inc:241 @@ -1919,7 +1921,7 @@ msgid "" "^BG%s^BG's previous record of ^F1%s^BG seconds" msgstr "" "^BG%s^BG capturou a bandeira ^TC^TT^BG em ^F2%s^BG segundos, não quebrando o " -"record anterior de ^BG%s^BG de ^F1%s^BG segundos" +"recorde anterior de ^BG%s^BG de ^F1%s^BG segundos" #: qcsrc/common/notifications/all.inc:246 msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" @@ -1949,12 +1951,11 @@ msgstr "^BGA bandeira caiu na base e retornou sozinha" msgid "" "^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to " "base" -msgstr "" -"^BGA bandeira ^TC^TT^BG caiu em algum lugar inacessível e retornou à base" +msgstr "^BGA bandeira ^TC^TT^BG caiu num lugar inacessível e retornou à base" #: qcsrc/common/notifications/all.inc:253 msgid "^BGThe flag fell somewhere it couldn't be reached and returned to base" -msgstr "^BGA bandeira caiu em algum lugar inacessível e retornou à base" +msgstr "^BGA bandeira caiu num lugar inacessível e retornou à base" #: qcsrc/common/notifications/all.inc:254 #, c-format @@ -1999,7 +2000,7 @@ msgstr "^BG%s^BG pegou a bandeira ^TC^TT^BG" #: qcsrc/common/notifications/all.inc:261 #, c-format msgid "^BG%s^BG got the flag" -msgstr "^BG%s^BG pegou a bandeira" +msgstr "^BG%s^BG pegou na bandeira" #: qcsrc/common/notifications/all.inc:262 #: qcsrc/common/notifications/all.inc:263 @@ -2011,35 +2012,35 @@ msgstr "^BG%s^BG retornou a bandeira ^TC^TT^BG" #: qcsrc/common/notifications/all.inc:553 #, c-format msgid "^F2Throwing coin... Result: %s^F2!" -msgstr "^F2Atirando moeda... Resultado: %s^F2!" +msgstr "^F2A atirar a moeda... Resultado: %s^F2!" #: qcsrc/common/notifications/all.inc:267 msgid "^BGYou don't have any fuel for the ^F1Jetpack" -msgstr "^BGVocê está sem combustível para a ^F1Mochila a Jato" +msgstr "^BGEstás sem combustível para a ^F1Mochila a Jato" #: qcsrc/common/notifications/all.inc:269 msgid "^F2You lack a UID, superspec options will not be saved/restored" msgstr "" -"^F2Você não tem um UID, opções de sperspec não serão salvas/restauradas" +"^F2Não tens um UID, as opções de sperspec não serão gravadas/restauradas" #: qcsrc/common/notifications/all.inc:271 msgid "^F1Round already started, you will join the game in the next round" -msgstr "^F1A rodada já começou, você entrará no jogo na próxima rodada" +msgstr "^F1A rodada já começou, vais entrar no jogo na próxima rodada" #: qcsrc/common/notifications/all.inc:272 msgid "^F2You will spectate in the next round" -msgstr "^F2Você ficará de espectador na próxima rodada" +msgstr "^F2Vais ficar no modo espetador na próxima rodada" #: qcsrc/common/notifications/all.inc:274 #, c-format msgid "^BG%s%s^K1 was killed by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" -msgstr "^BG%s%s^K1 foi morto pelo bônus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" +msgstr "^BG%s%s^K1 foi morto pelo bónus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" #: qcsrc/common/notifications/all.inc:274 #, c-format msgid "^BG%s%s^K1 was scored against by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" msgstr "" -"^BG%s%s^K1 foi pontuado contra pelo bônus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" +"^BG%s%s^K1 foi pontuado contra pelo bónus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" #: qcsrc/common/notifications/all.inc:275 #, c-format @@ -2060,7 +2061,7 @@ msgstr "^BG%s%s^K1 foi castigado por ^BG%s^K1%s%s" #, c-format msgid "^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s" msgstr "" -"^BG%s%s^K1 se sentiu um pouco quente por causa do fogo de ^BG%s^K1^K1%s%s" +"^BG%s%s^K1 sentiu-se um pouco quente por causa do fogo de ^BG%s^K1^K1%s%s" #: qcsrc/common/notifications/all.inc:278 #, c-format @@ -2075,7 +2076,7 @@ msgstr "^BG%s%s^K1 foi cozinhado por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:280 #, c-format msgid "^BG%s%s^K1 was pushed in front of a monster by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 foi empurrado em frente de um monstro por ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi empurrado na frente de um monstro por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:281 #, c-format @@ -2085,28 +2086,29 @@ msgstr "^BG%s%s^K1 foi explodido pela Granada de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:282 #, c-format msgid "^BG%s%s^K1 got too close to a napalm explosion%s%s" -msgstr "^BG%s%s^K1 se aproximou demais de uma explosão de napalm%s%s" +msgstr "^BG%s%s^K1 aproximou-se demais de uma explosão de napalm%s%s" #: qcsrc/common/notifications/all.inc:282 #, c-format msgid "^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s" msgstr "" -"^BG%s%s^K1 foi queimado até a morte pela Granada de Napalm de ^BG%s^K1%s%s" +"^BG%s%s^K1 foi queimado até à morte pela Granada de Napalm de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:283 #, c-format msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s" -msgstr "^BG%s%s^K1 foi explodido pega Granada de Gelo de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi explodido pela Granada de Gelo de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:284 #, c-format msgid "^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s" -msgstr "^BG%s%s^K1 foi explodido pega Granada de Gelo de ^BG%s^K1%s%s" +msgstr "" +"^BG%s%s^K1 foi congelado até à morte pela Granada de Gelo de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:285 #, c-format msgid "^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s" -msgstr "^BG%s%s^K1 não foi curado pela Granade de Cura de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 não foi curado pela Granada de Cura de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:286 #, c-format @@ -2131,23 +2133,23 @@ msgstr "^BG%s%s^K1 tentou ocupar o espaço do teletransporte de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:289 #, c-format msgid "^BG%s%s^K1 was telefragged by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 levou um telefrag de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi teletransmorto por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:290 #, c-format msgid "^BG%s%s^K1 died in an accident with ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 morreu em um acidente com ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 morreu num acidente com ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:291 #, c-format msgid "" "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Bumblebee exploded%s%s" -msgstr "^BG%s%s^K1 foi pego pela explosão de Bumblebee de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi apanhado pela explosão de Bumblebee de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:292 #, c-format msgid "^BG%s%s^K1 saw the pretty lights of ^BG%s^K1's Bumblebee gun%s%s" -msgstr "^BG%s%s^K1 viu as lindas luzes da arma do Bumblebee de ^BG%s^K1%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:293 #, c-format @@ -2167,28 +2169,29 @@ msgstr "^BG%s%s^K1 não resistiu às bolhas roxas de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:296 #, c-format msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Raptor exploded%s%s" -msgstr "^BG%s%s^K1 foi pego pela explosão do Raptor de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi apanhado pela explosão do Raptor de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:297 #, c-format msgid "" "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Spiderbot exploded%s%s" -msgstr "^BG%s%s^K1 foi pego pela explosão do Spiderbot de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi apanhado pela explosão do Robô Aranha de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:298 #, c-format msgid "^BG%s%s^K1 got shredded by ^BG%s^K1's Spiderbot%s%s" -msgstr "^BG%s%s^K1 foi picado pelo Spiderbot de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi picado pelo Robô Aranha de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:299 #, c-format msgid "^BG%s%s^K1 was blasted to bits by ^BG%s^K1's Spiderbot%s%s" -msgstr "^BG%s%s^K1 foi explodido em pedacinhos pelo Spiderbot de ^BG%s^K1%s%s" +msgstr "" +"^BG%s%s^K1 foi explodido em pedacinhos pelo Robô Aranha de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:300 #, c-format msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s" -msgstr "^BG%s%s^K1 foi pego pela explosão do Racer de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi apanhado pela explosão do Racer de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:301 #, c-format @@ -2203,7 +2206,7 @@ msgstr "^BG%s%s^K1 não conseguiu escapar do Racer de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:303 #, c-format msgid "^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 foi jogado em mundo de dor por ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi atirado para um mundo de dor por ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:305 #, c-format @@ -2213,18 +2216,17 @@ msgstr "^BG%s^K1 foi movido para o %s%s" #: qcsrc/common/notifications/all.inc:306 #, c-format msgid "^BG%s^K1 became enemies with the Lord of Teamplay%s%s" -msgstr "^BG%s^K1 tornou-se inimigo do Senhor do Trabalho em Equipe%s%s" +msgstr "^BG%s^K1 tornou-se inimigo do Senhor do Trabalho em Equipa%s%s" #: qcsrc/common/notifications/all.inc:307 #, c-format msgid "^BG%s^K1 thought they found a nice camping ground%s%s" -msgstr "" -"^BG%s^K1 acharam que tinham encontrado um ótimo lugar para camperar%s%s" +msgstr "^BG%s^K1 pensou que tinham encontrado um ótimo lugar para acampar%s%s" #: qcsrc/common/notifications/all.inc:308 #, c-format msgid "^BG%s^K1 unfairly eliminated themself%s%s" -msgstr "^BG%s^K1 se eliminou injustamente%s%s" +msgstr "^BG%s^K1 eliminou-se injustamente%s%s" #: qcsrc/common/notifications/all.inc:310 #, c-format @@ -2254,7 +2256,7 @@ msgstr "^BG%s^K1 ficou um pouco crocante%s%s" #: qcsrc/common/notifications/all.inc:312 #, c-format msgid "^BG%s^K1 felt a little hot%s%s" -msgstr "^BG%s^K1 se sentiu um pouco quente%s%s" +msgstr "^BG%s^K1 sentiu-se um pouco quente%s%s" #: qcsrc/common/notifications/all.inc:313 #, c-format @@ -2264,7 +2266,7 @@ msgstr "^BG%s^K1 morreu%s%s" #: qcsrc/common/notifications/all.inc:314 #, c-format msgid "^BG%s^K1 found a hot place%s%s" -msgstr "^BG%s^K1 achou um lugar quente%s%s" +msgstr "^BG%s^K1 encontrou um lugar quente%s%s" #: qcsrc/common/notifications/all.inc:314 #, c-format @@ -2280,18 +2282,18 @@ msgstr "^BG%s^K1 foi explodido por um Mago%s%s" #, c-format msgid "^BG%s^K1's innards became outwards by a Shambler%s%s" msgstr "" -"Os órgãos internos de ^BG%s^K1 se tornaram externos por causa de um Shambler " +"Os órgãos internos de ^BG%s^K1 tornaram-se externos por causa de um Shambler " "%s%s" #: qcsrc/common/notifications/all.inc:317 #, c-format msgid "^BG%s^K1 was smashed by a Shambler%s%s" -msgstr "^BG%s^K1 foi esmagado por um Shambler%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:318 #, c-format msgid "^BG%s^K1 was zapped to death by a Shambler%s%s" -msgstr "^BG%s^K1 foi eletrocutado até a morte por um Shambler%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:319 #, c-format @@ -2301,12 +2303,12 @@ msgstr "^BG%s^K1 foi picado por uma Aranha%s%s" #: qcsrc/common/notifications/all.inc:320 #, c-format msgid "^BG%s^K1 was fireballed by a Wyvern%s%s" -msgstr "^BG%s^K1 foi morto pela fireball de um Wyvern%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:321 #, c-format msgid "^BG%s^K1 joins the Zombies%s%s" -msgstr "^BG%s^K1 se juntou aos Zumbis%s%s" +msgstr "^BG%s^K1 juntou-se aos Zumbis%s%s" #: qcsrc/common/notifications/all.inc:322 #, c-format @@ -2324,13 +2326,12 @@ msgstr "^BG%s^K1 dominou a arte do suicídio com granadas%s%s" msgid "" "^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s" msgstr "" -"^BG%s^K1 decidiu dar uma olhada nos resultados de sua explosão de napalm%s%s" +"^BG%s^K1 decidiu dar uma olhada nos resultados da sua explosão de napalm%s%s" #: qcsrc/common/notifications/all.inc:324 #, c-format msgid "^BG%s^K1 was burned to death by their own Napalm Nade%s%s" -msgstr "" -"^BG%s^K1 foi queimado até a morte por sua própria Granada de Napalm%s%s" +msgstr "^BG%s^K1 foi queimado até a morte pela própria Granada de Napalm%s%s" #: qcsrc/common/notifications/all.inc:326 #, c-format @@ -2340,22 +2341,23 @@ msgstr "^BG%s^K1 sentiu-se um pouco friolento%s%s" #: qcsrc/common/notifications/all.inc:326 #, c-format msgid "^BG%s^K1 was frozen to death by their own Ice Nade%s%s" -msgstr "^BG%s^K1 foi congelado até a morte por sua própria Granada de Gelo%s%s" +msgstr "" +"^BG%s^K1 foi congelado até a morte pela sua própria Granada de Gelo%s%s" #: qcsrc/common/notifications/all.inc:327 #, c-format msgid "^BG%s^K1's Healing Nade didn't quite heal them%s%s" -msgstr "A Granada de Cura de ^BG%s^K1 não lhe curou corretamente%s%s" +msgstr "A Granada de Cura de ^BG%s^K1 não lhe fez lá muito bem%s%s" #: qcsrc/common/notifications/all.inc:328 #, c-format msgid "^BG%s^K1 died%s%s. What's the point of living without ammo?" -msgstr "^BG%s^K1 morreu%s%s. Qual o sentido de viver sem munição?" +msgstr "^BG%s^K1 morreu%s%s. Para quê viver se não tens munições?" #: qcsrc/common/notifications/all.inc:328 #, c-format msgid "^BG%s^K1 ran out of ammo%s%s" -msgstr "^BG%s^K1 ficou sem munição%s%s" +msgstr "^BG%s^K1 ficou sem munições%s%s" #: qcsrc/common/notifications/all.inc:329 #, c-format @@ -2365,7 +2367,7 @@ msgstr "^BG%s^K1 derreteu%s%s" #: qcsrc/common/notifications/all.inc:330 #, c-format msgid "^BG%s^K1 became a shooting star%s%s" -msgstr "^BG%s^K1 se tornou uma estrela cadente%s%s" +msgstr "^BG%s^K1 tornou-se uma estrela cadente%s%s" #: qcsrc/common/notifications/all.inc:331 #, c-format @@ -2390,44 +2392,43 @@ msgstr "^BG%s^K1 trocou para o %s%s" #: qcsrc/common/notifications/all.inc:335 #, c-format msgid "^BG%s^K1 died in an accident%s%s" -msgstr "^BG%s^K1 morreu em um acidente%s%s" +msgstr "^BG%s^K1 morreu num acidente%s%s" #: qcsrc/common/notifications/all.inc:336 #, c-format msgid "^BG%s^K1 ran into a turret%s%s" -msgstr "^BG%s^K1 deu de cara com uma sentinela%s%s" +msgstr "^BG%s^K1 deu de caras com uma sentinela%s%s" #: qcsrc/common/notifications/all.inc:337 #, c-format msgid "^BG%s^K1 was blasted away by an eWheel turret%s%s" -msgstr "^BG%s^K1 foi explodido por uma sentinela eWheel%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:338 #, c-format msgid "^BG%s^K1 got caught up in the FLAC turret fire%s%s" -msgstr "^BG%s^K1 se meteu no meio do tiroteio de uma sentinela FLAC%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:339 #, c-format msgid "^BG%s^K1 was blasted away by a Hellion turret%s%s" -msgstr "^BG%s^K1 foi explodido por uma sentinela Hellion%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:340 #, c-format msgid "^BG%s^K1 could not hide from the Hunter turret%s%s" -msgstr "^BG%s^K1 não conseguiu se esconder da sentinela Hunter%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:341 #, c-format msgid "^BG%s^K1 was riddled full of holes by a Machinegun turret%s%s" msgstr "" -"^BG%s^K1 ficou cheio de buracos por causa de uma sentinela de Metralhadora%s" -"%s" +"^BG%s^K1 ficou cheio de buracos por causa de uma sentinela Metralhadora%s%s" #: qcsrc/common/notifications/all.inc:342 #, c-format msgid "^BG%s^K1 got turned into smoldering gibs by an MLRS turret%s%s" -msgstr "^BG%s^K1 foi picado em pedacinhos latentes por uma sentinela MLRS%s%s" +msgstr "^BG%s^K1 foi triturado em pedacinhos por uma sentinela MLRS%s%s" #: qcsrc/common/notifications/all.inc:343 #, c-format @@ -2437,7 +2438,7 @@ msgstr "^BG%s^K1 foi eliminado por uma sentinela%s%s" #: qcsrc/common/notifications/all.inc:344 #, c-format msgid "^BG%s^K1 got served some superheated plasma from a turret%s%s" -msgstr "^BG%s^K1 levou um plasma superaquecido de uma sentinela%s%s" +msgstr "^BG%s^K1 levou com um plasma super aquecido de uma sentinela%s%s" #: qcsrc/common/notifications/all.inc:345 #, c-format @@ -2447,22 +2448,22 @@ msgstr "^BG%s^K1 foi eletrocutado por uma sentinela Tesla%s%s" #: qcsrc/common/notifications/all.inc:346 #, c-format msgid "^BG%s^K1 got served a lead enrichment by a Walker turret%s%s" -msgstr "^BG%s^K1 foi entupido de chumbo por uma sentinela Walker%s%s" +msgstr "^BG%s^K1 foi carregado de chumbo por uma sentinela Andante%s%s" #: qcsrc/common/notifications/all.inc:347 #, c-format msgid "^BG%s^K1 was impaled by a Walker turret%s%s" -msgstr "^BG%s^K1 foi empalado por uma sentinela Walker%s%s" +msgstr "^BG%s^K1 foi empalado por uma sentinela Andante%s%s" #: qcsrc/common/notifications/all.inc:348 #, c-format msgid "^BG%s^K1 was blasted away by a Walker turret%s%s" -msgstr "^BG%s^K1 foi explodido por uma sentinela Walker%s%s" +msgstr "^BG%s^K1 foi explodido por uma sentinela Andante%s%s" #: qcsrc/common/notifications/all.inc:349 #, c-format msgid "^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s" -msgstr "^BG%s^K1 foi pego pelo raio de uma explosão de Bumblebee%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:350 #, c-format @@ -2472,32 +2473,32 @@ msgstr "^BG%s^K1 foi esmagado por um veículo%s%s" #: qcsrc/common/notifications/all.inc:351 #, c-format msgid "^BG%s^K1 was caught in a Raptor cluster bomb%s%s" -msgstr "^BG%s^K1 foi pego por uma bomba de Raptor%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:352 #, c-format msgid "^BG%s^K1 got caught in the blast of a Raptor explosion%s%s" -msgstr "^BG%s^K1 foi pego pela explosão de um Raptor%s%s" +msgstr "" #: qcsrc/common/notifications/all.inc:353 #, c-format msgid "^BG%s^K1 got caught in the blast of a Spiderbot explosion%s%s" -msgstr "^BG%s^K1 foi pego pela explosão de um Spiderbot%s%s" +msgstr "^BG%s^K1 foi apanhado pela explosão de um Robô Aranha%s%s" #: qcsrc/common/notifications/all.inc:354 #, c-format msgid "^BG%s^K1 was blasted to bits by a Spiderbot rocket%s%s" -msgstr "^BG%s^K1 foi explodido em pedacinhos por um foguete de Spiderbot%s%s" +msgstr "^BG%s^K1 foi explodido em pedacinhos por um míssil de Robô Aranha%s%s" #: qcsrc/common/notifications/all.inc:355 #, c-format msgid "^BG%s^K1 got caught in the blast of a Racer explosion%s%s" -msgstr "^BG%s^K1 foi pego pela explosão de um Racer%s%s" +msgstr "^BG%s^K1 foi apanhado pela explosão de um Racer%s%s" #: qcsrc/common/notifications/all.inc:356 #, c-format msgid "^BG%s^K1 couldn't find shelter from a Racer rocket%s%s" -msgstr "^BG%s^K1 não conseguiu escapar do foguete de um Racer%s%s" +msgstr "^BG%s^K1 não conseguiu escapar ao míssil de um Racer%s%s" #: qcsrc/common/notifications/all.inc:359 #, c-format @@ -2537,12 +2538,12 @@ msgstr "^BG%s^K3 foi automaticamente ressuscitado depois de %s segundo(s)" #: qcsrc/common/notifications/all.inc:368 #, c-format msgid "^BG%s^K1 froze themself" -msgstr "^BG%s^K1 se congelou" +msgstr "^BG%s^K1 congelou-se" #: qcsrc/common/notifications/all.inc:370 #: qcsrc/common/notifications/all.inc:684 msgid "^TC^TT^BG team wins the round" -msgstr "A equipe ^TC^TT^BG venceu a rodada" +msgstr "A equipa ^TC^TT^BG venceu a rodada" #: qcsrc/common/notifications/all.inc:371 #: qcsrc/common/notifications/all.inc:685 @@ -2558,58 +2559,58 @@ msgstr "^BGRodada empatada" #: qcsrc/common/notifications/all.inc:373 #: qcsrc/common/notifications/all.inc:549 msgid "^BGRound over, there's no winner" -msgstr "^BGA rodada acabou sem vencedor" +msgstr "^BGA rodada acabou sem vencedores" #: qcsrc/common/notifications/all.inc:375 #, c-format msgid "^BGGodmode saved you %s units of damage, cheater!" -msgstr "^BGModo Deus te protegeu de %s de dano, seu trapaçeiro!" +msgstr "^BGModo Deus protegeu-te de %s unidades de dano, batoteiro!" #: qcsrc/common/notifications/all.inc:377 #, c-format msgid "^BG%s^BG got the %s^BG buff!" -msgstr "^BG%s^BG pegou o bônus de %s^BG!" +msgstr "^BG%s^BG apanhou o bónus de %s^BG!" #: qcsrc/common/notifications/all.inc:378 #, c-format msgid "^BG%s^BG lost the %s^BG buff!" -msgstr "^BG%s^BG perdeu o bônus de %s^BG!" +msgstr "^BG%s^BG perdeu o bónus de %s^BG!" #: qcsrc/common/notifications/all.inc:379 #: qcsrc/common/notifications/all.inc:692 #, c-format msgid "^BGYou dropped the %s^BG buff!" -msgstr "^BGVocê largou o bônus de %s^BG!" +msgstr "^BGLargaste o bónus de %s^BG!" #: qcsrc/common/notifications/all.inc:380 #: qcsrc/common/notifications/all.inc:693 #, c-format msgid "^BGYou got the %s^BG buff!" -msgstr "^BGVocê pegou o bônus de %s^BG!" +msgstr "^BGApanhaste o bónus de %s^BG!" #: qcsrc/common/notifications/all.inc:382 #: qcsrc/common/notifications/all.inc:696 #, c-format msgid "^BGYou do not have the ^F1%s" -msgstr "^BGVocê não tem a ^F1%s" +msgstr "^BGNão tens a ^F1%s" #: qcsrc/common/notifications/all.inc:383 #: qcsrc/common/notifications/all.inc:697 #, c-format msgid "^BGYou dropped the ^F1%s^BG%s" -msgstr "^BGVocê largou a ^F1%s^BG%s" +msgstr "^BGLargaste a ^F1%s^BG%s" #: qcsrc/common/notifications/all.inc:384 #: qcsrc/common/notifications/all.inc:698 #, c-format msgid "^BGYou got the ^F1%s" -msgstr "^BGVocê pegou a ^F1%s" +msgstr "^BGApanhaste a ^F1%s" #: qcsrc/common/notifications/all.inc:385 #: qcsrc/common/notifications/all.inc:699 #, c-format msgid "^BGYou don't have enough ammo for the ^F1%s" -msgstr "^BGVocê não tem munição suficiente para a ^F1%s" +msgstr "^BGNão tens munições suficientes para a ^F1%s" #: qcsrc/common/notifications/all.inc:386 #: qcsrc/common/notifications/all.inc:700 @@ -2626,7 +2627,7 @@ msgstr "^F1%s^BG^F4 não está disponível^BG neste mapa" #: qcsrc/common/notifications/all.inc:389 #, c-format msgid "^BG%s^BG is connecting..." -msgstr "^BG%s^BG está conectando..." +msgstr "^BG%s^BG está a conectar-se..." #: qcsrc/common/notifications/all.inc:390 #, c-format @@ -2636,17 +2637,17 @@ msgstr "^BG%s^F3 conectou-se" #: qcsrc/common/notifications/all.inc:391 #, c-format msgid "^BG%s^F3 connected and joined the ^TC^TT team" -msgstr "^BG%s^F3 conectou-se e se uniu à equipe ^TC^TT" +msgstr "^BG%s^F3 conectou-se e se juntou-se à equipa ^TC^TT" #: qcsrc/common/notifications/all.inc:392 #, c-format msgid "^BG%s^F3 is now playing" -msgstr "^BG%s^F3 está jogando agora" +msgstr "^BG%s^F3 está agora a jogar" #: qcsrc/common/notifications/all.inc:393 #, c-format msgid "^BG%s^F3 is now playing on the ^TC^TT team" -msgstr "^BG%s^F3 está jogando agora na equipe ^TC^TT" +msgstr "^BG%s^F3 está a jogar agora na equipa ^TC^TT" #: qcsrc/common/notifications/all.inc:395 #: qcsrc/common/notifications/all.inc:706 @@ -2658,12 +2659,12 @@ msgstr "^BG%s^BG largou a bola!" #: qcsrc/common/notifications/all.inc:707 #, c-format msgid "^BG%s^BG has picked up the ball!" -msgstr "^BG%s^BG pegou a bola!" +msgstr "^BG%s^BG apanhou a bola!" #: qcsrc/common/notifications/all.inc:398 #, c-format msgid "^BG%s^BG captured the keys for the ^TC^TT team" -msgstr "^BG%s^BG capturou as chaves para a equipe ^TC^TT" +msgstr "^BG%s^BG capturou as chaves para a equipa ^TC^TT" #: qcsrc/common/notifications/all.inc:399 #, c-format @@ -2688,7 +2689,7 @@ msgstr "^BG%s^BG destruiu a Chave ^TC^TT" #: qcsrc/common/notifications/all.inc:403 #, c-format msgid "^BG%s^BG picked up the ^TC^TT Key" -msgstr "^BG%s^BG pegou a Chave ^TC^TT" +msgstr "^BG%s^BG apanhou a Chave ^TC^TT" #: qcsrc/common/notifications/all.inc:405 #, c-format @@ -2702,21 +2703,21 @@ msgstr "^BG%s^F3 não tem mais vidas" #: qcsrc/common/notifications/all.inc:408 msgid "^BGMonsters are currently disabled" -msgstr "^BGMonstros estão atualmente desativados" +msgstr "^BGOs monstros estão desativados neste momento" #: qcsrc/common/notifications/all.inc:410 msgid "^BGThe ^TC^TT^BG team held the ball for too long" -msgstr "^BGA ^BGequipe ^TC^TT segurou a bola por muito tempo" +msgstr "^BGA ^BGequipa ^TC^TT segurou a bola por muito tempo" #: qcsrc/common/notifications/all.inc:412 #, c-format msgid "^BG%s^BG captured %s^BG control point" -msgstr "^BG%s^BG capturou o ponto de controle %s^BG" +msgstr "^BG%s^BG capturou o ponto de controlo %s^BG" #: qcsrc/common/notifications/all.inc:413 #, c-format msgid "^TC^TT^BG team %s^BG control point has been destroyed by %s" -msgstr "O ponto de controle %s^BG da equipe ^TC^TT^BG foi destruído por %s" +msgstr "O ponto de controlo %s^BG da equipa ^TC^TT^BG foi destruído por %s" #: qcsrc/common/notifications/all.inc:414 msgid "^TC^TT^BG generator has been destroyed" @@ -2725,28 +2726,28 @@ msgstr "O gerador ^TC^TT^BG foi destruído" #: qcsrc/common/notifications/all.inc:415 msgid "^TC^TT^BG generator spontaneously combusted due to overtime!" msgstr "" -"O gerador da equipe ^TC^TT^BG entrou em combustão espontaneamente devido aos " +"O gerador da equipa ^TC^TT^BG entrou em combustão espontaneamente devido aos " "acréscimos!" #: qcsrc/common/notifications/all.inc:417 #, c-format msgid "^BG%s^K1 picked up Invisibility" -msgstr "^BG%s^K1 pegou Invisibilidade" +msgstr "^BG%s^K1 apanhou a Invisibilidade" #: qcsrc/common/notifications/all.inc:418 #, c-format msgid "^BG%s^K1 picked up Shield" -msgstr "^BG%s^K1 pegou Escudo" +msgstr "^BG%s^K1 apanhou o Escudo" #: qcsrc/common/notifications/all.inc:419 #, c-format msgid "^BG%s^K1 picked up Speed" -msgstr "^BG%s^K1 pegou Velocidade" +msgstr "^BG%s^K1 apanhou a Velocidade" #: qcsrc/common/notifications/all.inc:420 #, c-format msgid "^BG%s^K1 picked up Strength" -msgstr "^BG%s^K1 pegou Força" +msgstr "^BG%s^K1 apanhou a Força" #: qcsrc/common/notifications/all.inc:422 #, c-format @@ -2763,13 +2764,13 @@ msgid "" "^F2You were kicked from the server because you are a spectator and " "spectators aren't allowed at the moment." msgstr "" -"^F2Você foi expulso do servidor porque você é um espectador e espectadores " -"não são permitidos no momento." +"^F2Foste expulso do servidor porque és um espetador e os espetadores não são " +"permitidos neste momento." #: qcsrc/common/notifications/all.inc:425 #, c-format msgid "^BG%s^F3 is now spectating" -msgstr "^BG%s^F3 está assistindo agora" +msgstr "^BG%s^F3 está agora a assistir" #: qcsrc/common/notifications/all.inc:427 #, c-format @@ -2779,12 +2780,12 @@ msgstr "^BG%s^BG abandonou a corrida" #: qcsrc/common/notifications/all.inc:428 #, c-format msgid "^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s" -msgstr "^BG%s^BG não puderam quebrar o recorde de lugar %s%s^BG de %s%s %s" +msgstr "^BG%s^BG não puderam bater o recorde de lugar %s%s^BG de %s%s %s" #: qcsrc/common/notifications/all.inc:429 #, c-format msgid "^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s" -msgstr "^BG%s^BG não pode quebrar o recorde de lugar %s%s^BG de %s%s %s" +msgstr "^BG%s^BG não pode bater o recorde de lugar %s%s^BG de %s%s %s" #: qcsrc/common/notifications/all.inc:430 #, c-format @@ -2795,13 +2796,13 @@ msgstr "^BG%s^BG acabou a corrida" #, c-format msgid "^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s" msgstr "" -"^BG%s^BG quebrou o recorde %s%s^BG de %s^BG e substituiu seu recorde com %s" -"%s %s" +"^BG%s^BG bateu o recorde %s%s^BG de %s^BG e substituiu seu recorde com %s%s " +"%s" #: qcsrc/common/notifications/all.inc:432 #, c-format msgid "^BG%s^BG improved their %s%s^BG place record with %s%s %s" -msgstr "^BG%s^BG melhoraram seus %s%s^BG com %s%s %s" +msgstr "^BG%s^BG melhoraram os seus %s%s^BG com %s%s %s" #: qcsrc/common/notifications/all.inc:433 #, c-format @@ -2810,7 +2811,7 @@ msgid "" "and will be lost." msgstr "" "^BG%s^BG atingiu um novo recorde com ^F2%s^BG, mas infelizmente, faltou uma " -"identidade de usuário e sua pontuação será perdida." +"identidade de utilizador e a sua pontuação vai para o badagaio." #: qcsrc/common/notifications/all.inc:434 #, c-format @@ -2818,8 +2819,8 @@ msgid "" "^BG%s^BG scored a new record with ^F2%s^BG, but is anonymous and will be " "lost." msgstr "" -"^BG%s^BG quebrou um novo recorde com ^F2%s^BG, mas é anônimo e, por isso, " -"será perdido." +"^BG%s^BG bateu um novo recorde com ^F2%s^BG, mas é anónimo e, por isso, o " +"recorde também ficará anónimo no esquecimento." #: qcsrc/common/notifications/all.inc:435 #, c-format @@ -2832,12 +2833,12 @@ msgid "" "^F4You have been invited by ^BG%s^F4 to join their game of ^F2%s^F4 " "(^F1%s^F4)" msgstr "" -"^F4Você foi convidado por ^BG%s^F4 para se juntar a eles na partida de " +"^F4Foste convidado por ^BG%s^F4 para te juntares a eles na partida de " "^F2%s^F4 (^F1%s^F4)" #: qcsrc/common/notifications/all.inc:439 msgid "^TC^TT ^BGteam scores!" -msgstr "A equipe ^TC^TT ^BG pontuou!" +msgstr "A equipa ^TC^TT ^BG pontuou!" #: qcsrc/common/notifications/all.inc:441 #, c-format @@ -2845,21 +2846,21 @@ msgid "" "^F2You have to become a player within the next %s, otherwise you will be " "kicked, because spectating isn't allowed at this time!" msgstr "" -"^F2Você precisa se tornar um jogador dentro de %s, caso contrário, você será " -"expulso, pois espectadores não são permitidos no momento!" +"^F2Tens de te tornar um jogador dentro de %s, caso contrário, serás expulso, " +"pois os espetadores não são permitidos neste momento!" #: qcsrc/common/notifications/all.inc:443 #, c-format msgid "^BG%s^K1 picked up a Superweapon" -msgstr "^BG%s^K1 pegou uma Superarma" +msgstr "^BG%s^K1 apanhou uma Super arma" #: qcsrc/common/notifications/all.inc:445 msgid "^BGYou cannot change to a larger team" -msgstr "^BGVocê não pode trocar para uma equipe maior" +msgstr "^BGNão podes mudar para uma equipa maior" #: qcsrc/common/notifications/all.inc:446 msgid "^BGYou are not allowed to change teams" -msgstr "^BGVocê não pode trocar de equipe" +msgstr "^BGNão tens permissão para trocar de equipa" #: qcsrc/common/notifications/all.inc:448 #, c-format @@ -2867,7 +2868,7 @@ msgid "" "^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have " "^F2Xonotic %s" msgstr "" -"^F4NOTA: ^BGO servidor está rodando ^F1Xonotic %s (beta)^BG, você tem o " +"^F4NOTA: ^BGO servidor está a executar o ^F1Xonotic %s (beta)^BG, tu tens o " "^F2Xonotic %s" #: qcsrc/common/notifications/all.inc:449 @@ -2875,8 +2876,8 @@ msgstr "" msgid "" "^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s" msgstr "" -"^F4NOTA: ^BGO servidor está rodando ^F1Xonotic %s^BG, você tem o ^F2Xonotic " -"%s" +"^F4NOTA: ^BGO servidor está a executar o ^F1Xonotic %s^BG, tu tens o " +"^F2Xonotic %s" #: qcsrc/common/notifications/all.inc:450 #, c-format @@ -2884,8 +2885,8 @@ msgid "" "^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get " "the update from ^F3http://www.xonotic.org/^BG!" msgstr "" -"^F4NOTA: ^F1Xonotic %s^BG foi lançado e você ainda está com o ^F2Xonotic " -"%s^BG - baixe a atualização pelo site ^F3http://www.xonotic.org/^BG!" +"^F4NOTA: ^F1Xonotic %s^BG foi lançado e ainda estás com o ^F2Xonotic %s^BG - " +"descarrega a atualização no site ^F3http://www.xonotic.org/^BG!" #: qcsrc/common/notifications/all.inc:452 #, c-format @@ -2898,12 +2899,12 @@ msgid "" "^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s" msgstr "" "^BG%s%s^K1 morreu por causa da grande habilidade de ^BG%s^K1 com a @!#%%'n " -"Accordeon%s%s" +"Acordeão%s%s" #: qcsrc/common/notifications/all.inc:455 #, c-format msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s" -msgstr "^BG%s^K1 feriu seus próprios ouvidos com a @!#%%'n Accordeon%s%s" +msgstr "^BG%s^K1 feriu seus próprios ouvidos com a @!#%%'n Acordeão%s%s" #: qcsrc/common/notifications/all.inc:456 #, c-format @@ -2923,7 +2924,7 @@ msgstr "^BG%s%s^K1 foi morto pelo Blaster de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:459 #, c-format msgid "^BG%s^K1 shot themself to hell with their Blaster%s%s" -msgstr "^BG%s^K1 atirou muito em si mesmo com seu Blaster%s%s" +msgstr "^BG%s^K1 disparou para si próprio até ao inferno com o Blaster%s%s" #: qcsrc/common/notifications/all.inc:460 #, c-format @@ -2938,7 +2939,7 @@ msgstr "^BG%s^K1 sentiu o forte impulso de sua Crylink%s%s" #: qcsrc/common/notifications/all.inc:462 #, c-format msgid "^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s" -msgstr "^BG%s%s^K1 comeu o foguete de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 comeu o míssil de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:463 #, c-format @@ -2948,42 +2949,39 @@ msgstr "" #: qcsrc/common/notifications/all.inc:464 #, c-format msgid "^BG%s^K1 blew themself up with their Devastator%s%s" -msgstr "^BG%s^K1 se explodiu com sua Devastator%s%s" +msgstr "^BG%s^K1 explodiu-se com a sua Devastadora%s%s" #: qcsrc/common/notifications/all.inc:465 #, c-format msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s" -msgstr "" -"^BG%s%s^K1 foi pulverizado por ^BG%s^K1', usando a arma de artefato " -"eletromagnético %s%s" +msgstr "^BG%s%s^K1 foi pulverizado pelo Parafuso Elétrico de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:466 #, c-format msgid "^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s" -msgstr "" -"^BG%s%s^K1 sentiu o ar eletrocutado do combo de Electro de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 sentiu o ar eletrocutado do combo Elétrico de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:467 #, c-format msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s" -msgstr "^BG%s%s^K1 ficou muito perto da esfera de Electro de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 ficou muito perto da esfera Elétrica de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:468 #, c-format msgid "^BG%s^K1 played with Electro bolts%s%s" -msgstr "^BG%s^K1 brincou com raios de Electro%s%s" +msgstr "^BG%s^K1 brincou com os Parafusos Elétricos%s%s" #: qcsrc/common/notifications/all.inc:469 #, c-format msgid "^BG%s^K1 could not remember where they put their Electro orb%s%s" msgstr "" -"^BG%s^K1 não conseguiu se lembrar onde tinha colocado a sua esfera de Electro" -"%s%s" +"^BG%s^K1 não conseguiu lembrar-se onde tinha colocado a sua esfera Elétrica%s" +"%s" #: qcsrc/common/notifications/all.inc:470 #, c-format msgid "^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s" -msgstr "^BG%s%s^K1 chegou muito perto da fireball de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 chegou muito perto da Bola de Fogo de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:471 #, c-format @@ -2993,28 +2991,28 @@ msgstr "^BG%s%s^K1 foi queimado pela mina de fogo de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:472 #, c-format msgid "^BG%s^K1 should have used a smaller gun%s%s" -msgstr "^BG%s^K1 deveria ter usado uma arma menor%s%s" +msgstr "^BG%s^K1 devia ter usado uma arma mais pequena%s%s" #: qcsrc/common/notifications/all.inc:473 #, c-format msgid "^BG%s^K1 forgot about their firemine%s%s" -msgstr "^BG%s^K1 se esqueceu da sua mina de fogo%s%s" +msgstr "^BG%s^K1 esqueceu-se da sua mina de fogo%s%s" #: qcsrc/common/notifications/all.inc:474 #, c-format msgid "^BG%s%s^K1 was pummeled by a burst of ^BG%s^K1's Hagar rockets%s%s" msgstr "" -"^BG%s%s^K1 foi atingido por uma rajada de foguetes do Hagar de ^BG%s^K1%s%s" +"^BG%s%s^K1 foi atingido por uma rajada de mísseis do Hagar de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:475 #, c-format msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Hagar rockets%s%s" -msgstr "^BG%s%s^K1 foi atingido pelos foguetes do Hagar de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi atingido pelos mísseis do Hagar de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:476 #, c-format msgid "^BG%s^K1 played with tiny Hagar rockets%s%s" -msgstr "^BG%s^K1 brincou com minúsculos foguetes de Hagar%s%s" +msgstr "^BG%s^K1 brincou com minúsculos mísseis de Hagar%s%s" #: qcsrc/common/notifications/all.inc:477 #, c-format @@ -3039,7 +3037,8 @@ msgstr "^BG%s%s^K1 foi despedaçado pela Metralhadora Pesada de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:481 #, c-format msgid "^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s" -msgstr "^BG%s%s^K1 foi pego pela bomba de gravidade do Gancho de ^BG%s^K1%s%s" +msgstr "" +"^BG%s%s^K1 foi apanhado pela bomba de gravidade do Gancho de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:482 #, c-format @@ -3047,18 +3046,18 @@ msgid "" "^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s" msgstr "" "^BG%s%s^K1 morreu por causa da grande habilidade de ^BG%s^K1 com a @!#%%'n " -"Klein Bottle%s%s" +"Garrafa Klein%s%s" #: qcsrc/common/notifications/all.inc:483 #, c-format msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s" msgstr "" -"^BG%s^K1 machucaram seus próprios ouvidos com a @!#%%'n Klein Bottle%s%s" +"^BG%s^K1 machucaram seus próprios ouvidos com a @!#%%'n Garrafa Klein%s%s" #: qcsrc/common/notifications/all.inc:484 #, c-format msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s" -msgstr "^BG%s%s^K1 foi atingido pela Machine Gun de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi atingido pela Metralhadora de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:485 #, c-format @@ -3070,7 +3069,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:790 #, c-format msgid "^BGYou cannot place more than ^F2%s^BG mines at a time" -msgstr "^BGVocê não pode pôr mais do que ^F2%s^BG minas por vez" +msgstr "^BGNão podes colocar mais do que ^F2%s^BG minas de uma vez" #: qcsrc/common/notifications/all.inc:487 #, c-format @@ -3080,22 +3079,22 @@ msgstr "^BG%s%s^K1 ficou muito perto da mina de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:488 #, c-format msgid "^BG%s^K1 forgot about their mine%s%s" -msgstr "^BG%s^K1 se esqueceu de sua mina%s%s" +msgstr "^BG%s^K1 esqueceu-se de sua mina%s%s" #: qcsrc/common/notifications/all.inc:489 #, c-format msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s" -msgstr "^BG%s%s^K1 ficou muito perto da granada de Mortar de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 ficou muito perto da granada de Morteiro de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:490 #, c-format msgid "^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s" -msgstr "^BG%s%s^K1 comeu a granada de Mortar de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 comeu a granada de Morteiro de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:491 #, c-format msgid "^BG%s^K1 didn't see their own Mortar grenade%s%s" -msgstr "^BG%s^K1 não viu a sua própria granada de Mortar%s%s" +msgstr "^BG%s^K1 não viu a sua própria granada de Morteiro%s%s" #: qcsrc/common/notifications/all.inc:492 #, c-format @@ -3105,22 +3104,23 @@ msgstr "^BG%s^K1 se explodiu com o seu próprio Mortar%s%s" #: qcsrc/common/notifications/all.inc:493 #, c-format msgid "^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 foi atingido pelo Rifle de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi atingido pela Espingarda de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:494 #, c-format msgid "^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s" -msgstr "^BG%s%s^K1 morreu pela bala do Rifle de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 morreu com a bala da Espingarda de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:495 #, c-format msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s" -msgstr "^BG%s%s^K1 falhou em se esconder da bala do Rifle de ^BG%s^K1%s%s" +msgstr "" +"^BG%s%s^K1 não conseguiu esconder-se da bala da Espingarda de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:496 #, c-format msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s" -msgstr "^BG%s%s^K1 falhou em se esconder do Rifle de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 não conseguiu esconder-se da Espingarda ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:497 #, c-format @@ -3130,17 +3130,17 @@ msgstr "^BG%s%s^K1 foi serrado ao meio pelo RPC de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:498 #, c-format msgid "^BG%s%s^K1 almost dodged ^BG%s^K1's Rocket Propelled Chainsaw%s%s" -msgstr "^BG%s%s^K1 quase desviou do RPC de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 quase que conseguia desviar-se do RPC de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:499 #, c-format msgid "^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s" -msgstr "^BG%s^K1 foi serrado ao meio pelo seu próprio RPC%s%s" +msgstr "^BG%s^K1 foi serrado ao meio pelo seu RPC%s%s" #: qcsrc/common/notifications/all.inc:500 #, c-format msgid "^BG%s^K1 blew themself up with their Rocket Propelled Chainsaw%s%s" -msgstr "^BG%s^K1 se explodiu com seu próprio RPC%s%s" +msgstr "^BG%s^K1 explodiu-se com o seu RPC%s%s" #: qcsrc/common/notifications/all.inc:501 #, c-format @@ -3155,32 +3155,33 @@ msgstr "^BG%s%s^K1 foi marcado pelo Seeker de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:503 #, c-format msgid "^BG%s^K1 played with tiny Seeker rockets%s%s" -msgstr "^BG%s^K1 brincou com minúsculos foguetes de Seeker%s%s" +msgstr "^BG%s^K1 brincou com minúsculos mísseis do Seeker%s%s" #: qcsrc/common/notifications/all.inc:504 #, c-format msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s" -msgstr "^BG%s%s^K1 foi morto pela Shockwave de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi morto pela Onda de Choque de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:505 #, c-format msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s" -msgstr "^BG%s%s^K1 deu uns tapas em ^BG%s^K1 com uma grande Shockwave%s%s" +msgstr "" +"^BG%s%s^K1 deu uns estalos em ^BG%s^K1 com uma grande Onda de Choque%s%s" #: qcsrc/common/notifications/all.inc:506 #, c-format msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s" -msgstr "^BG%s%s^K1 foi baleado pela Shotgun de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi baleado pela Caçadeira de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:507 #, c-format msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s" -msgstr "^BG%s%s^K1 deu uns tapas em ^BG%s^K1 com uma grande Shotgun%s%s" +msgstr "^BG%s%s^K1 deu uns estalos em ^BG%s^K1 com uma grande Caçadeira%s%s" #: qcsrc/common/notifications/all.inc:508 #, c-format msgid "^BG%s^K1 is now thinking with portals%s%s" -msgstr "^BG%s^K1 agora está pensando com portais%s%s" +msgstr "^BG%s^K1 agora está a pensar em portais%s%s" #: qcsrc/common/notifications/all.inc:509 #, c-format @@ -3192,29 +3193,29 @@ msgstr "" #: qcsrc/common/notifications/all.inc:510 #, c-format msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s" -msgstr "^BG%s^K1 feriu seus próprios ouvidos com a @!#%%'n Tuba%s%s" +msgstr "^BG%s^K1 feriu os próprios ouvidos com a @!#%%'n Tuba%s%s" #: qcsrc/common/notifications/all.inc:511 #, c-format msgid "^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s" -msgstr "^BG%s%s^K1 foi sublimado pela Vaporizer de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi sublimado pelo Vaporizador de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:512 #, c-format msgid "^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s" -msgstr "^BG%s%s^K1 foi vaporizado pela Vortex de ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi vaporizado pelo Vórtex de ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:537 msgid "^F4You are now alone!" -msgstr "^F4Você está sozinho agora!" +msgstr "^F4Agora estás sozinho!" #: qcsrc/common/notifications/all.inc:539 msgid "^BGYou are attacking!" -msgstr "^BGVocê está atacando!" +msgstr "^BGEstás a atacar!" #: qcsrc/common/notifications/all.inc:540 msgid "^BGYou are defending!" -msgstr "^BGVocê está defendendo!" +msgstr "^BGEstás a defender!" #: qcsrc/common/notifications/all.inc:541 #, c-format @@ -3227,19 +3228,19 @@ msgstr "^F4Começou!" #: qcsrc/common/notifications/all.inc:544 msgid "^F4Game starts in ^COUNT" -msgstr "^F4A partida iniciará em ^COUNT" +msgstr "^F4A partida vai começar em ^COUNT" #: qcsrc/common/notifications/all.inc:545 msgid "^F4Round starts in ^COUNT" -msgstr "^F4A rodada iniciará em ^COUNT" +msgstr "^F4A rodada vai começar em ^COUNT" #: qcsrc/common/notifications/all.inc:546 msgid "^F4Round cannot start" -msgstr "^F4A rodada não pode iniciar" +msgstr "^F4A rodada não pode começar" #: qcsrc/common/notifications/all.inc:551 msgid "^F2Don't camp!" -msgstr "^F2Não campere!" +msgstr "^F2Não acampes!" #: qcsrc/common/notifications/all.inc:555 msgid "" @@ -3247,13 +3248,13 @@ msgid "" "^BGFeel free to ^F2try to capture^BG the flag again\n" "^BGif you think you will succeed." msgstr "" -"^BGVocê está livre agora.\n" -"^BGSinta-se à vontade para ^F2tentar capturar^BG a bandeira de novo\n" -"^BGse você acha que irá conseguir." +"^BGAgora estás livre.\n" +"^BGEstá à vontade para ^F2tentar capturar^BG a bandeira de novo\n" +"^BGse achas que vais conseguir." #: qcsrc/common/notifications/all.inc:556 msgid "^BGThis flag is currently inactive" -msgstr "^BGEsta bandeira está atualmente inativa" +msgstr "^BGEsta bandeira está neste momento inativa" #: qcsrc/common/notifications/all.inc:557 msgid "" @@ -3261,23 +3262,22 @@ msgid "" "^BGfor ^F2too many unsuccessful attempts^BG to capture.\n" "^BGMake some defensive scores before trying again." msgstr "" -"^BGVocê agora está ^F1impedido^BG de carregar a(s) bandeira(s)\n" +"^BGAgora estás ^F1impedido^BG de carregar a(s) bandeira(s)\n" "^BGapós ^F2várias tentativas de captura sem êxito^BG.\n" -"^BGFaça alguns pontos defensivos antes de tentar novamente." +"^BGConsegue alguns pontos defensivos antes de tentares novamente." #: qcsrc/common/notifications/all.inc:558 msgid "^BGYou captured the ^TC^TT^BG flag!" -msgstr "^BGVocê capturou a bandeira ^TC^TT^BG!" +msgstr "^BGCapturaste a bandeira ^TC^TT^BG!" #: qcsrc/common/notifications/all.inc:559 msgid "^BGYou captured the flag!" -msgstr "^BGVocê capturou a bandeira!" +msgstr "^BGCapturaste a bandeira!" #: qcsrc/common/notifications/all.inc:560 #, c-format msgid "^BGToo many flag throws! Throwing disabled for %s." -msgstr "" -"^BGNão largue a bandeira várias vezes! Agora você não pode largar por %s." +msgstr "^BGNão largues a bandeira várias vezes! Agora não podes largar por %s." #: qcsrc/common/notifications/all.inc:561 #, c-format @@ -3292,193 +3292,195 @@ msgstr "^BG%s^BG passou a bandeira para %s" #: qcsrc/common/notifications/all.inc:563 #, c-format msgid "^BGYou received the ^TC^TT^BG flag from %s" -msgstr "^BGVocê recebeu a bandeira ^TC^TT^BG de %s" +msgstr "^BGRecebeste a bandeira ^TC^TT^BG de %s" #: qcsrc/common/notifications/all.inc:564 #, c-format msgid "^BGYou received the flag from %s" -msgstr "^BGVocê recebeu a bandeira de %s" +msgstr "^BGRecebeste a bandeira de %s" #: qcsrc/common/notifications/all.inc:565 #, c-format msgid "^BGPress ^F2%s^BG to receive the flag from %s^BG" -msgstr "^BGAperte ^F2%s^BG para receber a bandeira de %s^BG" +msgstr "^BGPressiona ^F2%s^BG para receber a bandeira de %s^BG" #: qcsrc/common/notifications/all.inc:566 #, c-format msgid "^BGRequesting %s^BG to pass you the flag" -msgstr "^BGPedindo à %s^BG para que te passe a bandeira" +msgstr "^BGA pedir a %s^BG para que te passe a bandeira" #: qcsrc/common/notifications/all.inc:567 #, c-format msgid "^BGYou passed the ^TC^TT^BG flag to %s" -msgstr "^BGVocê passou a bandeira ^TC^TT^BG para %s" +msgstr "^BGPassaste a bandeira ^TC^TT^BG para %s" #: qcsrc/common/notifications/all.inc:568 #, c-format msgid "^BGYou passed the flag to %s" -msgstr "^BGVocê passou a bandeira para %s" +msgstr "^BGPassaste a bandeira para %s" #: qcsrc/common/notifications/all.inc:569 msgid "^BGYou got the ^TC^TT^BG flag!" -msgstr "^BGVocê pegou a bandeira ^TC^TT^BG!" +msgstr "^BGApanhaste a bandeira ^TC^TT^BG!" #: qcsrc/common/notifications/all.inc:570 msgid "^BGYou got the flag!" -msgstr "^BGVocê pegou a bandeira!" +msgstr "^BGApanhaste a bandeira!" #: qcsrc/common/notifications/all.inc:571 #, c-format msgid "^BGYou got your %steam^BG's flag, return it!" -msgstr "^BGVocê pegou a bandeira da sua %sequipe^BG, retorne-a!" +msgstr "^BGApanhaste a bandeira da tua %sequipa^BG, retorna-a!" #: qcsrc/common/notifications/all.inc:572 #, c-format msgid "^BGYou got the %senemy^BG's flag, return it!" -msgstr "^BGVocê pegou a bandeira da %sequipe inimiga^BG, retorne-a!" +msgstr "^BGApanhaste a bandeira da %sequipa inimiga^BG, retorna-a!" #: qcsrc/common/notifications/all.inc:573 #, c-format msgid "^BGThe %senemy^BG got your flag! Retrieve it!" -msgstr "^BGO %sinimigo^BG pegou a sua bandeira! Recupere-a!" +msgstr "^BGO %sinimigo^BG apanhou a tua bandeira! Recupera-a!" #: qcsrc/common/notifications/all.inc:574 #, c-format msgid "^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!" -msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a sua bandeira! Recupere-a!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG apanhou a tua bandeira! Recupera-a!" #: qcsrc/common/notifications/all.inc:575 #, c-format msgid "^BGThe %senemy^BG got the flag! Retrieve it!" -msgstr "^BGO %sinimigo^BG pegou a bandeira! Recupere-a!" +msgstr "^BGO %sinimigo^BG apanhou a bandeira! Recupera-a!" #: qcsrc/common/notifications/all.inc:576 #, c-format msgid "^BGThe %senemy (^BG%s%s)^BG got the flag! Retrieve it!" -msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a bandeira! Recupere-a!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG apanhou a bandeira! Recupera-a!" #: qcsrc/common/notifications/all.inc:577 #, c-format msgid "^BGThe %senemy^BG got their flag! Retrieve it!" -msgstr "^BGO %sinimigo^BG pegou a bandeira deles! Recupere-a!" +msgstr "^BGO %sinimigo^BG apanhou a bandeira deles! Recupera-a!" #: qcsrc/common/notifications/all.inc:578 #, c-format msgid "^BGThe %senemy (^BG%s%s)^BG got their flag! Retrieve it!" -msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a bandeira deles! Recupere-a!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG apanhou a bandeira deles! Recupera-a!" #: qcsrc/common/notifications/all.inc:579 #, c-format msgid "^BGYour %steam mate^BG got the ^TC^TT^BG flag! Protect them!" -msgstr "^BGO seu %scolega de equipe^BG pegou a bandeira ^TC^TT^BG! Proteja-o!" +msgstr "" +"^BGO teu %scolega de equipa^BG apanhou a bandeira ^TC^TT^BG! Protege-o!" #: qcsrc/common/notifications/all.inc:580 #, c-format msgid "^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!" msgstr "" -"^BGO seu %scolega de equipe (^BG%s%s)^BG pegou a bandeira ^TC^TT^BG! Proteja-" -"o!" +"^BGO teu %scolega de equipa (^BG%s%s)^BG apanhou a bandeira ^TC^TT^BG! " +"Protege-o!" #: qcsrc/common/notifications/all.inc:581 #, c-format msgid "^BGYour %steam mate^BG got the flag! Protect them!" -msgstr "^BGO seu %scolega de equipe^BG pegou a bandeira! Proteja-o!" +msgstr "^BGO teu %scolega de equipa^BG apanhou a bandeira! Protege-o!" #: qcsrc/common/notifications/all.inc:582 #, c-format msgid "^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!" -msgstr "^BGO seu %scolega de equipe (^BG%s%s)^BG pegou a bandeira! Proteja-o!" +msgstr "" +"^BGO teu %scolega de equipa (^BG%s%s)^BG apanhou a bandeira! Protege-o!" #: qcsrc/common/notifications/all.inc:583 msgid "^BGEnemies can now see you on radar!" -msgstr "^BGAgora os inimigos podem te ver no radar!" +msgstr "^BGAgora os inimigos podem ver-te no radar!" #: qcsrc/common/notifications/all.inc:584 msgid "^BGYou returned the ^TC^TT^BG flag!" -msgstr "^BGVocê retornou a bandeira ^TC^TT^BG!" +msgstr "^BGRetornaste a bandeira ^TC^TT^BG!" #: qcsrc/common/notifications/all.inc:585 msgid "^BGStalemate! Enemies can now see you on radar!" -msgstr "^BGCuidado! Agora os inimigos podem te ver no radar!" +msgstr "^BGCuidado! Agora os inimigos podem ver-te no radar!" #: qcsrc/common/notifications/all.inc:586 msgid "^BGStalemate! Flag carriers can now be seen by enemies on radar!" msgstr "" -"^BGCuidado! Agora portadores da bandeira podem ser vistos pelos inimigos no " -"radar!" +"^BGCuidado! Agora os portadores da bandeira podem ser vistos pelos inimigos " +"no radar!" #: qcsrc/common/notifications/all.inc:590 #, c-format msgid "^K3%sYou fragged ^BG%s" -msgstr "^K3%sVocê executou ^BG%s" +msgstr "^K3%sExecutaste ^BG%s" #: qcsrc/common/notifications/all.inc:591 #: qcsrc/common/notifications/all.inc:600 #: qcsrc/common/notifications/all.inc:609 #, c-format msgid "^K3%sYou scored against ^BG%s" -msgstr "^K3%sVocê pontuou contra ^BG%s" +msgstr "^K3%sPontuaste contra ^BG%s" #: qcsrc/common/notifications/all.inc:592 #, c-format msgid "^K1%sYou were fragged by ^BG%s" -msgstr "^K1%sVocê foi executado por ^BG%s" +msgstr "^K1%sFoste executado por ^BG%s" #: qcsrc/common/notifications/all.inc:593 #: qcsrc/common/notifications/all.inc:602 #: qcsrc/common/notifications/all.inc:611 #, c-format msgid "^K1%sYou were scored against by ^BG%s" -msgstr "^K1%sVocê foi pontuado contra por ^BG%s" +msgstr "^K1%sFoste pontuado contra por ^BG%s" #: qcsrc/common/notifications/all.inc:599 #, c-format msgid "^K3%sYou burned ^BG%s" -msgstr "^K3%sVocê queimou ^BG%s" +msgstr "^K3%sQueimaste ^BG%s" #: qcsrc/common/notifications/all.inc:601 #, c-format msgid "^K1%sYou were burned by ^BG%s" -msgstr "^K1%sVocê foi queimado por ^BG%s" +msgstr "^K1%sFoste queimado por ^BG%s" #: qcsrc/common/notifications/all.inc:608 #, c-format msgid "^K3%sYou froze ^BG%s" -msgstr "^K3%sVocê congelou ^BG%s" +msgstr "^K3%sCongelaste ^BG%s" #: qcsrc/common/notifications/all.inc:610 #, c-format msgid "^K1%sYou were frozen by ^BG%s" -msgstr "^K1%sVocê foi congelado por ^BG%s" +msgstr "^K1%sFoste congelado por ^BG%s" #: qcsrc/common/notifications/all.inc:617 #, c-format msgid "^K1%sYou typefragged ^BG%s" -msgstr "^K1%sVocê executou ^BG%s enquanto digitava" +msgstr "^K1%sExecutaste ^BG%s enquanto escrevias" #: qcsrc/common/notifications/all.inc:618 #, c-format msgid "^K1%sYou scored against ^BG%s^K1 while they were typing" -msgstr "^K1%sVocê pontuou contra ^BG%s^K1 enquanto estavam digitando" +msgstr "^K1%sPontuaste contra ^BG%s^K1 enquanto estavam a escrever" #: qcsrc/common/notifications/all.inc:619 #, c-format msgid "^K1%sYou were typefragged by ^BG%s" -msgstr "^K1%sVocê foi executado enquanto digitava por ^BG%s" +msgstr "^K1%sFoste executado enquanto escrevias por ^BG%s" #: qcsrc/common/notifications/all.inc:620 #, c-format msgid "^K1%sYou were scored against by ^BG%s^K1 while typing" -msgstr "^K1%sVocê foi pontuado contra enquanto digitava por ^BG%s^K1" +msgstr "^K1%sFoste pontuado contra enquanto escrevias por ^BG%s^K1" #: qcsrc/common/notifications/all.inc:626 #, c-format msgid "^BGPress ^F2%s^BG again to toss the nade!" -msgstr "^BGAperte ^F2%s^BG de novo para lançar a granada!" +msgstr "^BGPressiona ^F2%s^BG de novo para lançar a granada!" #: qcsrc/common/notifications/all.inc:627 msgid "^F2You got a ^K1BONUS GRENADE^F2!" -msgstr "^F2Você pegou uma ^K1GRANADA BÔNUS^F2!" +msgstr "^F2Apanhaste uma ^K1GRANADA BÓNUS^F2!" #: qcsrc/common/notifications/all.inc:629 #, c-format @@ -3486,236 +3488,236 @@ msgid "" "^BGYou have been moved into a different team\n" "You are now on: %s" msgstr "" -"^BGVocê foi movido para uma equipe diferente\n" -"Agora você está na equipe: %s" +"^BGFoste movido para uma equipa diferente\n" +"Agora estás na equipa: %s" #: qcsrc/common/notifications/all.inc:630 msgid "^K1Don't go against your team mates!" -msgstr "^K1Não vá contra seus colegas de equipe!" +msgstr "^K1Não vás contra os teus colegas de equipa!" #: qcsrc/common/notifications/all.inc:630 msgid "^K1Don't shoot your team mates!" -msgstr "^K1Não atire nos seus colegas de equipe!" +msgstr "^K1Não atires nos teus colegas de equipa!" #: qcsrc/common/notifications/all.inc:631 msgid "^K1Die camper!" -msgstr "^K1Morra, camper!" +msgstr "^K1Morre campista!" #: qcsrc/common/notifications/all.inc:631 msgid "^K1Reconsider your tactics, camper!" -msgstr "^K1Reconsidere suas táticas, camper!" +msgstr "^K1Reconsidera as tuas táticas, campista!" #: qcsrc/common/notifications/all.inc:632 msgid "^K1You unfairly eliminated yourself!" -msgstr "^K1Você se matou injustamente!" +msgstr "^K1Eliminaste-te injustamente!" #: qcsrc/common/notifications/all.inc:633 #, c-format msgid "^K1You were %s" -msgstr "^K1Você foi %s" +msgstr "^K1Foste %s" #: qcsrc/common/notifications/all.inc:634 msgid "^K1You couldn't catch your breath!" -msgstr "^K1Você não recuperou seu fôlego!" +msgstr "^K1Não recuperaste o fôlego!" #: qcsrc/common/notifications/all.inc:635 msgid "^K1You hit the ground with a crunch!" -msgstr "^K1Você caiu no chão rigorosamente!" +msgstr "^K1Caíste no chão rigorosamente!" #: qcsrc/common/notifications/all.inc:636 msgid "^K1You felt a little too hot!" -msgstr "^K1Você se sentiu um pouco quente!" +msgstr "^K1Sentiste-te um pouco quente!" #: qcsrc/common/notifications/all.inc:636 msgid "^K1You got a little bit too crispy!" -msgstr "^K1Você ficou um pouco crocante demais!" +msgstr "^K1Fiscaste um bocadinho estaladiço!" #: qcsrc/common/notifications/all.inc:637 msgid "^K1You killed your own dumb self!" -msgstr "^K1Você se matou, seu burro!" +msgstr "^K1Mataste-te seu burro!" #: qcsrc/common/notifications/all.inc:637 msgid "^K1You need to be more careful!" -msgstr "^K1Você precisa ter mais cuidado!" +msgstr "^K1tens de ter mais cuidado!" #: qcsrc/common/notifications/all.inc:638 msgid "^K1You couldn't stand the heat!" -msgstr "^K1Você não suportou o calor!" +msgstr "^K1Não suportaste o calor!" #: qcsrc/common/notifications/all.inc:639 msgid "^K1You need to watch out for monsters!" -msgstr "^K1Você tem que se cuidar dos monstros!" +msgstr "^K1Tens de ter um olho nos monstros!" #: qcsrc/common/notifications/all.inc:639 msgid "^K1You were killed by a monster!" -msgstr "^K1Você foi morto por um monstro!" +msgstr "^K1Foste morto por um monstro!" #: qcsrc/common/notifications/all.inc:640 msgid "^K1Tastes like chicken!" -msgstr "^K1Tem gosto de frango!" +msgstr "^K1Sabe a frango!" #: qcsrc/common/notifications/all.inc:640 msgid "^K1You forgot to put the pin back in!" -msgstr "^K1Você se esqueceu de pôr o pino de volta!" +msgstr "^K1Esqueceste-te de tornar a pôr o pino!" #: qcsrc/common/notifications/all.inc:641 msgid "^K1Hanging around a napalm explosion is bad!" -msgstr "^K1Brincar no meio de uma explosão de napalm é errado!" +msgstr "^K1Brincar no meio de uma explosão de napalm é perigoso!" #: qcsrc/common/notifications/all.inc:642 msgid "^K1You felt a little chilly!" -msgstr "^K1Você sentiu um pouco de frio!" +msgstr "^K1Sentiste um pouco de frio!" #: qcsrc/common/notifications/all.inc:642 msgid "^K1You got a little bit too cold!" -msgstr "^K1Você ficou um pouco gelado demais!" +msgstr "^K1Ficaste um pouco gelado!" #: qcsrc/common/notifications/all.inc:643 msgid "^K1Your Healing Nade is a bit defective" -msgstr "^K1Sua Granada de Cura está um pouco defeituosa" +msgstr "^K1A tua Granada de Cura está um pouco defeituosa" #: qcsrc/common/notifications/all.inc:644 msgid "^K1You are respawning for running out of ammo..." -msgstr "^K1Você está ressurgindo por ficar sem munição..." +msgstr "^K1Estás a ressurgir por ficares sem munições..." #: qcsrc/common/notifications/all.inc:644 msgid "^K1You were killed for running out of ammo..." -msgstr "^K1Você foi morto por ficar sem munição..." +msgstr "^K1Foste morto por ficar sem munições..." #: qcsrc/common/notifications/all.inc:645 msgid "^K1You grew too old without taking your medicine" -msgstr "^K1Você ficou muito velho sem tomar o seu medicamento" +msgstr "^K1Ficaste demasiado velho sem tomares o teu medicamento" #: qcsrc/common/notifications/all.inc:645 msgid "^K1You need to preserve your health" -msgstr "^K1Você precisa conservar sua saúde" +msgstr "^K1Tens de conservar a tua saúde" #: qcsrc/common/notifications/all.inc:646 msgid "^K1You became a shooting star!" -msgstr "^K1Você virou uma estrela cadente!" +msgstr "^K1Tornaste-te numa estrela cadente!" #: qcsrc/common/notifications/all.inc:647 msgid "^K1You melted away in slime!" -msgstr "^K1Você derreteu na lama!" +msgstr "^K1Derreteste-te na lama!" #: qcsrc/common/notifications/all.inc:648 msgid "^K1You committed suicide!" -msgstr "^K1Você cometeu suicídio!" +msgstr "^K1Cometeste suicídio!" #: qcsrc/common/notifications/all.inc:648 msgid "^K1You ended it all!" -msgstr "^K1Você acabou com tudo!" +msgstr "^K1Acabaste com tudo!" #: qcsrc/common/notifications/all.inc:649 msgid "^K1You got stuck in a swamp!" -msgstr "^K1Você ficou preso em um pântano!" +msgstr "^K1Ficaste preso num pântano!" #: qcsrc/common/notifications/all.inc:650 #, c-format msgid "^BGYou are now on: %s" -msgstr "^BGVocê está agora em: %s" +msgstr "^BGEstás agora em: %s" #: qcsrc/common/notifications/all.inc:651 msgid "^K1You died in an accident!" -msgstr "^K1Você morreu em um acidente!" +msgstr "^K1Morreste num acidente!" #: qcsrc/common/notifications/all.inc:652 msgid "^K1You had an unfortunate run in with a turret!" -msgstr "^K1Você teve um encontro lamentável com uma sentinela!" +msgstr "^K1Tiveste um encontro lamentável com uma sentinela!" #: qcsrc/common/notifications/all.inc:652 msgid "^K1You were fragged by a turret!" -msgstr "^K1Você foi executado por uma sentinela!" +msgstr "^K1Foste executado por uma sentinela!" #: qcsrc/common/notifications/all.inc:653 msgid "^K1You had an unfortunate run in with an eWheel turret!" -msgstr "^K1Você teve um encontro lamentável com uma sentinela eWheel!" +msgstr "^K1Tiveste um encontro lamentável com uma sentinela eWheel!" #: qcsrc/common/notifications/all.inc:653 msgid "^K1You were fragged by an eWheel turret!" -msgstr "^K1Você foi executado por uma sentinela eWheel!" +msgstr "^K1Foste executado por uma sentinela eWheel!" #: qcsrc/common/notifications/all.inc:654 msgid "^K1You had an unfortunate run in with a Walker turret!" -msgstr "^K1Você teve um encontro lamentável com uma sentinela Walker!" +msgstr "^K1Tiveste um encontro lamentável com uma sentinela Walker!" #: qcsrc/common/notifications/all.inc:654 msgid "^K1You were fragged by a Walker turret!" -msgstr "^K1Você foi executado por uma sentinela Walker!" +msgstr "^K1FOste executado por uma sentinela Walker!" #: qcsrc/common/notifications/all.inc:655 msgid "^K1You got caught in the blast of a Bumblebee explosion!" -msgstr "^K1Você foi pego pelo raio de uma explosão de Bumblebee!" +msgstr "^K1Foste apanhado pelo raio de uma explosão de Bumblebee!" #: qcsrc/common/notifications/all.inc:656 msgid "^K1You were crushed by a vehicle!" -msgstr "^K1Você foi esmagado por um veículo!" +msgstr "^K1Foste esmagado por um veículo!" #: qcsrc/common/notifications/all.inc:657 msgid "^K1You were caught in a Raptor cluster bomb!" -msgstr "^K1Você foi pego por uma bomba Raptor!" +msgstr "^K1Foste apanhado por uma bomba Raptor!" #: qcsrc/common/notifications/all.inc:658 msgid "^K1You got caught in the blast of a Raptor explosion!" -msgstr "^K1Você foi pego no raio de uma explosão de Raptor!" +msgstr "^K1Foste apanhado no raio de uma explosão de Raptor!" #: qcsrc/common/notifications/all.inc:659 msgid "^K1You got caught in the blast of a Spiderbot explosion!" -msgstr "^K1Você foi pego no raio de uma explosão de Spiderbot!" +msgstr "^K1Foste apanhado no raio de uma explosão de Robô Aranha!" #: qcsrc/common/notifications/all.inc:660 msgid "^K1You were blasted to bits by a Spiderbot rocket!" -msgstr "^K1Você foi despedaçado por um foguete de Spiderbot!" +msgstr "^K1Foste despedaçado por um míssil de Robô Aranha!" #: qcsrc/common/notifications/all.inc:661 msgid "^K1You got caught in the blast of a Racer explosion!" -msgstr "^K1Você foi pego no raio de uma explosão de Racer!" +msgstr "^K1Foste apanhado no raio de uma explosão de Racer!" #: qcsrc/common/notifications/all.inc:662 msgid "^K1You couldn't find shelter from a Racer rocket!" -msgstr "^K1Você não conseguiu escapar do foguete de um Racer!" +msgstr "^K1Não conseguiste escapar do míssil de um Racer!" #: qcsrc/common/notifications/all.inc:663 msgid "^K1Watch your step!" -msgstr "^K1Cuidado onde pisa!" +msgstr "^K1Cuidado com o que pisas!" #: qcsrc/common/notifications/all.inc:665 #, c-format msgid "^K1Moron! You fragged ^BG%s^K1, a team mate!" -msgstr "^K1Idiota! Você executou ^BG%s^K1, um colega de equipe!" +msgstr "^K1Idiota! Executaste ^BG%s^K1, um colega de equipa!" #: qcsrc/common/notifications/all.inc:665 #, c-format msgid "^K1Moron! You went against ^BG%s^K1, a team mate!" -msgstr "^K1Idiota! Você foi contra ^BG%s^K1, um colega de equipe!" +msgstr "^K1Idiota! Foste contra ^BG%s^K1, um colega de equipa!" #: qcsrc/common/notifications/all.inc:666 #, c-format msgid "^K1You were fragged by ^BG%s^K1, a team mate" -msgstr "^K1Você foi executado por ^BG%s^K1, um colega de equipe" +msgstr "^K1Foste executado por ^BG%s^K1, um colega de equipa" #: qcsrc/common/notifications/all.inc:666 #, c-format msgid "^K1You were scored against by ^BG%s^K1, a team mate" -msgstr "^K1Você foi pontuado contra por ^BG%s^K1, um colega de equipe" +msgstr "^K1Foste pontuado contra por ^BG%s^K1, um colega de equipa" #: qcsrc/common/notifications/all.inc:668 msgid "" "^K1Stop idling!\n" "^BGDisconnecting in ^COUNT..." msgstr "" -"^K1Pare de ficar AFK!\n" -"^BGDesconectando em ^COUNT..." +"^K1Para de ficar parado!\n" +"^BGA desconectar em ^COUNT..." #: qcsrc/common/notifications/all.inc:670 #, c-format msgid "^BGYou need %s^BG!" -msgstr "^BGVocê precisa %s^BG!" +msgstr "^BGPrecisas de %s^BG!" #: qcsrc/common/notifications/all.inc:671 #, c-format msgid "^BGYou also need %s^BG!" -msgstr "^BGVocê também precisa %s^BG!" +msgstr "^BGTambém precisas de %s^BG!" #: qcsrc/common/notifications/all.inc:672 msgid "^BGDoor unlocked!" @@ -3723,42 +3725,42 @@ msgstr "^BGPorta destrancada!" #: qcsrc/common/notifications/all.inc:674 msgid "^F2You picked up some extra lives" -msgstr "^F2Você pegou algumas vidas extras" +msgstr "^F2Apanhaste algumas vidas extra" #: qcsrc/common/notifications/all.inc:676 #, c-format msgid "^K3You revived ^BG%s" -msgstr "^K3Você ressuscitou ^BG%s" +msgstr "^K3Ressuscitaste ^BG%s" #: qcsrc/common/notifications/all.inc:677 msgid "^K3You revived yourself" -msgstr "^K3Você se ressuscitou" +msgstr "^K3Ressuscitaste-te" #: qcsrc/common/notifications/all.inc:678 #, c-format msgid "^K3You were revived by ^BG%s" -msgstr "^K3Você foi ressuscitado por ^BG%s" +msgstr "^K3Foste ressuscitado por ^BG%s" #: qcsrc/common/notifications/all.inc:679 #, c-format msgid "^K3You were automatically revived after %s second(s)" -msgstr "^K3Você foi automaticamente ressuscitado após %s segundo(s)" +msgstr "^K3Foste automaticamente ressuscitado após %s segundo(s)" #: qcsrc/common/notifications/all.inc:681 msgid "^BGThe generator is under attack!" -msgstr "^BGO gerador está sobre ataque!" +msgstr "^BGO gerador está a ser atacado!" #: qcsrc/common/notifications/all.inc:683 msgid "^TC^TT^BG team loses the round" -msgstr "A equipe ^TC^TT^BG perdeu a rodada" +msgstr "A equipa ^TC^TT^BG perdeu a rodada" #: qcsrc/common/notifications/all.inc:687 msgid "^K1You froze yourself" -msgstr "^K1Você se congelou" +msgstr "^K1Congelaste-te" #: qcsrc/common/notifications/all.inc:688 msgid "^K1Round already started, you spawn as frozen" -msgstr "^K1A rodada já começou, você surgiu congelado" +msgstr "^K1A rodada já começou, surgiste congelado" #: qcsrc/common/notifications/all.inc:690 #, c-format @@ -3767,11 +3769,11 @@ msgstr "^K1Um %s chegou!" #: qcsrc/common/notifications/all.inc:694 msgid "^BGYou got the ^F1Fuel regenerator" -msgstr "^BGVocê pegou o ^F1Regenerador de combustível" +msgstr "^BGApanhaste o ^F1Regenerador de combustível" #: qcsrc/common/notifications/all.inc:695 msgid "^BGYou got the ^F1Jet pack" -msgstr "^BGVocê pegou a ^F1Mochila a Jato" +msgstr "^BGApanhaste a ^F1Mochila a Jato" #: qcsrc/common/notifications/all.inc:703 msgid "" @@ -3779,64 +3781,63 @@ msgid "" "Hope your team can fix it..." msgstr "" "^K1Não há pontos de surgimento disponíveis!\n" -"Tomara que sua equipe consiga consertar isso..." +"Oxalá que a tua equipa consiga corrigir isso..." #: qcsrc/common/notifications/all.inc:704 msgid "" "^K1You may not join the game at this time.\n" "The player limit reached maximum capacity." msgstr "" -"^K1Você não pode entrar no jogo neste momento.\n" -"A capacidade máxima de jogadores foi alcançada." +"^K1Não podes entrar no jogo neste momento.\n" +"A capacidade máxima de jogadores foi atingida." #: qcsrc/common/notifications/all.inc:708 msgid "^BGYou picked up the ball" -msgstr "^BGVocê pegou a bola" +msgstr "^BGApanhaste a bola" #: qcsrc/common/notifications/all.inc:709 msgid "^BGKilling people while you don't have the ball gives no points!" -msgstr "^BGMatar os outros enquanto você não tiver a bola não lhe dará pontos!" +msgstr "^BGMatar os outros enquanto não tiveres a bola não ganharás pontos!" #: qcsrc/common/notifications/all.inc:711 msgid "" "^BGAll keys are in your team's hands!\n" "Help the key carriers to meet!" msgstr "" -"^BGTodas as chaves estão com a sua equipe!\n" -"Ajude os portadores das chaves a se encontrarem!" +"^BGTodas as chaves estão com a tua equipa!\n" +"Ajuda os portadores das chaves a encontrarem-se!" #: qcsrc/common/notifications/all.inc:712 msgid "" "^BGAll keys are in ^TC^TT team^BG's hands!\n" "Interfere ^F4NOW^BG!" msgstr "" -"^BGTodas as chaves estão com a equipe ^TC^TT^BG!\n" -"Interfira ^F4AGORA^BG!" +"^BGTodas as chaves estão com a equipa ^TC^TT^BG!\n" +"Interfere ^F4AGORA^BG!" #: qcsrc/common/notifications/all.inc:713 msgid "" "^BGAll keys are in your team's hands!\n" "Meet the other key carriers ^F4NOW^BG!" msgstr "" -"^BGTodas as chaves estão com a sua equipe!\n" -"Encontre-se com os outros portadores das chaves ^F4AGORA^BG!" +"^BGTodas as chaves estão com a tua equipa!\n" +"Encontra-te com os outros portadores das chaves ^F4AGORA^BG!" #: qcsrc/common/notifications/all.inc:714 msgid "^F4Round will start in ^COUNT" -msgstr "^F4A rodada iniciará em ^COUNT" +msgstr "^F4A rodada vai começar dentro de ^COUNT" #: qcsrc/common/notifications/all.inc:715 msgid "^BGScanning frequency range..." -msgstr "^BGEscaneando alcance de frequência..." +msgstr "^BGA varrer o alcance de frequência..." #: qcsrc/common/notifications/all.inc:716 msgid "^BGYou are starting with the ^TC^TT Key" -msgstr "^BGVocê está começando com a Chave ^TC^TT" +msgstr "^BGEstás a começar com a Chave ^TC^TT" #: qcsrc/common/notifications/all.inc:718 msgid "^BGYou have no lives left, you must wait until the next match" -msgstr "" -"^BGVocê não tem vidas sobrando, você terá que aguardar até a próxima partida" +msgstr "^BGNão tens mais vidas, terás que esperar até à próxima partida" #: qcsrc/common/notifications/all.inc:720 #, c-format @@ -3844,29 +3845,29 @@ msgid "" "^BGWaiting for players to join...\n" "Need active players for: %s" msgstr "" -"^BGEsperando jogadores entrarem...\n" +"^BGà espera que os outros jogadores entrem...\n" "Precisa-se de jogadores ativos para: %s" #: qcsrc/common/notifications/all.inc:721 #, c-format msgid "^BGWaiting for %s player(s) to join..." -msgstr "^BGEsperando %s jogador(es) entrar(em)..." +msgstr "^BGÀ espera de %s jogador(es) para entrar(em)..." #: qcsrc/common/notifications/all.inc:723 msgid "^BGYour weapon has been downgraded until you find some ammo!" -msgstr "^BGA sua arma foi rebaixada até que você encontre alguma munição!" +msgstr "^BGA tua arma foi rebaixada até que encontres munições!" #: qcsrc/common/notifications/all.inc:724 msgid "^F4^COUNT^BG left to find some ammo!" -msgstr "^F4^COUNT^BG restante(s) para encontrar alguma munição!" +msgstr "^F4^COUNT^BG restante(s) para encontrares algumas munições!" #: qcsrc/common/notifications/all.inc:725 msgid "^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!" -msgstr "^BGEncontre alguma munição ou você morrerá em ^F4^COUNT^BG!" +msgstr "^BGEncontra algumas munições ou morrerás em ^F4^COUNT^BG!" #: qcsrc/common/notifications/all.inc:725 msgid "^BGGet some ammo! ^F4^COUNT^BG left!" -msgstr "^BGEncontre alguma munição! Falta ^F4^COUNT^BG!" +msgstr "^BGEncontra algumas munições! Falta ^F4^COUNT^BG!" #: qcsrc/common/notifications/all.inc:726 #, c-format @@ -3879,7 +3880,7 @@ msgid "" "^F2^COUNT^BG until weapon change...\n" "Next weapon: ^F1%s" msgstr "" -"^F2^CONTAGEM^BG até a mudança de arma...\n" +"^F2^CONTAGEM^BG até à mudança de arma...\n" "Próxima arma: ^F1%s" #: qcsrc/common/notifications/all.inc:731 @@ -3890,16 +3891,16 @@ msgstr "^F2Arma ativa: ^F1%s" #: qcsrc/common/notifications/all.inc:733 #, c-format msgid "^BGYou captured %s^BG control point" -msgstr "^BGVocê capturou o ponto de controle %s^BG" +msgstr "^BGCapturaste o ponto de controlo %s^BG" #: qcsrc/common/notifications/all.inc:734 #, c-format msgid "^TC^TT^BG team captured %s^BG control point" -msgstr "A equipe ^TC^TT^BG capturou o ponto de controle %s^BG" +msgstr "A equipa ^TC^TT^BG capturou o ponto de controlo %s^BG" #: qcsrc/common/notifications/all.inc:735 msgid "^BGThis control point currently cannot be captured" -msgstr "^BGEste ponto de controle atualmente não pode ser capturado" +msgstr "^BGEste ponto de controlo não pode ser capturado neste momento" #: qcsrc/common/notifications/all.inc:736 msgid "" @@ -3907,7 +3908,7 @@ msgid "" "^F2Capture some control points to unshield it" msgstr "" "^BGO gerador inimigo ainda não pode ser destruído\n" -"^F2Capture alguns pontos de controle para desprotegê-lo" +"^F2Captura alguns pontos de controlo para desprotegê-lo" #: qcsrc/common/notifications/all.inc:737 msgid "^BGThe ^TCenemy^BG generator is no longer shielded!" @@ -3918,34 +3919,34 @@ msgid "" "^K1Your generator is NOT shielded!\n" "^BGRe-capture control points to shield it!" msgstr "" -"^K1O seu gerador NÃO está blindado!\n" -"^BGRecapture pontos de controle para blindá-lo!" +"^K1O teu gerador NÃO está blindado!\n" +"^BGRecaptura pontos de controlo para blindá-lo!" #: qcsrc/common/notifications/all.inc:739 #, c-format msgid "^BGPress ^F2%s^BG to teleport" -msgstr "^BGAperte ^F2%s^BG para se teletransportar" +msgstr "^BGPressiona ^F2%s^BG para te teletransportares" #: qcsrc/common/notifications/all.inc:740 #, c-format msgid "^BGTeleporting disabled for %s" -msgstr "^BGTeletransporte desabilitado para %s" +msgstr "^BGTeletransporte desativado para %s" #: qcsrc/common/notifications/all.inc:742 msgid "" "^F2Now playing ^F4OVERTIME^F2!\n" "Keep fragging until we have a winner!" msgstr "" -"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" -"Continue executando até que tenhamos um vencedor!" +"^F2A jogar agora nos ^F4ACRÉSCIMOS^F2!\n" +"Continua a executar até que tenhamos um vencedor!" #: qcsrc/common/notifications/all.inc:742 msgid "" "^F2Now playing ^F4OVERTIME^F2!\n" "Keep scoring until we have a winner!" msgstr "" -"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" -"Continue pontuando até que tenhamos um vencedor!" +"^A jogar agora nos ^F4ACRÉSCIMOS^F2!\n" +"Continua a pontuar até que tenhamos um vencedor!" #: qcsrc/common/notifications/all.inc:743 msgid "" @@ -3955,10 +3956,10 @@ msgid "" "The more control points your team holds,\n" "the faster the enemy generator decays" msgstr "" -"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" +"^F2A jogar agora nos ^F4ACRÉSCIMOS^F2!\n" "\n" -"Os geradores estão enfraquecendo agora.\n" -"Quanto mais pontos de controle a sua equipe tiver,\n" +"Os geradores estão agora a enfraquecer.\n" +"Quanto mais pontos de controlo a sua equipa tiver,\n" "mais rápido o gerador inimigo enfraquecerá" #: qcsrc/common/notifications/all.inc:744 @@ -3967,68 +3968,68 @@ msgid "" "^F2Now playing ^F4OVERTIME^F2!\n" "^BGAdded ^F4%s^BG to the game!" msgstr "" -"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" -"^BGAdicionado ^F4%s^BG à partida!" +"^F2A jogar agora nos ^F4ACRÉSCIMOS^F2!\n" +"^BGFoi adicionado ^F4%s^BG ao jogo!" #: qcsrc/common/notifications/all.inc:746 msgid "^K1In^BG-portal created" -msgstr "^K1^BGPortal de entrada criado" +msgstr "^K1^BGCriado portal de entrada" #: qcsrc/common/notifications/all.inc:747 msgid "^F3Out^BG-portal created" -msgstr "^F3^BGPortal de saída criado" +msgstr "^F3^BGCriado portal de saída" #: qcsrc/common/notifications/all.inc:748 msgid "^F1Portal creation failed" -msgstr "^F1Falha ao criar portal" +msgstr "^F1Falha ao criar o portal" #: qcsrc/common/notifications/all.inc:750 msgid "^F2Strength infuses your weapons with devastating power" -msgstr "^F2A Força deixou suas armas com um poder devastador" +msgstr "^F2A Força deixou as tuas armas com um poder devastador" #: qcsrc/common/notifications/all.inc:751 msgid "^F2Strength has worn off" -msgstr "^F2A Força se esgotou" +msgstr "^F2A Força esgotou-se" #: qcsrc/common/notifications/all.inc:753 msgid "^F2Shield surrounds you" -msgstr "^F2O Escudo te cerca" +msgstr "^F2O Escudo envolve-te" #: qcsrc/common/notifications/all.inc:754 msgid "^F2Shield has worn off" -msgstr "^F2O Escudo se esgotou" +msgstr "^F2O Escudo esgotou-se" #: qcsrc/common/notifications/all.inc:756 msgid "^F2You are on speed" -msgstr "^F2Você tem a velocidade" +msgstr "^F2Tens a velocidade" #: qcsrc/common/notifications/all.inc:757 msgid "^F2Speed has worn off" -msgstr "^F2A Velocidade se esgotou" +msgstr "^F2A Velocidade esgotou-se" #: qcsrc/common/notifications/all.inc:759 msgid "^F2You are invisible" -msgstr "^F2Você está invisível" +msgstr "^F2Estás invisível" #: qcsrc/common/notifications/all.inc:760 msgid "^F2Invisibility has worn off" -msgstr "^F2A Invisibilidade se esgotou" +msgstr "^F2A Invisibilidade esgotou-se" #: qcsrc/common/notifications/all.inc:762 msgid "^F2The race is over, finish your lap!" -msgstr "^F2A corrida acabou, termine sua volta!" +msgstr "^F2A corrida acabou, termina a tua volta!" #: qcsrc/common/notifications/all.inc:764 msgid "^BGSecondary fire inflicts no damage!" -msgstr "^BGModo de disparo secundário não causa dano!" +msgstr "^BGO modo de disparo secundário não causa dano!" #: qcsrc/common/notifications/all.inc:766 msgid "^BGSequence completed!" -msgstr "^BGSequência completada!" +msgstr "^BGSequência completa!" #: qcsrc/common/notifications/all.inc:767 msgid "^BGThere are more to go..." -msgstr "^BGAinda tem mais..." +msgstr "^BGAinda há mais..." #: qcsrc/common/notifications/all.inc:768 #, c-format @@ -4037,7 +4038,7 @@ msgstr "^BGSó falta(m) %s^BG..." #: qcsrc/common/notifications/all.inc:770 msgid "^F2Superweapons have broken down" -msgstr "^F2As Superarmas quebraram" +msgstr "^F2As Superarmas estão partidas" #: qcsrc/common/notifications/all.inc:771 msgid "^F2Superweapons have been lost" @@ -4045,67 +4046,67 @@ msgstr "^F2As Superarmas foram perdidas" #: qcsrc/common/notifications/all.inc:772 msgid "^F2You now have a superweapon" -msgstr "^F2Agora você tem uma Superarma" +msgstr "^F2Agora tens uma Superarma" #: qcsrc/common/notifications/all.inc:774 msgid "^K1Changing to ^TC^TT^K1 in ^COUNT" -msgstr "^K1Trocando para ^TC^TT^K1 em ^COUNT" +msgstr "^K1A trocar para ^TC^TT^K1 em ^COUNT" #: qcsrc/common/notifications/all.inc:775 msgid "^K1Changing team in ^COUNT" -msgstr "^K1Trocando de equipe em ^COUNT" +msgstr "^K1A trocar de equipa em ^COUNT" #: qcsrc/common/notifications/all.inc:776 msgid "^K1Spectating in ^COUNT" -msgstr "^K1Trocando para espectador em ^COUNT" +msgstr "^K1A trocar para espetador em ^COUNT" #: qcsrc/common/notifications/all.inc:777 msgid "^K1Suicide in ^COUNT" -msgstr "^K1Cometendo suicídio em ^COUNT" +msgstr "^K1A cometer suicídio em ^COUNT" #: qcsrc/common/notifications/all.inc:779 msgid "^F4Timeout begins in ^COUNT" -msgstr "^F4Pausa iniciará em ^COUNT" +msgstr "^F4A pausa começa em ^COUNT" #: qcsrc/common/notifications/all.inc:780 msgid "^F4Timeout ends in ^COUNT" -msgstr "^F4Pausa acabará em ^COUNT" +msgstr "^F4A pausa termina em ^COUNT" #: qcsrc/common/notifications/all.inc:782 msgid "^K1Cannot join given minigame session!" -msgstr "^K1Não foi possível entrar na sessão de mini jogo fornecida!" +msgstr "^K1Não foi possível entrar na sessão de mini-jogo!" #: qcsrc/common/notifications/all.inc:784 #, c-format msgid "^BGPress ^F2%s^BG to enter/exit the vehicle" -msgstr "^BGAperte ^F2%s^BG para entrar/sair do veículo" +msgstr "^BGPressiona ^F2%s^BG para entrar/sair do veículo" #: qcsrc/common/notifications/all.inc:785 #, c-format msgid "^BGPress ^F2%s^BG to enter the vehicle gunner" -msgstr "^BGAperte ^F2%s^BG para usar a arma do veículo" +msgstr "^BGPressiona ^F2%s^BG para usar a arma do veículo" #: qcsrc/common/notifications/all.inc:786 #, c-format msgid "^BGPress ^F2%s^BG to steal this vehicle" -msgstr "^BGAperte ^F2%s^BG para roubar este veículo" +msgstr "^BGPressiona ^F2%s^BG para roubar este veículo" #: qcsrc/common/notifications/all.inc:787 msgid "" "^F2The enemy is stealing one of your vehicles!\n" "^F4Stop them!" msgstr "" -"^F2O inimigo está roubando um de seus veículos!\n" -"^F4Impeça-os!" +"^F2O inimigo está a roubar um dos teus veículos!\n" +"^F4Impede-os!" #: qcsrc/common/notifications/all.inc:788 msgid "^F2Intruder detected, disabling shields!" -msgstr "^F2Intruso detectado, desativando escudos!" +msgstr "^F2Intruso detetado, a desativar escudos!" #: qcsrc/common/notifications/all.qh:188 msgid "Notification dump command only works with cl_cmd and sv_cmd.\n" msgstr "" -"Comando de despejo de notificação funciona apenas com cl_cmd e sv_cmd.\n" +"O comando de notificação despejo funciona apenas com cl_cmd e sv_cmd.\n" #: qcsrc/common/notifications/all.qh:399 qcsrc/common/notifications/all.qh:400 #, c-format @@ -4134,7 +4135,7 @@ msgstr "largar bandeira" #: qcsrc/common/notifications/all.qh:420 msgid "throw nade" -msgstr "arremessar granada" +msgstr "atirar granada" #: qcsrc/common/notifications/all.qh:431 #, c-format @@ -4186,7 +4187,7 @@ msgstr "MASSACRE! " #: qcsrc/common/notifications/all.qh:447 #, c-format msgid "%s^K1 executed MAYHEM! %s^BG" -msgstr "%s^K1 executou uma MUTILAÇÃO! %s^BG" +msgstr "%s^K1 fez uma MUTILAÇÃO! %s^BG" #: qcsrc/common/notifications/all.qh:447 #, c-format @@ -4214,7 +4215,7 @@ msgstr "FURIOSO!" #: qcsrc/common/notifications/all.qh:449 #, c-format msgid "%s^K1 inflicts CARNAGE! %s^BG" -msgstr "%s^K1 está infligindo CARNIFICINA! %s^BG" +msgstr "%s^K1 está a infligir uma CARNIFICINA! %s^BG" #: qcsrc/common/notifications/all.qh:449 #, c-format @@ -4233,11 +4234,11 @@ msgstr "%s^K1 fez TRINTA PONTUAÇÕES SEGUIDAS! %s^BG" #: qcsrc/common/notifications/all.qh:450 #, c-format msgid "%s^K1 unleashes ARMAGEDDON! %s^BG" -msgstr "%s^K1 desencadeou o ARMAGEDOM! %s^BG" +msgstr "%s^K1 desencadeou o Fim do Mundo! %s^BG" #: qcsrc/common/notifications/all.qh:450 msgid "ARMAGEDDON! " -msgstr "ARMAGEDDON! " +msgstr "Fim do Mundo! " #: qcsrc/common/notifications/all.qh:457 #, c-format @@ -4256,7 +4257,7 @@ msgid "" "(Health ^1%d^BG / Armor ^2%d^BG)%s" msgstr "" "\n" -"(Saúde ^1%d^BG / Armadura ^2%d^BG)%s" +"(Vida ^1%d^BG / Armadura ^2%d^BG)%s" #: qcsrc/common/notifications/all.qh:468 #, c-format @@ -4316,22 +4317,22 @@ msgstr "%s^K1 foi o primeiro a pontuar! %s^BG" #: qcsrc/common/notifications/all.qh:595 #, c-format msgid ", ending their %d frag spree" -msgstr ", finalizando sua cadeia de %d execuções" +msgstr ", a finalizar a sua cadeia de %d execuções" #: qcsrc/common/notifications/all.qh:596 #, c-format msgid ", ending their %d score spree" -msgstr ", finalizando sua cadeia de %d pontuações" +msgstr ", a finalizar a sua cadeia de %d pontuações" #: qcsrc/common/notifications/all.qh:610 #, c-format msgid ", losing their %d frag spree" -msgstr ", perdendo sua cadeia de %d execuções" +msgstr ", a perder a sua cadeia de %d execuções" #: qcsrc/common/notifications/all.qh:611 #, c-format msgid ", losing their %d score spree" -msgstr ", perdendo sua cadeia de %d pontuações" +msgstr ", a perder a sua cadeia de %d pontuações" #: qcsrc/common/teams.qh:29 msgid "TEAM^Red" @@ -4351,7 +4352,7 @@ msgstr "Rosa" #: qcsrc/common/teams.qh:33 msgid "Team" -msgstr "Equipe" +msgstr "Equipa" #: qcsrc/common/teams.qh:34 msgid "Neutral" @@ -4412,7 +4413,7 @@ msgstr "O comando de despejo de sentinelas funciona apenas com sv_cmd.\n" #: qcsrc/common/turrets/cl_turrets.qc:129 #, c-format msgid "%s under attack!" -msgstr "%s sobre ataque!" +msgstr "%s sob ataque!" #: qcsrc/common/turrets/turret.qh:11 msgid "Turret" @@ -4452,7 +4453,7 @@ msgstr "Sentinela Hunter-Killer" #: qcsrc/common/turrets/turret/hk_weapon.qh:7 msgid "Hunter-Killer" -msgstr "Hunter-Killer" +msgstr "" #: qcsrc/common/turrets/turret/machinegun.qh:13 msgid "Machinegun Turret" @@ -4476,7 +4477,7 @@ msgstr "Canhão Phaser" #: qcsrc/common/turrets/turret/phaser_weapon.qh:7 msgid "Phaser" -msgstr "Phaser" +msgstr "" #: qcsrc/common/turrets/turret/plasma.qh:13 msgid "Plasma Cannon" @@ -4510,23 +4511,23 @@ msgstr "Walker" #: qcsrc/common/vehicles/cl_vehicles.qc:192 #, c-format msgid "Press %s" -msgstr "Aperte %s" +msgstr "Pressiona %s" #: qcsrc/common/vehicles/vehicle/bumblebee.qc:950 msgid "No right gunner!" -msgstr "Sem artilheiro na direita!" +msgstr "Sem artilheiro à direita!" #: qcsrc/common/vehicles/vehicle/bumblebee.qc:956 msgid "No left gunner!" -msgstr "Sem artilheiro na esquerda!" +msgstr "Sem artilheiro à esquerda!" #: qcsrc/common/vehicles/vehicle/bumblebee.qh:19 msgid "Bumblebee" -msgstr "Bumblebee" +msgstr "" #: qcsrc/common/vehicles/vehicle/racer.qh:19 msgid "Racer" -msgstr "Racer" +msgstr "" #: qcsrc/common/vehicles/vehicle/racer_weapon.qh:9 msgid "Racer cannon" @@ -4550,7 +4551,7 @@ msgstr "Chama de Raptor" #: qcsrc/common/vehicles/vehicle/spiderbot.qh:19 msgid "Spiderbot" -msgstr "Spiderbot" +msgstr "Robô Aranha" #: qcsrc/common/weapons/all.qh:78 msgid "Weapons dump command only works with sv_cmd.\n" @@ -4578,7 +4579,7 @@ msgstr "Electro" #: qcsrc/common/weapons/weapon/fireball.qc:17 msgid "Fireball" -msgstr "Fireball" +msgstr "Bola de Fogo" #: qcsrc/common/weapons/weapon/hagar.qc:17 msgid "Hagar" @@ -4586,23 +4587,23 @@ msgstr "Hagar" #: qcsrc/common/weapons/weapon/hlac.qc:17 msgid "Heavy Laser Assault Cannon" -msgstr "Heavy Laser Assault Cannon" +msgstr "Canhão de Assalto a Laser Pesado" #: qcsrc/common/weapons/weapon/hook.qc:17 msgid "Grappling Hook" -msgstr "Gancho (grappling hook)" +msgstr "Gancho" #: qcsrc/common/weapons/weapon/machinegun.qc:17 msgid "MachineGun" -msgstr "MachineGun" +msgstr "Metralhadora" #: qcsrc/common/weapons/weapon/minelayer.qc:17 msgid "Mine Layer" -msgstr "Mine Layer" +msgstr "Porta Minas" #: qcsrc/common/weapons/weapon/mortar.qc:17 msgid "Mortar" -msgstr "Mortar" +msgstr "Morteiro" #: qcsrc/common/weapons/weapon/porto.qc:17 msgid "Port-O-Launch" @@ -4610,7 +4611,7 @@ msgstr "Port-O-Launch" #: qcsrc/common/weapons/weapon/rifle.qc:18 msgid "Rifle" -msgstr "Rifle" +msgstr "Espingarda" #: qcsrc/common/weapons/weapon/seeker.qc:17 msgid "T.A.G. Seeker" @@ -4618,11 +4619,11 @@ msgstr "T.A.G. Seeker" #: qcsrc/common/weapons/weapon/shockwave.qc:17 msgid "Shockwave" -msgstr "Shockwave" +msgstr "Onda de Choque" #: qcsrc/common/weapons/weapon/shotgun.qc:17 msgid "Shotgun" -msgstr "Shotgun" +msgstr "Caçadeira" #: qcsrc/common/weapons/weapon/tuba.qc:17 #, no-c-format @@ -4631,11 +4632,11 @@ msgstr "@!#%'n Tuba" #: qcsrc/common/weapons/weapon/vaporizer.qc:18 msgid "Vaporizer" -msgstr "Vaporizer" +msgstr "Vaporizador" #: qcsrc/common/weapons/weapon/vortex.qc:18 msgid "Vortex" -msgstr "Vortex" +msgstr "Vórtex" #: qcsrc/lib/counting.qh:9 #, c-format @@ -4847,8 +4848,8 @@ msgid "" "Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, " "please file an issue." msgstr "" -"Campo de entidade %s.%s (%s) não está na lista branca. Se você acredita que " -"isso é um erro, por favor, reporte-o." +"O campo de entidade %s.%s (%s) não está na lista branca. Se achas que é um " +"erro, por favor, reporta-o." #: qcsrc/lib/string.qh:48 #, c-format @@ -4862,7 +4863,7 @@ msgstr "%02d:%02d:%02d" #: qcsrc/menu/command/menu_cmd.qc:48 msgid "Usage: menu_cmd command..., where possible commands are:\n" -msgstr "Uso: comando menu_cmd..., onde os possíveis comandos são:\n" +msgstr "Uso: comando menu_cmd..., onde os comandos possíveis são:\n" #: qcsrc/menu/command/menu_cmd.qc:49 msgid " sync - reloads all cvars on the current menu page\n" @@ -4870,7 +4871,7 @@ msgstr " sync - recarrega todas as cvars no menu atual\n" #: qcsrc/menu/command/menu_cmd.qc:50 msgid " directmenu ITEM - select a menu item as main item\n" -msgstr " directmenu ITEM - seleciona um item do menu como principal\n" +msgstr " directmenu ITEM - seleciona um item do menu como item principal\n" #: qcsrc/menu/command/menu_cmd.qc:79 msgid "Available options:\n" @@ -4879,7 +4880,7 @@ msgstr "Opções disponíveis:\n" #: qcsrc/menu/command/menu_cmd.qc:128 msgid "Invalid command. For a list of supported commands, try menu_cmd help.\n" msgstr "" -"Comando inválido. Para uma lista de comandos suportados, digite menu_cmd " +"Comando inválido. Para uma lista de comandos suportados, digita menu_cmd " "help.\n" #: qcsrc/menu/item/listbox.qc:415 @@ -4901,11 +4902,11 @@ msgstr "Nível %d: %s" #: qcsrc/menu/xonotic/credits.qc:4 msgid "Core Team" -msgstr "Equipe Principal" +msgstr "Equipa Principal" #: qcsrc/menu/xonotic/credits.qc:16 msgid "Extended Team" -msgstr "Equipe Estendida" +msgstr "Equipa Estendida" #: qcsrc/menu/xonotic/credits.qc:48 msgid "Website" @@ -4925,7 +4926,7 @@ msgstr "Animação" #: qcsrc/menu/xonotic/credits.qc:69 msgid "Level Design" -msgstr "Design de Mapas" +msgstr "Design de Níveis" #: qcsrc/menu/xonotic/credits.qc:92 msgid "Music / Sound FX" @@ -4933,7 +4934,7 @@ msgstr "Música / Efeitos de Som" #: qcsrc/menu/xonotic/credits.qc:108 msgid "Game Code" -msgstr "Codificação de Jogo" +msgstr "Codificação do Jogo" #: qcsrc/menu/xonotic/credits.qc:116 msgid "Marketing / PR" @@ -4945,7 +4946,7 @@ msgstr "Assuntos Legais" #: qcsrc/menu/xonotic/credits.qc:127 msgid "Game Engine" -msgstr "Motor de Jogo" +msgstr "Motor do Jogo" #: qcsrc/menu/xonotic/credits.qc:131 msgid "Engine Additions" @@ -4969,7 +4970,7 @@ msgstr "Asturiano" #: qcsrc/menu/xonotic/credits.qc:156 msgid "Belarusian" -msgstr "Bielorusso" +msgstr "Bielorrusso" #: qcsrc/menu/xonotic/credits.qc:159 msgid "Bulgarian" @@ -4989,7 +4990,7 @@ msgstr "Córnico" #: qcsrc/menu/xonotic/credits.qc:180 msgid "Czech" -msgstr "Tcheco" +msgstr "Checo" #: qcsrc/menu/xonotic/credits.qc:185 msgid "Dutch" @@ -5037,7 +5038,7 @@ msgstr "Coreano" #: qcsrc/menu/xonotic/credits.qc:247 msgid "Polish" -msgstr "Polônes " +msgstr "Polaco" #: qcsrc/menu/xonotic/credits.qc:255 msgid "Portuguese" @@ -5073,19 +5074,19 @@ msgstr "Ucraniano" #: qcsrc/menu/xonotic/credits.qc:310 msgid "Past Contributors" -msgstr "Colaboradores Passados" +msgstr "Colaboradores Anteriores" #: qcsrc/menu/xonotic/cvarlist.qc:73 msgid "forced to be saved to config.cfg" -msgstr "forçado a ser salvo em config.cfg" +msgstr "forçado a ser gravado em config.cfg" #: qcsrc/menu/xonotic/cvarlist.qc:79 qcsrc/menu/xonotic/cvarlist.qc:89 msgid "will not be saved" -msgstr "não será salvo" +msgstr "não será gravado" #: qcsrc/menu/xonotic/cvarlist.qc:84 msgid "will be saved to config.cfg" -msgstr "será salvo em config.cfg" +msgstr "será gravado em config.cfg" #: qcsrc/menu/xonotic/cvarlist.qc:93 msgid "private" @@ -5097,7 +5098,7 @@ msgstr "configuração do motor" #: qcsrc/menu/xonotic/cvarlist.qc:97 msgid "read only" -msgstr "somente leitura" +msgstr "apenas leitura" #: qcsrc/menu/xonotic/dialog_credits.qc:13 #: qcsrc/menu/xonotic/dialog_monstertools.qc:38 @@ -5122,9 +5123,8 @@ msgid "" "player name to get started. You can change these options later through the " "menu system." msgstr "" -"Bem-vindo ao Xonotic! Escolha o seu idioma de preferência e insira o seu " -"apelido para começar. Você pode alterar essas configurações mais tarde pelo " -"menu." +"Bem-vindo(a) ao Xonotic! Escolhe o teu idioma e introduz o teu apelido para " +"começar. Podes alterar estas configurações mais tarde através do menu." #: qcsrc/menu/xonotic/dialog_firstrun.qc:45 #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:28 @@ -5134,7 +5134,7 @@ msgstr "Nome:" #: qcsrc/menu/xonotic/dialog_firstrun.qc:53 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:60 msgid "Name under which you will appear in the game" -msgstr "Seu nome que aparecerá no jogo" +msgstr "O teu nome que vai aparecer no jogo" #: qcsrc/menu/xonotic/dialog_firstrun.qc:69 msgid "Text language:" @@ -5143,7 +5143,7 @@ msgstr "Idioma do texto:" #: qcsrc/menu/xonotic/dialog_firstrun.qc:78 msgid "Allow player statistics to use your nickname at stats.xonotic.org?" msgstr "" -"Permitir que as estatísticas de jogador usem o seu apelido em stats.xonotic." +"Permitir que as estatísticas de jogador usem o teu apelido em stats.xonotic." "org?" #: qcsrc/menu/xonotic/dialog_firstrun.qc:84 @@ -5152,29 +5152,29 @@ msgstr "Não decidido" #: qcsrc/menu/xonotic/dialog_firstrun.qc:88 msgid "Save settings" -msgstr "Salvar configurações" +msgstr "Gravar configurações" #: qcsrc/menu/xonotic/dialog_firstrun.qh:6 msgid "Welcome" -msgstr "Bem-vindo" +msgstr "Bem-vindo(a)" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:16 msgid "Ammunition display:" -msgstr "Exibir munições:" +msgstr "Mostrar munições:" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:19 msgid "Show only current ammo type" -msgstr "Exibir apenas o tipo de munição atual" +msgstr "Mostrar apenas o tipo de munição atual" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:22 #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:44 msgid "Noncurrent alpha:" -msgstr "Alfa não circulante:" +msgstr "Alfa não atual:" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:26 #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:48 msgid "Noncurrent scale:" -msgstr "Escala não circulante:" +msgstr "Escala não atual:" #: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:30 #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:24 @@ -5209,7 +5209,7 @@ msgstr "Painel de Munições" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:17 msgid "Message duration:" -msgstr "Duração de mensagem:" +msgstr "Duração da mensagem:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:21 msgid "Fade time:" @@ -5217,7 +5217,7 @@ msgstr "Tempo de desaparecimento:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:25 msgid "Flip messages order" -msgstr "Trocar ordem de notificações" +msgstr "Trocar ordem de mensagens" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:27 #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:15 @@ -5240,23 +5240,23 @@ msgstr "Painel Central" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:15 msgid "Chat entries:" -msgstr "Entradas do bate-papo:" +msgstr "Entradas da conversação:" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:18 msgid "Chat size:" -msgstr "Tamanho do bate-papo:" +msgstr "Tamanho da conversação:" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:22 msgid "Chat lifetime:" -msgstr "Tempo de vida do bate-papo:" +msgstr "Tempo de vida da conversação:" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:26 msgid "Chat beep sound" -msgstr "Som de aviso do bate-papo" +msgstr "Som de aviso da conversação" #: qcsrc/menu/xonotic/dialog_hudpanel_chat.qh:6 msgid "Chat Panel" -msgstr "Painel do Bate-papo" +msgstr "Painel da Conversação" #: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc:14 msgid "Engine info:" @@ -5272,18 +5272,18 @@ msgstr "Painel de Informações do Motor" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:15 msgid "Combine health and armor" -msgstr "Combinar saúde e armadura" +msgstr "Combinar vida e armadura" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:17 #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:28 #: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:15 msgid "Enable status bar" -msgstr "Habilitar barra de status" +msgstr "Ativar barra de estado" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:19 #: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:17 msgid "Status bar alignment:" -msgstr "Alinhamento da barra de status:" +msgstr "Alinhamento da barra de estado:" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:27 #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:37 @@ -5302,15 +5302,15 @@ msgstr "Para fora" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:32 #: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:30 msgid "Icon alignment:" -msgstr "Alinhamento de ícones:" +msgstr "Alinhamento dos ícones:" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:40 msgid "Flip health and armor positions" -msgstr "Trocar as posições da saúde e armadura" +msgstr "Trocar as posições da vida e da armadura" #: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh:6 msgid "Health/Armor Panel" -msgstr "Painel de Saúde/Armadura" +msgstr "Painel de Vida/Armadura" #: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc:14 msgid "Info messages:" @@ -5326,15 +5326,15 @@ msgstr "Painel de Mensagens de Informação" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:16 msgid "PNL^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:17 msgid "PNL^Enabled spectating" -msgstr "Espectadores habilitados" +msgstr "Ativado os Espetadores" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:18 msgid "PNL^Enabled even playing in warmup" -msgstr "Habilitado mesmo jogando em aquecimento" +msgstr "Ativado mesmo a jogar em aquecimento" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:29 msgid "Reduced" @@ -5350,7 +5350,7 @@ msgstr "Ocultar itens disponíveis" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:37 msgid "Hide big armor and health" -msgstr "Ocultar armadura grande e saúde grande" +msgstr "Ocultar armadura grande e vida grande" #: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:39 msgid "Dynamic size" @@ -5370,7 +5370,7 @@ msgstr "Notificações:" #: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:18 msgid "Also print notifications to the console" -msgstr "Mostrar notificações no console também" +msgstr "Mostrar notificações também na consola" #: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:21 msgid "Flip notify order" @@ -5392,23 +5392,23 @@ msgstr "Painel de Notificações" #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:15 msgid "Panel disabled" -msgstr "Painel desabilitado" +msgstr "Painel desativado" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:16 msgid "Panel enabled" -msgstr "Painel habilitado" +msgstr "Painel ativado" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 msgid "Panel enabled even observing" -msgstr "Painel habilitado enquanto estiver observando" +msgstr "Painel ativado mesmo ao observar" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:18 msgid "Panel enabled only in Race/CTS" -msgstr "Painel habilitado apenas em Corrida/CTS" +msgstr "Painel ativado apenas em Corrida/CTS" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:24 msgid "Status bar" -msgstr "Barra de status" +msgstr "Barra de estado" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:26 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:68 @@ -5466,7 +5466,7 @@ msgstr "nós" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:57 msgid "Show" -msgstr "Exibir" +msgstr "Mostrar" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:60 msgid "Top speed" @@ -5490,12 +5490,12 @@ msgstr "Painel de Potencializadores" #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:15 msgid "Panel enabled when spectating" -msgstr "Painel habilitado enquanto estiver de espectador" +msgstr "Painel ativado quando espetador" #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:16 #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:17 msgid "Panel always enabled" -msgstr "Painel sempre habilitado" +msgstr "Painel ativado sempre" #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:23 msgid "Forced aspect:" @@ -5511,11 +5511,11 @@ msgstr "Painel de Menu Instantâneo" #: qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh:6 msgid "Race Timer Panel" -msgstr "Painel do Cronômetro de Corrida" +msgstr "Painel do Cronómetro da Corrida" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:16 msgid "Panel enabled in teamgames" -msgstr "Painel habilitado em jogos de equipe" +msgstr "Painel ativado em jogos de equipa" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:23 msgid "Radar:" @@ -5540,7 +5540,7 @@ msgstr "Rotação:" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:32 msgid "Forward" -msgstr "Para frente" +msgstr "Para a frente" #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:33 msgid "West" @@ -5612,15 +5612,15 @@ msgstr "Painel da Pontuação" #: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:14 msgid "Timer:" -msgstr "Cronômetro:" +msgstr "Cronómetro:" #: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:17 msgid "Show elapsed time" -msgstr "Exibir tempo decorrido" +msgstr "Mostrar tempo decorrido" #: qcsrc/menu/xonotic/dialog_hudpanel_timer.qh:6 msgid "Timer Panel" -msgstr "Painel do Cronômetro" +msgstr "Painel do Cronómetro" #: qcsrc/menu/xonotic/dialog_hudpanel_vote.qc:15 msgid "Alpha after voting:" @@ -5672,11 +5672,11 @@ msgstr "Ícones das armas:" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:41 msgid "Show only owned weapons" -msgstr "Exibir apenas armas obtidas" +msgstr "Mostrar apenas armas obtidas" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:52 msgid "Show weapon ID as:" -msgstr "Exibir o ID da arma como:" +msgstr "Mostrar o ID da arma como:" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:53 msgid "SHOWAS^None" @@ -5696,15 +5696,15 @@ msgstr "Escala do ID da arma:" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:64 msgid "Show Accuracy" -msgstr "Exibir precisão" +msgstr "Mostrar Precisão" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:65 msgid "Show Ammo" -msgstr "Exibir munições" +msgstr "Mostrar Munições" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:68 msgid "Ammo bar alpha:" -msgstr "Cor da barra de munições:" +msgstr "Transparência da barra de munições:" #: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:74 msgid "Ammo bar color:" @@ -5716,7 +5716,7 @@ msgstr "Painel das Armas" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:19 msgid "HUD skins" -msgstr "Visuais de HUD" +msgstr "Visuais de Interface" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:22 #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:196 @@ -5741,7 +5741,7 @@ msgstr "Definir visual" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:37 msgid "Save current skin" -msgstr "Salvar visual atual" +msgstr "Gravar visual atual" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:46 msgid "Panel background defaults:" @@ -5759,22 +5759,22 @@ msgstr "Fundo:" #: qcsrc/menu/xonotic/util.qc:770 qcsrc/menu/xonotic/util.qc:786 #: qcsrc/menu/xonotic/util.qc:803 msgid "Disable" -msgstr "Desabilitar" +msgstr "Desativar" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:60 #: qcsrc/menu/xonotic/util.qc:783 msgid "Border size:" -msgstr "Tamanho das bordas:" +msgstr "Tamanho da borda:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:75 #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:114 msgid "Team color:" -msgstr "Cor de equipe:" +msgstr "Cor da equipa:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:83 #: qcsrc/menu/xonotic/util.qc:809 msgid "Test team color in configure mode" -msgstr "Testar cor de equipe no modo de configuração" +msgstr "Testar cor da equipa no modo de configuração" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:86 #: qcsrc/menu/xonotic/util.qc:812 @@ -5783,11 +5783,11 @@ msgstr "Preenchimento:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:93 msgid "HUD Dock:" -msgstr "Camada do HUD:" +msgstr "Camada da Interface:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:95 msgid "DOCK^Disabled" -msgstr "Desabilitada" +msgstr "Desativada" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:96 msgid "DOCK^Small" @@ -5803,15 +5803,15 @@ msgstr "Grande" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:121 msgid "Grid settings:" -msgstr "Configurações da rede:" +msgstr "Configurações da grelha:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:124 msgid "Snap panels to grid" -msgstr "Fixar painéis à grade" +msgstr "Fixar painéis à grelha" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:127 msgid "Grid size:" -msgstr "Tamanho da rede:" +msgstr "Tamanho da grelha:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:129 msgid "X:" @@ -5827,7 +5827,7 @@ msgstr "Sair da configuração" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qh:6 msgid "Panel HUD Setup" -msgstr "Painel de Configuração do HUD" +msgstr "Painel de Configuração da Interface" #: qcsrc/menu/xonotic/dialog_monstertools.qc:13 msgid "Monster:" @@ -5882,11 +5882,11 @@ msgstr "Servidores" #: qcsrc/menu/xonotic/dialog_multiplayer.qc:15 msgid "Find servers to play on" -msgstr "Encontre servidores para jogar" +msgstr "Encontrar servidores para jogar" #: qcsrc/menu/xonotic/dialog_multiplayer.qc:17 msgid "Host your own game" -msgstr "Hospede a sua própria partida" +msgstr "Alojar o meu jogo" #: qcsrc/menu/xonotic/dialog_multiplayer.qc:18 msgid "Media" @@ -5898,15 +5898,15 @@ msgstr "Perfil" #: qcsrc/menu/xonotic/dialog_multiplayer.qh:6 msgid "Multiplayer" -msgstr "Multijogador" +msgstr "Multi-jogador" #: qcsrc/menu/xonotic/dialog_multiplayer.qh:7 msgid "" "Play online, against your friends in LAN, view demos or change player " "settings" msgstr "" -"Jogue online, jogue contra seus amigos em rede local, assista a demos ou " -"altere as configurações de jogador." +"Joga online, joga contra os teus amigos em rede local, assiste a " +"demonstrações ou altera as configurações de jogador." #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:46 #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:134 @@ -5968,11 +5968,11 @@ msgstr "Voltas:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 msgid "Goals:" -msgstr "Gols:" +msgstr "Golos:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 msgid "The amount of goals needed before the match will end" -msgstr "A quantidade de gols necessária para acabar a partida" +msgstr "A quantidade de golos necessária para acabar a partida" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:97 msgid "Gametype" @@ -6007,19 +6007,19 @@ msgstr "Infinito" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:132 msgid "Teams:" -msgstr "Equipes:" +msgstr "Equipas:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:135 msgid "2 teams" -msgstr "2 equipes" +msgstr "2 equipas" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:136 msgid "3 teams" -msgstr "3 equipes" +msgstr "3 equipas" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:137 msgid "4 teams" -msgstr "4 equipes" +msgstr "4 equipas" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:140 msgid "Player slots:" @@ -6030,24 +6030,24 @@ msgid "" "The maximum amount of players or bots that can be connected to your server " "at once" msgstr "" -"O número máximo de jogadores ou bots que podem estar conectados ao seu " +"O número máximo de jogadores ou robôs que podem estar conectados ao teu " "servidor simultaneamente." #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:144 msgid "Number of bots:" -msgstr "Número de bots:" +msgstr "Número de robôs:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:146 msgid "Amount of bots on your server" -msgstr "Quantidade de bots no seu servidor" +msgstr "Quantidade de robôs no teu servidor" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:148 msgid "Bot skill:" -msgstr "Habilidade dos bots:" +msgstr "Habilidade dos robôs:" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:151 msgid "Specify how experienced the bots will be" -msgstr "Especifique a experiência dos bots" +msgstr "Especifica a perícia dos robôs" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:152 msgid "Botlike" @@ -6059,15 +6059,15 @@ msgstr "Iniciante" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:154 msgid "You will win" -msgstr "Você vai ganhar" +msgstr "Vais ganhar" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:155 msgid "You can win" -msgstr "Você pode ganhar" +msgstr "Podes ganhar" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:156 msgid "You might win" -msgstr "Você talvez ganhe" +msgstr "Talvez ganhes" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:157 msgid "Advanced" @@ -6110,24 +6110,25 @@ msgid "" "Click here or Ctrl-F to provide a keyword to narrow down the map list. Ctrl-" "Delete to clear; Enter when done." msgstr "" -"Clique aqui ou aperte Ctrl+F para digitar uma palavra chave e buscar o mapa " -"desejado. Você pode apertar Ctrl+Delete para apagar e Enter para confirmar." +"Clica aqui ou pressiona Ctrl+F para digitar uma palavra-chave e diminuir a " +"lista de mapas apresentados. Usar Ctrl+Delete para limpar e Enter para " +"confirmar." #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:207 msgid "Add shown" -msgstr "Adicionar exibidos" +msgstr "Adicionar mostrados" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:208 msgid "Add the maps shown in the list to your selection" -msgstr "Adiciona os mapas exibidos na lista para a sua seleção" +msgstr "Adiciona os mapas mostrados na lista para a tua seleção" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:211 msgid "Remove shown" -msgstr "Remover exibidos" +msgstr "Remover mostrados" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:212 msgid "Remove the maps shown in the list from your selection" -msgstr "Remove os mapas exibidos na lista da sua seleção" +msgstr "Remove os mapas mostrados na lista da tua seleção" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:217 msgid "Add all" @@ -6135,7 +6136,7 @@ msgstr "Adicionar todos" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:218 msgid "Add every available map to your selection" -msgstr "Adiciona todos os mapas disponíveis para a sua seleção" +msgstr "Adiciona todos os mapas disponíveis para a tua seleção" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:221 msgid "Remove all" @@ -6143,11 +6144,11 @@ msgstr "Remover todos" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:222 msgid "Remove all the maps from your selection" -msgstr "Remove todos os mapas da sua seleção" +msgstr "Remove todos os mapas da tua seleção" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:229 msgid "Start Multiplayer!" -msgstr "Iniciar Multijogador!" +msgstr "Iniciar Multi-jogador!" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:58 msgid "Title:" @@ -6180,7 +6181,7 @@ msgstr "Arena com Todas as Armas" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:30 msgid "Most Weapons Arena" -msgstr "Arena com Maior Parte das Armas" +msgstr "Arena com a Maior Parte das Armas" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:49 #, c-format @@ -6210,7 +6211,7 @@ msgstr "NIX" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:69 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:220 msgid "Rocket Flying" -msgstr "Voar com Foguetes" +msgstr "Voar com Mísseis" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:71 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:212 @@ -6249,7 +6250,7 @@ msgstr "Piñata" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:89 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:229 msgid "Weapons stay" -msgstr "Armas permanescentes " +msgstr "Armas permanescentes" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:91 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:184 @@ -6264,7 +6265,7 @@ msgstr "Mochila a Jato" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:95 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:170 msgid "Buffs" -msgstr "Bônus (buffs)" +msgstr "Bónus" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:97 msgid "Overkill" @@ -6293,7 +6294,7 @@ msgstr "Modificadores de jogabilidade:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:160 msgid "Enable dodging" -msgstr "Habilitar esquiva" +msgstr "Ativar esquivar" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:167 msgid "All players are almost invisible" @@ -6302,17 +6303,17 @@ msgstr "Todos jogadores ficarão quase invisíveis" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:174 msgid "Only possible to inflict damage on your enemy while he's airborne" msgstr "" -"Só é possível causar dano aos seus inimigos enquanto eles estiverem no ar" +"Só é possível causar dano aos teus inimigos enquanto eles estiverem no ar" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 msgid "Damage done to your enemy gets added to your own health" -msgstr "O dano causado aos seus inimigos será adicionado à sua saúde" +msgstr "O dano causado aos teus inimigos será adicionado à sua vida" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 msgid "" "Amount of health below which your player gets stunned because of blood loss" msgstr "" -"Quantidade de saúde abaixo a qual o seu jogador permanecerá atordoado devido " +"Quantidade de vida abaixo da qual o teu jogador permanecerá atordoado devido " "à perda de sangue" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:192 @@ -6327,24 +6328,24 @@ msgstr "Modificadores de armas e itens:" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:204 msgid "Grappling hook" -msgstr "Gancho (grappling hook)" +msgstr "Gancho de escalada" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:205 msgid "Players spawn with the grappling hook" -msgstr "Jogadores surgem com o gancho" +msgstr "Os jogadores surgem com o gancho" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:209 msgid "Players spawn with the jetpack" -msgstr "Jogadores surgem com a mochila a jato" +msgstr "Os jogadores surgem com a mochila a jato" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:225 msgid "Players will drop all weapons they possessed when they are killed" msgstr "" -"Ao morrerem, jogadores irão deixar cair no chão todas as armas que tinham" +"Ao morrerem, os jogadores irão deixar cair no chão todas as armas que tinham" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:230 msgid "Weapons stay after they are picked up" -msgstr "Armas permanecem no chão após serem coletadas" +msgstr "As armas permanecem no chão após serem pegadas" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:235 msgid "Regular (no arena)" @@ -6362,8 +6363,8 @@ msgid "" "as unlimited ammo, and disable all other weapon pickups." msgstr "" "Selecionar uma arena de armas concederá a todos os jogadores a arma " -"selecionada ao surgirem bem como munição ilimitada. Todas as outras armas " -"ficarão indisponíveis no mapa." +"selecionada ao surgirem, bem como as munições ilimitadas. Todas as outras " +"armas ficarão indisponíveis no mapa." #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 msgid "Most weapons" @@ -6385,8 +6386,8 @@ msgid "" "does not inflict any damage but is good for doing trickjumps." msgstr "" "Os jogadores terão uma arma, a qual pode instantaneamente matar o oponente " -"com um único disparo. Se o jogador ficar sem munição, ele terá 10 segundos " -"para encontrar alguma e se não conseguir fazer isso, irá morrer. O modo de " +"com um único disparo. Se o jogador ficar sem munições, ele terá 10 segundos " +"para encontrar munições e se não conseguir fazer isso, irá morrer. O modo de " "disparo secundário não causa nenhum dano, mas é útil para executar truques " "de movimento." @@ -6396,9 +6397,9 @@ msgid "" "weapon. After some time, a countdown will start, after which everyone will " "switch to another weapon." msgstr "" -"Sem itens Xonotic - em vez de pegar itens espalhados pelo mapa, todo mundo " -"joga com a mesma arma. Depois de um certo tempo, uma contagem regressiva irá " -"iniciar, e depois disso todos irão trocar para uma outra arma." +"Sem itens Xonotic - em vez de apanhar itens espalhados pelo mapa, todos " +"jogam com a mesma arma. Depois de um certo tempo, irá começar uma contagem, " +"e depois disso todos irão trocar para uma outra arma." #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:277 msgid "with blaster" @@ -6406,7 +6407,7 @@ msgstr "com blaster" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:278 msgid "Always carry the blaster as an additional weapon in Nix" -msgstr "Sempre carregue a blaster como uma arma adicional em Nix" +msgstr "Carregar sempre a blaster como uma arma adicional em Nix" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh:9 msgid "Mutators" @@ -6422,7 +6423,7 @@ msgstr "Vazio" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:42 msgid "Show empty servers" -msgstr "Exibir servidores vazios" +msgstr "Mostrar servidores vazios" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:46 msgid "SRVS^Full" @@ -6430,7 +6431,7 @@ msgstr "Cheio" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:47 msgid "Show full servers that have no slots available" -msgstr "Exibir servidores cheios que não contêm vagas disponíveis" +msgstr "Mostrar servidores cheios que não têm vagas disponíveis" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:51 msgid "Pause" @@ -6441,7 +6442,7 @@ msgid "" "Pause updating the server list to prevent servers from \"jumping around\"" msgstr "" "Pausa a atualização da lista de servidores para evitar que os servidores " -"fiquem saindo do lugar" +"estejam sempre a \"saltar\" do lugar" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 msgid "Reload the server list" @@ -6458,7 +6459,7 @@ msgstr "Informações..." #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:79 msgid "Show more information about the currently highlighted server" -msgstr "Exibir mais informações sobre o servidor atualmente destacado" +msgstr "Mostrar mais informações sobre o servidor atualmente destacado" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:84 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:303 @@ -6482,7 +6483,7 @@ msgstr "Oficial" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:169 msgid "N/A (auth library missing, can't connect)" msgstr "" -"N/A (biblioteca de autenticação não encontrada, não foi possível se conectar)" +"N/A (biblioteca de autenticação não encontrada, não é possível conectar)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:171 msgid "N/A (auth library missing)" @@ -6490,7 +6491,7 @@ msgstr "N/A (biblioteca de autenticação não encontrada)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:177 msgid "Not supported (can't connect)" -msgstr "Não suportado (não foi possível se conectar)" +msgstr "Não suportado (não é possível conectar)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:179 msgid "Not supported (won't encrypt)" @@ -6514,7 +6515,7 @@ msgstr "Solicitado (não encriptará)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:195 msgid "Required (can't connect)" -msgstr "Necessário (não foi possível se conectar)" +msgstr "Necessário (não é possível conectar)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:197 msgid "Required (will encrypt)" @@ -6551,7 +6552,7 @@ msgstr "Jogadores:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:263 msgid "Bots:" -msgstr "Bots:" +msgstr "Robôs:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:268 msgid "Free slots:" @@ -6579,7 +6580,7 @@ msgstr "Demos" #: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:26 msgid "Screenshots" -msgstr "Screenshots" +msgstr "Capturas de ecrã" #: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:27 msgid "Music Player" @@ -6591,13 +6592,13 @@ msgstr "Gravar demos automaticamente" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:57 msgid "Timedemo" -msgstr "Executar benchmark" +msgstr "Executar teste de desempenho" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:58 msgid "Benchmark how fast your computer can run the highlighted demo" msgstr "" -"Executa um teste de desempenho para saber quão rápido seu computador pode " -"rodar a demo destacada" +"Executa um teste de desempenho para saber quão rápido o teu computador pode " +"executar a demo destacada" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:62 msgid "DEMO^Play" @@ -6605,12 +6606,12 @@ msgstr "Reproduzir" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:13 msgid "Playing a demo will disconnect you from the current match." -msgstr "Reproduzir uma demo irá desconectar você da partida atual." +msgstr "Reproduzir uma demo irá desconectar-te da partida atual." #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:15 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:15 msgid "Do you really wish to disconnect now?" -msgstr "Você realmente deseja desconectar agora?" +msgstr "Queres mesmo desconectar-te agora?" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh:6 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh:6 @@ -6619,7 +6620,8 @@ msgstr "Desconectar" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:13 msgid "Timing a demo will disconnect you from the current match." -msgstr "Executar benchmark em uma demo irá desconectá-lo da partida atual." +msgstr "" +"Executar teste de desempenho numa demo irá desconectar-te da partida atual." #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:37 msgid "MUSICPL^Add" @@ -6651,11 +6653,11 @@ msgstr "Parar" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:63 msgid "MUSICPL^Play" -msgstr "Tocar" +msgstr "Reproduzir" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:66 msgid "MUSICPL^Pause" -msgstr "Pausar" +msgstr "Pausa" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:69 msgid "MUSICPL^Prev" @@ -6675,7 +6677,7 @@ msgstr "Remover todas" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 msgid "Auto screenshot scoreboard" -msgstr "Tirar screenshot automaticamente do placar" +msgstr "Tirar captura de ecrã automaticamente do placar de pontuação" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:63 msgid "Open in the viewer" @@ -6683,7 +6685,7 @@ msgstr "Abrir no visualizador" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:139 msgid "Reset" -msgstr "Redefinir" +msgstr "Repor" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:144 msgid "Previous" @@ -6728,19 +6730,19 @@ msgstr "Estatísticas" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:125 msgid "Allow player statistics to track your client" -msgstr "Permitir que as estatísticas de jogadores rastreiem o seu cliente" +msgstr "Permitir que as estatísticas de jogadores rastreiem o teu cliente" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:129 msgid "Allow player statistics to use your nickname" -msgstr "Permitir que as estatísticas de jogadores usem o seu apelido" +msgstr "Permitir que as estatísticas de jogadores usem o teu apelido" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:145 msgid "Country" -msgstr "Idioma" +msgstr "País" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:159 msgid "Gender:" -msgstr "Gênero:" +msgstr "Género:" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:161 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:174 @@ -6759,11 +6761,11 @@ msgstr "Masculino" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:166 msgid "Gender" -msgstr "Gênero" +msgstr "Género" #: qcsrc/menu/xonotic/dialog_quit.qc:11 msgid "Are you sure you want to quit?" -msgstr "Tem certeza de que deseja sair?" +msgstr "Tens certeza de que queres sair?" #: qcsrc/menu/xonotic/dialog_quit.qc:15 msgid "Back to work..." @@ -6803,7 +6805,7 @@ msgstr "Definir * como criança" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:32 msgid "Attach to *" -msgstr "Anexar à *" +msgstr "Anexar a *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:34 msgid "Detach from *" @@ -6811,7 +6813,7 @@ msgstr "Separar de *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:37 msgid "Visual object properties for *:" -msgstr "Propriedades de objeto visual para *:" +msgstr "Propriedades do objeto visual para *:" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:41 msgid "Set alpha:" @@ -6831,7 +6833,7 @@ msgstr "Definir frame:" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:54 msgid "Physical object properties for *:" -msgstr "Propriedades de objeto físico para *:" +msgstr "Propriedades do objeto físico para *:" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:56 msgid "Set material:" @@ -6859,7 +6861,7 @@ msgstr "Estática" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:67 msgid "Movable" -msgstr "Movível" +msgstr "Deslocável" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:68 msgid "Physical" @@ -6879,23 +6881,23 @@ msgstr "Resgatar *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:78 msgid "* object info" -msgstr "Informações de objeto *" +msgstr "Informações do objeto *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:79 msgid "* mesh info" -msgstr "Informações de malha *" +msgstr "Informações da malha *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:80 msgid "* attachment info" -msgstr "Informações de extras *" +msgstr "Informações dos extras *" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:81 msgid "Show help" -msgstr "Exibir ajuda" +msgstr "Mostrar ajuda" #: qcsrc/menu/xonotic/dialog_sandboxtools.qc:82 msgid "* is the object you are facing" -msgstr "* é o objeto para o qual você está virado" +msgstr "* é o objeto para o qual estás virado" #: qcsrc/menu/xonotic/dialog_sandboxtools.qh:6 msgid "Sandbox Tools" @@ -6923,7 +6925,7 @@ msgstr "Entrada" #: qcsrc/menu/xonotic/dialog_settings.qc:24 msgid "User" -msgstr "Usuário" +msgstr "Utilizador" #: qcsrc/menu/xonotic/dialog_settings.qc:25 #: qcsrc/menu/xonotic/keybinder.qc:105 @@ -6936,7 +6938,7 @@ msgstr "Configurações" #: qcsrc/menu/xonotic/dialog_settings.qh:7 msgid "Change the game settings" -msgstr "Altere as configurações do jogo" +msgstr "Altera as configurações do jogo" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:29 msgid "Master:" @@ -6984,7 +6986,7 @@ msgstr "Novo estilo de atenuação de som" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:102 msgid "Mute sounds when not active" -msgstr "Desabilita o som enquanto estiver em segundo plano" +msgstr "Desativa o som enquanto estiver em segundo plano" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:105 msgid "Frequency:" @@ -7076,27 +7078,27 @@ msgstr "Troca de lugar os canais esquerdo e direito" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:138 msgid "Headphone friendly mode" -msgstr "Modo de fones de ouvido" +msgstr "Modo de auscultadores" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:139 msgid "" "Enable spatialization (blend the right and left channel slightly to decrease " "stereo separation a bit for headphones)" msgstr "" -"Habilita espacialização (combina levemente os canais esquerdo e direito para " -"diminuir um pouco a separação de estéreo para fones de ouvido)" +"Ativa a espacialização (combina levemente os canais esquerdo e direito para " +"diminuir um pouco a separação dos canais de estéreo para auscultadores)" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:143 msgid "Hit indication sound" -msgstr "Som indicador de disparo acertado" +msgstr "Som indicador de tiro certeiro" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:144 msgid "Play a hit indicator sound when your shot hits an enemy" -msgstr "Reproduzuz um som indicando que você acertou um inimigo" +msgstr "Reproduz um som indicando que acertaste num inimigo" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:147 msgid "Chat message sound" -msgstr "Som de mensagem do bate-papo" +msgstr "Som de mensagem da conversação" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:149 msgid "Menu sounds" @@ -7104,7 +7106,7 @@ msgstr "Sons do menu" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:150 msgid "Play sounds when clicking menu items" -msgstr "Reproduz sons quando você clica nas opções do menu" +msgstr "Reproduz sons quando clicas nas opções do menu" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:151 msgid "Focus sounds" @@ -7112,7 +7114,7 @@ msgstr "Sons de foco" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:152 msgid "Play sounds when hovering over menu items too" -msgstr "Reproduz sons quando você passa o mouse sobre as opções do menu também" +msgstr "Reproduz sons quando passas com o rato sobre as opções do menu também" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:156 msgid "Time announcer:" @@ -7120,7 +7122,7 @@ msgstr "Aviso de tempo:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:158 msgid "WRN^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:160 msgid "5 minutes" @@ -7284,36 +7286,36 @@ msgstr "Evitar compressão de texturas com perdas" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:131 msgid "Show surfaces" -msgstr "Exibir superfícies" +msgstr "Mostrar superfícies" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:132 msgid "" "Disable textures completely for very slow hardware. This gives a huge " "performance boost, but looks very ugly. (default: disabled)" msgstr "" -"Desabilita completamente as texturas para PCs de baixo desempenho. Isso " -"garante uma alto ganho de desempenho, mas deixa o jogo muito feio. (padrão: " -"desabilitado)" +"Desativa completamente as texturas para computadores de baixo desempenho. " +"Isto melhora o desempenho, mas deixa o aspeto do jogo um bocado feio. " +"(padrão: desabilitado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:135 msgid "Use lightmaps" -msgstr "Usar lightmaps" +msgstr "Usar mapas de luzes" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:136 msgid "" "Use high resolution lightmaps, which will look pretty but use up some extra " "video memory (default: enabled)" msgstr "" -"Usa lightmaps de alta resolução, os quais ficarão elegantes, mas irão usar " -"um pouco mais de memória (padrão: habilitado)" +"Usa mapas de luzes de alta resolução, os quais ficarão elegantes, mas irão " +"usar um pouco mais de memória (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:138 msgid "Deluxe mapping" -msgstr "Mapeamento deluxe" +msgstr "Mapeamento de luxo" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:139 msgid "Use per-pixel lighting effects (default: enabled)" -msgstr "Usa efeitos de iluminação por pixel (padrão: habilitado)" +msgstr "Usa efeitos de iluminação por pixel (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:141 msgid "Gloss" @@ -7323,21 +7325,21 @@ msgstr "Lustro" msgid "" "Enable the use of glossmaps on textures supporting it (default: enabled)" msgstr "" -"Habilita o uso de glossmaps em texturas que suportam esse recurso (padrão: " -"habilitado)" +"Ativa a utilização de mapas de lustro em texturas que suportam esse recurso " +"(padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:145 msgid "Offset mapping" -msgstr "Mapeamento por paralaxe" +msgstr "Mapeamento por deslocação" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:146 msgid "" "Offset mapping effect that will make textures with bumpmaps appear like they " "\"pop out\" of the flat 2D surface (default: disabled)" msgstr "" -"Efeito de mapeamento por paralaxe que fará as texturas com bumpmaps " -"parecerem que estão \"saindo\" da superfície plana em 2D (padrão: " -"desabilitado)" +"Efeito de mapeamento por paralaxe que fará com que as texturas com mapa de " +"relevo (bumpmaps) pareçam que estão a \"sair\" da superfície plana em 2D " +"(padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:148 msgid "Relief mapping" @@ -7348,8 +7350,8 @@ msgid "" "Higher quality offset mapping, which also has a huge impact on performance " "(default: disabled)" msgstr "" -"Mapeamento por paralaxe de maior qualidade, o qual também causa um grande " -"impacto no desempenho (padrão: desabilitado)" +"Mapeamento por relevo de maior qualidade, o qual também causa um grande " +"impacto no desempenho (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:152 msgid "Reflections:" @@ -7361,7 +7363,7 @@ msgid "" "with reflecting surfaces (default: disabled)" msgstr "" "Qualidade de reflexos e refrações. Causa um grande impacto no desempenho em " -"mapas que contenham superfícies com reflexos (padrão: desabilitado)" +"mapas que contenham superfícies com reflexos (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:156 msgid "Resolution of reflections/refractions (default: good)" @@ -7385,7 +7387,7 @@ msgstr "Decalques" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 msgid "Enable decals (bullet holes and blood) (default: enabled)" -msgstr "Habilita decalques (buracos de bala e sangue) (padrão: habilitado)" +msgstr "Ativa os decalques (buracos de bala e sangue) (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:165 msgid "Decals on models" @@ -7398,7 +7400,8 @@ msgstr "Distância:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:172 msgid "Decals further away than this will not be drawn (default: 300)" -msgstr "Decalques mais distantes que isso não serão desenhados (padrão: 300)" +msgstr "" +"Os decalques mais distantes que este valor não serão mostrados (padrão: 300)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:176 msgid "Time:" @@ -7406,7 +7409,7 @@ msgstr "Tempo:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:179 msgid "Time in seconds before decals fade away (default: 2)" -msgstr "Tempo em segundos antes de decalques desaparecerem (padrão: 2)" +msgstr "Tempo em segundos antes dos decalques desaparecerem (padrão: 2)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:183 msgid "Damage effects:" @@ -7414,7 +7417,7 @@ msgstr "Efeitos de dano:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:185 msgid "DMGFX^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:186 msgid "Skeletal" @@ -7426,23 +7429,23 @@ msgstr "Todos" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:191 msgid "No dynamic lighting" -msgstr "Desabilitar iluminação dinâmica" +msgstr "Desativar iluminação dinâmica" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:192 msgid "Enable corona flares around certain lights (default: enabled)" -msgstr "Habilita luzes de corona ao redor de certas luzes (padrão: habilitado)" +msgstr "Ativa luzes de coroa ao redor de certas luzes (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:194 msgid "Fake corona lighting" -msgstr "Iluminação de coronas falsa" +msgstr "Iluminação de coroas falsa" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:195 msgid "" "Enable faster but uglier dynamic lights by rendering bright coronas instead " "of real dynamic lights (default: disabled)" msgstr "" -"Habilita luzes dinâmicas mais rápidas porém mais feias renderizando coronas " -"brilhantes em vez de luzes dinâmicas reais (padrão: desabilitado)" +"Ativa luzes dinâmicas mais rápidas mas também mais feias renderizando as " +"coroas brilhantes em vez de luzes dinâmicas reais (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:198 msgid "Realtime dynamic lighting" @@ -7453,8 +7456,8 @@ msgid "" "Enable rendering of dynamic lights such as explosions and rocket lights " "(default: enabled)" msgstr "" -"Habilita a renderização de luzes dinâmicas como explosões e luzes de " -"foguetes (padrão: habilitada)" +"Ativa a renderização de luzes dinâmicas como explosões e luzes de foguetes " +"(padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:201 #: qcsrc/menu/xonotic/dialog_settings_effects.qc:207 @@ -7464,8 +7467,8 @@ msgstr "Sombras" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:202 msgid "Enable rendering of shadows from dynamic lights (default: disabled)" msgstr "" -"Habilita a renderização de sombras a partir de luzes dinâmicas (padrão: " -"desabilitado)" +"Ativa a renderização de sombras a partir de luzes dinâmicas (padrão: " +"desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:205 msgid "Realtime world lighting" @@ -7476,16 +7479,16 @@ msgid "" "Enable rendering of full realtime world lighting on maps that support it. " "Note that this might have a big impact on performance. (default: disabled)" msgstr "" -"Habilita a renderização de iluminação de mundo em tempo real em mapas que a " -"suportam. Note que isso pode causar um grande impacto no desempenho (padrão: " -"desabilitado)" +"Ativa a renderização de iluminação de mundo em tempo real em mapas que a " +"suportam. Nota que isto pode causar um grande impacto no desempenho (padrão: " +"desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:208 msgid "" "Enable rendering of shadows from realtime world lights (default: disabled)" msgstr "" -"Habilita a renderização de sombras de luzes de mundo em tempo real (padrão: " -"desabilitado)" +"Ativa a renderização de sombras de luzes de mundo em tempo real (padrão: " +"desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:212 msgid "Use normal maps" @@ -7493,7 +7496,7 @@ msgstr "Usar normal maps" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:213 msgid "Enable use of directional shading on textures (default: enabled)" -msgstr "Habilita o uso de shaders direcionais em texturas (padrão: habilitado)" +msgstr "Ativa o uso de shaders direcionais em texturas (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:215 msgid "Soft shadows" @@ -7501,23 +7504,24 @@ msgstr "Sombras suaves" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:219 msgid "Fade corona according to visibility" -msgstr "Enfraquecer corona de acordo com a visibilidade" +msgstr "Enfraquecer coroa de acordo com a visibilidade" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:220 msgid "Fade coronas according to visibility (default: enabled)" -msgstr "Enfraquece coronas de acordo com a visibilidade (padrão: habilitado)" +msgstr "Enfraquece coroas de acordo com a visibilidade (padrão: ativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:224 msgid "Bloom" -msgstr "Bloom" +msgstr "Incandescência" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:225 msgid "" "Enable bloom effect, which brightens the neighboring pixels of very bright " "pixels. Has a big impact on performance. (default: disabled)" msgstr "" -"Habilita o efeito bloom, o qual ilumina os pixels próximos de pixels muito " -"brilhantes. Causa um grande impacto no desempenho (padrão: desabilitado)" +"Ativa o efeito de incandescência, o qual ilumina os pixeis próximos de " +"pixeis muito brilhantes. Causa um grande impacto no desempenho (padrão: " +"desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:226 msgid "Extra postprocessing effects" @@ -7528,16 +7532,16 @@ msgid "" "Enables special postprocessing effects for when damaged or under water or " "using a powerup (default: disabled)" msgstr "" -"Habilita efeitos especiais de pós-processamento para quando receber dano, " -"estar debaixo d'água ou ao usar potencializadores (padrão: desabilitado)" +"Ativa efeitos especiais de pós-processamento ao receber dano, estar debaixo " +"d'água ou ao usar potencializadores (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:232 msgid "Motion blur strength - 0.4 recommended" -msgstr "Intensidade do desfoque de movimento - 0.4 recomendado" +msgstr "Intensidade da desfocagem de movimento - 0.4 recomendado" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:233 msgid "Motion blur:" -msgstr "Desfoque de movimento:" +msgstr "Desfocagem de movimento:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:239 msgid "Particles" @@ -7563,11 +7567,11 @@ msgid "" "gives for better performance (default: 1.0)" msgstr "" "Multiplicador para a quantidade de partículas. Menos significa menos " -"partículas, o que resulta em um melhor desempenho (padrão: 1.0)" +"partículas, o que resulta num melhor desempenho (padrão: 1.0)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:256 msgid "Particles further away than this will not be drawn (default: 1000)" -msgstr "Partículas mais distantes que isso não serão desenhadas (padrão: 1000)" +msgstr "Partículas mais distantes que isto não irão aparecer (padrão: 1000)" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:31 msgid "No crosshair" @@ -7583,8 +7587,8 @@ msgid "" "Set a different crosshair for each weapon, good if you play without weapon " "models" msgstr "" -"Define uma mira diferente para cada uma das armas, uma boa opção caso você " -"jogue sem os modelos das armas na tela" +"Define uma mira diferente para cada uma das armas, uma boa opção caso jogues " +"sem aparecerem as armas no ecrã" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:48 #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:81 @@ -7594,15 +7598,15 @@ msgstr "Tamanho:" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:64 msgid "By health" -msgstr "Por saúde" +msgstr "Por vida" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:76 msgid "Use rings to indicate weapon status" -msgstr "Usar anéis para indicar status da arma" +msgstr "Usar anéis para indicar estado da arma" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:93 msgid "Enable center crosshair dot" -msgstr "Habilitar ponto no centro da mira" +msgstr "Ativar ponto no centro da mira" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:111 msgid "Use normal crosshair color" @@ -7623,12 +7627,12 @@ msgid "" "enlarge the crosshair when you would hit an enemy" msgstr "" "Nenhum: não realiza testes de acerto para a mira; Mira Real: desfoca a mira " -"quando há uma obstáculo entre a sua arma e o alvo; Inimigos: a mira também é " -"ampliada quando você acertaria um inimigo" +"quando há um obstáculo entre a tua arma e o alvo; Inimigos: a mira também é " +"ampliada quando acertarias num inimigo" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:129 msgid "HTTST^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:130 msgid "HTTST^TrueAim" @@ -7640,19 +7644,19 @@ msgstr "Inimigos" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:136 msgid "Blur crosshair if the shot is obstructed" -msgstr "Borrar mira se o disparo for obstruído" +msgstr "Desfocar mira se o disparo for obstruído" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:140 msgid "Enlarge crosshair if targeting an enemy" -msgstr "Ampliar mira ao focar em um inimigo" +msgstr "Ampliar mira ao focar num inimigo" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:143 msgid "Animate crosshair when hitting an enemy" -msgstr "Animar mira ao acertar um inimigo" +msgstr "Animar mira ao acertar num inimigo" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:146 msgid "Animate crosshair when picking up an item" -msgstr "Animar mira ao pegar um item" +msgstr "Animar mira ao apanhar um item" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh:7 msgid "Crosshair" @@ -7664,15 +7668,15 @@ msgstr "Vel. de desaparecimento:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:51 msgid "Enable rows / columns highlighting" -msgstr "Habilitar destacamento de fileiras/colunas" +msgstr "Ativar destaque de linhas/colunas" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:55 msgid "Show decimals in respawn countdown" -msgstr "Exibir decimais na contagem de ressurgimento" +msgstr "Mostrar decimais na contagem de ressurgimento" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:57 msgid "Show accuracy underneath scoreboard" -msgstr "Exibir precisão embaixo do placar" +msgstr "Mostrar pontaria por baixo do placar de pontuação" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:61 msgid "Waypoints" @@ -7697,11 +7701,11 @@ msgstr "Tamanho da fonte:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:80 msgid "Edge offset:" -msgstr "Extremidade:" +msgstr "Deslocamento da extremidade:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:89 msgid "Fade when near the crosshair" -msgstr "Enfraquecer ao se aproximar da mira" +msgstr "Suavizar quando perto da mira" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:94 msgid "Damage" @@ -7725,7 +7729,7 @@ msgstr "Nomes de Jogadores" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:114 msgid "Show names above players" -msgstr "Exibir nomes sobre jogadores" +msgstr "Mostrar nomes por cima dos jogadores" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:130 msgid "Max distance:" @@ -7733,20 +7737,20 @@ msgstr "Distância máxima:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:136 msgid "Decolorize:" -msgstr "Descolorização:" +msgstr "Descoloração:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:140 #: qcsrc/menu/xonotic/keybinder.qc:99 msgid "Teamplay" -msgstr "Equipe" +msgstr "Equipa" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:148 msgid "Only when near crosshair" -msgstr "Apenas quando próximo à mira" +msgstr "Apenas quando próximo da mira" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:152 msgid "Display health and armor" -msgstr "Exibir saúde e armadura" +msgstr "Mostrar vida e armadura" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:157 msgid "Damage overlay:" @@ -7754,32 +7758,33 @@ msgstr "Sobreposição do dano:" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:160 msgid "Dynamic HUD" -msgstr "HUD dinâmico" +msgstr "Interface dinâmica" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:161 msgid "HUD moves around following player's movement" -msgstr "O HUD se move de acordo com o movimento do jogador" +msgstr "A interface move-se de acordo com o movimento do jogador" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:163 msgid "Shake the HUD when hurt" -msgstr "Vibrar o HUD ao ser atingido" +msgstr "Vibrar a interface ao ser atingido" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:167 #: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh:6 msgid "Enter HUD editor" -msgstr "Entrar no editor do HUD" +msgstr "Entrar no editor da interface" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qh:7 msgid "HUD" -msgstr "HUD" +msgstr "Interface" #: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:21 msgid "In order for the HUD editor to show, you must first be in game." -msgstr "Para o editor do HUD aparecer, é necessário estar jogando em um mapa." +msgstr "" +"Para o editor da interface aparecer, é necessário estar a jogar num mapa." #: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:23 msgid "Do you wish to start a local game to set up the HUD?" -msgstr "Quer iniciar um jogo local para personalizar o HUD?" +msgstr "Queres iniciar um jogo local para personalizar a interface?" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:24 msgid "Frag Information" @@ -7787,19 +7792,19 @@ msgstr "Informações de Execuções" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:26 msgid "Display information about killing sprees" -msgstr "Exibir informação sobre sequências de mortes" +msgstr "Mostrar informação sobre sequências de mortes" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:29 msgid "Only display sprees if they are achievements" -msgstr "Apenas exibir sequências se forem conquistas" +msgstr "Mostrar apenas sequências se forem conquistas" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:34 msgid "Show spree information in centerprints" -msgstr "Exibir informação de sequências em impressões centrais" +msgstr "Mostrar informação de sequências em impressões centrais" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:38 msgid "Show spree information in death messages" -msgstr "Exibir informação de sequências em mensagens de morte" +msgstr "Mostrar informação de sequências em mensagens de morte" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:43 msgid "Sprees in info messages:" @@ -7807,7 +7812,7 @@ msgstr "Sequências em mensagens de informação:" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:46 msgid "SPREES^Disabled" -msgstr "Desabilitadas" +msgstr "Desativadas" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:47 msgid "Target" @@ -7842,11 +7847,11 @@ msgstr "Configurações do Modo de Jogo" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:67 msgid "Display capture times in Capture The Flag" -msgstr "Exibir tempos de captura em Capture a Bandeira" +msgstr "Mostrar tempos de captura em Capturar a Bandeira" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:71 msgid "Display name of flag stealer in Capture The Flag" -msgstr "Exibir nome do ladrão da bandeira em Capture a Bandeira" +msgstr "Mostrar nome do ladrão da bandeira em Capturar a Bandeira" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:76 #: qcsrc/menu/xonotic/dialog_settings_input.qc:91 @@ -7856,15 +7861,15 @@ msgstr "Outros" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:78 msgid "Display console messages in the top left corner" -msgstr "Exibir mensagens de console no canto superior esquerdo" +msgstr "Mostrar mensagens da consola no canto superior esquerdo" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:80 msgid "Display all info messages in the chatbox" -msgstr "Exibir todas as mensagens de informação no bate-papo" +msgstr "Mostrar todas as mensagens de informação na conversação" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:82 msgid "Display player statuses in the chatbox" -msgstr "Exibir status de jogadores no bate-papo" +msgstr "Mostrar estado dos jogadores na conversação" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:86 msgid "Powerup notifications" @@ -7945,11 +7950,11 @@ msgstr "Forçar modelos dos jogadores para ficarem iguais ao meu" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:53 msgid "Force player colors to mine" -msgstr "Forçar cores de jogadores para ficarem iguais às minhas" +msgstr "Forçar cores dos jogadores para ficarem iguais às minhas" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:56 msgid "In non teamplay modes only" -msgstr "Apenas em modos de jogo que não sejam de equipes" +msgstr "Apenas em modos de jogo que não sejam de equipas" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:60 msgid "Body fading:" @@ -7961,7 +7966,7 @@ msgstr "Tripas:" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:65 msgid "GIBS^None" -msgstr "Desabilitadas" +msgstr "Desativadas" #: qcsrc/menu/xonotic/dialog_settings_game_model.qc:66 msgid "GIBS^Few" @@ -7981,19 +7986,19 @@ msgstr "Modelos" #: qcsrc/menu/xonotic/dialog_settings_game_model.qh:8 msgid "Customize how players and items are displayed in game" -msgstr "Personalize como jogadores e itens são exibidos dentro do jogo" +msgstr "Personalizar como os jogadores e itens são mostrados dentro do jogo" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:26 msgid "1st person perspective" -msgstr "Perspectiva em 1ª pessoa" +msgstr "Perspetiva na 1ª pessoa" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:30 msgid "Slide to third person upon death" -msgstr "Mudar para terceira pessoa depois de morrer" +msgstr "Mudar para a terceira pessoa depois de morrer" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:34 msgid "Smooth the view when landing from a jump" -msgstr "Suavizar a visão quando aterrissar de um salto" +msgstr "Suavizar a visão quando aterrar de um salto" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:38 msgid "Smooth the view while crouching" @@ -8009,7 +8014,7 @@ msgstr "Oscilação de visão ao andar" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:51 msgid "3rd person perspective" -msgstr "Perspectiva em 3ª pessoa" +msgstr "Perspetiva na 3ª pessoa" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:55 msgid "Back distance" @@ -8021,7 +8026,7 @@ msgstr "Distância para cima" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:67 msgid "Allow passing through walls while spectating" -msgstr "Atravessar paredes quando estiver de espectador" +msgstr "Atravessar paredes quando for espetador" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:70 msgid "Field of view:" @@ -8046,7 +8051,7 @@ msgstr "Velocidade do zoom:" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:83 msgid "How fast the view will be zoomed, disable to zoom instantly" -msgstr "Quão rápido será o zoom da visão, desabilite para zoom instantâneo" +msgstr "Quão rápido será o zoom da visão, desativa para zoom instantâneo" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:92 msgid "ZOOM^Instant" @@ -8061,8 +8066,8 @@ msgid "" "How zoom changes sensitivity, from 0 (lower sensitivity) to 1 (no " "sensitivity change)" msgstr "" -"Como o zoom altera a sensibilidade, a partir do valor 0 (sensibilidade mais " -"baixa) para 1 (sem alterações na sensibilidade)" +"Como o zoom altera a sensibilidade. A partir do valor 0 (sensibilidade mais " +"baixa) até 1 (sem alterações na sensibilidade)" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:101 msgid "Velocity zoom" @@ -8070,7 +8075,7 @@ msgstr "Zoom baseado na velocidade" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:102 msgid "Forward movement only" -msgstr "Apenas ao movimentar-se para frente" +msgstr "Apenas no movimento para a frente" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:106 msgid "VZOOM^Factor" @@ -8078,15 +8083,15 @@ msgstr "Fator" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:113 msgid "Display reticle 2D overlay while zooming" -msgstr "Exibe uma sobreposição reticular em 2D durante o zoom" +msgstr "Mostra uma sobreposição reticular em 2D durante o zoom" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:116 msgid "Release zoom when you die or respawn" -msgstr "Soltar o zoom quando você morre ou ressurge" +msgstr "Largar o zoom quando morro ou ressurgo" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:120 msgid "Release zoom when you switch weapons" -msgstr "Soltar o zoom quando você troca de arma" +msgstr "Largar o zoom quando troco de arma" #: qcsrc/menu/xonotic/dialog_settings_game_view.qh:7 #: qcsrc/menu/xonotic/keybinder.qc:76 @@ -8112,50 +8117,49 @@ msgstr "Usar lista de prioridades como ordem de alternação de armas" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:51 msgid "" "Make use of the list above when cycling through weapons with the mouse wheel" -msgstr "" -"Faz uso da lista acima durante a alternação de armas com a roda do mouse" +msgstr "Usar a lista acima durante a alternação de armas com a roda do rato" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:53 msgid "Cycle through only usable weapon selections" -msgstr "Alterne somente entre armas utilizáveis" +msgstr "Alternar apenas entre armas utilizáveis" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:57 msgid "Auto switch weapons on pickup" -msgstr "Trocar para a arma coletada automaticamente" +msgstr "Trocar para a arma apanhada automaticamente" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:58 msgid "" "Automatically switch to newly picked up weapons if they are better than what " "you are carrying" msgstr "" -"Alterna automaticamente para a arma coletada caso ela seja melhor do que a " -"que você está carregando" +"Muda automaticamente para a arma apanhada caso ela seja melhor do que a que " +"estiveres a carregar" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:61 msgid "Release attack buttons when you switch weapons" -msgstr "Soltar os botões de ataque durante a troca de arma" +msgstr "Libertar os botões de ataque quando troco de arma" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:64 msgid "Draw 1st person weapon model" -msgstr "Renderizar modelo de arma em 1ª pessoa" +msgstr "Renderizar modelo de arma na 1ª pessoa" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:65 msgid "Draw the weapon model" -msgstr "Exibe os modelos das armas em sua tela" +msgstr "Mostra os modelos das armas no ecrã" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:69 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:72 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:75 msgid "Position of the weapon model; requires reconnect" -msgstr "Posicionamento do modelo de arma; é preciso reconectar-se" +msgstr "Posicionamento do modelo da arma; necessita de reconexão" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:80 msgid "Gun model swaying" -msgstr "Mover modelo de arma ao mover o mouse" +msgstr "Mover modelo de arma ao mover o rato" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:85 msgid "Gun model bobbing" -msgstr "Oscilar modelo de arma" +msgstr "Oscilar modelo da arma" #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qh:7 #: qcsrc/menu/xonotic/keybinder.qc:43 @@ -8180,11 +8184,11 @@ msgstr "Limpar" #: qcsrc/menu/xonotic/dialog_settings_input.qc:52 msgid "Reset all" -msgstr "Redefinir tudo" +msgstr "Repor tudo" #: qcsrc/menu/xonotic/dialog_settings_input.qc:57 msgid "Mouse" -msgstr "Mouse" +msgstr "Rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:59 msgid "Sensitivity:" @@ -8192,50 +8196,50 @@ msgstr "Sensibilidade:" #: qcsrc/menu/xonotic/dialog_settings_input.qc:61 msgid "Mouse speed multiplier" -msgstr "Multiplicador da velocidade do mouse" +msgstr "Multiplicador da velocidade do rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:63 msgid "Smooth aiming" -msgstr "Suavizar mouse" +msgstr "Suavizar rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:64 msgid "Smoothes the mouse movement, but makes aiming slightly less responsive" msgstr "" -"Suaviza os movimentos do mouse, mas torna a mira levemente menos responsiva" +"Suaviza os movimentos do rato, mas torna a mira um pouco menos responsiva" #: qcsrc/menu/xonotic/dialog_settings_input.qc:66 msgid "Invert aiming" -msgstr "Inverter mouse" +msgstr "Inverter rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:67 msgid "Invert mouse movement on the Y-axis" -msgstr "Inverter eixo Y do movimento do mouse" +msgstr "Inverter eixo Y do movimento do rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:69 msgid "Use system mouse positioning" -msgstr "Usar posicionamento de mouse do sistema" +msgstr "Usar posicionamento do rato do sistema" #: qcsrc/menu/xonotic/dialog_settings_input.qc:74 msgid "Enable built in mouse acceleration" -msgstr "Habilitar aceleração de mouse imbutida" +msgstr "Ativar aceleração de rato embebida" #: qcsrc/menu/xonotic/dialog_settings_input.qc:78 #: qcsrc/menu/xonotic/dialog_settings_input.qc:82 #: qcsrc/menu/xonotic/dialog_settings_input.qc:85 msgid "Disable system mouse acceleration" -msgstr "Desabilitar aceleração de mouse do SO" +msgstr "Desativar aceleração do rato do sistema" #: qcsrc/menu/xonotic/dialog_settings_input.qc:79 msgid "Make use of DGA mouse input" -msgstr "Fazer uso da entrada DGA de mouse" +msgstr "Fazer uso da entrada DGA do rato" #: qcsrc/menu/xonotic/dialog_settings_input.qc:93 msgid "Pressing \"enter console\" key also closes it" -msgstr " Pressionar \"abrir console\" também o fecha" +msgstr " Pressionar a tecla \"abrir consola\" também a fecha" #: qcsrc/menu/xonotic/dialog_settings_input.qc:94 msgid "Allow the console toggling bind to also close the console" -msgstr "Permite que o atalho para abrir o console também feche-o" +msgstr "Permite que o atalho para abrir a consola também a feixe" #: qcsrc/menu/xonotic/dialog_settings_input.qc:96 msgid "Automatically repeat jumping if holding jump" @@ -8247,11 +8251,11 @@ msgstr "Mochila a jato ao saltar:" #: qcsrc/menu/xonotic/dialog_settings_input.qc:101 msgid "JPJUMP^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_input.qc:102 msgid "Air only" -msgstr "Somente no ar" +msgstr "Apenas no ar" #: qcsrc/menu/xonotic/dialog_settings_input.qc:103 msgid "JPJUMP^All" @@ -8277,7 +8281,7 @@ msgstr "Cancelar" #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qh:7 msgid "User defined key bind" -msgstr "Botão de atalho definido pelo usuário" +msgstr "Botão de atalho definido pelo utilizador" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:11 #, c-format @@ -8306,7 +8310,7 @@ msgstr "Porta UDP do cliente:" msgid "Force client to use chosen port unless it is set to 0" msgstr "" "Força os clientes a utilizarem as portas escolhidas a menos que esteja " -"definido como 0 " +"definido como 0" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:34 msgid "Bandwidth:" @@ -8314,7 +8318,7 @@ msgstr "Largura de banda:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:36 msgid "Specify your network speed" -msgstr "Especifique a velocidade da sua rede" +msgstr "Especifica a velocidade da tua rede" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:37 msgid "56k" @@ -8350,15 +8354,15 @@ msgstr "Consultas ao servidor/s:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 msgid "Downloads:" -msgstr "Downloads:" +msgstr "Descarregamentos:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:54 msgid "Maximum number of concurrent HTTP/FTP downloads" -msgstr "Número máximo de downloads simultâneos via HTTP/FTP" +msgstr "Número máximo de descarregamentos simultâneos via HTTP/FTP" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:56 msgid "Download speed:" -msgstr "Velocidade de download:" +msgstr "Velocidade de descarregamento:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:69 msgid "Local latency:" @@ -8366,11 +8370,11 @@ msgstr "Latência local:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:73 msgid "Show netgraph" -msgstr "Exibir gráfico de rede" +msgstr "Mostrar gráfico de rede" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:74 msgid "Show a graph of packet sizes and other information" -msgstr "Exibe um gráfico de tamanhos de pacotes e outras informações" +msgstr "Mostra um gráfico de tamanhos de pacotes e outras informações" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:76 msgid "Client-side movement prediction" @@ -8402,11 +8406,11 @@ msgstr "Alvo:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:104 msgid "TRGT^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:116 msgid "Idle limit:" -msgstr "Em segundo plano:" +msgstr "Limite em segundo plano:" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:122 msgid "IDLFPS^Unlimited" @@ -8414,15 +8418,15 @@ msgstr "Ilimitado" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:126 msgid "Save processing time for other apps" -msgstr "Salvar tempo de processamento para outras aplicações" +msgstr "Gravar tempo de processamento para outras aplicações" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:129 msgid "Show frames per second" -msgstr "Exibir taxa de quadros por segundo" +msgstr "Mostrar taxa de quadros por segundo" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:130 msgid "Show your rendered frames per second" -msgstr "Exibe a sua taxa de quadros por segundo" +msgstr "Mostra a tua taxa de quadros por segundo" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:135 msgid "Menu tooltips:" @@ -8433,12 +8437,12 @@ msgid "" "Menu tooltips: disabled, standard or advanced (also shows cvar or console " "command bound to the menu item)" msgstr "" -"Dicas de menu: desabilitado, padrão ou avançado (também mostra a cvar ou " -"comando de console ligado ao item de menu)" +"Dicas de menu: desativado, padrão ou avançado (também mostra a cvar ou " +"comando da consola ligado ao item do menu)" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:138 msgid "TLTIP^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:139 msgid "TLTIP^Standard" @@ -8450,15 +8454,15 @@ msgstr "Avançado" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:143 msgid "Show current date and time" -msgstr "Exibir data e hora atual" +msgstr "Mostrar data e hora atual" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:144 msgid "Show current date and time of day, useful on screenshots" -msgstr "Exibe a data e hora atual do dia, útil para screenshots" +msgstr "Mostra a data e hora atual do dia, útil para capturas de ecrã" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:147 msgid "Enable developer mode" -msgstr "Habilitar modo de desenvolvedor" +msgstr "Ativar modo de programador" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:151 msgid "Advanced settings..." @@ -8466,13 +8470,12 @@ msgstr "Configurações avançadas..." #: qcsrc/menu/xonotic/dialog_settings_misc.qc:152 msgid "Advanced settings where you can tweak every single variable of the game" -msgstr "" -"Definições avançadas onde você poderá ajustar cada uma das variáveis do jogo" +msgstr "Definições avançadas onde podes ajustar cada uma das variáveis do jogo" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:157 #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qh:6 msgid "Factory reset" -msgstr "Configurações padrões" +msgstr "Configurações de fábrica" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:31 msgid "Cvar filter:" @@ -8480,7 +8483,7 @@ msgstr "Filtro de cvar:" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:38 msgid "Modified cvars only" -msgstr "Somente cvars modificadas" +msgstr "Apenas cvars alteradas" #: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:45 msgid "Setting:" @@ -8505,13 +8508,14 @@ msgstr "Configurações avançadas" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:11 msgid "Are you sure you want to reset all settings?" msgstr "" -"Tem certeza de que deseja redefinir todas as configurações para o padrão?" +"Tens certeza de que queres redefinir todas as configurações para as " +"originais?" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:13 msgid "This will create a backup config in your data directory" msgstr "" -"Isso irá criar uma cópia da sua configuração na seguinte pasta: C:\\Users" -"\\[usuário]\\Saved Games\\xonotic\\data" +"Isto irá criar uma cópia da tua configuração na seguinte pasta: C:\\Users" +"\\[utilizador]\\Saved Games\\xonotic\\data" #: qcsrc/menu/xonotic/dialog_settings_user.qc:25 msgid "Menu Skins" @@ -8527,7 +8531,7 @@ msgstr "Definir idioma" #: qcsrc/menu/xonotic/dialog_settings_user.qc:74 msgid "Disable gore effects and harsh language" -msgstr "Desabilitar sangue e linguagem inapropriada" +msgstr "Desativar sangue e linguagem inapropriada" #: qcsrc/menu/xonotic/dialog_settings_user.qc:75 msgid "" @@ -8535,18 +8539,19 @@ msgid "" "(default: disabled)" msgstr "" "Substitui o sangue com conteúdo que não contém nenhum efeito violento " -"(padrão: desabilitado)" +"(padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:10 msgid "While connected language changes will be applied only to the menu," msgstr "" -"Enquanto estiver conectado, alterações no idioma serão aplicadas somente no " -"menu." +"Enquanto estiver conectado, as alterações no idioma serão aplicadas apenas " +"no menu." #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:12 msgid "full language changes will take effect starting from the next game" msgstr "" -"Alterações completas de idioma surtirão efeito a partir da próxima partida" +"As alterações completas de idioma surtirão efeito apenas a partir da próxima " +"partida" #: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 msgid "Disconnect now" @@ -8622,7 +8627,7 @@ msgstr "32bit" #: qcsrc/menu/xonotic/dialog_settings_video.qc:59 msgid "Full screen" -msgstr "Tela cheia" +msgstr "Ecrã inteiro" #: qcsrc/menu/xonotic/dialog_settings_video.qc:61 msgid "Vertical Synchronization" @@ -8633,13 +8638,13 @@ msgid "" "Enable vertical synchronization to prevent tearing, will cap your fps to the " "screen refresh rate (default: disabled)" msgstr "" -"Habilita a sincronização vertical para evitar cortes na tela. Isso irá " -"limitar a quantidade de quadros por segundo em relação à taxa de atualização " -"do monitor (padrão: desabilitado)" +"Ativa a sincronização vertical para evitar cortes no ecrã. Isto irá limitar " +"a quantidade de frames por segundo em relação à taxa de atualização do " +"monitor (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:67 msgid "Flip view horizontally" -msgstr "Girar a visão horizontalmente" +msgstr "Inverter a visão na horizontal" #: qcsrc/menu/xonotic/dialog_settings_video.qc:68 msgid "Poor man's left handed mode (default: off)" @@ -8655,7 +8660,7 @@ msgstr "Qualidade do filtro anisotrópico (padrão: 1x)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:74 msgid "ANISO^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_video.qc:75 #: qcsrc/menu/xonotic/dialog_settings_video.qc:86 @@ -8684,12 +8689,12 @@ msgid "" "Enable antialiasing, which smooths the edges of 3D geometry. Note that it " "might decrease performance by quite a lot (default: disabled)" msgstr "" -"Habilita o anti-serrilhado, o qual suaviza as bordas da geometria em 3D. " -"Note que isso pode diminuir bastante o desempenho (padrão: desabilitado)" +"Ativa o anti-serrilhado, o qual suaviza as bordas da geometria em 3D. Nota " +"que isto pode diminuir bastante o desempenho (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:85 msgid "AA^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_video.qc:92 msgid "High-quality frame buffer" @@ -8704,12 +8709,12 @@ msgid "" "Eliminate overdraw by rendering a depth-only version of the scene before the " "normal rendering starts (default: disabled)" msgstr "" -"Elimina o sobredesenho renderizando uma versão de profundidade única da cena " -"antes que a renderização normal comece (padrão: desabilitado)" +"Elimina o sobre desenho renderizando uma versão de profundidade única da " +"cena antes que a renderização normal comece (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:100 msgid "DF^Disabled" -msgstr "Desabilitado" +msgstr "Desativado" #: qcsrc/menu/xonotic/dialog_settings_video.qc:101 msgid "DF^World" @@ -8797,19 +8802,19 @@ msgid "" "requires GLSL color control (default: 1)" msgstr "" "Ajuste da saturação (0 = tons de cinza, 1 = normal, 2 = muito saturado), " -"requer controle de cor GLSL (padrão: 1)" +"requer controlo da cor GLSL (padrão: 1)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:146 msgid "LIT^Ambient:" -msgstr "Iluminação ambiental:" +msgstr "Iluminação do ambiente:" #: qcsrc/menu/xonotic/dialog_settings_video.qc:148 msgid "" "Ambient lighting, if set too high it tends to make light on maps look dull " "and flat (default: 4)" msgstr "" -"Iluminação ambiental, caso o valor seja muito alto, poderá fazer com que as " -"luzes dos mapas fiquem achatadas e sem graça (padrão: 4)" +"Iluminação do ambiente, caso o valor seja muito alto, poderá fazer com que " +"as luzes dos mapas fiquem achatadas e sem piada (padrão: 4)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:150 msgid "Intensity:" @@ -8821,16 +8826,16 @@ msgstr "Brilho geral da renderização (padrão: 1)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:155 msgid "Wait for GPU to finish each frame" -msgstr "Esperar que a placa de vídeo termine cada quadro" +msgstr "Esperar que a placa gráfica termine cada frame" #: qcsrc/menu/xonotic/dialog_settings_video.qc:156 msgid "" "Make the CPU wait for the GPU to finish each frame, can help with some " "strange input or video lag on some machines (default: disabled)" msgstr "" -"Faz com que o processador espere pela placa de vídeo terminar cada quadro. " -"Pode ajudar no caso de alguns atrasos nos dispositivos de entrada ou lag de " -"vídeo em algumas máquinas (padrão: desabilitado)" +"Faz com que o processador espere pela placa de gráfica termine de renderizar " +"cada frame. Pode ajudar em alguns casos de atrasos nos dispositivos de " +"entrada ou atrasos de vídeo em algumas máquinas (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:158 msgid "Use OpenGL 2.0 shaders (GLSL)" @@ -8838,27 +8843,27 @@ msgstr "Usar shaders de OpenGL 2.0 (GLSL)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:162 msgid "Use GLSL to handle color control" -msgstr "Usar GLSL para o controle de cores" +msgstr "Usar GLSL para o controlo das cores" #: qcsrc/menu/xonotic/dialog_settings_video.qc:163 msgid "" "Enable use of GLSL to apply gamma correction, note that it might decrease " "performance by a lot (default: disabled)" msgstr "" -"Habilita o uso de GLSL para aplicar correção de gama. Note que isso pode " -"diminuir bastante o desempenho (padrão: desabilitado)" +"Ativa o uso de GLSL para aplicar a correção de gama. Nota que isto pode " +"diminuir bastante o desempenho (padrão: desativado)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:168 msgid "Psycho coloring (easter egg)" -msgstr "Cores 'Psycho' (easter egg)" +msgstr "Cores psicadélicas (easter egg)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:171 msgid "Trippy vertices (easter egg)" -msgstr "Vértices 'Trip' (easter egg)" +msgstr "Vértices viagem (easter egg)" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:110 msgid "Instant action! (random map with bots)" -msgstr "Ação Instantânea! (mapa aleatório com bots)" +msgstr "Ação instantânea! (mapa aleatório com robôs)" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:117 msgid "???" @@ -8891,8 +8896,8 @@ msgstr "Um Jogador" #: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 msgid "Play the singleplayer campaign or instant action matches against bots" msgstr "" -"Jogue a campanha de um jogador ou inicie uma partida de ação instantânea " -"contra bots" +"Joga a campanha de um jogador ou inicia uma partida de ação instantânea " +"contra robôs" #: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 msgid "Winner" @@ -8900,11 +8905,11 @@ msgstr "Vencedor" #: qcsrc/menu/xonotic/dialog_teamselect.qc:32 msgid "join 'best' team (auto-select)" -msgstr "juntar-se à 'melhor' equipe (seleção automática)" +msgstr "juntar-me à 'melhor' equipa (seleção automática)" #: qcsrc/menu/xonotic/dialog_teamselect.qc:33 msgid "Autoselect team (recommended)" -msgstr "Selecionar equipe automaticamente (recomendado)" +msgstr "Selecionar equipa automaticamente (recomendado)" #: qcsrc/menu/xonotic/dialog_teamselect.qc:37 msgid "red" @@ -8928,21 +8933,21 @@ msgstr "assistir" #: qcsrc/menu/xonotic/dialog_teamselect.qh:7 msgid "Team Selection" -msgstr "Seleção de Equipe" +msgstr "Seleção de Equipa" #: qcsrc/menu/xonotic/dialog_uid2name.qc:10 msgid "Allow player statistics to use your nickname?" -msgstr "Permitir que as estatísticas de jogadores usem o seu apelido?" +msgstr "Permitir que as estatísticas de jogadores usem o teu apelido?" #: qcsrc/menu/xonotic/dialog_uid2name.qc:12 msgid "Answering \"No\" you will appear as \"Anonymous player\"" msgstr "" -"Selecionando \"Não\" você aparecerá como \"Anonymous player\" (em português, " -"\"Jogador anônimo\")" +"Selecionando \"Não\" aparecerás como \"Anonymous player\" (em português, " +"\"Jogador anónimo\")" #: qcsrc/menu/xonotic/gametypelist.qc:86 msgid "teamplay" -msgstr "jogo em equipe" +msgstr "jogo em equipa" #: qcsrc/menu/xonotic/gametypelist.qc:88 msgid "free for all" @@ -8954,19 +8959,19 @@ msgstr "Movimento" #: qcsrc/menu/xonotic/keybinder.qc:30 msgid "forward" -msgstr "Mover-se para frente" +msgstr "mover para frente" #: qcsrc/menu/xonotic/keybinder.qc:31 msgid "backpedal" -msgstr "Mover-se para trás" +msgstr "mover para trás" #: qcsrc/menu/xonotic/keybinder.qc:32 msgid "strafe left" -msgstr "Mover-se para a esquerda" +msgstr "mover para a esquerda" #: qcsrc/menu/xonotic/keybinder.qc:33 msgid "strafe right" -msgstr "Mover-se para a direita" +msgstr "mover para a direita" #: qcsrc/menu/xonotic/keybinder.qc:34 msgid "jump / swim" @@ -9010,7 +9015,7 @@ msgstr "recarregar" #: qcsrc/menu/xonotic/keybinder.qc:49 msgid "drop weapon / throw nade" -msgstr "largar arma / arremessar granada" +msgstr "largar arma / atirar granada" #: qcsrc/menu/xonotic/keybinder.qc:77 msgid "hold zoom" @@ -9022,11 +9027,11 @@ msgstr "ativar/desativar zoom" #: qcsrc/menu/xonotic/keybinder.qc:79 msgid "show scores" -msgstr "exibir pontuações" +msgstr "mostrar pontuações" #: qcsrc/menu/xonotic/keybinder.qc:80 msgid "screen shot" -msgstr "tirar screenshot" +msgstr "tirar captura de ecrã" #: qcsrc/menu/xonotic/keybinder.qc:81 msgid "maximize radar" @@ -9034,11 +9039,11 @@ msgstr "maximizar radar" #: qcsrc/menu/xonotic/keybinder.qc:82 msgid "3rd person view" -msgstr "visão em 3ª pessoa" +msgstr "visão na 3ª pessoa" #: qcsrc/menu/xonotic/keybinder.qc:83 msgid "enter spectator mode" -msgstr "entrar no modo de expectador" +msgstr "entrar no modo de espectador" #: qcsrc/menu/xonotic/keybinder.qc:85 msgid "Communicate" @@ -9046,15 +9051,15 @@ msgstr "Comunicação" #: qcsrc/menu/xonotic/keybinder.qc:86 msgid "public chat" -msgstr "Bate-papo público" +msgstr "Conversação pública" #: qcsrc/menu/xonotic/keybinder.qc:87 qcsrc/menu/xonotic/keybinder.qc:100 msgid "team chat" -msgstr "Bate-papo de equipe" +msgstr "Conversação da equipa" #: qcsrc/menu/xonotic/keybinder.qc:88 msgid "show chat history" -msgstr "exibir histórico do bate-papo" +msgstr "mostrar histórico da conversação" #: qcsrc/menu/xonotic/keybinder.qc:89 msgid "vote YES" @@ -9070,7 +9075,7 @@ msgstr "Cliente" #: qcsrc/menu/xonotic/keybinder.qc:95 msgid "enter console" -msgstr "abrir o console" +msgstr "abrir a consola" #: qcsrc/menu/xonotic/keybinder.qc:96 msgid "disconnect" @@ -9082,7 +9087,7 @@ msgstr "sair" #: qcsrc/menu/xonotic/keybinder.qc:101 msgid "auto-join team" -msgstr "juntar-se à uma equipe automáticamente" +msgstr "juntar-me a uma equipa automaticamente" #: qcsrc/menu/xonotic/keybinder.qc:103 msgid "drop key / drop flag" @@ -9102,17 +9107,17 @@ msgstr "arrastar objeto" #: qcsrc/menu/xonotic/keybinder.qc:110 msgid "User defined" -msgstr "Definido pelo usuário" +msgstr "Definido pelo utilizador" #: qcsrc/menu/xonotic/mainwindow.qc:64 qcsrc/menu/xonotic/mainwindow.qc:67 msgid "Do not press this button again!" -msgstr "Não aperte este botão novamente!" +msgstr "Não pressiones este botão outra vez!" #: qcsrc/menu/xonotic/maplist.qc:291 msgid "" "Huh? Can't play this (m is NULL). Refiltering so this won't happen again.\n" msgstr "" -"Huh? Não posso jogar isto (m é NULO). Refiltrando para que isso não ocorra " +"Huh? Não posso jogar isto (m é NULO). Refiltrando para que isto não ocorra " "novamente.\n" #: qcsrc/menu/xonotic/maplist.qc:299 @@ -9125,12 +9130,12 @@ msgid "" "Huh? Can't play this (invalid game type). Refiltering so this won't happen " "again.\n" msgstr "" -"Huh? Não posso jogar isto (modo de jogo inválido). Refiltrando para que isso " +"Huh? Não posso jogar isto (modo de jogo inválido). Refiltrando para que isto " "não ocorra novamente.\n" #: qcsrc/menu/xonotic/playerlist.qc:100 qcsrc/menu/xonotic/playerlist.qc:110 msgid "spectator" -msgstr "espectador" +msgstr "espetador" #: qcsrc/menu/xonotic/playermodel.qc:170 msgid "<no model found>" @@ -9138,7 +9143,7 @@ msgstr "<nenhum modelo encontrado>" #: qcsrc/menu/xonotic/serverlist.qc:273 msgid "Favorite" -msgstr "Favoritar" +msgstr "Adicionar aos favoritos" #: qcsrc/menu/xonotic/serverlist.qc:274 msgid "" @@ -9185,7 +9190,7 @@ msgstr "modificação: %s" #: qcsrc/menu/xonotic/serverlist.qc:1063 #, c-format msgid "modified settings" -msgstr "configurações modificadas" +msgstr "configurações alteradas" #: qcsrc/menu/xonotic/serverlist.qc:1063 #, c-format @@ -9194,11 +9199,11 @@ msgstr "configurações oficiais" #: qcsrc/menu/xonotic/serverlist.qc:1065 msgid "stats disabled" -msgstr "estatísticas desabilitadas" +msgstr "estatísticas desativadas" #: qcsrc/menu/xonotic/serverlist.qc:1065 msgid "stats enabled" -msgstr "estatísticas habilitadas" +msgstr "estatísticas ativadas" #: qcsrc/menu/xonotic/serverlist.qh:151 msgid "SLCAT^Favorites" @@ -9222,7 +9227,7 @@ msgstr "Modo Competitivo" #: qcsrc/menu/xonotic/serverlist.qh:156 msgid "SLCAT^Modified Servers" -msgstr "Servidores Modificados" +msgstr "Servidores Alterados" #: qcsrc/menu/xonotic/serverlist.qh:157 msgid "SLCAT^Overkill" @@ -9263,7 +9268,7 @@ msgid "" "gives for better performance (default: 1)" msgstr "" "Multiplicador para a quantidade de partículas. Menos significa menos " -"partículas, o que resulta em um melhor desempenho (padrão: 1)" +"partículas, o que resulta num melhor desempenho (padrão: 1)" #: qcsrc/menu/xonotic/slider_particles.qc:14 msgid "PART^OMG" @@ -9306,7 +9311,7 @@ msgstr "" #: qcsrc/menu/xonotic/slider_resolution.qc:115 msgid "Screen resolution" -msgstr "Resolução da tela" +msgstr "Resolução do ecrã" #: qcsrc/menu/xonotic/slider_sbfadetime.qc:13 msgid "PART^Slow" @@ -9420,12 +9425,12 @@ msgid "" "Update can be downloaded at:\n" "%s\n" msgstr "" -"A atualização pode ser baixada em:\n" +"A atualização pode ser descarregada em:\n" "%s\n" #: qcsrc/menu/xonotic/util.qc:528 msgid "Autogenerating mapinfo for newly added maps..." -msgstr "Gerando mapinfo automaticamente para os novos mapas adicionados..." +msgstr "A gerar mapinfo automaticamente para os novos mapas adicionados..." #: qcsrc/menu/xonotic/util.qc:557 #, c-format @@ -9435,15 +9440,15 @@ msgstr "^1%s VERSÃO DE TESTE" #: qcsrc/menu/xonotic/util.qc:577 #, c-format msgid "Update to %s now!" -msgstr "Atualize para %s agora!" +msgstr "Atualizar para %s agora!" #: qcsrc/menu/xonotic/util.qc:662 msgid "" "^1ERROR: Texture compression is required but not supported.\n" "^1Expect visual problems.\n" msgstr "" -"^1ERRO: A compressão de texturas é necessária, mas não é suportada.\n" -"^1Espere problemas visuais.\n" +"^1ERRO: a compressão de texturas é necessária, mas não é suportada.\n" +"^1Conta com prováveis problemas visuais.\n" "\n" #: qcsrc/menu/xonotic/util.qc:780 @@ -9452,14 +9457,14 @@ msgstr "Usar padrão" #: qcsrc/menu/xonotic/util.qc:800 msgid "Team Color:" -msgstr "Cor de Equipe:" +msgstr "Cor da Equipa:" #: qcsrc/menu/xonotic/util.qh:44 msgid "Enable panel" -msgstr "Habilitar painel" +msgstr "Ativar painel" #~ msgid "QMCMD^Chat" -#~ msgstr "Bate-papo" +#~ msgstr "Conversação" #~ msgid "^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s" -#~ msgstr "^BG%s%s^K1 chegou muito perto do foguete de ^BG%s^K1%s%s" +#~ msgstr "^BG%s%s^K1 chegou muito perto do míssil de ^BG%s^K1%s%s" diff --git a/common.pt_BR.po b/common.pt_BR.po new file mode 100644 index 0000000000..7306a0dca6 --- /dev/null +++ b/common.pt_BR.po @@ -0,0 +1,9465 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# +# Translators: +# Ivan Paulos Tomé <greylica@gmail.com>, 2016 +# Jean Trindade Pereira <jean_trindade2@hotmail.com>, 2018 +# Mirio <opivy@hotmail.de>, 2017 +# NotThatPrivate Yes <henriqueferreira2009@gmail.com>, 2015 +# Ricardo Manuel da Cruz Coelho da Silva <ricardo.mccs@gmail.com>, 2015 +# Rui <xymarior@yandex.com>, 2018 +msgid "" +msgstr "" +"Project-Id-Version: Xonotic\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2017-07-09 00:35+0200\n" +"PO-Revision-Date: 2018-07-22 18:03+0000\n" +"Last-Translator: Jean Trindade Pereira <jean_trindade2@hotmail.com>\n" +"Language-Team: Portuguese (Brazil) (http://www.transifex.com/team-xonotic/" +"xonotic/language/pt_BR/)\n" +"Language: pt_BR\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n > 1);\n" + +#: qcsrc/client/hud/hud_config.qc:239 +#, c-format +msgid "^2Successfully exported to %s! (Note: It's saved in data/data/)\n" +msgstr "^2Exportado com sucesso para %s! (Nota: foi salvo em data/data/)\n" + +#: qcsrc/client/hud/hud_config.qc:243 +#, c-format +msgid "^1Couldn't write to %s\n" +msgstr "^1Não foi possível escrever para %s\n" + +#: qcsrc/client/hud/panel/chat.qc:82 +msgid "^3Player^7: This is the chat area." +msgstr "^3Jogador^7: Isto é a área do bate-papo." + +#: qcsrc/client/hud/panel/engineinfo.qc:69 +#, c-format +msgid "FPS: %.*f" +msgstr "FPS: %.*f" + +#: qcsrc/client/hud/panel/infomessages.qc:87 +msgid "^1Observing" +msgstr "^1Observando" + +#: qcsrc/client/hud/panel/infomessages.qc:89 +#, c-format +msgid "^1Spectating: ^7%s" +msgstr "^1Assistindo: ^7%s" + +#: qcsrc/client/hud/panel/infomessages.qc:100 +#, c-format +msgid "^1Press ^3%s^1 to spectate" +msgstr "^1Aperte ^3%s^1 para assistir" + +#: qcsrc/client/hud/panel/infomessages.qc:100 +#: qcsrc/menu/xonotic/keybinder.qc:40 +msgid "primary fire" +msgstr "disparo primário" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#, c-format +msgid "^1Press ^3%s^1 or ^3%s^1 for next or previous player" +msgstr "^1Aperte ^3%s^1 ou ^3%s^1 para o jogador seguinte ou anterior" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#: qcsrc/client/hud/panel/infomessages.qc:106 +msgid "next weapon" +msgstr "arma seguinte" + +#: qcsrc/client/hud/panel/infomessages.qc:102 +#: qcsrc/client/hud/panel/infomessages.qc:106 +msgid "previous weapon" +msgstr "arma anterior" + +#: qcsrc/client/hud/panel/infomessages.qc:106 +#, c-format +msgid "^1Use ^3%s^1 or ^3%s^1 to change the speed" +msgstr "^1Use ^3%s^1 ou ^3%s^1 para alterar a velocidade" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#, c-format +msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" +msgstr "^1Aperte ^3%s^1 para observar e ^3%s^1 para alterar o modo da câmera" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#: qcsrc/common/vehicles/cl_vehicles.qc:192 +msgid "drop weapon" +msgstr "largar arma" + +#: qcsrc/client/hud/panel/infomessages.qc:108 +#: qcsrc/menu/xonotic/keybinder.qc:41 +msgid "secondary fire" +msgstr "disparo secundário" + +#: qcsrc/client/hud/panel/infomessages.qc:111 +#, c-format +msgid "^1Press ^3%s^1 for gamemode info" +msgstr "^1Aperte ^3%s^1 para ver as informações do modo de jogo" + +#: qcsrc/client/hud/panel/infomessages.qc:111 +#: qcsrc/menu/xonotic/keybinder.qc:94 +msgid "server info" +msgstr "informações do servidor" + +#: qcsrc/client/hud/panel/infomessages.qc:124 +msgid "^1Match has already begun" +msgstr "^1A partida já começou" + +#: qcsrc/client/hud/panel/infomessages.qc:126 +msgid "^1You have no more lives left" +msgstr "^1Você não tem mais vidas sobrando" + +#: qcsrc/client/hud/panel/infomessages.qc:128 +#: qcsrc/client/hud/panel/infomessages.qc:131 +#, c-format +msgid "^1Press ^3%s^1 to join" +msgstr "^1Aperte ^3%s^1 para entrar no jogo" + +#: qcsrc/client/hud/panel/infomessages.qc:128 +#: qcsrc/client/hud/panel/infomessages.qc:131 +msgid "jump" +msgstr "saltar" + +#: qcsrc/client/hud/panel/infomessages.qc:139 +#, c-format +msgid "^1Game starts in ^3%d^1 seconds" +msgstr "^1A partida iniciará em ^3%d^1 segundo(s)" + +#: qcsrc/client/hud/panel/infomessages.qc:145 +msgid "^2Currently in ^1warmup^2 stage!" +msgstr "^2Atualmente em fase de ^1aquecimento^2!" + +#: qcsrc/client/hud/panel/infomessages.qc:160 +#, c-format +msgid "%sPress ^3%s%s to end warmup" +msgstr "%sAperte ^3%s%s para terminar o aquecimento" + +#: qcsrc/client/hud/panel/infomessages.qc:160 +#: qcsrc/client/hud/panel/infomessages.qc:162 +#: qcsrc/client/hud/panel/infomessages.qc:175 +#: qcsrc/menu/xonotic/keybinder.qc:91 +msgid "ready" +msgstr "pronto" + +#: qcsrc/client/hud/panel/infomessages.qc:162 +#, c-format +msgid "%sPress ^3%s%s once you are ready" +msgstr "%sAperte ^3%s%s assim que estiver pronto" + +#: qcsrc/client/hud/panel/infomessages.qc:167 +msgid "^2Waiting for others to ready up to end warmup..." +msgstr "" +"^2Aguardando outros jogadores ficarem prontos para terminar o aquecimento..." + +#: qcsrc/client/hud/panel/infomessages.qc:169 +msgid "^2Waiting for others to ready up..." +msgstr "^2Aguardando outros jogadores ficarem prontos..." + +#: qcsrc/client/hud/panel/infomessages.qc:175 +#, c-format +msgid "^2Press ^3%s^2 to end warmup" +msgstr "^2Aperte ^3%s^2 para terminar o aquecimento" + +#: qcsrc/client/hud/panel/infomessages.qc:196 +msgid "Teamnumbers are unbalanced!" +msgstr "As equipes estão desequilibradas!" + +#: qcsrc/client/hud/panel/infomessages.qc:199 +#, c-format +msgid " Press ^3%s%s to adjust" +msgstr " Aperte ^3%s%s para ajustar" + +#: qcsrc/client/hud/panel/infomessages.qc:199 +#: qcsrc/menu/xonotic/keybinder.qc:102 +msgid "team menu" +msgstr "menu de equipe" + +#: qcsrc/client/hud/panel/infomessages.qc:209 +msgid "^1Spectating this player:" +msgstr "^1Assistindo a este jogador:" + +#: qcsrc/client/hud/panel/infomessages.qc:209 +msgid "^1Spectating you:" +msgstr "^1Assistindo você:" + +#: qcsrc/client/hud/panel/infomessages.qc:225 +msgid "^7Press ^3ESC ^7to show HUD options." +msgstr "^7Aperte ^3ESC ^7para exibir as opções de interface." + +#: qcsrc/client/hud/panel/infomessages.qc:226 +msgid "^3Doubleclick ^7a panel for panel-specific options." +msgstr "" +"^3Clique duas vezes ^7em um painel para abrir sua janela de opções " +"específicas." + +#: qcsrc/client/hud/panel/infomessages.qc:227 +msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" +msgstr "Use ^3CTRL ^7para desligar o teste de colisão, e ^3SHIFT ^7e" + +#: qcsrc/client/hud/panel/infomessages.qc:228 +msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." +msgstr "^3ALT ^7+ ^3TECLAS DIRECIONAIS ^7para pequenos ajustes." + +#: qcsrc/client/hud/panel/modicons.qc:566 +msgid "Personal best" +msgstr "Recorde pessoal" + +#: qcsrc/client/hud/panel/modicons.qc:576 +msgid "Server best" +msgstr "Recorde do servidor" + +#: qcsrc/client/hud/panel/notify.qc:115 qcsrc/client/hud/panel/notify.qc:116 +#: qcsrc/client/hud/panel/score.qc:59 +#, c-format +msgid "Player %d" +msgstr "Jogador %d" + +#: qcsrc/client/hud/panel/quickmenu.qc:603 +#: qcsrc/client/hud/panel/quickmenu.qc:605 +#, c-format +msgid "Submenu%d" +msgstr "Submenu%d" + +#: qcsrc/client/hud/panel/quickmenu.qc:610 +#, c-format +msgid "Command%d" +msgstr "Comando%d" + +#: qcsrc/client/hud/panel/quickmenu.qc:636 +msgid "Continue..." +msgstr "Continuar..." + +#: qcsrc/client/hud/panel/quickmenu.qc:794 +#: qcsrc/client/hud/panel/quickmenu.qc:798 +msgid "Chat" +msgstr "" + +#: qcsrc/client/hud/panel/quickmenu.qc:795 +msgid "QMCMD^:-) / nice one" +msgstr ":-) / Boa jogada" + +#: qcsrc/client/hud/panel/quickmenu.qc:795 +msgid "QMCMD^nice one" +msgstr "Boa jogada" + +#: qcsrc/client/hud/panel/quickmenu.qc:796 +msgid "QMCMD^good game" +msgstr "Bom jogo" + +#: qcsrc/client/hud/panel/quickmenu.qc:797 +msgid "QMCMD^hi / good luck" +msgstr "Olá / Boa sorte" + +#: qcsrc/client/hud/panel/quickmenu.qc:797 +msgid "QMCMD^hi / good luck and have fun" +msgstr "Olá / Boa sorte e divirta-se" + +#: qcsrc/client/hud/panel/quickmenu.qc:802 +#: qcsrc/client/hud/panel/quickmenu.qc:818 +msgid "QMCMD^Team chat" +msgstr "Bate-papo de equipe" + +#: qcsrc/client/hud/panel/quickmenu.qc:803 +msgid "QMCMD^quad soon" +msgstr "Quad em breve" + +#: qcsrc/client/hud/panel/quickmenu.qc:804 +msgid "QMCMD^free item %x^7 (l:%y^7)" +msgstr "Item livre %x^7 (l:%y^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:804 +msgid "QMCMD^free item, icon" +msgstr "Item livre, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:805 +msgid "QMCMD^took item (l:%l^7)" +msgstr "Item pego (l:%l^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:805 +msgid "QMCMD^took item, icon" +msgstr "Item pego, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:806 +msgid "QMCMD^negative" +msgstr "Negativo" + +#: qcsrc/client/hud/panel/quickmenu.qc:807 +msgid "QMCMD^positive" +msgstr "Positivo" + +#: qcsrc/client/hud/panel/quickmenu.qc:808 +msgid "QMCMD^need help (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "Preciso de ajuda (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:808 +msgid "QMCMD^need help, icon" +msgstr "Preciso de ajuda, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:809 +msgid "QMCMD^enemy seen (l:%y^7)" +msgstr "Inimigo avistado (l:%y^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:809 +msgid "QMCMD^enemy seen, icon" +msgstr "Inimigo avistado, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:810 +msgid "QMCMD^flag seen (l:%y^7)" +msgstr "Bandeira avistada (l:%y^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:810 +msgid "QMCMD^flag seen, icon" +msgstr "Bandeira avistada, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:811 +msgid "QMCMD^defending (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "Defendendo (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:811 +msgid "QMCMD^defending, icon" +msgstr "Defendendo, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:812 +msgid "QMCMD^roaming (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "Patrulhando (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:812 +msgid "QMCMD^roaming, icon" +msgstr "Patrulhando, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:813 +msgid "QMCMD^attacking (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" +msgstr "Atacando (l:%l^7) (h:%h^7 a:%a^7 w:%w^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:813 +msgid "QMCMD^attacking, icon" +msgstr "Atacando, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:814 +msgid "QMCMD^killed flagcarrier (l:%y^7)" +msgstr "Portador da bandeira aniquilado (l:%y^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:814 +msgid "QMCMD^killed flagcarrier, icon" +msgstr "Portador da bandeira aniquilado, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:815 +#, c-format +msgid "QMCMD^dropped flag (l:%d^7)" +msgstr "Bandeira largada (l:%d^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:815 +msgid "QMCMD^dropped flag, icon" +msgstr "Bandeira largada, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:816 +msgid "QMCMD^drop weapon, icon" +msgstr "Largar arma, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:816 +msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" +msgstr "Arma largada %w^7 (l:%l^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:817 +msgid "QMCMD^drop flag/key, icon" +msgstr "Largar bandeira/chave, ícone" + +#: qcsrc/client/hud/panel/quickmenu.qc:817 +msgid "QMCMD^dropped flag/key %w^7 (l:%l^7)" +msgstr "Bandeira/Chave largada %w^7 (l:%l^7)" + +#: qcsrc/client/hud/panel/quickmenu.qc:821 +msgid "QMCMD^Send private message to" +msgstr "Enviar mensagem privada para" + +#: qcsrc/client/hud/panel/quickmenu.qc:823 +#: qcsrc/client/hud/panel/quickmenu.qc:860 +msgid "QMCMD^Settings" +msgstr "Configurações" + +#: qcsrc/client/hud/panel/quickmenu.qc:824 +#: qcsrc/client/hud/panel/quickmenu.qc:831 +msgid "QMCMD^View/HUD settings" +msgstr "Configurações de exibição/interface" + +#: qcsrc/client/hud/panel/quickmenu.qc:825 +msgid "QMCMD^3rd person view" +msgstr "Visão em 3ª pessoa" + +#: qcsrc/client/hud/panel/quickmenu.qc:826 +msgid "QMCMD^Player models like mine" +msgstr "Modelos de jogadores como o meu" + +#: qcsrc/client/hud/panel/quickmenu.qc:827 +msgid "QMCMD^Names above players" +msgstr "Nomes sobre jogadores" + +#: qcsrc/client/hud/panel/quickmenu.qc:828 +msgid "QMCMD^Crosshair per weapon" +msgstr "Retículo por arma" + +#: qcsrc/client/hud/panel/quickmenu.qc:829 +msgid "QMCMD^FPS" +msgstr "FPS" + +#: qcsrc/client/hud/panel/quickmenu.qc:830 +msgid "QMCMD^Net graph" +msgstr "Gráfico da rede" + +#: qcsrc/client/hud/panel/quickmenu.qc:833 +#: qcsrc/client/hud/panel/quickmenu.qc:836 +msgid "QMCMD^Sound settings" +msgstr "Configurações de som" + +#: qcsrc/client/hud/panel/quickmenu.qc:834 +msgid "QMCMD^Hit sound" +msgstr "Som de acerto" + +#: qcsrc/client/hud/panel/quickmenu.qc:835 +msgid "QMCMD^Chat sound" +msgstr "Som do bate-papo" + +#: qcsrc/client/hud/panel/quickmenu.qc:840 +#: qcsrc/client/hud/panel/quickmenu.qc:844 +msgid "QMCMD^Spectator camera" +msgstr "Câmera de espectador" + +#: qcsrc/client/hud/panel/quickmenu.qc:841 +msgid "QMCMD^1st person" +msgstr "1ª pessoa" + +#: qcsrc/client/hud/panel/quickmenu.qc:842 +msgid "QMCMD^3rd person around player" +msgstr "3ª pessoa em volta do jogador" + +#: qcsrc/client/hud/panel/quickmenu.qc:843 +msgid "QMCMD^3rd person behind" +msgstr "3ª pessoa traseira" + +#: qcsrc/client/hud/panel/quickmenu.qc:849 +#: qcsrc/client/hud/panel/quickmenu.qc:854 +msgid "QMCMD^Observer camera" +msgstr "Câmera de observador" + +#: qcsrc/client/hud/panel/quickmenu.qc:850 +msgid "QMCMD^Increase speed" +msgstr "Aumentar velocidade" + +#: qcsrc/client/hud/panel/quickmenu.qc:851 +msgid "QMCMD^Decrease speed" +msgstr "Reduzir velocidade" + +#: qcsrc/client/hud/panel/quickmenu.qc:852 +msgid "QMCMD^Wall collision off" +msgstr "Colisão com paredes desligada" + +#: qcsrc/client/hud/panel/quickmenu.qc:853 +msgid "QMCMD^Wall collision on" +msgstr "Colisão com paredes ligada" + +#: qcsrc/client/hud/panel/quickmenu.qc:857 +msgid "QMCMD^Fullscreen" +msgstr "Tela cheia" + +#: qcsrc/client/hud/panel/quickmenu.qc:859 +msgid "QMCMD^Translate chat messages" +msgstr "Traduzir mensagens do bate-papo" + +#: qcsrc/client/hud/panel/quickmenu.qc:862 +#: qcsrc/client/hud/panel/quickmenu.qc:872 +msgid "QMCMD^Call a vote" +msgstr "Iniciar uma votação" + +#: qcsrc/client/hud/panel/quickmenu.qc:863 +msgid "QMCMD^Restart the map" +msgstr "Reiniciar o mapa" + +#: qcsrc/client/hud/panel/quickmenu.qc:864 +msgid "QMCMD^End match" +msgstr "Terminar a partida" + +#: qcsrc/client/hud/panel/quickmenu.qc:867 +msgid "QMCMD^Reduce match time" +msgstr "Reduzir tempo de partida" + +#: qcsrc/client/hud/panel/quickmenu.qc:868 +msgid "QMCMD^Extend match time" +msgstr "Estender tempo de partida" + +#: qcsrc/client/hud/panel/quickmenu.qc:871 +msgid "QMCMD^Shuffle teams" +msgstr "Misturar as equipes" + +#: qcsrc/client/hud/panel/racetimer.qc:37 +#, c-format +msgid " (-%dL)" +msgstr " (-%dL)" + +#: qcsrc/client/hud/panel/racetimer.qc:42 +#, c-format +msgid " (+%dL)" +msgstr " (+%dL)" + +#: qcsrc/client/hud/panel/racetimer.qc:61 +msgid "Start line" +msgstr "Linha de partida" + +#: qcsrc/client/hud/panel/racetimer.qc:63 +#: qcsrc/client/hud/panel/racetimer.qc:67 +msgid "Finish line" +msgstr "Linha de chegada" + +#: qcsrc/client/hud/panel/racetimer.qc:65 +#, c-format +msgid "Intermediate %d" +msgstr "Intermediário %d" + +#: qcsrc/client/hud/panel/racetimer.qc:132 +msgid "^1Intermediate 1 (+15.42)" +msgstr "^1Intermediário 1 (+15.42)" + +#: qcsrc/client/hud/panel/racetimer.qc:135 +#: qcsrc/client/hud/panel/racetimer.qc:177 +#: qcsrc/client/hud/panel/racetimer.qc:227 +#, c-format +msgid "^1PENALTY: %.1f (%s)" +msgstr "^1PENALIDADE: %.1f (%s)" + +#: qcsrc/client/hud/panel/racetimer.qc:229 +#, c-format +msgid "^2PENALTY: %.1f (%s)" +msgstr "^2PENALIDADE: %.1f (%s)" + +#: qcsrc/client/hud/panel/scoreboard.qc:78 +msgid "SCO^bckills" +msgstr "pblvítimas" + +#: qcsrc/client/hud/panel/scoreboard.qc:79 +msgid "SCO^bctime" +msgstr "pbltempo" + +#: qcsrc/client/hud/panel/scoreboard.qc:80 +msgid "SCO^caps" +msgstr "capturas" + +#: qcsrc/client/hud/panel/scoreboard.qc:81 +msgid "SCO^captime" +msgstr "tempo de captura" + +#: qcsrc/client/hud/panel/scoreboard.qc:82 +msgid "SCO^deaths" +msgstr "mortes" + +#: qcsrc/client/hud/panel/scoreboard.qc:83 +msgid "SCO^destroyed" +msgstr "destruído" + +#: qcsrc/client/hud/panel/scoreboard.qc:84 +msgid "SCO^damage" +msgstr "dano" + +#: qcsrc/client/hud/panel/scoreboard.qc:85 +msgid "SCO^dmgtaken" +msgstr "dano recebido" + +#: qcsrc/client/hud/panel/scoreboard.qc:86 +msgid "SCO^drops" +msgstr "quedas" + +#: qcsrc/client/hud/panel/scoreboard.qc:87 +msgid "SCO^faults" +msgstr "faltas" + +#: qcsrc/client/hud/panel/scoreboard.qc:88 +msgid "SCO^fckills" +msgstr "pbndvítimas" + +#: qcsrc/client/hud/panel/scoreboard.qc:89 +msgid "SCO^goals" +msgstr "gols" + +#: qcsrc/client/hud/panel/scoreboard.qc:90 +msgid "SCO^kckills" +msgstr "pcvítimas" + +#: qcsrc/client/hud/panel/scoreboard.qc:91 +msgid "SCO^kdratio" +msgstr "taxa de v/m" + +#: qcsrc/client/hud/panel/scoreboard.qc:92 +msgid "SCO^k/d" +msgstr "v/m" + +#: qcsrc/client/hud/panel/scoreboard.qc:93 +msgid "SCO^kdr" +msgstr "fmr" + +#: qcsrc/client/hud/panel/scoreboard.qc:94 +msgid "SCO^kills" +msgstr "vítimas" + +#: qcsrc/client/hud/panel/scoreboard.qc:95 +msgid "SCO^laps" +msgstr "voltas" + +#: qcsrc/client/hud/panel/scoreboard.qc:96 +msgid "SCO^lives" +msgstr "vidas" + +#: qcsrc/client/hud/panel/scoreboard.qc:97 +msgid "SCO^losses" +msgstr "derrotas" + +#: qcsrc/client/hud/panel/scoreboard.qc:98 +msgid "SCO^name" +msgstr "nome" + +#: qcsrc/client/hud/panel/scoreboard.qc:99 +msgid "SCO^sum" +msgstr "soma" + +#: qcsrc/client/hud/panel/scoreboard.qc:100 +msgid "SCO^nick" +msgstr "apelido" + +#: qcsrc/client/hud/panel/scoreboard.qc:101 +msgid "SCO^objectives" +msgstr "objetivos" + +#: qcsrc/client/hud/panel/scoreboard.qc:102 +msgid "SCO^pickups" +msgstr "coletas" + +#: qcsrc/client/hud/panel/scoreboard.qc:103 +msgid "SCO^ping" +msgstr "ping" + +#: qcsrc/client/hud/panel/scoreboard.qc:104 +msgid "SCO^pl" +msgstr "pp" + +#: qcsrc/client/hud/panel/scoreboard.qc:105 +msgid "SCO^pushes" +msgstr "empurrões" + +#: qcsrc/client/hud/panel/scoreboard.qc:106 +msgid "SCO^rank" +msgstr "classificação" + +#: qcsrc/client/hud/panel/scoreboard.qc:107 +msgid "SCO^returns" +msgstr "retornos" + +#: qcsrc/client/hud/panel/scoreboard.qc:108 +msgid "SCO^revivals" +msgstr "ressurreições" + +#: qcsrc/client/hud/panel/scoreboard.qc:109 +msgid "SCO^rounds won" +msgstr "rodadas vencidas" + +#: qcsrc/client/hud/panel/scoreboard.qc:110 +msgid "SCO^score" +msgstr "pontuação" + +#: qcsrc/client/hud/panel/scoreboard.qc:111 +msgid "SCO^suicides" +msgstr "suicídios" + +#: qcsrc/client/hud/panel/scoreboard.qc:112 +msgid "SCO^takes" +msgstr "tomadas" + +#: qcsrc/client/hud/panel/scoreboard.qc:113 +msgid "SCO^ticks" +msgstr "ticks" + +#: qcsrc/client/hud/panel/scoreboard.qc:295 +msgid "" +"You can modify the scoreboard using the ^2scoreboard_columns_set command.\n" +msgstr "" +"É possível modificar o placar usando o comando ^2scoreboard_columns_set.\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:296 +msgid "^3|---------------------------------------------------------------|\n" +msgstr "^3|---------------------------------------------------------------|\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:297 +msgid "Usage:\n" +msgstr "Uso:\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:298 +msgid "^2scoreboard_columns_set default\n" +msgstr "^2scoreboard_columns_set padrão\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:299 +msgid "^2scoreboard_columns_set ^7field1 field2 ...\n" +msgstr "^2scoreboard_columns_set ^7field1 field2...\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:300 +msgid "The following field names are recognized (case insensitive):\n" +msgstr "" +"Os seguintes nomes de campo são reconhecidos (maiúsculas e minúsculas não " +"diferem):\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:301 +msgid "You can use a ^3|^7 to start the right-aligned fields.\n" +msgstr "Você pode usar um ^3|^7 para iniciar os campos alinhados à direita.\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:304 +msgid "^3name^7 or ^3nick^7 Name of a player\n" +msgstr "^3nome^7 ou ^3apelido^7 Nome de um jogador\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:305 +msgid "^3ping^7 Ping time\n" +msgstr "^3ping^7 Tempo de ping\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:306 +msgid "^3pl^7 Packet loss\n" +msgstr "^3pp^7 Perda de pacotes\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:307 +msgid "^3elo^7 Player ELO\n" +msgstr "^3elo^7 ELO do jogador\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:308 +msgid "^3kills^7 Number of kills\n" +msgstr "^3vítimas^7 Número de vítimas\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:309 +msgid "^3deaths^7 Number of deaths\n" +msgstr "^3mortes^7 Número de mortes\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:310 +msgid "^3suicides^7 Number of suicides\n" +msgstr "^3suicídios^7 Número de suicídios\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:311 +msgid "^3frags^7 kills - suicides\n" +msgstr "^3execuções^7 vítimas - suicídios\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:312 +msgid "^3kd^7 The kill-death ratio\n" +msgstr "^3vm^7 A taxa de vítimas/mortes\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:313 +msgid "^3dmg^7 The total damage done\n" +msgstr "^3dano^7 O dano total causado\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:314 +msgid "^3dmgtaken^7 The total damage taken\n" +msgstr "^3dano recebido^7 O dano total recebido\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:315 +msgid "^3sum^7 frags - deaths\n" +msgstr "^3soma^7 execuções - mortes\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:316 +msgid "" +"^3caps^7 How often a flag (CTF) or a key (KeyHunt) was " +"captured\n" +msgstr "" +"^3capturas^7 Quão frequente uma bandeira (CTF) ou uma chave (Caça a Chaves) " +"foi capturada\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:317 +msgid "" +"^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a " +"ball (Keepaway) was picked up\n" +msgstr "" +"^3coletas^7. Quão frequente uma bandeira (CTF), uma chave (Caça a Chaves) ou " +"uma bola (Keepaway) foi coletada\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:318 +msgid "^3captime^7 Time of fastest cap (CTF)\n" +msgstr "^3tempodecaptura^7 Tempo da captura mais rápida (CTF)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:319 +msgid "^3fckills^7 Number of flag carrier kills\n" +msgstr "^3pbndvítimas^7 Número de portadores de bandeiras aniquilados\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:320 +msgid "^3returns^7 Number of flag returns\n" +msgstr "^3retornos^7 Número de bandeiras retomadas\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:321 +msgid "^3drops^7 Number of flag drops\n" +msgstr "^3quedas^7 Número de bandeiras largadas\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:322 +msgid "^3lives^7 Number of lives (LMS)\n" +msgstr "^3vidas^7 Número de vidas (LMS)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:323 +msgid "^3rank^7 Player rank\n" +msgstr "^3posição^7 Classificação do jogador\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:324 +msgid "^3pushes^7 Number of players pushed into void\n" +msgstr "^3empurrões^7 Número de jogadores empurrados para o vazio\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:325 +msgid "" +"^3destroyed^7 Number of keys destroyed by pushing them into " +"void\n" +msgstr "" +"^3destruídas^7 Número de chaves destruídas ao empurrá-las para o vazio\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:326 +msgid "^3kckills^7 Number of keys carrier kills\n" +msgstr "^3pcvítimas^7 Número de portadores de chaves aniquilados\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:327 +msgid "^3losses^7 Number of times a key was lost\n" +msgstr "^3perdas^7 Número de vezes em que uma chave foi perdida\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:328 +msgid "^3laps^7 Number of laps finished (race/cts)\n" +msgstr "^3voltas^7 Número de voltas concluídas (corrida/cts)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:329 +msgid "^3time^7 Total time raced (race/cts)\n" +msgstr "^3tempo^7 Tempo total em corridas (corrida/cts)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:330 +msgid "^3fastest^7 Time of fastest lap (race/cts)\n" +msgstr "^3mais rápida^7 Tempo da volta mais rápida (corrida/cts)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:331 +msgid "^3ticks^7 Number of ticks (DOM)\n" +msgstr "^3ticks^7 Número de ticks (DOM)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:332 +msgid "^3takes^7 Number of domination points taken (DOM)\n" +msgstr "^3tomados^7 Número de pontos de dominação tomados (DOM)\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:333 +msgid "^3bckills^7 Number of ball carrier kills\n" +msgstr "^3pblvítimas^7 Número de portadores de bolas aniquilados\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:334 +msgid "" +"^3bctime^7 Total amount of time holding the ball in " +"Keepaway\n" +msgstr "^3pbltempo^7 Tempo total segurando a bola em Keepaway\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:335 +msgid "^3score^7 Total score\n" +msgstr "^3pontuação^7 Pontuação total\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:338 +msgid "" +"Before a field you can put a + or - sign, then a comma separated list\n" +"of game types, then a slash, to make the field show up only in these\n" +"or in all but these game types. You can also specify 'all' as a\n" +"field to show all fields available for the current game mode.\n" +"\n" +msgstr "" +"Antes dos campos de digitação, você pode inserir um sinal de + ou -, e usar " +"uma lista de modos de jogo separada por vírgulas.\n" +"Insira barras para fazer com que os campos sejam mostrados somente nesses " +"modos de jogo ou em todos os modos exceto os especificados.\n" +"Você também pode especificar a palavra 'all' como um campo para mostrar " +"todos os campos disponíveis para o modo de jogo atual.\n" +"\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:343 +msgid "" +"The special game type names 'teams' and 'noteams' can be used to\n" +"include/exclude ALL teams/noteams game modes.\n" +"\n" +msgstr "" +"Os nomes especiais de modos de jogo 'teams' e 'noteams' podem ser usados\n" +"para incluir/excluir TODOS os modos de jogo de equipe/sem equipe\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:346 +msgid "Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n" +msgstr "" +"Exemplo: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:347 +msgid "" +"will display name, ping and pl aligned to the left, and the fields\n" +"right of the vertical bar aligned to the right.\n" +msgstr "" +"irá exibir nome, ping e pp alinhados à esquerda e os campos\n" +"à direita da barra vertical alinhados à direita.\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:349 +msgid "" +"'field3' will only be shown in CTF, and 'field4' will be shown in all\n" +"other gamemodes except DM.\n" +msgstr "" +"'field3' só será exibido em CTF e 'field4' será exibido em todos\n" +"os outros modos de jogo, exceto DM.\n" + +#: qcsrc/client/hud/panel/scoreboard.qc:611 +#: qcsrc/client/hud/panel/scoreboard.qc:618 +#: qcsrc/client/hud/panel/scoreboard.qc:670 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:86 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:87 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:203 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:208 +msgid "N/A" +msgstr "N/A" + +#: qcsrc/client/hud/panel/scoreboard.qc:1156 +#, c-format +msgid "Accuracy stats (average %d%%)" +msgstr "Estatísticas de precisão (média %d%%)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1295 +msgid "Map stats:" +msgstr "Estatísticas do mapa:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1325 +msgid "Monsters killed:" +msgstr "Monstros mortos:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1332 +msgid "Secrets found:" +msgstr "Segredos encontrados:" + +#: qcsrc/client/hud/panel/scoreboard.qc:1354 +msgid "Capture time rankings" +msgstr "Classificações de tempo de capturas" + +#: qcsrc/client/hud/panel/scoreboard.qc:1354 +msgid "Rankings" +msgstr "Classificações" + +#: qcsrc/client/hud/panel/scoreboard.qc:1519 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 +msgid "Scoreboard" +msgstr "Placar" + +#: qcsrc/client/hud/panel/scoreboard.qc:1584 +#, c-format +msgid "Speed award: %d%s ^7(%s^7)" +msgstr "Prêmio de velocidade: %d%s ^7(%s^7)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1588 +#, c-format +msgid "All-time fastest: %d%s ^7(%s^7)" +msgstr "O mais rápido de todos: %d%s ^7(%s^7)" + +#: qcsrc/client/hud/panel/scoreboard.qc:1604 +#, c-format +msgid "Spectators" +msgstr "Espectadores" + +#: qcsrc/client/hud/panel/scoreboard.qc:1619 +#, c-format +msgid "playing ^3%s^7 on ^2%s^7" +msgstr "jogando ^3%s^7 em ^2%s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1626 +#: qcsrc/client/hud/panel/scoreboard.qc:1631 +#, c-format +msgid " for up to ^1%1.0f minutes^7" +msgstr " por até ^1%1.0f minutos^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1635 +#: qcsrc/client/hud/panel/scoreboard.qc:1654 +msgid " or" +msgstr " ou" + +#: qcsrc/client/hud/panel/scoreboard.qc:1638 +#: qcsrc/client/hud/panel/scoreboard.qc:1645 +#, c-format +msgid " until ^3%s %s^7" +msgstr " até ^3%s %s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1639 +#: qcsrc/client/hud/panel/scoreboard.qc:1646 +#: qcsrc/client/hud/panel/scoreboard.qc:1658 +#: qcsrc/client/hud/panel/scoreboard.qc:1665 +msgid "SCO^points" +msgstr "pontos" + +#: qcsrc/client/hud/panel/scoreboard.qc:1640 +#: qcsrc/client/hud/panel/scoreboard.qc:1647 +#: qcsrc/client/hud/panel/scoreboard.qc:1659 +#: qcsrc/client/hud/panel/scoreboard.qc:1666 +msgid "SCO^is beaten" +msgstr "foi espancado" + +#: qcsrc/client/hud/panel/scoreboard.qc:1657 +#: qcsrc/client/hud/panel/scoreboard.qc:1664 +#, c-format +msgid " until a lead of ^3%s %s^7" +msgstr " até uma vantagem de ^3%s %s^7" + +#: qcsrc/client/hud/panel/scoreboard.qc:1688 +#, c-format +msgid "^1Respawning in ^3%s^1..." +msgstr "^1Ressurgindo em ^3%s^1..." + +#: qcsrc/client/hud/panel/scoreboard.qc:1698 +#, c-format +msgid "You are dead, wait ^3%s^7 before respawning" +msgstr "Você morreu. Espere ^3%s^7 antes de ressurgir" + +#: qcsrc/client/hud/panel/scoreboard.qc:1707 +#, c-format +msgid "You are dead, press ^2%s^7 to respawn" +msgstr "Você morreu. Aperte ^2%s^7 para ressurgir" + +#: qcsrc/client/hud/panel/vote.qc:24 +msgid "^1You must answer before entering hud configure mode\n" +msgstr "" +"^1Você tem que responder antes de entrar no modo de configuração da " +"interface\n" + +#: qcsrc/client/hud/panel/vote.qc:29 +msgid "^2Name ^7instead of \"^1Anonymous player^7\" in stats" +msgstr "^2Nome ^7em vez de \"^1Jogador anônimo^7\" nas estatísticas" + +#: qcsrc/client/hud/panel/vote.qc:115 +msgid "A vote has been called for:" +msgstr "Uma votação foi iniciada para:" + +#: qcsrc/client/hud/panel/vote.qc:117 +msgid "Allow servers to store and display your name?" +msgstr "Permitir que servidores armazenem e mostrem o seu nome?" + +#: qcsrc/client/hud/panel/vote.qc:121 +msgid "^1Configure the HUD" +msgstr "^1Configurar a interface" + +#: qcsrc/client/hud/panel/vote.qc:125 qcsrc/menu/xonotic/dialog_firstrun.qc:82 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:18 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:18 +#: qcsrc/menu/xonotic/dialog_quit.qc:14 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:26 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:16 +#: qcsrc/menu/xonotic/dialog_uid2name.qc:15 +msgid "Yes" +msgstr "Sim" + +#: qcsrc/client/hud/panel/vote.qc:127 qcsrc/menu/xonotic/dialog_firstrun.qc:83 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:21 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:21 +#: qcsrc/menu/xonotic/dialog_quit.qc:16 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:29 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:17 +#: qcsrc/menu/xonotic/dialog_uid2name.qc:17 +msgid "No" +msgstr "Não" + +#: qcsrc/client/hud/panel/weapons.qc:530 +msgid "Out of ammo" +msgstr "Sem munição" + +#: qcsrc/client/hud/panel/weapons.qc:534 +msgid "Don't have" +msgstr "Não tem" + +#: qcsrc/client/hud/panel/weapons.qc:538 +msgid "Unavailable" +msgstr "Indisponível" + +#: qcsrc/client/main.qc:1014 +msgid " qu/s" +msgstr "qu/s" + +#: qcsrc/client/main.qc:1016 +msgid " m/s" +msgstr "m/s" + +#: qcsrc/client/main.qc:1018 +msgid " km/h" +msgstr "km/h" + +#: qcsrc/client/main.qc:1020 +msgid " mph" +msgstr "mph" + +#: qcsrc/client/main.qc:1022 +msgid " knots" +msgstr "nós" + +#: qcsrc/client/main.qc:1264 +#, c-format +msgid "%s (not bound)" +msgstr "%s (não tem atalho definido)" + +#: qcsrc/client/mapvoting.qc:49 +msgid " (1 vote)" +msgstr "(1 voto)" + +#: qcsrc/client/mapvoting.qc:51 +#, c-format +msgid " (%d votes)" +msgstr "(%d votos)" + +#: qcsrc/client/mapvoting.qc:271 +msgid "Don't care" +msgstr "Não importa" + +#: qcsrc/client/mapvoting.qc:365 +msgid "Decide the gametype" +msgstr "Decidir o modo de jogo" + +#: qcsrc/client/mapvoting.qc:365 +msgid "Vote for a map" +msgstr "Vote em um mapa" + +#: qcsrc/client/mapvoting.qc:382 +#, c-format +msgid "%d seconds left" +msgstr "Faltam %d segundos" + +#: qcsrc/client/mapvoting.qc:497 +msgid "" +"mv_mapdownload: ^3You're not supposed to use this command on your own!\n" +msgstr "mv_mapdownload: ^3Você não pode usar esse comando para si próprio!\n" + +#: qcsrc/client/mapvoting.qc:507 +msgid "^1Error:^7 Couldn't find pak index.\n" +msgstr "^1Erro:^7 Não foi possível encontrar o índice do pak.\n" + +#: qcsrc/client/mapvoting.qc:516 +msgid "Requesting preview...\n" +msgstr "Solicitando previsão...\n" + +#: qcsrc/client/miscfunctions.qc:109 +msgid "Trying to remove a team which is not in the teamlist!" +msgstr "" +"Você está tentando remover uma equipe que não está na lista de equipes!" + +#: qcsrc/client/view.qc:1380 +msgid "Nade timer" +msgstr "Temporizador de granada" + +#: qcsrc/client/view.qc:1385 +msgid "Capture progress" +msgstr "Progresso de captura" + +#: qcsrc/client/view.qc:1390 +msgid "Revival progress" +msgstr "Progresso de renascimento" + +#: qcsrc/common/command/generic.qc:157 +msgid "error creating curl handle\n" +msgstr "erro ao criar curl handle\n" + +#: qcsrc/common/command/generic.qc:403 +msgid "Notification restart command only works with cl_cmd and sv_cmd.\n" +msgstr "" +"Comando de reinício de notificação funciona apenas com cl_cmd e sv_cmd.\n" + +#: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 +msgid "Ball Stealer" +msgstr "Ladrão de Bolas" + +#: qcsrc/common/items/item/armor.qh:111 +msgid "Big armor" +msgstr "Armadura grande" + +#: qcsrc/common/items/item/armor.qh:147 +msgid "Mega armor" +msgstr "Mega armadura" + +#: qcsrc/common/items/item/health.qh:111 +msgid "Big health" +msgstr "Saúde grande" + +#: qcsrc/common/items/item/health.qh:147 +msgid "Mega health" +msgstr "Mega saúde" + +#: qcsrc/common/items/item/jetpack.qh:35 +msgid "Jet Pack" +msgstr "Mochila a Jato" + +#: qcsrc/common/items/item/jetpack.qh:82 +msgid "Fuel regen" +msgstr "Regeneração de combustível" + +#: qcsrc/common/items/item/powerup.qh:44 +msgid "Strength" +msgstr "Força" + +#: qcsrc/common/items/item/powerup.qh:76 +msgid "Shield" +msgstr "Escudo" + +#: qcsrc/common/mapinfo.qc:639 +#, no-c-format +msgid "@!#%'n Tuba Throwing" +msgstr "@!#%'n Tuba Throwing" + +#: qcsrc/common/mapinfo.qh:99 +msgid "Deathmatch" +msgstr "Mata-mata" + +#: qcsrc/common/mapinfo.qh:99 +msgid "Score as many frags as you can" +msgstr "Consiga o máximo de execuções que puder" + +#: qcsrc/common/mapinfo.qh:111 +msgid "Last Man Standing" +msgstr "Último Homem de Pé" + +#: qcsrc/common/mapinfo.qh:111 +msgid "Survive and kill until the enemies have no lives left" +msgstr "Sobreviva e mate até que os inimigos não tenham vidas sobrando" + +#: qcsrc/common/mapinfo.qh:126 +msgid "Race" +msgstr "Corrida" + +#: qcsrc/common/mapinfo.qh:126 +msgid "Race against other players to the finish line" +msgstr "Corra contra outros jogadores até a linha de chegada" + +#: qcsrc/common/mapinfo.qh:160 +msgid "Race CTS" +msgstr "Corrida CTS" + +#: qcsrc/common/mapinfo.qh:160 +msgid "Race for fastest time." +msgstr "Corra pelo melhor tempo." + +#: qcsrc/common/mapinfo.qh:184 +msgid "Help your team score the most frags against the enemy team" +msgstr "Ajude sua equipe a conseguir mais execuções do que a equipe inimiga" + +#: qcsrc/common/mapinfo.qh:184 +msgid "Team Deathmatch" +msgstr "Mata-mata por Equipe" + +#: qcsrc/common/mapinfo.qh:220 +msgid "Capture the Flag" +msgstr "Capture a Bandeira" + +#: qcsrc/common/mapinfo.qh:220 +msgid "" +"Find and bring the enemy flag to your base to capture it, defend your base " +"from the other team" +msgstr "" +"Encontre e retorne a bandeira inimiga à sua base para capturá-la e defenda " +"sua base da equipe oponente" + +#: qcsrc/common/mapinfo.qh:249 +msgid "Clan Arena" +msgstr "Clã Arena" + +#: qcsrc/common/mapinfo.qh:249 +msgid "Kill all enemy teammates to win the round" +msgstr "Mate todos os inimigos para vencer a rodada" + +#: qcsrc/common/mapinfo.qh:287 +msgid "Capture and defend all the control points to win" +msgstr "Capture e defenda todos os pontos de controle para vencer" + +#: qcsrc/common/mapinfo.qh:287 +msgid "Domination" +msgstr "Dominação" + +#: qcsrc/common/mapinfo.qh:319 +msgid "Gather all the keys to win the round" +msgstr "Colete todas as chaves para vencer a rodada" + +#: qcsrc/common/mapinfo.qh:319 +msgid "Key Hunt" +msgstr "Caça a Chaves" + +#: qcsrc/common/mapinfo.qh:353 +msgid "Assault" +msgstr "Assalto" + +#: qcsrc/common/mapinfo.qh:353 +msgid "" +"Destroy obstacles to find and destroy the enemy power core before time runs " +"out" +msgstr "" +"Destrua obstáculos para encontrar e destruir o núcleo de poder inimigo antes " +"que o tempo acabe" + +#: qcsrc/common/mapinfo.qh:371 +msgid "Capture control points to reach and destroy the enemy generator" +msgstr "Capture pontos de controle para alcançar e destruir o gerador inimigo" + +#: qcsrc/common/mapinfo.qh:371 +msgid "Onslaught" +msgstr "Massacre" + +#: qcsrc/common/mapinfo.qh:387 +msgid "Nexball" +msgstr "Bola Nex" + +#: qcsrc/common/mapinfo.qh:387 +msgid "Shoot and kick the ball into the enemies goal, keep your goal clean" +msgstr "Atire e chute a bola no gol inimigo e mantenha seu gol limpo" + +#: qcsrc/common/mapinfo.qh:408 +msgid "Freeze Tag" +msgstr "Congela" + +#: qcsrc/common/mapinfo.qh:408 +msgid "" +"Kill enemies to freeze them, stand next to frozen teammates to revive them; " +"freeze all enemies to win" +msgstr "" +"Mate inimigos para congelá-los. Fique perto de colegas para descongelá-los; " +"congele todos os inimigos para vencer" + +#: qcsrc/common/mapinfo.qh:446 +msgid "Hold the ball to get points for kills" +msgstr "Segure a bola para ganhar pontos por cada jogador aniquilado" + +#: qcsrc/common/mapinfo.qh:446 +msgid "Keepaway" +msgstr "Keepaway" + +#: qcsrc/common/mapinfo.qh:461 +msgid "Invasion" +msgstr "Invasão" + +#: qcsrc/common/mapinfo.qh:461 +msgid "Survive against waves of monsters" +msgstr "Sobreviva contra ondas de monstros" + +#: qcsrc/common/minigames/cl_minigames.qc:383 +msgid "It's your turn" +msgstr "É a sua vez" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:331 +#: qcsrc/menu/xonotic/dialog_quit.qh:6 +msgid "Quit" +msgstr "Sair" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:336 +msgid "Invite" +msgstr "Convidar" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:378 +msgid "Current Game" +msgstr "Jogo Atual" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:403 +msgid "Exit Menu" +msgstr "Sair do Menu" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:415 +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:16 +msgid "Create" +msgstr "Criar" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:418 +msgid "Join" +msgstr "Entrar" + +#: qcsrc/common/minigames/cl_minigames_hud.qc:489 +msgid "Minigames" +msgstr "Minijogos" + +#: qcsrc/common/minigames/minigame/bd.qc:1168 +msgid "Better luck next time!" +msgstr "Mais sorte da próxima vez!" + +#: qcsrc/common/minigames/minigame/bd.qc:1172 +msgid "Tubular! Press \"Next Level\" to continue!" +msgstr "Tubular! Clique em \"Próximo Mapa\" para continuar!" + +#: qcsrc/common/minigames/minigame/bd.qc:1174 +msgid "Wicked! Press \"Next Level\" to continue!" +msgstr "Enfeitiçado! Clique em \"Próximo Mapa\" para continuar!" + +#: qcsrc/common/minigames/minigame/bd.qc:1177 +msgid "Press the space bar to change your currently selected tile" +msgstr "" +"Aperte a barra de espaço para alterar a sua imagem de equipe atualmente " +"selecionada" + +#: qcsrc/common/minigames/minigame/bd.qc:1180 +msgid "Push the boulders onto the targets" +msgstr "Empurre os pedregulhos em direção aos alvos" + +#: qcsrc/common/minigames/minigame/bd.qc:1404 +msgid "Next Level" +msgstr "Próximo Mapa" + +#: qcsrc/common/minigames/minigame/bd.qc:1405 +msgid "Restart" +msgstr "Reiniciar" + +#: qcsrc/common/minigames/minigame/bd.qc:1406 +msgid "Editor" +msgstr "Editor" + +#: qcsrc/common/minigames/minigame/bd.qc:1407 +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:37 +msgid "Save" +msgstr "Salvar" + +#: qcsrc/common/minigames/minigame/c4.qc:373 +#: qcsrc/common/minigames/minigame/pp.qc:438 +#: qcsrc/common/minigames/minigame/ttt.qc:319 +msgid "Draw" +msgstr "Empate" + +#: qcsrc/common/minigames/minigame/c4.qc:378 +#: qcsrc/common/minigames/minigame/nmm.qc:601 +msgid "You lost the game!" +msgstr "Você perdeu o jogo!" + +#: qcsrc/common/minigames/minigame/c4.qc:379 +#: qcsrc/common/minigames/minigame/nmm.qc:602 +msgid "You win!" +msgstr "Você venceu!" + +#: qcsrc/common/minigames/minigame/c4.qc:383 +#: qcsrc/common/minigames/minigame/nmm.qc:606 +#: qcsrc/common/minigames/minigame/pp.qc:455 +#: qcsrc/common/minigames/minigame/ttt.qc:336 +msgid "Wait for your opponent to make their move" +msgstr "Espere o seu oponente terminar a vez dele" + +#: qcsrc/common/minigames/minigame/c4.qc:386 +#: qcsrc/common/minigames/minigame/nmm.qc:608 +#: qcsrc/common/minigames/minigame/pp.qc:458 +#: qcsrc/common/minigames/minigame/ttt.qc:339 +msgid "Click on the game board to place your piece" +msgstr "Clique no tabuleiro de jogo para posicionar sua peça" + +#: qcsrc/common/minigames/minigame/nmm.qc:610 +msgid "" +"You can select one of your pieces to move it in one of the surrounding places" +msgstr "" +"Você pode selecionar uma de suas peças para movê-la para um dos lugares ao " +"redor" + +#: qcsrc/common/minigames/minigame/nmm.qc:612 +msgid "You can select one of your pieces to move it anywhere on the board" +msgstr "" +"Você pode selecionar uma de suas peças para movê-la para qualquer lugar no " +"tabuleiro" + +#: qcsrc/common/minigames/minigame/nmm.qc:614 +msgid "You can take one of the opponent's pieces" +msgstr "Você pode tomar uma das peças do seu oponente" + +#: qcsrc/common/minigames/minigame/pong.qc:570 +#: qcsrc/common/minigames/minigame/ttt.qc:299 +msgid "AI" +msgstr "IA" + +#: qcsrc/common/minigames/minigame/pong.qc:587 +msgid "Press ^1Start Match^7 to start the match with the current players" +msgstr "" +"Aperte ^1Iniciar Partida^7 para iniciar a partida com os jogadores atuais" + +#: qcsrc/common/minigames/minigame/pong.qc:651 +msgid "Start Match" +msgstr "Iniciar Partida" + +#: qcsrc/common/minigames/minigame/pong.qc:652 +msgid "Add AI player" +msgstr "Adicionar bot" + +#: qcsrc/common/minigames/minigame/pong.qc:653 +msgid "Remove AI player" +msgstr "Remover bot" + +#: qcsrc/common/minigames/minigame/pp.qc:443 +#: qcsrc/common/minigames/minigame/ttt.qc:324 +msgid "" +"You lost the game!\n" +"Select \"^1Next Match^7\" on the menu for a rematch!" +msgstr "" +"Você perdeu o jogo!\n" +"Selecione \"^1Próxima Partida^7\" no menu para uma revanche!" + +#: qcsrc/common/minigames/minigame/pp.qc:444 +#: qcsrc/common/minigames/minigame/ttt.qc:325 +msgid "" +"You win!\n" +"Select \"^1Next Match^7\" on the menu to start a new match!" +msgstr "" +"Você venceu!\n" +"Selecione \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" + +#: qcsrc/common/minigames/minigame/pp.qc:450 +#: qcsrc/common/minigames/minigame/ttt.qc:331 +msgid "Select \"^1Next Match^7\" on the menu to start a new match!" +msgstr "" +"Selecione \"^1Próxima Partida^7\" no menu para iniciar uma nova partida!" + +#: qcsrc/common/minigames/minigame/pp.qc:451 +#: qcsrc/common/minigames/minigame/ttt.qc:332 +msgid "Wait for your opponent to confirm the rematch" +msgstr "Espere o seu oponente confirmar a revanche" + +#: qcsrc/common/minigames/minigame/pp.qc:582 +#: qcsrc/common/minigames/minigame/ttt.qc:665 +msgid "Next Match" +msgstr "Próxima Partida" + +#: qcsrc/common/minigames/minigame/ps.qc:478 +#, c-format +msgid "Pieces left: %s" +msgstr "Pedaços restantes: %s" + +#: qcsrc/common/minigames/minigame/ps.qc:488 +msgid "No more valid moves" +msgstr "Não há mais movimentos válidos" + +#: qcsrc/common/minigames/minigame/ps.qc:491 +msgid "Well done, you win!" +msgstr "Bom trabalho, você venceu!" + +#: qcsrc/common/minigames/minigame/ps.qc:494 +msgid "Jump a piece over another to capture it" +msgstr "Faça uma peça saltar sobre outra para capturá-la" + +#: qcsrc/common/minigames/minigame/ttt.qc:666 +msgid "Single Player" +msgstr "Um Jogador" + +#: qcsrc/common/monsters/monster/mage.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:18 +msgid "Mage" +msgstr "Mago" + +#: qcsrc/common/monsters/monster/mage.qh:29 +msgid "Mage spike" +msgstr "Prego de mago" + +#: qcsrc/common/monsters/monster/shambler.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:17 +msgid "Shambler" +msgstr "Shambler" + +#: qcsrc/common/monsters/monster/spider.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:16 +msgid "Spider" +msgstr "Aranha" + +#: qcsrc/common/monsters/monster/spider.qh:28 +msgid "Spider attack" +msgstr "Ataque da Aranha" + +#: qcsrc/common/monsters/monster/wyvern.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:19 +msgid "Wyvern" +msgstr "Wyvern" + +#: qcsrc/common/monsters/monster/wyvern.qh:28 +msgid "Wyvern attack" +msgstr "Ataque do Wyvern" + +#: qcsrc/common/monsters/monster/zombie.qh:17 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:15 +msgid "Zombie" +msgstr "Zumbi" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:15 +msgid "Ammo" +msgstr "Munição" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:24 +msgid "Resistance" +msgstr "Resistência" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:33 +#: qcsrc/common/mutators/mutator/instagib/items.qh:94 +msgid "Speed" +msgstr "Velocidade" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:43 +msgid "Medic" +msgstr "Médico" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:54 +msgid "Bash" +msgstr "Pancada" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:62 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:85 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:177 +msgid "Vampire" +msgstr "Vampiro" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:70 +msgid "Disability" +msgstr "Incapacidade" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:78 +msgid "Vengeance" +msgstr "Vingança" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:86 +msgid "Jump" +msgstr "Saltar" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:95 +msgid "Invisible" +msgstr "Invisível" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:104 +msgid "Inferno" +msgstr "Inferno" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:112 +msgid "Swapper" +msgstr "Trocador" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:120 +msgid "Magnet" +msgstr "Ímã" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:128 +msgid "Luck" +msgstr "Sorte" + +#: qcsrc/common/mutators/mutator/buffs/all.inc:136 +msgid "Flight" +msgstr "Voo" + +#: qcsrc/common/mutators/mutator/buffs/buffs.qh:7 +msgid "Buff" +msgstr "Bônus" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:8 +msgid "Damage text" +msgstr "Texto de dano" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:18 +msgid "Draw damage numbers" +msgstr "Exibir números de dano" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 +msgid "Font size minimum:" +msgstr "Tamanho da fonte mínimo:" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 +msgid "Font size maximum:" +msgstr "Tamanho da fonte máximo:" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 +msgid "Accumulate range:" +msgstr "Alcance de acúmulo:" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:35 +msgid "Lifetime:" +msgstr "Tempo de vida:" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:40 +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:50 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:55 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:102 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:60 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:109 +#: qcsrc/menu/xonotic/util.qc:775 +msgid "Color:" +msgstr "Cor:" + +#: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 +msgid "Draw damage numbers for friendly fire" +msgstr "Exibir números de dano para fogo amigo" + +#: qcsrc/common/mutators/mutator/instagib/items.qh:56 +msgid "Extra life" +msgstr "Vida extra" + +#: qcsrc/common/mutators/mutator/instagib/items.qh:75 +msgid "Invisibility" +msgstr "Invisibilidade" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:18 +msgid "Napalm grenade" +msgstr "Granada de napalm" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:26 +msgid "Ice grenade" +msgstr "Granada de gelo" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:34 +msgid "Translocate grenade" +msgstr "Granada de deslocamento" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:42 +msgid "Spawn grenade" +msgstr "Granada de fragmentação" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:50 +msgid "Heal grenade" +msgstr "Granada de cura" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:58 +msgid "Monster grenade" +msgstr "Granada monstro" + +#: qcsrc/common/mutators/mutator/nades/nades.inc:66 +msgid "Entrap grenade" +msgstr "Granada de armadilha" + +#: qcsrc/common/mutators/mutator/nades/nades.qh:32 +msgid "Grenade" +msgstr "Granada" + +#: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 +msgid "Heavy Machine Gun" +msgstr "Metralhadora Pesada" + +#: qcsrc/common/mutators/mutator/overkill/rpc.qh:17 +msgid "Rocket Propelled Chainsaw" +msgstr "Rocket Propelled Chainsaw" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:3 +msgid "Waypoint" +msgstr "Ponto de passagem" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:4 +msgid "Help me!" +msgstr "Preciso de ajuda!" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:5 +msgid "Here" +msgstr "Aqui" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:6 +msgid "DANGER" +msgstr "PERIGO" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:8 +msgid "Frozen!" +msgstr "Congelado!" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:10 +msgid "Item" +msgstr "Item" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:12 +msgid "Checkpoint" +msgstr "Ponto de checagem" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:13 +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 +msgid "Finish" +msgstr "Final" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:14 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:15 +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 +msgid "Start" +msgstr "Início" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:17 +msgid "Defend" +msgstr "Defender" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:18 +msgid "Destroy" +msgstr "Destruir" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:19 +msgid "Push" +msgstr "Empurrar" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:21 +msgid "Flag carrier" +msgstr "Portador de bandeiras" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:22 +msgid "Enemy carrier" +msgstr "Portador inimigo" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:23 +msgid "Dropped flag" +msgstr "Bandeira largada" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:24 +msgid "White base" +msgstr "Base branca" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:25 +msgid "Red base" +msgstr "Base vermelha" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:26 +msgid "Blue base" +msgstr "Base azul" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:27 +msgid "Yellow base" +msgstr "Base amarela" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:28 +msgid "Pink base" +msgstr "Base rosa" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:29 +msgid "Return flag here" +msgstr "Traga a bandeira para cá" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:31 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:32 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:33 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:34 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:35 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:51 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:52 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:53 +msgid "Control point" +msgstr "Ponto de controle" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:37 +msgid "Dropped key" +msgstr "Chave largada" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:38 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:40 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:41 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:42 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:43 +msgid "Key carrier" +msgstr "Portador de chaves" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:39 +msgid "Run here" +msgstr "Corra aqui" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:45 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:48 +msgid "Ball" +msgstr "Bola" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:46 +msgid "Ball carrier" +msgstr "Portador da bola" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:49 +msgid "Goal" +msgstr "Gol" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:54 +#: qcsrc/common/mutators/mutator/waypoints/all.inc:55 +msgid "Generator" +msgstr "Gerador" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:57 +msgid "Weapon" +msgstr "Arma" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:59 +msgid "Monster" +msgstr "Monstro" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:61 +msgid "Vehicle" +msgstr "Veículo" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:62 +msgid "Intruder!" +msgstr "Intruso!" + +#: qcsrc/common/mutators/mutator/waypoints/all.inc:64 +msgid "Tagged" +msgstr "Marcado" + +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:651 +#: qcsrc/common/turrets/cl_turrets.qc:120 +msgid "Spam" +msgstr "Spam" + +#: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:655 +#, c-format +msgid "%s needing help!" +msgstr "%s precisando de ajuda!" + +#: qcsrc/common/net_notice.qc:87 +msgid "^1Server notices:" +msgstr "^1Avisos do servidor:" + +#: qcsrc/common/notifications/all.inc:239 +msgid "^F4NOTE: ^BGSpectator chat is not sent to players during the match" +msgstr "" +"^F4NOTA: ^BGMensagens no bate-papo de espectador não serão enviadas aos " +"jogadores durante a partida" + +#: qcsrc/common/notifications/all.inc:241 +#, c-format +msgid "^BG%s^BG captured the ^TC^TT^BG flag" +msgstr "^BG%s^BG capturou a bandeira ^TC^TT^BG" + +#: qcsrc/common/notifications/all.inc:242 +#, c-format +msgid "" +"^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG" +"%s^BG's previous record of ^F2%s^BG seconds" +msgstr "" +"^BG%s^BG capturou a bandeira ^TC^TT^BG em ^F1%s^BG segundos, quebrando o " +"recorde anterior de ^BG%s^BG de ^F2%s^BG segundos" + +#: qcsrc/common/notifications/all.inc:243 +#, c-format +msgid "^BG%s^BG captured the flag" +msgstr "^BG%s^BG capturou a bandeira" + +#: qcsrc/common/notifications/all.inc:244 +#, c-format +msgid "^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds" +msgstr "^BG%s^BG capturou a bandeira ^TC^TT^BG em ^F1%s^BG segundos" + +#: qcsrc/common/notifications/all.inc:245 +#, c-format +msgid "" +"^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break " +"^BG%s^BG's previous record of ^F1%s^BG seconds" +msgstr "" +"^BG%s^BG capturou a bandeira ^TC^TT^BG em ^F2%s^BG segundos, não quebrando o " +"record anterior de ^BG%s^BG de ^F1%s^BG segundos" + +#: qcsrc/common/notifications/all.inc:246 +msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" +msgstr "^BGA bandeira ^TC^TT^BG foi retornada à base pelo seu dono" + +#: qcsrc/common/notifications/all.inc:247 +msgid "^BGThe flag was returned by its owner" +msgstr "^BGA bandeira ^TC^TT^BG foi retornada pelo seu dono" + +#: qcsrc/common/notifications/all.inc:248 +msgid "^BGThe ^TC^TT^BG flag was destroyed and returned to base" +msgstr "^BGA bandeira ^TC^TT^BG foi destruída e retornada à base" + +#: qcsrc/common/notifications/all.inc:249 +msgid "^BGThe flag was destroyed and returned to base" +msgstr "^BGA bandeira foi destruída e retornada à base" + +#: qcsrc/common/notifications/all.inc:250 +msgid "^BGThe ^TC^TT^BG flag was dropped in the base and returned itself" +msgstr "^BGA bandeira ^TC^TT^BG caiu na base e retornou sozinha" + +#: qcsrc/common/notifications/all.inc:251 +msgid "^BGThe flag was dropped in the base and returned itself" +msgstr "^BGA bandeira caiu na base e retornou sozinha" + +#: qcsrc/common/notifications/all.inc:252 +msgid "" +"^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to " +"base" +msgstr "" +"^BGA bandeira ^TC^TT^BG caiu em algum lugar inacessível e retornou à base" + +#: qcsrc/common/notifications/all.inc:253 +msgid "^BGThe flag fell somewhere it couldn't be reached and returned to base" +msgstr "^BGA bandeira caiu em algum lugar inacessível e retornou à base" + +#: qcsrc/common/notifications/all.inc:254 +#, c-format +msgid "" +"^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned " +"itself" +msgstr "" +"^BGA bandeira ^TC^TT^BG ficou impaciente depois de ^F1%.2f^BG segundos e " +"retornou sozinha" + +#: qcsrc/common/notifications/all.inc:255 +#, c-format +msgid "" +"^BGThe flag became impatient after ^F1%.2f^BG seconds and returned itself" +msgstr "" +"^BGA bandeira ficou impaciente depois de ^F1%.2f^BG segundos e retornou " +"sozinha" + +#: qcsrc/common/notifications/all.inc:256 +msgid "^BGThe ^TC^TT^BG flag has returned to the base" +msgstr "^BGA bandeira ^TC^TT^BG retornou à base" + +#: qcsrc/common/notifications/all.inc:257 +msgid "^BGThe flag has returned to the base" +msgstr "^BGA bandeira retornou à base" + +#: qcsrc/common/notifications/all.inc:258 +#, c-format +msgid "^BG%s^BG lost the ^TC^TT^BG flag" +msgstr "^BG%s^BG perdeu a bandeira ^TC^TT^BG" + +#: qcsrc/common/notifications/all.inc:259 +#, c-format +msgid "^BG%s^BG lost the flag" +msgstr "^BG%s^BG perdeu a bandeira" + +#: qcsrc/common/notifications/all.inc:260 +#, c-format +msgid "^BG%s^BG got the ^TC^TT^BG flag" +msgstr "^BG%s^BG pegou a bandeira ^TC^TT^BG" + +#: qcsrc/common/notifications/all.inc:261 +#, c-format +msgid "^BG%s^BG got the flag" +msgstr "^BG%s^BG pegou a bandeira" + +#: qcsrc/common/notifications/all.inc:262 +#: qcsrc/common/notifications/all.inc:263 +#, c-format +msgid "^BG%s^BG returned the ^TC^TT^BG flag" +msgstr "^BG%s^BG retornou a bandeira ^TC^TT^BG" + +#: qcsrc/common/notifications/all.inc:265 +#: qcsrc/common/notifications/all.inc:553 +#, c-format +msgid "^F2Throwing coin... Result: %s^F2!" +msgstr "^F2Atirando moeda... Resultado: %s^F2!" + +#: qcsrc/common/notifications/all.inc:267 +msgid "^BGYou don't have any fuel for the ^F1Jetpack" +msgstr "^BGVocê está sem combustível para a ^F1Mochila a Jato" + +#: qcsrc/common/notifications/all.inc:269 +msgid "^F2You lack a UID, superspec options will not be saved/restored" +msgstr "" +"^F2Você não tem um UID, opções de sperspec não serão salvas/restauradas" + +#: qcsrc/common/notifications/all.inc:271 +msgid "^F1Round already started, you will join the game in the next round" +msgstr "^F1A rodada já começou, você entrará no jogo na próxima rodada" + +#: qcsrc/common/notifications/all.inc:272 +msgid "^F2You will spectate in the next round" +msgstr "^F2Você ficará de espectador na próxima rodada" + +#: qcsrc/common/notifications/all.inc:274 +#, c-format +msgid "^BG%s%s^K1 was killed by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" +msgstr "^BG%s%s^K1 foi morto pelo bônus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" + +#: qcsrc/common/notifications/all.inc:274 +#, c-format +msgid "^BG%s%s^K1 was scored against by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" +msgstr "" +"^BG%s%s^K1 foi pontuado contra pelo bônus de ^BG%s^K1 de ^BG%s^K1 ^K1%s%s" + +#: qcsrc/common/notifications/all.inc:275 +#, c-format +msgid "^BG%s%s^K1 was unfairly eliminated by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi morto injustamente por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:276 +#, c-format +msgid "^BG%s%s^K1 was drowned by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi afogado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:277 +#, c-format +msgid "^BG%s%s^K1 was grounded by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi castigado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:278 +#, c-format +msgid "^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s" +msgstr "" +"^BG%s%s^K1 se sentiu um pouco quente por causa do fogo de ^BG%s^K1^K1%s%s" + +#: qcsrc/common/notifications/all.inc:278 +#, c-format +msgid "^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi cruelmente queimado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:279 +#, c-format +msgid "^BG%s%s^K1 was cooked by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi cozinhado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:280 +#, c-format +msgid "^BG%s%s^K1 was pushed in front of a monster by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi empurrado em frente de um monstro por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:281 +#, c-format +msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Nade%s%s" +msgstr "^BG%s%s^K1 foi explodido pela Granada de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:282 +#, c-format +msgid "^BG%s%s^K1 got too close to a napalm explosion%s%s" +msgstr "^BG%s%s^K1 se aproximou demais de uma explosão de napalm%s%s" + +#: qcsrc/common/notifications/all.inc:282 +#, c-format +msgid "^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s" +msgstr "" +"^BG%s%s^K1 foi queimado até a morte pela Granada de Napalm de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:283 +#, c-format +msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s" +msgstr "^BG%s%s^K1 foi explodido pela Granada de Gelo de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:284 +#, c-format +msgid "^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s" +msgstr "" +"^BG%s%s^K1 foi congelado até a morte pela Granada de Gelo de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:285 +#, c-format +msgid "^BG%s%s^K1 has not been healed by ^BG%s^K1's Healing Nade%s%s" +msgstr "^BG%s%s^K1 não foi curado pela Granada de Cura de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:286 +#, c-format +msgid "^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi lançado para o espaço por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:287 +#, c-format +msgid "^BG%s%s^K1 was slimed by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi dissolvido por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:288 +#, c-format +msgid "^BG%s%s^K1 was preserved by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi preservado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:289 +#, c-format +msgid "^BG%s%s^K1 tried to occupy ^BG%s^K1's teleport destination space%s%s" +msgstr "^BG%s%s^K1 tentou ocupar o espaço do teletransporte de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:289 +#, c-format +msgid "^BG%s%s^K1 was telefragged by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 levou um telefrag de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:290 +#, c-format +msgid "^BG%s%s^K1 died in an accident with ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 morreu em um acidente com ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:291 +#, c-format +msgid "" +"^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Bumblebee exploded%s%s" +msgstr "^BG%s%s^K1 foi pego pela explosão de Bumblebee de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:292 +#, c-format +msgid "^BG%s%s^K1 saw the pretty lights of ^BG%s^K1's Bumblebee gun%s%s" +msgstr "^BG%s%s^K1 viu as lindas luzes da arma do Bumblebee de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:293 +#, c-format +msgid "^BG%s%s^K1 was crushed by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi esmagado por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:294 +#, c-format +msgid "^BG%s%s^K1 was cluster bombed by ^BG%s^K1's Raptor%s%s" +msgstr "^BG%s%s^K1 foi bombardeado pelo Raptor de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:295 +#, c-format +msgid "^BG%s%s^K1 couldn't resist ^BG%s^K1's purple blobs%s%s" +msgstr "^BG%s%s^K1 não resistiu às bolhas roxas de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:296 +#, c-format +msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Raptor exploded%s%s" +msgstr "^BG%s%s^K1 foi pego pela explosão do Raptor de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:297 +#, c-format +msgid "" +"^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Spiderbot exploded%s%s" +msgstr "^BG%s%s^K1 foi pego pela explosão do Spiderbot de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:298 +#, c-format +msgid "^BG%s%s^K1 got shredded by ^BG%s^K1's Spiderbot%s%s" +msgstr "^BG%s%s^K1 foi picado pelo Spiderbot de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:299 +#, c-format +msgid "^BG%s%s^K1 was blasted to bits by ^BG%s^K1's Spiderbot%s%s" +msgstr "^BG%s%s^K1 foi explodido em pedacinhos pelo Spiderbot de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:300 +#, c-format +msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s" +msgstr "^BG%s%s^K1 foi pego pela explosão do Racer de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:301 +#, c-format +msgid "^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s" +msgstr "^BG%s%s^K1 foi aparafusado pelo Racer de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:302 +#, c-format +msgid "^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s" +msgstr "^BG%s%s^K1 não conseguiu escapar do Racer de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:303 +#, c-format +msgid "^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi jogado em mundo de dor por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:305 +#, c-format +msgid "^BG%s^K1 was moved into the %s%s" +msgstr "^BG%s^K1 foi movido para o %s%s" + +#: qcsrc/common/notifications/all.inc:306 +#, c-format +msgid "^BG%s^K1 became enemies with the Lord of Teamplay%s%s" +msgstr "^BG%s^K1 tornou-se inimigo do Senhor do Trabalho em Equipe%s%s" + +#: qcsrc/common/notifications/all.inc:307 +#, c-format +msgid "^BG%s^K1 thought they found a nice camping ground%s%s" +msgstr "" +"^BG%s^K1 acharam que tinham encontrado um ótimo lugar para camperar%s%s" + +#: qcsrc/common/notifications/all.inc:308 +#, c-format +msgid "^BG%s^K1 unfairly eliminated themself%s%s" +msgstr "^BG%s^K1 se eliminou injustamente%s%s" + +#: qcsrc/common/notifications/all.inc:310 +#, c-format +msgid "^BG%s^K1 couldn't catch their breath%s%s" +msgstr "^BG%s^K1 ficou sem fôlego%s%s" + +#: qcsrc/common/notifications/all.inc:310 +#, c-format +msgid "^BG%s^K1 was in the water for too long%s%s" +msgstr "^BG%s^K1 ficou na água por muito tempo%s%s" + +#: qcsrc/common/notifications/all.inc:311 +#, c-format +msgid "^BG%s^K1 hit the ground with a bit too much force%s%s" +msgstr "^BG%s^K1 caiu no chão com muita força%s%s" + +#: qcsrc/common/notifications/all.inc:311 +#, c-format +msgid "^BG%s^K1 hit the ground with a crunch%s%s" +msgstr "^BG%s^K1 caiu no chão rigorosamente%s%s" + +#: qcsrc/common/notifications/all.inc:312 +#, c-format +msgid "^BG%s^K1 became a bit too crispy%s%s" +msgstr "^BG%s^K1 ficou um pouco crocante%s%s" + +#: qcsrc/common/notifications/all.inc:312 +#, c-format +msgid "^BG%s^K1 felt a little hot%s%s" +msgstr "^BG%s^K1 se sentiu um pouco quente%s%s" + +#: qcsrc/common/notifications/all.inc:313 +#, c-format +msgid "^BG%s^K1 died%s%s" +msgstr "^BG%s^K1 morreu%s%s" + +#: qcsrc/common/notifications/all.inc:314 +#, c-format +msgid "^BG%s^K1 found a hot place%s%s" +msgstr "^BG%s^K1 achou um lugar quente%s%s" + +#: qcsrc/common/notifications/all.inc:314 +#, c-format +msgid "^BG%s^K1 turned into hot slag%s%s" +msgstr "^BG%s^K1 tornou-se uma escória quente%s%s" + +#: qcsrc/common/notifications/all.inc:315 +#, c-format +msgid "^BG%s^K1 was exploded by a Mage%s%s" +msgstr "^BG%s^K1 foi explodido por um Mago%s%s" + +#: qcsrc/common/notifications/all.inc:316 +#, c-format +msgid "^BG%s^K1's innards became outwards by a Shambler%s%s" +msgstr "" +"Os órgãos internos de ^BG%s^K1 se tornaram externos por causa de um Shambler " +"%s%s" + +#: qcsrc/common/notifications/all.inc:317 +#, c-format +msgid "^BG%s^K1 was smashed by a Shambler%s%s" +msgstr "^BG%s^K1 foi esmagado por um Shambler%s%s" + +#: qcsrc/common/notifications/all.inc:318 +#, c-format +msgid "^BG%s^K1 was zapped to death by a Shambler%s%s" +msgstr "^BG%s^K1 foi eletrocutado até a morte por um Shambler%s%s" + +#: qcsrc/common/notifications/all.inc:319 +#, c-format +msgid "^BG%s^K1 was bitten by a Spider%s%s" +msgstr "^BG%s^K1 foi picado por uma Aranha%s%s" + +#: qcsrc/common/notifications/all.inc:320 +#, c-format +msgid "^BG%s^K1 was fireballed by a Wyvern%s%s" +msgstr "^BG%s^K1 foi morto pela fireball de um Wyvern%s%s" + +#: qcsrc/common/notifications/all.inc:321 +#, c-format +msgid "^BG%s^K1 joins the Zombies%s%s" +msgstr "^BG%s^K1 se juntou aos Zumbis%s%s" + +#: qcsrc/common/notifications/all.inc:322 +#, c-format +msgid "^BG%s^K1 was given kung fu lessons by a Zombie%s%s" +msgstr "^BG%s^K1 recebeu lições de kung fu de um Zumbi%s%s" + +#: qcsrc/common/notifications/all.inc:323 +#: qcsrc/common/notifications/all.inc:325 +#, c-format +msgid "^BG%s^K1 mastered the art of self-nading%s%s" +msgstr "^BG%s^K1 dominou a arte do suicídio com granadas%s%s" + +#: qcsrc/common/notifications/all.inc:324 +#, c-format +msgid "" +"^BG%s^K1 decided to take a look at the results of their napalm explosion%s%s" +msgstr "" +"^BG%s^K1 decidiu dar uma olhada nos resultados de sua explosão de napalm%s%s" + +#: qcsrc/common/notifications/all.inc:324 +#, c-format +msgid "^BG%s^K1 was burned to death by their own Napalm Nade%s%s" +msgstr "" +"^BG%s^K1 foi queimado até a morte por sua própria Granada de Napalm%s%s" + +#: qcsrc/common/notifications/all.inc:326 +#, c-format +msgid "^BG%s^K1 felt a little chilly%s%s" +msgstr "^BG%s^K1 sentiu-se um pouco friolento%s%s" + +#: qcsrc/common/notifications/all.inc:326 +#, c-format +msgid "^BG%s^K1 was frozen to death by their own Ice Nade%s%s" +msgstr "^BG%s^K1 foi congelado até a morte por sua própria Granada de Gelo%s%s" + +#: qcsrc/common/notifications/all.inc:327 +#, c-format +msgid "^BG%s^K1's Healing Nade didn't quite heal them%s%s" +msgstr "A Granada de Cura de ^BG%s^K1 não lhe curou corretamente%s%s" + +#: qcsrc/common/notifications/all.inc:328 +#, c-format +msgid "^BG%s^K1 died%s%s. What's the point of living without ammo?" +msgstr "^BG%s^K1 morreu%s%s. Qual o sentido de viver sem munição?" + +#: qcsrc/common/notifications/all.inc:328 +#, c-format +msgid "^BG%s^K1 ran out of ammo%s%s" +msgstr "^BG%s^K1 ficou sem munição%s%s" + +#: qcsrc/common/notifications/all.inc:329 +#, c-format +msgid "^BG%s^K1 rotted away%s%s" +msgstr "^BG%s^K1 derreteu%s%s" + +#: qcsrc/common/notifications/all.inc:330 +#, c-format +msgid "^BG%s^K1 became a shooting star%s%s" +msgstr "^BG%s^K1 se tornou uma estrela cadente%s%s" + +#: qcsrc/common/notifications/all.inc:331 +#, c-format +msgid "^BG%s^K1 was slimed%s%s" +msgstr "^BG%s^K1 foi dissolvido%s%s" + +#: qcsrc/common/notifications/all.inc:332 +#, c-format +msgid "^BG%s^K1 couldn't take it anymore%s%s" +msgstr "^BG%s^K1 não aguentava mais%s%s" + +#: qcsrc/common/notifications/all.inc:333 +#, c-format +msgid "^BG%s^K1 is now preserved for centuries to come%s%s" +msgstr "^BG%s^K1 agora está preservado para os séculos que virão%s%s" + +#: qcsrc/common/notifications/all.inc:334 +#, c-format +msgid "^BG%s^K1 switched to the %s%s" +msgstr "^BG%s^K1 trocou para o %s%s" + +#: qcsrc/common/notifications/all.inc:335 +#, c-format +msgid "^BG%s^K1 died in an accident%s%s" +msgstr "^BG%s^K1 morreu em um acidente%s%s" + +#: qcsrc/common/notifications/all.inc:336 +#, c-format +msgid "^BG%s^K1 ran into a turret%s%s" +msgstr "^BG%s^K1 deu de cara com uma sentinela%s%s" + +#: qcsrc/common/notifications/all.inc:337 +#, c-format +msgid "^BG%s^K1 was blasted away by an eWheel turret%s%s" +msgstr "^BG%s^K1 foi explodido por uma sentinela eWheel%s%s" + +#: qcsrc/common/notifications/all.inc:338 +#, c-format +msgid "^BG%s^K1 got caught up in the FLAC turret fire%s%s" +msgstr "^BG%s^K1 se meteu no meio do tiroteio de uma sentinela FLAC%s%s" + +#: qcsrc/common/notifications/all.inc:339 +#, c-format +msgid "^BG%s^K1 was blasted away by a Hellion turret%s%s" +msgstr "^BG%s^K1 foi explodido por uma sentinela Hellion%s%s" + +#: qcsrc/common/notifications/all.inc:340 +#, c-format +msgid "^BG%s^K1 could not hide from the Hunter turret%s%s" +msgstr "^BG%s^K1 não conseguiu se esconder da sentinela Hunter%s%s" + +#: qcsrc/common/notifications/all.inc:341 +#, c-format +msgid "^BG%s^K1 was riddled full of holes by a Machinegun turret%s%s" +msgstr "" +"^BG%s^K1 ficou cheio de buracos por causa de uma sentinela de Metralhadora%s" +"%s" + +#: qcsrc/common/notifications/all.inc:342 +#, c-format +msgid "^BG%s^K1 got turned into smoldering gibs by an MLRS turret%s%s" +msgstr "^BG%s^K1 foi picado em pedacinhos latentes por uma sentinela MLRS%s%s" + +#: qcsrc/common/notifications/all.inc:343 +#, c-format +msgid "^BG%s^K1 was phased out by a turret%s%s" +msgstr "^BG%s^K1 foi eliminado por uma sentinela%s%s" + +#: qcsrc/common/notifications/all.inc:344 +#, c-format +msgid "^BG%s^K1 got served some superheated plasma from a turret%s%s" +msgstr "^BG%s^K1 levou um plasma superaquecido de uma sentinela%s%s" + +#: qcsrc/common/notifications/all.inc:345 +#, c-format +msgid "^BG%s^K1 was electrocuted by a Tesla turret%s%s" +msgstr "^BG%s^K1 foi eletrocutado por uma sentinela Tesla%s%s" + +#: qcsrc/common/notifications/all.inc:346 +#, c-format +msgid "^BG%s^K1 got served a lead enrichment by a Walker turret%s%s" +msgstr "^BG%s^K1 foi entupido de chumbo por uma sentinela Walker%s%s" + +#: qcsrc/common/notifications/all.inc:347 +#, c-format +msgid "^BG%s^K1 was impaled by a Walker turret%s%s" +msgstr "^BG%s^K1 foi empalado por uma sentinela Walker%s%s" + +#: qcsrc/common/notifications/all.inc:348 +#, c-format +msgid "^BG%s^K1 was blasted away by a Walker turret%s%s" +msgstr "^BG%s^K1 foi explodido por uma sentinela Walker%s%s" + +#: qcsrc/common/notifications/all.inc:349 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Bumblebee explosion%s%s" +msgstr "^BG%s^K1 foi pego pelo raio de uma explosão de Bumblebee%s%s" + +#: qcsrc/common/notifications/all.inc:350 +#, c-format +msgid "^BG%s^K1 was crushed by a vehicle%s%s" +msgstr "^BG%s^K1 foi esmagado por um veículo%s%s" + +#: qcsrc/common/notifications/all.inc:351 +#, c-format +msgid "^BG%s^K1 was caught in a Raptor cluster bomb%s%s" +msgstr "^BG%s^K1 foi pego por uma bomba de Raptor%s%s" + +#: qcsrc/common/notifications/all.inc:352 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Raptor explosion%s%s" +msgstr "^BG%s^K1 foi pego pela explosão de um Raptor%s%s" + +#: qcsrc/common/notifications/all.inc:353 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Spiderbot explosion%s%s" +msgstr "^BG%s^K1 foi pego pela explosão de um Spiderbot%s%s" + +#: qcsrc/common/notifications/all.inc:354 +#, c-format +msgid "^BG%s^K1 was blasted to bits by a Spiderbot rocket%s%s" +msgstr "^BG%s^K1 foi explodido em pedacinhos por um foguete de Spiderbot%s%s" + +#: qcsrc/common/notifications/all.inc:355 +#, c-format +msgid "^BG%s^K1 got caught in the blast of a Racer explosion%s%s" +msgstr "^BG%s^K1 foi pego pela explosão de um Racer%s%s" + +#: qcsrc/common/notifications/all.inc:356 +#, c-format +msgid "^BG%s^K1 couldn't find shelter from a Racer rocket%s%s" +msgstr "^BG%s^K1 não conseguiu escapar do foguete de um Racer%s%s" + +#: qcsrc/common/notifications/all.inc:359 +#, c-format +msgid "^BG%s^K1 was betrayed by ^BG%s^K1%s%s" +msgstr "^BG%s^K1 foi traído por ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:361 +#, c-format +msgid "^BG%s^BG%s^BG (%s %s every %s seconds)" +msgstr "^BG%s^BG%s^BG (%s %s a cada %s segundos)" + +#: qcsrc/common/notifications/all.inc:363 +#, c-format +msgid "^BG%s^K1 was frozen by ^BG%s" +msgstr "^BG%s^K1 foi congelado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:364 +#, c-format +msgid "^BG%s^K3 was revived by ^BG%s" +msgstr "^BG%s^K3 foi ressuscitado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:365 +#, c-format +msgid "^BG%s^K3 was revived by falling" +msgstr "^BG%s^K3 foi ressuscitado por cair" + +#: qcsrc/common/notifications/all.inc:366 +#, c-format +msgid "^BG%s^K3 was revived by their Nade explosion" +msgstr "^BG%s^K3 foi ressuscitado pela explosão da Granada deles" + +#: qcsrc/common/notifications/all.inc:367 +#, c-format +msgid "^BG%s^K3 was automatically revived after %s second(s)" +msgstr "^BG%s^K3 foi automaticamente ressuscitado depois de %s segundo(s)" + +#: qcsrc/common/notifications/all.inc:368 +#, c-format +msgid "^BG%s^K1 froze themself" +msgstr "^BG%s^K1 se congelou" + +#: qcsrc/common/notifications/all.inc:370 +#: qcsrc/common/notifications/all.inc:684 +msgid "^TC^TT^BG team wins the round" +msgstr "A equipe ^TC^TT^BG venceu a rodada" + +#: qcsrc/common/notifications/all.inc:371 +#: qcsrc/common/notifications/all.inc:685 +#, c-format +msgid "^BG%s^BG wins the round" +msgstr "^BG%s^BG venceu a rodada" + +#: qcsrc/common/notifications/all.inc:372 +#: qcsrc/common/notifications/all.inc:548 +msgid "^BGRound tied" +msgstr "^BGRodada empatada" + +#: qcsrc/common/notifications/all.inc:373 +#: qcsrc/common/notifications/all.inc:549 +msgid "^BGRound over, there's no winner" +msgstr "^BGA rodada acabou sem vencedor" + +#: qcsrc/common/notifications/all.inc:375 +#, c-format +msgid "^BGGodmode saved you %s units of damage, cheater!" +msgstr "^BGModo Deus te protegeu de %s de dano, seu trapaçeiro!" + +#: qcsrc/common/notifications/all.inc:377 +#, c-format +msgid "^BG%s^BG got the %s^BG buff!" +msgstr "^BG%s^BG pegou o bônus de %s^BG!" + +#: qcsrc/common/notifications/all.inc:378 +#, c-format +msgid "^BG%s^BG lost the %s^BG buff!" +msgstr "^BG%s^BG perdeu o bônus de %s^BG!" + +#: qcsrc/common/notifications/all.inc:379 +#: qcsrc/common/notifications/all.inc:692 +#, c-format +msgid "^BGYou dropped the %s^BG buff!" +msgstr "^BGVocê largou o bônus de %s^BG!" + +#: qcsrc/common/notifications/all.inc:380 +#: qcsrc/common/notifications/all.inc:693 +#, c-format +msgid "^BGYou got the %s^BG buff!" +msgstr "^BGVocê pegou o bônus de %s^BG!" + +#: qcsrc/common/notifications/all.inc:382 +#: qcsrc/common/notifications/all.inc:696 +#, c-format +msgid "^BGYou do not have the ^F1%s" +msgstr "^BGVocê não tem a ^F1%s" + +#: qcsrc/common/notifications/all.inc:383 +#: qcsrc/common/notifications/all.inc:697 +#, c-format +msgid "^BGYou dropped the ^F1%s^BG%s" +msgstr "^BGVocê largou a ^F1%s^BG%s" + +#: qcsrc/common/notifications/all.inc:384 +#: qcsrc/common/notifications/all.inc:698 +#, c-format +msgid "^BGYou got the ^F1%s" +msgstr "^BGVocê pegou a ^F1%s" + +#: qcsrc/common/notifications/all.inc:385 +#: qcsrc/common/notifications/all.inc:699 +#, c-format +msgid "^BGYou don't have enough ammo for the ^F1%s" +msgstr "^BGVocê não tem munição suficiente para a ^F1%s" + +#: qcsrc/common/notifications/all.inc:386 +#: qcsrc/common/notifications/all.inc:700 +#, c-format +msgid "^F1%s %s^BG is unable to fire, but its ^F1%s^BG can" +msgstr "(%s) O ^F1modo %s^BG não pode atirar, mas o ^F1%s^BG pode" + +#: qcsrc/common/notifications/all.inc:387 +#: qcsrc/common/notifications/all.inc:701 +#, c-format +msgid "^F1%s^BG is ^F4not available^BG on this map" +msgstr "^F1%s^BG^F4 não está disponível^BG neste mapa" + +#: qcsrc/common/notifications/all.inc:389 +#, c-format +msgid "^BG%s^BG is connecting..." +msgstr "^BG%s^BG está conectando..." + +#: qcsrc/common/notifications/all.inc:390 +#, c-format +msgid "^BG%s^F3 connected" +msgstr "^BG%s^F3 conectou-se" + +#: qcsrc/common/notifications/all.inc:391 +#, c-format +msgid "^BG%s^F3 connected and joined the ^TC^TT team" +msgstr "^BG%s^F3 conectou-se e se uniu à equipe ^TC^TT" + +#: qcsrc/common/notifications/all.inc:392 +#, c-format +msgid "^BG%s^F3 is now playing" +msgstr "^BG%s^F3 está jogando agora" + +#: qcsrc/common/notifications/all.inc:393 +#, c-format +msgid "^BG%s^F3 is now playing on the ^TC^TT team" +msgstr "^BG%s^F3 está jogando agora na equipe ^TC^TT" + +#: qcsrc/common/notifications/all.inc:395 +#: qcsrc/common/notifications/all.inc:706 +#, c-format +msgid "^BG%s^BG has dropped the ball!" +msgstr "^BG%s^BG largou a bola!" + +#: qcsrc/common/notifications/all.inc:396 +#: qcsrc/common/notifications/all.inc:707 +#, c-format +msgid "^BG%s^BG has picked up the ball!" +msgstr "^BG%s^BG pegou a bola!" + +#: qcsrc/common/notifications/all.inc:398 +#, c-format +msgid "^BG%s^BG captured the keys for the ^TC^TT team" +msgstr "^BG%s^BG capturou as chaves para a equipe ^TC^TT" + +#: qcsrc/common/notifications/all.inc:399 +#, c-format +msgid "^BG%s^BG dropped the ^TC^TT Key" +msgstr "^BG%s^BG largou a Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:400 +#, c-format +msgid "^BG%s^BG lost the ^TC^TT Key" +msgstr "^BG%s^BG perdeu a Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:401 +#, c-format +msgid "^BG%s^BG pushed %s^BG causing the ^TC^TT Key ^BGdestruction" +msgstr "^BG%s^BG empurrou %s^BG causando a ^BGdestruição da Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:402 +#, c-format +msgid "^BG%s^BG destroyed the ^TC^TT Key" +msgstr "^BG%s^BG destruiu a Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:403 +#, c-format +msgid "^BG%s^BG picked up the ^TC^TT Key" +msgstr "^BG%s^BG pegou a Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:405 +#, c-format +msgid "^BG%s^F3 forfeited" +msgstr "^BG%s^F3 desistiu" + +#: qcsrc/common/notifications/all.inc:406 +#, c-format +msgid "^BG%s^F3 has no more lives left" +msgstr "^BG%s^F3 não tem mais vidas" + +#: qcsrc/common/notifications/all.inc:408 +msgid "^BGMonsters are currently disabled" +msgstr "^BGMonstros estão atualmente desativados" + +#: qcsrc/common/notifications/all.inc:410 +msgid "^BGThe ^TC^TT^BG team held the ball for too long" +msgstr "^BGA ^BGequipe ^TC^TT segurou a bola por muito tempo" + +#: qcsrc/common/notifications/all.inc:412 +#, c-format +msgid "^BG%s^BG captured %s^BG control point" +msgstr "^BG%s^BG capturou o ponto de controle %s^BG" + +#: qcsrc/common/notifications/all.inc:413 +#, c-format +msgid "^TC^TT^BG team %s^BG control point has been destroyed by %s" +msgstr "O ponto de controle %s^BG da equipe ^TC^TT^BG foi destruído por %s" + +#: qcsrc/common/notifications/all.inc:414 +msgid "^TC^TT^BG generator has been destroyed" +msgstr "O gerador ^TC^TT^BG foi destruído" + +#: qcsrc/common/notifications/all.inc:415 +msgid "^TC^TT^BG generator spontaneously combusted due to overtime!" +msgstr "" +"O gerador da equipe ^TC^TT^BG entrou em combustão espontaneamente devido aos " +"acréscimos!" + +#: qcsrc/common/notifications/all.inc:417 +#, c-format +msgid "^BG%s^K1 picked up Invisibility" +msgstr "^BG%s^K1 pegou Invisibilidade" + +#: qcsrc/common/notifications/all.inc:418 +#, c-format +msgid "^BG%s^K1 picked up Shield" +msgstr "^BG%s^K1 pegou Escudo" + +#: qcsrc/common/notifications/all.inc:419 +#, c-format +msgid "^BG%s^K1 picked up Speed" +msgstr "^BG%s^K1 pegou Velocidade" + +#: qcsrc/common/notifications/all.inc:420 +#, c-format +msgid "^BG%s^K1 picked up Strength" +msgstr "^BG%s^K1 pegou Força" + +#: qcsrc/common/notifications/all.inc:422 +#, c-format +msgid "^BG%s^F3 disconnected" +msgstr "^BG%s^F3 desconectou-se" + +#: qcsrc/common/notifications/all.inc:423 +#, c-format +msgid "^BG%s^F3 was kicked for idling" +msgstr "^BG%s^F3 foi expulso por inatividade" + +#: qcsrc/common/notifications/all.inc:424 +msgid "" +"^F2You were kicked from the server because you are a spectator and " +"spectators aren't allowed at the moment." +msgstr "" +"^F2Você foi expulso do servidor porque você é um espectador e espectadores " +"não são permitidos no momento." + +#: qcsrc/common/notifications/all.inc:425 +#, c-format +msgid "^BG%s^F3 is now spectating" +msgstr "^BG%s^F3 está assistindo agora" + +#: qcsrc/common/notifications/all.inc:427 +#, c-format +msgid "^BG%s^BG has abandoned the race" +msgstr "^BG%s^BG abandonou a corrida" + +#: qcsrc/common/notifications/all.inc:428 +#, c-format +msgid "^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s" +msgstr "^BG%s^BG não puderam quebrar o recorde de lugar %s%s^BG de %s%s %s" + +#: qcsrc/common/notifications/all.inc:429 +#, c-format +msgid "^BG%s^BG couldn't break the %s%s^BG place record of %s%s %s" +msgstr "^BG%s^BG não pode quebrar o recorde de lugar %s%s^BG de %s%s %s" + +#: qcsrc/common/notifications/all.inc:430 +#, c-format +msgid "^BG%s^BG has finished the race" +msgstr "^BG%s^BG acabou a corrida" + +#: qcsrc/common/notifications/all.inc:431 +#, c-format +msgid "^BG%s^BG broke %s^BG's %s%s^BG place record with %s%s %s" +msgstr "" +"^BG%s^BG quebrou o recorde %s%s^BG de %s^BG e substituiu seu recorde com %s" +"%s %s" + +#: qcsrc/common/notifications/all.inc:432 +#, c-format +msgid "^BG%s^BG improved their %s%s^BG place record with %s%s %s" +msgstr "^BG%s^BG melhoraram seus %s%s^BG com %s%s %s" + +#: qcsrc/common/notifications/all.inc:433 +#, c-format +msgid "" +"^BG%s^BG scored a new record with ^F2%s^BG, but unfortunately lacks a UID " +"and will be lost." +msgstr "" +"^BG%s^BG atingiu um novo recorde com ^F2%s^BG, mas infelizmente, faltou uma " +"identidade de usuário e sua pontuação será perdida." + +#: qcsrc/common/notifications/all.inc:434 +#, c-format +msgid "" +"^BG%s^BG scored a new record with ^F2%s^BG, but is anonymous and will be " +"lost." +msgstr "" +"^BG%s^BG quebrou um novo recorde com ^F2%s^BG, mas é anônimo e, por isso, " +"será perdido." + +#: qcsrc/common/notifications/all.inc:435 +#, c-format +msgid "^BG%s^BG set the %s%s^BG place record with %s%s" +msgstr "^BG%s^BG definiu o recorde do local %s%s^BG com a pontuação %s%s" + +#: qcsrc/common/notifications/all.inc:437 +#, c-format +msgid "" +"^F4You have been invited by ^BG%s^F4 to join their game of ^F2%s^F4 " +"(^F1%s^F4)" +msgstr "" +"^F4Você foi convidado por ^BG%s^F4 para se juntar a eles na partida de " +"^F2%s^F4 (^F1%s^F4)" + +#: qcsrc/common/notifications/all.inc:439 +msgid "^TC^TT ^BGteam scores!" +msgstr "A equipe ^TC^TT ^BG pontuou!" + +#: qcsrc/common/notifications/all.inc:441 +#, c-format +msgid "" +"^F2You have to become a player within the next %s, otherwise you will be " +"kicked, because spectating isn't allowed at this time!" +msgstr "" +"^F2Você precisa se tornar um jogador dentro de %s, caso contrário, você será " +"expulso, pois espectadores não são permitidos no momento!" + +#: qcsrc/common/notifications/all.inc:443 +#, c-format +msgid "^BG%s^K1 picked up a Superweapon" +msgstr "^BG%s^K1 pegou uma Superarma" + +#: qcsrc/common/notifications/all.inc:445 +msgid "^BGYou cannot change to a larger team" +msgstr "^BGVocê não pode trocar para uma equipe maior" + +#: qcsrc/common/notifications/all.inc:446 +msgid "^BGYou are not allowed to change teams" +msgstr "^BGVocê não pode trocar de equipe" + +#: qcsrc/common/notifications/all.inc:448 +#, c-format +msgid "" +"^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have " +"^F2Xonotic %s" +msgstr "" +"^F4NOTA: ^BGO servidor está rodando ^F1Xonotic %s (beta)^BG, você tem o " +"^F2Xonotic %s" + +#: qcsrc/common/notifications/all.inc:449 +#, c-format +msgid "" +"^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s" +msgstr "" +"^F4NOTA: ^BGO servidor está rodando ^F1Xonotic %s^BG, você tem o ^F2Xonotic " +"%s" + +#: qcsrc/common/notifications/all.inc:450 +#, c-format +msgid "" +"^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get " +"the update from ^F3http://www.xonotic.org/^BG!" +msgstr "" +"^F4NOTA: ^F1Xonotic %s^BG foi lançado e você ainda está com o ^F2Xonotic " +"%s^BG - baixe a atualização pelo site ^F3http://www.xonotic.org/^BG!" + +#: qcsrc/common/notifications/all.inc:452 +#, c-format +msgid "^F3SVQC Build information: ^F4%s" +msgstr "^F3SVQC Informação da versão: ^F4%s" + +#: qcsrc/common/notifications/all.inc:454 +#, c-format +msgid "" +"^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Accordeon%s%s" +msgstr "" +"^BG%s%s^K1 morreu por causa da grande habilidade de ^BG%s^K1 com a @!#%%'n " +"Accordeon%s%s" + +#: qcsrc/common/notifications/all.inc:455 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Accordeon%s%s" +msgstr "^BG%s^K1 feriu seus próprios ouvidos com a @!#%%'n Accordeon%s%s" + +#: qcsrc/common/notifications/all.inc:456 +#, c-format +msgid "^BG%s%s^K1 was electrocuted by ^BG%s^K1's Arc%s%s" +msgstr "^BG%s%s^K1 foi eletrocutado pelo Arc de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:457 +#, c-format +msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Arc bolts%s%s" +msgstr "^BG%s%s^K1 foi explodido pelos raios do Arc de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:458 +#, c-format +msgid "^BG%s%s^K1 was shot to death by ^BG%s^K1's Blaster%s%s" +msgstr "^BG%s%s^K1 foi morto pelo Blaster de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:459 +#, c-format +msgid "^BG%s^K1 shot themself to hell with their Blaster%s%s" +msgstr "^BG%s^K1 atirou muito em si mesmo com seu Blaster%s%s" + +#: qcsrc/common/notifications/all.inc:460 +#, c-format +msgid "^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s" +msgstr "^BG%s%s^K1 sentiu o forte impulso da Crylink de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:461 +#, c-format +msgid "^BG%s^K1 felt the strong pull of their Crylink%s%s" +msgstr "^BG%s^K1 sentiu o forte impulso de sua Crylink%s%s" + +#: qcsrc/common/notifications/all.inc:462 +#, c-format +msgid "^BG%s%s^K1 ate ^BG%s^K1's rocket%s%s" +msgstr "^BG%s%s^K1 comeu o foguete de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:463 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's rocket%s%s" +msgstr "" + +#: qcsrc/common/notifications/all.inc:464 +#, c-format +msgid "^BG%s^K1 blew themself up with their Devastator%s%s" +msgstr "^BG%s^K1 se explodiu com sua Devastator%s%s" + +#: qcsrc/common/notifications/all.inc:465 +#, c-format +msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s" +msgstr "^BG%s%s^K1 foi pulverizado por raios de Electro de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:466 +#, c-format +msgid "^BG%s%s^K1 felt the electrifying air of ^BG%s^K1's Electro combo%s%s" +msgstr "" +"^BG%s%s^K1 sentiu o ar eletrocutado do combo de Electro de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:467 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Electro orb%s%s" +msgstr "^BG%s%s^K1 ficou muito perto da esfera de Electro de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:468 +#, c-format +msgid "^BG%s^K1 played with Electro bolts%s%s" +msgstr "^BG%s^K1 brincou com raios de Electro%s%s" + +#: qcsrc/common/notifications/all.inc:469 +#, c-format +msgid "^BG%s^K1 could not remember where they put their Electro orb%s%s" +msgstr "" +"^BG%s^K1 não conseguiu se lembrar onde tinha colocado a sua esfera de Electro" +"%s%s" + +#: qcsrc/common/notifications/all.inc:470 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's fireball%s%s" +msgstr "^BG%s%s^K1 chegou muito perto da fireball de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:471 +#, c-format +msgid "^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s" +msgstr "^BG%s%s^K1 foi queimado pela mina de fogo de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:472 +#, c-format +msgid "^BG%s^K1 should have used a smaller gun%s%s" +msgstr "^BG%s^K1 deveria ter usado uma arma menor%s%s" + +#: qcsrc/common/notifications/all.inc:473 +#, c-format +msgid "^BG%s^K1 forgot about their firemine%s%s" +msgstr "^BG%s^K1 se esqueceu da sua mina de fogo%s%s" + +#: qcsrc/common/notifications/all.inc:474 +#, c-format +msgid "^BG%s%s^K1 was pummeled by a burst of ^BG%s^K1's Hagar rockets%s%s" +msgstr "" +"^BG%s%s^K1 foi atingido por uma rajada de foguetes do Hagar de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:475 +#, c-format +msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Hagar rockets%s%s" +msgstr "^BG%s%s^K1 foi atingido pelos foguetes do Hagar de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:476 +#, c-format +msgid "^BG%s^K1 played with tiny Hagar rockets%s%s" +msgstr "^BG%s^K1 brincou com minúsculos foguetes de Hagar%s%s" + +#: qcsrc/common/notifications/all.inc:477 +#, c-format +msgid "^BG%s%s^K1 was cut down with ^BG%s^K1's HLAC%s%s" +msgstr "^BG%s%s^K1 foi rasgado pelo HLAC de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:478 +#, c-format +msgid "^BG%s^K1 got a little jumpy with their HLAC%s%s" +msgstr "^BG%s^K1 ficou um pouco agitado com o seu HLAC%s%s" + +#: qcsrc/common/notifications/all.inc:479 +#, c-format +msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Heavy Machine Gun%s%s" +msgstr "^BG%s%s^K1 foi atingido pela Metralhadora Pesada de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:480 +#, c-format +msgid "^BG%s%s^K1 was torn to bits by ^BG%s^K1's Heavy Machine Gun%s%s" +msgstr "^BG%s%s^K1 foi despedaçado pela Metralhadora Pesada de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:481 +#, c-format +msgid "^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s" +msgstr "^BG%s%s^K1 foi pego pela bomba de gravidade do Gancho de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:482 +#, c-format +msgid "" +"^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s" +msgstr "" +"^BG%s%s^K1 morreu por causa da grande habilidade de ^BG%s^K1 com a @!#%%'n " +"Klein Bottle%s%s" + +#: qcsrc/common/notifications/all.inc:483 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s" +msgstr "" +"^BG%s^K1 machucaram seus próprios ouvidos com a @!#%%'n Klein Bottle%s%s" + +#: qcsrc/common/notifications/all.inc:484 +#, c-format +msgid "^BG%s%s^K1 was sniped by ^BG%s^K1's Machine Gun%s%s" +msgstr "^BG%s%s^K1 foi atingido pela Metralhadora de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:485 +#, c-format +msgid "^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Machine Gun%s%s" +msgstr "" +"^BG%s%s^K1 ficou cheio de buracos por causa da Metralhadora de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:486 +#: qcsrc/common/notifications/all.inc:790 +#, c-format +msgid "^BGYou cannot place more than ^F2%s^BG mines at a time" +msgstr "^BGVocê não pode pôr mais do que ^F2%s^BG minas por vez" + +#: qcsrc/common/notifications/all.inc:487 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's mine%s%s" +msgstr "^BG%s%s^K1 ficou muito perto da mina de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:488 +#, c-format +msgid "^BG%s^K1 forgot about their mine%s%s" +msgstr "^BG%s^K1 se esqueceu de sua mina%s%s" + +#: qcsrc/common/notifications/all.inc:489 +#, c-format +msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s" +msgstr "^BG%s%s^K1 ficou muito perto da granada de Mortar de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:490 +#, c-format +msgid "^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s" +msgstr "^BG%s%s^K1 comeu a granada de Mortar de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:491 +#, c-format +msgid "^BG%s^K1 didn't see their own Mortar grenade%s%s" +msgstr "^BG%s^K1 não viu a sua própria granada de Mortar%s%s" + +#: qcsrc/common/notifications/all.inc:492 +#, c-format +msgid "^BG%s^K1 blew themself up with their own Mortar%s%s" +msgstr "^BG%s^K1 se explodiu com o seu próprio Mortar%s%s" + +#: qcsrc/common/notifications/all.inc:493 +#, c-format +msgid "^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 foi atingido pelo Rifle de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:494 +#, c-format +msgid "^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s" +msgstr "^BG%s%s^K1 morreu pela bala do Rifle de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:495 +#, c-format +msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s" +msgstr "^BG%s%s^K1 falhou em se esconder da bala do Rifle de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:496 +#, c-format +msgid "^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s" +msgstr "^BG%s%s^K1 falhou em se esconder do Rifle de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:497 +#, c-format +msgid "^BG%s%s^K1 was sawn in half by ^BG%s^K1's Rocket Propelled Chainsaw%s%s" +msgstr "^BG%s%s^K1 foi serrado ao meio pelo RPC de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:498 +#, c-format +msgid "^BG%s%s^K1 almost dodged ^BG%s^K1's Rocket Propelled Chainsaw%s%s" +msgstr "^BG%s%s^K1 quase desviou do RPC de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:499 +#, c-format +msgid "^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s" +msgstr "^BG%s^K1 foi serrado ao meio pelo seu próprio RPC%s%s" + +#: qcsrc/common/notifications/all.inc:500 +#, c-format +msgid "^BG%s^K1 blew themself up with their Rocket Propelled Chainsaw%s%s" +msgstr "^BG%s^K1 se explodiu com seu próprio RPC%s%s" + +#: qcsrc/common/notifications/all.inc:501 +#, c-format +msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s" +msgstr "^BG%s%s^K1 foi surrado pelos foguetes do Seeker de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:502 +#, c-format +msgid "^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s" +msgstr "^BG%s%s^K1 foi marcado pelo Seeker de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:503 +#, c-format +msgid "^BG%s^K1 played with tiny Seeker rockets%s%s" +msgstr "^BG%s^K1 brincou com minúsculos foguetes de Seeker%s%s" + +#: qcsrc/common/notifications/all.inc:504 +#, c-format +msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s" +msgstr "^BG%s%s^K1 foi morto pela Shockwave de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:505 +#, c-format +msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s" +msgstr "^BG%s%s^K1 deu uns tapas em ^BG%s^K1 com uma grande Shockwave%s%s" + +#: qcsrc/common/notifications/all.inc:506 +#, c-format +msgid "^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s" +msgstr "^BG%s%s^K1 foi baleado pela Shotgun de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:507 +#, c-format +msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s" +msgstr "^BG%s%s^K1 deu uns tapas em ^BG%s^K1 com uma grande Shotgun%s%s" + +#: qcsrc/common/notifications/all.inc:508 +#, c-format +msgid "^BG%s^K1 is now thinking with portals%s%s" +msgstr "^BG%s^K1 agora está pensando com portais%s%s" + +#: qcsrc/common/notifications/all.inc:509 +#, c-format +msgid "^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Tuba%s%s" +msgstr "" +"^BG%s%s^K1 morreu por causa da grande habilidade de ^BG%s^K1 com a @!#%%'n " +"Tuba%s%s" + +#: qcsrc/common/notifications/all.inc:510 +#, c-format +msgid "^BG%s^K1 hurt their own ears with the @!#%%'n Tuba%s%s" +msgstr "^BG%s^K1 feriu seus próprios ouvidos com a @!#%%'n Tuba%s%s" + +#: qcsrc/common/notifications/all.inc:511 +#, c-format +msgid "^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s" +msgstr "^BG%s%s^K1 foi sublimado pela Vaporizer de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:512 +#, c-format +msgid "^BG%s%s^K1 has been vaporized by ^BG%s^K1's Vortex%s%s" +msgstr "^BG%s%s^K1 foi vaporizado pela Vortex de ^BG%s^K1%s%s" + +#: qcsrc/common/notifications/all.inc:537 +msgid "^F4You are now alone!" +msgstr "^F4Você está sozinho agora!" + +#: qcsrc/common/notifications/all.inc:539 +msgid "^BGYou are attacking!" +msgstr "^BGVocê está atacando!" + +#: qcsrc/common/notifications/all.inc:540 +msgid "^BGYou are defending!" +msgstr "^BGVocê está defendendo!" + +#: qcsrc/common/notifications/all.inc:541 +#, c-format +msgid "^BGObjective destroyed in ^F4%s^BG!" +msgstr "^BGObjetivo destruído em ^F4%s^BG!" + +#: qcsrc/common/notifications/all.inc:543 +msgid "^F4Begin!" +msgstr "^F4Começou!" + +#: qcsrc/common/notifications/all.inc:544 +msgid "^F4Game starts in ^COUNT" +msgstr "^F4A partida iniciará em ^COUNT" + +#: qcsrc/common/notifications/all.inc:545 +msgid "^F4Round starts in ^COUNT" +msgstr "^F4A rodada iniciará em ^COUNT" + +#: qcsrc/common/notifications/all.inc:546 +msgid "^F4Round cannot start" +msgstr "^F4A rodada não pode iniciar" + +#: qcsrc/common/notifications/all.inc:551 +msgid "^F2Don't camp!" +msgstr "^F2Não campere!" + +#: qcsrc/common/notifications/all.inc:555 +msgid "" +"^BGYou are now free.\n" +"^BGFeel free to ^F2try to capture^BG the flag again\n" +"^BGif you think you will succeed." +msgstr "" +"^BGVocê está livre agora.\n" +"^BGSinta-se à vontade para ^F2tentar capturar^BG a bandeira de novo\n" +"^BGse você acha que irá conseguir." + +#: qcsrc/common/notifications/all.inc:556 +msgid "^BGThis flag is currently inactive" +msgstr "^BGEsta bandeira está atualmente inativa" + +#: qcsrc/common/notifications/all.inc:557 +msgid "" +"^BGYou are now ^F1shielded^BG from the flag(s)\n" +"^BGfor ^F2too many unsuccessful attempts^BG to capture.\n" +"^BGMake some defensive scores before trying again." +msgstr "" +"^BGVocê agora está ^F1impedido^BG de carregar a(s) bandeira(s)\n" +"^BGapós ^F2várias tentativas de captura sem êxito^BG.\n" +"^BGFaça alguns pontos defensivos antes de tentar novamente." + +#: qcsrc/common/notifications/all.inc:558 +msgid "^BGYou captured the ^TC^TT^BG flag!" +msgstr "^BGVocê capturou a bandeira ^TC^TT^BG!" + +#: qcsrc/common/notifications/all.inc:559 +msgid "^BGYou captured the flag!" +msgstr "^BGVocê capturou a bandeira!" + +#: qcsrc/common/notifications/all.inc:560 +#, c-format +msgid "^BGToo many flag throws! Throwing disabled for %s." +msgstr "" +"^BGNão largue a bandeira várias vezes! Agora você não pode largar por %s." + +#: qcsrc/common/notifications/all.inc:561 +#, c-format +msgid "^BG%s^BG passed the ^TC^TT^BG flag to %s" +msgstr "^BG%s^BG passou a bandeira ^TC^TT^BG para %s" + +#: qcsrc/common/notifications/all.inc:562 +#, c-format +msgid "^BG%s^BG passed the flag to %s" +msgstr "^BG%s^BG passou a bandeira para %s" + +#: qcsrc/common/notifications/all.inc:563 +#, c-format +msgid "^BGYou received the ^TC^TT^BG flag from %s" +msgstr "^BGVocê recebeu a bandeira ^TC^TT^BG de %s" + +#: qcsrc/common/notifications/all.inc:564 +#, c-format +msgid "^BGYou received the flag from %s" +msgstr "^BGVocê recebeu a bandeira de %s" + +#: qcsrc/common/notifications/all.inc:565 +#, c-format +msgid "^BGPress ^F2%s^BG to receive the flag from %s^BG" +msgstr "^BGAperte ^F2%s^BG para receber a bandeira de %s^BG" + +#: qcsrc/common/notifications/all.inc:566 +#, c-format +msgid "^BGRequesting %s^BG to pass you the flag" +msgstr "^BGPedindo à %s^BG para que te passe a bandeira" + +#: qcsrc/common/notifications/all.inc:567 +#, c-format +msgid "^BGYou passed the ^TC^TT^BG flag to %s" +msgstr "^BGVocê passou a bandeira ^TC^TT^BG para %s" + +#: qcsrc/common/notifications/all.inc:568 +#, c-format +msgid "^BGYou passed the flag to %s" +msgstr "^BGVocê passou a bandeira para %s" + +#: qcsrc/common/notifications/all.inc:569 +msgid "^BGYou got the ^TC^TT^BG flag!" +msgstr "^BGVocê pegou a bandeira ^TC^TT^BG!" + +#: qcsrc/common/notifications/all.inc:570 +msgid "^BGYou got the flag!" +msgstr "^BGVocê pegou a bandeira!" + +#: qcsrc/common/notifications/all.inc:571 +#, c-format +msgid "^BGYou got your %steam^BG's flag, return it!" +msgstr "^BGVocê pegou a bandeira da sua %sequipe^BG, retorne-a!" + +#: qcsrc/common/notifications/all.inc:572 +#, c-format +msgid "^BGYou got the %senemy^BG's flag, return it!" +msgstr "^BGVocê pegou a bandeira da %sequipe inimiga^BG, retorne-a!" + +#: qcsrc/common/notifications/all.inc:573 +#, c-format +msgid "^BGThe %senemy^BG got your flag! Retrieve it!" +msgstr "^BGO %sinimigo^BG pegou a sua bandeira! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:574 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got your flag! Retrieve it!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a sua bandeira! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:575 +#, c-format +msgid "^BGThe %senemy^BG got the flag! Retrieve it!" +msgstr "^BGO %sinimigo^BG pegou a bandeira! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:576 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got the flag! Retrieve it!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a bandeira! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:577 +#, c-format +msgid "^BGThe %senemy^BG got their flag! Retrieve it!" +msgstr "^BGO %sinimigo^BG pegou a bandeira deles! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:578 +#, c-format +msgid "^BGThe %senemy (^BG%s%s)^BG got their flag! Retrieve it!" +msgstr "^BGO %sinimigo (^BG%s%s)^BG pegou a bandeira deles! Recupere-a!" + +#: qcsrc/common/notifications/all.inc:579 +#, c-format +msgid "^BGYour %steam mate^BG got the ^TC^TT^BG flag! Protect them!" +msgstr "^BGO seu %scolega de equipe^BG pegou a bandeira ^TC^TT^BG! Proteja-o!" + +#: qcsrc/common/notifications/all.inc:580 +#, c-format +msgid "^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!" +msgstr "" +"^BGO seu %scolega de equipe (^BG%s%s)^BG pegou a bandeira ^TC^TT^BG! Proteja-" +"o!" + +#: qcsrc/common/notifications/all.inc:581 +#, c-format +msgid "^BGYour %steam mate^BG got the flag! Protect them!" +msgstr "^BGO seu %scolega de equipe^BG pegou a bandeira! Proteja-o!" + +#: qcsrc/common/notifications/all.inc:582 +#, c-format +msgid "^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!" +msgstr "^BGO seu %scolega de equipe (^BG%s%s)^BG pegou a bandeira! Proteja-o!" + +#: qcsrc/common/notifications/all.inc:583 +msgid "^BGEnemies can now see you on radar!" +msgstr "^BGAgora os inimigos podem te ver no radar!" + +#: qcsrc/common/notifications/all.inc:584 +msgid "^BGYou returned the ^TC^TT^BG flag!" +msgstr "^BGVocê retornou a bandeira ^TC^TT^BG!" + +#: qcsrc/common/notifications/all.inc:585 +msgid "^BGStalemate! Enemies can now see you on radar!" +msgstr "^BGCuidado! Agora os inimigos podem te ver no radar!" + +#: qcsrc/common/notifications/all.inc:586 +msgid "^BGStalemate! Flag carriers can now be seen by enemies on radar!" +msgstr "" +"^BGCuidado! Agora portadores da bandeira podem ser vistos pelos inimigos no " +"radar!" + +#: qcsrc/common/notifications/all.inc:590 +#, c-format +msgid "^K3%sYou fragged ^BG%s" +msgstr "^K3%sVocê executou ^BG%s" + +#: qcsrc/common/notifications/all.inc:591 +#: qcsrc/common/notifications/all.inc:600 +#: qcsrc/common/notifications/all.inc:609 +#, c-format +msgid "^K3%sYou scored against ^BG%s" +msgstr "^K3%sVocê pontuou contra ^BG%s" + +#: qcsrc/common/notifications/all.inc:592 +#, c-format +msgid "^K1%sYou were fragged by ^BG%s" +msgstr "^K1%sVocê foi executado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:593 +#: qcsrc/common/notifications/all.inc:602 +#: qcsrc/common/notifications/all.inc:611 +#, c-format +msgid "^K1%sYou were scored against by ^BG%s" +msgstr "^K1%sVocê foi pontuado contra por ^BG%s" + +#: qcsrc/common/notifications/all.inc:599 +#, c-format +msgid "^K3%sYou burned ^BG%s" +msgstr "^K3%sVocê queimou ^BG%s" + +#: qcsrc/common/notifications/all.inc:601 +#, c-format +msgid "^K1%sYou were burned by ^BG%s" +msgstr "^K1%sVocê foi queimado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:608 +#, c-format +msgid "^K3%sYou froze ^BG%s" +msgstr "^K3%sVocê congelou ^BG%s" + +#: qcsrc/common/notifications/all.inc:610 +#, c-format +msgid "^K1%sYou were frozen by ^BG%s" +msgstr "^K1%sVocê foi congelado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:617 +#, c-format +msgid "^K1%sYou typefragged ^BG%s" +msgstr "^K1%sVocê executou ^BG%s enquanto digitava" + +#: qcsrc/common/notifications/all.inc:618 +#, c-format +msgid "^K1%sYou scored against ^BG%s^K1 while they were typing" +msgstr "^K1%sVocê pontuou contra ^BG%s^K1 enquanto estavam digitando" + +#: qcsrc/common/notifications/all.inc:619 +#, c-format +msgid "^K1%sYou were typefragged by ^BG%s" +msgstr "^K1%sVocê foi executado enquanto digitava por ^BG%s" + +#: qcsrc/common/notifications/all.inc:620 +#, c-format +msgid "^K1%sYou were scored against by ^BG%s^K1 while typing" +msgstr "^K1%sVocê foi pontuado contra enquanto digitava por ^BG%s^K1" + +#: qcsrc/common/notifications/all.inc:626 +#, c-format +msgid "^BGPress ^F2%s^BG again to toss the nade!" +msgstr "^BGAperte ^F2%s^BG de novo para lançar a granada!" + +#: qcsrc/common/notifications/all.inc:627 +msgid "^F2You got a ^K1BONUS GRENADE^F2!" +msgstr "^F2Você pegou uma ^K1GRANADA BÔNUS^F2!" + +#: qcsrc/common/notifications/all.inc:629 +#, c-format +msgid "" +"^BGYou have been moved into a different team\n" +"You are now on: %s" +msgstr "" +"^BGVocê foi movido para uma equipe diferente\n" +"Agora você está na equipe: %s" + +#: qcsrc/common/notifications/all.inc:630 +msgid "^K1Don't go against your team mates!" +msgstr "^K1Não vá contra seus colegas de equipe!" + +#: qcsrc/common/notifications/all.inc:630 +msgid "^K1Don't shoot your team mates!" +msgstr "^K1Não atire nos seus colegas de equipe!" + +#: qcsrc/common/notifications/all.inc:631 +msgid "^K1Die camper!" +msgstr "^K1Morra, camper!" + +#: qcsrc/common/notifications/all.inc:631 +msgid "^K1Reconsider your tactics, camper!" +msgstr "^K1Reconsidere suas táticas, camper!" + +#: qcsrc/common/notifications/all.inc:632 +msgid "^K1You unfairly eliminated yourself!" +msgstr "^K1Você se matou injustamente!" + +#: qcsrc/common/notifications/all.inc:633 +#, c-format +msgid "^K1You were %s" +msgstr "^K1Você foi %s" + +#: qcsrc/common/notifications/all.inc:634 +msgid "^K1You couldn't catch your breath!" +msgstr "^K1Você não recuperou seu fôlego!" + +#: qcsrc/common/notifications/all.inc:635 +msgid "^K1You hit the ground with a crunch!" +msgstr "^K1Você caiu no chão rigorosamente!" + +#: qcsrc/common/notifications/all.inc:636 +msgid "^K1You felt a little too hot!" +msgstr "^K1Você se sentiu um pouco quente!" + +#: qcsrc/common/notifications/all.inc:636 +msgid "^K1You got a little bit too crispy!" +msgstr "^K1Você ficou um pouco crocante demais!" + +#: qcsrc/common/notifications/all.inc:637 +msgid "^K1You killed your own dumb self!" +msgstr "^K1Você se matou, seu burro!" + +#: qcsrc/common/notifications/all.inc:637 +msgid "^K1You need to be more careful!" +msgstr "^K1Você precisa ter mais cuidado!" + +#: qcsrc/common/notifications/all.inc:638 +msgid "^K1You couldn't stand the heat!" +msgstr "^K1Você não suportou o calor!" + +#: qcsrc/common/notifications/all.inc:639 +msgid "^K1You need to watch out for monsters!" +msgstr "^K1Você tem que se cuidar dos monstros!" + +#: qcsrc/common/notifications/all.inc:639 +msgid "^K1You were killed by a monster!" +msgstr "^K1Você foi morto por um monstro!" + +#: qcsrc/common/notifications/all.inc:640 +msgid "^K1Tastes like chicken!" +msgstr "^K1Tem gosto de frango!" + +#: qcsrc/common/notifications/all.inc:640 +msgid "^K1You forgot to put the pin back in!" +msgstr "^K1Você se esqueceu de pôr o pino de volta!" + +#: qcsrc/common/notifications/all.inc:641 +msgid "^K1Hanging around a napalm explosion is bad!" +msgstr "^K1Brincar no meio de uma explosão de napalm é errado!" + +#: qcsrc/common/notifications/all.inc:642 +msgid "^K1You felt a little chilly!" +msgstr "^K1Você sentiu um pouco de frio!" + +#: qcsrc/common/notifications/all.inc:642 +msgid "^K1You got a little bit too cold!" +msgstr "^K1Você ficou um pouco gelado demais!" + +#: qcsrc/common/notifications/all.inc:643 +msgid "^K1Your Healing Nade is a bit defective" +msgstr "^K1Sua Granada de Cura está um pouco defeituosa" + +#: qcsrc/common/notifications/all.inc:644 +msgid "^K1You are respawning for running out of ammo..." +msgstr "^K1Você está ressurgindo por ficar sem munição..." + +#: qcsrc/common/notifications/all.inc:644 +msgid "^K1You were killed for running out of ammo..." +msgstr "^K1Você foi morto por ficar sem munição..." + +#: qcsrc/common/notifications/all.inc:645 +msgid "^K1You grew too old without taking your medicine" +msgstr "^K1Você ficou muito velho sem tomar o seu medicamento" + +#: qcsrc/common/notifications/all.inc:645 +msgid "^K1You need to preserve your health" +msgstr "^K1Você precisa conservar sua saúde" + +#: qcsrc/common/notifications/all.inc:646 +msgid "^K1You became a shooting star!" +msgstr "^K1Você virou uma estrela cadente!" + +#: qcsrc/common/notifications/all.inc:647 +msgid "^K1You melted away in slime!" +msgstr "^K1Você derreteu na lama!" + +#: qcsrc/common/notifications/all.inc:648 +msgid "^K1You committed suicide!" +msgstr "^K1Você cometeu suicídio!" + +#: qcsrc/common/notifications/all.inc:648 +msgid "^K1You ended it all!" +msgstr "^K1Você acabou com tudo!" + +#: qcsrc/common/notifications/all.inc:649 +msgid "^K1You got stuck in a swamp!" +msgstr "^K1Você ficou preso em um pântano!" + +#: qcsrc/common/notifications/all.inc:650 +#, c-format +msgid "^BGYou are now on: %s" +msgstr "^BGVocê está agora em: %s" + +#: qcsrc/common/notifications/all.inc:651 +msgid "^K1You died in an accident!" +msgstr "^K1Você morreu em um acidente!" + +#: qcsrc/common/notifications/all.inc:652 +msgid "^K1You had an unfortunate run in with a turret!" +msgstr "^K1Você teve um encontro lamentável com uma sentinela!" + +#: qcsrc/common/notifications/all.inc:652 +msgid "^K1You were fragged by a turret!" +msgstr "^K1Você foi executado por uma sentinela!" + +#: qcsrc/common/notifications/all.inc:653 +msgid "^K1You had an unfortunate run in with an eWheel turret!" +msgstr "^K1Você teve um encontro lamentável com uma sentinela eWheel!" + +#: qcsrc/common/notifications/all.inc:653 +msgid "^K1You were fragged by an eWheel turret!" +msgstr "^K1Você foi executado por uma sentinela eWheel!" + +#: qcsrc/common/notifications/all.inc:654 +msgid "^K1You had an unfortunate run in with a Walker turret!" +msgstr "^K1Você teve um encontro lamentável com uma sentinela Walker!" + +#: qcsrc/common/notifications/all.inc:654 +msgid "^K1You were fragged by a Walker turret!" +msgstr "^K1Você foi executado por uma sentinela Walker!" + +#: qcsrc/common/notifications/all.inc:655 +msgid "^K1You got caught in the blast of a Bumblebee explosion!" +msgstr "^K1Você foi pego pelo raio de uma explosão de Bumblebee!" + +#: qcsrc/common/notifications/all.inc:656 +msgid "^K1You were crushed by a vehicle!" +msgstr "^K1Você foi esmagado por um veículo!" + +#: qcsrc/common/notifications/all.inc:657 +msgid "^K1You were caught in a Raptor cluster bomb!" +msgstr "^K1Você foi pego por uma bomba Raptor!" + +#: qcsrc/common/notifications/all.inc:658 +msgid "^K1You got caught in the blast of a Raptor explosion!" +msgstr "^K1Você foi pego no raio de uma explosão de Raptor!" + +#: qcsrc/common/notifications/all.inc:659 +msgid "^K1You got caught in the blast of a Spiderbot explosion!" +msgstr "^K1Você foi pego no raio de uma explosão de Spiderbot!" + +#: qcsrc/common/notifications/all.inc:660 +msgid "^K1You were blasted to bits by a Spiderbot rocket!" +msgstr "^K1Você foi despedaçado por um foguete de Spiderbot!" + +#: qcsrc/common/notifications/all.inc:661 +msgid "^K1You got caught in the blast of a Racer explosion!" +msgstr "^K1Você foi pego no raio de uma explosão de Racer!" + +#: qcsrc/common/notifications/all.inc:662 +msgid "^K1You couldn't find shelter from a Racer rocket!" +msgstr "^K1Você não conseguiu escapar do foguete de um Racer!" + +#: qcsrc/common/notifications/all.inc:663 +msgid "^K1Watch your step!" +msgstr "^K1Cuidado onde pisa!" + +#: qcsrc/common/notifications/all.inc:665 +#, c-format +msgid "^K1Moron! You fragged ^BG%s^K1, a team mate!" +msgstr "^K1Idiota! Você executou ^BG%s^K1, um colega de equipe!" + +#: qcsrc/common/notifications/all.inc:665 +#, c-format +msgid "^K1Moron! You went against ^BG%s^K1, a team mate!" +msgstr "^K1Idiota! Você foi contra ^BG%s^K1, um colega de equipe!" + +#: qcsrc/common/notifications/all.inc:666 +#, c-format +msgid "^K1You were fragged by ^BG%s^K1, a team mate" +msgstr "^K1Você foi executado por ^BG%s^K1, um colega de equipe" + +#: qcsrc/common/notifications/all.inc:666 +#, c-format +msgid "^K1You were scored against by ^BG%s^K1, a team mate" +msgstr "^K1Você foi pontuado contra por ^BG%s^K1, um colega de equipe" + +#: qcsrc/common/notifications/all.inc:668 +msgid "" +"^K1Stop idling!\n" +"^BGDisconnecting in ^COUNT..." +msgstr "" +"^K1Pare de ficar AFK!\n" +"^BGDesconectando em ^COUNT..." + +#: qcsrc/common/notifications/all.inc:670 +#, c-format +msgid "^BGYou need %s^BG!" +msgstr "^BGVocê precisa %s^BG!" + +#: qcsrc/common/notifications/all.inc:671 +#, c-format +msgid "^BGYou also need %s^BG!" +msgstr "^BGVocê também precisa %s^BG!" + +#: qcsrc/common/notifications/all.inc:672 +msgid "^BGDoor unlocked!" +msgstr "^BGPorta destrancada!" + +#: qcsrc/common/notifications/all.inc:674 +msgid "^F2You picked up some extra lives" +msgstr "^F2Você pegou algumas vidas extras" + +#: qcsrc/common/notifications/all.inc:676 +#, c-format +msgid "^K3You revived ^BG%s" +msgstr "^K3Você ressuscitou ^BG%s" + +#: qcsrc/common/notifications/all.inc:677 +msgid "^K3You revived yourself" +msgstr "^K3Você se ressuscitou" + +#: qcsrc/common/notifications/all.inc:678 +#, c-format +msgid "^K3You were revived by ^BG%s" +msgstr "^K3Você foi ressuscitado por ^BG%s" + +#: qcsrc/common/notifications/all.inc:679 +#, c-format +msgid "^K3You were automatically revived after %s second(s)" +msgstr "^K3Você foi automaticamente ressuscitado após %s segundo(s)" + +#: qcsrc/common/notifications/all.inc:681 +msgid "^BGThe generator is under attack!" +msgstr "^BGO gerador está sobre ataque!" + +#: qcsrc/common/notifications/all.inc:683 +msgid "^TC^TT^BG team loses the round" +msgstr "A equipe ^TC^TT^BG perdeu a rodada" + +#: qcsrc/common/notifications/all.inc:687 +msgid "^K1You froze yourself" +msgstr "^K1Você se congelou" + +#: qcsrc/common/notifications/all.inc:688 +msgid "^K1Round already started, you spawn as frozen" +msgstr "^K1A rodada já começou, você surgiu congelado" + +#: qcsrc/common/notifications/all.inc:690 +#, c-format +msgid "^K1A %s has arrived!" +msgstr "^K1Um %s chegou!" + +#: qcsrc/common/notifications/all.inc:694 +msgid "^BGYou got the ^F1Fuel regenerator" +msgstr "^BGVocê pegou o ^F1Regenerador de combustível" + +#: qcsrc/common/notifications/all.inc:695 +msgid "^BGYou got the ^F1Jet pack" +msgstr "^BGVocê pegou a ^F1Mochila a Jato" + +#: qcsrc/common/notifications/all.inc:703 +msgid "" +"^K1No spawnpoints available!\n" +"Hope your team can fix it..." +msgstr "" +"^K1Não há pontos de surgimento disponíveis!\n" +"Tomara que sua equipe consiga consertar isso..." + +#: qcsrc/common/notifications/all.inc:704 +msgid "" +"^K1You may not join the game at this time.\n" +"The player limit reached maximum capacity." +msgstr "" +"^K1Você não pode entrar no jogo neste momento.\n" +"A capacidade máxima de jogadores foi alcançada." + +#: qcsrc/common/notifications/all.inc:708 +msgid "^BGYou picked up the ball" +msgstr "^BGVocê pegou a bola" + +#: qcsrc/common/notifications/all.inc:709 +msgid "^BGKilling people while you don't have the ball gives no points!" +msgstr "^BGMatar os outros enquanto você não tiver a bola não lhe dará pontos!" + +#: qcsrc/common/notifications/all.inc:711 +msgid "" +"^BGAll keys are in your team's hands!\n" +"Help the key carriers to meet!" +msgstr "" +"^BGTodas as chaves estão com a sua equipe!\n" +"Ajude os portadores das chaves a se encontrarem!" + +#: qcsrc/common/notifications/all.inc:712 +msgid "" +"^BGAll keys are in ^TC^TT team^BG's hands!\n" +"Interfere ^F4NOW^BG!" +msgstr "" +"^BGTodas as chaves estão com a equipe ^TC^TT^BG!\n" +"Interfira ^F4AGORA^BG!" + +#: qcsrc/common/notifications/all.inc:713 +msgid "" +"^BGAll keys are in your team's hands!\n" +"Meet the other key carriers ^F4NOW^BG!" +msgstr "" +"^BGTodas as chaves estão com a sua equipe!\n" +"Encontre-se com os outros portadores das chaves ^F4AGORA^BG!" + +#: qcsrc/common/notifications/all.inc:714 +msgid "^F4Round will start in ^COUNT" +msgstr "^F4A rodada iniciará em ^COUNT" + +#: qcsrc/common/notifications/all.inc:715 +msgid "^BGScanning frequency range..." +msgstr "^BGEscaneando alcance de frequência..." + +#: qcsrc/common/notifications/all.inc:716 +msgid "^BGYou are starting with the ^TC^TT Key" +msgstr "^BGVocê está começando com a Chave ^TC^TT" + +#: qcsrc/common/notifications/all.inc:718 +msgid "^BGYou have no lives left, you must wait until the next match" +msgstr "" +"^BGVocê não tem vidas sobrando, você terá que aguardar até a próxima partida" + +#: qcsrc/common/notifications/all.inc:720 +#, c-format +msgid "" +"^BGWaiting for players to join...\n" +"Need active players for: %s" +msgstr "" +"^BGEsperando jogadores entrarem...\n" +"Precisa-se de jogadores ativos para: %s" + +#: qcsrc/common/notifications/all.inc:721 +#, c-format +msgid "^BGWaiting for %s player(s) to join..." +msgstr "^BGEsperando %s jogador(es) entrar(em)..." + +#: qcsrc/common/notifications/all.inc:723 +msgid "^BGYour weapon has been downgraded until you find some ammo!" +msgstr "^BGA sua arma foi rebaixada até que você encontre alguma munição!" + +#: qcsrc/common/notifications/all.inc:724 +msgid "^F4^COUNT^BG left to find some ammo!" +msgstr "^F4^COUNT^BG restante(s) para encontrar alguma munição!" + +#: qcsrc/common/notifications/all.inc:725 +msgid "^BGGet some ammo or you'll be dead in ^F4^COUNT^BG!" +msgstr "^BGEncontre alguma munição ou você morrerá em ^F4^COUNT^BG!" + +#: qcsrc/common/notifications/all.inc:725 +msgid "^BGGet some ammo! ^F4^COUNT^BG left!" +msgstr "^BGEncontre alguma munição! Falta ^F4^COUNT^BG!" + +#: qcsrc/common/notifications/all.inc:726 +#, c-format +msgid "^F2Extra lives remaining: ^K1%s" +msgstr "^F2Vidas extras restantes: ^K1%s" + +#: qcsrc/common/notifications/all.inc:730 +#, c-format +msgid "" +"^F2^COUNT^BG until weapon change...\n" +"Next weapon: ^F1%s" +msgstr "" +"^F2^CONTAGEM^BG até a mudança de arma...\n" +"Próxima arma: ^F1%s" + +#: qcsrc/common/notifications/all.inc:731 +#, c-format +msgid "^F2Active weapon: ^F1%s" +msgstr "^F2Arma ativa: ^F1%s" + +#: qcsrc/common/notifications/all.inc:733 +#, c-format +msgid "^BGYou captured %s^BG control point" +msgstr "^BGVocê capturou o ponto de controle %s^BG" + +#: qcsrc/common/notifications/all.inc:734 +#, c-format +msgid "^TC^TT^BG team captured %s^BG control point" +msgstr "A equipe ^TC^TT^BG capturou o ponto de controle %s^BG" + +#: qcsrc/common/notifications/all.inc:735 +msgid "^BGThis control point currently cannot be captured" +msgstr "^BGEste ponto de controle atualmente não pode ser capturado" + +#: qcsrc/common/notifications/all.inc:736 +msgid "" +"^BGThe enemy generator cannot be destroyed yet\n" +"^F2Capture some control points to unshield it" +msgstr "" +"^BGO gerador inimigo ainda não pode ser destruído\n" +"^F2Capture alguns pontos de controle para desprotegê-lo" + +#: qcsrc/common/notifications/all.inc:737 +msgid "^BGThe ^TCenemy^BG generator is no longer shielded!" +msgstr "^BGO gerador ^TCinimigo^BG não está mais protegido!" + +#: qcsrc/common/notifications/all.inc:738 +msgid "" +"^K1Your generator is NOT shielded!\n" +"^BGRe-capture control points to shield it!" +msgstr "" +"^K1O seu gerador NÃO está blindado!\n" +"^BGRecapture pontos de controle para blindá-lo!" + +#: qcsrc/common/notifications/all.inc:739 +#, c-format +msgid "^BGPress ^F2%s^BG to teleport" +msgstr "^BGAperte ^F2%s^BG para se teletransportar" + +#: qcsrc/common/notifications/all.inc:740 +#, c-format +msgid "^BGTeleporting disabled for %s" +msgstr "^BGTeletransporte desabilitado para %s" + +#: qcsrc/common/notifications/all.inc:742 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"Keep fragging until we have a winner!" +msgstr "" +"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" +"Continue executando até que tenhamos um vencedor!" + +#: qcsrc/common/notifications/all.inc:742 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"Keep scoring until we have a winner!" +msgstr "" +"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" +"Continue pontuando até que tenhamos um vencedor!" + +#: qcsrc/common/notifications/all.inc:743 +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"\n" +"Generators are now decaying.\n" +"The more control points your team holds,\n" +"the faster the enemy generator decays" +msgstr "" +"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" +"\n" +"Os geradores estão enfraquecendo agora.\n" +"Quanto mais pontos de controle a sua equipe tiver,\n" +"mais rápido o gerador inimigo enfraquecerá" + +#: qcsrc/common/notifications/all.inc:744 +#, c-format +msgid "" +"^F2Now playing ^F4OVERTIME^F2!\n" +"^BGAdded ^F4%s^BG to the game!" +msgstr "" +"^F2Jogando agora nos ^F4ACRÉSCIMOS^F2!\n" +"^BGAdicionado ^F4%s^BG à partida!" + +#: qcsrc/common/notifications/all.inc:746 +msgid "^K1In^BG-portal created" +msgstr "^K1^BGPortal de entrada criado" + +#: qcsrc/common/notifications/all.inc:747 +msgid "^F3Out^BG-portal created" +msgstr "^F3^BGPortal de saída criado" + +#: qcsrc/common/notifications/all.inc:748 +msgid "^F1Portal creation failed" +msgstr "^F1Falha ao criar portal" + +#: qcsrc/common/notifications/all.inc:750 +msgid "^F2Strength infuses your weapons with devastating power" +msgstr "^F2A Força deixou suas armas com um poder devastador" + +#: qcsrc/common/notifications/all.inc:751 +msgid "^F2Strength has worn off" +msgstr "^F2A Força se esgotou" + +#: qcsrc/common/notifications/all.inc:753 +msgid "^F2Shield surrounds you" +msgstr "^F2O Escudo te cerca" + +#: qcsrc/common/notifications/all.inc:754 +msgid "^F2Shield has worn off" +msgstr "^F2O Escudo se esgotou" + +#: qcsrc/common/notifications/all.inc:756 +msgid "^F2You are on speed" +msgstr "^F2Você tem a velocidade" + +#: qcsrc/common/notifications/all.inc:757 +msgid "^F2Speed has worn off" +msgstr "^F2A Velocidade se esgotou" + +#: qcsrc/common/notifications/all.inc:759 +msgid "^F2You are invisible" +msgstr "^F2Você está invisível" + +#: qcsrc/common/notifications/all.inc:760 +msgid "^F2Invisibility has worn off" +msgstr "^F2A Invisibilidade se esgotou" + +#: qcsrc/common/notifications/all.inc:762 +msgid "^F2The race is over, finish your lap!" +msgstr "^F2A corrida acabou, termine sua volta!" + +#: qcsrc/common/notifications/all.inc:764 +msgid "^BGSecondary fire inflicts no damage!" +msgstr "^BGModo de disparo secundário não causa dano!" + +#: qcsrc/common/notifications/all.inc:766 +msgid "^BGSequence completed!" +msgstr "^BGSequência completada!" + +#: qcsrc/common/notifications/all.inc:767 +msgid "^BGThere are more to go..." +msgstr "^BGAinda tem mais..." + +#: qcsrc/common/notifications/all.inc:768 +#, c-format +msgid "^BGOnly %s^BG more to go..." +msgstr "^BGSó falta(m) %s^BG..." + +#: qcsrc/common/notifications/all.inc:770 +msgid "^F2Superweapons have broken down" +msgstr "^F2As Superarmas quebraram" + +#: qcsrc/common/notifications/all.inc:771 +msgid "^F2Superweapons have been lost" +msgstr "^F2As Superarmas foram perdidas" + +#: qcsrc/common/notifications/all.inc:772 +msgid "^F2You now have a superweapon" +msgstr "^F2Agora você tem uma Superarma" + +#: qcsrc/common/notifications/all.inc:774 +msgid "^K1Changing to ^TC^TT^K1 in ^COUNT" +msgstr "^K1Trocando para ^TC^TT^K1 em ^COUNT" + +#: qcsrc/common/notifications/all.inc:775 +msgid "^K1Changing team in ^COUNT" +msgstr "^K1Trocando de equipe em ^COUNT" + +#: qcsrc/common/notifications/all.inc:776 +msgid "^K1Spectating in ^COUNT" +msgstr "^K1Trocando para espectador em ^COUNT" + +#: qcsrc/common/notifications/all.inc:777 +msgid "^K1Suicide in ^COUNT" +msgstr "^K1Cometendo suicídio em ^COUNT" + +#: qcsrc/common/notifications/all.inc:779 +msgid "^F4Timeout begins in ^COUNT" +msgstr "^F4Pausa iniciará em ^COUNT" + +#: qcsrc/common/notifications/all.inc:780 +msgid "^F4Timeout ends in ^COUNT" +msgstr "^F4Pausa acabará em ^COUNT" + +#: qcsrc/common/notifications/all.inc:782 +msgid "^K1Cannot join given minigame session!" +msgstr "^K1Não foi possível entrar na sessão de mini jogo fornecida!" + +#: qcsrc/common/notifications/all.inc:784 +#, c-format +msgid "^BGPress ^F2%s^BG to enter/exit the vehicle" +msgstr "^BGAperte ^F2%s^BG para entrar/sair do veículo" + +#: qcsrc/common/notifications/all.inc:785 +#, c-format +msgid "^BGPress ^F2%s^BG to enter the vehicle gunner" +msgstr "^BGAperte ^F2%s^BG para usar a arma do veículo" + +#: qcsrc/common/notifications/all.inc:786 +#, c-format +msgid "^BGPress ^F2%s^BG to steal this vehicle" +msgstr "^BGAperte ^F2%s^BG para roubar este veículo" + +#: qcsrc/common/notifications/all.inc:787 +msgid "" +"^F2The enemy is stealing one of your vehicles!\n" +"^F4Stop them!" +msgstr "" +"^F2O inimigo está roubando um de seus veículos!\n" +"^F4Impeça-os!" + +#: qcsrc/common/notifications/all.inc:788 +msgid "^F2Intruder detected, disabling shields!" +msgstr "^F2Intruso detectado, desativando escudos!" + +#: qcsrc/common/notifications/all.qh:188 +msgid "Notification dump command only works with cl_cmd and sv_cmd.\n" +msgstr "" +"Comando de despejo de notificação funciona apenas com cl_cmd e sv_cmd.\n" + +#: qcsrc/common/notifications/all.qh:399 qcsrc/common/notifications/all.qh:400 +#, c-format +msgid " (near %s)" +msgstr " (próximo de %s)" + +#: qcsrc/common/notifications/all.qh:407 qcsrc/common/notifications/all.qh:408 +msgid "primary" +msgstr "primário" + +#: qcsrc/common/notifications/all.qh:407 qcsrc/common/notifications/all.qh:408 +msgid "secondary" +msgstr "secundário" + +#: qcsrc/common/notifications/all.qh:410 +msgid "point" +msgstr "ponto" + +#: qcsrc/common/notifications/all.qh:410 +msgid "points" +msgstr "pontos" + +#: qcsrc/common/notifications/all.qh:419 +msgid "drop flag" +msgstr "largar bandeira" + +#: qcsrc/common/notifications/all.qh:420 +msgid "throw nade" +msgstr "arremessar granada" + +#: qcsrc/common/notifications/all.qh:431 +#, c-format +msgid " with %s" +msgstr " com %s" + +#: qcsrc/common/notifications/all.qh:444 +#, c-format +msgid "%s^K1 made a TRIPLE FRAG! %s^BG" +msgstr "%s^K1 fez uma EXECUÇÃO TRIPLA! %s^BG" + +#: qcsrc/common/notifications/all.qh:444 +#, c-format +msgid "%s^K1 made a TRIPLE SCORE! %s^BG" +msgstr "%s^K1 fez uma PONTUAÇÃO TRIPLA! %s^BG" + +#: qcsrc/common/notifications/all.qh:444 +msgid "TRIPLE FRAG! " +msgstr "EXECUÇÃO TRIPLA! " + +#: qcsrc/common/notifications/all.qh:445 +#, c-format +msgid "%s^K1 made FIVE SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez CINCO PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:445 +#, c-format +msgid "%s^K1 unlocked RAGE! %s^BG" +msgstr "%s^K1 desbloqueou a FÚRIA! %s^BG" + +#: qcsrc/common/notifications/all.qh:445 +msgid "RAGE! " +msgstr "FÚRIA! " + +#: qcsrc/common/notifications/all.qh:446 +#, c-format +msgid "%s^K1 made TEN SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez DEZ PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:446 +#, c-format +msgid "%s^K1 started a MASSACRE! %s^BG" +msgstr "%s^K1 começou um MASSACRE! %s^BG" + +#: qcsrc/common/notifications/all.qh:446 +msgid "MASSACRE! " +msgstr "MASSACRE! " + +#: qcsrc/common/notifications/all.qh:447 +#, c-format +msgid "%s^K1 executed MAYHEM! %s^BG" +msgstr "%s^K1 executou uma MUTILAÇÃO! %s^BG" + +#: qcsrc/common/notifications/all.qh:447 +#, c-format +msgid "%s^K1 made FIFTEEN SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez QUINZE PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:447 +msgid "MAYHEM! " +msgstr "MUTILAÇÃO! " + +#: qcsrc/common/notifications/all.qh:448 +#, c-format +msgid "%s^K1 is a BERSERKER! %s^BG" +msgstr "%s^K1 está FURIOSO! %s^BG" + +#: qcsrc/common/notifications/all.qh:448 +#, c-format +msgid "%s^K1 made TWENTY SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez VINTE PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:448 +msgid "BERSERKER! " +msgstr "FURIOSO!" + +#: qcsrc/common/notifications/all.qh:449 +#, c-format +msgid "%s^K1 inflicts CARNAGE! %s^BG" +msgstr "%s^K1 está infligindo CARNIFICINA! %s^BG" + +#: qcsrc/common/notifications/all.qh:449 +#, c-format +msgid "%s^K1 made TWENTY FIVE SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez VINTE E CINCO PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:449 +msgid "CARNAGE! " +msgstr "CARNIFICINA!" + +#: qcsrc/common/notifications/all.qh:450 +#, c-format +msgid "%s^K1 made THIRTY SCORES IN A ROW! %s^BG" +msgstr "%s^K1 fez TRINTA PONTUAÇÕES SEGUIDAS! %s^BG" + +#: qcsrc/common/notifications/all.qh:450 +#, c-format +msgid "%s^K1 unleashes ARMAGEDDON! %s^BG" +msgstr "%s^K1 desencadeou o ARMAGEDOM! %s^BG" + +#: qcsrc/common/notifications/all.qh:450 +msgid "ARMAGEDDON! " +msgstr "ARMAGEDDON! " + +#: qcsrc/common/notifications/all.qh:457 +#, c-format +msgid "%s(^F1Bot^BG)" +msgstr "%s(^F1Bot^BG)" + +#: qcsrc/common/notifications/all.qh:459 +#, c-format +msgid "%s(Ping ^F1%d^BG)" +msgstr "%s(Ping ^F1%d^BG)" + +#: qcsrc/common/notifications/all.qh:466 +#, c-format +msgid "" +"\n" +"(Health ^1%d^BG / Armor ^2%d^BG)%s" +msgstr "" +"\n" +"(Saúde ^1%d^BG / Armadura ^2%d^BG)%s" + +#: qcsrc/common/notifications/all.qh:468 +#, c-format +msgid "" +"\n" +"(^F4Dead^BG)%s" +msgstr "" +"\n" +"(^F4Morto^BG)%s" + +#: qcsrc/common/notifications/all.qh:489 qcsrc/common/notifications/all.qh:502 +#, c-format +msgid "%d score spree! " +msgstr "%d pontuações seguidas!" + +#: qcsrc/common/notifications/all.qh:501 +#, c-format +msgid "%d frag spree! " +msgstr "%d execuções seguidas!" + +#: qcsrc/common/notifications/all.qh:514 +msgid "First blood! " +msgstr "Primeira morte! " + +#: qcsrc/common/notifications/all.qh:514 +msgid "First score! " +msgstr "Primeiro ponto!" + +#: qcsrc/common/notifications/all.qh:518 +msgid "First casualty! " +msgstr "Primeira baixa!" + +#: qcsrc/common/notifications/all.qh:518 +msgid "First victim! " +msgstr "Primeira vítima!" + +#: qcsrc/common/notifications/all.qh:559 +#, c-format +msgid "%s^K1 has %d frags in a row! %s^BG" +msgstr "%s^K1 tem %d execuções seguidas! %s^BG" + +#: qcsrc/common/notifications/all.qh:560 +#, c-format +msgid "%s^K1 made %d scores in a row! %s^BG" +msgstr "%s^K1 fez %d pontos seguidos! %s^BG" + +#: qcsrc/common/notifications/all.qh:578 +#, c-format +msgid "%s^K1 drew first blood! %s^BG" +msgstr "%s^K1 foi o primeiro a matar alguém! %s^BG" + +#: qcsrc/common/notifications/all.qh:579 +#, c-format +msgid "%s^K1 got the first score! %s^BG" +msgstr "%s^K1 foi o primeiro a pontuar! %s^BG" + +#: qcsrc/common/notifications/all.qh:595 +#, c-format +msgid ", ending their %d frag spree" +msgstr ", finalizando sua cadeia de %d execuções" + +#: qcsrc/common/notifications/all.qh:596 +#, c-format +msgid ", ending their %d score spree" +msgstr ", finalizando sua cadeia de %d pontuações" + +#: qcsrc/common/notifications/all.qh:610 +#, c-format +msgid ", losing their %d frag spree" +msgstr ", perdendo sua cadeia de %d execuções" + +#: qcsrc/common/notifications/all.qh:611 +#, c-format +msgid ", losing their %d score spree" +msgstr ", perdendo sua cadeia de %d pontuações" + +#: qcsrc/common/teams.qh:29 +msgid "TEAM^Red" +msgstr "Vermelha" + +#: qcsrc/common/teams.qh:30 +msgid "TEAM^Blue" +msgstr "Azul" + +#: qcsrc/common/teams.qh:31 +msgid "TEAM^Yellow" +msgstr "Amarela" + +#: qcsrc/common/teams.qh:32 +msgid "TEAM^Pink" +msgstr "Rosa" + +#: qcsrc/common/teams.qh:33 +msgid "Team" +msgstr "Equipe" + +#: qcsrc/common/teams.qh:34 +msgid "Neutral" +msgstr "Neutro" + +#: qcsrc/common/teams.qh:37 +msgid "KEY^Red" +msgstr "Vermelha" + +#: qcsrc/common/teams.qh:38 +msgid "KEY^Blue" +msgstr "Azul" + +#: qcsrc/common/teams.qh:39 +msgid "KEY^Yellow" +msgstr "Amarela" + +#: qcsrc/common/teams.qh:40 +msgid "KEY^Pink" +msgstr "Rosa" + +#: qcsrc/common/teams.qh:41 +msgid "FLAG^Red" +msgstr "Vermelha" + +#: qcsrc/common/teams.qh:42 +msgid "FLAG^Blue" +msgstr "Azul" + +#: qcsrc/common/teams.qh:43 +msgid "FLAG^Yellow" +msgstr "Amarela" + +#: qcsrc/common/teams.qh:44 +msgid "FLAG^Pink" +msgstr "Rosa" + +#: qcsrc/common/teams.qh:45 +msgid "GENERATOR^Red" +msgstr "Vermelho" + +#: qcsrc/common/teams.qh:46 +msgid "GENERATOR^Blue" +msgstr "Azul" + +#: qcsrc/common/teams.qh:47 +msgid "GENERATOR^Yellow" +msgstr "Amarelo" + +#: qcsrc/common/teams.qh:48 +msgid "GENERATOR^Pink" +msgstr "Rosa" + +#: qcsrc/common/turrets/all.qh:51 +msgid "Turrets dump command only works with sv_cmd.\n" +msgstr "O comando de despejo de sentinelas funciona apenas com sv_cmd.\n" + +#: qcsrc/common/turrets/cl_turrets.qc:129 +#, c-format +msgid "%s under attack!" +msgstr "%s está sob ataque!" + +#: qcsrc/common/turrets/turret.qh:11 +msgid "Turret" +msgstr "Sentinela" + +#: qcsrc/common/turrets/turret/ewheel.qh:15 +msgid "eWheel Turret" +msgstr "Sentinela eWheel" + +#: qcsrc/common/turrets/turret/ewheel_weapon.qh:7 +msgid "eWheel" +msgstr "eWheel" + +#: qcsrc/common/turrets/turret/flac.qh:13 +msgid "FLAC Cannon" +msgstr "Canhão FLAC" + +#: qcsrc/common/turrets/turret/flac_weapon.qh:7 +msgid "FLAC" +msgstr "FLAC" + +#: qcsrc/common/turrets/turret/fusionreactor.qh:11 +msgid "Fusion Reactor" +msgstr "Reator de Fusão" + +#: qcsrc/common/turrets/turret/hellion.qh:13 +msgid "Hellion Missile Turret" +msgstr "Sentinela de Míssil Hellion" + +#: qcsrc/common/turrets/turret/hellion_weapon.qh:7 +msgid "Hellion" +msgstr "Hellion" + +#: qcsrc/common/turrets/turret/hk.qh:15 +msgid "Hunter-Killer Turret" +msgstr "Sentinela Hunter-Killer" + +#: qcsrc/common/turrets/turret/hk_weapon.qh:7 +msgid "Hunter-Killer" +msgstr "Hunter-Killer" + +#: qcsrc/common/turrets/turret/machinegun.qh:13 +msgid "Machinegun Turret" +msgstr "Sentinela de Metralhadora" + +#: qcsrc/common/turrets/turret/machinegun_weapon.qh:7 +msgid "Machinegun" +msgstr "Metralhadora" + +#: qcsrc/common/turrets/turret/mlrs.qh:13 +msgid "MLRS Turret" +msgstr "Sentinela MLRS" + +#: qcsrc/common/turrets/turret/mlrs_weapon.qh:7 +msgid "MLRS" +msgstr "MLRS" + +#: qcsrc/common/turrets/turret/phaser.qh:13 +msgid "Phaser Cannon" +msgstr "Canhão Phaser" + +#: qcsrc/common/turrets/turret/phaser_weapon.qh:7 +msgid "Phaser" +msgstr "Phaser" + +#: qcsrc/common/turrets/turret/plasma.qh:13 +msgid "Plasma Cannon" +msgstr "Canhão de Plasma" + +#: qcsrc/common/turrets/turret/plasma_dual.qh:7 +msgid "Dual plasma" +msgstr "Plasma duplo" + +#: qcsrc/common/turrets/turret/plasma_dual.qh:19 +msgid "Dual Plasma Cannon" +msgstr "Canhão de Plasma Duplo" + +#: qcsrc/common/turrets/turret/plasma_weapon.qh:7 +msgid "Plasma" +msgstr "Plasma" + +#: qcsrc/common/turrets/turret/tesla.qh:13 +#: qcsrc/common/turrets/turret/tesla_weapon.qh:7 +msgid "Tesla Coil" +msgstr "Bobina de Tesla" + +#: qcsrc/common/turrets/turret/walker.qh:15 +msgid "Walker Turret" +msgstr "Sentinela Walker" + +#: qcsrc/common/turrets/turret/walker_weapon.qh:7 +msgid "Walker" +msgstr "Walker" + +#: qcsrc/common/vehicles/cl_vehicles.qc:192 +#, c-format +msgid "Press %s" +msgstr "Aperte %s" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qc:950 +msgid "No right gunner!" +msgstr "Sem artilheiro na direita!" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qc:956 +msgid "No left gunner!" +msgstr "Sem artilheiro na esquerda!" + +#: qcsrc/common/vehicles/vehicle/bumblebee.qh:19 +msgid "Bumblebee" +msgstr "Bumblebee" + +#: qcsrc/common/vehicles/vehicle/racer.qh:19 +msgid "Racer" +msgstr "Racer" + +#: qcsrc/common/vehicles/vehicle/racer_weapon.qh:9 +msgid "Racer cannon" +msgstr "Canhão Racer" + +#: qcsrc/common/vehicles/vehicle/raptor.qh:19 +msgid "Raptor" +msgstr "Raptor" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:9 +msgid "Raptor cannon" +msgstr "Canhão de Raptor" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:17 +msgid "Raptor bomb" +msgstr "Bomba de Raptor" + +#: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:25 +msgid "Raptor flare" +msgstr "Chama de Raptor" + +#: qcsrc/common/vehicles/vehicle/spiderbot.qh:19 +msgid "Spiderbot" +msgstr "Spiderbot" + +#: qcsrc/common/weapons/all.qh:78 +msgid "Weapons dump command only works with sv_cmd.\n" +msgstr "O comando de despejo de armas funciona apenas com sv_cmd.\n" + +#: qcsrc/common/weapons/weapon/arc.qc:17 +msgid "Arc" +msgstr "Arc" + +#: qcsrc/common/weapons/weapon/blaster.qc:17 +msgid "Blaster" +msgstr "Blaster" + +#: qcsrc/common/weapons/weapon/crylink.qc:17 +msgid "Crylink" +msgstr "Crylink" + +#: qcsrc/common/weapons/weapon/devastator.qc:17 +msgid "Devastator" +msgstr "Devastator" + +#: qcsrc/common/weapons/weapon/electro.qc:17 +msgid "Electro" +msgstr "Electro" + +#: qcsrc/common/weapons/weapon/fireball.qc:17 +msgid "Fireball" +msgstr "Fireball" + +#: qcsrc/common/weapons/weapon/hagar.qc:17 +msgid "Hagar" +msgstr "Hagar" + +#: qcsrc/common/weapons/weapon/hlac.qc:17 +msgid "Heavy Laser Assault Cannon" +msgstr "Heavy Laser Assault Cannon" + +#: qcsrc/common/weapons/weapon/hook.qc:17 +msgid "Grappling Hook" +msgstr "Gancho (grappling hook)" + +#: qcsrc/common/weapons/weapon/machinegun.qc:17 +msgid "MachineGun" +msgstr "Metralhadora" + +#: qcsrc/common/weapons/weapon/minelayer.qc:17 +msgid "Mine Layer" +msgstr "Mine Layer" + +#: qcsrc/common/weapons/weapon/mortar.qc:17 +msgid "Mortar" +msgstr "Mortar" + +#: qcsrc/common/weapons/weapon/porto.qc:17 +msgid "Port-O-Launch" +msgstr "Port-O-Launch" + +#: qcsrc/common/weapons/weapon/rifle.qc:18 +msgid "Rifle" +msgstr "Rifle" + +#: qcsrc/common/weapons/weapon/seeker.qc:17 +msgid "T.A.G. Seeker" +msgstr "T.A.G. Seeker" + +#: qcsrc/common/weapons/weapon/shockwave.qc:17 +msgid "Shockwave" +msgstr "Shockwave" + +#: qcsrc/common/weapons/weapon/shotgun.qc:17 +msgid "Shotgun" +msgstr "Shotgun" + +#: qcsrc/common/weapons/weapon/tuba.qc:17 +#, no-c-format +msgid "@!#%'n Tuba" +msgstr "@!#%'n Tuba" + +#: qcsrc/common/weapons/weapon/vaporizer.qc:18 +msgid "Vaporizer" +msgstr "Vaporizer" + +#: qcsrc/common/weapons/weapon/vortex.qc:18 +msgid "Vortex" +msgstr "Vortex" + +#: qcsrc/lib/counting.qh:9 +#, c-format +msgid "CI_DEC^%s years" +msgstr "%s anos" + +#: qcsrc/lib/counting.qh:12 +#, c-format +msgid "CI_ZER^%d years" +msgstr "%d anos" + +#: qcsrc/lib/counting.qh:13 +#, c-format +msgid "CI_FIR^%d year" +msgstr "^%d ano" + +#: qcsrc/lib/counting.qh:14 +#, c-format +msgid "CI_SEC^%d years" +msgstr "%d anos" + +#: qcsrc/lib/counting.qh:15 +#, c-format +msgid "CI_THI^%d years" +msgstr "%d anos" + +#: qcsrc/lib/counting.qh:16 +#, c-format +msgid "CI_MUL^%d years" +msgstr "^%d anos" + +#: qcsrc/lib/counting.qh:18 +#, c-format +msgid "CI_DEC^%s weeks" +msgstr "^%s semanas" + +#: qcsrc/lib/counting.qh:21 +#, c-format +msgid "CI_ZER^%d weeks" +msgstr "^%d semanas" + +#: qcsrc/lib/counting.qh:22 +#, c-format +msgid "CI_FIR^%d week" +msgstr "^%d semana" + +#: qcsrc/lib/counting.qh:23 +#, c-format +msgid "CI_SEC^%d weeks" +msgstr "^%d semanas" + +#: qcsrc/lib/counting.qh:24 +#, c-format +msgid "CI_THI^%d weeks" +msgstr "^%d semanas" + +#: qcsrc/lib/counting.qh:25 +#, c-format +msgid "CI_MUL^%d weeks" +msgstr "^%d semanas" + +#: qcsrc/lib/counting.qh:27 +#, c-format +msgid "CI_DEC^%s days" +msgstr "^%s dias" + +#: qcsrc/lib/counting.qh:30 +#, c-format +msgid "CI_ZER^%d days" +msgstr "^%d dias" + +#: qcsrc/lib/counting.qh:31 +#, c-format +msgid "CI_FIR^%d day" +msgstr "^%d dia" + +#: qcsrc/lib/counting.qh:32 +#, c-format +msgid "CI_SEC^%d days" +msgstr "^%d dias" + +#: qcsrc/lib/counting.qh:33 +#, c-format +msgid "CI_THI^%d days" +msgstr "^%d dias" + +#: qcsrc/lib/counting.qh:34 +#, c-format +msgid "CI_MUL^%d days" +msgstr "^%d dias" + +#: qcsrc/lib/counting.qh:36 +#, c-format +msgid "CI_DEC^%s hours" +msgstr "^%s horas" + +#: qcsrc/lib/counting.qh:39 +#, c-format +msgid "CI_ZER^%d hours" +msgstr "^%d horas" + +#: qcsrc/lib/counting.qh:40 +#, c-format +msgid "CI_FIR^%d hour" +msgstr "^%d hora" + +#: qcsrc/lib/counting.qh:41 +#, c-format +msgid "CI_SEC^%d hours" +msgstr "^%d horas" + +#: qcsrc/lib/counting.qh:42 +#, c-format +msgid "CI_THI^%d hours" +msgstr "^%d horas" + +#: qcsrc/lib/counting.qh:43 +#, c-format +msgid "CI_MUL^%d hours" +msgstr "^%d horas" + +#: qcsrc/lib/counting.qh:46 +#, c-format +msgid "CI_DEC^%s minutes" +msgstr "^%s minutos" + +#: qcsrc/lib/counting.qh:49 +#, c-format +msgid "CI_ZER^%d minutes" +msgstr "^%d minutos" + +#: qcsrc/lib/counting.qh:50 +#, c-format +msgid "CI_FIR^%d minute" +msgstr "^%d minuto" + +#: qcsrc/lib/counting.qh:51 +#, c-format +msgid "CI_SEC^%d minutes" +msgstr "^%d minutos" + +#: qcsrc/lib/counting.qh:52 +#, c-format +msgid "CI_THI^%d minutes" +msgstr "^%d minutos" + +#: qcsrc/lib/counting.qh:53 +#, c-format +msgid "CI_MUL^%d minutes" +msgstr "^%d minutos" + +#: qcsrc/lib/counting.qh:55 +#, c-format +msgid "CI_DEC^%s seconds" +msgstr "^%s segundos" + +#: qcsrc/lib/counting.qh:58 +#, c-format +msgid "CI_ZER^%d seconds" +msgstr "^%d segundos" + +#: qcsrc/lib/counting.qh:59 +#, c-format +msgid "CI_FIR^%d second" +msgstr "^%d segundo" + +#: qcsrc/lib/counting.qh:60 +#, c-format +msgid "CI_SEC^%d seconds" +msgstr "^%d segundos" + +#: qcsrc/lib/counting.qh:61 +#, c-format +msgid "CI_THI^%d seconds" +msgstr "^%d segundos" + +#: qcsrc/lib/counting.qh:62 +#, c-format +msgid "CI_MUL^%d seconds" +msgstr "^%d segundos" + +#: qcsrc/lib/counting.qh:79 +#, c-format +msgid "%dst" +msgstr "%dst" + +#: qcsrc/lib/counting.qh:80 +#, c-format +msgid "%dnd" +msgstr "%dnd" + +#: qcsrc/lib/counting.qh:81 +#, c-format +msgid "%drd" +msgstr "%drd" + +#: qcsrc/lib/counting.qh:82 qcsrc/lib/counting.qh:85 +#, c-format +msgid "%dth" +msgstr "%dth" + +#: qcsrc/lib/oo.qh:298 +msgid "No description" +msgstr "Sem descrição" + +#: qcsrc/lib/spawnfunc.qh:65 +#, c-format +msgid "" +"Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, " +"please file an issue." +msgstr "" +"Campo de entidade %s.%s (%s) não está na lista branca. Se você acredita que " +"isso é um erro, por favor, reporte-o." + +#: qcsrc/lib/string.qh:48 +#, c-format +msgid "%d days, %02d:%02d:%02d" +msgstr "%d dias, %02d:%02d:%02d" + +#: qcsrc/lib/string.qh:49 +#, c-format +msgid "%02d:%02d:%02d" +msgstr "%02d:%02d:%02d" + +#: qcsrc/menu/command/menu_cmd.qc:48 +msgid "Usage: menu_cmd command..., where possible commands are:\n" +msgstr "Uso: comando menu_cmd..., onde os possíveis comandos são:\n" + +#: qcsrc/menu/command/menu_cmd.qc:49 +msgid " sync - reloads all cvars on the current menu page\n" +msgstr " sync - recarrega todas as cvars no menu atual\n" + +#: qcsrc/menu/command/menu_cmd.qc:50 +msgid " directmenu ITEM - select a menu item as main item\n" +msgstr " directmenu ITEM - seleciona um item do menu como principal\n" + +#: qcsrc/menu/command/menu_cmd.qc:79 +msgid "Available options:\n" +msgstr "Opções disponíveis:\n" + +#: qcsrc/menu/command/menu_cmd.qc:128 +msgid "Invalid command. For a list of supported commands, try menu_cmd help.\n" +msgstr "" +"Comando inválido. Para uma lista de comandos suportados, digite menu_cmd " +"help.\n" + +#: qcsrc/menu/item/listbox.qc:415 +#, c-format +msgid "Item %d" +msgstr "Item %d" + +#: qcsrc/menu/item/textslider.qc:11 qcsrc/menu/item/textslider.qc:12 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:37 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:68 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:115 +msgid "Custom" +msgstr "Personalizado" + +#: qcsrc/menu/xonotic/campaign.qc:241 +#, c-format +msgid "Level %d: %s" +msgstr "Nível %d: %s" + +#: qcsrc/menu/xonotic/credits.qc:4 +msgid "Core Team" +msgstr "Equipe Principal" + +#: qcsrc/menu/xonotic/credits.qc:16 +msgid "Extended Team" +msgstr "Equipe Estendida" + +#: qcsrc/menu/xonotic/credits.qc:48 +msgid "Website" +msgstr "Site" + +#: qcsrc/menu/xonotic/credits.qc:53 +msgid "Stats" +msgstr "Estatísticas" + +#: qcsrc/menu/xonotic/credits.qc:57 +msgid "Art" +msgstr "Arte" + +#: qcsrc/menu/xonotic/credits.qc:65 +msgid "Animation" +msgstr "Animação" + +#: qcsrc/menu/xonotic/credits.qc:69 +msgid "Level Design" +msgstr "Design de Mapas" + +#: qcsrc/menu/xonotic/credits.qc:92 +msgid "Music / Sound FX" +msgstr "Música / Efeitos de Som" + +#: qcsrc/menu/xonotic/credits.qc:108 +msgid "Game Code" +msgstr "Codificação de Jogo" + +#: qcsrc/menu/xonotic/credits.qc:116 +msgid "Marketing / PR" +msgstr "Marketing / Relações Públicas" + +#: qcsrc/menu/xonotic/credits.qc:122 +msgid "Legal" +msgstr "Assuntos Legais" + +#: qcsrc/menu/xonotic/credits.qc:127 +msgid "Game Engine" +msgstr "Motor de Jogo" + +#: qcsrc/menu/xonotic/credits.qc:131 +msgid "Engine Additions" +msgstr "Adições ao Motor" + +#: qcsrc/menu/xonotic/credits.qc:136 +msgid "Compiler" +msgstr "Compilador" + +#: qcsrc/menu/xonotic/credits.qc:142 +msgid "Other Active Contributors" +msgstr "Outros Contribuidores Ativos" + +#: qcsrc/menu/xonotic/credits.qc:149 +msgid "Translators" +msgstr "Tradutores" + +#: qcsrc/menu/xonotic/credits.qc:151 +msgid "Asturian" +msgstr "Asturiano" + +#: qcsrc/menu/xonotic/credits.qc:156 +msgid "Belarusian" +msgstr "Bielorrusso" + +#: qcsrc/menu/xonotic/credits.qc:159 +msgid "Bulgarian" +msgstr "Búlgaro" + +#: qcsrc/menu/xonotic/credits.qc:166 +msgid "Chinese (China)" +msgstr "Chinês (China)" + +#: qcsrc/menu/xonotic/credits.qc:172 +msgid "Chinese (Taiwan)" +msgstr "Chinês (Taiwan)" + +#: qcsrc/menu/xonotic/credits.qc:177 +msgid "Cornish" +msgstr "Córnico" + +#: qcsrc/menu/xonotic/credits.qc:180 +msgid "Czech" +msgstr "Tcheco" + +#: qcsrc/menu/xonotic/credits.qc:185 +msgid "Dutch" +msgstr "Holandês" + +#: qcsrc/menu/xonotic/credits.qc:192 +msgid "English (Australia)" +msgstr "Inglês (Austrália)" + +#: qcsrc/menu/xonotic/credits.qc:197 +msgid "Finnish" +msgstr "Finlandês" + +#: qcsrc/menu/xonotic/credits.qc:202 +msgid "French" +msgstr "Francês" + +#: qcsrc/menu/xonotic/credits.qc:210 +msgid "German" +msgstr "Alemão" + +#: qcsrc/menu/xonotic/credits.qc:221 +msgid "Greek" +msgstr "Grego" + +#: qcsrc/menu/xonotic/credits.qc:227 +msgid "Hungarian" +msgstr "Húngaro" + +#: qcsrc/menu/xonotic/credits.qc:231 +msgid "Irish" +msgstr "Irlandês" + +#: qcsrc/menu/xonotic/credits.qc:234 +msgid "Italian" +msgstr "Italiano" + +#: qcsrc/menu/xonotic/credits.qc:240 +msgid "Kazakh" +msgstr "Cazaque" + +#: qcsrc/menu/xonotic/credits.qc:243 +msgid "Korean" +msgstr "Coreano" + +#: qcsrc/menu/xonotic/credits.qc:247 +msgid "Polish" +msgstr "Polônes" + +#: qcsrc/menu/xonotic/credits.qc:255 +msgid "Portuguese" +msgstr "Português" + +#: qcsrc/menu/xonotic/credits.qc:261 +msgid "Romanian" +msgstr "Romeno" + +#: qcsrc/menu/xonotic/credits.qc:268 +msgid "Russian" +msgstr "Russo" + +#: qcsrc/menu/xonotic/credits.qc:279 +msgid "Scottish Gaelic" +msgstr "Gaélico Escocês" + +#: qcsrc/menu/xonotic/credits.qc:282 +msgid "Serbian" +msgstr "Sérvio" + +#: qcsrc/menu/xonotic/credits.qc:288 +msgid "Spanish" +msgstr "Espanhol" + +#: qcsrc/menu/xonotic/credits.qc:299 +msgid "Swedish" +msgstr "Sueco" + +#: qcsrc/menu/xonotic/credits.qc:303 +msgid "Ukrainian" +msgstr "Ucraniano" + +#: qcsrc/menu/xonotic/credits.qc:310 +msgid "Past Contributors" +msgstr "Colaboradores Passados" + +#: qcsrc/menu/xonotic/cvarlist.qc:73 +msgid "forced to be saved to config.cfg" +msgstr "forçado a ser salvo em config.cfg" + +#: qcsrc/menu/xonotic/cvarlist.qc:79 qcsrc/menu/xonotic/cvarlist.qc:89 +msgid "will not be saved" +msgstr "não será salvo" + +#: qcsrc/menu/xonotic/cvarlist.qc:84 +msgid "will be saved to config.cfg" +msgstr "será salvo em config.cfg" + +#: qcsrc/menu/xonotic/cvarlist.qc:93 +msgid "private" +msgstr "privado" + +#: qcsrc/menu/xonotic/cvarlist.qc:95 +msgid "engine setting" +msgstr "configuração do motor" + +#: qcsrc/menu/xonotic/cvarlist.qc:97 +msgid "read only" +msgstr "somente leitura" + +#: qcsrc/menu/xonotic/dialog_credits.qc:13 +#: qcsrc/menu/xonotic/dialog_monstertools.qc:38 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:287 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:85 +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:75 +#: qcsrc/menu/xonotic/dialog_singleplayer_winner.qc:14 +msgid "OK" +msgstr "OK" + +#: qcsrc/menu/xonotic/dialog_credits.qh:7 +msgid "Credits" +msgstr "Créditos" + +#: qcsrc/menu/xonotic/dialog_credits.qh:8 +msgid "The Xonotic credits" +msgstr "Créditos - Xonotic" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:39 +msgid "" +"Welcome to Xonotic, please select your language preference and enter your " +"player name to get started. You can change these options later through the " +"menu system." +msgstr "" +"Bem-vindo(a) ao Xonotic! Escolha o seu idioma de preferência e insira o seu " +"apelido para começar. Você pode alterar essas configurações mais tarde pelo " +"menu." + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:45 +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:28 +msgid "Name:" +msgstr "Nome:" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:53 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:60 +msgid "Name under which you will appear in the game" +msgstr "Seu nome que aparecerá no jogo" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:69 +msgid "Text language:" +msgstr "Idioma do texto:" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:78 +msgid "Allow player statistics to use your nickname at stats.xonotic.org?" +msgstr "" +"Permitir que as estatísticas de jogador usem o seu apelido em stats.xonotic." +"org?" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:84 +msgid "Undecided" +msgstr "Não decidido" + +#: qcsrc/menu/xonotic/dialog_firstrun.qc:88 +msgid "Save settings" +msgstr "Salvar configurações" + +#: qcsrc/menu/xonotic/dialog_firstrun.qh:6 +msgid "Welcome" +msgstr "Bem-vindo(a)" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:16 +msgid "Ammunition display:" +msgstr "Exibir munições:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:19 +msgid "Show only current ammo type" +msgstr "Exibir apenas o tipo de munição atual" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:22 +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:44 +msgid "Noncurrent alpha:" +msgstr "Alfa não circulante:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:26 +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:48 +msgid "Noncurrent scale:" +msgstr "Escala não circulante:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:30 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:24 +msgid "Align icon:" +msgstr "Alinhar ícone:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:31 +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:30 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:23 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:35 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:21 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:33 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:18 +msgid "Left" +msgstr "Esquerda" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:36 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:26 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:23 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:34 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:20 +msgid "Right" +msgstr "Direita" + +#: qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh:6 +msgid "Ammo Panel" +msgstr "Painel de Munições" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:17 +msgid "Message duration:" +msgstr "Duração de mensagem:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:21 +msgid "Fade time:" +msgstr "Tempo de desaparecimento:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:25 +msgid "Flip messages order" +msgstr "Trocar ordem de mensagens" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:15 +msgid "Text alignment:" +msgstr "Alinhamento do texto:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:31 +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:19 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:71 +msgid "Center" +msgstr "Centro" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:35 +msgid "Font scale:" +msgstr "Tamanho da fonte:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh:6 +msgid "Centerprint Panel" +msgstr "Painel Central" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:15 +msgid "Chat entries:" +msgstr "Entradas do bate-papo:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:18 +msgid "Chat size:" +msgstr "Tamanho do bate-papo:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:22 +msgid "Chat lifetime:" +msgstr "Tempo de vida do bate-papo:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qc:26 +msgid "Chat beep sound" +msgstr "Som de aviso do bate-papo" + +#: qcsrc/menu/xonotic/dialog_hudpanel_chat.qh:6 +msgid "Chat Panel" +msgstr "Painel do Bate-papo" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc:14 +msgid "Engine info:" +msgstr "Informações do Motor:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc:17 +msgid "Use an averaging algorithm for fps" +msgstr "Usar um algoritmo médio para o fps" + +#: qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh:6 +msgid "Engine Info Panel" +msgstr "Painel de Informações do Motor" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:15 +msgid "Combine health and armor" +msgstr "Combinar saúde e armadura" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:17 +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:28 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:15 +msgid "Enable status bar" +msgstr "Habilitar barra de status" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:19 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:17 +msgid "Status bar alignment:" +msgstr "Alinhamento da barra de status:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:37 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:25 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:35 +msgid "Inward" +msgstr "Para dentro" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:29 +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:38 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:27 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:36 +msgid "Outward" +msgstr "Para fora" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:32 +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc:30 +msgid "Icon alignment:" +msgstr "Alinhamento de ícones:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc:40 +msgid "Flip health and armor positions" +msgstr "Trocar as posições da saúde e armadura" + +#: qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh:6 +msgid "Health/Armor Panel" +msgstr "Painel de Saúde/Armadura" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc:14 +msgid "Info messages:" +msgstr "Mensagens de informação:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc:17 +msgid "Flip align" +msgstr "Trocar alinhamento" + +#: qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh:6 +msgid "Info Messages Panel" +msgstr "Painel de Mensagens de Informação" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:16 +msgid "PNL^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:17 +msgid "PNL^Enabled spectating" +msgstr "Espectadores habilitados" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:18 +msgid "PNL^Enabled even playing in warmup" +msgstr "Habilitado mesmo jogando em aquecimento" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:29 +msgid "Reduced" +msgstr "Reduzido" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:32 +msgid "Text/icon ratio:" +msgstr "Proporção para textos e ícones:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:35 +msgid "Hide spawned items" +msgstr "Ocultar itens disponíveis" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:37 +msgid "Hide big armor and health" +msgstr "Ocultar armadura grande e saúde grande" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc:39 +msgid "Dynamic size" +msgstr "Tamanho dinâmico" + +#: qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh:6 +msgid "Items Time Panel" +msgstr "Painel de Tempo dos Itens " + +#: qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh:6 +msgid "Mod Icons Panel" +msgstr "Painel de Ícones de Mod" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:15 +msgid "Notifications:" +msgstr "Notificações:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:18 +msgid "Also print notifications to the console" +msgstr "Mostrar notificações no console também" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:21 +msgid "Flip notify order" +msgstr "Trocar ordem de notificações" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:24 +msgid "Entry lifetime:" +msgstr "Tempo de vida de cada entrada:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qc:28 +msgid "Entry fadetime:" +msgstr "Desaparecimento de cada entrada:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_notification.qh:6 +msgid "Notification Panel" +msgstr "Painel de Notificações" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:15 +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:15 +msgid "Panel disabled" +msgstr "Painel desabilitado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:16 +msgid "Panel enabled" +msgstr "Painel habilitado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 +msgid "Panel enabled even observing" +msgstr "Painel habilitado enquanto estiver observando" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:18 +msgid "Panel enabled only in Race/CTS" +msgstr "Painel habilitado apenas em Corrida/CTS" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:24 +msgid "Status bar" +msgstr "Barra de status" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:26 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:68 +msgid "Left align" +msgstr "Alinhamento à esquerda" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:27 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:74 +msgid "Right align" +msgstr "Alinhamento à direita" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:28 +msgid "Inward align" +msgstr "Alinhamento para dentro" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:29 +msgid "Outward align" +msgstr "Alinhamento para fora" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:33 +msgid "Flip speed/acceleration positions" +msgstr "Trocar posição da velocidade e da aceleração" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:37 +msgid "Speed:" +msgstr "Velocidade:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:38 +msgid "Include vertical speed" +msgstr "Incluir velocidade vertical" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:49 +msgid "Speed unit:" +msgstr "Unidade de velocidade:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:51 +msgid "qu/s" +msgstr "qu/s" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:52 +msgid "m/s" +msgstr "m/s" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:53 +msgid "km/h" +msgstr "km/h" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:54 +msgid "mph" +msgstr "mph" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:55 +msgid "knots" +msgstr "nós" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:57 +msgid "Show" +msgstr "Exibir" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:60 +msgid "Top speed" +msgstr "Velocidade máxima" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:66 +msgid "Acceleration:" +msgstr "Aceleração:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:67 +msgid "Include vertical acceleration" +msgstr "Incluir aceleração vertical" + +#: qcsrc/menu/xonotic/dialog_hudpanel_physics.qh:6 +msgid "Physics Panel" +msgstr "Painel de Física" + +#: qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh:6 +msgid "Powerups Panel" +msgstr "Painel de Potencializadores" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:15 +msgid "Panel enabled when spectating" +msgstr "Painel habilitado enquanto estiver de espectador" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:16 +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:17 +msgid "Panel always enabled" +msgstr "Painel sempre habilitado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:23 +msgid "Forced aspect:" +msgstr "Forçar aspecto:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh:6 +msgid "Pressed Keys Panel" +msgstr "Painel de Teclas Pressionadas" + +#: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh:6 +msgid "Quick Menu Panel" +msgstr "Painel de Menu Instantâneo" + +#: qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh:6 +msgid "Race Timer Panel" +msgstr "Painel do Cronômetro de Corrida" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:16 +msgid "Panel enabled in teamgames" +msgstr "Painel habilitado em jogos de equipe" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:23 +msgid "Radar:" +msgstr "Radar:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:26 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:68 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:107 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:54 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:87 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:103 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:45 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:67 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:118 +#: qcsrc/menu/xonotic/util.qc:792 +msgid "Alpha:" +msgstr "Alfa:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:30 +msgid "Rotation:" +msgstr "Rotação:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:32 +msgid "Forward" +msgstr "Para a frente" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:33 +msgid "West" +msgstr "Para o oeste" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:34 +msgid "South" +msgstr "Para o sul" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:35 +msgid "East" +msgstr "Para o leste" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:36 +msgid "North" +msgstr "Para o norte" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:40 +msgid "Scale:" +msgstr "Escala:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:44 +msgid "Zoom mode:" +msgstr "Modo de zoom:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:46 +msgid "Zoomed in" +msgstr "Ampliado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:47 +msgid "Zoomed out" +msgstr "Afastado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:48 +msgid "Always zoomed" +msgstr "Sempre ampliado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:49 +msgid "Never zoomed" +msgstr "Nunca ampliado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_radar.qh:6 +msgid "Radar Panel" +msgstr "Painel do Radar" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:15 +msgid "Score:" +msgstr "Pontuação:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:18 +msgid "Rankings:" +msgstr "Classificações:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:19 +msgid "Off" +msgstr "Desligado" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:20 +msgid "And me" +msgstr "E eu" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qc:21 +msgid "Pure" +msgstr "Puro" + +#: qcsrc/menu/xonotic/dialog_hudpanel_score.qh:6 +msgid "Score Panel" +msgstr "Painel da Pontuação" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:14 +msgid "Timer:" +msgstr "Cronômetro:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qc:17 +msgid "Show elapsed time" +msgstr "Exibir tempo decorrido" + +#: qcsrc/menu/xonotic/dialog_hudpanel_timer.qh:6 +msgid "Timer Panel" +msgstr "Painel do Cronômetro" + +#: qcsrc/menu/xonotic/dialog_hudpanel_vote.qc:15 +msgid "Alpha after voting:" +msgstr "Alfa após votação:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_vote.qh:6 +msgid "Vote Panel" +msgstr "Painel de Votação" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:20 +msgid "Fade out after:" +msgstr "Desaparecer após:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:22 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:167 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:139 +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:55 +msgid "Never" +msgstr "Nunca" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:24 +#, c-format +msgid "%ds" +msgstr "%ds" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:28 +msgid "Fade effect:" +msgstr "Efeito de desaparecimento:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:31 +msgid "EF^None" +msgstr "Nenhum" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:32 +msgid "Alpha" +msgstr "Alfa" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:33 +msgid "Slide" +msgstr "Deslizar" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:34 +msgid "EF^Both" +msgstr "Ambos" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:38 +msgid "Weapon icons:" +msgstr "Ícones das armas:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:41 +msgid "Show only owned weapons" +msgstr "Exibir apenas armas obtidas" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:52 +msgid "Show weapon ID as:" +msgstr "Exibir o ID da arma como:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:53 +msgid "SHOWAS^None" +msgstr "Nenhum" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:54 +msgid "Number" +msgstr "Número" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:55 +msgid "Bind" +msgstr "Atalho" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:58 +msgid "Weapon ID scale:" +msgstr "Escala do ID da arma:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:64 +msgid "Show Accuracy" +msgstr "Exibir precisão" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:65 +msgid "Show Ammo" +msgstr "Exibir munições" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:68 +msgid "Ammo bar alpha:" +msgstr "Transparência da barra de munições:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc:74 +msgid "Ammo bar color:" +msgstr "Cor da barra de munições:" + +#: qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh:6 +msgid "Weapons Panel" +msgstr "Painel das Armas" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:19 +msgid "HUD skins" +msgstr "Visuais de interface" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:22 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:196 +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:31 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:42 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:25 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:35 +msgid "Filter:" +msgstr "Filtrar:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:30 +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:49 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:44 +msgid "Refresh" +msgstr "Atualizar" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:33 +#: qcsrc/menu/xonotic/dialog_settings_user.qc:30 +msgid "Set skin" +msgstr "Definir visual" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:37 +msgid "Save current skin" +msgstr "Salvar visual atual" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:46 +msgid "Panel background defaults:" +msgstr "Padrões de fundo do painel:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:48 +#: qcsrc/menu/xonotic/util.qc:767 +msgid "Background:" +msgstr "Fundo:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:50 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:62 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:77 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:116 +#: qcsrc/menu/xonotic/util.qc:770 qcsrc/menu/xonotic/util.qc:786 +#: qcsrc/menu/xonotic/util.qc:803 +msgid "Disable" +msgstr "Desabilitar" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:60 +#: qcsrc/menu/xonotic/util.qc:783 +msgid "Border size:" +msgstr "Tamanho das bordas:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:75 +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:114 +msgid "Team color:" +msgstr "Cor de equipe:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:83 +#: qcsrc/menu/xonotic/util.qc:809 +msgid "Test team color in configure mode" +msgstr "Testar cor de equipe no modo de configuração" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:86 +#: qcsrc/menu/xonotic/util.qc:812 +msgid "Padding:" +msgstr "Preenchimento:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:93 +msgid "HUD Dock:" +msgstr "Camada da interface:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:95 +msgid "DOCK^Disabled" +msgstr "Desabilitada" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:96 +msgid "DOCK^Small" +msgstr "Pequena" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:97 +msgid "DOCK^Medium" +msgstr "Média" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:98 +msgid "DOCK^Large" +msgstr "Grande" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:121 +msgid "Grid settings:" +msgstr "Configurações da rede:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:124 +msgid "Snap panels to grid" +msgstr "Fixar painéis à grade" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:127 +msgid "Grid size:" +msgstr "Tamanho da rede:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:129 +msgid "X:" +msgstr "X:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:136 +msgid "Y:" +msgstr "Y:" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:145 +msgid "Exit setup" +msgstr "Sair da configuração" + +#: qcsrc/menu/xonotic/dialog_hudsetup_exit.qh:6 +msgid "Panel HUD Setup" +msgstr "Painel de configuração da interface" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:13 +msgid "Monster:" +msgstr "Monstro:" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:22 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:20 +msgid "Spawn" +msgstr "Surgir" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:23 +#: qcsrc/menu/xonotic/serverlist.qc:268 +msgid "Remove" +msgstr "Remover" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:25 +msgid "Move target:" +msgstr "Mover alvo:" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:26 +msgid "Follow" +msgstr "Seguir" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:27 +msgid "Wander" +msgstr "Vaguear" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:28 +msgid "Spawnpoint" +msgstr "Ponto de surgimento" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:29 +msgid "No moving" +msgstr "Sem movimento" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:31 +msgid "Colors:" +msgstr "Cores:" + +#: qcsrc/menu/xonotic/dialog_monstertools.qc:33 +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:39 +msgid "Set skin:" +msgstr "Definir visual:" + +#: qcsrc/menu/xonotic/dialog_monstertools.qh:6 +msgid "Monster Tools" +msgstr "Ferramentas de Monstros" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:14 +msgid "Servers" +msgstr "Servidores" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:15 +msgid "Find servers to play on" +msgstr "Encontre servidores para jogar" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:17 +msgid "Host your own game" +msgstr "Crie a sua própria partida" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:18 +msgid "Media" +msgstr "Mídia" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qc:19 +msgid "Profile" +msgstr "Perfil" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qh:6 +msgid "Multiplayer" +msgstr "Multijogador" + +#: qcsrc/menu/xonotic/dialog_multiplayer.qh:7 +msgid "" +"Play online, against your friends in LAN, view demos or change player " +"settings" +msgstr "" +"Jogue online, jogue contra seus amigos em rede local, assista a demos ou " +"altere as configurações de jogador." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:46 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:134 +#: qcsrc/menu/xonotic/skinlist.qc:88 qcsrc/menu/xonotic/util.qc:769 +#: qcsrc/menu/xonotic/util.qc:785 qcsrc/menu/xonotic/util.qc:794 +#: qcsrc/menu/xonotic/util.qc:802 qcsrc/menu/xonotic/util.qc:814 +msgid "Default" +msgstr "Padrão" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:48 +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:64 +msgid "Unlimited" +msgstr "Ilimitado" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:66 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:78 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:128 +msgid "Frag limit:" +msgstr "Limite de execuções:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:66 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:78 +msgid "The amount of frags needed before the match will end" +msgstr "A quantidade de execuções necessária para acabar a partida" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:67 +msgid "Capture limit:" +msgstr "Limite de capturas:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:67 +msgid "The amount of captures needed before the match will end" +msgstr "A quantidade de capturas necessária para acabar a partida" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:68 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:73 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:74 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:75 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:76 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:77 +msgid "Point limit:" +msgstr "Limite de pontos:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:68 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:77 +msgid "The amount of points needed before the match will end" +msgstr "A quantidade de pontos necessária para acabar a partida" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:70 +msgid "Lives:" +msgstr "Vidas:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:71 +msgid "Laps:" +msgstr "Voltas:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 +msgid "Goals:" +msgstr "Gols:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:72 +msgid "The amount of goals needed before the match will end" +msgstr "A quantidade de gols necessária para acabar a partida" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:97 +msgid "Gametype" +msgstr "Modo de jogo" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:102 +msgid "Time limit:" +msgstr "Tempo limite:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:104 +msgid "Timelimit in minutes that when hit, will end the match" +msgstr "" +"Limite de tempo em minutos que, ao ser atingido, irá encerrar a partida." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:105 +#, c-format +msgid "%d minutes" +msgstr "%d minutos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:106 +msgid "TIMLIM^Default" +msgstr "Padrão" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:107 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:159 +msgid "1 minute" +msgstr "1 minuto" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:124 +msgid "TIMLIM^Infinite" +msgstr "Infinito" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:132 +msgid "Teams:" +msgstr "Equipes:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:135 +msgid "2 teams" +msgstr "2 equipes" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:136 +msgid "3 teams" +msgstr "3 equipes" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:137 +msgid "4 teams" +msgstr "4 equipes" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:140 +msgid "Player slots:" +msgstr "Vagas para jogadores:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:142 +msgid "" +"The maximum amount of players or bots that can be connected to your server " +"at once" +msgstr "" +"O número máximo de jogadores ou bots que podem estar conectados ao seu " +"servidor simultaneamente." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:144 +msgid "Number of bots:" +msgstr "Número de bots:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:146 +msgid "Amount of bots on your server" +msgstr "Quantidade de bots no seu servidor" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:148 +msgid "Bot skill:" +msgstr "Habilidade dos bots:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:151 +msgid "Specify how experienced the bots will be" +msgstr "Especifique a experiência dos bots" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:152 +msgid "Botlike" +msgstr "Perdido" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:153 +msgid "Beginner" +msgstr "Iniciante" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:154 +msgid "You will win" +msgstr "Você vai ganhar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:155 +msgid "You can win" +msgstr "Você pode ganhar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:156 +msgid "You might win" +msgstr "Você talvez ganhe" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:157 +msgid "Advanced" +msgstr "Avançado" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:158 +msgid "Expert" +msgstr "Experiente" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:159 +msgid "Pro" +msgstr "Profissional" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:160 +msgid "Assassin" +msgstr "Assassino" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:161 +msgid "Unhuman" +msgstr "Desumano" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:162 +msgid "Godlike" +msgstr "Divino" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:178 +msgid "Mutators..." +msgstr "Modificadores..." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:179 +msgid "Mutators and weapon arenas" +msgstr "Modificadores e arenas de armas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:188 +msgid "Maplist" +msgstr "Lista de mapas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:198 +msgid "" +"Click here or Ctrl-F to provide a keyword to narrow down the map list. Ctrl-" +"Delete to clear; Enter when done." +msgstr "" +"Clique aqui ou aperte Ctrl+F para digitar uma palavra chave e buscar o mapa " +"desejado. Você pode apertar Ctrl+Delete para apagar e Enter para confirmar." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:207 +msgid "Add shown" +msgstr "Adicionar exibidos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:208 +msgid "Add the maps shown in the list to your selection" +msgstr "Adiciona os mapas exibidos na lista para a sua seleção" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:211 +msgid "Remove shown" +msgstr "Remover exibidos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:212 +msgid "Remove the maps shown in the list from your selection" +msgstr "Remove os mapas exibidos na lista da sua seleção" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:217 +msgid "Add all" +msgstr "Adicionar todos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:218 +msgid "Add every available map to your selection" +msgstr "Adiciona todos os mapas disponíveis para a sua seleção" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:221 +msgid "Remove all" +msgstr "Remover todos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:222 +msgid "Remove all the maps from your selection" +msgstr "Remove todos os mapas da sua seleção" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:229 +msgid "Start Multiplayer!" +msgstr "Iniciar Multijogador!" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:58 +msgid "Title:" +msgstr "Título:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:64 +msgid "Author:" +msgstr "Autor:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:70 +msgid "Game types:" +msgstr "Modos de jogo:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:93 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:296 +msgid "Close" +msgstr "Fechar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:96 +msgid "MAP^Play" +msgstr "Jogar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh:7 +msgid "Map Information" +msgstr "Informações do Mapa" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:28 +msgid "All Weapons Arena" +msgstr "Arena com Todas as Armas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:30 +msgid "Most Weapons Arena" +msgstr "Arena com Maior Parte das Armas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:49 +#, c-format +msgid "%s Arena" +msgstr "%s Arena" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:61 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:159 +msgid "Dodging" +msgstr "Esquiva" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:63 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:267 +msgid "InstaGib" +msgstr "InstaGib" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:65 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:216 +msgid "New Toys" +msgstr "Novos Brinquedos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:67 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:272 +msgid "NIX" +msgstr "NIX" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:69 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:220 +msgid "Rocket Flying" +msgstr "Voar com Foguetes" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:71 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:212 +msgid "Invincible Projectiles" +msgstr "Projéteis Invencíveis" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:75 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:282 +msgid "No start weapons" +msgstr "Sem armas iniciais" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:77 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:195 +msgid "Low gravity" +msgstr "Pouca gravidade" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:79 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:166 +msgid "Cloaked" +msgstr "Oculto" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:81 +msgid "Hook" +msgstr "Gancho" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:83 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:173 +msgid "Midair" +msgstr "No ar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:87 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:224 +msgid "Piñata" +msgstr "Piñata" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:89 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:229 +msgid "Weapons stay" +msgstr "Armas permanescentes" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:91 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:184 +msgid "Blood loss" +msgstr "Perda de sangue" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:93 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:208 +msgid "Jet pack" +msgstr "Mochila a Jato" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:95 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:170 +msgid "Buffs" +msgstr "Bônus (buffs)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:97 +msgid "Overkill" +msgstr "Exagero" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:99 +msgid "No powerups" +msgstr "Sem potencializadores" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:101 +msgid "Powerups" +msgstr "Potencializadores" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:103 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:163 +msgid "Touch explode" +msgstr "Toque explosivo" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:105 +msgid "MUT^None" +msgstr "Nenhum" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:156 +msgid "Gameplay mutators:" +msgstr "Modificadores de jogabilidade:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:160 +msgid "Enable dodging" +msgstr "Habilitar esquiva" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:167 +msgid "All players are almost invisible" +msgstr "Todos jogadores ficarão quase invisíveis" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:174 +msgid "Only possible to inflict damage on your enemy while he's airborne" +msgstr "" +"Só é possível causar dano aos seus inimigos enquanto eles estiverem no ar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:178 +msgid "Damage done to your enemy gets added to your own health" +msgstr "O dano causado aos seus inimigos será adicionado à sua saúde" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:183 +msgid "" +"Amount of health below which your player gets stunned because of blood loss" +msgstr "" +"Quantidade de saúde abaixo a qual o seu jogador permanecerá atordoado devido " +"à perda de sangue" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:192 +msgid "Make things fall to the ground slower, lower value means lower gravity" +msgstr "" +"Faz com que as coisas caiam no chão mais devagar. Valores mais baixos " +"significam gravidade mais baixa" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:201 +msgid "Weapon & item mutators:" +msgstr "Modificadores de armas e itens:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:204 +msgid "Grappling hook" +msgstr "Gancho de escalada" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:205 +msgid "Players spawn with the grappling hook" +msgstr "Jogadores surgem com o gancho" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:209 +msgid "Players spawn with the jetpack" +msgstr "Jogadores surgem com a mochila a jato" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:225 +msgid "Players will drop all weapons they possessed when they are killed" +msgstr "" +"Ao morrerem, jogadores irão deixar cair no chão todas as armas que tinham" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:230 +msgid "Weapons stay after they are picked up" +msgstr "Armas permanecem no chão após serem coletadas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:235 +msgid "Regular (no arena)" +msgstr "Normal (sem arena)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:237 +msgid "Weapon arenas:" +msgstr "Arenas de armas:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:238 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:256 +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:261 +msgid "" +"Selecting a weapon arena will give all players that weapon at spawn as well " +"as unlimited ammo, and disable all other weapon pickups." +msgstr "" +"Selecionar uma arena de armas concederá a todos os jogadores a arma " +"selecionada ao surgirem bem como munição ilimitada. Todas as outras armas " +"ficarão indisponíveis no mapa." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:255 +msgid "Most weapons" +msgstr "Maior parte das armas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:260 +msgid "All weapons" +msgstr "Todas as armas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:264 +msgid "Special arenas:" +msgstr "Arenas especiais:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:268 +msgid "" +"Players will be given only one weapon, which can instantly kill the opponent " +"with a single shot. If the player runs out of ammo, he will have 10 seconds " +"to find some or if he fails to do so, face death. The secondary fire mode " +"does not inflict any damage but is good for doing trickjumps." +msgstr "" +"Os jogadores terão uma arma, a qual pode instantaneamente matar o oponente " +"com um único disparo. Se o jogador ficar sem munição, ele terá 10 segundos " +"para encontrar alguma e se não conseguir fazer isso, irá morrer. O modo de " +"disparo secundário não causa nenhum dano, mas é útil para executar truques " +"de movimento." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:273 +msgid "" +"No items Xonotic - instead of pickup items, everyone plays with the same " +"weapon. After some time, a countdown will start, after which everyone will " +"switch to another weapon." +msgstr "" +"Sem itens Xonotic - em vez de pegar itens espalhados pelo mapa, todo mundo " +"joga com a mesma arma. Depois de um certo tempo, uma contagem regressiva irá " +"iniciar, e depois disso todos irão trocar para uma outra arma." + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:277 +msgid "with blaster" +msgstr "com blaster" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:278 +msgid "Always carry the blaster as an additional weapon in Nix" +msgstr "Sempre carregue a blaster como uma arma adicional em Nix" + +#: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh:9 +msgid "Mutators" +msgstr "Modificadores" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:38 +msgid "SRVS^Categories" +msgstr "Categorias" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:41 +msgid "SRVS^Empty" +msgstr "Vazio" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:42 +msgid "Show empty servers" +msgstr "Exibir servidores vazios" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:46 +msgid "SRVS^Full" +msgstr "Cheio" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:47 +msgid "Show full servers that have no slots available" +msgstr "Exibir servidores cheios que não contêm vagas disponíveis" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:51 +msgid "Pause" +msgstr "Pausar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:52 +msgid "" +"Pause updating the server list to prevent servers from \"jumping around\"" +msgstr "" +"Pausa a atualização da lista de servidores para evitar que os servidores " +"fiquem saindo do lugar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 +msgid "Reload the server list" +msgstr "Atualizar a lista de servidores" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:67 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:223 +msgid "Address:" +msgstr "Endereço:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:78 +msgid "Info..." +msgstr "Informações..." + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:79 +msgid "Show more information about the currently highlighted server" +msgstr "Exibir mais informações sobre o servidor atualmente destacado" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:84 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:303 +msgid "Join!" +msgstr "Conectar!" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:154 +#: qcsrc/menu/xonotic/serverlist.qc:1061 +msgid "MOD^Default" +msgstr "Padrão" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +#, c-format +msgid "%d modified" +msgstr "%d modificadas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 +msgid "Official" +msgstr "Oficial" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:169 +msgid "N/A (auth library missing, can't connect)" +msgstr "" +"N/A (biblioteca de autenticação não encontrada, não foi possível se conectar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:171 +msgid "N/A (auth library missing)" +msgstr "N/A (biblioteca de autenticação não encontrada)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:177 +msgid "Not supported (can't connect)" +msgstr "Não suportado (não foi possível se conectar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:179 +msgid "Not supported (won't encrypt)" +msgstr "Não suportado (não encriptará)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:183 +msgid "Supported (will encrypt)" +msgstr "Suportado (irá encriptar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:185 +msgid "Supported (won't encrypt)" +msgstr "Suportado (não encriptará)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:189 +msgid "Requested (will encrypt)" +msgstr "Solicitado (irá encriptar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:191 +msgid "Requested (won't encrypt)" +msgstr "Solicitado (não encriptará)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:195 +msgid "Required (can't connect)" +msgstr "Necessário (não foi possível se conectar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:197 +msgid "Required (will encrypt)" +msgstr "Necessário (irá encriptar)" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:217 +msgid "Hostname:" +msgstr "Servidor:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:231 +msgid "Gametype:" +msgstr "Modo de jogo:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:236 +msgid "Map:" +msgstr "Mapa:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:241 +msgid "Mod:" +msgstr "Mod:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:246 +msgid "Version:" +msgstr "Versão:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:251 +msgid "Settings:" +msgstr "Configurações:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:258 +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:290 +msgid "Players:" +msgstr "Jogadores:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:263 +msgid "Bots:" +msgstr "Bots:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:268 +msgid "Free slots:" +msgstr "Vagas livres:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:274 +msgid "Encryption:" +msgstr "Encriptação:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:279 +msgid "ID:" +msgstr "ID:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:284 +msgid "Key:" +msgstr "Chave:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh:7 +msgid "Server Information" +msgstr "Informações do Servidor" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:25 +msgid "Demos" +msgstr "Demos" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:26 +msgid "Screenshots" +msgstr "Capturas de tela" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media.qc:27 +msgid "Music Player" +msgstr "Reprodutor de Músicas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:48 +msgid "Auto record demos" +msgstr "Gravar demos automaticamente" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:57 +msgid "Timedemo" +msgstr "Executar benchmark" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:58 +msgid "Benchmark how fast your computer can run the highlighted demo" +msgstr "" +"Executa um teste de desempenho para saber quão rápido seu computador pode " +"rodar a demo destacada" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qc:62 +msgid "DEMO^Play" +msgstr "Reproduzir" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:13 +msgid "Playing a demo will disconnect you from the current match." +msgstr "Reproduzir uma demo irá desconectar você da partida atual." + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:15 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:15 +msgid "Do you really wish to disconnect now?" +msgstr "Você realmente deseja desconectar agora?" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh:6 +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh:6 +msgid "Disconnect" +msgstr "Desconectar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:13 +msgid "Timing a demo will disconnect you from the current match." +msgstr "Executar benchmark em uma demo irá desconectá-lo da partida atual." + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:37 +msgid "MUSICPL^Add" +msgstr "Adicionar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:40 +msgid "MUSICPL^Add all" +msgstr "Adicionar todas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:44 +msgid "Set as menu track" +msgstr "Definir como música do menu" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:48 +msgid "Reset default menu track" +msgstr "Redefinir música padrão do menu" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:54 +msgid "Playlist:" +msgstr "Lista de reprodução:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:55 +msgid "Random order" +msgstr "Ordem aleatória" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:60 +msgid "MUSICPL^Stop" +msgstr "Parar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:63 +msgid "MUSICPL^Play" +msgstr "Tocar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:66 +msgid "MUSICPL^Pause" +msgstr "Pausar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:69 +msgid "MUSICPL^Prev" +msgstr "Anterior" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:72 +msgid "MUSICPL^Next" +msgstr "Seguinte" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:76 +msgid "MUSICPL^Remove" +msgstr "Remover" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:79 +msgid "MUSICPL^Remove all" +msgstr "Remover todas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 +msgid "Auto screenshot scoreboard" +msgstr "Tirar capturas de tela automaticamente do placar" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:63 +msgid "Open in the viewer" +msgstr "Abrir no visualizador" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:139 +msgid "Reset" +msgstr "Redefinir" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:144 +msgid "Previous" +msgstr "Anterior" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:147 +msgid "Next" +msgstr "Seguinte" + +#: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc:152 +msgid "Slide show" +msgstr "Apresentação de slides" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:34 +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:21 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:37 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:25 +#: qcsrc/menu/xonotic/dialog_settings_user.qc:20 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:21 +msgid "Apply immediately" +msgstr "Aplicar imediatamente" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:48 +msgid "Name" +msgstr "Nome" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:77 +msgid "Model" +msgstr "Modelo" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:96 +msgid "Glowing color" +msgstr "Cor brilhante" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:106 +msgid "Detail color" +msgstr "Cor do detalhe" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:121 +msgid "Statistics" +msgstr "Estatísticas" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:125 +msgid "Allow player statistics to track your client" +msgstr "Permitir que as estatísticas de jogadores rastreiem o seu cliente" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:129 +msgid "Allow player statistics to use your nickname" +msgstr "Permitir que as estatísticas de jogadores usem o seu apelido" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:145 +msgid "Country" +msgstr "Idioma" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:159 +msgid "Gender:" +msgstr "Gênero:" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:161 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:174 +msgid "Undisclosed" +msgstr "Não revelado" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:162 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:172 +msgid "Female" +msgstr "Feminino" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:163 +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:173 +msgid "Male" +msgstr "Masculino" + +#: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:166 +msgid "Gender" +msgstr "Gênero" + +#: qcsrc/menu/xonotic/dialog_quit.qc:11 +msgid "Are you sure you want to quit?" +msgstr "Tem certeza de que deseja sair?" + +#: qcsrc/menu/xonotic/dialog_quit.qc:15 +msgid "Back to work..." +msgstr "De volta ao trabalho..." + +#: qcsrc/menu/xonotic/dialog_quit.qc:17 +msgid "I got some more fragging to do!" +msgstr "Está na hora das execuções!" + +#: qcsrc/menu/xonotic/dialog_quit.qh:7 +msgid "Quit the game" +msgstr "Sair do jogo" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:15 +msgid "Model:" +msgstr "Modelo:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:21 +msgid "Remove *" +msgstr "Remover *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:23 +msgid "Copy *" +msgstr "Copiar *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:24 +msgid "Paste" +msgstr "Colar" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:26 +msgid "Bone:" +msgstr "Osso:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:31 +msgid "Set * as child" +msgstr "Definir * como criança" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:32 +msgid "Attach to *" +msgstr "Anexar à *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:34 +msgid "Detach from *" +msgstr "Separar de *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:37 +msgid "Visual object properties for *:" +msgstr "Propriedades de objeto visual para *:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:41 +msgid "Set alpha:" +msgstr "Definir alfa:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:44 +msgid "Set color main:" +msgstr "Definir cor principal:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:46 +msgid "Set color glow:" +msgstr "Definir cor do brilho:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:50 +msgid "Set frame:" +msgstr "Definir frame:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:54 +msgid "Physical object properties for *:" +msgstr "Propriedades de objeto físico para *:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:56 +msgid "Set material:" +msgstr "Definir material:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:62 +msgid "Set solidity:" +msgstr "Definir solidez:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:63 +msgid "Non-solid" +msgstr "Não sólido" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:64 +msgid "Solid" +msgstr "Sólido" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:65 +msgid "Set physics:" +msgstr "Definir física:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:66 +msgid "Static" +msgstr "Estática" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:67 +msgid "Movable" +msgstr "Movível" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:68 +msgid "Physical" +msgstr "Físico" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:70 +msgid "Set scale:" +msgstr "Definir escala:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:72 +msgid "Set force:" +msgstr "Definir força:" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:76 +msgid "Claim *" +msgstr "Resgatar *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:78 +msgid "* object info" +msgstr "Informações de objeto *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:79 +msgid "* mesh info" +msgstr "Informações de malha *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:80 +msgid "* attachment info" +msgstr "Informações de extras *" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:81 +msgid "Show help" +msgstr "Exibir ajuda" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qc:82 +msgid "* is the object you are facing" +msgstr "* é o objeto para o qual você está virado" + +#: qcsrc/menu/xonotic/dialog_sandboxtools.qh:6 +msgid "Sandbox Tools" +msgstr "Ferramentas Sandbox" + +#: qcsrc/menu/xonotic/dialog_settings.qc:18 +msgid "Video" +msgstr "Vídeo" + +#: qcsrc/menu/xonotic/dialog_settings.qc:19 +msgid "Effects" +msgstr "Efeitos" + +#: qcsrc/menu/xonotic/dialog_settings.qc:20 +msgid "Audio" +msgstr "Áudio" + +#: qcsrc/menu/xonotic/dialog_settings.qc:22 +msgid "Game" +msgstr "Jogo" + +#: qcsrc/menu/xonotic/dialog_settings.qc:23 +msgid "Input" +msgstr "Entrada" + +#: qcsrc/menu/xonotic/dialog_settings.qc:24 +msgid "User" +msgstr "Usuário" + +#: qcsrc/menu/xonotic/dialog_settings.qc:25 +#: qcsrc/menu/xonotic/keybinder.qc:105 +msgid "Misc" +msgstr "Diversos" + +#: qcsrc/menu/xonotic/dialog_settings.qh:6 +msgid "Settings" +msgstr "Configurações" + +#: qcsrc/menu/xonotic/dialog_settings.qh:7 +msgid "Change the game settings" +msgstr "Altere as configurações do jogo" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:29 +msgid "Master:" +msgstr "Principal:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:35 +msgid "Music:" +msgstr "Música:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:43 +msgid "VOL^Ambient:" +msgstr "Ambiente:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:50 +msgid "Info:" +msgstr "Informação:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:57 +msgid "Items:" +msgstr "Itens:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:64 +msgid "Pain:" +msgstr "Dor:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:71 +msgid "Player:" +msgstr "Jogador:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:78 +msgid "Shots:" +msgstr "Disparos:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:85 +msgid "Voice:" +msgstr "Voz:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:93 +msgid "Weapons:" +msgstr "Armas:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:99 +msgid "New style sound attenuation" +msgstr "Novo estilo de atenuação de som" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:102 +msgid "Mute sounds when not active" +msgstr "Desabilita o som enquanto estiver em segundo plano" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:105 +msgid "Frequency:" +msgstr "Frequência:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:107 +msgid "Sound output frequency" +msgstr "Frequência da saída de som" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:108 +msgid "8 kHz" +msgstr "8 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:109 +msgid "11.025 kHz" +msgstr "11.025 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:110 +msgid "16 kHz" +msgstr "16 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:111 +msgid "22.05 kHz" +msgstr "22.05 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:112 +msgid "24 kHz" +msgstr "24 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:113 +msgid "32 kHz" +msgstr "32 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:114 +msgid "44.1 kHz" +msgstr "44.1 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:115 +msgid "48 kHz" +msgstr "48 kHz" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:119 +msgid "Channels:" +msgstr "Canais:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:121 +msgid "Number of channels for the sound output" +msgstr "Número de canais para a saída de som" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:122 +msgid "Mono" +msgstr "Mono" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:123 +msgid "Stereo" +msgstr "Estéreo" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:124 +msgid "2.1" +msgstr "2.1" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:125 +msgid "4" +msgstr "4" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:126 +msgid "5" +msgstr "5" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:127 +msgid "5.1" +msgstr "5.1" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:128 +msgid "6.1" +msgstr "6.1" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:129 +msgid "7.1" +msgstr "7.1" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:134 +msgid "Swap stereo output channels" +msgstr "Trocar canais de saída estéreo de lugar" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:135 +msgid "Swap left/right channels" +msgstr "Troca de lugar os canais esquerdo e direito" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:138 +msgid "Headphone friendly mode" +msgstr "Modo de fones de ouvido" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:139 +msgid "" +"Enable spatialization (blend the right and left channel slightly to decrease " +"stereo separation a bit for headphones)" +msgstr "" +"Habilita espacialização (combina levemente os canais esquerdo e direito para " +"diminuir um pouco a separação de estéreo para fones de ouvido)" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:143 +msgid "Hit indication sound" +msgstr "Som indicador de disparo acertado" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:144 +msgid "Play a hit indicator sound when your shot hits an enemy" +msgstr "Reproduz um som indicando que você acertou um inimigo" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:147 +msgid "Chat message sound" +msgstr "Som de mensagem do bate-papo" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:149 +msgid "Menu sounds" +msgstr "Sons do menu" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:150 +msgid "Play sounds when clicking menu items" +msgstr "Reproduz sons quando você clica nas opções do menu" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:151 +msgid "Focus sounds" +msgstr "Sons de foco" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:152 +msgid "Play sounds when hovering over menu items too" +msgstr "Reproduz sons quando você passa o mouse sobre as opções do menu também" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:156 +msgid "Time announcer:" +msgstr "Aviso de tempo:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:158 +msgid "WRN^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:160 +msgid "5 minutes" +msgstr "5 minutos" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:161 +msgid "WRN^Both" +msgstr "Ambos" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:164 +msgid "Automatic taunts:" +msgstr "Provocações automáticas:" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:166 +msgid "Automatically taunt enemies after fragging them" +msgstr "Provocar inimigos automaticamente depois de executá-los" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:168 +msgid "Sometimes" +msgstr "Às vezes" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:169 +msgid "Often" +msgstr "Frequentemente" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:170 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:141 +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:57 +msgid "Always" +msgstr "Sempre" + +#: qcsrc/menu/xonotic/dialog_settings_audio.qc:176 +msgid "Debug info about sounds" +msgstr "Depurar informações sobre sons" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:41 +msgid "Quality preset:" +msgstr "Predefinição de qualidade:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:45 +msgid "PRE^OMG!" +msgstr "MEU DEUS!" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:48 +msgid "PRE^Low" +msgstr "Baixa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:50 +msgid "PRE^Medium" +msgstr "Média" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:52 +msgid "PRE^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:54 +msgid "PRE^High" +msgstr "Alta" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:56 +msgid "PRE^Ultra" +msgstr "Ultra" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:60 +msgid "PRE^Ultimate" +msgstr "Máxima" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:65 +msgid "Geometry detail:" +msgstr "Detalhes da geometria:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:67 +msgid "Change the smoothness of the curves on the map (default: normal)" +msgstr "Altera a suavização das curvas no mapa (padrão: normal)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:68 +msgid "DET^Lowest" +msgstr "Mínimo" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:69 +msgid "DET^Low" +msgstr "Baixo" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:70 +msgid "DET^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:71 +msgid "DET^Good" +msgstr "Bom" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:72 +msgid "DET^Best" +msgstr "Melhor" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:73 +msgid "DET^Insane" +msgstr "Insano" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:77 +msgid "Player detail:" +msgstr "Detalhes do jogador:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:79 +msgid "PDET^Low" +msgstr "Baixo" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:80 +msgid "PDET^Medium" +msgstr "Médio" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:81 +msgid "PDET^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:82 +msgid "PDET^Good" +msgstr "Bom" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:83 +msgid "PDET^Best" +msgstr "Melhor" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:87 +msgid "Texture resolution:" +msgstr "Resolução das texturas:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:91 +msgid "RES^Leet" +msgstr "Elite" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:92 +msgid "RES^Lowest" +msgstr "Mínima" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:93 +msgid "RES^Very low" +msgstr "Muito baixa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:94 +msgid "RES^Low" +msgstr "Baixa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:95 +msgid "RES^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:96 +msgid "RES^Good" +msgstr "Boa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:97 +msgid "RES^Best" +msgstr "Melhor" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:110 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:115 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:120 +msgid "Avoid lossy texture compression" +msgstr "Evitar compressão de texturas com perdas" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:131 +msgid "Show surfaces" +msgstr "Exibir superfícies" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:132 +msgid "" +"Disable textures completely for very slow hardware. This gives a huge " +"performance boost, but looks very ugly. (default: disabled)" +msgstr "" +"Desabilita completamente as texturas para PCs de baixo desempenho. Isso " +"garante uma alto ganho de desempenho, mas deixa o jogo muito feio. (padrão: " +"desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:135 +msgid "Use lightmaps" +msgstr "Usar lightmaps" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:136 +msgid "" +"Use high resolution lightmaps, which will look pretty but use up some extra " +"video memory (default: enabled)" +msgstr "" +"Usa lightmaps de alta resolução, os quais ficarão elegantes, mas irão usar " +"um pouco mais de memória (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:138 +msgid "Deluxe mapping" +msgstr "Mapeamento deluxe" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:139 +msgid "Use per-pixel lighting effects (default: enabled)" +msgstr "Usa efeitos de iluminação por pixel (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:141 +msgid "Gloss" +msgstr "Lustro" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:142 +msgid "" +"Enable the use of glossmaps on textures supporting it (default: enabled)" +msgstr "" +"Habilita o uso de glossmaps em texturas que suportam esse recurso (padrão: " +"habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:145 +msgid "Offset mapping" +msgstr "Mapeamento por paralaxe" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:146 +msgid "" +"Offset mapping effect that will make textures with bumpmaps appear like they " +"\"pop out\" of the flat 2D surface (default: disabled)" +msgstr "" +"Efeito de mapeamento por paralaxe que fará as texturas com bumpmaps " +"parecerem que estão \"saindo\" da superfície plana em 2D (padrão: " +"desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:148 +msgid "Relief mapping" +msgstr "Mapeamento de relevo" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:149 +msgid "" +"Higher quality offset mapping, which also has a huge impact on performance " +"(default: disabled)" +msgstr "" +"Mapeamento por paralaxe de maior qualidade, o qual também causa um grande " +"impacto no desempenho (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:152 +msgid "Reflections:" +msgstr "Reflexos:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:153 +msgid "" +"Reflection and refraction quality, has a huge impact on performance on maps " +"with reflecting surfaces (default: disabled)" +msgstr "" +"Qualidade de reflexos e refrações. Causa um grande impacto no desempenho em " +"mapas que contenham superfícies com reflexos (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:156 +msgid "Resolution of reflections/refractions (default: good)" +msgstr "Resolução dos reflexos e refrações (padrão: boa)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:157 +msgid "Blurred" +msgstr "Borrados" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:158 +msgid "REFL^Good" +msgstr "Boa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:159 +msgid "Sharp" +msgstr "Alta" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:163 +msgid "Decals" +msgstr "Decalques" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:164 +msgid "Enable decals (bullet holes and blood) (default: enabled)" +msgstr "Habilita decalques (buracos de bala e sangue) (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:165 +msgid "Decals on models" +msgstr "Decalques em modelos" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:169 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:253 +msgid "Distance:" +msgstr "Distância:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:172 +msgid "Decals further away than this will not be drawn (default: 300)" +msgstr "Decalques mais distantes que isso não serão desenhados (padrão: 300)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:176 +msgid "Time:" +msgstr "Tempo:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:179 +msgid "Time in seconds before decals fade away (default: 2)" +msgstr "Tempo em segundos antes de decalques desaparecerem (padrão: 2)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:183 +msgid "Damage effects:" +msgstr "Efeitos de dano:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:185 +msgid "DMGFX^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:186 +msgid "Skeletal" +msgstr "Esquelético" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:187 +msgid "DMGFX^All" +msgstr "Todos" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:191 +msgid "No dynamic lighting" +msgstr "Desabilitar iluminação dinâmica" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:192 +msgid "Enable corona flares around certain lights (default: enabled)" +msgstr "Habilita luzes de corona ao redor de certas luzes (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:194 +msgid "Fake corona lighting" +msgstr "Iluminação de coronas falsa" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:195 +msgid "" +"Enable faster but uglier dynamic lights by rendering bright coronas instead " +"of real dynamic lights (default: disabled)" +msgstr "" +"Habilita luzes dinâmicas mais rápidas porém mais feias renderizando coronas " +"brilhantes em vez de luzes dinâmicas reais (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:198 +msgid "Realtime dynamic lighting" +msgstr "Iluminação dinâmica em tempo real" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:199 +msgid "" +"Enable rendering of dynamic lights such as explosions and rocket lights " +"(default: enabled)" +msgstr "" +"Habilita a renderização de luzes dinâmicas como explosões e luzes de " +"foguetes (padrão: habilitada)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:201 +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:207 +msgid "Shadows" +msgstr "Sombras" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:202 +msgid "Enable rendering of shadows from dynamic lights (default: disabled)" +msgstr "" +"Habilita a renderização de sombras a partir de luzes dinâmicas (padrão: " +"desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:205 +msgid "Realtime world lighting" +msgstr "Iluminação de mundo em tempo real" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:206 +msgid "" +"Enable rendering of full realtime world lighting on maps that support it. " +"Note that this might have a big impact on performance. (default: disabled)" +msgstr "" +"Habilita a renderização de iluminação de mundo em tempo real em mapas que a " +"suportam. Note que isso pode causar um grande impacto no desempenho (padrão: " +"desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:208 +msgid "" +"Enable rendering of shadows from realtime world lights (default: disabled)" +msgstr "" +"Habilita a renderização de sombras de luzes de mundo em tempo real (padrão: " +"desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:212 +msgid "Use normal maps" +msgstr "Usar normal maps" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:213 +msgid "Enable use of directional shading on textures (default: enabled)" +msgstr "Habilita o uso de shaders direcionais em texturas (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:215 +msgid "Soft shadows" +msgstr "Sombras suaves" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:219 +msgid "Fade corona according to visibility" +msgstr "Enfraquecer corona de acordo com a visibilidade" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:220 +msgid "Fade coronas according to visibility (default: enabled)" +msgstr "Enfraquece coronas de acordo com a visibilidade (padrão: habilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:224 +msgid "Bloom" +msgstr "Bloom" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:225 +msgid "" +"Enable bloom effect, which brightens the neighboring pixels of very bright " +"pixels. Has a big impact on performance. (default: disabled)" +msgstr "" +"Habilita o efeito bloom, o qual ilumina os pixeis próximos de pixeis muito " +"brilhantes. Causa um grande impacto no desempenho (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:226 +msgid "Extra postprocessing effects" +msgstr "Efeitos extras de pós-processamento" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:227 +msgid "" +"Enables special postprocessing effects for when damaged or under water or " +"using a powerup (default: disabled)" +msgstr "" +"Habilita efeitos especiais de pós-processamento para quando receber dano, " +"estar debaixo d'água ou ao usar potencializadores (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:232 +msgid "Motion blur strength - 0.4 recommended" +msgstr "Intensidade do desfoque de movimento - 0.4 recomendado" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:233 +msgid "Motion blur:" +msgstr "Desfoque de movimento:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:239 +msgid "Particles" +msgstr "Partículas" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:240 +msgid "Spawnpoint effects" +msgstr "Efeitos de ponto de surgimento" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:241 +msgid "Particles effects at all spawn points and whenever a player spawns" +msgstr "" +"Efeitos de partículas em todos os pontos de surgimento e sempre que um " +"jogador nascer" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:246 +msgid "Quality:" +msgstr "Qualidade:" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:249 +msgid "" +"Multiplier for amount of particles. Less means less particles, which in turn " +"gives for better performance (default: 1.0)" +msgstr "" +"Multiplicador para a quantidade de partículas. Menos significa menos " +"partículas, o que resulta em um melhor desempenho (padrão: 1.0)" + +#: qcsrc/menu/xonotic/dialog_settings_effects.qc:256 +msgid "Particles further away than this will not be drawn (default: 1000)" +msgstr "Partículas mais distantes que isso não serão desenhadas (padrão: 1000)" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:31 +msgid "No crosshair" +msgstr "Sem retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:33 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:62 +msgid "Per weapon" +msgstr "Por arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:34 +msgid "" +"Set a different crosshair for each weapon, good if you play without weapon " +"models" +msgstr "" +"Define um retículo diferente para cada uma das armas, uma boa opção caso " +"você jogue sem os modelos das armas na tela" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:48 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:81 +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:97 +msgid "Size:" +msgstr "Tamanho:" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:64 +msgid "By health" +msgstr "Por saúde" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:76 +msgid "Use rings to indicate weapon status" +msgstr "Usar anéis para indicar status da arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:93 +msgid "Enable center crosshair dot" +msgstr "Habilitar ponto no centro do retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:111 +msgid "Use normal crosshair color" +msgstr "Usa cor normal do retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:122 +msgid "Smooth effects of crosshairs" +msgstr "Suavizar efeitos dos retículos" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:125 +msgid "Hit testing:" +msgstr "Teste de acerto:" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:128 +msgid "" +"None: do not do hit tests for the crosshair; TrueAim: blur the crosshair " +"when there's an obstacle between your gun and the target; Enemies: also " +"enlarge the crosshair when you would hit an enemy" +msgstr "" +"Nenhum: não realiza testes de acerto para o retículo; Retículo Real: desfoca " +"o retículo quando há um obstáculo entre a sua arma e o alvo; Inimigos: o " +"retículo também é ampliado quando você acertaria um inimigo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:129 +msgid "HTTST^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:130 +msgid "HTTST^TrueAim" +msgstr "Mira Real" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:131 +msgid "HTTST^Enemies" +msgstr "Inimigos" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:136 +msgid "Blur crosshair if the shot is obstructed" +msgstr "Borrar retículo se o disparo for obstruído" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:140 +msgid "Enlarge crosshair if targeting an enemy" +msgstr "Ampliar retículo ao focar em um inimigo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:143 +msgid "Animate crosshair when hitting an enemy" +msgstr "Animar retículo ao acertar um inimigo" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:146 +msgid "Animate crosshair when picking up an item" +msgstr "Animar retículo ao pegar um item" + +#: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh:7 +msgid "Crosshair" +msgstr "Retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:48 +msgid "Fading speed:" +msgstr "Vel. de desaparecimento:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:51 +msgid "Enable rows / columns highlighting" +msgstr "Habilitar destacamento de fileiras/colunas" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:55 +msgid "Show decimals in respawn countdown" +msgstr "Exibir decimais na contagem de ressurgimento" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:57 +msgid "Show accuracy underneath scoreboard" +msgstr "Exibir precisão embaixo do placar" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:61 +msgid "Waypoints" +msgstr "Caminhos" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:63 +msgid "Display waypoint markers for objectives on the map" +msgstr "Mostra os marcadores de caminhos para objetivos no mapa" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:64 +msgid "Show various gametype specific waypoints" +msgstr "Mostra diversos caminhos específicos de modos de jogo" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:70 +msgid "Control transparency of the waypoints" +msgstr "Transparência dos caminhos" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:74 +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:124 +msgid "Fontsize:" +msgstr "Tamanho da fonte:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:80 +msgid "Edge offset:" +msgstr "Extremidade:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:89 +msgid "Fade when near the crosshair" +msgstr "Desvanecer ao se aproximar do retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:94 +msgid "Damage" +msgstr "Dano" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:96 +msgid "Overlay:" +msgstr "Sobreposição:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:99 +msgid "Factor:" +msgstr "Fator:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:104 +msgid "Fade rate:" +msgstr "Taxa de desaparecimento:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:112 +msgid "Player Names" +msgstr "Nomes de Jogadores" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:114 +msgid "Show names above players" +msgstr "Exibir nomes sobre jogadores" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:130 +msgid "Max distance:" +msgstr "Distância máxima:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:136 +msgid "Decolorize:" +msgstr "Descoloração:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:140 +#: qcsrc/menu/xonotic/keybinder.qc:99 +msgid "Teamplay" +msgstr "Equipe" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:148 +msgid "Only when near crosshair" +msgstr "Apenas quando próximo ao retículo" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:152 +msgid "Display health and armor" +msgstr "Exibir saúde e armadura" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:157 +msgid "Damage overlay:" +msgstr "Sobreposição do dano:" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:160 +msgid "Dynamic HUD" +msgstr "Interface dinâmica" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:161 +msgid "HUD moves around following player's movement" +msgstr "A interface se move de acordo com o movimento do jogador" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:163 +msgid "Shake the HUD when hurt" +msgstr "Vibrar a interface ao receber dano" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:167 +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh:6 +msgid "Enter HUD editor" +msgstr "Entrar no editor de interface" + +#: qcsrc/menu/xonotic/dialog_settings_game_hud.qh:7 +msgid "HUD" +msgstr "Interface" + +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:21 +msgid "In order for the HUD editor to show, you must first be in game." +msgstr "" +"Para abrir o editor de interface, é necessário estar jogando em um mapa." + +#: qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qc:23 +msgid "Do you wish to start a local game to set up the HUD?" +msgstr "Quer iniciar um jogo local para personalizar a interface?" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:24 +msgid "Frag Information" +msgstr "Informações de Execuções" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:26 +msgid "Display information about killing sprees" +msgstr "Exibir informação sobre sequências de mortes" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:29 +msgid "Only display sprees if they are achievements" +msgstr "Apenas exibir sequências se forem conquistas" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:34 +msgid "Show spree information in centerprints" +msgstr "Exibir informação de sequências em impressões centrais" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:38 +msgid "Show spree information in death messages" +msgstr "Exibir informação de sequências em mensagens de morte" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:43 +msgid "Sprees in info messages:" +msgstr "Sequências em mensagens de informação:" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:46 +msgid "SPREES^Disabled" +msgstr "Desabilitadas" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:47 +msgid "Target" +msgstr "Alvo" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:48 +msgid "Attacker" +msgstr "Atacante" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:49 +msgid "SPREES^Both" +msgstr "Ambos" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:55 +msgid "Print on a seperate line" +msgstr "Imprimir numa linha separada" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:58 +msgid "Add extra frag information to centerprint when available" +msgstr "" +"Adicionar informações extras de execução à impressão central quando " +"disponível" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:62 +msgid "Add frag location to death messages when available" +msgstr "" +"Adicionar localização de execução nas mensagens de morte quando disponível" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:65 +msgid "Gamemode Settings" +msgstr "Configurações do Modo de Jogo" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:67 +msgid "Display capture times in Capture The Flag" +msgstr "Exibir tempos de captura em Capture a Bandeira" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:71 +msgid "Display name of flag stealer in Capture The Flag" +msgstr "Exibir nome do ladrão da bandeira em Capture a Bandeira" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:76 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:91 +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:133 +msgid "Other" +msgstr "Outros" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:78 +msgid "Display console messages in the top left corner" +msgstr "Exibir mensagens de console no canto superior esquerdo" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:80 +msgid "Display all info messages in the chatbox" +msgstr "Exibir todas as mensagens de informação no bate-papo" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:82 +msgid "Display player statuses in the chatbox" +msgstr "Exibir status de jogadores no bate-papo" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:86 +msgid "Powerup notifications" +msgstr "Notificações de potencializador" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:89 +msgid "Weapon centerprint notifications" +msgstr "Notificações centrais de armas" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:92 +msgid "Weapon info message notifications" +msgstr "Notificações de informação de arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:96 +msgid "Announcers" +msgstr "Locutores" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:98 +msgid "Respawn countdown sounds" +msgstr "Sons da contagem de ressurgimento" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:101 +msgid "Killstreak sounds" +msgstr "Sons de sequência de mortes" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:104 +msgid "Achievement sounds" +msgstr "Sons de conquistas" + +#: qcsrc/menu/xonotic/dialog_settings_game_messages.qh:7 +msgid "Messages" +msgstr "Mensagens" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:30 +msgid "Items" +msgstr "Itens" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:32 +msgid "Use simple 2D images instead of item models" +msgstr "Usar imagens 2D simples em vez de modelos de itens" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:34 +msgid "Unavailable alpha:" +msgstr "Alfa indisponível:" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:37 +msgid "Unavailable color:" +msgstr "Cor indisponível:" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:39 +msgid "GHOITEMS^Black" +msgstr "Preto" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:40 +msgid "GHOITEMS^Dark" +msgstr "Escuro" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:41 +msgid "GHOITEMS^Tinted" +msgstr "Pintado" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:42 +msgid "GHOITEMS^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:43 +msgid "GHOITEMS^Blue" +msgstr "Azul" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:49 +#: qcsrc/menu/xonotic/serverlist.qc:767 +msgid "Players" +msgstr "Jogadores" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:51 +msgid "Force player models to mine" +msgstr "Forçar modelos dos jogadores para ficarem iguais ao meu" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:53 +msgid "Force player colors to mine" +msgstr "Forçar cores de jogadores para ficarem iguais às minhas" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:56 +msgid "In non teamplay modes only" +msgstr "Apenas em modos de jogo que não sejam de equipes" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:60 +msgid "Body fading:" +msgstr "Desaparecimento de corpos:" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:63 +msgid "Gibs:" +msgstr "Tripas:" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:65 +msgid "GIBS^None" +msgstr "Desabilitadas" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:66 +msgid "GIBS^Few" +msgstr "Poucas" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:67 +msgid "GIBS^Many" +msgstr "Muitas" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qc:68 +msgid "GIBS^Lots" +msgstr "Excessivas" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qh:7 +msgid "Models" +msgstr "Modelos" + +#: qcsrc/menu/xonotic/dialog_settings_game_model.qh:8 +msgid "Customize how players and items are displayed in game" +msgstr "Personalize como jogadores e itens são exibidos dentro do jogo" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:26 +msgid "1st person perspective" +msgstr "Perspectiva em 1ª pessoa" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:30 +msgid "Slide to third person upon death" +msgstr "Mudar para terceira pessoa depois de morrer" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:34 +msgid "Smooth the view when landing from a jump" +msgstr "Suavizar a visão quando aterrissar de um salto" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:38 +msgid "Smooth the view while crouching" +msgstr "Suavizar a visão quando estiver agachado" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:42 +msgid "View waving while idle" +msgstr "Ver acenos quando ausente" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:46 +msgid "View bobbing while walking around" +msgstr "Oscilação de visão ao andar" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:51 +msgid "3rd person perspective" +msgstr "Perspectiva em 3ª pessoa" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:55 +msgid "Back distance" +msgstr "Distância das costas" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:61 +msgid "Up distance" +msgstr "Distância para cima" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:67 +msgid "Allow passing through walls while spectating" +msgstr "Atravessar paredes quando estiver de espectador" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:70 +msgid "Field of view:" +msgstr "Campo de visão:" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:72 +msgid "Field of vision in degrees (default: 100)" +msgstr "Campo de visão em graus (padrão: 100)" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:76 +msgid "ZOOM^Zoom factor:" +msgstr "Fator do zoom:" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:78 +msgid "How big the zoom factor is when the zoom button is pressed" +msgstr "" +"Quão grande o fator do zoom será quando o botão de zoom for pressionado" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:81 +msgid "ZOOM^Zoom speed:" +msgstr "Velocidade do zoom:" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:83 +msgid "How fast the view will be zoomed, disable to zoom instantly" +msgstr "Quão rápido será o zoom da visão, desabilite para zoom instantâneo" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:92 +msgid "ZOOM^Instant" +msgstr "Instantâneo" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:96 +msgid "ZOOM^Zoom sensitivity:" +msgstr "Sensibilidade do zoom:" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:98 +msgid "" +"How zoom changes sensitivity, from 0 (lower sensitivity) to 1 (no " +"sensitivity change)" +msgstr "" +"Como o zoom altera a sensibilidade, a partir do valor 0 (sensibilidade mais " +"baixa) até 1 (sem alterações na sensibilidade)" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:101 +msgid "Velocity zoom" +msgstr "Zoom baseado na velocidade" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:102 +msgid "Forward movement only" +msgstr "Apenas ao movimentar-se para frente" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:106 +msgid "VZOOM^Factor" +msgstr "Fator" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:113 +msgid "Display reticle 2D overlay while zooming" +msgstr "Exibe uma sobreposição reticular em 2D durante o zoom" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:116 +msgid "Release zoom when you die or respawn" +msgstr "Soltar o zoom quando você morre ou ressurge" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qc:120 +msgid "Release zoom when you switch weapons" +msgstr "Soltar o zoom quando você troca de arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_view.qh:7 +#: qcsrc/menu/xonotic/keybinder.qc:76 +msgid "View" +msgstr "Visão" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:34 +msgid "Weapon Priority List (* = mutator weapon)" +msgstr "Lista de Prioridade de Armas (* = arma de modificador)" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:40 +msgid "Up" +msgstr "Mover para cima" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:44 +msgid "Down" +msgstr "Mover para baixo" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:50 +msgid "Use priority list for weapon cycling" +msgstr "Usar lista de prioridades como ordem de alternação de armas" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:51 +msgid "" +"Make use of the list above when cycling through weapons with the mouse wheel" +msgstr "" +"Faz uso da lista acima durante a alternação de armas com a roda do mouse" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:53 +msgid "Cycle through only usable weapon selections" +msgstr "Alterne somente entre armas utilizáveis" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:57 +msgid "Auto switch weapons on pickup" +msgstr "Trocar para a arma coletada automaticamente" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:58 +msgid "" +"Automatically switch to newly picked up weapons if they are better than what " +"you are carrying" +msgstr "" +"Alterna automaticamente para a arma coletada caso ela seja melhor do que a " +"que você está carregando" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:61 +msgid "Release attack buttons when you switch weapons" +msgstr "Soltar os botões de ataque durante a troca de arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:64 +msgid "Draw 1st person weapon model" +msgstr "Renderizar modelo de arma em 1ª pessoa" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:65 +msgid "Draw the weapon model" +msgstr "Exibe os modelos das armas em sua tela" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:69 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:72 +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:75 +msgid "Position of the weapon model; requires reconnect" +msgstr "Posicionamento do modelo de arma; é preciso reconectar-se" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:80 +msgid "Gun model swaying" +msgstr "Mover modelo de arma ao mover o mouse" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:85 +msgid "Gun model bobbing" +msgstr "Oscilar modelo de arma" + +#: qcsrc/menu/xonotic/dialog_settings_game_weapons.qh:7 +#: qcsrc/menu/xonotic/keybinder.qc:43 +msgid "Weapons" +msgstr "Armas" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:33 +msgid "Key Bindings" +msgstr "Teclas de Atalho" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:37 +msgid "Change key..." +msgstr "Alterar botão..." + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:41 +msgid "Edit..." +msgstr "Editar..." + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:47 +msgid "Clear" +msgstr "Limpar" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:52 +msgid "Reset all" +msgstr "Redefinir tudo" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:57 +msgid "Mouse" +msgstr "Mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:59 +msgid "Sensitivity:" +msgstr "Sensibilidade:" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:61 +msgid "Mouse speed multiplier" +msgstr "Multiplicador da velocidade do mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:63 +msgid "Smooth aiming" +msgstr "Suavizar mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:64 +msgid "Smoothes the mouse movement, but makes aiming slightly less responsive" +msgstr "" +"Suaviza os movimentos do mouse, mas torna a mira levemente menos responsiva" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:66 +msgid "Invert aiming" +msgstr "Inverter mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:67 +msgid "Invert mouse movement on the Y-axis" +msgstr "Inverter eixo Y do movimento do mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:69 +msgid "Use system mouse positioning" +msgstr "Usar posicionamento de mouse do sistema" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:74 +msgid "Enable built in mouse acceleration" +msgstr "Habilitar aceleração de mouse imbutida" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:78 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:82 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:85 +msgid "Disable system mouse acceleration" +msgstr "Desabilitar aceleração de mouse do SO" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:79 +msgid "Make use of DGA mouse input" +msgstr "Fazer uso da entrada DGA de mouse" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:93 +msgid "Pressing \"enter console\" key also closes it" +msgstr " Pressionar \"abrir console\" também o fecha" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:94 +msgid "Allow the console toggling bind to also close the console" +msgstr "Permite que o atalho para abrir o console também feche-o" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:96 +msgid "Automatically repeat jumping if holding jump" +msgstr " Saltar continuamente ao segurar o botão de saltar" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:99 +msgid "Jetpack on jump:" +msgstr "Mochila a jato ao saltar:" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:101 +msgid "JPJUMP^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:102 +msgid "Air only" +msgstr "Somente no ar" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:103 +msgid "JPJUMP^All" +msgstr "Todos" + +#: qcsrc/menu/xonotic/dialog_settings_input.qc:109 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:114 +#: qcsrc/menu/xonotic/dialog_settings_input.qc:119 +msgid "Use joystick input" +msgstr "Usar entrada de gamepad" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:31 +msgid "Command when pressed:" +msgstr "Comando quando pressionado:" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:34 +msgid "Command when released:" +msgstr "Comando quando largado:" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:40 +msgid "Cancel" +msgstr "Cancelar" + +#: qcsrc/menu/xonotic/dialog_settings_input_userbind.qh:7 +msgid "User defined key bind" +msgstr "Botão de atalho definido pelo usuário" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:11 +#, c-format +msgid "%d fps" +msgstr "%d fps" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:12 +#, c-format +msgid "%d KB/s" +msgstr "%d KB/s" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:13 +#, c-format +msgid "%d MB/s" +msgstr "%d MB/s" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:27 +msgid "Network" +msgstr "Rede" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:29 +msgid "Client UDP port:" +msgstr "Porta UDP do cliente:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:31 +msgid "Force client to use chosen port unless it is set to 0" +msgstr "" +"Força os clientes a utilizarem as portas escolhidas a menos que esteja " +"definido como 0" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:34 +msgid "Bandwidth:" +msgstr "Largura de banda:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:36 +msgid "Specify your network speed" +msgstr "Especifique a velocidade da sua rede" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:37 +msgid "56k" +msgstr "56k" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:38 +msgid "ISDN" +msgstr "ISDN" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:39 +msgid "Slow ADSL" +msgstr "ADSL lenta" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:40 +msgid "Fast ADSL" +msgstr "ADSL rápida" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:41 +msgid "Broadband" +msgstr "Banda larga" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:44 +msgid "Input packets/s:" +msgstr "Pacotes de entrada/s:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:46 +msgid "How many input packets to send to the server each second" +msgstr "Quantos pacotes de entrada serão enviados ao servidor a cada segundo" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:48 +msgid "Server queries/s:" +msgstr "Consultas ao servidor/s:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:52 +msgid "Downloads:" +msgstr "Downloads:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:54 +msgid "Maximum number of concurrent HTTP/FTP downloads" +msgstr "Número máximo de downloads simultâneos via HTTP/FTP" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:56 +msgid "Download speed:" +msgstr "Velocidade de download:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:69 +msgid "Local latency:" +msgstr "Latência local:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:73 +msgid "Show netgraph" +msgstr "Exibir gráfico de rede" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:74 +msgid "Show a graph of packet sizes and other information" +msgstr "Exibe um gráfico de tamanhos de pacotes e outras informações" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:76 +msgid "Client-side movement prediction" +msgstr "Previsão de movimento pelo cliente" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:78 +msgid "Movement error compensation" +msgstr "Compensação de erro de movimento" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:82 +msgid "Use encryption (AES) when available" +msgstr "Usar encriptação (AES) quando disponível" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:85 +msgid "Framerate" +msgstr "Taxa de Quadros por Segundo (FPS)" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:87 +msgid "Maximum:" +msgstr "Máximo:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:99 +msgid "MAXFPS^Unlimited" +msgstr "Ilimitada" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:102 +msgid "Target:" +msgstr "Alvo:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:104 +msgid "TRGT^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:116 +msgid "Idle limit:" +msgstr "Em segundo plano:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:122 +msgid "IDLFPS^Unlimited" +msgstr "Ilimitado" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:126 +msgid "Save processing time for other apps" +msgstr "Salvar tempo de processamento para outras aplicações" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:129 +msgid "Show frames per second" +msgstr "Exibir taxa de quadros por segundo" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:130 +msgid "Show your rendered frames per second" +msgstr "Exibe a sua taxa de quadros por segundo" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:135 +msgid "Menu tooltips:" +msgstr "Dicas de menu:" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:137 +msgid "" +"Menu tooltips: disabled, standard or advanced (also shows cvar or console " +"command bound to the menu item)" +msgstr "" +"Dicas de menu: desabilitado, padrão ou avançado (também mostra a cvar ou " +"comando de console ligado ao item de menu)" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:138 +msgid "TLTIP^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:139 +msgid "TLTIP^Standard" +msgstr "Padrão" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:140 +msgid "TLTIP^Advanced" +msgstr "Avançado" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:143 +msgid "Show current date and time" +msgstr "Exibir data e hora atual" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:144 +msgid "Show current date and time of day, useful on screenshots" +msgstr "Exibe a data e hora atual do dia, útil para capturas de tela" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:147 +msgid "Enable developer mode" +msgstr "Habilitar modo de desenvolvedor" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:151 +msgid "Advanced settings..." +msgstr "Configurações avançadas..." + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:152 +msgid "Advanced settings where you can tweak every single variable of the game" +msgstr "" +"Definições avançadas onde você poderá ajustar cada uma das variáveis do jogo" + +#: qcsrc/menu/xonotic/dialog_settings_misc.qc:157 +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qh:6 +msgid "Factory reset" +msgstr "Configurações padrões" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:31 +msgid "Cvar filter:" +msgstr "Filtro de cvar:" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:38 +msgid "Modified cvars only" +msgstr "Somente cvars modificadas" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:45 +msgid "Setting:" +msgstr "Configuração:" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:49 +msgid "Type:" +msgstr "Modo:" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:53 +msgid "Value:" +msgstr "Valor:" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qc:70 +msgid "Description:" +msgstr "Descrição:" + +#: qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh:7 +msgid "Advanced settings" +msgstr "Configurações avançadas" + +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:11 +msgid "Are you sure you want to reset all settings?" +msgstr "" +"Tem certeza de que deseja redefinir todas as configurações para o padrão?" + +#: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:13 +msgid "This will create a backup config in your data directory" +msgstr "" +"Isso irá criar uma cópia da sua configuração na seguinte pasta: C:\\Users" +"\\[usuário]\\Saved Games\\xonotic\\data" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:25 +msgid "Menu Skins" +msgstr "Visuais de Menu" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:64 +msgid "Text Language" +msgstr "Idioma dos Textos" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:69 +msgid "Set language" +msgstr "Definir idioma" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:74 +msgid "Disable gore effects and harsh language" +msgstr "Desabilitar sangue e linguagem inapropriada" + +#: qcsrc/menu/xonotic/dialog_settings_user.qc:75 +msgid "" +"Replace blood and gibs with content that does not have any gore effects " +"(default: disabled)" +msgstr "" +"Substitui o sangue com conteúdo que não contém nenhum efeito violento " +"(padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:10 +msgid "While connected language changes will be applied only to the menu," +msgstr "" +"Enquanto estiver conectado, alterações no idioma serão aplicadas somente no " +"menu." + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:12 +msgid "full language changes will take effect starting from the next game" +msgstr "" +"Alterações completas de idioma surtirão efeito a partir da próxima partida" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:16 +msgid "Disconnect now" +msgstr "Desconectar agora" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qc:17 +msgid "Switch language" +msgstr "Alterar idioma" + +#: qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh:6 +msgid "Warning" +msgstr "Aviso" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:33 +msgid "Resolution:" +msgstr "Resolução:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:37 +msgid "Font/UI size:" +msgstr "Tamanho da fonte/UI:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:39 +msgid "SZ^Unreadable" +msgstr "Ilegível" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:40 +msgid "SZ^Tiny" +msgstr "Minúsculo" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:41 +msgid "SZ^Little" +msgstr "Muito Pequeno" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:42 +msgid "SZ^Small" +msgstr "Pequeno" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:43 +msgid "SZ^Medium" +msgstr "Médio" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:44 +msgid "SZ^Large" +msgstr "Grande" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:45 +msgid "SZ^Huge" +msgstr "Enorme" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:46 +msgid "SZ^Gigantic" +msgstr "Gigante" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:47 +msgid "SZ^Colossal" +msgstr "Colossal" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:51 +msgid "Color depth:" +msgstr "Profundidade da cor:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:53 +msgid "How many bits per pixel (BPP) to render at, 32 is recommended" +msgstr "Quantos bits por pixel (BPP) a serem renderizados, 32 é o recomendado" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:54 +msgid "16bit" +msgstr "16bit" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:55 +msgid "32bit" +msgstr "32bit" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:59 +msgid "Full screen" +msgstr "Tela cheia" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:61 +msgid "Vertical Synchronization" +msgstr "Sincronização Vertical" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:62 +msgid "" +"Enable vertical synchronization to prevent tearing, will cap your fps to the " +"screen refresh rate (default: disabled)" +msgstr "" +"Habilita a sincronização vertical para evitar cortes na tela. Isso irá " +"limitar a quantidade de quadros por segundo em relação à taxa de atualização " +"do monitor (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:67 +msgid "Flip view horizontally" +msgstr "Girar a visão horizontalmente" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:68 +msgid "Poor man's left handed mode (default: off)" +msgstr "Modo canhoto (padrão: desligado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:71 +msgid "Anisotropy:" +msgstr "Filtro anisotrópico:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:73 +msgid "Anisotropic filtering quality (default: 1x)" +msgstr "Qualidade do filtro anisotrópico (padrão: 1x)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:74 +msgid "ANISO^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:75 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:86 +msgid "2x" +msgstr "2x" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:76 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:87 +msgid "4x" +msgstr "4x" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:77 +msgid "8x" +msgstr "8x" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:78 +msgid "16x" +msgstr "16x" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:81 +msgid "Antialiasing:" +msgstr "Anti-serrilhado:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:84 +msgid "" +"Enable antialiasing, which smooths the edges of 3D geometry. Note that it " +"might decrease performance by quite a lot (default: disabled)" +msgstr "" +"Habilita o anti-serrilhado, o qual suaviza as bordas da geometria em 3D. " +"Note que isso pode diminuir bastante o desempenho (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:85 +msgid "AA^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:92 +msgid "High-quality frame buffer" +msgstr "Buffer de quadro de alta qualidade" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:97 +msgid "Depth first:" +msgstr "Profundidade principal:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:99 +msgid "" +"Eliminate overdraw by rendering a depth-only version of the scene before the " +"normal rendering starts (default: disabled)" +msgstr "" +"Elimina o sobredesenho renderizando uma versão de profundidade única da cena " +"antes que a renderização normal comece (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:100 +msgid "DF^Disabled" +msgstr "Desabilitado" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:101 +msgid "DF^World" +msgstr "Mundo" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:102 +msgid "DF^All" +msgstr "Todos" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:105 +msgid "Vertex Buffer Objects (VBOs)" +msgstr "Objetos de Buffers de Vertex (VBOs)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:108 +msgid "VBO^Off" +msgstr "Desligado" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:109 +msgid "Vertices, some Tris (compatible)" +msgstr "Vértices, alguns Triângulos (compatível)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:110 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:114 +#: qcsrc/menu/xonotic/dialog_settings_video.qc:116 +msgid "" +"Make use of Vertex Buffer Objects to store static geometry in video memory " +"for faster rendering (default: Vertex and Triangles)" +msgstr "" +"Faz uso de objetos de buffers de vertex (VBOs) para armazenar geometria " +"estática na memória de vídeo para renderização mais rápida. (padrão: " +"Vértices e Triângulos)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:113 +msgid "Vertices" +msgstr "Vértices" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:115 +msgid "Vertices and Triangles" +msgstr "Vértices e Triângulos" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:119 +msgid "Brightness:" +msgstr "Brilho:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:121 +msgid "Brightness of black (default: 0)" +msgstr "Brilho do preto (padrão: 0)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:123 +msgid "Contrast:" +msgstr "Contraste:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:125 +msgid "Brightness of white (default: 1)" +msgstr "Brilho do branco (padrão: 1)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:127 +msgid "Gamma:" +msgstr "Gama:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:130 +msgid "" +"Inverse gamma correction value, a brightness effect that does not affect " +"white or black (default: 1.125)" +msgstr "" +"Valor de correção de gama inverso, é um efeito de brilho que não afeta o " +"branco ou preto (padrão: 1.125)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:133 +msgid "Contrast boost:" +msgstr "Impulso do contraste:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:136 +msgid "By how much to multiply the contrast in dark areas (default: 1)" +msgstr "" +"Por quanto deve ser multiplicado o contraste em áreas escuras (padrão: 1)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:139 +msgid "Saturation:" +msgstr "Saturação:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:142 +msgid "" +"Saturation adjustment (0 = grayscale, 1 = normal, 2 = oversaturated), " +"requires GLSL color control (default: 1)" +msgstr "" +"Ajuste da saturação (0 = tons de cinza, 1 = normal, 2 = muito saturado), " +"requer controle de cor GLSL (padrão: 1)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:146 +msgid "LIT^Ambient:" +msgstr "Iluminação ambiental:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:148 +msgid "" +"Ambient lighting, if set too high it tends to make light on maps look dull " +"and flat (default: 4)" +msgstr "" +"Iluminação ambiental, caso o valor seja muito alto, poderá fazer com que as " +"luzes dos mapas fiquem achatadas e sem graça (padrão: 4)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:150 +msgid "Intensity:" +msgstr "Intensidade:" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:152 +msgid "Global rendering brightness (default: 1)" +msgstr "Brilho geral da renderização (padrão: 1)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:155 +msgid "Wait for GPU to finish each frame" +msgstr "Esperar que a placa de vídeo termine cada quadro" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:156 +msgid "" +"Make the CPU wait for the GPU to finish each frame, can help with some " +"strange input or video lag on some machines (default: disabled)" +msgstr "" +"Faz com que o processador espere pela placa de vídeo terminar cada quadro. " +"Pode ajudar no caso de alguns atrasos nos dispositivos de entrada ou lag de " +"vídeo em algumas máquinas (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:158 +msgid "Use OpenGL 2.0 shaders (GLSL)" +msgstr "Usar shaders de OpenGL 2.0 (GLSL)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:162 +msgid "Use GLSL to handle color control" +msgstr "Usar GLSL para o controle de cores" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:163 +msgid "" +"Enable use of GLSL to apply gamma correction, note that it might decrease " +"performance by a lot (default: disabled)" +msgstr "" +"Habilita o uso de GLSL para aplicar correção de gama. Note que isso pode " +"diminuir bastante o desempenho (padrão: desabilitado)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:168 +msgid "Psycho coloring (easter egg)" +msgstr "Cores 'Psycho' (easter egg)" + +#: qcsrc/menu/xonotic/dialog_settings_video.qc:171 +msgid "Trippy vertices (easter egg)" +msgstr "Vértices 'Trip' (easter egg)" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:110 +msgid "Instant action! (random map with bots)" +msgstr "Ação Instantânea! (mapa aleatório com bots)" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:117 +msgid "???" +msgstr "???" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:130 +msgid "Campaign Difficulty:" +msgstr "Dificuldade da Campanha:" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:131 +msgid "CSKL^Easy" +msgstr "Fácil" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:132 +msgid "CSKL^Medium" +msgstr "Média" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:133 +msgid "CSKL^Hard" +msgstr "Difícil" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qc:135 +msgid "Start Singleplayer!" +msgstr "Iniciar Campanha!" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qh:6 +msgid "Singleplayer" +msgstr "Um Jogador" + +#: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 +msgid "Play the singleplayer campaign or instant action matches against bots" +msgstr "" +"Jogue a campanha de um jogador ou inicie uma partida de ação instantânea " +"contra bots" + +#: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 +msgid "Winner" +msgstr "Vencedor" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:32 +msgid "join 'best' team (auto-select)" +msgstr "juntar-se à 'melhor' equipe (seleção automática)" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:33 +msgid "Autoselect team (recommended)" +msgstr "Selecionar equipe automaticamente (recomendado)" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:37 +msgid "red" +msgstr "vermelha" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:38 +msgid "blue" +msgstr "azul" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:39 +msgid "yellow" +msgstr "amarela" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:40 +msgid "pink" +msgstr "rosa" + +#: qcsrc/menu/xonotic/dialog_teamselect.qc:43 +msgid "spectate" +msgstr "assistir" + +#: qcsrc/menu/xonotic/dialog_teamselect.qh:7 +msgid "Team Selection" +msgstr "Seleção de Equipe" + +#: qcsrc/menu/xonotic/dialog_uid2name.qc:10 +msgid "Allow player statistics to use your nickname?" +msgstr "Permitir que as estatísticas de jogadores usem o seu apelido?" + +#: qcsrc/menu/xonotic/dialog_uid2name.qc:12 +msgid "Answering \"No\" you will appear as \"Anonymous player\"" +msgstr "" +"Selecionando \"Não\" você aparecerá como \"Anonymous player\" (em português, " +"\"Jogador anônimo\")" + +#: qcsrc/menu/xonotic/gametypelist.qc:86 +msgid "teamplay" +msgstr "jogo em equipe" + +#: qcsrc/menu/xonotic/gametypelist.qc:88 +msgid "free for all" +msgstr "cada um por si" + +#: qcsrc/menu/xonotic/keybinder.qc:29 +msgid "Moving" +msgstr "Movimento" + +#: qcsrc/menu/xonotic/keybinder.qc:30 +msgid "forward" +msgstr "Mover-se para frente" + +#: qcsrc/menu/xonotic/keybinder.qc:31 +msgid "backpedal" +msgstr "Mover-se para trás" + +#: qcsrc/menu/xonotic/keybinder.qc:32 +msgid "strafe left" +msgstr "Mover-se para a esquerda" + +#: qcsrc/menu/xonotic/keybinder.qc:33 +msgid "strafe right" +msgstr "Mover-se para a direita" + +#: qcsrc/menu/xonotic/keybinder.qc:34 +msgid "jump / swim" +msgstr "saltar / nadar" + +#: qcsrc/menu/xonotic/keybinder.qc:35 +msgid "crouch / sink" +msgstr "agachar / afundar" + +#: qcsrc/menu/xonotic/keybinder.qc:36 +msgid "off-hand hook" +msgstr "gancho imediato" + +#: qcsrc/menu/xonotic/keybinder.qc:37 +msgid "jet pack" +msgstr "mochila a jato" + +#: qcsrc/menu/xonotic/keybinder.qc:39 +msgid "Attacking" +msgstr "Ataques" + +#: qcsrc/menu/xonotic/keybinder.qc:44 +msgid "WEAPON^previous" +msgstr "anterior" + +#: qcsrc/menu/xonotic/keybinder.qc:45 +msgid "WEAPON^next" +msgstr "seguinte" + +#: qcsrc/menu/xonotic/keybinder.qc:46 +msgid "WEAPON^previously used" +msgstr "usada anteriormente" + +#: qcsrc/menu/xonotic/keybinder.qc:47 +msgid "WEAPON^best" +msgstr "melhor" + +#: qcsrc/menu/xonotic/keybinder.qc:48 +msgid "reload" +msgstr "recarregar" + +#: qcsrc/menu/xonotic/keybinder.qc:49 +msgid "drop weapon / throw nade" +msgstr "largar arma / arremessar granada" + +#: qcsrc/menu/xonotic/keybinder.qc:77 +msgid "hold zoom" +msgstr "manter zoom" + +#: qcsrc/menu/xonotic/keybinder.qc:78 +msgid "toggle zoom" +msgstr "ativar/desativar zoom" + +#: qcsrc/menu/xonotic/keybinder.qc:79 +msgid "show scores" +msgstr "exibir pontuações" + +#: qcsrc/menu/xonotic/keybinder.qc:80 +msgid "screen shot" +msgstr "tirar captura de tela" + +#: qcsrc/menu/xonotic/keybinder.qc:81 +msgid "maximize radar" +msgstr "maximizar radar" + +#: qcsrc/menu/xonotic/keybinder.qc:82 +msgid "3rd person view" +msgstr "visão em 3ª pessoa" + +#: qcsrc/menu/xonotic/keybinder.qc:83 +msgid "enter spectator mode" +msgstr "entrar no modo de espectador" + +#: qcsrc/menu/xonotic/keybinder.qc:85 +msgid "Communicate" +msgstr "Comunicação" + +#: qcsrc/menu/xonotic/keybinder.qc:86 +msgid "public chat" +msgstr "Bate-papo público" + +#: qcsrc/menu/xonotic/keybinder.qc:87 qcsrc/menu/xonotic/keybinder.qc:100 +msgid "team chat" +msgstr "Bate-papo de equipe" + +#: qcsrc/menu/xonotic/keybinder.qc:88 +msgid "show chat history" +msgstr "exibir histórico do bate-papo" + +#: qcsrc/menu/xonotic/keybinder.qc:89 +msgid "vote YES" +msgstr "votar SIM" + +#: qcsrc/menu/xonotic/keybinder.qc:90 +msgid "vote NO" +msgstr "votar NÃO" + +#: qcsrc/menu/xonotic/keybinder.qc:93 +msgid "Client" +msgstr "Cliente" + +#: qcsrc/menu/xonotic/keybinder.qc:95 +msgid "enter console" +msgstr "abrir o console" + +#: qcsrc/menu/xonotic/keybinder.qc:96 +msgid "disconnect" +msgstr "desconectar" + +#: qcsrc/menu/xonotic/keybinder.qc:97 +msgid "quit" +msgstr "sair" + +#: qcsrc/menu/xonotic/keybinder.qc:101 +msgid "auto-join team" +msgstr "juntar-se à uma equipe automáticamente" + +#: qcsrc/menu/xonotic/keybinder.qc:103 +msgid "drop key / drop flag" +msgstr "largar chave / largar bandeira" + +#: qcsrc/menu/xonotic/keybinder.qc:106 +msgid "quick menu" +msgstr "menu rápido" + +#: qcsrc/menu/xonotic/keybinder.qc:107 +msgid "sandbox menu" +msgstr "menu sandbox" + +#: qcsrc/menu/xonotic/keybinder.qc:108 +msgid "drag object" +msgstr "arrastar objeto" + +#: qcsrc/menu/xonotic/keybinder.qc:110 +msgid "User defined" +msgstr "Definido pelo usuário" + +#: qcsrc/menu/xonotic/mainwindow.qc:64 qcsrc/menu/xonotic/mainwindow.qc:67 +msgid "Do not press this button again!" +msgstr "Não aperte este botão novamente!" + +#: qcsrc/menu/xonotic/maplist.qc:291 +msgid "" +"Huh? Can't play this (m is NULL). Refiltering so this won't happen again.\n" +msgstr "" +"Huh? Não posso jogar isto (m é NULO). Refiltrando para que isso não ocorra " +"novamente.\n" + +#: qcsrc/menu/xonotic/maplist.qc:299 +#, c-format +msgid "%s's Xonotic Server" +msgstr "Servidor de Xonotic de %s" + +#: qcsrc/menu/xonotic/maplist.qc:304 +msgid "" +"Huh? Can't play this (invalid game type). Refiltering so this won't happen " +"again.\n" +msgstr "" +"Huh? Não posso jogar isto (modo de jogo inválido). Refiltrando para que isso " +"não ocorra novamente.\n" + +#: qcsrc/menu/xonotic/playerlist.qc:100 qcsrc/menu/xonotic/playerlist.qc:110 +msgid "spectator" +msgstr "espectador" + +#: qcsrc/menu/xonotic/playermodel.qc:170 +msgid "<no model found>" +msgstr "<nenhum modelo encontrado>" + +#: qcsrc/menu/xonotic/serverlist.qc:273 +msgid "Favorite" +msgstr "Favoritar" + +#: qcsrc/menu/xonotic/serverlist.qc:274 +msgid "" +"Bookmark the currently highlighted server so that it's faster to find in the " +"future" +msgstr "" +"Marca o servidor selecionado como favorito para que seja mais rápido " +"encontrá-lo no futuro" + +#: qcsrc/menu/xonotic/serverlist.qc:763 +msgid "Ping" +msgstr "Ping" + +#: qcsrc/menu/xonotic/serverlist.qc:764 +msgid "Hostname" +msgstr "Servidor" + +#: qcsrc/menu/xonotic/serverlist.qc:765 +msgid "Map" +msgstr "Mapa" + +#: qcsrc/menu/xonotic/serverlist.qc:766 +msgid "Type" +msgstr "Modo" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +#, c-format +msgid "AES level %d" +msgstr "Nível AES %d" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +msgid "ENC^none" +msgstr "nenhuma" + +#: qcsrc/menu/xonotic/serverlist.qc:1060 +msgid "encryption:" +msgstr "encriptação:" + +#: qcsrc/menu/xonotic/serverlist.qc:1061 +#, c-format +msgid "mod: %s" +msgstr "modificação: %s" + +#: qcsrc/menu/xonotic/serverlist.qc:1063 +#, c-format +msgid "modified settings" +msgstr "configurações modificadas" + +#: qcsrc/menu/xonotic/serverlist.qc:1063 +#, c-format +msgid "official settings" +msgstr "configurações oficiais" + +#: qcsrc/menu/xonotic/serverlist.qc:1065 +msgid "stats disabled" +msgstr "estatísticas desabilitadas" + +#: qcsrc/menu/xonotic/serverlist.qc:1065 +msgid "stats enabled" +msgstr "estatísticas habilitadas" + +#: qcsrc/menu/xonotic/serverlist.qh:151 +msgid "SLCAT^Favorites" +msgstr "Favoritos" + +#: qcsrc/menu/xonotic/serverlist.qh:152 +msgid "SLCAT^Recommended" +msgstr "Recomendados" + +#: qcsrc/menu/xonotic/serverlist.qh:153 +msgid "SLCAT^Normal Servers" +msgstr "Servidores Normais" + +#: qcsrc/menu/xonotic/serverlist.qh:154 +msgid "SLCAT^Servers" +msgstr "Servidores" + +#: qcsrc/menu/xonotic/serverlist.qh:155 +msgid "SLCAT^Competitive Mode" +msgstr "Modo Competitivo" + +#: qcsrc/menu/xonotic/serverlist.qh:156 +msgid "SLCAT^Modified Servers" +msgstr "Servidores Modificados" + +#: qcsrc/menu/xonotic/serverlist.qh:157 +msgid "SLCAT^Overkill" +msgstr "Overkill" + +#: qcsrc/menu/xonotic/serverlist.qh:158 +msgid "SLCAT^InstaGib" +msgstr "InstaGib" + +#: qcsrc/menu/xonotic/serverlist.qh:159 +msgid "SLCAT^Defrag Mode" +msgstr "Modo Defrag" + +#: qcsrc/menu/xonotic/skinlist.qc:70 +msgid "<TITLE>" +msgstr "<TÍTULO>" + +#: qcsrc/menu/xonotic/skinlist.qc:71 +msgid "<AUTHOR>" +msgstr "<AUTOR>" + +#: qcsrc/menu/xonotic/slider_decibels.qc:72 +msgid "VOL^MAX" +msgstr "MÁX" + +#: qcsrc/menu/xonotic/slider_decibels.qc:74 +msgid "VOL^OFF" +msgstr "DESLIGADO" + +#: qcsrc/menu/xonotic/slider_decibels.qc:82 +#, c-format +msgid "%s dB" +msgstr "%s dB" + +#: qcsrc/menu/xonotic/slider_particles.qc:13 +msgid "" +"Multiplier for amount of particles. Less means less particles, which in turn " +"gives for better performance (default: 1)" +msgstr "" +"Multiplicador para a quantidade de partículas. Menos significa menos " +"partículas, o que resulta em um melhor desempenho (padrão: 1)" + +#: qcsrc/menu/xonotic/slider_particles.qc:14 +msgid "PART^OMG" +msgstr "MEUDEUS" + +#: qcsrc/menu/xonotic/slider_particles.qc:15 +msgid "PART^Low" +msgstr "Baixa" + +#: qcsrc/menu/xonotic/slider_particles.qc:16 +msgid "PART^Medium" +msgstr "Média" + +#: qcsrc/menu/xonotic/slider_particles.qc:17 +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:14 +msgid "PART^Normal" +msgstr "Normal" + +#: qcsrc/menu/xonotic/slider_particles.qc:18 +msgid "PART^High" +msgstr "Alta" + +#: qcsrc/menu/xonotic/slider_particles.qc:19 +msgid "PART^Ultra" +msgstr "Ultra" + +#: qcsrc/menu/xonotic/slider_particles.qc:20 +msgid "PART^Ultimate" +msgstr "Extrema" + +#: qcsrc/menu/xonotic/slider_picmip.qc:13 +msgid "" +"Change the sharpness of the textures. Lowering it will effectively reduce " +"texture memory usage, but make the textures appear very blurry. (default: " +"good)" +msgstr "" +"Altera a nitidez das texturas. Usar valores baixos irá efetivamente reduzir " +"a utilização de memória para as texturas, mas fará com que elas pareçam mais " +"desfocadas. (padrão: boa)" + +#: qcsrc/menu/xonotic/slider_resolution.qc:115 +msgid "Screen resolution" +msgstr "Resolução da tela" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:13 +msgid "PART^Slow" +msgstr "Lento" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:15 +msgid "PART^Fast" +msgstr "Rápido" + +#: qcsrc/menu/xonotic/slider_sbfadetime.qc:16 +msgid "PART^Instant" +msgstr "Instantâneo" + +#: qcsrc/menu/xonotic/statslist.qc:29 +msgid "January" +msgstr "Janeiro" + +#: qcsrc/menu/xonotic/statslist.qc:30 +msgid "February" +msgstr "Fevereiro" + +#: qcsrc/menu/xonotic/statslist.qc:31 +msgid "March" +msgstr "Março" + +#: qcsrc/menu/xonotic/statslist.qc:32 +msgid "April" +msgstr "Abril" + +#: qcsrc/menu/xonotic/statslist.qc:33 +msgid "May" +msgstr "Maio" + +#: qcsrc/menu/xonotic/statslist.qc:34 +msgid "June" +msgstr "Junho" + +#: qcsrc/menu/xonotic/statslist.qc:35 +msgid "July" +msgstr "Julho" + +#: qcsrc/menu/xonotic/statslist.qc:36 +msgid "August" +msgstr "Agosto" + +#: qcsrc/menu/xonotic/statslist.qc:37 +msgid "September" +msgstr "Setembro" + +#: qcsrc/menu/xonotic/statslist.qc:38 +msgid "October" +msgstr "Outubro" + +#: qcsrc/menu/xonotic/statslist.qc:39 +msgid "November" +msgstr "Novembro" + +#: qcsrc/menu/xonotic/statslist.qc:40 +msgid "December" +msgstr "Dezembro" + +#: qcsrc/menu/xonotic/statslist.qc:96 +msgid "Joined:" +msgstr "Juntou-se:" + +#: qcsrc/menu/xonotic/statslist.qc:103 +msgid "Last_Seen:" +msgstr "Última_Visita:" + +#: qcsrc/menu/xonotic/statslist.qc:110 +msgid "Time_Played:" +msgstr "Tempo_Jogado:" + +#: qcsrc/menu/xonotic/statslist.qc:117 +msgid "Favorite_Map:" +msgstr "Mapa_Favorito:" + +#: qcsrc/menu/xonotic/statslist.qc:201 qcsrc/menu/xonotic/statslist.qc:245 +#, c-format +msgid "%s_Matches:" +msgstr "%s_Partidas:" + +#: qcsrc/menu/xonotic/statslist.qc:208 +#, c-format +msgid "%s_ELO:" +msgstr "%s_ELO:" + +#: qcsrc/menu/xonotic/statslist.qc:215 +#, c-format +msgid "%s_Rank:" +msgstr "%s_Posição:" + +#: qcsrc/menu/xonotic/statslist.qc:222 +#, c-format +msgid "%s_Percentile:" +msgstr "%s_Percentil:" + +#: qcsrc/menu/xonotic/statslist.qc:231 +#, c-format +msgid "%s_Favorite_Map:" +msgstr "%s_Mapa_Favorito:" + +#: qcsrc/menu/xonotic/statslist.qc:246 +#, c-format +msgid "%d (unranked)" +msgstr "%d (não classificado)" + +#: qcsrc/menu/xonotic/util.qc:417 +#, c-format +msgid "" +"Update can be downloaded at:\n" +"%s\n" +msgstr "" +"A atualização pode ser baixada em:\n" +"%s\n" + +#: qcsrc/menu/xonotic/util.qc:528 +msgid "Autogenerating mapinfo for newly added maps..." +msgstr "Gerando mapinfo automaticamente para os novos mapas adicionados..." + +#: qcsrc/menu/xonotic/util.qc:557 +#, c-format +msgid "^1%s TEST BUILD" +msgstr "^1%s VERSÃO DE TESTE" + +#: qcsrc/menu/xonotic/util.qc:577 +#, c-format +msgid "Update to %s now!" +msgstr "Atualize para %s agora!" + +#: qcsrc/menu/xonotic/util.qc:662 +msgid "" +"^1ERROR: Texture compression is required but not supported.\n" +"^1Expect visual problems.\n" +msgstr "" +"^1ERRO: A compressão de texturas é necessária, mas não é suportada.\n" +"^1Espere problemas visuais.\n" +"\n" + +#: qcsrc/menu/xonotic/util.qc:780 +msgid "Use default" +msgstr "Usar padrão" + +#: qcsrc/menu/xonotic/util.qc:800 +msgid "Team Color:" +msgstr "Cor de Equipe:" + +#: qcsrc/menu/xonotic/util.qh:44 +msgid "Enable panel" +msgstr "Habilitar painel" + +#~ msgid "QMCMD^Chat" +#~ msgstr "Bate-papo" + +#~ msgid "^BG%s%s^K1 got too close ^BG%s^K1's rocket%s%s" +#~ msgstr "^BG%s%s^K1 chegou muito perto do foguete de ^BG%s^K1%s%s" diff --git a/common.ru.po b/common.ru.po index 55e03f1051..7b2b73abe3 100644 --- a/common.ru.po +++ b/common.ru.po @@ -6,7 +6,7 @@ # adem4ik, 2014 # Alex Talker <alextalker7@gmail.com>, 2014-2015 # Andrei Stepanov, 2014 -# Andrei Stepanov, 2014-2018 +# Andrei Stepanov <adem4ik@gmail.com>, 2014-2018 # Andrey P <andrey.pyntikov@gmail.com>, 2016 # Artem Vorotnikov <artem@vorotnikov.me>, 2015 # Lord Canistra <lordcanistra@gmail.com>, 2011 @@ -18,8 +18,8 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2018-03-01 20:50+0000\n" -"Last-Translator: Andrei Stepanov\n" +"PO-Revision-Date: 2018-03-16 06:43+0000\n" +"Last-Translator: Andrei Stepanov <adem4ik@gmail.com>\n" "Language-Team: Russian (http://www.transifex.com/team-xonotic/xonotic/" "language/ru/)\n" "Language: ru\n" @@ -102,7 +102,7 @@ msgstr "бросить оружие" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/menu/xonotic/keybinder.qc:41 msgid "secondary fire" -msgstr "вторичный огонь" +msgstr "альтернативный огонь" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format @@ -136,7 +136,7 @@ msgstr "прыжок" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format msgid "^1Game starts in ^3%d^1 seconds" -msgstr "^1Старт игры через ^3%d^1 секунд(ы)" +msgstr "^1Старт игры через ^3%d^1 секунд" #: qcsrc/client/hud/panel/infomessages.qc:145 msgid "^2Currently in ^1warmup^2 stage!" @@ -1022,7 +1022,7 @@ msgstr "^2Имя ^7вместо \"^1Anonymous player^7\" в статистике #: qcsrc/client/hud/panel/vote.qc:115 msgid "A vote has been called for:" -msgstr "Голосование было создано для:" +msgstr "Создано голосование для:" #: qcsrc/client/hud/panel/vote.qc:117 msgid "Allow servers to store and display your name?" @@ -1113,7 +1113,7 @@ msgstr "Голосование за карту" #: qcsrc/client/mapvoting.qc:382 #, c-format msgid "%d seconds left" -msgstr "Осталось %d секунд(ы)" +msgstr "Осталось %d секунд" #: qcsrc/client/mapvoting.qc:497 msgid "" @@ -1912,11 +1912,11 @@ msgstr "" #: qcsrc/common/notifications/all.inc:246 msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" -msgstr "^BG ^TC^TT^BG Флаг был возвращён на базу владельцем" +msgstr "^BG ^TC^TT^BG Флаг возвращён на базу владельцем" #: qcsrc/common/notifications/all.inc:247 msgid "^BGThe flag was returned by its owner" -msgstr "^BGФлаг был возвращён на базу владельцем" +msgstr "^BGФлаг возвращён на базу владельцем" #: qcsrc/common/notifications/all.inc:248 msgid "^BGThe ^TC^TT^BG flag was destroyed and returned to base" @@ -2022,12 +2022,12 @@ msgstr "^F2Вы станете зрителем со следующего рау #: qcsrc/common/notifications/all.inc:274 #, c-format msgid "^BG%s%s^K1 was killed by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" -msgstr "^BG%s%s^K1 был убит ^BG%s^K1 с усилителем ^BG%s^K1 ^K1%s%s" +msgstr "^BG%s%s^K1 убит ^BG%s^K1 с усилителем ^BG%s^K1 ^K1%s%s" #: qcsrc/common/notifications/all.inc:274 #, c-format msgid "^BG%s%s^K1 was scored against by ^BG%s^K1's ^BG%s^K1 buff ^K1%s%s" -msgstr "^BG%s%s^K1 был растерзан ^BG%s^K1 с усилителем ^BG%s^K1 ^K1%s%s" +msgstr "^BG%s%s^K1 растерзан ^BG%s^K1 с усилителем ^BG%s^K1 ^K1%s%s" #: qcsrc/common/notifications/all.inc:275 #, c-format @@ -2047,22 +2047,22 @@ msgstr "^BG%s%s^K1 был впечатан в землю ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:278 #, c-format msgid "^BG%s%s^K1 felt a little hot from ^BG%s^K1's fire^K1%s%s" -msgstr "^BG%s%s^K1 был немного подпалён из ^BG%s^K1^K1%s%s" +msgstr "^BG%s%s^K1 немного подпалён из ^BG%s^K1^K1%s%s" #: qcsrc/common/notifications/all.inc:278 #, c-format msgid "^BG%s%s^K1 was burnt up into a crisp by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 был прожарен до хрустящей корочки ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 прожарен до хрустящей корочки ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:279 #, c-format msgid "^BG%s%s^K1 was cooked by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 был испечён с помощью ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 испечён с помощью ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:280 #, c-format msgid "^BG%s%s^K1 was pushed in front of a monster by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 был отправлен к монстрам рукой ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 отправлен к монстрам рукой ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:281 #, c-format @@ -2077,17 +2077,17 @@ msgstr "^BG%s%s^K1 слишком близко подошёл к взрыву н #: qcsrc/common/notifications/all.inc:282 #, c-format msgid "^BG%s%s^K1 was burned to death by ^BG%s^K1's Napalm Nade%s%s" -msgstr "^BG%s%s^K1 был сожжён заживо Гранатой Напалма ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 сожжён заживо Гранатой Напалма ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:283 #, c-format msgid "^BG%s%s^K1 was blown up by ^BG%s^K1's Ice Nade%s%s" -msgstr "^BG%s%s^K1 был взорван Ледяной Гранатой ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 подорван Ледяной Гранатой ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:284 #, c-format msgid "^BG%s%s^K1 was frozen to death by ^BG%s^K1's Ice Nade%s%s" -msgstr "^BG%s%s^K1 был заморожен Ледяной Гранатой ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 заморожен Ледяной Гранатой ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:285 #, c-format @@ -2097,7 +2097,7 @@ msgstr "^BG%s%s^K1 не был вылечен Лечащей Гранатой ^B #: qcsrc/common/notifications/all.inc:286 #, c-format msgid "^BG%s%s^K1 was shot into space by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 был отправлен в открытый космос ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 отправлен в открытый космос ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:287 #, c-format @@ -2176,8 +2176,7 @@ msgstr "^BG%s%s^K1 измельчён Пуком-ботом, управляем #: qcsrc/common/notifications/all.inc:299 #, c-format msgid "^BG%s%s^K1 was blasted to bits by ^BG%s^K1's Spiderbot%s%s" -msgstr "" -"^BG%s%s^K1 был разорван на куски Пауком-ботом, управляемым ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 разорван на куски Пауком-ботом, управляемым ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:300 #, c-format @@ -2198,7 +2197,7 @@ msgstr "^BG%s%s^K1 не смог скрыться от Гонщика, упра #: qcsrc/common/notifications/all.inc:303 #, c-format msgid "^BG%s%s^K1 was thrown into a world of hurt by ^BG%s^K1%s%s" -msgstr "^BG%s%s^K1 был отправлен в мир боли рукой ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 отправлен в мир боли рукой ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:305 #, c-format @@ -2268,7 +2267,7 @@ msgstr "^BG%s^K1 сгорел дотла%s%s" #: qcsrc/common/notifications/all.inc:315 #, c-format msgid "^BG%s^K1 was exploded by a Mage%s%s" -msgstr "^BG%s^K1 был взорван Магом%s%s" +msgstr "^BG%s^K1 подорван Магом%s%s" #: qcsrc/common/notifications/all.inc:316 #, c-format @@ -2278,22 +2277,22 @@ msgstr "^BG%s^K1's вывернут наизнанку Шамблером%s%s" #: qcsrc/common/notifications/all.inc:317 #, c-format msgid "^BG%s^K1 was smashed by a Shambler%s%s" -msgstr "^BG%s^K1 был раздавлен Шамблером%s%s" +msgstr "^BG%s^K1 раздавлен Шамблером%s%s" #: qcsrc/common/notifications/all.inc:318 #, c-format msgid "^BG%s^K1 was zapped to death by a Shambler%s%s" -msgstr "^BG%s^K1 был стёрт в порошок Шамблером%s%s" +msgstr "^BG%s^K1 стёрт в порошок Шамблером%s%s" #: qcsrc/common/notifications/all.inc:319 #, c-format msgid "^BG%s^K1 was bitten by a Spider%s%s" -msgstr "^BG%s^K1 был побит Пауком%s%s" +msgstr "^BG%s^K1 побит Пауком%s%s" #: qcsrc/common/notifications/all.inc:320 #, c-format msgid "^BG%s^K1 was fireballed by a Wyvern%s%s" -msgstr "^BG%s^K1 был поражён огненным шаром Виверна%s%s" +msgstr "^BG%s^K1 поражён огненным шаром Виверна%s%s" #: qcsrc/common/notifications/all.inc:321 #, c-format @@ -2320,7 +2319,7 @@ msgstr "^BG%s^K1 захотел посмотреть на взрыв своег #: qcsrc/common/notifications/all.inc:324 #, c-format msgid "^BG%s^K1 was burned to death by their own Napalm Nade%s%s" -msgstr "^BG%s^K1 был сожжён заживо своей же Гранатой Напалма%s%s" +msgstr "^BG%s^K1 сожжён заживо своей же Гранатой Напалма%s%s" #: qcsrc/common/notifications/all.inc:326 #, c-format @@ -2330,7 +2329,7 @@ msgstr "^BG%s^K1 немного обжёгся%s%s" #: qcsrc/common/notifications/all.inc:326 #, c-format msgid "^BG%s^K1 was frozen to death by their own Ice Nade%s%s" -msgstr "^BG%s^K1 был заморожен своей же Ледяной Гранатой%s%s" +msgstr "^BG%s^K1 заморожен своей же Ледяной Гранатой%s%s" #: qcsrc/common/notifications/all.inc:327 #, c-format @@ -2390,7 +2389,7 @@ msgstr "^BG%s^K1 налетел на турель%s%s" #: qcsrc/common/notifications/all.inc:337 #, c-format msgid "^BG%s^K1 was blasted away by an eWheel turret%s%s" -msgstr "^BG%s^K1 был разорван в клочья турелью еМобиля%s%s" +msgstr "^BG%s^K1 разорван в клочья турелью еМобиля%s%s" #: qcsrc/common/notifications/all.inc:338 #, c-format @@ -2400,7 +2399,7 @@ msgstr "^BG%s^K1 попал под огонь Зенитной Пушки %s%s" #: qcsrc/common/notifications/all.inc:339 #, c-format msgid "^BG%s^K1 was blasted away by a Hellion turret%s%s" -msgstr "^BG%s^K1 был разорван в клочья турелью Hellion%s%s" +msgstr "^BG%s^K1 разорван в клочья турелью Hellion%s%s" #: qcsrc/common/notifications/all.inc:340 #, c-format @@ -2415,12 +2414,12 @@ msgstr "^BG%s^K1 изрешечён Пулемётной башней%s%s" #: qcsrc/common/notifications/all.inc:342 #, c-format msgid "^BG%s^K1 got turned into smoldering gibs by an MLRS turret%s%s" -msgstr "^BG%s^K1 был разорван на тлеющие кусочки турелью MLRS%s%s" +msgstr "^BG%s^K1 разорван на тлеющие кусочки турелью MLRS%s%s" #: qcsrc/common/notifications/all.inc:343 #, c-format msgid "^BG%s^K1 was phased out by a turret%s%s" -msgstr "^BG%s^K1 был отменён турелью%s%s" +msgstr "^BG%s^K1 ликвидирован турелью%s%s" #: qcsrc/common/notifications/all.inc:344 #, c-format @@ -2430,7 +2429,7 @@ msgstr "^BG%s^K1 отведал перегретой плазмы из туре #: qcsrc/common/notifications/all.inc:345 #, c-format msgid "^BG%s^K1 was electrocuted by a Tesla turret%s%s" -msgstr "^BG%s^K1 был убит электрическим током турели Теслы%s%s" +msgstr "^BG%s^K1 убит электрическим током турели Теслы%s%s" #: qcsrc/common/notifications/all.inc:346 #, c-format @@ -2440,12 +2439,12 @@ msgstr "^BG%s^K1 обогащён свинцом из турели Ходунa%s #: qcsrc/common/notifications/all.inc:347 #, c-format msgid "^BG%s^K1 was impaled by a Walker turret%s%s" -msgstr "^BG%s^K1 был пронзён турелью Ходуна%s%s" +msgstr "^BG%s^K1 пронзён турелью Ходуна%s%s" #: qcsrc/common/notifications/all.inc:348 #, c-format msgid "^BG%s^K1 was blasted away by a Walker turret%s%s" -msgstr "^BG%s^K1 был разорван в клочья турелью Ходуна%s%s" +msgstr "^BG%s^K1 разорван в клочья турелью Ходуна%s%s" #: qcsrc/common/notifications/all.inc:349 #, c-format @@ -2455,7 +2454,7 @@ msgstr "^BG%s^K1 зацепило взрывной волной от Шмеля #: qcsrc/common/notifications/all.inc:350 #, c-format msgid "^BG%s^K1 was crushed by a vehicle%s%s" -msgstr "^BG%s^K1 был раздавлен весом тяжёлой машины%s%s" +msgstr "^BG%s^K1 раздавлен весом тяжёлой машины%s%s" #: qcsrc/common/notifications/all.inc:351 #, c-format @@ -2475,7 +2474,7 @@ msgstr "^BG%s^K1 задело взрывной волной от Паука-бо #: qcsrc/common/notifications/all.inc:354 #, c-format msgid "^BG%s^K1 was blasted to bits by a Spiderbot rocket%s%s" -msgstr "^BG%s^K1 был разорван на кусочки ракетой Паука-бота%s%s" +msgstr "^BG%s^K1 разорван на кусочки ракетой Паука-бота%s%s" #: qcsrc/common/notifications/all.inc:355 #, c-format @@ -2515,12 +2514,12 @@ msgstr "^BG%s^K3 воскрес после падения" #: qcsrc/common/notifications/all.inc:366 #, c-format msgid "^BG%s^K3 was revived by their Nade explosion" -msgstr "^BG%s^K3 был оживлён взрывом своей гранаты" +msgstr "^BG%s^K3 оживлён взрывом своей гранаты" #: qcsrc/common/notifications/all.inc:367 #, c-format msgid "^BG%s^K3 was automatically revived after %s second(s)" -msgstr "^BG%s^K3 был автоматически оживлён после %s секунд(ы)" +msgstr "^BG%s^K3 автоматически оживлён после %s секунд(ы)" #: qcsrc/common/notifications/all.inc:368 #, c-format @@ -2708,7 +2707,7 @@ msgstr "Контрольная точка %s^BG команды ^TC^TT^BG был #: qcsrc/common/notifications/all.inc:414 msgid "^TC^TT^BG generator has been destroyed" -msgstr "^TC^TT^BG генератор был уничтожен" +msgstr "^TC^TT^BG генератор уничтожен" #: qcsrc/common/notifications/all.inc:415 msgid "^TC^TT^BG generator spontaneously combusted due to overtime!" @@ -2894,7 +2893,7 @@ msgstr "^BG%s%s^K1 попал под раздачу тока из Дуговой #: qcsrc/common/notifications/all.inc:457 #, c-format msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Arc bolts%s%s" -msgstr "^BG%s%s^K1 был взорван кривыми болтами%s%s ^BG%s^K1's" +msgstr "^BG%s%s^K1 взорван кривыми болтами%s%s ^BG%s^K1's" #: qcsrc/common/notifications/all.inc:458 #, c-format @@ -2934,7 +2933,7 @@ msgstr "^BG%s^K1 взорвал себя с помощью Разрушител #: qcsrc/common/notifications/all.inc:465 #, c-format msgid "^BG%s%s^K1 was blasted by ^BG%s^K1's Electro bolt%s%s" -msgstr "^BG%s%s^K1 был разорван зарядом Электро от ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 разорван зарядом Электро ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:466 #, c-format @@ -2964,7 +2963,7 @@ msgstr "^BG%s%s^K1 оказался слишком близко к огненн #: qcsrc/common/notifications/all.inc:471 #, c-format msgid "^BG%s%s^K1 got burnt by ^BG%s^K1's firemine%s%s" -msgstr "^BG%s%s^K1 был сожжён зажигательной миной ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 сожжён зажигательной миной ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:472 #, c-format @@ -2974,7 +2973,7 @@ msgstr "^BG%s^K1 должен был выбрать пушку поменьше% #: qcsrc/common/notifications/all.inc:473 #, c-format msgid "^BG%s^K1 forgot about their firemine%s%s" -msgstr "^BG%s^K1 забыл о своей зажигательной мине ds%s%s" +msgstr "^BG%s^K1 забыл о своей зажигательной мине %s%s" #: qcsrc/common/notifications/all.inc:474 #, c-format @@ -2984,7 +2983,7 @@ msgstr "^BG%s%s^K1 утрамбован очередью из Хагара от #: qcsrc/common/notifications/all.inc:475 #, c-format msgid "^BG%s%s^K1 was pummeled by ^BG%s^K1's Hagar rockets%s%s" -msgstr "^BG%s%s^K1 обстрелян из Хагара со стороны ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 обстрелян из Хагара ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:476 #, c-format @@ -3100,7 +3099,7 @@ msgstr "^BG%s%s^K1 не смог спрятаться от Винтовки ^BG% #: qcsrc/common/notifications/all.inc:497 #, c-format msgid "^BG%s%s^K1 was sawn in half by ^BG%s^K1's Rocket Propelled Chainsaw%s%s" -msgstr "^BG%s%s^K1 был распилен пополам Реактивной Бензопилой ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 распилен пополам Реактивной Бензопилой ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:498 #, c-format @@ -3110,7 +3109,7 @@ msgstr "^BG%s%s^K1 почти уклонился от Реактивной Бе #: qcsrc/common/notifications/all.inc:499 #, c-format msgid "^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s" -msgstr "^BG%s^K1 был распилен пополам своей же Реактивной Бензопилой%s%s" +msgstr "^BG%s^K1 распилен пополам своей же Реактивной Бензопилой%s%s" #: qcsrc/common/notifications/all.inc:500 #, c-format @@ -3142,7 +3141,7 @@ msgstr "^BG%s%s^K1 застрелен из Шоковой Волны ^BG%s^K1%s% #: qcsrc/common/notifications/all.inc:505 #, c-format msgid "^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s" -msgstr "^BG%s%s^K1 был отшлёпан ^BG%s^K1 огромной Шоковой Волной%s%s" +msgstr "^BG%s%s^K1 отшлёпан ^BG%s^K1 огромной Шоковой Волной%s%s" #: qcsrc/common/notifications/all.inc:506 #, c-format @@ -3172,7 +3171,7 @@ msgstr "^BGУ %s^K1 завяли уши от собственной игры @!# #: qcsrc/common/notifications/all.inc:511 #, c-format msgid "^BG%s%s^K1 has been sublimated by ^BG%s^K1's Vaporizer%s%s" -msgstr "^BG%s%s^K1 был преобразован в пар Испарителем ^BG%s^K1%s%s" +msgstr "^BG%s%s^K1 преобразован в пар Испарителем ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:512 #, c-format @@ -3824,7 +3823,7 @@ msgstr "^BGОжидание присоединения %s игроков(а)..." #: qcsrc/common/notifications/all.inc:723 msgid "^BGYour weapon has been downgraded until you find some ammo!" -msgstr "^BGВаше оружие было ослаблено до тех пор, пока вы не найдёте патроны!" +msgstr "^BGВаше оружие ослаблено до тех пор, пока вы не найдёте патроны!" #: qcsrc/common/notifications/all.inc:724 msgid "^F4^COUNT^BG left to find some ammo!" @@ -3990,7 +3989,7 @@ msgstr "^F2Гонка окончена, завершите ваш круг!" #: qcsrc/common/notifications/all.inc:764 msgid "^BGSecondary fire inflicts no damage!" -msgstr "^BGВторичный режим огня не наносит урона!" +msgstr "^BGАльтернативный режим огня не наносит урона!" #: qcsrc/common/notifications/all.inc:766 msgid "^BGSequence completed!" @@ -4087,7 +4086,7 @@ msgstr "основной" #: qcsrc/common/notifications/all.qh:407 qcsrc/common/notifications/all.qh:408 msgid "secondary" -msgstr "вторичный" +msgstr "альтернативный" #: qcsrc/common/notifications/all.qh:410 msgid "point" @@ -4295,7 +4294,7 @@ msgstr ", прервав серию из %d очков подряд" #: qcsrc/common/notifications/all.qh:610 #, c-format msgid ", losing their %d frag spree" -msgstr ", окончив свою серию из %d убийств подряд" +msgstr ", прервав свою серию из %d убийств подряд" #: qcsrc/common/notifications/all.qh:611 #, c-format @@ -6345,8 +6344,8 @@ msgid "" msgstr "" "Игроки получат только одно оружие, которое немедленно убьёт противника с " "одного выстрела. Если игрок испытывает недостаток патронов, у него есть 10 " -"секунд, чтобы найти ещё, иначе он встретит смерть. Режим вторичного огня не " -"наносит урона, но он хорош для трюков." +"секунд, чтобы найти ещё, иначе он встретит смерть. Режим альтернативного " +"огня не наносит урона, но он хорош для трюков." #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:273 msgid "" @@ -8361,11 +8360,11 @@ msgstr "Беречь время процессора для других при #: qcsrc/menu/xonotic/dialog_settings_misc.qc:129 msgid "Show frames per second" -msgstr "Показывать кол-во кадров в секунду (FPS)" +msgstr "Показывать счётчик кадров (FPS)" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:130 msgid "Show your rendered frames per second" -msgstr "Показывать кол-во отрисованных кадров в секунду" +msgstr "Показывать количество отрисованных кадров в секунду" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:135 msgid "Menu tooltips:" @@ -8833,7 +8832,7 @@ msgstr "Победитель" #: qcsrc/menu/xonotic/dialog_teamselect.qc:32 msgid "join 'best' team (auto-select)" -msgstr "присоединиться к 'лучшей' команде (автовыбор)" +msgstr "автовыбор 'лучшей' команды" #: qcsrc/menu/xonotic/dialog_teamselect.qc:33 msgid "Autoselect team (recommended)" diff --git a/common.tr.po b/common.tr.po index a62ab51912..dc97f52172 100644 --- a/common.tr.po +++ b/common.tr.po @@ -3,13 +3,15 @@ # This file is distributed under the same license as the PACKAGE package. # # Translators: +# Çağlar Turalı <caglarturali@gmail.com>, 2018 +# Demiray Muhterem <mdemiray@msn.com>, 2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-07-05 15:06+0000\n" -"Last-Translator: divVerent <divVerent@xonotic.org>\n" +"PO-Revision-Date: 2018-10-27 11:04+0000\n" +"Last-Translator: Çağlar Turalı <caglarturali@gmail.com>\n" "Language-Team: Turkish (http://www.transifex.com/team-xonotic/xonotic/" "language/tr/)\n" "Language: tr\n" @@ -22,164 +24,166 @@ msgstr "" #, c-format msgid "^2Successfully exported to %s! (Note: It's saved in data/data/)\n" msgstr "" +"^2Başarıyla dışa aktarıldı: %s (Not: data/data/ klasörüne kaydedildi)\n" #: qcsrc/client/hud/hud_config.qc:243 #, c-format msgid "^1Couldn't write to %s\n" -msgstr "" +msgstr "^1Şuraya yazılamıyor %s\n" #: qcsrc/client/hud/panel/chat.qc:82 msgid "^3Player^7: This is the chat area." -msgstr "" +msgstr "^3Oyuncu^7: Burası sohbet alanı." #: qcsrc/client/hud/panel/engineinfo.qc:69 #, c-format msgid "FPS: %.*f" -msgstr "" +msgstr "FPS: %.*f" #: qcsrc/client/hud/panel/infomessages.qc:87 msgid "^1Observing" -msgstr "" +msgstr "^1Gözlemleniyor" #: qcsrc/client/hud/panel/infomessages.qc:89 #, c-format msgid "^1Spectating: ^7%s" -msgstr "" +msgstr "^1İzleniyor: ^7%s" #: qcsrc/client/hud/panel/infomessages.qc:100 #, c-format msgid "^1Press ^3%s^1 to spectate" -msgstr "" +msgstr "^1İzlemek için ^3%s^1 basın" #: qcsrc/client/hud/panel/infomessages.qc:100 #: qcsrc/menu/xonotic/keybinder.qc:40 msgid "primary fire" -msgstr "" +msgstr "birincil ateş" #: qcsrc/client/hud/panel/infomessages.qc:102 #, c-format msgid "^1Press ^3%s^1 or ^3%s^1 for next or previous player" -msgstr "" +msgstr "^1Önceki veya sonraki oyuncu için ^3%s^1 veya ^3%s^1 tuşuna basın" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "next weapon" -msgstr "" +msgstr "bir sonraki silah" #: qcsrc/client/hud/panel/infomessages.qc:102 #: qcsrc/client/hud/panel/infomessages.qc:106 msgid "previous weapon" -msgstr "" +msgstr "bir önceki silah" #: qcsrc/client/hud/panel/infomessages.qc:106 #, c-format msgid "^1Use ^3%s^1 or ^3%s^1 to change the speed" -msgstr "" +msgstr "^1Hızı değiştirmek için ^3%s^1 veya ^3%s^1 basın" #: qcsrc/client/hud/panel/infomessages.qc:108 #, c-format msgid "^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode" msgstr "" +"^1Gözlemlemek için ^3%s^1, kamera modunu değiştirmek için ^3%s^1 tuşuna basın" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/common/vehicles/cl_vehicles.qc:192 msgid "drop weapon" -msgstr "" +msgstr "silah bırak" #: qcsrc/client/hud/panel/infomessages.qc:108 #: qcsrc/menu/xonotic/keybinder.qc:41 msgid "secondary fire" -msgstr "" +msgstr "ikincil ateş" #: qcsrc/client/hud/panel/infomessages.qc:111 #, c-format msgid "^1Press ^3%s^1 for gamemode info" -msgstr "" +msgstr "^1Oyun modu bilgisi için ^3%s^1 tuşuna basın" #: qcsrc/client/hud/panel/infomessages.qc:111 #: qcsrc/menu/xonotic/keybinder.qc:94 msgid "server info" -msgstr "" +msgstr "sunucu bilgisi" #: qcsrc/client/hud/panel/infomessages.qc:124 msgid "^1Match has already begun" -msgstr "" +msgstr "^1Savaş çoktan başladı" #: qcsrc/client/hud/panel/infomessages.qc:126 msgid "^1You have no more lives left" -msgstr "" +msgstr "^1Fazla canın kalmadı" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 #, c-format msgid "^1Press ^3%s^1 to join" -msgstr "" +msgstr "^1Katılmak için ^3%s^1 tuşuna basın" #: qcsrc/client/hud/panel/infomessages.qc:128 #: qcsrc/client/hud/panel/infomessages.qc:131 msgid "jump" -msgstr "" +msgstr "zıpla" #: qcsrc/client/hud/panel/infomessages.qc:139 #, c-format msgid "^1Game starts in ^3%d^1 seconds" -msgstr "" +msgstr "^1Oyun ^3%d^1 saniye içerisinde başlıyor" #: qcsrc/client/hud/panel/infomessages.qc:145 msgid "^2Currently in ^1warmup^2 stage!" -msgstr "" +msgstr "^2Şu an ^1ısınma^2 aşamasında!" #: qcsrc/client/hud/panel/infomessages.qc:160 #, c-format msgid "%sPress ^3%s%s to end warmup" -msgstr "" +msgstr "%sIsınmayı bitirmek için ^3%s%s basın" #: qcsrc/client/hud/panel/infomessages.qc:160 #: qcsrc/client/hud/panel/infomessages.qc:162 #: qcsrc/client/hud/panel/infomessages.qc:175 #: qcsrc/menu/xonotic/keybinder.qc:91 msgid "ready" -msgstr "" +msgstr "hazır" #: qcsrc/client/hud/panel/infomessages.qc:162 #, c-format msgid "%sPress ^3%s%s once you are ready" -msgstr "" +msgstr "%sHazır olduğunuzda ^3%s%s basın" #: qcsrc/client/hud/panel/infomessages.qc:167 msgid "^2Waiting for others to ready up to end warmup..." -msgstr "" +msgstr "^2Diğerlerinin ısınma aşamasını bitirmesi bekleniyor..." #: qcsrc/client/hud/panel/infomessages.qc:169 msgid "^2Waiting for others to ready up..." -msgstr "" +msgstr "^2Diğerlerinin hazır olması bekleniyor..." #: qcsrc/client/hud/panel/infomessages.qc:175 #, c-format msgid "^2Press ^3%s^2 to end warmup" -msgstr "" +msgstr "^2Isınmayı bitirmek için ^3%s^2 tuşuna basın" #: qcsrc/client/hud/panel/infomessages.qc:196 msgid "Teamnumbers are unbalanced!" -msgstr "" +msgstr "Takım sayıları dengesiz!" #: qcsrc/client/hud/panel/infomessages.qc:199 #, c-format msgid " Press ^3%s%s to adjust" -msgstr "" +msgstr "Ayarlamak için ^3%s%s basın" #: qcsrc/client/hud/panel/infomessages.qc:199 #: qcsrc/menu/xonotic/keybinder.qc:102 msgid "team menu" -msgstr "" +msgstr "takım menüsü" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating this player:" -msgstr "" +msgstr "^1Oyuncu izleniyor:" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating you:" -msgstr "" +msgstr "^1Sen izleniyorsun:" #: qcsrc/client/hud/panel/infomessages.qc:225 msgid "^7Press ^3ESC ^7to show HUD options." @@ -199,32 +203,32 @@ msgstr "" #: qcsrc/client/hud/panel/modicons.qc:566 msgid "Personal best" -msgstr "" +msgstr "Kişisel rekor" #: qcsrc/client/hud/panel/modicons.qc:576 msgid "Server best" -msgstr "" +msgstr "Sunucu rekoru" #: qcsrc/client/hud/panel/notify.qc:115 qcsrc/client/hud/panel/notify.qc:116 #: qcsrc/client/hud/panel/score.qc:59 #, c-format msgid "Player %d" -msgstr "" +msgstr "Oyuncu %d" #: qcsrc/client/hud/panel/quickmenu.qc:603 #: qcsrc/client/hud/panel/quickmenu.qc:605 #, c-format msgid "Submenu%d" -msgstr "" +msgstr "Altmenü%d" #: qcsrc/client/hud/panel/quickmenu.qc:610 #, c-format msgid "Command%d" -msgstr "" +msgstr "Komut%d" #: qcsrc/client/hud/panel/quickmenu.qc:636 msgid "Continue..." -msgstr "" +msgstr "Devam..." #: qcsrc/client/hud/panel/quickmenu.qc:794 #: qcsrc/client/hud/panel/quickmenu.qc:798 @@ -233,40 +237,40 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:795 msgid "QMCMD^:-) / nice one" -msgstr "" +msgstr "QMCMD^:-) / güzel" #: qcsrc/client/hud/panel/quickmenu.qc:795 msgid "QMCMD^nice one" -msgstr "" +msgstr "QMCMD^güzel" #: qcsrc/client/hud/panel/quickmenu.qc:796 msgid "QMCMD^good game" -msgstr "" +msgstr "QMCMD^iyi oyun" #: qcsrc/client/hud/panel/quickmenu.qc:797 msgid "QMCMD^hi / good luck" -msgstr "" +msgstr "QMCMD^selam / iyi şanslar" #: qcsrc/client/hud/panel/quickmenu.qc:797 msgid "QMCMD^hi / good luck and have fun" -msgstr "" +msgstr "QMCMD^selam / iyi şanslar ve iyi eğlenceler" #: qcsrc/client/hud/panel/quickmenu.qc:802 #: qcsrc/client/hud/panel/quickmenu.qc:818 msgid "QMCMD^Team chat" -msgstr "" +msgstr "QMCMD^Takım sohbet" #: qcsrc/client/hud/panel/quickmenu.qc:803 msgid "QMCMD^quad soon" -msgstr "" +msgstr "QMCMD^dört yakında" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item %x^7 (l:%y^7)" -msgstr "" +msgstr "QMCMD^ücretsiz malzeme %x^7 (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:804 msgid "QMCMD^free item, icon" -msgstr "" +msgstr "QMCMD^ücretsiz malzeme, simge" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item (l:%l^7)" @@ -372,16 +376,16 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:823 #: qcsrc/client/hud/panel/quickmenu.qc:860 msgid "QMCMD^Settings" -msgstr "" +msgstr "QMCMD^Ayarları" #: qcsrc/client/hud/panel/quickmenu.qc:824 #: qcsrc/client/hud/panel/quickmenu.qc:831 msgid "QMCMD^View/HUD settings" -msgstr "" +msgstr "QMCMD^View/HUD ayarları" #: qcsrc/client/hud/panel/quickmenu.qc:825 msgid "QMCMD^3rd person view" -msgstr "" +msgstr "QMCMD^Üçüncü şahıs görünümü" #: qcsrc/client/hud/panel/quickmenu.qc:826 msgid "QMCMD^Player models like mine" @@ -406,7 +410,7 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:833 #: qcsrc/client/hud/panel/quickmenu.qc:836 msgid "QMCMD^Sound settings" -msgstr "" +msgstr "QMCMD^Ses ayarları" #: qcsrc/client/hud/panel/quickmenu.qc:834 msgid "QMCMD^Hit sound" @@ -499,12 +503,12 @@ msgstr "" #: qcsrc/client/hud/panel/racetimer.qc:61 msgid "Start line" -msgstr "" +msgstr "Başlangıç çizgisi" #: qcsrc/client/hud/panel/racetimer.qc:63 #: qcsrc/client/hud/panel/racetimer.qc:67 msgid "Finish line" -msgstr "" +msgstr "Bitiş çizgisi" #: qcsrc/client/hud/panel/racetimer.qc:65 #, c-format @@ -886,43 +890,43 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:1295 msgid "Map stats:" -msgstr "" +msgstr "Harita verileri:" #: qcsrc/client/hud/panel/scoreboard.qc:1325 msgid "Monsters killed:" -msgstr "" +msgstr "Canavarlar öldürüldü:" #: qcsrc/client/hud/panel/scoreboard.qc:1332 msgid "Secrets found:" -msgstr "" +msgstr "Sırlar bulundu:" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Capture time rankings" -msgstr "" +msgstr "Zaman sıralamasını yakala" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Rankings" -msgstr "" +msgstr "Sıralamalar" #: qcsrc/client/hud/panel/scoreboard.qc:1519 #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:43 msgid "Scoreboard" -msgstr "" +msgstr "Sonuçlar" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format msgid "Speed award: %d%s ^7(%s^7)" -msgstr "" +msgstr "Hız ödülü: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1588 #, c-format msgid "All-time fastest: %d%s ^7(%s^7)" -msgstr "" +msgstr "Tüm zamanların en hızlısı: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1604 #, c-format msgid "Spectators" -msgstr "" +msgstr "Seyirciler" #: qcsrc/client/hud/panel/scoreboard.qc:1619 #, c-format @@ -938,7 +942,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:1635 #: qcsrc/client/hud/panel/scoreboard.qc:1654 msgid " or" -msgstr "" +msgstr "veya" #: qcsrc/client/hud/panel/scoreboard.qc:1638 #: qcsrc/client/hud/panel/scoreboard.qc:1645 @@ -969,21 +973,21 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:1688 #, c-format msgid "^1Respawning in ^3%s^1..." -msgstr "" +msgstr "^1Yeniden doğuma son ^3%s^1..." #: qcsrc/client/hud/panel/scoreboard.qc:1698 #, c-format msgid "You are dead, wait ^3%s^7 before respawning" -msgstr "" +msgstr "Öldünüz, yeniden doğmadan önce ^3%s^7 bekleyin" #: qcsrc/client/hud/panel/scoreboard.qc:1707 #, c-format msgid "You are dead, press ^2%s^7 to respawn" -msgstr "" +msgstr "Öldünüz, yeniden doğmak için ^2%s^7 basın" #: qcsrc/client/hud/panel/vote.qc:24 msgid "^1You must answer before entering hud configure mode\n" -msgstr "" +msgstr "^1HUD yapılandırma moduna girmeden önce cevaplamalısınız\n" #: qcsrc/client/hud/panel/vote.qc:29 msgid "^2Name ^7instead of \"^1Anonymous player^7\" in stats" @@ -991,15 +995,15 @@ msgstr "" #: qcsrc/client/hud/panel/vote.qc:115 msgid "A vote has been called for:" -msgstr "" +msgstr "Bir oylama yapılacak:" #: qcsrc/client/hud/panel/vote.qc:117 msgid "Allow servers to store and display your name?" -msgstr "" +msgstr "Sunucuların adınızı saklamasına ve görüntülemesine izin verilsin mi?" #: qcsrc/client/hud/panel/vote.qc:121 msgid "^1Configure the HUD" -msgstr "" +msgstr "^1HUD'ı Yapılandır" #: qcsrc/client/hud/panel/vote.qc:125 qcsrc/menu/xonotic/dialog_firstrun.qc:82 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:18 @@ -1009,7 +1013,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:16 #: qcsrc/menu/xonotic/dialog_uid2name.qc:15 msgid "Yes" -msgstr "" +msgstr "Evet" #: qcsrc/client/hud/panel/vote.qc:127 qcsrc/menu/xonotic/dialog_firstrun.qc:83 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:21 @@ -1019,19 +1023,19 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc_reset.qc:17 #: qcsrc/menu/xonotic/dialog_uid2name.qc:17 msgid "No" -msgstr "" +msgstr "Hayır" #: qcsrc/client/hud/panel/weapons.qc:530 msgid "Out of ammo" -msgstr "" +msgstr "Cephane kalmadı" #: qcsrc/client/hud/panel/weapons.qc:534 msgid "Don't have" -msgstr "" +msgstr "Sahip değilsin" #: qcsrc/client/hud/panel/weapons.qc:538 msgid "Unavailable" -msgstr "" +msgstr "Kullanım dışı" #: qcsrc/client/main.qc:1014 msgid " qu/s" @@ -1056,33 +1060,33 @@ msgstr "" #: qcsrc/client/main.qc:1264 #, c-format msgid "%s (not bound)" -msgstr "" +msgstr "%s (bağlı değil)" #: qcsrc/client/mapvoting.qc:49 msgid " (1 vote)" -msgstr "" +msgstr " (1 oy)" #: qcsrc/client/mapvoting.qc:51 #, c-format msgid " (%d votes)" -msgstr "" +msgstr " (%d oy)" #: qcsrc/client/mapvoting.qc:271 msgid "Don't care" -msgstr "" +msgstr "Umrumda değil" #: qcsrc/client/mapvoting.qc:365 msgid "Decide the gametype" -msgstr "" +msgstr "Oyun türüne karar verin" #: qcsrc/client/mapvoting.qc:365 msgid "Vote for a map" -msgstr "" +msgstr "Haritayı oyla" #: qcsrc/client/mapvoting.qc:382 #, c-format msgid "%d seconds left" -msgstr "" +msgstr "%d saniye kaldı" #: qcsrc/client/mapvoting.qc:497 msgid "" @@ -1095,27 +1099,27 @@ msgstr "" #: qcsrc/client/mapvoting.qc:516 msgid "Requesting preview...\n" -msgstr "" +msgstr "Önizleme isteniyor...\n" #: qcsrc/client/miscfunctions.qc:109 msgid "Trying to remove a team which is not in the teamlist!" -msgstr "" +msgstr "Takım listesinde olmayan bir takımı çıkarmaya çalışma!" #: qcsrc/client/view.qc:1380 msgid "Nade timer" -msgstr "" +msgstr "Nade zamanlayıcısı" #: qcsrc/client/view.qc:1385 msgid "Capture progress" -msgstr "" +msgstr "İlerlemeyi yakala" #: qcsrc/client/view.qc:1390 msgid "Revival progress" -msgstr "" +msgstr "İlerlemeyi canlandır" #: qcsrc/common/command/generic.qc:157 msgid "error creating curl handle\n" -msgstr "" +msgstr "curl tanıtımı oluşturma hatası\n" #: qcsrc/common/command/generic.qc:403 msgid "Notification restart command only works with cl_cmd and sv_cmd.\n" @@ -1123,68 +1127,68 @@ msgstr "" #: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 msgid "Ball Stealer" -msgstr "" +msgstr "Top hırsızı" #: qcsrc/common/items/item/armor.qh:111 msgid "Big armor" -msgstr "" +msgstr "Büyük zırh" #: qcsrc/common/items/item/armor.qh:147 msgid "Mega armor" -msgstr "" +msgstr "Mega zırh" #: qcsrc/common/items/item/health.qh:111 msgid "Big health" -msgstr "" +msgstr "Büyük sağlık" #: qcsrc/common/items/item/health.qh:147 msgid "Mega health" -msgstr "" +msgstr "Mega sağlık" #: qcsrc/common/items/item/jetpack.qh:35 msgid "Jet Pack" -msgstr "" +msgstr "Jet Paketi" #: qcsrc/common/items/item/jetpack.qh:82 msgid "Fuel regen" -msgstr "" +msgstr "Yakıt yağmuru" #: qcsrc/common/items/item/powerup.qh:44 msgid "Strength" -msgstr "" +msgstr "Kuvvet" #: qcsrc/common/items/item/powerup.qh:76 msgid "Shield" -msgstr "" +msgstr "Zırh" #: qcsrc/common/mapinfo.qc:639 #, no-c-format msgid "@!#%'n Tuba Throwing" -msgstr "" +msgstr "@!#%'n Tuba Atma" #: qcsrc/common/mapinfo.qh:99 msgid "Deathmatch" -msgstr "" +msgstr "Ölümmaçı" #: qcsrc/common/mapinfo.qh:99 msgid "Score as many frags as you can" -msgstr "" +msgstr "Yapabildiğiniz kadar çok sayıda parçaya puan verin" #: qcsrc/common/mapinfo.qh:111 msgid "Last Man Standing" -msgstr "" +msgstr "Ayakta Kalan Son Kişi" #: qcsrc/common/mapinfo.qh:111 msgid "Survive and kill until the enemies have no lives left" -msgstr "" +msgstr "Hayatta kalanlar yok olana kadar hayatta kal ve öldür" #: qcsrc/common/mapinfo.qh:126 msgid "Race" -msgstr "" +msgstr "Yarış" #: qcsrc/common/mapinfo.qh:126 msgid "Race against other players to the finish line" -msgstr "" +msgstr "Bitiş çizgisine diğer oyunculara karşı yarışın" #: qcsrc/common/mapinfo.qh:160 msgid "Race CTS" @@ -1192,236 +1196,245 @@ msgstr "" #: qcsrc/common/mapinfo.qh:160 msgid "Race for fastest time." -msgstr "" +msgstr "En hızlı zaman için yarış." #: qcsrc/common/mapinfo.qh:184 msgid "Help your team score the most frags against the enemy team" msgstr "" +"Takımınıza, düşman takımına karşı en iyi skoru elde etmesi için yardım edin." #: qcsrc/common/mapinfo.qh:184 msgid "Team Deathmatch" -msgstr "" +msgstr "Ölümmaçı Takımı" #: qcsrc/common/mapinfo.qh:220 msgid "Capture the Flag" -msgstr "" +msgstr "Bayrağı kap" #: qcsrc/common/mapinfo.qh:220 msgid "" "Find and bring the enemy flag to your base to capture it, defend your base " "from the other team" msgstr "" +"Yakalamak için düşman bayrağını bulup üsse getir, üssünüzü de diğer takımdan " +"koru" #: qcsrc/common/mapinfo.qh:249 msgid "Clan Arena" -msgstr "" +msgstr "Klan Alanı" #: qcsrc/common/mapinfo.qh:249 msgid "Kill all enemy teammates to win the round" -msgstr "" +msgstr "Turu kazanmak için tüm düşman takım unsurlarını öldür" #: qcsrc/common/mapinfo.qh:287 msgid "Capture and defend all the control points to win" -msgstr "" +msgstr "Kazanmak için tüm kontrol noktalarını yakalayın ve savunun" #: qcsrc/common/mapinfo.qh:287 msgid "Domination" -msgstr "" +msgstr "Hakimiyet" #: qcsrc/common/mapinfo.qh:319 msgid "Gather all the keys to win the round" -msgstr "" +msgstr "Turu kazanmak için tüm anahtarları topla" #: qcsrc/common/mapinfo.qh:319 msgid "Key Hunt" -msgstr "" +msgstr "Anahtar Avı" #: qcsrc/common/mapinfo.qh:353 msgid "Assault" -msgstr "" +msgstr "Hücum" #: qcsrc/common/mapinfo.qh:353 msgid "" "Destroy obstacles to find and destroy the enemy power core before time runs " "out" -msgstr "" +msgstr "Zaman dolmadan düşman güç merkezini bul ve yok etmek için engelleri aş" #: qcsrc/common/mapinfo.qh:371 msgid "Capture control points to reach and destroy the enemy generator" -msgstr "" +msgstr "Düşman jeneratörüne ulaşmak ve yok etmek için kontrol noktalarını geç" #: qcsrc/common/mapinfo.qh:371 msgid "Onslaught" -msgstr "" +msgstr "Saldırı" #: qcsrc/common/mapinfo.qh:387 msgid "Nexball" -msgstr "" +msgstr "Nexball" #: qcsrc/common/mapinfo.qh:387 msgid "Shoot and kick the ball into the enemies goal, keep your goal clean" -msgstr "" +msgstr "Düşman kalesine gol at, kendi kaleni koru" #: qcsrc/common/mapinfo.qh:408 msgid "Freeze Tag" -msgstr "" +msgstr "Dondurucu Etiket" #: qcsrc/common/mapinfo.qh:408 msgid "" "Kill enemies to freeze them, stand next to frozen teammates to revive them; " "freeze all enemies to win" msgstr "" +"Onları dondurmak için düşmanları öldürün, donmuş ekip arkadaşlarının yanında " +"onları canlandırın; kazanmak için tüm düşmanları dondurun" #: qcsrc/common/mapinfo.qh:446 msgid "Hold the ball to get points for kills" -msgstr "" +msgstr "Öldürmek için puan almak için topu tut" #: qcsrc/common/mapinfo.qh:446 msgid "Keepaway" -msgstr "" +msgstr "Uzak dur" #: qcsrc/common/mapinfo.qh:461 msgid "Invasion" -msgstr "" +msgstr "İstila" #: qcsrc/common/mapinfo.qh:461 msgid "Survive against waves of monsters" -msgstr "" +msgstr "Canavarların saldırı dalgalarına karşı hayatta kal" #: qcsrc/common/minigames/cl_minigames.qc:383 msgid "It's your turn" -msgstr "" +msgstr "Senin sıran" #: qcsrc/common/minigames/cl_minigames_hud.qc:331 #: qcsrc/menu/xonotic/dialog_quit.qh:6 msgid "Quit" -msgstr "" +msgstr "Çıkış" #: qcsrc/common/minigames/cl_minigames_hud.qc:336 msgid "Invite" -msgstr "" +msgstr "Davet" #: qcsrc/common/minigames/cl_minigames_hud.qc:378 msgid "Current Game" -msgstr "" +msgstr "Geçerli Oyun" #: qcsrc/common/minigames/cl_minigames_hud.qc:403 msgid "Exit Menu" -msgstr "" +msgstr "Menüden Çık" #: qcsrc/common/minigames/cl_minigames_hud.qc:415 #: qcsrc/menu/xonotic/dialog_multiplayer.qc:16 msgid "Create" -msgstr "" +msgstr "Oluştur" #: qcsrc/common/minigames/cl_minigames_hud.qc:418 msgid "Join" -msgstr "" +msgstr "Giriş" #: qcsrc/common/minigames/cl_minigames_hud.qc:489 msgid "Minigames" -msgstr "" +msgstr "Küçükoyunlar" #: qcsrc/common/minigames/minigame/bd.qc:1168 msgid "Better luck next time!" -msgstr "" +msgstr "Bir dahaki sefere daha iyi şanslar!" #: qcsrc/common/minigames/minigame/bd.qc:1172 msgid "Tubular! Press \"Next Level\" to continue!" -msgstr "" +msgstr "Boru gibi! Devam etmek için \"Sonraki Seviye\" ye basın!" #: qcsrc/common/minigames/minigame/bd.qc:1174 msgid "Wicked! Press \"Next Level\" to continue!" -msgstr "" +msgstr "Kötü! Devam etmek için \"Sonraki Seviye\" ye basın!" #: qcsrc/common/minigames/minigame/bd.qc:1177 msgid "Press the space bar to change your currently selected tile" -msgstr "" +msgstr "Seçili olan kutucuğunuzu değiştirmek için boşluk tuşuna basın" #: qcsrc/common/minigames/minigame/bd.qc:1180 msgid "Push the boulders onto the targets" -msgstr "" +msgstr "Kayalar hedeflere doğru itin" #: qcsrc/common/minigames/minigame/bd.qc:1404 msgid "Next Level" -msgstr "" +msgstr "Sonraki Oyun" #: qcsrc/common/minigames/minigame/bd.qc:1405 msgid "Restart" -msgstr "" +msgstr "Yeniden Başlat" #: qcsrc/common/minigames/minigame/bd.qc:1406 msgid "Editor" -msgstr "" +msgstr "Yayımcı" #: qcsrc/common/minigames/minigame/bd.qc:1407 #: qcsrc/menu/xonotic/dialog_settings_input_userbind.qc:37 msgid "Save" -msgstr "" +msgstr "Kaydet" #: qcsrc/common/minigames/minigame/c4.qc:373 #: qcsrc/common/minigames/minigame/pp.qc:438 #: qcsrc/common/minigames/minigame/ttt.qc:319 msgid "Draw" -msgstr "" +msgstr "Çiz" #: qcsrc/common/minigames/minigame/c4.qc:378 #: qcsrc/common/minigames/minigame/nmm.qc:601 msgid "You lost the game!" -msgstr "" +msgstr "Oyunu kaybettin!" #: qcsrc/common/minigames/minigame/c4.qc:379 #: qcsrc/common/minigames/minigame/nmm.qc:602 msgid "You win!" -msgstr "" +msgstr "Kazandın!" #: qcsrc/common/minigames/minigame/c4.qc:383 #: qcsrc/common/minigames/minigame/nmm.qc:606 #: qcsrc/common/minigames/minigame/pp.qc:455 #: qcsrc/common/minigames/minigame/ttt.qc:336 msgid "Wait for your opponent to make their move" -msgstr "" +msgstr "Rakibinizin harekete geçmesini bekleyin" #: qcsrc/common/minigames/minigame/c4.qc:386 #: qcsrc/common/minigames/minigame/nmm.qc:608 #: qcsrc/common/minigames/minigame/pp.qc:458 #: qcsrc/common/minigames/minigame/ttt.qc:339 msgid "Click on the game board to place your piece" -msgstr "" +msgstr "Parçanızı yerleştirmek için oyun tahtasına tıklayın" #: qcsrc/common/minigames/minigame/nmm.qc:610 msgid "" "You can select one of your pieces to move it in one of the surrounding places" msgstr "" +"Çevrenizdeki yerlerden birinde taşımak için parçalarınızdan birini " +"seçebilirsiniz" #: qcsrc/common/minigames/minigame/nmm.qc:612 msgid "You can select one of your pieces to move it anywhere on the board" msgstr "" +"Tahtada herhangi bir yere taşımak için parçalarınızdan birini seçebilirsiniz" #: qcsrc/common/minigames/minigame/nmm.qc:614 msgid "You can take one of the opponent's pieces" -msgstr "" +msgstr "Rakibin parçalarından birini alabilirsin" #: qcsrc/common/minigames/minigame/pong.qc:570 #: qcsrc/common/minigames/minigame/ttt.qc:299 msgid "AI" -msgstr "" +msgstr "Yapay Zeka" #: qcsrc/common/minigames/minigame/pong.qc:587 msgid "Press ^1Start Match^7 to start the match with the current players" msgstr "" +"Mevcut oyuncularla maçı başlatmak için ^1Müsabaka Başlat^7 tuşuna basın" #: qcsrc/common/minigames/minigame/pong.qc:651 msgid "Start Match" -msgstr "" +msgstr "Müsabaka Başlat" #: qcsrc/common/minigames/minigame/pong.qc:652 msgid "Add AI player" -msgstr "" +msgstr "YZ Oyuncusu Ekle" #: qcsrc/common/minigames/minigame/pong.qc:653 msgid "Remove AI player" -msgstr "" +msgstr "YZ Oyuncusunu Sil" #: qcsrc/common/minigames/minigame/pp.qc:443 #: qcsrc/common/minigames/minigame/ttt.qc:324 @@ -1429,6 +1442,8 @@ msgid "" "You lost the game!\n" "Select \"^1Next Match^7\" on the menu for a rematch!" msgstr "" +"Oyunu kaybettin!\n" +"Tekrar oynamak için menüden \"^1Sonraki Müsabaka^7\" öğesini seç." #: qcsrc/common/minigames/minigame/pp.qc:444 #: qcsrc/common/minigames/minigame/ttt.qc:325 @@ -1436,6 +1451,8 @@ msgid "" "You win!\n" "Select \"^1Next Match^7\" on the menu to start a new match!" msgstr "" +"Sen kazandın!\n" +"Yeni bir müsabaka için menüden \"^1Sonraki Müsabaka^7\" öğesini seç!" #: qcsrc/common/minigames/minigame/pp.qc:450 #: qcsrc/common/minigames/minigame/ttt.qc:331 @@ -1450,33 +1467,33 @@ msgstr "" #: qcsrc/common/minigames/minigame/pp.qc:582 #: qcsrc/common/minigames/minigame/ttt.qc:665 msgid "Next Match" -msgstr "" +msgstr "Sonraki Müsabaka" #: qcsrc/common/minigames/minigame/ps.qc:478 #, c-format msgid "Pieces left: %s" -msgstr "" +msgstr "Kalan parça: %s" #: qcsrc/common/minigames/minigame/ps.qc:488 msgid "No more valid moves" -msgstr "" +msgstr "Daha fazla geçerli hareket yok" #: qcsrc/common/minigames/minigame/ps.qc:491 msgid "Well done, you win!" -msgstr "" +msgstr "Aferin, sen kazandın!" #: qcsrc/common/minigames/minigame/ps.qc:494 msgid "Jump a piece over another to capture it" -msgstr "" +msgstr "Yakalamak için bir parçayı diğerine atla" #: qcsrc/common/minigames/minigame/ttt.qc:666 msgid "Single Player" -msgstr "" +msgstr "Tek Oyuncu" #: qcsrc/common/monsters/monster/mage.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:18 msgid "Mage" -msgstr "" +msgstr "Sihirbaz" #: qcsrc/common/monsters/monster/mage.qh:29 msgid "Mage spike" @@ -1490,116 +1507,116 @@ msgstr "" #: qcsrc/common/monsters/monster/spider.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:16 msgid "Spider" -msgstr "" +msgstr "Örümcek" #: qcsrc/common/monsters/monster/spider.qh:28 msgid "Spider attack" -msgstr "" +msgstr "Örümcek saldırısı" #: qcsrc/common/monsters/monster/wyvern.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:19 msgid "Wyvern" -msgstr "" +msgstr "Ejder" #: qcsrc/common/monsters/monster/wyvern.qh:28 msgid "Wyvern attack" -msgstr "" +msgstr "Ejder saldırısı" #: qcsrc/common/monsters/monster/zombie.qh:17 #: qcsrc/menu/xonotic/dialog_monstertools.qc:15 msgid "Zombie" -msgstr "" +msgstr "Zombi" #: qcsrc/common/mutators/mutator/buffs/all.inc:15 msgid "Ammo" -msgstr "" +msgstr "Cephane" #: qcsrc/common/mutators/mutator/buffs/all.inc:24 msgid "Resistance" -msgstr "" +msgstr "Direnç" #: qcsrc/common/mutators/mutator/buffs/all.inc:33 #: qcsrc/common/mutators/mutator/instagib/items.qh:94 msgid "Speed" -msgstr "" +msgstr "Hız" #: qcsrc/common/mutators/mutator/buffs/all.inc:43 msgid "Medic" -msgstr "" +msgstr "Sıhhiye" #: qcsrc/common/mutators/mutator/buffs/all.inc:54 msgid "Bash" -msgstr "" +msgstr "Darbe" #: qcsrc/common/mutators/mutator/buffs/all.inc:62 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:85 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:177 msgid "Vampire" -msgstr "" +msgstr "Vampir" #: qcsrc/common/mutators/mutator/buffs/all.inc:70 msgid "Disability" -msgstr "" +msgstr "Sakatlık" #: qcsrc/common/mutators/mutator/buffs/all.inc:78 msgid "Vengeance" -msgstr "" +msgstr "İntikam" #: qcsrc/common/mutators/mutator/buffs/all.inc:86 msgid "Jump" -msgstr "" +msgstr "Zıpla" #: qcsrc/common/mutators/mutator/buffs/all.inc:95 msgid "Invisible" -msgstr "" +msgstr "Görünmez" #: qcsrc/common/mutators/mutator/buffs/all.inc:104 msgid "Inferno" -msgstr "" +msgstr "Cehennem" #: qcsrc/common/mutators/mutator/buffs/all.inc:112 msgid "Swapper" -msgstr "" +msgstr "Takascı" #: qcsrc/common/mutators/mutator/buffs/all.inc:120 msgid "Magnet" -msgstr "" +msgstr "Mıknatıs" #: qcsrc/common/mutators/mutator/buffs/all.inc:128 msgid "Luck" -msgstr "" +msgstr "Şans" #: qcsrc/common/mutators/mutator/buffs/all.inc:136 msgid "Flight" -msgstr "" +msgstr "Uçuş" #: qcsrc/common/mutators/mutator/buffs/buffs.qh:7 msgid "Buff" -msgstr "" +msgstr "Devetüyü" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:8 msgid "Damage text" -msgstr "" +msgstr "Hasar metni" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:18 msgid "Draw damage numbers" -msgstr "" +msgstr "Hasar numarasını çiz" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 msgid "Font size minimum:" -msgstr "" +msgstr "Asgari yazı tipi boyutu:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 msgid "Font size maximum:" -msgstr "" +msgstr "Azami yazı tipi boyutu:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 msgid "Accumulate range:" -msgstr "" +msgstr "Birikim aralığı:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:35 msgid "Lifetime:" -msgstr "" +msgstr "Ömür:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:40 #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:50 @@ -1609,146 +1626,146 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:109 #: qcsrc/menu/xonotic/util.qc:775 msgid "Color:" -msgstr "" +msgstr "Renk:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:47 msgid "Draw damage numbers for friendly fire" -msgstr "" +msgstr "Dost ateşi için hasar numarası çizin" #: qcsrc/common/mutators/mutator/instagib/items.qh:56 msgid "Extra life" -msgstr "" +msgstr "İlave can" #: qcsrc/common/mutators/mutator/instagib/items.qh:75 msgid "Invisibility" -msgstr "" +msgstr "Görünmezlik" #: qcsrc/common/mutators/mutator/nades/nades.inc:18 msgid "Napalm grenade" -msgstr "" +msgstr "Napalm bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:26 msgid "Ice grenade" -msgstr "" +msgstr "Buz bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:34 msgid "Translocate grenade" -msgstr "" +msgstr "Yer değiştirme bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:42 msgid "Spawn grenade" -msgstr "" +msgstr "Ortaya çıkma bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:50 msgid "Heal grenade" -msgstr "" +msgstr "İyileşme bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:58 msgid "Monster grenade" -msgstr "" +msgstr "Canavar bombası" #: qcsrc/common/mutators/mutator/nades/nades.inc:66 msgid "Entrap grenade" -msgstr "" +msgstr "Tuzak bombası" #: qcsrc/common/mutators/mutator/nades/nades.qh:32 msgid "Grenade" -msgstr "" +msgstr "Bomba" #: qcsrc/common/mutators/mutator/overkill/hmg.qh:17 msgid "Heavy Machine Gun" -msgstr "" +msgstr "Ağır makineli tüfek" #: qcsrc/common/mutators/mutator/overkill/rpc.qh:17 msgid "Rocket Propelled Chainsaw" -msgstr "" +msgstr "Roket tahrikli testere" #: qcsrc/common/mutators/mutator/waypoints/all.inc:3 msgid "Waypoint" -msgstr "" +msgstr "Ara nokta" #: qcsrc/common/mutators/mutator/waypoints/all.inc:4 msgid "Help me!" -msgstr "" +msgstr "Yardım et!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:5 msgid "Here" -msgstr "" +msgstr "İşte" #: qcsrc/common/mutators/mutator/waypoints/all.inc:6 msgid "DANGER" -msgstr "" +msgstr "TEHLİKE" #: qcsrc/common/mutators/mutator/waypoints/all.inc:8 msgid "Frozen!" -msgstr "" +msgstr "Soğuk!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:10 msgid "Item" -msgstr "" +msgstr "Nesne" #: qcsrc/common/mutators/mutator/waypoints/all.inc:12 msgid "Checkpoint" -msgstr "" +msgstr "Kontrol noktası" #: qcsrc/common/mutators/mutator/waypoints/all.inc:13 #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 msgid "Finish" -msgstr "" +msgstr "Bitiş" #: qcsrc/common/mutators/mutator/waypoints/all.inc:14 #: qcsrc/common/mutators/mutator/waypoints/all.inc:15 #: qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc:252 msgid "Start" -msgstr "" +msgstr "Başlat" #: qcsrc/common/mutators/mutator/waypoints/all.inc:17 msgid "Defend" -msgstr "" +msgstr "Savun" #: qcsrc/common/mutators/mutator/waypoints/all.inc:18 msgid "Destroy" -msgstr "" +msgstr "Yoket" #: qcsrc/common/mutators/mutator/waypoints/all.inc:19 msgid "Push" -msgstr "" +msgstr "İt" #: qcsrc/common/mutators/mutator/waypoints/all.inc:21 msgid "Flag carrier" -msgstr "" +msgstr "Bayrak taşıyıcı" #: qcsrc/common/mutators/mutator/waypoints/all.inc:22 msgid "Enemy carrier" -msgstr "" +msgstr "Düşman taşıyıcı" #: qcsrc/common/mutators/mutator/waypoints/all.inc:23 msgid "Dropped flag" -msgstr "" +msgstr "Bırakılan bayrak" #: qcsrc/common/mutators/mutator/waypoints/all.inc:24 msgid "White base" -msgstr "" +msgstr "Beyaz zemin" #: qcsrc/common/mutators/mutator/waypoints/all.inc:25 msgid "Red base" -msgstr "" +msgstr "Kırmızı zemin" #: qcsrc/common/mutators/mutator/waypoints/all.inc:26 msgid "Blue base" -msgstr "" +msgstr "Mavi zemin" #: qcsrc/common/mutators/mutator/waypoints/all.inc:27 msgid "Yellow base" -msgstr "" +msgstr "Sarı zemin" #: qcsrc/common/mutators/mutator/waypoints/all.inc:28 msgid "Pink base" -msgstr "" +msgstr "Pembe zemin" #: qcsrc/common/mutators/mutator/waypoints/all.inc:29 msgid "Return flag here" -msgstr "" +msgstr "Bayrağı buraya getir" #: qcsrc/common/mutators/mutator/waypoints/all.inc:31 #: qcsrc/common/mutators/mutator/waypoints/all.inc:32 @@ -1759,11 +1776,11 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:52 #: qcsrc/common/mutators/mutator/waypoints/all.inc:53 msgid "Control point" -msgstr "" +msgstr "Kontrol noktası" #: qcsrc/common/mutators/mutator/waypoints/all.inc:37 msgid "Dropped key" -msgstr "" +msgstr "Bırakılan anahtar" #: qcsrc/common/mutators/mutator/waypoints/all.inc:38 #: qcsrc/common/mutators/mutator/waypoints/all.inc:40 @@ -1771,7 +1788,7 @@ msgstr "" #: qcsrc/common/mutators/mutator/waypoints/all.inc:42 #: qcsrc/common/mutators/mutator/waypoints/all.inc:43 msgid "Key carrier" -msgstr "" +msgstr "Anahtar taşıyıcı" #: qcsrc/common/mutators/mutator/waypoints/all.inc:39 msgid "Run here" @@ -4945,11 +4962,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_credits.qh:7 msgid "Credits" -msgstr "" +msgstr "Katkılar" #: qcsrc/menu/xonotic/dialog_credits.qh:8 msgid "The Xonotic credits" -msgstr "" +msgstr "Xonotic katkıcıları" #: qcsrc/menu/xonotic/dialog_firstrun.qc:39 msgid "" @@ -4982,7 +4999,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_firstrun.qc:88 msgid "Save settings" -msgstr "" +msgstr "Ayarları kaydet" #: qcsrc/menu/xonotic/dialog_firstrun.qh:6 msgid "Welcome" @@ -5625,7 +5642,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:97 msgid "DOCK^Medium" -msgstr "" +msgstr "DOCK^Vasat" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:98 msgid "DOCK^Large" @@ -5633,7 +5650,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:121 msgid "Grid settings:" -msgstr "" +msgstr "Izgara ayarları:" #: qcsrc/menu/xonotic/dialog_hudsetup_exit.qc:124 msgid "Snap panels to grid" @@ -5728,7 +5745,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer.qh:6 msgid "Multiplayer" -msgstr "" +msgstr "Çoklu Oyuncu" #: qcsrc/menu/xonotic/dialog_multiplayer.qh:7 msgid "" @@ -5970,7 +5987,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_create.qc:229 msgid "Start Multiplayer!" -msgstr "" +msgstr "Çoklu Oyuncu Başlat!" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc:58 msgid "Title:" @@ -6345,7 +6362,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:251 msgid "Settings:" -msgstr "" +msgstr "Ayarlar:" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:258 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:290 @@ -6733,11 +6750,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings.qh:6 msgid "Settings" -msgstr "" +msgstr "Ayarlar" #: qcsrc/menu/xonotic/dialog_settings.qh:7 msgid "Change the game settings" -msgstr "" +msgstr "Oyun ayarlarını değiştir" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:29 msgid "Master:" @@ -6969,7 +6986,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:50 msgid "PRE^Medium" -msgstr "" +msgstr "PRE^Vasat" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:52 msgid "PRE^Normal" @@ -7029,7 +7046,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:80 msgid "PDET^Medium" -msgstr "" +msgstr "PDET^Vasat" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:81 msgid "PDET^Normal" @@ -7090,6 +7107,8 @@ msgid "" "Disable textures completely for very slow hardware. This gives a huge " "performance boost, but looks very ugly. (default: disabled)" msgstr "" +"Çok yavaş donanım için dokuları tamamen devre dışı bırakın. Bu büyük bir " +"performans artışı sağlar, ancak çok çirkin görünür. (varsayılan: devre dışı)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:135 msgid "Use lightmaps" @@ -7596,7 +7615,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:65 msgid "Gamemode Settings" -msgstr "" +msgstr "Oyun modu ayarları" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:67 msgid "Display capture times in Capture The Flag" @@ -8209,7 +8228,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_misc.qc:151 msgid "Advanced settings..." -msgstr "" +msgstr "Gelişmiş ayarlar..." #: qcsrc/menu/xonotic/dialog_settings_misc.qc:152 msgid "Advanced settings where you can tweak every single variable of the game" @@ -8324,7 +8343,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_video.qc:43 msgid "SZ^Medium" -msgstr "" +msgstr "SZ^Vasat" #: qcsrc/menu/xonotic/dialog_settings_video.qc:44 msgid "SZ^Large" @@ -8586,27 +8605,27 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:131 msgid "CSKL^Easy" -msgstr "" +msgstr "CSKL^Kolay" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:132 msgid "CSKL^Medium" -msgstr "" +msgstr "CSKL^Vasat" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:133 msgid "CSKL^Hard" -msgstr "" +msgstr "CSKL^Zor" #: qcsrc/menu/xonotic/dialog_singleplayer.qc:135 msgid "Start Singleplayer!" -msgstr "" +msgstr "Tek Oyuncu Başlat!" #: qcsrc/menu/xonotic/dialog_singleplayer.qh:6 msgid "Singleplayer" -msgstr "" +msgstr "Tekli Oyuncu" #: qcsrc/menu/xonotic/dialog_singleplayer.qh:7 msgid "Play the singleplayer campaign or instant action matches against bots" -msgstr "" +msgstr "Tek oyunculu mücadele veya botlara karşı anlık müsabakaları oynayın" #: qcsrc/menu/xonotic/dialog_singleplayer_winner.qh:7 msgid "Winner" @@ -8891,12 +8910,12 @@ msgstr "" #: qcsrc/menu/xonotic/serverlist.qc:1063 #, c-format msgid "modified settings" -msgstr "" +msgstr "Ayarları düzenle" #: qcsrc/menu/xonotic/serverlist.qc:1063 #, c-format msgid "official settings" -msgstr "" +msgstr "Varsayılan ayarlar" #: qcsrc/menu/xonotic/serverlist.qc:1065 msgid "stats disabled" @@ -8979,7 +8998,7 @@ msgstr "" #: qcsrc/menu/xonotic/slider_particles.qc:16 msgid "PART^Medium" -msgstr "" +msgstr "PART^Vasat" #: qcsrc/menu/xonotic/slider_particles.qc:17 #: qcsrc/menu/xonotic/slider_sbfadetime.qc:14 @@ -9004,10 +9023,13 @@ msgid "" "texture memory usage, but make the textures appear very blurry. (default: " "good)" msgstr "" +"Doku keskinliğini değiştirin. Ayarları düşürmek, doku belleği kullanımını " +"etkili bir şekilde azaltır, ancak dokuların çok bulanık görünmesine sebep " +"olur. (varsayılan: iyi)" #: qcsrc/menu/xonotic/slider_resolution.qc:115 msgid "Screen resolution" -msgstr "" +msgstr "Ekran çözünürlüğü" #: qcsrc/menu/xonotic/slider_sbfadetime.qc:13 msgid "PART^Slow" @@ -9023,97 +9045,97 @@ msgstr "" #: qcsrc/menu/xonotic/statslist.qc:29 msgid "January" -msgstr "" +msgstr "Ocak" #: qcsrc/menu/xonotic/statslist.qc:30 msgid "February" -msgstr "" +msgstr "Şubat" #: qcsrc/menu/xonotic/statslist.qc:31 msgid "March" -msgstr "" +msgstr "Mart" #: qcsrc/menu/xonotic/statslist.qc:32 msgid "April" -msgstr "" +msgstr "Nisan" #: qcsrc/menu/xonotic/statslist.qc:33 msgid "May" -msgstr "" +msgstr "Mayıs" #: qcsrc/menu/xonotic/statslist.qc:34 msgid "June" -msgstr "" +msgstr "Haziran" #: qcsrc/menu/xonotic/statslist.qc:35 msgid "July" -msgstr "" +msgstr "Temmuz" #: qcsrc/menu/xonotic/statslist.qc:36 msgid "August" -msgstr "" +msgstr "Ağustos" #: qcsrc/menu/xonotic/statslist.qc:37 msgid "September" -msgstr "" +msgstr "Eylül" #: qcsrc/menu/xonotic/statslist.qc:38 msgid "October" -msgstr "" +msgstr "Ekim" #: qcsrc/menu/xonotic/statslist.qc:39 msgid "November" -msgstr "" +msgstr "Kasım" #: qcsrc/menu/xonotic/statslist.qc:40 msgid "December" -msgstr "" +msgstr "Aralık" #: qcsrc/menu/xonotic/statslist.qc:96 msgid "Joined:" -msgstr "" +msgstr "Katıldı:" #: qcsrc/menu/xonotic/statslist.qc:103 msgid "Last_Seen:" -msgstr "" +msgstr "Son_Görülme:" #: qcsrc/menu/xonotic/statslist.qc:110 msgid "Time_Played:" -msgstr "" +msgstr "Oynama_Süresi:" #: qcsrc/menu/xonotic/statslist.qc:117 msgid "Favorite_Map:" -msgstr "" +msgstr "Favori_Harita:" #: qcsrc/menu/xonotic/statslist.qc:201 qcsrc/menu/xonotic/statslist.qc:245 #, c-format msgid "%s_Matches:" -msgstr "" +msgstr "%s_Maçlar:" #: qcsrc/menu/xonotic/statslist.qc:208 #, c-format msgid "%s_ELO:" -msgstr "" +msgstr "%s_ELO:" #: qcsrc/menu/xonotic/statslist.qc:215 #, c-format msgid "%s_Rank:" -msgstr "" +msgstr "%s_Derece:" #: qcsrc/menu/xonotic/statslist.qc:222 #, c-format msgid "%s_Percentile:" -msgstr "" +msgstr "%s_Yüzdelik_Dilim:" #: qcsrc/menu/xonotic/statslist.qc:231 #, c-format msgid "%s_Favorite_Map:" -msgstr "" +msgstr "%s_Favori_Harita:" #: qcsrc/menu/xonotic/statslist.qc:246 #, c-format msgid "%d (unranked)" -msgstr "" +msgstr "%d (derecelendirilmemiş)" #: qcsrc/menu/xonotic/util.qc:417 #, c-format @@ -9121,35 +9143,42 @@ msgid "" "Update can be downloaded at:\n" "%s\n" msgstr "" +"Güncelleme şuradan indirilebilir:\n" +"%s\n" #: qcsrc/menu/xonotic/util.qc:528 msgid "Autogenerating mapinfo for newly added maps..." -msgstr "" +msgstr "Yeni eklenen haritalar için harita bilgisi oluşturuluyor..." #: qcsrc/menu/xonotic/util.qc:557 #, c-format msgid "^1%s TEST BUILD" -msgstr "" +msgstr "^1%s TEST YAPISI" #: qcsrc/menu/xonotic/util.qc:577 #, c-format msgid "Update to %s now!" -msgstr "" +msgstr "Şimdi %s 'e güncelle!" #: qcsrc/menu/xonotic/util.qc:662 msgid "" "^1ERROR: Texture compression is required but not supported.\n" "^1Expect visual problems.\n" msgstr "" +"^1HATA: Doku sıkıştırması gerekli ama deskteklenmiyor.\n" +"^1Görsel problemler olabilir.\n" #: qcsrc/menu/xonotic/util.qc:780 msgid "Use default" -msgstr "" +msgstr "Varsayılanı kullan" #: qcsrc/menu/xonotic/util.qc:800 msgid "Team Color:" -msgstr "" +msgstr "Takım Rengi:" #: qcsrc/menu/xonotic/util.qh:44 msgid "Enable panel" -msgstr "" +msgstr "Paneli etkinleştir" + +#~ msgid "QMCMD^Chat" +#~ msgstr "QMCMD^Sohbet" diff --git a/common.uk.po b/common.uk.po index a57869003c..f164a92f21 100644 --- a/common.uk.po +++ b/common.uk.po @@ -20,8 +20,10 @@ msgstr "" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" -"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" +"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != " +"11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % " +"100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || " +"(n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" #: qcsrc/client/hud/hud_config.qc:239 #, c-format diff --git a/common.zh_CN.po b/common.zh_CN.po index 0287246689..669d4a45c5 100644 --- a/common.zh_CN.po +++ b/common.zh_CN.po @@ -7,14 +7,16 @@ # Antoni Das <Antonidas159@gmail.com>, 2015,2017 # sapphireliu <balancedliu@gmail.com>, 2014 # kalawore <kalawore@outlook.com>, 2015 +# Losier Blackheath <losier.cc@gmail.com>, 2018 # sapphireliu <balancedliu@gmail.com>, 2014 +# 茂森 杜 <dumaosen_main01@outlook.com>, 2018 msgid "" msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2017-07-09 00:35+0200\n" -"PO-Revision-Date: 2017-09-19 23:30+0000\n" -"Last-Translator: divVerent <divVerent@xonotic.org>\n" +"PO-Revision-Date: 2018-09-02 02:29+0000\n" +"Last-Translator: Losier Blackheath <losier.cc@gmail.com>\n" "Language-Team: Chinese (China) (http://www.transifex.com/team-xonotic/" "xonotic/language/zh_CN/)\n" "Language: zh_CN\n" @@ -180,11 +182,11 @@ msgstr "团队菜单" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating this player:" -msgstr "" +msgstr "^1观看这个玩家:" #: qcsrc/client/hud/panel/infomessages.qc:209 msgid "^1Spectating you:" -msgstr "" +msgstr "^1观看你自己:" #: qcsrc/client/hud/panel/infomessages.qc:225 msgid "^7Press ^3ESC ^7to show HUD options." @@ -192,15 +194,15 @@ msgstr "^7按下 ^3ESC ^7来显示HUD设置。" #: qcsrc/client/hud/panel/infomessages.qc:226 msgid "^3Doubleclick ^7a panel for panel-specific options." -msgstr "" +msgstr "^3双击 ^7面板以获取面板特定选项。" #: qcsrc/client/hud/panel/infomessages.qc:227 msgid "^3CTRL ^7to disable collision testing, ^3SHIFT ^7and" -msgstr "" +msgstr "^3CTRL ^7以禁用碰撞检测, ^3SHIFT ^7以及" #: qcsrc/client/hud/panel/infomessages.qc:228 msgid "^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments." -msgstr "" +msgstr "^3ALT ^7+ ^3箭头键 ^7以微调。" #: qcsrc/client/hud/panel/modicons.qc:566 msgid "Personal best" @@ -250,11 +252,11 @@ msgstr "QMCMD^好游戏" #: qcsrc/client/hud/panel/quickmenu.qc:797 msgid "QMCMD^hi / good luck" -msgstr "QMCMD^hi / 祝好运" +msgstr "QMCMD^hi / 祝你好运" #: qcsrc/client/hud/panel/quickmenu.qc:797 msgid "QMCMD^hi / good luck and have fun" -msgstr "" +msgstr "QMCMD^hi / 祝你好运,玩的开心" #: qcsrc/client/hud/panel/quickmenu.qc:802 #: qcsrc/client/hud/panel/quickmenu.qc:818 @@ -275,7 +277,7 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item (l:%l^7)" -msgstr "" +msgstr "QMCMD^捡起物品 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:805 msgid "QMCMD^took item, icon" @@ -339,7 +341,7 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier (l:%y^7)" -msgstr "" +msgstr "QMCMD^killed flagcarrier (l:%y^7)" #: qcsrc/client/hud/panel/quickmenu.qc:814 msgid "QMCMD^killed flagcarrier, icon" @@ -356,7 +358,7 @@ msgstr "" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^drop weapon, icon" -msgstr "" +msgstr "QMCMD^掉落武器, icon" #: qcsrc/client/hud/panel/quickmenu.qc:816 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" @@ -432,11 +434,11 @@ msgstr "QMCMD^第一人称" #: qcsrc/client/hud/panel/quickmenu.qc:842 msgid "QMCMD^3rd person around player" -msgstr "" +msgstr "QMCMD^玩家周围的第三人称视角" #: qcsrc/client/hud/panel/quickmenu.qc:843 msgid "QMCMD^3rd person behind" -msgstr "" +msgstr "QMCMD^身后的第三人称视角" #: qcsrc/client/hud/panel/quickmenu.qc:849 #: qcsrc/client/hud/panel/quickmenu.qc:854 @@ -453,11 +455,11 @@ msgstr "QMCMD^减少速度" #: qcsrc/client/hud/panel/quickmenu.qc:852 msgid "QMCMD^Wall collision off" -msgstr "" +msgstr "QMCMD^关闭墙壁碰撞" #: qcsrc/client/hud/panel/quickmenu.qc:853 msgid "QMCMD^Wall collision on" -msgstr "" +msgstr "QMCMD^开启墙壁碰撞" #: qcsrc/client/hud/panel/quickmenu.qc:857 msgid "QMCMD^Fullscreen" @@ -490,7 +492,7 @@ msgstr "QMCMD^延长比赛时间" #: qcsrc/client/hud/panel/quickmenu.qc:871 msgid "QMCMD^Shuffle teams" -msgstr "" +msgstr "QMCMD^随机组队" #: qcsrc/client/hud/panel/racetimer.qc:37 #, c-format @@ -546,11 +548,11 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:81 msgid "SCO^captime" -msgstr "" +msgstr "SCO^captime" #: qcsrc/client/hud/panel/scoreboard.qc:82 msgid "SCO^deaths" -msgstr "SCO^死亡" +msgstr "SCO^死亡数" #: qcsrc/client/hud/panel/scoreboard.qc:83 msgid "SCO^destroyed" @@ -586,19 +588,19 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:91 msgid "SCO^kdratio" -msgstr "" +msgstr "SCO^击杀/死亡比" #: qcsrc/client/hud/panel/scoreboard.qc:92 msgid "SCO^k/d" -msgstr "" +msgstr "SCO^击杀/死亡" #: qcsrc/client/hud/panel/scoreboard.qc:93 msgid "SCO^kdr" -msgstr "" +msgstr "SCO^击杀/死亡比" #: qcsrc/client/hud/panel/scoreboard.qc:94 msgid "SCO^kills" -msgstr "" +msgstr "SCO^击杀数" #: qcsrc/client/hud/panel/scoreboard.qc:95 msgid "SCO^laps" @@ -606,7 +608,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:96 msgid "SCO^lives" -msgstr "" +msgstr "SCO^生命数" #: qcsrc/client/hud/panel/scoreboard.qc:97 msgid "SCO^losses" @@ -622,7 +624,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:100 msgid "SCO^nick" -msgstr "" +msgstr "SCO^昵称" #: qcsrc/client/hud/panel/scoreboard.qc:101 msgid "SCO^objectives" @@ -650,15 +652,15 @@ msgstr "SCO^排名" #: qcsrc/client/hud/panel/scoreboard.qc:107 msgid "SCO^returns" -msgstr "" +msgstr "SCO^带回数" #: qcsrc/client/hud/panel/scoreboard.qc:108 msgid "SCO^revivals" -msgstr "" +msgstr "SCO^重生数" #: qcsrc/client/hud/panel/scoreboard.qc:109 msgid "SCO^rounds won" -msgstr "" +msgstr "SCO^赢局数" #: qcsrc/client/hud/panel/scoreboard.qc:110 msgid "SCO^score" @@ -666,7 +668,7 @@ msgstr "SCO^分数" #: qcsrc/client/hud/panel/scoreboard.qc:111 msgid "SCO^suicides" -msgstr "" +msgstr "SCO^自杀数" #: qcsrc/client/hud/panel/scoreboard.qc:112 msgid "SCO^takes" @@ -711,11 +713,11 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:305 msgid "^3ping^7 Ping time\n" -msgstr "^3延迟^7 延迟时间\n" +msgstr "^3ping^7 延迟\n" #: qcsrc/client/hud/panel/scoreboard.qc:306 msgid "^3pl^7 Packet loss\n" -msgstr "" +msgstr "^3pl^7 丢包率\n" #: qcsrc/client/hud/panel/scoreboard.qc:307 msgid "^3elo^7 Player ELO\n" @@ -731,39 +733,42 @@ msgstr "^3死亡^7 死亡数量\n" #: qcsrc/client/hud/panel/scoreboard.qc:310 msgid "^3suicides^7 Number of suicides\n" -msgstr "" +msgstr "^3suicides^7 自杀数\n" #: qcsrc/client/hud/panel/scoreboard.qc:311 msgid "^3frags^7 kills - suicides\n" -msgstr "" +msgstr "^3frags^7 击杀数 - 自杀数\n" #: qcsrc/client/hud/panel/scoreboard.qc:312 msgid "^3kd^7 The kill-death ratio\n" -msgstr "" +msgstr "^3kd^7 击杀-死亡比\n" #: qcsrc/client/hud/panel/scoreboard.qc:313 msgid "^3dmg^7 The total damage done\n" -msgstr "" +msgstr "^3dmg^7 造成的总破坏值\n" #: qcsrc/client/hud/panel/scoreboard.qc:314 msgid "^3dmgtaken^7 The total damage taken\n" -msgstr "" +msgstr "^3dmgtaken^7 遭受的总破坏值\n" #: qcsrc/client/hud/panel/scoreboard.qc:315 msgid "^3sum^7 frags - deaths\n" -msgstr "" +msgstr "^3sum^7 击杀数 - 死亡数\n" #: qcsrc/client/hud/panel/scoreboard.qc:316 msgid "" "^3caps^7 How often a flag (CTF) or a key (KeyHunt) was " "captured\n" msgstr "" +"^3caps^7 旗帜(CTF)或钥匙(KeyHunt)被夺取的频率\n" #: qcsrc/client/hud/panel/scoreboard.qc:317 msgid "" "^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a " "ball (Keepaway) was picked up\n" msgstr "" +"^3pickups^7 旗帜 (CTF) 或钥匙 (KeyHunt) 或球 (Keepaway) 被捡" +"起来\n" #: qcsrc/client/hud/panel/scoreboard.qc:318 msgid "^3captime^7 Time of fastest cap (CTF)\n" @@ -771,19 +776,19 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:319 msgid "^3fckills^7 Number of flag carrier kills\n" -msgstr "" +msgstr "^3fckills^7 击杀旗帜携带者数\n" #: qcsrc/client/hud/panel/scoreboard.qc:320 msgid "^3returns^7 Number of flag returns\n" -msgstr "" +msgstr "^3returns^7 带回旗帜数\n" #: qcsrc/client/hud/panel/scoreboard.qc:321 msgid "^3drops^7 Number of flag drops\n" -msgstr "" +msgstr "^3drops^7 旗帜掉落数\n" #: qcsrc/client/hud/panel/scoreboard.qc:322 msgid "^3lives^7 Number of lives (LMS)\n" -msgstr "" +msgstr "^3lives^7 生命数 (LMS)\n" #: qcsrc/client/hud/panel/scoreboard.qc:323 msgid "^3rank^7 Player rank\n" @@ -791,21 +796,21 @@ msgstr "^3排名^7 玩家排名\n" #: qcsrc/client/hud/panel/scoreboard.qc:324 msgid "^3pushes^7 Number of players pushed into void\n" -msgstr "" +msgstr "^3pushes^7 被推入虚空中的玩家数\n" #: qcsrc/client/hud/panel/scoreboard.qc:325 msgid "" "^3destroyed^7 Number of keys destroyed by pushing them into " "void\n" -msgstr "" +msgstr "^3destroyed^7 被推入虚空而销毁的钥匙数\n" #: qcsrc/client/hud/panel/scoreboard.qc:326 msgid "^3kckills^7 Number of keys carrier kills\n" -msgstr "" +msgstr "^3kckills^7 击杀钥匙携带者数\n" #: qcsrc/client/hud/panel/scoreboard.qc:327 msgid "^3losses^7 Number of times a key was lost\n" -msgstr "" +msgstr "^3losses^7 丢失钥匙数\n" #: qcsrc/client/hud/panel/scoreboard.qc:328 msgid "^3laps^7 Number of laps finished (race/cts)\n" @@ -839,7 +844,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:335 msgid "^3score^7 Total score\n" -msgstr "" +msgstr "^3score^7 总分\n" #: qcsrc/client/hud/panel/scoreboard.qc:338 msgid "" @@ -903,7 +908,7 @@ msgstr "秘密已发现:" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Capture time rankings" -msgstr "" +msgstr "占领时间排名" #: qcsrc/client/hud/panel/scoreboard.qc:1354 msgid "Rankings" @@ -917,7 +922,7 @@ msgstr "计分板" #: qcsrc/client/hud/panel/scoreboard.qc:1584 #, c-format msgid "Speed award: %d%s ^7(%s^7)" -msgstr "" +msgstr "速度奖励: %d%s ^7(%s^7)" #: qcsrc/client/hud/panel/scoreboard.qc:1588 #, c-format @@ -927,7 +932,7 @@ msgstr "" #: qcsrc/client/hud/panel/scoreboard.qc:1604 #, c-format msgid "Spectators" -msgstr "观察者" +msgstr "观众" #: qcsrc/client/hud/panel/scoreboard.qc:1619 #, c-format @@ -949,21 +954,21 @@ msgstr "或者" #: qcsrc/client/hud/panel/scoreboard.qc:1645 #, c-format msgid " until ^3%s %s^7" -msgstr "" +msgstr " 直到 ^3%s %s^7" #: qcsrc/client/hud/panel/scoreboard.qc:1639 #: qcsrc/client/hud/panel/scoreboard.qc:1646 #: qcsrc/client/hud/panel/scoreboard.qc:1658 #: qcsrc/client/hud/panel/scoreboard.qc:1665 msgid "SCO^points" -msgstr "" +msgstr "SCO^分" #: qcsrc/client/hud/panel/scoreboard.qc:1640 #: qcsrc/client/hud/panel/scoreboard.qc:1647 #: qcsrc/client/hud/panel/scoreboard.qc:1659 #: qcsrc/client/hud/panel/scoreboard.qc:1666 msgid "SCO^is beaten" -msgstr "" +msgstr "SCO^被击败" #: qcsrc/client/hud/panel/scoreboard.qc:1657 #: qcsrc/client/hud/panel/scoreboard.qc:1664 @@ -992,11 +997,11 @@ msgstr "" #: qcsrc/client/hud/panel/vote.qc:29 msgid "^2Name ^7instead of \"^1Anonymous player^7\" in stats" -msgstr "" +msgstr "^2名字^7而不是“^1匿名玩家^7”在统计信息中" #: qcsrc/client/hud/panel/vote.qc:115 msgid "A vote has been called for:" -msgstr "" +msgstr "一轮投票被发起:" #: qcsrc/client/hud/panel/vote.qc:117 msgid "Allow servers to store and display your name?" @@ -1061,7 +1066,7 @@ msgstr "节点" #: qcsrc/client/main.qc:1264 #, c-format msgid "%s (not bound)" -msgstr "" +msgstr "%s (未绑定)" #: qcsrc/client/mapvoting.qc:49 msgid " (1 vote)" @@ -1112,15 +1117,15 @@ msgstr "节点计时器" #: qcsrc/client/view.qc:1385 msgid "Capture progress" -msgstr "" +msgstr "占领进度" #: qcsrc/client/view.qc:1390 msgid "Revival progress" -msgstr "" +msgstr "重生进度" #: qcsrc/common/command/generic.qc:157 msgid "error creating curl handle\n" -msgstr "" +msgstr "创建下载描述符错误\n" #: qcsrc/common/command/generic.qc:403 msgid "Notification restart command only works with cl_cmd and sv_cmd.\n" @@ -1128,23 +1133,23 @@ msgstr "" #: qcsrc/common/gamemodes/gamemode/nexball/weapon.qh:7 msgid "Ball Stealer" -msgstr "" +msgstr "偷球者" #: qcsrc/common/items/item/armor.qh:111 msgid "Big armor" -msgstr "" +msgstr "大护甲" #: qcsrc/common/items/item/armor.qh:147 msgid "Mega armor" -msgstr "" +msgstr "超级护甲" #: qcsrc/common/items/item/health.qh:111 msgid "Big health" -msgstr "" +msgstr "大血包" #: qcsrc/common/items/item/health.qh:147 msgid "Mega health" -msgstr "" +msgstr "超级血包" #: qcsrc/common/items/item/jetpack.qh:35 msgid "Jet Pack" @@ -1156,7 +1161,7 @@ msgstr "恢复燃料" #: qcsrc/common/items/item/powerup.qh:44 msgid "Strength" -msgstr "加强" +msgstr "神力" #: qcsrc/common/items/item/powerup.qh:76 msgid "Shield" @@ -1189,7 +1194,7 @@ msgstr "赛跑" #: qcsrc/common/mapinfo.qh:126 msgid "Race against other players to the finish line" -msgstr "" +msgstr "与其他玩家赛跑到达终点线" #: qcsrc/common/mapinfo.qh:160 msgid "Race CTS" @@ -1201,7 +1206,7 @@ msgstr "" #: qcsrc/common/mapinfo.qh:184 msgid "Help your team score the most frags against the enemy team" -msgstr "" +msgstr "在与敌方队伍的斗争中帮助你的队伍拿到最多人头数" #: qcsrc/common/mapinfo.qh:184 msgid "Team Deathmatch" @@ -1219,7 +1224,7 @@ msgstr "找到并将敌人的旗帜带到你的基地并保护你的基地" #: qcsrc/common/mapinfo.qh:249 msgid "Clan Arena" -msgstr "" +msgstr "组队竞技" #: qcsrc/common/mapinfo.qh:249 msgid "Kill all enemy teammates to win the round" @@ -1249,11 +1254,11 @@ msgstr "突击" msgid "" "Destroy obstacles to find and destroy the enemy power core before time runs " "out" -msgstr "" +msgstr "在一定时间内摧毁障碍物以寻找并摧毁敌方能量核心" #: qcsrc/common/mapinfo.qh:371 msgid "Capture control points to reach and destroy the enemy generator" -msgstr "" +msgstr "占领控制点以到达并摧毁敌方发电器" #: qcsrc/common/mapinfo.qh:371 msgid "Onslaught" @@ -1261,7 +1266,7 @@ msgstr "猛攻" #: qcsrc/common/mapinfo.qh:387 msgid "Nexball" -msgstr "" +msgstr "Nexball" #: qcsrc/common/mapinfo.qh:387 msgid "Shoot and kick the ball into the enemies goal, keep your goal clean" @@ -1275,7 +1280,7 @@ msgstr "冻结式对战" msgid "" "Kill enemies to freeze them, stand next to frozen teammates to revive them; " "freeze all enemies to win" -msgstr "" +msgstr "杀死敌人以将他们封冻,站在被封冻的队友边以复活他们;封冻所有敌人即胜利" #: qcsrc/common/mapinfo.qh:446 msgid "Hold the ball to get points for kills" @@ -1291,7 +1296,7 @@ msgstr "入侵" #: qcsrc/common/mapinfo.qh:461 msgid "Survive against waves of monsters" -msgstr "" +msgstr "在许多波怪物的攻势下幸存" #: qcsrc/common/minigames/cl_minigames.qc:383 msgid "It's your turn" @@ -1329,7 +1334,7 @@ msgstr "小游戏" #: qcsrc/common/minigames/minigame/bd.qc:1168 msgid "Better luck next time!" -msgstr "" +msgstr "祝你下次好运!" #: qcsrc/common/minigames/minigame/bd.qc:1172 msgid "Tubular! Press \"Next Level\" to continue!" @@ -1349,7 +1354,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/bd.qc:1404 msgid "Next Level" -msgstr "" +msgstr "下一关" #: qcsrc/common/minigames/minigame/bd.qc:1405 msgid "Restart" @@ -1455,7 +1460,7 @@ msgstr "" #: qcsrc/common/minigames/minigame/pp.qc:582 #: qcsrc/common/minigames/minigame/ttt.qc:665 msgid "Next Match" -msgstr "" +msgstr "下场比赛" #: qcsrc/common/minigames/minigame/ps.qc:478 #, c-format @@ -1592,11 +1597,11 @@ msgstr "" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:20 msgid "Font size minimum:" -msgstr "" +msgstr "最小字体大小:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:25 msgid "Font size maximum:" -msgstr "" +msgstr "最大字体大小:" #: qcsrc/common/mutators/mutator/damagetext/ui_damagetext.qc:30 msgid "Accumulate range:" @@ -1753,7 +1758,7 @@ msgstr "粉红军基地" #: qcsrc/common/mutators/mutator/waypoints/all.inc:29 msgid "Return flag here" -msgstr "" +msgstr "把旗帜带回这里" #: qcsrc/common/mutators/mutator/waypoints/all.inc:31 #: qcsrc/common/mutators/mutator/waypoints/all.inc:32 @@ -1814,7 +1819,7 @@ msgstr "载具" #: qcsrc/common/mutators/mutator/waypoints/all.inc:62 msgid "Intruder!" -msgstr "" +msgstr "入侵者!" #: qcsrc/common/mutators/mutator/waypoints/all.inc:64 msgid "Tagged" @@ -1836,12 +1841,12 @@ msgstr "^1服务器提示:" #: qcsrc/common/notifications/all.inc:239 msgid "^F4NOTE: ^BGSpectator chat is not sent to players during the match" -msgstr "" +msgstr "^F4注意:^BG比赛过程中观众聊天不会被发送给玩家" #: qcsrc/common/notifications/all.inc:241 #, c-format msgid "^BG%s^BG captured the ^TC^TT^BG flag" -msgstr "" +msgstr "^BG%s^BG捕获了^TC^TT^BG旗帜" #: qcsrc/common/notifications/all.inc:242 #, c-format @@ -1849,16 +1854,17 @@ msgid "" "^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds, breaking ^BG" "%s^BG's previous record of ^F2%s^BG seconds" msgstr "" +"^BG%s^BG捕获^TC^TT^BG旗帜用了^F1%s^BG秒,打破了原来^BG%s^BG的^F2%s^BG秒的记录" #: qcsrc/common/notifications/all.inc:243 #, c-format msgid "^BG%s^BG captured the flag" -msgstr "" +msgstr "^BG%s^BG捕获旗帜" #: qcsrc/common/notifications/all.inc:244 #, c-format msgid "^BG%s^BG captured the ^TC^TT^BG flag in ^F1%s^BG seconds" -msgstr "" +msgstr "^BG%s^BG捕获^TC^TT^BG旗帜用了^F1%s^BG秒" #: qcsrc/common/notifications/all.inc:245 #, c-format @@ -1866,66 +1872,68 @@ msgid "" "^BG%s^BG captured the ^TC^TT^BG flag in ^F2%s^BG seconds, failing to break " "^BG%s^BG's previous record of ^F1%s^BG seconds" msgstr "" +"^BG%s^BG捕获^TC^TT^BG旗帜用了^F2%s^BG秒,未能打破原来^BG%s^BG的^F1%s^BG秒的记" +"录" #: qcsrc/common/notifications/all.inc:246 msgid "^BGThe ^TC^TT^BG flag was returned to base by its owner" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜被它的所有者带回基地" #: qcsrc/common/notifications/all.inc:247 msgid "^BGThe flag was returned by its owner" -msgstr "" +msgstr "^BG旗帜被它的所有者带回" #: qcsrc/common/notifications/all.inc:248 msgid "^BGThe ^TC^TT^BG flag was destroyed and returned to base" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜被摧毁并回到基地" #: qcsrc/common/notifications/all.inc:249 msgid "^BGThe flag was destroyed and returned to base" -msgstr "" +msgstr "^BG旗帜被摧毁并回到基地" #: qcsrc/common/notifications/all.inc:250 msgid "^BGThe ^TC^TT^BG flag was dropped in the base and returned itself" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜掉落在基地并自己返回" #: qcsrc/common/notifications/all.inc:251 msgid "^BGThe flag was dropped in the base and returned itself" -msgstr "" +msgstr "^BG旗帜掉落在基地并自己返回" #: qcsrc/common/notifications/all.inc:252 msgid "" "^BGThe ^TC^TT^BG flag fell somewhere it couldn't be reached and returned to " "base" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜掉落在无法到达的地方而自动回到基地" #: qcsrc/common/notifications/all.inc:253 msgid "^BGThe flag fell somewhere it couldn't be reached and returned to base" -msgstr "" +msgstr "^BG旗帜掉落在无法到达的地方而自动回到基地" #: qcsrc/common/notifications/all.inc:254 #, c-format msgid "" "^BGThe ^TC^TT^BG flag became impatient after ^F1%.2f^BG seconds and returned " "itself" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜在^F1%.2f^BG秒后失去耐心而自动返回基地" #: qcsrc/common/notifications/all.inc:255 #, c-format msgid "" "^BGThe flag became impatient after ^F1%.2f^BG seconds and returned itself" -msgstr "" +msgstr "^BG旗帜在 ^F1%.2f^BG秒后失去耐心而自动返回基地" #: qcsrc/common/notifications/all.inc:256 msgid "^BGThe ^TC^TT^BG flag has returned to the base" -msgstr "" +msgstr "^BG^TC^TT^BG旗帜已回到基地" #: qcsrc/common/notifications/all.inc:257 msgid "^BGThe flag has returned to the base" -msgstr "" +msgstr "^BG旗帜已回到基地" #: qcsrc/common/notifications/all.inc:258 #, c-format msgid "^BG%s^BG lost the ^TC^TT^BG flag" -msgstr "" +msgstr "^BG%s^BG失去了^TC^TT^BG旗帜" #: qcsrc/common/notifications/all.inc:259 #, c-format @@ -1935,24 +1943,24 @@ msgstr "^BG%s^BG 丢掉了旗帜" #: qcsrc/common/notifications/all.inc:260 #, c-format msgid "^BG%s^BG got the ^TC^TT^BG flag" -msgstr "" +msgstr "^BG%s^BG拿到了^TC^TT^BG旗帜" #: qcsrc/common/notifications/all.inc:261 #, c-format msgid "^BG%s^BG got the flag" -msgstr "" +msgstr "^BG%s^BG拿到了旗帜" #: qcsrc/common/notifications/all.inc:262 #: qcsrc/common/notifications/all.inc:263 #, c-format msgid "^BG%s^BG returned the ^TC^TT^BG flag" -msgstr "" +msgstr "^BG%s^BG带回^TC^TT^BG旗帜" #: qcsrc/common/notifications/all.inc:265 #: qcsrc/common/notifications/all.inc:553 #, c-format msgid "^F2Throwing coin... Result: %s^F2!" -msgstr "" +msgstr "^F2丢硬币……结果:%s^F2!" #: qcsrc/common/notifications/all.inc:267 msgid "^BGYou don't have any fuel for the ^F1Jetpack" @@ -1964,11 +1972,11 @@ msgstr "" #: qcsrc/common/notifications/all.inc:271 msgid "^F1Round already started, you will join the game in the next round" -msgstr "" +msgstr "^F1这轮比赛已经开始,你将在下一轮加入游戏" #: qcsrc/common/notifications/all.inc:272 msgid "^F2You will spectate in the next round" -msgstr "" +msgstr "^F2你将在下一轮旁观" #: qcsrc/common/notifications/all.inc:274 #, c-format @@ -2008,7 +2016,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:279 #, c-format msgid "^BG%s%s^K1 was cooked by ^BG%s^K1%s%s" -msgstr "" +msgstr "^BG%s%s^K1 was cooked by ^BG%s^K1%s%s" #: qcsrc/common/notifications/all.inc:280 #, c-format @@ -2125,17 +2133,17 @@ msgstr "" #: qcsrc/common/notifications/all.inc:300 #, c-format msgid "^BG%s%s^K1 got caught in the blast when ^BG%s^K1's Racer exploded%s%s" -msgstr "" +msgstr "^BG%s%s^K1被 ^BG%s^K1的火箭弹给炸飞了%s%s" #: qcsrc/common/notifications/all.inc:301 #, c-format msgid "^BG%s%s^K1 was bolted down by ^BG%s^K1's Racer%s%s" -msgstr "" +msgstr "^BG%s%s^K1给^BG%s^K1的火箭枪炸翻了%s%s" #: qcsrc/common/notifications/all.inc:302 #, c-format msgid "^BG%s%s^K1 couldn't find shelter from ^BG%s^K1's Racer%s%s" -msgstr "" +msgstr "^BG%s%s^K1来不及躲闪^BG%s^K1的火箭弹%s%s" #: qcsrc/common/notifications/all.inc:303 #, c-format @@ -2240,12 +2248,12 @@ msgstr "" #: qcsrc/common/notifications/all.inc:321 #, c-format msgid "^BG%s^K1 joins the Zombies%s%s" -msgstr "" +msgstr "^BG%s^K1参演行尸走肉%s%s" #: qcsrc/common/notifications/all.inc:322 #, c-format msgid "^BG%s^K1 was given kung fu lessons by a Zombie%s%s" -msgstr "" +msgstr "^K1僵尸给^BG%s^K1上了一节功夫课%s%s" #: qcsrc/common/notifications/all.inc:323 #: qcsrc/common/notifications/all.inc:325 @@ -2282,7 +2290,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:328 #, c-format msgid "^BG%s^K1 died%s%s. What's the point of living without ammo?" -msgstr "" +msgstr "^BG%s^K1卒%s%s。与其没有弹药的苟活,不如轰轰烈烈的牺牲" #: qcsrc/common/notifications/all.inc:328 #, c-format @@ -2367,7 +2375,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:344 #, c-format msgid "^BG%s^K1 got served some superheated plasma from a turret%s%s" -msgstr "" +msgstr "^BG%s^K1 被炮台发出的高温离子弹教会做人%s%s" #: qcsrc/common/notifications/all.inc:345 #, c-format @@ -2422,37 +2430,37 @@ msgstr "" #: qcsrc/common/notifications/all.inc:355 #, c-format msgid "^BG%s^K1 got caught in the blast of a Racer explosion%s%s" -msgstr "" +msgstr "^BG%s^K1被爆炸的火箭弹打中了%s%s" #: qcsrc/common/notifications/all.inc:356 #, c-format msgid "^BG%s^K1 couldn't find shelter from a Racer rocket%s%s" -msgstr "" +msgstr "^BG%s^K1来不及躲闪火箭弹%s%s" #: qcsrc/common/notifications/all.inc:359 #, c-format msgid "^BG%s^K1 was betrayed by ^BG%s^K1%s%s" -msgstr "" +msgstr "^BG%s^K1被^BG%s^K1背叛%s%s" #: qcsrc/common/notifications/all.inc:361 #, c-format msgid "^BG%s^BG%s^BG (%s %s every %s seconds)" -msgstr "" +msgstr "^BG%s^BG%s^BG(%s %s每%s秒)" #: qcsrc/common/notifications/all.inc:363 #, c-format msgid "^BG%s^K1 was frozen by ^BG%s" -msgstr "" +msgstr "^BG%s^K1被^BG%s^K1封冻" #: qcsrc/common/notifications/all.inc:364 #, c-format msgid "^BG%s^K3 was revived by ^BG%s" -msgstr "" +msgstr "^BG%s^K3被^BG%s^K3复活" #: qcsrc/common/notifications/all.inc:365 #, c-format msgid "^BG%s^K3 was revived by falling" -msgstr "" +msgstr "^BG%s^K3由于跌落而复活" #: qcsrc/common/notifications/all.inc:366 #, c-format @@ -2472,23 +2480,23 @@ msgstr "^BG%s^K1 把自己冰冻了" #: qcsrc/common/notifications/all.inc:370 #: qcsrc/common/notifications/all.inc:684 msgid "^TC^TT^BG team wins the round" -msgstr "" +msgstr "^TC^TT^BG赢了这一局" #: qcsrc/common/notifications/all.inc:371 #: qcsrc/common/notifications/all.inc:685 #, c-format msgid "^BG%s^BG wins the round" -msgstr "" +msgstr "^BG%s^BG赢了这一局" #: qcsrc/common/notifications/all.inc:372 #: qcsrc/common/notifications/all.inc:548 msgid "^BGRound tied" -msgstr "" +msgstr "^BG平局" #: qcsrc/common/notifications/all.inc:373 #: qcsrc/common/notifications/all.inc:549 msgid "^BGRound over, there's no winner" -msgstr "" +msgstr "^BG这一局结束,没有赢家" #: qcsrc/common/notifications/all.inc:375 #, c-format @@ -2674,7 +2682,7 @@ msgstr "^BG%s^K1 捡到了加速" #: qcsrc/common/notifications/all.inc:420 #, c-format msgid "^BG%s^K1 picked up Strength" -msgstr "^BG%s^K1 捡到了力量" +msgstr "^BG%s^K1 捡到了神力" #: qcsrc/common/notifications/all.inc:422 #, c-format @@ -2836,12 +2844,12 @@ msgstr "" #: qcsrc/common/notifications/all.inc:460 #, c-format msgid "^BG%s%s^K1 felt the strong pull of ^BG%s^K1's Crylink%s%s" -msgstr "" +msgstr "^BG%s%s^K1 感受到 ^BG%s^K1手里的紫电的强大推力%s%s" #: qcsrc/common/notifications/all.inc:461 #, c-format msgid "^BG%s^K1 felt the strong pull of their Crylink%s%s" -msgstr "" +msgstr "^BG%s^K1 感受到自己手里的紫电的强大推力%s%s" #: qcsrc/common/notifications/all.inc:462 #, c-format @@ -2983,22 +2991,22 @@ msgstr "" #: qcsrc/common/notifications/all.inc:489 #, c-format msgid "^BG%s%s^K1 got too close to ^BG%s^K1's Mortar grenade%s%s" -msgstr "" +msgstr "^BG%s%s^K1离^BG%s^K1打出的榴弹太近了%s%s" #: qcsrc/common/notifications/all.inc:490 #, c-format msgid "^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s" -msgstr "" +msgstr "^BG%s%s^K1 吃了一记 ^BG%s^K1的榴弹%s%s" #: qcsrc/common/notifications/all.inc:491 #, c-format msgid "^BG%s^K1 didn't see their own Mortar grenade%s%s" -msgstr "" +msgstr "^BG%s^K1 没看到他们自己的榴弹%s%s" #: qcsrc/common/notifications/all.inc:492 #, c-format msgid "^BG%s^K1 blew themself up with their own Mortar%s%s" -msgstr "" +msgstr "^BG%s^K1 被他们自己的榴弹枪炸成碎尸%s%s" #: qcsrc/common/notifications/all.inc:493 #, c-format @@ -3106,11 +3114,11 @@ msgstr "" #: qcsrc/common/notifications/all.inc:539 msgid "^BGYou are attacking!" -msgstr "" +msgstr "^BG你是攻击方!" #: qcsrc/common/notifications/all.inc:540 msgid "^BGYou are defending!" -msgstr "" +msgstr "^BG你是防守方!" #: qcsrc/common/notifications/all.inc:541 #, c-format @@ -3119,15 +3127,15 @@ msgstr "" #: qcsrc/common/notifications/all.inc:543 msgid "^F4Begin!" -msgstr "" +msgstr "^F4开始!" #: qcsrc/common/notifications/all.inc:544 msgid "^F4Game starts in ^COUNT" -msgstr "" +msgstr "^F4游戏开始倒计时^COUNT" #: qcsrc/common/notifications/all.inc:545 msgid "^F4Round starts in ^COUNT" -msgstr "" +msgstr "^F4下一局开始倒计时^COUNT" #: qcsrc/common/notifications/all.inc:546 msgid "^F4Round cannot start" @@ -3161,7 +3169,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:559 msgid "^BGYou captured the flag!" -msgstr "" +msgstr "^BG你捡到了旗帜!" #: qcsrc/common/notifications/all.inc:560 #, c-format @@ -3206,7 +3214,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:568 #, c-format msgid "^BGYou passed the flag to %s" -msgstr "" +msgstr "^BG你把旗帜交给了 %s" #: qcsrc/common/notifications/all.inc:569 msgid "^BGYou got the ^TC^TT^BG flag!" @@ -3219,12 +3227,12 @@ msgstr "" #: qcsrc/common/notifications/all.inc:571 #, c-format msgid "^BGYou got your %steam^BG's flag, return it!" -msgstr "" +msgstr "^BG你拿到你们 %s队^BG的旗帜,带回它!" #: qcsrc/common/notifications/all.inc:572 #, c-format msgid "^BGYou got the %senemy^BG's flag, return it!" -msgstr "" +msgstr "^BG你拿到敌人%s队^BG的旗帜,带回它!" #: qcsrc/common/notifications/all.inc:573 #, c-format @@ -3282,7 +3290,7 @@ msgstr "" #: qcsrc/common/notifications/all.inc:584 msgid "^BGYou returned the ^TC^TT^BG flag!" -msgstr "" +msgstr "^BG你带回了^TC^TT^BG旗帜!" #: qcsrc/common/notifications/all.inc:585 msgid "^BGStalemate! Enemies can now see you on radar!" @@ -3552,11 +3560,11 @@ msgstr "" #: qcsrc/common/notifications/all.inc:661 msgid "^K1You got caught in the blast of a Racer explosion!" -msgstr "" +msgstr "^K1你被爆炸的火箭弹给打中了!" #: qcsrc/common/notifications/all.inc:662 msgid "^K1You couldn't find shelter from a Racer rocket!" -msgstr "" +msgstr "^K1你没能躲过火箭弹!" #: qcsrc/common/notifications/all.inc:663 msgid "^K1Watch your step!" @@ -3591,51 +3599,51 @@ msgstr "" #: qcsrc/common/notifications/all.inc:670 #, c-format msgid "^BGYou need %s^BG!" -msgstr "" +msgstr "^BG你需要 %s^BG!" #: qcsrc/common/notifications/all.inc:671 #, c-format msgid "^BGYou also need %s^BG!" -msgstr "" +msgstr "^BG你也需要 %s^BG!" #: qcsrc/common/notifications/all.inc:672 msgid "^BGDoor unlocked!" -msgstr "" +msgstr "^BG门已解锁!" #: qcsrc/common/notifications/all.inc:674 msgid "^F2You picked up some extra lives" -msgstr "" +msgstr "^F2你捡到了额外的生命" #: qcsrc/common/notifications/all.inc:676 #, c-format msgid "^K3You revived ^BG%s" -msgstr "" +msgstr "^K3你复活了 ^BG%s" #: qcsrc/common/notifications/all.inc:677 msgid "^K3You revived yourself" -msgstr "" +msgstr "^K3你复活了自己" #: qcsrc/common/notifications/all.inc:678 #, c-format msgid "^K3You were revived by ^BG%s" -msgstr "" +msgstr "^K3你被 ^BG%s^K3复活了" #: qcsrc/common/notifications/all.inc:679 #, c-format msgid "^K3You were automatically revived after %s second(s)" -msgstr "" +msgstr "^K3已在 %s 秒(s)后自动复活了" #: qcsrc/common/notifications/all.inc:681 msgid "^BGThe generator is under attack!" -msgstr "" +msgstr "^BG发电器正在被攻击!" #: qcsrc/common/notifications/all.inc:683 msgid "^TC^TT^BG team loses the round" -msgstr "" +msgstr "^TC^TT^BG 队伍输了这一轮" #: qcsrc/common/notifications/all.inc:687 msgid "^K1You froze yourself" -msgstr "" +msgstr "^K1你把自己封冻了" #: qcsrc/common/notifications/all.inc:688 msgid "^K1Round already started, you spawn as frozen" @@ -3644,15 +3652,15 @@ msgstr "" #: qcsrc/common/notifications/all.inc:690 #, c-format msgid "^K1A %s has arrived!" -msgstr "" +msgstr "^K1A %s 到了!" #: qcsrc/common/notifications/all.inc:694 msgid "^BGYou got the ^F1Fuel regenerator" -msgstr "" +msgstr "^BG你得到了 ^F1燃料重生成器" #: qcsrc/common/notifications/all.inc:695 msgid "^BGYou got the ^F1Jet pack" -msgstr "" +msgstr "^BG你得到了 ^F1喷气式背包" #: qcsrc/common/notifications/all.inc:703 msgid "" @@ -3835,11 +3843,11 @@ msgstr "" #: qcsrc/common/notifications/all.inc:750 msgid "^F2Strength infuses your weapons with devastating power" -msgstr "" +msgstr "^F2神力给你的武器融入毁灭性的力量" #: qcsrc/common/notifications/all.inc:751 msgid "^F2Strength has worn off" -msgstr "" +msgstr "^F2神力已失效" #: qcsrc/common/notifications/all.inc:753 msgid "^F2Shield surrounds you" @@ -4179,19 +4187,19 @@ msgstr "" #: qcsrc/common/teams.qh:29 msgid "TEAM^Red" -msgstr "" +msgstr "TEAM^红" #: qcsrc/common/teams.qh:30 msgid "TEAM^Blue" -msgstr "" +msgstr "TEAM^蓝" #: qcsrc/common/teams.qh:31 msgid "TEAM^Yellow" -msgstr "" +msgstr "TEAM^黄" #: qcsrc/common/teams.qh:32 msgid "TEAM^Pink" -msgstr "" +msgstr "TEAM^粉" #: qcsrc/common/teams.qh:33 msgid "Team" @@ -4203,51 +4211,51 @@ msgstr "中立" #: qcsrc/common/teams.qh:37 msgid "KEY^Red" -msgstr "" +msgstr "KEY^红" #: qcsrc/common/teams.qh:38 msgid "KEY^Blue" -msgstr "" +msgstr "KEY^蓝" #: qcsrc/common/teams.qh:39 msgid "KEY^Yellow" -msgstr "" +msgstr "KEY^黄" #: qcsrc/common/teams.qh:40 msgid "KEY^Pink" -msgstr "" +msgstr "KEY^粉" #: qcsrc/common/teams.qh:41 msgid "FLAG^Red" -msgstr "" +msgstr "FLAG^红" #: qcsrc/common/teams.qh:42 msgid "FLAG^Blue" -msgstr "" +msgstr "FLAG^蓝" #: qcsrc/common/teams.qh:43 msgid "FLAG^Yellow" -msgstr "" +msgstr "FLAG^黄" #: qcsrc/common/teams.qh:44 msgid "FLAG^Pink" -msgstr "" +msgstr "FLAG^粉" #: qcsrc/common/teams.qh:45 msgid "GENERATOR^Red" -msgstr "" +msgstr "GENERATOR^红" #: qcsrc/common/teams.qh:46 msgid "GENERATOR^Blue" -msgstr "" +msgstr "GENERATOR^蓝" #: qcsrc/common/teams.qh:47 msgid "GENERATOR^Yellow" -msgstr "" +msgstr "GENERATOR^黄" #: qcsrc/common/teams.qh:48 msgid "GENERATOR^Pink" -msgstr "" +msgstr "GENERATOR^粉" #: qcsrc/common/turrets/all.qh:51 msgid "Turrets dump command only works with sv_cmd.\n" @@ -4288,7 +4296,7 @@ msgstr "" #: qcsrc/common/turrets/turret/hellion_weapon.qh:7 msgid "Hellion" -msgstr "" +msgstr "海龙" #: qcsrc/common/turrets/turret/hk.qh:15 msgid "Hunter-Killer Turret" @@ -4296,11 +4304,11 @@ msgstr "" #: qcsrc/common/turrets/turret/hk_weapon.qh:7 msgid "Hunter-Killer" -msgstr "" +msgstr "猎手" #: qcsrc/common/turrets/turret/machinegun.qh:13 msgid "Machinegun Turret" -msgstr "" +msgstr "机枪炮台" #: qcsrc/common/turrets/turret/machinegun_weapon.qh:7 msgid "Machinegun" @@ -4312,31 +4320,31 @@ msgstr "" #: qcsrc/common/turrets/turret/mlrs_weapon.qh:7 msgid "MLRS" -msgstr "" +msgstr "MLRS" #: qcsrc/common/turrets/turret/phaser.qh:13 msgid "Phaser Cannon" -msgstr "" +msgstr "飞射炮" #: qcsrc/common/turrets/turret/phaser_weapon.qh:7 msgid "Phaser" -msgstr "" +msgstr "飞射" #: qcsrc/common/turrets/turret/plasma.qh:13 msgid "Plasma Cannon" -msgstr "" +msgstr "离子炮" #: qcsrc/common/turrets/turret/plasma_dual.qh:7 msgid "Dual plasma" -msgstr "" +msgstr "双离子枪" #: qcsrc/common/turrets/turret/plasma_dual.qh:19 msgid "Dual Plasma Cannon" -msgstr "" +msgstr "双离子炮" #: qcsrc/common/turrets/turret/plasma_weapon.qh:7 msgid "Plasma" -msgstr "" +msgstr "离子枪" #: qcsrc/common/turrets/turret/tesla.qh:13 #: qcsrc/common/turrets/turret/tesla_weapon.qh:7 @@ -4349,7 +4357,7 @@ msgstr "" #: qcsrc/common/turrets/turret/walker_weapon.qh:7 msgid "Walker" -msgstr "" +msgstr "沃克" #: qcsrc/common/vehicles/cl_vehicles.qc:192 #, c-format @@ -4366,19 +4374,19 @@ msgstr "" #: qcsrc/common/vehicles/vehicle/bumblebee.qh:19 msgid "Bumblebee" -msgstr "" +msgstr "黄蜂" #: qcsrc/common/vehicles/vehicle/racer.qh:19 msgid "Racer" -msgstr "" +msgstr "火箭枪" #: qcsrc/common/vehicles/vehicle/racer_weapon.qh:9 msgid "Racer cannon" -msgstr "" +msgstr "火箭炮" #: qcsrc/common/vehicles/vehicle/raptor.qh:19 msgid "Raptor" -msgstr "" +msgstr "猛枭" #: qcsrc/common/vehicles/vehicle/raptor_weapons.qh:9 msgid "Raptor cannon" @@ -4410,15 +4418,15 @@ msgstr "" #: qcsrc/common/weapons/weapon/crylink.qc:17 msgid "Crylink" -msgstr "" +msgstr "紫电" #: qcsrc/common/weapons/weapon/devastator.qc:17 msgid "Devastator" -msgstr "" +msgstr "灭世" #: qcsrc/common/weapons/weapon/electro.qc:17 msgid "Electro" -msgstr "" +msgstr "电射枪" #: qcsrc/common/weapons/weapon/fireball.qc:17 msgid "Fireball" @@ -4426,7 +4434,7 @@ msgstr "火球" #: qcsrc/common/weapons/weapon/hagar.qc:17 msgid "Hagar" -msgstr "" +msgstr "哈格" #: qcsrc/common/weapons/weapon/hlac.qc:17 msgid "Heavy Laser Assault Cannon" @@ -4446,7 +4454,7 @@ msgstr "地雷放置者" #: qcsrc/common/weapons/weapon/mortar.qc:17 msgid "Mortar" -msgstr "" +msgstr "榴弹枪" #: qcsrc/common/weapons/weapon/porto.qc:17 msgid "Port-O-Launch" @@ -4462,7 +4470,7 @@ msgstr "" #: qcsrc/common/weapons/weapon/shockwave.qc:17 msgid "Shockwave" -msgstr "" +msgstr "脉冲波枪" #: qcsrc/common/weapons/weapon/shotgun.qc:17 msgid "Shotgun" @@ -4664,22 +4672,22 @@ msgstr "CI_MUL^%d 秒" #: qcsrc/lib/counting.qh:79 #, c-format msgid "%dst" -msgstr "" +msgstr "第%d名" #: qcsrc/lib/counting.qh:80 #, c-format msgid "%dnd" -msgstr "" +msgstr "第%d名" #: qcsrc/lib/counting.qh:81 #, c-format msgid "%drd" -msgstr "" +msgstr "第%d名" #: qcsrc/lib/counting.qh:82 qcsrc/lib/counting.qh:85 #, c-format msgid "%dth" -msgstr "" +msgstr "第%d名" #: qcsrc/lib/oo.qh:298 msgid "No description" @@ -4695,12 +4703,12 @@ msgstr "" #: qcsrc/lib/string.qh:48 #, c-format msgid "%d days, %02d:%02d:%02d" -msgstr "" +msgstr "%d天,%02d:%02d:%02d" #: qcsrc/lib/string.qh:49 #, c-format msgid "%02d:%02d:%02d" -msgstr "" +msgstr "%02d:%02d:%02d" #: qcsrc/menu/command/menu_cmd.qc:48 msgid "Usage: menu_cmd command..., where possible commands are:\n" @@ -4777,11 +4785,11 @@ msgstr "游戏编码" #: qcsrc/menu/xonotic/credits.qc:116 msgid "Marketing / PR" -msgstr "" +msgstr "销售 / 人力资源" #: qcsrc/menu/xonotic/credits.qc:122 msgid "Legal" -msgstr "" +msgstr "法律信息" #: qcsrc/menu/xonotic/credits.qc:127 msgid "Game Engine" @@ -4797,7 +4805,7 @@ msgstr "" #: qcsrc/menu/xonotic/credits.qc:142 msgid "Other Active Contributors" -msgstr "" +msgstr "其他活跃贡献者" #: qcsrc/menu/xonotic/credits.qc:149 msgid "Translators" @@ -4809,39 +4817,39 @@ msgstr "" #: qcsrc/menu/xonotic/credits.qc:156 msgid "Belarusian" -msgstr "" +msgstr "白俄罗斯语" #: qcsrc/menu/xonotic/credits.qc:159 msgid "Bulgarian" -msgstr "" +msgstr "保加利亚语" #: qcsrc/menu/xonotic/credits.qc:166 msgid "Chinese (China)" -msgstr "中文(中国)" +msgstr "中文(中国)" #: qcsrc/menu/xonotic/credits.qc:172 msgid "Chinese (Taiwan)" -msgstr "" +msgstr "中文(台湾)" #: qcsrc/menu/xonotic/credits.qc:177 msgid "Cornish" -msgstr "" +msgstr "康沃尔语" #: qcsrc/menu/xonotic/credits.qc:180 msgid "Czech" -msgstr "" +msgstr "捷克语" #: qcsrc/menu/xonotic/credits.qc:185 msgid "Dutch" -msgstr "" +msgstr "荷兰语" #: qcsrc/menu/xonotic/credits.qc:192 msgid "English (Australia)" -msgstr "" +msgstr "英语(澳大利亚)" #: qcsrc/menu/xonotic/credits.qc:197 msgid "Finnish" -msgstr "" +msgstr "芬兰语" #: qcsrc/menu/xonotic/credits.qc:202 msgid "French" @@ -4857,11 +4865,11 @@ msgstr "希腊语" #: qcsrc/menu/xonotic/credits.qc:227 msgid "Hungarian" -msgstr "" +msgstr "匈牙利语" #: qcsrc/menu/xonotic/credits.qc:231 msgid "Irish" -msgstr "" +msgstr "爱尔兰语" #: qcsrc/menu/xonotic/credits.qc:234 msgid "Italian" @@ -4869,11 +4877,11 @@ msgstr "意大利语" #: qcsrc/menu/xonotic/credits.qc:240 msgid "Kazakh" -msgstr "" +msgstr "哈萨克语" #: qcsrc/menu/xonotic/credits.qc:243 msgid "Korean" -msgstr "" +msgstr "韩语" #: qcsrc/menu/xonotic/credits.qc:247 msgid "Polish" @@ -4881,11 +4889,11 @@ msgstr "波兰语" #: qcsrc/menu/xonotic/credits.qc:255 msgid "Portuguese" -msgstr "" +msgstr "葡萄牙语" #: qcsrc/menu/xonotic/credits.qc:261 msgid "Romanian" -msgstr "" +msgstr "罗马尼亚语" #: qcsrc/menu/xonotic/credits.qc:268 msgid "Russian" @@ -4893,19 +4901,19 @@ msgstr "俄文" #: qcsrc/menu/xonotic/credits.qc:279 msgid "Scottish Gaelic" -msgstr "" +msgstr "苏格兰盖尔语" #: qcsrc/menu/xonotic/credits.qc:282 msgid "Serbian" -msgstr "" +msgstr "塞尔维亚语" #: qcsrc/menu/xonotic/credits.qc:288 msgid "Spanish" -msgstr "" +msgstr "西班牙语" #: qcsrc/menu/xonotic/credits.qc:299 msgid "Swedish" -msgstr "" +msgstr "瑞典语" #: qcsrc/menu/xonotic/credits.qc:303 msgid "Ukrainian" @@ -4913,19 +4921,19 @@ msgstr "乌克兰语" #: qcsrc/menu/xonotic/credits.qc:310 msgid "Past Contributors" -msgstr "" +msgstr "过去的贡献者" #: qcsrc/menu/xonotic/cvarlist.qc:73 msgid "forced to be saved to config.cfg" -msgstr "" +msgstr "强制保存到config.cfg" #: qcsrc/menu/xonotic/cvarlist.qc:79 qcsrc/menu/xonotic/cvarlist.qc:89 msgid "will not be saved" -msgstr "" +msgstr "将不会被保存" #: qcsrc/menu/xonotic/cvarlist.qc:84 msgid "will be saved to config.cfg" -msgstr "" +msgstr "将被保存到config.cfg" #: qcsrc/menu/xonotic/cvarlist.qc:93 msgid "private" @@ -4971,7 +4979,7 @@ msgstr "名字:" #: qcsrc/menu/xonotic/dialog_firstrun.qc:53 #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:60 msgid "Name under which you will appear in the game" -msgstr "" +msgstr "你在游戏里的名字" #: qcsrc/menu/xonotic/dialog_firstrun.qc:69 msgid "Text language:" @@ -4979,7 +4987,7 @@ msgstr "文本语言:" #: qcsrc/menu/xonotic/dialog_firstrun.qc:78 msgid "Allow player statistics to use your nickname at stats.xonotic.org?" -msgstr "" +msgstr "允许stats.xonotic.org使用你的昵称做玩家统计?" #: qcsrc/menu/xonotic/dialog_firstrun.qc:84 msgid "Undecided" @@ -5057,13 +5065,13 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:27 #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:15 msgid "Text alignment:" -msgstr "" +msgstr "文本对齐:" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:31 #: qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc:19 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:71 msgid "Center" -msgstr "" +msgstr "中心" #: qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc:35 msgid "Font scale:" @@ -5221,17 +5229,17 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_hudpanel_notification.qh:6 msgid "Notification Panel" -msgstr "" +msgstr "通知面板" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:15 #: qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc:14 #: qcsrc/menu/xonotic/dialog_hudpanel_radar.qc:15 msgid "Panel disabled" -msgstr "" +msgstr "面板已禁用" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:16 msgid "Panel enabled" -msgstr "" +msgstr "面板已启用" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:17 msgid "Panel enabled even observing" @@ -5248,20 +5256,20 @@ msgstr "状态栏" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:26 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:68 msgid "Left align" -msgstr "" +msgstr "向左对齐" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:27 #: qcsrc/menu/xonotic/dialog_settings_game_weapons.qc:74 msgid "Right align" -msgstr "" +msgstr "向右对齐" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:28 msgid "Inward align" -msgstr "" +msgstr "向内对齐" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:29 msgid "Outward align" -msgstr "" +msgstr "向外对齐" #: qcsrc/menu/xonotic/dialog_hudpanel_physics.qc:33 msgid "Flip speed/acceleration positions" @@ -6072,7 +6080,7 @@ msgstr "半空中" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:87 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:224 msgid "Piñata" -msgstr "" +msgstr "皮纳塔" #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:89 #: qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc:229 @@ -6225,23 +6233,23 @@ msgstr "插件" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:38 msgid "SRVS^Categories" -msgstr "" +msgstr "SRVS^类别" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:41 msgid "SRVS^Empty" -msgstr "" +msgstr "SRVS^空" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:42 msgid "Show empty servers" -msgstr "" +msgstr "显示空服务器" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:46 msgid "SRVS^Full" -msgstr "" +msgstr "SRVS^满员" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:47 msgid "Show full servers that have no slots available" -msgstr "" +msgstr "显示没有多余空位的满员服务器" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:51 msgid "Pause" @@ -6254,7 +6262,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:53 msgid "Reload the server list" -msgstr "" +msgstr "重新载入服务器列表" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:67 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:223 @@ -6267,7 +6275,7 @@ msgstr "信息..." #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:79 msgid "Show more information about the currently highlighted server" -msgstr "" +msgstr "显示当前高亮服务器的更多信息" #: qcsrc/menu/xonotic/dialog_multiplayer_join.qc:84 #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:303 @@ -6277,7 +6285,7 @@ msgstr "加入!" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:154 #: qcsrc/menu/xonotic/serverlist.qc:1061 msgid "MOD^Default" -msgstr "" +msgstr "MOD^默认" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 #, c-format @@ -6286,31 +6294,31 @@ msgstr "%d 修改" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:161 msgid "Official" -msgstr "正式" +msgstr "官方" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:169 msgid "N/A (auth library missing, can't connect)" -msgstr "" +msgstr "N/A (缺失验证库,无法连接)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:171 msgid "N/A (auth library missing)" -msgstr "" +msgstr "N/A (缺失验证库)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:177 msgid "Not supported (can't connect)" -msgstr "" +msgstr "不支持(无法连接)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:179 msgid "Not supported (won't encrypt)" -msgstr "" +msgstr "不支持(不加密)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:183 msgid "Supported (will encrypt)" -msgstr "" +msgstr "支持(加密)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:185 msgid "Supported (won't encrypt)" -msgstr "" +msgstr "支持(不加密)" #: qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc:189 msgid "Requested (will encrypt)" @@ -6416,7 +6424,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qc:15 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qc:15 msgid "Do you really wish to disconnect now?" -msgstr "" +msgstr "你真的希望现在断开连接吗?" #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh:6 #: qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh:6 @@ -6429,55 +6437,55 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:37 msgid "MUSICPL^Add" -msgstr "" +msgstr "MUSICPL^添加" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:40 msgid "MUSICPL^Add all" -msgstr "" +msgstr "MUSICPL^添加全部" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:44 msgid "Set as menu track" -msgstr "" +msgstr "设为菜单音轨" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:48 msgid "Reset default menu track" -msgstr "" +msgstr "重设默认菜单音轨" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:54 msgid "Playlist:" -msgstr "播放列表" +msgstr "播放列表:" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:55 msgid "Random order" -msgstr "" +msgstr "随机顺序" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:60 msgid "MUSICPL^Stop" -msgstr "" +msgstr "MUSICPL^停止" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:63 msgid "MUSICPL^Play" -msgstr "" +msgstr "MUSICPL^播放" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:66 msgid "MUSICPL^Pause" -msgstr "" +msgstr "MUSICPL^暂停" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:69 msgid "MUSICPL^Prev" -msgstr "" +msgstr "MUSICPL^上一首" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:72 msgid "MUSICPL^Next" -msgstr "" +msgstr "MUSICPL^下一首" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:76 msgid "MUSICPL^Remove" -msgstr "" +msgstr "MUSICPL^移除" #: qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qc:79 msgid "MUSICPL^Remove all" -msgstr "" +msgstr "MUSICPL^全部移除" #: qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc:43 msgid "Auto screenshot scoreboard" @@ -6510,7 +6518,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_user.qc:20 #: qcsrc/menu/xonotic/dialog_settings_video.qc:21 msgid "Apply immediately" -msgstr "" +msgstr "立即应用" #: qcsrc/menu/xonotic/dialog_multiplayer_profile.qc:48 msgid "Name" @@ -6786,11 +6794,11 @@ msgstr "武器装备:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:99 msgid "New style sound attenuation" -msgstr "" +msgstr "新式声音衰减" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:102 msgid "Mute sounds when not active" -msgstr "" +msgstr "不活动时静音" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:105 msgid "Frequency:" @@ -6798,7 +6806,7 @@ msgstr "频率:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:107 msgid "Sound output frequency" -msgstr "" +msgstr "音频输出频率" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:108 msgid "8 kHz" @@ -6838,7 +6846,7 @@ msgstr "声道:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:121 msgid "Number of channels for the sound output" -msgstr "" +msgstr "音频输出声道数" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:122 msgid "Mono" @@ -6908,15 +6916,15 @@ msgstr "菜单声音" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:150 msgid "Play sounds when clicking menu items" -msgstr "" +msgstr "点击菜单项时播放声音" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:151 msgid "Focus sounds" -msgstr "重点音效" +msgstr "焦点音效" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:152 msgid "Play sounds when hovering over menu items too" -msgstr "" +msgstr "悬停在菜单项时也播放声音" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:156 msgid "Time announcer:" @@ -6940,7 +6948,7 @@ msgstr "自动嘲讽:" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:166 msgid "Automatically taunt enemies after fragging them" -msgstr "" +msgstr "杀死敌人后自动嘲讽他们" #: qcsrc/menu/xonotic/dialog_settings_audio.qc:168 msgid "Sometimes" @@ -6998,7 +7006,7 @@ msgstr "几何细节:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:67 msgid "Change the smoothness of the curves on the map (default: normal)" -msgstr "" +msgstr "改变地图中曲线的光滑程度(默认:正常)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:68 msgid "DET^Lowest" @@ -7006,27 +7014,27 @@ msgstr "DET^极低" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:69 msgid "DET^Low" -msgstr "DET^低阶" +msgstr "DET^低" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:70 msgid "DET^Normal" -msgstr "DET^标准" +msgstr "DET^正常" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:71 msgid "DET^Good" -msgstr "DET^好的" +msgstr "DET^好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:72 msgid "DET^Best" -msgstr "DET^Best" +msgstr "DET^最好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:73 msgid "DET^Insane" -msgstr "DET^Insane" +msgstr "DET^疯狂" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:77 msgid "Player detail:" -msgstr "玩家详细信息:" +msgstr "玩家身体细节:" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:79 msgid "PDET^Low" @@ -7042,11 +7050,11 @@ msgstr "PDET^标准" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:82 msgid "PDET^Good" -msgstr "PDET^好的" +msgstr "PDET^好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:83 msgid "PDET^Best" -msgstr "PDET^Best" +msgstr "PDET^最好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:87 msgid "Texture resolution:" @@ -7066,7 +7074,7 @@ msgstr "RES^超低" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:94 msgid "RES^Low" -msgstr "RES^低阶" +msgstr "RES^低" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:95 msgid "RES^Normal" @@ -7074,11 +7082,11 @@ msgstr "RES^标准" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:96 msgid "RES^Good" -msgstr "RES^好的" +msgstr "RES^好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:97 msgid "RES^Best" -msgstr "RES^Best" +msgstr "RES^最好" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:110 #: qcsrc/menu/xonotic/dialog_settings_effects.qc:115 @@ -7095,6 +7103,8 @@ msgid "" "Disable textures completely for very slow hardware. This gives a huge " "performance boost, but looks very ugly. (default: disabled)" msgstr "" +"完全禁用纹理,针对速度非常慢的硬件。这可以导致性能的巨大提升,但画面会非常难" +"看。(默认:禁用)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:135 msgid "Use lightmaps" @@ -7105,6 +7115,7 @@ msgid "" "Use high resolution lightmaps, which will look pretty but use up some extra " "video memory (default: enabled)" msgstr "" +"使用高分辨率光照贴图,可以改善视觉效果,但会消耗额外的显存(默认:启用)" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:138 msgid "Deluxe mapping" @@ -7212,7 +7223,7 @@ msgstr "骨骼肌" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:187 msgid "DMGFX^All" -msgstr "" +msgstr "DMGFX^全部" #: qcsrc/menu/xonotic/dialog_settings_effects.qc:191 msgid "No dynamic lighting" @@ -7410,15 +7421,15 @@ msgstr "如果显示镜头受阻,则模糊准星显示" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:140 msgid "Enlarge crosshair if targeting an enemy" -msgstr "如果有针对性敌人,则放大准星显示" +msgstr "瞄准敌人时放大准星" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:143 msgid "Animate crosshair when hitting an enemy" -msgstr "击中敌人时,则动画准星" +msgstr "向敌人射击时抖动准星" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qc:146 msgid "Animate crosshair when picking up an item" -msgstr "拿起物品时,则动画准星" +msgstr "拿起物品时抖动准星" #: qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh:7 msgid "Crosshair" @@ -7508,7 +7519,7 @@ msgstr "团队配合" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:148 msgid "Only when near crosshair" -msgstr "" +msgstr "仅在接近准星时" #: qcsrc/menu/xonotic/dialog_settings_game_hud.qc:152 msgid "Display health and armor" @@ -7623,11 +7634,11 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:80 msgid "Display all info messages in the chatbox" -msgstr "" +msgstr "在聊天盒中显示所有信息" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:82 msgid "Display player statuses in the chatbox" -msgstr "" +msgstr "在聊天盒中显示玩家状态" #: qcsrc/menu/xonotic/dialog_settings_game_messages.qc:86 msgid "Powerup notifications" @@ -7776,27 +7787,27 @@ msgstr "第三人称透视" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:55 msgid "Back distance" -msgstr "" +msgstr "后方距离" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:61 msgid "Up distance" -msgstr "" +msgstr "上方距离" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:67 msgid "Allow passing through walls while spectating" -msgstr "" +msgstr "旁观时允许穿过墙体" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:70 msgid "Field of view:" -msgstr "" +msgstr "视野:" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:72 msgid "Field of vision in degrees (default: 100)" -msgstr "" +msgstr "视场角度(默认:100)" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:76 msgid "ZOOM^Zoom factor:" -msgstr "" +msgstr "ZOOM^缩放因子:" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:78 msgid "How big the zoom factor is when the zoom button is pressed" @@ -7804,7 +7815,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:81 msgid "ZOOM^Zoom speed:" -msgstr "" +msgstr "ZOOM^缩放速度:" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:83 msgid "How fast the view will be zoomed, disable to zoom instantly" @@ -7812,7 +7823,7 @@ msgstr "" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:92 msgid "ZOOM^Instant" -msgstr "" +msgstr "ZOOM^即时" #: qcsrc/menu/xonotic/dialog_settings_game_view.qc:96 msgid "ZOOM^Zoom sensitivity:" @@ -8375,7 +8386,7 @@ msgstr "垂直同步" msgid "" "Enable vertical synchronization to prevent tearing, will cap your fps to the " "screen refresh rate (default: disabled)" -msgstr "" +msgstr "启用垂直同步以阻止图像撕裂,这会把fps限制在屏幕刷新率以内 (默认: 禁用)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:67 msgid "Flip view horizontally" @@ -8423,7 +8434,7 @@ msgstr "抗锯齿:" msgid "" "Enable antialiasing, which smooths the edges of 3D geometry. Note that it " "might decrease performance by quite a lot (default: disabled)" -msgstr "" +msgstr "启用抗锯齿,这会平滑3D图形的边缘。注意它会大大降低性能 (默认: 禁用)" #: qcsrc/menu/xonotic/dialog_settings_video.qc:85 msgid "AA^Disabled" @@ -8707,19 +8718,19 @@ msgstr "攻击" #: qcsrc/menu/xonotic/keybinder.qc:44 msgid "WEAPON^previous" -msgstr "" +msgstr "WEAPON^前一个" #: qcsrc/menu/xonotic/keybinder.qc:45 msgid "WEAPON^next" -msgstr "" +msgstr "WEAPON^后一个" #: qcsrc/menu/xonotic/keybinder.qc:46 msgid "WEAPON^previously used" -msgstr "" +msgstr "WEAPON^曾用过" #: qcsrc/menu/xonotic/keybinder.qc:47 msgid "WEAPON^best" -msgstr "" +msgstr "WEAPON^最佳" #: qcsrc/menu/xonotic/keybinder.qc:48 msgid "reload" @@ -8807,7 +8818,7 @@ msgstr "" #: qcsrc/menu/xonotic/keybinder.qc:106 msgid "quick menu" -msgstr "" +msgstr "快速菜单" #: qcsrc/menu/xonotic/keybinder.qc:107 msgid "sandbox menu" @@ -8857,7 +8868,7 @@ msgstr "偏好" msgid "" "Bookmark the currently highlighted server so that it's faster to find in the " "future" -msgstr "" +msgstr "收藏当前高亮的服务器以便日后查找" #: qcsrc/menu/xonotic/serverlist.qc:763 msgid "Ping" @@ -8865,7 +8876,7 @@ msgstr "延迟" #: qcsrc/menu/xonotic/serverlist.qc:764 msgid "Hostname" -msgstr "" +msgstr "主机名" #: qcsrc/menu/xonotic/serverlist.qc:765 msgid "Map" @@ -8886,7 +8897,7 @@ msgstr "" #: qcsrc/menu/xonotic/serverlist.qc:1060 msgid "encryption:" -msgstr "" +msgstr "加密:" #: qcsrc/menu/xonotic/serverlist.qc:1061 #, c-format @@ -8933,7 +8944,7 @@ msgstr "SLCAT^竞争模式" #: qcsrc/menu/xonotic/serverlist.qh:156 msgid "SLCAT^Modified Servers" -msgstr "已更改服务器" +msgstr "SLCAT^Mod服务器" #: qcsrc/menu/xonotic/serverlist.qh:157 msgid "SLCAT^Overkill" @@ -8972,11 +8983,11 @@ msgstr "%s dB" msgid "" "Multiplier for amount of particles. Less means less particles, which in turn " "gives for better performance (default: 1)" -msgstr "" +msgstr "粒子数量的倍数。该值越小代表粒子数目更少,以及更高的性能(默认:1)" #: qcsrc/menu/xonotic/slider_particles.qc:14 msgid "PART^OMG" -msgstr "" +msgstr "PART^OMG" #: qcsrc/menu/xonotic/slider_particles.qc:15 msgid "PART^Low" @@ -9009,6 +9020,8 @@ msgid "" "texture memory usage, but make the textures appear very blurry. (default: " "good)" msgstr "" +"改变纹理的锐度,降低该值可以有效减少纹理的内存占用,但会使纹理显得非常模糊。" +"(默认:好)" #: qcsrc/menu/xonotic/slider_resolution.qc:115 msgid "Screen resolution" @@ -9080,7 +9093,7 @@ msgstr "加入:" #: qcsrc/menu/xonotic/statslist.qc:103 msgid "Last_Seen:" -msgstr "" +msgstr "上次活动:" #: qcsrc/menu/xonotic/statslist.qc:110 msgid "Time_Played:" diff --git a/effectinfo.txt b/effectinfo.txt index b2544fa074..c07774a05b 100644 --- a/effectinfo.txt +++ b/effectinfo.txt @@ -2751,14 +2751,36 @@ effect flac_explode velocityjitter 256 256 256 velocityoffset 0 0 80 effect tr_bullet - type spark - alpha 256 256 2560 - color 0xff8960 0xff8533 - size 4 4 - stretchfactor 0.200000 - tex 70 70 - trailspacing 750 - velocitymultiplier 3 + type beam + alpha 500 600 10000 + color 0xf03000 0xff6010 + countabsolute 1 + sizeincrease -3 + size 0.6 0.8 + tex 200 200 +effect tr_bullet + type smoke + airfriction -4 + alpha 256 256 350 + color 0x202020 0x404040 + notunderwater + sizeincrease 0.400000 + size 1 2 + tex 0 8 + trailspacing 16 + velocityjitter 4 4 4 +effect tr_bullet + type bubble + alpha 256 256 128 + bounce 1.500000 + color 0x404040 0x808080 + gravity -0.125000 + liquidfriction 4 + size 0.5 0.6 + tex 62 62 + trailspacing 16 + underwater + velocityjitter 16 16 16 effect smoking_smallemitter type alphastatic airfriction -1 @@ -7581,6 +7603,15 @@ effect arc_lightning underwater velocityjitter 250 250 250 velocitymultiplier 20 +effect arc_lightning + type smoke + alpha 40 40 350 + color 0x80C0FF 0x80C0FF + countabsolute 1 + sizeincrease 400 + size 4 4 + tex 38 38 + velocitymultiplier 100 effect arc_beam type spark airfriction -10 @@ -8253,3 +8284,34 @@ effect arc_bolt_explode tex 40 40 velocityjitter 224 224 224 velocityoffset 0 0 80 +effect tr_bullet_weak + type beam + alpha 75 100 3000 + color 0xf03000 0xff6010 + countabsolute 1 + sizeincrease -3 + size 0.6 0.8 + tex 200 200 +effect tr_bullet_weak + type smoke + airfriction -4 + alpha 256 256 350 + color 0x202020 0x404040 + notunderwater + sizeincrease 0.400000 + size 1 2 + tex 0 8 + trailspacing 16 + velocityjitter 4 4 4 +effect tr_bullet_weak + type bubble + alpha 256 256 128 + bounce 1.500000 + color 0x404040 0x808080 + gravity -0.125000 + liquidfriction 4 + size 0.5 0.6 + tex 62 62 + trailspacing 32 + underwater + velocityjitter 16 16 16 diff --git a/effects-high.cfg b/effects-high.cfg index 48c8253a3a..597bb4660a 100644 --- a/effects-high.cfg +++ b/effects-high.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 1 cl_playerdetailreduction 4 gl_flashblend 0 gl_picmip -1 +gl_texturecompression_2d 0 +gl_texturecompression_sky 0 mod_q3bsp_nolightmaps 0 r_bloom 1 r_coronas 1 diff --git a/effects-low.cfg b/effects-low.cfg index b7e9a98a3a..c9549581bd 100644 --- a/effects-low.cfg +++ b/effects-low.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 0 cl_playerdetailreduction 4 gl_flashblend 1 gl_picmip 1 +gl_texturecompression_2d 1 +gl_texturecompression_sky 1 mod_q3bsp_nolightmaps 1 r_bloom 0 r_coronas 1 diff --git a/effects-med.cfg b/effects-med.cfg index 66eed5aa5d..4ea20a1699 100644 --- a/effects-med.cfg +++ b/effects-med.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 0 cl_playerdetailreduction 4 gl_flashblend 0 gl_picmip 0 +gl_texturecompression_2d 0 +gl_texturecompression_sky 1 mod_q3bsp_nolightmaps 0 r_bloom 0 r_coronas 1 diff --git a/effects-normal.cfg b/effects-normal.cfg index 63dcd134a0..ba708cb9f8 100644 --- a/effects-normal.cfg +++ b/effects-normal.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 1 cl_playerdetailreduction 4 gl_flashblend 0 gl_picmip 0 +gl_texturecompression_2d 0 +gl_texturecompression_sky 0 mod_q3bsp_nolightmaps 0 r_bloom 0 r_coronas 1 diff --git a/effects-omg.cfg b/effects-omg.cfg index 9018ee2c94..7614417ce2 100644 --- a/effects-omg.cfg +++ b/effects-omg.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 0 cl_playerdetailreduction 4 gl_flashblend 1 gl_picmip 1337 +gl_texturecompression_2d 1 +gl_texturecompression_sky 1 mod_q3bsp_nolightmaps 1 r_bloom 0 r_coronas 1 diff --git a/effects-ultimate.cfg b/effects-ultimate.cfg index 0cf3a899cb..3ce6f55a7f 100644 --- a/effects-ultimate.cfg +++ b/effects-ultimate.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 1 cl_playerdetailreduction 0 gl_flashblend 0 gl_picmip -1 +gl_texturecompression_2d 0 +gl_texturecompression_sky 0 mod_q3bsp_nolightmaps 0 r_bloom 1 r_coronas 1 diff --git a/effects-ultra.cfg b/effects-ultra.cfg index 5909f3b83d..d42d7c58ba 100644 --- a/effects-ultra.cfg +++ b/effects-ultra.cfg @@ -8,6 +8,8 @@ cl_spawn_point_particles 1 cl_playerdetailreduction 0 gl_flashblend 0 gl_picmip -1 +gl_texturecompression_2d 0 +gl_texturecompression_sky 0 mod_q3bsp_nolightmaps 0 r_bloom 1 r_coronas 1 diff --git a/gamemodes-client.cfg b/gamemodes-client.cfg index e7a1607f15..c43b9d1d3f 100644 --- a/gamemodes-client.cfg +++ b/gamemodes-client.cfg @@ -31,6 +31,7 @@ alias cl_hook_gamestart_cts alias cl_hook_gamestart_ka alias cl_hook_gamestart_ft alias cl_hook_gamestart_inv +alias cl_hook_gamestart_duel alias cl_hook_gameend "rpn /cl_matchcount dup load 1 + =" // increase match count every time a game ends alias cl_hook_shutdown alias cl_hook_activeweapon diff --git a/gamemodes-server.cfg b/gamemodes-server.cfg index 6790a3b4fb..57c9f7b708 100644 --- a/gamemodes-server.cfg +++ b/gamemodes-server.cfg @@ -28,6 +28,7 @@ alias sv_hook_gamestart_cts alias sv_hook_gamestart_ka alias sv_hook_gamestart_ft alias sv_hook_gamestart_inv +alias sv_hook_gamestart_duel alias sv_hook_gamerestart alias sv_hook_gameend @@ -38,8 +39,7 @@ alias sv_hook_gameend // These are called when the mode is switched via gametype vote screen, // earlier than gamestart hooks (useful for enabling per-gamemode mutators) // The _all hook is called before the specific one -// here it sets g_maxplayers to undo what duel does -alias sv_vote_gametype_hook_all "set g_maxplayers 0" +alias sv_vote_gametype_hook_all alias sv_vote_gametype_hook_as alias sv_vote_gametype_hook_ca alias sv_vote_gametype_hook_ctf @@ -55,15 +55,17 @@ alias sv_vote_gametype_hook_nb alias sv_vote_gametype_hook_ons alias sv_vote_gametype_hook_rc alias sv_vote_gametype_hook_tdm +alias sv_vote_gametype_hook_duel -// Preset to allow duel to be used for the gametype voting screen +// Example preset to allow 1v1ctf to be used for the gametype voting screen // sv_vote_gametype_*_type Must be set to the name of the gametype the option is based on // sv_vote_gametype_*_name Contains a human-readable name of the gametype // sv_vote_gametype_*_description Contains a longer description -set sv_vote_gametype_duel_type dm -set sv_vote_gametype_duel_name Duel -set sv_vote_gametype_duel_description "One vs One match" -alias sv_vote_gametype_hook_duel "set g_maxplayers 2" +//set sv_vote_gametype_1v1ctf_type ctf +//set sv_vote_gametype_1v1ctf_name "Capture the Flag Duel" +//set sv_vote_gametype_1v1ctf_description "One vs One match in CTF" +//alias sv_vote_gametype_hook_all "set g_maxplayers 0" +//alias sv_vote_gametype_hook_1v1ctf "set g_maxplayers 2" // =========== @@ -196,6 +198,13 @@ set g_inv_respawn_delay_large_count 0 set g_inv_respawn_delay_max 0 set g_inv_respawn_waves 0 set g_inv_weapon_stay 0 +set g_duel_respawn_delay_small 0 +set g_duel_respawn_delay_small_count 0 +set g_duel_respawn_delay_large 0 +set g_duel_respawn_delay_large_count 0 +set g_duel_respawn_delay_max 0 +set g_duel_respawn_waves 0 +set g_duel_weapon_stay 0 // ========= @@ -308,12 +317,16 @@ exec ctfscoring-samual.cfg set g_cts 0 "CTS: complete the stage" set g_cts_selfdamage 1 "0 = disable all selfdamage and falldamage in cts" set g_cts_finish_kill_delay 10 "prevent cheating by running back to the start line, and starting out with more speed than otherwise possible" +set g_cts_send_rankings_cnt 15 "send this number of map records to clients" +set g_cts_removeprojectiles 0 "remove projectiles when the player dies, to prevent using weapons earlier in the stage than intended" // ========================== // deathmatch (ffa or team) // ========================== set g_dm 1 "Deathmatch: killing any other player is one frag, player with most frags wins" +set g_tdm 0 "Team Deathmatch: the team who kills their opponents most often wins" +set g_tdm_on_dm_maps 0 "when this is set, all DM maps automatically support TDM" set g_tdm_teams 2 "how many teams are in team deathmatch (set by mapinfo)" set g_tdm_team_spawns 0 "when 1, players spawn from the team spawnpoints of the map, if any" seta g_tdm_teams_override 0 "how many teams are in team deathmatch" @@ -477,7 +490,7 @@ seta g_nexball_tackling 1 "Allow ball theft?" set g_onslaught 0 "Onslaught: take control points towards the enemy generator and then destroy it" set g_onslaught_point_limit 1 "Onslaught point limit overriding the mapinfo specified one (use 0 to play without limit, and -1 to use the mapinfo's limit)" set g_onslaught_warmup 5 -set g_onslaught_round_timelimit 280 +set g_onslaught_round_timelimit 500 set g_onslaught_teleport_radius 200 "Allows teleporting from a control point to another" set g_onslaught_teleport_wait 5 "Time before player can teleport again" set g_onslaught_spawn_choose 1 "Allow players to choose the control point to be spawned at" @@ -522,3 +535,10 @@ set g_invasion_spawnpoint_spawn_delay 0.5 set g_invasion_teams 0 "number of teams in invasion (note: use mapinfo to set this)" set g_invasion_team_spawns 1 "use team spawns in teamplay invasion mode" set g_invasion_type 0 "type of invasion mode - 0: round-based, 1: hunting, 2: complete the stage (note: use mapinfo to set this)" + +// ====== +// duel +// ====== +set g_duel 0 "Duel: frag the opponent more in a one versus one arena battle" +//set g_duel_warmup 180 "Have a short warmup period before beginning the actual duel" +set g_duel_with_powerups 0 "Enable powerups to spawn in the duel gamemode" diff --git a/gfx/hud/default/as_defend.tga b/gfx/hud/default/as_defend.tga new file mode 100644 index 0000000000..5aca85e594 Binary files /dev/null and b/gfx/hud/default/as_defend.tga differ diff --git a/gfx/hud/default/as_destroy.tga b/gfx/hud/default/as_destroy.tga new file mode 100644 index 0000000000..7c779a1aa2 Binary files /dev/null and b/gfx/hud/default/as_destroy.tga differ diff --git a/gfx/hud/default/nade_veil.tga b/gfx/hud/default/nade_veil.tga new file mode 100644 index 0000000000..3134d1bd81 Binary files /dev/null and b/gfx/hud/default/nade_veil.tga differ diff --git a/gfx/hud/luma/as_defend.tga b/gfx/hud/luma/as_defend.tga new file mode 100644 index 0000000000..5aca85e594 Binary files /dev/null and b/gfx/hud/luma/as_defend.tga differ diff --git a/gfx/hud/luma/as_destroy.tga b/gfx/hud/luma/as_destroy.tga new file mode 100644 index 0000000000..7c779a1aa2 Binary files /dev/null and b/gfx/hud/luma/as_destroy.tga differ diff --git a/gfx/hud/luma/nade_veil.tga b/gfx/hud/luma/nade_veil.tga new file mode 100644 index 0000000000..edb66b52ed Binary files /dev/null and b/gfx/hud/luma/nade_veil.tga differ diff --git a/gfx/hud/luma/ok_weapon_rail.tga b/gfx/hud/luma/ok_weapon_rail.tga index e2e83ae758..b90d291fb4 100644 Binary files a/gfx/hud/luma/ok_weapon_rail.tga and b/gfx/hud/luma/ok_weapon_rail.tga differ diff --git a/gfx/hud/luma/ok_weapon_shotgun.tga b/gfx/hud/luma/ok_weapon_shotgun.tga index f5fb92e13e..b0e7bf9327 100644 Binary files a/gfx/hud/luma/ok_weapon_shotgun.tga and b/gfx/hud/luma/ok_weapon_shotgun.tga differ diff --git a/gfx/hud/luma/ok_weapon_smg.tga b/gfx/hud/luma/ok_weapon_smg.tga index 5aee32ab54..cb26a1657d 100644 Binary files a/gfx/hud/luma/ok_weapon_smg.tga and b/gfx/hud/luma/ok_weapon_smg.tga differ diff --git a/gfx/hud/luma/weaponhmg.tga b/gfx/hud/luma/weaponhmg.tga index c36c3f5e7e..cb0414cc1e 100644 Binary files a/gfx/hud/luma/weaponhmg.tga and b/gfx/hud/luma/weaponhmg.tga differ diff --git a/gfx/hud/luma/weaponrpc.tga b/gfx/hud/luma/weaponrpc.tga index 643c5cafee..74019d86f9 100644 Binary files a/gfx/hud/luma/weaponrpc.tga and b/gfx/hud/luma/weaponrpc.tga differ diff --git a/gfx/menu/luma/bigbutton_c.tga b/gfx/menu/luma/bigbutton_c.tga index 3565f3c36e..49f3b7db3c 100644 Binary files a/gfx/menu/luma/bigbutton_c.tga and b/gfx/menu/luma/bigbutton_c.tga differ diff --git a/gfx/menu/luma/bigbutton_d.tga b/gfx/menu/luma/bigbutton_d.tga index 0efae80e72..5fb8904809 100644 Binary files a/gfx/menu/luma/bigbutton_d.tga and b/gfx/menu/luma/bigbutton_d.tga differ diff --git a/gfx/menu/luma/bigbutton_f.tga b/gfx/menu/luma/bigbutton_f.tga index 3edc7dab2f..638dcd4875 100644 Binary files a/gfx/menu/luma/bigbutton_f.tga and b/gfx/menu/luma/bigbutton_f.tga differ diff --git a/gfx/menu/luma/bigbutton_n.tga b/gfx/menu/luma/bigbutton_n.tga index 4f07b0c4be..0c773b252b 100644 Binary files a/gfx/menu/luma/bigbutton_n.tga and b/gfx/menu/luma/bigbutton_n.tga differ diff --git a/gfx/menu/luma/bigbuttongray_c.tga b/gfx/menu/luma/bigbuttongray_c.tga index e18e0f6e79..eed1781f99 100644 Binary files a/gfx/menu/luma/bigbuttongray_c.tga and b/gfx/menu/luma/bigbuttongray_c.tga differ diff --git a/gfx/menu/luma/bigbuttongray_d.tga b/gfx/menu/luma/bigbuttongray_d.tga index 87b5dd5286..8b6ef4be35 100644 Binary files a/gfx/menu/luma/bigbuttongray_d.tga and b/gfx/menu/luma/bigbuttongray_d.tga differ diff --git a/gfx/menu/luma/bigbuttongray_f.tga b/gfx/menu/luma/bigbuttongray_f.tga index 3624987845..b87d09474e 100644 Binary files a/gfx/menu/luma/bigbuttongray_f.tga and b/gfx/menu/luma/bigbuttongray_f.tga differ diff --git a/gfx/menu/luma/bigbuttongray_n.tga b/gfx/menu/luma/bigbuttongray_n.tga index e18e0f6e79..eed1781f99 100644 Binary files a/gfx/menu/luma/bigbuttongray_n.tga and b/gfx/menu/luma/bigbuttongray_n.tga differ diff --git a/gfx/menu/luma/border.tga b/gfx/menu/luma/border.tga index 95bdc3c488..40705b6931 100644 Binary files a/gfx/menu/luma/border.tga and b/gfx/menu/luma/border.tga differ diff --git a/gfx/menu/luma/button_c.tga b/gfx/menu/luma/button_c.tga index b86b63a25f..33c7cb74bb 100644 Binary files a/gfx/menu/luma/button_c.tga and b/gfx/menu/luma/button_c.tga differ diff --git a/gfx/menu/luma/button_d.tga b/gfx/menu/luma/button_d.tga index f80433038e..e5b65d7563 100644 Binary files a/gfx/menu/luma/button_d.tga and b/gfx/menu/luma/button_d.tga differ diff --git a/gfx/menu/luma/button_f.tga b/gfx/menu/luma/button_f.tga index 0b54fee340..30d32521a5 100644 Binary files a/gfx/menu/luma/button_f.tga and b/gfx/menu/luma/button_f.tga differ diff --git a/gfx/menu/luma/button_n.tga b/gfx/menu/luma/button_n.tga index 0dc4fd6af4..6d098576a3 100644 Binary files a/gfx/menu/luma/button_n.tga and b/gfx/menu/luma/button_n.tga differ diff --git a/gfx/menu/luma/buttongray_c.tga b/gfx/menu/luma/buttongray_c.tga index 4c1ae7c0fe..d05266b40f 100644 Binary files a/gfx/menu/luma/buttongray_c.tga and b/gfx/menu/luma/buttongray_c.tga differ diff --git a/gfx/menu/luma/buttongray_d.tga b/gfx/menu/luma/buttongray_d.tga index e0e8318e00..4bfab65e99 100644 Binary files a/gfx/menu/luma/buttongray_d.tga and b/gfx/menu/luma/buttongray_d.tga differ diff --git a/gfx/menu/luma/buttongray_f.tga b/gfx/menu/luma/buttongray_f.tga index cc604bc824..d5ac917fda 100644 Binary files a/gfx/menu/luma/buttongray_f.tga and b/gfx/menu/luma/buttongray_f.tga differ diff --git a/gfx/menu/luma/buttongray_n.tga b/gfx/menu/luma/buttongray_n.tga index 4c1ae7c0fe..d05266b40f 100644 Binary files a/gfx/menu/luma/buttongray_n.tga and b/gfx/menu/luma/buttongray_n.tga differ diff --git a/gfx/menu/luma/checkbox_c0.tga b/gfx/menu/luma/checkbox_c0.tga index f8e6960cc2..30669ca6af 100644 Binary files a/gfx/menu/luma/checkbox_c0.tga and b/gfx/menu/luma/checkbox_c0.tga differ diff --git a/gfx/menu/luma/checkbox_c1.tga b/gfx/menu/luma/checkbox_c1.tga index 32d2f6d7b5..acb9bc6097 100644 Binary files a/gfx/menu/luma/checkbox_c1.tga and b/gfx/menu/luma/checkbox_c1.tga differ diff --git a/gfx/menu/luma/checkbox_d0.tga b/gfx/menu/luma/checkbox_d0.tga index b059ab64c4..f42971a26a 100644 Binary files a/gfx/menu/luma/checkbox_d0.tga and b/gfx/menu/luma/checkbox_d0.tga differ diff --git a/gfx/menu/luma/checkbox_d1.tga b/gfx/menu/luma/checkbox_d1.tga index e38b9629ce..2346dc7387 100644 Binary files a/gfx/menu/luma/checkbox_d1.tga and b/gfx/menu/luma/checkbox_d1.tga differ diff --git a/gfx/menu/luma/checkbox_f0.tga b/gfx/menu/luma/checkbox_f0.tga index 5201675e67..ddc0842f76 100644 Binary files a/gfx/menu/luma/checkbox_f0.tga and b/gfx/menu/luma/checkbox_f0.tga differ diff --git a/gfx/menu/luma/checkbox_f1.tga b/gfx/menu/luma/checkbox_f1.tga index 116b29c80d..fb16d9055b 100644 Binary files a/gfx/menu/luma/checkbox_f1.tga and b/gfx/menu/luma/checkbox_f1.tga differ diff --git a/gfx/menu/luma/checkbox_n0.tga b/gfx/menu/luma/checkbox_n0.tga index 9786dc84ed..4f4d7120b9 100644 Binary files a/gfx/menu/luma/checkbox_n0.tga and b/gfx/menu/luma/checkbox_n0.tga differ diff --git a/gfx/menu/luma/checkbox_n1.tga b/gfx/menu/luma/checkbox_n1.tga index 427d3ce421..1102900dac 100644 Binary files a/gfx/menu/luma/checkbox_n1.tga and b/gfx/menu/luma/checkbox_n1.tga differ diff --git a/gfx/menu/luma/checkmark.tga b/gfx/menu/luma/checkmark.tga index eb4b259fcd..c6698a2c20 100644 Binary files a/gfx/menu/luma/checkmark.tga and b/gfx/menu/luma/checkmark.tga differ diff --git a/gfx/menu/luma/clearbutton_c.tga b/gfx/menu/luma/clearbutton_c.tga index 36fc40b804..efcf9b5a56 100644 Binary files a/gfx/menu/luma/clearbutton_c.tga and b/gfx/menu/luma/clearbutton_c.tga differ diff --git a/gfx/menu/luma/clearbutton_f.tga b/gfx/menu/luma/clearbutton_f.tga index c2418a40d3..2306d91456 100644 Binary files a/gfx/menu/luma/clearbutton_f.tga and b/gfx/menu/luma/clearbutton_f.tga differ diff --git a/gfx/menu/luma/clearbutton_n.tga b/gfx/menu/luma/clearbutton_n.tga index 3ac282b6e9..343ae6a173 100644 Binary files a/gfx/menu/luma/clearbutton_n.tga and b/gfx/menu/luma/clearbutton_n.tga differ diff --git a/gfx/menu/luma/closebutton_c.tga b/gfx/menu/luma/closebutton_c.tga index 3a8f46fd45..1e03c392db 100644 Binary files a/gfx/menu/luma/closebutton_c.tga and b/gfx/menu/luma/closebutton_c.tga differ diff --git a/gfx/menu/luma/closebutton_f.tga b/gfx/menu/luma/closebutton_f.tga index 0b3933b6d3..7f1f5b58f4 100644 Binary files a/gfx/menu/luma/closebutton_f.tga and b/gfx/menu/luma/closebutton_f.tga differ diff --git a/gfx/menu/luma/closebutton_n.tga b/gfx/menu/luma/closebutton_n.tga index 4ea4b1cc12..0313efab7c 100644 Binary files a/gfx/menu/luma/closebutton_n.tga and b/gfx/menu/luma/closebutton_n.tga differ diff --git a/gfx/menu/luma/colorbutton_c.tga b/gfx/menu/luma/colorbutton_c.tga index 91f343e703..086f9a7a3d 100644 Binary files a/gfx/menu/luma/colorbutton_c.tga and b/gfx/menu/luma/colorbutton_c.tga differ diff --git a/gfx/menu/luma/colorbutton_f.tga b/gfx/menu/luma/colorbutton_f.tga index 0aa806b041..32dd33e479 100644 Binary files a/gfx/menu/luma/colorbutton_f.tga and b/gfx/menu/luma/colorbutton_f.tga differ diff --git a/gfx/menu/luma/colorbutton_n.tga b/gfx/menu/luma/colorbutton_n.tga index bcba096780..ca128b7922 100644 Binary files a/gfx/menu/luma/colorbutton_n.tga and b/gfx/menu/luma/colorbutton_n.tga differ diff --git a/gfx/menu/luma/colorpicker_m.tga b/gfx/menu/luma/colorpicker_m.tga index 6775942eec..30ec7f78a7 100644 Binary files a/gfx/menu/luma/colorpicker_m.tga and b/gfx/menu/luma/colorpicker_m.tga differ diff --git a/gfx/menu/luma/cursor.tga b/gfx/menu/luma/cursor.tga index b12436a983..87a12b6f2c 100644 Binary files a/gfx/menu/luma/cursor.tga and b/gfx/menu/luma/cursor.tga differ diff --git a/gfx/menu/luma/cursor_move.tga b/gfx/menu/luma/cursor_move.tga index 81e265d993..19268ea7d0 100644 Binary files a/gfx/menu/luma/cursor_move.tga and b/gfx/menu/luma/cursor_move.tga differ diff --git a/gfx/menu/luma/cursor_resize.tga b/gfx/menu/luma/cursor_resize.tga index 617323edee..84f53f99e8 100644 Binary files a/gfx/menu/luma/cursor_resize.tga and b/gfx/menu/luma/cursor_resize.tga differ diff --git a/gfx/menu/luma/cursor_resize2.tga b/gfx/menu/luma/cursor_resize2.tga index 606015b012..10a550d512 100644 Binary files a/gfx/menu/luma/cursor_resize2.tga and b/gfx/menu/luma/cursor_resize2.tga differ diff --git a/gfx/menu/luma/gametype_as.tga b/gfx/menu/luma/gametype_as.tga index 9724f40497..41e1c28944 100644 Binary files a/gfx/menu/luma/gametype_as.tga and b/gfx/menu/luma/gametype_as.tga differ diff --git a/gfx/menu/luma/gametype_ca.tga b/gfx/menu/luma/gametype_ca.tga index 196196e5ec..e4e827c7a5 100644 Binary files a/gfx/menu/luma/gametype_ca.tga and b/gfx/menu/luma/gametype_ca.tga differ diff --git a/gfx/menu/luma/gametype_ctf.tga b/gfx/menu/luma/gametype_ctf.tga index eefb2fe0b5..13a1175301 100644 Binary files a/gfx/menu/luma/gametype_ctf.tga and b/gfx/menu/luma/gametype_ctf.tga differ diff --git a/gfx/menu/luma/gametype_cts.tga b/gfx/menu/luma/gametype_cts.tga index da86fa4faf..86c4a1309d 100644 Binary files a/gfx/menu/luma/gametype_cts.tga and b/gfx/menu/luma/gametype_cts.tga differ diff --git a/gfx/menu/luma/gametype_dm.tga b/gfx/menu/luma/gametype_dm.tga index 3d0874ead1..37a3657b80 100644 Binary files a/gfx/menu/luma/gametype_dm.tga and b/gfx/menu/luma/gametype_dm.tga differ diff --git a/gfx/menu/luma/gametype_dom.tga b/gfx/menu/luma/gametype_dom.tga index eb6605b5b3..3e7b0dedbd 100644 Binary files a/gfx/menu/luma/gametype_dom.tga and b/gfx/menu/luma/gametype_dom.tga differ diff --git a/gfx/menu/luma/gametype_duel.tga b/gfx/menu/luma/gametype_duel.tga index 3558725a54..ac9a4e2e96 100644 Binary files a/gfx/menu/luma/gametype_duel.tga and b/gfx/menu/luma/gametype_duel.tga differ diff --git a/gfx/menu/luma/gametype_ft.tga b/gfx/menu/luma/gametype_ft.tga index f6228a5106..b0636ba87a 100644 Binary files a/gfx/menu/luma/gametype_ft.tga and b/gfx/menu/luma/gametype_ft.tga differ diff --git a/gfx/menu/luma/gametype_inf.tga b/gfx/menu/luma/gametype_inf.tga index 9093837972..237ed6e6f5 100644 Binary files a/gfx/menu/luma/gametype_inf.tga and b/gfx/menu/luma/gametype_inf.tga differ diff --git a/gfx/menu/luma/gametype_inv.tga b/gfx/menu/luma/gametype_inv.tga index d954af1ebc..5003f6ce5a 100644 Binary files a/gfx/menu/luma/gametype_inv.tga and b/gfx/menu/luma/gametype_inv.tga differ diff --git a/gfx/menu/luma/gametype_jb.tga b/gfx/menu/luma/gametype_jb.tga index e6b35a4e1c..105ffcfbbc 100644 Binary files a/gfx/menu/luma/gametype_jb.tga and b/gfx/menu/luma/gametype_jb.tga differ diff --git a/gfx/menu/luma/gametype_ka.tga b/gfx/menu/luma/gametype_ka.tga index 38acfc705e..3731ab3c1a 100644 Binary files a/gfx/menu/luma/gametype_ka.tga and b/gfx/menu/luma/gametype_ka.tga differ diff --git a/gfx/menu/luma/gametype_kh.tga b/gfx/menu/luma/gametype_kh.tga index df30ff0ea1..a3a678f0f3 100644 Binary files a/gfx/menu/luma/gametype_kh.tga and b/gfx/menu/luma/gametype_kh.tga differ diff --git a/gfx/menu/luma/gametype_lms.tga b/gfx/menu/luma/gametype_lms.tga index e14d2a2f9b..4cccac846b 100644 Binary files a/gfx/menu/luma/gametype_lms.tga and b/gfx/menu/luma/gametype_lms.tga differ diff --git a/gfx/menu/luma/gametype_nb.tga b/gfx/menu/luma/gametype_nb.tga index 93b021e455..37be7c751d 100644 Binary files a/gfx/menu/luma/gametype_nb.tga and b/gfx/menu/luma/gametype_nb.tga differ diff --git a/gfx/menu/luma/gametype_ons.tga b/gfx/menu/luma/gametype_ons.tga index 48a0c5a13a..d73d19a966 100644 Binary files a/gfx/menu/luma/gametype_ons.tga and b/gfx/menu/luma/gametype_ons.tga differ diff --git a/gfx/menu/luma/gametype_rc.tga b/gfx/menu/luma/gametype_rc.tga index a03204238f..a88c9eef52 100644 Binary files a/gfx/menu/luma/gametype_rc.tga and b/gfx/menu/luma/gametype_rc.tga differ diff --git a/gfx/menu/luma/gametype_tdm.tga b/gfx/menu/luma/gametype_tdm.tga index 3a6c0922f4..2b4a343024 100644 Binary files a/gfx/menu/luma/gametype_tdm.tga and b/gfx/menu/luma/gametype_tdm.tga differ diff --git a/gfx/menu/luma/gametype_vip.tga b/gfx/menu/luma/gametype_vip.tga index 8a8f7d984d..d9af96b936 100644 Binary files a/gfx/menu/luma/gametype_vip.tga and b/gfx/menu/luma/gametype_vip.tga differ diff --git a/gfx/menu/luma/icon_aeslevel1.tga b/gfx/menu/luma/icon_aeslevel1.tga index c32dd97a85..f7c190e8bc 100644 Binary files a/gfx/menu/luma/icon_aeslevel1.tga and b/gfx/menu/luma/icon_aeslevel1.tga differ diff --git a/gfx/menu/luma/icon_aeslevel2.tga b/gfx/menu/luma/icon_aeslevel2.tga index c32dd97a85..f7c190e8bc 100644 Binary files a/gfx/menu/luma/icon_aeslevel2.tga and b/gfx/menu/luma/icon_aeslevel2.tga differ diff --git a/gfx/menu/luma/icon_aeslevel3.tga b/gfx/menu/luma/icon_aeslevel3.tga index c90efa8c0d..f84432ddd3 100644 Binary files a/gfx/menu/luma/icon_aeslevel3.tga and b/gfx/menu/luma/icon_aeslevel3.tga differ diff --git a/gfx/menu/luma/icon_aeslevel4.tga b/gfx/menu/luma/icon_aeslevel4.tga index c90efa8c0d..f84432ddd3 100644 Binary files a/gfx/menu/luma/icon_aeslevel4.tga and b/gfx/menu/luma/icon_aeslevel4.tga differ diff --git a/gfx/menu/luma/icon_aeslevel5.tga b/gfx/menu/luma/icon_aeslevel5.tga index c90efa8c0d..f84432ddd3 100644 Binary files a/gfx/menu/luma/icon_aeslevel5.tga and b/gfx/menu/luma/icon_aeslevel5.tga differ diff --git a/gfx/menu/luma/icon_ipv4.tga b/gfx/menu/luma/icon_ipv4.tga index 83659d08ef..e7baf78b9c 100644 Binary files a/gfx/menu/luma/icon_ipv4.tga and b/gfx/menu/luma/icon_ipv4.tga differ diff --git a/gfx/menu/luma/icon_ipv6.tga b/gfx/menu/luma/icon_ipv6.tga index 43d9d41785..db474afe75 100644 Binary files a/gfx/menu/luma/icon_ipv6.tga and b/gfx/menu/luma/icon_ipv6.tga differ diff --git a/gfx/menu/luma/icon_mod_.tga b/gfx/menu/luma/icon_mod_.tga index 79a1f24504..8ba3747e0f 100644 Binary files a/gfx/menu/luma/icon_mod_.tga and b/gfx/menu/luma/icon_mod_.tga differ diff --git a/gfx/menu/luma/icon_mod_XDF.tga b/gfx/menu/luma/icon_mod_XDF.tga deleted file mode 100644 index c0b4e6dd99..0000000000 Binary files a/gfx/menu/luma/icon_mod_XDF.tga and /dev/null differ diff --git a/gfx/menu/luma/icon_mod_instagib.tga b/gfx/menu/luma/icon_mod_instagib.tga index ce8c2d50d7..750bb19b3a 100644 Binary files a/gfx/menu/luma/icon_mod_instagib.tga and b/gfx/menu/luma/icon_mod_instagib.tga differ diff --git a/gfx/menu/luma/icon_mod_jeff.tga b/gfx/menu/luma/icon_mod_jeff.tga index ba3080a7df..9fc2a9a83a 100644 Binary files a/gfx/menu/luma/icon_mod_jeff.tga and b/gfx/menu/luma/icon_mod_jeff.tga differ diff --git a/gfx/menu/luma/icon_mod_minstagib.tga b/gfx/menu/luma/icon_mod_minstagib.tga index ce8c2d50d7..750bb19b3a 100644 Binary files a/gfx/menu/luma/icon_mod_minstagib.tga and b/gfx/menu/luma/icon_mod_minstagib.tga differ diff --git a/gfx/menu/luma/icon_mod_newtoys.tga b/gfx/menu/luma/icon_mod_newtoys.tga index c5f212c078..7862e58f38 100644 Binary files a/gfx/menu/luma/icon_mod_newtoys.tga and b/gfx/menu/luma/icon_mod_newtoys.tga differ diff --git a/gfx/menu/luma/icon_mod_overkill.tga b/gfx/menu/luma/icon_mod_overkill.tga index aeeaeb883a..d72e7f622e 100644 Binary files a/gfx/menu/luma/icon_mod_overkill.tga and b/gfx/menu/luma/icon_mod_overkill.tga differ diff --git a/gfx/menu/luma/icon_mod_quake.tga b/gfx/menu/luma/icon_mod_quake.tga index e9eb0dfd0f..58901465d5 100644 Binary files a/gfx/menu/luma/icon_mod_quake.tga and b/gfx/menu/luma/icon_mod_quake.tga differ diff --git a/gfx/menu/luma/icon_mod_xdf.tga b/gfx/menu/luma/icon_mod_xdf.tga new file mode 100644 index 0000000000..6749a4833a Binary files /dev/null and b/gfx/menu/luma/icon_mod_xdf.tga differ diff --git a/gfx/menu/luma/icon_mod_xpm.tga b/gfx/menu/luma/icon_mod_xpm.tga new file mode 100644 index 0000000000..47c3fb641b Binary files /dev/null and b/gfx/menu/luma/icon_mod_xpm.tga differ diff --git a/gfx/menu/luma/icon_pure1.tga b/gfx/menu/luma/icon_pure1.tga index fbe4dc9ffa..041921822f 100644 Binary files a/gfx/menu/luma/icon_pure1.tga and b/gfx/menu/luma/icon_pure1.tga differ diff --git a/gfx/menu/luma/icon_stats1.tga b/gfx/menu/luma/icon_stats1.tga index 841dde650f..564edf5b3c 100644 Binary files a/gfx/menu/luma/icon_stats1.tga and b/gfx/menu/luma/icon_stats1.tga differ diff --git a/gfx/menu/luma/inputbox_f.tga b/gfx/menu/luma/inputbox_f.tga index c31aa438ef..658e9c41e5 100644 Binary files a/gfx/menu/luma/inputbox_f.tga and b/gfx/menu/luma/inputbox_f.tga differ diff --git a/gfx/menu/luma/inputbox_n.tga b/gfx/menu/luma/inputbox_n.tga index 621f50d9cd..0bbd45023d 100644 Binary files a/gfx/menu/luma/inputbox_n.tga and b/gfx/menu/luma/inputbox_n.tga differ diff --git a/gfx/menu/luma/nopreview_map.tga b/gfx/menu/luma/nopreview_map.tga index 5cd70916d5..aa8ee77bd3 100644 Binary files a/gfx/menu/luma/nopreview_map.tga and b/gfx/menu/luma/nopreview_map.tga differ diff --git a/gfx/menu/luma/nopreview_menuskin.tga b/gfx/menu/luma/nopreview_menuskin.tga index c332b6345a..1806719d0d 100644 Binary files a/gfx/menu/luma/nopreview_menuskin.tga and b/gfx/menu/luma/nopreview_menuskin.tga differ diff --git a/gfx/menu/luma/nopreview_player.tga b/gfx/menu/luma/nopreview_player.tga index e1fa52c67e..583a91b475 100644 Binary files a/gfx/menu/luma/nopreview_player.tga and b/gfx/menu/luma/nopreview_player.tga differ diff --git a/gfx/menu/luma/radiobutton_c0.tga b/gfx/menu/luma/radiobutton_c0.tga index 8b60a1b274..2cb80f979f 100644 Binary files a/gfx/menu/luma/radiobutton_c0.tga and b/gfx/menu/luma/radiobutton_c0.tga differ diff --git a/gfx/menu/luma/radiobutton_c1.tga b/gfx/menu/luma/radiobutton_c1.tga index d23f9df020..d1561539e9 100644 Binary files a/gfx/menu/luma/radiobutton_c1.tga and b/gfx/menu/luma/radiobutton_c1.tga differ diff --git a/gfx/menu/luma/radiobutton_d0.tga b/gfx/menu/luma/radiobutton_d0.tga index 16a4ff8a32..8bc37140dd 100644 Binary files a/gfx/menu/luma/radiobutton_d0.tga and b/gfx/menu/luma/radiobutton_d0.tga differ diff --git a/gfx/menu/luma/radiobutton_d1.tga b/gfx/menu/luma/radiobutton_d1.tga index b4eacffbc2..93bf16dbd0 100644 Binary files a/gfx/menu/luma/radiobutton_d1.tga and b/gfx/menu/luma/radiobutton_d1.tga differ diff --git a/gfx/menu/luma/radiobutton_f0.tga b/gfx/menu/luma/radiobutton_f0.tga index 59ecc0805b..7875e38813 100644 Binary files a/gfx/menu/luma/radiobutton_f0.tga and b/gfx/menu/luma/radiobutton_f0.tga differ diff --git a/gfx/menu/luma/radiobutton_f1.tga b/gfx/menu/luma/radiobutton_f1.tga index 396e2771a8..63ddaf5b47 100644 Binary files a/gfx/menu/luma/radiobutton_f1.tga and b/gfx/menu/luma/radiobutton_f1.tga differ diff --git a/gfx/menu/luma/radiobutton_n0.tga b/gfx/menu/luma/radiobutton_n0.tga index 13a96e9690..150a39286f 100644 Binary files a/gfx/menu/luma/radiobutton_n0.tga and b/gfx/menu/luma/radiobutton_n0.tga differ diff --git a/gfx/menu/luma/radiobutton_n1.tga b/gfx/menu/luma/radiobutton_n1.tga index a64b50cc31..34b6618a93 100644 Binary files a/gfx/menu/luma/radiobutton_n1.tga and b/gfx/menu/luma/radiobutton_n1.tga differ diff --git a/gfx/menu/luma/scrollbar_c.tga b/gfx/menu/luma/scrollbar_c.tga index 09333d1412..6bedb66c96 100644 Binary files a/gfx/menu/luma/scrollbar_c.tga and b/gfx/menu/luma/scrollbar_c.tga differ diff --git a/gfx/menu/luma/scrollbar_f.tga b/gfx/menu/luma/scrollbar_f.tga index 109278c9f0..539328d00a 100644 Binary files a/gfx/menu/luma/scrollbar_f.tga and b/gfx/menu/luma/scrollbar_f.tga differ diff --git a/gfx/menu/luma/scrollbar_n.tga b/gfx/menu/luma/scrollbar_n.tga index b8a01d1779..e081a306b3 100644 Binary files a/gfx/menu/luma/scrollbar_n.tga and b/gfx/menu/luma/scrollbar_n.tga differ diff --git a/gfx/menu/luma/scrollbar_s.tga b/gfx/menu/luma/scrollbar_s.tga index 699e11c328..c1e21e4f97 100644 Binary files a/gfx/menu/luma/scrollbar_s.tga and b/gfx/menu/luma/scrollbar_s.tga differ diff --git a/gfx/menu/luma/skinpreview.tga b/gfx/menu/luma/skinpreview.tga index f89a2e605f..50e4ffd3dc 100644 Binary files a/gfx/menu/luma/skinpreview.tga and b/gfx/menu/luma/skinpreview.tga differ diff --git a/gfx/menu/luma/skinvalues.txt b/gfx/menu/luma/skinvalues.txt index cfaa1e5804..eef45e9a0a 100644 --- a/gfx/menu/luma/skinvalues.txt +++ b/gfx/menu/luma/skinvalues.txt @@ -12,7 +12,7 @@ author sev ALIGN_BACKGROUND c5h5 ALIGN_BACKGROUND_INGAME c5h5 ALPHA_BACKGROUND_INGAME 1 -ALPHA_DISABLED 0.2 +ALPHA_DISABLED 0.25 ALPHA_BEHIND 0.5 // button @@ -115,7 +115,7 @@ POSITION_DIALOG_CREDITS '-0.05 1.2 0' POSITION_DIALOG_QUIT '1.05 1.2 0' // font -ALPHA_TEXT 0.8 +ALPHA_TEXT 0.875 COLOR_TEXT '0.96 0.99 1' ALPHA_HEADER 0.5 COLOR_HEADER '0.96 0.99 1' @@ -144,7 +144,7 @@ COLOR_KEYGRABBER_TITLES '0.03 0.25 0.49' ALPHA_LISTBOX_BACKGROUND 0.5 COLOR_LISTBOX_BACKGROUND '0 0 0' ALPHA_LISTBOX_SELECTED 1 -COLOR_LISTBOX_SELECTED '0.97 0.56 0.27' +COLOR_LISTBOX_SELECTED '0.9 0.53 0.28' ALPHA_LISTBOX_WAITING 0.8 COLOR_LISTBOX_WAITING '0.73 0.82 0.9' ALPHA_LISTBOX_FOCUSED 0.55 @@ -209,7 +209,7 @@ WIDTH_SLIDERTEXT 0.333333333333 // tooltip ALPHA_TOOLTIP 0.8 -COLOR_TOOLTIP '1 0.97 0.94' +COLOR_TOOLTIP '1 0.97 0.95' AVOID_TOOLTIP '8 8 0' BORDER_TOOLTIP '16 16 0' MARGIN_TOOLTIP '10 8 0' diff --git a/gfx/menu/luma/slider_c.tga b/gfx/menu/luma/slider_c.tga index 8471b32d97..b460e27408 100644 Binary files a/gfx/menu/luma/slider_c.tga and b/gfx/menu/luma/slider_c.tga differ diff --git a/gfx/menu/luma/slider_d.tga b/gfx/menu/luma/slider_d.tga index c264fee985..fbd76c22a5 100644 Binary files a/gfx/menu/luma/slider_d.tga and b/gfx/menu/luma/slider_d.tga differ diff --git a/gfx/menu/luma/slider_f.tga b/gfx/menu/luma/slider_f.tga index 6ff26873fd..96e5318df2 100644 Binary files a/gfx/menu/luma/slider_f.tga and b/gfx/menu/luma/slider_f.tga differ diff --git a/gfx/menu/luma/slider_n.tga b/gfx/menu/luma/slider_n.tga index 2ee36af322..4d68120e6e 100644 Binary files a/gfx/menu/luma/slider_n.tga and b/gfx/menu/luma/slider_n.tga differ diff --git a/gfx/menu/luma/slider_s.tga b/gfx/menu/luma/slider_s.tga index e5b7141dd3..e78e8a4134 100644 Binary files a/gfx/menu/luma/slider_s.tga and b/gfx/menu/luma/slider_s.tga differ diff --git a/gfx/menu/luma/tooltip.tga b/gfx/menu/luma/tooltip.tga index 32ea27c8ed..fbb29936a6 100644 Binary files a/gfx/menu/luma/tooltip.tga and b/gfx/menu/luma/tooltip.tga differ diff --git a/gfx/menu/luminos/icon_mod_xpm.tga b/gfx/menu/luminos/icon_mod_xpm.tga new file mode 100644 index 0000000000..3d51ff3be2 Binary files /dev/null and b/gfx/menu/luminos/icon_mod_xpm.tga differ diff --git a/gfx/menu/wickedx/icon_mod_xpm.tga b/gfx/menu/wickedx/icon_mod_xpm.tga new file mode 100644 index 0000000000..3d51ff3be2 Binary files /dev/null and b/gfx/menu/wickedx/icon_mod_xpm.tga differ diff --git a/gfx/menu/xaw/icon_mod_xpm.tga b/gfx/menu/xaw/icon_mod_xpm.tga new file mode 100644 index 0000000000..3d51ff3be2 Binary files /dev/null and b/gfx/menu/xaw/icon_mod_xpm.tga differ diff --git a/gfx/scoreboard/player_ready.tga b/gfx/scoreboard/player_ready.tga index 71cbd68a71..66bfa6e308 100644 Binary files a/gfx/scoreboard/player_ready.tga and b/gfx/scoreboard/player_ready.tga differ diff --git a/gfx/scoreboard/playercolor_base.tga b/gfx/scoreboard/playercolor_base.tga index 443abc4f6d..c67a4ace5f 100644 Binary files a/gfx/scoreboard/playercolor_base.tga and b/gfx/scoreboard/playercolor_base.tga differ diff --git a/gfx/scoreboard/playercolor_pants.tga b/gfx/scoreboard/playercolor_pants.tga index f9c7307973..d48ea9a98c 100644 Binary files a/gfx/scoreboard/playercolor_pants.tga and b/gfx/scoreboard/playercolor_pants.tga differ diff --git a/gfx/scoreboard/playercolor_shirt.tga b/gfx/scoreboard/playercolor_shirt.tga index eeeaf4c370..54a3826006 100644 Binary files a/gfx/scoreboard/playercolor_shirt.tga and b/gfx/scoreboard/playercolor_shirt.tga differ diff --git a/gfx/scoreboard/scoreboard_bg.tga b/gfx/scoreboard/scoreboard_bg.tga index e8a5d348b3..13bc9f3fb6 100644 Binary files a/gfx/scoreboard/scoreboard_bg.tga and b/gfx/scoreboard/scoreboard_bg.tga differ diff --git a/gfx/scoreboard/scoreboard_tableheader.tga b/gfx/scoreboard/scoreboard_tableheader.tga index 122d4f482d..9a050d5ca6 100644 Binary files a/gfx/scoreboard/scoreboard_tableheader.tga and b/gfx/scoreboard/scoreboard_tableheader.tga differ diff --git a/help-overkill.cfg b/help-overkill.cfg new file mode 100644 index 0000000000..e48147c874 --- /dev/null +++ b/help-overkill.cfg @@ -0,0 +1,18 @@ +set help_msg_0 "Wondering why you die so often? Because you're ignoring armor and health pickups" +set help_msg_1 "Use secondary fire (blaster) on the floor to jump higher" +set help_msg_2 "Double press W after spawning to accelerate by jumping forward (=dodging)" +set help_msg_3 "Keep the jump key pressed to stay fast (=bunny-hopping)" +set help_msg_4 "Run along a flat wall and quickly press W twice to gain speed (=wall dodging)" +set help_msg_5 "Pick up armor shards from dead enemies to survive the next hit" +set help_msg_6 "Stand *completely* still, then quickly double press W, A, S or D to dodge" +set help_msg_7 "Use the blaster to make large jumps or climb walls" +set help_msg_8 "Use the Shotgun (or Machine Gun) to slow down fast players" +set help_msg_9 "When running, blaster the floor or walls to gain more speed" +set help_msg_10 "Use G (default key) to throw Nades (you want the 'dropweapon' bind, not 'hook', for better timing)" +set help_msg_11 "Don't reload, switch to Shotgun" +set help_msg_12 "You can blaster the flag or dropped Nades to push them away" +set help_msg_13 "This is how most pros throw Nades: press G (dropweapon bind), wait *several* seconds, press G again" +set help_msg_14 "Dodge forward to climb walls faster" +set help_msg_15 "Double press W, then hold space to start moving" +set help_msg_16 "Spectate stronger players to learn their tricks" +set help_msg_count 17 // update this when adding messages - it should be the number of messages (which means last message index + 1) diff --git a/help-xonotic.cfg b/help-xonotic.cfg new file mode 100644 index 0000000000..1bd9e272e2 --- /dev/null +++ b/help-xonotic.cfg @@ -0,0 +1,60 @@ +set help_msg_0 "Big Admin is watching you, so please be friendly or feel their almighty ban-hammer!" +set help_msg_1 "If you want to learn more about Xonotic, read ^1'Halogene's Newbie Corner' (https://xonotic.org/guide) ${help_cfg_prefix}as it contains lots of useful tips and tricks, explains all the weapons and helps to improve your gameplay." +set help_msg_2 "Please watch out for balanced teams and change by pressing F5 (teammenu) or F6 (auto join 'best' team)." +set help_msg_3 "When trying to bunny-hop you can ^1hold the jump button ${help_cfg_prefix}while you are still in the air, this will make those jumps VERY easy to time and work more reliable." +set help_msg_4 "When a vote is called you can accept it via F1 or reject it via F2 (default keys)." +set help_msg_5 "Spectating other (good) players helps to learn new tricks. To spectate press F3 and then Mouse1 to switch between the players you want to spectate. F5, F6 or jump will get you back into the game (default keys)." +set help_msg_6 "Being a beginner is great! You can learn so many new tricks and improve quickly. Watch others, ask for advice and use your common sense effectively." +set help_msg_7 "If others are better than you, it does not mean they cheat. Save such complaints for when you have more experience and know what kind of funky stuff is possible." +set help_msg_8 "In CTF, it's good to move around and get involved in the action. You get fragged quite a bit but you are also most helpful to your team." +set help_msg_9 "Use the radar to see where your teammates are. Pressing zoom will expand the radar image to give you a better overview." +set help_msg_10 "Most teammessages display waypoints by default. Use those to guide your teammates. You can also see them and the flagcarrier in the radar." +set help_msg_11 "Protect your flagcarrier at all cost! Also save health and armor for him, he might need them more than you!" +set help_msg_12 "You can use the Blaster and most explosive weapons to jump around. Just look 'at your feet' and press fire. If you also jump at the same time, you get even higher." +set help_msg_13 "Be friendly and helpful to other players! Being angry at others' mistakes is understandable, but nobody is perfect. Try to use calm words when telling them how to correct their mistake." +set help_msg_14 "You can use the zoom key with all guns, only the Vortex has it as a extra function on Mouse2 (default key)." +set help_msg_15 "Notice what is happening around you! If your base is empty in CTF, then STAY and defend the flag! Make sure someone defends the flagcarrier or assist him yourself." +set help_msg_16 "You can drop the weapon you currently have with Backspace (default key). You can help your unarmed teammates this way." +set help_msg_17 "Learn to use the team messages! You find them in the Setting/Input menu. Try changing them to keys you can press easier than the defaults." +set help_msg_18 "Gaming should be fun! Try to have a nice time, be helpful, mindful and treat others like you want to be treated." +set help_msg_19 "Visit the official forum on ^1https://forums.xonotic.org/ ${help_cfg_prefix}and feel free to open a thread if you have questions." +set help_msg_20 "If you already have a good weapon, it's a great idea to let your teammates get something better than the Shotgun too!" +set help_msg_21 "Press T to chat with others, press Y for messages to your team only, press TAB to see the scores and U for the chat history (default keys)." +set help_msg_22 "You can use ^1'suggestmap PART_OF_NAME' ${help_cfg_prefix}to make a map come up at the vote screen after a map was played." +set help_msg_23 "The console is accessible through the ~ key or by pressing Shift+ESC. It has many more advanced features, use 'cmdlist' and 'cvarlist' to get a full list of available commands/settings." +set help_msg_24 "The Blaster is a useful tool for gaining speed and jumping around, but it does little damage." +set help_msg_25 "The Shotgun's primary firemode has spread and is more useful at a closer distance, use secondary for the melee attack and slap into other players faces!" +set help_msg_26 "The Machine Gun secondary has a burst fire mode and less spread than the primary mode." +set help_msg_27 "The Mortar is a good all around gun but takes some practice to aim it, because of the projectile's curve." +set help_msg_28 "The Electro has a combo attack. Fire the primary mode at the balls from the secondary mode for a huge and powerful explosion." +set help_msg_29 "The Crylink's primary fire bounces. Both firemodes pull your enemies, making it a great tool to stop a flag carrier." +set help_msg_30 "The Vortex is a powerful sniper gun. Aim carefully!" +set help_msg_31 "The Hagar is underestimated, but very powerful if you aim right. The secondary mode charges up to four rockets and causes devasting damage if you release them." +set help_msg_32 "The Devastator is powerful but slow. Keep Mouse1 pressed to guide the rockets. Secondary firemode makes the rocket(s) explode." +set help_msg_33 "The Arc is a strong lighting beam, which bends slighty if you move your mouse. The secondary firemode is very strong but short. Both firemodes also heal teammates if you shoot at them." +set help_msg_34 "By default, explosions go through walls. Be careful when hiding behind walls!" +set help_msg_35 "Get on IRC to chat with fellow players. Take a look at ^1https://xonotic.org/chat/${help_cfg_prefix}." +set help_msg_36 "Don't drink and frag." +set help_msg_37 "Don't shoot at players who are typing/chatting. You recognize those players by the keyboard symbols above their head." +set help_msg_38 "'gg' is shorthand for 'Good Game', 'gl' means 'Good luck' and 'hf' 'Have fun'." +set help_msg_39 "Players with the prefix '$bot_prefix${help_cfg_prefix}' in their nick are bots on this server. There is also a clan named [BOT]." +set help_msg_40 "You spawn with ^1two ${help_cfg_prefix}weapons. Use the Blaster for much faster movement." +set help_msg_41 "Visit the stats page at ^1https://stats.xonotic.org/ ${help_cfg_prefix}and check out how you are doing in the rankings!" +set help_msg_42 "Start playing 1on1 if you want to learn fast" +set help_msg_43 "Visit ^1https://xonotic.org/pickup/ ${help_cfg_prefix}to get in touch with the experienced players, ask them stuff and play with them!" +set help_msg_44 "Look for servers that have a good ping for you. You can't play this game well with a ping > 100. If there are no servers close enough to you, cooperate with your friends to setup one." +set help_msg_45 "If you want to play the next map you can cast a vote via 'vcall endmatch' in the console. Other players can vote using F1 and F2 (default keys)." +set help_msg_46 "Always watch your back. Do not just run away, fight back as you retreat. Otherwise, you could be shot in the back." +set help_msg_47 "Try to get as much armor and health as you can, but remember your teammates need armor and health too." +set help_msg_48 "Do not attack if you have neither a good weapon, nor health, nor armor." +set help_msg_49 "Standing still makes you an easy target. You can move around the map faster by bunny hopping." +set help_msg_50 "You can use the Blaster to climb up walls. Before trying this, become familiar with the basics of Blaster movement." +set help_msg_51 "You can control your movement in air. Use it to prevent yourself from falling off the map when somebody starts pushing you around." +set help_msg_52 "Use the Blaster, Mortar or Devastator in space maps to push other players off the map. They will enjoy it." +set help_msg_53 "You can turn off automatic weapon changing in the Player menu. If you configure your key bindings, manually switching weapons can be faster and easier." +set help_msg_54 "Choose the right weapon for the job, not just the one that the game automatically puts in your hand." +set help_msg_55 "Enter ^1'lsmaps' ${help_cfg_prefix}in the console to get a list of maps configured on the server." +set help_msg_56 "While you are in the air, release the forward key, press one of the left/right keys and move your mouse in the same direction. This will bend your fly/jump path." +set help_msg_57 "Hold the jump key to keep on jumping (called ^1'bunny-hopping'${help_cfg_prefix}) which will accelerate your speed." +set help_msg_58 "'xonotic.org/guide changed my life' -- ^x777S^x090Є^x088Є^x000Қ^x900⁻ʸ${help_cfg_prefix}, 2018" +set help_msg_count 59 // update this when adding messages - it should be the number of messages (which means last message index + 1) diff --git a/help.cfg b/help.cfg new file mode 100644 index 0000000000..52cfb09f33 --- /dev/null +++ b/help.cfg @@ -0,0 +1,24 @@ +// Simple help message system +// It prints messages with the configured name + +// You can start the help system with the command help_loop but this has been found to annoy players. +// A better way is to put `alias sv_hook_gamestart_all "defer 20 help_next"` into your server.cfg, +// that way you get one message per match. + +// the messages need to be starting from 0 and be consecutive +// for manual use: help_inc switches to the next message, help_doit will print the current message, help_next will do both together + +// settings +set help_cfg_nick "^2Help System^3" "the messages will appear in chat coming from the sever using this name" +set help_cfg_time 5 "the time between two messages in seconds when started using help_loop" +set help_cfg_prefix "^2" "prepended to each message, useful to color the nick and message differently" + +// aliases making up the actual helpsystem +set help_tmp_index -1 // -1 since we first increment, then show it +alias help_say "set help_tmp_oldnick \"$sv_adminnick\"; set sv_adminnick \"$help_cfg_nick\"; say \"$*\"; help_say2" +alias help_say2 "set sv_adminnick \"$help_tmp_oldnick\"" +alias help_doit "sv_cmd rpn /help_tmp_msg help_msg_$help_tmp_index def; help_doit2" +alias help_doit2 "help_say $help_cfg_prefix$help_tmp_msg" +alias help_inc "sv_cmd rpn /help_tmp_index help_tmp_index 1 add $help_msg_count mod def" +alias help_next "help_inc; help_doit" // increment first - if the ruleset changed, the number of tips could have too, this avoids overflow +alias help_loop "help_next; defer $help_cfg_time help_loop" diff --git a/hud_luma.cfg b/hud_luma.cfg index 9572657167..9dcfe0629a 100644 --- a/hud_luma.cfg +++ b/hud_luma.cfg @@ -44,7 +44,7 @@ seta hud_panel_weapons_bg_alpha "" seta hud_panel_weapons_bg_border "" seta hud_panel_weapons_bg_padding "0" seta hud_panel_weapons_accuracy "0" -seta hud_panel_weapons_label "1" +seta hud_panel_weapons_label "2" seta hud_panel_weapons_label_scale "0.3" seta hud_panel_weapons_complainbubble "1" seta hud_panel_weapons_complainbubble_padding "0" diff --git a/languages.txt b/languages.txt index 8c037dc23d..04ea5c6995 100644 --- a/languages.txt +++ b/languages.txt @@ -1,24 +1,25 @@ -ast Asturian "Asturianu" 73% -de German "Deutsch" -de_CH German "Deutsch (Schweiz)" -en English "English" -en_AU English "English (Australia)" 86% -es Spanish "Español" 99% -fr French "Français" -ga Irish "Irish" 32% -it Italian "Italiano" -hu Hungarian "Magyar" 53% -nl Dutch "Nederlands" 67% -pl Polish "Polski" 80% -pt Portuguese "Português" -ro Romanian "Romana" 83% -fi Finnish "Suomi" 31% -zh_CN "Chinese (China)" "中文" 47% -zh_TW "Chinese (Taiwan)" "國語" 67% -ko Korean "한국의" 32% -el Greek "Ελληνική" 32% -be Belarusian "Беларуская" 59% -bg Bulgarian "Български" 66% -ru Russian "Русский" -sr Serbian "Српски" 69% -uk Ukrainian "Українська" 57% \ No newline at end of file +ast "Asturian" "Asturianu" 73% +de "German" "Deutsch" 99% +de_CH "German (Switzerland)" "Deutsch (Schweiz)" 99% +en "English" "English" 100% +en_AU "English (Australia)" "English (Australia)" 86% +es "Spanish" "Español" 99% +fr "French" "Français" 99% +ga "Irish" "Irish" 35% +it "Italian" "Italiano" 99% +hu "Hungarian" "Magyar" 57% +nl "Dutch" "Nederlands" 70% +pl "Polish" "Polski" 81% +pt "Portuguese" "Português" 98% +pt_BR "Portuguese (Brazil)" "Português (Brasil)" 99% +ro "Romanian" "Romana" 83% +fi "Finnish" "Suomi" 33% +el "Greek" "Ελληνική" 34% +be "Belarusian" "Беларуская" 61% +bg "Bulgarian" "Български" 68% +ru "Russian" "Русский" 99% +sr "Serbian" "Српски" 71% +uk "Ukrainian" "Українська" 57% +zh_CN "Chinese (China)" "中文" 63% +zh_TW "Chinese (Taiwan)" "國語" 68% +ko "Korean" "한국의" 33% diff --git a/models/items/a_cells.md3 b/models/items/a_cells.md3 index 04f6afed4c..792d14536b 100644 Binary files a/models/items/a_cells.md3 and b/models/items/a_cells.md3 differ diff --git a/models/items/a_rockets.md3 b/models/items/a_rockets.md3 index 3f1a594d06..3e9a8f82cb 100644 Binary files a/models/items/a_rockets.md3 and b/models/items/a_rockets.md3 differ diff --git a/models/player/gak.iqm_0.txt b/models/player/gak.iqm_0.txt index 27b579027f..efdb0b2b07 100644 --- a/models/player/gak.iqm_0.txt +++ b/models/player/gak.iqm_0.txt @@ -10,3 +10,4 @@ bone_aim1 0.4 spine4 bone_aim2 0.35 bip01 r hand bone_weapon bip01 r hand fixbone 1 +hidden 1 diff --git a/models/player/gakmasked.iqm_0.txt b/models/player/gakmasked.iqm_0.txt index 44843b7058..fbc6b672c3 100644 --- a/models/player/gakmasked.iqm_0.txt +++ b/models/player/gakmasked.iqm_0.txt @@ -1,4 +1,4 @@ -name Gak Masked +name Gak species alien sex Male weight 87 diff --git a/models/player/ignismasked.iqm_0.txt b/models/player/ignismasked.iqm_0.txt index 2610d2b92d..4a35d9099e 100644 --- a/models/player/ignismasked.iqm_0.txt +++ b/models/player/ignismasked.iqm_0.txt @@ -11,3 +11,4 @@ bone_aim2 0.2 upperarm_L bone_aim3 0.35 bip01 r hand bone_weapon bip01 r hand fixbone 1 +hidden 1 diff --git a/models/player/seraphinamasked.iqm_0.txt b/models/player/seraphinamasked.iqm_0.txt index c997d01f36..76ca617ba1 100644 --- a/models/player/seraphinamasked.iqm_0.txt +++ b/models/player/seraphinamasked.iqm_0.txt @@ -10,3 +10,4 @@ bone_aim1 0.4 spine4 bone_aim2 0.35 bip01 r hand bone_weapon bip01 r hand fixbone 1 +hidden 1 diff --git a/models/weapons/g_crylink.md3 b/models/weapons/g_crylink.md3 index b325c9c2be..be0ea1770e 100644 Binary files a/models/weapons/g_crylink.md3 and b/models/weapons/g_crylink.md3 differ diff --git a/models/weapons/g_electro.md3 b/models/weapons/g_electro.md3 index 76c2e8962b..022891a97e 100644 Binary files a/models/weapons/g_electro.md3 and b/models/weapons/g_electro.md3 differ diff --git a/models/weapons/h_crylink.iqm b/models/weapons/h_crylink.iqm index d8de4644f5..62d571bd25 100644 Binary files a/models/weapons/h_crylink.iqm and b/models/weapons/h_crylink.iqm differ diff --git a/models/weapons/h_crylink.iqm.framegroups b/models/weapons/h_crylink.iqm.framegroups index 0a59625b6a..92a05ebf36 100644 --- a/models/weapons/h_crylink.iqm.framegroups +++ b/models/weapons/h_crylink.iqm.framegroups @@ -1,4 +1,9 @@ -1 8 20 0 // fire -9 5 20 0 // fire2 -15 200 20 1 // idle -215 40 20 0 // reload +/* +Generated framegroups file for h_crylink +Used by DarkPlaces to simulate frame groups in DPM models. +*/ + +1 26 30 0 // h_crylink fire +27 26 30 0 // h_crylink fire +53 101 3 1 // h_crylink idle +154 101 3 1 // h_crylink idle diff --git a/models/weapons/h_electro.iqm b/models/weapons/h_electro.iqm index a726b33f24..df0d79506d 100644 Binary files a/models/weapons/h_electro.iqm and b/models/weapons/h_electro.iqm differ diff --git a/models/weapons/h_electro.iqm.framegroups b/models/weapons/h_electro.iqm.framegroups index 0a59625b6a..f6df25a717 100644 --- a/models/weapons/h_electro.iqm.framegroups +++ b/models/weapons/h_electro.iqm.framegroups @@ -1,4 +1,9 @@ -1 8 20 0 // fire -9 5 20 0 // fire2 -15 200 20 1 // idle -215 40 20 0 // reload +/* +Generated framegroups file for h_electro +Used by DarkPlaces to simulate frame groups in DPM models. +*/ + +1 36 30 0 // h_electro fire +37 36 30 0 // h_electro fire +73 101 3 1 // h_electro idle +174 101 3 1 // h_electro idle diff --git a/models/weapons/v_crylink.md3 b/models/weapons/v_crylink.md3 index c3b9f3ca83..dc73d47dc0 100644 Binary files a/models/weapons/v_crylink.md3 and b/models/weapons/v_crylink.md3 differ diff --git a/models/weapons/v_electro.md3 b/models/weapons/v_electro.md3 index 67105bcf6f..2e45a67866 100644 Binary files a/models/weapons/v_electro.md3 and b/models/weapons/v_electro.md3 differ diff --git a/mutators.cfg b/mutators.cfg index 51ae3f140f..afa1782498 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -55,6 +55,7 @@ set g_instagib_friendlypush 1 "allow pushing teammates with the vaporizer primar // overkill // ========== set g_overkill 0 "internal cvar, to enable overkill, use `exec ruleset-overkill.cfg`" +set g_overkill_weapons 0 "Whether to enable overkill weapons outside of overkill ruleset." set g_overkill_powerups_replace 1 set g_overkill_itemwaypoints 1 @@ -198,7 +199,7 @@ set g_nades_nade_edgedamage 90 set g_nades_nade_radius 300 set g_nades_nade_force 650 set g_nades_nade_newton_style 0 "0 is absolute, 1 is relative (takes into account player speed), 2 is something in between" -set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade 8:entrap" +set g_nades_nade_type 1 "Type of the off-hand grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade 8:entrap 9:veil" seta cl_nade_timer 1 "show a visual timer for nades, 1 = only circle, 2 = circle with text" seta cl_nade_type 3 @@ -216,7 +217,7 @@ seta cl_pokenade_type "zombie" // set g_nades_bonus 0 "Enable bonus grenades" set g_nades_bonus_client_select 0 "Allow client side selection of bonus nade type" -set g_nades_bonus_type 2 "Type of the bonus grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade 8:entrap" +set g_nades_bonus_type 2 "Type of the bonus grenade. 1:normal 2:napalm 3:ice 4:translocate 5:spawn 6:heal 7:pokenade 8:entrap 9:veil" set g_nades_bonus_onstrength 1 "Always give bonus grenades to players that have the strength powerup" set g_nades_bonus_max 3 "Maximum number of bonus grenades" set g_nades_bonus_only 0 "Disallow regular nades, only bonus nades can be used" @@ -274,6 +275,10 @@ set g_nades_entrap_speed 0.5 "Running speed while entrapped" set g_nades_entrap_time 10 "Life time of the orb" set g_nades_entrap_radius 500 +// Veil (9) +set g_nades_veil_time 8 "Life time of the orb" +set g_nades_veil_radius 200 + // ============ // camp check @@ -289,7 +294,7 @@ set g_campcheck_distance 1800 // ========== set g_new_toys 0 "Mutator 'New Toys': enable extra fun guns" set g_new_toys_autoreplace 2 "0: never replace, 1: always auto replace guns by available new toys, 2: randomly auto replace guns by available new toys" -set g_new_toys_use_pickupsound 1 "play the 'new toys, new toys!' roflsound when picking up a new toys weapon" +set g_new_toys_use_pickupsound 0 "play the 'new toys, new toys!' roflsound when picking up a new toys weapon" // ======= @@ -307,7 +312,7 @@ set g_buffs_random_location 0 "randomize buff location on start and when reset" set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up" set g_buffs_spawn_count 0 "how many buffs to spawn on the map if none exist already" set g_buffs_replace_powerups 0 "replace powerups on the map with random buffs" -set g_buffs_drop 1 "allow dropping buffs" +set g_buffs_drop 0 "allow dropping buffs" set g_buffs_cooldown_activate 5 "cooldown period when buff is first activated" set g_buffs_cooldown_respawn 3 "cooldown period when buff is reloading" set g_buffs_ammo 1 "ammo buff: infinite ammunition" diff --git a/notifications.cfg b/notifications.cfg index feb9b871b0..07423e3aac 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -235,7 +235,6 @@ seta notification_INFO_ITEM_WEAPON_PRIMORSEC "0" "0 = off, 1 = print to console, seta notification_INFO_ITEM_WEAPON_UNAVAILABLE "0" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JETPACK_NOFUEL "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JOIN_CONNECT "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_JOIN_CONNECT_TEAM "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JOIN_PLAY "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_JOIN_PLAY_TEAM "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_KEEPAWAY_DROPPED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -262,7 +261,7 @@ seta notification_INFO_POWERUP_STRENGTH "1" "0 = off, 1 = print to console, 2 = seta notification_INFO_QUIT_DISCONNECT "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_IDLING "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_QUIT_KICK_SPECTATING "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_QUIT_SPECTATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_QUIT_SPECTATE "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_ABANDONED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_FAIL_RANKED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_RACE_FAIL_UNRANKED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -282,7 +281,7 @@ seta notification_INFO_SUPERSPEC_MISSING_UID "2" "0 = off, 1 = print to console, seta notification_INFO_SUPERWEAPON_PICKUP "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_TEAMCHANGE_LARGERTEAM "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_TEAMCHANGE_NOTALLOWED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -seta notification_INFO_VERSION_BETA "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_VERSION_BETA "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_VERSION_OLD "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_VERSION_OUTDATED "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WATERMARK "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -694,11 +693,11 @@ seta notification_WEAPON_VAPORIZER_MURDER "1" "Enable this multiple notification seta notification_WEAPON_VORTEX_MURDER "1" "Enable this multiple notification" // MSG_CHOICE notifications (count = 28): -seta notification_CHOICE_CTF_CAPTURE_BROKEN "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_CTF_CAPTURE_BROKEN "2" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_CTF_CAPTURE_BROKEN_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" -seta notification_CHOICE_CTF_CAPTURE_TIME "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_CTF_CAPTURE_TIME "2" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_CTF_CAPTURE_TIME_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" -seta notification_CHOICE_CTF_CAPTURE_UNBROKEN "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_CTF_CAPTURE_UNBROKEN "2" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_CTF_CAPTURE_UNBROKEN_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" seta notification_CHOICE_CTF_PICKUP_ENEMY "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_CTF_PICKUP_ENEMY_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" diff --git a/physics.cfg b/physics.cfg index b74f68b3db..823e85bce3 100644 --- a/physics.cfg +++ b/physics.cfg @@ -7,7 +7,7 @@ // Main options // ============== set g_physics_clientselect 0 "allow clients to select their physics set" -set g_physics_clientselect_options "xonotic nexuiz quake warsow defrag quake3 vecxis quake2 bones" +set g_physics_clientselect_options "xonotic nexuiz quake warsow defrag quake3 vecxis quake2 bones overkill" set g_physics_clientselect_default "" "override default physics" // ========= diff --git a/physicsX07.cfg b/physicsX07.cfg index 8ae771f1cf..61354633d5 100644 --- a/physicsX07.cfg +++ b/physicsX07.cfg @@ -1,5 +1,5 @@ g_mod_physics Xonotic -// current Xonotic physics +// Xonotic 0.7 physics sv_gravity 800 sv_maxspeed 360 diff --git a/qcsrc/client/_mod.inc b/qcsrc/client/_mod.inc index 240e07a4ae..ab9184b9b9 100644 --- a/qcsrc/client/_mod.inc +++ b/qcsrc/client/_mod.inc @@ -6,10 +6,10 @@ #include <client/mapvoting.qc> #include <client/miscfunctions.qc> #include <client/player_skeleton.qc> +#include <client/resources.qc> #include <client/shownames.qc> #include <client/teamradar.qc> #include <client/view.qc> -#include <client/wall.qc> #include <client/commands/_mod.inc> #include <client/hud/_mod.inc> diff --git a/qcsrc/client/_mod.qh b/qcsrc/client/_mod.qh index 10482caaa4..971cc01de6 100644 --- a/qcsrc/client/_mod.qh +++ b/qcsrc/client/_mod.qh @@ -6,10 +6,10 @@ #include <client/mapvoting.qh> #include <client/miscfunctions.qh> #include <client/player_skeleton.qh> +#include <client/resources.qh> #include <client/shownames.qh> #include <client/teamradar.qh> #include <client/view.qh> -#include <client/wall.qh> #include <client/commands/_mod.qh> #include <client/hud/_mod.qh> diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index 62b732bec2..0195db43a4 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -1,6 +1,6 @@ #include "announcer.qh" -#include "mutators/events.qh" +#include <client/mutators/_mod.qh> #include <common/notifications/all.qh> #include <common/stats.qh> @@ -129,6 +129,9 @@ void Announcer_Gamestart() void Announcer_Time() { + if(intermission) + return; + float timeleft; if(warmup_stage) { diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index b6b33c3b17..db756608de 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -39,13 +39,13 @@ float autocvar_cl_effects_lightningarc_drift_end; float autocvar_cl_effects_lightningarc_drift_start; float autocvar_cl_effects_lightningarc_segmentlength; bool autocvar_cl_effects_lightningarc_simple; -int autocvar_cl_gentle; +bool autocvar_cl_gentle; int autocvar_cl_gentle_damage; int autocvar_cl_gentle_gibs; int autocvar_cl_gentle_messages; float autocvar_cl_gibs_damageforcescale = 3.5; float autocvar_cl_gibs_lifetime = 14; -float autocvar_cl_gibs_maxcount = 100; +int autocvar_cl_gibs_maxcount = 100; bool autocvar_cl_gibs_sloppy = 1; float autocvar_cl_gibs_ticrate = 0.1; float autocvar_cl_gibs_velocity_random = 1; @@ -344,7 +344,7 @@ float autocvar_hud_panel_weapons_complainbubble_padding; float autocvar_hud_panel_weapons_complainbubble_time; int autocvar_hud_panel_weapons_label; float autocvar_hud_panel_weapons_label_scale = 0.5; -bool autocvar_hud_panel_weapons_onlyowned; +int autocvar_hud_panel_weapons_onlyowned; float autocvar_hud_panel_weapons_noncurrent_alpha = 1; float autocvar_hud_panel_weapons_noncurrent_scale = 1; float autocvar_hud_panel_weapons_selection_radius = 0; @@ -395,12 +395,12 @@ string autocvar_hud_skin; float autocvar_menu_mouse_speed; string autocvar_menu_skin; int autocvar_r_fakelight; -int autocvar_r_fullbright; +bool autocvar_r_fullbright; float autocvar_r_letterbox; string autocvar_scoreboard_columns; bool autocvar_v_flipped; -float autocvar_vid_conheight; -float autocvar_vid_conwidth; +int autocvar_vid_conheight; +int autocvar_vid_conwidth; float autocvar_vid_pixelheight; float autocvar_viewsize; bool autocvar_cl_eventchase_vehicle = 1; @@ -411,6 +411,8 @@ float autocvar_cl_hitsound_min_pitch = 0.75; float autocvar_cl_hitsound_max_pitch = 1.5; float autocvar_cl_hitsound_nom_damage = 25; float autocvar_cl_hitsound_antispam_time; +int autocvar_cl_eventchase_spectated_change = 1; +float autocvar_cl_eventchase_spectated_change_time = 1; int autocvar_cl_eventchase_death = 1; float autocvar_cl_eventchase_distance = 140; bool autocvar_cl_eventchase_frozen = false; diff --git a/qcsrc/client/bgmscript.qc b/qcsrc/client/bgmscript.qc index 2db9e54cf1..02739194a8 100644 --- a/qcsrc/client/bgmscript.qc +++ b/qcsrc/client/bgmscript.qc @@ -11,12 +11,12 @@ float bgmscriptbuf; float bgmscriptbufsize; float bgmscriptbufloaded; -class(BGMScript) .float bgmscriptline; -class(BGMScript) .float bgmscriptline0; -class(BGMScript) .float bgmscriptvolume; -class(BGMScript) .float bgmscripttime; -class(BGMScript) .float bgmscriptstate; -class(BGMScript) .float bgmscriptstatetime; +classfield(BGMScript) .float bgmscriptline; +classfield(BGMScript) .float bgmscriptline0; +classfield(BGMScript) .float bgmscriptvolume; +classfield(BGMScript) .float bgmscripttime; +classfield(BGMScript) .float bgmscriptstate; +classfield(BGMScript) .float bgmscriptstatetime; float GetAttackDecaySustainAmplitude(float a, float d, float s, float t) { @@ -139,8 +139,7 @@ void BGMScript_InitEntity(entity e) if(i >= bgmscriptbufsize) { LOG_INFOF("ERROR: bgmscript does not define %s", e.bgmscript); - strunzone(e.bgmscript); - e.bgmscript = string_null; + strfree(e.bgmscript); } } } diff --git a/qcsrc/client/bgmscript.qh b/qcsrc/client/bgmscript.qh index 18b0d5ee42..a3044d537c 100644 --- a/qcsrc/client/bgmscript.qh +++ b/qcsrc/client/bgmscript.qh @@ -1,13 +1,13 @@ #pragma once entityclass(BGMScript); -class(BGMScript) .string bgmscript; -class(BGMScript) .float bgmscriptattack; -class(BGMScript) .float bgmscriptdecay; -class(BGMScript) .float bgmscriptsustain; -class(BGMScript) .float bgmscriptrelease; +classfield(BGMScript) .string bgmscript; +classfield(BGMScript) .float bgmscriptattack; +classfield(BGMScript) .float bgmscriptdecay; +classfield(BGMScript) .float bgmscriptsustain; +classfield(BGMScript) .float bgmscriptrelease; -class(BGMScript) .float just_toggled; +classfield(BGMScript) .float just_toggled; #ifdef CSQC void BGMScript_InitEntity(entity e); diff --git a/qcsrc/client/commands/cl_cmd.qc b/qcsrc/client/commands/cl_cmd.qc index 8faf0f387b..c4b1ec0411 100644 --- a/qcsrc/client/commands/cl_cmd.qc +++ b/qcsrc/client/commands/cl_cmd.qc @@ -10,11 +10,15 @@ #include "../autocvars.qh" #include "../defs.qh" #include <client/hud/_mod.qh> +#include <client/hud/panel/quickmenu.qh> +#include <client/hud/panel/radar.qh> #include "../main.qh" #include "../mapvoting.qh" #include "../miscfunctions.qh" -#include "../mutators/events.qh" +#include <client/mutators/_mod.qh> + +#include <common/minigames/cl_minigames_hud.qh> #include <common/mapinfo.qh> @@ -39,7 +43,7 @@ void DrawDebugModel(entity this) void LocalCommand_blurtest(int request) { - TC(int, request); + TC(int, request); // Simple command to work with postprocessing temporarily... possibly completely pointless, the glsl shader is used for a real feature now... // Anyway, to enable it, just compile the client with -DBLURTEST and then you can use the command. @@ -75,7 +79,7 @@ void LocalCommand_blurtest(int request) void LocalCommand_boxparticles(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -131,7 +135,7 @@ void LocalCommand_boxparticles(int request, int argc) void LocalCommand_create_scrshot_ent(int request) { - TC(int, request); + TC(int, request); switch (request) { case CMD_REQUEST_COMMAND: @@ -171,7 +175,7 @@ void LocalCommand_create_scrshot_ent(int request) void LocalCommand_debugmodel(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -201,7 +205,7 @@ void LocalCommand_debugmodel(int request, int argc) void LocalCommand_handlevote(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -249,19 +253,9 @@ void LocalCommand_handlevote(int request, int argc) } } -bool QuickMenu_IsOpened(); -void QuickMenu_Close(); -bool QuickMenu_Open(string mode, string submenu, string file); - -bool HUD_MinigameMenu_IsOpened(); -void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger); -void HUD_MinigameMenu_Open(); - -void HUD_Radar_Show_Maximized(bool doshow, bool clickable); - void LocalCommand_hud(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -331,7 +325,8 @@ void LocalCommand_hud(int request, int argc) case "clickradar": { - HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1); + if(!isdemo()) + HUD_Radar_Show_Maximized(!hud_panel_radar_mouse, 1); return; } } @@ -356,7 +351,7 @@ void LocalCommand_hud(int request, int argc) void LocalCommand_localprint(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -383,7 +378,7 @@ void LocalCommand_localprint(int request, int argc) void LocalCommand_mv_download(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -410,7 +405,7 @@ void LocalCommand_mv_download(int request, int argc) void LocalCommand_sendcvar(int request, int argc) { - TC(int, request); TC(int, argc); + TC(int, request); TC(int, argc); switch (request) { case CMD_REQUEST_COMMAND: @@ -418,7 +413,7 @@ void LocalCommand_sendcvar(int request, int argc) if (argv(1)) { // W_FixWeaponOrder will trash argv, so save what we need. - string thiscvar = strzone(argv(1)); + string thiscvar = string_null; strcpy(thiscvar, argv(1)); string s = cvar_string(thiscvar); if (thiscvar == "cl_weaponpriority") @@ -427,7 +422,7 @@ void LocalCommand_sendcvar(int request, int argc) s = W_FixWeaponOrder(W_NumberWeaponOrder(s), 0); localcmd("cmd sentcvar ", thiscvar, " \"", s, "\"\n"); - strunzone(thiscvar); + strfree(thiscvar); return; } } diff --git a/qcsrc/client/commands/cl_cmd.qh b/qcsrc/client/commands/cl_cmd.qh index f1be4315fe..f6f96501ae 100644 --- a/qcsrc/client/commands/cl_cmd.qh +++ b/qcsrc/client/commands/cl_cmd.qh @@ -2,6 +2,7 @@ void Cmd_Scoreboard_SetFields(int); void Cmd_Scoreboard_Help(); +void ConsoleCommand_macro_init(); // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void LocalCommand_macro_write_aliases(int fh); diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index 6499a683e8..961fc77572 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -2,7 +2,7 @@ #include "autocvars.qh" #include "csqcmodel_hooks.qh" #include "miscfunctions.qh" -#include "mutators/events.qh" +#include <client/mutators/_mod.qh> #include "player_skeleton.qh" #include "weapons/projectile.qh" #include <common/animdecide.qh> @@ -18,10 +18,6 @@ .float death_time; .int modelflags; -void CSQCModel_Hook_PreDraw(entity this, bool isplayer); - -.bool isplayermodel; - // FEATURE: LOD .int lodmodelindex0; .int lodmodelindex1; @@ -364,7 +360,7 @@ void CSQCPlayer_AnimDecide_PostUpdate(entity this, bool isnew) } int CSQCPlayer_FallbackFrame(entity this, int f) { - TC(int, f); + TC(int, f); if(frameduration(this.modelindex, f) > 0) return f; // goooooood if(frameduration(this.modelindex, 1) <= 0) @@ -417,7 +413,7 @@ void CSQCModel_AutoTagIndex_Apply(entity this) // recursive predraw call to fix issues with forcemodels and LOD if bone indexes mismatch if(this.tag_entity.classname == "csqcmodel") { - CSQCModel_Hook_PreDraw(this.tag_entity, (this.tag_entity.entnum >= 1 && this.tag_entity.entnum <= maxclients)); + CSQCModel_Hook_PreDraw(this.tag_entity, (this.tag_entity.isplayermodel & ISPLAYER_CLIENT)); } if(this.tag_entity.modelindex != this.tag_entity_lastmodelindex) @@ -545,7 +541,7 @@ void CSQCModel_Effects_Apply(entity this) if(this.csqcmodel_modelflags & MF_ROTATE) { this.renderflags |= RF_USEAXIS; - makevectors(this.angles + '0 100 0' * fmod(time, 3.6)); + MAKEVECTORS(makevectors, this.angles + '0 100 0' * fmod(time, 3.6), v_forward, v_right, v_up); } if(this.csqcmodel_modelflags & MF_TRACER) tref = EFFECT_TR_WIZSPIKE.m_id; @@ -602,7 +598,7 @@ void CSQCModel_Hook_PreDraw(entity this, bool isplayer) return; this.csqcmodel_predraw_run = framecount; - if(!this.modelindex || this.model == "null") + if(!this.modelindex || this.model == "null" || this.alpha < 0) { this.drawmask = 0; return; @@ -610,9 +606,9 @@ void CSQCModel_Hook_PreDraw(entity this, bool isplayer) else this.drawmask = MASK_NORMAL; - if(this.isplayermodel) // this checks if it's a player MODEL! + if(this.isplayermodel && this.drawmask) // this checks if it's a player MODEL! { - CSQCPlayer_ModelAppearance_Apply(this, this.entnum == player_localnum + 1); + CSQCPlayer_ModelAppearance_Apply(this, (this.isplayermodel & ISPLAYER_LOCAL)); CSQCPlayer_LOD_Apply(this); if(!isplayer) @@ -712,7 +708,9 @@ void CSQCModel_Hook_PreUpdate(entity this, bool isnew, bool isplayer, bool isloc void CSQCModel_Hook_PostUpdate(entity this, bool isnew, bool isplayer, bool islocalplayer) { // is it a player model? (shared state) - this.isplayermodel = (substring(this.model, 0, 14) == "models/player/" || substring(this.model, 0, 17) == "models/ok_player/" || (substring(this.model, 0, 16) == "models/monsters/" && (this.entnum >= 1 && this.entnum <= maxclients))); + bool is_playermodel = (substring(this.model, 0, 14) == "models/player/" || substring(this.model, 0, 17) == "models/ok_player/" || + (substring(this.model, 0, 16) == "models/monsters/" && (this.isplayermodel & BIT(1)))); + this.isplayermodel = BITSET(this.isplayermodel, ISPLAYER_MODEL, is_playermodel); // save values set by server if(this.isplayermodel) diff --git a/qcsrc/client/csqcmodel_hooks.qh b/qcsrc/client/csqcmodel_hooks.qh index 56a3fb4a54..f952d0b0a0 100644 --- a/qcsrc/client/csqcmodel_hooks.qh +++ b/qcsrc/client/csqcmodel_hooks.qh @@ -23,4 +23,8 @@ const int MF_TRACER3 = BIT(7); // purple trail .int csqcmodel_modelflags; .int csqcmodel_traileffect; +.int isplayermodel; + void CSQCModel_Effects_Apply(entity this); + +void CSQCModel_Hook_PreDraw(entity this, bool isplayer); diff --git a/qcsrc/client/defs.qh b/qcsrc/client/defs.qh index 9a5335eff0..5204e8f36d 100644 --- a/qcsrc/client/defs.qh +++ b/qcsrc/client/defs.qh @@ -5,7 +5,7 @@ float scoreboard_showscores; float scoreboard_showaccuracy; .string message; -.int renderflags; +.float renderflags; // float coop; // float deathmatch; @@ -16,7 +16,6 @@ float dmg_take; // Darkplaces Render Modifications #if 0 .float alpha; -.float renderflags; .vector colormod; .float scale; #endif @@ -78,6 +77,7 @@ float nb_pb_period; // 0 - playing // >0 - id of spectated player float spectatee_status; +float spectatee_status_changed_time; // short mapname string shortmapname; diff --git a/qcsrc/client/hud/hud.qc b/qcsrc/client/hud/hud.qc index 9bcdd3d662..bee8d0568e 100644 --- a/qcsrc/client/hud/hud.qc +++ b/qcsrc/client/hud/hud.qc @@ -2,6 +2,7 @@ #include <client/defs.qh> #include <client/miscfunctions.qh> +#include <client/view.qh> #include "panel/scoreboard.qh" #include "hud_config.qh" #include "../mapvoting.qh" @@ -13,10 +14,12 @@ #include <common/items/_mod.qh> #include <common/mapinfo.qh> #include <common/vehicles/all.qh> +#include <common/vehicles/vehicle/bumblebee.qh> #include <common/mutators/mutator/waypoints/all.qh> #include <common/stats.qh> #include <lib/csqcmodel/cl_player.qh> -#include <server/mutators/mutator/gamemode_ctf.qh> // TODO: remove +#include <lib/csqcmodel/cl_model.qh> +#include <common/gamemodes/_mod.qh> /* @@ -72,14 +75,14 @@ vector HUD_Get_Num_Color (float hp, float maxvalue) float HUD_GetRowCount(int item_count, vector size, float item_aspect) { - TC(int, item_count); + TC(int, item_count); float aspect = size_y / size_x; return bound(1, floor((sqrt(4 * item_aspect * aspect * item_count + aspect * aspect) + aspect + 0.5) / 2), item_count); } vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect) { - TC(int, item_count); + TC(int, item_count); float columns, rows; float ratio, best_ratio = 0; float best_columns = 1, best_rows = 1; @@ -177,7 +180,7 @@ void HUD_Panel_LoadCvars() //basically the same code of draw_ButtonPicture and draw_VertButtonPicture for the menu void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag) { - TC(bool, vertical); TC(int, drawflag); + TC(bool, vertical); TC(int, drawflag); if(!length_ratio || !theAlpha) return; if(length_ratio > 1) @@ -284,7 +287,7 @@ void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, flo void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag) { - TC(int, drawflag); + TC(int, drawflag); if(!theAlpha) return; @@ -305,7 +308,7 @@ void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theA void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp) { - TC(bool, vertical); TC(int, icon_right_align); + TC(bool, vertical); TC(int, icon_right_align); vector newPos = '0 0 0', newSize = '0 0 0'; vector picpos, numpos; @@ -385,7 +388,7 @@ void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string ic void DrawNumIcon(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha) { - TC(bool, vertical); TC(int, icon_right_align); + TC(bool, vertical); TC(int, icon_right_align); DrawNumIcon_expanding(myPos, mySize, theTime, icon, vertical, icon_right_align, color, theAlpha, 0); } @@ -395,8 +398,6 @@ Main HUD system ================== */ -void CSQC_BUMBLE_GUN_HUD(); - void HUD_Vehicle() { if(autocvar__hud_configure) return; @@ -485,8 +486,6 @@ bool Hud_Shake_Update() return true; } -entity CSQCModel_server2csqc(int i); -void calc_followmodel_ofs(entity view); void Hud_Dynamic_Frame() { vector ofs = '0 0 0'; @@ -562,6 +561,26 @@ void Hud_Dynamic_Frame() HUD_Scale_Disable(); } +bool HUD_WouldShowCursor() +{ + if(autocvar__hud_configure) + return true; + if(hud_panel_radar_mouse) + return true; + if(mv_active) + return true; + //entity local_player = ((csqcplayer) ? csqcplayer : CSQCModel_server2csqc(player_localentnum - 1)); // TODO: doesn't use regular cursor handling + //if(local_player.viewloc && (local_player.viewloc.spawnflags & VIEWLOC_FREEAIM)) + //return true; + if(HUD_Radar_Clickable()) + return true; + if(HUD_MinigameMenu_IsOpened()) + return true; + if(QuickMenu_IsOpened()) + return true; + return false; +} + void HUD_Main() { int i; @@ -579,12 +598,8 @@ void HUD_Main() // Drawing stuff if (hud_skin_prev != autocvar_hud_skin) { - if (hud_skin_path) - strunzone(hud_skin_path); - hud_skin_path = strzone(strcat("gfx/hud/", autocvar_hud_skin)); - if (hud_skin_prev) - strunzone(hud_skin_prev); - hud_skin_prev = strzone(autocvar_hud_skin); + strcpy(hud_skin_path, strcat("gfx/hud/", autocvar_hud_skin)); + strcpy(hud_skin_prev, autocvar_hud_skin); } // draw the dock @@ -659,9 +674,7 @@ void HUD_Main() LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder"); cvar_set("_hud_panelorder", s); - if(hud_panelorder_prev) - strunzone(hud_panelorder_prev); - hud_panelorder_prev = strzone(s); + strcpy(hud_panelorder_prev, s); //now properly set panel_order tokenize_console(s); @@ -683,10 +696,15 @@ void HUD_Main() HUD_Panel_Draw(HUD_PANEL(RADAR)); if(autocvar__con_chat_maximized) HUD_Panel_Draw(HUD_PANEL(CHAT)); - if(hud_panel_quickmenu) + if (QuickMenu_IsOpened()) HUD_Panel_Draw(HUD_PANEL(QUICKMENU)); HUD_Panel_Draw(HUD_PANEL(SCOREBOARD)); + bool cursor_active_prev = cursor_active; + cursor_active = HUD_WouldShowCursor(); + if (cursor_active_prev != cursor_active && autocvar_hud_cursormode) + setcursormode(cursor_active); + if (intermission == 2) HUD_Reset(); diff --git a/qcsrc/client/hud/hud.qh b/qcsrc/client/hud/hud.qh index d070dce380..d2349a6a5c 100644 --- a/qcsrc/client/hud/hud.qh +++ b/qcsrc/client/hud/hud.qh @@ -7,6 +7,8 @@ void Hud_Dynamic_Frame(); bool HUD_Radar_Clickable(); void HUD_Radar_Mouse(); +bool HUD_WouldShowCursor(); +bool QuickMenu_IsOpened(); REGISTRY(hud_panels, BITS(6)) #define hud_panels_from(i) _hud_panels_from(i, NULL) @@ -73,8 +75,6 @@ int vote_prev; // previous state of vote_active to check for a change float vote_alpha; float vote_change; // "time" when vote_active changed -float hud_panel_quickmenu; - vector mousepos; vector panel_click_distance; // mouse cursor distance from the top left corner of the panel (saved only upon a click) vector panel_click_resizeorigin; // coordinates for opposite point when resizing @@ -86,8 +86,8 @@ const float BORDER_MULTIPLIER = 4; float scoreboard_bottom; int weapon_accuracy[Weapons_MAX]; -int complain_weapon; -float complain_weapon_type; +entity complain_weapon; +int complain_weapon_type; float complain_weapon_time; PlayerScoreField ps_primary, ps_secondary; @@ -120,18 +120,18 @@ vector panel_size_copied; entity panel; entityclass(HUDPanel); -class(HUDPanel) .string panel_name; -class(HUDPanel) .int panel_id; -class(HUDPanel) .vector current_panel_pos; -class(HUDPanel) .vector current_panel_size; -class(HUDPanel) .string current_panel_bg; -class(HUDPanel) .float current_panel_bg_alpha; -class(HUDPanel) .float current_panel_bg_border; -class(HUDPanel) .vector current_panel_bg_color; -class(HUDPanel) .float current_panel_bg_color_team; -class(HUDPanel) .float current_panel_bg_padding; -class(HUDPanel) .float current_panel_fg_alpha; -class(HUDPanel) .float update_time; +classfield(HUDPanel) .string panel_name; +classfield(HUDPanel) .int panel_id; +classfield(HUDPanel) .vector current_panel_pos; +classfield(HUDPanel) .vector current_panel_size; +classfield(HUDPanel) .string current_panel_bg; +classfield(HUDPanel) .float current_panel_bg_alpha; +classfield(HUDPanel) .float current_panel_bg_border; +classfield(HUDPanel) .vector current_panel_bg_color; +classfield(HUDPanel) .float current_panel_bg_color_team; +classfield(HUDPanel) .float current_panel_bg_padding; +classfield(HUDPanel) .float current_panel_fg_alpha; +classfield(HUDPanel) .float update_time; float panel_enabled; vector panel_pos; vector panel_size; @@ -148,7 +148,7 @@ string panel_bg_border_str; float panel_bg_padding; string panel_bg_padding_str; -class(HUDPanel) .void() panel_draw; +classfield(HUDPanel) .void() panel_draw; // chat panel can be reduced / moved while the mapvote is active // let know the mapvote panel about chat pos and size @@ -175,8 +175,6 @@ vector hud_shift; vector hud_shift_current = '0 0 0'; vector hud_scale_center; -float stringwidth_colors(string s, vector theSize); -float stringwidth_nocolors(string s, vector theSize); void HUD_Panel_DrawProgressBar(vector theOrigin, vector theSize, string pic, float length_ratio, bool vertical, float baralign, vector theColor, float theAlpha, int drawflag); .int panel_showflags; @@ -230,7 +228,7 @@ REGISTER_HUD_PANEL(MINIGAMEHELP, HUD_MinigameHelp, PANEL_CONFIG_NO REGISTER_HUD_PANEL(MINIGAMEMENU, HUD_MinigameMenu, PANEL_CONFIG_NO , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // MINIGAMEMENU REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE ) // MAPVOTE REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // ITEMSTIME -REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, PANEL_CONFIG_MAIN , PANEL_SHOW_MAINGAME ) // QUICKMENU +REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, PANEL_CONFIG_MAIN , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME ) // QUICKMENU REGISTER_HUD_PANEL(SCOREBOARD, Scoreboard_Draw, PANEL_CONFIG_NO , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // SCOREBOARD // always add new panels to the end of list @@ -264,9 +262,7 @@ REGISTER_HUD_PANEL(SCOREBOARD, Scoreboard_Draw, PANEL_CONFIG_NO } \ } \ } \ - if (panel.current_panel_bg) \ - strunzone(panel.current_panel_bg); \ - panel.current_panel_bg = strzone(panel_bg); \ + strcpy(panel.current_panel_bg, panel_bg); \ } MACRO_END // Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector. diff --git a/qcsrc/client/hud/hud_config.qc b/qcsrc/client/hud/hud_config.qc index ec07ee4095..3043e6e686 100644 --- a/qcsrc/client/hud/hud_config.qc +++ b/qcsrc/client/hud/hud_config.qc @@ -5,6 +5,7 @@ #include <client/autocvars.qh> #include <client/defs.qh> #include <client/miscfunctions.qh> +#include <client/view.qh> #define HUD_Write(s) fputs(fh, s) #define HUD_Write_Cvar(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n")) @@ -253,6 +254,7 @@ void HUD_Configure_Exit_Force() hud_configure_menu_open = 0; localcmd("togglemenu\n"); } + cursor_type = CURSOR_NORMAL; cvar_set("_hud_configure", "0"); } @@ -660,12 +662,10 @@ void HUD_Panel_Arrow_Action(float nPrimary) } } -void HUD_Panel_EnableMenu(); entity tab_panels[hud_panels_MAX]; entity tab_panel; vector tab_panel_pos; float tab_backward; -void HUD_Panel_FirstInDrawQ(float id); void reset_tab_panels() { for (int i = 0; i < hud_panels_COUNT; ++i) @@ -762,7 +762,7 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary) if (bInputType == 1) return true; if (!hud_configure_menu_open) - cvar_set("_hud_configure", "0"); + HUD_Configure_Exit_Force(); } else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel { @@ -953,7 +953,7 @@ LABEL(find_tab_panel) return true; } -float HUD_Panel_Check_Mouse_Pos(float allow_move) +int HUD_Panel_Check_Mouse_Pos(bool allow_move) { int i, j = 0; while(j < hud_panels_COUNT) @@ -970,30 +970,30 @@ float HUD_Panel_Check_Mouse_Pos(float allow_move) // move if(allow_move && mousepos.x > panel_pos.x && mousepos.y > panel_pos.y && mousepos.x < panel_pos.x + panel_size.x && mousepos.y < panel_pos.y + panel_size.y) { - return 1; + return CURSOR_MOVE; } // resize from topleft border else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) { - return 2; + return CURSOR_RESIZE; } // resize from topright border else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y - border && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + 0.5 * panel_size.y) { - return 3; + return CURSOR_RESIZE2; } // resize from bottomleft border else if(mousepos.x >= panel_pos.x - border && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + 0.5 * panel_size.x && mousepos.y <= panel_pos.y + panel_size.y + border) { - return 3; + return CURSOR_RESIZE2; } // resize from bottomright border else if(mousepos.x >= panel_pos.x + 0.5 * panel_size.x && mousepos.y >= panel_pos.y + 0.5 * panel_size.y && mousepos.x <= panel_pos.x + panel_size.x + border && mousepos.y <= panel_pos.y + panel_size.y + border) { - return 2; + return CURSOR_RESIZE; } } - return 0; + return CURSOR_NORMAL; } // move a panel to the beginning of the panel order array (which means it gets drawn last, on top of everything else) @@ -1029,9 +1029,7 @@ void HUD_Panel_FirstInDrawQ(float id) s = strcat(s, ftos(panel_order[i]), " "); } cvar_set("_hud_panelorder", s); - if(hud_panelorder_prev) - strunzone(hud_panelorder_prev); - hud_panelorder_prev = strzone(autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here + strcpy(hud_panelorder_prev, autocvar__hud_panelorder); // prevent HUD_Main from doing useless update, we already updated here } void HUD_Panel_Highlight(float allow_move) @@ -1115,15 +1113,11 @@ void HUD_Panel_EnableMenu() hud_configure_menu_open = 2; localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n"); } -float mouse_over_panel; void HUD_Panel_Mouse() { if(autocvar__menu_alpha == 1) return; - if (!autocvar_hud_cursormode) - update_mousepos(); - if(mouseClicked) { if(prevMouseClicked == 0) @@ -1154,7 +1148,7 @@ void HUD_Panel_Mouse() prevMouseClickedTime = time; prevMouseClickedPos = mousepos; } - mouse_over_panel = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1); + cursor_type = HUD_Panel_Check_Mouse_Pos(mouseClicked & S_MOUSE1); } } else @@ -1207,25 +1201,12 @@ void HUD_Panel_Mouse() if(prevMouseClicked) highlightedAction = 0; if(hud_configure_menu_open == 2) - mouse_over_panel = 0; + cursor_type = CURSOR_NORMAL; else - mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true); - if (mouse_over_panel && !tab_panel) + cursor_type = HUD_Panel_Check_Mouse_Pos(true); + if (cursor_type != CURSOR_NORMAL && !tab_panel) // mouse over a panel? drawfill(panel_pos - '1 1 0' * panel_bg_border, panel_size + '2 2 0' * panel_bg_border, '1 1 1', .1, DRAWFLAG_NORMAL); } - // draw cursor after performing move/resize to have the panel pos/size updated before mouse_over_panel - float cursor_alpha = 1 - autocvar__menu_alpha; - - if(!mouse_over_panel) - draw_cursor_normal(mousepos, '1 1 1', cursor_alpha); - else if(mouse_over_panel == 1) - draw_cursor(mousepos, '0.5 0.5 0', "/cursor_move", '1 1 1', cursor_alpha); - else if(mouse_over_panel == 2) - draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize", '1 1 1', cursor_alpha); - else - draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize2", '1 1 1', cursor_alpha); - - prevMouseClicked = mouseClicked; } void HUD_Configure_DrawGrid() { @@ -1262,8 +1243,6 @@ void HUD_Configure_Frame() if(!hud_configure_prev) { - if(autocvar_hud_cursormode) - setcursormode(1); hudShiftState = 0; for(i = hud_panels_COUNT - 1; i >= 0; --i) hud_panels_from(panel_order[i]).update_time = time; @@ -1283,8 +1262,6 @@ void HUD_Configure_Frame() { if(hud_configure_menu_open) hud_configure_menu_open = 0; - if(autocvar_hud_cursormode) - setcursormode(0); hud_dynamic_shake_factor = -1; } } diff --git a/qcsrc/client/hud/hud_config.qh b/qcsrc/client/hud/hud_config.qh index 6ab64f6aed..d91fe370e1 100644 --- a/qcsrc/client/hud/hud_config.qh +++ b/qcsrc/client/hud/hud_config.qh @@ -23,3 +23,7 @@ void HUD_Configure_Frame(); void HUD_Configure_PostDraw(); float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary); + +void HUD_Panel_EnableMenu(); + +void HUD_Panel_FirstInDrawQ(float id); diff --git a/qcsrc/client/hud/panel.qh b/qcsrc/client/hud/panel.qh index 66ca0772b6..1ea23ae2e7 100644 --- a/qcsrc/client/hud/panel.qh +++ b/qcsrc/client/hud/panel.qh @@ -2,4 +2,4 @@ #include "hud.qh" #include "hud_config.qh" -#include <client/mutators/events.qh> +#include <client/mutators/_mod.qh> diff --git a/qcsrc/client/hud/panel/ammo.qc b/qcsrc/client/hud/panel/ammo.qc index 0636f3f2e9..ce700586ca 100644 --- a/qcsrc/client/hud/panel/ammo.qc +++ b/qcsrc/client/hud/panel/ammo.qc @@ -6,6 +6,7 @@ #include <client/view.qh> #include <common/t_items.qh> #include <common/wepent.qh> +#include <common/mutators/mutator/nades/nades.qh> // Ammo (#1) @@ -19,11 +20,9 @@ void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector col autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); } -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) { - TC(bool, isCurrent); TC(bool, isInfinite); + TC(bool, isCurrent); TC(bool, isInfinite); if(ammoType == RESOURCE_NONE) return; diff --git a/qcsrc/client/hud/panel/centerprint.qc b/qcsrc/client/hud/panel/centerprint.qc index a92bdc6923..90a496e0d1 100644 --- a/qcsrc/client/hud/panel/centerprint.qc +++ b/qcsrc/client/hud/panel/centerprint.qc @@ -20,7 +20,7 @@ bool centerprint_showing; void centerprint_generic(int new_id, string strMessage, float duration, int countdown_num) { - TC(int, new_id); TC(int, countdown_num); + TC(int, new_id); TC(int, countdown_num); //printf("centerprint_generic(%d, '%s^7', %d, %d);\n", new_id, strMessage, duration, countdown_num); int i, j; @@ -75,9 +75,7 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun cpm_index = CENTERPRINT_MAX_MSGS - 1; j = cpm_index; } - if(centerprint_messages[j]) - strunzone(centerprint_messages[j]); - centerprint_messages[j] = strzone(strMessage); + strcpy(centerprint_messages[j], strMessage); centerprint_msgID[j] = new_id; if (duration < 0) { @@ -96,7 +94,7 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun void centerprint_kill(int id) { - TC(int, id); + TC(int, id); centerprint_generic(id, "", 0, 0); } @@ -112,9 +110,7 @@ void reset_centerprint_messages() centerprint_expire_time[i] = 0; centerprint_time[i] = 1; centerprint_msgID[i] = 0; - if(centerprint_messages[i]) - strunzone(centerprint_messages[i]); - centerprint_messages[i] = string_null; + strfree(centerprint_messages[i]); } } float hud_configure_cp_generation_time; diff --git a/qcsrc/client/hud/panel/chat.qc b/qcsrc/client/hud/panel/chat.qc index 74d4b6d0f4..5f309d0b02 100644 --- a/qcsrc/client/hud/panel/chat.qc +++ b/qcsrc/client/hud/panel/chat.qc @@ -48,9 +48,7 @@ void HUD_Chat() panel_bg = strcat(hud_skin_path, "/border_default"); if(precache_pic(panel_bg) == "") panel_bg = "gfx/hud/default/border_default"; - if(panel.current_panel_bg) - strunzone(panel.current_panel_bg); - panel.current_panel_bg = strzone(panel_bg); + strcpy(panel.current_panel_bg, panel_bg); chat_panel_modified = true; } panel_bg_alpha = max(0.75, panel_bg_alpha); diff --git a/qcsrc/client/hud/panel/infomessages.qc b/qcsrc/client/hud/panel/infomessages.qc index 1e5a0c9f2f..f63ffb1dea 100644 --- a/qcsrc/client/hud/panel/infomessages.qc +++ b/qcsrc/client/hud/panel/infomessages.qc @@ -33,7 +33,6 @@ int img_select(int group_id) return img_cur_msg[group_id]; } -float stringwidth_colors(string s, vector theSize); vector InfoMessages_drawstring(string s, vector pos, vector sz, float a, vector fontsize) { getWrappedLine_remaining = s; @@ -119,7 +118,7 @@ void HUD_InfoMessages() MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize); - if(!warmup_stage && gametype == MAPINFO_TYPE_LMS) + if(!warmup_stage && ISGAMETYPE(LMS)) { entity sk; sk = playerslots[player_localnum]; diff --git a/qcsrc/client/hud/panel/modicons.qc b/qcsrc/client/hud/panel/modicons.qc index 65682b3ec7..4d1691a7fd 100644 --- a/qcsrc/client/hud/panel/modicons.qc +++ b/qcsrc/client/hud/panel/modicons.qc @@ -5,7 +5,7 @@ #include <common/mapinfo.qh> #include <common/ent_cs.qh> #include <common/scores.qh> -#include <server/mutators/mutator/gamemode_ctf.qh> // TODO: remove +#include <common/gamemodes/_mod.qh> // Mod icons (#10) @@ -13,7 +13,7 @@ bool mod_active; // is there any active mod icon? void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) { - TC(int, layout); TC(int, i); + TC(int, layout); TC(int, i); int stat = -1; string pic = ""; vector color = '0 0 0'; @@ -54,9 +54,9 @@ void HUD_Mod_CA(vector myPos, vector mySize) mod_active = 1; // required in each mod function that always shows something int layout; - if(gametype == MAPINFO_TYPE_CA) + if(ISGAMETYPE(CA)) layout = autocvar_hud_panel_modicons_ca_layout; - else //if(gametype == MAPINFO_TYPE_FREEZETAG) + else //if(ISGAMETYPE(FREEZETAG)) layout = autocvar_hud_panel_modicons_freezetag_layout; int rows, columns; float aspect_ratio; @@ -528,7 +528,7 @@ void HUD_Mod_Race(vector pos, vector mySize) // clientside personal record string rr; - if(gametype == MAPINFO_TYPE_CTS) + if(ISGAMETYPE(CTS)) rr = CTS_RECORD; else rr = RACE_RECORD; @@ -583,9 +583,7 @@ void HUD_Mod_Race(vector pos, vector mySize) if (race_status != race_status_prev || race_status_name != race_status_name_prev) { race_status_time = time + 5; race_status_prev = race_status; - if (race_status_name_prev) - strunzone(race_status_name_prev); - race_status_name_prev = strzone(race_status_name); + strcpy(race_status_name_prev, race_status_name); } // race "awards" @@ -621,18 +619,14 @@ void HUD_Mod_Race(vector pos, vector mySize) if (race_status_time - time <= 0) { race_status_prev = -1; race_status = -1; - if(race_status_name) - strunzone(race_status_name); - race_status_name = string_null; - if(race_status_name_prev) - strunzone(race_status_name_prev); - race_status_name_prev = string_null; + strfree(race_status_name); + strfree(race_status_name_prev); } } void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) { - TC(int, layout); TC(int, i); + TC(int, layout); TC(int, i); float stat = -1; string pic = ""; vector color = '0 0 0'; diff --git a/qcsrc/client/hud/panel/physics.qc b/qcsrc/client/hud/panel/physics.qc index a6c65183d4..aa77690a6a 100644 --- a/qcsrc/client/hud/panel/physics.qc +++ b/qcsrc/client/hud/panel/physics.qc @@ -18,7 +18,7 @@ void HUD_Physics() { if(!autocvar_hud_panel_physics) return; if(spectatee_status == -1 && (autocvar_hud_panel_physics == 1 || autocvar_hud_panel_physics == 3)) return; - if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(autocvar_hud_panel_physics == 3 && !(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; } HUD_Panel_LoadCvars(); diff --git a/qcsrc/client/hud/panel/powerups.qc b/qcsrc/client/hud/panel/powerups.qc index dd574cf9b0..49b7a97018 100644 --- a/qcsrc/client/hud/panel/powerups.qc +++ b/qcsrc/client/hud/panel/powerups.qc @@ -47,7 +47,7 @@ void addPowerupItem(string name, string icon, vector color, float currentTime, f int getPowerupItemAlign(int align, int column, int row, int columns, int rows, bool isVertical) { - TC(int, align); TC(int, column); TC(int, row); TC(int, columns); TC(int, rows); TC(bool, isVertical); + TC(int, align); TC(int, column); TC(int, row); TC(int, columns); TC(int, rows); TC(bool, isVertical); if(align < 2) return align; @@ -103,7 +103,7 @@ void HUD_Powerups() addPowerupItem("Strength", "strength", autocvar_hud_progressbar_strength_color, strengthTime, 30); if(shieldTime) addPowerupItem("Shield", "shield", autocvar_hud_progressbar_shield_color, shieldTime, 30); - if(superTime) + if(superTime && !(allItems & IT_UNLIMITED_SUPERWEAPONS)) addPowerupItem("Superweapons", "superweapons", autocvar_hud_progressbar_superweapons_color, superTime, 30); MUTATOR_CALLHOOK(HUD_Powerups_add); diff --git a/qcsrc/client/hud/panel/quickmenu.qc b/qcsrc/client/hud/panel/quickmenu.qc index 9af8673dab..29907505bc 100644 --- a/qcsrc/client/hud/panel/quickmenu.qc +++ b/qcsrc/client/hud/panel/quickmenu.qc @@ -4,6 +4,7 @@ #include <client/defs.qh> #include <client/miscfunctions.qh> #include <common/ent_cs.qh> +#include <common/minigames/cl_minigames.qh> #include <client/hud/_mod.qh> #include <client/mapvoting.qh> @@ -43,32 +44,35 @@ float QuickMenu_TimeOut; // if s1 is not empty s will be displayed as command otherwise as submenu void QuickMenu_Page_LoadEntry(int i, string s, string s1) { - TC(int, i); - //printf("^xc80 entry %d: %s, %s\n", i, s, s1); - if (QuickMenu_Page_Description[i]) - strunzone(QuickMenu_Page_Description[i]); - QuickMenu_Page_Description[i] = strzone(s); - if (QuickMenu_Page_Command[i]) - strunzone(QuickMenu_Page_Command[i]); - QuickMenu_Page_Command[i] = strzone(s1); + TC(int, i); + //LOG_INFOF("^xc80 entry %d: %s, %s\n", i, s, s1); + strcpy(QuickMenu_Page_Description[i], s); + strcpy(QuickMenu_Page_Command[i], s1); } void QuickMenu_Page_ClearEntry(int i) { - TC(int, i); - if (QuickMenu_Page_Description[i]) - strunzone(QuickMenu_Page_Description[i]); - QuickMenu_Page_Description[i] = string_null; - if (QuickMenu_Page_Command[i]) - strunzone(QuickMenu_Page_Command[i]); - QuickMenu_Page_Command[i] = string_null; + TC(int, i); + strfree(QuickMenu_Page_Description[i]); + strfree(QuickMenu_Page_Command[i]); QuickMenu_Page_Command_Type[i] = 0; } -float QuickMenu_Page_Load(string target_submenu, float new_page); -void QuickMenu_Default(string submenu); +bool HUD_QuickMenu_Forbidden() +{ + return (mv_active + || (hud_configure_prev && hud_configure_prev != -1) + || HUD_MinigameMenu_IsOpened() + || (QuickMenu_TimeOut && time > QuickMenu_TimeOut)); +} + +// returns true if succeded, false otherwise bool QuickMenu_Open(string mode, string submenu, string file) { + QuickMenu_TimeOut = 0; + if (HUD_QuickMenu_Forbidden()) + return false; + int fh = -1; string s; @@ -115,7 +119,7 @@ bool QuickMenu_Open(string mode, string submenu, string file) while((s = fgets(fh)) && QuickMenu_Buffer_Size < QUICKMENU_BUFFER_MAXENTRIES) { // first skip invalid entries, so we don't check them anymore - float argc; + int argc; argc = tokenize_console(s); if(argc == 0 || argv(0) == "") continue; @@ -176,9 +180,6 @@ bool QuickMenu_Open(string mode, string submenu, string file) else QuickMenu_Page_Load("", 0); - hud_panel_quickmenu = 1; - if(autocvar_hud_cursormode) - setcursormode(1); hudShiftState = 0; QuickMenu_TimeOut = ((autocvar_hud_panel_quickmenu_time > 0) ? time + autocvar_hud_panel_quickmenu_time : 0); @@ -197,38 +198,30 @@ void QuickMenu_Buffer_Close() void QuickMenu_Close() { - if (QuickMenu_CurrentSubMenu) - strunzone(QuickMenu_CurrentSubMenu); - QuickMenu_CurrentSubMenu = string_null; + strfree(QuickMenu_CurrentSubMenu); int i; for (i = 0; i < QUICKMENU_MAXLINES; ++i) QuickMenu_Page_ClearEntry(i); QuickMenu_Page_Entries = 0; - hud_panel_quickmenu = 0; mouseClicked = 0; prevMouseClicked = 0; QuickMenu_Buffer_Close(); - - if(autocvar_hud_cursormode) - if(!mv_active) - setcursormode(0); } // It assumes submenu open tag is already detected void QuickMenu_skip_submenu(string submenu) { - string s, z_submenu; - z_submenu = strzone(submenu); + string z_submenu = string_null; strcpy(z_submenu, submenu); for(++QuickMenu_Buffer_Index ; QuickMenu_Buffer_Index < QuickMenu_Buffer_Size; ++QuickMenu_Buffer_Index) { - s = QuickMenu_Buffer_Get(); + string s = QuickMenu_Buffer_Get(); if(substring(s, 0, 1) != QM_TAG_SUBMENU) continue; if(substring(s, 1, -1) == z_submenu) // submenu end break; QuickMenu_skip_submenu(substring(s, 1, -1)); } - strunzone(z_submenu); + strfree(z_submenu); } bool QuickMenu_IsOpened() @@ -236,10 +229,9 @@ bool QuickMenu_IsOpened() return (QuickMenu_Page_Entries > 0); } -void HUD_Quickmenu_PlayerListEntries(string cmd, int teamplayers, bool without_me); bool HUD_Quickmenu_PlayerListEntries_Create(string cmd, int teamplayers, bool without_me) { - TC(int, teamplayers); TC(bool, without_me); + TC(int, teamplayers); TC(bool, without_me); int i; for(i = 0; i < QUICKMENU_MAXLINES; ++i) QuickMenu_Page_ClearEntry(i); @@ -264,7 +256,7 @@ bool HUD_Quickmenu_PlayerListEntries_Create(string cmd, int teamplayers, bool wi int QuickMenu_Buffer_Index_Prev; bool QuickMenu_Page_Load(string target_submenu, bool new_page) { - TC(bool, new_page); + TC(bool, new_page); string s = string_null, cmd = string_null, z_submenu; if (new_page == 0) @@ -273,9 +265,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) ++QuickMenu_Page; z_submenu = strzone(target_submenu); - if (QuickMenu_CurrentSubMenu) - strunzone(QuickMenu_CurrentSubMenu); - QuickMenu_CurrentSubMenu = strzone(z_submenu); + strcpy(QuickMenu_CurrentSubMenu, z_submenu); QuickMenu_IsLastPage = true; QuickMenu_Page_Entries = 0; @@ -289,11 +279,11 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) s = QuickMenu_Buffer_Get(); if(substring(s, 0, 1) == QM_TAG_SUBMENU && substring(s, 1, -1) == z_submenu) { - // printf("^3 beginning of %s\n", z_submenu); + //LOG_INFOF("^3 beginning of %s\n", z_submenu); ++QuickMenu_Buffer_Index; break; // target_submenu found! } - // printf("^1 skipping %s\n", s); + //LOG_INFOF("^1 skipping %s\n", s); } if(QuickMenu_Buffer_Index == QuickMenu_Buffer_Size) LOG_WARNF("Couldn't find submenu \"%s\"", z_submenu); @@ -310,7 +300,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) if(z_submenu != "" && substring(s, 1, -1) == z_submenu) { - // printf("^3 end of %s\n", z_submenu); + //LOG_INFOF("^3 end of %s\n", z_submenu); break; } @@ -335,28 +325,32 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) QuickMenu_Page_LoadEntry(QuickMenu_Page_Entries, substring(s, 1, -1), ""); QuickMenu_skip_submenu(substring(s, 1, -1)); } - else if(entry_num >= first_entry && substring(s, 0, 1) == QM_TAG_TITLE) + else if(substring(s, 0, 1) == QM_TAG_TITLE) { ++QuickMenu_Buffer_Index; - cmd = QuickMenu_Buffer_Get(); - string command_code = substring(cmd, 0, 1); - if(command_code == QM_TAG_COMMAND) - cmd = substring(cmd, 1, -1); - else if(command_code == QM_TAG_PLCOMMAND) + if(entry_num >= first_entry) { - // throw away the current quickmenu buffer and load a new one - cmd = substring(cmd, 1, -1); - strunzone(z_submenu); - if(HUD_Quickmenu_PlayerListEntries_Create(cmd, stof(substring(s, 1, 1)), stof(substring(s, 2, 1)))) - return QuickMenu_Page_Load("", 0); - QuickMenu_Close(); - return false; - } + cmd = QuickMenu_Buffer_Get(); + string command_code = substring(cmd, 0, 1); + if(command_code == QM_TAG_COMMAND) + cmd = substring(cmd, 1, -1); + else if(command_code == QM_TAG_PLCOMMAND) + { + // throw away the current quickmenu buffer and load a new one + cmd = substring(cmd, 1, -1); + strunzone(z_submenu); + if(HUD_Quickmenu_PlayerListEntries_Create(cmd, stof(substring(s, 1, 1)), stof(substring(s, 2, 1)))) + return QuickMenu_Page_Load("", 0); + QuickMenu_Close(); + return false; + } + + tokenize_console(cmd); + QuickMenu_Page_Command_Type[QuickMenu_Page_Entries] = (argv(1) && argv(0) == "toggle"); - tokenize_console(cmd); - QuickMenu_Page_Command_Type[QuickMenu_Page_Entries] = (argv(1) && argv(0) == "toggle"); + QuickMenu_Page_LoadEntry(QuickMenu_Page_Entries, substring(s, 1, -1), cmd); + } - QuickMenu_Page_LoadEntry(QuickMenu_Page_Entries, substring(s, 1, -1), cmd); } ++entry_num; @@ -373,7 +367,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) bool QuickMenu_ActionForNumber(int num) { - TC(int, num); + TC(int, num); if (!QuickMenu_IsLastPage) { if (num < 0 || num >= QUICKMENU_MAXLINES) @@ -401,7 +395,7 @@ bool QuickMenu_ActionForNumber(int num) void QuickMenu_Page_ActiveEntry(int entry_num) { - TC(int, entry_num); + TC(int, entry_num); QuickMenu_Page_ActivatedEntry = entry_num; QuickMenu_Page_ActivatedEntry_Time = time + 0.1; if(QuickMenu_Page_Command[QuickMenu_Page_ActivatedEntry]) @@ -419,7 +413,7 @@ void QuickMenu_Page_ActiveEntry(int entry_num) bool QuickMenu_InputEvent(int bInputType, float nPrimary, float nSecondary) { - TC(int, bInputType); + TC(int, bInputType); // we only care for keyboard events if(bInputType == 2) return false; @@ -499,9 +493,6 @@ void QuickMenu_Mouse() return; } - if (!autocvar_hud_cursormode) - update_mousepos(); - panel = HUD_PANEL(QUICKMENU); HUD_Panel_LoadCvars(); @@ -519,10 +510,10 @@ void QuickMenu_Mouse() if (mousepos.x >= panel_pos.x && mousepos.y >= first_entry_pos && mousepos.x <= panel_pos.x + panel_size.x && mousepos.y <= first_entry_pos + entries_height) { - float entry_num; - entry_num = floor((mousepos.y - first_entry_pos) / fontsize.y); + int entry_num = min(QuickMenu_Page_Entries - 1, floor((mousepos.y - first_entry_pos) / fontsize.y)); if (QuickMenu_IsLastPage || entry_num != QUICKMENU_MAXLINES - 2) { + // recycling panel_pos as entry_pos panel_pos.y = first_entry_pos + entry_num * fontsize.y; vector color; if(mouseClicked & S_MOUSE1) @@ -537,10 +528,6 @@ void QuickMenu_Mouse() QuickMenu_Page_ActiveEntry((entry_num < QUICKMENU_MAXLINES - 1) ? entry_num + 1 : 0); } } - - draw_cursor_normal(mousepos, '1 1 1', 0.8); - - prevMouseClicked = mouseClicked; } void HUD_Quickmenu_DrawEntry(vector pos, string desc, string option, vector fontsize) @@ -583,16 +570,10 @@ void HUD_QuickMenu() { if(!autocvar__hud_configure) { - if (hud_configure_prev && hud_configure_prev != -1) - QuickMenu_Close(); - - if(!hud_draw_maximized) return; - if(mv_active) return; - //if(!autocvar_hud_panel_quickmenu) return; - if(!hud_panel_quickmenu) return; + if (!hud_draw_maximized || !QuickMenu_IsOpened()) + return; - if(QuickMenu_TimeOut) - if(time > QuickMenu_TimeOut) + if (HUD_QuickMenu_Forbidden()) { QuickMenu_Close(); return; @@ -760,7 +741,7 @@ void HUD_QuickMenu() void HUD_Quickmenu_PlayerListEntries(string cmd, int teamplayers, bool without_me) { - TC(int, teamplayers); TC(bool, without_me); + TC(int, teamplayers); TC(bool, without_me); entity pl; if(teamplayers && !team_count) return; @@ -798,6 +779,8 @@ void QuickMenu_Default(string target_submenu) QUICKMENU_ENTRY_TC(CTX(_("QMCMD^nice one")), "say %s", ":-) / nice one", CTX(_("QMCMD^:-) / nice one"))) QUICKMENU_ENTRY_TC(CTX(_("QMCMD^good game")), "say %s", "good game", CTX(_("QMCMD^good game"))) QUICKMENU_ENTRY_TC(CTX(_("QMCMD^hi / good luck")), "say %s", "hi / good luck and have fun", CTX(_("QMCMD^hi / good luck and have fun"))) + if(prvm_language != "en") + QUICKMENU_ENTRY(CTX(_("QMCMD^Send in English")), "toggle hud_panel_quickmenu_translatecommands 0 1; quickmenu; wait; quickmenu default Chat") QUICKMENU_SMENU(_("Chat"), "Chat") if(teamplay) @@ -858,8 +841,6 @@ void QuickMenu_Default(string target_submenu) } QUICKMENU_ENTRY(CTX(_("QMCMD^Fullscreen")), "toggle vid_fullscreen; vid_restart") - if(prvm_language != "en") - QUICKMENU_ENTRY(CTX(_("QMCMD^Translate chat messages")), "toggle hud_panel_quickmenu_translatecommands") QUICKMENU_SMENU(CTX(_("QMCMD^Settings")), "Settings") QUICKMENU_SMENU(CTX(_("QMCMD^Call a vote")), "Call a vote") @@ -876,9 +857,9 @@ void QuickMenu_Default(string target_submenu) if(target_submenu != "" && !target_submenu_found) { - LOG_WARNF("Couldn't find submenu \"%s\"", target_submenu); + LOG_INFOF("Couldn't find submenu \"%s\"", target_submenu); if(prvm_language != "en") - LOG_WARNF("^3Warning: submenu must be in English", target_submenu); + LOG_INFOF("^3Warning: submenu title must be in English", target_submenu); QuickMenu_Buffer_Size = 0; } } diff --git a/qcsrc/client/hud/panel/quickmenu.qh b/qcsrc/client/hud/panel/quickmenu.qh index aad86e6bd6..694f0d1d7e 100644 --- a/qcsrc/client/hud/panel/quickmenu.qh +++ b/qcsrc/client/hud/panel/quickmenu.qh @@ -4,3 +4,8 @@ bool QuickMenu_InputEvent(float bInputType, float nPrimary, float nSecondary); bool QuickMenu_IsOpened(); void QuickMenu_Mouse(); +float QuickMenu_Page_Load(string target_submenu, float new_page); +void QuickMenu_Default(string submenu); +void HUD_Quickmenu_PlayerListEntries(string cmd, int teamplayers, bool without_me); +void QuickMenu_Close(); +bool QuickMenu_Open(string mode, string submenu, string file); diff --git a/qcsrc/client/hud/panel/racetimer.qc b/qcsrc/client/hud/panel/racetimer.qc index 6a190f2ca5..7d09cf1ff5 100644 --- a/qcsrc/client/hud/panel/racetimer.qc +++ b/qcsrc/client/hud/panel/racetimer.qc @@ -98,7 +98,7 @@ void HUD_RaceTimer () if(!autocvar__hud_configure) { if(!autocvar_hud_panel_racetimer) return; - if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(!(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; if(spectatee_status == -1) return; } diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index b1cc222ccf..cd4551725b 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -6,6 +6,7 @@ #include <common/ent_cs.qh> #include <common/mapinfo.qh> #include <client/mapvoting.qh> +#include <client/resources.qh> #include <client/teamradar.qh> #include <common/mutators/mutator/waypoints/all.qh> @@ -18,7 +19,7 @@ bool HUD_Radar_Clickable() void HUD_Radar_Show_Maximized(bool doshow, bool clickable) { - TC(bool, doshow); + TC(bool, doshow); hud_panel_radar_maximized = doshow; hud_panel_radar_temp_hidden = 0; @@ -26,8 +27,6 @@ void HUD_Radar_Show_Maximized(bool doshow, bool clickable) { if (clickable) { - if(autocvar_hud_cursormode) - setcursormode(1); hud_panel_radar_mouse = 1; // we must unset the player's buttons, as they aren't released elsewhere @@ -42,9 +41,6 @@ void HUD_Radar_Show_Maximized(bool doshow, bool clickable) { hud_panel_radar_mouse = 0; mouseClicked = 0; - if(autocvar_hud_cursormode) - if(!mv_active) - setcursormode(0); } } void HUD_Radar_Hide_Maximized() @@ -55,7 +51,7 @@ void HUD_Radar_Hide_Maximized() float HUD_Radar_InputEvent(int bInputType, float nPrimary, float nSecondary) { - TC(int, bInputType); + TC(int, bInputType); if(!hud_panel_radar_maximized || !hud_panel_radar_mouse || autocvar__hud_configure || mv_active) return false; @@ -140,9 +136,6 @@ void HUD_Radar_Mouse() return; } - if (!autocvar_hud_cursormode) - update_mousepos(); - panel = HUD_PANEL(RADAR); HUD_Panel_LoadCvars(); @@ -168,9 +161,6 @@ void HUD_Radar_Mouse() HUD_Radar_Hide_Maximized(); return; } - - - draw_cursor_normal(mousepos, '1 1 1', 0.8); } void HUD_Radar() @@ -214,9 +204,7 @@ void HUD_Radar() panel_bg = "gfx/hud/default/border_default"; // fallback if(!radar_panel_modified && panel_bg != panel.current_panel_bg) radar_panel_modified = true; - if(panel.current_panel_bg) - strunzone(panel.current_panel_bg); - panel.current_panel_bg = strzone(panel_bg); + strcpy(panel.current_panel_bg, panel_bg); switch(hud_panel_radar_maximized_zoommode) { @@ -354,8 +342,8 @@ void HUD_Radar() IL_EACH(g_radaricons, it.teamradar_icon, { if ( hud_panel_radar_mouse ) - if ( it.health >= 0 ) - if ( it.team == myteam + 1 || gametype == MAPINFO_TYPE_RACE || !teamplay ) + if ( GetResourceAmount(it, RESOURCE_HEALTH) >= 0 ) + if ( it.team == myteam + 1 || ISGAMETYPE(RACE) || !teamplay ) { vector coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(it.origin)); if(vdist((mousepos - coord), <, 8)) diff --git a/qcsrc/client/hud/panel/radar.qh b/qcsrc/client/hud/panel/radar.qh index 6db88c68b3..d2fbc8f6b2 100644 --- a/qcsrc/client/hud/panel/radar.qh +++ b/qcsrc/client/hud/panel/radar.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void HUD_Radar_Show_Maximized(bool doshow, bool clickable); diff --git a/qcsrc/client/hud/panel/score.qc b/qcsrc/client/hud/panel/score.qc index 56fa5867c2..525bf614b8 100644 --- a/qcsrc/client/hud/panel/score.qc +++ b/qcsrc/client/hud/panel/score.qc @@ -140,7 +140,7 @@ void HUD_Score() if(!autocvar__hud_configure) { if(!autocvar_hud_panel_score) return; - if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; + if(spectatee_status == -1 && (ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) return; } HUD_Panel_LoadCvars(); diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc index 200f5b2c55..4989aac508 100644 --- a/qcsrc/client/hud/panel/scoreboard.qc +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -2,6 +2,7 @@ #include <client/autocvars.qh> #include <client/defs.qh> +#include <client/main.qh> #include <client/miscfunctions.qh> #include "quickmenu.qh" #include <common/ent_cs.qh> @@ -60,6 +61,9 @@ float autocvar_hud_panel_scoreboard_namesize = 15; bool autocvar_hud_panel_scoreboard_accuracy = true; bool autocvar_hud_panel_scoreboard_accuracy_doublerows = false; bool autocvar_hud_panel_scoreboard_accuracy_nocolors = false; +float autocvar_hud_panel_scoreboard_accuracy_showdelay = 2; +float autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos = 0.75; + bool autocvar_hud_panel_scoreboard_ctf_leaderboard = true; bool autocvar_hud_panel_scoreboard_dynamichud = false; @@ -70,10 +74,6 @@ bool autocvar_hud_panel_scoreboard_spectators_showping = true; bool autocvar_hud_panel_scoreboard_spectators_aligned = false; float autocvar_hud_panel_scoreboard_minwidth = 0.4; - -void drawstringright(vector, string, vector, vector, float, float); -void drawstringcenter(vector, string, vector, vector, float, float); - // wrapper to put all possible scores titles through gettext string TranslateScoresLabel(string l) { @@ -150,7 +150,6 @@ void Scoreboard_InitScores() Cmd_Scoreboard_SetFields(0); } -float SetTeam(entity pl, float Team); //float lastpnum; void Scoreboard_UpdatePlayerTeams() { @@ -179,7 +178,7 @@ void Scoreboard_UpdatePlayerTeams() int Scoreboard_CompareScore(int vl, int vr, int f) { - TC(int, vl); TC(int, vr); TC(int, f); + TC(int, vl); TC(int, vr); TC(int, f); if(f & SFL_ZERO_IS_WORST) { if(vl == 0 && vr != 0) @@ -297,61 +296,68 @@ void Scoreboard_UpdateTeamPos(entity Team) void Cmd_Scoreboard_Help() { LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.")); - LOG_INFO(_("^3|---------------------------------------------------------------|")); LOG_INFO(_("Usage:")); - LOG_INFO(_("^2scoreboard_columns_set default")); - LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...")); - LOG_INFO(_("The following field names are recognized (case insensitive):")); + LOG_INFO("^2scoreboard_columns_set ^3default"); + LOG_INFO(_("^2scoreboard_columns_set ^3field1 field2 ...")); + LOG_INFO(_("^2scoreboard_columns_set ^7without arguments reads the arguments from the cvar scoreboard_columns")); + LOG_INFO(_(" ^5Note: ^7scoreboard_columns_set without arguments is executed on every map start")); + LOG_INFO(_("^2scoreboard_columns_set ^3expand_default ^7loads default layout and expands it into the cvar scoreboard_columns so you can edit it")); LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.")); + LOG_INFO(_("The following field names are recognized (case insensitive):")); LOG_INFO(""); - LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player")); - LOG_INFO(_("^3ping^7 Ping time")); - LOG_INFO(_("^3pl^7 Packet loss")); - LOG_INFO(_("^3elo^7 Player ELO")); - LOG_INFO(_("^3kills^7 Number of kills")); - 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")); - LOG_INFO(_("^3sum^7 frags - deaths")); - LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured")); - LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up")); - LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)")); - LOG_INFO(_("^3fckills^7 Number of flag carrier kills")); - LOG_INFO(_("^3returns^7 Number of flag returns")); - LOG_INFO(_("^3drops^7 Number of flag drops")); - LOG_INFO(_("^3lives^7 Number of lives (LMS)")); - LOG_INFO(_("^3rank^7 Player rank")); - LOG_INFO(_("^3pushes^7 Number of players pushed into void")); - LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void")); - LOG_INFO(_("^3kckills^7 Number of keys carrier kills")); - LOG_INFO(_("^3losses^7 Number of times a key was lost")); - LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)")); - LOG_INFO(_("^3time^7 Total time raced (race/cts)")); - LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)")); - LOG_INFO(_("^3ticks^7 Number of ticks (DOM)")); - LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)")); - LOG_INFO(_("^3bckills^7 Number of ball carrier kills")); - LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway")); - LOG_INFO(_("^3score^7 Total score")); + LOG_INFO(strcat("^3name^7 ", _("Name of a player"))); + LOG_INFO(strcat("^3nick^7 ", _("Name of a player"))); + LOG_INFO(strcat("^3ping^7 ", _("Ping time"))); + LOG_INFO(strcat("^3pl^7 ", _("Packet loss"))); + LOG_INFO(strcat("^3elo^7 ", _("Player ELO"))); + LOG_INFO(strcat("^3fps^7 ", _("Player FPS"))); + LOG_INFO(strcat("^3kills^7 ", _("Number of kills"))); + LOG_INFO(strcat("^3deaths^7 ", _("Number of deaths"))); + LOG_INFO(strcat("^3suicides^7 ", _("Number of suicides"))); + LOG_INFO(strcat("^3frags^7 ", _("kills - suicides"))); + LOG_INFO(strcat("^3teamkills^7 ", _("Number of teamkills"))); + LOG_INFO(strcat("^3kd^7 ", _("The kill-death ratio"))); + LOG_INFO(strcat("^3dmg^7 ", _("The total damage done"))); + LOG_INFO(strcat("^3dmgtaken^7 ", _("The total damage taken"))); + LOG_INFO(strcat("^3sum^7 ", _("kills - deaths"))); + LOG_INFO(strcat("^3caps^7 ", _("How often a flag (CTF) or a key (KeyHunt) was captured"))); + LOG_INFO(strcat("^3pickups^7 ", _("How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up"))); + LOG_INFO(strcat("^3captime^7 ", _("Time of fastest cap (CTF)"))); + LOG_INFO(strcat("^3fckills^7 ", _("Number of flag carrier kills"))); + LOG_INFO(strcat("^3returns^7 ", _("Number of flag returns"))); + LOG_INFO(strcat("^3drops^7 ", _("Number of flag drops"))); + LOG_INFO(strcat("^3lives^7 ", _("Number of lives (LMS)"))); + LOG_INFO(strcat("^3rank^7 ", _("Player rank"))); + LOG_INFO(strcat("^3pushes^7 ", _("Number of players pushed into void"))); + LOG_INFO(strcat("^3destroyed^7 ", _("Number of keys destroyed by pushing them into void"))); + LOG_INFO(strcat("^3kckills^7 ", _("Number of keys carrier kills"))); + LOG_INFO(strcat("^3losses^7 ", _("Number of times a key was lost"))); + LOG_INFO(strcat("^3laps^7 ", _("Number of laps finished (race/cts)"))); + LOG_INFO(strcat("^3time^7 ", _("Total time raced (race/cts)"))); + LOG_INFO(strcat("^3fastest^7 ", _("Time of fastest lap (race/cts)"))); + LOG_INFO(strcat("^3ticks^7 ", _("Number of ticks (DOM)"))); + LOG_INFO(strcat("^3takes^7 ", _("Number of domination points taken (DOM)"))); + LOG_INFO(strcat("^3bckills^7 ", _("Number of ball carrier kills"))); + LOG_INFO(strcat("^3bctime^7 ", _("Total amount of time holding the ball in Keepaway"))); + LOG_INFO(strcat("^3score^7 ", _("Total score"))); LOG_INFO(""); LOG_INFO(_("Before a field you can put a + or - sign, then a comma separated list\n" "of game types, then a slash, to make the field show up only in these\n" "or in all but these game types. You can also specify 'all' as a\n" - "field to show all fields available for the current game mode.\n\n")); + "field to show all fields available for the current game mode.")); + LOG_INFO(""); LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n" - "include/exclude ALL teams/noteams game modes.\n\n")); + "include/exclude ALL teams/noteams game modes.")); + LOG_INFO(""); LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4")); - LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields" - "right of the vertical bar aligned to the right.\n")); - LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\nother gamemodes except DM.\n")); + LOG_INFO(_("will display name, ping and pl aligned to the left, and the fields\n" + "right of the vertical bar aligned to the right.")); + LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n" + "other gamemodes except DM.")); } // NOTE: adding a gametype with ? to not warn for an optional field @@ -359,7 +365,7 @@ void Cmd_Scoreboard_Help() // otherwise the previous exclusive rule warns anyway // e.g. -teams,rc,cts,lms/kills ?+rc/kills #define SCOREBOARD_DEFAULT_COLUMNS \ -"ping pl name |" \ +"ping pl fps name |" \ " -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \ " -teams,lms/deaths +ft,tdm/deaths" \ " +tdm/sum" \ @@ -378,7 +384,7 @@ void Cmd_Scoreboard_Help() void Cmd_Scoreboard_SetFields(int argc) { - TC(int, argc); + TC(int, argc); int i, slash; string str, pattern; bool have_name = false, have_primary = false, have_secondary = false, have_separator = false; @@ -401,12 +407,15 @@ void Cmd_Scoreboard_SetFields(int argc) if(argc == 3) { - if(argv(2) == "default") + if(argv(2) == "default" || argv(2) == "expand_default") + { + if(argv(2) == "expand_default") + cvar_set("scoreboard_columns", SCOREBOARD_DEFAULT_COLUMNS); argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " "); + } else if(argv(2) == "all") { - string s; - s = "ping pl name |"; + string s = "ping pl name |"; // scores without a label FOREACH(Scores, true, { if(it != ps_primary) if(it != ps_secondary) @@ -427,10 +436,8 @@ void Cmd_Scoreboard_SetFields(int argc) for(i = 1; i < argc - 1; ++i) { - float nocomplain; str = argv(i+1); - - nocomplain = false; + bool nocomplain = false; if(substring(str, 0, 1) == "?") { nocomplain = true; @@ -447,8 +454,7 @@ void Cmd_Scoreboard_SetFields(int argc) continue; } - strunzone(sbt_field_title[sbt_num_fields]); - sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(str)); + strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(str)); sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize); str = strtolower(str); @@ -464,6 +470,7 @@ void Cmd_Scoreboard_SetFields(int argc) case "elo": sbt_field[sbt_num_fields] = SP_ELO; break; case "dmg": case "damage": sbt_field[sbt_num_fields] = SP_DMG; break; case "dmgtaken": case "damagetaken": sbt_field[sbt_num_fields] = SP_DMGTAKEN; break; + case "fps": sbt_field[sbt_num_fields] = SP_FPS; break; default: { FOREACH(Scores, true, { @@ -538,8 +545,7 @@ LABEL(found) } else if(!have_separator) { - strunzone(sbt_field_title[sbt_num_fields]); - sbt_field_title[sbt_num_fields] = strzone("|"); + strcpy(sbt_field_title[sbt_num_fields], "|"); sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize); sbt_field[sbt_num_fields] = SP_SEPARATOR; ++sbt_num_fields; @@ -547,8 +553,7 @@ LABEL(found) } if(!have_secondary) { - strunzone(sbt_field_title[sbt_num_fields]); - sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_secondary))); + strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_secondary))); sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize); sbt_field[sbt_num_fields] = ps_secondary; ++sbt_num_fields; @@ -556,8 +561,7 @@ LABEL(found) } if(!have_primary) { - strunzone(sbt_field_title[sbt_num_fields]); - sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(scores_label(ps_primary))); + strcpy(sbt_field_title[sbt_num_fields], TranslateScoresLabel(scores_label(ps_primary))); sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize); sbt_field[sbt_num_fields] = ps_primary; ++sbt_num_fields; @@ -679,6 +683,19 @@ string Scoreboard_GetField(entity pl, PlayerScoreField field) } } + case SP_FPS: + { + float fps = pl.(scores(SP_FPS)); + if(fps == 0) + { + sbt_field_rgb = '1 1 1'; + return ((pl.ping == 0) ? _("N/A") : "..."); // if 0 ping, either connecting or bot (either case can't show proper score) + } + //sbt_field_rgb = HUD_Get_Num_Color(fps, 200); + sbt_field_rgb = '1 0 0' + '0 1 1' * (bound(0, fps, 60) / 60); + return ftos(fps); + } + case SP_DMG: case SP_DMGTAKEN: return sprintf("%.1f k", pl.(scores(field)) / 1000); @@ -702,7 +719,7 @@ float sbt_fixcolumnwidth_marginlen; string Scoreboard_FixColumnWidth(int i, string str) { - TC(int, i); + TC(int, i); float f; vector sz; @@ -826,7 +843,7 @@ vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players) void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number) { - TC(bool, is_self); TC(int, pl_number); + TC(bool, is_self); TC(int, pl_number); string str; bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR); @@ -1011,6 +1028,12 @@ vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity field_pos.x += fieldpadding + (max(fieldsize, min_fieldsize) - fieldsize) * 0.5; drawstring(field_pos, field, hud_fontsize, sbt_field_rgb, sbt_fg_alpha, DRAWFLAG_NORMAL); } + if(pl.eliminated) + { + h_size.x = column_width + hud_fontsize.x * 0.25; + h_size.y = hud_fontsize.y; + drawfill(pos - hud_fontsize.x * 0.25 * eX, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL); + } pos.x += column_width; pos.x += hud_fontsize.x; } @@ -1122,7 +1145,7 @@ bool Scoreboard_WouldDraw() return true; else if (intermission == 2) return false; - else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame) + else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && !ISGAMETYPE(CTS) && !active_minigame) return true; else if (scoreboard_showscores_force) return true; @@ -1132,6 +1155,15 @@ bool Scoreboard_WouldDraw() float average_accuracy; vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) { + if (frametime) + { + if (scoreboard_fade_alpha == 1) + scoreboard_acc_fade_alpha = min(1, scoreboard_acc_fade_alpha + frametime * 10); + else + scoreboard_acc_fade_alpha = 1; // sync fading with the scoreboard + } + vector initial_pos = pos; + WepSet weapons_stat = WepSet_GetFromStat(); WepSet weapons_inmap = WepSet_GetFromStat_InMap(); int disownedcnt = 0; @@ -1165,7 +1197,7 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) float weapon_height = 29; float height = hud_fontsize.y + weapon_height; - drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(pos + eX * panel_bg_padding, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', panel_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); pos.y += 1.25 * hud_fontsize.y; if(panel.current_panel_bg != "0") pos.y += panel_bg_border; @@ -1173,7 +1205,11 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) panel_pos = pos; panel_size.y = height * rows; panel_size.y += panel_bg_padding * 2; + + float panel_bg_alpha_save = panel_bg_alpha; + panel_bg_alpha *= scoreboard_acc_fade_alpha; HUD_Panel_DrawBg(); + panel_bg_alpha = panel_bg_alpha_save; vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y); if(panel.current_panel_bg != "0") @@ -1191,18 +1227,18 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) float weapon_width = tmp.x / columnns / rows; if (sbt_bg_alpha) - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL); + drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); if(sbt_highlight) { // column highlighting for (int i = 0; i < columnns; ++i) if ((i % 2) == 0) - drawfill(pos + eX * weapon_width * rows * i, vec2(weapon_width * rows, height * rows), '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + drawfill(pos + eX * weapon_width * rows * i, vec2(weapon_width * rows, height * rows), '0 0 0', sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); // row highlighting for (int i = 0; i < rows; ++i) - drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); + drawfill(pos + eY * (weapon_height + height * i), vec2(tmp.x, hud_fontsize.y), rgb, sbt_highlight_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); } average_accuracy = 0; @@ -1235,7 +1271,7 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) weapon_alpha = 0.2 * sbt_fg_alpha; // weapon icon - drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); + drawpic_aspect_skin(tmpos, it.model2, vec2(weapon_width, weapon_height), '1 1 1', weapon_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); // the accuracy if (weapon_stats >= 0) { weapons_with_stats += 1; @@ -1250,7 +1286,7 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) if(!autocvar_hud_panel_scoreboard_accuracy_nocolors) rgb = Accuracy_GetColor(weapon_stats); - drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha, DRAWFLAG_NORMAL); + drawstring(tmpos + vec2(padding, weapon_height), s, hud_fontsize, rgb, sbt_fg_alpha * scoreboard_acc_fade_alpha, DRAWFLAG_NORMAL); } tmpos.x += weapon_width * rows; pos.x += weapon_width * rows; @@ -1266,7 +1302,10 @@ vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5); panel_size.x += panel_bg_padding * 2; // restore initial width - return end_pos; + + if (scoreboard_acc_fade_alpha == 1) + return end_pos; + return initial_pos + (end_pos - initial_pos) * scoreboard_acc_fade_alpha; } vector MapStats_DrawKeyValue(vector pos, string key, string value) { @@ -1365,7 +1404,7 @@ vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_siz vector hl_rgb = rgb + '0.5 0.5 0.5'; pos.y += hud_fontsize.y; - drawstring(pos + eX * panel_bg_padding, ((gametype == MAPINFO_TYPE_CTF) ? _("Capture time rankings") : _("Rankings")), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(pos + eX * panel_bg_padding, ((ISGAMETYPE(CTF)) ? _("Capture time rankings") : _("Rankings")), hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += 1.25 * hud_fontsize.y; if(panel.current_panel_bg != "0") pos.y += panel_bg_border; @@ -1455,6 +1494,39 @@ vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_siz return end_pos; } +float scoreboard_time; +bool have_weapon_stats; +bool Scoreboard_AccuracyStats_WouldDraw(float ypos) +{ + if (ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || ISGAMETYPE(NEXBALL)) + return false; + if (!autocvar_hud_panel_scoreboard_accuracy || warmup_stage || ypos > 0.91 * vid_conheight) + return false; + + if (time < scoreboard_time + autocvar_hud_panel_scoreboard_accuracy_showdelay + && ypos > autocvar_hud_panel_scoreboard_accuracy_showdelay_minpos * vid_conheight + && !intermission) + { + return false; + } + + if (!have_weapon_stats) + { + FOREACH(Weapons, it != WEP_Null, { + int weapon_stats = weapon_accuracy[i - WEP_FIRST]; + if (weapon_stats >= 0) + { + have_weapon_stats = true; + break; + } + }); + if (!have_weapon_stats) + return false; + } + + return true; +} + void Scoreboard_Draw() { if(!autocvar__hud_configure) @@ -1463,6 +1535,8 @@ void Scoreboard_Draw() // frametime checks allow to toggle the scoreboard even when the game is paused if(scoreboard_active) { + if (scoreboard_fade_alpha < 1) + scoreboard_time = time; if(hud_configure_menu_open == 1) scoreboard_fade_alpha = 1; float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed; @@ -1474,9 +1548,7 @@ void Scoreboard_Draw() { hud_fontsize = HUD_GetFontsize("hud_fontsize"); Scoreboard_initFieldSizes(); - if(hud_fontsize_str) - strunzone(hud_fontsize_str); - hud_fontsize_str = strzone(autocvar_hud_fontsize); + strcpy(hud_fontsize_str, autocvar_hud_fontsize); } } else { @@ -1488,7 +1560,10 @@ void Scoreboard_Draw() } if (!scoreboard_fade_alpha) + { + scoreboard_acc_fade_alpha = 0; return; + } } else scoreboard_fade_alpha = 0; @@ -1588,12 +1663,10 @@ void Scoreboard_Draw() pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size); } - bool show_accuracy = (gametype != MAPINFO_TYPE_CTS && gametype != MAPINFO_TYPE_RACE && gametype != MAPINFO_TYPE_NEXBALL); - - if (show_accuracy && autocvar_hud_panel_scoreboard_accuracy && !warmup_stage) + if (Scoreboard_AccuracyStats_WouldDraw(pos.y)) pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size); - if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (autocvar_hud_panel_scoreboard_ctf_leaderboard && gametype == MAPINFO_TYPE_CTF && STAT(CTF_SHOWLEADERBOARD))) { + if(ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || (autocvar_hud_panel_scoreboard_ctf_leaderboard && ISGAMETYPE(CTF) && STAT(CTF_SHOWLEADERBOARD))) { if(race_speedaward) { drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, ColorTranslateRGB(race_speedaward_holder)), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += 1.25 * hud_fontsize.y; @@ -1634,7 +1707,7 @@ void Scoreboard_Draw() tl = STAT(TIMELIMIT); fl = STAT(FRAGLIMIT); ll = STAT(LEADLIMIT); - if(gametype == MAPINFO_TYPE_LMS) + if(ISGAMETYPE(LMS)) { if(tl > 0) str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); diff --git a/qcsrc/client/hud/panel/scoreboard.qh b/qcsrc/client/hud/panel/scoreboard.qh index a560b74c74..f585b480fe 100644 --- a/qcsrc/client/hud/panel/scoreboard.qh +++ b/qcsrc/client/hud/panel/scoreboard.qh @@ -3,8 +3,9 @@ bool scoreboard_active; float scoreboard_fade_alpha; +float scoreboard_acc_fade_alpha; -void Cmd_Scoreboard_SetFields(float argc); +void Cmd_Scoreboard_SetFields(int argc); void Scoreboard_Draw(); void Scoreboard_InitScores(); void Scoreboard_UpdatePlayerTeams(); diff --git a/qcsrc/client/hud/panel/timer.qc b/qcsrc/client/hud/panel/timer.qc index 0dcbb70dba..e01aa75175 100644 --- a/qcsrc/client/hud/panel/timer.qc +++ b/qcsrc/client/hud/panel/timer.qc @@ -63,6 +63,8 @@ void HUD_Timer() if (intermission_time) { timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME)))); + } else if (warmup_stage && warmup_timeleft >= 60) { + timer = _("WARMUP"); } else if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) { if (time < STAT(GAMESTARTTIME)) timer = seconds_tostring(0); //while restart is still active, show 00:00 diff --git a/qcsrc/client/hud/panel/vote.qc b/qcsrc/client/hud/panel/vote.qc index 15e18e8f44..0337eccfc2 100644 --- a/qcsrc/client/hud/panel/vote.qc +++ b/qcsrc/client/hud/panel/vote.qc @@ -9,7 +9,7 @@ void HUD_Vote() { - if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE || (serverflags & SERVERFLAG_PLAYERSTATS))) + if(autocvar_cl_allow_uid2name == -1 && (ISGAMETYPE(CTS) || ISGAMETYPE(RACE) || (serverflags & SERVERFLAG_PLAYERSTATS))) { // this dialog gets overriden by the uid2name menu dialog, if it exists // TODO remove this client side uid2name dialog in the next release @@ -27,9 +27,7 @@ void HUD_Vote() LOG_INFO(_("^1You must answer before entering hud configure mode")); cvar_set("_hud_configure", "0"); } - if(vote_called_vote) - strunzone(vote_called_vote); - vote_called_vote = strzone(_("^2Name ^7instead of \"^1Anonymous player^7\" in stats")); + strcpy(vote_called_vote, _("^2Name ^7instead of \"^1Anonymous player^7\" in stats")); uid2name_dialog = 1; } diff --git a/qcsrc/client/hud/panel/weapons.qc b/qcsrc/client/hud/panel/weapons.qc index cebd8f5d65..8bf11cf1ac 100644 --- a/qcsrc/client/hud/panel/weapons.qc +++ b/qcsrc/client/hud/panel/weapons.qc @@ -11,7 +11,7 @@ entity weaponorder[Weapons_MAX]; void weaponorder_swap(int i, int j, entity pass) { - TC(int, i); TC(int, j); + TC(int, i); TC(int, j); entity h = weaponorder[i]; weaponorder[i] = weaponorder[j]; weaponorder[j] = h; @@ -20,7 +20,7 @@ void weaponorder_swap(int i, int j, entity pass) string weaponorder_cmp_str; int weaponorder_cmp(int i, int j, entity pass) { - TC(int, i); TC(int, j); + TC(int, i); TC(int, j); int ai = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[i].m_id), 0); int aj = strstrofs(weaponorder_cmp_str, sprintf(" %d ", weaponorder[j].m_id), 0); return aj - ai; // the string is in REVERSE order (higher prio at the right is what we want, but higher prio first is the string) @@ -30,7 +30,7 @@ int weaponorder_cmp(int i, int j, entity pass) int nHidden = 0; \ FOREACH(Weapons, it != WEP_Null, { \ if (weapons_stat & WepSet_FromWeapon(it)) continue; \ - if (it.spawnflags & WEP_FLAG_HIDDEN || it.spawnflags & WEP_FLAG_MUTATORBLOCKED) nHidden += 1; \ + if ((it.spawnflags & WEP_FLAG_HIDDEN) || (it.spawnflags & WEP_FLAG_MUTATORBLOCKED)) nHidden += 1; \ }); \ vector table_size = HUD_GetTableSize_BestItemAR((Weapons_COUNT - 1) - nHidden, panel_size, aspect); \ columns = table_size.x; \ @@ -94,13 +94,8 @@ void HUD_Weapons() if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0]) { int weapon_cnt; - if(weaponorder_bypriority) - strunzone(weaponorder_bypriority); - if(weaponorder_byimpulse) - strunzone(weaponorder_byimpulse); - - weaponorder_bypriority = strzone(autocvar_cl_weaponpriority); - weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority)))); + strcpy(weaponorder_bypriority, autocvar_cl_weaponpriority); + strcpy(weaponorder_byimpulse, W_FixWeaponOrder_BuildImpulseList(W_FixWeaponOrder_ForceComplete(W_NumberWeaponOrder(weaponorder_bypriority)))); weaponorder_cmp_str = strcat(" ", weaponorder_byimpulse, " "); weapon_cnt = 0; @@ -113,7 +108,14 @@ void HUD_Weapons() } if(!autocvar_hud_panel_weapons_complainbubble || autocvar__hud_configure || time - complain_weapon_time >= when + fadetime) - complain_weapon = 0; + complain_weapon = NULL; + + entity wepent = viewmodels[0]; // TODO: unhardcode + + if (wepent.switchweapon == WEP_Null) + panel_switchweapon = NULL; + else if (!panel_switchweapon) + panel_switchweapon = wepent.switchweapon; if(autocvar__hud_configure) { @@ -164,10 +166,18 @@ void HUD_Weapons() // do we own this weapon? weapon_count = 0; - for(i = 0; i <= WEP_LAST-WEP_FIRST; ++i) - if((weapons_stat & WepSet_FromWeapon(weaponorder[i])) || (weaponorder[i].m_id == complain_weapon)) - ++weapon_count; - + if (autocvar_hud_panel_weapons_onlyowned >= 2) // only current + { + for (i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + if (weaponorder[i] == panel_switchweapon || weaponorder[i] == complain_weapon) + ++weapon_count; + } + else + { + for (i = 0; i <= WEP_LAST-WEP_FIRST; ++i) + if ((weapons_stat & WepSet_FromWeapon(weaponorder[i])) || weaponorder[i] == complain_weapon) + ++weapon_count; + } // might as well commit suicide now, no reason to live ;) if (weapon_count == 0) @@ -378,13 +388,6 @@ void HUD_Weapons() switch_speed = frametime * autocvar_hud_panel_weapons_selection_speed; vector radius_size = weapon_size * (autocvar_hud_panel_weapons_selection_radius + 1); - entity wepent = viewmodels[0]; // TODO: unhardcode - - if(wepent.switchweapon == WEP_Null) - panel_switchweapon = NULL; - else if(!panel_switchweapon) - panel_switchweapon = wepent.switchweapon; - // draw background behind currently selected weapon // do it earlier to make sure bg is drawn behind every weapon icons while it's moving if(panel_switchweapon) @@ -400,14 +403,22 @@ void HUD_Weapons() if(!it || weapon_id < 0) { continue; } // skip this weapon if we don't own it (and onlyowned is enabled)-- or if weapons_complainbubble is showing for this weapon - if(autocvar_hud_panel_weapons_onlyowned) + if (autocvar_hud_panel_weapons_onlyowned) { - if (!((weapons_stat & WepSet_FromWeapon(it)) || (it.m_id == complain_weapon))) - continue; + if (autocvar_hud_panel_weapons_onlyowned >= 2) // only current + { + if (!(it == panel_switchweapon || it == complain_weapon)) + continue; + } + else + { + if (!((weapons_stat & WepSet_FromWeapon(it)) || (it == complain_weapon))) + continue; + } } else { - if (it.spawnflags & WEP_FLAG_MUTATORBLOCKED && !(weapons_stat & WepSet_FromWeapon(it))) + if (((it.spawnflags & WEP_FLAG_HIDDEN) || (it.spawnflags & WEP_FLAG_MUTATORBLOCKED)) && !(weapons_stat & WepSet_FromWeapon(it))) continue; } @@ -523,7 +534,7 @@ void HUD_Weapons() } // draw the complain message - if(it.m_id == complain_weapon) + if(it == complain_weapon) { if(fadetime) a = ((complain_weapon_time + when > time) ? 1 : bound(0, (complain_weapon_time + when + fadetime - time) / fadetime, 1)); diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index 9c146c09bd..e32c0e8f57 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -8,13 +8,14 @@ #include <common/effects/all.qh> #include <common/effects/all.inc> #include "hud/_mod.qh" +#include "commands/cl_cmd.qh" #include "mapvoting.qh" -#include "mutators/events.qh" +#include <client/mutators/_mod.qh> #include "hud/panel/scoreboard.qh" #include "hud/panel/quickmenu.qh" #include "shownames.qh" +#include "view.qh" #include <common/t_items.qh> -#include "wall.qh" #include "weapons/projectile.qh" #include <common/deathtypes/all.qh> #include <common/items/_mod.qh> @@ -24,7 +25,7 @@ #include <common/net_linked.qh> #include <common/net_notice.qh> #include <common/scores.qh> -#include <common/triggers/include.qh> +#include <common/mapobjects/_mod.qh> #include <common/vehicles/all.qh> #include <lib/csqcmodel/cl_model.qh> #include <lib/csqcmodel/interpolate.qh> @@ -91,7 +92,6 @@ void LoadMenuSkinValues() // CSQC_Init : Called every time the CSQC code is initialized (essentially at map load) // Useful for precaching things -void ConsoleCommand_macro_init(); void CSQC_Init() { prvm_language = strzone(cvar_string("prvm_language")); @@ -140,6 +140,9 @@ void CSQC_Init() registercvar("cl_spawn_near_teammate", "1"); + registercvar("cl_weapon_switch_reload", "1"); + registercvar("cl_weapon_switch_fallback_to_impulse", "1"); + if(autocvar_cl_lockview) cvar_set("cl_lockview", "0"); @@ -223,14 +226,16 @@ void Shutdown() localcmd("\ncl_hook_shutdown\n"); + localcmd("\n-button14\n"); + deactivate_minigame(); HUD_MinigameMenu_Close(NULL, NULL, NULL); } .float has_team; -float SetTeam(entity o, int Team) +bool SetTeam(entity o, int Team) { - TC(int, Team); + TC(int, Team); devassert_once(Team); entity tm; if(teamplay) @@ -343,7 +348,6 @@ void Playerchecker_Think(entity this) this.nextthink = time + 0.2; } -void TrueAim_Init(); void PostInit() { entity playerchecker = new_pure(playerchecker); @@ -364,20 +368,21 @@ void PostInit() // In the case of mouse input after a setcursormode(1) call, nPrimary is xpos, nSecondary is ypos. float CSQC_InputEvent(int bInputType, float nPrimary, float nSecondary) { - TC(int, bInputType); - if (HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary)) + TC(int, bInputType); + bool override = false; + override |= HUD_Panel_InputEvent(bInputType, nPrimary, nSecondary); + if (override) return true; - if (QuickMenu_InputEvent(bInputType, nPrimary, nSecondary)) - return true; + override |= QuickMenu_InputEvent(bInputType, nPrimary, nSecondary); - if (HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary)) - return true; + override |= HUD_Radar_InputEvent(bInputType, nPrimary, nSecondary); - if (MapVote_InputEvent(bInputType, nPrimary, nSecondary)) - return true; + override |= MapVote_InputEvent(bInputType, nPrimary, nSecondary); - if (HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary)) + override |= HUD_Minigame_InputEvent(bInputType, nPrimary, nSecondary); + + if(override) return true; return false; @@ -389,8 +394,6 @@ float CSQC_InputEvent(int bInputType, float nPrimary, float nSecondary) // -------------------------------------------------------------------------- // BEGIN OPTIONAL CSQC FUNCTIONS -void Ent_Remove(entity this); - void Ent_RemovePlayerScore(entity this) { if(this.owner) { @@ -531,6 +534,7 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew) race_laptime = 0; race_checkpointtime = 0; hud_dynamic_shake_factor = -1; + spectatee_status_changed_time = time; } if (autocvar_hud_panel_healtharmor_progressbar_gfx) { @@ -555,9 +559,7 @@ NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew) if(!(nags & BIT(2))) { - if(vote_called_vote) - strunzone(vote_called_vote); - vote_called_vote = string_null; + strfree(vote_called_vote); vote_active = 0; } else @@ -575,23 +577,21 @@ NET_HANDLE(ENT_CLIENT_NAGGER, bool isnew) if(nags & BIT(7)) { - if(vote_called_vote) - strunzone(vote_called_vote); - vote_called_vote = strzone(ReadString()); + strcpy(vote_called_vote, ReadString()); } if(nags & 1) { for(j = 0; j < maxclients; ++j) if(playerslots[j]) - playerslots[j].ready = 1; + playerslots[j].ready = true; for(i = 1; i <= maxclients; i += 8) { f = ReadByte(); for(j = i-1, b = BIT(0); b < BIT(8); b <<= 1, ++j) if (!(f & b)) if(playerslots[j]) - playerslots[j].ready = 0; + playerslots[j].ready = false; } } @@ -612,7 +612,7 @@ NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew) if (sf & 1) { for (int j = 0; j < maxclients; ++j) { if (playerslots[j]) { - playerslots[j].eliminated = 1; + playerslots[j].eliminated = true; } } for (int i = 1; i <= maxclients; i += 8) { @@ -622,7 +622,7 @@ NET_HANDLE(ENT_CLIENT_ELIMINATEDPLAYERS, bool isnew) if (f & BIT(b)) continue; int j = i - 1 + b; if (playerslots[j]) { - playerslots[j].eliminated = 0; + playerslots[j].eliminated = false; } } } @@ -951,7 +951,6 @@ void Fog_Force() localcmd(sprintf("\nfog %s\nr_fog_exp2 0\nr_drawfog 1\n", forcefog)); } -void Gamemode_Init(); NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew) { make_pure(this); @@ -959,14 +958,12 @@ NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew) teamplay = _MapInfo_GetTeamPlayBool(gametype); HUD_ModIcons_SetFunc(); FOREACH(Scores, true, { - if (scores_label(it)) strunzone(scores_label(it)); - scores_label(it) = strzone(ReadString()); + strcpy(scores_label(it), ReadString()); scores_flags(it) = ReadByte(); }); for (int i = 0; i < MAX_TEAMSCORE; ++i) { - if (teamscores_label(i)) strunzone(teamscores_label(i)); - teamscores_label(i) = strzone(ReadString()); + strcpy(teamscores_label(i), ReadString()); teamscores_flags(i) = ReadByte(); } return = true; @@ -987,8 +984,7 @@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew) arc_shotorigin[2] = decompressShotOrigin(ReadInt24_t()); arc_shotorigin[3] = decompressShotOrigin(ReadInt24_t()); - if (forcefog) strunzone(forcefog); - forcefog = strzone(ReadString()); + strcpy(forcefog, ReadString()); armorblockpercent = ReadByte() / 255.0; damagepush_speedfactor = ReadByte() / 255.0; @@ -1051,17 +1047,15 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) race_time = ReadInt24_t(); race_previousbesttime = ReadInt24_t(); race_mypreviousbesttime = ReadInt24_t(); - if(race_previousbestname) - strunzone(race_previousbestname); string pbestname = ReadString(); if(autocvar_cl_race_cptimes_onlyself) { race_previousbesttime = race_mypreviousbesttime; race_mypreviousbesttime = 0; - race_previousbestname = strzone(""); + strcpy(race_previousbestname, ""); } else - race_previousbestname = strzone(pbestname); + strcpy(race_previousbestname, pbestname); race_checkpointtime = time; @@ -1087,17 +1081,15 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) race_nextbesttime = ReadInt24_t(); if(b != RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING) // not while spectating (matches server) race_mybesttime = ReadInt24_t(); - if(race_nextbestname) - strunzone(race_nextbestname); string newname = ReadString(); if(autocvar_cl_race_cptimes_onlyself && b != RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING) { race_nextbesttime = race_mybesttime; race_mybesttime = 0; - race_nextbestname = strzone(""); + strcpy(race_nextbestname, ""); } else - race_nextbestname = strzone(newname); + strcpy(race_nextbestname, newname); break; case RACE_NET_CHECKPOINT_HIT_RACE: @@ -1107,13 +1099,11 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) race_mycheckpointlapsdelta = ReadByte(); if(race_mycheckpointlapsdelta >= 128) race_mycheckpointlapsdelta -= 256; - if(race_mycheckpointenemy) - strunzone(race_mycheckpointenemy); int who = ReadByte(); if(who) - race_mycheckpointenemy = strzone(entcs_GetName(who - 1)); + strcpy(race_mycheckpointenemy, entcs_GetName(who - 1)); else - race_mycheckpointenemy = strzone(""); // TODO: maybe string_null works fine here? + strcpy(race_mycheckpointenemy, ""); // TODO: maybe string_null works fine here? break; case RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT: @@ -1123,31 +1113,25 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) race_othercheckpointlapsdelta = ReadByte(); if(race_othercheckpointlapsdelta >= 128) race_othercheckpointlapsdelta -= 256; - if(race_othercheckpointenemy) - strunzone(race_othercheckpointenemy); int what = ReadByte(); if(what) - race_othercheckpointenemy = strzone(entcs_GetName(what - 1)); + strcpy(race_othercheckpointenemy, entcs_GetName(what - 1)); else - race_othercheckpointenemy = strzone(""); // TODO: maybe string_null works fine here? + strcpy(race_othercheckpointenemy, ""); // TODO: maybe string_null works fine here? break; case RACE_NET_PENALTY_RACE: race_penaltyeventtime = time; race_penaltytime = ReadShort(); //race_penaltyaccumulator += race_penaltytime; - if(race_penaltyreason) - strunzone(race_penaltyreason); - race_penaltyreason = strzone(ReadString()); + strcpy(race_penaltyreason, ReadString()); break; case RACE_NET_PENALTY_QUALIFYING: race_penaltyeventtime = time; race_penaltytime = ReadShort(); race_penaltyaccumulator += race_penaltytime; - if(race_penaltyreason) - strunzone(race_penaltyreason); - race_penaltyreason = strzone(ReadString()); + strcpy(race_penaltyreason, ReadString()); break; case RACE_NET_SERVER_RECORD: @@ -1155,21 +1139,16 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) break; case RACE_NET_SPEED_AWARD: race_speedaward = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit); - if(race_speedaward_holder) - strunzone(race_speedaward_holder); - race_speedaward_holder = strzone(ReadString()); - if(race_speedaward_unit) - strunzone(race_speedaward_unit); - race_speedaward_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit)); + strcpy(race_speedaward_holder, ReadString()); + strcpy(race_speedaward_unit, GetSpeedUnit(autocvar_hud_panel_physics_speed_unit)); break; case RACE_NET_SPEED_AWARD_BEST: race_speedaward_alltimebest = ReadInt24_t() * GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit); - if(race_speedaward_alltimebest_holder) - strunzone(race_speedaward_alltimebest_holder); - race_speedaward_alltimebest_holder = strzone(ReadString()); - if(race_speedaward_alltimebest_unit) - strunzone(race_speedaward_alltimebest_unit); - race_speedaward_alltimebest_unit = strzone(GetSpeedUnit(autocvar_hud_panel_physics_speed_unit)); + strcpy(race_speedaward_alltimebest_holder, ReadString()); + strcpy(race_speedaward_alltimebest_unit, GetSpeedUnit(autocvar_hud_panel_physics_speed_unit)); + break; + case RACE_NET_RANKINGS_CNT: + RANKINGS_DISPLAY_CNT = ReadByte(); break; case RACE_NET_SERVER_RANKINGS: float prevpos, del; @@ -1180,49 +1159,44 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) // move other rankings out of the way int i; if (prevpos) { - for (i=prevpos-1;i>pos-1;--i) { + int m = min(prevpos, RANKINGS_DISPLAY_CNT); + for (i=m-1; i>pos-1; --i) { grecordtime[i] = grecordtime[i-1]; - if(grecordholder[i]) - strunzone(grecordholder[i]); - grecordholder[i] = strzone(grecordholder[i-1]); + strcpy(grecordholder[i], grecordholder[i-1]); } } else if (del) { // a record has been deleted by the admin - for (i=pos-1; i<= RANKINGS_CNT-1; ++i) { - if (i == RANKINGS_CNT-1) { // clear out last record + for (i=pos-1; i<= RANKINGS_DISPLAY_CNT-1; ++i) { + if (i == RANKINGS_DISPLAY_CNT-1) { // clear out last record grecordtime[i] = 0; - if (grecordholder[i]) - strunzone(grecordholder[i]); - grecordholder[i] = string_null; + strfree(grecordholder[i]); } else { grecordtime[i] = grecordtime[i+1]; - if (grecordholder[i]) - strunzone(grecordholder[i]); - grecordholder[i] = strzone(grecordholder[i+1]); + strcpy(grecordholder[i], grecordholder[i+1]); } } } else { // player has no ranked record yet - for (i=RANKINGS_CNT-1;i>pos-1;--i) { + for (i=RANKINGS_DISPLAY_CNT-1;i>pos-1;--i) { grecordtime[i] = grecordtime[i-1]; - if(grecordholder[i]) - strunzone(grecordholder[i]); - grecordholder[i] = strzone(grecordholder[i-1]); + strcpy(grecordholder[i], grecordholder[i-1]); } } + if (grecordtime[RANKINGS_DISPLAY_CNT]) { + // kick off the player who fell from the last displayed position + grecordtime[RANKINGS_DISPLAY_CNT] = 0; + strfree(grecordholder[RANKINGS_DISPLAY_CNT]); + } + // store new ranking - if(grecordholder[pos-1] != "") - strunzone(grecordholder[pos-1]); - grecordholder[pos-1] = strzone(ReadString()); + strcpy(grecordholder[pos-1], ReadString()); grecordtime[pos-1] = ReadInt24_t(); if(strdecolorize(grecordholder[pos-1]) == strdecolorize(entcs_GetName(player_localnum))) race_myrank = pos; break; case RACE_NET_SERVER_STATUS: race_status = ReadShort(); - if(race_status_name) - strunzone(race_status_name); - race_status_name = strzone(ReadString()); + strcpy(race_status_name, ReadString()); } return true; } @@ -1249,7 +1223,8 @@ NET_HANDLE(TE_CSQC_PINGPLREPORT, bool isNew) NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) { - complain_weapon = ReadByte(); + int weapon_id = ReadByte(); + complain_weapon = Weapons_from(weapon_id); complain_weapon_type = ReadByte(); return = true; @@ -1258,9 +1233,9 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) switch(complain_weapon_type) { - case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, complain_weapon); break; - case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, complain_weapon); break; - default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, complain_weapon); break; + case 0: Local_Notification(MSG_MULTI, ITEM_WEAPON_NOAMMO, weapon_id); break; + case 1: Local_Notification(MSG_MULTI, ITEM_WEAPON_DONTHAVE, weapon_id); break; + default: Local_Notification(MSG_MULTI, ITEM_WEAPON_UNAVAILABLE, weapon_id); break; } } diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index a2f4d18bba..f0f8f1d4be 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -9,8 +9,10 @@ vector mi_scale; // Minimap string minimapname; -float postinit; +bool postinit; entity gametype; +// temporary hack +#define ISGAMETYPE(NAME) (gametype == MAPINFO_TYPE_##NAME) float FONT_USER = 8; @@ -21,18 +23,27 @@ void draw_cursor(vector pos, vector ofs, string img, vector col, float a); void draw_cursor_normal(vector pos, vector col, float a); void LoadMenuSkinValues(); +void PostInit(); + +void Ent_Remove(entity this); + +void Gamemode_Init(); + +bool SetTeam(entity pl, int Team); + vector hud_fontsize; float RANKINGS_RECEIVED_CNT; +float RANKINGS_DISPLAY_CNT; string grecordholder[RANKINGS_CNT]; float grecordtime[RANKINGS_CNT]; entity playerslots[255]; // 255 is engine limit on maxclients entity teamslots[17]; // 17 teams (including "spectator team") -.float gotscores; +.bool gotscores; .entity owner; -.float ready; -.float eliminated; +.bool ready; +.bool eliminated; .void(entity) draw; IntrusiveList g_drawables; @@ -55,7 +66,7 @@ bool button_attack2; float current_viewzoom; float zoomin_effect; -float warmup_stage; +bool warmup_stage; void Fog_Force(); @@ -64,15 +75,16 @@ string _getcommandkey(string text, string command, bool forcename); #define getcommandkey_forcename(cmd_name, command) _getcommandkey(cmd_name, command, true) string vote_called_vote; -float ready_waiting; -float ready_waiting_for_me; -float vote_waiting; -float vote_waiting_for_me; +bool ready_waiting; +bool ready_waiting_for_me; +bool vote_waiting; +bool vote_waiting_for_me; float current_zoomfraction; -float cs_project_is_b0rked; -float vid_width, vid_height, vid_pixelheight; +int cs_project_is_b0rked; +int vid_width, vid_height; +float vid_pixelheight; float camera_active; // Demo camera is active if set to true float chase_active_backup; @@ -105,7 +117,6 @@ const int MAX_SPECTATORS = 7; int spectatorlist[MAX_SPECTATORS]; int framecount; -.float health; float GetSpeedUnitFactor(int speed_unit); string GetSpeedUnit(int speed_unit); diff --git a/qcsrc/client/mapvoting.qc b/qcsrc/client/mapvoting.qc index 87b6d585b3..37cb59e082 100644 --- a/qcsrc/client/mapvoting.qc +++ b/qcsrc/client/mapvoting.qc @@ -43,7 +43,7 @@ int n_ssdirs; string MapVote_FormatMapItem(int id, string map, float _count, float maxwidth, vector fontsize) { - TC(int, id); + TC(int, id); string pre, post; pre = sprintf("%d. ", id+1); if(mv_detail) @@ -64,7 +64,7 @@ string MapVote_FormatMapItem(int id, string map, float _count, float maxwidth, v vector MapVote_RGB(int id) { - TC(int, id); + TC(int, id); if(!(mv_flags[id] & GTV_AVAILABLE)) return '1 1 1'; if(id == mv_ownvote) @@ -77,7 +77,7 @@ vector MapVote_RGB(int id) void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string gtype, string pic, float _count, int id) { - TC(int, id); + TC(int, id); // Find the correct alpha float alpha; if(!(mv_flags_start[id] & GTV_AVAILABLE)) @@ -189,7 +189,7 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float _count, int id) { - TC(int, id); + TC(int, id); vector img_size = '0 0 0'; string label; float text_size; @@ -264,7 +264,7 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin void MapVote_DrawAbstain(vector pos, float isize, float tsize, float _count, int id) { - TC(int, id); + TC(int, id); vector rgb; float text_size; string label; @@ -281,7 +281,7 @@ void MapVote_DrawAbstain(vector pos, float isize, float tsize, float _count, int vector MapVote_GridVec(vector gridspec, int i, int m) { - TC(int, i); TC(int, m); + TC(int, i); TC(int, m); int r = i % m; return '1 0 0' * (gridspec.x * r) @@ -321,7 +321,7 @@ float MapVote_Selection(vector topleft, vector cellsize, float rows, float colum return mv_mouse_selection; } -vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect); +vector prev_mousepos; void MapVote_Draw() { string map; @@ -340,10 +340,11 @@ void MapVote_Draw() if (!autocvar_hud_cursormode) { - vector mpos = mousepos; - update_mousepos(); - if (mpos.x != mousepos.x || mpos.y != mousepos.y) + if (mousepos.x != prev_mousepos.x || mousepos.y != prev_mousepos.y) + { mv_selection_keyboard = 0; + prev_mousepos = mousepos; + } } center = (vid_conwidth - 1)/2; @@ -486,13 +487,11 @@ void MapVote_Draw() pos.x = (xmax+xmin)*0.5; MapVote_DrawAbstain(pos, dist.x, xmax - xmin, tmp, i); } - - draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha); } void Cmd_MapVote_MapDownload(int argc) { - TC(int, argc); + TC(int, argc); entity pak; if(argc != 2 || !mv_pk3list) @@ -523,7 +522,7 @@ void Cmd_MapVote_MapDownload(int argc) void MapVote_CheckPK3(string pic, string pk3, int id) { - TC(int, id); + TC(int, id); entity pak; pak = spawn(); pak.netname = pk3; @@ -545,7 +544,7 @@ void MapVote_CheckPK3(string pic, string pk3, int id) void MapVote_CheckPic(string pic, string pk3, int id) { - TC(int, id); + TC(int, id); // never try to retrieve a pic for the "don't care" 'map' if(mv_abstain && id == mv_num_maps - 1) return; @@ -588,7 +587,7 @@ void MapVote_ReadMask() void MapVote_ReadOption(int i) { - TC(int, i); + TC(int, i); string map = strzone(ReadString()); string pk3 = strzone(ReadString()); int j = bound(0, ReadByte(), n_ssdirs - 1); @@ -605,7 +604,7 @@ void MapVote_ReadOption(int i) void GameTypeVote_ReadOption(int i) { - TC(int, i); + TC(int, i); string gt = strzone(ReadString()); mv_maps[i] = gt; @@ -650,8 +649,7 @@ void GameTypeVote_ReadOption(int i) void MapVote_Init() { mv_active = 1; - if(autocvar_hud_cursormode) setcursormode(1); - else mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; + if(!autocvar_hud_cursormode) mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; mv_selection = -1; mv_selection_keyboard = 0; @@ -712,13 +710,13 @@ void MapVote_Init() void MapVote_SendChoice(int index) { - TC(int, index); + TC(int, index); localcmd(strcat("\nimpulse ", ftos(index+1), "\n")); } int MapVote_MoveLeft(int pos) { - TC(int, pos); + TC(int, pos); int imp; if ( pos < 0 ) imp = mv_num_maps - 1; @@ -730,7 +728,7 @@ int MapVote_MoveLeft(int pos) } int MapVote_MoveRight(int pos) { - TC(int, pos); + TC(int, pos); int imp; if ( pos < 0 ) imp = 0; @@ -742,7 +740,7 @@ int MapVote_MoveRight(int pos) } int MapVote_MoveUp(int pos) { - TC(int, pos); + TC(int, pos); int imp; if ( pos < 0 ) imp = mv_num_maps - 1; @@ -762,7 +760,7 @@ int MapVote_MoveUp(int pos) } int MapVote_MoveDown(int pos) { - TC(int, pos); + TC(int, pos); int imp; if ( pos < 0 ) imp = 0; @@ -779,7 +777,7 @@ int MapVote_MoveDown(int pos) float MapVote_InputEvent(int bInputType, float nPrimary, float nSecondary) { - TC(int, bInputType); + TC(int, bInputType); float imp; if (!mv_active) diff --git a/qcsrc/client/mapvoting.qh b/qcsrc/client/mapvoting.qh index 2f95102a99..8a6f542a0a 100644 --- a/qcsrc/client/mapvoting.qh +++ b/qcsrc/client/mapvoting.qh @@ -3,7 +3,7 @@ #include <common/constants.qh> void MapVote_Draw(); -void Cmd_MapVote_MapDownload(float argc); +void Cmd_MapVote_MapDownload(int argc); float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary); diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index fd1647ddcc..360305601a 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -121,7 +121,7 @@ void RemoveTeam(entity Team) entity GetTeam(int Team, bool add) { - TC(int, Team); TC(bool, add); + TC(int, Team); TC(bool, add); int num = (Team == NUM_SPECTATOR) ? 16 : Team; if(teamslots[num]) return teamslots[num]; @@ -553,7 +553,7 @@ void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector /** engine callback */ void URI_Get_Callback(int id, int status, string data) { - TC(int, id); TC(int, status); + TC(int, id); TC(int, status); if(url_URI_Get_Callback(id, status, data)) { // handled @@ -577,9 +577,7 @@ void Accuracy_LoadLevels() { if(autocvar_accuracy_color_levels != acc_color_levels) { - if(acc_color_levels) - strunzone(acc_color_levels); - acc_color_levels = strzone(autocvar_accuracy_color_levels); + strcpy(acc_color_levels, autocvar_accuracy_color_levels); acc_levels = tokenize_console(acc_color_levels); if(acc_levels > MAX_ACCURACY_LEVELS) acc_levels = MAX_ACCURACY_LEVELS; diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index f23a3976b5..0143d1a013 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -34,7 +34,7 @@ float PreviewExists(string name); vector Rotate(vector v, float a); -#define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : ((s).health <= 0)) +#define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : (GetResourceAmount((s), RESOURCE_HEALTH) <= 0)) // decolorizes and team colors the player name when needed diff --git a/qcsrc/client/mutators/events.qh b/qcsrc/client/mutators/events.qh index 24d634ce82..cc6fced9b8 100644 --- a/qcsrc/client/mutators/events.qh +++ b/qcsrc/client/mutators/events.qh @@ -2,6 +2,11 @@ #include <common/mutators/base.qh> +// register all possible hooks here + +// to use a hook, first register your mutator using REGISTER_MUTATOR +// then create your function using MUTATOR_HOOKFUNCTION + /** * Called when a client command is parsed * NOTE: hooks MUST start with if (MUTATOR_RETURNVALUE) return false; diff --git a/qcsrc/client/player_skeleton.qc b/qcsrc/client/player_skeleton.qc index 55e54d8ac2..d9c9ab1421 100644 --- a/qcsrc/client/player_skeleton.qc +++ b/qcsrc/client/player_skeleton.qc @@ -2,19 +2,19 @@ #include <common/physics/movetypes/movetypes.qh> #include <common/physics/player.qh> -#include "mutators/events.qh" +#include <client/mutators/_mod.qh> #include "../lib/csqcmodel/cl_player.qh" #include "../lib/warpzone/anglestransform.qh" .float v_angle_save_x; -class(Skeleton) .float skeleton_info_modelindex; -class(Skeleton) .float skeleton_info_skin; +classfield(Skeleton) .float skeleton_info_modelindex; +classfield(Skeleton) .float skeleton_info_skin; const int BONETYPE_LOWER = 0; const int BONETYPE_UPPER = 1; const int MAX_BONES = 128; -class(Skeleton) .float skeleton_bonetype[MAX_BONES]; -class(Skeleton) .float skeleton_numbones; +classfield(Skeleton) .float skeleton_bonetype[MAX_BONES]; +classfield(Skeleton) .float skeleton_numbones; void skeleton_loadinfo(entity e) { @@ -83,7 +83,7 @@ void skeleton_markbones(entity e) void skel_set_boneabs(float s, int bone, vector absorg) { - TC(int, bone); + TC(int, bone); vector absang = fixedvectoangles2(v_forward, v_up); vector parentorg = skel_get_boneabs(s, skel_get_boneparent(s, bone)); @@ -113,7 +113,7 @@ void free_skeleton_from_frames(entity e) void skeleton_from_frames(entity e, bool is_dead) { - TC(bool, is_dead); + TC(bool, is_dead); float m = e.modelindex; if(!e.skeletonindex) { diff --git a/qcsrc/client/player_skeleton.qh b/qcsrc/client/player_skeleton.qh index 8c5969b09f..082335e4b0 100644 --- a/qcsrc/client/player_skeleton.qh +++ b/qcsrc/client/player_skeleton.qh @@ -7,8 +7,8 @@ void skeleton_from_frames(entity e, float is_dead); void skeleton_loadinfo(entity e); entityclass(Skeleton); -class(Skeleton) .float bone_upperbody; -class(Skeleton) .int bone_weapon; -class(Skeleton) .float bone_aim[MAX_AIM_BONES]; -class(Skeleton) .float bone_aimweight[MAX_AIM_BONES]; -class(Skeleton) .float fixbone; +classfield(Skeleton) .float bone_upperbody; +classfield(Skeleton) .int bone_weapon; +classfield(Skeleton) .float bone_aim[MAX_AIM_BONES]; +classfield(Skeleton) .float bone_aimweight[MAX_AIM_BONES]; +classfield(Skeleton) .float fixbone; diff --git a/qcsrc/client/resources.qc b/qcsrc/client/resources.qc new file mode 100644 index 0000000000..285ebad639 --- /dev/null +++ b/qcsrc/client/resources.qc @@ -0,0 +1,87 @@ +#include "resources.qh" +#include <common/items/item/ammo.qh> + +/// \file +/// \brief Source file that contains implementation of the resource system. +/// \copyright GNU GPLv2 or any later version. + +float GetResourceAmount(entity e, int resource_type) +{ + .float resource_field = GetResourceField(resource_type); + return e.(resource_field); +} + +bool SetResourceAmountExplicit(entity e, int resource_type, float amount) +{ + .float resource_field = GetResourceField(resource_type); + if (e.(resource_field) != amount) + { + e.(resource_field) = amount; + return true; + } + return false; +} + +void SetResourceAmount(entity e, int resource_type, float amount) +{ + SetResourceAmountExplicit(e, resource_type, amount); +} + +void TakeResource(entity receiver, int resource_type, float amount) +{ + if (amount == 0) + { + return; + } + SetResourceAmount(receiver, resource_type, + GetResourceAmount(receiver, resource_type) - amount); +} + +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit) +{ + if (amount == 0) + { + return; + } + float current_amount = GetResourceAmount(receiver, resource_type); + if (current_amount - amount < limit) + { + amount = limit + current_amount; + } + TakeResource(receiver, resource_type, amount); +} + +int GetResourceType(.float resource_field) +{ + switch (resource_field) + { + case health: { return RESOURCE_HEALTH; } + case armorvalue: { return RESOURCE_ARMOR; } + case ammo_shells: { return RESOURCE_SHELLS; } + case ammo_nails: { return RESOURCE_BULLETS; } + case ammo_rockets: { return RESOURCE_ROCKETS; } + case ammo_cells: { return RESOURCE_CELLS; } + case ammo_plasma: { return RESOURCE_PLASMA; } + case ammo_fuel: { return RESOURCE_FUEL; } + } + error("GetResourceType: Invalid field."); + return 0; +} + +.float GetResourceField(int resource_type) +{ + switch (resource_type) + { + case RESOURCE_HEALTH: { return health; } + case RESOURCE_ARMOR: { return armorvalue; } + case RESOURCE_SHELLS: { return ammo_shells; } + case RESOURCE_BULLETS: { return ammo_nails; } + case RESOURCE_ROCKETS: { return ammo_rockets; } + case RESOURCE_CELLS: { return ammo_cells; } + case RESOURCE_PLASMA: { return ammo_plasma; } + case RESOURCE_FUEL: { return ammo_fuel; } + } + error("GetResourceField: Invalid resource type."); + return health; +} diff --git a/qcsrc/client/resources.qh b/qcsrc/client/resources.qh new file mode 100644 index 0000000000..3aaa8aab59 --- /dev/null +++ b/qcsrc/client/resources.qh @@ -0,0 +1,61 @@ +#pragma once + +/// \file +/// \brief Header file that describes the resource system. +/// \copyright GNU GPLv2 or any later version. + +#include <common/resources.qh> + +// ============================ Public API ==================================== + +/// \brief Returns the current amount of resource the given entity has. +/// \param[in] e Entity to check. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \return Current amount of resource the given entity has. +float GetResourceAmount(entity e, int resource_type); + +/// \brief Sets the resource amount of an entity without calling any hooks. +/// \param[in,out] e Entity to adjust. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to set. +/// \return Boolean for whether the ammo amount was changed +bool SetResourceAmountExplicit(entity e, int resource_type, float amount); + +/// \brief Sets the current amount of resource the given entity will have. +/// \param[in,out] e Entity to adjust. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to set. +/// \return No return. +void SetResourceAmount(entity e, int resource_type, float amount); + +/// \brief Takes an entity some resource. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \return No return. +void TakeResource(entity receiver, int resource_type, float amount); + +/// \brief Takes an entity some resource but not less than a limit. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \param[in] limit Limit of resources to take. +/// \return No return. +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit); + +// ===================== Legacy and/or internal API =========================== + +/// \brief Converts an entity field to resource type. +/// \param[in] resource_field Entity field to convert. +/// \return Resource type (a RESOURCE_* constant). +int GetResourceType(.float resource_field); + +/// \brief Converts resource type (a RESOURCE_* constant) to entity field. +/// \param[in] resource_type Type of the resource. +/// \return Entity field for that resource. +.float GetResourceField(int resource_type); + +/// \brief Legacy fields for the resources. To be removed. +.float health; +.float armorvalue; diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 8a7d225bff..2fc1559494 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -2,6 +2,7 @@ #include "autocvars.qh" #include "miscfunctions.qh" +#include "resources.qh" #include "hud/_mod.qh" #include <common/ent_cs.qh> @@ -37,8 +38,18 @@ const float SHOWNAMES_FADESPEED = 4; const float SHOWNAMES_FADEDELAY = 0.4; void Draw_ShowNames(entity this) { - if (this.sv_entnum == (current_player + 1)) // self or spectatee - if (!(autocvar_hud_shownames_self && autocvar_chase_active)) return; + if (this.sv_entnum == current_player + 1) // self or spectatee + { + if (!autocvar_chase_active) + return; + + if (!autocvar_hud_shownames_self + && !(spectatee_status > 0 && time <= spectatee_status_changed_time + 1)) + { + return; + } + } + if (!this.sameteam && !autocvar_hud_shownames_enemies) return; bool hit; if (!autocvar_hud_shownames_crosshairdistance && this.sameteam) @@ -117,7 +128,7 @@ void Draw_ShowNames(entity this) // FIXME: alpha is negative when dead, breaking death fade if (!this.csqcmodel_isdead) a *= f; } - if (a < ALPHA_MIN_VISIBLE && gametype != MAPINFO_TYPE_CTS) return; + if (a < ALPHA_MIN_VISIBLE && ISGAMETYPE(CTS)) return; if (vdist(this.origin - view_origin, >=, max_shot_distance)) return; float dist = vlen(this.origin - view_origin); if (autocvar_hud_shownames_maxdistance) @@ -157,10 +168,10 @@ void Draw_ShowNames(entity this) this.healthvalue / autocvar_hud_panel_healtharmor_maxhealth, false, 1, '1 0 0', a, DRAWFLAG_NORMAL); } - if (this.armorvalue > 0) + if (GetResourceAmount(this, RESOURCE_ARMOR) > 0) { HUD_Panel_DrawProgressBar(pos + eX * 0.5 * mySize.x, sz, "nametag_statusbar", - this.armorvalue / autocvar_hud_panel_healtharmor_maxarmor, false, 0, '0 1 0', a, + GetResourceAmount(this, RESOURCE_ARMOR) / autocvar_hud_panel_healtharmor_maxarmor, false, 0, '0 1 0', a, DRAWFLAG_NORMAL); } } @@ -193,13 +204,13 @@ void Draw_ShowNames_All() if (entcs.m_entcs_private) { it.healthvalue = entcs.healthvalue; - it.armorvalue = entcs.armorvalue; + SetResourceAmountExplicit(it, RESOURCE_ARMOR, GetResourceAmount(entcs, RESOURCE_ARMOR)); it.sameteam = true; } else { it.healthvalue = 0; - it.armorvalue = 0; + SetResourceAmountExplicit(it, RESOURCE_ARMOR, 0); it.sameteam = false; } bool dead = entcs_IsDead(i) || entcs_IsSpectating(i); diff --git a/qcsrc/client/shownames.qh b/qcsrc/client/shownames.qh index 24f6568d26..a05ca701d0 100644 --- a/qcsrc/client/shownames.qh +++ b/qcsrc/client/shownames.qh @@ -1,10 +1,10 @@ #pragma once entityclass(ShowNames); -class(ShowNames) .float healthvalue; -class(ShowNames) .float armorvalue; -class(ShowNames) .float sameteam; -class(ShowNames) .float fadedelay; -class(ShowNames) .float pointtime; +classfield(ShowNames) .float healthvalue; +classfield(ShowNames) .float armorvalue; +classfield(ShowNames) .float sameteam; +classfield(ShowNames) .float fadedelay; +classfield(ShowNames) .float pointtime; void Draw_ShowNames_All(); diff --git a/qcsrc/client/teamradar.qc b/qcsrc/client/teamradar.qc index c5f1c2fb4b..26de41e724 100644 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@ -93,18 +93,19 @@ void draw_teamradar_player(vector coord3d, vector pangles, vector rgb) coord = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(coord3d)); - makevectors(pangles - '0 1 0' * teamradar_angle); + vector forward = '0 0 0', right = '0 0 0', up = '0 0 0'; + MAKEVECTORS(makevectors, pangles - '0 1 0' * teamradar_angle, forward, right, up); if(v_flipped) { - v_forward.x = -v_forward.x; - v_right.x = -v_right.x; - v_up.x = -v_up.x; + forward.x = -forward.x; + right.x = -right.x; + up.x = -up.x; // TODO: unused! } - v_forward.z = 0; - v_forward = normalize(v_forward); - v_forward.y *= -1.0; - v_right.x = -v_forward.y; - v_right.y = v_forward.x; + forward.z = 0; + forward = normalize(forward); + forward.y *= -1.0; + right.x = -forward.y; + right.y = forward.x; if(rgb == '1 1 1') rgb2 = '0 0 0'; @@ -112,17 +113,17 @@ void draw_teamradar_player(vector coord3d, vector pangles, vector rgb) rgb2 = '1 1 1'; R_BeginPolygon("", 0); - R_PolygonVertex(coord+v_forward*3, '0 0 0', rgb2, panel_fg_alpha); - R_PolygonVertex(coord+v_right*4-v_forward*2.5, '0 1 0', rgb2, panel_fg_alpha); - R_PolygonVertex(coord-v_forward*2, '1 0 0', rgb2, panel_fg_alpha); - R_PolygonVertex(coord-v_right*4-v_forward*2.5, '1 1 0', rgb2, panel_fg_alpha); + R_PolygonVertex(coord+forward*3, '0 0 0', rgb2, panel_fg_alpha); + R_PolygonVertex(coord+right*4-forward*2.5, '0 1 0', rgb2, panel_fg_alpha); + R_PolygonVertex(coord-forward*2, '1 0 0', rgb2, panel_fg_alpha); + R_PolygonVertex(coord-right*4-forward*2.5, '1 1 0', rgb2, panel_fg_alpha); R_EndPolygon(); R_BeginPolygon("", 0); - R_PolygonVertex(coord+v_forward*2, '0 0 0', rgb, panel_fg_alpha); - R_PolygonVertex(coord+v_right*3-v_forward*2, '0 1 0', rgb, panel_fg_alpha); - R_PolygonVertex(coord-v_forward, '1 0 0', rgb, panel_fg_alpha); - R_PolygonVertex(coord-v_right*3-v_forward*2, '1 1 0', rgb, panel_fg_alpha); + R_PolygonVertex(coord+forward*2, '0 0 0', rgb, panel_fg_alpha); + R_PolygonVertex(coord+right*3-forward*2, '0 1 0', rgb, panel_fg_alpha); + R_PolygonVertex(coord-forward, '1 0 0', rgb, panel_fg_alpha); + R_PolygonVertex(coord-right*3-forward*2, '1 1 0', rgb, panel_fg_alpha); R_EndPolygon(); } @@ -149,7 +150,7 @@ void draw_teamradar_icon(vector coord, entity icon, entity pingdata, vector rgb, void draw_teamradar_link(vector start, vector end, int colors) { - TC(int, colors); + TC(int, colors); vector c0, c1, norm; start = teamradar_texcoord_to_2dcoord(teamradar_3dcoord_to_texcoord(start)); diff --git a/qcsrc/client/teamradar.qh b/qcsrc/client/teamradar.qh index 251c1a53f1..41c7d46c56 100644 --- a/qcsrc/client/teamradar.qh +++ b/qcsrc/client/teamradar.qh @@ -4,10 +4,10 @@ const int MAX_TEAMRADAR_TIMES = 32; entityclass(TeamRadar); // to make entities have dots on the team radar -class(TeamRadar) .float teamradar_icon; -class(TeamRadar) .float teamradar_times[MAX_TEAMRADAR_TIMES]; -class(TeamRadar) .int teamradar_time_index; -class(TeamRadar) .vector teamradar_color; +classfield(TeamRadar) .float teamradar_icon; +classfield(TeamRadar) .float teamradar_times[MAX_TEAMRADAR_TIMES]; +classfield(TeamRadar) .int teamradar_time_index; +classfield(TeamRadar) .vector teamradar_color; float teamradar_angle; // player yaw angle vector teamradar_origin3d_in_texcoord; // player origin diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 9bfa8db9ad..ffab462363 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -9,7 +9,7 @@ #include "hud/panel/scoreboard.qh" #include "hud/panel/quickmenu.qh" -#include "mutators/events.qh" +#include <client/mutators/_mod.qh> #include <common/animdecide.qh> #include <common/deathtypes/all.qh> @@ -17,12 +17,13 @@ #include <common/anim.qh> #include <common/constants.qh> #include <common/net_linked.qh> +#include <common/net_notice.qh> #include <common/debug.qh> #include <common/mapinfo.qh> #include <common/gamemodes/_mod.qh> #include <common/physics/player.qh> #include <common/stats.qh> -#include <common/triggers/target/music.qh> +#include <common/mapobjects/target/music.qh> #include <common/teams.qh> #include <common/wepent.qh> @@ -30,8 +31,10 @@ #include <common/vehicles/all.qh> #include <common/weapons/_all.qh> +#include <common/mutators/mutator/overkill/oknex.qh> +#include <common/mutators/mutator/waypoints/all.qh> #include <common/viewloc.qh> -#include <common/triggers/trigger/viewloc.qh> +#include <common/mapobjects/trigger/viewloc.qh> #include <common/minigames/cl_minigames.qh> #include <common/minigames/cl_minigames_hud.qh> @@ -45,6 +48,7 @@ #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOSHADOW | EF_SELECTABLE | EF_TELEPORT_BIT) float autocvar_cl_viewmodel_scale; +float autocvar_cl_viewmodel_alpha; bool autocvar_cl_bobmodel; float autocvar_cl_bobmodel_speed; @@ -294,12 +298,9 @@ void viewmodel_draw(entity this) if(!this.activeweapon || !autocvar_r_drawviewmodel) return; int mask = (intermission || (STAT(HEALTH) <= 0) || autocvar_chase_active) ? 0 : MASK_NORMAL; - float a = this.alpha; - static bool wasinvehicle; + float a = ((autocvar_cl_viewmodel_alpha) ? bound(-1, autocvar_cl_viewmodel_alpha, this.m_alpha) : this.m_alpha); bool invehicle = player_localentnum > maxclients; if (invehicle) a = -1; - else if (wasinvehicle) a = 1; - wasinvehicle = invehicle; Weapon wep = this.activeweapon; int c = entcs_GetClientColors(current_player); vector g = weaponentity_glowmod(wep, NULL, c, this); @@ -370,7 +371,35 @@ STATIC_INIT(viewmodel) { viewmodels[slot] = new(viewmodel); } -void Porto_Draw(entity this); +float showfps_prevfps; +float showfps_prevfps_time; +int showfps_framecounter; + +void fpscounter_update() +{ + if(!STAT(SHOWFPS)) + return; + + float currentTime = gettime(GETTIME_REALTIME); + showfps_framecounter += 1; + if(currentTime - showfps_prevfps_time > STAT(SHOWFPS)) + { + showfps_prevfps = showfps_framecounter/(currentTime - showfps_prevfps_time); + showfps_framecounter = 0; + showfps_prevfps_time = currentTime; + + int channel = MSG_C2S; + WriteHeader(channel, fpsreport); + WriteShort(channel, bound(0, rint(showfps_prevfps), 65535)); // prevent insane fps values + } +} + +STATIC_INIT(fpscounter_init) +{ + float currentTime = gettime(GETTIME_REALTIME); + showfps_prevfps_time = currentTime; // we must initialize it to avoid an instant low frame sending +} + STATIC_INIT(Porto) { entity e = new_pure(porto); @@ -396,6 +425,10 @@ void Porto_Draw(entity this) vector pos = view_origin; vector dir = view_forward; + makevectors(((autocvar_chase_active) ? warpzone_save_view_angles : view_angles)); + pos += v_right * -wepent.movedir.y + + v_up * wepent.movedir.z; + if (wepent.angles_held_status) { makevectors(wepent.angles_held); @@ -460,9 +493,8 @@ vector GetCurrentFov(float fov) if(zoomfactor < 1 || zoomfactor > 30) zoomfactor = 2.5; zoomspeed = autocvar_cl_zoomspeed; - if(zoomspeed >= 0) - if(zoomspeed < 0.5 || zoomspeed > 16) - zoomspeed = 3.5; + if (zoomspeed >= 0 && (zoomspeed < 0.5 || zoomspeed > 16)) + zoomspeed = 3.5; zoomdir = button_zoom; @@ -495,7 +527,11 @@ vector GetCurrentFov(float fov) if(zoomdir) { zoomin_effect = 0; } - if(camera_active) + if (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2) + { + current_viewzoom = 1; + } + else if (camera_active) { current_viewzoom = min(1, current_viewzoom + drawframetime); } @@ -539,10 +575,10 @@ vector GetCurrentFov(float fov) if(autocvar_cl_velocityzoom_enabled && autocvar_cl_velocityzoom_type) // _type = 0 disables velocity zoom too { - if(intermission) { curspeed = 0; } + if (intermission || (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2)) + curspeed = 0; else { - makevectors(view_angles); v = pmove_vel; if(csqcplayer) @@ -597,6 +633,8 @@ vector GetOrthoviewFOV(vector ov_worldmin, vector ov_worldmax, vector ov_mid, ve // this function must match W_SetupShot! float zoomscript_caught; +bool minigame_wasactive; + vector wcross_origin; float wcross_scale_prev, wcross_alpha_prev; vector wcross_color_prev; @@ -669,6 +707,7 @@ float TrueAimCheck(entity wepent) case WEP_MORTAR: // toss curve return SHOTTYPE_HITWORLD; case WEP_VORTEX: + case WEP_OVERKILL_NEX: case WEP_VAPORIZER: mv = MOVE_NORMAL; break; @@ -737,8 +776,6 @@ float TrueAimCheck(entity wepent) return SHOTTYPE_HITWORLD; } -void PostInit(); -void CSQC_Demo_Camera(); float camera_mode; const float CAMERA_FREE = 1; const float CAMERA_CHASE = 2; @@ -766,34 +803,41 @@ vector liquidcolor_prev; float eventchase_current_distance; float eventchase_running; -bool WantEventchase(entity this) +int WantEventchase(entity this) { if(autocvar_cl_orthoview) - return false; + return 0; if(STAT(GAME_STOPPED) || intermission) - return true; + return 1; if(this.viewloc) - return true; + return 1; if(spectatee_status >= 0) { if(hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0)) - return true; + return 1; if(MUTATOR_CALLHOOK(WantEventchase, this)) - return true; + return 1; if(autocvar_cl_eventchase_frozen && STAT(FROZEN)) - return true; + return 1; if(autocvar_cl_eventchase_death && (STAT(HEALTH) <= 0)) { if(autocvar_cl_eventchase_death == 2) { // don't stop eventchase once it's started (even if velocity changes afterwards) if(this.velocity == '0 0 0' || eventchase_running) - return true; + return 1; } - else return true; + else return 1; + } + if (spectatee_status > 0 && autocvar_cl_eventchase_spectated_change) + { + if (time <= spectatee_status_changed_time + min(3, autocvar_cl_eventchase_spectated_change_time)) + return 1; + else if (eventchase_running) + return -1; // disable chase_active while eventchase is still enabled so to avoid a glicth } } - return false; + return 0; } void HUD_Crosshair_Vehicle(entity this) @@ -1199,6 +1243,8 @@ void HUD_Crosshair(entity this) float arc_heat = wepent.arc_heat_percent; float vcharge = wepent.vortex_charge; float vchargepool = wepent.vortex_chargepool_ammo; + float oknex_charge_ = wepent.oknex_charge; + float oknex_chargepool_ = wepent.oknex_chargepool_ammo; if(vortex_charge_movingavg == 0) // this should only happen if we have just loaded up the game vortex_charge_movingavg = vcharge; @@ -1224,6 +1270,26 @@ void HUD_Crosshair(entity this) ring_rgb = wcross_color; ring_image = "gfx/crosshair_ring_nexgun.tga"; } + else if (autocvar_crosshair_ring && (wepent.activeweapon == WEP_OVERKILL_NEX) && oknex_charge_ && autocvar_crosshair_ring_vortex) + { + if (oknex_chargepool_ || use_vortex_chargepool) { + use_vortex_chargepool = 1; + ring_inner_value = oknex_chargepool_; + } else { + vortex_charge_movingavg = (1 - autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate) * vortex_charge_movingavg + autocvar_crosshair_ring_vortex_currentcharge_movingavg_rate * oknex_charge_; + ring_inner_value = bound(0, autocvar_crosshair_ring_vortex_currentcharge_scale * (oknex_charge_ - vortex_charge_movingavg), 1); + } + + ring_inner_alpha = autocvar_crosshair_ring_vortex_inner_alpha; + ring_inner_rgb = eX * autocvar_crosshair_ring_vortex_inner_color_red + eY * autocvar_crosshair_ring_vortex_inner_color_green + eZ * autocvar_crosshair_ring_vortex_inner_color_blue; + ring_inner_image = "gfx/crosshair_ring_inner.tga"; + + // draw the outer ring to show the current charge of the weapon + ring_value = oknex_charge_; + ring_alpha = autocvar_crosshair_ring_vortex_alpha; + ring_rgb = wcross_color; + ring_image = "gfx/crosshair_ring_nexgun.tga"; + } else if (autocvar_crosshair_ring && wepent.activeweapon == WEP_MINE_LAYER && WEP_CVAR(minelayer, limit) && autocvar_crosshair_ring_minelayer) { ring_value = bound(0, wepent.minelayer_mines / WEP_CVAR(minelayer, limit), 1); // if you later need to use the count of bullets in another place, then add a float for it. For now, no need to. @@ -1346,12 +1412,8 @@ void HUD_Crosshair(entity this) wcross_scale_goal_prev = 0; wcross_alpha_goal_prev = 0; wcross_changedonetime = 0; - if(wcross_name_goal_prev) - strunzone(wcross_name_goal_prev); - wcross_name_goal_prev = string_null; - if(wcross_name_goal_prev_prev) - strunzone(wcross_name_goal_prev_prev); - wcross_name_goal_prev_prev = string_null; + strfree(wcross_name_goal_prev); + strfree(wcross_name_goal_prev_prev); wcross_name_changestarttime = 0; wcross_name_changedonetime = 0; wcross_name_alpha_goal_prev = 0; @@ -1477,7 +1539,7 @@ void HUD_Draw(entity this) if(autocvar_r_letterbox == 0) if(autocvar_viewsize < 120) { - if(!(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) + if(!(ISGAMETYPE(RACE) || ISGAMETYPE(CTS))) Accuracy_LoadLevels(); HUD_Main(); @@ -1504,6 +1566,51 @@ void ViewLocation_Mouse() //draw_cursor(viewloc_mousepos, '0.5 0.5 0', "/cursor_move", '1 1 1', cursor_alpha); } +void HUD_Cursor_Show() +{ + float cursor_alpha = 1 - autocvar__menu_alpha; + if(cursor_type == CURSOR_NORMAL) + draw_cursor_normal(mousepos, '1 1 1', cursor_alpha); + else if(cursor_type == CURSOR_MOVE) + draw_cursor(mousepos, '0.5 0.5 0', "/cursor_move", '1 1 1', cursor_alpha); + else if(cursor_type == CURSOR_RESIZE) + draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize", '1 1 1', cursor_alpha); + else if(cursor_type == CURSOR_RESIZE2) + draw_cursor(mousepos, '0.5 0.5 0', "/cursor_resize2", '1 1 1', cursor_alpha); +} + +void HUD_Mouse(entity player) +{ + if(autocvar__menu_alpha == 1) + return; + + if(!cursor_active) + { + if(player.viewloc && (player.viewloc.spawnflags & VIEWLOC_FREEAIM)) + ViewLocation_Mouse(); // NOTE: doesn't use cursormode + return; + } + + if(!autocvar_hud_cursormode) + update_mousepos(); + + if(autocvar__hud_configure) + HUD_Panel_Mouse(); + else + { + if (HUD_MinigameMenu_IsOpened()) + HUD_Minigame_Mouse(); + if (QuickMenu_IsOpened()) + QuickMenu_Mouse(); + if (HUD_Radar_Clickable()) + HUD_Radar_Mouse(); + } + + prevMouseClicked = mouseClicked; + + HUD_Cursor_Show(); +} + bool ov_enabled; float oldr_nearclip; float oldr_farclip_base; @@ -1512,15 +1619,12 @@ float oldr_novis; float oldr_useportalculling; float oldr_useinfinitefarclip; -void cl_notice_run(); - float prev_myteam; int lasthud; float vh_notice_time; -void WaypointSprite_Load(); void CSQC_UpdateView(entity this, float w, float h) { - TC(int, w); TC(int, h); + TC(int, w); TC(int, h); entity e; float fov; float f; @@ -1643,7 +1747,8 @@ void CSQC_UpdateView(entity this, float w, float h) } } - if(WantEventchase(this)) + int eventchase = WantEventchase(this); + if (eventchase) { vector current_view_origin_override = '0 0 0'; vector view_offset_override = '0 0 0'; @@ -1721,7 +1826,8 @@ void CSQC_UpdateView(entity this, float w, float h) if(!local_player.viewloc) setproperty(VF_ANGLES, WarpZone_TransformVAngles(WarpZone_trace_transform, view_angles)); } - else if(autocvar_chase_active < 0) // time to disable chase_active if it was set by this code + + if (eventchase <= 0 && autocvar_chase_active < 0) // time to disable chase_active if it was set by this code { eventchase_running = false; cvar_set("chase_active", "0"); @@ -1840,10 +1946,7 @@ void CSQC_UpdateView(entity this, float w, float h) // Render the Scene view_origin = getpropertyvec(VF_ORIGIN); view_angles = getpropertyvec(VF_ANGLES); - makevectors(view_angles); - view_forward = v_forward; - view_right = v_right; - view_up = v_up; + MAKEVECTORS(makevectors, view_angles, view_forward, view_right, view_up); #ifdef BLURTEST if(time > blurtest_time0 && time < blurtest_time1) @@ -1866,6 +1969,7 @@ void CSQC_UpdateView(entity this, float w, float h) TargetMusic_Advance(); Fog_Force(); + fpscounter_update(); if(drawtime == 0) drawframetime = 0.01666667; // when we don't know fps yet, we assume 60fps @@ -1911,6 +2015,20 @@ void CSQC_UpdateView(entity this, float w, float h) } } + if(active_minigame && HUD_MinigameMenu_IsOpened()) + { + if(!minigame_wasactive) + { + localcmd("+button14\n"); + minigame_wasactive = true; + } + } + else if(minigame_wasactive) + { + localcmd("-button14\n"); + minigame_wasactive = false; + } + ColorTranslateMode = autocvar_cl_stripcolorcodes; for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) @@ -2011,7 +2129,7 @@ void CSQC_UpdateView(entity this, float w, float h) IL_EACH(g_drawables, it.draw, it.draw(it)); - addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); + addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); // TODO: .health is used in cl_deathfade (a feature we have turned off currently) renderscene(); // now switch to 2D drawing mode by calling a 2D drawing function @@ -2346,7 +2464,7 @@ void CSQC_UpdateView(entity this, float w, float h) else if(cvar("r_glsl_postprocess") == 2) cvar_set("r_glsl_postprocess", "0"); - /*if(gametype == MAPINFO_TYPE_CTF) + /*if(ISGAMETYPE(CTF)) { ctf_view(); } else */ @@ -2395,16 +2513,7 @@ void CSQC_UpdateView(entity this, float w, float h) cvar_set("vid_conheight", h0); } - if(autocvar__hud_configure) - HUD_Panel_Mouse(); - else if (HUD_MinigameMenu_IsOpened() || active_minigame) - HUD_Minigame_Mouse(); - else if(QuickMenu_IsOpened()) - QuickMenu_Mouse(); - else if(local_player.viewloc && (local_player.viewloc.spawnflags & VIEWLOC_FREEAIM)) - ViewLocation_Mouse(); // NOTE: doesn't use cursormode - else - HUD_Radar_Mouse(); + HUD_Mouse(local_player); cl_notice_run(); unpause_update(); diff --git a/qcsrc/client/view.qh b/qcsrc/client/view.qh index 0a2c5c0c70..5bce2fe324 100644 --- a/qcsrc/client/view.qh +++ b/qcsrc/client/view.qh @@ -4,6 +4,21 @@ vector crosshair_getcolor(entity this, float health_stat); +void calc_followmodel_ofs(entity view); + +void Porto_Draw(entity this); + +void CSQC_Demo_Camera(); + +void TrueAim_Init(); + entity viewmodels[MAX_WEAPONSLOTS]; vector viewloc_mousepos; + +bool cursor_active; +int cursor_type; +const int CURSOR_NORMAL = 0; +const int CURSOR_MOVE = 1; +const int CURSOR_RESIZE = 2; +const int CURSOR_RESIZE2 = 3; diff --git a/qcsrc/client/wall.qc b/qcsrc/client/wall.qc deleted file mode 100644 index b572809548..0000000000 --- a/qcsrc/client/wall.qc +++ /dev/null @@ -1,230 +0,0 @@ -#include "wall.qh" - -#include "autocvars.qh" -#include "main.qh" -#include "bgmscript.qh" - - -#include "../lib/csqcmodel/interpolate.qh" - -.float alpha; -.float scale; -.vector movedir; - -void Ent_Wall_PreDraw(entity this) -{ - if (this.inactive) - { - this.alpha = 0; - } - else - { - vector org = getpropertyvec(VF_ORIGIN); - if(!checkpvs(org, this)) - this.alpha = 0; - else if(this.fade_start || this.fade_end) { - vector offset = '0 0 0'; - offset_z = this.fade_vertical_offset; - float player_dist = vlen(org - this.origin - 0.5 * (this.mins + this.maxs) + offset); - if (this.fade_end == this.fade_start) - { - if (player_dist >= this.fade_start) - this.alpha = 0; - else - this.alpha = 1; - } - else - { - this.alpha = (this.alpha_min + this.alpha_max * bound(0, - (this.fade_end - player_dist) - / (this.fade_end - this.fade_start), 1)) / 100.0; - } - } - else - { - this.alpha = 1; - } - } - if(this.alpha <= 0) - this.drawmask = 0; - else - this.drawmask = MASK_NORMAL; -} - -void Ent_Wall_Draw(entity this) -{ - float f; - var .vector fld; - - if(this.bgmscriptangular) - fld = angles; - else - fld = origin; - this.(fld) = this.saved; - - if(this.lodmodelindex1) - { - if(autocvar_cl_modeldetailreduction <= 0) - { - if(this.lodmodelindex2 && autocvar_cl_modeldetailreduction <= -2) - this.modelindex = this.lodmodelindex2; - else if(autocvar_cl_modeldetailreduction <= -1) - this.modelindex = this.lodmodelindex1; - else - this.modelindex = this.lodmodelindex0; - } - else - { - float distance = vlen(NearestPointOnBox(this, view_origin) - view_origin); - f = (distance * current_viewzoom + 100.0) * autocvar_cl_modeldetailreduction; - f *= 1.0 / bound(0.01, view_quality, 1); - if(this.lodmodelindex2 && f > this.loddistance2) - this.modelindex = this.lodmodelindex2; - else if(f > this.loddistance1) - this.modelindex = this.lodmodelindex1; - else - this.modelindex = this.lodmodelindex0; - } - } - - InterpolateOrigin_Do(this); - - this.saved = this.(fld); - - f = doBGMScript(this); - if(f >= 0) - { - if(this.lip < 0) // < 0: alpha goes from 1 to 1-|lip| when toggled (toggling subtracts lip) - this.alpha = 1 + this.lip * f; - else // > 0: alpha goes from 1-|lip| to 1 when toggled (toggling adds lip) - this.alpha = 1 - this.lip * (1 - f); - this.(fld) = this.(fld) + this.movedir * f; - } - else - this.alpha = 1; - - if(this.alpha >= ALPHA_MIN_VISIBLE) - this.drawmask = MASK_NORMAL; - else - this.drawmask = 0; -} - -void Ent_Wall_Remove(entity this) -{ - if(this.bgmscript) - strunzone(this.bgmscript); - this.bgmscript = string_null; -} - -NET_HANDLE(ENT_CLIENT_WALL, bool isnew) -{ - int f; - var .vector fld; - - InterpolateOrigin_Undo(this); - this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; - - if(this.bgmscriptangular) - fld = angles; - else - fld = origin; - this.(fld) = this.saved; - - f = ReadByte(); - - if(f & 1) - { - if(f & 0x40) - this.colormap = ReadShort(); - else - this.colormap = 0; - this.skin = ReadByte(); - } - - if(f & 2) - { - this.origin = ReadVector(); - setorigin(this, this.origin); - } - - if(f & 4) - { - if(f & 0x10) - { - this.angles_x = ReadAngle(); - this.angles_y = ReadAngle(); - this.angles_z = ReadAngle(); - } - else - this.angles = '0 0 0'; - } - - if(f & 8) - { - if(f & 0x80) - { - this.lodmodelindex0 = ReadShort(); - this.loddistance1 = ReadShort(); - this.lodmodelindex1 = ReadShort(); - this.loddistance2 = ReadShort(); - this.lodmodelindex2 = ReadShort(); - } - else - { - this.modelindex = ReadShort(); - this.loddistance1 = 0; - this.loddistance2 = 0; - } - this.solid = ReadByte(); - this.scale = ReadShort() / 256.0; - if(f & 0x20) - { - this.mins = ReadVector(); - this.maxs = ReadVector(); - } - else - this.mins = this.maxs = '0 0 0'; - setsize(this, this.mins, this.maxs); - - if(this.bgmscript) - strunzone(this.bgmscript); - this.bgmscript = ReadString(); - if(substring(this.bgmscript, 0, 1) == "<") - { - this.bgmscript = strzone(substring(this.bgmscript, 1, -1)); - this.bgmscriptangular = 1; - } - else - { - this.bgmscript = strzone(this.bgmscript); - this.bgmscriptangular = 0; - } - if(this.bgmscript != "") - { - this.bgmscriptattack = ReadByte() / 64.0; - this.bgmscriptdecay = ReadByte() / 64.0; - this.bgmscriptsustain = ReadByte() / 255.0; - this.bgmscriptrelease = ReadByte() / 64.0; - this.movedir = ReadVector(); - this.lip = ReadByte() / 255.0; - } - this.fade_start = ReadByte(); - this.fade_end = ReadByte(); - this.alpha_max = ReadByte(); - this.alpha_min = ReadByte(); - this.inactive = ReadByte(); - this.fade_vertical_offset = ReadShort(); - BGMScript_InitEntity(this); - } - - return = true; - - InterpolateOrigin_Note(this); - - this.saved = this.(fld); - - this.entremove = Ent_Wall_Remove; - this.draw = Ent_Wall_Draw; - if (isnew) IL_PUSH(g_drawables, this); - setpredraw(this, Ent_Wall_PreDraw); -} diff --git a/qcsrc/client/wall.qh b/qcsrc/client/wall.qh deleted file mode 100644 index 11aebd0ed5..0000000000 --- a/qcsrc/client/wall.qh +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -entityclass(Wall); -class(Wall) .float lip; -class(Wall) .float bgmscriptangular; -class(Wall) .int lodmodelindex0, lodmodelindex1, lodmodelindex2; -class(Wall) .float loddistance1, loddistance2; -class(Wall) .vector saved; - -// Needed for interactive clientwalls -.float inactive; // Clientwall disappears when inactive -.float alpha_max, alpha_min; -// If fade_start > fade_end, fadeout will be inverted -// fade_vertical_offset is a vertival offset for player position -.float fade_start, fade_end, fade_vertical_offset; -.float default_solid; - -void Ent_Wall_Draw(entity this); - -void Ent_Wall_Remove(entity this); diff --git a/qcsrc/client/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc index 089c7df11f..067c0badb7 100644 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@ -3,7 +3,7 @@ #include "../autocvars.qh" #include "../defs.qh" #include "../main.qh" -#include "../mutators/events.qh" +#include <client/mutators/_mod.qh> #include <common/constants.qh> #include <common/effects/effect.qh> @@ -11,6 +11,8 @@ #include <common/net_linked.qh> #include <common/physics/movetypes/movetypes.qh> +#include <common/mutators/mutator/nades/nades.qh> + #include <lib/csqcmodel/interpolate.qh> #include <lib/warpzone/anglestransform.qh> @@ -50,8 +52,6 @@ void Projectile_DrawTrail(entity this, vector to) } } -bool Projectile_isnade(int proj); // TODO: remove - void Projectile_Draw(entity this) { vector rot; @@ -171,7 +171,7 @@ void Projectile_Draw(entity this) void loopsound(entity e, int ch, Sound samp, float vol, float attn) { - TC(int, ch); + TC(int, ch); if (e.silent) return; @@ -265,19 +265,19 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) this.fade_rate = 0; } - int myteam = ReadByte(); - this.team = myteam - 1; + int proj_team = ReadByte(); + this.team = proj_team - 1; if(teamplay) { - if(myteam) + if(proj_team) this.colormap = (this.team) * 0x11; // note: team - 1 on server (client uses different numbers) else this.colormap = 0x00; this.colormap |= BIT(10); // RENDER_COLORMAPPED } else - this.colormap = myteam; + this.colormap = proj_team; // TODO: projectiles use glowmaps for their color, not teams #if 0 if(this.colormap > 0) diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh index a6ae463477..eaa80d05f0 100644 --- a/qcsrc/client/weapons/projectile.qh +++ b/qcsrc/client/weapons/projectile.qh @@ -3,20 +3,20 @@ #include <common/sounds/sound.qh> entityclass(Projectile); -class(Projectile).int traileffect; - -class(Projectile).vector iorigin1, iorigin2; -class(Projectile).float spawntime; -class(Projectile).vector trail_oldorigin; -class(Projectile).float trail_oldtime; -class(Projectile).float fade_time, fade_rate; - -class(Projectile).float alphamod; -class(Projectile).int count; // set if clientside projectile -class(Projectile).int cnt; // sound index -class(Projectile).float gravity; -class(Projectile).int snd_looping; -class(Projectile).bool silent; +classfield(Projectile).int traileffect; + +classfield(Projectile).vector iorigin1, iorigin2; +classfield(Projectile).float spawntime; +classfield(Projectile).vector trail_oldorigin; +classfield(Projectile).float trail_oldtime; +classfield(Projectile).float fade_time, fade_rate; + +classfield(Projectile).float alphamod; +classfield(Projectile).int count; // set if clientside projectile +classfield(Projectile).int cnt; // sound index +classfield(Projectile).float gravity; +classfield(Projectile).int snd_looping; +classfield(Projectile).bool silent; void SUB_Stop(entity this, entity toucher); diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc index 32dbf5208f..9d7c68a8ee 100644 --- a/qcsrc/common/_all.inc +++ b/qcsrc/common/_all.inc @@ -22,7 +22,7 @@ noref float autocvar_net_connecttimeout = 30; #ifdef GAMEQC #include "physics/all.inc" -#include "triggers/include.qc" +#include "mapobjects/_mod.inc" #include "viewloc.qc" #endif diff --git a/qcsrc/common/animdecide.qc b/qcsrc/common/animdecide.qc index b53a9ba0e9..69fe458269 100644 --- a/qcsrc/common/animdecide.qc +++ b/qcsrc/common/animdecide.qc @@ -210,9 +210,8 @@ vector animdecide_getloweranim(entity e) return vec3(e.anim_duckwalkbackright.x, t, ANIMPRIO_CROUCH); case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT: return vec3(e.anim_duckwalkbackleft.x, t, ANIMPRIO_CROUCH); - default: - return vec3(e.anim_duckidle.x, t, ANIMPRIO_CROUCH); } + return vec3(e.anim_duckidle.x, t, ANIMPRIO_CROUCH); } else { @@ -236,12 +235,11 @@ vector animdecide_getloweranim(entity e) return vec3(e.anim_backright.x, t, ANIMPRIO_ACTIVE); case ANIMIMPLICITSTATE_BACKWARDS | ANIMIMPLICITSTATE_LEFT: return vec3(e.anim_backleft.x, t, ANIMPRIO_ACTIVE); - default: - return vec3(e.anim_idle.x, t, ANIMPRIO_IDLE); } + return vec3(e.anim_idle.x, t, ANIMPRIO_IDLE); } // can't get here - return vec3(e.anim_idle.x, t, ANIMPRIO_IDLE); + //return vec3(e.anim_idle.x, t, ANIMPRIO_IDLE); } void animdecide_setimplicitstate(entity e, float onground) diff --git a/qcsrc/common/campaign_common.qh b/qcsrc/common/campaign_common.qh index 3bdc8725c2..25c008d260 100644 --- a/qcsrc/common/campaign_common.qh +++ b/qcsrc/common/campaign_common.qh @@ -29,4 +29,4 @@ void CampaignFile_Unload(); // Sets up the campaign for the n-th array item (meaning: campaign_offset+nth // level) using localcmd() -void CampaignSetup(float n); +void CampaignSetup(int n); diff --git a/qcsrc/common/campaign_file.qc b/qcsrc/common/campaign_file.qc index d3efe77e84..4f099b5330 100644 --- a/qcsrc/common/campaign_file.qc +++ b/qcsrc/common/campaign_file.qc @@ -52,8 +52,6 @@ float CampaignFile_Load(int offset, float n) a = ""; \ else \ ++i -// What you're seeing here is what people will do when your compiler supports -// C-style macros but no line continuations. i = -1; // starts at -1 so I don't need postincrement; that is, i points to BEFORE the current arg! CAMPAIGN_GETARG; campaign_gametype[campaign_entries] = strzone(a); @@ -88,16 +86,15 @@ void CampaignFile_Unload() { if(campaign_title) { - strunzone(campaign_title); + strfree(campaign_title); for(int i = 0; i < campaign_entries; ++i) { - strunzone(campaign_gametype[i]); - strunzone(campaign_mapname[i]); - strunzone(campaign_mutators[i]); - strunzone(campaign_shortdesc[i]); - strunzone(campaign_longdesc[i]); + strfree(campaign_gametype[i]); + strfree(campaign_mapname[i]); + strfree(campaign_mutators[i]); + strfree(campaign_shortdesc[i]); + strfree(campaign_longdesc[i]); } campaign_entries = 0; - campaign_title = string_null; } } diff --git a/qcsrc/common/command/command.qh b/qcsrc/common/command/command.qh index 349d492da8..26308b89db 100644 --- a/qcsrc/common/command/command.qh +++ b/qcsrc/common/command/command.qh @@ -8,6 +8,6 @@ CLASS(Command, Object) ATTRIB(Command, m_description, string); METHOD(Command, m_invokecmd, void(Command this, int request, entity caller, int arguments, string command)) { - TC(Command, this); + TC(Command, this); } ENDCLASS(Command) diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index c58a3df75a..2cac0b1e5b 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -57,7 +57,7 @@ void Curl_URI_Get_Callback(int id, float status, string data) // Command Sub-Functions // ======================= -void GenericCommand_addtolist(float request, float argc) +void GenericCommand_addtolist(int request, int argc) { switch(request) { @@ -97,7 +97,7 @@ void GenericCommand_addtolist(float request, float argc) } } -void GenericCommand_qc_curl(float request, float argc) +void GenericCommand_qc_curl(int request, int argc) { switch(request) { @@ -169,7 +169,7 @@ void GenericCommand_qc_curl(float request, float argc) } } -GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to *_cmd_dump.txt") +GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to <program>_cmd_dump.txt") { switch(request) { @@ -184,23 +184,28 @@ GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to *_cmd_dump.tx #ifdef SVQC CMD_Write("dump of server console commands:\n"); GameCommand_macro_write_aliases(fh); + CMD_Write("\n"); - CMD_Write("\ndump of networked client only commands:\n"); + CMD_Write("dump of networked client only commands:\n"); ClientCommand_macro_write_aliases(fh); + CMD_Write("\n"); - CMD_Write("\ndump of common commands:\n"); + CMD_Write("dump of common commands:\n"); CommonCommand_macro_write_aliases(fh); + CMD_Write("\n"); - CMD_Write("\ndump of ban commands:\n"); + CMD_Write("dump of ban commands:\n"); BanCommand_macro_write_aliases(fh); + CMD_Write("\n"); #endif #ifdef CSQC CMD_Write("dump of client commands:\n"); LocalCommand_macro_write_aliases(fh); + CMD_Write("\n"); #endif - CMD_Write("\ndump of generic commands:\n"); + CMD_Write("dump of generic commands:\n"); GenericCommand_macro_write_aliases(fh); LOG_INFO("Completed dump of aliases in ^2data/data/", GetProgramCommandPrefix(), "_dump.txt^7."); @@ -224,7 +229,7 @@ GENERIC_COMMAND(dumpcommands, "Dump all commands on the program to *_cmd_dump.tx } } -void GenericCommand_maplist(float request, float argc) +void GenericCommand_maplist(int request, int argc) { switch(request) { @@ -307,7 +312,7 @@ void GenericCommand_maplist(float request, float argc) } } -void GenericCommand_nextframe(float request, float arguments, string command) +void GenericCommand_nextframe(int request, string command) { switch(request) { @@ -327,7 +332,7 @@ void GenericCommand_nextframe(float request, float arguments, string command) } } -void GenericCommand_removefromlist(float request, float argc) +void GenericCommand_removefromlist(int request, int argc) { switch(request) { @@ -362,7 +367,7 @@ void GenericCommand_removefromlist(float request, float argc) } } -void GenericCommand_restartnotifs(float request) +void GenericCommand_restartnotifs(int request) { switch(request) { @@ -410,7 +415,7 @@ void GenericCommand_restartnotifs(float request) } } -void GenericCommand_settemp(float request, float argc) +void GenericCommand_settemp(int request, int argc) { switch(request) { @@ -441,7 +446,7 @@ void GenericCommand_settemp(float request, float argc) } } -void GenericCommand_settemp_restore(float request, float argc) +void GenericCommand_settemp_restore(int request) { switch(request) { @@ -468,7 +473,7 @@ void GenericCommand_settemp_restore(float request, float argc) } } -void GenericCommand_runtest(float request, float argc) +void GenericCommand_runtest(int request, int argc) { switch(request) { @@ -496,7 +501,7 @@ void GenericCommand_runtest(float request, float argc) /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void GenericCommand_(float request) +void GenericCommand_(int request) { switch(request) { @@ -520,13 +525,13 @@ void GenericCommand_(float request) // Do not hard code aliases for these, instead create them in commands.cfg... also: keep in alphabetical order, please ;) GENERIC_COMMAND(addtolist, "Add a string to a cvar") { GenericCommand_addtolist(request, arguments); } GENERIC_COMMAND(maplist, "Automatic control of maplist") { GenericCommand_maplist(request, arguments); } -GENERIC_COMMAND(nextframe, "Execute the given command next frame of this VM") { GenericCommand_nextframe(request, arguments, command); } +GENERIC_COMMAND(nextframe, "Execute the given command next frame of this VM") { GenericCommand_nextframe(request, command); } GENERIC_COMMAND(qc_curl, "Queries a URL") { GenericCommand_qc_curl(request, arguments); } GENERIC_COMMAND(removefromlist, "Remove a string from a cvar") { GenericCommand_removefromlist(request, arguments); } GENERIC_COMMAND(restartnotifs, "Re-initialize all notifications") { GenericCommand_restartnotifs(request); } GENERIC_COMMAND(rpn, "RPN calculator") { GenericCommand_rpn(request, arguments, command); } GENERIC_COMMAND(settemp, "Temporarily set a value to a cvar which is restored later") { GenericCommand_settemp(request, arguments); } -GENERIC_COMMAND(settemp_restore, "Restore all cvars set by settemp command") { GenericCommand_settemp_restore(request, arguments); } +GENERIC_COMMAND(settemp_restore, "Restore all cvars set by settemp command") { GenericCommand_settemp_restore(request); } GENERIC_COMMAND(runtest, "Run unit tests") { GenericCommand_runtest(request, arguments); } void GenericCommand_macro_help() @@ -534,7 +539,7 @@ void GenericCommand_macro_help() FOREACH(GENERIC_COMMANDS, true, LOG_INFOF(" ^2%s^7: %s", it.m_name, it.m_description)); } -float GenericCommand_macro_command(float argc, string command) +float GenericCommand_macro_command(int argc, string command) { string c = strtolower(argv(0)); FOREACH(GENERIC_COMMANDS, it.m_name == c, { @@ -544,7 +549,7 @@ float GenericCommand_macro_command(float argc, string command) return false; } -float GenericCommand_macro_usage(float argc) +float GenericCommand_macro_usage(int argc) { string c = strtolower(argv(1)); FOREACH(GENERIC_COMMANDS, it.m_name == c, { @@ -567,7 +572,7 @@ void GenericCommand_macro_write_aliases(float fh) float GenericCommand(string command) { - float argc = tokenize_console(command); + int argc = tokenize_console(command); float n, j, f, i; string s, s2, c; vector rgb; diff --git a/qcsrc/common/command/generic.qh b/qcsrc/common/command/generic.qh index b39c799014..68aa0ae88a 100644 --- a/qcsrc/common/command/generic.qh +++ b/qcsrc/common/command/generic.qh @@ -9,9 +9,9 @@ void GenericCommand_macro_help(); -float GenericCommand_macro_command(float argc, string command); +float GenericCommand_macro_command(int argc, string command); -float GenericCommand_macro_usage(float argc); +float GenericCommand_macro_usage(int argc); void GenericCommand_macro_write_aliases(float fh); diff --git a/qcsrc/common/command/rpn.qc b/qcsrc/common/command/rpn.qc index 7e1c4f52eb..0998fad9db 100644 --- a/qcsrc/common/command/rpn.qc +++ b/qcsrc/common/command/rpn.qc @@ -53,7 +53,7 @@ float rpn_popf() { return stof(rpn_pop()); } void rpn_pushf(float f) { return rpn_push(sprintf("%.9g", f)); } void rpn_setf(float f) { return rpn_set(sprintf("%.9g", f)); } -void GenericCommand_rpn(float request, float argc, string command) +void GenericCommand_rpn(int request, int argc, string command) { switch(request) { diff --git a/qcsrc/common/command/rpn.qh b/qcsrc/common/command/rpn.qh index ba028e2484..75f5ba65d8 100644 --- a/qcsrc/common/command/rpn.qh +++ b/qcsrc/common/command/rpn.qh @@ -11,4 +11,4 @@ int rpn_error; int rpn_sp; string rpn_stack[MAX_RPN_STACK]; -void GenericCommand_rpn(float request, float argc, string command); +void GenericCommand_rpn(int request, int argc, string command); diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 18ea6d7d16..675b75c0a4 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -1,6 +1,6 @@ #pragma once -const int RANKINGS_CNT = 15; +const int RANKINGS_CNT = 99; /////////////////////////// // keys pressed diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index 836c3983cc..7c5a218b21 100644 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@ -56,7 +56,7 @@ CSQCMODEL_ENDIF \ CSQCMODEL_PROPERTY(BIT(10), float, ReadAngle, WriteAngle, v_angle_x) \ CSQCMODEL_PROPERTY(BIT(11), int, ReadByte, WriteByte, traileffect) \ - CSQCMODEL_PROPERTY_SCALED(BIT(12), float, ReadByte, WriteByte, scale, 16, 0, 255) \ + CSQCMODEL_PROPERTY(BIT(12), float, ReadCoord, WriteCoord, scale) \ CSQCMODEL_PROPERTY(BIT(13), int, ReadInt24_t, WriteInt24_t, dphitcontentsmask) \ CSQCMODEL_PROPERTY(BIT(14), TAG_VIEWLOC_TYPE, ReadShort, WriteEntity, TAG_VIEWLOC_NAME) \ CSQCMODEL_PROPERTY(BIT(16), int, ReadByte, WriteByte, multijump_count) \ diff --git a/qcsrc/common/deathtypes/all.qh b/qcsrc/common/deathtypes/all.qh index b489a56e4d..beb8e3e912 100644 --- a/qcsrc/common/deathtypes/all.qh +++ b/qcsrc/common/deathtypes/all.qh @@ -25,10 +25,10 @@ const int HITTYPE_SECONDARY = BITS(1) << 8; /** automatically set by RadiusDamage */ const int HITTYPE_SPLASH = BITS(1) << 9; const int HITTYPE_BOUNCE = BITS(1) << 10; +const int HITTYPE_ARMORPIERCE = BITS(1) << 11; // unused yet -const int HITTYPE_RESERVED = BITS(1) << 11; -const int HITTYPE_RESERVED2 = BITS(1) << 12; -const int DEATH_HITTYPEMASK = HITTYPE_SECONDARY | HITTYPE_SPLASH | HITTYPE_BOUNCE | HITTYPE_RESERVED | HITTYPE_RESERVED2; +const int HITTYPE_RESERVED = BITS(1) << 12; +const int DEATH_HITTYPEMASK = HITTYPE_SECONDARY | HITTYPE_SPLASH | HITTYPE_BOUNCE | HITTYPE_ARMORPIERCE | HITTYPE_RESERVED; // normal deaths begin const int DT_FIRST = BIT(13); diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 7144bf3da5..b1dea6dcc4 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -1,5 +1,9 @@ #pragma once +#ifdef CSQC +#include <client/resources.qh> +#endif + // This includes some functions useful for debugging. // Some more bot-specific ones are in server/pathlib/debug.qc. @@ -288,7 +292,7 @@ MUTATOR_HOOKFUNCTION(trace, SV_StartFrame) it.debug_trace_button = btn; if (!btn || skip) continue; FOREACH_ENTITY(true, { - it.solid_prev = it.solid; + it.solid_prev = it.solid; it.solid = SOLID_BBOX; }); vector forward = '0 0 0'; vector right = '0 0 0'; vector up = '0 0 0'; @@ -296,8 +300,8 @@ MUTATOR_HOOKFUNCTION(trace, SV_StartFrame) vector pos = it.origin + it.view_ofs; traceline(pos, pos + forward * max_shot_distance, MOVE_NORMAL, it); FOREACH_ENTITY(true, { - it.solid = it.solid_prev; - it.solid_prev = 0; + it.solid = it.solid_prev; + it.solid_prev = 0; }); entity e = trace_ent; int i = etof(e); @@ -403,7 +407,7 @@ CLASS(DebugText3d, Object) CONSTRUCT(DebugText3d); this.origin = pos; this.message = strzone(msg); - this.health = align; + SetResourceAmount(this, RESOURCE_HEALTH, align); this.hit_time = time; this.fade_rate = fade_rate_; this.velocity = vel; @@ -411,7 +415,7 @@ CLASS(DebugText3d, Object) } DESTRUCTOR(DebugText3d) { - strunzone(this.message); + strfree(this.message); } void DebugText3d_draw2d(DebugText3d this) { @@ -425,7 +429,7 @@ CLASS(DebugText3d, Object) int size = 8; vector screen_pos = project_3d_to_2d(this.origin) + since_created * this.velocity; - float align = this.health; + float align = GetResourceAmount(this, RESOURCE_HEALTH); if (align > 0) screen_pos.x -= stringwidth(this.message, true, size * '1 1 0') * min(1, align); if (screen_pos.z < 0) return; // behind camera @@ -459,13 +463,13 @@ NET_HANDLE(debug_text_3d, bool is_new) { #define debug_text_3d_5(pos, msg, align, dur, vel) debug_text_3d_fn(pos, msg, align, dur, vel) ERASEABLE -void debug_text_3d_fn(vector pos, string msg, float align, float duration, vector velocity) { +void debug_text_3d_fn(vector pos, string msg, float align, float duration, vector vel) { WriteHeader(MSG_BROADCAST, debug_text_3d); WriteVector(MSG_BROADCAST, pos); WriteString(MSG_BROADCAST, msg); WriteFloat(MSG_BROADCAST, align); WriteFloat(MSG_BROADCAST, duration); - WriteVector(MSG_BROADCAST, velocity); + WriteVector(MSG_BROADCAST, vel); } #endif // SVQC diff --git a/qcsrc/common/effects/all.inc b/qcsrc/common/effects/all.inc index 6439a49bb2..882e715cde 100644 --- a/qcsrc/common/effects/all.inc +++ b/qcsrc/common/effects/all.inc @@ -229,6 +229,7 @@ EFFECT(0, SMOKING, "smoking") EFFECT(0, SMOKE_RING, "smoke_ring") EFFECT(0, JUMPPAD, "jumppad_activate") EFFECT(1, BULLET, "tr_bullet") +EFFECT(1, BULLET_WEAK, "tr_bullet_weak") EFFECT(0, EF_FLAME, "EF_FLAME") EFFECT(0, EF_STARDUST, "EF_STARDUST") EFFECT(0, TE_EXPLOSION, "TE_EXPLOSION") @@ -258,6 +259,6 @@ entity EFFECT_ROCKETMINSTA_LASER(int teamid) case NUM_TEAM_4: e = EFFECT_ROCKETMINSTA_LASER_PINK; break; default: e = EFFECT_ROCKETMINSTA_LASER_NEUTRAL; break; } - if (particleeffectnum(e) < 0 || Team_TeamToNumber(teamid) == -1) { e = EFFECT_TR_NEXUIZPLASMA; } + if (particleeffectnum(e) < 0 || !Team_IsValidTeam(teamid)) { e = EFFECT_TR_NEXUIZPLASMA; } return e; } diff --git a/qcsrc/common/effects/all.qc b/qcsrc/common/effects/all.qc index 252f913379..9732be2e3b 100644 --- a/qcsrc/common/effects/all.qc +++ b/qcsrc/common/effects/all.qc @@ -75,7 +75,7 @@ void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt) Send_Effect(it, eff_loc, eff_vel, eff_cnt); return; }); - // revert to engine handling + // revert to engine handling TODO: send the effect name and draw it on the client side? not as light on networking, but resolves the use of server side effects __pointparticles(_particleeffectnum(eff_name), eff_loc, eff_vel, eff_cnt); } #endif diff --git a/qcsrc/common/effects/effectinfo.inc b/qcsrc/common/effects/effectinfo.inc index 56303a44ff..d3c184fab7 100644 --- a/qcsrc/common/effects/effectinfo.inc +++ b/qcsrc/common/effects/effectinfo.inc @@ -1,3 +1,12 @@ +// docs: https://www.quakewiki.net/darkplaces-wiki/effectinfo-scripting-reference/ +// use `cl_particles_reloadeffects` to reload effects without restarting engine +// use `chase_active 1` and `cl_lockview 1` to see effects from different perspectives +// `dumpeffectinfo` currently doesn't work so edit effectinfo.txt manually, just try to keep the files in sync + +// `tex` are indices into particles/particlefont.tga (see particles/particlefont-template.tga for numbers) +// the first index is inclusive, second exclusive (so `tex 0 8` will use images 0 though 7) +// unless they're equal (`tex 69 69` is the same as `tex 69 70`) + // item respawn effect DEF(TE_WIZSPIKE); // flare particle and light @@ -4410,20 +4419,50 @@ SUB(flac_explode) { // bullet trail (somewhat like a tracer) DEF(tr_bullet); +SUB(tr_bullet) { + MY(alpha) = '500 600 10000'; + MY(color_min) = "0xf03000"; + MY(color_max) = "0xff6010"; + MY(countabsolute) = 1; + MY(sizeincrease) = -3; + MY(size_min) = 0.6; + MY(size_max) = 0.8; + my(tex_min) = 200; + my(tex_max) = 200; + MY(type) = "beam"; +} +SUB(tr_bullet) { + MY(airfriction) = -4; + MY(alpha) = '256 256 350'; + MY(color_min) = "0x202020"; + MY(color_max) = "0x404040"; + MY(notunderwater) = true; + MY(sizeincrease) = 0.4; + MY(size_min) = 1; + MY(size_max) = 2; + MY(tex_min) = 0; + MY(tex_max) = 8; + MY(trailspacing) = 16; + MY(type) = "smoke"; + MY(velocityjitter) = '4 4 4'; +} SUB(tr_bullet) { MY(alpha_min) = 256; MY(alpha_max) = 256; - MY(alpha_fade) = 2560; - MY(color_min) = "0xff8960"; - MY(color_max) = "0xff8533"; - MY(size_min) = 4; - MY(size_max) = 4; - MY(stretchfactor) = 0.200000; - MY(tex_min) = 70; - MY(tex_max) = 70; - MY(trailspacing) = 750; - MY(type) = "spark"; - MY(velocitymultiplier) = 3; + MY(alpha_fade) = 128; + MY(bounce) = 1.500000; + MY(color_min) = "0x404040"; + MY(color_max) = "0x808080"; + MY(gravity) = -0.125000; + MY(liquidfriction) = 4; + MY(size_min) = 0.5; + MY(size_max) = 0.6; + MY(tex_min) = 62; + MY(tex_max) = 62; + MY(trailspacing) = 16; + MY(type) = "bubble"; + MY(underwater) = true; + MY(velocityjitter) = '16.0 16.0 16.0'; } // smoke emitter for small pipes @@ -8529,6 +8568,22 @@ SUB(arc_lightning) { MY(velocityjitter) = '250.0 250.0 250.0'; MY(velocitymultiplier) = 20; } +// impact smoke +SUB(arc_lightning) { + MY(alpha_min) = 40; + MY(alpha_max) = 40; + MY(alpha_fade) = 350; + MY(color_min) = "0x80C0FF"; + MY(color_max) = "0x80C0FF"; + MY(countabsolute) = 1; + MY(sizeincrease) = 400; + MY(size_min) = 4; + MY(size_max) = 4; + MY(tex_min) = 38; + MY(tex_max) = 38; + MY(type) = "smoke"; + MY(velocitymultiplier) = 100; +} DEF(arc_beam); // sparks on beam @@ -8873,3 +8928,51 @@ SUB(arc_bolt_explode) { MY(velocityjitter) = '224.0 224.0 224.0'; MY(velocityoffset) = '0.0 0.0 80.0'; } + +// weak bullet trail (somewhat like a tracer) +DEF(tr_bullet_weak); +SUB(tr_bullet_weak) { + MY(alpha) = '75 100 3000'; + MY(color_min) = "0xf03000"; + MY(color_max) = "0xff6010"; + MY(countabsolute) = 1; + MY(sizeincrease) = -3; + MY(size_min) = 0.6; + MY(size_max) = 0.8; + my(tex_min) = 200; + my(tex_max) = 200; + MY(type) = "beam"; +} +SUB(tr_bullet_weak) { + MY(airfriction) = -4; + MY(alpha) = '256 256 350'; + MY(color_min) = "0x202020"; + MY(color_max) = "0x404040"; + MY(notunderwater) = true; + MY(sizeincrease) = 0.4; + MY(size_min) = 1; + MY(size_max) = 2; + MY(tex_min) = 0; + MY(tex_max) = 8; + MY(trailspacing) = 16; + MY(type) = "smoke"; + MY(velocityjitter) = '4 4 4'; +} +SUB(tr_bullet_weak) { + MY(alpha_min) = 256; + MY(alpha_max) = 256; + MY(alpha_fade) = 128; + MY(bounce) = 1.500000; + MY(color_min) = "0x404040"; + MY(color_max) = "0x808080"; + MY(gravity) = -0.125000; + MY(liquidfriction) = 4; + MY(size_min) = 0.5; + MY(size_max) = 0.6; + MY(tex_min) = 62; + MY(tex_max) = 62; + MY(trailspacing) = 32; + MY(type) = "bubble"; + MY(underwater) = true; + MY(velocityjitter) = '16.0 16.0 16.0'; +} diff --git a/qcsrc/common/effects/effectinfo.qc b/qcsrc/common/effects/effectinfo.qc index 71260eb3d0..a1b2787305 100644 --- a/qcsrc/common/effects/effectinfo.qc +++ b/qcsrc/common/effects/effectinfo.qc @@ -327,7 +327,7 @@ REGISTRY(EffectInfos, BITS(9)) #define EffectInfos_from(i) _EffectInfos_from(i, NULL) REGISTER_REGISTRY(EffectInfos) #define EFFECTINFO(name) \ - [[accumulate]] void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { } \ + ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { } \ REGISTER(EffectInfos, EFFECTINFO, name, m_id, NEW(EffectInfoGroup)) { \ effectinfo_##name(this, NULL); \ } @@ -335,8 +335,8 @@ REGISTER_REGISTRY(EffectInfos) #define MY(f) this.effectinfo_##f #define DEF(name) EFFECTINFO(name) #define SUB(name) \ - [[accumulate]] void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { parent = EFFECTINFO_##name; parent.children[parent.children_count++] = this = NEW(EffectInfo, #name); } \ - [[accumulate]] void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) + ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) { parent = EFFECTINFO_##name; parent.children[parent.children_count++] = this = NEW(EffectInfo, #name); } \ + ACCUMULATE void effectinfo_##name(EffectInfoGroup parent, EffectInfo this) #include "effectinfo.inc" #undef MY #undef DEF diff --git a/qcsrc/common/effects/qc/casings.qc b/qcsrc/common/effects/qc/casings.qc index 2abf2122c2..d225b337bc 100644 --- a/qcsrc/common/effects/qc/casings.qc +++ b/qcsrc/common/effects/qc/casings.qc @@ -30,10 +30,10 @@ void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float ran #ifdef CSQC entityclass(Casing); -class(Casing) .float alpha; -class(Casing) .bool silent; -class(Casing) .int state; -class(Casing) .float cnt; +classfield(Casing) .float alpha; +classfield(Casing) .bool silent; +classfield(Casing) .int state; +classfield(Casing) .float cnt; void Casing_Delete(entity this) { diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index 1fc64d1d25..7ab6697c34 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -53,7 +53,6 @@ void Damage_DamageInfo(vector org, float coredamage, float edgedamage, float rad .float cnt; .int state; -.bool isplayermodel; void DamageEffect_Think(entity this) { @@ -79,7 +78,7 @@ void DamageEffect_Think(entity this) return; } this.state = this.owner.csqcmodel_isdead; - if(this.owner.isplayermodel && (this.owner.entnum == player_localentnum) && !autocvar_chase_active) + if(this.owner.isplayermodel && (this.owner.isplayermodel & ISPLAYER_LOCAL) && !autocvar_chase_active) return; // if we aren't using a third person camera, hide our own effects // now generate the particles diff --git a/qcsrc/common/effects/qc/damageeffects.qh b/qcsrc/common/effects/qc/damageeffects.qh index 2a1d587ca4..68b43b1760 100644 --- a/qcsrc/common/effects/qc/damageeffects.qh +++ b/qcsrc/common/effects/qc/damageeffects.qh @@ -3,7 +3,7 @@ #ifdef CSQC #include <common/deathtypes/all.qh> #include <common/physics/movetypes/movetypes.qh> -#include <client/mutators/events.qh> +#include <client/mutators/_mod.qh> #include <common/vehicles/all.qh> #include <common/weapons/_all.qh> #endif diff --git a/qcsrc/common/effects/qc/globalsound.qc b/qcsrc/common/effects/qc/globalsound.qc index 4875a40ee9..8c0dfd5080 100644 --- a/qcsrc/common/effects/qc/globalsound.qc +++ b/qcsrc/common/effects/qc/globalsound.qc @@ -237,8 +237,7 @@ .string fld = it.m_playersoundfld; if (this.(fld)) { - strunzone(this.(fld)); - this.(fld) = string_null; + strfree(this.(fld)); } }); } @@ -269,8 +268,7 @@ } string file = argv(1); string variants = argv(2); - if (this.(field)) strunzone(this.(field)); - this.(field) = strzone(strcat(file, " ", variants)); + strcpy(this.(field), strcat(file, " ", variants)); } fclose(fh); return true; @@ -284,8 +282,7 @@ void UpdatePlayerSounds(entity this) { if (this.model == this.model_for_playersound && this.skin == this.skin_for_playersound) return; - if (this.model_for_playersound) strunzone(this.model_for_playersound); - this.model_for_playersound = strzone(this.model); + strcpy(this.model_for_playersound, this.model); this.skin_for_playersound = this.skin; ClearPlayerSounds(this); LoadPlayerSounds(this, "sound/player/default.sounds", true); diff --git a/qcsrc/common/effects/qc/modeleffects.qc b/qcsrc/common/effects/qc/modeleffects.qc index 26b3ec9f52..9849b5be73 100644 --- a/qcsrc/common/effects/qc/modeleffects.qc +++ b/qcsrc/common/effects/qc/modeleffects.qc @@ -80,10 +80,10 @@ void modeleffect_spawn(string m, float s, float f, vector o, vector v, vector an #ifdef CSQC entityclass(ModelEffect); -class(ModelEffect) .float frame1time; -class(ModelEffect) .float lifetime, fadetime; -class(ModelEffect) .float teleport_time; -class(ModelEffect) .float scale1, scale2; +classfield(ModelEffect) .float frame1time; +classfield(ModelEffect) .float lifetime, fadetime; +classfield(ModelEffect) .float teleport_time; +classfield(ModelEffect) .float scale1, scale2; .float cnt; .float scale; diff --git a/qcsrc/common/effects/qc/rubble.qh b/qcsrc/common/effects/qc/rubble.qh index dd3785b68e..6eda9b15ef 100644 --- a/qcsrc/common/effects/qc/rubble.qh +++ b/qcsrc/common/effects/qc/rubble.qh @@ -3,7 +3,7 @@ #ifdef CSQC entityclass(Rubble); -class(Rubble).float creationtime; +classfield(Rubble).float creationtime; IntrusiveList g_rubble; STATIC_INIT(g_rubble) { g_rubble = IL_NEW(); } diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index 12abc21b18..7b4c54f9f6 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -1,4 +1,9 @@ #include "ent_cs.qh" +#include <common/gamemodes/_mod.qh> +#include <common/resources.qh> +#ifdef SVQC +#include <server/resources.qh> +#endif REGISTRY(EntCSProps, BITS(16) - 1) #define EntCSProps_from(i) _EntCSProps_from(i, NULL) @@ -33,14 +38,33 @@ STATIC_INIT(RegisterEntCSProps_renumber) { FOREACH(EntCSProps, true, it.m_id = i } #endif +#ifdef SVQC +#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, svsend, clreceive) \ + bool id##_check(entity ent, entity player) { return (GetResourceAmount(ent, checkprop) != GetResourceAmount(player, checkprop)); } \ + void id##_set(entity ent, entity player) { SetResourceAmountExplicit(ent, checkprop, GetResourceAmount(player, checkprop)); } \ + void id##_send(int chan, entity ent) { LAMBDA(svsend); } \ + REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \ + this.m_public = ispublic; \ + this.m_check = id##_check; \ + this.m_set = id##_set; \ + this.m_send = id##_send; \ + } +#elif defined(CSQC) +#define ENTCS_PROP_RESOURCE(id, ispublic, checkprop, setprop, svsend, clreceive) \ + void id##_receive(entity ent) { LAMBDA(clreceive); } \ + REGISTER(EntCSProps, ENTCS_PROP, id, m_id, new_pure(entcs_prop)) { \ + this.m_public = ispublic; \ + this.m_receive = id##_receive; \ + } +#endif + #define ENTCS_SET_NORMAL(var, x) MACRO_BEGIN \ var = x; \ MACRO_END /** the engine player name strings are mutable! */ #define ENTCS_SET_MUTABLE_STRING(var, x) MACRO_BEGIN \ - if (var) strunzone(var); \ - var = strzone(x); \ + strcpy(var, x); \ MACRO_END ENTCS_PROP(ENTNUM, false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ @@ -53,21 +77,21 @@ ENTCS_PROP(ANGLES, false, angles_y, ENTCS_SET_NORMAL, { WriteByte(chan, ent.angles.y / 360 * 256); }, { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; ent.angles = v; }) -ENTCS_PROP(HEALTH, false, health, ENTCS_SET_NORMAL, - { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ }, +ENTCS_PROP_RESOURCE(HEALTH, false, RESOURCE_HEALTH, ENTCS_SET_NORMAL, + { WriteByte(chan, bound(0, GetResourceAmount(ent, RESOURCE_HEALTH) / 10, 255)); /* FIXME: use a better scale? */ }, { ent.healthvalue = ReadByte() * 10; }) -ENTCS_PROP(ARMOR, false, armorvalue, ENTCS_SET_NORMAL, - { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, - { ent.armorvalue = ReadByte() * 10; }) +ENTCS_PROP_RESOURCE(ARMOR, false, RESOURCE_ARMOR, ENTCS_SET_NORMAL, + { WriteByte(chan, bound(0, GetResourceAmount(ent, RESOURCE_ARMOR) / 10, 255)); /* FIXME: use a better scale? */ }, + { SetResourceAmountExplicit(ent, RESOURCE_ARMOR, ReadByte() * 10); }) ENTCS_PROP(NAME, true, netname, ENTCS_SET_MUTABLE_STRING, { WriteString(chan, ent.netname); }, - { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); }) + { strcpy(ent.netname, ReadString()); }) ENTCS_PROP(MODEL, true, model, ENTCS_SET_NORMAL, { WriteString(chan, ent.model); }, - { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); }) + { strcpy(ent.model, ReadString()); }) ENTCS_PROP(SKIN, true, skin, ENTCS_SET_NORMAL, { WriteByte(chan, ent.skin); }, @@ -140,23 +164,23 @@ ENTCS_PROP(FRAGS, true, frags, ENTCS_SET_NORMAL, void entcs_attach(entity player) { - entity e = player.entcs = new(entcs_sender); + entity e = CS(player).entcs = new(entcs_sender); e.owner = player; setthink(e, entcs_think); e.nextthink = time; Net_LinkEntity(e, false, 0, entcs_send); if (!IS_REAL_CLIENT(player)) return; FOREACH_CLIENT(true, { - assert(it.entcs); - _entcs_send(it.entcs, msg_entity = player, BITS(23), MSG_ONE); + assert(CS(it).entcs); + _entcs_send(CS(it).entcs, msg_entity = player, BITS(23), MSG_ONE); }); } void entcs_detach(entity player) { - if (!player.entcs) return; - delete(player.entcs); - player.entcs = NULL; + if (!CS(player).entcs) return; + delete(CS(player).entcs); + CS(player).entcs = NULL; } #endif @@ -168,10 +192,8 @@ ENTCS_PROP(FRAGS, true, frags, ENTCS_SET_NORMAL, int n = this.sv_entnum; entity e = entcs_receiver(n); entcs_receiver(n, NULL); - if (e.netname) strunzone(e.netname); - e.netname = string_null; - if (e.model) strunzone(e.model); - e.model = string_null; + strfree(e.netname); + strfree(e.model); if (e != this) delete(e); } @@ -188,8 +210,7 @@ ENTCS_PROP(FRAGS, true, frags, ENTCS_SET_NORMAL, // `cl_forceplayermodels 1` sounds will be wrong until the player has been in the PVS, but so be it if (this.model != e.model) { - if (this.model) strunzone(this.model); - this.model = strzone(e.model); + strcpy(this.model, e.model); } } diff --git a/qcsrc/common/ent_cs.qh b/qcsrc/common/ent_cs.qh index 0180fea784..3a9f084bcf 100644 --- a/qcsrc/common/ent_cs.qh +++ b/qcsrc/common/ent_cs.qh @@ -43,7 +43,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) .int m_forceupdate; /** Force an origin update, for player sounds */ - #define entcs_force_origin(e) ((e).entcs.m_forceupdate = BIT(2)) + #define entcs_force_origin(e) (CS(e).entcs.m_forceupdate = BIT(2)) #endif diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index 2fc2c40467..a33ec87a01 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -1,4 +1,18 @@ // generated file; do not modify +#include <common/gamemodes/gamemode/assault/_mod.inc> +#include <common/gamemodes/gamemode/clanarena/_mod.inc> +#include <common/gamemodes/gamemode/ctf/_mod.inc> +#include <common/gamemodes/gamemode/cts/_mod.inc> +#include <common/gamemodes/gamemode/deathmatch/_mod.inc> +#include <common/gamemodes/gamemode/domination/_mod.inc> +#include <common/gamemodes/gamemode/duel/_mod.inc> +#include <common/gamemodes/gamemode/freezetag/_mod.inc> +#include <common/gamemodes/gamemode/invasion/_mod.inc> +#include <common/gamemodes/gamemode/keepaway/_mod.inc> +#include <common/gamemodes/gamemode/keyhunt/_mod.inc> +#include <common/gamemodes/gamemode/lms/_mod.inc> #include <common/gamemodes/gamemode/nexball/_mod.inc> #include <common/gamemodes/gamemode/onslaught/_mod.inc> +#include <common/gamemodes/gamemode/race/_mod.inc> +#include <common/gamemodes/gamemode/tdm/_mod.inc> diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index d799570126..ffd71d59d3 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -1,4 +1,18 @@ // generated file; do not modify +#include <common/gamemodes/gamemode/assault/_mod.qh> +#include <common/gamemodes/gamemode/clanarena/_mod.qh> +#include <common/gamemodes/gamemode/ctf/_mod.qh> +#include <common/gamemodes/gamemode/cts/_mod.qh> +#include <common/gamemodes/gamemode/deathmatch/_mod.qh> +#include <common/gamemodes/gamemode/domination/_mod.qh> +#include <common/gamemodes/gamemode/duel/_mod.qh> +#include <common/gamemodes/gamemode/freezetag/_mod.qh> +#include <common/gamemodes/gamemode/invasion/_mod.qh> +#include <common/gamemodes/gamemode/keepaway/_mod.qh> +#include <common/gamemodes/gamemode/keyhunt/_mod.qh> +#include <common/gamemodes/gamemode/lms/_mod.qh> #include <common/gamemodes/gamemode/nexball/_mod.qh> #include <common/gamemodes/gamemode/onslaught/_mod.qh> +#include <common/gamemodes/gamemode/race/_mod.qh> +#include <common/gamemodes/gamemode/tdm/_mod.qh> diff --git a/qcsrc/common/gamemodes/gamemode/assault/_mod.inc b/qcsrc/common/gamemodes/gamemode/assault/_mod.inc new file mode 100644 index 0000000000..e03d3c52e6 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/assault/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/assault/sv_assault.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/assault/_mod.qh b/qcsrc/common/gamemodes/gamemode/assault/_mod.qh new file mode 100644 index 0000000000..211daa89e3 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/assault/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/assault/sv_assault.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc new file mode 100644 index 0000000000..95dd412f49 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc @@ -0,0 +1,645 @@ +#include "sv_assault.qh" + +.entity sprite; +#define AS_ROUND_DELAY 5 + +IntrusiveList g_assault_destructibles; +IntrusiveList g_assault_objectivedecreasers; +IntrusiveList g_assault_objectives; +STATIC_INIT(g_assault) +{ + g_assault_destructibles = IL_NEW(); + g_assault_objectivedecreasers = IL_NEW(); + g_assault_objectives = IL_NEW(); +} + +// random functions +void assault_objective_use(entity this, entity actor, entity trigger) +{ + // activate objective + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); + //print("^2Activated objective ", this.targetname, "=", etos(this), "\n"); + //print("Activator is ", actor.classname, "\n"); + + IL_EACH(g_assault_objectivedecreasers, it.target == this.targetname, + { + target_objective_decrease_activate(it); + }); +} + +vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current) +{ + float hlth = GetResourceAmount(this, RESOURCE_HEALTH); + if (hlth < 0 || hlth >= ASSAULT_VALUE_INACTIVE) + return '-1 0 0'; + return current; +} + +// reset this objective. Used when spawning an objective +// and when a new round starts +void assault_objective_reset(entity this) +{ + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ASSAULT_VALUE_INACTIVE); +} + +// decrease the health of targeted objectives +void assault_objective_decrease_use(entity this, entity actor, entity trigger) +{ + if(actor.team != assault_attacker_team) + { + // wrong team triggered decrease + return; + } + + if(trigger.assault_sprite) + { + WaypointSprite_Disown(trigger.assault_sprite, waypointsprite_deadlifetime); + if(trigger.classname == "func_assault_destructible") + trigger.sprite = NULL; // TODO: just unsetting it?! + } + else + return; // already activated! cannot activate again! + + float hlth = GetResourceAmount(this.enemy, RESOURCE_HEALTH); + if (hlth < ASSAULT_VALUE_INACTIVE) + { + if (hlth - this.dmg > 0.5) + { + GameRules_scoring_add_team(actor, SCORE, this.dmg); + TakeResource(this.enemy, RESOURCE_HEALTH, this.dmg); + } + else + { + GameRules_scoring_add_team(actor, SCORE, hlth); + GameRules_scoring_add_team(actor, ASSAULT_OBJECTIVES, 1); + SetResourceAmountExplicit(this.enemy, RESOURCE_HEALTH, -1); + + if(this.enemy.message) + FOREACH_CLIENT(IS_PLAYER(it), { centerprint(it, this.enemy.message); }); + + SUB_UseTargets(this.enemy, this, trigger); + } + } +} + +void assault_setenemytoobjective(entity this) +{ + IL_EACH(g_assault_objectives, it.targetname == this.target, + { + if(this.enemy == NULL) + this.enemy = it; + else + objerror(this, "more than one objective as target - fix the map!"); + break; + }); + + if(this.enemy == NULL) + objerror(this, "no objective as target - fix the map!"); +} + +bool assault_decreaser_sprite_visible(entity this, entity player, entity view) +{ + if(GetResourceAmount(this.assault_decreaser.enemy, RESOURCE_HEALTH) >= ASSAULT_VALUE_INACTIVE) + return false; + + return true; +} + +void target_objective_decrease_activate(entity this) +{ + entity spr; + this.owner = NULL; + FOREACH_ENTITY_STRING(target, this.targetname, + { + if(it.assault_sprite != NULL) + { + WaypointSprite_Disown(it.assault_sprite, waypointsprite_deadlifetime); + if(it.classname == "func_assault_destructible") + it.sprite = NULL; // TODO: just unsetting it?! + } + + spr = WaypointSprite_SpawnFixed(WP_AssaultDefend, 0.5 * (it.absmin + it.absmax), it, assault_sprite, RADARICON_OBJECTIVE); + spr.assault_decreaser = this; + spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible; + spr.classname = "sprite_waypoint"; + WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY); + if(it.classname == "func_assault_destructible") + { + WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy); + WaypointSprite_UpdateMaxHealth(spr, it.max_health); + WaypointSprite_UpdateHealth(spr, GetResourceAmount(it, RESOURCE_HEALTH)); + it.sprite = spr; + } + else + WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultPush, WP_AssaultPush); + }); +} + +void target_objective_decrease_findtarget(entity this) +{ + assault_setenemytoobjective(this); +} + +void target_assault_roundend_reset(entity this) +{ + //print("round end reset\n"); + ++this.cnt; // up round counter + this.winning = false; // up round +} + +void target_assault_roundend_use(entity this, entity actor, entity trigger) +{ + this.winning = 1; // round has been won by attackers +} + +void assault_roundstart_use(entity this, entity actor, entity trigger) +{ + SUB_UseTargets(this, this, trigger); + + //(Re)spawn all turrets + IL_EACH(g_turrets, true, + { + // Swap turret teams + if(it.team == NUM_TEAM_1) + it.team = NUM_TEAM_2; + else + it.team = NUM_TEAM_1; + + // Doubles as teamchange + turret_respawn(it); + }); +} +void assault_roundstart_use_this(entity this) +{ + assault_roundstart_use(this, NULL, NULL); +} + +void assault_wall_think(entity this) +{ + if(GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 0) + { + this.model = ""; + this.solid = SOLID_NOT; + } + else + { + this.model = this.mdl; + this.solid = SOLID_BSP; + } + + this.nextthink = time + 0.2; +} + +// trigger new round +// reset objectives, toggle spawnpoints, reset triggers, ... +void assault_new_round(entity this) +{ + //bprint("ASSAULT: new round\n"); + + // up round counter + this.winning = this.winning + 1; + + // swap attacker/defender roles + if(assault_attacker_team == NUM_TEAM_1) + assault_attacker_team = NUM_TEAM_2; + else + assault_attacker_team = NUM_TEAM_1; + + IL_EACH(g_saved_team, !IS_CLIENT(it), + { + if(it.team_saved == NUM_TEAM_1) + it.team_saved = NUM_TEAM_2; + else if(it.team_saved == NUM_TEAM_2) + it.team_saved = NUM_TEAM_1; + }); + + // reset the level with a countdown + cvar_set("timelimit", ftos(ceil(time - AS_ROUND_DELAY - game_starttime) / 60)); + ReadyRestart_force(); // sets game_starttime +} + +entity as_round; +.entity ent_winning; +void as_round_think() +{ + game_stopped = false; + assault_new_round(as_round.ent_winning); + delete(as_round); + as_round = NULL; +} + +// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives) +// they win. Otherwise the defending team wins once the timelimit passes. +int WinningCondition_Assault() +{ + if(as_round) + return WINNING_NO; + + WinningConditionHelper(NULL); // set worldstatus + + int status = WINNING_NO; + // as the timelimit has not yet passed just assume the defending team will win + if(assault_attacker_team == NUM_TEAM_1) + { + SetWinners(team, NUM_TEAM_2); + } + else + { + SetWinners(team, NUM_TEAM_1); + } + + entity ent; + ent = find(NULL, classname, "target_assault_roundend"); + if(ent) + { + if(ent.winning) // round end has been triggered by attacking team + { + bprint("Assault: round completed.\n"); + SetWinners(team, assault_attacker_team); + + TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0)); + + if(ent.cnt == 1 || autocvar_g_campaign) // this was the second round + { + status = WINNING_YES; + } + else + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ASSAULT_OBJ_DESTROYED, ceil(time - game_starttime)); + as_round = new(as_round); + as_round.think = as_round_think; + as_round.ent_winning = ent; + as_round.nextthink = time + AS_ROUND_DELAY; + game_stopped = true; + + // make sure timelimit isn't hit while the game is blocked + if(autocvar_timelimit > 0) + if(time + AS_ROUND_DELAY >= game_starttime + autocvar_timelimit * 60) + cvar_set("timelimit", ftos(autocvar_timelimit + AS_ROUND_DELAY / 60)); + } + } + } + + return status; +} + +// spawnfuncs +spawnfunc(info_player_attacker) +{ + if (!g_assault) { delete(this); return; } + + this.team = NUM_TEAM_1; // red, gets swapped every round + spawnfunc_info_player_deathmatch(this); +} + +spawnfunc(info_player_defender) +{ + if (!g_assault) { delete(this); return; } + + this.team = NUM_TEAM_2; // blue, gets swapped every round + spawnfunc_info_player_deathmatch(this); +} + +spawnfunc(target_objective) +{ + if (!g_assault) { delete(this); return; } + + this.classname = "target_objective"; + IL_PUSH(g_assault_objectives, this); + this.use = assault_objective_use; + this.reset = assault_objective_reset; + this.reset(this); + this.spawn_evalfunc = target_objective_spawn_evalfunc; +} + +spawnfunc(target_objective_decrease) +{ + if (!g_assault) { delete(this); return; } + + this.classname = "target_objective_decrease"; + IL_PUSH(g_assault_objectivedecreasers, this); + + if(!this.dmg) + this.dmg = 101; + + this.use = assault_objective_decrease_use; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ASSAULT_VALUE_INACTIVE); + this.max_health = ASSAULT_VALUE_INACTIVE; + this.enemy = NULL; + + InitializeEntity(this, target_objective_decrease_findtarget, INITPRIO_FINDTARGET); +} + +// destructible walls that can be used to trigger target_objective_decrease +bool destructible_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + float hlth = GetResourceAmount(targ, RESOURCE_HEALTH); + if (hlth <= 0 || hlth >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.sprite) + { + WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + } + func_breakable_colormod(targ); + return true; +} + +spawnfunc(func_breakable); +spawnfunc(func_assault_destructible) +{ + if (!g_assault) { delete(this); return; } + + this.spawnflags = 3; + this.classname = "func_assault_destructible"; + this.event_heal = destructible_heal; + IL_PUSH(g_assault_destructibles, this); + + if(assault_attacker_team == NUM_TEAM_1) + this.team = NUM_TEAM_2; + else + this.team = NUM_TEAM_1; + + spawnfunc_func_breakable(this); +} + +spawnfunc(func_assault_wall) +{ + if (!g_assault) { delete(this); return; } + + this.classname = "func_assault_wall"; + this.mdl = this.model; + _setmodel(this, this.mdl); + this.solid = SOLID_BSP; + setthink(this, assault_wall_think); + this.nextthink = time; + InitializeEntity(this, assault_setenemytoobjective, INITPRIO_FINDTARGET); +} + +spawnfunc(target_assault_roundend) +{ + if (!g_assault) { delete(this); return; } + + this.winning = 0; // round not yet won by attackers + this.classname = "target_assault_roundend"; + this.use = target_assault_roundend_use; + this.cnt = 0; // first round + this.reset = target_assault_roundend_reset; +} + +spawnfunc(target_assault_roundstart) +{ + if (!g_assault) { delete(this); return; } + + assault_attacker_team = NUM_TEAM_1; + this.classname = "target_assault_roundstart"; + this.use = assault_roundstart_use; + this.reset2 = assault_roundstart_use_this; + InitializeEntity(this, assault_roundstart_use_this, INITPRIO_FINDTARGET); +} + +// legacy bot code +void havocbot_goalrating_ast_targets(entity this, float ratingscale) +{ + IL_EACH(g_assault_destructibles, it.bot_attack, + { + if (it.target == "") + continue; + + bool found = false; + entity destr = it; + IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target, + { + float hlth = GetResourceAmount(it.enemy, RESOURCE_HEALTH); + if (hlth > 0 && hlth < ASSAULT_VALUE_INACTIVE) + { + found = true; + break; + } + }); + + if(!found) + continue; + + vector p = 0.5 * (it.absmin + it.absmax); + + // Find and rate waypoints around it + found = false; + entity best = NULL; + float bestvalue = FLOAT_MAX; + entity des = it; + for (float radius = 500; radius <= 1500 && !found; radius += 500) + { + FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED), + { + if(checkpvs(it.origin, des)) + { + found = true; + if(it.cnt < bestvalue) + { + best = it; + bestvalue = it.cnt; + } + } + }); + } + + if(best) + { + /// dprint("waypoints around target were found\n"); + // te_lightning2(NULL, '0 0 0', best.origin); + // te_knightspike(best.origin); + + navigation_routerating(this, best, ratingscale, 4000); + best.cnt += 1; + + this.havocbot_attack_time = 0; + + if(checkpvs(this.origin + this.view_ofs, it)) + if(checkpvs(this.origin + this.view_ofs, best)) + { + // dprint("increasing attack time for this target\n"); + this.havocbot_attack_time = time + 2; + } + } + }); +} + +void havocbot_role_ast_offense(entity this) +{ + if(IS_DEAD(this)) + { + this.havocbot_attack_time = 0; + havocbot_ast_reset_role(this); + return; + } + + // Set the role timeout if necessary + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 120; + + if (time > this.havocbot_role_timeout) + { + havocbot_ast_reset_role(this); + return; + } + + if(this.havocbot_attack_time>time) + return; + + if (navigation_goalrating_timeout(this)) + { + // role: offense + navigation_goalrating_start(this); + havocbot_goalrating_enemyplayers(this, 10000, this.origin, 650); + havocbot_goalrating_ast_targets(this, 20000); + havocbot_goalrating_items(this, 30000, this.origin, 10000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ast_defense(entity this) +{ + if(IS_DEAD(this)) + { + this.havocbot_attack_time = 0; + havocbot_ast_reset_role(this); + return; + } + + // Set the role timeout if necessary + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 120; + + if (time > this.havocbot_role_timeout) + { + havocbot_ast_reset_role(this); + return; + } + + if(this.havocbot_attack_time>time) + return; + + if (navigation_goalrating_timeout(this)) + { + // role: defense + navigation_goalrating_start(this); + havocbot_goalrating_enemyplayers(this, 10000, this.origin, 3000); + havocbot_goalrating_ast_targets(this, 20000); + havocbot_goalrating_items(this, 30000, this.origin, 10000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ast_setrole(entity this, float role) +{ + switch(role) + { + case HAVOCBOT_AST_ROLE_DEFENSE: + this.havocbot_role = havocbot_role_ast_defense; + this.havocbot_role_timeout = 0; + break; + case HAVOCBOT_AST_ROLE_OFFENSE: + this.havocbot_role = havocbot_role_ast_offense; + this.havocbot_role_timeout = 0; + break; + } +} + +void havocbot_ast_reset_role(entity this) +{ + if(IS_DEAD(this)) + return; + + if(this.team == assault_attacker_team) + havocbot_role_ast_setrole(this, HAVOCBOT_AST_ROLE_OFFENSE); + else + havocbot_role_ast_setrole(this, HAVOCBOT_AST_ROLE_DEFENSE); +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(as, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(player.team == assault_attacker_team) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_ATTACKING); + else + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_DEFENDING); +} + +MUTATOR_HOOKFUNCTION(as, TurretSpawn) +{ + entity turret = M_ARGV(0, entity); + + if(!turret.team || turret.team == FLOAT_MAX) + turret.team = 5; // this gets reversed when match starts? +} + +MUTATOR_HOOKFUNCTION(as, VehicleInit) +{ + entity veh = M_ARGV(0, entity); + + veh.nextthink = time + 0.5; +} + +MUTATOR_HOOKFUNCTION(as, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + havocbot_ast_reset_role(bot); + return true; +} + +MUTATOR_HOOKFUNCTION(as, PlayHitsound) +{ + entity frag_victim = M_ARGV(0, entity); + + return (frag_victim.classname == "func_assault_destructible"); +} + +MUTATOR_HOOKFUNCTION(as, TeamBalance_CheckAllowedTeams) +{ + // assault always has 2 teams + M_ARGV(0, float) = BIT(0) | BIT(1); + return true; +} + +MUTATOR_HOOKFUNCTION(as, CheckRules_World) +{ + M_ARGV(0, float) = WinningCondition_Assault(); + return true; +} + +MUTATOR_HOOKFUNCTION(as, ReadLevelCvars) +{ + // incompatible + warmup_stage = 0; + sv_ready_restart_after_countdown = 0; +} + +MUTATOR_HOOKFUNCTION(as, OnEntityPreSpawn) +{ + entity ent = M_ARGV(0, entity); + + switch(ent.classname) + { + case "info_player_team1": + case "info_player_team2": + case "info_player_team3": + case "info_player_team4": + return true; + } +} + +MUTATOR_HOOKFUNCTION(as, ReadyRestart_Deny) +{ + // readyrestart not supported (yet) + return true; +} diff --git a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh new file mode 100644 index 0000000000..fcfc78917f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qh @@ -0,0 +1,48 @@ +#pragma once + +#include <common/mutators/base.qh> +#include <common/scores.qh> + +const int ASSAULT_VALUE_INACTIVE = 1000; + +const int ST_ASSAULT_OBJECTIVES = 1; + +REGISTER_MUTATOR(as, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + int teams = BITS(2); // always red vs blue + GameRules_scoring(teams, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, { + field_team(ST_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); + field(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); + }); + } + return 0; +} + +// sprites +.entity assault_decreaser; +.entity assault_sprite; + +// legacy bot defs +const int HAVOCBOT_AST_ROLE_NONE = 0; +const int HAVOCBOT_AST_ROLE_DEFENSE = 2; +const int HAVOCBOT_AST_ROLE_OFFENSE = 4; + +.float havocbot_attack_time; + +void(entity this) havocbot_role_ast_defense; +void(entity this) havocbot_role_ast_offense; + +void(entity bot) havocbot_ast_reset_role; + +void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items; +void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; + +// assault game mode: Which team is attacking in this round? +float assault_attacker_team; + +// predefined spawnfuncs +void target_objective_decrease_activate(entity this); diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/_mod.inc b/qcsrc/common/gamemodes/gamemode/clanarena/_mod.inc new file mode 100644 index 0000000000..ce7b59399a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/clanarena/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/clanarena/sv_clanarena.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/_mod.qh b/qcsrc/common/gamemodes/gamemode/clanarena/_mod.qh new file mode 100644 index 0000000000..55789f77ab --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/clanarena/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/clanarena/sv_clanarena.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc new file mode 100644 index 0000000000..2bbed4a9ab --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qc @@ -0,0 +1,503 @@ +#include "sv_clanarena.qh" + +float autocvar_g_ca_damage2score_multiplier; +bool autocvar_g_ca_spectate_enemies; + +void CA_count_alive_players() +{ + total_players = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + Team_SetNumberOfAlivePlayers(Team_GetTeamFromIndex(i), 0); + } + FOREACH_CLIENT(IS_PLAYER(it) && Entity_HasValidTeam(it), + { + ++total_players; + if (IS_DEAD(it)) + { + continue; + } + entity team_ = Entity_GetTeam(it); + int num_alive = Team_GetNumberOfAlivePlayers(team_); + ++num_alive; + Team_SetNumberOfAlivePlayers(team_, num_alive); + }); + FOREACH_CLIENT(IS_REAL_CLIENT(it), + { + STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex( + 1)); + STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(2)); + STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(3)); + STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(4)); + }); +} + +int CA_GetWinnerTeam() +{ + int winner_team = 0; + if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1)) >= 1) + { + winner_team = NUM_TEAM_1; + } + for (int i = 2; i <= NUM_TEAMS; ++i) + { + if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) >= 1) + { + if (winner_team != 0) + { + return 0; + } + winner_team = Team_IndexToTeam(i); + } + } + if (winner_team) + { + return winner_team; + } + return -1; // no player left +} + +void nades_Clear(entity player); + +#define CA_ALIVE_TEAMS_OK() (Team_GetNumberOfAliveTeams() == NumTeams(ca_teams)) +float CA_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); + FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); + + allowed_to_spawn = false; + game_stopped = true; + round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); + return 1; + } + + CA_count_alive_players(); + if (Team_GetNumberOfAliveTeams() > 1) + { + return 0; + } + + int winner_team = CA_GetWinnerTeam(); + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); + TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); + } + + allowed_to_spawn = false; + game_stopped = true; + round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); + + FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); + + return 1; +} + +void CA_RoundStart() +{ + allowed_to_spawn = boolean(warmup_stage); +} + +bool CA_CheckTeams() +{ + static int prev_missing_teams_mask; + allowed_to_spawn = true; + CA_count_alive_players(); + if(CA_ALIVE_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return true; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return false; + } + int missing_teams_mask = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if ((ca_teams & Team_IndexToBit(i)) && + (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) == 0)) + { + missing_teams_mask |= Team_IndexToBit(i); + } + } + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return false; +} + +bool ca_isEliminated(entity e) +{ + if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_LMS_LOSER)) + return true; + if(e.caplayer == 0.5) + return true; + return false; +} + +/** Returns next available player to spectate if g_ca_spectate_enemies == 0 */ +entity CA_SpectateNext(entity player, entity start) +{ + if (SAME_TEAM(start, player)) return start; + // continue from current player + for (entity e = start; (e = find(e, classname, STR_PLAYER)); ) + { + if (SAME_TEAM(player, e)) return e; + } + // restart from begining + for (entity e = NULL; (e = find(e, classname, STR_PLAYER)); ) + { + if (SAME_TEAM(player, e)) return e; + } + return start; +} + + +MUTATOR_HOOKFUNCTION(ca, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.caplayer = 1; + if (!warmup_stage) + eliminatedPlayers.SendFlags |= 1; +} + +MUTATOR_HOOKFUNCTION(ca, ForbidSpawn) +{ + entity player = M_ARGV(0, entity); + + // spectators / observers that weren't playing can join; they are + // immediately forced to observe in the PutClientInServer hook + // this way they are put in a team and can play in the next round + if (!allowed_to_spawn && player.caplayer) + return true; + return false; +} + +MUTATOR_HOOKFUNCTION(ca, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join + { + TRANSMUTE(Observer, player); + if (CS(player).jointime != time && !player.caplayer) // not when connecting + { + player.caplayer = 0.5; + Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE); + } + } +} + +MUTATOR_HOOKFUNCTION(ca, reset_map_players) +{ + FOREACH_CLIENT(true, { + CS(it).killcount = 0; + if (!it.caplayer && IS_BOT_CLIENT(it)) + { + it.team = -1; + it.caplayer = 1; + } + if (it.caplayer) + { + TRANSMUTE(Player, it); + it.caplayer = 1; + PutClientInServer(it); + } + }); + bot_relinkplayerlist(); + return true; +} + +MUTATOR_HOOKFUNCTION(ca, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + TRANSMUTE(Observer, player); + return true; +} + +MUTATOR_HOOKFUNCTION(ca, reset_map_global) +{ + allowed_to_spawn = true; + return true; +} + +MUTATOR_HOOKFUNCTION(ca, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(0, float) = ca_teams; + return true; +} + +entity ca_LastPlayerForTeam(entity this) +{ + entity last_pl = NULL; + FOREACH_CLIENT(IS_PLAYER(it) && it != this, { + if (!IS_DEAD(it)) + if (SAME_TEAM(this, it)) + if (!last_pl) + last_pl = it; + else + return NULL; + }); + return last_pl; +} + +void ca_LastPlayerForTeam_Notify(entity this) +{ + if (!warmup_stage && round_handler_IsActive() && round_handler_IsRoundStarted()) + { + entity pl = ca_LastPlayerForTeam(this); + if (pl) + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); + } +} + +MUTATOR_HOOKFUNCTION(ca, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + ca_LastPlayerForTeam_Notify(frag_target); + if (!allowed_to_spawn) + { + frag_target.respawn_flags = RESPAWN_SILENT; + // prevent unwanted sudden rejoin as spectator and movement of spectator camera + frag_target.respawn_time = time + 2; + } + frag_target.respawn_flags |= RESPAWN_FORCE; + if (!warmup_stage) + { + eliminatedPlayers.SendFlags |= 1; + if (IS_BOT_CLIENT(frag_target)) + bot_clear(frag_target); + } + return true; +} + +MUTATOR_HOOKFUNCTION(ca, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + if (IS_PLAYER(player) && !IS_DEAD(player)) + ca_LastPlayerForTeam_Notify(player); + return true; +} + +MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + if (IS_PLAYER(player) && !IS_DEAD(player)) + ca_LastPlayerForTeam_Notify(player); + if (player.killindicator_teamchange == -2) // player wants to spectate + player.caplayer = 0; + if (player.caplayer) + player.frags = FRAGS_LMS_LOSER; + if (!warmup_stage) + eliminatedPlayers.SendFlags |= 1; + if (!player.caplayer) + return false; // allow team reset + return true; // prevent team reset +} + +MUTATOR_HOOKFUNCTION(ca, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(ca, GiveFragsForKill, CBC_ORDER_FIRST) +{ + M_ARGV(2, float) = 0; // score will be given to the winner team when the round ends + return true; +} + +MUTATOR_HOOKFUNCTION(ca, SetStartItems) +{ + start_items &= ~IT_UNLIMITED_AMMO; + start_health = warmup_start_health = cvar("g_lms_start_health"); + start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); + start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); + start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); + start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); + start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); + start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); + start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); +} + +MUTATOR_HOOKFUNCTION(ca, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + float frag_mirrordamage = M_ARGV(5, float); + + if (IS_PLAYER(frag_target)) + if (!IS_DEAD(frag_target)) + if (frag_target == frag_attacker || SAME_TEAM(frag_target, frag_attacker) || frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + frag_mirrordamage = 0; + + M_ARGV(4, float) = frag_damage; + M_ARGV(5, float) = frag_mirrordamage; +} + +MUTATOR_HOOKFUNCTION(ca, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if (autocvar_g_powerups <= 0) + if (item.flags & FL_POWERUP) + return true; + + if (autocvar_g_pickup_items <= 0) + return true; +} + +MUTATOR_HOOKFUNCTION(ca, PlayerDamage_SplitHealthArmor) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_damage = M_ARGV(7, float); + float damage_take = bound(0, M_ARGV(4, float), GetResourceAmount(frag_target, RESOURCE_HEALTH)); + float damage_save = bound(0, M_ARGV(5, float), GetResourceAmount(frag_target, RESOURCE_ARMOR)); + + float excess = max(0, frag_damage - damage_take - damage_save); + + if (frag_target != frag_attacker && IS_PLAYER(frag_attacker) && DIFF_TEAM(frag_target, frag_attacker)) + GameRules_scoring_add_team(frag_attacker, SCORE, (frag_damage - excess) * autocvar_g_ca_damage2score_multiplier); +} + +MUTATOR_HOOKFUNCTION(ca, CalculateRespawnTime) +{ + // no respawn calculations needed, player is forced to spectate anyway + return true; +} + +MUTATOR_HOOKFUNCTION(ca, PlayerRegen) +{ + // no regeneration in CA + return true; +} + +MUTATOR_HOOKFUNCTION(ca, Scores_CountFragsRemaining) +{ + // announce remaining frags + return true; +} + +MUTATOR_HOOKFUNCTION(ca, SpectateSet) +{ + entity client = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + + if (!autocvar_g_ca_spectate_enemies && client.caplayer) + if (DIFF_TEAM(targ, client)) + return true; +} + +MUTATOR_HOOKFUNCTION(ca, SpectateNext) +{ + entity client = M_ARGV(0, entity); + + if (!autocvar_g_ca_spectate_enemies && client.caplayer) + { + entity targ = M_ARGV(1, entity); + M_ARGV(1, entity) = CA_SpectateNext(client, targ); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ca, SpectatePrev) +{ + entity client = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + entity first = M_ARGV(2, entity); + + if (!autocvar_g_ca_spectate_enemies && client.caplayer) + { + do { targ = targ.chain; } + while(targ && DIFF_TEAM(targ, client)); + + if (!targ) + { + for (targ = first; targ && DIFF_TEAM(targ, client); targ = targ.chain); + + if (targ == client.enemy) + return MUT_SPECPREV_RETURN; + } + } + + M_ARGV(1, entity) = targ; + + return MUT_SPECPREV_FOUND; +} + +MUTATOR_HOOKFUNCTION(ca, Bot_FixCount, CBC_ORDER_EXCLUSIVE) +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it), { + if (IS_PLAYER(it) || it.caplayer == 1) + ++M_ARGV(0, int); + ++M_ARGV(1, int); + }); + return true; +} + +MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate) +{ + entity player = M_ARGV(0, entity); + + if (player.caplayer) + { + // they're going to spec, we can do other checks + if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player))) + Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE); + return MUT_SPECCMD_FORCE; + } + + return MUT_SPECCMD_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(ca, WantWeapon) +{ + M_ARGV(2, bool) = true; // all weapons +} + +MUTATOR_HOOKFUNCTION(ca, HideTeamNagger) +{ + return true; // doesn't work well with the whole spectator as player thing +} + +MUTATOR_HOOKFUNCTION(ca, GetPlayerStatus) +{ + entity player = M_ARGV(0, entity); + + return player.caplayer == 1; +} + +MUTATOR_HOOKFUNCTION(ca, SetWeaponArena) +{ + // most weapons arena + if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most"; +} diff --git a/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qh b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qh new file mode 100644 index 0000000000..c4755feee9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/clanarena/sv_clanarena.qh @@ -0,0 +1,54 @@ +#pragma once + +#include <common/mutators/base.qh> +#include <server/round_handler.qh> +#include <server/miscfunctions.qh> + +int autocvar_g_ca_point_limit; +int autocvar_g_ca_point_leadlimit; +float autocvar_g_ca_round_timelimit; +bool autocvar_g_ca_team_spawns; +//int autocvar_g_ca_teams; +int autocvar_g_ca_teams_override; +float autocvar_g_ca_warmup; + + +int ca_teams; +bool allowed_to_spawn; + +const int ST_CA_ROUNDS = 1; + +bool CA_CheckTeams(); +bool CA_CheckWinner(); +void CA_RoundStart(); +bool ca_isEliminated(entity e); + +REGISTER_MUTATOR(ca, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_ca_team_spawns); + GameRules_limit_score(autocvar_g_ca_point_limit); + GameRules_limit_lead(autocvar_g_ca_point_leadlimit); + + ca_teams = autocvar_g_ca_teams_override; + if (ca_teams < 2) + ca_teams = cvar("g_ca_teams"); // read the cvar directly as it gets written earlier in the same frame + + ca_teams = BITS(bound(2, ca_teams, 4)); + GameRules_scoring(ca_teams, SFL_SORT_PRIO_PRIMARY, 0, { + field_team(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + }); + + allowed_to_spawn = true; + round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart); + round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); + EliminatedPlayers_Init(ca_isEliminated); + } + return 0; +} + +// should be removed in the future, as other code should not have to care +.float caplayer; // 0.5 if scheduled to join the next round diff --git a/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc b/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc new file mode 100644 index 0000000000..7bc5a9679c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/ctf/sv_ctf.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh b/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh new file mode 100644 index 0000000000..e7fcea7bcf --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh @@ -0,0 +1,5 @@ +// generated file; do not modify +#include <common/gamemodes/gamemode/ctf/ctf.qh> +#ifdef SVQC + #include <common/gamemodes/gamemode/ctf/sv_ctf.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh new file mode 100644 index 0000000000..3cbd334b27 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/ctf.qh @@ -0,0 +1,20 @@ +#pragma once + +const int CTF_RED_FLAG_TAKEN = 1; +const int CTF_RED_FLAG_LOST = 2; +const int CTF_RED_FLAG_CARRYING = 3; +const int CTF_BLUE_FLAG_TAKEN = 4; +const int CTF_BLUE_FLAG_LOST = 8; +const int CTF_BLUE_FLAG_CARRYING = 12; +const int CTF_YELLOW_FLAG_TAKEN = 16; +const int CTF_YELLOW_FLAG_LOST = 32; +const int CTF_YELLOW_FLAG_CARRYING = 48; +const int CTF_PINK_FLAG_TAKEN = 64; +const int CTF_PINK_FLAG_LOST = 128; +const int CTF_PINK_FLAG_CARRYING = 192; +const int CTF_NEUTRAL_FLAG_TAKEN = 256; +const int CTF_NEUTRAL_FLAG_LOST = 512; +const int CTF_NEUTRAL_FLAG_CARRYING = 768; +const int CTF_FLAG_NEUTRAL = 2048; +const int CTF_SHIELDED = 4096; +const int CTF_STALEMATE = 8192; diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc new file mode 100644 index 0000000000..2696a4e878 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc @@ -0,0 +1,2810 @@ +#include "sv_ctf.qh" + +#include <common/effects/all.qh> +#include <common/vehicles/all.qh> +#include <server/teamplay.qh> + +#include <lib/warpzone/common.qh> + +bool autocvar_g_ctf_allow_vehicle_carry; +bool autocvar_g_ctf_allow_vehicle_touch; +bool autocvar_g_ctf_allow_monster_touch; +bool autocvar_g_ctf_throw; +float autocvar_g_ctf_throw_angle_max; +float autocvar_g_ctf_throw_angle_min; +int autocvar_g_ctf_throw_punish_count; +float autocvar_g_ctf_throw_punish_delay; +float autocvar_g_ctf_throw_punish_time; +float autocvar_g_ctf_throw_strengthmultiplier; +float autocvar_g_ctf_throw_velocity_forward; +float autocvar_g_ctf_throw_velocity_up; +float autocvar_g_ctf_drop_velocity_up; +float autocvar_g_ctf_drop_velocity_side; +bool autocvar_g_ctf_oneflag_reverse; +bool autocvar_g_ctf_portalteleport; +bool autocvar_g_ctf_pass; +float autocvar_g_ctf_pass_arc; +float autocvar_g_ctf_pass_arc_max; +float autocvar_g_ctf_pass_directional_max; +float autocvar_g_ctf_pass_directional_min; +float autocvar_g_ctf_pass_radius; +float autocvar_g_ctf_pass_wait; +bool autocvar_g_ctf_pass_request; +float autocvar_g_ctf_pass_turnrate; +float autocvar_g_ctf_pass_timelimit; +float autocvar_g_ctf_pass_velocity; +bool autocvar_g_ctf_dynamiclights; +float autocvar_g_ctf_flag_collect_delay; +float autocvar_g_ctf_flag_damageforcescale; +bool autocvar_g_ctf_flag_dropped_waypoint; +bool autocvar_g_ctf_flag_dropped_floatinwater; +bool autocvar_g_ctf_flag_glowtrails; +int autocvar_g_ctf_flag_health; +bool autocvar_g_ctf_flag_return; +bool autocvar_g_ctf_flag_return_carrying; +float autocvar_g_ctf_flag_return_carried_radius; +float autocvar_g_ctf_flag_return_time; +bool autocvar_g_ctf_flag_return_when_unreachable; +float autocvar_g_ctf_flag_return_damage; +float autocvar_g_ctf_flag_return_damage_delay; +float autocvar_g_ctf_flag_return_dropped; +float autocvar_g_ctf_flagcarrier_auto_helpme_damage; +float autocvar_g_ctf_flagcarrier_auto_helpme_time; +float autocvar_g_ctf_flagcarrier_selfdamagefactor; +float autocvar_g_ctf_flagcarrier_selfforcefactor; +float autocvar_g_ctf_flagcarrier_damagefactor; +float autocvar_g_ctf_flagcarrier_forcefactor; +//float autocvar_g_ctf_flagcarrier_waypointforenemy_spotting; +bool autocvar_g_ctf_fullbrightflags; +bool autocvar_g_ctf_ignore_frags; +bool autocvar_g_ctf_score_ignore_fields; +int autocvar_g_ctf_score_capture; +int autocvar_g_ctf_score_capture_assist; +int autocvar_g_ctf_score_kill; +int autocvar_g_ctf_score_penalty_drop; +int autocvar_g_ctf_score_penalty_returned; +int autocvar_g_ctf_score_pickup_base; +int autocvar_g_ctf_score_pickup_dropped_early; +int autocvar_g_ctf_score_pickup_dropped_late; +int autocvar_g_ctf_score_return; +float autocvar_g_ctf_shield_force; +float autocvar_g_ctf_shield_max_ratio; +int autocvar_g_ctf_shield_min_negscore; +bool autocvar_g_ctf_stalemate; +int autocvar_g_ctf_stalemate_endcondition; +float autocvar_g_ctf_stalemate_time; +bool autocvar_g_ctf_reverse; +float autocvar_g_ctf_dropped_capture_delay; +float autocvar_g_ctf_dropped_capture_radius; + +void ctf_FakeTimeLimit(entity e, float t) +{ + msg_entity = e; + WriteByte(MSG_ONE, 3); // svc_updatestat + WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT + if(t < 0) + WriteCoord(MSG_ONE, autocvar_timelimit); + else + WriteCoord(MSG_ONE, (t + 1) / 60); +} + +void ctf_EventLog(string mode, int flagteam, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != NULL) ? ftos(actor.playerid) : ""))); + //GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +void ctf_CaptureRecord(entity flag, entity player) +{ + float cap_record = ctf_captimerecord; + float cap_time = (time - flag.ctf_pickuptime); + string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname")); + + // notify about shit + if(ctf_oneflag) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); + else if(!ctf_captimerecord) + Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_TIME), player.netname, TIME_ENCODE(cap_time)); + else if(cap_time < cap_record) + Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, TIME_ENCODE(cap_time), TIME_ENCODE(cap_record)); + else + Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, TIME_ENCODE(cap_time), TIME_ENCODE(cap_record)); + + // write that shit in the database + if(!ctf_oneflag) // but not in 1-flag mode + if((!ctf_captimerecord) || (cap_time < cap_record)) + { + ctf_captimerecord = cap_time; + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time)); + db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname); + write_recordmarker(player, flag.ctf_pickuptime, cap_time); + } + + if(autocvar_g_ctf_leaderboard && !ctf_oneflag) + race_setTime(GetMapname(), TIME_ENCODE(cap_time), player.crypto_idfp, player.netname, player, false); +} + +bool ctf_Immediate_Return_Allowed(entity flag, entity toucher) +{ + int num_perteam = 0; + FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), { ++num_perteam; }); + + // automatically return if there's only 1 player on the team + return ((autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried)) + && flag.team); +} + +bool ctf_Return_Customize(entity this, entity client) +{ + // only to the carrier + return boolean(client == this.owner); +} + +void ctf_FlagcarrierWaypoints(entity player) +{ + WaypointSprite_Spawn(WP_FlagCarrier, 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_flagcarrier, true, RADARICON_FLAG); + WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2); + WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(player, RESOURCE_HEALTH), GetResourceAmount(player, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); + WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team)); + + if(player.flagcarried && CTF_SAMETEAM(player, player.flagcarried)) + { + if(!player.wps_enemyflagcarrier) + { + entity wp = WaypointSprite_Spawn(((ctf_oneflag) ? WP_FlagCarrier : WP_FlagCarrierEnemy), 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, 0, player, wps_enemyflagcarrier, true, RADARICON_FLAG); + wp.colormod = WPCOLOR_ENEMYFC(player.team); + setcefc(wp, ctf_Stalemate_Customize); + + if(IS_REAL_CLIENT(player) && !ctf_stalemate) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_VISIBLE); + } + + if(!player.wps_flagreturn) + { + entity owp = WaypointSprite_SpawnFixed(WP_FlagReturn, player.flagcarried.ctf_spawnorigin + FLAG_WAYPOINT_OFFSET, player, wps_flagreturn, RADARICON_FLAG); + owp.colormod = '0 0.8 0.8'; + //WaypointSprite_UpdateTeamRadar(player.wps_flagreturn, RADARICON_FLAG, ((player.team) ? colormapPaletteColor(player.team - 1, false) : '1 1 1')); + setcefc(owp, ctf_Return_Customize); + } + } +} + +void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate) +{ + float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis + float initial_height = min(autocvar_g_ctf_pass_arc_max, (flag.pass_distance * tanh(autocvar_g_ctf_pass_arc))); + float current_height = (initial_height * min(1, (current_distance / flag.pass_distance))); + //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n"); + + vector targpos; + if(current_height) // make sure we can actually do this arcing path + { + targpos = (to + ('0 0 1' * current_height)); + WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); + if(trace_fraction < 1) + { + //print("normal arc line failed, trying to find new pos..."); + WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, flag); + targpos = (trace_endpos + eZ * FLAG_PASS_ARC_OFFSET_Z); + WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); + if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ } + /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */ + } + } + else { targpos = to; } + + //flag.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y)); + + vector desired_direction = normalize(targpos - from); + if(turnrate) { flag.velocity = (normalize(normalize(flag.velocity) + (desired_direction * autocvar_g_ctf_pass_turnrate)) * autocvar_g_ctf_pass_velocity); } + else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); } +} + +bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer) +{ + if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min) + { + // directional tracing only + float spreadlimit; + makevectors(passer_angle); + + // find the closest point on the enemy to the center of the attack + float h; // hypotenuse, which is the distance between attacker to head + float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin + + h = vlen(head_center - passer_center); + a = h * (normalize(head_center - passer_center) * v_forward); + + vector nearest_on_line = (passer_center + a * v_forward); + float distance_from_line = vlen(nearest_to_passer - nearest_on_line); + + spreadlimit = (autocvar_g_ctf_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_ctf_pass_radius)) : 1); + spreadlimit = (autocvar_g_ctf_pass_directional_min * (1 - spreadlimit) + autocvar_g_ctf_pass_directional_max * spreadlimit); + + if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90)) + { return true; } + else + { return false; } + } + else { return true; } +} + + +// ======================= +// CaptureShield Functions +// ======================= + +bool ctf_CaptureShield_CheckStatus(entity p) +{ + int s, s2, s3, s4, se, se2, se3, se4, sr, ser; + int players_worseeq, players_total; + + if(ctf_captureshield_max_ratio <= 0) + return false; + + s = GameRules_scoring_add(p, CTF_CAPS, 0); + s2 = GameRules_scoring_add(p, CTF_PICKUPS, 0); + s3 = GameRules_scoring_add(p, CTF_RETURNS, 0); + s4 = GameRules_scoring_add(p, CTF_FCKILLS, 0); + + sr = ((s - s2) + (s3 + s4)); + + if(sr >= -ctf_captureshield_min_negscore) + return false; + + players_total = players_worseeq = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + if(DIFF_TEAM(it, p)) + continue; + se = GameRules_scoring_add(it, CTF_CAPS, 0); + se2 = GameRules_scoring_add(it, CTF_PICKUPS, 0); + se3 = GameRules_scoring_add(it, CTF_RETURNS, 0); + se4 = GameRules_scoring_add(it, CTF_FCKILLS, 0); + + ser = ((se - se2) + (se3 + se4)); + + if(ser <= sr) + ++players_worseeq; + ++players_total; + }); + + // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse + // use this rule here + + if(players_worseeq >= players_total * ctf_captureshield_max_ratio) + return false; + + return true; +} + +void ctf_CaptureShield_Update(entity player, bool wanted_status) +{ + bool updated_status = ctf_CaptureShield_CheckStatus(player); + if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((updated_status) ? CENTER_CTF_CAPTURESHIELD_SHIELDED : CENTER_CTF_CAPTURESHIELD_FREE)); + player.ctf_captureshielded = updated_status; + } +} + +bool ctf_CaptureShield_Customize(entity this, entity client) +{ + if(!client.ctf_captureshielded) { return false; } + if(CTF_SAMETEAM(this, client)) { return false; } + + return true; +} + +void ctf_CaptureShield_Touch(entity this, entity toucher) +{ + if(!toucher.ctf_captureshielded) { return; } + if(CTF_SAMETEAM(this, toucher)) { return; } + + vector mymid = (this.absmin + this.absmax) * 0.5; + vector theirmid = (toucher.absmin + toucher.absmax) * 0.5; + + Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, mymid, normalize(theirmid - mymid) * ctf_captureshield_force); + if(IS_REAL_CLIENT(toucher)) { Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); } +} + +void ctf_CaptureShield_Spawn(entity flag) +{ + entity shield = new(ctf_captureshield); + + shield.enemy = flag; + shield.team = flag.team; + settouch(shield, ctf_CaptureShield_Touch); + setcefc(shield, ctf_CaptureShield_Customize); + shield.effects = EF_ADDITIVE; + set_movetype(shield, MOVETYPE_NOCLIP); + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 0.5; + + setorigin(shield, flag.origin); + setmodel(shield, MDL_CTF_SHIELD); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} + + +// ==================== +// Drop/Pass/Throw Code +// ==================== + +void ctf_Handle_Drop(entity flag, entity player, int droptype) +{ + // declarations + player = (player ? player : flag.pass_sender); + + // main + set_movetype(flag, MOVETYPE_TOSS); + flag.takedamage = DAMAGE_YES; + flag.angles = '0 0 0'; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); + flag.ctf_droptime = time; + flag.ctf_dropper = player; + flag.ctf_status = FLAG_DROPPED; + + // messages and sounds + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_LOST), player.netname); + _sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE); + ctf_EventLog("dropped", player.team, player); + + // scoring + GameRules_scoring_add_team(player, SCORE, -((flag.score_drop) ? flag.score_drop : autocvar_g_ctf_score_penalty_drop)); + GameRules_scoring_add(player, CTF_DROPS, 1); + + // waypoints + if(autocvar_g_ctf_flag_dropped_waypoint) { + entity wp = WaypointSprite_Spawn(WP_FlagDropped, 0, 0, flag, FLAG_WAYPOINT_OFFSET, NULL, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, true, RADARICON_FLAG); + wp.colormod = WPCOLOR_DROPPEDFLAG(flag.team); + } + + if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health)) + { + WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health); + WaypointSprite_UpdateHealth(flag.wps_flagdropped, GetResourceAmount(flag, RESOURCE_HEALTH)); + } + + player.throw_antispam = time + autocvar_g_ctf_pass_wait; + + if(droptype == DROP_PASS) + { + flag.pass_distance = 0; + flag.pass_sender = NULL; + flag.pass_target = NULL; + } +} + +void ctf_Handle_Retrieve(entity flag, entity player) +{ + entity sender = flag.pass_sender; + + // transfer flag to player + flag.owner = player; + flag.owner.flagcarried = flag; + GameRules_scoring_vip(player, true); + + // reset flag + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } + set_movetype(flag, MOVETYPE_NONE); + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.angles = '0 0 0'; + flag.ctf_status = FLAG_CARRY; + + // messages and sounds + _sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTEN_NORM); + ctf_EventLog("receive", flag.team, player); + + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { + if(it == sender) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_SENT), player.netname); + else if(it == player) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_RECEIVED), sender.netname); + else if(SAME_TEAM(it, sender)) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_OTHER), sender.netname, player.netname); + }); + + // create new waypoint + ctf_FlagcarrierWaypoints(player); + + sender.throw_antispam = time + autocvar_g_ctf_pass_wait; + player.throw_antispam = sender.throw_antispam; + + flag.pass_distance = 0; + flag.pass_sender = NULL; + flag.pass_target = NULL; +} + +void ctf_Handle_Throw(entity player, entity receiver, int droptype) +{ + entity flag = player.flagcarried; + vector targ_origin, flag_velocity; + + if(!flag) { return; } + if((droptype == DROP_PASS) && !receiver) { return; } + + if(flag.speedrunning) { ctf_RespawnFlag(flag); return; } + + // reset the flag + setattachment(flag, NULL, ""); + setorigin(flag, player.origin + FLAG_DROP_OFFSET); + flag.owner.flagcarried = NULL; + GameRules_scoring_vip(flag.owner, false); + flag.owner = NULL; + flag.solid = SOLID_TRIGGER; + flag.ctf_dropper = player; + flag.ctf_droptime = time; + + flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS + + switch(droptype) + { + case DROP_PASS: + { + // warpzone support: + // for the examples, we assume player -> wz1 -> ... -> wzn -> receiver + // findradius has already put wzn ... wz1 into receiver's warpzone parameters! + WarpZone_RefSys_Copy(flag, receiver); + WarpZone_RefSys_AddInverse(flag, receiver); // wz1^-1 ... wzn^-1 receiver + targ_origin = WarpZone_RefSys_TransformOrigin(receiver, flag, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the flag + + flag.pass_distance = vlen((('1 0 0' * targ_origin.x) + ('0 1 0' * targ_origin.y)) - (('1 0 0' * player.origin.x) + ('0 1 0' * player.origin.y))); // for the sake of this check, exclude Z axis + ctf_CalculatePassVelocity(flag, targ_origin, player.origin, false); + + // main + set_movetype(flag, MOVETYPE_FLY); + flag.takedamage = DAMAGE_NO; + flag.pass_sender = player; + flag.pass_target = receiver; + flag.ctf_status = FLAG_PASSING; + + // other + _sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); + WarpZone_TrailParticles(NULL, _particleeffectnum(flag.passeffect), player.origin, targ_origin); + ctf_EventLog("pass", flag.team, player); + break; + } + + case DROP_THROW: + { + makevectors((player.v_angle.y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle.x, autocvar_g_ctf_throw_angle_max) * '1 0 0')); + + flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & ITEM_Strength.m_itemid) ? autocvar_g_ctf_throw_strengthmultiplier : 1))); + flag.velocity = W_CalculateProjectileVelocity(player, player.velocity, flag_velocity, false); + ctf_Handle_Drop(flag, player, droptype); + navigation_dynamicgoal_set(flag, player); + break; + } + + case DROP_RESET: + { + flag.velocity = '0 0 0'; // do nothing + break; + } + + default: + case DROP_NORMAL: + { + flag.velocity = W_CalculateProjectileVelocity(player, player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), false); + ctf_Handle_Drop(flag, player, droptype); + navigation_dynamicgoal_set(flag, player); + break; + } + } + + // kill old waypointsprite + WaypointSprite_Ping(player.wps_flagcarrier); + WaypointSprite_Kill(player.wps_flagcarrier); + + if(player.wps_enemyflagcarrier) + WaypointSprite_Kill(player.wps_enemyflagcarrier); + + if(player.wps_flagreturn) + WaypointSprite_Kill(player.wps_flagreturn); + + // captureshield + ctf_CaptureShield_Update(player, 0); // shield player from picking up flag +} + +void shockwave_spawn(string m, vector org, float sz, float t1, float t2) +{ + return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2); +} + +// ============== +// Event Handlers +// ============== + +void nades_GiveBonus(entity player, float score); + +void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) +{ + entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher); + entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper); + entity player_team_flag = NULL, tmp_entity; + float old_time, new_time; + + if(!player) { return; } // without someone to give the reward to, we can't possibly cap + if(CTF_DIFFTEAM(player, flag)) { return; } + if((flag.cnt || enemy_flag.cnt) && flag.cnt != enemy_flag.cnt) { return; } // this should catch some edge cases (capturing grouped flag at ungrouped flag disallowed etc) + + if (toucher.goalentity == flag.bot_basewaypoint) + toucher.goalentity_lock_timeout = 0; + + if(ctf_oneflag) + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + if(SAME_TEAM(tmp_entity, player)) + { + player_team_flag = tmp_entity; + break; + } + + nades_GiveBonus(player, autocvar_g_nades_bonus_score_high ); + + player.throw_prevtime = time; + player.throw_count = 0; + + // messages and sounds + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_NUM(enemy_flag.team, CENTER_CTF_CAPTURE)); + ctf_CaptureRecord(enemy_flag, player); + _sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE); + + switch(capturetype) + { + case CAPTURE_NORMAL: ctf_EventLog("capture", enemy_flag.team, player); break; + case CAPTURE_DROPPED: ctf_EventLog("droppedcapture", enemy_flag.team, player); break; + default: break; + } + + // scoring + float pscore = 0; + if(enemy_flag.score_capture || flag.score_capture) + pscore = floor((max(1, enemy_flag.score_capture) + max(1, flag.score_capture)) * 0.5); + GameRules_scoring_add_team(player, SCORE, ((pscore) ? pscore : autocvar_g_ctf_score_capture)); + float capscore = 0; + if(enemy_flag.score_team_capture || flag.score_team_capture) + capscore = floor((max(1, enemy_flag.score_team_capture) + max(1, flag.score_team_capture)) * 0.5); + GameRules_scoring_add_team(player, CTF_CAPS, ((capscore) ? capscore : 1)); + + old_time = GameRules_scoring_add(player, CTF_CAPTIME, 0); + new_time = TIME_ENCODE(time - enemy_flag.ctf_pickuptime); + if(!old_time || new_time < old_time) + GameRules_scoring_add(player, CTF_CAPTIME, new_time - old_time); + + // effects + Send_Effect_(flag.capeffect, flag.origin, '0 0 0', 1); + //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1); + + // other + if(capturetype == CAPTURE_NORMAL) + { + WaypointSprite_Kill(player.wps_flagcarrier); + if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); } + + if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper)) + { GameRules_scoring_add_team(enemy_flag.ctf_dropper, SCORE, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); } + } + + flag.enemy = toucher; + + // reset the flag + player.next_take_time = time + autocvar_g_ctf_flag_collect_delay; + ctf_RespawnFlag(enemy_flag); +} + +void ctf_Handle_Return(entity flag, entity player) +{ + // messages and sounds + if(IS_MONSTER(player)) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN_MONSTER), player.monster_name); + } + else if(flag.team) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_RETURN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN), player.netname); + } + _sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE); + ctf_EventLog("return", flag.team, player); + + // scoring + if(IS_PLAYER(player)) + { + GameRules_scoring_add_team(player, SCORE, ((flag.score_return) ? flag.score_return : autocvar_g_ctf_score_return)); // reward for return + GameRules_scoring_add(player, CTF_RETURNS, 1); // add to count of returns + + nades_GiveBonus(player,autocvar_g_nades_bonus_score_medium); + } + + TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it + + if(flag.ctf_dropper) + { + GameRules_scoring_add(flag.ctf_dropper, SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag + ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag + flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time + } + + // other + if(player.flagcarried == flag) + WaypointSprite_Kill(player.wps_flagcarrier); + + flag.enemy = player; + + // reset the flag + ctf_RespawnFlag(flag); +} + +void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) +{ + // declarations + float pickup_dropped_score; // used to calculate dropped pickup score + + // attach the flag to the player + flag.owner = player; + player.flagcarried = flag; + GameRules_scoring_vip(player, true); + if(player.vehicle) + { + setattachment(flag, player.vehicle, ""); + setorigin(flag, VEHICLE_FLAG_OFFSET); + flag.scale = VEHICLE_FLAG_SCALE; + } + else + { + setattachment(flag, player, ""); + setorigin(flag, FLAG_CARRY_OFFSET); + } + + // flag setup + set_movetype(flag, MOVETYPE_NONE); + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.angles = '0 0 0'; + flag.ctf_status = FLAG_CARRY; + + switch(pickuptype) + { + case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs + case PICKUP_DROPPED: SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); break; // reset health/return timelimit + default: break; + } + + // messages and sounds + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_PICKUP), player.netname); + if(ctf_stalemate) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); + if(!flag.team) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); + else if(CTF_DIFFTEAM(player, flag)) + Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_PICKUP)); + else + Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_RETURN : CENTER_CTF_PICKUP_RETURN_ENEMY), Team_ColorCode(flag.team)); + + Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); + + if(!flag.team) + FOREACH_CLIENT(IS_PLAYER(it) && it != player && DIFF_TEAM(it, player), { Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname); }); + + if(flag.team) + FOREACH_CLIENT(IS_PLAYER(it) && it != player, { + if(CTF_SAMETEAM(flag, it)) + if(SAME_TEAM(player, it)) + Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); + else + Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname); + }); + + _sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE); + + // scoring + GameRules_scoring_add(player, CTF_PICKUPS, 1); + nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor); + switch(pickuptype) + { + case PICKUP_BASE: + { + GameRules_scoring_add_team(player, SCORE, ((flag.score_pickup) ? flag.score_pickup : autocvar_g_ctf_score_pickup_base)); + ctf_EventLog("steal", flag.team, player); + break; + } + + case PICKUP_DROPPED: + { + pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1); + pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5); + LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score)); + GameRules_scoring_add_team(player, SCORE, pickup_dropped_score); + ctf_EventLog("pickup", flag.team, player); + break; + } + + default: break; + } + + // speedrunning + if(pickuptype == PICKUP_BASE) + { + flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record + if((player.speedrunning) && (ctf_captimerecord)) + ctf_FakeTimeLimit(player, time + ctf_captimerecord); + } + + // effects + Send_Effect_(flag.toucheffect, player.origin, '0 0 0', 1); + + // waypoints + if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); } + ctf_FlagcarrierWaypoints(player); + WaypointSprite_Ping(player.wps_flagcarrier); +} + + +// =================== +// Main Flag Functions +// =================== + +void ctf_CheckFlagReturn(entity flag, int returntype) +{ + if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING)) + { + if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, GetResourceAmount(flag, RESOURCE_HEALTH)); } + + if((GetResourceAmount(flag, RESOURCE_HEALTH) <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time)) + { + switch(returntype) + { + case RETURN_DROPPED: + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DROPPED)); break; + case RETURN_DAMAGE: + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DAMAGED)); break; + case RETURN_SPEEDRUN: + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_SPEEDRUN), TIME_ENCODE(ctf_captimerecord)); break; + case RETURN_NEEDKILL: + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_NEEDKILL)); break; + default: + case RETURN_TIMEOUT: + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_TIMEOUT)); break; + } + _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE); + ctf_EventLog("returned", flag.team, NULL); + flag.enemy = NULL; + ctf_RespawnFlag(flag); + } + } +} + +bool ctf_Stalemate_Customize(entity this, entity client) +{ + // make spectators see what the player would see + entity e = WaypointSprite_getviewentity(client); + entity wp_owner = this.owner; + + // team waypoints + //if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; } + if(SAME_TEAM(wp_owner, e)) { return false; } + if(!IS_PLAYER(e)) { return false; } + + return true; +} + +void ctf_CheckStalemate() +{ + // declarations + int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0; + entity tmp_entity; + + entity ctf_staleflaglist = NULL; // reset the list, we need to build the list each time this function runs + + // build list of stale flags + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + if(autocvar_g_ctf_stalemate) + if(tmp_entity.ctf_status != FLAG_BASE) + if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time || !tmp_entity.team) // instant stalemate in oneflag + { + tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist + ctf_staleflaglist = tmp_entity; + + switch(tmp_entity.team) + { + case NUM_TEAM_1: ++stale_red_flags; break; + case NUM_TEAM_2: ++stale_blue_flags; break; + case NUM_TEAM_3: ++stale_yellow_flags; break; + case NUM_TEAM_4: ++stale_pink_flags; break; + default: ++stale_neutral_flags; break; + } + } + } + + if(ctf_oneflag) + stale_flags = (stale_neutral_flags >= 1); + else + stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1); + + if(ctf_oneflag && stale_flags == 1) + ctf_stalemate = true; + else if(stale_flags >= 2) + ctf_stalemate = true; + else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2) + { ctf_stalemate = false; wpforenemy_announced = false; } + else if(stale_flags < 2 && autocvar_g_ctf_stalemate_endcondition == 1) + { ctf_stalemate = false; wpforenemy_announced = false; } + + // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary + if(ctf_stalemate) + { + for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext) + { + if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier)) + { + entity wp = WaypointSprite_Spawn(((ctf_oneflag) ? WP_FlagCarrier : WP_FlagCarrierEnemy), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, NULL, 0, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG); + wp.colormod = WPCOLOR_ENEMYFC(tmp_entity.owner.team); + setcefc(tmp_entity.owner.wps_enemyflagcarrier, ctf_Stalemate_Customize); + } + } + + if (!wpforenemy_announced) + { + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((it.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER)); }); + + wpforenemy_announced = true; + } + } +} + +void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + if(autocvar_g_ctf_flag_return_damage_delay) + this.ctf_flagdamaged_byworld = true; + else + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); + ctf_CheckFlagReturn(this, RETURN_NEEDKILL); + } + return; + } + if(autocvar_g_ctf_flag_return_damage) + { + // reduce health and check if it should be returned + TakeResource(this, RESOURCE_HEALTH, damage); + ctf_CheckFlagReturn(this, RETURN_DAMAGE); + return; + } +} + +void ctf_FlagThink(entity this) +{ + // declarations + entity tmp_entity; + + this.nextthink = time + FLAG_THINKRATE; // only 5 fps, more is unnecessary. + + // captureshield + if(this == ctf_worldflaglist) // only for the first flag + FOREACH_CLIENT(true, { ctf_CaptureShield_Update(it, 1); }); // release shield only + + // sanity checks + if(this.mins != this.m_mins || this.maxs != this.m_maxs) { // reset the flag boundaries in case it got squished + LOG_TRACE("wtf the flag got squashed?"); + tracebox(this.origin, this.m_mins, this.m_maxs, this.origin, MOVE_NOMONSTERS, this); + if(!trace_startsolid || this.noalign) // can we resize it without getting stuck? + setsize(this, this.m_mins, this.m_maxs); + } + + // main think method + switch(this.ctf_status) + { + case FLAG_BASE: + { + if(autocvar_g_ctf_dropped_capture_radius) + { + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + if(tmp_entity.ctf_status == FLAG_DROPPED) + if(vdist(this.origin - tmp_entity.origin, <, autocvar_g_ctf_dropped_capture_radius)) + if(time > tmp_entity.ctf_droptime + autocvar_g_ctf_dropped_capture_delay) + ctf_Handle_Capture(this, tmp_entity, CAPTURE_DROPPED); + } + return; + } + + case FLAG_DROPPED: + { + this.angles = '0 0 0'; // reset flag angles in case warpzones adjust it + + if(autocvar_g_ctf_flag_dropped_floatinwater) + { + vector midpoint = ((this.absmin + this.absmax) * 0.5); + if(pointcontents(midpoint) == CONTENT_WATER) + { + this.velocity = this.velocity * 0.5; + + if (pointcontents(midpoint + eZ * FLAG_FLOAT_OFFSET_Z) == CONTENT_WATER) + { this.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; } + else + { set_movetype(this, MOVETYPE_FLY); } + } + else if(this.move_movetype == MOVETYPE_FLY) { set_movetype(this, MOVETYPE_TOSS); } + } + if(autocvar_g_ctf_flag_return_dropped) + { + if((vdist(this.origin - this.ctf_spawnorigin, <=, autocvar_g_ctf_flag_return_dropped)) || (autocvar_g_ctf_flag_return_dropped == -1)) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); + ctf_CheckFlagReturn(this, RETURN_DROPPED); + return; + } + } + if(this.ctf_flagdamaged_byworld) + { + TakeResource(this, RESOURCE_HEALTH, ((this.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE)); + ctf_CheckFlagReturn(this, RETURN_NEEDKILL); + return; + } + else if(autocvar_g_ctf_flag_return_time) + { + TakeResource(this, RESOURCE_HEALTH, ((this.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE)); + ctf_CheckFlagReturn(this, RETURN_TIMEOUT); + return; + } + return; + } + + case FLAG_CARRY: + { + if(this.speedrunning && ctf_captimerecord && (time >= this.ctf_pickuptime + ctf_captimerecord)) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); + ctf_CheckFlagReturn(this, RETURN_SPEEDRUN); + + CS(this.owner).impulse = CHIMPULSE_SPEEDRUN.impulse; // move the player back to the waypoint they set + ImpulseCommands(this.owner); + } + if(autocvar_g_ctf_stalemate) + { + if(time >= wpforenemy_nextthink) + { + ctf_CheckStalemate(); + wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check) + } + } + if(CTF_SAMETEAM(this, this.owner) && this.team) + { + if(autocvar_g_ctf_flag_return) // drop the flag if reverse status has changed + ctf_Handle_Throw(this.owner, NULL, DROP_THROW); + else if(vdist(this.owner.origin - this.ctf_spawnorigin, <=, autocvar_g_ctf_flag_return_carried_radius)) + ctf_Handle_Return(this, this.owner); + } + return; + } + + case FLAG_PASSING: + { + vector targ_origin = ((this.pass_target.absmin + this.pass_target.absmax) * 0.5); + targ_origin = WarpZone_RefSys_TransformOrigin(this.pass_target, this, targ_origin); // origin of target as seen by the flag (us) + WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this); + + if((this.pass_target == NULL) + || (IS_DEAD(this.pass_target)) + || (this.pass_target.flagcarried) + || (vdist(this.origin - targ_origin, >, autocvar_g_ctf_pass_radius)) + || ((trace_fraction < 1) && (trace_ent != this.pass_target)) + || (time > this.ctf_droptime + autocvar_g_ctf_pass_timelimit)) + { + // give up, pass failed + ctf_Handle_Drop(this, NULL, DROP_PASS); + } + else + { + // still a viable target, go for it + ctf_CalculatePassVelocity(this, targ_origin, this.origin, true); + } + return; + } + + default: // this should never happen + { + LOG_TRACE("ctf_FlagThink(): Flag exists with no status?"); + return; + } + } +} + +METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) +{ + return = false; + if(game_stopped) return; + if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; } + + bool is_not_monster = (!IS_MONSTER(toucher)); + + // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces + if(ITEM_TOUCH_NEEDKILL()) + { + if(!autocvar_g_ctf_flag_return_damage_delay) + { + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, 0); + ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); + } + if(!flag.ctf_flagdamaged_byworld) { return; } + } + + // special touch behaviors + if(STAT(FROZEN, toucher)) { return; } + else if(IS_VEHICLE(toucher)) + { + if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner) + toucher = toucher.owner; // the player is actually the vehicle owner, not other + else + return; // do nothing + } + else if(IS_MONSTER(toucher)) + { + if(!autocvar_g_ctf_allow_monster_touch) + return; // do nothing + } + else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world + { + if(time > flag.wait) // if we haven't in a while, play a sound/effect + { + Send_Effect_(flag.toucheffect, flag.origin, '0 0 0', 1); + _sound(flag, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); + flag.wait = time + FLAG_TOUCHRATE; + } + return; + } + else if(IS_DEAD(toucher)) { return; } + + switch(flag.ctf_status) + { + case FLAG_BASE: + { + if(ctf_oneflag) + { + if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base + else if(!flag.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the neutral flag + } + else if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, flag) && is_not_monster) + ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base + else if(CTF_DIFFTEAM(toucher, flag) && (toucher.flagcarried) && CTF_SAMETEAM(toucher.flagcarried, toucher) && (!toucher.ctf_captureshielded) && autocvar_g_ctf_flag_return_carrying && (time > toucher.next_take_time) && is_not_monster) + { + ctf_Handle_Return(toucher.flagcarried, toucher); // return their current flag + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // now pickup the flag + } + else if(CTF_DIFFTEAM(toucher, flag) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) + ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the enemies flag + break; + } + + case FLAG_DROPPED: + { + if(CTF_SAMETEAM(toucher, flag) && ctf_Immediate_Return_Allowed(flag, toucher)) + ctf_Handle_Return(flag, toucher); // toucher just returned his own flag + else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) + ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag + break; + } + + case FLAG_CARRY: + { + LOG_TRACE("Someone touched a flag even though it was being carried?"); + break; + } + + case FLAG_PASSING: + { + if((IS_PLAYER(toucher)) && !IS_DEAD(toucher) && (toucher != flag.pass_sender)) + { + if(DIFF_TEAM(toucher, flag.pass_sender)) + { + if(ctf_Immediate_Return_Allowed(flag, toucher)) + ctf_Handle_Return(flag, toucher); + else if(is_not_monster && (!toucher.flagcarried)) + ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); + } + else if(!toucher.flagcarried) + ctf_Handle_Retrieve(flag, toucher); + } + break; + } + } +} + +.float last_respawn; +void ctf_RespawnFlag(entity flag) +{ + // check for flag respawn being called twice in a row + if(flag.last_respawn > time - 0.5) + { backtrace("flag respawn called twice quickly! please notify Samual about this..."); } + + flag.last_respawn = time; + + // reset the player (if there is one) + if((flag.owner) && (flag.owner.flagcarried == flag)) + { + WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); + WaypointSprite_Kill(flag.owner.wps_flagreturn); + WaypointSprite_Kill(flag.wps_flagcarrier); + + flag.owner.flagcarried = NULL; + GameRules_scoring_vip(flag.owner, false); + + if(flag.speedrunning) + ctf_FakeTimeLimit(flag.owner, -1); + } + + if((flag.owner) && (flag.owner.vehicle)) + flag.scale = FLAG_SCALE; + + if(flag.ctf_status == FLAG_DROPPED) + { WaypointSprite_Kill(flag.wps_flagdropped); } + + // reset the flag + setattachment(flag, NULL, ""); + setorigin(flag, flag.ctf_spawnorigin); + + set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS)); + flag.takedamage = DAMAGE_NO; + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); + flag.solid = SOLID_TRIGGER; + flag.velocity = '0 0 0'; + flag.angles = flag.mangle; + flag.flags = FL_ITEM | FL_NOTARGET; + + flag.ctf_status = FLAG_BASE; + flag.owner = NULL; + flag.pass_distance = 0; + flag.pass_sender = NULL; + flag.pass_target = NULL; + flag.ctf_dropper = NULL; + flag.ctf_pickuptime = 0; + flag.ctf_droptime = 0; + flag.ctf_flagdamaged_byworld = false; + navigation_dynamicgoal_unset(flag); + + ctf_CheckStalemate(); +} + +void ctf_Reset(entity this) +{ + if(this.owner && IS_PLAYER(this.owner)) + ctf_Handle_Throw(this.owner, NULL, DROP_RESET); + + this.enemy = NULL; + ctf_RespawnFlag(this); +} + +bool ctf_FlagBase_Customize(entity this, entity client) +{ + entity e = WaypointSprite_getviewentity(client); + entity wp_owner = this.owner; + entity flag = e.flagcarried; + if(flag && CTF_SAMETEAM(e, flag)) + return false; + if(flag && (flag.cnt || wp_owner.cnt) && wp_owner.cnt != flag.cnt) + return false; + return true; +} + +void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map by ctf_FlagSetup() +{ + // bot waypoints + waypoint_spawnforitem_force(this, this.origin); + navigation_dynamicgoal_init(this, true); + + // waypointsprites + entity basename; + switch (this.team) + { + case NUM_TEAM_1: basename = WP_FlagBaseRed; break; + case NUM_TEAM_2: basename = WP_FlagBaseBlue; break; + case NUM_TEAM_3: basename = WP_FlagBaseYellow; break; + case NUM_TEAM_4: basename = WP_FlagBasePink; break; + default: basename = WP_FlagBaseNeutral; break; + } + + entity wp = WaypointSprite_SpawnFixed(basename, this.origin + FLAG_WAYPOINT_OFFSET, this, wps_flagbase, RADARICON_FLAG); + wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 1 1'); + WaypointSprite_UpdateTeamRadar(this.wps_flagbase, RADARICON_FLAG, ((this.team) ? colormapPaletteColor(this.team - 1, false) : '1 1 1')); + setcefc(wp, ctf_FlagBase_Customize); + + // captureshield setup + ctf_CaptureShield_Spawn(this); +} + +.bool pushable; + +void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc +{ + // main setup + flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist + ctf_worldflaglist = flag; + + setattachment(flag, NULL, ""); + + flag.netname = strzone(sprintf("%s%s^7 flag", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber))); + flag.team = teamnumber; + flag.classname = "item_flag_team"; + flag.target = "###item###"; // for finding the nearest item using findnearest + flag.flags = FL_ITEM | FL_NOTARGET; + IL_PUSH(g_items, flag); + flag.solid = SOLID_TRIGGER; + flag.takedamage = DAMAGE_NO; + flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale; + flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100); + SetResourceAmountExplicit(flag, RESOURCE_HEALTH, flag.max_flag_health); + flag.event_damage = ctf_FlagDamage; + flag.pushable = true; + flag.teleportable = TELEPORT_NORMAL; + flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP; + flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable; + flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable; + if(flag.damagedbycontents) + IL_PUSH(g_damagedbycontents, flag); + flag.velocity = '0 0 0'; + flag.mangle = flag.angles; + flag.reset = ctf_Reset; + settouch(flag, ctf_FlagTouch); + setthink(flag, ctf_FlagThink); + flag.nextthink = time + FLAG_THINKRATE; + flag.ctf_status = FLAG_BASE; + + // crudely force them all to 0 + if(autocvar_g_ctf_score_ignore_fields) + flag.cnt = flag.score_assist = flag.score_team_capture = flag.score_capture = flag.score_drop = flag.score_pickup = flag.score_return = 0; + + string teamname = Static_Team_ColorName_Lower(teamnumber); + // appearence + if(!flag.scale) { flag.scale = FLAG_SCALE; } + if(flag.skin == 0) { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); } + if(flag.model == "") { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); } + if (flag.toucheffect == "") { flag.toucheffect = EFFECT_FLAG_TOUCH(teamnumber).eent_eff_name; } + if (flag.passeffect == "") { flag.passeffect = EFFECT_PASS(teamnumber).eent_eff_name; } + if (flag.capeffect == "") { flag.capeffect = EFFECT_CAP(teamnumber).eent_eff_name; } + + // sounds +#define X(s,b) \ + if(flag.s == "") flag.s = b; \ + precache_sound(flag.s); + + X(snd_flag_taken, strzone(SND(CTF_TAKEN(teamnumber)))) + X(snd_flag_returned, strzone(SND(CTF_RETURNED(teamnumber)))) + X(snd_flag_capture, strzone(SND(CTF_CAPTURE(teamnumber)))) + X(snd_flag_dropped, strzone(SND(CTF_DROPPED(teamnumber)))) + X(snd_flag_respawn, strzone(SND(CTF_RESPAWN))) + X(snd_flag_touch, strzone(SND(CTF_TOUCH))) + X(snd_flag_pass, strzone(SND(CTF_PASS))) +#undef X + + // precache + precache_model(flag.model); + + // appearence + _setmodel(flag, flag.model); // precision set below + setsize(flag, CTF_FLAG.m_mins * flag.scale, CTF_FLAG.m_maxs * flag.scale); + flag.m_mins = flag.mins; // store these for squash checks + flag.m_maxs = flag.maxs; + setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET)); + + if(autocvar_g_ctf_flag_glowtrails) + { + switch(teamnumber) + { + case NUM_TEAM_1: flag.glow_color = 251; break; + case NUM_TEAM_2: flag.glow_color = 210; break; + case NUM_TEAM_3: flag.glow_color = 110; break; + case NUM_TEAM_4: flag.glow_color = 145; break; + default: flag.glow_color = 254; break; + } + flag.glow_size = 25; + flag.glow_trail = 1; + } + + flag.effects |= EF_LOWPRECISION; + if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; } + if(autocvar_g_ctf_dynamiclights) + { + switch(teamnumber) + { + case NUM_TEAM_1: flag.effects |= EF_RED; break; + case NUM_TEAM_2: flag.effects |= EF_BLUE; break; + case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break; + case NUM_TEAM_4: flag.effects |= EF_RED; break; + default: flag.effects |= EF_DIMLIGHT; break; + } + } + + // flag placement + if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location + { + flag.dropped_origin = flag.origin; + flag.noalign = true; + set_movetype(flag, MOVETYPE_NONE); + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + flag.noalign = false; + droptofloor(flag); + set_movetype(flag, MOVETYPE_NONE); + } + + InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION); +} + + +// ================ +// Bot player logic +// ================ + +// NOTE: LEGACY CODE, needs to be re-written! + +void havocbot_ctf_calculate_middlepoint() +{ + entity f; + vector s = '0 0 0'; + vector fo = '0 0 0'; + int n = 0; + + f = ctf_worldflaglist; + while (f) + { + fo = f.origin; + s = s + fo; + f = f.ctf_worldflagnext; + n++; + } + if(!n) + return; + + havocbot_middlepoint = s / n; + havocbot_middlepoint_radius = vlen(fo - havocbot_middlepoint); + + havocbot_symmetry_axis_m = 0; + havocbot_symmetry_axis_q = 0; + if(n == 2) + { + // for symmetrical editing of waypoints + entity f1 = ctf_worldflaglist; + entity f2 = f1.ctf_worldflagnext; + float m = -(f1.origin.y - f2.origin.y) / (f1.origin.x - f2.origin.x); + float q = havocbot_middlepoint.y - m * havocbot_middlepoint.x; + havocbot_symmetry_axis_m = m; + havocbot_symmetry_axis_q = q; + } + havocbot_symmetry_origin_order = n; +} + + +entity havocbot_ctf_find_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if (CTF_SAMETEAM(bot, f)) + return f; + f = f.ctf_worldflagnext; + } + return NULL; +} + +entity havocbot_ctf_find_enemy_flag(entity bot) +{ + entity f; + f = ctf_worldflaglist; + while (f) + { + if(ctf_oneflag) + { + if(CTF_DIFFTEAM(bot, f)) + { + if(f.team) + { + if(bot.flagcarried) + return f; + } + else if(!bot.flagcarried) + return f; + } + } + else if (CTF_DIFFTEAM(bot, f)) + return f; + f = f.ctf_worldflagnext; + } + return NULL; +} + +int havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) +{ + if (!teamplay) + return 0; + + int c = 0; + + FOREACH_CLIENT(IS_PLAYER(it), { + if(DIFF_TEAM(it, bot) || IS_DEAD(it) || it == bot) + continue; + + if(vdist(it.origin - org, <, tc_radius)) + ++c; + }); + + return c; +} + +// unused +#if 0 +void havocbot_goalrating_ctf_ourflag(entity this, float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (CTF_SAMETEAM(this, head)) + break; + head = head.ctf_worldflagnext; + } + if (head) + navigation_routerating(this, head, ratingscale, 10000); +} +#endif + +void havocbot_goalrating_ctf_ourbase(entity this, float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if (CTF_SAMETEAM(this, head)) + { + if (this.flagcarried) + if ((this.flagcarried.cnt || head.cnt) && this.flagcarried.cnt != head.cnt) + { + head = head.ctf_worldflagnext; // skip base if it has a different group + continue; + } + break; + } + head = head.ctf_worldflagnext; + } + if (!head) + return; + + navigation_routerating(this, head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_enemyflag(entity this, float ratingscale) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + if(ctf_oneflag) + { + if(CTF_DIFFTEAM(this, head)) + { + if(head.team) + { + if(this.flagcarried) + break; + } + else if(!this.flagcarried) + break; + } + } + else if(CTF_DIFFTEAM(this, head)) + break; + head = head.ctf_worldflagnext; + } + if (head) + { + if (head.ctf_status == FLAG_CARRY) + { + // adjust rating of our flag carrier depending on his health + head = head.tag_entity; + float f = bound(0, (GetResourceAmount(head, RESOURCE_HEALTH) + GetResourceAmount(head, RESOURCE_ARMOR)) / 100, 2) - 1; + ratingscale += ratingscale * f * 0.1; + } + navigation_routerating(this, head, ratingscale, 10000); + } +} + +void havocbot_goalrating_ctf_enemybase(entity this, float ratingscale) +{ + // disabled because we always spawn waypoints for flags with waypoint_spawnforitem_force + /* + if (!bot_waypoints_for_items) + { + havocbot_goalrating_ctf_enemyflag(this, ratingscale); + return; + } + */ + entity head; + + head = havocbot_ctf_find_enemy_flag(this); + + if (!head) + return; + + navigation_routerating(this, head.bot_basewaypoint, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_ourstolenflag(entity this, float ratingscale) +{ + entity mf; + + mf = havocbot_ctf_find_flag(this); + + if(mf.ctf_status == FLAG_BASE) + return; + + if(mf.tag_entity) + navigation_routerating(this, mf.tag_entity, ratingscale, 10000); +} + +void havocbot_goalrating_ctf_droppedflags(entity this, float ratingscale, vector org, float df_radius) +{ + entity head; + head = ctf_worldflaglist; + while (head) + { + // flag is out in the field + if(head.ctf_status != FLAG_BASE) + if(head.tag_entity==NULL) // dropped + { + if(df_radius) + { + if(vdist(org - head.origin, <, df_radius)) + navigation_routerating(this, head, ratingscale, 10000); + } + else + navigation_routerating(this, head, ratingscale, 10000); + } + + head = head.ctf_worldflagnext; + } +} + +void havocbot_ctf_reset_role(entity this) +{ + float cdefense, cmiddle, coffense; + entity mf, ef; + + if(IS_DEAD(this)) + return; + + // Check ctf flags + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(this); + ef = havocbot_ctf_find_enemy_flag(this); + + // Retrieve stolen flag + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + // If enemy flag is taken go to the middle to intercept pursuers + if(ef.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + + // if there is no one else on the team switch to offense + int count = 0; + // don't check if this bot is a player since it isn't true when the bot is added to the server + FOREACH_CLIENT(it != this && IS_PLAYER(it) && SAME_TEAM(it, this), { ++count; }); + + if (count == 0) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_OFFENSE); + return; + } + else if (time < CS(this).jointime + 1) + { + // if bots spawn all at once set good default roles + if (count == 1) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_DEFENSE); + return; + } + else if (count == 2) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); + return; + } + } + + // Evaluate best position to take + // Count mates on middle position + cmiddle = havocbot_ctf_teamcount(this, havocbot_middlepoint, havocbot_middlepoint_radius * 0.5); + + // Count mates on defense position + cdefense = havocbot_ctf_teamcount(this, mf.dropped_origin, havocbot_middlepoint_radius * 0.5); + + // Count mates on offense position + coffense = havocbot_ctf_teamcount(this, ef.dropped_origin, havocbot_middlepoint_radius); + + if(cdefense<=coffense) + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_DEFENSE); + else if(coffense<=cmiddle) + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_OFFENSE); + else + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); + + // if bots spawn all at once assign them a more appropriated role after a while + if (time < CS(this).jointime + 1 && count > 2) + this.havocbot_role_timeout = time + 10 + random() * 10; +} + +bool havocbot_ctf_is_basewaypoint(entity item) +{ + if (item.classname != "waypoint") + return false; + + entity head = ctf_worldflaglist; + while (head) + { + if (item == head.bot_basewaypoint) + return true; + head = head.ctf_worldflagnext; + } + return false; +} + +void havocbot_role_ctf_carrier(entity this) +{ + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried == NULL) + { + havocbot_ctf_reset_role(this); + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + // role: carrier + entity mf = havocbot_ctf_find_flag(this); + vector base_org = mf.dropped_origin; + float base_rating = (mf.ctf_status == FLAG_BASE) ? 10000 : (vdist(this.origin - base_org, >, 100) ? 2000 : 1000); + if(ctf_oneflag) + havocbot_goalrating_ctf_enemybase(this, base_rating); + else + havocbot_goalrating_ctf_ourbase(this, base_rating); + + // start collecting items very close to the bot but only inside of own base radius + if (vdist(this.origin - base_org, <, havocbot_middlepoint_radius)) + havocbot_goalrating_items(this, 15000, this.origin, min(500, havocbot_middlepoint_radius * 0.5)); + + havocbot_goalrating_items(this, 10000, base_org, havocbot_middlepoint_radius * 0.5); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + + entity goal = this.goalentity; + if (havocbot_ctf_is_basewaypoint(goal) && vdist(goal.origin - this.origin, <, 100)) + this.goalentity_lock_timeout = time + ((this.bot_aimtarg) ? 2 : 3); + + if (goal) + this.havocbot_cantfindflag = time + 10; + else if (time > this.havocbot_cantfindflag) + { + // Can't navigate to my own base, suicide! + // TODO: drop it and wander around + Damage(this, this, this, 100000, DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); + return; + } + } +} + +void havocbot_role_ctf_escort(entity this) +{ + entity mf, ef; + + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If enemy flag is back on the base switch to previous role + ef = havocbot_ctf_find_enemy_flag(this); + if(ef.ctf_status==FLAG_BASE) + { + this.havocbot_role = this.havocbot_previous_role; + this.havocbot_role_timeout = 0; + return; + } + if (ef.ctf_status == FLAG_DROPPED) + { + navigation_goalrating_timeout_expire(this, 1); + return; + } + + // If the flag carrier reached the base switch to defense + mf = havocbot_ctf_find_flag(this); + if (mf.ctf_status != FLAG_BASE && vdist(ef.origin - mf.dropped_origin, <, 900)) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_DEFENSE); + return; + } + + // Set the role timeout if necessary + if (!this.havocbot_role_timeout) + { + this.havocbot_role_timeout = time + random() * 30 + 60; + } + + // If nothing happened just switch to previous role + if (time > this.havocbot_role_timeout) + { + this.havocbot_role = this.havocbot_previous_role; + this.havocbot_role_timeout = 0; + return; + } + + // Chase the flag carrier + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + // role: escort + havocbot_goalrating_ctf_enemyflag(this, 10000); + havocbot_goalrating_ctf_ourstolenflag(this, 6000); + havocbot_goalrating_items(this, 21000, this.origin, 10000); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ctf_offense(entity this) +{ + entity mf, ef; + vector pos; + + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // Check flags + mf = havocbot_ctf_find_flag(this); + ef = havocbot_ctf_find_enemy_flag(this); + + // Own flag stolen + if(mf.ctf_status!=FLAG_BASE) + { + if(mf.tag_entity) + pos = mf.tag_entity.origin; + else + pos = mf.origin; + + // Try to get it if closer than the enemy base + if(vlen2(this.origin-ef.dropped_origin)>vlen2(this.origin-pos)) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + } + + // Escort flag carrier + if(ef.ctf_status!=FLAG_BASE) + { + if(ef.tag_entity) + pos = ef.tag_entity.origin; + else + pos = ef.origin; + + if(vdist(pos - mf.dropped_origin, >, 700)) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_ESCORT); + return; + } + } + + // Set the role timeout if necessary + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 120; + + if (time > this.havocbot_role_timeout) + { + havocbot_ctf_reset_role(this); + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + // role: offense + havocbot_goalrating_ctf_ourstolenflag(this, 10000); + havocbot_goalrating_ctf_enemybase(this, 10000); + havocbot_goalrating_items(this, 22000, this.origin, 10000); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +// Retriever (temporary role): +void havocbot_role_ctf_retriever(entity this) +{ + entity mf; + + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If flag is back on the base switch to previous role + mf = havocbot_ctf_find_flag(this); + if(mf.ctf_status==FLAG_BASE) + { + if (mf.enemy == this) // did this bot return the flag? + navigation_goalrating_timeout_force(this); + havocbot_ctf_reset_role(this); + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 20; + + if (time > this.havocbot_role_timeout) + { + havocbot_ctf_reset_role(this); + return; + } + + if (navigation_goalrating_timeout(this)) + { + const float RT_RADIUS = 10000; + + navigation_goalrating_start(this); + + // role: retriever + havocbot_goalrating_ctf_ourstolenflag(this, 10000); + havocbot_goalrating_ctf_droppedflags(this, 12000, this.origin, RT_RADIUS); + havocbot_goalrating_ctf_enemybase(this, 8000); + entity ef = havocbot_ctf_find_enemy_flag(this); + vector enemy_base_org = ef.dropped_origin; + // start collecting items very close to the bot but only inside of enemy base radius + if (vdist(this.origin - enemy_base_org, <, havocbot_middlepoint_radius)) + havocbot_goalrating_items(this, 27000, this.origin, min(500, havocbot_middlepoint_radius * 0.5)); + havocbot_goalrating_items(this, 18000, this.origin, havocbot_middlepoint_radius); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ctf_middle(entity this) +{ + entity mf; + + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + mf = havocbot_ctf_find_flag(this); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 10; + + if (time > this.havocbot_role_timeout) + { + havocbot_ctf_reset_role(this); + return; + } + + if (navigation_goalrating_timeout(this)) + { + vector org; + + org = havocbot_middlepoint; + org.z = this.origin.z; + + navigation_goalrating_start(this); + + // role: middle + havocbot_goalrating_ctf_ourstolenflag(this, 8000); + havocbot_goalrating_ctf_droppedflags(this, 9000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 25000, org, havocbot_middlepoint_radius * 0.5); + havocbot_goalrating_items(this, 25000, org, havocbot_middlepoint_radius * 0.5); + havocbot_goalrating_items(this, 18000, this.origin, 10000); + havocbot_goalrating_ctf_enemybase(this, 3000); + + navigation_goalrating_end(this); + + entity goal = this.goalentity; + if (havocbot_ctf_is_basewaypoint(goal) && vdist(goal.origin - this.origin, <, 100)) + this.goalentity_lock_timeout = time + 2; + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ctf_defense(entity this) +{ + entity mf; + + if(IS_DEAD(this)) + { + havocbot_ctf_reset_role(this); + return; + } + + if (this.flagcarried) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); + return; + } + + // If own flag was captured + mf = havocbot_ctf_find_flag(this); + if(mf.ctf_status!=FLAG_BASE) + { + havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + 30; + + if (time > this.havocbot_role_timeout) + { + havocbot_ctf_reset_role(this); + return; + } + if (navigation_goalrating_timeout(this)) + { + vector org = mf.dropped_origin; + + navigation_goalrating_start(this); + + // if enemies are closer to our base, go there + entity closestplayer = NULL; + float distance, bestdistance = 10000; + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { + distance = vlen(org - it.origin); + if(distance<bestdistance) + { + closestplayer = it; + bestdistance = distance; + } + }); + + // role: defense + if(closestplayer) + if(DIFF_TEAM(closestplayer, this)) + if(vdist(org - this.origin, >, 1000)) + if(checkpvs(this.origin,closestplayer)||random()<0.5) + havocbot_goalrating_ctf_ourbase(this, 10000); + + havocbot_goalrating_ctf_ourstolenflag(this, 5000); + havocbot_goalrating_ctf_droppedflags(this, 6000, org, havocbot_middlepoint_radius); + havocbot_goalrating_enemyplayers(this, 25000, org, havocbot_middlepoint_radius); + havocbot_goalrating_items(this, 25000, org, havocbot_middlepoint_radius); + havocbot_goalrating_items(this, 18000, this.origin, 10000); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ctf_setrole(entity bot, int role) +{ + string s = "(null)"; + switch(role) + { + case HAVOCBOT_CTF_ROLE_CARRIER: + s = "carrier"; + bot.havocbot_role = havocbot_role_ctf_carrier; + bot.havocbot_role_timeout = 0; + bot.havocbot_cantfindflag = time + 10; + if (bot.havocbot_previous_role != bot.havocbot_role) + navigation_goalrating_timeout_force(bot); + break; + case HAVOCBOT_CTF_ROLE_DEFENSE: + s = "defense"; + bot.havocbot_role = havocbot_role_ctf_defense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_MIDDLE: + s = "middle"; + bot.havocbot_role = havocbot_role_ctf_middle; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_OFFENSE: + s = "offense"; + bot.havocbot_role = havocbot_role_ctf_offense; + bot.havocbot_role_timeout = 0; + break; + case HAVOCBOT_CTF_ROLE_RETRIEVER: + s = "retriever"; + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_retriever; + bot.havocbot_role_timeout = time + 10; + if (bot.havocbot_previous_role != bot.havocbot_role) + navigation_goalrating_timeout_expire(bot, 2); + break; + case HAVOCBOT_CTF_ROLE_ESCORT: + s = "escort"; + bot.havocbot_previous_role = bot.havocbot_role; + bot.havocbot_role = havocbot_role_ctf_escort; + bot.havocbot_role_timeout = time + 30; + if (bot.havocbot_previous_role != bot.havocbot_role) + navigation_goalrating_timeout_expire(bot, 2); + break; + } + LOG_TRACE(bot.netname, " switched to ", s); +} + + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + int t = 0, t2 = 0, t3 = 0; + bool b1 = false, b2 = false, b3 = false, b4 = false, b5 = false; // TODO: kill this, we WANT to show the other flags, somehow! (note: also means you don't see if you're FC) + + // initially clear items so they can be set as necessary later. + STAT(CTF_FLAGSTATUS, player) &= ~(CTF_RED_FLAG_CARRYING | CTF_RED_FLAG_TAKEN | CTF_RED_FLAG_LOST + | CTF_BLUE_FLAG_CARRYING | CTF_BLUE_FLAG_TAKEN | CTF_BLUE_FLAG_LOST + | CTF_YELLOW_FLAG_CARRYING | CTF_YELLOW_FLAG_TAKEN | CTF_YELLOW_FLAG_LOST + | CTF_PINK_FLAG_CARRYING | CTF_PINK_FLAG_TAKEN | CTF_PINK_FLAG_LOST + | CTF_NEUTRAL_FLAG_CARRYING | CTF_NEUTRAL_FLAG_TAKEN | CTF_NEUTRAL_FLAG_LOST + | CTF_FLAG_NEUTRAL | CTF_SHIELDED | CTF_STALEMATE); + + // scan through all the flags and notify the client about them + for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + if(flag.team == NUM_TEAM_1 && !b1) { b1 = true; t = CTF_RED_FLAG_CARRYING; t2 = CTF_RED_FLAG_TAKEN; t3 = CTF_RED_FLAG_LOST; } + if(flag.team == NUM_TEAM_2 && !b2) { b2 = true; t = CTF_BLUE_FLAG_CARRYING; t2 = CTF_BLUE_FLAG_TAKEN; t3 = CTF_BLUE_FLAG_LOST; } + if(flag.team == NUM_TEAM_3 && !b3) { b3 = true; t = CTF_YELLOW_FLAG_CARRYING; t2 = CTF_YELLOW_FLAG_TAKEN; t3 = CTF_YELLOW_FLAG_LOST; } + if(flag.team == NUM_TEAM_4 && !b4) { b4 = true; t = CTF_PINK_FLAG_CARRYING; t2 = CTF_PINK_FLAG_TAKEN; t3 = CTF_PINK_FLAG_LOST; } + if(flag.team == 0 && !b5) { b5 = true; t = CTF_NEUTRAL_FLAG_CARRYING; t2 = CTF_NEUTRAL_FLAG_TAKEN; t3 = CTF_NEUTRAL_FLAG_LOST; STAT(CTF_FLAGSTATUS, player) |= CTF_FLAG_NEUTRAL; } + + switch(flag.ctf_status) + { + case FLAG_PASSING: + case FLAG_CARRY: + { + if((flag.owner == player) || (flag.pass_sender == player)) + STAT(CTF_FLAGSTATUS, player) |= t; // carrying: player is currently carrying the flag + else + STAT(CTF_FLAGSTATUS, player) |= t2; // taken: someone else is carrying the flag + break; + } + case FLAG_DROPPED: + { + STAT(CTF_FLAGSTATUS, player) |= t3; // lost: the flag is dropped somewhere on the map + break; + } + } + } + + // item for stopping players from capturing the flag too often + if(player.ctf_captureshielded) + STAT(CTF_FLAGSTATUS, player) |= CTF_SHIELDED; + + if(ctf_stalemate) + STAT(CTF_FLAGSTATUS, player) |= CTF_STALEMATE; + + // update the health of the flag carrier waypointsprite + if(player.wps_flagcarrier) + WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(player, RESOURCE_HEALTH), GetResourceAmount(player, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); +} + +MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_damage = M_ARGV(4, float); + vector frag_force = M_ARGV(6, vector); + + if(frag_attacker.flagcarried) // if the attacker is a flagcarrier + { + if(frag_target == frag_attacker) // damage done to yourself + { + frag_damage *= autocvar_g_ctf_flagcarrier_selfdamagefactor; + frag_force *= autocvar_g_ctf_flagcarrier_selfforcefactor; + } + else // damage done to everyone else + { + frag_damage *= autocvar_g_ctf_flagcarrier_damagefactor; + frag_force *= autocvar_g_ctf_flagcarrier_forcefactor; + } + + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; + } + else if(frag_target.flagcarried && !IS_DEAD(frag_target) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier + { + if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(GetResourceAmount(frag_target, RESOURCE_HEALTH), GetResourceAmount(frag_target, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id))) + if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) + { + frag_target.wps_helpme_time = time; + WaypointSprite_HelpMePing(frag_target.wps_flagcarrier); + } + // todo: add notification for when flag carrier needs help? + } +} + +MUTATOR_HOOKFUNCTION(ctf, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried)) + { + GameRules_scoring_add_team(frag_attacker, SCORE, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill)); + GameRules_scoring_add(frag_attacker, CTF_FCKILLS, 1); + } + + if(frag_target.flagcarried) + { + entity tmp_entity = frag_target.flagcarried; + ctf_Handle_Throw(frag_target, NULL, DROP_NORMAL); + tmp_entity.ctf_dropper = NULL; + } +} + +MUTATOR_HOOKFUNCTION(ctf, GiveFragsForKill) +{ + M_ARGV(2, float) = 0; // frag score + return (autocvar_g_ctf_ignore_frags); // no frags counted in ctf if this is true +} + +void ctf_RemovePlayer(entity player) +{ + if(player.flagcarried) + { ctf_Handle_Throw(player, NULL, DROP_NORMAL); } + + for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + if(flag.pass_sender == player) { flag.pass_sender = NULL; } + if(flag.pass_target == player) { flag.pass_target = NULL; } + if(flag.ctf_dropper == player) { flag.ctf_dropper = NULL; } + } +} + +MUTATOR_HOOKFUNCTION(ctf, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + ctf_RemovePlayer(player); +} + +MUTATOR_HOOKFUNCTION(ctf, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + ctf_RemovePlayer(player); +} + +MUTATOR_HOOKFUNCTION(ctf, ClientConnect) +{ + if(!autocvar_g_ctf_leaderboard) + return; + + entity player = M_ARGV(0, entity); + + if(IS_REAL_CLIENT(player)) + { + int m = min(RANKINGS_CNT, autocvar_g_cts_send_rankings_cnt); + race_send_rankings_cnt(MSG_ONE); + for (int i = 1; i <= m; ++i) + { + race_SendRankings(i, 0, 0, MSG_ONE); + } + } +} + +MUTATOR_HOOKFUNCTION(ctf, GetPressedKeys) +{ + if(!autocvar_g_ctf_leaderboard) + return; + + entity player = M_ARGV(0, entity); + + if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) + { + if (!player.stored_netname) + player.stored_netname = strzone(uid2name(player.crypto_idfp)); + if(player.stored_netname != player.netname) + { + db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); + strcpy(player.stored_netname, player.netname); + } + } +} + +MUTATOR_HOOKFUNCTION(ctf, PortalTeleport) +{ + entity player = M_ARGV(0, entity); + + if(player.flagcarried) + if(!autocvar_g_ctf_portalteleport) + { ctf_Handle_Throw(player, NULL, DROP_NORMAL); } +} + +MUTATOR_HOOKFUNCTION(ctf, PlayerUseKey) +{ + if(MUTATOR_RETURNVALUE || game_stopped) return; + + entity player = M_ARGV(0, entity); + + if((time > player.throw_antispam) && !IS_DEAD(player) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch)) + { + // pass the flag to a team mate + if(autocvar_g_ctf_pass) + { + entity head, closest_target = NULL; + head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, true); + + while(head) // find the closest acceptable target to pass to + { + if(IS_PLAYER(head) && !IS_DEAD(head)) + if(head != player && SAME_TEAM(head, player)) + if(!head.speedrunning && !head.vehicle) + { + // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) + vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head)); + vector passer_center = CENTER_OR_VIEWOFS(player); + + if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest)) + { + if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) + { + if(IS_BOT_CLIENT(head)) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname); + ctf_Handle_Throw(head, player, DROP_PASS); + } + else + { + Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_CTF_PASS_REQUESTED, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname); + } + player.throw_antispam = time + autocvar_g_ctf_pass_wait; + return true; + } + else if(player.flagcarried && !head.flagcarried) + { + if(closest_target) + { + vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target)); + if(vlen2(passer_center - head_center) < vlen2(passer_center - closest_target_center)) + { closest_target = head; } + } + else { closest_target = head; } + } + } + } + head = head.chain; + } + + if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return true; } + } + + // throw the flag in front of you + if(autocvar_g_ctf_throw && player.flagcarried) + { + if(player.throw_count == -1) + { + if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) + { + player.throw_prevtime = time; + player.throw_count = 1; + ctf_Handle_Throw(player, NULL, DROP_THROW); + return true; + } + else + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_FLAG_THROW_PUNISH, rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)); + return false; + } + } + else + { + if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_time) { player.throw_count = 1; } + else { player.throw_count += 1; } + if(player.throw_count >= autocvar_g_ctf_throw_punish_count) { player.throw_count = -1; } + + player.throw_prevtime = time; + ctf_Handle_Throw(player, NULL, DROP_THROW); + return true; + } + } + } +} + +MUTATOR_HOOKFUNCTION(ctf, HelpMePing) +{ + entity player = M_ARGV(0, entity); + + if(player.wps_flagcarrier) // update the flagcarrier waypointsprite with "NEEDING HELP" notification + { + player.wps_helpme_time = time; + WaypointSprite_HelpMePing(player.wps_flagcarrier); + } + else // create a normal help me waypointsprite + { + WaypointSprite_Spawn(WP_Helpme, waypointsprite_deployed_lifetime, waypointsprite_limitedrange, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_helpme, false, RADARICON_HELPME); + WaypointSprite_Ping(player.wps_helpme); + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ctf, VehicleEnter) +{ + entity player = M_ARGV(0, entity); + entity veh = M_ARGV(1, entity); + + if(player.flagcarried) + { + if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch) + { + ctf_Handle_Throw(player, NULL, DROP_NORMAL); + } + else + { + player.flagcarried.nodrawtoclient = player; // hide the flag from the driver + setattachment(player.flagcarried, veh, ""); + setorigin(player.flagcarried, VEHICLE_FLAG_OFFSET); + player.flagcarried.scale = VEHICLE_FLAG_SCALE; + //player.flagcarried.angles = '0 0 0'; + } + return true; + } +} + +MUTATOR_HOOKFUNCTION(ctf, VehicleExit) +{ + entity player = M_ARGV(0, entity); + + if(player.flagcarried) + { + setattachment(player.flagcarried, player, ""); + setorigin(player.flagcarried, FLAG_CARRY_OFFSET); + player.flagcarried.scale = FLAG_SCALE; + player.flagcarried.angles = '0 0 0'; + player.flagcarried.nodrawtoclient = NULL; + return true; + } +} + +MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun) +{ + entity player = M_ARGV(0, entity); + + if(player.flagcarried) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(player.flagcarried.team, INFO_CTF_FLAGRETURN_ABORTRUN)); + ctf_RespawnFlag(player.flagcarried); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ctf, MatchEnd) +{ + entity flag; // temporary entity for the search method + + for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) + { + switch(flag.ctf_status) + { + case FLAG_DROPPED: + case FLAG_PASSING: + { + // lock the flag, game is over + set_movetype(flag, MOVETYPE_NONE); + flag.takedamage = DAMAGE_NO; + flag.solid = SOLID_NOT; + flag.nextthink = false; // stop thinking + + //dprint("stopping the ", flag.netname, " from moving.\n"); + break; + } + + default: + case FLAG_BASE: + case FLAG_CARRY: + { + // do nothing for these flags + break; + } + } + } +} + +MUTATOR_HOOKFUNCTION(ctf, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + havocbot_ctf_reset_role(bot); + return true; +} + +MUTATOR_HOOKFUNCTION(ctf, TeamBalance_CheckAllowedTeams) +{ + M_ARGV(1, string) = "ctf_team"; +} + +MUTATOR_HOOKFUNCTION(ctf, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + STAT(CTF_FLAGSTATUS, client) = STAT(CTF_FLAGSTATUS, spectatee); +} + +MUTATOR_HOOKFUNCTION(ctf, GetRecords) +{ + int record_page = M_ARGV(0, int); + string ret_string = M_ARGV(1, string); + + for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) + { + if (MapInfo_Get_ByID(i)) + { + float r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time"))); + + if(!r) + continue; + + // TODO: uid2name + string h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname")); + ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n"); + } + } + + M_ARGV(1, string) = ret_string; +} + +bool superspec_Spectate(entity this, entity targ); // TODO +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel); // TODO +MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) +{ + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if(IS_PLAYER(player) || MUTATOR_RETURNVALUE || !cvar("g_superspectate")) { return false; } + + if(cmd_name == "followfc") + { + if(!g_ctf) + return true; + + int _team = 0; + bool found = false; + + if(cmd_argc == 2) + { + switch(argv(1)) + { + case "red": if(ctf_teams & BIT(0)) _team = NUM_TEAM_1; break; + case "blue": if(ctf_teams & BIT(1)) _team = NUM_TEAM_2; break; + case "yellow": if(ctf_teams & BIT(2)) _team = NUM_TEAM_3; break; + case "pink": if(ctf_teams & BIT(3)) _team = NUM_TEAM_4; break; + } + } + + FOREACH_CLIENT(IS_PLAYER(it), { + if(it.flagcarried && (it.team == _team || _team == 0)) + { + found = true; + if(_team == 0 && IS_SPEC(player) && player.enemy == it) + continue; // already spectating this fc, try another + return superspec_Spectate(player, it); + } + }); + + if(!found) + superspec_msg("", "", player, "No active flag carrier\n", 1); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems) +{ + entity frag_target = M_ARGV(0, entity); + + if(frag_target.flagcarried) + ctf_Handle_Throw(frag_target, NULL, DROP_THROW); +} + + +// ========== +// Spawnfuncs +// ========== + +/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team one (Red). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red and blue as skins 0 and 1... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +spawnfunc(item_flag_team1) +{ + if(!g_ctf) { delete(this); return; } + + ctf_FlagSetup(NUM_TEAM_1, this); +} + +/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team two (Blue). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red and blue as skins 0 and 1... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +spawnfunc(item_flag_team2) +{ + if(!g_ctf) { delete(this); return; } + + ctf_FlagSetup(NUM_TEAM_2, this); +} + +/*QUAKED spawnfunc_item_flag_team3 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team three (Yellow). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +spawnfunc(item_flag_team3) +{ + if(!g_ctf) { delete(this); return; } + + ctf_FlagSetup(NUM_TEAM_3, this); +} + +/*QUAKED spawnfunc_item_flag_team4 (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag for team four (Pink). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +spawnfunc(item_flag_team4) +{ + if(!g_ctf) { delete(this); return; } + + ctf_FlagSetup(NUM_TEAM_4, this); +} + +/*QUAKED spawnfunc_item_flag_neutral (0 0.5 0.8) (-48 -48 -37) (48 48 37) +CTF flag (Neutral). +Keys: +"angle" Angle the flag will point (minus 90 degrees)... +"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... +"noise" sound played when flag is picked up... +"noise1" sound played when flag is returned by a teammate... +"noise2" sound played when flag is captured... +"noise3" sound played when flag is lost in the field and respawns itself... +"noise4" sound played when flag is dropped by a player... +"noise5" sound played when flag touches the ground... */ +spawnfunc(item_flag_neutral) +{ + if(!g_ctf) { delete(this); return; } + if(!cvar("g_ctf_oneflag")) { delete(this); return; } + + ctf_FlagSetup(0, this); +} + +/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32) +Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map. +Note: If you use spawnfunc_ctf_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too. +Keys: +"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)... +"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ +spawnfunc(ctf_team) +{ + if(!g_ctf) { delete(this); return; } + + this.classname = "ctf_team"; + this.team = this.cnt + 1; +} + +// compatibility for quake maps +spawnfunc(team_CTF_redflag) { spawnfunc_item_flag_team1(this); } +spawnfunc(team_CTF_blueflag) { spawnfunc_item_flag_team2(this); } +spawnfunc(info_player_team1); +spawnfunc(team_CTF_redplayer) { spawnfunc_info_player_team1(this); } +spawnfunc(team_CTF_redspawn) { spawnfunc_info_player_team1(this); } +spawnfunc(info_player_team2); +spawnfunc(team_CTF_blueplayer) { spawnfunc_info_player_team2(this); } +spawnfunc(team_CTF_bluespawn) { spawnfunc_info_player_team2(this); } + +spawnfunc(team_CTF_neutralflag) { spawnfunc_item_flag_neutral(this); } +spawnfunc(team_neutralobelisk) { spawnfunc_item_flag_neutral(this); } + +// compatibility for wop maps +spawnfunc(team_redplayer) { spawnfunc_info_player_team1(this); } +spawnfunc(team_blueplayer) { spawnfunc_info_player_team2(this); } +spawnfunc(team_ctl_redlolly) { spawnfunc_item_flag_team1(this); } +spawnfunc(team_CTL_redlolly) { spawnfunc_item_flag_team1(this); } +spawnfunc(team_ctl_bluelolly) { spawnfunc_item_flag_team2(this); } +spawnfunc(team_CTL_bluelolly) { spawnfunc_item_flag_team2(this); } + + +// ============== +// Initialization +// ============== + +// scoreboard setup +void ctf_ScoreRules(int teams) +{ + GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { + field_team(ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); + field(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + field(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); + field(SP_CTF_PICKUPS, "pickups", 0); + field(SP_CTF_FCKILLS, "fckills", 0); + field(SP_CTF_RETURNS, "returns", 0); + field(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); + }); +} + +// code from here on is just to support maps that don't have flag and team entities +void ctf_SpawnTeam (string teamname, int teamcolor) +{ + entity this = new_pure(ctf_team); + this.netname = teamname; + this.cnt = teamcolor - 1; + this.spawnfunc_checked = true; + this.team = teamcolor; +} + +void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. +{ + ctf_teams = 0; + + entity tmp_entity; + for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) + { + //if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } + //if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } + + switch(tmp_entity.team) + { + case NUM_TEAM_1: BITSET_ASSIGN(ctf_teams, BIT(0)); break; + case NUM_TEAM_2: BITSET_ASSIGN(ctf_teams, BIT(1)); break; + case NUM_TEAM_3: BITSET_ASSIGN(ctf_teams, BIT(2)); break; + case NUM_TEAM_4: BITSET_ASSIGN(ctf_teams, BIT(3)); break; + } + if(tmp_entity.team == 0) { ctf_oneflag = true; } + } + + havocbot_ctf_calculate_middlepoint(); + + if(NumTeams(ctf_teams) < 2) // somehow, there's not enough flags! + { + ctf_teams = 0; // so set the default red and blue teams + BITSET_ASSIGN(ctf_teams, BIT(0)); + BITSET_ASSIGN(ctf_teams, BIT(1)); + } + + //ctf_teams = bound(2, ctf_teams, 4); + + // if no teams are found, spawn defaults + if(find(NULL, classname, "ctf_team") == NULL) + { + LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway."); + if(ctf_teams & BIT(0)) + ctf_SpawnTeam("Red", NUM_TEAM_1); + if(ctf_teams & BIT(1)) + ctf_SpawnTeam("Blue", NUM_TEAM_2); + if(ctf_teams & BIT(2)) + ctf_SpawnTeam("Yellow", NUM_TEAM_3); + if(ctf_teams & BIT(3)) + ctf_SpawnTeam("Pink", NUM_TEAM_4); + } + + ctf_ScoreRules(ctf_teams); +} + +void ctf_Initialize() +{ + ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"))); + + ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore; + ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio; + ctf_captureshield_force = autocvar_g_ctf_shield_force; + + InitializeEntity(NULL, ctf_DelayedInit, INITPRIO_GAMETYPE); +} diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh new file mode 100644 index 0000000000..350ad8c4ff --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh @@ -0,0 +1,169 @@ +#pragma once + +#include "ctf.qh" + +void ctf_Initialize(); + +REGISTER_MUTATOR(ctf, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + GameRules_limit_score(autocvar_capturelimit_override); + GameRules_limit_lead(autocvar_captureleadlimit_override); + + ctf_Initialize(); + } + return 0; +} + +// used in cheats.qc +void ctf_RespawnFlag(entity flag); + +// score rule declarations +const int ST_CTF_CAPS = 1; + +CLASS(Flag, Pickup) + ATTRIB(Flag, m_mins, vector, (PL_MIN_CONST + '0 0 -13') * 1.4); // scaling be damned + ATTRIB(Flag, m_maxs, vector, (PL_MAX_CONST + '0 0 -13') * 1.4); +ENDCLASS(Flag) +Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } +void ctf_FlagTouch(entity this, entity toucher) { ITEM_HANDLE(Pickup, CTF_FLAG, this, toucher); } + +// flag constants // for most of these, there is just one question to be asked: WHYYYYY? + +const float FLAG_SCALE = 0.6; + +const float FLAG_THINKRATE = 0.2; +const float FLAG_TOUCHRATE = 0.5; +const float WPFE_THINKRATE = 0.5; + +const vector FLAG_DROP_OFFSET = ('0 0 32'); +const vector FLAG_CARRY_OFFSET = ('-16 0 8'); +#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) +const vector FLAG_WAYPOINT_OFFSET = ('0 0 64'); +const int FLAG_FLOAT_OFFSET_Z = 32; +const int FLAG_PASS_ARC_OFFSET_Z = -10; + +const vector VEHICLE_FLAG_OFFSET = ('0 0 96'); +const float VEHICLE_FLAG_SCALE = 1.0; + +// waypoint colors +#define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') +#define WPCOLOR_FLAGCARRIER(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') +//#define WPCOLOR_FLAGCARRIER(t) (WP_FlagCarrier.m_color) +#define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5 : '1 1 1') + +// sounds +#define snd_flag_taken noise +#define snd_flag_returned noise1 +#define snd_flag_capture noise2 +#define snd_flag_respawn noise3 +.string snd_flag_dropped; +.string snd_flag_touch; +.string snd_flag_pass; + +// score fields +.float score_assist; +.float score_capture; +.float score_drop; // note: negated +.float score_pickup; +.float score_return; +.float score_team_capture; // shouldn't be too high + +// effects +.string toucheffect; +.string passeffect; +.string capeffect; + +// list of flags on the map +entity ctf_worldflaglist; +.entity ctf_worldflagnext; +.entity ctf_staleflagnext; + +// waypoint sprites +.entity wps_helpme; +.entity wps_flagbase; +.entity wps_flagcarrier; +.entity wps_flagdropped; +.entity wps_flagreturn; +.entity wps_enemyflagcarrier; +.float wps_helpme_time; +bool wpforenemy_announced; +float wpforenemy_nextthink; + +// statuses +const int FLAG_BASE = 1; +const int FLAG_DROPPED = 2; +const int FLAG_CARRY = 3; +const int FLAG_PASSING = 4; + +const int DROP_NORMAL = 1; +const int DROP_THROW = 2; +const int DROP_PASS = 3; +const int DROP_RESET = 4; + +const int PICKUP_BASE = 1; +const int PICKUP_DROPPED = 2; + +const int CAPTURE_NORMAL = 1; +const int CAPTURE_DROPPED = 2; + +const int RETURN_TIMEOUT = 1; +const int RETURN_DROPPED = 2; +const int RETURN_DAMAGE = 3; +const int RETURN_SPEEDRUN = 4; +const int RETURN_NEEDKILL = 5; + +bool ctf_Stalemate_Customize(entity this, entity client); + +void ctf_Handle_Throw(entity player, entity receiver, float droptype); + +// flag properties +#define ctf_spawnorigin dropped_origin +bool ctf_stalemate; // indicates that a stalemate is active +float ctf_captimerecord; // record time for capturing the flag +.float ctf_pickuptime; +.float ctf_droptime; +.int ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally) +.entity ctf_dropper; // don't allow spam of dropping the flag +.int max_flag_health; +.float next_take_time; +.bool ctf_flagdamaged_byworld; +int ctf_teams; +.entity enemy; // when flag is back in the base, it remembers last player who carried/touched the flag, useful to bots + +// passing/throwing properties +.float pass_distance; +.entity pass_sender; +.entity pass_target; +.float throw_antispam; +.float throw_prevtime; +.int throw_count; + +// CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag. +.bool ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture +float ctf_captureshield_min_negscore; // punish at -20 points +float ctf_captureshield_max_ratio; // punish at most 30% of each team +float ctf_captureshield_force; // push force of the shield + +// 1 flag ctf +bool ctf_oneflag; // indicates whether or not a neutral flag has been found + +// bot player logic +const int HAVOCBOT_CTF_ROLE_NONE = 0; +const int HAVOCBOT_CTF_ROLE_DEFENSE = 2; +const int HAVOCBOT_CTF_ROLE_MIDDLE = 4; +const int HAVOCBOT_CTF_ROLE_OFFENSE = 8; +const int HAVOCBOT_CTF_ROLE_CARRIER = 16; +const int HAVOCBOT_CTF_ROLE_RETRIEVER = 32; +const int HAVOCBOT_CTF_ROLE_ESCORT = 64; + +.bool havocbot_cantfindflag; + +void havocbot_role_ctf_setrole(entity bot, int role); + +// team checking +#define CTF_SAMETEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? DIFF_TEAM(a,b) : SAME_TEAM(a,b)) +#define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b)) diff --git a/qcsrc/common/gamemodes/gamemode/cts/_mod.inc b/qcsrc/common/gamemodes/gamemode/cts/_mod.inc new file mode 100644 index 0000000000..a48cd89dda --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/cts/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/cts/sv_cts.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/cts/_mod.qh b/qcsrc/common/gamemodes/gamemode/cts/_mod.qh new file mode 100644 index 0000000000..d05e62987f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/cts/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/cts/sv_cts.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc new file mode 100644 index 0000000000..31bdae0790 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qc @@ -0,0 +1,416 @@ +#include "sv_cts.qh" + +#include <server/race.qh> +#include <server/items.qh> + +float autocvar_g_cts_finish_kill_delay; +bool autocvar_g_cts_selfdamage; +bool autocvar_g_cts_removeprojectiles; + +// legacy bot roles +.float race_checkpoint; +void havocbot_role_cts(entity this) +{ + if(IS_DEAD(this)) + return; + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + bool raw_touch_check = true; + int cp = this.race_checkpoint; + + LABEL(search_racecheckpoints) + IL_EACH(g_racecheckpoints, true, + { + if(it.cnt == cp || cp == -1) + { + // redirect bot to next goal if it touched the waypoint of an untouchable checkpoint + // e.g. checkpoint in front of Stormkeep's warpzone + // the same workaround is applied in Race game mode + if (raw_touch_check && vdist(this.origin - it.nearestwaypoint.origin, <, 30)) + { + cp = race_NextCheckpoint(cp); + raw_touch_check = false; + goto search_racecheckpoints; + } + navigation_routerating(this, it, 1000000, 5000); + } + }); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void cts_ScoreRules() +{ + GameRules_score_enabled(false); + GameRules_scoring(0, 0, 0, { + if (g_race_qualifying) { + field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } else { + field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } + }); +} + +void cts_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":cts:", mode, ":", ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +MUTATOR_HOOKFUNCTION(cts, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + float dt = M_ARGV(1, float); + + player.race_movetime_frac += dt; + float f = floor(player.race_movetime_frac); + player.race_movetime_frac -= f; + player.race_movetime_count += f; + player.race_movetime = player.race_movetime_frac + player.race_movetime_count; + + if(IS_PLAYER(player)) + { + if (player.race_penalty) + if (time > player.race_penalty) + player.race_penalty = 0; + if(player.race_penalty) + { + player.velocity = '0 0 0'; + set_movetype(player, MOVETYPE_NONE); + player.disableclientprediction = 2; + } + } + + // force kbd movement for fairness + float wishspeed; + vector wishvel; + + // if record times matter + // ensure nothing EVIL is being done (i.e. div0_evade) + // this hinders joystick users though + // but it still gives SOME analog control + wishvel.x = fabs(CS(player).movement.x); + wishvel.y = fabs(CS(player).movement.y); + if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y) + { + wishvel.z = 0; + wishspeed = vlen(wishvel); + if(wishvel.x >= 2 * wishvel.y) + { + // pure X motion + if(CS(player).movement.x > 0) + CS(player).movement_x = wishspeed; + else + CS(player).movement_x = -wishspeed; + CS(player).movement_y = 0; + } + else if(wishvel.y >= 2 * wishvel.x) + { + // pure Y motion + CS(player).movement_x = 0; + if(CS(player).movement.y > 0) + CS(player).movement_y = wishspeed; + else + CS(player).movement_y = -wishspeed; + } + else + { + // diagonal + if(CS(player).movement.x > 0) + CS(player).movement_x = M_SQRT1_2 * wishspeed; + else + CS(player).movement_x = -M_SQRT1_2 * wishspeed; + if(CS(player).movement.y > 0) + CS(player).movement_y = M_SQRT1_2 * wishspeed; + else + CS(player).movement_y = -M_SQRT1_2 * wishspeed; + } + } +} + +MUTATOR_HOOKFUNCTION(cts, reset_map_global) +{ + float s; + + Score_NicePrint(NULL); + + race_ClearRecords(); + PlayerScore_Sort(race_place, 0, 1, 0); + + FOREACH_CLIENT(true, { + if(it.race_place) + { + s = GameRules_scoring_add(it, RACE_FASTEST, 0); + if(!s) + it.race_place = 0; + } + cts_EventLog(ftos(it.race_place), it); + }); + + if(g_race_qualifying == 2) + { + g_race_qualifying = 0; + independent_players = 0; + cvar_set("fraglimit", ftos(race_fraglimit)); + cvar_set("leadlimit", ftos(race_leadlimit)); + cvar_set("timelimit", ftos(race_timelimit)); + cts_ScoreRules(); + } +} + +MUTATOR_HOOKFUNCTION(cts, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + race_PreparePlayer(player); + player.race_checkpoint = -1; + + if(IS_REAL_CLIENT(player)) + { + string rr = CTS_RECORD; + + msg_entity = player; + race_send_recordtime(MSG_ONE); + race_send_speedaward(MSG_ONE); + + speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); + speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); + race_send_speedaward_alltimebest(MSG_ONE); + + float i; + int m = min(RANKINGS_CNT, autocvar_g_cts_send_rankings_cnt); + race_send_rankings_cnt(MSG_ONE); + for (i = 1; i <= m; ++i) + { + race_SendRankings(i, 0, 0, MSG_ONE); + } + } +} + +MUTATOR_HOOKFUNCTION(cts, AbortSpeedrun) +{ + entity player = M_ARGV(0, entity); + + if(autocvar_g_allow_checkpoints) + race_PreparePlayer(player); // nice try +} + +MUTATOR_HOOKFUNCTION(cts, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + if(GameRules_scoring_add(player, RACE_FASTEST, 0)) + player.frags = FRAGS_LMS_LOSER; + else + player.frags = FRAGS_SPECTATOR; + + race_PreparePlayer(player); + player.race_checkpoint = -1; +} + +MUTATOR_HOOKFUNCTION(cts, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + + if(spawn_spot.target == "") + // Emergency: this wasn't a real spawnpoint. Can this ever happen? + race_PreparePlayer(player); + + // if we need to respawn, do it right + player.race_respawn_checkpoint = player.race_checkpoint; + player.race_respawn_spotref = spawn_spot; + + player.race_place = 0; +} + +MUTATOR_HOOKFUNCTION(cts, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + if(!game_stopped) + { + if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn + race_PreparePlayer(player); + else // respawn + race_RetractPlayer(player); + + race_AbandonRaceCheck(player); + } +} + +MUTATOR_HOOKFUNCTION(cts, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.respawn_flags |= RESPAWN_FORCE; + race_AbandonRaceCheck(frag_target); + + if(autocvar_g_cts_removeprojectiles) + { + IL_EACH(g_projectiles, it.owner == frag_target && (it.flags & FL_PROJECTILE), + { + delete(it); + }); + } +} + +MUTATOR_HOOKFUNCTION(cts, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + bot.havocbot_role = havocbot_role_cts; + return true; +} + +MUTATOR_HOOKFUNCTION(cts, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) + { + if (!player.stored_netname) + player.stored_netname = strzone(uid2name(player.crypto_idfp)); + if(player.stored_netname != player.netname) + { + db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); + strcpy(player.stored_netname, player.netname); + } + } + + if (!IS_OBSERVER(player)) + { + if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed)) + { + speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1'); + speedaward_holder = player.netname; + speedaward_uid = player.crypto_idfp; + speedaward_lastupdate = time; + } + if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) + { + string rr = CTS_RECORD; + race_send_speedaward(MSG_ALL); + speedaward_lastsent = speedaward_speed; + if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") + { + speedaward_alltimebest = speedaward_speed; + speedaward_alltimebest_holder = speedaward_holder; + speedaward_alltimebest_uid = speedaward_uid; + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest)); + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid); + race_send_speedaward_alltimebest(MSG_ALL); + } + } + } +} + +MUTATOR_HOOKFUNCTION(cts, ForbidThrowCurrentWeapon) +{ + // no weapon dropping in CTS + return true; +} + +MUTATOR_HOOKFUNCTION(cts, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if (Item_IsLoot(item)) + { + return true; + } +} + +MUTATOR_HOOKFUNCTION(cts, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + float frag_damage = M_ARGV(4, float); + + if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL.m_id) + if(!autocvar_g_cts_selfdamage) + { + frag_damage = 0; + M_ARGV(4, float) = frag_damage; + } +} + +MUTATOR_HOOKFUNCTION(cts, ForbidPlayerScore_Clear) +{ + return true; // in CTS, you don't lose score by observing +} + +MUTATOR_HOOKFUNCTION(cts, GetRecords) +{ + int record_page = M_ARGV(0, int); + string ret_string = M_ARGV(1, string); + + for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) + { + if(MapInfo_Get_ByID(i)) + { + float r = race_readTime(MapInfo_Map_bspname, 1); + + if(!r) + continue; + + string h = race_readName(MapInfo_Map_bspname, 1); + ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n"); + } + } + + M_ARGV(1, string) = ret_string; +} + +MUTATOR_HOOKFUNCTION(cts, ClientKill) +{ + M_ARGV(1, float) = 0; // kill delay +} + +MUTATOR_HOOKFUNCTION(cts, Race_FinalCheckpoint) +{ + entity player = M_ARGV(0, entity); + + // useful to prevent cheating by running back to the start line and starting out with more speed + if(autocvar_g_cts_finish_kill_delay) + ClientKill_Silent(player, autocvar_g_cts_finish_kill_delay); +} + +MUTATOR_HOOKFUNCTION(cts, HideTeamNagger) +{ + return true; // doesn't work so well (but isn't cts a teamless mode?) +} + +MUTATOR_HOOKFUNCTION(cts, FixClientCvars) +{ + entity player = M_ARGV(0, entity); + + stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n"); +} + +MUTATOR_HOOKFUNCTION(cts, WantWeapon) +{ + M_ARGV(1, float) = (M_ARGV(0, entity) == WEP_SHOTGUN); // want weapon = weapon info + M_ARGV(3, bool) = true; // want mutator blocked + return true; +} + +MUTATOR_HOOKFUNCTION(cts, ForbidDropCurrentWeapon) +{ + return true; +} + +void cts_Initialize() +{ + cts_ScoreRules(); +} diff --git a/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh new file mode 100644 index 0000000000..8c8453d768 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/cts/sv_cts.qh @@ -0,0 +1,24 @@ +#pragma once + +#include <common/mutators/base.qh> +#include <server/race.qh> + +void cts_Initialize(); + +REGISTER_MUTATOR(cts, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + g_race_qualifying = true; + independent_players = 1; + GameRules_limit_score(0); + GameRules_limit_lead(0); + + cts_Initialize(); + } + return 0; +} + +// scores +const float ST_CTS_LAPS = 1; diff --git a/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.inc b/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.inc new file mode 100644 index 0000000000..ba2449386c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.qh b/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.qh new file mode 100644 index 0000000000..abc7db377a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/deathmatch/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/deathmatch/sv_deathmatch.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc new file mode 100644 index 0000000000..e622a1942f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qc @@ -0,0 +1,7 @@ +#include "sv_deathmatch.qh" + +MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining) +{ + // announce remaining frags + return true; +} diff --git a/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qh b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qh new file mode 100644 index 0000000000..c08d5f87a1 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/deathmatch/sv_deathmatch.qh @@ -0,0 +1,8 @@ +#pragma once + +#include <common/mutators/base.qh> +REGISTER_MUTATOR(dm, false) +{ + MUTATOR_STATIC(); + return 0; +} diff --git a/qcsrc/common/gamemodes/gamemode/domination/_mod.inc b/qcsrc/common/gamemodes/gamemode/domination/_mod.inc new file mode 100644 index 0000000000..ff9bc11f88 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/domination/sv_domination.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/domination/_mod.qh b/qcsrc/common/gamemodes/gamemode/domination/_mod.qh new file mode 100644 index 0000000000..0c5e841214 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/domination/sv_domination.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc new file mode 100644 index 0000000000..78ff64e514 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qc @@ -0,0 +1,670 @@ +#include "sv_domination.qh" + +#include <server/teamplay.qh> + +bool g_domination; + +int autocvar_g_domination_default_teams; +bool autocvar_g_domination_disable_frags; +int autocvar_g_domination_point_amt; +bool autocvar_g_domination_point_fullbright; +float autocvar_g_domination_round_timelimit; +float autocvar_g_domination_warmup; +float autocvar_g_domination_point_rate; +int autocvar_g_domination_teams_override; + +void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":dom:", mode, ":", ftos(team_before), ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +void set_dom_state(entity e) +{ + STAT(DOM_TOTAL_PPS, e) = total_pps; + STAT(DOM_PPS_RED, e) = pps_red; + STAT(DOM_PPS_BLUE, e) = pps_blue; + if(domination_teams >= 3) + STAT(DOM_PPS_YELLOW, e) = pps_yellow; + if(domination_teams >= 4) + STAT(DOM_PPS_PINK, e) = pps_pink; +} + +void dompoint_captured(entity this) +{ + float old_delay, old_team, real_team; + + // now that the delay has expired, switch to the latest team to lay claim to this point + entity head = this.owner; + + real_team = this.cnt; + this.cnt = -1; + + dom_EventLog("taken", this.team, this.dmg_inflictor); + this.dmg_inflictor = NULL; + + this.goalentity = head; + this.model = head.mdl; + this.modelindex = head.dmg; + this.skin = head.skin; + + float points, wait_time; + if (autocvar_g_domination_point_amt) + points = autocvar_g_domination_point_amt; + else + points = this.frags; + if (autocvar_g_domination_point_rate) + wait_time = autocvar_g_domination_point_rate; + else + wait_time = this.wait; + + if(domination_roundbased) + bprint(sprintf("^3%s^3%s\n", head.netname, this.message)); + else + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_DOMINATION_CAPTURE_TIME, head.netname, this.message, points, wait_time); + + if(this.enemy.playerid == this.enemy_playerid) + GameRules_scoring_add(this.enemy, DOM_TAKES, 1); + else + this.enemy = NULL; + + if (head.noise != "") + if(this.enemy) + _sound(this.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM); + else + _sound(this, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM); + if (head.noise1 != "") + play2all(head.noise1); + + this.delay = time + wait_time; + + // do trigger work + old_delay = this.delay; + old_team = this.team; + this.team = real_team; + this.delay = 0; + SUB_UseTargets (this, this, NULL); + this.delay = old_delay; + this.team = old_team; + + entity msg = WP_DomNeut; + switch(real_team) + { + case NUM_TEAM_1: msg = WP_DomRed; break; + case NUM_TEAM_2: msg = WP_DomBlue; break; + case NUM_TEAM_3: msg = WP_DomYellow; break; + case NUM_TEAM_4: msg = WP_DomPink; break; + } + + WaypointSprite_UpdateSprites(this.sprite, msg, WP_Null, WP_Null); + + total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; + IL_EACH(g_dompoints, true, + { + if (autocvar_g_domination_point_amt) + points = autocvar_g_domination_point_amt; + else + points = it.frags; + if (autocvar_g_domination_point_rate) + wait_time = autocvar_g_domination_point_rate; + else + wait_time = it.wait; + switch(it.goalentity.team) + { + case NUM_TEAM_1: pps_red += points/wait_time; break; + case NUM_TEAM_2: pps_blue += points/wait_time; break; + case NUM_TEAM_3: pps_yellow += points/wait_time; break; + case NUM_TEAM_4: pps_pink += points/wait_time; break; + } + total_pps += points/wait_time; + }); + + WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DOMPOINT, colormapPaletteColor(this.goalentity.team - 1, 0)); + WaypointSprite_Ping(this.sprite); + + this.captime = time; + + FOREACH_CLIENT(IS_REAL_CLIENT(it), { set_dom_state(it); }); +} + +void AnimateDomPoint(entity this) +{ + if(this.pain_finished > time) + return; + this.pain_finished = time + this.t_width; + if(this.nextthink > this.pain_finished) + this.nextthink = this.pain_finished; + + this.frame = this.frame + 1; + if(this.frame > this.t_length) + this.frame = 0; +} + +void dompointthink(entity this) +{ + float fragamt; + + this.nextthink = time + 0.1; + + //this.frame = this.frame + 1; + //if(this.frame > 119) + // this.frame = 0; + AnimateDomPoint(this); + + // give points + + if (game_stopped || this.delay > time || time < game_starttime) // game has ended, don't keep giving points + return; + + if(autocvar_g_domination_point_rate) + this.delay = time + autocvar_g_domination_point_rate; + else + this.delay = time + this.wait; + + // give credit to the team + // NOTE: this defaults to 0 + if (!domination_roundbased) + if (this.goalentity.netname != "") + { + if(autocvar_g_domination_point_amt) + fragamt = autocvar_g_domination_point_amt; + else + fragamt = this.frags; + TeamScore_AddToTeam(this.goalentity.team, ST_SCORE, fragamt); + TeamScore_AddToTeam(this.goalentity.team, ST_DOM_TICKS, fragamt); + + // give credit to the individual player, if he is still there + if (this.enemy.playerid == this.enemy_playerid) + { + GameRules_scoring_add(this.enemy, SCORE, fragamt); + GameRules_scoring_add(this.enemy, DOM_TICKS, fragamt); + } + else + this.enemy = NULL; + } +} + +void dompointtouch(entity this, entity toucher) +{ + if(!IS_PLAYER(toucher)) + return; + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) + return; + + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) + return; + + if(time < this.captime + 0.3) + return; + + // only valid teams can claim it + entity head = find(NULL, classname, "dom_team"); + while (head && head.team != toucher.team) + head = find(head, classname, "dom_team"); + if (!head || head.netname == "" || head == this.goalentity) + return; + + // delay capture + + this.team = this.goalentity.team; // this stores the PREVIOUS team! + + this.cnt = toucher.team; + this.owner = head; // team to switch to after the delay + this.dmg_inflictor = toucher; + + // this.state = 1; + // this.delay = time + cvar("g_domination_point_capturetime"); + //this.nextthink = time + cvar("g_domination_point_capturetime"); + //this.think = dompoint_captured; + + // go to neutral team in the mean time + head = find(NULL, classname, "dom_team"); + while (head && head.netname != "") + head = find(head, classname, "dom_team"); + if(head == NULL) + return; + + WaypointSprite_UpdateSprites(this.sprite, WP_DomNeut, WP_Null, WP_Null); + WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DOMPOINT, '0 1 1'); + WaypointSprite_Ping(this.sprite); + + this.goalentity = head; + this.model = head.mdl; + this.modelindex = head.dmg; + this.skin = head.skin; + + this.enemy = toucher; // individual player scoring + this.enemy_playerid = toucher.playerid; + dompoint_captured(this); +} + +void dom_controlpoint_setup(entity this) +{ + entity head; + // find the spawnfunc_dom_team representing unclaimed points + head = find(NULL, classname, "dom_team"); + while(head && head.netname != "") + head = find(head, classname, "dom_team"); + if (!head) + objerror(this, "no spawnfunc_dom_team with netname \"\" found\n"); + + // copy important properties from spawnfunc_dom_team entity + this.goalentity = head; + _setmodel(this, head.mdl); // precision already set + this.skin = head.skin; + + this.cnt = -1; + + if(this.message == "") + this.message = " has captured a control point"; + + if(this.frags <= 0) + this.frags = 1; + if(this.wait <= 0) + this.wait = 5; + + float points, waittime; + if (autocvar_g_domination_point_amt) + points = autocvar_g_domination_point_amt; + else + points = this.frags; + if (autocvar_g_domination_point_rate) + waittime = autocvar_g_domination_point_rate; + else + waittime = this.wait; + + total_pps += points/waittime; + + if(!this.t_width) + this.t_width = 0.02; // frame animation rate + if(!this.t_length) + this.t_length = 239; // maximum frame + + setthink(this, dompointthink); + this.nextthink = time; + settouch(this, dompointtouch); + this.solid = SOLID_TRIGGER; + if(!this.flags & FL_ITEM) + IL_PUSH(g_items, this); + this.flags = FL_ITEM; + setsize(this, '-32 -32 -32', '32 32 32'); + setorigin(this, this.origin + '0 0 20'); + droptofloor(this); + + waypoint_spawnforitem(this); + WaypointSprite_SpawnFixed(WP_DomNeut, this.origin + '0 0 32', this, sprite, RADARICON_DOMPOINT); +} + +int total_control_points; +void Domination_count_controlpoints() +{ + total_control_points = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + Team_SetNumberOfControlPoints(Team_GetTeamFromIndex(i), 0); + } + IL_EACH(g_dompoints, true, + { + ++total_control_points; + if (!Entity_HasValidTeam(it.goalentity)) + { + continue; + } + entity team_ = Entity_GetTeam(it.goalentity); + int num_control_points = Team_GetNumberOfControlPoints(team_); + ++num_control_points; + Team_SetNumberOfControlPoints(team_, num_control_points); + }); +} + +int Domination_GetWinnerTeam() +{ + int winner_team = 0; + if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(1)) == + total_control_points) + { + winner_team = NUM_TEAM_1; + } + for (int i = 2; i <= NUM_TEAMS; ++i) + { + if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(i)) == + total_control_points) + { + if (winner_team != 0) + { + return 0; + } + winner_team = Team_IndexToTeam(i); + } + } + if (winner_team) + { + return winner_team; + } + return -1; // no control points left? +} + +bool Domination_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); + + game_stopped = true; + round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); + return true; + } + + Domination_count_controlpoints(); + + float winner_team = Domination_GetWinnerTeam(); + + if(winner_team == -1) + return false; + + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); + TeamScore_AddToTeam(winner_team, ST_DOM_CAPS, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); + } + + game_stopped = true; + round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); + + return true; +} + +bool Domination_CheckPlayers() +{ + return true; +} + +void Domination_RoundStart() +{ + FOREACH_CLIENT(IS_PLAYER(it), { it.player_blocked = false; }); +} + +//go to best items, or control points you don't own +void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) +{ + IL_EACH(g_dompoints, vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius), + { + if(it.cnt > -1) // this is just being fought + navigation_routerating(this, it, ratingscale, 5000); + else if(it.goalentity.cnt == 0) // unclaimed + navigation_routerating(this, it, ratingscale, 5000); + else if(it.goalentity.team != this.team) // other team's point + navigation_routerating(this, it, ratingscale, 5000); + }); +} + +void havocbot_role_dom(entity this) +{ + if(IS_DEAD(this)) + return; + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + havocbot_goalrating_controlpoints(this, 10000, this.origin, 15000); + havocbot_goalrating_items(this, 20000, this.origin, 8000); + //havocbot_goalrating_enemyplayers(this, 1500, this.origin, 2000); + havocbot_goalrating_waypoints(this, 1, this.origin, 3000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +MUTATOR_HOOKFUNCTION(dom, TeamBalance_CheckAllowedTeams) +{ + // fallback? + M_ARGV(0, float) = domination_teams; + string ret_string = "dom_team"; + + entity head = find(NULL, classname, ret_string); + while(head) + { + if(head.netname != "") + { + if (Team_IsValidTeam(head.team)) + { + M_ARGV(0, float) |= Team_TeamToBit(head.team); + } + } + + head = find(head, classname, ret_string); + } + + M_ARGV(1, string) = string_null; + + return true; +} + +MUTATOR_HOOKFUNCTION(dom, reset_map_players) +{ + total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + PutClientInServer(it); + if(domination_roundbased) + it.player_blocked = 1; + if(IS_REAL_CLIENT(it)) + set_dom_state(it); + }); + return true; +} + +MUTATOR_HOOKFUNCTION(dom, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(domination_roundbased) + if(!round_handler_IsRoundStarted()) + player.player_blocked = 1; + else + player.player_blocked = 0; +} + +MUTATOR_HOOKFUNCTION(dom, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + set_dom_state(player); +} + +MUTATOR_HOOKFUNCTION(dom, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + bot.havocbot_role = havocbot_role_dom; + return true; +} + +/*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32) +Control point for Domination gameplay. +*/ +spawnfunc(dom_controlpoint) +{ + if(!g_domination) + { + delete(this); + return; + } + setthink(this, dom_controlpoint_setup); + this.nextthink = time + 0.1; + this.reset = dom_controlpoint_setup; + + if(!this.scale) + this.scale = 0.6; + + this.effects = this.effects | EF_LOWPRECISION; + if (autocvar_g_domination_point_fullbright) + this.effects |= EF_FULLBRIGHT; + + IL_PUSH(g_dompoints, this); +} + +/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32) +Team declaration for Domination gameplay, this allows you to decide what team +names and control point models are used in your map. + +Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two +can have netname set! The nameless team owns all control points at start. + +Keys: +"netname" + Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc) +"cnt" + Scoreboard color of the team (for example 4 is red and 13 is blue) +"model" + Model to use for control points owned by this team (for example + "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver + keycard) +"skin" + Skin of the model to use (for team skins on a single model) +"noise" + Sound to play when this team captures a point. + (this is a localized sound, like a small alarm or other effect) +"noise1" + Narrator speech to play when this team captures a point. + (this is a global sound, like "Red team has captured a control point") +*/ + +spawnfunc(dom_team) +{ + if(!g_domination || autocvar_g_domination_teams_override >= 2) + { + delete(this); + return; + } + precache_model(this.model); + if (this.noise != "") + precache_sound(this.noise); + if (this.noise1 != "") + precache_sound(this.noise1); + this.classname = "dom_team"; + _setmodel(this, this.model); // precision not needed + this.mdl = this.model; + this.dmg = this.modelindex; + this.model = ""; + this.modelindex = 0; + // this would have to be changed if used in quakeworld + if(this.cnt) + this.team = this.cnt + 1; // WHY are these different anyway? +} + +// scoreboard setup +void ScoreRules_dom(int teams) +{ + if(domination_roundbased) + { + GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { + field_team(ST_DOM_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); + field(SP_DOM_TAKES, "takes", 0); + }); + } + else + { + float sp_domticks, sp_score; + sp_score = sp_domticks = 0; + if(autocvar_g_domination_disable_frags) + sp_domticks = SFL_SORT_PRIO_PRIMARY; + else + sp_score = SFL_SORT_PRIO_PRIMARY; + GameRules_scoring(teams, sp_score, sp_score, { + field_team(ST_DOM_TICKS, "ticks", sp_domticks); + field(SP_DOM_TICKS, "ticks", sp_domticks); + field(SP_DOM_TAKES, "takes", 0); + }); + } +} + +// code from here on is just to support maps that don't have control point and team entities +void dom_spawnteam(string teamname, float teamcolor, string pointmodel, float pointskin, Sound capsound, string capnarration, string capmessage) +{ + TC(Sound, capsound); + entity e = new_pure(dom_team); + e.netname = strzone(teamname); + e.cnt = teamcolor; + e.model = pointmodel; + e.skin = pointskin; + e.noise = strzone(Sound_fixpath(capsound)); + e.noise1 = strzone(capnarration); + e.message = strzone(capmessage); + + // this code is identical to spawnfunc_dom_team + _setmodel(e, e.model); // precision not needed + e.mdl = e.model; + e.dmg = e.modelindex; + e.model = ""; + e.modelindex = 0; + // this would have to be changed if used in quakeworld + e.team = e.cnt + 1; + + //eprint(e); +} + +void dom_spawnpoint(vector org) +{ + entity e = spawn(); + e.classname = "dom_controlpoint"; + setthink(e, spawnfunc_dom_controlpoint); + e.nextthink = time; + setorigin(e, org); + spawnfunc_dom_controlpoint(e); +} + +// spawn some default teams if the map is not set up for domination +void dom_spawnteams(int teams) +{ + TC(int, teams); + dom_spawnteam(Team_ColoredFullName(NUM_TEAM_1), NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, SND_DOM_CLAIM, "", "Red team has captured a control point"); + dom_spawnteam(Team_ColoredFullName(NUM_TEAM_2), NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, SND_DOM_CLAIM, "", "Blue team has captured a control point"); + if(teams >= 3) + dom_spawnteam(Team_ColoredFullName(NUM_TEAM_3), NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, SND_DOM_CLAIM, "", "Yellow team has captured a control point"); + if(teams >= 4) + dom_spawnteam(Team_ColoredFullName(NUM_TEAM_4), NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, SND_DOM_CLAIM, "", "Pink team has captured a control point"); + dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, SND_Null, "", ""); +} + +void dom_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. +{ + // if no teams are found, spawn defaults + if(find(NULL, classname, "dom_team") == NULL || autocvar_g_domination_teams_override >= 2) + { + LOG_TRACE("No \"dom_team\" entities found on this map, creating them anyway."); + domination_teams = autocvar_g_domination_teams_override; + if (domination_teams < 2) + domination_teams = autocvar_g_domination_default_teams; + domination_teams = bound(2, domination_teams, 4); + dom_spawnteams(domination_teams); + } + + entity balance = TeamBalance_CheckAllowedTeams(NULL); + int teams = TeamBalance_GetAllowedTeams(balance); + TeamBalance_Destroy(balance); + domination_teams = teams; + + domination_roundbased = autocvar_g_domination_roundbased; + + ScoreRules_dom(domination_teams); + + if(domination_roundbased) + { + round_handler_Spawn(Domination_CheckPlayers, Domination_CheckWinner, Domination_RoundStart); + round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); + } +} + +void dom_Initialize() +{ + g_domination = true; + InitializeEntity(NULL, dom_DelayedInit, INITPRIO_GAMETYPE); +} diff --git a/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qh b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qh new file mode 100644 index 0000000000..67e00082c3 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/sv_domination.qh @@ -0,0 +1,52 @@ +#pragma once + +#include <common/mutators/base.qh> +#define autocvar_g_domination_point_limit cvar("g_domination_point_limit") +bool autocvar_g_domination_roundbased; +int autocvar_g_domination_roundbased_point_limit; +int autocvar_g_domination_point_leadlimit; + +void dom_Initialize(); + +REGISTER_MUTATOR(dom, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + int fraglimit_override = autocvar_g_domination_point_limit; + if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) + fraglimit_override = autocvar_g_domination_roundbased_point_limit; + + GameRules_teams(true); + GameRules_limit_score(fraglimit_override); + GameRules_limit_lead(autocvar_g_domination_point_leadlimit); + + dom_Initialize(); + } + return 0; +} + +// score rule declarations +const float ST_DOM_TICKS = 1; +const float ST_DOM_CAPS = 1; + +// pps: points per second +float total_pps; +float pps_red; +float pps_blue; +float pps_yellow; +float pps_pink; + +// capture declarations +.float enemy_playerid; +.entity sprite; +.float captime; + +// misc globals +float domination_roundbased; +float domination_teams; + +void AnimateDomPoint(entity this); + +IntrusiveList g_dompoints; +STATIC_INIT(g_dompoints) { g_dompoints = IL_NEW(); } diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.inc b/qcsrc/common/gamemodes/gamemode/duel/_mod.inc new file mode 100644 index 0000000000..5925816750 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/duel/sv_duel.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/duel/_mod.qh b/qcsrc/common/gamemodes/gamemode/duel/_mod.qh new file mode 100644 index 0000000000..00e553c202 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/duel/sv_duel.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc new file mode 100644 index 0000000000..fc662e2a9f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qc @@ -0,0 +1,22 @@ +#include "sv_duel.qh" + +MUTATOR_HOOKFUNCTION(duel, GetPlayerLimit) +{ + M_ARGV(0, int) = 2; // duel is always 1v1! +} + +MUTATOR_HOOKFUNCTION(duel, Scores_CountFragsRemaining) +{ + // announce remaining frags? + return true; +} + +MUTATOR_HOOKFUNCTION(duel, FilterItemDefinition) +{ + entity definition = M_ARGV(0, entity); + + if(definition.instanceOfPowerup) + { + return !autocvar_g_duel_with_powerups; + } +} diff --git a/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh new file mode 100644 index 0000000000..d255c9b353 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/duel/sv_duel.qh @@ -0,0 +1,10 @@ +#pragma once + +#include <common/mutators/base.qh> +REGISTER_MUTATOR(duel, false) +{ + MUTATOR_STATIC(); + return 0; +} + +bool autocvar_g_duel_with_powerups; diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/_mod.inc b/qcsrc/common/gamemodes/gamemode/freezetag/_mod.inc new file mode 100644 index 0000000000..4d62800356 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/freezetag/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/freezetag/sv_freezetag.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/_mod.qh b/qcsrc/common/gamemodes/gamemode/freezetag/_mod.qh new file mode 100644 index 0000000000..785d7b8136 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/freezetag/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/freezetag/sv_freezetag.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc new file mode 100644 index 0000000000..0b87c03bd1 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qc @@ -0,0 +1,625 @@ +#include "sv_freezetag.qh" + +#include <server/resources.qh> + +float autocvar_g_freezetag_frozen_maxtime; +float autocvar_g_freezetag_revive_clearspeed; +float autocvar_g_freezetag_round_timelimit; +//int autocvar_g_freezetag_teams; +int autocvar_g_freezetag_teams_override; +float autocvar_g_freezetag_warmup; + +void freezetag_count_alive_players() +{ + total_players = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + Team_SetNumberOfAlivePlayers(Team_GetTeamFromIndex(i), 0); + } + FOREACH_CLIENT(IS_PLAYER(it) && Entity_HasValidTeam(it), + { + ++total_players; + if (GetResourceAmount(it, RESOURCE_HEALTH) < 1 || STAT(FROZEN, it) == FROZEN_NORMAL) + { + continue; + } + entity team_ = Entity_GetTeam(it); + int num_alive = Team_GetNumberOfAlivePlayers(team_); + ++num_alive; + Team_SetNumberOfAlivePlayers(team_, num_alive); + }); + FOREACH_CLIENT(IS_REAL_CLIENT(it), + { + STAT(REDALIVE, it) = Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex( + 1)); + STAT(BLUEALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(2)); + STAT(YELLOWALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(3)); + STAT(PINKALIVE, it) = Team_GetNumberOfAlivePlayers( + Team_GetTeamFromIndex(4)); + }); + + eliminatedPlayers.SendFlags |= 1; +} + +#define FREEZETAG_ALIVE_TEAMS_OK() (Team_GetNumberOfAliveTeams() == NumTeams(freezetag_teams)) + +bool freezetag_CheckTeams() +{ + static float prev_missing_teams_mask; + if(FREEZETAG_ALIVE_TEAMS_OK()) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return true; + } + if(total_players == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + return false; + } + int missing_teams_mask = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if ((freezetag_teams & Team_IndexToBit(i)) && + (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) == 0)) + { + missing_teams_mask |= Team_IndexToBit(i); + } + } + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + return false; +} + +int freezetag_getWinnerTeam() +{ + int winner_team = 0; + if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(1)) >= 1) + { + winner_team = NUM_TEAM_1; + } + for (int i = 2; i <= NUM_TEAMS; ++i) + { + if (Team_GetNumberOfAlivePlayers(Team_GetTeamFromIndex(i)) >= 1) + { + if (winner_team != 0) + { + return 0; + } + winner_team = Team_IndexToTeam(i); + } + } + if (winner_team) + { + return winner_team; + } + return -1; // no player left +} + +void nades_Clear(entity); +void nades_GiveBonus(entity player, float score); + +bool freezetag_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); + FOREACH_CLIENT(IS_PLAYER(it), { + it.freezetag_frozen_timeout = 0; + nades_Clear(it); + }); + game_stopped = true; + round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); + return true; + } + + if (Team_GetNumberOfAliveTeams() > 1) + { + return false; + } + + int winner_team = freezetag_getWinnerTeam(); + if(winner_team > 0) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); + TeamScore_AddToTeam(winner_team, ST_SCORE, +1); + } + else if(winner_team == -1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); + } + + FOREACH_CLIENT(IS_PLAYER(it), { + it.freezetag_frozen_timeout = 0; + nades_Clear(it); + }); + + game_stopped = true; + round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); + return true; +} + +entity freezetag_LastPlayerForTeam(entity this) +{ + entity last_pl = NULL; + FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), { + if (STAT(FROZEN, it) != FROZEN_NORMAL && GetResourceAmount(it, RESOURCE_HEALTH) >= 1) + { + if (!last_pl) + last_pl = it; + else + return NULL; + } + }); + return last_pl; +} + +void freezetag_LastPlayerForTeam_Notify(entity this) +{ + if(round_handler_IsActive()) + if(round_handler_IsRoundStarted()) + { + entity pl = freezetag_LastPlayerForTeam(this); + if(pl) + Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); + } +} + +void freezetag_Add_Score(entity targ, entity attacker) +{ + if(attacker == targ) + { + // you froze your own dumb targ + // counted as "suicide" already + GameRules_scoring_add(targ, SCORE, -1); + } + else if(IS_PLAYER(attacker)) + { + // got frozen by an enemy + // counted as "kill" and "death" already + GameRules_scoring_add(targ, SCORE, -1); + GameRules_scoring_add(attacker, SCORE, +1); + } + // else nothing - got frozen by the game type rules themselves +} + +// to be called when the player is frozen by freezetag (on death, spectator join etc), gives the score +void freezetag_Freeze(entity targ, entity attacker) +{ + if(STAT(FROZEN, targ)) + return; + + if(autocvar_g_freezetag_frozen_maxtime > 0) + targ.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime; + + Freeze(targ, 0, FROZEN_NORMAL, true); + + freezetag_count_alive_players(); + + freezetag_Add_Score(targ, attacker); +} + +bool freezetag_isEliminated(entity e) +{ + if(IS_PLAYER(e) && (STAT(FROZEN, e) == FROZEN_NORMAL || IS_DEAD(e))) + return true; + return false; +} + + +// ================ +// Bot player logic +// ================ + +void(entity this) havocbot_role_ft_freeing; +void(entity this) havocbot_role_ft_offense; + +void havocbot_goalrating_ft_freeplayers(entity this, float ratingscale, vector org, float sradius) +{ + entity best_pl = NULL; + float best_dist2 = FLOAT_MAX; + FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), { + if (STAT(FROZEN, it) == FROZEN_NORMAL) + { + if(vdist(it.origin - org, >, sradius)) + continue; + navigation_routerating(this, it, ratingscale, 2000); + } + else if (best_dist2 + && GetResourceAmount(it, RESOURCE_HEALTH) < GetResourceAmount(this, RESOURCE_HEALTH) + 30 + && vlen2(it.origin - org) < best_dist2) + { + // If teamate is not frozen still seek them out as fight better + // in a group. + best_dist2 = vlen2(it.origin - org); + if (best_dist2 < 700 ** 2) + { + best_pl = NULL; + best_dist2 = 0; // already close to a teammate + } + else + best_pl = it; + } + }); + if (best_pl) + navigation_routerating(this, best_pl, ratingscale / 2, 2000); +} + +void havocbot_role_ft_offense(entity this) +{ + if(IS_DEAD(this)) + return; + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + random() * 10 + 20; + + // Count how many players on team are unfrozen. + int unfrozen = 0; + FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, this) && STAT(FROZEN, it) != FROZEN_NORMAL, { + unfrozen++; + }); + + // If only one left on team or if role has timed out then start trying to free players. + if ((!unfrozen && STAT(FROZEN, this) != FROZEN_NORMAL) || time > this.havocbot_role_timeout) + { + LOG_TRACE("changing role to freeing"); + this.havocbot_role = havocbot_role_ft_freeing; + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + havocbot_goalrating_items(this, 12000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 10000, this.origin, 10000); + havocbot_goalrating_ft_freeplayers(this, 9000, this.origin, 10000); + havocbot_goalrating_waypoints(this, 1, this.origin, 3000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_ft_freeing(entity this) +{ + if(IS_DEAD(this)) + return; + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + random() * 10 + 20; + + if (time > this.havocbot_role_timeout) + { + LOG_TRACE("changing role to offense"); + this.havocbot_role = havocbot_role_ft_offense; + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + havocbot_goalrating_items(this, 10000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 5000, this.origin, 10000); + havocbot_goalrating_ft_freeplayers(this, 20000, this.origin, 10000); + havocbot_goalrating_waypoints(this, 1, this.origin, 3000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + + +// ============== +// Hook Functions +// ============== + +void ft_RemovePlayer(entity this) +{ + if (STAT(FROZEN, this) != FROZEN_NORMAL) + freezetag_LastPlayerForTeam_Notify(this); + Unfreeze(this, false); + + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // neccessary to correctly count alive players + freezetag_count_alive_players(); +} + +MUTATOR_HOOKFUNCTION(ft, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + ft_RemovePlayer(player); + return true; +} + +MUTATOR_HOOKFUNCTION(ft, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + ft_RemovePlayer(player); +} + +MUTATOR_HOOKFUNCTION(ft, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + + if(round_handler_IsActive()) + if(round_handler_CountdownRunning()) + { + if (STAT(FROZEN, frag_target) == FROZEN_NORMAL) + Unfreeze(frag_target, true); + freezetag_count_alive_players(); + return true; // let the player die so that he can respawn whenever he wants + } + + // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe + // you succeed changing team through the menu: you both really die (gibbing) and get frozen + if(ITEM_DAMAGE_NEEDKILL(frag_deathtype) + || frag_deathtype == DEATH_TEAMCHANGE.m_id || frag_deathtype == DEATH_AUTOTEAMCHANGE.m_id) + { + // let the player die, he will be automatically frozen when he respawns + if (STAT(FROZEN, frag_target) != FROZEN_NORMAL) + { + freezetag_Add_Score(frag_target, frag_attacker); + freezetag_count_alive_players(); + freezetag_LastPlayerForTeam_Notify(frag_target); + } + else + Unfreeze(frag_target, false); // remove ice + frag_target.freezetag_frozen_timeout = -2; // freeze on respawn + return true; + } + + if (STAT(FROZEN, frag_target) == FROZEN_NORMAL) + return true; + + freezetag_Freeze(frag_target, frag_attacker); + freezetag_LastPlayerForTeam_Notify(frag_target); + + if(frag_attacker == frag_target || frag_attacker == NULL) + { + if(IS_PLAYER(frag_target)) + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_SELF); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_SELF, frag_target.netname); + } + else + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname); + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ft, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(player.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players + return true; // do nothing, round is starting right now + + if(player.freezetag_frozen_timeout == -2) // player was dead + { + freezetag_Freeze(player, NULL); + return true; + } + + freezetag_count_alive_players(); + + if(round_handler_IsActive()) + if(round_handler_IsRoundStarted()) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE); + freezetag_Freeze(player, NULL); + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ft, reset_map_players) +{ + FOREACH_CLIENT(IS_PLAYER(it), { + CS(it).killcount = 0; + it.freezetag_frozen_timeout = -1; + PutClientInServer(it); + it.freezetag_frozen_timeout = 0; + }); + freezetag_count_alive_players(); + return true; +} + +MUTATOR_HOOKFUNCTION(ft, GiveFragsForKill, CBC_ORDER_FIRST) +{ + M_ARGV(2, float) = 0; // no frags counted in Freeze Tag + return true; +} + +MUTATOR_HOOKFUNCTION(ft, Unfreeze) +{ + entity targ = M_ARGV(0, entity); + targ.freezetag_frozen_time = 0; + targ.freezetag_frozen_timeout = 0; +} + +#ifdef IS_REVIVING + #undef IS_REVIVING +#endif + +// returns true if player is reviving it +#define IS_REVIVING(player, it, revive_extra_size) \ + (it != player && !STAT(FROZEN, it) && !IS_DEAD(it) && SAME_TEAM(it, player) \ + && boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax)) + +MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) +{ + if(game_stopped) + return true; + + if(round_handler_IsActive()) + if(!round_handler_IsRoundStarted()) + return true; + + int n; + entity player = M_ARGV(0, entity); + //if (STAT(FROZEN, player) == FROZEN_NORMAL) + //if(player.freezetag_frozen_timeout > 0 && time < player.freezetag_frozen_timeout) + //player.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (player.freezetag_frozen_timeout - time) / (player.freezetag_frozen_timeout - player.freezetag_frozen_time); + + IntrusiveList reviving_players = NULL; + + if(player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout) + n = -1; + else + { + n = 0; + vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REVIVING(player, it, revive_extra_size), { + if (!reviving_players) + reviving_players = IL_NEW(); + IL_PUSH(reviving_players, it); + ++n; + }); + } + + if (!n) // no teammate nearby + { + if (STAT(FROZEN, player) == FROZEN_NORMAL) + { + STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed, 1); + SetResourceAmountExplicit(player, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health))); + } + else if (!STAT(FROZEN, player)) + STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody + } + else if (STAT(FROZEN, player) == FROZEN_NORMAL) // OK, there is at least one teammate reviving us + { + STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); + SetResourceAmountExplicit(player, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health))); + + if(STAT(REVIVE_PROGRESS, player) >= 1) + { + Unfreeze(player, false); + freezetag_count_alive_players(); + + if(n == -1) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, autocvar_g_freezetag_frozen_maxtime); + return true; + } + + // EVERY team mate nearby gets a point (even if multiple!) + IL_EACH(reviving_players, true, { + GameRules_scoring_add(it, FREEZETAG_REVIVALS, +1); + GameRules_scoring_add(it, SCORE, +1); + nades_GiveBonus(it, autocvar_g_nades_bonus_score_low); + }); + + entity first = IL_FIRST(reviving_players); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, first.netname); + Send_Notification(NOTIF_ONE, first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED, player.netname, first.netname); + } + + IL_EACH(reviving_players, true, { + STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player); + }); + } + + if (reviving_players) + IL_DELETE(reviving_players); + return true; +} + +MUTATOR_HOOKFUNCTION(ft, SetStartItems) +{ + start_items &= ~IT_UNLIMITED_AMMO; + //start_health = warmup_start_health = cvar("g_lms_start_health"); + //start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); + start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); + start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); + start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); + start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); + start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); + start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); +} + +MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + if (!IS_DEAD(bot)) + { + if (random() < 0.5) + bot.havocbot_role = havocbot_role_ft_freeing; + else + bot.havocbot_role = havocbot_role_ft_offense; + } + + // if bots spawn all at once assign them a more appropriated role after a while + if (time < CS(bot).jointime + 1) + bot.havocbot_role_timeout = time + 10 + random() * 10; + + return true; +} + +MUTATOR_HOOKFUNCTION(ft, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(0, float) = freezetag_teams; + return true; +} + +MUTATOR_HOOKFUNCTION(ft, SetWeaponArena) +{ + // most weapons arena + if(M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") + M_ARGV(0, string) = "most"; +} + +MUTATOR_HOOKFUNCTION(ft, FragCenterMessage) +{ + entity frag_attacker = M_ARGV(0, entity); + entity frag_target = M_ARGV(1, entity); + //float frag_deathtype = M_ARGV(2, float); + int kill_count_to_attacker = M_ARGV(3, int); + int kill_count_to_target = M_ARGV(4, int); + + if(STAT(FROZEN, frag_target) == FROZEN_NORMAL) + return; // target was already frozen, so this is just pushing them off the cliff + + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : CS(frag_target).ping)); + Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, + GetResourceAmount(frag_attacker, RESOURCE_HEALTH), GetResourceAmount(frag_attacker, RESOURCE_ARMOR), (IS_BOT_CLIENT(frag_attacker) ? -1 : CS(frag_attacker).ping)); + + return true; +} + +void freezetag_Initialize() +{ + freezetag_teams = autocvar_g_freezetag_teams_override; + if(freezetag_teams < 2) + freezetag_teams = cvar("g_freezetag_teams"); // read the cvar directly as it gets written earlier in the same frame + + freezetag_teams = BITS(bound(2, freezetag_teams, 4)); + GameRules_scoring(freezetag_teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, { + field(SP_FREEZETAG_REVIVALS, "revivals", 0); + }); + + round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null); + round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); + + EliminatedPlayers_Init(freezetag_isEliminated); +} diff --git a/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qh b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qh new file mode 100644 index 0000000000..d637ae46f6 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/freezetag/sv_freezetag.qh @@ -0,0 +1,33 @@ +#pragma once + +#include <common/mutators/base.qh> +int autocvar_g_freezetag_point_limit; +int autocvar_g_freezetag_point_leadlimit; +bool autocvar_g_freezetag_team_spawns; +void freezetag_Initialize(); + +REGISTER_MUTATOR(ft, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_freezetag_team_spawns); + GameRules_limit_score(autocvar_g_freezetag_point_limit); + GameRules_limit_lead(autocvar_g_freezetag_point_leadlimit); + + freezetag_Initialize(); + } + return 0; +} + +.float freezetag_frozen_time; +.float freezetag_frozen_timeout; +const float ICE_MAX_ALPHA = 1; +const float ICE_MIN_ALPHA = 0.1; +float freezetag_teams; + +float autocvar_g_freezetag_revive_extra_size; +float autocvar_g_freezetag_revive_speed; +bool autocvar_g_freezetag_revive_nade; +float autocvar_g_freezetag_revive_nade_health; diff --git a/qcsrc/common/gamemodes/gamemode/invasion/_mod.inc b/qcsrc/common/gamemodes/gamemode/invasion/_mod.inc new file mode 100644 index 0000000000..a197891fe8 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/invasion/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/invasion/sv_invasion.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/invasion/_mod.qh b/qcsrc/common/gamemodes/gamemode/invasion/_mod.qh new file mode 100644 index 0000000000..f90ea9b31e --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/invasion/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/invasion/sv_invasion.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc new file mode 100644 index 0000000000..c9670a150c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qc @@ -0,0 +1,603 @@ +#include "sv_invasion.qh" + +#include <common/monsters/sv_spawn.qh> +#include <common/monsters/sv_spawner.qh> +#include <common/monsters/sv_monsters.qh> + +#include <server/teamplay.qh> + +IntrusiveList g_invasion_roundends; +IntrusiveList g_invasion_waves; +IntrusiveList g_invasion_spawns; +STATIC_INIT(g_invasion) +{ + g_invasion_roundends = IL_NEW(); + g_invasion_waves = IL_NEW(); + g_invasion_spawns = IL_NEW(); +} + +float autocvar_g_invasion_round_timelimit; +float autocvar_g_invasion_spawnpoint_spawn_delay; +float autocvar_g_invasion_warmup; +int autocvar_g_invasion_monster_count; +bool autocvar_g_invasion_zombies_only; +float autocvar_g_invasion_spawn_delay; + +bool victent_present; +.bool inv_endreached; + +bool inv_warning_shown; // spammy + +void target_invasion_roundend_use(entity this, entity actor, entity trigger) +{ + if(!IS_PLAYER(actor)) { return; } + + actor.inv_endreached = true; + + int plnum = 0; + int realplnum = 0; + // let's not count bots + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { + ++realplnum; + if(it.inv_endreached) + ++plnum; + }); + if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players + return; + + this.winning = true; +} + +spawnfunc(target_invasion_roundend) +{ + if(!g_invasion) { delete(this); return; } + + victent_present = true; // a victory entity is present, we don't need to rely on monster count TODO: merge this with the intrusive list (can check empty) + + if(!this.count) { this.count = 0.7; } // require at least 70% of the players to reach the end before triggering victory + + this.use = target_invasion_roundend_use; + + IL_PUSH(g_invasion_roundends, this); +} + +spawnfunc(invasion_wave) +{ + if(!g_invasion) { delete(this); return; } + + IL_PUSH(g_invasion_waves, this); +} + +spawnfunc(invasion_spawnpoint) +{ + if(!g_invasion) { delete(this); return; } + + this.classname = "invasion_spawnpoint"; + IL_PUSH(g_invasion_spawns, this); +} + +void ClearWinners(); + +// Invasion stage mode winning condition: If the attackers triggered a round end (by fulfilling all objectives) +// they win. +int WinningCondition_Invasion() +{ + WinningConditionHelper(NULL); // set worldstatus + + int status = WINNING_NO; + + if(autocvar_g_invasion_type == INV_TYPE_STAGE) + { + SetWinners(inv_endreached, true); + + int found = 0; + IL_EACH(g_invasion_roundends, true, + { + ++found; + if(it.winning) + { + bprint("Invasion: round completed.\n"); + // winners already set (TODO: teamplay support) + + status = WINNING_YES; + break; + } + }); + + if(!found) + status = WINNING_YES; // just end it? TODO: should warn mapper! + } + else if(autocvar_g_invasion_type == INV_TYPE_HUNT) + { + ClearWinners(); + + int found = 0; // NOTE: this ends the round if no monsters are placed + IL_EACH(g_monsters, !(it.spawnflags & MONSTERFLAG_RESPAWNED), + { + ++found; + }); + + if(found <= 0) + { + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + it.winning = true; + }); + status = WINNING_YES; + } + } + + return status; +} + +Monster invasion_PickMonster(int supermonster_count) +{ + RandomSelection_Init(); + + FOREACH(Monsters, it != MON_Null, + { + if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM) || + (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1)) + continue; + if(autocvar_g_invasion_zombies_only && !(it.spawnflags & MONSTER_TYPE_UNDEAD)) + continue; + RandomSelection_AddEnt(it, 1, 1); + }); + + return RandomSelection_chosen_ent; +} + +entity invasion_PickSpawn() +{ + RandomSelection_Init(); + + IL_EACH(g_invasion_spawns, true, + { + RandomSelection_AddEnt(it, 1, ((time < it.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating + it.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay; + }); + + return RandomSelection_chosen_ent; +} + +entity invasion_GetWaveEntity(int wavenum) +{ + IL_EACH(g_invasion_waves, it.cnt == wavenum, + { + return it; // found one + }); + + // if no specific one is found, find the last existing wave ent + entity best = NULL; + IL_EACH(g_invasion_waves, it.cnt <= wavenum, + { + if(!best || it.cnt > best.cnt) + best = it; + }); + + return best; +} + +void invasion_SpawnChosenMonster(Monster mon) +{ + entity monster; + entity spawn_point = invasion_PickSpawn(); + entity wave_ent = invasion_GetWaveEntity(inv_roundcnt); + + string tospawn = ""; + if(wave_ent && wave_ent.spawnmob && wave_ent.spawnmob != "") + { + RandomSelection_Init(); + FOREACH_WORD(wave_ent.spawnmob, true, + { + RandomSelection_AddString(it, 1, 1); + }); + + tospawn = RandomSelection_chosen_string; + } + + if(spawn_point == NULL) + { + if(!inv_warning_shown) + { + inv_warning_shown = true; + LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations"); + } + entity e = spawn(); + setsize(e, mon.m_mins, mon.m_maxs); + + if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) + monster = spawnmonster(e, tospawn, mon.monsterid, NULL, NULL, e.origin, false, false, 2); + else + { + delete(e); + return; + } + } + else // if spawnmob field falls through (unset), fallback to mon (relying on spawnmonster for that behaviour) + monster = spawnmonster(spawn(), ((spawn_point.spawnmob && spawn_point.spawnmob != "") ? spawn_point.spawnmob : tospawn), mon.monsterid, spawn_point, spawn_point, spawn_point.origin, false, false, 2); + + if(!monster) + return; + + monster.spawnshieldtime = time; + + if(spawn_point) + { + if(spawn_point.target_range) + monster.target_range = spawn_point.target_range; + monster.target2 = spawn_point.target2; + } + + if(teamplay) + { + if(spawn_point && spawn_point.team && inv_monsters_perteam[spawn_point.team] > 0) + monster.team = spawn_point.team; + else + { + RandomSelection_Init(); + if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_AddFloat(NUM_TEAM_1, 1, 1); + if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_AddFloat(NUM_TEAM_2, 1, 1); + if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_AddFloat(NUM_TEAM_3, 1, 1); } + if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_AddFloat(NUM_TEAM_4, 1, 1); } + + monster.team = RandomSelection_chosen_float; + } + + monster_setupcolors(monster); + + if(monster.sprite) + { + WaypointSprite_UpdateTeamRadar(monster.sprite, RADARICON_DANGER, ((monster.team) ? Team_ColorRGB(monster.team) : '1 0 0')); + + monster.sprite.team = 0; + monster.sprite.SendFlags |= 1; + } + } + + if(monster.monster_attack) + IL_REMOVE(g_monster_targets, monster); + monster.monster_attack = false; // it's the player's job to kill all the monsters + + if(inv_roundcnt >= inv_maxrounds) + monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses +} + +void invasion_SpawnMonsters(int supermonster_count) +{ + Monster chosen_monster = invasion_PickMonster(supermonster_count); + + invasion_SpawnChosenMonster(chosen_monster); +} + +bool Invasion_CheckWinner() +{ + if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) + { + IL_EACH(g_monsters, true, + { + Monster_Remove(it); + }); + IL_CLEAR(g_monsters); + + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); + round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); + return 1; + } + + float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0; + + IL_EACH(g_monsters, GetResourceAmount(it, RESOURCE_HEALTH) > 0, + { + if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) + ++supermonster_count; + ++total_alive_monsters; + + if(teamplay) + switch(it.team) + { + case NUM_TEAM_1: ++red_alive; break; + case NUM_TEAM_2: ++blue_alive; break; + case NUM_TEAM_3: ++yellow_alive; break; + case NUM_TEAM_4: ++pink_alive; break; + } + }); + + if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned) + { + if(time >= inv_lastcheck) + { + invasion_SpawnMonsters(supermonster_count); + inv_lastcheck = time + autocvar_g_invasion_spawn_delay; + } + + return 0; + } + + if(inv_numspawned < 1) + return 0; // nothing has spawned yet + + if(teamplay) + { + if(((red_alive > 0) + (blue_alive > 0) + (yellow_alive > 0) + (pink_alive > 0)) > 1) + return 0; + } + else if(inv_numkilled < inv_maxspawned) + return 0; + + entity winner = NULL; + float winning_score = 0, winner_team = 0; + + + if(teamplay) + { + if(red_alive > 0) { winner_team = NUM_TEAM_1; } + if(blue_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_2; } + if(yellow_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_3; } + if(pink_alive > 0) + if(winner_team) { winner_team = 0; } + else { winner_team = NUM_TEAM_4; } + } + else + { + FOREACH_CLIENT(IS_PLAYER(it), { + float cs = GameRules_scoring_add(it, KILLS, 0); + if(cs > winning_score) + { + winning_score = cs; + winner = it; + } + }); + } + + IL_EACH(g_monsters, true, + { + Monster_Remove(it); + }); + IL_CLEAR(g_monsters); + + if(teamplay) + { + if(winner_team) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); + } + } + else if(winner) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, winner.netname); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_PLAYER_WIN, winner.netname); + } + + round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); + + return 1; +} + +bool Invasion_CheckPlayers() +{ + return true; +} + +void Invasion_RoundStart() +{ + int numplayers = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + it.player_blocked = false; + ++numplayers; + }); + + if(inv_roundcnt < inv_maxrounds) + inv_roundcnt += 1; // a limiter to stop crazy counts + + inv_monsterskill = inv_roundcnt + max(1, numplayers * 0.3); + + inv_maxcurrent = 0; + inv_numspawned = 0; + inv_numkilled = 0; + + inv_maxspawned = rint(max(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5))); + + if(teamplay) + { + DistributeEvenly_Init(inv_maxspawned, invasion_teams); + inv_monsters_perteam[NUM_TEAM_1] = DistributeEvenly_Get(1); + inv_monsters_perteam[NUM_TEAM_2] = DistributeEvenly_Get(1); + if(invasion_teams >= 3) inv_monsters_perteam[NUM_TEAM_3] = DistributeEvenly_Get(1); + if(invasion_teams >= 4) inv_monsters_perteam[NUM_TEAM_4] = DistributeEvenly_Get(1); + } +} + +MUTATOR_HOOKFUNCTION(inv, MonsterDies) +{ + entity frag_target = M_ARGV(0, entity); + entity frag_attacker = M_ARGV(1, entity); + + if(!(frag_target.spawnflags & MONSTERFLAG_RESPAWNED)) + { + if(autocvar_g_invasion_type == INV_TYPE_ROUND) + { + inv_numkilled += 1; + inv_maxcurrent -= 1; + } + if(teamplay) { inv_monsters_perteam[frag_target.team] -= 1; } + + if(IS_PLAYER(frag_attacker)) + if(SAME_TEAM(frag_attacker, frag_target)) // in non-teamplay modes, same team = same player, so this works + GameRules_scoring_add(frag_attacker, KILLS, -1); + else + { + GameRules_scoring_add(frag_attacker, KILLS, +1); + if(teamplay) + TeamScore_AddToTeam(frag_attacker.team, ST_INV_KILLS, +1); + } + } +} + +MUTATOR_HOOKFUNCTION(inv, MonsterSpawn) +{ + entity mon = M_ARGV(0, entity); + mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP; + + if(autocvar_g_invasion_type == INV_TYPE_HUNT) + return false; // allowed + + if(!(mon.spawnflags & MONSTERFLAG_SPAWNED)) + return true; + + if(!(mon.spawnflags & MONSTERFLAG_RESPAWNED)) + { + inv_numspawned += 1; + inv_maxcurrent += 1; + } + + mon.monster_skill = inv_monsterskill; + + if((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, mon.monster_name); +} + +MUTATOR_HOOKFUNCTION(inv, SV_StartFrame) +{ + if(autocvar_g_invasion_type != INV_TYPE_ROUND) + return; // uses map spawned monsters + + monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned + monsters_killed = inv_numkilled; +} + +MUTATOR_HOOKFUNCTION(inv, PlayerRegen) +{ + // no regeneration in invasion, regardless of the game type + return true; +} + +MUTATOR_HOOKFUNCTION(inv, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(player.bot_attack) + IL_REMOVE(g_bot_targets, player); + player.bot_attack = false; +} + +MUTATOR_HOOKFUNCTION(inv, Damage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_damage = M_ARGV(4, float); + vector frag_force = M_ARGV(6, vector); + + if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker != frag_target) + { + frag_damage = 0; + frag_force = '0 0 0'; + + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; + } +} + +MUTATOR_HOOKFUNCTION(inv, BotShouldAttack) +{ + entity targ = M_ARGV(1, entity); + + if(!IS_MONSTER(targ)) + return true; +} + +MUTATOR_HOOKFUNCTION(inv, SetStartItems) +{ + if(autocvar_g_invasion_type == INV_TYPE_ROUND) + { + start_health = 200; + start_armorvalue = 200; + } +} + +MUTATOR_HOOKFUNCTION(inv, AccuracyTargetValid) +{ + entity frag_target = M_ARGV(1, entity); + + if(IS_MONSTER(frag_target)) + return MUT_ACCADD_INVALID; + return MUT_ACCADD_INDIFFERENT; +} + +MUTATOR_HOOKFUNCTION(inv, AllowMobSpawning) +{ + // monster spawning disabled during an invasion + M_ARGV(1, string) = "You cannot spawn monsters during an invasion!"; + return true; +} + +MUTATOR_HOOKFUNCTION(inv, CheckRules_World) +{ + if(autocvar_g_invasion_type == INV_TYPE_ROUND) + return false; + + M_ARGV(0, float) = WinningCondition_Invasion(); + return true; +} + +MUTATOR_HOOKFUNCTION(inv, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(0, float) = invasion_teams; + return true; +} + +MUTATOR_HOOKFUNCTION(inv, AllowMobButcher) +{ + M_ARGV(0, string) = "This command does not work during an invasion!"; + return true; +} + +void invasion_ScoreRules(int inv_teams) +{ + GameRules_score_enabled(false); + GameRules_scoring(inv_teams, 0, 0, { + if (inv_teams) { + field_team(ST_INV_KILLS, "frags", SFL_SORT_PRIO_PRIMARY); + } + field(SP_KILLS, "frags", ((inv_teams) ? SFL_SORT_PRIO_SECONDARY : SFL_SORT_PRIO_PRIMARY)); + }); +} + +void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. +{ + if(autocvar_g_invasion_type == INV_TYPE_HUNT || autocvar_g_invasion_type == INV_TYPE_STAGE) + cvar_set("fraglimit", "0"); + + if(autocvar_g_invasion_teams) + { + invasion_teams = BITS(bound(2, autocvar_g_invasion_teams, 4)); + } + else + invasion_teams = 0; + + independent_players = 1; // to disable extra useless scores + + invasion_ScoreRules(invasion_teams); + + independent_players = 0; + + if(autocvar_g_invasion_type == INV_TYPE_ROUND) + { + round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart); + round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); + + inv_roundcnt = 0; + inv_maxrounds = 15; // 15? + } +} + +void invasion_Initialize() +{ + InitializeEntity(NULL, invasion_DelayedInit, INITPRIO_GAMETYPE); +} diff --git a/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qh b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qh new file mode 100644 index 0000000000..167380eeb2 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/invasion/sv_invasion.qh @@ -0,0 +1,46 @@ +#pragma once + +#include <common/mutators/base.qh> +#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") +int autocvar_g_invasion_teams; +int autocvar_g_invasion_type; +bool autocvar_g_invasion_team_spawns; +bool g_invasion; +void invasion_Initialize(); + +REGISTER_MUTATOR(inv, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + if (autocvar_g_invasion_teams >= 2) { + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_invasion_team_spawns); + } + GameRules_limit_score(autocvar_g_invasion_point_limit); + + g_invasion = true; + cvar_settemp("g_monsters", "1"); + invasion_Initialize(); + } + return 0; +} + +float inv_numspawned; +float inv_maxspawned; +float inv_roundcnt; +float inv_maxrounds; +float inv_numkilled; +float inv_lastcheck; +float inv_maxcurrent; + +float invasion_teams; +float inv_monsters_perteam[17]; + +float inv_monsterskill; + +const float ST_INV_KILLS = 1; + +const int INV_TYPE_ROUND = 0; // round-based waves of enemies +const int INV_TYPE_HUNT = 1; // clear the map of placed enemies +const int INV_TYPE_STAGE = 2; // reach the end of the level diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc new file mode 100644 index 0000000000..420f7af78a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/keepaway/sv_keepaway.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh new file mode 100644 index 0000000000..145ca49f0a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/keepaway/sv_keepaway.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc new file mode 100644 index 0000000000..0b8144deb7 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qc @@ -0,0 +1,479 @@ +#include "sv_keepaway.qh" + +#include <common/effects/all.qh> + +.entity ballcarried; + +int autocvar_g_keepaway_ballcarrier_effects; +float autocvar_g_keepaway_ballcarrier_damage; +float autocvar_g_keepaway_ballcarrier_force; +float autocvar_g_keepaway_ballcarrier_highspeed; +float autocvar_g_keepaway_ballcarrier_selfdamage; +float autocvar_g_keepaway_ballcarrier_selfforce; +float autocvar_g_keepaway_noncarrier_damage; +float autocvar_g_keepaway_noncarrier_force; +float autocvar_g_keepaway_noncarrier_selfdamage; +float autocvar_g_keepaway_noncarrier_selfforce; +bool autocvar_g_keepaway_noncarrier_warn; +int autocvar_g_keepaway_score_bckill; +int autocvar_g_keepaway_score_killac; +int autocvar_g_keepaway_score_timepoints; +float autocvar_g_keepaway_score_timeinterval; +float autocvar_g_keepawayball_damageforcescale; +int autocvar_g_keepawayball_effects; +float autocvar_g_keepawayball_respawntime; +int autocvar_g_keepawayball_trail_color; + +bool ka_ballcarrier_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs on waypoints which are attached to ballcarriers, updates once per frame +{ + if(view.ballcarried) + if(IS_SPEC(player)) + return false; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen + + // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup + + return true; +} + +void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":ka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +void ka_TouchEvent(entity this, entity toucher); +void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated +{ + if(game_stopped) return; + vector oldballorigin = this.origin; + + if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) + { + entity spot = SelectSpawnPoint(this, true); + setorigin(this, spot.origin); + this.angles = spot.angles; + } + + makevectors(this.angles); + set_movetype(this, MOVETYPE_BOUNCE); + this.velocity = '0 0 200'; + this.angles = '0 0 0'; + this.effects = autocvar_g_keepawayball_effects; + settouch(this, ka_TouchEvent); + setthink(this, ka_RespawnBall); + this.nextthink = time + autocvar_g_keepawayball_respawntime; + navigation_dynamicgoal_set(this, NULL); + + Send_Effect(EFFECT_ELECTRO_COMBO, oldballorigin, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, this.origin, '0 0 0', 1); + + WaypointSprite_Spawn(WP_KaBall, 0, 0, this, '0 0 64', NULL, this.team, this, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); + WaypointSprite_Ping(this.waypointsprite_attachedforcarrier); + + sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) +} + +void ka_TimeScoring(entity this) +{ + if(this.owner.ballcarried) + { // add points for holding the ball after a certain amount of time + if(autocvar_g_keepaway_score_timepoints) + GameRules_scoring_add(this.owner, SCORE, autocvar_g_keepaway_score_timepoints); + + GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" + this.nextthink = time + autocvar_g_keepaway_score_timeinterval; + } +} + +void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something +{ + if (!this || game_stopped) + return; + + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { // The ball fell off the map, respawn it since players can't get to it + ka_RespawnBall(this); + return; + } + if(IS_DEAD(toucher)) { return; } + if(STAT(FROZEN, toucher)) { return; } + if (!IS_PLAYER(toucher)) + { // The ball just touched an object, most likely the world + Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1); + sound(this, CH_TRIGGER, SND_KA_TOUCH, VOL_BASE, ATTEN_NORM); + return; + } + else if(this.wait > time) { return; } + + // attach the ball to the player + this.owner = toucher; + toucher.ballcarried = this; + GameRules_scoring_vip(toucher, true); + setattachment(this, toucher, ""); + setorigin(this, '0 0 0'); + + // make the ball invisible/unable to do anything/set up time scoring + this.velocity = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.effects |= EF_NODRAW; + settouch(this, func_null); + setthink(this, ka_TimeScoring); + this.nextthink = time + autocvar_g_keepaway_score_timeinterval; + this.takedamage = DAMAGE_NO; + navigation_dynamicgoal_unset(this); + + // apply effects to player + toucher.glow_color = autocvar_g_keepawayball_trail_color; + toucher.glow_trail = true; + toucher.effects |= autocvar_g_keepaway_ballcarrier_effects; + + // messages and sounds + ka_EventLog("pickup", toucher); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname); + Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, toucher.netname); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF); + sound(this.owner, CH_TRIGGER, SND_KA_PICKEDUP, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) + + // scoring + GameRules_scoring_add(toucher, KEEPAWAY_PICKUPS, 1); + + // waypoints + WaypointSprite_AttachCarrier(WP_KaBallCarrier, toucher, RADARICON_FLAGCARRIER); + toucher.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player; + WaypointSprite_UpdateRule(toucher.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); + WaypointSprite_Ping(toucher.waypointsprite_attachedforcarrier); + WaypointSprite_Kill(this.waypointsprite_attachedforcarrier); +} + +void ka_PlayerReset(entity plyr) +{ + plyr.ballcarried = NULL; + GameRules_scoring_vip(plyr, false); + WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier); + + // reset the player effects + plyr.glow_trail = false; + plyr.effects &= ~autocvar_g_keepaway_ballcarrier_effects; +} + +void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball +{ + entity ball; + ball = plyr.ballcarried; + + if(!ball) { return; } + + // reset the ball + setattachment(ball, NULL, ""); + set_movetype(ball, MOVETYPE_BOUNCE); + ball.wait = time + 1; + settouch(ball, ka_TouchEvent); + setthink(ball, ka_RespawnBall); + ball.nextthink = time + autocvar_g_keepawayball_respawntime; + ball.takedamage = DAMAGE_YES; + ball.effects &= ~EF_NODRAW; + setorigin(ball, plyr.origin + '0 0 10'); + ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom(); + ball.owner = NULL; + navigation_dynamicgoal_set(ball, plyr); + + // messages and sounds + ka_EventLog("dropped", plyr); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_DROPPED, plyr.netname); + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEEPAWAY_DROPPED, plyr.netname); + sound(NULL, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) + + // waypoints + WaypointSprite_Spawn(WP_KaBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); + WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); + WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); + + ka_PlayerReset(plyr); +} + +.bool pushable; + +MODEL(KA_BALL, "models/orbs/orbblue.md3"); + +void ka_RemoveBall() +{ + entity plyr = ka_ball.owner; + if (plyr) // it was attached + ka_PlayerReset(plyr); + else + WaypointSprite_DetachCarrier(ka_ball); + delete(ka_ball); + ka_ball = NULL; +} + +void ka_SpawnBall() +{ + entity e = new(keepawayball); + setmodel(e, MDL_KA_BALL); + setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off + e.damageforcescale = autocvar_g_keepawayball_damageforcescale; + e.takedamage = DAMAGE_YES; + e.solid = SOLID_TRIGGER; + set_movetype(e, MOVETYPE_BOUNCE); + e.glow_color = autocvar_g_keepawayball_trail_color; + e.glow_trail = true; + e.flags = FL_ITEM; + IL_PUSH(g_items, e); + e.pushable = true; + settouch(e, ka_TouchEvent); + e.owner = NULL; + ka_ball = e; + navigation_dynamicgoal_init(ka_ball, false); + + InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. +} + +void ka_Handler_CheckBall(entity this) +{ + if(time < game_starttime) + { + if (ka_ball) + ka_RemoveBall(); + } + else + { + if (!ka_ball) + ka_SpawnBall(); + } + + this.nextthink = time; +} + +void ka_Initialize() // run at the start of a match, initiates game mode +{ + ka_Handler = new(ka_Handler); + setthink(ka_Handler, ka_Handler_CheckBall); + ka_Handler.nextthink = time; +} + + +// ================ +// Bot player logic +// ================ + +void havocbot_goalrating_ball(entity this, float ratingscale, vector org) +{ + entity ball_owner; + ball_owner = ka_ball.owner; + + if (ball_owner == this) + return; + + if (ball_owner) + navigation_routerating(this, ball_owner, ratingscale, 2000); + else + navigation_routerating(this, ka_ball, ratingscale, 2000); +} + +void havocbot_role_ka_carrier(entity this) +{ + if (IS_DEAD(this)) + return; + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + havocbot_goalrating_items(this, 10000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 10000, this.origin, 10000); + havocbot_goalrating_waypoints(this, 1, this.origin, 3000); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } + + if (!this.ballcarried) + { + this.havocbot_role = havocbot_role_ka_collector; + navigation_goalrating_timeout_expire(this, 2); + } +} + +void havocbot_role_ka_collector(entity this) +{ + if (IS_DEAD(this)) + return; + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + havocbot_goalrating_items(this, 10000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 500, this.origin, 10000); + havocbot_goalrating_ball(this, 8000, this.origin); + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } + + if (this.ballcarried) + { + this.havocbot_role = havocbot_role_ka_carrier; + navigation_goalrating_timeout_expire(this, 2); + } +} + + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ka, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker))) + { + if(frag_target.ballcarried) { // add to amount of times killing carrier + GameRules_scoring_add(frag_attacker, KEEPAWAY_CARRIERKILLS, 1); + if(autocvar_g_keepaway_score_bckill) // add bckills to the score + GameRules_scoring_add(frag_attacker, SCORE, autocvar_g_keepaway_score_bckill); + } + else if(!frag_attacker.ballcarried) + if(autocvar_g_keepaway_noncarrier_warn) + Send_Notification(NOTIF_ONE_ONLY, frag_attacker, MSG_CENTER, CENTER_KEEPAWAY_WARN); + + if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier + GameRules_scoring_add(frag_attacker, SCORE, autocvar_g_keepaway_score_killac); + } + + if(frag_target.ballcarried) { ka_DropEvent(frag_target); } // a player with the ball has died, drop it +} + +MUTATOR_HOOKFUNCTION(ka, GiveFragsForKill) +{ + M_ARGV(2, float) = 0; // no frags counted in keepaway + return true; // you deceptive little bugger ;3 This needs to be true in order for this function to even count. +} + +MUTATOR_HOOKFUNCTION(ka, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + // clear the item used for the ball in keepaway + player.items &= ~IT_KEY1; + + // if the player has the ball, make sure they have the item for it (Used for HUD primarily) + if(player.ballcarried) + player.items |= IT_KEY1; +} + +MUTATOR_HOOKFUNCTION(ka, PlayerUseKey) +{ + entity player = M_ARGV(0, entity); + + if(MUTATOR_RETURNVALUE == 0) + if(player.ballcarried) + { + ka_DropEvent(player); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_damage = M_ARGV(4, float); + vector frag_force = M_ARGV(6, vector); + + if(frag_attacker.ballcarried) // if the attacker is a ballcarrier + { + if(frag_target == frag_attacker) // damage done to yourself + { + frag_damage *= autocvar_g_keepaway_ballcarrier_selfdamage; + frag_force *= autocvar_g_keepaway_ballcarrier_selfforce; + } + else // damage done to noncarriers + { + frag_damage *= autocvar_g_keepaway_ballcarrier_damage; + frag_force *= autocvar_g_keepaway_ballcarrier_force; + } + } + else if (IS_PLAYER(frag_attacker) && !frag_target.ballcarried) // if the target is a noncarrier + { + if(frag_target == frag_attacker) // damage done to yourself + { + frag_damage *= autocvar_g_keepaway_noncarrier_selfdamage; + frag_force *= autocvar_g_keepaway_noncarrier_selfforce; + } + else // damage done to other noncarriers + { + frag_damage *= autocvar_g_keepaway_noncarrier_damage; + frag_force *= autocvar_g_keepaway_noncarrier_force; + } + } + + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; +} + +MUTATOR_HOOKFUNCTION(ka, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it +} + +MUTATOR_HOOKFUNCTION(ka, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it +} + +MUTATOR_HOOKFUNCTION(ka, PlayerPowerups) +{ + entity player = M_ARGV(0, entity); + + // In the future this hook is supposed to allow me to do some extra stuff with waypointsprites and invisibility powerup + // So bare with me until I can fix a certain bug with ka_ballcarrier_waypointsprite_visible_for_player() + + player.effects &= ~autocvar_g_keepaway_ballcarrier_effects; + + if(player.ballcarried) + player.effects |= autocvar_g_keepaway_ballcarrier_effects; +} + + +MUTATOR_HOOKFUNCTION(ka, PlayerPhysics_UpdateStats) +{ + entity player = M_ARGV(0, entity); + // these automatically reset, no need to worry + + if(player.ballcarried) + STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_keepaway_ballcarrier_highspeed; +} + +MUTATOR_HOOKFUNCTION(ka, BotShouldAttack) +{ + entity bot = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + + // if neither player has ball then don't attack unless the ball is on the ground + if(!targ.ballcarried && !bot.ballcarried && ka_ball.owner) + return true; +} + +MUTATOR_HOOKFUNCTION(ka, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + if (bot.ballcarried) + bot.havocbot_role = havocbot_role_ka_carrier; + else + bot.havocbot_role = havocbot_role_ka_collector; + return true; +} + +MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) +{ + entity frag_target = M_ARGV(0, entity); + + if(frag_target.ballcarried) + ka_DropEvent(frag_target); +} diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh new file mode 100644 index 0000000000..3c14c89af1 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/sv_keepaway.qh @@ -0,0 +1,30 @@ +#pragma once + +#include <common/mutators/base.qh> +#include <common/scores.qh> +void ka_Initialize(); + +REGISTER_MUTATOR(ka, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, { + field(SP_KEEPAWAY_PICKUPS, "pickups", 0); + field(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); + field(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); + }); + + ka_Initialize(); + } + return false; +} + + +entity ka_ball; +entity ka_Handler; + +void(entity this) havocbot_role_ka_carrier; +void(entity this) havocbot_role_ka_collector; + +void ka_DropEvent(entity plyr); diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc new file mode 100644 index 0000000000..4f44840c41 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh new file mode 100644 index 0000000000..e4143fc84c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc new file mode 100644 index 0000000000..f8456734ca --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc @@ -0,0 +1,1304 @@ +#include "sv_keyhunt.qh" + +float autocvar_g_balance_keyhunt_damageforcescale; +float autocvar_g_balance_keyhunt_delay_collect; +float autocvar_g_balance_keyhunt_delay_damage_return; +float autocvar_g_balance_keyhunt_delay_return; +float autocvar_g_balance_keyhunt_delay_round; +float autocvar_g_balance_keyhunt_delay_tracking; +float autocvar_g_balance_keyhunt_return_when_unreachable; +float autocvar_g_balance_keyhunt_dropvelocity; +float autocvar_g_balance_keyhunt_maxdist; +float autocvar_g_balance_keyhunt_protecttime; + +int autocvar_g_balance_keyhunt_score_capture; +int autocvar_g_balance_keyhunt_score_carrierfrag; +int autocvar_g_balance_keyhunt_score_collect; +int autocvar_g_balance_keyhunt_score_destroyed; +int autocvar_g_balance_keyhunt_score_destroyed_ownfactor; +int autocvar_g_balance_keyhunt_score_push; +float autocvar_g_balance_keyhunt_throwvelocity; + +//int autocvar_g_keyhunt_teams; +int autocvar_g_keyhunt_teams_override; + +// #define KH_PLAYER_USE_ATTACHMENT +// #define KH_PLAYER_USE_CARRIEDMODEL + +#ifdef KH_PLAYER_USE_ATTACHMENT +const vector KH_PLAYER_ATTACHMENT_DIST_ROTATED = '0 -4 0'; +const vector KH_PLAYER_ATTACHMENT_DIST = '4 0 0'; +const vector KH_PLAYER_ATTACHMENT = '0 0 0'; +const vector KH_PLAYER_ATTACHMENT_ANGLES = '0 0 0'; +const string KH_PLAYER_ATTACHMENT_BONE = ""; +#else +const float KH_KEY_ZSHIFT = 22; +const float KH_KEY_XYDIST = 24; +const float KH_KEY_XYSPEED = 45; +#endif +const float KH_KEY_WP_ZSHIFT = 20; + +const vector KH_KEY_MIN = '-10 -10 -46'; +const vector KH_KEY_MAX = '10 10 3'; +const float KH_KEY_BRIGHTNESS = 2; + +bool kh_no_radar_circles; + +// kh_state +// bits 0- 4: team of key 1, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self +// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self +.float siren_time; // time delay the siren +//.float stuff_time; // time delay to stuffcmd a cvar + +int kh_Team_ByID(int t) +{ + if(t == 0) return NUM_TEAM_1; + if(t == 1) return NUM_TEAM_2; + if(t == 2) return NUM_TEAM_3; + if(t == 3) return NUM_TEAM_4; + return 0; +} + +//entity kh_worldkeylist; +.entity kh_worldkeynext; +entity kh_controller; +//bool kh_tracking_enabled; +int kh_teams; +int kh_interferemsg_team; +float kh_interferemsg_time; +.entity kh_next, kh_prev; // linked list +.float kh_droptime; +.int kh_dropperteam; +.entity kh_previous_owner; +.int kh_previous_owner_playerid; + +int kh_key_dropped, kh_key_carried; + +int kh_Key_AllOwnedByWhichTeam(); + +const int ST_KH_CAPS = 1; +void kh_ScoreRules(int teams) +{ + GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, { + field_team(ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + field(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + field(SP_KH_PUSHES, "pushes", 0); + field(SP_KH_DESTROYS, "destroyed", SFL_LOWER_IS_BETTER); + field(SP_KH_PICKUPS, "pickups", 0); + field(SP_KH_KCKILLS, "kckills", 0); + field(SP_KH_LOSSES, "losses", SFL_LOWER_IS_BETTER); + }); +} + +bool kh_KeyCarrier_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs all the time +{ + if(!IS_PLAYER(view) || DIFF_TEAM(this, view)) + if(!kh_tracking_enabled) + return false; + + return true; +} + +bool kh_Key_waypointsprite_visible_for_player(entity this, entity player, entity view) +{ + if(!kh_tracking_enabled) + return false; + if(!this.owner) + return true; + if(!this.owner.owner) + return true; + return false; // draw only when key is not owned +} + +void kh_update_state() +{ + entity key; + int f; + int s = 0; + FOR_EACH_KH_KEY(key) + { + if(key.owner) + f = key.team; + else + f = 30; + s |= (32 ** key.count) * f; + } + + FOREACH_CLIENT(true, { STAT(KH_KEYS, it) = s; }); + + FOR_EACH_KH_KEY(key) + { + if(key.owner) + STAT(KH_KEYS, key.owner) |= (32 ** key.count) * 31; + } + //print(ftos((nextent(NULL)).kh_state), "\n"); +} + + + + +var kh_Think_t kh_Controller_Thinkfunc; +void kh_Controller_SetThink(float t, kh_Think_t func) // runs occasionaly +{ + kh_Controller_Thinkfunc = func; + kh_controller.cnt = ceil(t); + if(t == 0) + kh_controller.nextthink = time; // force +} +void kh_WaitForPlayers(); +void kh_Controller_Think(entity this) // called a lot +{ + if(game_stopped) + return; + if(this.cnt > 0) + { + if(getthink(this) != kh_WaitForPlayers) + this.cnt -= 1; + } + else if(this.cnt == 0) + { + this.cnt -= 1; + kh_Controller_Thinkfunc(); + } + this.nextthink = time + 1; +} + +// frags f: take from cvar * f +// frags 0: no frags +void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner) // update the score when a key is captured +{ + string s; + if(game_stopped) + return; + + if(frags_player) + UpdateFrags(player, frags_player); + + if(key && key.owner && frags_owner) + UpdateFrags(key.owner, frags_owner); + + if(!autocvar_sv_eventlog) //output extra info to the console or text file + return; + + s = strcat(":keyhunt:", what, ":", ftos(player.playerid), ":", ftos(frags_player)); + + if(key && key.owner) + s = strcat(s, ":", ftos(key.owner.playerid)); + else + s = strcat(s, ":0"); + + s = strcat(s, ":", ftos(frags_owner), ":"); + + if(key) + s = strcat(s, key.netname); + + GameLogEcho(s); +} + +vector kh_AttachedOrigin(entity e) // runs when a team captures the flag, it can run 2 or 3 times. +{ + if(e.tag_entity) + { + makevectors(e.tag_entity.angles); + return e.tag_entity.origin + e.origin.x * v_forward - e.origin.y * v_right + e.origin.z * v_up; + } + else + return e.origin; +} + +void kh_Key_Attach(entity key) // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round +{ +#ifdef KH_PLAYER_USE_ATTACHMENT + entity first = key.owner.kh_next; + if(key == first) + { + setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE); + if(key.kh_next) + { + setattachment(key.kh_next, key, ""); + setorigin(key, key.kh_next.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); + setorigin(key.kh_next, KH_PLAYER_ATTACHMENT_DIST_ROTATED); + key.kh_next.angles = '0 0 0'; + } + else + setorigin(key, KH_PLAYER_ATTACHMENT); + key.angles = KH_PLAYER_ATTACHMENT_ANGLES; + } + else + { + setattachment(key, key.kh_prev, ""); + if(key.kh_next) + setattachment(key.kh_next, key, ""); + setorigin(key, KH_PLAYER_ATTACHMENT_DIST_ROTATED); + setorigin(first, first.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); + key.angles = '0 0 0'; + } +#else + setattachment(key, key.owner, ""); + setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think + key.angles_y -= key.owner.angles.y; +#endif + key.flags = 0; + if(IL_CONTAINS(g_items, key)) + IL_REMOVE(g_items, key); + key.solid = SOLID_NOT; + set_movetype(key, MOVETYPE_NONE); + key.team = key.owner.team; + key.nextthink = time; + key.damageforcescale = 0; + key.takedamage = DAMAGE_NO; + key.modelindex = kh_key_carried; + navigation_dynamicgoal_unset(key); +} + +void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured +{ +#ifdef KH_PLAYER_USE_ATTACHMENT + entity first = key.owner.kh_next; + if(key == first) + { + if(key.kh_next) + { + setattachment(key.kh_next, key.owner, KH_PLAYER_ATTACHMENT_BONE); + setorigin(key.kh_next, key.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); + key.kh_next.angles = KH_PLAYER_ATTACHMENT_ANGLES; + } + } + else + { + if(key.kh_next) + setattachment(key.kh_next, key.kh_prev, ""); + setorigin(first, first.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); + } + // in any case: + setattachment(key, NULL, ""); + setorigin(key, key.owner.origin + '0 0 1' * (STAT(PL_MIN, key.owner).z - KH_KEY_MIN_z)); + key.angles = key.owner.angles; +#else + setorigin(key, key.owner.origin + key.origin.z * '0 0 1'); + setattachment(key, NULL, ""); + key.angles_y += key.owner.angles.y; +#endif + key.flags = FL_ITEM; + if(!IL_CONTAINS(g_items, key)) + IL_PUSH(g_items, key); + key.solid = SOLID_TRIGGER; + set_movetype(key, MOVETYPE_TOSS); + key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return; + key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale; + key.takedamage = DAMAGE_YES; + // let key.team stay + key.modelindex = kh_key_dropped; + navigation_dynamicgoal_set(key, key.owner); + key.kh_previous_owner = key.owner; + key.kh_previous_owner_playerid = key.owner.playerid; +} + +void kh_Key_AssignTo(entity key, entity player) // runs every time a key is picked up or assigned. Runs prior to kh_key_attach +{ + if(key.owner == player) + return; + + int ownerteam0 = kh_Key_AllOwnedByWhichTeam(); + + if(key.owner) + { + kh_Key_Detach(key); + + // remove from linked list + if(key.kh_next) + key.kh_next.kh_prev = key.kh_prev; + key.kh_prev.kh_next = key.kh_next; + key.kh_next = NULL; + key.kh_prev = NULL; + + if(key.owner.kh_next == NULL) + { + // No longer a key carrier + if(!kh_no_radar_circles) + WaypointSprite_Ping(key.owner.waypointsprite_attachedforcarrier); + WaypointSprite_DetachCarrier(key.owner); + } + } + + key.owner = player; + + if(player) + { + // insert into linked list + key.kh_next = player.kh_next; + key.kh_prev = player; + player.kh_next = key; + if(key.kh_next) + key.kh_next.kh_prev = key; + + kh_Key_Attach(key); + + if(key.kh_next == NULL) + { + // player is now a key carrier + entity wp = WaypointSprite_AttachCarrier(WP_Null, player, RADARICON_FLAGCARRIER); + wp.colormod = colormapPaletteColor(player.team - 1, 0); + player.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_KeyCarrier_waypointsprite_visible_for_player; + WaypointSprite_UpdateRule(player.waypointsprite_attachedforcarrier, player.team, SPRITERULE_TEAMPLAY); + if(player.team == NUM_TEAM_1) + WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierRed, WP_KeyCarrierFriend, WP_KeyCarrierRed); + else if(player.team == NUM_TEAM_2) + WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierBlue, WP_KeyCarrierFriend, WP_KeyCarrierBlue); + else if(player.team == NUM_TEAM_3) + WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierYellow, WP_KeyCarrierFriend, WP_KeyCarrierYellow); + else if(player.team == NUM_TEAM_4) + WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierPink, WP_KeyCarrierFriend, WP_KeyCarrierPink); + if(!kh_no_radar_circles) + WaypointSprite_Ping(player.waypointsprite_attachedforcarrier); + } + } + + // moved that here, also update if there's no player + kh_update_state(); + + key.pusher = NULL; + + int ownerteam = kh_Key_AllOwnedByWhichTeam(); + if(ownerteam != ownerteam0) + { + entity k; + if(ownerteam != -1) + { + kh_interferemsg_time = time + 0.2; + kh_interferemsg_team = player.team; + + // audit all key carrier sprites, update them to "Run here" + FOR_EACH_KH_KEY(k) + { + if (!k.owner) continue; + entity first = WP_Null; + FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, { first = it; break; }); + entity third = WP_Null; + FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, { third = it; break; }); + WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFinish, third); + } + } + else + { + kh_interferemsg_time = 0; + + // audit all key carrier sprites, update them to "Key Carrier" + FOR_EACH_KH_KEY(k) + { + if (!k.owner) continue; + entity first = WP_Null; + FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, { first = it; break; }); + entity third = WP_Null; + FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, { third = it; break; }); + WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFriend, third); + } + } + } +} + +void kh_Key_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.owner) + return; + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + this.pain_finished = bound(time, time + autocvar_g_balance_keyhunt_delay_damage_return, this.pain_finished); + return; + } + if(force == '0 0 0') + return; + if(time > this.pushltime) + if(IS_PLAYER(attacker)) + this.team = attacker.team; +} + +void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key +{ + sound(player, CH_TRIGGER, SND_KH_COLLECT, VOL_BASE, ATTEN_NORM); + + if(key.kh_dropperteam != player.team) + { + kh_Scores_Event(player, key, "collect", autocvar_g_balance_keyhunt_score_collect, 0); + GameRules_scoring_add(player, KH_PICKUPS, 1); + } + key.kh_dropperteam = 0; + int realteam = kh_Team_ByID(key.count); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_PICKUP), player.netname); + + kh_Key_AssignTo(key, player); // this also updates .kh_state +} + +void kh_Key_Touch(entity this, entity toucher) // runs many, many times when a key has been dropped and can be picked up +{ + if(game_stopped) + return; + + if(this.owner) // already carried + return; + + if(ITEM_TOUCH_NEEDKILL()) + { + this.pain_finished = bound(time, time + autocvar_g_balance_keyhunt_delay_damage_return, this.pain_finished); + return; + } + + if (!IS_PLAYER(toucher)) + return; + if(IS_DEAD(toucher)) + return; + if(toucher == this.enemy) + if(time < this.kh_droptime + autocvar_g_balance_keyhunt_delay_collect) + return; // you just dropped it! + kh_Key_Collect(this, toucher); +} + +void kh_Key_Remove(entity key) // runs after when all the keys have been collected or when a key has been dropped for more than X seconds +{ + entity o = key.owner; + kh_Key_AssignTo(key, NULL); + if(o) // it was attached + WaypointSprite_Kill(key.waypointsprite_attachedforcarrier); + else // it was dropped + WaypointSprite_DetachCarrier(key); + + // remove key from key list + if (kh_worldkeylist == key) + kh_worldkeylist = kh_worldkeylist.kh_worldkeynext; + else + { + o = kh_worldkeylist; + while (o) + { + if (o.kh_worldkeynext == key) + { + o.kh_worldkeynext = o.kh_worldkeynext.kh_worldkeynext; + break; + } + o = o.kh_worldkeynext; + } + } + + delete(key); + + kh_update_state(); +} + +void kh_FinishRound() // runs when a team captures the keys +{ + // prepare next round + kh_interferemsg_time = 0; + entity key; + + kh_no_radar_circles = true; + FOR_EACH_KH_KEY(key) + kh_Key_Remove(key); + kh_no_radar_circles = false; + + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); +} + +void nades_GiveBonus(entity player, float score); + +void kh_WinnerTeam(int winner_team) // runs when a team wins +{ + // all key carriers get some points + entity key; + float score = (NumTeams(kh_teams) - 1) * autocvar_g_balance_keyhunt_score_capture; + DistributeEvenly_Init(score, NumTeams(kh_teams)); + // twice the score for 3 team games, three times the score for 4 team games! + // note: for a win by destroying the key, this should NOT be applied + FOR_EACH_KH_KEY(key) + { + float f = DistributeEvenly_Get(1); + kh_Scores_Event(key.owner, key, "capture", f, 0); + GameRules_scoring_add_team(key.owner, KH_CAPS, 1); + nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high); + } + + bool first = true; + string keyowner = ""; + FOR_EACH_KH_KEY(key) + if(key.owner.kh_next == key) + { + if(!first) + keyowner = strcat(keyowner, ", "); + keyowner = key.owner.netname; + first = false; + } + + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_KEYHUNT_CAPTURE), keyowner); + + first = true; + vector firstorigin = '0 0 0', lastorigin = '0 0 0', midpoint = '0 0 0'; + FOR_EACH_KH_KEY(key) + { + vector thisorigin = kh_AttachedOrigin(key); + //dprint("Key origin: ", vtos(thisorigin), "\n"); + midpoint += thisorigin; + + if(!first) + te_lightning2(NULL, lastorigin, thisorigin); + lastorigin = thisorigin; + if(first) + firstorigin = thisorigin; + first = false; + } + if(NumTeams(kh_teams) > 2) + { + te_lightning2(NULL, lastorigin, firstorigin); + } + midpoint = midpoint * (1 / NumTeams(kh_teams)); + te_customflash(midpoint, 1000, 1, Team_ColorRGB(winner_team) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component + + play2all(SND(KH_CAPTURE)); + kh_FinishRound(); +} + +void kh_LoserTeam(int loser_team, entity lostkey) // runs when a player pushes a flag carrier off the map +{ + float f; + entity attacker = NULL; + if(lostkey.pusher) + if(lostkey.pusher.team != loser_team) + if(IS_PLAYER(lostkey.pusher)) + attacker = lostkey.pusher; + + if(attacker) + { + if(lostkey.kh_previous_owner) + kh_Scores_Event(lostkey.kh_previous_owner, NULL, "pushed", 0, -autocvar_g_balance_keyhunt_score_push); + // don't actually GIVE him the -nn points, just log + kh_Scores_Event(attacker, NULL, "push", autocvar_g_balance_keyhunt_score_push, 0); + GameRules_scoring_add(attacker, KH_PUSHES, 1); + //centerprint(attacker, "Your push is the best!"); // does this really need to exist? + } + else + { + int players = 0; + float of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; + + FOREACH_CLIENT(IS_PLAYER(it) && it.team != loser_team, { ++players; }); + + entity key; + int keys = 0; + FOR_EACH_KH_KEY(key) + if(key.owner && key.team != loser_team) + ++keys; + + if(lostkey.kh_previous_owner) + kh_Scores_Event(lostkey.kh_previous_owner, NULL, "destroyed", 0, -autocvar_g_balance_keyhunt_score_destroyed); + // don't actually GIVE him the -nn points, just log + + if(lostkey.kh_previous_owner.playerid == lostkey.kh_previous_owner_playerid) + GameRules_scoring_add(lostkey.kh_previous_owner, KH_DESTROYS, 1); + + DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players); + + FOR_EACH_KH_KEY(key) + if(key.owner && key.team != loser_team) + { + f = DistributeEvenly_Get(of); + kh_Scores_Event(key.owner, NULL, "destroyed_holdingkey", f, 0); + } + + int fragsleft = DistributeEvenly_Get(players); + + // Now distribute these among all other teams... + int j = NumTeams(kh_teams) - 1; + for(int i = 0; i < NumTeams(kh_teams); ++i) + { + int thisteam = kh_Team_ByID(i); + if(thisteam == loser_team) // bad boy, no cookie - this WILL happen + continue; + + players = 0; + FOREACH_CLIENT(IS_PLAYER(it) && it.team == thisteam, { ++players; }); + + DistributeEvenly_Init(fragsleft, j); + fragsleft = DistributeEvenly_Get(j - 1); + DistributeEvenly_Init(DistributeEvenly_Get(1), players); + + FOREACH_CLIENT(IS_PLAYER(it) && it.team == thisteam, { + f = DistributeEvenly_Get(1); + kh_Scores_Event(it, NULL, "destroyed", f, 0); + }); + + --j; + } + } + + int realteam = kh_Team_ByID(lostkey.count); + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(loser_team, CENTER_ROUND_TEAM_LOSS)); + if(attacker) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_PUSHED), attacker.netname, lostkey.kh_previous_owner.netname); + else + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_DESTROYED), lostkey.kh_previous_owner.netname); + + play2all(SND(KH_DESTROY)); + te_tarexplosion(lostkey.origin); + + kh_FinishRound(); +} + +void kh_Key_Think(entity this) // runs all the time +{ + if(game_stopped) + return; + + if(this.owner) + { +#ifndef KH_PLAYER_USE_ATTACHMENT + makevectors('0 1 0' * (this.cnt + (time % 360) * KH_KEY_XYSPEED)); + setorigin(this, v_forward * KH_KEY_XYDIST + '0 0 1' * this.origin.z); +#endif + } + + // if in nodrop or time over, end the round + if(!this.owner) + if(time > this.pain_finished) + kh_LoserTeam(this.team, this); + + if(this.owner) + if(kh_Key_AllOwnedByWhichTeam() != -1) + { + if(this.siren_time < time) + { + sound(this.owner, CH_TRIGGER, SND_KH_ALARM, VOL_BASE, ATTEN_NORM); // play a simple alarm + this.siren_time = time + 2.5; // repeat every 2.5 seconds + } + + entity key; + vector p = this.owner.origin; + FOR_EACH_KH_KEY(key) + if(vdist(key.owner.origin - p, >, autocvar_g_balance_keyhunt_maxdist)) + goto not_winning; + kh_WinnerTeam(this.team); +LABEL(not_winning) + } + + if(kh_interferemsg_time && time > kh_interferemsg_time) + { + kh_interferemsg_time = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + if(it.team == kh_interferemsg_team) + if(it.kh_next) + Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_KEYHUNT_MEET); + else + Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_KEYHUNT_HELP); + else + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(kh_interferemsg_team, CENTER_KEYHUNT_INTERFERE)); + }); + } + + this.nextthink = time + 0.05; +} + +void key_reset(entity this) +{ + kh_Key_AssignTo(this, NULL); + kh_Key_Remove(this); +} + +const string STR_ITEM_KH_KEY = "item_kh_key"; +void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every time a new flag is created, ie after all the keys have been collected +{ + entity key = spawn(); + key.count = i; + key.classname = STR_ITEM_KH_KEY; + settouch(key, kh_Key_Touch); + setthink(key, kh_Key_Think); + key.nextthink = time; + key.items = IT_KEY1 | IT_KEY2; + key.cnt = _angle; + key.angles = '0 360 0' * random(); + key.event_damage = kh_Key_Damage; + key.takedamage = DAMAGE_YES; + key.damagedbytriggers = autocvar_g_balance_keyhunt_return_when_unreachable; + key.damagedbycontents = autocvar_g_balance_keyhunt_return_when_unreachable; + key.modelindex = kh_key_dropped; + key.model = "key"; + key.kh_dropperteam = 0; + key.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + setsize(key, KH_KEY_MIN, KH_KEY_MAX); + key.colormod = Team_ColorRGB(initial_owner.team) * KH_KEY_BRIGHTNESS; + key.reset = key_reset; + navigation_dynamicgoal_init(key, false); + + switch(initial_owner.team) + { + case NUM_TEAM_1: + key.netname = "^1red key"; + break; + case NUM_TEAM_2: + key.netname = "^4blue key"; + break; + case NUM_TEAM_3: + key.netname = "^3yellow key"; + break; + case NUM_TEAM_4: + key.netname = "^6pink key"; + break; + default: + key.netname = "NETGIER key"; + break; + } + + // link into key list + key.kh_worldkeynext = kh_worldkeylist; + kh_worldkeylist = key; + + Send_Notification(NOTIF_ONE, initial_owner, MSG_CENTER, APP_TEAM_NUM(initial_owner.team, CENTER_KEYHUNT_START)); + + WaypointSprite_Spawn(WP_KeyDropped, 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, NULL, key.team, key, waypointsprite_attachedforcarrier, false, RADARICON_FLAG); + key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player; + + kh_Key_AssignTo(key, initial_owner); +} + +// -1 when no team completely owns all keys yet +int kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team +{ + entity key; + int teem = -1; + int keys = NumTeams(kh_teams); + FOR_EACH_KH_KEY(key) + { + if(!key.owner) + return -1; + if(teem == -1) + teem = key.team; + else if(teem != key.team) + return -1; + --keys; + } + if(keys != 0) + return -1; + return teem; +} + +void kh_Key_DropOne(entity key) +{ + // prevent collecting this one for some time + entity player = key.owner; + + key.kh_droptime = time; + key.enemy = player; + + kh_Scores_Event(player, key, "dropkey", 0, 0); + GameRules_scoring_add(player, KH_LOSSES, 1); + int realteam = kh_Team_ByID(key.count); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_DROP), player.netname); + + kh_Key_AssignTo(key, NULL); + makevectors(player.v_angle); + key.velocity = W_CalculateProjectileVelocity(player, player.velocity, autocvar_g_balance_keyhunt_throwvelocity * v_forward, false); + key.pusher = NULL; + key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; + key.kh_dropperteam = key.team; + + sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM); +} + +void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies +{ + if(player.kh_next) + { + entity mypusher = NULL; + if(player.pusher) + if(time < player.pushltime) + mypusher = player.pusher; + + entity key; + while((key = player.kh_next)) + { + kh_Scores_Event(player, key, "losekey", 0, 0); + GameRules_scoring_add(player, KH_LOSSES, 1); + int realteam = kh_Team_ByID(key.count); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_LOST), player.netname); + kh_Key_AssignTo(key, NULL); + makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); + key.velocity = W_CalculateProjectileVelocity(player, player.velocity, autocvar_g_balance_keyhunt_dropvelocity * v_forward, false); + key.pusher = mypusher; + key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; + if(suicide) + key.kh_dropperteam = player.team; + } + sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM); + } +} + +int kh_GetMissingTeams() +{ + int missing_teams = 0; + for(int i = 0; i < NumTeams(kh_teams); ++i) + { + int teem = kh_Team_ByID(i); + int players = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) + ++players; + }); + if (!players) + missing_teams |= (2 ** i); + } + return missing_teams; +} + +void kh_WaitForPlayers() // delay start of the round until enough players are present +{ + static int prev_missing_teams_mask; + if(time < game_starttime) + { + if (prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); + return; + } + + int missing_teams_mask = kh_GetMissingTeams(); + if(!missing_teams_mask) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); + } + else + { + if(player_count == 0) + { + if(prev_missing_teams_mask > 0) + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); + prev_missing_teams_mask = -1; + } + else + { + if(prev_missing_teams_mask != missing_teams_mask) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); + prev_missing_teams_mask = missing_teams_mask; + } + } + kh_Controller_SetThink(1, kh_WaitForPlayers); + } +} + +void kh_EnableTrackingDevice() // runs after each round +{ + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT); + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER); + + kh_tracking_enabled = true; +} + +void kh_StartRound() // runs at the start of each round +{ + if(time < game_starttime) + { + kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); + return; + } + + if(kh_GetMissingTeams()) + { + kh_Controller_SetThink(1, kh_WaitForPlayers); + return; + } + + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT); + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER); + + for(int i = 0; i < NumTeams(kh_teams); ++i) + { + int teem = kh_Team_ByID(i); + int players = 0; + entity my_player = NULL; + FOREACH_CLIENT(IS_PLAYER(it), { + if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) + { + ++players; + if(random() * players <= 1) + my_player = it; + } + }); + kh_Key_Spawn(my_player, 360 * i / NumTeams(kh_teams), i); + } + + kh_tracking_enabled = false; + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_SCAN, autocvar_g_balance_keyhunt_delay_tracking); + kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_tracking, kh_EnableTrackingDevice); +} + +float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score +{ + if(attacker == targ) + return f; + + if(targ.kh_next) + { + if(attacker.team == targ.team) + { + int nk = 0; + for(entity k = targ.kh_next; k != NULL; k = k.kh_next) + ++nk; + kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0); + } + else + { + kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", autocvar_g_balance_keyhunt_score_carrierfrag-1, 0); + GameRules_scoring_add(attacker, KH_KCKILLS, 1); + // the frag gets added later + } + } + + return f; +} + +void kh_Initialize() // sets up th KH environment +{ + // setup variables + kh_teams = autocvar_g_keyhunt_teams_override; + if(kh_teams < 2) + kh_teams = cvar("g_keyhunt_teams"); // read the cvar directly as it gets written earlier in the same frame + kh_teams = BITS(bound(2, kh_teams, 4)); + + // make a KH entity for controlling the game + kh_controller = spawn(); + setthink(kh_controller, kh_Controller_Think); + kh_Controller_SetThink(0, kh_WaitForPlayers); + + setmodel(kh_controller, MDL_KH_KEY); + kh_key_dropped = kh_controller.modelindex; + /* + dprint(vtos(kh_controller.mins)); + dprint(vtos(kh_controller.maxs)); + dprint("\n"); + */ +#ifdef KH_PLAYER_USE_CARRIEDMODEL + setmodel(kh_controller, MDL_KH_KEY_CARRIED); + kh_key_carried = kh_controller.modelindex; +#else + kh_key_carried = kh_key_dropped; +#endif + + kh_controller.model = ""; + kh_controller.modelindex = 0; + + kh_ScoreRules(kh_teams); +} + +void kh_finalize() +{ + // to be called before intermission + kh_FinishRound(); + delete(kh_controller); + kh_controller = NULL; +} + +// legacy bot role + +void(entity this) havocbot_role_kh_carrier; +void(entity this) havocbot_role_kh_defense; +void(entity this) havocbot_role_kh_offense; +void(entity this) havocbot_role_kh_freelancer; + + +void havocbot_goalrating_kh(entity this, float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy) +{ + entity head; + for (head = kh_worldkeylist; head; head = head.kh_worldkeynext) + { + if(head.owner == this) + continue; + if(!kh_tracking_enabled) + { + // if it's carried by our team we know about it + // otherwise we have to see it to know about it + if(!head.owner || head.team != this.team) + { + traceline(this.origin + this.view_ofs, head.origin, MOVE_NOMONSTERS, this); + if (trace_fraction < 1 && trace_ent != head) + continue; // skip what I can't see + } + } + if(!head.owner) + navigation_routerating(this, head, ratingscale_dropped * 10000, 100000); + else if(head.team == this.team) + navigation_routerating(this, head.owner, ratingscale_team * 10000, 100000); + else + navigation_routerating(this, head.owner, ratingscale_enemy * 10000, 100000); + } + + havocbot_goalrating_items(this, 1, this.origin, 10000); +} + +void havocbot_role_kh_carrier(entity this) +{ + if(IS_DEAD(this)) + return; + + if (!(this.kh_next)) + { + LOG_TRACE("changing role to freelancer"); + this.havocbot_role = havocbot_role_kh_freelancer; + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + if(kh_Key_AllOwnedByWhichTeam() == this.team) + havocbot_goalrating_kh(this, 10, 0.1, 0.05); // bring home + else + havocbot_goalrating_kh(this, 4, 4, 0.5); // play defensively + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_kh_defense(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.kh_next) + { + LOG_TRACE("changing role to carrier"); + this.havocbot_role = havocbot_role_kh_carrier; + this.havocbot_role_timeout = 0; + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + random() * 10 + 20; + if (time > this.havocbot_role_timeout) + { + LOG_TRACE("changing role to freelancer"); + this.havocbot_role = havocbot_role_kh_freelancer; + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + float key_owner_team; + navigation_goalrating_start(this); + + key_owner_team = kh_Key_AllOwnedByWhichTeam(); + if(key_owner_team == this.team) + havocbot_goalrating_kh(this, 10, 0.1, 0.05); // defend key carriers + else if(key_owner_team == -1) + havocbot_goalrating_kh(this, 4, 1, 0.05); // play defensively + else + havocbot_goalrating_kh(this, 0.1, 0.1, 5); // ATTACK ANYWAY + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_kh_offense(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.kh_next) + { + LOG_TRACE("changing role to carrier"); + this.havocbot_role = havocbot_role_kh_carrier; + this.havocbot_role_timeout = 0; + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + random() * 10 + 20; + if (time > this.havocbot_role_timeout) + { + LOG_TRACE("changing role to freelancer"); + this.havocbot_role = havocbot_role_kh_freelancer; + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + float key_owner_team; + + navigation_goalrating_start(this); + + key_owner_team = kh_Key_AllOwnedByWhichTeam(); + if(key_owner_team == this.team) + havocbot_goalrating_kh(this, 10, 0.1, 0.05); // defend anyway + else if(key_owner_team == -1) + havocbot_goalrating_kh(this, 0.1, 1, 2); // play offensively + else + havocbot_goalrating_kh(this, 0.1, 0.1, 5); // ATTACK! EMERGENCY! + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void havocbot_role_kh_freelancer(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.kh_next) + { + LOG_TRACE("changing role to carrier"); + this.havocbot_role = havocbot_role_kh_carrier; + this.havocbot_role_timeout = 0; + return; + } + + if (!this.havocbot_role_timeout) + this.havocbot_role_timeout = time + random() * 10 + 10; + if (time > this.havocbot_role_timeout) + { + if (random() < 0.5) + { + LOG_TRACE("changing role to offense"); + this.havocbot_role = havocbot_role_kh_offense; + } + else + { + LOG_TRACE("changing role to defense"); + this.havocbot_role = havocbot_role_kh_defense; + } + this.havocbot_role_timeout = 0; + return; + } + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + int key_owner_team = kh_Key_AllOwnedByWhichTeam(); + if(key_owner_team == this.team) + havocbot_goalrating_kh(this, 10, 0.1, 0.05); // defend anyway + else if(key_owner_team == -1) + havocbot_goalrating_kh(this, 1, 10, 2); // prefer dropped keys + else + havocbot_goalrating_kh(this, 0.1, 0.1, 5); // ATTACK ANYWAY + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + + +// register this as a mutator + +MUTATOR_HOOKFUNCTION(kh, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + kh_Key_DropAll(player, true); +} + +MUTATOR_HOOKFUNCTION(kh, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + kh_Key_DropAll(player, true); +} + +MUTATOR_HOOKFUNCTION(kh, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(frag_target == frag_attacker) + kh_Key_DropAll(frag_target, true); + else if(IS_PLAYER(frag_attacker)) + kh_Key_DropAll(frag_target, false); + else + kh_Key_DropAll(frag_target, true); +} + +MUTATOR_HOOKFUNCTION(kh, GiveFragsForKill, CBC_ORDER_FIRST) +{ + entity frag_attacker = M_ARGV(0, entity); + entity frag_target = M_ARGV(1, entity); + float frag_score = M_ARGV(2, float); + M_ARGV(2, float) = kh_HandleFrags(frag_attacker, frag_target, frag_score); +} + +MUTATOR_HOOKFUNCTION(kh, MatchEnd) +{ + kh_finalize(); +} + +MUTATOR_HOOKFUNCTION(kh, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(0, float) = kh_teams; + return true; +} + +MUTATOR_HOOKFUNCTION(kh, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + STAT(KH_KEYS, client) = STAT(KH_KEYS, spectatee); +} + +MUTATOR_HOOKFUNCTION(kh, PlayerUseKey) +{ + entity player = M_ARGV(0, entity); + + if(MUTATOR_RETURNVALUE == 0) + { + entity k = player.kh_next; + if(k) + { + kh_Key_DropOne(k); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + if(IS_DEAD(bot)) + return true; + + float r = random() * 3; + if (r < 1) + bot.havocbot_role = havocbot_role_kh_offense; + else if (r < 2) + bot.havocbot_role = havocbot_role_kh_defense; + else + bot.havocbot_role = havocbot_role_kh_freelancer; + + return true; +} + +MUTATOR_HOOKFUNCTION(kh, DropSpecialItems) +{ + entity frag_target = M_ARGV(0, entity); + + kh_Key_DropAll(frag_target, false); +} + +MUTATOR_HOOKFUNCTION(kh, reset_map_global) +{ + kh_WaitForPlayers(); // takes care of killing the "missing teams" message +} diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh new file mode 100644 index 0000000000..345a3d166e --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh @@ -0,0 +1,34 @@ +#pragma once + +#include <common/mutators/base.qh> +#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit") +int autocvar_g_keyhunt_point_leadlimit; +bool autocvar_g_keyhunt_team_spawns; +void kh_Initialize(); + +REGISTER_MUTATOR(kh, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_keyhunt_team_spawns); + GameRules_limit_score(autocvar_g_keyhunt_point_limit); + GameRules_limit_lead(autocvar_g_keyhunt_point_leadlimit); + + kh_Initialize(); + } + return 0; +} + +#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext ) + +// ALL OF THESE should be removed in the future, as other code should not have to care + +// used by bots: +bool kh_tracking_enabled; +.entity kh_next; + +USING(kh_Think_t, void()); +void kh_StartRound(); +void kh_Controller_SetThink(float t, kh_Think_t func); diff --git a/qcsrc/common/gamemodes/gamemode/lms/_mod.inc b/qcsrc/common/gamemodes/gamemode/lms/_mod.inc new file mode 100644 index 0000000000..fcf63d7cc9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/lms/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/lms/sv_lms.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/lms/_mod.qh b/qcsrc/common/gamemodes/gamemode/lms/_mod.qh new file mode 100644 index 0000000000..51c1ee15f4 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/lms/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/lms/sv_lms.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc new file mode 100644 index 0000000000..493320cceb --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qc @@ -0,0 +1,436 @@ +#include "sv_lms.qh" + +#include <common/mutators/mutator/instagib/items.qh> +#include <server/campaign.qh> +#include <server/command/_mod.qh> + +int autocvar_g_lms_extra_lives; +bool autocvar_g_lms_join_anytime; +int autocvar_g_lms_last_join; +bool autocvar_g_lms_regenerate; + +// main functions +float LMS_NewPlayerLives() +{ + float fl; + fl = autocvar_fraglimit; + if(fl == 0) + fl = 999; + + // first player has left the game for dying too much? Nobody else can get in. + if(lms_lowest_lives < 1) + return 0; + + if(!autocvar_g_lms_join_anytime) + if(lms_lowest_lives < fl - autocvar_g_lms_last_join) + return 0; + + return bound(1, lms_lowest_lives, fl); +} + +void ClearWinners(); + +// LMS winning condition: game terminates if and only if there's at most one +// one player who's living lives. Top two scores being equal cancels the time +// limit. +int WinningCondition_LMS() +{ + entity first_player = NULL; + int totalplayers = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + if (!totalplayers) + first_player = it; + ++totalplayers; + }); + + if (totalplayers) + { + if (totalplayers > 1) + { + // two or more active players - continue with the game + + if (autocvar_g_campaign) + { + FOREACH_CLIENT(IS_REAL_CLIENT(it), { + float pl_lives = GameRules_scoring_add(it, LMS_LIVES, 0); + if (!pl_lives) + return WINNING_YES; // human player lost, game over + break; + }); + } + } + else + { + // exactly one player? + + ClearWinners(); + SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out + + if (LMS_NewPlayerLives()) + { + // game still running (that is, nobody got removed from the game by a frag yet)? then continue + return WINNING_NO; + } + else + { + // a winner! + // and assign him his first place + GameRules_scoring_add(first_player, LMS_RANK, 1); + if(warmup_stage) + return WINNING_NO; + else + return WINNING_YES; + } + } + } + else + { + // nobody is playing at all... + if (LMS_NewPlayerLives()) + { + // wait for players... + } + else + { + // SNAFU (maybe a draw game?) + ClearWinners(); + LOG_TRACE("No players, ending game."); + return WINNING_YES; + } + } + + // When we get here, we have at least two players who are actually LIVING, + // now check if the top two players have equal score. + WinningConditionHelper(NULL); + + ClearWinners(); + if(WinningConditionHelper_winner) + WinningConditionHelper_winner.winning = true; + if(WinningConditionHelper_topscore == WinningConditionHelper_secondscore) + return WINNING_NEVER; + + // Top two have different scores? Way to go for our beloved TIMELIMIT! + return WINNING_NO; +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(lms, reset_map_global) +{ + lms_lowest_lives = 999; +} + +MUTATOR_HOOKFUNCTION(lms, reset_map_players) +{ + FOREACH_CLIENT(true, { + TRANSMUTE(Player, it); + it.frags = FRAGS_PLAYER; + GameRules_scoring_add(it, LMS_LIVES, LMS_NewPlayerLives()); + PutClientInServer(it); + }); +} + +MUTATOR_HOOKFUNCTION(lms, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if(player.frags == FRAGS_SPECTATOR) + TRANSMUTE(Observer, player); + else + { + float tl = GameRules_scoring_add(player, LMS_LIVES, 0); + if(tl < lms_lowest_lives) + lms_lowest_lives = tl; + if(tl <= 0) + TRANSMUTE(Observer, player); + if(warmup_stage) + GameRules_scoring_add(player, LMS_RANK, -GameRules_scoring_add(player, LMS_RANK, 0)); + } +} + +MUTATOR_HOOKFUNCTION(lms, ForbidSpawn) +{ + entity player = M_ARGV(0, entity); + + if(warmup_stage) + return false; + if(player.frags == FRAGS_SPECTATOR) + return true; + if(GameRules_scoring_add(player, LMS_LIVES, 0) <= 0) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES); + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(lms, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.respawn_flags |= RESPAWN_FORCE; +} + +void lms_RemovePlayer(entity player) +{ + static int quitters = 0; + float player_rank = GameRules_scoring_add(player, LMS_RANK, 0); + if (!player_rank) + { + int pl_cnt = 0; + FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; }); + if (player.lms_spectate_warning != 2) + { + if(IS_BOT_CLIENT(player)) + bot_clear(player); + player.frags = FRAGS_LMS_LOSER; + GameRules_scoring_add(player, LMS_RANK, pl_cnt + 1); + } + else + { + lms_lowest_lives = 999; + FOREACH_CLIENT(true, { + if (it.frags == FRAGS_LMS_LOSER) + { + float it_rank = GameRules_scoring_add(it, LMS_RANK, 0); + if (it_rank > player_rank && it_rank <= 256) + GameRules_scoring_add(it, LMS_RANK, -1); + lms_lowest_lives = 0; + } + else if (it.frags != FRAGS_SPECTATOR) + { + float tl = GameRules_scoring_add(it, LMS_LIVES, 0); + if(tl < lms_lowest_lives) + lms_lowest_lives = tl; + } + }); + GameRules_scoring_add(player, LMS_RANK, 665 - quitters); // different from 666 + if(!warmup_stage) + { + GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0)); + ++quitters; + } + player.frags = FRAGS_LMS_LOSER; + TRANSMUTE(Observer, player); + } + if (pl_cnt == 2 && !warmup_stage) // a player is forfeiting leaving only one player + lms_lowest_lives = 0; // end the game now! + } + + if(CS(player).killcount != FRAGS_SPECTATOR) + if(GameRules_scoring_add(player, LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_LMS_NOLIVES, player.netname); + else + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_LMS_FORFEIT, player.netname); +} + +MUTATOR_HOOKFUNCTION(lms, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + lms_RemovePlayer(player); +} + +MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + if (!IS_PLAYER(player)) + return true; + + lms_RemovePlayer(player); + return true; // prevent team reset +} + +MUTATOR_HOOKFUNCTION(lms, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + if(GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()) <= 0) + { + GameRules_scoring_add(player, LMS_RANK, 666); // mark as forced spectator for the hud code + player.frags = FRAGS_SPECTATOR; + } +} + +MUTATOR_HOOKFUNCTION(lms, AutoJoinOnConnection) +{ + if(autocvar_g_campaign) + return false; + return true; +} + +MUTATOR_HOOKFUNCTION(lms, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(player.deadflag == DEAD_DYING) + player.deadflag = DEAD_RESPAWNING; +} + +MUTATOR_HOOKFUNCTION(lms, PlayerRegen) +{ + if(autocvar_g_lms_regenerate) + return false; + return true; +} + +MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon) +{ + // forbode! + return true; +} + +MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) +{ + entity frag_target = M_ARGV(1, entity); + + if (!warmup_stage) + { + // remove a life + int tl = GameRules_scoring_add(frag_target, LMS_LIVES, -1); + if(tl < lms_lowest_lives) + lms_lowest_lives = tl; + if(tl <= 0) + { + int pl_cnt = 0; + FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; }); + if(IS_BOT_CLIENT(frag_target)) + bot_clear(frag_target); + frag_target.frags = FRAGS_LMS_LOSER; + GameRules_scoring_add(frag_target, LMS_RANK, pl_cnt); + } + } + M_ARGV(2, float) = 0; // frag score + + return true; +} + +MUTATOR_HOOKFUNCTION(lms, SetStartItems) +{ + start_items &= ~IT_UNLIMITED_AMMO; + start_health = warmup_start_health = cvar("g_lms_start_health"); + start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); + start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); + start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); + start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); + start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); + start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); + start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); +} + +MUTATOR_HOOKFUNCTION(lms, ForbidPlayerScore_Clear) +{ + // don't clear player score + return true; +} + +MUTATOR_HOOKFUNCTION(lms, FilterItemDefinition) +{ + entity definition = M_ARGV(0, entity); + + if (autocvar_g_lms_extra_lives && definition == ITEM_ExtraLife) + { + return false; + } + return true; +} + +void lms_extralife(entity this) +{ + StartItem(this, ITEM_ExtraLife); +} + +MUTATOR_HOOKFUNCTION(lms, OnEntityPreSpawn) +{ + if (!autocvar_g_powerups) return false; + if (!autocvar_g_lms_extra_lives) return false; + + entity ent = M_ARGV(0, entity); + + // Can't use .itemdef here + if (ent.classname != "item_health_mega") return false; + + entity e = spawn(); + setthink(e, lms_extralife); + + e.nextthink = time + 0.1; + e.spawnflags = ent.spawnflags; + e.noalign = ent.noalign; + setorigin(e, ent.origin); + + return true; +} + +MUTATOR_HOOKFUNCTION(lms, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + if(item.itemdef == ITEM_ExtraLife) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); + GameRules_scoring_add(toucher, LMS_LIVES, autocvar_g_lms_extra_lives); + return MUT_ITEMTOUCH_PICKUP; + } + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE) +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it), { + ++M_ARGV(0, int); // activerealplayers + ++M_ARGV(1, int); // realplayers + }); + + return true; +} + +MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate) +{ + entity player = M_ARGV(0, entity); + + if(warmup_stage || player.lms_spectate_warning) + { + // for the forfeit message... + player.lms_spectate_warning = 2; + } + else + { + if(player.frags != FRAGS_SPECTATOR && player.frags != FRAGS_LMS_LOSER) + { + player.lms_spectate_warning = 1; + sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n"); + } + return MUT_SPECCMD_RETURN; + } + return MUT_SPECCMD_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(lms, CheckRules_World) +{ + M_ARGV(0, float) = WinningCondition_LMS(); + return true; +} + +MUTATOR_HOOKFUNCTION(lms, WantWeapon) +{ + M_ARGV(2, bool) = true; // all weapons +} + +MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(lms, AddPlayerScore) +{ + if(game_stopped) + if(M_ARGV(0, entity) == SP_LMS_RANK) // score field + return true; // allow writing to this field in intermission as it is needed for newly joining players +} + +void lms_Initialize() +{ + lms_lowest_lives = 9999; +} diff --git a/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh new file mode 100644 index 0000000000..256620a454 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/lms/sv_lms.qh @@ -0,0 +1,29 @@ +#pragma once + +#include <common/mutators/base.qh> +#include <common/scores.qh> +.float lms_spectate_warning; +#define autocvar_g_lms_lives_override cvar("g_lms_lives_override") +void lms_Initialize(); + +REGISTER_MUTATOR(lms, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_limit_score(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override)); + GameRules_limit_lead(0); + GameRules_score_enabled(false); + GameRules_scoring(0, 0, 0, { + field(SP_LMS_LIVES, "lives", SFL_SORT_PRIO_SECONDARY); + field(SP_LMS_RANK, "rank", SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE); + }); + + lms_Initialize(); + } + return 0; +} + +// lives related defs +float lms_lowest_lives; +float LMS_NewPlayerLives(); diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index fa718eb16f..65c5bcd8a0 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -7,7 +7,7 @@ REGISTER_MUTATOR(cl_nb, true); MUTATOR_HOOKFUNCTION(cl_nb, WantEventchase) { - if(autocvar_cl_eventchase_nexball && gametype == MAPINFO_TYPE_NEXBALL && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) + if(autocvar_cl_eventchase_nexball && ISGAMETYPE(NEXBALL) && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) return true; return false; } @@ -195,9 +195,9 @@ void GiveBall(entity plyr, entity ball) ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold; } - plyr.(weaponentity).weapons = plyr.weapons; + STAT(WEAPONS, plyr.(weaponentity)) = STAT(WEAPONS, plyr); plyr.m_switchweapon = plyr.(weaponentity).m_weapon; - plyr.weapons = WEPSET(NEXBALL); + STAT(WEAPONS, plyr) = WEPSET(NEXBALL); Weapon w = WEP_NEXBALL; w.wr_resetplayer(w, plyr); plyr.(weaponentity).m_switchweapon = WEP_NEXBALL; @@ -308,7 +308,7 @@ void football_touch(entity this, entity toucher) } if (!IS_PLAYER(toucher)) return; - if(toucher.health < 1) + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) return; if(!this.cnt) this.nextthink = time + autocvar_g_nexball_delay_idle; @@ -348,7 +348,7 @@ void basketball_touch(entity this, entity toucher) } if(!this.cnt && IS_PLAYER(toucher) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (toucher != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect)) { - if(toucher.health <= 0) + if(GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) return; LogNB("caught", toucher); GiveBall(toucher, this); @@ -635,7 +635,7 @@ void SpawnGoal(entity this) EXACTTRIGGER_INIT; - if(this.team != GOAL_OUT && Team_TeamToNumber(this.team) != -1) + if(this.team != GOAL_OUT && Team_IsValidTeam(this.team)) { entity wp = WaypointSprite_SpawnFixed(WP_NbGoal, (this.absmin + this.absmax) * 0.5, this, sprite, RADARICON_NONE); wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0.5 0'); @@ -828,15 +828,15 @@ MUTATOR_HOOKFUNCTION(nb, PlayerPreThink) { .entity weaponentity = weaponentities[slot]; - if(player.(weaponentity).weapons) + if(STAT(WEAPONS, player.(weaponentity))) { - player.weapons = player.(weaponentity).weapons; + STAT(WEAPONS, player) = STAT(WEAPONS, player.(weaponentity)); Weapon w = WEP_NEXBALL; w.wr_resetplayer(w, player); player.(weaponentity).m_switchweapon = player.m_switchweapon; W_SwitchWeapon(player, player.(weaponentity).m_switchweapon, weaponentity); - player.(weaponentity).weapons = '0 0 0'; + STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; } } } @@ -862,13 +862,13 @@ MUTATOR_HOOKFUNCTION(nb, PlayerSpawn) for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { .entity weaponentity = weaponentities[slot]; - player.(weaponentity).weapons = '0 0 0'; + STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; } if (nexball_mode & NBM_BASKETBALL) - player.weapons |= WEPSET(NEXBALL); + STAT(WEAPONS, player) |= WEPSET(NEXBALL); else - player.weapons = '0 0 0'; + STAT(WEAPONS, player) = '0 0 0'; return false; } @@ -920,7 +920,7 @@ MUTATOR_HOOKFUNCTION(nb, ItemTouch) return MUT_ITEMTOUCH_CONTINUE; } -MUTATOR_HOOKFUNCTION(nb, CheckAllowedTeams) +MUTATOR_HOOKFUNCTION(nb, TeamBalance_CheckAllowedTeams) { M_ARGV(1, string) = "nexball_team"; return true; diff --git a/qcsrc/common/gamemodes/gamemode/nexball/sv_weapon.qc b/qcsrc/common/gamemodes/gamemode/nexball/sv_weapon.qc index 838d0dd7e9..02b57b2ca8 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/sv_weapon.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/sv_weapon.qc @@ -1,7 +1,7 @@ #include "sv_weapon.qh" -void W_Nexball_Attack(entity actor, .entity weaponentity, float t); -void W_Nexball_Attack2(entity actor, .entity weaponentity); +void W_Nexball_Attack(Weapon thiswep, entity actor, .entity weaponentity, float t); +void W_Nexball_Attack2(Weapon thiswep, entity actor, .entity weaponentity); vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity); METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity weaponentity, int fire)) @@ -18,19 +18,19 @@ METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity we } else { - W_Nexball_Attack(actor, weaponentity, -1); + W_Nexball_Attack(thiswep, actor, weaponentity, -1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } if(fire & 2) if(weapon_prepareattack(thiswep, actor, weaponentity, true, autocvar_g_balance_nexball_secondary_refire)) { - W_Nexball_Attack2(actor, weaponentity); + W_Nexball_Attack2(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready); } if(!(fire & 1) && STAT(NB_METERSTART, actor) && actor.ballcarried) { - W_Nexball_Attack(actor, weaponentity, time - STAT(NB_METERSTART, actor)); + W_Nexball_Attack(thiswep, actor, weaponentity, time - STAT(NB_METERSTART, actor)); // DropBall or stealing will set metertime back to 0 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } @@ -105,7 +105,7 @@ void W_Nexball_Touch(entity this, entity toucher) delete(this); } -void W_Nexball_Attack(entity actor, .entity weaponentity, float t) +void W_Nexball_Attack(Weapon thiswep, entity actor, .entity weaponentity, float t) { entity ball; float mul, mi, ma; @@ -141,7 +141,7 @@ void W_Nexball_Attack(entity actor, .entity weaponentity, float t) //TODO: use the speed_up cvar too ?? } -void W_Nexball_Attack2(entity actor, .entity weaponentity) +void W_Nexball_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { if(actor.ballcarried.enemy) { diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc index a03e5b3353..b8d49b10f2 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc @@ -30,7 +30,7 @@ void cpicon_draw(entity this) this.cp_bob_spd = this.cp_bob_spd + 1.875 * frametime; this.colormod = '1 1 1' * (2 - bound(0, (this.pain_finished - time) / 10, 1)); - if(!this.iscaptured) this.alpha = this.health / this.max_health; + if(!this.iscaptured) this.alpha = GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health; if(this.iscaptured) { @@ -165,14 +165,14 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) this.origin = ReadVector(); setorigin(this, this.origin); - this.health = ReadByte(); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ReadByte()); this.max_health = ReadByte(); this.count = ReadByte(); this.team = ReadByte(); this.iscaptured = ReadByte(); if(!this.count) - this.count = (this.health - this.max_health) * frametime; + this.count = (GetResourceAmount(this, RESOURCE_HEALTH) - this.max_health) * frametime; cpicon_changeteam(this); cpicon_construct(this, isnew); @@ -189,9 +189,9 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) _tmp = ReadByte(); - if(_tmp != this.health) + if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH)) cpicon_damage(this, _tmp); - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } } diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc index a3374bf91e..9d12c5548e 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc @@ -48,10 +48,10 @@ void generator_draw(entity this) if(time < this.move_time) return; - if(this.health > 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) > 0) { // damaged fx (less probable the more damaged is the generator) - if(random() < 0.9 - this.health / this.max_health) + if(random() < 0.9 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) if(random() < 0.01) { pointparticles(EFFECT_ELECTRO_BALLEXPLODE, this.origin + randompos('-50 -50 -20', '50 50 50'), '0 0 0', 1); @@ -195,7 +195,7 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) this.origin = ReadVector(); setorigin(this, this.origin); - this.health = ReadByte(); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, ReadByte()); this.max_health = ReadByte(); this.count = ReadByte(); this.team = ReadByte(); @@ -219,9 +219,9 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) _tmp = ReadByte(); - if(_tmp != this.health) + if(_tmp != GetResourceAmount(this, RESOURCE_HEALTH)) generator_damage(this, _tmp); - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } } diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index 80d6a6be29..5deef7ec22 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -36,7 +36,7 @@ MUTATOR_HOOKFUNCTION(cl_ons, WantEventchase) entity gen = NULL; if(ons_roundlost) { - IL_EACH(g_onsgenerators, it.health <= 0, + IL_EACH(g_onsgenerators, GetResourceAmount(it, RESOURCE_HEALTH) <= 0, { gen = it; break; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc index d3b6d5c7f4..a00af18ff8 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc @@ -12,7 +12,7 @@ bool cpicon_send(entity this, entity to, int sf) { WriteVector(MSG_ENTITY, this.origin); - WriteByte(MSG_ENTITY, this.health); + WriteByte(MSG_ENTITY, GetResourceAmount(this, RESOURCE_HEALTH)); WriteByte(MSG_ENTITY, this.max_health); WriteByte(MSG_ENTITY, this.count); WriteByte(MSG_ENTITY, this.team); @@ -23,10 +23,10 @@ bool cpicon_send(entity this, entity to, int sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc index ac0596f2e2..a33a430124 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qc @@ -8,7 +8,7 @@ bool generator_send(entity this, entity to, int sf) { WriteVector(MSG_ENTITY, this.origin); - WriteByte(MSG_ENTITY, this.health); + WriteByte(MSG_ENTITY, GetResourceAmount(this, RESOURCE_HEALTH)); WriteByte(MSG_ENTITY, this.max_health); WriteByte(MSG_ENTITY, this.count); WriteByte(MSG_ENTITY, this.team); @@ -18,10 +18,10 @@ bool generator_send(entity this, entity to, int sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc index 2637aeef21..b4c4dc68fe 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -27,7 +27,6 @@ float autocvar_g_onslaught_teleport_radius; float autocvar_g_onslaught_spawn_choose; float autocvar_g_onslaught_click_radius; -void FixSize(entity e); entity cam; // ======================= @@ -114,12 +113,6 @@ void ons_CaptureShield_Spawn(entity generator, bool is_generator) // Junk Pile // ========== -void setmodel_fixsize(entity e, Model m) -{ - setmodel(e, m); - FixSize(e); -} - void onslaught_updatelinks() { entity l; @@ -400,11 +393,11 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker ons_notification_time[this.team] = time; } - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); if(this.owner.iscaptured) - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); else - WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - GetResourceAmount(this, RESOURCE_HEALTH)) / (this.count / ONS_CP_THINKRATE)); this.pain_finished = time + 1; // particles on every hit pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); @@ -414,7 +407,7 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker else sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); - if (this.health < 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 0) { sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); @@ -438,7 +431,7 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker this.owner.waslinked = this.owner.islinked; if(this.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1); + setmodel(this.owner, MDL_ONS_CP_PAD1); //setsize(this, '-32 -32 0', '32 32 8'); delete(this); @@ -447,6 +440,23 @@ void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker this.SendFlags |= CPSF_STATUS; } +bool ons_ControlPoint_Icon_Heal(entity targ, entity inflictor, float amount, float limit) +{ + float hlth = GetResourceAmount(targ, RESOURCE_HEALTH); + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if (hlth <= 0 || hlth >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + hlth = GetResourceAmount(targ, RESOURCE_HEALTH); + if(targ.owner.iscaptured) + WaypointSprite_UpdateHealth(targ.owner.sprite, hlth); + else + WaypointSprite_UpdateBuildFinished(targ.owner.sprite, time + (targ.max_health - hlth) / (targ.count / ONS_CP_THINKRATE)); + targ.SendFlags |= CPSF_STATUS; + return true; +} + void ons_ControlPoint_Icon_Think(entity this) { this.nextthink = time + ONS_CP_THINKRATE; @@ -469,9 +479,9 @@ void ons_ControlPoint_Icon_Think(entity this) _friendly_count = _friendly_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); _enemy_count = _enemy_count * (autocvar_g_onslaught_cp_proxydecap_dps * ONS_CP_THINKRATE); - this.health = bound(0, this.health + (_friendly_count - _enemy_count), this.max_health); + GiveResourceWithLimit(this, RESOURCE_HEALTH, (_friendly_count - _enemy_count), this.max_health); this.SendFlags |= CPSF_STATUS; - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, DMG_NOWEP, this.origin, '0 0 0'); return; @@ -480,12 +490,10 @@ void ons_ControlPoint_Icon_Think(entity this) if (time > this.pain_finished + 5) { - if(this.health < this.max_health) + if(GetResourceAmount(this, RESOURCE_HEALTH) < this.max_health) { - this.health = this.health + this.count; - if (this.health >= this.max_health) - this.health = this.max_health; - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + GiveResourceWithLimit(this, RESOURCE_HEALTH, this.count, this.max_health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } } @@ -504,7 +512,7 @@ void ons_ControlPoint_Icon_Think(entity this) } // damaged fx - if(random() < 0.6 - this.health / this.max_health) + if(random() < 0.6 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) { Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); @@ -526,13 +534,13 @@ void ons_ControlPoint_Icon_BuildThink(entity this) if(!a) return; - this.health = this.health + this.count; + GiveResource(this, RESOURCE_HEALTH, this.count); this.SendFlags |= CPSF_STATUS; - if (this.health >= this.max_health) + if (GetResourceAmount(this, RESOURCE_HEALTH) >= this.max_health) { - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.count = autocvar_g_onslaught_cp_regen * ONS_CP_THINKRATE; // slow repair rate from now on setthink(this, ons_ControlPoint_Icon_Think); sound(this, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILT, VOL_BASE, ATTEN_NORM); @@ -542,7 +550,7 @@ void ons_ControlPoint_Icon_BuildThink(entity this) Send_Effect(EFFECT_CAP(this.owner.team), this.owner.origin, '0 0 0', 1); WaypointSprite_UpdateMaxHealth(this.owner.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + WaypointSprite_UpdateHealth(this.owner.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); if(IS_PLAYER(this.owner.ons_toucher)) { @@ -563,9 +571,9 @@ void ons_ControlPoint_Icon_BuildThink(entity this) this.SendFlags |= CPSF_SETUP; } if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); + setmodel(this.owner, MDL_ONS_CP_PAD2); - if(random() < 0.9 - this.health / this.max_health) + if(random() < 0.9 - GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); } @@ -580,15 +588,16 @@ void ons_ControlPoint_Icon_Spawn(entity cp, entity player) e.owner = cp; e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, autocvar_g_onslaught_cp_buildhealth); e.solid = SOLID_NOT; e.takedamage = DAMAGE_AIM; e.bot_attack = true; IL_PUSH(g_bot_targets, e); e.event_damage = ons_ControlPoint_Icon_Damage; + e.event_heal = ons_ControlPoint_Icon_Heal; e.team = player.team; e.colormap = 1024 + (e.team - 1) * 17; - e.count = (e.max_health - e.health) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build + e.count = (e.max_health - GetResourceAmount(e, RESOURCE_HEALTH)) * ONS_CP_THINKRATE / autocvar_g_onslaught_cp_buildtime; // how long it takes to build sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); @@ -598,7 +607,7 @@ void ons_ControlPoint_Icon_Spawn(entity cp, entity player) Send_Effect(EFFECT_FLAG_TOUCH(player.team), e.origin, '0 0 0', 1); - WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - e.health) / (e.count / ONS_CP_THINKRATE)); + WaypointSprite_UpdateBuildFinished(cp.sprite, time + (e.max_health - GetResourceAmount(e, RESOURCE_HEALTH)) / (e.count / ONS_CP_THINKRATE)); WaypointSprite_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); cp.sprite.SendFlags |= 16; @@ -640,7 +649,7 @@ void ons_ControlPoint_UpdateSprite(entity e) else { WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + WaypointSprite_UpdateHealth(e.sprite, GetResourceAmount(e.goalentity, RESOURCE_HEALTH)); } } if(e.lastshielded) @@ -721,7 +730,7 @@ void ons_ControlPoint_Reset(entity this) setthink(this, ons_ControlPoint_Think); this.ons_toucher = NULL; this.nextthink = time + ONS_CP_THINKRATE; - setmodel_fixsize(this, MDL_ONS_CP_PAD1); + setmodel(this, MDL_ONS_CP_PAD1); WaypointSprite_UpdateMaxHealth(this.sprite, 0); WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); @@ -765,7 +774,7 @@ void ons_ControlPoint_Setup(entity cp) if(cp.message == "") { cp.message = "a"; } // appearence - setmodel_fixsize(cp, MDL_ONS_CP_PAD1); + setmodel(cp, MDL_ONS_CP_PAD1); // control point placement if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location @@ -889,14 +898,15 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); } } - this.health = this.health - damage; - WaypointSprite_UpdateHealth(this.sprite, this.health); + TakeResource(this, RESOURCE_HEALTH, damage); + float hlth = GetResourceAmount(this, RESOURCE_HEALTH); + WaypointSprite_UpdateHealth(this.sprite, hlth); // choose an animation frame based on health - this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); + this.frame = 10 * bound(0, (1 - hlth / this.max_health), 1); // see if the generator is still functional, or dying - if (this.health > 0) + if (hlth > 0) { - this.lasthealth = this.health; + this.lasthealth = hlth; } else { @@ -912,6 +922,7 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d this.isshielded = false; this.takedamage = DAMAGE_NO; // can't be hurt anymore this.event_damage = func_null; // won't do anything if hurt + this.event_heal = func_null; this.count = 0; // reset counter setthink(this, func_null); this.nextthink = 0; @@ -946,31 +957,48 @@ void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float d this.SendFlags |= GSF_STATUS; } +bool ons_GeneratorHeal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + float hlth = GetResourceAmount(targ, RESOURCE_HEALTH); + if (hlth <= 0 || hlth >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + hlth = GetResourceAmount(targ, RESOURCE_HEALTH); + WaypointSprite_UpdateHealth(targ.sprite, hlth); + targ.frame = 10 * bound(0, (1 - hlth / targ.max_health), 1); + targ.lasthealth = hlth; + targ.SendFlags |= GSF_STATUS; + return true; +} + void ons_GeneratorThink(entity this) { this.nextthink = time + GEN_THINKRATE; - if (!game_stopped) + + if (game_stopped || this.isshielded || time < this.wait) + return; + + this.wait = time + 5; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(!this.isshielded && this.wait < time) + if (SAME_TEAM(it, this)) { - this.wait = time + 5; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(SAME_TEAM(it, this)) - { - Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); - soundto(MSG_ONE, it, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? - } - else - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); - }); + Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); + msg_entity = it; + soundto(MSG_ONE, this, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? } - } + else + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); + }); } void ons_GeneratorReset(entity this) { this.team = this.team_saved; - this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, autocvar_g_onslaught_gen_health); + this.lasthealth = this.max_health = autocvar_g_onslaught_gen_health; this.takedamage = DAMAGE_AIM; this.bot_attack = true; if(!IL_CONTAINS(g_bot_targets, this)) @@ -979,6 +1007,7 @@ void ons_GeneratorReset(entity this) this.islinked = true; this.isshielded = true; this.event_damage = ons_GeneratorDamage; + this.event_heal = ons_GeneratorHeal; setthink(this, ons_GeneratorThink); this.nextthink = time + GEN_THINKRATE; @@ -988,7 +1017,7 @@ void ons_GeneratorReset(entity this) this.SendFlags |= GSF_STATUS; WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); onslaught_updatelinks(); @@ -1035,11 +1064,13 @@ void ons_GeneratorSetup(entity gen) // called when spawning a generator entity o gen.team_saved = teamnumber; IL_PUSH(g_saved_team, gen); set_movetype(gen, MOVETYPE_NONE); - gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; + gen.lasthealth = gen.max_health = autocvar_g_onslaught_gen_health; + SetResourceAmountExplicit(gen, RESOURCE_HEALTH, autocvar_g_onslaught_gen_health); gen.takedamage = DAMAGE_AIM; gen.bot_attack = true; IL_PUSH(g_bot_targets, gen); gen.event_damage = ons_GeneratorDamage; + gen.event_heal = ons_GeneratorHeal; gen.reset = ons_GeneratorReset; setthink(gen, ons_GeneratorThink); gen.nextthink = time + GEN_THINKRATE; @@ -1061,7 +1092,7 @@ void ons_GeneratorSetup(entity gen) // called when spawning a generator entity o WaypointSprite_SpawnFixed(WP_Null, gen.origin + CPGEN_WAYPOINT_OFFSET, gen, sprite, RADARICON_NONE); WaypointSprite_UpdateRule(gen.sprite, gen.team, SPRITERULE_TEAMPLAY); WaypointSprite_UpdateMaxHealth(gen.sprite, gen.max_health); - WaypointSprite_UpdateHealth(gen.sprite, gen.health); + WaypointSprite_UpdateHealth(gen.sprite, GetResourceAmount(gen, RESOURCE_HEALTH)); InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); } @@ -1075,46 +1106,52 @@ int total_generators; void Onslaught_count_generators() { entity e; - total_generators = redowned = blueowned = yellowowned = pinkowned = 0; + total_generators = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + Team_SetNumberOfControlPoints(Team_GetTeamFromIndex(i), 0); + } for(e = ons_worldgeneratorlist; e; e = e.ons_worldgeneratornext) { ++total_generators; - redowned += (e.team == NUM_TEAM_1 && e.health > 0); - blueowned += (e.team == NUM_TEAM_2 && e.health > 0); - yellowowned += (e.team == NUM_TEAM_3 && e.health > 0); - pinkowned += (e.team == NUM_TEAM_4 && e.health > 0); + if (GetResourceAmount(e, RESOURCE_HEALTH) < 1) + { + continue; + } + entity team_ = Entity_GetTeam(e); + int num_control_points = Team_GetNumberOfControlPoints(team_); + ++num_control_points; + Team_SetNumberOfControlPoints(team_, num_control_points); } } int Onslaught_GetWinnerTeam() { int winner_team = 0; - if(redowned > 0) - winner_team = NUM_TEAM_1; - if(blueowned > 0) + if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(1)) >= 1) { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; + winner_team = NUM_TEAM_1; } - if(yellowowned > 0) + for (int i = 2; i <= NUM_TEAMS; ++i) { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; + if (Team_GetNumberOfControlPoints(Team_GetTeamFromIndex(i)) >= 1) + { + if (winner_team != 0) + { + return 0; + } + winner_team = Team_IndexToTeam(i); + } } - if(pinkowned > 0) + if (winner_team) { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) return winner_team; + } return -1; // no generators left? } void nades_Clear(entity e); -#define ONS_OWNED_GENERATORS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) -#define ONS_OWNED_GENERATORS_OK() (ONS_OWNED_GENERATORS() > 1) bool Onslaught_CheckWinner() { if ((autocvar_timelimit && time > game_starttime + autocvar_timelimit * 60) || (round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0)) @@ -1160,8 +1197,10 @@ bool Onslaught_CheckWinner() Onslaught_count_generators(); - if(ONS_OWNED_GENERATORS_OK()) + if (Team_GetNumberOfTeamsWithControlPoints() > 1) + { return 0; + } int winner_team = Onslaught_GetWinnerTeam(); @@ -1218,71 +1257,26 @@ void Onslaught_RoundStart() // NOTE: LEGACY CODE, needs to be re-written! -void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius) -{ - bool needarmor = false, needweapons = false; - - // Needs armor/health? - if(this.health<100) - needarmor = true; - - // Needs weapons? - int c = 0; - FOREACH(Weapons, it != WEP_Null, { - if(this.weapons & (it.m_wepset)) - if(++c >= 4) - break; - }); - - if(c<4) - needweapons = true; - - if(!needweapons && !needarmor) - return; - - LOG_DEBUG(this.netname, " needs weapons ", ftos(needweapons)); - LOG_DEBUG(this.netname, " needs armor ", ftos(needarmor)); - - // See what is around - IL_EACH(g_items, it.bot_pickup, - { - // gather health and armor only - if (it.solid) - if ( ((it.health || it.armorvalue) && needarmor) || (it.weapons && needweapons ) ) - if (vdist(it.origin - org, <, sradius)) - { - int t = it.bot_pickupevalfunc(this, it); - if (t > 0) - navigation_routerating(this, it, t * ratingscale, 500); - } - }); -} - void havocbot_role_ons_setrole(entity this, int role) { - LOG_DEBUG(this.netname," switched to "); switch(role) { case HAVOCBOT_ONS_ROLE_DEFENSE: - LOG_DEBUG("defense"); + LOG_DEBUG(this.netname, " switched to defense"); this.havocbot_role = havocbot_role_ons_defense; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_DEFENSE; this.havocbot_role_timeout = 0; break; case HAVOCBOT_ONS_ROLE_ASSISTANT: - LOG_DEBUG("assistant"); + LOG_DEBUG(this.netname, " switched to assistant"); this.havocbot_role = havocbot_role_ons_assistant; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_ASSISTANT; this.havocbot_role_timeout = 0; break; case HAVOCBOT_ONS_ROLE_OFFENSE: - LOG_DEBUG("offense"); + LOG_DEBUG(this.netname, " switched to offense"); this.havocbot_role = havocbot_role_ons_offense; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; this.havocbot_role_timeout = 0; break; } - LOG_DEBUG(""); } void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale) @@ -1307,9 +1301,9 @@ void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale // Count team mates interested in this control point // (easier and cleaner than keeping counters per cp and teams) - FOREACH_CLIENT(IS_PLAYER(it), { + FOREACH_CLIENT(it != this && IS_PLAYER(it), { if(SAME_TEAM(it, this)) - if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(it.havocbot_role == havocbot_role_ons_offense) if(it.havocbot_ons_target == cp2) ++c; }); @@ -1320,7 +1314,7 @@ void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale } // We'll consider only the best case - bestvalue = 99999999999; + bestvalue = FLOAT_MAX; cp = NULL; for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) { @@ -1346,23 +1340,21 @@ void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale // Rate waypoints near it found = false; best = NULL; - bestvalue = 99999999999; - for(radius=0; radius<1000 && !found; radius+=500) + bestvalue = FLOAT_MAX; + for (radius = 500; radius <= 1000 && !found; radius += 500) { - for(wp=findradius(cp.origin,radius); wp; wp=wp.chain) + IL_EACH(g_waypoints, vdist(cp.origin - it.origin, <, radius), { - if(!(wp.wpflags & WAYPOINTFLAG_GENERATED)) - if(wp.classname=="waypoint") - if(checkpvs(wp.origin,cp)) + if (!(it.wpflags & WAYPOINTFLAG_GENERATED) && checkpvs(it.origin, cp)) { found = true; - if(wp.cnt<bestvalue) + if (it.cnt < bestvalue) { - best = wp; - bestvalue = wp.cnt; + best = it; + bestvalue = it.cnt; } } - } + }); } if(best) @@ -1385,22 +1377,7 @@ void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale { // Should be touched LOG_DEBUG(this.netname, " found a touchable controlpoint at ", vtos(cp.origin)); - found = false; - - // Look for auto generated waypoint - if (!bot_waypoints_for_items) - for (wp = findradius(cp.origin,100); wp; wp = wp.chain) - { - if(wp.classname=="waypoint") - { - navigation_routerating(this, wp, ratingscale, 10000); - found = true; - } - } - - // Nothing found, rate the controlpoint itself - if (!found) - navigation_routerating(this, cp, ratingscale, 10000); + navigation_routerating(this, cp, ratingscale * 2, 10000); } } @@ -1408,7 +1385,7 @@ bool havocbot_goalrating_ons_generator_attack(entity this, float ratingscale) { entity g, wp, bestwp; bool found; - int best; + int bestvalue; for(g = ons_worldgeneratorlist; g; g = g.ons_worldgeneratornext) { @@ -1419,21 +1396,20 @@ bool havocbot_goalrating_ons_generator_attack(entity this, float ratingscale) // Rate waypoints near it found = false; bestwp = NULL; - best = 99999999999; + bestvalue = FLOAT_MAX; - for(wp=findradius(g.origin,400); wp; wp=wp.chain) + IL_EACH(g_waypoints, vdist(g.origin - it.origin, <, 400), { - if(wp.classname=="waypoint") - if(checkpvs(wp.origin,g)) + if (checkpvs(it.origin, g)) { found = true; - if(wp.cnt<best) + if (it.cnt < bestvalue) { - bestwp = wp; - best = wp.cnt; + bestwp = it; + bestvalue = it.cnt; } } - } + }); if(bestwp) { @@ -1486,9 +1462,9 @@ void havocbot_role_ons_offense(entity this) { navigation_goalrating_start(this); havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650); - if(!havocbot_goalrating_ons_generator_attack(this, 20000)) - havocbot_goalrating_ons_controlpoints_attack(this, 20000); - havocbot_goalrating_ons_offenseitems(this, 10000, this.origin, 10000); + if(!havocbot_goalrating_ons_generator_attack(this, 10000)) + havocbot_goalrating_ons_controlpoints_attack(this, 10000); + havocbot_goalrating_items(this, 25000, this.origin, 10000); navigation_goalrating_end(this); navigation_goalrating_timeout_set(this); @@ -1922,17 +1898,14 @@ MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) return true; } -MUTATOR_HOOKFUNCTION(ons, CheckAllowedTeams) +MUTATOR_HOOKFUNCTION(ons, TeamBalance_CheckAllowedTeams) { // onslaught is special for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) { - switch(tmp_entity.team) + if (Team_IsValidTeam(tmp_entity.team)) { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; + M_ARGV(0, float) |= Team_TeamToBit(tmp_entity.team); } } @@ -1972,7 +1945,7 @@ MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) { entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - if ( !source_point && player.health > 0 ) + if ( !source_point && GetResourceAmount(player, RESOURCE_HEALTH) > 0 ) { sprint(player, "\nYou need to be next to a control point\n"); return true; @@ -1987,7 +1960,7 @@ MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) return true; } - if ( player.health <= 0 ) + if ( GetResourceAmount(player, RESOURCE_HEALTH) <= 0 ) { player.ons_spawn_by = closest_target; player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; @@ -2053,14 +2026,14 @@ MUTATOR_HOOKFUNCTION(ons, SendWaypoint) { entity wp_owner = wp.owner; entity e = WaypointSprite_getviewentity(to); - if(SAME_TEAM(e, wp_owner) && wp_owner.goalentity.health >= wp_owner.goalentity.max_health) { wp_flag |= 2; } + if(SAME_TEAM(e, wp_owner) && GetResourceAmount(wp_owner.goalentity, RESOURCE_HEALTH) >= wp_owner.goalentity.max_health) { wp_flag |= 2; } if(!ons_ControlPoint_Attackable(wp_owner, e.team)) { wp_flag |= 2; } } if(wp.owner.classname == "onslaught_generator") { entity wp_owner = wp.owner; - if(wp_owner.isshielded && wp_owner.health >= wp_owner.max_health) { wp_flag |= 2; } - if(wp_owner.health <= 0) { wp_flag |= 2; } + if(wp_owner.isshielded && GetResourceAmount(wp_owner, RESOURCE_HEALTH) >= wp_owner.max_health) { wp_flag |= 2; } + if(GetResourceAmount(wp_owner, RESOURCE_HEALTH) <= 0) { wp_flag |= 2; } } } @@ -2162,12 +2135,9 @@ spawnfunc(onslaught_generator) // scoreboard setup void ons_ScoreRules() { - CheckAllowedTeams(NULL); - int teams = 0; - if(c1 >= 0) teams |= BIT(0); - if(c2 >= 0) teams |= BIT(1); - if(c3 >= 0) teams |= BIT(2); - if(c4 >= 0) teams |= BIT(3); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + int teams = TeamBalance_GetAllowedTeams(balance); + TeamBalance_Destroy(balance); GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { field_team(ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); field(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh index 5c7fd46973..7a9b192a5a 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh @@ -80,7 +80,6 @@ const int HAVOCBOT_ONS_ROLE_OFFENSE = 8; .entity havocbot_ons_target; -.int havocbot_role_flags; .float havocbot_attack_time; void havocbot_role_ons_defense(entity this); diff --git a/qcsrc/common/gamemodes/gamemode/race/_mod.inc b/qcsrc/common/gamemodes/gamemode/race/_mod.inc new file mode 100644 index 0000000000..5ed2c95981 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/race/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/race/sv_race.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/race/_mod.qh b/qcsrc/common/gamemodes/gamemode/race/_mod.qh new file mode 100644 index 0000000000..1e76e7af30 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/race/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/race/sv_race.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/race/sv_race.qc b/qcsrc/common/gamemodes/gamemode/race/sv_race.qc new file mode 100644 index 0000000000..31309f285d --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/race/sv_race.qc @@ -0,0 +1,490 @@ +#include "sv_race.qh" + +#include <server/race.qh> + +#define autocvar_g_race_laps_limit cvar("g_race_laps_limit") +float autocvar_g_race_qualifying_timelimit; +float autocvar_g_race_qualifying_timelimit_override; +int autocvar_g_race_teams; + +// legacy bot roles +.float race_checkpoint; +void havocbot_role_race(entity this) +{ + if(IS_DEAD(this)) + return; + + if (navigation_goalrating_timeout(this)) + { + navigation_goalrating_start(this); + + bool raw_touch_check = true; + int cp = this.race_checkpoint; + + LABEL(search_racecheckpoints) + IL_EACH(g_racecheckpoints, true, + { + if(it.cnt == cp || cp == -1) + { + // redirect bot to next goal if it touched the waypoint of an untouchable checkpoint + // e.g. checkpoint in front of Stormkeep's warpzone + // the same workaround is applied in CTS game mode + if (raw_touch_check && vdist(this.origin - it.nearestwaypoint.origin, <, 30)) + { + cp = race_NextCheckpoint(cp); + raw_touch_check = false; + goto search_racecheckpoints; + } + navigation_routerating(this, it, 1000000, 5000); + } + }); + + navigation_goalrating_end(this); + + navigation_goalrating_timeout_set(this); + } +} + +void race_ScoreRules() +{ + GameRules_score_enabled(false); + GameRules_scoring(race_teams, 0, 0, { + if (race_teams) { + field_team(ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } else if (g_race_qualifying) { + field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + } else { + field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + } + }); +} + +void race_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later +{ + if(autocvar_sv_eventlog) + GameLogEcho(strcat(":race:", mode, ":", ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); +} + +float WinningCondition_Race(float fraglimit) +{ + float wc; + float n, c; + + n = 0; + c = 0; + FOREACH_CLIENT(IS_PLAYER(it), { + ++n; + if(CS(it).race_completed) + ++c; + }); + if(n && (n == c)) + return WINNING_YES; + wc = WinningCondition_Scores(fraglimit, 0); + + // ALWAYS initiate overtime, unless EVERYONE has finished the race! + if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME) + // do NOT support equality when the laps are all raced! + return WINNING_STARTSUDDENDEATHOVERTIME; + else + return WINNING_NEVER; +} + +float WinningCondition_QualifyingThenRace(float limit) +{ + float wc; + wc = WinningCondition_Scores(limit, 0); + + // NEVER initiate overtime + if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME) + { + return WINNING_YES; + } + + return wc; +} + +MUTATOR_HOOKFUNCTION(rc, ClientKill) +{ + if(g_race_qualifying) + M_ARGV(1, float) = 0; // killtime +} + +MUTATOR_HOOKFUNCTION(rc, AbortSpeedrun) +{ + entity player = M_ARGV(0, entity); + + if(autocvar_g_allow_checkpoints) + race_PreparePlayer(player); // nice try +} + +MUTATOR_HOOKFUNCTION(rc, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + float dt = M_ARGV(1, float); + + player.race_movetime_frac += dt; + float f = floor(player.race_movetime_frac); + player.race_movetime_frac -= f; + player.race_movetime_count += f; + player.race_movetime = player.race_movetime_frac + player.race_movetime_count; + +#ifdef SVQC + if(IS_PLAYER(player)) + { + if (player.race_penalty) + if (time > player.race_penalty) + player.race_penalty = 0; + if(player.race_penalty) + { + player.velocity = '0 0 0'; + set_movetype(player, MOVETYPE_NONE); + player.disableclientprediction = 2; + } + } +#endif + + // force kbd movement for fairness + float wishspeed; + vector wishvel; + + // if record times matter + // ensure nothing EVIL is being done (i.e. div0_evade) + // this hinders joystick users though + // but it still gives SOME analog control + wishvel.x = fabs(CS(player).movement.x); + wishvel.y = fabs(CS(player).movement.y); + if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y) + { + wishvel.z = 0; + wishspeed = vlen(wishvel); + if(wishvel.x >= 2 * wishvel.y) + { + // pure X motion + if(CS(player).movement.x > 0) + CS(player).movement_x = wishspeed; + else + CS(player).movement_x = -wishspeed; + CS(player).movement_y = 0; + } + else if(wishvel.y >= 2 * wishvel.x) + { + // pure Y motion + CS(player).movement_x = 0; + if(CS(player).movement.y > 0) + CS(player).movement_y = wishspeed; + else + CS(player).movement_y = -wishspeed; + } + else + { + // diagonal + if(CS(player).movement.x > 0) + CS(player).movement_x = M_SQRT1_2 * wishspeed; + else + CS(player).movement_x = -M_SQRT1_2 * wishspeed; + if(CS(player).movement.y > 0) + CS(player).movement_y = M_SQRT1_2 * wishspeed; + else + CS(player).movement_y = -M_SQRT1_2 * wishspeed; + } + } +} + +MUTATOR_HOOKFUNCTION(rc, reset_map_global) +{ + float s; + + Score_NicePrint(NULL); + + race_ClearRecords(); + PlayerScore_Sort(race_place, 0, 1, 0); + + FOREACH_CLIENT(true, { + if(it.race_place) + { + s = GameRules_scoring_add(it, RACE_FASTEST, 0); + if(!s) + it.race_place = 0; + } + race_EventLog(ftos(it.race_place), it); + }); + + if(g_race_qualifying == 2) + { + g_race_qualifying = 0; + independent_players = 0; + cvar_set("fraglimit", ftos(race_fraglimit)); + cvar_set("leadlimit", ftos(race_leadlimit)); + cvar_set("timelimit", ftos(race_timelimit)); + race_ScoreRules(); + } +} + +MUTATOR_HOOKFUNCTION(rc, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + race_PreparePlayer(player); + player.race_checkpoint = -1; + + string rr = RACE_RECORD; + + if(IS_REAL_CLIENT(player)) + { + msg_entity = player; + race_send_recordtime(MSG_ONE); + race_send_speedaward(MSG_ONE); + + speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); + speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); + race_send_speedaward_alltimebest(MSG_ONE); + + float i; + int m = min(RANKINGS_CNT, autocvar_g_cts_send_rankings_cnt); + race_send_rankings_cnt(MSG_ONE); + for (i = 1; i <= m; ++i) + { + race_SendRankings(i, 0, 0, MSG_ONE); + } + } +} + +MUTATOR_HOOKFUNCTION(rc, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + if(g_race_qualifying) + if(GameRules_scoring_add(player, RACE_FASTEST, 0)) + player.frags = FRAGS_LMS_LOSER; + else + player.frags = FRAGS_SPECTATOR; + + race_PreparePlayer(player); + player.race_checkpoint = -1; +} + +MUTATOR_HOOKFUNCTION(rc, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + + if(spawn_spot.target == "") + // Emergency: this wasn't a real spawnpoint. Can this ever happen? + race_PreparePlayer(player); + + // if we need to respawn, do it right + player.race_respawn_checkpoint = player.race_checkpoint; + player.race_respawn_spotref = spawn_spot; + + player.race_place = 0; +} + +MUTATOR_HOOKFUNCTION(rc, PutClientInServer) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + if(!game_stopped) + { + if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn + race_PreparePlayer(player); + else // respawn + race_RetractPlayer(player); + + race_AbandonRaceCheck(player); + } +} + +MUTATOR_HOOKFUNCTION(rc, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.respawn_flags |= RESPAWN_FORCE; + race_AbandonRaceCheck(frag_target); +} + +MUTATOR_HOOKFUNCTION(rc, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + bot.havocbot_role = havocbot_role_race; + return true; +} + +MUTATOR_HOOKFUNCTION(rc, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) + { + if (!player.stored_netname) + player.stored_netname = strzone(uid2name(player.crypto_idfp)); + if(player.stored_netname != player.netname) + { + db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); + strcpy(player.stored_netname, player.netname); + } + } + + if (!IS_OBSERVER(player)) + { + if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed)) + { + speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1'); + speedaward_holder = player.netname; + speedaward_uid = player.crypto_idfp; + speedaward_lastupdate = time; + } + if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) + { + string rr = RACE_RECORD; + race_send_speedaward(MSG_ALL); + speedaward_lastsent = speedaward_speed; + if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") + { + speedaward_alltimebest = speedaward_speed; + speedaward_alltimebest_holder = speedaward_holder; + speedaward_alltimebest_uid = speedaward_uid; + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest)); + db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid); + race_send_speedaward_alltimebest(MSG_ALL); + } + } + } +} + +MUTATOR_HOOKFUNCTION(rc, ForbidPlayerScore_Clear) +{ + if(g_race_qualifying) + return true; // in qualifying, you don't lose score by observing +} + +MUTATOR_HOOKFUNCTION(rc, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(0, float) = race_teams; + return true; +} + +MUTATOR_HOOKFUNCTION(rc, Scores_CountFragsRemaining) +{ + // announce remaining frags if not in qualifying mode + if(!g_race_qualifying) + return true; +} + +MUTATOR_HOOKFUNCTION(rc, GetRecords) +{ + int record_page = M_ARGV(0, int); + string ret_string = M_ARGV(1, string); + + for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) + { + if(MapInfo_Get_ByID(i)) + { + float r = race_readTime(MapInfo_Map_bspname, 1); + + if(!r) + continue; + + string h = race_readName(MapInfo_Map_bspname, 1); + ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n"); + } + } + + M_ARGV(1, string) = ret_string; +} + +MUTATOR_HOOKFUNCTION(rc, HideTeamNagger) +{ + return true; // doesn't work so well +} + +MUTATOR_HOOKFUNCTION(rc, FixClientCvars) +{ + entity player = M_ARGV(0, entity); + + stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n"); +} + +MUTATOR_HOOKFUNCTION(rc, CheckRules_World) +{ + float checkrules_timelimit = M_ARGV(1, float); + float checkrules_fraglimit = M_ARGV(2, float); + + if(checkrules_timelimit >= 0) + { + if(!g_race_qualifying) + { + M_ARGV(0, float) = WinningCondition_Race(checkrules_fraglimit); + return true; + } + else if(g_race_qualifying == 2) + { + M_ARGV(0, float) = WinningCondition_QualifyingThenRace(checkrules_fraglimit); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(rc, ReadLevelCvars) +{ + if(g_race_qualifying == 2) + warmup_stage = 0; +} + +void race_Initialize() +{ + race_ScoreRules(); + if(g_race_qualifying == 2) + warmup_stage = 0; +} + +void rc_SetLimits() +{ + int fraglimit_override, leadlimit_override; + float timelimit_override, qualifying_override; + + if(autocvar_g_race_teams) + { + GameRules_teams(true); + race_teams = BITS(bound(2, autocvar_g_race_teams, 4)); + } + else + race_teams = 0; + + qualifying_override = autocvar_g_race_qualifying_timelimit_override; + fraglimit_override = autocvar_g_race_laps_limit; + leadlimit_override = 0; // currently not supported by race + timelimit_override = autocvar_timelimit_override; + + float want_qualifying = ((qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit) > 0; + + if(autocvar_g_campaign) + { + g_race_qualifying = 1; + independent_players = 1; + } + else if(want_qualifying) + { + g_race_qualifying = 2; + independent_players = 1; + race_fraglimit = (fraglimit_override >= 0) ? fraglimit_override : autocvar_fraglimit; + race_leadlimit = (leadlimit_override >= 0) ? leadlimit_override : autocvar_leadlimit; + race_timelimit = (timelimit_override >= 0) ? timelimit_override : autocvar_timelimit; + qualifying_override = (qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit; + fraglimit_override = 0; + leadlimit_override = 0; + timelimit_override = qualifying_override; + } + else + g_race_qualifying = 0; + GameRules_limit_score(fraglimit_override); + GameRules_limit_lead(leadlimit_override); + GameRules_limit_time(timelimit_override); + GameRules_limit_time_qualifying(qualifying_override); +} diff --git a/qcsrc/common/gamemodes/gamemode/race/sv_race.qh b/qcsrc/common/gamemodes/gamemode/race/sv_race.qh new file mode 100644 index 0000000000..9928b8cb75 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/race/sv_race.qh @@ -0,0 +1,17 @@ +#pragma once + +#include <common/mutators/base.qh> +void rc_SetLimits(); +void race_Initialize(); + +REGISTER_MUTATOR(rc, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + rc_SetLimits(); + + race_Initialize(); + } + return 0; +} diff --git a/qcsrc/common/gamemodes/gamemode/tdm/_mod.inc b/qcsrc/common/gamemodes/gamemode/tdm/_mod.inc new file mode 100644 index 0000000000..5c0e949a8a --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tdm/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/tdm/sv_tdm.qc> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/tdm/_mod.qh b/qcsrc/common/gamemodes/gamemode/tdm/_mod.qh new file mode 100644 index 0000000000..5be8ea6e6f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tdm/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include <common/gamemodes/gamemode/tdm/sv_tdm.qh> +#endif diff --git a/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc new file mode 100644 index 0000000000..43a9938447 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qc @@ -0,0 +1,63 @@ +#include "sv_tdm.qh" + +// TODO? rename to teamdeathmatch +int autocvar_g_tdm_teams; +int autocvar_g_tdm_teams_override; + +/*QUAKED spawnfunc_tdm_team (0 .5 .8) (-16 -16 -24) (16 16 32) +Team declaration for TDM gameplay, this allows you to decide what team names and control point models are used in your map. +Note: If you use spawnfunc_tdm_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too. +Keys: +"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)... +"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ +spawnfunc(tdm_team) +{ + if(!g_tdm || !this.cnt) { delete(this); return; } + + this.classname = "tdm_team"; + this.team = this.cnt + 1; +} + +// code from here on is just to support maps that don't have team entities +void tdm_SpawnTeam (string teamname, int teamcolor) +{ + entity this = new_pure(tdm_team); + this.netname = teamname; + this.cnt = teamcolor - 1; + this.team = teamcolor; + this.spawnfunc_checked = true; + //spawnfunc_tdm_team(this); +} + +void tdm_DelayedInit(entity this) +{ + // if no teams are found, spawn defaults + if(find(NULL, classname, "tdm_team") == NULL) + { + LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway."); + + int numteams = autocvar_g_tdm_teams_override; + if(numteams < 2) { numteams = autocvar_g_tdm_teams; } + + int teams = BITS(bound(2, numteams, 4)); + if(teams & BIT(0)) + tdm_SpawnTeam("Red", NUM_TEAM_1); + if(teams & BIT(1)) + tdm_SpawnTeam("Blue", NUM_TEAM_2); + if(teams & BIT(2)) + tdm_SpawnTeam("Yellow", NUM_TEAM_3); + if(teams & BIT(3)) + tdm_SpawnTeam("Pink", NUM_TEAM_4); + } +} + +MUTATOR_HOOKFUNCTION(tdm, TeamBalance_CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) +{ + M_ARGV(1, string) = "tdm_team"; +} + +MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining) +{ + // announce remaining frags + return true; +} diff --git a/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qh b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qh new file mode 100644 index 0000000000..adc6a3d6c0 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/tdm/sv_tdm.qh @@ -0,0 +1,22 @@ +#pragma once + +#include <common/mutators/base.qh> +int autocvar_g_tdm_point_limit; +int autocvar_g_tdm_point_leadlimit; +bool autocvar_g_tdm_team_spawns; +void tdm_DelayedInit(entity this); + +REGISTER_MUTATOR(tdm, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + GameRules_teams(true); + GameRules_spawning_teams(autocvar_g_tdm_team_spawns); + GameRules_limit_score(autocvar_g_tdm_point_limit); + GameRules_limit_lead(autocvar_g_tdm_point_leadlimit); + + InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); + } + return 0; +} diff --git a/qcsrc/common/gamemodes/sv_rules.qh b/qcsrc/common/gamemodes/sv_rules.qh index 35a643d53b..f8950684b4 100644 --- a/qcsrc/common/gamemodes/sv_rules.qh +++ b/qcsrc/common/gamemodes/sv_rules.qh @@ -1,5 +1,8 @@ #pragma once +// TODO: find a better location for these? +int total_players; + // todo: accept the number of teams as a parameter void GameRules_teams(bool value); diff --git a/qcsrc/common/impulses/all.qh b/qcsrc/common/impulses/all.qh index 45a8f1323b..8bd0c41dab 100644 --- a/qcsrc/common/impulses/all.qh +++ b/qcsrc/common/impulses/all.qh @@ -186,20 +186,6 @@ LEGACY_IMPULSE(g_waypointsprite_clear_personal, 47, "waypoint_clear_personal") REGISTER_IMPULSE(waypoint_clear, 48) LEGACY_IMPULSE(g_waypointsprite_clear, 48, "waypoint_clear") -REGISTER_IMPULSE(navwaypoint_spawn, 103) -LEGACY_IMPULSE(g_waypointeditor_spawn, 103, "navwaypoint_spawn") - -REGISTER_IMPULSE(navwaypoint_remove, 104) -LEGACY_IMPULSE(g_waypointeditor_remove, 104, "navwaypoint_remove") - -REGISTER_IMPULSE(navwaypoint_relink, 105) -LEGACY_IMPULSE(g_waypointeditor_relinkall, 105, "navwaypoint_relink") - -REGISTER_IMPULSE(navwaypoint_save, 106) -LEGACY_IMPULSE(g_waypointeditor_saveall, 106, "navwaypoint_save") - -REGISTER_IMPULSE(navwaypoint_unreachable, 107) -LEGACY_IMPULSE(g_waypointeditor_unreachable, 107, "navwaypoint_unreachable") #define CHIMPULSE(id, n) _CHIMPULSE(CHIMPULSE_##id, n) #define _CHIMPULSE(id, n) \ diff --git a/qcsrc/common/items/inventory.qh b/qcsrc/common/items/inventory.qh index 8520075019..2ec0cdb7b1 100644 --- a/qcsrc/common/items/inventory.qh +++ b/qcsrc/common/items/inventory.qh @@ -21,9 +21,11 @@ const int Inventory_groups_minor = 8; // ceil(Items_MAX / Inventory_groups_major #define G_MINOR(id) ((id) % Inventory_groups_minor) #ifdef CSQC +Inventory g_inventory; NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew) { make_pure(this); + g_inventory = this; const int majorBits = ReadShort(); for (int i = 0; i < Inventory_groups_major; ++i) { if (!(majorBits & BIT(i))) { @@ -104,20 +106,23 @@ bool Inventory_Send(Inventory this, Client to, int sf) { TC(Inventory, this); WriteHeader(MSG_ENTITY, ENT_CLIENT_INVENTORY); - entity e = this.owner; - if (IS_SPEC(e)) e = e.enemy; - TC(Player, e); - Inventory data = e.inventory; - Inventory_Write(data); + TC(PlayerState, this.owner); + Inventory_Write(this); return true; } -void Inventory_new(entity e) +bool Inventory_customize(entity this, entity client) +{ + // sends to spectators too! + return (PS(client) && PS(client).inventory == this); +} + +void Inventory_new(PlayerState this) { Inventory inv = NEW(Inventory), bak = NEW(Inventory); inv.inventory = bak; - inv.drawonlytoclient = e; - Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send); + setcefc(inv, Inventory_customize); + Net_LinkEntity((inv.owner = this).inventory = inv, false, 0, Inventory_Send); } void Inventory_delete(entity e) { delete(e.inventory.inventory); delete(e.inventory); } void Inventory_update(entity e) { e.inventory.SendFlags = 0xFFFFFF; } diff --git a/qcsrc/common/items/item.qh b/qcsrc/common/items/item.qh index b7fc933e8b..030b4db1c0 100644 --- a/qcsrc/common/items/item.qh +++ b/qcsrc/common/items/item.qh @@ -7,6 +7,10 @@ #include <common/stats.qh> #endif +#ifdef SVQC +#include <server/items.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. const int IT_UNLIMITED_SUPERWEAPONS = BIT(1); // when this bit is set, superweapons don't expire. Checkpoints can give this powerup. @@ -47,8 +51,27 @@ const int IT_PICKUPMASK = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FU .float strength_finished = _STAT(STRENGTH_FINISHED); .float invincible_finished = _STAT(INVINCIBLE_FINISHED); +#define spawnfunc_body(item) \ + if (!Item_IsDefinitionAllowed(item)) \ + { \ + startitem_failed = true; \ + delete(this); \ + return; \ + } \ + StartItem(this, item) + #define SPAWNFUNC_ITEM(name, item) \ - spawnfunc(name) { StartItem(this, item); } + spawnfunc(name) \ + { \ + spawnfunc_body(item); \ + } + +#define SPAWNFUNC_ITEM_COND(name, cond, item1, item2) \ + spawnfunc(name) \ + { \ + entity item = (cond) ? item1 : item2; \ + spawnfunc_body(item); \ + } #else @@ -59,9 +82,8 @@ const int IT_PICKUPMASK = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FU 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) + ITEM_FLAG_MUTATORBLOCKED = BIT(1), + ITEM_FLAG_RESOURCE = BIT(2) ///< Item is is a resource, not a held item. }; #define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__) diff --git a/qcsrc/common/items/item/ammo.qh b/qcsrc/common/items/item/ammo.qh index 1d5bd87bac..3249f07bca 100644 --- a/qcsrc/common/items/item/ammo.qh +++ b/qcsrc/common/items/item/ammo.qh @@ -1,10 +1,13 @@ #pragma once #include "pickup.qh" +#include <common/items/all.qh> #ifdef SVQC #include <common/t_items.qh> + #include <server/resources.qh> #endif +#if 1 .int ammo_none; .int ammo_shells; .int ammo_nails; @@ -17,6 +20,11 @@ .int ammo_plasma; .int ammo_fuel; #endif +#endif + +#ifdef GAMEQC +.int spawnflags; +#endif #ifdef SVQC PROPERTY(float, g_pickup_ammo_anyway); @@ -38,10 +46,10 @@ MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); #ifdef SVQC PROPERTY(int, g_pickup_nails); -void ammo_bullets_init(entity item) +void ammo_bullets_init(Pickup this, entity item) { - if(!item.ammo_nails) - item.ammo_nails = g_pickup_nails; + if(!GetResourceAmount(item, RESOURCE_BULLETS)) + SetResourceAmountExplicit(item, RESOURCE_BULLETS, g_pickup_nails); } #endif @@ -51,11 +59,11 @@ ENDCLASS(Bullets) REGISTER_ITEM(Bullets, Bullets) { this.m_canonical_spawnfunc = "item_bullets"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Bullets_ITEM; #endif this.netname = "bullets"; - this.m_name = "bullets"; + this.m_name = _("bullets"); this.m_icon = "ammo_bullets"; #ifdef SVQC this.m_botvalue = 1500; @@ -72,20 +80,20 @@ MODEL(Cells_ITEM, Item_Model("a_cells.md3")); #ifdef SVQC PROPERTY(int, g_pickup_cells); -void ammo_cells_init(entity item) +void ammo_cells_init(Pickup this, entity item) { - if(!item.ammo_cells) - item.ammo_cells = g_pickup_cells; + if(!GetResourceAmount(item, RESOURCE_CELLS)) + SetResourceAmountExplicit(item, RESOURCE_CELLS, g_pickup_cells); } #endif REGISTER_ITEM(Cells, Ammo) { this.m_canonical_spawnfunc = "item_cells"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Cells_ITEM; #endif this.netname = "cells"; - this.m_name = "cells"; + this.m_name = _("cells"); this.m_icon = "ammo_cells"; #ifdef SVQC this.m_botvalue = 1500; @@ -102,20 +110,20 @@ MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); #ifdef SVQC PROPERTY(int, g_pickup_plasma); -void ammo_plasma_init(entity item) +void ammo_plasma_init(Pickup this, entity item) { - if(!item.ammo_plasma) - item.ammo_plasma = g_pickup_plasma; + if(!GetResourceAmount(item, RESOURCE_PLASMA)) + SetResourceAmountExplicit(item, RESOURCE_PLASMA, g_pickup_plasma); } #endif REGISTER_ITEM(Plasma, Ammo) { this.m_canonical_spawnfunc = "item_plasma"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Plasma_ITEM; #endif this.netname = "plasma"; - this.m_name = "plasma"; + this.m_name = _("plasma"); this.m_icon = "ammo_plasma"; #ifdef SVQC this.m_botvalue = 1500; @@ -132,20 +140,20 @@ MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); #ifdef SVQC PROPERTY(int, g_pickup_rockets); -void ammo_rockets_init(entity item) +void ammo_rockets_init(Pickup this, entity item) { - if(!item.ammo_rockets) - item.ammo_rockets = g_pickup_rockets; + if(!GetResourceAmount(item, RESOURCE_ROCKETS)) + SetResourceAmountExplicit(item, RESOURCE_ROCKETS, g_pickup_rockets); } #endif REGISTER_ITEM(Rockets, Ammo) { this.m_canonical_spawnfunc = "item_rockets"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Rockets_ITEM; #endif this.netname = "rockets"; - this.m_name = "rockets"; + this.m_name = _("rockets"); this.m_icon = "ammo_rockets"; #ifdef SVQC this.m_botvalue = 1500; @@ -162,10 +170,10 @@ MODEL(Shells_ITEM, Item_Model("a_shells.md3")); #ifdef SVQC PROPERTY(int, g_pickup_shells); -void ammo_shells_init(entity item) +void ammo_shells_init(Pickup this, entity item) { - if(!item.ammo_shells) - item.ammo_shells = g_pickup_shells; + if(!GetResourceAmount(item, RESOURCE_SHELLS)) + SetResourceAmountExplicit(item, RESOURCE_SHELLS, g_pickup_shells); } #endif @@ -175,11 +183,11 @@ ENDCLASS(Shells) REGISTER_ITEM(Shells, Shells) { this.m_canonical_spawnfunc = "item_shells"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_Shells_ITEM; #endif this.netname = "shells"; - this.m_name = "shells"; + this.m_name = _("shells"); this.m_icon = "ammo_shells"; #ifdef SVQC this.m_botvalue = 1000; diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index 7f37c75aec..2ecd835571 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -22,24 +22,24 @@ SOUND(ArmorSmall, Item_Sound("armor1")); PROPERTY(float, g_pickup_armorsmall_anyway); PROPERTY(int, g_pickup_armorsmall); PROPERTY(int, g_pickup_armorsmall_max); -void item_armorsmall_init(entity item) +void item_armorsmall_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armorsmall_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armorsmall; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armorsmall); } #endif REGISTER_ITEM(ArmorSmall, Armor) { this.m_canonical_spawnfunc = "item_armor_small"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorSmall_ITEM; this.m_sound = SND_ArmorSmall; #endif this.netname = "armor_small"; - this.m_name = "5 Armor"; + this.m_name = _("Small armor"); this.m_icon = "armor"; #ifdef SVQC this.m_itemid = IT_ARMOR_SHARD; @@ -60,24 +60,24 @@ SOUND(ArmorMedium, Item_Sound("armor10")); PROPERTY(float, g_pickup_armormedium_anyway); PROPERTY(int, g_pickup_armormedium); PROPERTY(int, g_pickup_armormedium_max); -void item_armormedium_init(entity item) +void item_armormedium_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armormedium_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armormedium; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armormedium); } #endif REGISTER_ITEM(ArmorMedium, Armor) { this.m_canonical_spawnfunc = "item_armor_medium"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorMedium_ITEM; this.m_sound = SND_ArmorMedium; #endif this.netname = "armor_medium"; - this.m_name = "25 Armor"; + this.m_name = _("Medium armor"); this.m_icon = "armor"; #ifdef SVQC this.m_itemid = IT_ARMOR; @@ -98,24 +98,24 @@ SOUND(ArmorBig, Item_Sound("armor17_5")); PROPERTY(float, g_pickup_armorbig_anyway); PROPERTY(int, g_pickup_armorbig); PROPERTY(int, g_pickup_armorbig_max); -void item_armorbig_init(entity item) +void item_armorbig_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armorbig_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armorbig; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armorbig); } #endif REGISTER_ITEM(ArmorBig, Armor) { this.m_canonical_spawnfunc = "item_armor_big"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorBig_ITEM; this.m_sound = SND_ArmorBig; #endif this.netname = "armor_big"; - this.m_name = "50 Armor"; + this.m_name = _("Big armor"); this.m_icon = "armor"; this.m_color = '0 1 0'; this.m_waypoint = _("Big armor"); @@ -138,24 +138,24 @@ SOUND(ArmorMega, Item_Sound("armor25")); PROPERTY(float, g_pickup_armormega_anyway); PROPERTY(int, g_pickup_armormega); PROPERTY(int, g_pickup_armormega_max); -void item_armormega_init(entity item) +void item_armormega_init(Pickup this, entity item) { if(!item.max_armorvalue) item.max_armorvalue = g_pickup_armormega_max; - if(!item.armorvalue) - item.armorvalue = g_pickup_armormega; + if(!GetResourceAmount(item, RESOURCE_ARMOR)) + SetResourceAmountExplicit(item, RESOURCE_ARMOR, g_pickup_armormega); } #endif REGISTER_ITEM(ArmorMega, Armor) { this.m_canonical_spawnfunc = "item_armor_mega"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_ArmorMega_ITEM; this.m_sound = SND_ArmorMega; #endif this.netname = "armor_mega"; - this.m_name = "100 Armor"; + this.m_name = _("Mega armor"); this.m_icon = "item_large_armor"; this.m_color = '0 1 0'; this.m_waypoint = _("Mega armor"); diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index da431086e1..e6a9dd9883 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -22,24 +22,24 @@ SOUND(HealthSmall, Item_Sound("minihealth")); PROPERTY(float, g_pickup_healthsmall_anyway); PROPERTY(int, g_pickup_healthsmall); PROPERTY(int, g_pickup_healthsmall_max); -void item_healthsmall_init(entity item) +void item_healthsmall_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthsmall_max; - if(!item.health) - item.health = g_pickup_healthsmall; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthsmall); } #endif REGISTER_ITEM(HealthSmall, Health) { this.m_canonical_spawnfunc = "item_health_small"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthSmall_ITEM; this.m_sound = SND_HealthSmall; #endif this.netname = "health_small"; - this.m_name = "5 Health"; + this.m_name = _("Small health"); this.m_icon = "health"; #ifdef SVQC this.m_itemid = IT_5HP; @@ -60,24 +60,24 @@ SOUND(HealthMedium, Item_Sound("mediumhealth")); PROPERTY(float, g_pickup_healthmedium_anyway); PROPERTY(int, g_pickup_healthmedium); PROPERTY(int, g_pickup_healthmedium_max); -void item_healthmedium_init(entity item) +void item_healthmedium_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthmedium_max; - if(!item.health) - item.health = g_pickup_healthmedium; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthmedium); } #endif REGISTER_ITEM(HealthMedium, Health) { this.m_canonical_spawnfunc = "item_health_medium"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthMedium_ITEM; this.m_sound = SND_HealthMedium; #endif this.netname = "health_medium"; - this.m_name = "25 Health"; + this.m_name = _("Medium health"); this.m_icon = "health"; #ifdef SVQC this.m_itemid = IT_25HP; @@ -98,24 +98,24 @@ SOUND(HealthBig, Item_Sound("mediumhealth")); PROPERTY(float, g_pickup_healthbig_anyway); PROPERTY(int, g_pickup_healthbig); PROPERTY(int, g_pickup_healthbig_max); -void item_healthbig_init(entity item) +void item_healthbig_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthbig_max; - if(!item.health) - item.health = g_pickup_healthbig; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthbig); } #endif REGISTER_ITEM(HealthBig, Health) { this.m_canonical_spawnfunc = "item_health_big"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthBig_ITEM; this.m_sound = SND_HealthBig; #endif this.netname = "health_big"; - this.m_name = "50 Health"; + this.m_name = _("Big health"); this.m_icon = "health"; this.m_color = '1 0 0'; this.m_waypoint = _("Big health"); @@ -138,24 +138,24 @@ SOUND(HealthMega, Item_Sound("megahealth")); PROPERTY(float, g_pickup_healthmega_anyway); PROPERTY(int, g_pickup_healthmega); PROPERTY(int, g_pickup_healthmega_max); -void item_healthmega_init(entity item) +void item_healthmega_init(Pickup this, entity item) { if(!item.max_health) item.max_health = g_pickup_healthmega_max; - if(!item.health) - item.health = g_pickup_healthmega; + if(!GetResourceAmount(item, RESOURCE_HEALTH)) + SetResourceAmountExplicit(item, RESOURCE_HEALTH, g_pickup_healthmega); } #endif REGISTER_ITEM(HealthMega, Health) { this.m_canonical_spawnfunc = "item_health_mega"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_OVERKILL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_HealthMega_ITEM; this.m_sound = SND_HealthMega; #endif this.netname = "health_mega"; - this.m_name = "100 Health"; + this.m_name = _("Mega health"); this.m_icon = "item_mega_health"; this.m_color = '1 0 0'; this.m_waypoint = _("Mega health"); diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh index 284bf3d390..24d040d56b 100644 --- a/qcsrc/common/items/item/jetpack.qh +++ b/qcsrc/common/items/item/jetpack.qh @@ -17,10 +17,10 @@ MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); #ifdef SVQC PROPERTY(int, g_pickup_fuel_jetpack); -void powerup_jetpack_init(entity item) +void powerup_jetpack_init(Pickup this, entity item) { - if(!item.ammo_fuel) - item.ammo_fuel = g_pickup_fuel_jetpack; + if(!GetResourceAmount(item, RESOURCE_FUEL)) + SetResourceAmountExplicit(item, RESOURCE_FUEL, g_pickup_fuel_jetpack); } #endif @@ -35,10 +35,10 @@ REGISTER_ITEM(Jetpack, Powerup) { this.m_itemid = IT_JETPACK; #endif this.netname = "jetpack"; - this.m_name = "Jet pack"; + this.m_name = _("Jetpack"); this.m_icon = "jetpack"; this.m_color = '0.5 0.5 0.5'; - this.m_waypoint = _("Jet Pack"); + this.m_waypoint = _("Jetpack"); this.m_waypointblink = 2; #ifdef SVQC this.m_botvalue = 3000; @@ -55,20 +55,20 @@ MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); #ifdef SVQC PROPERTY(int, g_pickup_fuel); -void ammo_fuel_init(entity item) +void ammo_fuel_init(Pickup this, entity item) { - if(!item.ammo_fuel) - item.ammo_fuel = g_pickup_fuel; + if(!GetResourceAmount(item, RESOURCE_FUEL)) + SetResourceAmountExplicit(item, RESOURCE_FUEL, g_pickup_fuel); } #endif REGISTER_ITEM(JetpackFuel, Ammo) { this.m_canonical_spawnfunc = "item_fuel"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_NORMAL; + this.spawnflags = ITEM_FLAG_NORMAL | ITEM_FLAG_RESOURCE; this.m_model = MDL_JetpackFuel_ITEM; #endif this.netname = "fuel"; - this.m_name = "Fuel"; + this.m_name = _("fuel"); this.m_icon = "ammo_fuel"; #ifdef SVQC this.m_botvalue = 2000; @@ -93,7 +93,7 @@ REGISTER_ITEM(JetpackRegen, JetpackRegen) { this.m_model = MDL_JetpackRegen_ITEM; #endif this.netname = "fuel_regen"; - this.m_name = "Fuel regenerator"; + this.m_name = _("Fuel regenerator"); this.m_icon = "fuelregen"; this.m_color = '1 0.5 0'; this.m_waypoint = _("Fuel regen"); diff --git a/qcsrc/common/items/item/pickup.qc b/qcsrc/common/items/item/pickup.qc index b5944fc0a3..a3c2d779ed 100644 --- a/qcsrc/common/items/item/pickup.qc +++ b/qcsrc/common/items/item/pickup.qc @@ -11,9 +11,10 @@ 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); + //LOG_DEBUGF("entity %i picked up %s", player, this.m_name); + entity store = IS_PLAYER(player) ? PS(player) : player; + store.inventory.inv_items[this.m_id]++; + Inventory_update(store); } return b; } diff --git a/qcsrc/common/items/item/pickup.qh b/qcsrc/common/items/item/pickup.qh index fb4bc28cd8..0f09901af2 100644 --- a/qcsrc/common/items/item/pickup.qh +++ b/qcsrc/common/items/item/pickup.qh @@ -42,7 +42,7 @@ CLASS(Pickup, GameItem) ATTRIB(Pickup, m_respawntime, float()); ATTRIB(Pickup, m_respawntimejitter, float()); ATTRIB(Pickup, m_pickupanyway, float()); - ATTRIB(Pickup, m_iteminit, void(entity item)); + ATTRIB(Pickup, m_iteminit, void(Pickup this, entity item)); float Item_GiveTo(entity item, entity player); METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player)); bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player); diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh index fe47b63430..43414b3d06 100644 --- a/qcsrc/common/items/item/powerup.qh +++ b/qcsrc/common/items/item/powerup.qh @@ -10,7 +10,7 @@ CLASS(Powerup, Pickup) #ifdef SVQC ATTRIB(Powerup, m_mins, vector, '-16 -16 0'); ATTRIB(Powerup, m_maxs, vector, '16 16 80'); - ATTRIB(Powerup, m_botvalue, int, 20000); + ATTRIB(Powerup, m_botvalue, int, 11000); ATTRIB(Powerup, m_itemflags, int, FL_POWERUP); ATTRIB(Powerup, m_respawntime, float(), GET(g_pickup_respawntime_powerup)); ATTRIB(Powerup, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_powerup)); @@ -24,7 +24,7 @@ SOUND(Strength, Item_Sound("powerup")); #ifdef SVQC float autocvar_g_balance_powerup_strength_time; -void powerup_strength_init(entity item) +void powerup_strength_init(Pickup this, entity item) { if(!item.strength_finished) item.strength_finished = autocvar_g_balance_powerup_strength_time; @@ -40,7 +40,7 @@ REGISTER_ITEM(Strength, Powerup) { this.m_respawnsound = SND_STRENGTH_RESPAWN; #endif this.netname = "strength"; - this.m_name = "Strength Powerup"; + this.m_name = _("Strength"); this.m_icon = "strength"; this.m_color = '0 0 1'; this.m_waypoint = _("Strength"); @@ -60,7 +60,7 @@ SOUND(Shield, Item_Sound("powerup_shield")); #ifdef SVQC float autocvar_g_balance_powerup_invincible_time; -void powerup_shield_init(entity item) +void powerup_shield_init(Pickup this, entity item) { if(!item.invincible_finished) item.invincible_finished = autocvar_g_balance_powerup_invincible_time; @@ -76,7 +76,7 @@ REGISTER_ITEM(Shield, Powerup) { this.m_respawnsound = SND_SHIELD_RESPAWN; #endif this.netname = "invincible"; - this.m_name = "Shield"; + this.m_name = _("Shield"); this.m_icon = "shield"; this.m_color = '1 0 1'; this.m_waypoint = _("Shield"); diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 0884bc8d79..68c548aaef 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -424,27 +424,7 @@ void _MapInfo_Map_Reset() string _MapInfo_GetDefault(Gametype t) { - switch(t) - { - case MAPINFO_TYPE_DEATHMATCH: return "30 20 0"; - case MAPINFO_TYPE_TEAM_DEATHMATCH: return "50 20 2 0"; - case MAPINFO_TYPE_DOMINATION: return "200 20 0"; - case MAPINFO_TYPE_CTF: return "300 20 10 0"; - case MAPINFO_TYPE_LMS: return "9 20 0"; - case MAPINFO_TYPE_CA: return "10 20 0"; - case MAPINFO_TYPE_KEYHUNT: return "1000 20 3 0"; - case MAPINFO_TYPE_ASSAULT: return "20 0"; - case MAPINFO_TYPE_RACE: return "20 5 7 15 0"; - case MAPINFO_TYPE_ONSLAUGHT: return "20 0"; - case MAPINFO_TYPE_NEXBALL: return "5 20 0"; - case MAPINFO_TYPE_CTS: return "20 0 0"; - case MAPINFO_TYPE_FREEZETAG: return "10 20 0"; - // NOTE: DO NOT ADD ANY MORE GAME TYPES HERE - // THIS IS JUST LEGACY SUPPORT FOR NEXUIZ MAPS - // ONLY ADD NEW STUFF TO _MapInfo_GetDefaultEx - // THIS FUNCTION WILL EVENTUALLY BE REMOVED - default: return ""; - } + return t.m_legacydefaults; } void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisType, int load_default) @@ -457,7 +437,7 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy if(load_default) _MapInfo_Map_ApplyGametype(_MapInfo_GetDefault(pThisType), pWantedType, pThisType, false); - if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_RACE || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit + if(!pWantedType.frags) // these modes don't use fraglimit { cvar_set("fraglimit", "0"); } @@ -485,6 +465,8 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy // rc = timelimit timelimit_qualification laps laps_teamplay if(pWantedType == MAPINFO_TYPE_RACE) { + cvar_set("fraglimit", "0"); // special case! + sa = car(s); if(sa == "") sa = cvar_string("timelimit"); cvar_set("g_race_qualifying_timelimit", sa); s = cdr(s); @@ -502,7 +484,7 @@ void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisTy s = cdr(s); } - if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit + if(!pWantedType.frags) // these modes don't use fraglimit { cvar_set("leadlimit", "0"); } @@ -742,22 +724,19 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float MapInfo_isRedundant(string fn, string t) { // normalize file name - fn = strreplace("_", "-", fn); + fn = strreplace("_", "", fn); + fn = strreplace("-", "", fn); // normalize visible title - t = strreplace(": ", "-", t); - t = strreplace(":", "-", t); - t = strreplace(" ", "-", t); - t = strreplace("_", "-", t); - t = strreplace("'", "-", t); - - if(!strcasecmp(fn, t)) - return true; + t = strreplace(":", "", t); + t = strreplace(" ", "", t); + t = strreplace("_", "", t); + t = strreplace("-", "", t); + t = strreplace("'", "", t); + t = strdecolorize(t); // we allow the visible title to have punctuation the file name does // not, but not vice versa - t = strreplace("-", "", t); - if(!strcasecmp(fn, t)) return true; @@ -854,6 +833,9 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gamet if(fexists(strcat("scripts/", pFilename, ".arena"))) fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n"); + if(fexists(strcat("scripts/", pFilename, ".defi"))) + fputs(fh, "settemp_for_type all sv_vq3compat 1\n"); + fputs(fh, "// optional: fog density red green blue alpha mindist maxdist\n"); fputs(fh, "// optional: settemp_for_type (all|gametypename) cvarname value\n"); fputs(fh, "// optional: clientsettemp_for_type (all|gametypename) cvarname value\n"); @@ -1064,13 +1046,7 @@ int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametyp { int r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet); - if(cvar("g_tdm_on_dm_maps")) - { - // if this is set, all DM maps support TDM too - if (!(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_TEAM_DEATHMATCH.m_flags)) - if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags) - _MapInfo_Map_ApplyGametypeEx ("", pGametypeToSet, MAPINFO_TYPE_TEAM_DEATHMATCH); - } + FOREACH(Gametypes, it.m_isForcedSupported(it), _MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, it)); if(pGametypeToSet) { diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index 2dd84596e4..6fbb7ce896 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -31,6 +31,8 @@ CLASS(Gametype, Object) ATTRIB(Gametype, message, string); /** does this gametype support teamplay? */ ATTRIB(Gametype, team, bool, false); + /** does this gametype use a point limit? */ + ATTRIB(Gametype, frags, bool, true); /** game type defaults */ ATTRIB(Gametype, model2, string); /** game type description */ @@ -40,6 +42,9 @@ CLASS(Gametype, Object) ATTRIB(Gametype, m_modicons_reset, void()); #endif + /** DO NOT USE, this is compatibility for legacy maps! */ + ATTRIB(Gametype, m_legacydefaults, string, ""); + ATTRIB(Gametype, m_mutators, string); METHOD(Gametype, m_parse_mapinfo, bool(string k, string v)) { @@ -57,6 +62,15 @@ CLASS(Gametype, Object) { return false; } + METHOD(Gametype, m_isForcedSupported, bool(Gametype this)) + { + return false; + } + METHOD(Gametype, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); + } METHOD(Gametype, describe, string(Gametype this)) { @@ -70,7 +84,7 @@ CLASS(Gametype, Object) returns(this.message, strcat("gametype_", this.mdl)); } - METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, string mutators, string defaults, string gdescription)) + METHOD(Gametype, gametype_init, void(Gametype this, string hname, string sname, string g_name, bool gteamplay, bool gusepoints, string mutators, string defaults, string gdescription)) { this.netname = g_name; this.mdl = sname; @@ -79,6 +93,7 @@ CLASS(Gametype, Object) this.m_mutators = cons(sname, mutators); this.model2 = defaults; this.gametype_description = gdescription; + this.frags = gusepoints; // same as `1 << m_id` MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1); @@ -96,24 +111,31 @@ REGISTRY_CHECK(Gametypes) CLASS(Deathmatch, Gametype) INIT(Deathmatch) { - this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,"","timelimit=20 pointlimit=30 leadlimit=0",_("Score as many frags as you can")); + this.gametype_init(this, _("Deathmatch"),"dm","g_dm",false,true,"","timelimit=15 pointlimit=30 leadlimit=0",_("Score as many frags as you can")); } METHOD(Deathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { return true; } + ATTRIB(Deathmatch, m_legacydefaults, string, "30 20 0"); ENDCLASS(Deathmatch) REGISTER_GAMETYPE(DEATHMATCH, NEW(Deathmatch)); CLASS(LastManStanding, Gametype) INIT(LastManStanding) { - this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); + this.gametype_init(this, _("Last Man Standing"),"lms","g_lms",false,true,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); } METHOD(LastManStanding, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { return true; } + METHOD(LastManStanding, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Lives:"), 3, 50, 1, "g_lms_lives_override", string_null, string_null); + } + ATTRIB(LastManStanding, m_legacydefaults, string, "9 20 0"); ENDCLASS(LastManStanding) REGISTER_GAMETYPE(LMS, NEW(LastManStanding)); @@ -123,7 +145,7 @@ void HUD_Mod_Race(vector pos, vector mySize); CLASS(Race, Gametype) INIT(Race) { - this.gametype_init(this, _("Race"),"rc","g_race",false,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); + this.gametype_init(this, _("Race"),"rc","g_race",false,true,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")); } METHOD(Race, m_parse_mapinfo, bool(string k, string v)) { @@ -147,9 +169,15 @@ CLASS(Race, Gametype) { return true; } + METHOD(Race, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Laps:"), 1, 25, 1, "g_race_laps_limit", string_null, string_null); + } #ifdef CSQC ATTRIB(Race, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); #endif + ATTRIB(Race, m_legacydefaults, string, "20 5 7 15 0"); ENDCLASS(Race) REGISTER_GAMETYPE(RACE, NEW(Race)); #define g_race IS_GAMETYPE(RACE) @@ -157,7 +185,7 @@ REGISTER_GAMETYPE(RACE, NEW(Race)); CLASS(RaceCTS, Gametype) INIT(RaceCTS) { - this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,"cloaked","timelimit=20",_("Race for fastest time.")); + this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,false,"cloaked","timelimit=20",_("Race for fastest time.")); } METHOD(RaceCTS, m_generate_mapinfo, void(Gametype this, string v)) { @@ -171,9 +199,15 @@ CLASS(RaceCTS, Gametype) // for map databases // cvar_set("fraglimit", sa); } + METHOD(RaceCTS, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } #ifdef CSQC ATTRIB(RaceCTS, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); #endif + ATTRIB(RaceCTS, m_legacydefaults, string, "20 0 0"); ENDCLASS(RaceCTS) REGISTER_GAMETYPE(CTS, NEW(RaceCTS)); #define g_cts IS_GAMETYPE(CTS) @@ -181,7 +215,7 @@ REGISTER_GAMETYPE(CTS, NEW(RaceCTS)); CLASS(TeamDeathmatch, Gametype) INIT(TeamDeathmatch) { - this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")); + this.gametype_init(this, _("Team Deathmatch"),"tdm","g_tdm",true,true,"","timelimit=15 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")); } METHOD(TeamDeathmatch, m_parse_mapinfo, bool(string k, string v)) { @@ -202,10 +236,26 @@ CLASS(TeamDeathmatch, Gametype) return true; return false; } + METHOD(TeamDeathmatch, m_isForcedSupported, bool(Gametype this)) + { + if(cvar("g_tdm_on_dm_maps")) + { + // if this is set, all DM maps support TDM too + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)) + return true; // TODO: references another gametype (alternatively, we could check which gamemodes are always enabled and append this if any are supported) + } + return false; + } METHOD(TeamDeathmatch, m_setTeams, void(string sa)) { cvar_set("g_tdm_teams", sa); } + METHOD(TeamDeathmatch, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); + } + ATTRIB(TeamDeathmatch, m_legacydefaults, string, "50 20 2 0"); ENDCLASS(TeamDeathmatch) REGISTER_GAMETYPE(TEAM_DEATHMATCH, NEW(TeamDeathmatch)); #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH) @@ -217,7 +267,7 @@ void HUD_Mod_CTF_Reset(); CLASS(CaptureTheFlag, Gametype) INIT(CaptureTheFlag) { - this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team")); + this.gametype_init(this, _("Capture the Flag"),"ctf","g_ctf",true,true,"","timelimit=20 caplimit=10 leadlimit=6",_("Find and bring the enemy flag to your base to capture it, defend your base from the other team")); } METHOD(CaptureTheFlag, m_generate_mapinfo, void(Gametype this, string v)) { @@ -232,10 +282,16 @@ CLASS(CaptureTheFlag, Gametype) { cvar_set("fraglimit", sa); } + METHOD(CaptureTheFlag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Capture limit:"), 1, 20, 1, "capturelimit_override", string_null, _("The amount of captures needed before the match will end")); + } #ifdef CSQC ATTRIB(CaptureTheFlag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CTF); ATTRIB(CaptureTheFlag, m_modicons_reset, void(), HUD_Mod_CTF_Reset); #endif + ATTRIB(CaptureTheFlag, m_legacydefaults, string, "300 20 10 0"); ENDCLASS(CaptureTheFlag) REGISTER_GAMETYPE(CTF, NEW(CaptureTheFlag)); #define g_ctf IS_GAMETYPE(CTF) @@ -246,7 +302,7 @@ void HUD_Mod_CA(vector pos, vector mySize); CLASS(ClanArena, Gametype) INIT(ClanArena) { - this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); + this.gametype_init(this, _("Clan Arena"),"ca","g_ca",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")); } METHOD(ClanArena, m_parse_mapinfo, bool(string k, string v)) { @@ -271,9 +327,15 @@ CLASS(ClanArena, Gametype) { cvar_set("g_ca_teams", sa); } + METHOD(ClanArena, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_ca_teams_override", _("The amount of frags needed before the match will end")); + } #ifdef CSQC ATTRIB(ClanArena, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); #endif + ATTRIB(ClanArena, m_legacydefaults, string, "10 20 0"); ENDCLASS(ClanArena) REGISTER_GAMETYPE(CA, NEW(ClanArena)); #define g_ca IS_GAMETYPE(CA) @@ -284,7 +346,7 @@ void HUD_Mod_Dom(vector pos, vector mySize); CLASS(Domination, Gametype) INIT(Domination) { - this.gametype_init(this, _("Domination"),"dom","g_domination",true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win")); + this.gametype_init(this, _("Domination"),"dom","g_domination",true,true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win")); } METHOD(Domination, m_parse_mapinfo, bool(string k, string v)) { @@ -304,9 +366,15 @@ CLASS(Domination, Gametype) if(v == "dom_controlpoint") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Domination, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, "g_domination_point_limit", "g_domination_teams_override", _("The amount of points needed before the match will end")); + } #ifdef CSQC ATTRIB(Domination, m_modicons, void(vector pos, vector mySize), HUD_Mod_Dom); #endif + ATTRIB(Domination, m_legacydefaults, string, "200 20 0"); ENDCLASS(Domination) REGISTER_GAMETYPE(DOMINATION, NEW(Domination)); @@ -316,7 +384,7 @@ void HUD_Mod_KH(vector pos, vector mySize); CLASS(KeyHunt, Gametype) INIT(KeyHunt) { - this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); + this.gametype_init(this, _("Key Hunt"),"kh","g_keyhunt",true,true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")); } METHOD(KeyHunt, m_parse_mapinfo, bool(string k, string v)) { @@ -341,16 +409,22 @@ CLASS(KeyHunt, Gametype) { cvar_set("g_keyhunt_teams", sa); } + METHOD(KeyHunt, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 200, 1500, 50, "g_keyhunt_point_limit", "g_keyhunt_teams_override", _("The amount of points needed before the match will end")); + } #ifdef CSQC ATTRIB(KeyHunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_KH); #endif + ATTRIB(KeyHunt, m_legacydefaults, string, "1000 20 3 0"); ENDCLASS(KeyHunt) REGISTER_GAMETYPE(KEYHUNT, NEW(KeyHunt)); CLASS(Assault, Gametype) INIT(Assault) { - this.gametype_init(this, _("Assault"),"as","g_assault",true,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); + this.gametype_init(this, _("Assault"),"as","g_assault",true,false,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); } METHOD(Assault, m_generate_mapinfo, void(Gametype this, string v)) { @@ -361,6 +435,12 @@ CLASS(Assault, Gametype) { return true; } + METHOD(Assault, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } + ATTRIB(Assault, m_legacydefaults, string, "20 0"); ENDCLASS(Assault) REGISTER_GAMETYPE(ASSAULT, NEW(Assault)); #define g_assault IS_GAMETYPE(ASSAULT) @@ -368,13 +448,19 @@ REGISTER_GAMETYPE(ASSAULT, NEW(Assault)); CLASS(Onslaught, Gametype) INIT(Onslaught) { - this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator")); + this.gametype_init(this, _("Onslaught"),"ons","g_onslaught",true,false,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator")); } METHOD(Onslaught, m_generate_mapinfo, void(Gametype this, string v)) { if(v == "onslaught_generator") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Onslaught, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } + ATTRIB(Onslaught, m_legacydefaults, string, "20 0"); ENDCLASS(Onslaught) REGISTER_GAMETYPE(ONSLAUGHT, NEW(Onslaught)); @@ -384,7 +470,7 @@ void HUD_Mod_NexBall(vector pos, vector mySize); CLASS(NexBall, Gametype) INIT(NexBall) { - this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean")); + this.gametype_init(this, _("Nexball"),"nb","g_nexball",true,true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean")); } METHOD(NexBall, m_generate_mapinfo, void(Gametype this, string v)) { @@ -395,9 +481,15 @@ CLASS(NexBall, Gametype) { return true; } + METHOD(NexBall, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Goals:"), 1, 50, 1, "g_nexball_goallimit", string_null, _("The amount of goals needed before the match will end")); + } #ifdef CSQC ATTRIB(NexBall, m_modicons, void(vector pos, vector mySize), HUD_Mod_NexBall); #endif + ATTRIB(NexBall, m_legacydefaults, string, "5 20 0"); ENDCLASS(NexBall) REGISTER_GAMETYPE(NEXBALL, NEW(NexBall)); #define g_nexball IS_GAMETYPE(NEXBALL) @@ -405,7 +497,7 @@ REGISTER_GAMETYPE(NEXBALL, NEW(NexBall)); CLASS(FreezeTag, Gametype) INIT(FreezeTag) { - this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to frozen teammates to revive them; freeze all enemies to win")); + this.gametype_init(this, _("Freeze Tag"),"ft","g_freezetag",true,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to frozen teammates to revive them; freeze all enemies to win")); } METHOD(FreezeTag, m_parse_mapinfo, bool(string k, string v)) { @@ -430,9 +522,15 @@ CLASS(FreezeTag, Gametype) { cvar_set("g_freezetag_teams", sa); } + METHOD(FreezeTag, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_freezetag_teams_override", _("The amount of frags needed before the match will end")); + } #ifdef CSQC ATTRIB(FreezeTag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); #endif + ATTRIB(FreezeTag, m_legacydefaults, string, "10 20 0"); ENDCLASS(FreezeTag) REGISTER_GAMETYPE(FREEZETAG, NEW(FreezeTag)); #define g_freezetag IS_GAMETYPE(FREEZETAG) @@ -443,7 +541,7 @@ void HUD_Mod_Keepaway(vector pos, vector mySize); CLASS(Keepaway, Gametype) INIT(Keepaway) { - this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); + this.gametype_init(this, _("Keepaway"),"ka","g_keepaway",false,true,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); } METHOD(Keepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) { @@ -458,7 +556,7 @@ REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway)); CLASS(Invasion, Gametype) INIT(Invasion) { - this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters")); + this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,true,"","pointlimit=50 teams=0 type=0",_("Survive against waves of monsters")); } METHOD(Invasion, m_parse_mapinfo, bool(string k, string v)) { @@ -477,9 +575,35 @@ CLASS(Invasion, Gametype) if(v == "invasion_spawnpoint") MapInfo_Map_supportedGametypes |= this.m_flags; } + METHOD(Invasion, m_configuremenu, void(Gametype this, entity menu, void(entity me, string pLabel, float pMin, float pMax, float pStep, string pCvar, string tCvar, string pTooltip) returns)) + { + TC(Gametype, this); + returns(menu, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); + } ENDCLASS(Invasion) REGISTER_GAMETYPE(INVASION, NEW(Invasion)); +CLASS(Duel, Gametype) + INIT(Duel) + { + this.gametype_init(this, _("Duel"),"duel","g_duel",false,true,"","timelimit=10 pointlimit=0 leadlimit=0",_("Fight in a one versus one arena battle to decide the winner")); + } + METHOD(Duel, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return (diameter < 16384); + } + METHOD(Duel, m_isForcedSupported, bool(Gametype this)) + { + // force all DM maps to work in duel?! + // TODO: we should really check the size of maps, some DM maps do not work for duel! + if(!(MapInfo_Map_supportedGametypes & this.m_flags) && (MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH.m_flags)) + return true; + return false; + } +ENDCLASS(Duel) +REGISTER_GAMETYPE(DUEL, NEW(Duel)); +#define g_duel IS_GAMETYPE(DUEL) + const int MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps const int MAPINFO_FEATURE_VEHICLES = 2; const int MAPINFO_FEATURE_TURRETS = 4; @@ -550,4 +674,4 @@ void MapInfo_ClearTemps(); // call this when done with mapinfo for this frame void MapInfo_Shutdown(); // call this in the shutdown handler #define MAPINFO_SETTEMP_ACL_USER cvar_string("g_mapinfo_settemp_acl") -#define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* +*" +#define MAPINFO_SETTEMP_ACL_SYSTEM "-g_mapinfo_* -rcon_* -_* -g_ban* -r_water +*" diff --git a/qcsrc/common/mapobjects/_mod.inc b/qcsrc/common/mapobjects/_mod.inc new file mode 100644 index 0000000000..1aab2b927e --- /dev/null +++ b/qcsrc/common/mapobjects/_mod.inc @@ -0,0 +1,11 @@ +// generated file; do not modify +#include <common/mapobjects/models.qc> +#include <common/mapobjects/platforms.qc> +#include <common/mapobjects/subs.qc> +#include <common/mapobjects/teleporters.qc> +#include <common/mapobjects/triggers.qc> + +#include <common/mapobjects/func/_mod.inc> +#include <common/mapobjects/misc/_mod.inc> +#include <common/mapobjects/target/_mod.inc> +#include <common/mapobjects/trigger/_mod.inc> diff --git a/qcsrc/common/mapobjects/_mod.qh b/qcsrc/common/mapobjects/_mod.qh new file mode 100644 index 0000000000..ebb7a43252 --- /dev/null +++ b/qcsrc/common/mapobjects/_mod.qh @@ -0,0 +1,11 @@ +// generated file; do not modify +#include <common/mapobjects/models.qh> +#include <common/mapobjects/platforms.qh> +#include <common/mapobjects/subs.qh> +#include <common/mapobjects/teleporters.qh> +#include <common/mapobjects/triggers.qh> + +#include <common/mapobjects/func/_mod.qh> +#include <common/mapobjects/misc/_mod.qh> +#include <common/mapobjects/target/_mod.qh> +#include <common/mapobjects/trigger/_mod.qh> diff --git a/qcsrc/common/mapobjects/defs.qh b/qcsrc/common/mapobjects/defs.qh new file mode 100644 index 0000000000..45afb51f9a --- /dev/null +++ b/qcsrc/common/mapobjects/defs.qh @@ -0,0 +1,41 @@ +#pragma once + +//----------- +// SPAWNFLAGS +//----------- +const int START_ENABLED = BIT(0); +const int START_DISABLED = BIT(0); +const int ALL_ENTITIES = BIT(1); +const int ON_MAPLOAD = BIT(1); +const int INVERT_TEAMS = BIT(2); +const int CRUSH = BIT(2); +const int NOSPLASH = BIT(8); // generic anti-splashdamage spawnflag +const int ONLY_PLAYERS = BIT(14); + +// triggers +const int SPAWNFLAG_NOMESSAGE = BIT(0); +const int SPAWNFLAG_NOTOUCH = BIT(0); + +//---------- +// SENDFLAGS +//---------- +const int SF_TRIGGER_INIT = BIT(0); +const int SF_TRIGGER_UPDATE = BIT(1); +const int SF_TRIGGER_RESET = BIT(2); + +//---------------- +// STATES & ACTIVE +//---------------- +#ifdef CSQC +// this stuff is defined in the server side engine VM, so we must define it separately here +const int STATE_TOP = 0; +const int STATE_BOTTOM = 1; +const int STATE_UP = 2; +const int STATE_DOWN = 3; + +const int ACTIVE_NOT = 0; +const int ACTIVE_ACTIVE = 1; +const int ACTIVE_IDLE = 2; +const int ACTIVE_BUSY = 2; +const int ACTIVE_TOGGLE = 3; +#endif diff --git a/qcsrc/common/mapobjects/func/_mod.inc b/qcsrc/common/mapobjects/func/_mod.inc new file mode 100644 index 0000000000..0c82e979a1 --- /dev/null +++ b/qcsrc/common/mapobjects/func/_mod.inc @@ -0,0 +1,18 @@ +// generated file; do not modify +#include <common/mapobjects/func/bobbing.qc> +#include <common/mapobjects/func/breakable.qc> +#include <common/mapobjects/func/button.qc> +#include <common/mapobjects/func/conveyor.qc> +#include <common/mapobjects/func/door.qc> +#include <common/mapobjects/func/door_rotating.qc> +#include <common/mapobjects/func/door_secret.qc> +#include <common/mapobjects/func/fourier.qc> +#include <common/mapobjects/func/ladder.qc> +#include <common/mapobjects/func/pendulum.qc> +#include <common/mapobjects/func/plat.qc> +#include <common/mapobjects/func/pointparticles.qc> +#include <common/mapobjects/func/rainsnow.qc> +#include <common/mapobjects/func/rotating.qc> +#include <common/mapobjects/func/stardust.qc> +#include <common/mapobjects/func/train.qc> +#include <common/mapobjects/func/vectormamamam.qc> diff --git a/qcsrc/common/mapobjects/func/_mod.qh b/qcsrc/common/mapobjects/func/_mod.qh new file mode 100644 index 0000000000..052edb9541 --- /dev/null +++ b/qcsrc/common/mapobjects/func/_mod.qh @@ -0,0 +1,18 @@ +// generated file; do not modify +#include <common/mapobjects/func/bobbing.qh> +#include <common/mapobjects/func/breakable.qh> +#include <common/mapobjects/func/button.qh> +#include <common/mapobjects/func/conveyor.qh> +#include <common/mapobjects/func/door.qh> +#include <common/mapobjects/func/door_rotating.qh> +#include <common/mapobjects/func/door_secret.qh> +#include <common/mapobjects/func/fourier.qh> +#include <common/mapobjects/func/ladder.qh> +#include <common/mapobjects/func/pendulum.qh> +#include <common/mapobjects/func/plat.qh> +#include <common/mapobjects/func/pointparticles.qh> +#include <common/mapobjects/func/rainsnow.qh> +#include <common/mapobjects/func/rotating.qh> +#include <common/mapobjects/func/stardust.qh> +#include <common/mapobjects/func/train.qh> +#include <common/mapobjects/func/vectormamamam.qh> diff --git a/qcsrc/common/mapobjects/func/bobbing.qc b/qcsrc/common/mapobjects/func/bobbing.qc new file mode 100644 index 0000000000..60920fafb8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/bobbing.qc @@ -0,0 +1,87 @@ +#include "bobbing.qh" +#ifdef SVQC +.float height; +void func_bobbing_controller_think(entity this) +{ + vector v; + this.nextthink = time + 0.1; + + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + // calculate sinewave using makevectors + makevectors((this.nextthink * this.owner.cnt + this.owner.phase * 360) * '0 1 0'); + v = this.owner.destvec + this.owner.movedir * v_forward_y; + if(this.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed + // * 10 so it will arrive in 0.1 sec + this.owner.velocity = (v - this.owner.origin) * 10; +} + +/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS +Brush model that moves back and forth on one axis (default Z). +speed : how long one cycle takes in seconds (default 4) +height : how far the cycle moves (default 32) +phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0) +noise : path/name of looping .wav file to play. +dmg : Do this mutch dmg every .dmgtime intervall when blocked +dmgtime : See above. +*/ +spawnfunc(func_bobbing) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + if (!this.speed) + this.speed = 4; + if (!this.height) + this.height = 32; + // center of bobbing motion + this.destvec = this.origin; + // time scale to get degrees + this.cnt = 360 / this.speed; + + this.active = ACTIVE_ACTIVE; + + this.draggable = drag_undraggable; + + // damage when blocked + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + // how far to bob + if (this.spawnflags & BOBBING_XAXIS) + this.movedir = '1 0 0' * this.height; + else if (this.spawnflags & BOBBING_YAXIS) + this.movedir = '0 1 0' * this.height; + else // Z + this.movedir = '0 0 1' * this.height; + + if (!InitMovingBrushTrigger(this)) + return; + + // wait for targets to spawn + controller = new(func_bobbing_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_bobbing_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/bobbing.qh b/qcsrc/common/mapobjects/func/bobbing.qh new file mode 100644 index 0000000000..58f7bb7145 --- /dev/null +++ b/qcsrc/common/mapobjects/func/bobbing.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int BOBBING_XAXIS = BIT(0); +const int BOBBING_YAXIS = BIT(1); diff --git a/qcsrc/common/mapobjects/func/breakable.qc b/qcsrc/common/mapobjects/func/breakable.qc new file mode 100644 index 0000000000..cb17ac442c --- /dev/null +++ b/qcsrc/common/mapobjects/func/breakable.qc @@ -0,0 +1,388 @@ +#include "breakable.qh" +#ifdef SVQC + +#include <server/g_damage.qh> +#include <server/bot/api.qh> +#include <common/csqcmodel_settings.qh> +#include <lib/csqcmodel/sv_model.qh> +#include <server/weapons/common.qh> + +.entity sprite; + +.float dmg; +.float dmg_edge; +.float dmg_radius; +.float dmg_force; +.float debrismovetype; +.float debrissolid; +.vector debrisvelocity; +.vector debrisvelocityjitter; +.vector debrisavelocityjitter; +.float debristime; +.float debristimejitter; +.float debrisfadetime; +.float debrisdamageforcescale; +.float debrisskin; + +.string mdl_dead; // or "" to hide when broken +.string debris; // space separated list of debris models +// other fields: +// mdl = particle effect name +// count = particle effect multiplier +// targetname = target to trigger to unbreak the model +// target = targets to trigger when broken +// health = amount of damage it can take +// spawnflags: +// START_DISABLED: needs to be triggered to activate +// BREAKABLE_INDICATE_DAMAGE: indicate damage +// BREAKABLE_NODAMAGE: don't take direct damage (needs to be triggered to 'explode', then triggered again to restore) +// NOSPLASH: don't take splash damage +// notes: +// for mdl_dead to work, origin must be set (using a common/origin brush). +// Otherwise mdl_dead will be displayed at the map origin, and nobody would +// want that! + +void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); + +// +// func_breakable +// - basically func_assault_destructible for general gameplay use +// +void LaunchDebris (entity this, string debrisname, vector force) +{ + entity dbr = spawn(); + vector org = this.absmin + + '1 0 0' * random() * (this.absmax.x - this.absmin.x) + + '0 1 0' * random() * (this.absmax.y - this.absmin.y) + + '0 0 1' * random() * (this.absmax.z - this.absmin.z); + setorigin(dbr, org); + _setmodel (dbr, debrisname ); + dbr.skin = this.debrisskin; + dbr.colormap = this.colormap; // inherit team colors + dbr.owner = this; // do not be affected by our own explosion + set_movetype(dbr, this.debrismovetype); + dbr.solid = this.debrissolid; + if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out + setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it + dbr.velocity_x = this.debrisvelocity.x + this.debrisvelocityjitter.x * crandom(); + dbr.velocity_y = this.debrisvelocity.y + this.debrisvelocityjitter.y * crandom(); + dbr.velocity_z = this.debrisvelocity.z + this.debrisvelocityjitter.z * crandom(); + dbr.velocity = dbr.velocity + force * this.debrisdamageforcescale; + dbr.angles = this.angles; + dbr.avelocity_x = random()*this.debrisavelocityjitter.x; + dbr.avelocity_y = random()*this.debrisavelocityjitter.y; + dbr.avelocity_z = random()*this.debrisavelocityjitter.z; + dbr.damageforcescale = this.debrisdamageforcescale; + if(dbr.damageforcescale) + dbr.takedamage = DAMAGE_YES; + SUB_SetFade(dbr, time + this.debristime + crandom() * this.debristimejitter, this.debrisfadetime); +} + +void func_breakable_colormod(entity this) +{ + float h; + if (!(this.spawnflags & BREAKABLE_INDICATE_DAMAGE)) + return; + h = GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health; + if(h < 0.25) + this.colormod = '1 0 0'; + else if(h <= 0.75) + this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); + else + this.colormod = '1 1 1'; +} + +void func_breakable_look_destroyed(entity this) +{ + float floorZ; + + if(this.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first + this.dropped_origin = this.origin; + + if(this.mdl_dead == "") + this.effects |= EF_NODRAW; + else { + if (this.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. + floorZ = this.absmin.z; + setorigin(this, ((this.absmax + this.absmin) * 0.5)); + this.origin_z = floorZ; + } + _setmodel(this, this.mdl_dead); + ApplyMinMaxScaleAngles(this); + this.effects &= ~EF_NODRAW; + } + + this.solid = SOLID_NOT; +} + +void func_breakable_look_restore(entity this) +{ + _setmodel(this, this.mdl); + ApplyMinMaxScaleAngles(this); + this.effects &= ~EF_NODRAW; + + if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow + setorigin(this, this.dropped_origin); + + this.solid = SOLID_BSP; +} + +void func_breakable_behave_destroyed(entity this) +{ + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + this.takedamage = DAMAGE_NO; + if(this.bot_attack) + IL_REMOVE(g_bot_targets, this); + this.bot_attack = false; + this.event_damage = func_null; + this.state = STATE_BROKEN; + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_null; + func_breakable_colormod(this); + if (this.noise1) + stopsound (this, CH_TRIGGER_SINGLE); + + IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, + { + RemoveHook(it); + }); +} + +void func_breakable_think(entity this) +{ + this.nextthink = time; + CSQCMODEL_AUTOUPDATE(this); +} + +void func_breakable_destroy(entity this, entity actor, entity trigger); +void func_breakable_behave_restore(entity this) +{ + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + if(this.sprite) + { + WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); + } + if(!(this.spawnflags & BREAKABLE_NODAMAGE)) + { + this.takedamage = DAMAGE_AIM; + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); + this.bot_attack = true; + this.event_damage = func_breakable_damage; + } + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset + this.state = STATE_ALIVE; + //this.nextthink = 0; // cancel auto respawn + setthink(this, func_breakable_think); + this.nextthink = time + 0.1; + func_breakable_colormod(this); + if (this.noise1) + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); +} + +void func_breakable_init_for_player(entity this, entity player) +{ + if (this.noise1 && this.state == STATE_ALIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto (MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + } +} + +void func_breakable_destroyed(entity this) +{ + func_breakable_look_destroyed(this); + func_breakable_behave_destroyed(this); +} + +void func_breakable_restore(entity this, entity actor, entity trigger) +{ + func_breakable_look_restore(this); + func_breakable_behave_restore(this); +} + +void func_breakable_restore_self(entity this) +{ + // TODO: use a clipgroup for all func_breakables so they don't collide with eachother + float oldhit = this.dphitcontentsmask; + this.dphitcontentsmask = DPCONTENTS_BODY; // we really only care about when players are standing inside, obey the mapper in other cases! + tracebox(this.origin, this.mins, this.maxs, this.origin, MOVE_NORMAL, this); + this.dphitcontentsmask = oldhit; + if(trace_startsolid || trace_fraction < 1) + { + this.nextthink = time + 5; // retry every 5 seconds until the area becomes clear + return; + } + func_breakable_restore(this, NULL, NULL); +} + +vector debrisforce; // global, set before calling this +void func_breakable_destroy(entity this, entity actor, entity trigger) +{ + float n, i; + string oldmsg; + + entity act = this.owner; + this.owner = NULL; // set by W_PrepareExplosionByDamage + + // now throw around the debris + n = tokenize_console(this.debris); + for(i = 0; i < n; ++i) + LaunchDebris(this, argv(i), debrisforce); + + func_breakable_destroyed(this); + + if(this.noise) + _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + + if(this.dmg) + RadiusDamage(this, act, this.dmg, this.dmg_edge, this.dmg_radius, this, NULL, this.dmg_force, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, NULL); + + if(this.cnt) // TODO + __pointparticles(this.cnt, this.absmin * 0.5 + this.absmax * 0.5, '0 0 0', this.count); + + if(this.respawntime) + { + CSQCMODEL_AUTOUPDATE(this); + setthink(this, func_breakable_restore_self); + this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter; + } + + oldmsg = this.message; + this.message = ""; + SUB_UseTargets(this, act, trigger); + this.message = oldmsg; +} + +void func_breakable_destroy_self(entity this) +{ + func_breakable_destroy(this, NULL, NULL); +} + +void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.state == STATE_BROKEN) + return; + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(this.team) + if(attacker.team == this.team) + return; + this.pain_finished = time; + TakeResource(this, RESOURCE_HEALTH, damage); + if(this.sprite) + { + WaypointSprite_Ping(this.sprite); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); + } + func_breakable_colormod(this); + + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + { + debrisforce = force; + + this.takedamage = DAMAGE_NO; + this.event_damage = func_null; + + if(IS_CLIENT(attacker)) //&& this.classname == "func_assault_destructible") + { + this.owner = attacker; + this.realowner = attacker; + } + + // do not explode NOW but in the NEXT FRAME! + // because recursive calls to RadiusDamage are not allowed + this.nextthink = time; + CSQCMODEL_AUTOUPDATE(this); + setthink(this, func_breakable_destroy_self); + } +} + +void func_breakable_reset(entity this) +{ + this.team = this.team_saved; + func_breakable_look_restore(this); + if(this.spawnflags & START_DISABLED) + func_breakable_behave_destroyed(this); + else + func_breakable_behave_restore(this); +} + +// destructible walls that can be used to trigger target_objective_decrease +spawnfunc(func_breakable) +{ + float n, i; + if(!GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); + + // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway + if(!this.debrismovetype) this.debrismovetype = MOVETYPE_BOUNCE; + if(!this.debrissolid) this.debrissolid = SOLID_NOT; + if(this.debrisvelocity == '0 0 0') this.debrisvelocity = '0 0 140'; + if(this.debrisvelocityjitter == '0 0 0') this.debrisvelocityjitter = '70 70 70'; + if(this.debrisavelocityjitter == '0 0 0') this.debrisavelocityjitter = '600 600 600'; + if(!this.debristime) this.debristime = 3.5; + if(!this.debristimejitter) this.debristime = 2.5; + + if(this.mdl != "") + this.cnt = _particleeffectnum(this.mdl); + if(this.count == 0) + this.count = 1; + + if(this.message == "") + this.message = "got too close to an explosion"; + if(this.message2 == "") + this.message2 = "was pushed into an explosion by"; + if(!this.dmg_radius) + this.dmg_radius = 150; + if(!this.dmg_force) + this.dmg_force = 200; + + this.mdl = this.model; + SetBrushEntityModel(this); + + if(this.spawnflags & BREAKABLE_NODAMAGE) + this.use = func_breakable_destroy; + else + this.use = func_breakable_restore; + + if(this.spawnflags & BREAKABLE_NODAMAGE) + { + this.takedamage = DAMAGE_NO; + this.event_damage = func_null; + this.bot_attack = false; + } + + // precache all the models + if (this.mdl_dead) + precache_model(this.mdl_dead); + n = tokenize_console(this.debris); + for(i = 0; i < n; ++i) + precache_model(argv(i)); + if(this.noise) + precache_sound(this.noise); + if(this.noise1) + precache_sound(this.noise1); + + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + this.dropped_origin = this.origin; + + this.reset = func_breakable_reset; + this.reset(this); + + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_breakable_init_for_player; + + CSQCMODEL_AUTOINIT(this); +} + +// for use in maps with a "model" key set +spawnfunc(misc_breakablemodel) { + spawnfunc_func_breakable(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/breakable.qh b/qcsrc/common/mapobjects/func/breakable.qh new file mode 100644 index 0000000000..0efbcfaed5 --- /dev/null +++ b/qcsrc/common/mapobjects/func/breakable.qh @@ -0,0 +1,12 @@ +#pragma once + + +const int BREAKABLE_INDICATE_DAMAGE = BIT(1); +const int BREAKABLE_NODAMAGE = BIT(2); + +const int STATE_ALIVE = 0; +const int STATE_BROKEN = 1; + +#ifdef SVQC +spawnfunc(func_breakable); +#endif diff --git a/qcsrc/common/mapobjects/func/button.qc b/qcsrc/common/mapobjects/func/button.qc new file mode 100644 index 0000000000..024d5cfd87 --- /dev/null +++ b/qcsrc/common/mapobjects/func/button.qc @@ -0,0 +1,224 @@ +#include "button.qh" +#ifdef SVQC +// button and multiple button + +void button_wait(entity this); +void button_return(entity this); + +// in case button is deactivated by a relay_deactivate while it pressed down +// set both fields to -1 in button_return!! +.float wait_remaining; +.float activation_time; + +void button_setactive(entity this, int astate) +{ + int oldstate = this.active; + if (astate == ACTIVE_TOGGLE) + { + if (this.active == ACTIVE_ACTIVE) + this.active = ACTIVE_NOT; + else + this.active = ACTIVE_ACTIVE; + } + else + this.active = astate; + + if (this.active == ACTIVE_ACTIVE && oldstate == ACTIVE_NOT) + { + // button was deactivated while it was pressed + if (this.wait_remaining >= 0) + { + this.nextthink = this.wait_remaining + this.ltime; + setthink(this, button_return); + } + } + else if (this.active == ACTIVE_NOT && oldstate == ACTIVE_ACTIVE) + { + // check if button is in pressed state + if (this.activation_time >= 0) + { + this.wait_remaining = this.wait - (time - this.activation_time); + } + } +} + +void button_wait(entity this) +{ + this.state = STATE_TOP; + if(this.wait >= 0) + { + this.nextthink = this.ltime + this.wait; + setthink(this, button_return); + } + SUB_UseTargets(this, this.enemy, NULL); + this.frame = 1; // use alternate textures +} + +void button_done(entity this) +{ + this.state = STATE_BOTTOM; +} + +void button_return(entity this) +{ + if (this.active != ACTIVE_ACTIVE) + { + return; + } + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done); + this.frame = 0; // use normal textures + if (GetResourceAmount(this, RESOURCE_HEALTH)) + this.takedamage = DAMAGE_YES; // can be shot again + this.wait_remaining = -1; + this.activation_time = -1; +} + + +void button_blocked(entity this, entity blocker) +{ + // do nothing, just don't come all the way back out +} + + +void button_fire(entity this) +{ + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + this.takedamage = DAMAGE_NO; // will be reset upon return + + if (this.state == STATE_UP || this.state == STATE_TOP) + return; + + this.activation_time = time; + + if (this.noise != "") + _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + + this.state = STATE_UP; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, button_wait); +} + +void button_reset(entity this) +{ + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + setorigin(this, this.pos1); + this.frame = 0; // use normal textures + this.state = STATE_BOTTOM; + this.velocity = '0 0 0'; + this.wait_remaining = -1; + this.activation_time = -1; + this.active = ACTIVE_ACTIVE; + setthink(this, func_null); + this.nextthink = 0; + if (GetResourceAmount(this, RESOURCE_HEALTH)) + this.takedamage = DAMAGE_YES; // can be shot again +} + +void button_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + this.enemy = actor; + button_fire(this); +} + +void button_touch(entity this, entity toucher) +{ + if (this.active != ACTIVE_ACTIVE) + return; + if (!toucher) + return; + if (!toucher.iscreature) + return; + if(toucher.velocity * this.movedir < 0) + return; + this.enemy = toucher; + if (toucher.owner) + this.enemy = toucher.owner; + button_fire (this); +} + +void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if (this.active != ACTIVE_ACTIVE) + return; + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if (this.spawnflags & BUTTON_DONTACCUMULATEDMG) + { + if (GetResourceAmount(this, RESOURCE_HEALTH) <= damage) + { + this.enemy = attacker; + button_fire(this); + } + } + else + { + TakeResource(this, RESOURCE_HEALTH, damage); + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + { + this.enemy = attacker; + button_fire(this); + } + } +} + + +/*QUAKED spawnfunc_func_button (0 .5 .8) ? +When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. + +"angle" determines the opening direction +"target" all entities with a matching targetname will be used +"speed" override the default 40 speed +"wait" override the default 1 second wait (-1 = never return) +"lip" override the default 4 pixel lip remaining at end of move +"health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser +"noise" sound that is played when the button is activated +*/ +spawnfunc(func_button) +{ + SetMovedir(this); + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + + setblocked(this, button_blocked); + this.use = button_use; + +// if (this.health == 0) // all buttons are now shootable +// this.health = 10; + if (GetResourceAmount(this, RESOURCE_HEALTH)) + { + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); + this.event_damage = button_damage; + this.takedamage = DAMAGE_YES; + } + else + settouch(this, button_touch); + + if (!this.speed) + this.speed = 40; + if (!this.wait) + this.wait = 1; + if (!this.lip) + this.lip = 4; + + if(this.noise != "") + precache_sound(this.noise); + + this.draggable = drag_undraggable; + + this.setactive = button_setactive; + + this.pos1 = this.origin; + this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); + this.flags |= FL_NOTARGET; + + this.reset = button_reset; + + button_reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/button.qh b/qcsrc/common/mapobjects/func/button.qh new file mode 100644 index 0000000000..86a0fc9180 --- /dev/null +++ b/qcsrc/common/mapobjects/func/button.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int BUTTON_DONTACCUMULATEDMG = BIT(7); diff --git a/qcsrc/common/mapobjects/func/conveyor.qc b/qcsrc/common/mapobjects/func/conveyor.qc new file mode 100644 index 0000000000..4c40598d35 --- /dev/null +++ b/qcsrc/common/mapobjects/func/conveyor.qc @@ -0,0 +1,176 @@ +#include "conveyor.qh" +REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) + +void conveyor_think(entity this) +{ +#ifdef CSQC + // TODO: check if this is what is causing the glitchiness when switching between them + float dt = time - this.move_time; + this.move_time = time; + if(dt <= 0) { return; } +#endif + + // set myself as current conveyor where possible + IL_EACH(g_conveyed, it.conveyor == this, + { + it.conveyor = NULL; + IL_REMOVE(g_conveyed, it); + }); + + if(this.active == ACTIVE_ACTIVE) + { + FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, it.conveyor.active == ACTIVE_NOT && isPushable(it), + { + vector emin = it.absmin; + vector emax = it.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate + { + if(!it.conveyor) + IL_PUSH(g_conveyed, it); + it.conveyor = this; + } + }); + + IL_EACH(g_conveyed, it.conveyor == this, + { + if(IS_CLIENT(it)) // doing it via velocity has quite some advantages + continue; // done in SV_PlayerPhysics continue; + + setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME); + move_out_of_solid(it); +#ifdef SVQC + UpdateCSQCProjectile(it); +#endif + /* + // stupid conveyor code + tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it); + if(trace_fraction > 0) + setorigin(it, trace_endpos); + */ + }); + } + +#ifdef SVQC + this.nextthink = time; +#endif +} + +#ifdef SVQC + +bool conveyor_send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR); + WriteByte(MSG_ENTITY, sendflags); + + if(sendflags & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.warpzone_isboxy); + WriteVector(MSG_ENTITY, this.origin); + + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + + WriteVector(MSG_ENTITY, this.movedir); + + WriteByte(MSG_ENTITY, this.speed); + WriteByte(MSG_ENTITY, this.active); + + WriteString(MSG_ENTITY, this.targetname); + WriteString(MSG_ENTITY, this.target); + } + + if(sendflags & SF_TRIGGER_UPDATE) + WriteByte(MSG_ENTITY, this.active); + + return true; +} + +void conveyor_init(entity this) +{ + if (!this.speed) this.speed = 200; + this.movedir *= this.speed; + setthink(this, conveyor_think); + this.nextthink = time; + this.setactive = generic_netlinked_setactive; + IFTARGETED + { + // backwards compatibility + this.use = generic_netlinked_legacy_use; + } + this.reset = generic_netlinked_reset; + this.reset(this); + + Net_LinkEntity(this, 0, false, conveyor_send); + + this.SendFlags |= SF_TRIGGER_INIT; +} + +spawnfunc(trigger_conveyor) +{ + SetMovedir(this); + EXACTTRIGGER_INIT; + conveyor_init(this); +} + +spawnfunc(func_conveyor) +{ + SetMovedir(this); + InitMovingBrushTrigger(this); + set_movetype(this, MOVETYPE_NONE); + conveyor_init(this); +} + +#elif defined(CSQC) + +void conveyor_draw(entity this) { conveyor_think(this); } + +void conveyor_init(entity this, bool isnew) +{ + if(isnew) + IL_PUSH(g_drawables, this); + this.draw = conveyor_draw; + this.drawmask = MASK_NORMAL; + + set_movetype(this, MOVETYPE_NONE); + this.model = ""; + this.solid = SOLID_TRIGGER; + this.move_time = time; +} + +NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew) +{ + int sendflags = ReadByte(); + + if(sendflags & SF_TRIGGER_INIT) + { + this.warpzone_isboxy = ReadByte(); + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.mins = ReadVector(); + this.maxs = ReadVector(); + setsize(this, this.mins, this.maxs); + + this.movedir = ReadVector(); + + this.speed = ReadByte(); + this.active = ReadByte(); + + this.targetname = strzone(ReadString()); + this.target = strzone(ReadString()); + + conveyor_init(this, isnew); + } + + if(sendflags & SF_TRIGGER_UPDATE) + this.active = ReadByte(); + + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/conveyor.qh b/qcsrc/common/mapobjects/func/conveyor.qh new file mode 100644 index 0000000000..22b674ff36 --- /dev/null +++ b/qcsrc/common/mapobjects/func/conveyor.qh @@ -0,0 +1,5 @@ +#pragma once +#include "../defs.qh" + +IntrusiveList g_conveyed; +STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); } diff --git a/qcsrc/common/mapobjects/func/door.qc b/qcsrc/common/mapobjects/func/door.qc new file mode 100644 index 0000000000..1ba7bad3aa --- /dev/null +++ b/qcsrc/common/mapobjects/func/door.qc @@ -0,0 +1,799 @@ +#include "door.qh" +#include "door_rotating.qh" +/* + +Doors are similar to buttons, but can spawn a fat trigger field around them +to open without a touch, and they link together to form simultanious +double/quad doors. + +Door.owner is the master door. If there is only one door, it points to itself. +If multiple doors, all will point to a single one. + +Door.enemy chains from the master door through all doors linked in the chain. + +*/ + + +/* +============================================================================= + +THINK FUNCTIONS + +============================================================================= +*/ + +void door_go_down(entity this); +void door_go_up(entity this, entity actor, entity trigger); + +void door_blocked(entity this, entity blocker) +{ + if((this.spawnflags & DOOR_CRUSH) +#ifdef SVQC + && (blocker.takedamage != DAMAGE_NO) +#elif defined(CSQC) + && !IS_DEAD(blocker) +#endif + ) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + + // don't change direction for dead or dying stuff + if(IS_DEAD(blocker) +#ifdef SVQC + && (blocker.takedamage == DAMAGE_NO) +#endif + ) + { + if (this.wait >= 0) + { + if (this.state == STATE_DOWN) + { + if (this.classname == "door") + door_go_up(this, NULL, NULL); + else + door_rotating_go_up(this, blocker); + } + else + { + if (this.classname == "door") + door_go_down(this); + else + door_rotating_go_down(this); + } + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + } +} + +void door_hit_top(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + if (this.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + if (this.classname == "door") + { + setthink(this, door_go_down); + } else + { + setthink(this, door_rotating_go_down); + } + this.nextthink = this.ltime + this.wait; +} + +void door_hit_bottom(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_BOTTOM; +} + +void door_go_down(entity this) +{ + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + if (this.max_health) + { + this.takedamage = DAMAGE_YES; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + } + + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_hit_bottom); +} + +void door_go_up(entity this, entity actor, entity trigger) +{ + if (this.state == STATE_UP) + return; // already going up + + if (this.state == STATE_TOP) + { // reset top wait time + this.nextthink = this.ltime + this.wait; + return; + } + + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_hit_top); + + string oldmessage; + oldmessage = this.message; + this.message = ""; + SUB_UseTargets(this, actor, trigger); + this.message = oldmessage; +} + + +/* +============================================================================= + +ACTIVATION FUNCTIONS + +============================================================================= +*/ + +bool door_check_keys(entity door, entity player) +{ + if(door.owner) + door = door.owner; + + // no key needed + if(!door.itemkeys) + return true; + + // this door require a key + // only a player can have a key + if(!IS_PLAYER(player)) + return false; + + entity store = player; +#ifdef SVQC + store = PS(player); +#endif + int valid = (door.itemkeys & store.itemkeys); + door.itemkeys &= ~valid; // only some of the needed keys were given + + if(!door.itemkeys) + { +#ifdef SVQC + play2(player, door.noise); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED); +#endif + return true; + } + + if(!valid) + { +#ifdef SVQC + if(player.key_door_messagetime <= time) + { + play2(player, door.noise3); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); + player.key_door_messagetime = time + 2; + } +#endif + return false; + } + + // door needs keys the player doesn't have +#ifdef SVQC + if(player.key_door_messagetime <= time) + { + play2(player, door.noise3); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); + player.key_door_messagetime = time + 2; + } +#endif + + return false; +} + +void door_fire(entity this, entity actor, entity trigger) +{ + if (this.owner != this) + objerror (this, "door_fire: this.owner != this"); + + if (this.spawnflags & DOOR_TOGGLE) + { + if (this.state == STATE_UP || this.state == STATE_TOP) + { + entity e = this; + do { + if (e.classname == "door") { + door_go_down(e); + } else { + door_rotating_go_down(e); + } + e = e.enemy; + } while ((e != this) && (e != NULL)); + return; + } + } + +// trigger all paired doors + entity e = this; + do { + if (e.classname == "door") { + 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 & DOOR_ROTATING_BIDIR) && trigger.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) { + e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating + e.pos2 = '0 0 0' - e.pos2; + } + // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side + if (!((e.spawnflags & DOOR_ROTATING_BIDIR) && (e.spawnflags & DOOR_ROTATING_BIDIR_IN_DOWN) && e.state == STATE_DOWN + && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0))))) + { + door_rotating_go_up(e, trigger); + } + } + e = e.enemy; + } while ((e != this) && (e != NULL)); +} + +void door_use(entity this, entity actor, entity trigger) +{ + //dprint("door_use (model: ");dprint(this.model);dprint(")\n"); + + if (this.owner) + door_fire(this.owner, actor, trigger); +} + +void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + TakeResource(this, RESOURCE_HEALTH, damage); + + if (this.itemkeys) + { + // don't allow opening doors through damage if keys are required + return; + } + + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + { + SetResourceAmountExplicit(this.owner, RESOURCE_HEALTH, this.owner.max_health); + this.owner.takedamage = DAMAGE_NO; // will be reset upon return + door_use(this.owner, attacker, NULL); + } +} + +.float door_finished; + +/* +================ +door_touch + +Prints messages +================ +*/ + +void door_touch(entity this, entity toucher) +{ + if (!IS_PLAYER(toucher)) + return; + if (this.owner.door_finished > time) + return; + + this.owner.door_finished = time + 2; + +#ifdef SVQC + if (!(this.owner.dmg) && (this.owner.message != "")) + { + if (IS_CLIENT(toucher)) + centerprint(toucher, this.owner.message); + play2(toucher, this.owner.noise); + } +#endif +} + +void door_generic_plat_blocked(entity this, entity blocker) +{ + if((this.spawnflags & DOOR_CRUSH) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { + +#ifdef SVQC + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + + //Dont chamge direction for dead or dying stuff + if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO)) + { + if (this.wait >= 0) + { + if (this.state == STATE_DOWN) + door_rotating_go_up (this, blocker); + else + door_rotating_go_down (this); + } + } +#ifdef SVQC + else + { + //gib dying stuff just to make sure + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + } +} + +/* +========================================= +door trigger + +Spawned if a door lacks a real activator +========================================= +*/ + +void door_trigger_touch(entity this, entity toucher) +{ + if (GetResourceAmount(toucher, RESOURCE_HEALTH) < 1) +#ifdef SVQC + if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) +#elif defined(CSQC) + if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher))) +#endif + return; + + if (time < this.door_finished) + return; + + // check if door is locked + if (!door_check_keys(this, toucher)) + return; + + this.door_finished = time + 1; + + door_use(this.owner, toucher, NULL); +} + +void door_spawnfield(entity this, vector fmins, vector fmaxs) +{ + entity trigger; + vector t1 = fmins, t2 = fmaxs; + + trigger = new(doortriggerfield); + set_movetype(trigger, MOVETYPE_NONE); + trigger.solid = SOLID_TRIGGER; + trigger.owner = this; +#ifdef SVQC + settouch(trigger, door_trigger_touch); +#endif + + setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); +} + + +/* +============= +LinkDoors + + +============= +*/ + +entity LinkDoors_nextent(entity cur, entity near, entity pass) +{ + while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & DOOR_DONT_LINK) || cur.enemy)) + { + } + return cur; +} + +bool LinkDoors_isconnected(entity e1, entity e2, entity pass) +{ + float DELTA = 4; + if((e1.absmin_x > e2.absmax_x + DELTA) + || (e1.absmin_y > e2.absmax_y + DELTA) + || (e1.absmin_z > e2.absmax_z + DELTA) + || (e2.absmin_x > e1.absmax_x + DELTA) + || (e2.absmin_y > e1.absmax_y + DELTA) + || (e2.absmin_z > e1.absmax_z + DELTA) + ) { return false; } + return true; +} + +#ifdef SVQC +void door_link(); +#endif +void LinkDoors(entity this) +{ + entity t; + vector cmins, cmaxs; + +#ifdef SVQC + door_link(); +#endif + + if (this.enemy) + return; // already linked by another door + if (this.spawnflags & DOOR_DONT_LINK) + { + this.owner = this.enemy = this; + + if (GetResourceAmount(this, RESOURCE_HEALTH)) + return; + IFTARGETED + return; + if (this.items) + return; + + door_spawnfield(this, this.absmin, this.absmax); + + return; // don't want to link this door + } + + FindConnectedComponent(this, enemy, LinkDoors_nextent, LinkDoors_isconnected, this); + + // set owner, and make a loop of the chain + LOG_TRACE("LinkDoors: linking doors:"); + for(t = this; ; t = t.enemy) + { + LOG_TRACE(" ", etos(t)); + t.owner = this; + if(t.enemy == NULL) + { + t.enemy = this; + break; + } + } + LOG_TRACE(""); + + // collect health, targetname, message, size + cmins = this.absmin; + cmaxs = this.absmax; + for(t = this; ; t = t.enemy) + { + if(GetResourceAmount(t, RESOURCE_HEALTH) && !GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(t, RESOURCE_HEALTH)); + if((t.targetname != "") && (this.targetname == "")) + this.targetname = t.targetname; + if((t.message != "") && (this.message == "")) + this.message = t.message; + if (t.absmin_x < cmins_x) + cmins_x = t.absmin_x; + if (t.absmin_y < cmins_y) + cmins_y = t.absmin_y; + if (t.absmin_z < cmins_z) + cmins_z = t.absmin_z; + if (t.absmax_x > cmaxs_x) + cmaxs_x = t.absmax_x; + if (t.absmax_y > cmaxs_y) + cmaxs_y = t.absmax_y; + if (t.absmax_z > cmaxs_z) + cmaxs_z = t.absmax_z; + if(t.enemy == this) + break; + } + + // distribute health, targetname, message + for(t = this; t; t = t.enemy) + { + SetResourceAmountExplicit(t, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); + t.targetname = this.targetname; + t.message = this.message; + if(t.enemy == this) + break; + } + + // shootable, or triggered doors just needed the owner/enemy links, + // they don't spawn a field + + if (GetResourceAmount(this, RESOURCE_HEALTH)) + return; + IFTARGETED + return; + if (this.items) + return; + + door_spawnfield(this, cmins, cmaxs); +} + +REGISTER_NET_LINKED(ENT_CLIENT_DOOR) + +#ifdef SVQC +/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE +if two doors touch, they are assumed to be connected and operate as a unit. + +TOGGLE causes the door to wait in both the start and end states for a trigger event. + +START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). + +GOLD_KEY causes the door to open only if the activator holds a gold key. + +SILVER_KEY causes the door to open only if the activator holds a silver key. + +"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet +"angle" determines the opening direction +"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. +"health" if set, door must be shot open +"speed" movement speed (100 default) +"wait" wait before returning (3 default, -1 = never return) +"lip" lip remaining at end of move (8 default) +"dmg" damage to inflict when blocked (2 default) +"sounds" +0) no sound +1) stone +2) base +3) stone chain +4) screechy metal +FIXME: only one sound set available at the time being + +*/ + +float door_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteString(MSG_ENTITY, this.classname); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteShort(MSG_ENTITY, this.wait); + WriteShort(MSG_ENTITY, this.speed); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + WriteCoord(MSG_ENTITY, this.ltime); + } + + if(sf & SF_TRIGGER_RESET) + { + // client makes use of this, we do not + } + + if(sf & SF_TRIGGER_UPDATE) + { + WriteVector(MSG_ENTITY, this.origin); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + } + + return true; +} + +void door_link() +{ + //Net_LinkEntity(this, false, 0, door_send); +} +#endif + +void door_init_startopen(entity this) +{ + setorigin(this, this.pos2); + this.pos2 = this.pos1; + this.pos1 = this.origin; + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_UPDATE; +#endif +} + +void door_reset(entity this) +{ + setorigin(this, this.pos1); + this.velocity = '0 0 0'; + this.state = STATE_BOTTOM; + setthink(this, func_null); + this.nextthink = 0; + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +#ifdef SVQC + +// common code for func_door and func_door_rotating spawnfuncs +void door_init_shared(entity this) +{ + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); + + // unlock sound + if(this.noise == "") + { + this.noise = "misc/talk.wav"; + } + // door still locked sound + if(this.noise3 == "") + { + this.noise3 = "misc/talk.wav"; + } + precache_sound(this.noise); + precache_sound(this.noise3); + + if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message == "")) + { + this.message = "was squished"; + } + if((this.dmg || (this.spawnflags & DOOR_CRUSH)) && (this.message2 == "")) + { + this.message2 = "was squished by"; + } + + // TODO: other soundpacks + if (this.sounds > 0) + { + this.noise2 = "plats/medplat1.wav"; + this.noise1 = "plats/medplat2.wav"; + } + + // sound when door stops moving + if(this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + // sound when door is moving + if(this.noise2 && this.noise2 != "") + { + precache_sound(this.noise2); + } + + if (!this.wait) + { + this.wait = 3; + } + if (!this.lip) + { + this.lip = 8; + } + + this.state = STATE_BOTTOM; + + if (GetResourceAmount(this, RESOURCE_HEALTH)) + { + //this.canteamdamage = true; // TODO + this.takedamage = DAMAGE_YES; + this.event_damage = door_damage; + } + + if (this.items) + { + this.wait = -1; + } +} + +// spawnflags require key (for now only func_door) +spawnfunc(func_door) +{ + // Quake 1 keys compatibility + if (this.spawnflags & SPAWNFLAGS_GOLD_KEY) + this.itemkeys |= ITEM_KEY_BIT(0); + if (this.spawnflags & SPAWNFLAGS_SILVER_KEY) + this.itemkeys |= ITEM_KEY_BIT(1); + + SetMovedir(this); + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + this.classname = "door"; + + setblocked(this, door_blocked); + this.use = door_use; + + if(this.spawnflags & DOOR_NONSOLID) + this.solid = SOLID_NOT; + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (this.spawnflags & DOOR_START_OPEN) + InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION); + + door_init_shared(this); + + this.pos1 = this.origin; + this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); + + if (!this.speed) + { + this.speed = 100; + } + + settouch(this, door_touch); + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); + + this.reset = door_reset; +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) +{ + int sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.classname = strzone(ReadString()); + this.spawnflags = ReadByte(); + + this.mdl = strzone(ReadString()); + _setmodel(this, this.mdl); + + trigger_common_read(this, true); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.wait = ReadShort(); + this.speed = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + this.ltime = ReadCoord(); + + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.use = door_use; + + LinkDoors(this); + + if(this.spawnflags & DOOR_START_OPEN) + door_init_startopen(this); + + this.move_time = time; + set_movetype(this, MOVETYPE_PUSH); + } + + if(sf & SF_TRIGGER_RESET) + { + door_reset(this); + } + + if(sf & SF_TRIGGER_UPDATE) + { + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + } + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/func/door.qh b/qcsrc/common/mapobjects/func/door.qh new file mode 100644 index 0000000000..181de8b7b8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door.qh @@ -0,0 +1,18 @@ +#pragma once + + +const int DOOR_START_OPEN = BIT(0); +const int DOOR_DONT_LINK = BIT(2); +const int SPAWNFLAGS_GOLD_KEY = BIT(3); // Quake 1 compat, can only be used with func_door! +const int SPAWNFLAGS_SILVER_KEY = BIT(4); // Quake 1 compat, can only be used with func_door! +const int DOOR_TOGGLE = BIT(5); + +const int DOOR_NONSOLID = BIT(10); +const int DOOR_CRUSH = BIT(11); // can't use CRUSH cause that is the same as DOOR_DONT_LINK + + +#ifdef CSQC +// stuff for preload + +.float door_finished; +#endif diff --git a/qcsrc/common/mapobjects/func/door_rotating.qc b/qcsrc/common/mapobjects/func/door_rotating.qc new file mode 100644 index 0000000000..39c02a8669 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_rotating.qc @@ -0,0 +1,159 @@ +#include "door_rotating.qh" +/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS +if two doors touch, they are assumed to be connected and operate as a unit. + +TOGGLE causes the door to wait in both the start and end states for a trigger event. + +BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor. +The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction +must have set trigger_reverse to 1. +BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side. + +START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). + +"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet +"angle" determines the destination angle for opening. negative values reverse the direction. +"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. +"health" if set, door must be shot open +"speed" movement speed (100 default) +"wait" wait before returning (3 default, -1 = never return) +"dmg" damage to inflict when blocked (2 default) +"sounds" +0) no sound +1) stone +2) base +3) stone chain +4) screechy metal +FIXME: only one sound set available at the time being +*/ + +#ifdef GAMEQC +void door_rotating_hit_top(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + if (this.spawnflags & DOOR_TOGGLE) + return; // don't come down automatically + setthink(this, door_rotating_go_down); + this.nextthink = this.ltime + this.wait; +} + +void door_rotating_hit_bottom(entity this) +{ + if (this.noise1 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating + { + this.pos2 = '0 0 0' - this.pos2; + this.lip = 0; + } + this.state = STATE_BOTTOM; +} + +void door_rotating_go_down(entity this) +{ + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + if (this.max_health) + { + this.takedamage = DAMAGE_YES; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + } + + this.state = STATE_DOWN; + SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom); +} + +void door_rotating_go_up(entity this, entity oth) +{ + if (this.state == STATE_UP) + return; // already going up + + if (this.state == STATE_TOP) + { // reset top wait time + this.nextthink = this.ltime + this.wait; + return; + } + if (this.noise2 != "") + _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top); + + string oldmessage; + oldmessage = this.message; + this.message = ""; + SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here? + this.message = oldmessage; +} +#endif + +#ifdef SVQC +void door_rotating_reset(entity this) +{ + this.angles = this.pos1; + this.avelocity = '0 0 0'; + this.state = STATE_BOTTOM; + setthink(this, func_null); + this.nextthink = 0; +} + +void door_rotating_init_startopen(entity this) +{ + this.angles = this.movedir; + this.pos2 = '0 0 0'; + this.pos1 = this.movedir; +} + +spawnfunc(func_door_rotating) +{ + //if (!this.deathtype) // map makers can override this + // this.deathtype = " got in the way"; + + // I abuse "movedir" for denoting the axis for now + if (this.spawnflags & DOOR_ROTATING_XAXIS) + this.movedir = '0 0 1'; + else if (this.spawnflags & DOOR_ROTATING_YAXIS) + this.movedir = '1 0 0'; + else // Z + this.movedir = '0 1 0'; + + if (this.angles_y==0) this.angles_y = 90; + + this.movedir = this.movedir * this.angles_y; + this.angles = '0 0 0'; + + this.avelocity = this.movedir; + if (!InitMovingBrushTrigger(this)) + return; + this.velocity = '0 0 0'; + //this.effects |= EF_LOWPRECISION; + this.classname = "door_rotating"; + + setblocked(this, door_blocked); + this.use = door_use; + + this.pos1 = '0 0 0'; + this.pos2 = this.movedir; + +// DOOR_START_OPEN is to allow an entity to be lighted in the closed position +// but spawn in the open position + if (this.spawnflags & DOOR_START_OPEN) + InitializeEntity(this, door_rotating_init_startopen, INITPRIO_SETLOCATION); + + door_init_shared(this); + if (!this.speed) + { + this.speed = 50; + } + this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating + + settouch(this, door_touch); + +// LinkDoors can't be done until all of the doors have been spawned, so +// the sizes can be detected properly. + InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); + + this.reset = door_rotating_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/func/door_rotating.qh b/qcsrc/common/mapobjects/func/door_rotating.qh new file mode 100644 index 0000000000..5ff572859b --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_rotating.qh @@ -0,0 +1,14 @@ +#pragma once + + +const int DOOR_ROTATING_BIDIR = BIT(1); +const int DOOR_ROTATING_BIDIR_IN_DOWN = BIT(3); + +const int DOOR_ROTATING_XAXIS = BIT(6); +const int DOOR_ROTATING_YAXIS = BIT(7); + + +#ifdef GAMEQC +void door_rotating_go_down(entity this); +void door_rotating_go_up(entity this, entity oth); +#endif diff --git a/qcsrc/common/mapobjects/func/door_secret.qc b/qcsrc/common/mapobjects/func/door_secret.qc new file mode 100644 index 0000000000..f06f39e911 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_secret.qc @@ -0,0 +1,266 @@ +#include "door_secret.qh" +#ifdef SVQC +void fd_secret_move1(entity this); +void fd_secret_move2(entity this); +void fd_secret_move3(entity this); +void fd_secret_move4(entity this); +void fd_secret_move5(entity this); +void fd_secret_move6(entity this); +void fd_secret_done(entity this); + +void fd_secret_use(entity this, entity actor, entity trigger) +{ + float temp; + string message_save; + + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); + this.bot_attack = true; + + // exit if still moving around... + if (this.origin != this.oldorigin) + return; + + message_save = this.message; + this.message = ""; // no more message + SUB_UseTargets(this, actor, trigger); // fire all targets / killtargets + this.message = message_save; + + this.velocity = '0 0 0'; + + // Make a sound, wait a little... + + if (this.noise1 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.nextthink = this.ltime + 0.1; + + temp = 1 - (this.spawnflags & DOOR_SECRET_1ST_LEFT); // 1 or -1 + makevectors(this.mangle); + + if (!this.t_width) + { + if (this.spawnflags & DOOR_SECRET_1ST_DOWN) + this.t_width = fabs(v_up * this.size); + else + this.t_width = fabs(v_right * this.size); + } + + if (!this.t_length) + this.t_length = fabs(v_forward * this.size); + + if (this.spawnflags & DOOR_SECRET_1ST_DOWN) + this.dest1 = this.origin - v_up * this.t_width; + else + this.dest1 = this.origin + v_right * (this.t_width * temp); + + this.dest2 = this.dest1 + v_forward * this.t_length; + SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move1); + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + fd_secret_use(this, NULL, NULL); +} + +// Wait after first movement... +void fd_secret_move1(entity this) +{ + this.nextthink = this.ltime + 1.0; + setthink(this, fd_secret_move2); + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +// Start moving sideways w/sound... +void fd_secret_move2(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.dest2, TSPEED_LINEAR, this.speed, fd_secret_move3); +} + +// Wait here until time to go back... +void fd_secret_move3(entity this) +{ + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); + if (!(this.spawnflags & DOOR_SECRET_OPEN_ONCE) && this.wait >= 0) + { + this.nextthink = this.ltime + this.wait; + setthink(this, fd_secret_move4); + } +} + +// Move backward... +void fd_secret_move4(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move5); +} + +// Wait 1 second... +void fd_secret_move5(entity this) +{ + this.nextthink = this.ltime + 1.0; + setthink(this, fd_secret_move6); + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +void fd_secret_move6(entity this) +{ + if (this.noise2 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); + SUB_CalcMove(this, this.oldorigin, TSPEED_LINEAR, this.speed, fd_secret_done); +} + +void fd_secret_done(entity this) +{ + if (this.spawnflags&DOOR_SECRET_YES_SHOOT) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); + this.takedamage = DAMAGE_YES; + //this.th_pain = fd_secret_use; + } + if (this.noise3 != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); +} + +.float door_finished; + +void secret_blocked(entity this, entity blocker) +{ + if (time < this.door_finished) + return; + this.door_finished = time + 0.5; + //T_Damage (other, this, this, this.dmg, this.dmg, this.deathtype, DT_IMPACT, (this.absmin + this.absmax) * 0.5, '0 0 0', Obituary_Generic); +} + +/* +============== +secret_touch + +Prints messages +================ +*/ +void secret_touch(entity this, entity toucher) +{ + if (!toucher.iscreature) + return; + if (this.door_finished > time) + return; + + this.door_finished = time + 2; + + if (this.message) + { + if (IS_CLIENT(toucher)) + centerprint(toucher, this.message); + play2(toucher, this.noise); + } +} + +void secret_reset(entity this) +{ + if (this.spawnflags & DOOR_SECRET_YES_SHOOT) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); + this.takedamage = DAMAGE_YES; + } + setorigin(this, this.oldorigin); + setthink(this, func_null); + this.nextthink = 0; +} + +/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot +Basic secret door. Slides back, then to the side. Angle determines direction. +wait = # of seconds before coming back +1st_left = 1st move is left of arrow +1st_down = 1st move is down from arrow +always_shoot = even if targeted, keep shootable +t_width = override WIDTH to move back (or height if going down) +t_length = override LENGTH to move sideways +"dmg" damage to inflict when blocked (2 default) + +If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. +"sounds" +1) medieval +2) metal +3) base +*/ + +spawnfunc(func_door_secret) +{ + /*if (!this.deathtype) // map makers can override this + this.deathtype = " got in the way";*/ + + if (!this.dmg) + { + this.dmg = 2; + } + + // Magic formula... + this.mangle = this.angles; + this.angles = '0 0 0'; + this.classname = "door"; + if (!InitMovingBrushTrigger(this)) return; + this.effects |= EF_LOWPRECISION; + + // TODO: other soundpacks + if (this.sounds > 0) + { + this.noise1 = "plats/medplat1.wav"; + this.noise2 = "plats/medplat1.wav"; + this.noise3 = "plats/medplat2.wav"; + } + + // sound on touch + if (this.noise == "") + { + this.noise = "misc/talk.wav"; + } + precache_sound(this.noise); + // sound while moving backwards + if (this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + // sound while moving sideways + if (this.noise2 && this.noise2 != "") + { + precache_sound(this.noise2); + } + // sound when door stops moving + if (this.noise3 && this.noise3 != "") + { + precache_sound(this.noise3); + } + + settouch(this, secret_touch); + setblocked(this, secret_blocked); + this.speed = 50; + this.use = fd_secret_use; + IFTARGETED + { + } + else + this.spawnflags |= DOOR_SECRET_YES_SHOOT; + + if (this.spawnflags & DOOR_SECRET_YES_SHOOT) + { + //this.canteamdamage = true; // TODO + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10000); + this.takedamage = DAMAGE_YES; + this.event_damage = fd_secret_damage; + } + this.oldorigin = this.origin; + if (!this.wait) this.wait = 5; // seconds before closing + + this.reset = secret_reset; + this.reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/door_secret.qh b/qcsrc/common/mapobjects/func/door_secret.qh new file mode 100644 index 0000000000..ee575bc424 --- /dev/null +++ b/qcsrc/common/mapobjects/func/door_secret.qh @@ -0,0 +1,8 @@ +#pragma once + + +const int DOOR_SECRET_OPEN_ONCE = BIT(0); // stays open - LEGACY, set wait to -1 instead +const int DOOR_SECRET_1ST_LEFT = BIT(1); // 1st move is left of arrow +const int DOOR_SECRET_1ST_DOWN = BIT(2); // 1st move is down from arrow +const int DOOR_SECRET_NO_SHOOT = BIT(3); // only opened by trigger +const int DOOR_SECRET_YES_SHOOT = BIT(4); // shootable even if targeted diff --git a/qcsrc/common/mapobjects/func/fourier.qc b/qcsrc/common/mapobjects/func/fourier.qc new file mode 100644 index 0000000000..28e0f0f7c7 --- /dev/null +++ b/qcsrc/common/mapobjects/func/fourier.qc @@ -0,0 +1,89 @@ +#include "fourier.qh" +#ifdef SVQC +/*QUAKED spawnfunc_func_fourier (0 .5 .8) ? +Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions. +netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults +speed: how long one cycle of frequency multiplier 1 in seconds (default 4) +height: amplitude modifier (default 32) +phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0) +noise: path/name of looping .wav file to play. +dmg: Do this mutch dmg every .dmgtime intervall when blocked +dmgtime: See above. +*/ + +void func_fourier_controller_think(entity this) +{ + vector v; + float n, i, t; + + this.nextthink = time + 0.1; + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + + n = floor((tokenize_console(this.owner.netname)) / 5); + t = this.nextthink * this.owner.cnt + this.owner.phase * 360; + + v = this.owner.destvec; + + for(i = 0; i < n; ++i) + { + makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0'); + v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * this.owner.height * v_forward_y; + } + + if(this.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed + // * 10 so it will arrive in 0.1 sec + this.owner.velocity = (v - this.owner.origin) * 10; +} + +spawnfunc(func_fourier) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + + if (!this.speed) + this.speed = 4; + if (!this.height) + this.height = 32; + this.destvec = this.origin; + this.cnt = 360 / this.speed; + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if(this.netname == "") + this.netname = "1 0 0 0 1"; + + if (!InitMovingBrushTrigger(this)) + return; + + this.active = ACTIVE_ACTIVE; + + // wait for targets to spawn + controller = new(func_fourier_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_fourier_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/fourier.qh b/qcsrc/common/mapobjects/func/fourier.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/func/fourier.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/ladder.qc b/qcsrc/common/mapobjects/func/ladder.qc new file mode 100644 index 0000000000..020ecca085 --- /dev/null +++ b/qcsrc/common/mapobjects/func/ladder.qc @@ -0,0 +1,148 @@ +#include "ladder.qh" +REGISTER_NET_LINKED(ENT_CLIENT_LADDER) + +void func_ladder_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + if(IS_VEHICLE(toucher)) + return; +#elif defined(CSQC) + if(!toucher.isplayermodel) + return; +#endif + + EXACTTRIGGER_TOUCH(this, toucher); + + toucher.ladder_time = time + 0.1; + toucher.ladder_entity = this; +} + +#ifdef SVQC +bool func_ladder_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER); + + WriteString(MSG_ENTITY, this.classname); + WriteByte(MSG_ENTITY, this.skin); + WriteCoord(MSG_ENTITY, this.speed); + + trigger_common_write(this, false); + + return true; +} + +void func_ladder_link(entity this) +{ + trigger_link(this, func_ladder_send); + //this.model = "null"; +} + +void func_ladder_init(entity this) +{ + settouch(this, func_ladder_touch); + trigger_init(this); + func_ladder_link(this); + + if(min(this.absmax.x - this.absmin.x, this.absmax.y - this.absmin.y) > 100) + return; + + entity tracetest_ent = spawn(); + setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); + tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + + vector top_min = (this.absmin + this.absmax) / 2; + top_min.z = this.absmax.z; + vector top_max = top_min; + top_max.z += PL_MAX_CONST.z - PL_MIN_CONST.z; + tracebox(top_max + jumpstepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + tracebox(top_max + stepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x + && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) + { + // move top on one side + top_max.y = top_min.y = this.absmin.y + (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; + } + else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y + && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) + { + // move top on one side + top_max.x = top_min.x = this.absmin.x + (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; + } + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + if(trace_startsolid) + { + if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x + && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) + { + // alternatively on the other side + top_max.y = top_min.y = this.absmax.y - (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; + } + else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y + && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) + { + // alternatively on the other side + top_max.x = top_min.x = this.absmax.x - (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; + } + tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); + } + } + } + } + if(trace_startsolid || trace_endpos.z < this.absmax.z) + { + delete(tracetest_ent); + return; + } + + this.bot_pickup = true; // allow bots to make use of this ladder + float cost = waypoint_getlinearcost(trace_endpos.z - this.absmin.z); + top_min = trace_endpos; + waypoint_spawnforteleporter_boxes(this, WAYPOINTFLAG_LADDER, this.absmin, this.absmax, top_min, top_min, cost); +} + +spawnfunc(func_ladder) +{ + IL_PUSH(g_ladders, this); // TODO: also func_water? bots currently loop through func_ladder only + + func_ladder_init(this); +} + +spawnfunc(func_water) +{ + func_ladder_init(this); +} + +#elif defined(CSQC) +.float speed; + +void func_ladder_remove(entity this) +{ + strfree(this.classname); +} + +NET_HANDLE(ENT_CLIENT_LADDER, bool isnew) +{ + this.classname = strzone(ReadString()); + this.skin = ReadByte(); + this.speed = ReadCoord(); + + trigger_common_read(this, false); + + this.solid = SOLID_TRIGGER; + settouch(this, func_ladder_touch); + this.drawmask = MASK_NORMAL; + this.move_time = time; + this.entremove = func_ladder_remove; + + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/ladder.qh b/qcsrc/common/mapobjects/func/ladder.qh new file mode 100644 index 0000000000..26cbbda032 --- /dev/null +++ b/qcsrc/common/mapobjects/func/ladder.qh @@ -0,0 +1,4 @@ +#pragma once + +.float ladder_time; +.entity ladder_entity; diff --git a/qcsrc/common/mapobjects/func/pendulum.qc b/qcsrc/common/mapobjects/func/pendulum.qc new file mode 100644 index 0000000000..a59f7a93ba --- /dev/null +++ b/qcsrc/common/mapobjects/func/pendulum.qc @@ -0,0 +1,77 @@ +#include "pendulum.qh" +#ifdef SVQC +.float freq; +void func_pendulum_controller_think(entity this) +{ + float v; + this.nextthink = time + 0.1; + + if (!(this.owner.active == ACTIVE_ACTIVE)) + { + this.owner.avelocity_x = 0; + return; + } + + // calculate sinewave using makevectors + makevectors((this.nextthink * this.owner.freq + this.owner.phase) * '0 360 0'); + v = this.owner.speed * v_forward_y + this.cnt; + if(this.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed + { + // * 10 so it will arrive in 0.1 sec + this.owner.avelocity_z = (remainder(v - this.owner.angles_z, 360)) * 10; + } +} + +spawnfunc(func_pendulum) +{ + entity controller; + if (this.noise != "") + { + precache_sound(this.noise); + soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + + this.active = ACTIVE_ACTIVE; + + // keys: angle, speed, phase, noise, freq + + if(!this.speed) + this.speed = 30; + // not initializing this.dmg to 2, to allow damageless pendulum + + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + setblocked(this, generic_plat_blocked); + + this.avelocity_z = 0.0000001; + if (!InitMovingBrushTrigger(this)) + return; + + if(!this.freq) + { + // find pendulum length (same formula as Q3A) + this.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(this.mins_z)))); + } + + // copy initial angle + this.cnt = this.angles_z; + + // wait for targets to spawn + controller = new(func_pendulum_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_pendulum_controller_think); + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + //this.effects |= EF_LOWPRECISION; + + // TODO make a reset function for this one +} +#endif diff --git a/qcsrc/common/mapobjects/func/pendulum.qh b/qcsrc/common/mapobjects/func/pendulum.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/func/pendulum.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/plat.qc b/qcsrc/common/mapobjects/func/plat.qc new file mode 100644 index 0000000000..53dbed02fa --- /dev/null +++ b/qcsrc/common/mapobjects/func/plat.qc @@ -0,0 +1,191 @@ +#include "plat.qh" +REGISTER_NET_LINKED(ENT_CLIENT_PLAT) + +#ifdef SVQC +void plat_link(entity this); + +void plat_delayedinit(entity this) +{ + plat_link(this); + plat_spawn_inside_trigger(this); // the "start moving" trigger +} + +float plat_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.platmovetype_start); + WriteByte(MSG_ENTITY, this.platmovetype_turn); + WriteByte(MSG_ENTITY, this.platmovetype_end); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + + WriteShort(MSG_ENTITY, this.speed); + WriteShort(MSG_ENTITY, this.height); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + + WriteShort(MSG_ENTITY, this.dmg); + } + + if(sf & SF_TRIGGER_RESET) + { + // used on client + } + + return true; +} + +void plat_link(entity this) +{ + //Net_LinkEntity(this, 0, false, plat_send); +} + +spawnfunc(func_plat) +{ + if (this.spawnflags & CRUSH) + { + this.dmg = 10000; + } + + if (this.dmg && (this.message == "")) + { + this.message = "was squished"; + } + if (this.dmg && (this.message2 == "")) + { + this.message2 = "was squished by"; + } + + if (this.sounds == 1) + { + this.noise = "plats/plat1.wav"; + this.noise1 = "plats/plat2.wav"; + } + + if (this.sounds == 2) + { + this.noise = "plats/medplat1.wav"; + this.noise1 = "plats/medplat2.wav"; + } + + // WARNING: backwards compatibility because people don't use already existing fields :( + if (this.sound1) + this.noise = this.sound1; + if (this.sound2) + this.noise1 = this.sound2; + + if(this.noise && this.noise != "") + { + precache_sound(this.noise); + } + if(this.noise1 && this.noise1 != "") + { + precache_sound(this.noise1); + } + + this.mangle = this.angles; + this.angles = '0 0 0'; + + this.classname = "plat"; + this.draggable = drag_undraggable; + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + setsize (this, this.mins , this.maxs); + + setblocked(this, plat_crush); + + if (!this.speed) this.speed = 150; + if (!this.lip) this.lip = 16; + if (!this.height) this.height = this.size.z - this.lip; + + this.pos1 = this.origin; + this.pos2 = this.origin; + this.pos2_z = this.origin.z - this.height; + + this.reset = plat_reset; + this.reset(this); + + InitializeEntity(this, plat_delayedinit, INITPRIO_FINDTARGET); +} +#elif defined(CSQC) +void plat_draw(entity this) +{ + Movetype_Physics_NoMatchServer(this); + //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); +} + +NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.platmovetype_start = ReadByte(); + this.platmovetype_turn = ReadByte(); + this.platmovetype_end = ReadByte(); + this.spawnflags = ReadByte(); + + this.model = strzone(ReadString()); + _setmodel(this, this.model); + + trigger_common_read(this, true); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + this.speed = ReadShort(); + this.height = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + + this.dmg = ReadShort(); + + this.classname = "plat"; + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.drawmask = MASK_NORMAL; + this.draw = plat_draw; + if (isnew) IL_PUSH(g_drawables, this); + this.use = plat_use; + this.entremove = trigger_remove_generic; + + plat_reset(this); // also called here + + set_movetype(this, MOVETYPE_PUSH); + this.move_time = time; + + plat_spawn_inside_trigger(this); + } + + if(sf & SF_TRIGGER_RESET) + { + plat_reset(this); + + this.move_time = time; + } + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/func/plat.qh b/qcsrc/common/mapobjects/func/plat.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/func/plat.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/pointparticles.qc b/qcsrc/common/mapobjects/func/pointparticles.qc new file mode 100644 index 0000000000..7de5a03ef8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/pointparticles.qc @@ -0,0 +1,341 @@ +#include "pointparticles.qh" +REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) + +#ifdef SVQC +// NOTE: also contains func_sparks + +bool pointparticles_SendEntity(entity this, entity to, float sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); + + // optional features to save space + sendflags = sendflags & 0x0F; + if(this.spawnflags & PARTICLES_IMPULSE) + sendflags |= SF_POINTPARTICLES_IMPULSE; // absolute count on toggle-on + if(this.movedir != '0 0 0' || this.velocity != '0 0 0') + sendflags |= SF_POINTPARTICLES_MOVING; // 4 bytes - saves CPU + if(this.waterlevel || this.count != 1) + sendflags |= SF_POINTPARTICLES_JITTER_AND_COUNT; // 4 bytes - obscure features almost never used + if(this.mins != '0 0 0' || this.maxs != '0 0 0') + sendflags |= SF_POINTPARTICLES_BOUNDS; // 14 bytes - saves lots of space + + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_TRIGGER_UPDATE) + { + if(this.active == ACTIVE_ACTIVE) + WriteCoord(MSG_ENTITY, this.impulse); + else + WriteCoord(MSG_ENTITY, 0); // off + } + if(sendflags & SF_TRIGGER_RESET) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_TRIGGER_INIT) + { + if(this.model != "null") + { + WriteShort(MSG_ENTITY, this.modelindex); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + } + } + else + { + WriteShort(MSG_ENTITY, 0); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + WriteVector(MSG_ENTITY, this.maxs); + } + } + WriteShort(MSG_ENTITY, this.cnt); + WriteString(MSG_ENTITY, this.mdl); + if(sendflags & SF_POINTPARTICLES_MOVING) + { + WriteShort(MSG_ENTITY, compressShortVector(this.velocity)); + WriteShort(MSG_ENTITY, compressShortVector(this.movedir)); + } + if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) + { + WriteShort(MSG_ENTITY, this.waterlevel * 16.0); + WriteByte(MSG_ENTITY, this.count * 16.0); + } + WriteString(MSG_ENTITY, this.noise); + if(this.noise != "") + { + WriteByte(MSG_ENTITY, floor(this.atten * 64)); + WriteByte(MSG_ENTITY, floor(this.volume * 255)); + } + WriteString(MSG_ENTITY, this.bgmscript); + if(this.bgmscript != "") + { + WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); + } + } + return 1; +} + +void pointparticles_think(entity this) +{ + if(this.origin != this.oldorigin) + { + this.SendFlags |= SF_TRIGGER_RESET; + this.oldorigin = this.origin; + } + this.nextthink = time; +} + +spawnfunc(func_pointparticles) +{ + if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); } + if(this.noise != "") precache_sound(this.noise); + if(this.mdl != "") this.cnt = 0; // use a good handler + + if(!this.bgmscriptsustain) this.bgmscriptsustain = 1; + else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0; + + if(!this.atten) this.atten = ATTEN_NORM; + else if(this.atten < 0) this.atten = 0; + if(!this.volume) this.volume = 1; + if(!this.count) this.count = 1; + if(!this.impulse) this.impulse = 1; + + if(!this.modelindex) + { + setorigin(this, this.origin + this.mins); + setsize(this, '0 0 0', this.maxs - this.mins); + } + //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl); + this.setactive = generic_netlinked_setactive; + + Net_LinkEntity(this, (this.spawnflags & PARTICLES_VISCULLING), 0, pointparticles_SendEntity); + + IFTARGETED + { + // backwards compatibility + this.use = generic_netlinked_legacy_use; + } + this.reset = generic_netlinked_reset; + this.reset(this); + setthink(this, pointparticles_think); + this.nextthink = time; +} + +spawnfunc(func_sparks) +{ + if(this.count < 1) { + this.count = 25.0; // nice default value + } + + if(this.impulse < 0.5) { + this.impulse = 2.5; // nice default value + } + + this.mins = '0 0 0'; + this.maxs = '0 0 0'; + this.velocity = '0 0 -1'; + this.mdl = "TE_SPARK"; + this.cnt = 0; // use mdl + + spawnfunc_func_pointparticles(this); +} +#elif defined(CSQC) + +.int dphitcontentsmask; + +entityclass(PointParticles); +classfield(PointParticles) .int cnt; // effect number +classfield(PointParticles) .vector velocity; // particle velocity +classfield(PointParticles) .float waterlevel; // direction jitter +classfield(PointParticles) .int count; // count multiplier +classfield(PointParticles) .int impulse; // density +classfield(PointParticles) .string noise; // sound +classfield(PointParticles) .float atten; +classfield(PointParticles) .float volume; +classfield(PointParticles) .float absolute; // 1 = count per second is absolute, ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = only spawn at toggle +classfield(PointParticles) .vector movedir; // trace direction +classfield(PointParticles) .float glow_color; // palette index + +const int ABSOLUTE_ONLY_SPAWN_AT_TOGGLE = 2; + +void Draw_PointParticles(entity this) +{ + float n, i, fail; + vector p; + vector sz; + vector o; + o = this.origin; + sz = this.maxs - this.mins; + n = doBGMScript(this); + if(this.absolute == ABSOLUTE_ONLY_SPAWN_AT_TOGGLE) + { + if(n >= 0) + n = this.just_toggled ? this.impulse : 0; + else + n = this.impulse * drawframetime; + } + else + { + n *= this.impulse * drawframetime; + if(this.just_toggled) + if(n < 1) + n = 1; + } + if(n == 0) + return; + fail = 0; + for(i = random(); i <= n && fail <= 64*n; ++i) + { + p = o + this.mins; + p.x += random() * sz.x; + p.y += random() * sz.y; + p.z += random() * sz.z; + if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL)) + { + if(this.movedir != '0 0 0') + { + traceline(p, p + normalize(this.movedir) * 4096, 0, NULL); + p = trace_endpos; + int eff_num; + if(this.cnt) + eff_num = this.cnt; + else + eff_num = _particleeffectnum(this.mdl); + __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count); + } + else + { + int eff_num; + if(this.cnt) + eff_num = this.cnt; + else + eff_num = _particleeffectnum(this.mdl); + __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count); + } + if(this.noise != "") + { + setorigin(this, p); + _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten); + } + this.just_toggled = 0; + } + else if(this.absolute) + { + ++fail; + --i; + } + } + setorigin(this, o); +} + +void Ent_PointParticles_Remove(entity this) +{ + strfree(this.noise); + strfree(this.bgmscript); + strfree(this.mdl); +} + +NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) +{ + float i; + vector v; + int sendflags = ReadByte(); + if(sendflags & SF_TRIGGER_UPDATE) + { + i = ReadCoord(); // density (<0: point, >0: volume) + if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed + this.just_toggled = 1; + this.impulse = i; + } + if(sendflags & SF_TRIGGER_RESET) + { + this.origin = ReadVector(); + } + if(sendflags & SF_TRIGGER_INIT) + { + this.modelindex = ReadShort(); + if(sendflags & SF_POINTPARTICLES_BOUNDS) + { + if(this.modelindex) + { + this.mins = ReadVector(); + this.maxs = ReadVector(); + } + else + { + this.mins = '0 0 0'; + this.maxs = ReadVector(); + } + } + else + { + this.mins = this.maxs = '0 0 0'; + } + + this.cnt = ReadShort(); // effect number + this.mdl = strzone(ReadString()); // effect string + + if(sendflags & SF_POINTPARTICLES_MOVING) + { + this.velocity = decompressShortVector(ReadShort()); + this.movedir = decompressShortVector(ReadShort()); + } + else + { + this.velocity = this.movedir = '0 0 0'; + } + if(sendflags & SF_POINTPARTICLES_JITTER_AND_COUNT) + { + this.waterlevel = ReadShort() / 16.0; + this.count = ReadByte() / 16.0; + } + else + { + this.waterlevel = 0; + this.count = 1; + } + strcpy(this.noise, ReadString()); + if(this.noise != "") + { + this.atten = ReadByte() / 64.0; + this.volume = ReadByte() / 255.0; + } + strcpy(this.bgmscript, ReadString()); + if(this.bgmscript != "") + { + this.bgmscriptattack = ReadByte() / 64.0; + this.bgmscriptdecay = ReadByte() / 64.0; + this.bgmscriptsustain = ReadByte() / 255.0; + this.bgmscriptrelease = ReadByte() / 64.0; + } + BGMScript_InitEntity(this); + } + + return = true; + + if(sendflags & SF_TRIGGER_UPDATE) + { + this.absolute = (this.impulse >= 0); + if(!this.absolute) + { + v = this.maxs - this.mins; + this.impulse *= -v.x * v.y * v.z / (64**3); // relative: particles per 64^3 cube + } + } + + if(sendflags & SF_POINTPARTICLES_IMPULSE) + this.absolute = ABSOLUTE_ONLY_SPAWN_AT_TOGGLE; + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.solid = SOLID_NOT; + this.draw = Draw_PointParticles; + if (isnew) IL_PUSH(g_drawables, this); + this.entremove = Ent_PointParticles_Remove; +} +#endif diff --git a/qcsrc/common/mapobjects/func/pointparticles.qh b/qcsrc/common/mapobjects/func/pointparticles.qh new file mode 100644 index 0000000000..b167527bc8 --- /dev/null +++ b/qcsrc/common/mapobjects/func/pointparticles.qh @@ -0,0 +1,11 @@ +#pragma once + +// spawnflags +const int PARTICLES_IMPULSE = BIT(1); +const int PARTICLES_VISCULLING = BIT(2); + +// sendflags +const int SF_POINTPARTICLES_IMPULSE = BIT(4); +const int SF_POINTPARTICLES_MOVING = BIT(5); // Send velocity and movedir +const int SF_POINTPARTICLES_JITTER_AND_COUNT = BIT(6); // Send waterlevel (=jitter) and count +const int SF_POINTPARTICLES_BOUNDS = BIT(7); // Send min and max of the brush diff --git a/qcsrc/common/mapobjects/func/rainsnow.qc b/qcsrc/common/mapobjects/func/rainsnow.qc new file mode 100644 index 0000000000..c765a4293b --- /dev/null +++ b/qcsrc/common/mapobjects/func/rainsnow.qc @@ -0,0 +1,142 @@ +#include "rainsnow.qh" +REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) + +#ifdef SVQC +bool rainsnow_SendEntity(entity this, entity to, float sf) +{ + vector myorg = this.origin + this.mins; + vector mysize = this.maxs - this.mins; + WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW); + WriteByte(MSG_ENTITY, this.state); + WriteVector(MSG_ENTITY, myorg); + WriteVector(MSG_ENTITY, mysize); + WriteShort(MSG_ENTITY, compressShortVector(this.dest)); + WriteShort(MSG_ENTITY, this.count); + WriteByte(MSG_ENTITY, this.cnt); + return true; +} + +/*QUAKED spawnfunc_func_rain (0 .5 .8) ? +This is an invisible area like a trigger, which rain falls inside of. + +Keys: +"velocity" + falling direction (should be something like '0 0 -700', use the X and Y velocity for wind) +"cnt" + sets color of rain (default 12 - white) +"count" + adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 +*/ +spawnfunc(func_rain) +{ + this.dest = this.velocity; + this.velocity = '0 0 0'; + if (!this.dest) + this.dest = '0 0 -700'; + this.angles = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.solid = SOLID_NOT; + SetBrushEntityModel(this); + if (!this.cnt) + { + this.cnt = 12; + } + if (!this.count) + this.count = 2000; + // relative to absolute particle count + this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); + if (this.count < 1) + this.count = 1; + if(this.count > 65535) + this.count = 65535; + + this.state = RAINSNOW_RAIN; + + Net_LinkEntity(this, false, 0, rainsnow_SendEntity); +} + + +/*QUAKED spawnfunc_func_snow (0 .5 .8) ? +This is an invisible area like a trigger, which snow falls inside of. + +Keys: +"velocity" + falling direction (should be something like '0 0 -300', use the X and Y velocity for wind) +"cnt" + sets color of rain (default 12 - white) +"count" + adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 +*/ +spawnfunc(func_snow) +{ + this.dest = this.velocity; + this.velocity = '0 0 0'; + if (!this.dest) + this.dest = '0 0 -300'; + this.angles = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.solid = SOLID_NOT; + SetBrushEntityModel(this); + if (!this.cnt) + { + this.cnt = 12; + } + if (!this.count) + this.count = 2000; + // relative to absolute particle count + this.count = 0.1 * this.count * (this.size_x / 1024) * (this.size_y / 1024); + if (this.count < 1) + this.count = 1; + if(this.count > 65535) + this.count = 65535; + + this.state = RAINSNOW_SNOW; + + Net_LinkEntity(this, false, 0, rainsnow_SendEntity); +} +#elif defined(CSQC) +float autocvar_cl_rainsnow_maxdrawdist = 2048; + +void Draw_Rain(entity this) +{ + vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; + maxdist.z = 5; + if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) + //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) + te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); +} + +void Draw_Snow(entity this) +{ + vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; + maxdist.z = 5; + if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) + //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) + te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); +} + +NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) +{ + this.state = ReadByte(); // Rain, Snow, or Whatever + this.origin = ReadVector(); + this.maxs = ReadVector(); + this.velocity = decompressShortVector(ReadShort()); + this.count = ReadShort(); + this.glow_color = ReadByte(); // color + + return = true; + + this.mins = -0.5 * this.maxs; + this.maxs = 0.5 * this.maxs; + this.origin = this.origin - this.mins; + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.solid = SOLID_NOT; + if (isnew) IL_PUSH(g_drawables, this); + if(this.state == RAINSNOW_RAIN) + this.draw = Draw_Rain; + else + this.draw = Draw_Snow; +} +#endif diff --git a/qcsrc/common/mapobjects/func/rainsnow.qh b/qcsrc/common/mapobjects/func/rainsnow.qh new file mode 100644 index 0000000000..d60eb4f481 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rainsnow.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int RAINSNOW_SNOW = 0; +const int RAINSNOW_RAIN = 1; diff --git a/qcsrc/common/mapobjects/func/rotating.qc b/qcsrc/common/mapobjects/func/rotating.qc new file mode 100644 index 0000000000..35351ee08e --- /dev/null +++ b/qcsrc/common/mapobjects/func/rotating.qc @@ -0,0 +1,110 @@ +#include "rotating.qh" +#ifdef SVQC + +void func_rotating_setactive(entity this, int astate) +{ + if (astate == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + this.active = ACTIVE_NOT; + else + this.active = ACTIVE_ACTIVE; + } + else + this.active = astate; + + if(this.active == ACTIVE_NOT) + { + this.avelocity = '0 0 0'; + stopsound(this, CH_AMBIENT_SINGLE); + } + else + { + this.avelocity = this.pos1; + if(this.noise && this.noise != "") + { + _sound(this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + } +} + +void func_rotating_reset(entity this) +{ + // TODO: reset angles as well? + + if(this.spawnflags & FUNC_ROTATING_STARTOFF) + { + this.setactive(this, ACTIVE_NOT); + } + else + { + this.setactive(this, ACTIVE_ACTIVE); + } +} + +void func_rotating_init_for_player(entity this, entity player) +{ + if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto (MSG_ONE, this, CH_AMBIENT_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } +} + +/*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS +Brush model that spins in place on one axis (default Z). +speed : speed to rotate (in degrees per second) +noise : path/name of looping .wav file to play. +dmg : Do this mutch dmg every .dmgtime intervall when blocked +dmgtime : See above. +*/ + +spawnfunc(func_rotating) +{ + if (this.noise && this.noise != "") + { + precache_sound(this.noise); + } + + this.setactive = func_rotating_setactive; + + if (!this.speed) + this.speed = 100; + if (this.spawnflags & FUNC_ROTATING_XAXIS) + this.avelocity = '0 0 1' * this.speed; + else if (this.spawnflags & FUNC_ROTATING_YAXIS) + this.avelocity = '1 0 0' * this.speed; + else // Z + this.avelocity = '0 1 0' * this.speed; + + this.pos1 = this.avelocity; + + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + + + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + + this.dmgtime2 = time; + + if (!InitMovingBrushTrigger(this)) + return; + // no EF_LOWPRECISION here, as rounding angles is bad + + setblocked(this, generic_plat_blocked); + + // wait for targets to spawn + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + this.reset = func_rotating_reset; + this.reset(this); + + // maybe send sound to new players + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_rotating_init_for_player; +} +#endif diff --git a/qcsrc/common/mapobjects/func/rotating.qh b/qcsrc/common/mapobjects/func/rotating.qh new file mode 100644 index 0000000000..ad1b6ec920 --- /dev/null +++ b/qcsrc/common/mapobjects/func/rotating.qh @@ -0,0 +1,6 @@ +#pragma once + + +const int FUNC_ROTATING_XAXIS = BIT(2); +const int FUNC_ROTATING_YAXIS = BIT(3); +const int FUNC_ROTATING_STARTOFF = BIT(4); diff --git a/qcsrc/common/mapobjects/func/stardust.qc b/qcsrc/common/mapobjects/func/stardust.qc new file mode 100644 index 0000000000..9c2fba8ada --- /dev/null +++ b/qcsrc/common/mapobjects/func/stardust.qc @@ -0,0 +1,9 @@ +#include "stardust.qh" +#ifdef SVQC +spawnfunc(func_stardust) +{ + this.effects = EF_STARDUST; + + CSQCMODEL_AUTOINIT(this); +} +#endif diff --git a/qcsrc/common/mapobjects/func/stardust.qh b/qcsrc/common/mapobjects/func/stardust.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/func/stardust.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/func/train.qc b/qcsrc/common/mapobjects/func/train.qc new file mode 100644 index 0000000000..4e9c334562 --- /dev/null +++ b/qcsrc/common/mapobjects/func/train.qc @@ -0,0 +1,351 @@ +#include "train.qh" +.float train_wait_turning; +.entity future_target; +void train_next(entity this); +#ifdef SVQC +void train_use(entity this, entity actor, entity trigger); +#endif +void train_wait(entity this) +{ + SUB_UseTargets(this.enemy, NULL, NULL); + this.enemy = NULL; + + // if turning is enabled, the train will turn toward the next point while waiting + if(this.platmovetype_turn && !this.train_wait_turning) + { + entity targ, cp; + vector ang; + targ = this.future_target; + if((this.spawnflags & TRAIN_CURVE) && targ.curvetarget) + cp = find(NULL, targetname, targ.curvetarget); + else + cp = NULL; + + if(cp) // bezier curves movement + ang = cp.origin - (this.origin - this.view_ofs); // use the origin of the control point of the next path_corner + else // linear movement + ang = targ.origin - (this.origin - this.view_ofs); // use the origin of the next path_corner + ang = vectoangles(ang); + ang_x = -ang_x; // flip up / down orientation + + if(this.wait > 0) // slow turning + SUB_CalcAngleMove(this, ang, TSPEED_TIME, this.ltime - time + this.wait, train_wait); + else // instant turning + SUB_CalcAngleMove(this, ang, TSPEED_TIME, 0.0000001, train_wait); + this.train_wait_turning = true; + return; + } + +#ifdef SVQC + if(this.noise != "") + stopsoundto(MSG_BROADCAST, this, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway +#endif + +#ifdef SVQC + entity tg = this.future_target; + if(tg.spawnflags & TRAIN_NEEDACTIVATION) + { + this.use = train_use; + setthink(this, func_null); + this.nextthink = 0; + } + else +#endif + if(this.wait < 0 || this.train_wait_turning) // no waiting or we already waited while turning + { + this.train_wait_turning = false; + train_next(this); + } + else + { + setthink(this, train_next); + this.nextthink = this.ltime + this.wait; + } +} + +entity train_next_find(entity this) +{ + if(this.target_random) + { + RandomSelection_Init(); + for(entity t = NULL; (t = find(t, targetname, this.target));) + { + RandomSelection_AddEnt(t, 1, 0); + } + return RandomSelection_chosen_ent; + } + else + { + return find(NULL, targetname, this.target); + } +} + +void train_next(entity this) +{ + entity targ = NULL, cp = NULL; + vector cp_org = '0 0 0'; + + targ = this.future_target; + + this.target = targ.target; + this.target_random = targ.target_random; + this.future_target = train_next_find(targ); + + if (this.spawnflags & TRAIN_CURVE) + { + if(targ.curvetarget) + { + cp = find(NULL, targetname, targ.curvetarget); // get its second target (the control point) + cp_org = cp.origin - this.view_ofs; // no control point found, assume a straight line to the destination + } + } + if (this.target == "") + objerror(this, "train_next: no next target"); + this.wait = targ.wait; + if (!this.wait) + this.wait = 0.1; + + if(targ.platmovetype) + { + // this path_corner contains a movetype overrider, apply it + this.platmovetype_start = targ.platmovetype_start; + this.platmovetype_end = targ.platmovetype_end; + } + else + { + // this path_corner doesn't contain a movetype overrider, use the train's defaults + this.platmovetype_start = this.platmovetype_start_default; + this.platmovetype_end = this.platmovetype_end_default; + } + + if (targ.speed) + { + if (cp) + SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + else + SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); + } + else + { + if (cp) + SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); + else + SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); + } + + if(this.noise != "") + _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); +} + +REGISTER_NET_LINKED(ENT_CLIENT_TRAIN) + +#ifdef SVQC +float train_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN); + WriteByte(MSG_ENTITY, sf); + + if(sf & SF_TRIGGER_INIT) + { + WriteString(MSG_ENTITY, this.platmovetype); + WriteByte(MSG_ENTITY, this.platmovetype_turn); + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteString(MSG_ENTITY, this.model); + + trigger_common_write(this, true); + + WriteString(MSG_ENTITY, this.curvetarget); + + WriteVector(MSG_ENTITY, this.pos1); + WriteVector(MSG_ENTITY, this.pos2); + + WriteVector(MSG_ENTITY, this.size); + + WriteVector(MSG_ENTITY, this.view_ofs); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + + WriteShort(MSG_ENTITY, this.speed); + WriteShort(MSG_ENTITY, this.height); + WriteByte(MSG_ENTITY, this.lip); + WriteByte(MSG_ENTITY, this.state); + WriteByte(MSG_ENTITY, this.wait); + + WriteShort(MSG_ENTITY, this.dmg); + WriteByte(MSG_ENTITY, this.dmgtime); + } + + if(sf & SF_TRIGGER_RESET) + { + // used on client + } + + return true; +} + +void train_link(entity this) +{ + //Net_LinkEntity(this, 0, false, train_send); +} + +void train_use(entity this, entity actor, entity trigger) +{ + this.nextthink = this.ltime + 1; + setthink(this, train_next); + this.use = func_null; // not again, next target can set it again if needed + if(trigger.target2 && trigger.target2 != "") + this.future_target = find(NULL, targetname, trigger.target2); +} + +void func_train_find(entity this) +{ + entity targ = train_next_find(this); + this.target = targ.target; + this.target_random = targ.target_random; + // save the future target for later + this.future_target = train_next_find(targ); + if (this.target == "") + objerror(this, "func_train_find: no next target"); + setorigin(this, targ.origin - this.view_ofs); + + if(!(this.spawnflags & TRAIN_NEEDACTIVATION)) + { + this.nextthink = this.ltime + 1; + setthink(this, train_next); + } + + train_link(this); +} + +#endif + +/*QUAKED spawnfunc_func_train (0 .5 .8) ? +Ridable platform, targets spawnfunc_path_corner path to follow. +speed : speed the train moves (can be overridden by each spawnfunc_path_corner) +target : targetname of first spawnfunc_path_corner (starts here) +*/ +#ifdef SVQC +spawnfunc(func_train) +{ + if (this.noise != "") + precache_sound(this.noise); + + if (this.target == "") + objerror(this, "func_train without a target"); + if (!this.speed) + this.speed = 100; + + if (!InitMovingBrushTrigger(this)) + return; + this.effects |= EF_LOWPRECISION; + + if(this.spawnflags & TRAIN_NEEDACTIVATION) + this.use = train_use; + + if (this.spawnflags & TRAIN_TURN) + { + this.platmovetype_turn = true; + this.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now + } + else + this.view_ofs = this.mins; + + // wait for targets to spawn + InitializeEntity(this, func_train_find, INITPRIO_FINDTARGET); + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message2 == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if(!set_platmovetype(this, this.platmovetype)) + return; + this.platmovetype_start_default = this.platmovetype_start; + this.platmovetype_end_default = this.platmovetype_end; + + // TODO make a reset function for this one +} +#elif defined(CSQC) +void train_draw(entity this) +{ + //Movetype_Physics_NoMatchServer(); + Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy); +} + +NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) +{ + float sf = ReadByte(); + + if(sf & SF_TRIGGER_INIT) + { + this.platmovetype = strzone(ReadString()); + this.platmovetype_turn = ReadByte(); + this.spawnflags = ReadByte(); + + this.model = strzone(ReadString()); + _setmodel(this, this.model); + + trigger_common_read(this, true); + + this.curvetarget = strzone(ReadString()); + + this.pos1 = ReadVector(); + this.pos2 = ReadVector(); + + this.size = ReadVector(); + + this.view_ofs = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + this.speed = ReadShort(); + this.height = ReadShort(); + this.lip = ReadByte(); + this.state = ReadByte(); + this.wait = ReadByte(); + + this.dmg = ReadShort(); + this.dmgtime = ReadByte(); + + this.classname = "func_train"; + this.solid = SOLID_BSP; + set_movetype(this, MOVETYPE_PUSH); + this.drawmask = MASK_NORMAL; + this.draw = train_draw; + if (isnew) IL_PUSH(g_drawables, this); + this.entremove = trigger_remove_generic; + + if(set_platmovetype(this, this.platmovetype)) + { + this.platmovetype_start_default = this.platmovetype_start; + this.platmovetype_end_default = this.platmovetype_end; + } + + // everything is set up by the time the train is linked, we shouldn't need this + //func_train_find(); + + // but we will need these + train_next(this); + + set_movetype(this, MOVETYPE_PUSH); + this.move_time = time; + } + + if(sf & SF_TRIGGER_RESET) + { + // TODO: make a reset function for trains + } + + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/func/train.qh b/qcsrc/common/mapobjects/func/train.qh new file mode 100644 index 0000000000..0b2a099c53 --- /dev/null +++ b/qcsrc/common/mapobjects/func/train.qh @@ -0,0 +1,10 @@ +#pragma once + + +const int TRAIN_CURVE = BIT(0); +const int TRAIN_TURN = BIT(1); +const int TRAIN_NEEDACTIVATION = BIT(2); + +#ifdef CSQC +.float dmgtime; +#endif diff --git a/qcsrc/common/mapobjects/func/vectormamamam.qc b/qcsrc/common/mapobjects/func/vectormamamam.qc new file mode 100644 index 0000000000..61da52acbc --- /dev/null +++ b/qcsrc/common/mapobjects/func/vectormamamam.qc @@ -0,0 +1,194 @@ +#include "vectormamamam.qh" +#ifdef SVQC +// reusing some fields havocbots declared +.entity wp00, wp01, wp02, wp03; + +.float targetfactor, target2factor, target3factor, target4factor; +.vector targetnormal, target2normal, target3normal, target4normal; + +vector func_vectormamamam_origin(entity o, float timestep) +{ + vector v, p; + float flags; + entity e; + + flags = o.spawnflags; + v = '0 0 0'; + + e = o.wp00; + if(e) + { + p = e.origin + timestep * e.velocity; + if(flags & PROJECT_ON_TARGETNORMAL) + v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor; + else + v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor; + } + + e = o.wp01; + if(e) + { + p = e.origin + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET2NORMAL) + v = v + (p * o.target2normal) * o.target2normal * o.target2factor; + else + v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor; + } + + e = o.wp02; + if(e) + { + p = e.origin + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET3NORMAL) + v = v + (p * o.target3normal) * o.target3normal * o.target3factor; + else + v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor; + } + + e = o.wp03; + if(e) + { + p = e.origin + timestep * e.velocity; + if(flags & PROJECT_ON_TARGET4NORMAL) + v = v + (p * o.target4normal) * o.target4normal * o.target4factor; + else + v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor; + } + + return v; +} + +void func_vectormamamam_controller_think(entity this) +{ + this.nextthink = time + vectormamamam_timestep; + + if(this.owner.active != ACTIVE_ACTIVE) + { + this.owner.velocity = '0 0 0'; + return; + } + + if(this.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed + this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, vectormamamam_timestep) - this.owner.origin) * 10; +} + +void func_vectormamamam_findtarget(entity this) +{ + if(this.target != "") + this.wp00 = find(NULL, targetname, this.target); + + if(this.target2 != "") + this.wp01 = find(NULL, targetname, this.target2); + + if(this.target3 != "") + this.wp02 = find(NULL, targetname, this.target3); + + if(this.target4 != "") + this.wp03 = find(NULL, targetname, this.target4); + + if(!this.wp00 && !this.wp01 && !this.wp02 && !this.wp03) + objerror(this, "No reference entity found, so there is nothing to move. Aborting."); + + this.destvec = this.origin - func_vectormamamam_origin(this, 0); + + entity controller; + controller = new(func_vectormamamam_controller); + controller.owner = this; + controller.nextthink = time + 1; + setthink(controller, func_vectormamamam_controller_think); +} + +void func_vectormamamam_setactive(entity this, int astate) +{ + if (astate == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + this.active = ACTIVE_NOT; + else + this.active = ACTIVE_ACTIVE; + } + else + this.active = astate; + + if(this.active == ACTIVE_NOT) + { + stopsound(this, CH_TRIGGER_SINGLE); + } + else + { + if(this.noise && this.noise != "") + { + _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } + } +} + +void func_vectormamamam_init_for_player(entity this, entity player) +{ + if (this.noise && this.noise != "" && this.active == ACTIVE_ACTIVE && IS_REAL_CLIENT(player)) + { + msg_entity = player; + soundto(MSG_ONE, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); + } +} + +spawnfunc(func_vectormamamam) +{ + if (this.noise != "") + { + precache_sound(this.noise); + } + + if(!this.targetfactor) + this.targetfactor = 1; + + if(!this.target2factor) + this.target2factor = 1; + + if(!this.target3factor) + this.target3factor = 1; + + if(!this.target4factor) + this.target4factor = 1; + + if(this.targetnormal) + this.targetnormal = normalize(this.targetnormal); + + if(this.target2normal) + this.target2normal = normalize(this.target2normal); + + if(this.target3normal) + this.target3normal = normalize(this.target3normal); + + if(this.target4normal) + this.target4normal = normalize(this.target4normal); + + setblocked(this, generic_plat_blocked); + if(this.dmg && (this.message == "")) + this.message = " was squished"; + if(this.dmg && (this.message == "")) + this.message2 = "was squished by"; + if(this.dmg && (!this.dmgtime)) + this.dmgtime = 0.25; + this.dmgtime2 = time; + + if (!InitMovingBrushTrigger(this)) + return; + + // wait for targets to spawn + this.nextthink = this.ltime + 999999999; + setthink(this, SUB_NullThink); // for PushMove + + // Savage: Reduce bandwith, critical on e.g. nexdm02 + this.effects |= EF_LOWPRECISION; + + this.setactive = func_vectormamamam_setactive; + this.setactive(this, ACTIVE_ACTIVE); + + // maybe send sound to new players + IL_PUSH(g_initforplayer, this); + this.init_for_player = func_vectormamamam_init_for_player; + + InitializeEntity(this, func_vectormamamam_findtarget, INITPRIO_FINDTARGET); +} +#endif diff --git a/qcsrc/common/mapobjects/func/vectormamamam.qh b/qcsrc/common/mapobjects/func/vectormamamam.qh new file mode 100644 index 0000000000..7eb6b0a63b --- /dev/null +++ b/qcsrc/common/mapobjects/func/vectormamamam.qh @@ -0,0 +1,9 @@ +#pragma once + + +const int PROJECT_ON_TARGETNORMAL = BIT(0); +const int PROJECT_ON_TARGET2NORMAL = BIT(1); +const int PROJECT_ON_TARGET3NORMAL = BIT(2); +const int PROJECT_ON_TARGET4NORMAL = BIT(3); + +const float vectormamamam_timestep = 0.1; diff --git a/qcsrc/common/mapobjects/misc/_mod.inc b/qcsrc/common/mapobjects/misc/_mod.inc new file mode 100644 index 0000000000..c7f1619ad5 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/_mod.inc @@ -0,0 +1,7 @@ +// generated file; do not modify +#include <common/mapobjects/misc/corner.qc> +#include <common/mapobjects/misc/dynlight.qc> +#include <common/mapobjects/misc/follow.qc> +#include <common/mapobjects/misc/keys.qc> +#include <common/mapobjects/misc/laser.qc> +#include <common/mapobjects/misc/teleport_dest.qc> diff --git a/qcsrc/common/mapobjects/misc/_mod.qh b/qcsrc/common/mapobjects/misc/_mod.qh new file mode 100644 index 0000000000..617db807b9 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/_mod.qh @@ -0,0 +1,7 @@ +// generated file; do not modify +#include <common/mapobjects/misc/corner.qh> +#include <common/mapobjects/misc/dynlight.qh> +#include <common/mapobjects/misc/follow.qh> +#include <common/mapobjects/misc/keys.qh> +#include <common/mapobjects/misc/laser.qh> +#include <common/mapobjects/misc/teleport_dest.qh> diff --git a/qcsrc/common/mapobjects/misc/corner.qc b/qcsrc/common/mapobjects/misc/corner.qc new file mode 100644 index 0000000000..a0f67b759b --- /dev/null +++ b/qcsrc/common/mapobjects/misc/corner.qc @@ -0,0 +1,75 @@ +#include "corner.qh" +REGISTER_NET_LINKED(ENT_CLIENT_CORNER) + +#ifdef SVQC +bool corner_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER); + + WriteString(MSG_ENTITY, this.platmovetype); + + WriteVector(MSG_ENTITY, this.origin); + + WriteString(MSG_ENTITY, this.target); + WriteString(MSG_ENTITY, this.target2); + WriteString(MSG_ENTITY, this.target3); + WriteString(MSG_ENTITY, this.target4); + WriteString(MSG_ENTITY, this.targetname); + WriteByte(MSG_ENTITY, this.target_random); + + WriteByte(MSG_ENTITY, this.wait); + + return true; +} + +void corner_link(entity this) +{ + //Net_LinkEntity(this, false, 0, corner_send); +} + +spawnfunc(path_corner) +{ + // setup values for overriding train movement + // if a second value does not exist, both start and end speeds are the single value specified + set_platmovetype(this, this.platmovetype); + + corner_link(this); +} +#elif defined(CSQC) + +void corner_remove(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.platmovetype); +} + +NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) +{ + this.platmovetype = strzone(ReadString()); + + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.target = strzone(ReadString()); + this.target2 = strzone(ReadString()); + this.target3 = strzone(ReadString()); + this.target4 = strzone(ReadString()); + this.targetname = strzone(ReadString()); + this.target_random = ReadByte(); + + this.wait = ReadByte(); + + return = true; + + this.classname = "path_corner"; + this.drawmask = MASK_NORMAL; + this.entremove = corner_remove; + + set_platmovetype(this, this.platmovetype); +} + +#endif diff --git a/qcsrc/common/mapobjects/misc/corner.qh b/qcsrc/common/mapobjects/misc/corner.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/misc/corner.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/misc/dynlight.qc b/qcsrc/common/mapobjects/misc/dynlight.qc new file mode 100644 index 0000000000..7c70b8444e --- /dev/null +++ b/qcsrc/common/mapobjects/misc/dynlight.qc @@ -0,0 +1,135 @@ +#include "dynlight.qh" + +#ifdef SVQC +#include <server/defs.qh> +#include <server/miscfunctions.qh> + +const float LOOP = 1; + +.float speed; + +//const int DNOSHADOW = 2; +const int DFOLLOW = 4; +.float light_lev; +.float lefty; +.vector color; +.string dtagname; + +/*QUAKED dynlight (0 1 0) (-8 -8 -8) (8 8 8) START_OFF NOSHADOW FOLLOW +Dynamic spawnfunc_light. +Can do one of these things: sit still and be just a silly spawnfunc_light, travel along a path, follow an entity around, attach to a tag on an entity. +It can spin around it's own axis in all the above cases. +If targeted, it will toggle between on or off. +keys: +"light_lev" spawnfunc_light radius, default 200 +"color" spawnfunc_light color in rgb and brightness, 1 1 1 produces bright white, up to 255 255 255 (nuclear blast), recommended values up to 1 1 1, default 1 1 1 +"style" lightstyle, same as for static lights +"angles" initial orientation +"avelocity" a vector value, the direction and speed it rotates in +"skin" cubemap number, must be 16 or above +"dtagname" will attach to this tag on the entity which "targetname" matches "target". If the "target" is either not an md3 model or is missing tags, it will attach to the targets origin. Note that the "target" must be visible to the spawnfunc_light +"targetname" will toggle on and off when triggered +"target" if issued with a target, preferrably spawnfunc_path_corner, it will move along the path. If also issued with the FOLLOW spawnflag, then this is the entity it will follow. If issued with the "tagname" key it will attach it to this targets tag called "tagname", does not work together with FOLLOW or path movement +"speed" the speed it will travel along the path, default 100 +flags: +"START_OFF" spawnfunc_light will be in off state until targeted +"NOSHADOW" will not cast shadows in realtime lighting mode +"FOLLOW" will follow the entity which "targetname" matches "target" +*/ +void dynlight_think(entity this) +{ + if(!this.owner) + delete(this); + + this.nextthink = time + 0.1; +} +void dynlight_find_aiment(entity this) +{ + entity targ; + if (!this.target) + objerror (this, "dynlight: no target to follow"); + + targ = find(NULL, targetname, this.target); + set_movetype(this, MOVETYPE_FOLLOW); + this.aiment = targ; + this.owner = targ; + this.punchangle = targ.angles; + this.view_ofs = this.origin - targ.origin; + this.v_angle = this.angles - targ.angles; + setthink(this, dynlight_think); + this.nextthink = time + 0.1; +} +void dynlight_find_path(entity this) +{ + entity targ; + if (!this.target) + objerror (this, "dynlight: no target to follow"); + + targ = find(NULL, targetname, this.target); + this.target = targ.target; + setorigin(this, targ.origin); + setthink(this, train_next); // TODO: reliant on the train's pathing functions + this.nextthink = time + 0.1; +} +void dynlight_find_target(entity this) +{ + entity targ; + if (!this.target) + objerror (this, "dynlight: no target to follow"); + + targ = find(NULL, targetname, this.target); + setattachment(this, targ, this.dtagname); + this.owner = targ; + setthink(this, dynlight_think); + this.nextthink = time + 0.1; +} +void dynlight_use(entity this, entity actor, entity trigger) +{ + if (this.light_lev == 0) + this.light_lev = this.lefty; + else + this.light_lev = 0; +} +spawnfunc(dynlight) +{ + if (!this.light_lev) + this.light_lev = 200; + if (!this.color) + this.color = '1 1 1'; + this.lefty = this.light_lev; + this.use = dynlight_use; + setsize (this, '0 0 0', '0 0 0'); + setorigin(this, this.origin); + //this.pflags = PFLAGS_FULLDYNAMIC; + this.solid = SOLID_NOT; + //this.blocked = func_null; + //if (this.spawnflags & DNOSHADOW) + // this.pflags = this.pflags + PFLAGS_NOSHADOW; + //if (this.spawnflags & START_OFF) + // this.light_lev = 0; + +//tag attaching + if (this.dtagname) + { + InitializeEntity(this, dynlight_find_target, INITPRIO_FINDTARGET); + return; + } + +// entity following + if (this.spawnflags & DFOLLOW) + { + InitializeEntity(this, dynlight_find_aiment, INITPRIO_FINDTARGET); + return; + } +// path following + if (this.target) +// if (!(this.spawnflags & DFOLLOW)) + { + set_movetype(this, MOVETYPE_NOCLIP); + if (!this.speed) + this.speed = 100; + InitializeEntity(this, dynlight_find_path, INITPRIO_FINDTARGET); + return; + } +} +#endif diff --git a/qcsrc/common/mapobjects/misc/dynlight.qh b/qcsrc/common/mapobjects/misc/dynlight.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/misc/dynlight.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/misc/follow.qc b/qcsrc/common/mapobjects/misc/follow.qc new file mode 100644 index 0000000000..87619ca711 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/follow.qc @@ -0,0 +1,70 @@ +#include "follow.qh" +// the way this entity works makes it no use to CSQC, as it removes itself instantly + +#ifdef SVQC +void follow_init(entity this) +{ + entity src, dst; + src = NULL; + dst = NULL; + if(this.killtarget != "") + src = find(NULL, targetname, this.killtarget); + if(this.target != "") + dst = find(NULL, targetname, this.target); + + if(!src && !dst) + { + objerror(this, "follow: could not find target/killtarget"); + return; + } + + if(this.jointtype) + { + // already done :P entity must stay + this.aiment = src; + this.enemy = dst; + } + else if(!src || !dst) + { + objerror(this, "follow: could not find target/killtarget"); + return; + } + else if(this.spawnflags & FOLLOW_ATTACH) + { + // attach + if(this.spawnflags & FOLLOW_LOCAL) + { + setattachment(dst, src, this.message); + } + else + { + attach_sameorigin(dst, src, this.message); + } + + dst.solid = SOLID_NOT; // solid doesn't work with attachment + delete(this); + } + else + { + if(this.spawnflags & FOLLOW_LOCAL) + { + set_movetype(dst, MOVETYPE_FOLLOW); + dst.aiment = src; + // dst.punchangle = '0 0 0'; // keep unchanged + dst.view_ofs = dst.origin; + dst.v_angle = dst.angles; + } + else + { + follow_sameorigin(dst, src); + } + + delete(this); + } +} + +spawnfunc(misc_follow) +{ + InitializeEntity(this, follow_init, INITPRIO_FINDTARGET); +} +#endif diff --git a/qcsrc/common/mapobjects/misc/follow.qh b/qcsrc/common/mapobjects/misc/follow.qh new file mode 100644 index 0000000000..aef491ff32 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/follow.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int FOLLOW_ATTACH = BIT(0); +const int FOLLOW_LOCAL = BIT(1); diff --git a/qcsrc/common/mapobjects/misc/keys.qc b/qcsrc/common/mapobjects/misc/keys.qc new file mode 100644 index 0000000000..2c8574249e --- /dev/null +++ b/qcsrc/common/mapobjects/misc/keys.qc @@ -0,0 +1,292 @@ +#include "keys.qh" + +#ifdef CSQC +bool item_keys_usekey(entity l, entity p) +{ + int valid = (l.itemkeys & p.itemkeys); // TODO: itemkeys isn't networked or anything! + l.itemkeys &= ~valid; // only some of the needed keys were given + return valid != 0; +} +#endif + +#ifdef SVQC +/* +TODO: +- add an unlock sound (here to trigger_keylock and to func_door) +- display available keys on the HUD +- make more tests +- think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility +- should keys have a trigger? +*/ + +bool item_keys_usekey(entity l, entity p) +{ + int valid = l.itemkeys & p.itemkeys; + + if (!valid) { + // player has none of the needed keys + return false; + } else if (l.itemkeys == valid) { + // ALL needed keys were given + l.itemkeys = 0; + return true; + } else { + // only some of the needed keys were given + l.itemkeys &= ~valid; + return true; + } +} + +string item_keys_keylist(float keylist) { + // no keys + if (!keylist) + return ""; + + // one key + if ((keylist & (keylist-1)) == 0) + return strcat("the ", item_keys_names[lowestbit(keylist)]); + + string n = ""; + int base = 0; + while (keylist) { + int l = lowestbit(keylist); + if (n) + n = strcat(n, ", the ", item_keys_names[base + l]); + else + n = strcat("the ", item_keys_names[base + l]); + + keylist = bitshift(keylist, -(l + 1)); + base+= l + 1; + } + + return n; +} + + +/* +================================ +item_key +================================ +*/ + +/** + * Key touch handler. + */ +void item_key_touch(entity this, entity toucher) +{ + if (!IS_PLAYER(toucher)) + return; + + // player already picked up this key + if (PS(toucher).itemkeys & this.itemkeys) + return; + + PS(toucher).itemkeys |= this.itemkeys; + play2(toucher, this.noise); + + centerprint(toucher, this.message); + + string oldmsg = this.message; + this.message = ""; + SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for the trigger here? + this.message = oldmsg; +} + +/** + * Spawn a key with given model, key code and color. + */ +void spawn_item_key(entity this) +{ + precache_model(this.model); + + if (this.spawnflags & 1) // FLOATING + this.noalign = 1; + + if (this.noalign) + set_movetype(this, MOVETYPE_NONE); + else + set_movetype(this, MOVETYPE_TOSS); + + precache_sound(this.noise); + + this.mdl = this.model; + this.effects = EF_LOWPRECISION; + _setmodel(this, this.model); + //setsize(this, '-16 -16 -24', '16 16 32'); + setorigin(this, this.origin + '0 0 32'); + setsize(this, '-16 -16 -56', '16 16 0'); + this.modelflags |= MF_ROTATE; + this.solid = SOLID_TRIGGER; + + if (!this.noalign) + { + // first nudge it off the floor a little bit to avoid math errors + setorigin(this, this.origin + '0 0 1'); + // note droptofloor returns false if stuck/or would fall too far + droptofloor(this); + } + + settouch(this, item_key_touch); +} + + +/*QUAKED item_key (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING +A key entity. +The itemkeys should contain one of the following key IDs: +1 - GOLD key - +2 - SILVER key +4 - BRONZE key +8 - RED keycard +16 - BLUE keycard +32 - GREEN keycard +Custom keys: +... - last key is 1<<23 +Keys with bigger Id than 32 don't have a default netname and model, if you use one of them, you MUST provide those. +-----------KEYS------------ +colormod: color of the key (default: '.9 .9 .9'). +itemkeys: a key Id. +message: message to print when player picks up this key. +model: custom key model to use. +netname: the display name of the key. +noise: custom sound to play when player picks up the key. +-------- SPAWNFLAGS -------- +FLOATING: the item will float in air, instead of aligning to the floor by falling +---------NOTES---------- +This is the only correct way to put keys on the map! + +itemkeys MUST always have exactly one bit set. +*/ +spawnfunc(item_key) +{ + string _netname; + vector _colormod; + + // reject this entity if more than one key was set! + if (this.itemkeys>0 && (this.itemkeys & (this.itemkeys-1)) != 0) { + objerror(this, "item_key.itemkeys must contain only 1 bit set specifying the key it represents!"); + delete(this); + return; + } + + // find default netname and colormod + switch(this.itemkeys) { + case BIT(0): + _netname = "GOLD key"; + _colormod = '1 .9 0'; + break; + + case BIT(1): + _netname = "SILVER key"; + _colormod = '.9 .9 .9'; + break; + + case BIT(2): + _netname = "BRONZE key"; + _colormod = '.6 .25 0'; + break; + + case BIT(3): + _netname = "RED keycard"; + _colormod = '.9 0 0'; + break; + + case BIT(4): + _netname = "BLUE keycard"; + _colormod = '0 0 .9'; + break; + + case BIT(5): + _netname = "GREEN keycard"; + _colormod = '0 .9 0'; + break; + + default: + _netname = "FLUFFY PINK keycard"; + _colormod = '1 1 1'; + + if (this.netname == "") { + objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!"); + delete(this); + return; + } + break; + + } + + // find default model + string _model = string_null; + if (this.itemkeys <= ITEM_KEY_BIT(2)) { + _model = "models/keys/key.md3"; + } else if (this.itemkeys >= ITEM_KEY_BIT(3) && this.itemkeys <= ITEM_KEY_BIT(5)) { + _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model! + } else if (this.model == "") { + objerror(this, "item_key doesn't have a default model for this key and a custom one was not specified!"); + delete(this); + return; + } + + // set defailt netname + if (this.netname == "") + this.netname = _netname; + + // set default colormod + if (!this.colormod) + this.colormod = _colormod; + + // set default model + if (this.model == "") + this.model = _model; + + // set default pickup message + if (this.message == "") + this.message = strzone(strcat("You've picked up the ", this.netname, "!")); + + if (this.noise == "") + this.noise = strzone(SND(ITEMPICKUP)); + + // save the name for later + item_keys_names[lowestbit(this.itemkeys)] = this.netname; + + // put the key on the map + spawn_item_key(this); +} + +/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING +SILVER key. +-----------KEYS------------ +colormod: color of the key (default: '.9 .9 .9'). +message: message to print when player picks up this key. +model: custom model to use. +noise: custom sound to play when player picks up the key. +-------- SPAWNFLAGS -------- +FLOATING: the item will float in air, instead of aligning to the floor by falling +---------NOTES---------- +Don't use this entity on new maps! Use item_key instead. +*/ +spawnfunc(item_key1) +{ + this.classname = "item_key"; + this.itemkeys = ITEM_KEY_BIT(1); + spawnfunc_item_key(this); +} + +/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING +GOLD key. +-----------KEYS------------ +colormod: color of the key (default: '1 .9 0'). +message: message to print when player picks up this key. +model: custom model to use. +noise: custom sound to play when player picks up the key. +-------- SPAWNFLAGS -------- +FLOATING: the item will float in air, instead of aligning to the floor by falling +---------NOTES---------- +Don't use this entity on new maps! Use item_key instead. +*/ +spawnfunc(item_key2) +{ + this.classname = "item_key"; + this.itemkeys = ITEM_KEY_BIT(0); + spawnfunc_item_key(this); +} + +#endif diff --git a/qcsrc/common/mapobjects/misc/keys.qh b/qcsrc/common/mapobjects/misc/keys.qh new file mode 100644 index 0000000000..50be5f8dba --- /dev/null +++ b/qcsrc/common/mapobjects/misc/keys.qh @@ -0,0 +1,26 @@ +#pragma once + +/** + * Returns the bit ID of a key + */ +#define ITEM_KEY_BIT(n) ( bitshift(1, n) ) + +#define ITEM_KEY_MAX 24 + +/** + * list of key names. + */ +#ifdef SVQC +string item_keys_names[ITEM_KEY_MAX]; + +/** + * Use keys from p on l. + * Returns true if any new keys were given, false otherwise. + */ +float item_keys_usekey(entity l, entity p); + +/** + * Returns a string with a comma separated list of key names, as specified in keylist. + */ +string item_keys_keylist(float keylist); +#endif diff --git a/qcsrc/common/mapobjects/misc/laser.qc b/qcsrc/common/mapobjects/misc/laser.qc new file mode 100644 index 0000000000..df88b750f2 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/laser.qc @@ -0,0 +1,418 @@ +#include "laser.qh" +#if defined(CSQC) + #include <lib/csqcmodel/interpolate.qh> + #include <client/main.qh> + #include <lib/csqcmodel/cl_model.qh> +#elif defined(MENUQC) +#elif defined(SVQC) +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_LASER) + +#ifdef SVQC +.float modelscale; +void misc_laser_aim(entity this) +{ + vector a; + if(this.enemy) + { + if(this.spawnflags & LASER_FINITE) + { + if(this.enemy.origin != this.mangle) + { + this.mangle = this.enemy.origin; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + else + { + a = vectoangles(this.enemy.origin - this.origin); + a_x = -a_x; + if(a != this.mangle) + { + this.mangle = a; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + } + else + { + if(this.angles != this.mangle) + { + this.mangle = this.angles; + this.SendFlags |= SF_LASER_UPDATE_TARGET; + } + } + if(this.origin != this.oldorigin) + { + this.SendFlags |= SF_LASER_UPDATE_ORIGIN; + this.oldorigin = this.origin; + } +} + +void misc_laser_init(entity this) +{ + if(this.target != "") + this.enemy = find(NULL, targetname, this.target); +} + +.entity pusher; +void misc_laser_think(entity this) +{ + vector o; + entity hitent; + vector hitloc; + + this.nextthink = time; + + if(this.active == ACTIVE_NOT) + return; + + misc_laser_aim(this); + + if(this.enemy) + { + o = this.enemy.origin; + if (!(this.spawnflags & LASER_FINITE)) + o = this.origin + normalize(o - this.origin) * LASER_BEAM_MAXLENGTH; + } + else + { + makevectors(this.mangle); + o = this.origin + v_forward * LASER_BEAM_MAXLENGTH; + } + + if(this.dmg || this.enemy.target != "") + { + traceline(this.origin, o, MOVE_NORMAL, this); + } + hitent = trace_ent; + hitloc = trace_endpos; + + if(this.enemy.target != "") // DETECTOR laser + { + if(trace_ent.iscreature) + { + this.pusher = hitent; + if(!this.count) + { + this.count = 1; + + SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); + } + } + else + { + if(this.count) + { + this.count = 0; + + SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); + } + } + } + + if(this.dmg) + { + if(this.team) + if(((this.spawnflags & LASER_INVERT_TEAM) == 0) == (this.team != hitent.team)) + return; + if(hitent.takedamage) + Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0'); + } +} + +bool laser_SendEntity(entity this, entity to, float sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER); + sendflags = sendflags & 0x0F; // use that bit to indicate finite length laser + if(this.spawnflags & LASER_FINITE) + sendflags |= SF_LASER_FINITE; + if(this.alpha) + sendflags |= SF_LASER_ALPHA; + if(this.scale != 1 || this.modelscale != 1) + sendflags |= SF_LASER_SCALE; + if(this.spawnflags & LASER_NOTRACE) + sendflags |= SF_LASER_NOTRACE; + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_LASER_UPDATE_ORIGIN) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_LASER_UPDATE_EFFECT) + { + WriteByte(MSG_ENTITY, this.beam_color.x * 255.0); + WriteByte(MSG_ENTITY, this.beam_color.y * 255.0); + WriteByte(MSG_ENTITY, this.beam_color.z * 255.0); + if(sendflags & SF_LASER_ALPHA) + WriteByte(MSG_ENTITY, this.alpha * 255.0); + if(sendflags & SF_LASER_SCALE) + { + WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255)); + WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255)); + } + if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) // effect doesn't need sending if the laser is infinite and has collision testing turned off + WriteShort(MSG_ENTITY, this.cnt); + } + if(sendflags & SF_LASER_UPDATE_TARGET) + { + if(sendflags & SF_LASER_FINITE) + { + WriteVector(MSG_ENTITY, this.enemy.origin); + } + else + { + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + } + } + if(sendflags & SF_LASER_UPDATE_ACTIVE) + WriteByte(MSG_ENTITY, this.active); + return true; +} + +/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED +Any object touching the beam will be hurt +Keys: +"target" + spawnfunc_target_position where the laser ends +"mdl" + name of beam end effect to use +"beam_color" + color of the beam (default: red) +"dmg" + damage per second (-1 for a laser that kills immediately) +*/ + +void laser_setactive(entity this, int act) +{ + int old_status = this.active; + if(act == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + { + this.active = ACTIVE_NOT; + } + else + { + this.active = ACTIVE_ACTIVE; + } + } + else + { + this.active = act; + } + + if (this.active != old_status) + { + this.SendFlags |= SF_LASER_UPDATE_ACTIVE; + misc_laser_aim(this); + } +} + +void laser_use(entity this, entity actor, entity trigger) +{ + this.setactive(this, ACTIVE_TOGGLE); +} + +spawnfunc(misc_laser) +{ + if(this.mdl) + { + if(this.mdl == "none") + this.cnt = -1; + else + { + this.cnt = _particleeffectnum(this.mdl); + if(this.cnt < 0 && this.dmg) + this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); + } + } + else if(!this.cnt) + { + if(this.dmg) + this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); + else + this.cnt = -1; + } + if(this.cnt < 0) + this.cnt = -1; + + if(!this.beam_color && this.colormod) + { + LOG_WARN("misc_laser uses legacy field 'colormod', please use 'beam_color' instead"); + this.beam_color = this.colormod; + } + + if(this.beam_color == '0 0 0') + { + if(!this.alpha) + this.beam_color = '1 0 0'; + } + + if(this.message == "") + { + this.message = "saw the light"; + } + if (this.message2 == "") + { + this.message2 = "was pushed into a laser by"; + } + if(!this.scale) + { + this.scale = 1; + } + if(!this.modelscale) + { + this.modelscale = 1; + } + else if(this.modelscale < 0) + { + this.modelscale = 0; + } + setthink(this, misc_laser_think); + this.nextthink = time; + InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET); + + this.mangle = this.angles; + + Net_LinkEntity(this, false, 0, laser_SendEntity); + + this.setactive = laser_setactive; + + IFTARGETED + { + // backwards compatibility + this.use = laser_use; + } + + this.reset = generic_netlinked_reset; + this.reset(this); +} +#elif defined(CSQC) + +// a laser goes from origin in direction angles +// it has color 'beam_color' +// and stops when something is in the way +entityclass(Laser); +classfield(Laser) .int cnt; // end effect +classfield(Laser) .vector colormod; +classfield(Laser) .int state; // on-off +classfield(Laser) .int count; // flags for the laser +classfield(Laser) .vector velocity; // laser endpoint if it is FINITE +classfield(Laser) .float alpha; +classfield(Laser) .float scale; // scaling factor of the thickness +classfield(Laser) .float modelscale; // scaling factor of the dlight + +void Draw_Laser(entity this) +{ + if(this.active == ACTIVE_NOT) + return; + InterpolateOrigin_Do(this); + if(this.count & SF_LASER_FINITE) + { + if(this.count & SF_LASER_NOTRACE) + { + trace_endpos = this.velocity; + trace_dphitq3surfaceflags = 0; + } + else + traceline(this.origin, this.velocity, 0, this); + } + else + { + if(this.count & SF_LASER_NOTRACE) + { + makevectors(this.angles); + trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; + trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY; + } + else + { + makevectors(this.angles); + traceline(this.origin, this.origin + v_forward * LASER_BEAM_MAXLENGTH, 0, this); + if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) + trace_endpos = this.origin + v_forward * LASER_BEAM_MAXWORLDSIZE; + } + } + if(this.scale != 0) + { + if(this.alpha) + { + Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, this.alpha, DRAWFLAG_NORMAL, view_origin); + } + else + { + Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.beam_color, 0.5, DRAWFLAG_ADDITIVE, view_origin); + } + } + if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) + { + if(this.cnt >= 0) + __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); + if(this.beam_color != '0 0 0' && this.modelscale != 0) + adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.beam_color * 5); + } +} + +NET_HANDLE(ENT_CLIENT_LASER, bool isnew) +{ + InterpolateOrigin_Undo(this); + + // 30 bytes, or 13 bytes for just moving + int sendflags = ReadByte(); + this.count = (sendflags & 0xF0); + + if(this.count & SF_LASER_FINITE) + this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; + else + this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; + + if(sendflags & SF_LASER_UPDATE_ORIGIN) + { + this.origin = ReadVector(); + setorigin(this, this.origin); + } + if(sendflags & SF_LASER_UPDATE_EFFECT) + { + this.beam_color.x = ReadByte() / 255.0; + this.beam_color.y = ReadByte() / 255.0; + this.beam_color.z = ReadByte() / 255.0; + if(sendflags & SF_LASER_ALPHA) + this.alpha = ReadByte() / 255.0; + else + this.alpha = 0; + this.scale = 2; // NOTE: why 2? + this.modelscale = 50; // NOTE: why 50? + if(sendflags & SF_LASER_SCALE) + { + this.scale *= ReadByte() / 16.0; // beam radius + this.modelscale *= ReadByte() / 16.0; // dlight radius + } + if((sendflags & SF_LASER_FINITE) || !(sendflags & SF_LASER_NOTRACE)) + this.cnt = ReadShort(); // effect number + else + this.cnt = 0; + } + if(sendflags & SF_LASER_UPDATE_TARGET) + { + if(sendflags & SF_LASER_FINITE) + { + this.velocity = ReadVector(); + } + else + { + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + } + } + if(sendflags & SF_LASER_UPDATE_ACTIVE) + this.active = ReadByte(); + + return = true; + + InterpolateOrigin_Note(this); + this.draw = Draw_Laser; + if (isnew) IL_PUSH(g_drawables, this); +} +#endif diff --git a/qcsrc/common/mapobjects/misc/laser.qh b/qcsrc/common/mapobjects/misc/laser.qh new file mode 100644 index 0000000000..0ff57646ad --- /dev/null +++ b/qcsrc/common/mapobjects/misc/laser.qh @@ -0,0 +1,22 @@ +#pragma once + + +const int LASER_FINITE = BIT(1); +const int LASER_NOTRACE = BIT(2); +const int LASER_INVERT_TEAM = BIT(3); + +const int SF_LASER_UPDATE_ORIGIN = BIT(0); +const int SF_LASER_UPDATE_TARGET = BIT(1); +const int SF_LASER_UPDATE_ACTIVE = BIT(2); +const int SF_LASER_UPDATE_EFFECT = BIT(3); + +const int SF_LASER_NOTRACE = BIT(4); +const int SF_LASER_SCALE = BIT(5); +const int SF_LASER_ALPHA = BIT(6); +const int SF_LASER_FINITE = BIT(7); + +.vector beam_color; + +const float LASER_BEAM_MAXLENGTH = 32768; // maximum length of a beam trace +// TODO: find a better way to do this +const float LASER_BEAM_MAXWORLDSIZE = 1048576; // to make sure the endpoint of the beam is not visible inside diff --git a/qcsrc/common/mapobjects/misc/teleport_dest.qc b/qcsrc/common/mapobjects/misc/teleport_dest.qc new file mode 100644 index 0000000000..126a20ea26 --- /dev/null +++ b/qcsrc/common/mapobjects/misc/teleport_dest.qc @@ -0,0 +1,89 @@ +#include "teleport_dest.qh" +REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) + +#ifdef SVQC + +bool teleport_dest_send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TELEPORT_DEST); + WriteByte(MSG_ENTITY, sendflags); + + if(sendflags & SF_TRIGGER_INIT) + { + WriteByte(MSG_ENTITY, this.cnt); + WriteCoord(MSG_ENTITY, this.speed); + WriteString(MSG_ENTITY, this.targetname); + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.mangle_x); + WriteAngle(MSG_ENTITY, this.mangle_y); + WriteAngle(MSG_ENTITY, this.mangle_z); + } + + return true; +} + +void teleport_dest_link(entity this) +{ + Net_LinkEntity(this, false, 0, teleport_dest_send); + this.SendFlags |= SF_TRIGGER_INIT; +} + +spawnfunc(info_teleport_destination) +{ + this.classname = "info_teleport_destination"; + + this.mangle = this.angles; + this.angles = '0 0 0'; + + //setorigin(this, this.origin + '0 0 27'); // To fix a mappers' habit as old as Quake + setorigin(this, this.origin); + + IFTARGETED + { + } + else + objerror (this, "^3Teleport destination without a targetname"); + + teleport_dest_link(this); +} + +spawnfunc(misc_teleporter_dest) +{ + spawnfunc_info_teleport_destination(this); +} + +#elif defined(CSQC) + +void teleport_dest_remove(entity this) +{ + // strfree(this.classname); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_TELEPORT_DEST, bool isnew) +{ + int sendflags = ReadByte(); + + if(sendflags & SF_TRIGGER_INIT) + { + this.classname = "info_teleport_destination"; + this.cnt = ReadByte(); + this.speed = ReadCoord(); + this.targetname = strzone(ReadString()); + this.origin = ReadVector(); + + this.mangle_x = ReadAngle(); + this.mangle_y = ReadAngle(); + this.mangle_z = ReadAngle(); + + setorigin(this, this.origin); + + this.drawmask = MASK_NORMAL; + this.entremove = teleport_dest_remove; + } + + return = true; +} + +#endif diff --git a/qcsrc/common/mapobjects/misc/teleport_dest.qh b/qcsrc/common/mapobjects/misc/teleport_dest.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/misc/teleport_dest.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/models.qc b/qcsrc/common/mapobjects/models.qc new file mode 100644 index 0000000000..10c3900408 --- /dev/null +++ b/qcsrc/common/mapobjects/models.qc @@ -0,0 +1,418 @@ +#include "models.qh" + +#ifdef SVQC +#include <server/defs.qh> +#include <server/miscfunctions.qh> +#include <common/net_linked.qh> +#include "subs.qh" +#include "triggers.qh" + +entityclass(BGMScript); +classfield(BGMScript) .string bgmscript; +classfield(BGMScript) .float bgmscriptattack; +classfield(BGMScript) .float bgmscriptdecay; +classfield(BGMScript) .float bgmscriptsustain; +classfield(BGMScript) .float bgmscriptrelease; + +#include <common/constants.qh> +#include "../../lib/csqcmodel/sv_model.qh" + +.float modelscale; + +void g_model_setcolormaptoactivator(entity this, entity actor, entity trigger) +{ + if(teamplay) + { + if(actor.team) + this.colormap = (actor.team - 1) * 0x11; + else + this.colormap = 0x00; + } + else + this.colormap = floor(random() * 256); + this.colormap |= BIT(10); // RENDER_COLORMAPPED +} + +void g_clientmodel_setcolormaptoactivator(entity this, entity actor, entity trigger) +{ + g_model_setcolormaptoactivator(this, actor, trigger); + this.SendFlags |= (BIT(3) | BIT(0)); +} + +void g_clientmodel_use(entity this, entity actor, entity trigger) +{ + // Flag to set func_clientwall state + // 1 == deactivate, 2 == activate, 0 == do nothing + if(this.classname == "func_clientwall" || this.classname == "func_clientillusionary") + this.antiwall_flag = trigger.antiwall_flag; + + if (this.antiwall_flag == 1) + { + this.inactive = 1; + this.solid = SOLID_NOT; + } + else if (this.antiwall_flag == 2) + { + this.inactive = 0; + this.solid = this.default_solid; + } + g_clientmodel_setcolormaptoactivator(this, actor, trigger); +} + +void g_model_dropbyspawnflags(entity this) +{ + if((this.spawnflags & 3) == 1) // ALIGN_ORIGIN + { + traceline(this.origin, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); + setorigin(this, trace_endpos); + } + else if((this.spawnflags & 3) == 2) // ALIGN_BOTTOM + { + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); + setorigin(this, trace_endpos); + } + else if((this.spawnflags & 3) == 3) // ALIGN_ORIGIN | ALIGN_BOTTOM + { + traceline(this.origin, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); + setorigin(this, trace_endpos - '0 0 1' * this.mins.z); + } +} + +void g_clientmodel_dropbyspawnflags(entity this) +{ + vector o0; + o0 = this.origin; + g_model_dropbyspawnflags(this); + if(this.origin != o0) + this.SendFlags |= 2; +} + +bool g_clientmodel_genericsendentity(entity this, entity to, int sf) +{ + sf = sf & 0x0F; + if(this.angles != '0 0 0') + sf |= 0x10; + if(this.mins != '0 0 0' || this.maxs != '0 0 0') + sf |= 0x20; + if(this.colormap != 0) + sf |= 0x40; + if(this.lodmodelindex1) + sf |= 0x80; + + WriteHeader(MSG_ENTITY, ENT_CLIENT_WALL); + WriteByte(MSG_ENTITY, sf); + + if(sf & BIT(0)) + { + if(sf & 0x40) + WriteShort(MSG_ENTITY, this.colormap); + WriteByte(MSG_ENTITY, this.skin); + } + + if(sf & BIT(1)) + { + WriteVector(MSG_ENTITY, this.origin); + } + + if(sf & BIT(2)) + { + if(sf & 0x10) + { + WriteAngle(MSG_ENTITY, this.angles.x); + WriteAngle(MSG_ENTITY, this.angles.y); + WriteAngle(MSG_ENTITY, this.angles.z); + } + } + + if(sf & BIT(3)) + { + if(sf & 0x80) + { + WriteShort(MSG_ENTITY, this.lodmodelindex0); + WriteShort(MSG_ENTITY, bound(0, this.loddistance1, 65535)); + WriteShort(MSG_ENTITY, this.lodmodelindex1); + WriteShort(MSG_ENTITY, bound(0, this.loddistance2, 65535)); + WriteShort(MSG_ENTITY, this.lodmodelindex2); + } + else + WriteShort(MSG_ENTITY, this.modelindex); + WriteByte(MSG_ENTITY, this.solid); + WriteShort(MSG_ENTITY, floor(this.scale * 256)); + if(sf & 0x20) + { + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + } + WriteString(MSG_ENTITY, this.bgmscript); + if(this.bgmscript != "") + { + WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); + WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); + WriteVector(MSG_ENTITY, this.movedir); + WriteByte(MSG_ENTITY, floor(this.lip * 255)); + } + WriteShort(MSG_ENTITY, bound(0, this.fade_start, 65535)); + WriteShort(MSG_ENTITY, bound(0, this.fade_end, 65535)); + WriteByte(MSG_ENTITY, floor(this.alpha_max * 256)); + WriteByte(MSG_ENTITY, floor(this.alpha_min * 256)); + WriteByte(MSG_ENTITY, this.inactive); + WriteShort(MSG_ENTITY, this.fade_vertical_offset); + } + + return true; +} + + +#define G_MODEL_INIT(ent,sol) \ + if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \ + if(!ent.scale) ent.scale = ent.modelscale; \ + SetBrushEntityModel(ent); \ + ent.use = g_model_setcolormaptoactivator; \ + InitializeEntity(ent, g_model_dropbyspawnflags, INITPRIO_DROPTOFLOOR); \ + if(!ent.solid) ent.solid = (sol); else if(ent.solid < 0) ent.solid = SOLID_NOT; + +#define G_CLIENTMODEL_INIT(ent,sol) \ + if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \ + if(!ent.scale) ent.scale = ent.modelscale; \ + SetBrushEntityModel(ent); \ + ent.use = g_clientmodel_use; \ + InitializeEntity(ent, g_clientmodel_dropbyspawnflags, INITPRIO_DROPTOFLOOR); \ + if(!ent.solid) ent.solid = (sol); else if(ent.solid < 0) ent.solid = SOLID_NOT; \ + if(!ent.bgmscriptsustain) ent.bgmscriptsustain = 1; else if(ent.bgmscriptsustain < 0) ent.bgmscriptsustain = 0; \ + Net_LinkEntity(ent, true, 0, g_clientmodel_genericsendentity); \ + ent.default_solid = sol; + +// non-solid model entities: +spawnfunc(misc_gamemodel) { this.angles_x = -this.angles.x; G_MODEL_INIT (this, SOLID_NOT) } // model entity +spawnfunc(misc_clientmodel) { this.angles_x = -this.angles.x; G_CLIENTMODEL_INIT(this, SOLID_NOT) } // model entity +spawnfunc(misc_models) { this.angles_x = -this.angles.x; G_MODEL_INIT (this, SOLID_NOT) } // DEPRECATED old compat entity with confusing name, do not use + +// non-solid brush entities: +spawnfunc(func_illusionary) { G_MODEL_INIT (this, SOLID_NOT) } // Q1 name (WARNING: MISPREDICTED) +spawnfunc(func_clientillusionary) { G_CLIENTMODEL_INIT(this, SOLID_NOT) } // brush entity +spawnfunc(func_static) { G_MODEL_INIT (this, SOLID_NOT) } // DEPRECATED old alias name from some other game + +// solid brush entities +spawnfunc(func_wall) { G_MODEL_INIT (this, SOLID_BSP) } // Q1 name +spawnfunc(func_clientwall) { G_CLIENTMODEL_INIT(this, SOLID_BSP) } // brush entity (WARNING: MISPREDICTED) +#elif defined(CSQC) +.float alpha; +.float scale; +.vector movedir; + +void Ent_Wall_PreDraw(entity this) +{ + float alph = this.alpha; + if (this.inactive) + { + alph = 0; + } + else + { + vector org = getpropertyvec(VF_ORIGIN); + if(!checkpvs(org, this)) + alph = 0; + else if(this.fade_start || this.fade_end) { + vector offset = '0 0 0'; + offset_z = this.fade_vertical_offset; + vector player_dist_math = org - this.origin - 0.5 * (this.mins + this.maxs) + offset; + if (this.fade_end == this.fade_start) + { + if (vdist(player_dist_math, >=, this.fade_start)) + alph = 0; + else + alph = 1; + } + else + { + float player_dist = vlen(player_dist_math); + alph = (this.alpha_min + this.alpha_max * bound(0, + (this.fade_end - player_dist) + / (this.fade_end - this.fade_start), 1)) / 100.0; + } + } + else + { + alph = 1; + } + } + this.alpha = alph; + this.drawmask = (alph <= 0) ? 0 : MASK_NORMAL; +} + +void Ent_Wall_Draw(entity this) +{ + float f; + var .vector fld; + + if(this.bgmscriptangular) + fld = angles; + else + fld = origin; + this.(fld) = this.saved; + + if(this.lodmodelindex1) + { + if(autocvar_cl_modeldetailreduction <= 0) + { + if(this.lodmodelindex2 && autocvar_cl_modeldetailreduction <= -2) + this.modelindex = this.lodmodelindex2; + else if(autocvar_cl_modeldetailreduction <= -1) + this.modelindex = this.lodmodelindex1; + else + this.modelindex = this.lodmodelindex0; + } + else + { + float distance = vlen(NearestPointOnBox(this, view_origin) - view_origin); + f = (distance * current_viewzoom + 100.0) * autocvar_cl_modeldetailreduction; + f *= 1.0 / bound(0.01, view_quality, 1); + if(this.lodmodelindex2 && f > this.loddistance2) + this.modelindex = this.lodmodelindex2; + else if(f > this.loddistance1) + this.modelindex = this.lodmodelindex1; + else + this.modelindex = this.lodmodelindex0; + } + } + + InterpolateOrigin_Do(this); + + this.saved = this.(fld); + + f = doBGMScript(this); + if(f >= 0) + { + if(this.lip < 0) // < 0: alpha goes from 1 to 1-|lip| when toggled (toggling subtracts lip) + this.alpha = 1 + this.lip * f; + else // > 0: alpha goes from 1-|lip| to 1 when toggled (toggling adds lip) + this.alpha = 1 - this.lip * (1 - f); + this.(fld) = this.(fld) + this.movedir * f; + } + else + this.alpha = 1; + + if(this.alpha >= ALPHA_MIN_VISIBLE) + this.drawmask = MASK_NORMAL; + else + this.drawmask = 0; +} + +void Ent_Wall_Remove(entity this) +{ + strfree(this.bgmscript); +} + +NET_HANDLE(ENT_CLIENT_WALL, bool isnew) +{ + int f; + var .vector fld; + + InterpolateOrigin_Undo(this); + this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; + + if(this.bgmscriptangular) + fld = angles; + else + fld = origin; + this.(fld) = this.saved; + + f = ReadByte(); + + if(f & 1) + { + if(f & 0x40) + this.colormap = ReadShort(); + else + this.colormap = 0; + this.skin = ReadByte(); + } + + if(f & 2) + { + this.origin = ReadVector(); + setorigin(this, this.origin); + } + + if(f & 4) + { + if(f & 0x10) + { + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + this.angles_z = ReadAngle(); + } + else + this.angles = '0 0 0'; + } + + if(f & 8) + { + if(f & 0x80) + { + this.lodmodelindex0 = ReadShort(); + this.loddistance1 = ReadShort(); + this.lodmodelindex1 = ReadShort(); + this.loddistance2 = ReadShort(); + this.lodmodelindex2 = ReadShort(); + } + else + { + this.modelindex = ReadShort(); + this.loddistance1 = 0; + this.loddistance2 = 0; + } + this.solid = ReadByte(); + this.scale = ReadShort() / 256.0; + if(f & 0x20) + { + this.mins = ReadVector(); + this.maxs = ReadVector(); + } + else + this.mins = this.maxs = '0 0 0'; + setsize(this, this.mins, this.maxs); + + string s = ReadString(); + if(substring(s, 0, 1) == "<") + { + strcpy(this.bgmscript, substring(s, 1, -1)); + this.bgmscriptangular = 1; + } + else + { + strcpy(this.bgmscript, s); + this.bgmscriptangular = 0; + } + if(this.bgmscript != "") + { + this.bgmscriptattack = ReadByte() / 64.0; + this.bgmscriptdecay = ReadByte() / 64.0; + this.bgmscriptsustain = ReadByte() / 255.0; + this.bgmscriptrelease = ReadByte() / 64.0; + this.movedir = ReadVector(); + this.lip = ReadByte() / 255.0; + } + this.fade_start = ReadShort(); + this.fade_end = ReadShort(); + this.alpha_max = ReadByte() / 255.0; + this.alpha_min = ReadByte() / 255.0; + this.inactive = ReadByte(); + this.fade_vertical_offset = ReadShort(); + BGMScript_InitEntity(this); + } + + return = true; + + InterpolateOrigin_Note(this); + + this.saved = this.(fld); + + this.entremove = Ent_Wall_Remove; + this.draw = Ent_Wall_Draw; + if (isnew) IL_PUSH(g_drawables, this); + setpredraw(this, Ent_Wall_PreDraw); +} +#endif diff --git a/qcsrc/common/mapobjects/models.qh b/qcsrc/common/mapobjects/models.qh new file mode 100644 index 0000000000..45346dc8ea --- /dev/null +++ b/qcsrc/common/mapobjects/models.qh @@ -0,0 +1,22 @@ +#pragma once + +#ifdef CSQC +entityclass(Wall); +classfield(Wall) .float lip; +classfield(Wall) .float bgmscriptangular; +classfield(Wall) .int lodmodelindex0, lodmodelindex1, lodmodelindex2; +classfield(Wall) .float loddistance1, loddistance2; +classfield(Wall) .vector saved; + +// Needed for interactive clientwalls +.bool inactive; // Clientwall disappears when inactive +.float alpha_max, alpha_min; +// If fade_start > fade_end, fadeout will be inverted +// fade_vertical_offset is a vertival offset for player position +.float fade_start, fade_end, fade_vertical_offset; +.float default_solid; + +void Ent_Wall_Draw(entity this); + +void Ent_Wall_Remove(entity this); +#endif diff --git a/qcsrc/common/mapobjects/platforms.qc b/qcsrc/common/mapobjects/platforms.qc new file mode 100644 index 0000000000..cc909e5c56 --- /dev/null +++ b/qcsrc/common/mapobjects/platforms.qc @@ -0,0 +1,227 @@ +#include "platforms.qh" +void generic_plat_blocked(entity this, entity blocker) +{ +#ifdef SVQC + if(this.dmg && blocker.takedamage != DAMAGE_NO) + { + if(this.dmgtime2 < time) + { + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + this.dmgtime2 = time + this.dmgtime; + } + + // Gib dead/dying stuff + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif +} + +void plat_spawn_inside_trigger(entity this) +{ + entity trigger; + vector tmin, tmax; + + trigger = spawn(); + settouch(trigger, plat_center_touch); + set_movetype(trigger, MOVETYPE_NONE); + trigger.solid = SOLID_TRIGGER; + trigger.enemy = this; + + tmin = this.absmin + '25 25 0'; + tmax = this.absmax - '25 25 -8'; + tmin_z = tmax_z - (this.pos1_z - this.pos2_z + 8); + if (this.spawnflags & PLAT_LOW_TRIGGER) + tmax_z = tmin_z + 8; + + if (this.size_x <= 50) + { + tmin_x = (this.mins_x + this.maxs_x) / 2; + tmax_x = tmin_x + 1; + } + if (this.size_y <= 50) + { + tmin_y = (this.mins_y + this.maxs_y) / 2; + tmax_y = tmin_y + 1; + } + + if(tmin_x < tmax_x) + if(tmin_y < tmax_y) + if(tmin_z < tmax_z) + { + setsize (trigger, tmin, tmax); + return; + } + + // otherwise, something is fishy... + delete(trigger); + objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); +} + +void plat_hit_top(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_TOP; + + setthink(this, plat_go_down); + this.nextthink = this.ltime + 3; +} + +void plat_hit_bottom(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); + this.state = STATE_BOTTOM; +} + +void plat_go_down(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); + this.state = STATE_DOWN; + SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom); +} + +void plat_go_up(entity this) +{ + _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); + this.state = STATE_UP; + SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top); +} + +void plat_center_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + + if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(toucher)) + return; + if(IS_DEAD(toucher)) + return; +#endif + + if (this.enemy.state == STATE_BOTTOM) { + plat_go_up(this.enemy); + } else if (this.enemy.state == STATE_TOP) + this.enemy.nextthink = this.enemy.ltime + 1; +} + +void plat_outside_touch(entity this, entity toucher) +{ +#ifdef SVQC + if (!toucher.iscreature) + return; + + if (GetResourceAmount(toucher, RESOURCE_HEALTH) <= 0) + return; +#elif defined(CSQC) + if (!IS_PLAYER(toucher)) + return; +#endif + + if (this.enemy.state == STATE_TOP) { + entity e = this.enemy; + plat_go_down(e); + } +} + +void plat_trigger_use(entity this, entity actor, entity trigger) +{ + if (getthink(this)) + return; // already activated + plat_go_down(this); +} + + +void plat_crush(entity this, entity blocker) +{ + if((this.spawnflags & CRUSH) && (blocker.takedamage != DAMAGE_NO)) + { // KIll Kill Kill!! +#ifdef SVQC + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); +#endif + } + else + { +#ifdef SVQC + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) + { // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + // Gib dead/dying stuff + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); + } +#endif + + if (this.state == STATE_UP) + plat_go_down (this); + else if (this.state == STATE_DOWN) + plat_go_up (this); + // when in other states, then the plat_crush event came delayed after + // plat state already had changed + // this isn't a bug per se! + } +} + +void plat_use(entity this, entity actor, entity trigger) +{ + this.use = func_null; + if (this.state != STATE_UP) + objerror (this, "plat_use: not in up state"); + plat_go_down(this); +} + +// WARNING: backwards compatibility because people don't use already existing fields :( +// TODO: Check if any maps use these fields and remove these fields if it doesn't break maps +.string sound1, sound2; + +void plat_reset(entity this) +{ + IFTARGETED + { + setorigin(this, this.pos1); + this.state = STATE_UP; + this.use = plat_use; + } + else + { + setorigin(this, this.pos2); + this.state = STATE_BOTTOM; + this.use = plat_trigger_use; + } + +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_RESET; +#endif +} + +.float platmovetype_start_default, platmovetype_end_default; +bool set_platmovetype(entity e, string s) +{ + // sets platmovetype_start and platmovetype_end based on a string consisting of two values + + int n = tokenize_console(s); + if(n > 0) + e.platmovetype_start = stof(argv(0)); + else + e.platmovetype_start = 0; + + if(n > 1) + e.platmovetype_end = stof(argv(1)); + else + e.platmovetype_end = e.platmovetype_start; + + if(n > 2) + if(argv(2) == "force") + return true; // no checking, return immediately + + if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end)) + { + objerror(e, "Invalid platform move type; platform would go in reverse, which is not allowed."); + return false; + } + + return true; +} diff --git a/qcsrc/common/mapobjects/platforms.qh b/qcsrc/common/mapobjects/platforms.qh new file mode 100644 index 0000000000..346cebc716 --- /dev/null +++ b/qcsrc/common/mapobjects/platforms.qh @@ -0,0 +1,15 @@ +#pragma once + + +const int PLAT_LOW_TRIGGER = BIT(0); + +.float dmgtime2; + +void plat_center_touch(entity this, entity toucher); +void plat_outside_touch(entity this, entity toucher); +void plat_trigger_use(entity this, entity actor, entity trigger); +void plat_go_up(entity this); +void plat_go_down(entity this); +void plat_crush(entity this, entity blocker); + +.float dmg; diff --git a/qcsrc/common/mapobjects/subs.qc b/qcsrc/common/mapobjects/subs.qc new file mode 100644 index 0000000000..f9e50dcf8f --- /dev/null +++ b/qcsrc/common/mapobjects/subs.qc @@ -0,0 +1,588 @@ +#include "subs.qh" +void SUB_NullThink(entity this) { } + +void SUB_CalcMoveDone(entity this); +void SUB_CalcAngleMoveDone(entity this); + +#ifdef SVQC +spawnfunc(info_null) +{ + delete(this); + // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately. +} +#endif + +/* +================== +SUB_Friction + +Applies some friction to this +================== +*/ +.float friction; +void SUB_Friction (entity this) +{ + this.nextthink = time; + if(IS_ONGROUND(this)) + this.velocity = this.velocity * (1 - frametime * this.friction); +} + +/* +================== +SUB_VanishOrRemove + +Makes client invisible or removes non-client +================== +*/ +void SUB_VanishOrRemove (entity ent) +{ + if (IS_CLIENT(ent)) + { + // vanish + ent.alpha = -1; + ent.effects = 0; +#ifdef SVQC + ent.glow_size = 0; + ent.pflags = 0; +#endif + } + else + { + // remove + delete(ent); + } +} + +void SUB_SetFade_Think (entity this) +{ + if(this.alpha == 0) + this.alpha = 1; + setthink(this, SUB_SetFade_Think); + this.nextthink = time; + this.alpha -= frametime * this.fade_rate; + if (this.alpha < 0.01) + SUB_VanishOrRemove(this); + else + this.nextthink = time; +} + +/* +================== +SUB_SetFade + +Fade 'ent' out when time >= 'when' +================== +*/ +void SUB_SetFade (entity ent, float when, float fading_time) +{ + ent.fade_rate = 1/fading_time; + setthink(ent, SUB_SetFade_Think); + ent.nextthink = when; +} + +/* +============= +SUB_CalcMove + +calculate this.velocity and this.nextthink to reach dest from +this.origin traveling at speed +=============== +*/ +void SUB_CalcMoveDone(entity this) +{ + // After moving, set origin to exact final destination + + setorigin (this, this.finaldest); + this.velocity = '0 0 0'; + this.nextthink = -1; + if (this.think1 && this.think1 != SUB_CalcMoveDone) + this.think1 (this); +} + +.float platmovetype_turn; +void SUB_CalcMove_controller_think (entity this) +{ + float traveltime; + float phasepos; + float nexttick; + vector delta; + vector delta2; + vector veloc; + vector angloc; + vector nextpos; + delta = this.destvec; + delta2 = this.destvec2; + if(time < this.animstate_endtime) + { + nexttick = time + PHYS_INPUT_FRAMETIME; + + traveltime = this.animstate_endtime - this.animstate_starttime; + phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1] + phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos); + nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); + // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) + + if(this.owner.platmovetype_turn) + { + vector destangle; + destangle = delta + 2 * delta2 * phasepos; + destangle = vectoangles(destangle); + destangle_x = -destangle_x; // flip up / down orientation + + // take the shortest distance for the angles + vector v = this.owner.angles; + v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5); + v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5); + v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5); + this.owner.angles = v; + angloc = destangle - this.owner.angles; + angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame + this.owner.avelocity = angloc; + } + if(nexttick < this.animstate_endtime) + veloc = nextpos - this.owner.origin; + else + veloc = this.finaldest - this.owner.origin; + veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame + + this.owner.velocity = veloc; + this.nextthink = nexttick; + } + else + { + // derivative: delta + 2 * delta2 (e.g. for angle positioning) + entity own = this.owner; + setthink(own, this.think1); + // set the owner's reference to this entity to NULL + own.move_controller = NULL; + delete(this); + getthink(own)(own); + } +} + +void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin) +{ + // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t + // 2 * control * t - 2 * control * t * t + destin * t * t + // 2 * control * t + (destin - 2 * control) * t * t + + setorigin(controller, org); + control -= org; + destin -= org; + + controller.destvec = 2 * control; // control point + controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point + // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control) +} + +void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin) +{ + // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t + // 2 * control * t - 2 * control * t * t + destin * t * t + // 2 * control * t + (destin - 2 * control) * t * t + + setorigin(controller, org); + destin -= org; + + controller.destvec = destin; // end point + controller.destvec2 = '0 0 0'; +} + +void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + float traveltime; + entity controller; + + if (!tspeed) + objerror (this, "No speed is defined!"); + + this.think1 = func; + this.finaldest = tdest; + setthink(this, SUB_CalcMoveDone); + + switch(tspeedtype) + { + default: + case TSPEED_START: + traveltime = 2 * vlen(tcontrol - this.origin) / tspeed; + break; + case TSPEED_END: + traveltime = 2 * vlen(tcontrol - tdest) / tspeed; + break; + case TSPEED_LINEAR: + traveltime = vlen(tdest - this.origin) / tspeed; + break; + case TSPEED_TIME: + traveltime = tspeed; + break; + } + + if (traveltime < 0.1) // useless anim + { + this.velocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + // delete the previous controller, otherwise changing movement midway is glitchy + if (this.move_controller != NULL) + { + delete(this.move_controller); + } + controller = new(SUB_CalcMove_controller); + controller.owner = this; + this.move_controller = controller; + controller.platmovetype = this.platmovetype; + controller.platmovetype_start = this.platmovetype_start; + controller.platmovetype_end = this.platmovetype_end; + SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest); + controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit. + controller.animstate_starttime = time; + controller.animstate_endtime = time + traveltime; + setthink(controller, SUB_CalcMove_controller_think); + controller.think1 = getthink(this); + + // the thinking is now done by the controller + setthink(this, SUB_NullThink); // for PushMove + this.nextthink = this.ltime + traveltime; + + // invoke controller + getthink(controller)(controller); +} + +void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + vector delta; + float traveltime; + + if (!tspeed) + objerror (this, "No speed is defined!"); + + this.think1 = func; + this.finaldest = tdest; + setthink(this, SUB_CalcMoveDone); + + if (tdest == this.origin) + { + this.velocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + delta = tdest - this.origin; + + switch(tspeedtype) + { + default: + case TSPEED_START: + case TSPEED_END: + case TSPEED_LINEAR: + traveltime = vlen (delta) / tspeed; + break; + case TSPEED_TIME: + traveltime = tspeed; + break; + } + + // Very short animations don't really show off the effect + // of controlled animation, so let's just use linear movement. + // Alternatively entities can choose to specify non-controlled movement. + // The only currently implemented alternative movement is linear (value 1) + if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct? + { + this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division + this.nextthink = this.ltime + traveltime; + return; + } + + // now just run like a bezier curve... + SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func); +} + +void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func) +{ + SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func); +} + +/* +============= +SUB_CalcAngleMove + +calculate this.avelocity and this.nextthink to reach destangle from +this.angles rotating + +The calling function should make sure this.setthink is valid +=============== +*/ +void SUB_CalcAngleMoveDone(entity this) +{ + // After rotating, set angle to exact final angle + this.angles = this.finalangle; + this.avelocity = '0 0 0'; + this.nextthink = -1; + if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops + this.think1 (this); +} + +// FIXME: I fixed this function only for rotation around the main axes +void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func) +{ + if (!tspeed) + objerror (this, "No speed is defined!"); + + // take the shortest distance for the angles + this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5); + this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5); + this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5); + vector delta = destangle - this.angles; + float traveltime; + + switch(tspeedtype) + { + default: + case TSPEED_START: + case TSPEED_END: + case TSPEED_LINEAR: + traveltime = vlen (delta) / tspeed; + break; + case TSPEED_TIME: + traveltime = tspeed; + break; + } + + this.think1 = func; + this.finalangle = destangle; + setthink(this, SUB_CalcAngleMoveDone); + + if (traveltime < 0.1) + { + this.avelocity = '0 0 0'; + this.nextthink = this.ltime + 0.1; + return; + } + + this.avelocity = delta * (1 / traveltime); + this.nextthink = this.ltime + traveltime; +} + +void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func) +{ + SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func); +} + +#ifdef SVQC +void ApplyMinMaxScaleAngles(entity e) +{ + if(e.angles.x != 0 || e.angles.z != 0 || e.avelocity.x != 0 || e.avelocity.z != 0) // "weird" rotation + { + e.maxs = '1 1 1' * vlen( + '1 0 0' * max(-e.mins.x, e.maxs.x) + + '0 1 0' * max(-e.mins.y, e.maxs.y) + + '0 0 1' * max(-e.mins.z, e.maxs.z) + ); + e.mins = -e.maxs; + } + else if(e.angles.y != 0 || e.avelocity.y != 0) // yaw only is a bit better + { + e.maxs_x = vlen( + '1 0 0' * max(-e.mins.x, e.maxs.x) + + '0 1 0' * max(-e.mins.y, e.maxs.y) + ); + e.maxs_y = e.maxs.x; + e.mins_x = -e.maxs.x; + e.mins_y = -e.maxs.x; + } + if(e.scale) + setsize(e, e.mins * e.scale, e.maxs * e.scale); + else + setsize(e, e.mins, e.maxs); +} + +void SetBrushEntityModel(entity this) +{ + if(this.model != "") + { + precache_model(this.model); + if(this.mins != '0 0 0' || this.maxs != '0 0 0') + { + vector mi = this.mins; + vector ma = this.maxs; + _setmodel(this, this.model); // no precision needed + setsize(this, mi, ma); + } + else + _setmodel(this, this.model); // no precision needed + InitializeEntity(this, LODmodel_attach, INITPRIO_FINDTARGET); + } + setorigin(this, this.origin); + ApplyMinMaxScaleAngles(this); +} + +void SetBrushEntityModelNoLOD(entity this) +{ + if(this.model != "") + { + precache_model(this.model); + if(this.mins != '0 0 0' || this.maxs != '0 0 0') + { + vector mi = this.mins; + vector ma = this.maxs; + _setmodel(this, this.model); // no precision needed + setsize(this, mi, ma); + } + else + _setmodel(this, this.model); // no precision needed + } + setorigin(this, this.origin); + ApplyMinMaxScaleAngles(this); +} + +bool LOD_customize(entity this, entity client) +{ + if(autocvar_loddebug) + { + int d = autocvar_loddebug; + if(d == 1) + this.modelindex = this.lodmodelindex0; + else if(d == 2 || !this.lodmodelindex2) + this.modelindex = this.lodmodelindex1; + else // if(d == 3) + this.modelindex = this.lodmodelindex2; + return true; + } + + // TODO csqc network this so it only gets sent once + vector near_point = NearestPointOnBox(this, client.origin); + if(vdist(near_point - client.origin, <, this.loddistance1)) + this.modelindex = this.lodmodelindex0; + else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2)) + this.modelindex = this.lodmodelindex1; + else + this.modelindex = this.lodmodelindex2; + + return true; +} + +void LOD_uncustomize(entity this) +{ + this.modelindex = this.lodmodelindex0; +} + +void LODmodel_attach(entity this) +{ + entity e; + + if(!this.loddistance1) + this.loddistance1 = 1000; + if(!this.loddistance2) + this.loddistance2 = 2000; + this.lodmodelindex0 = this.modelindex; + + if(this.lodtarget1 != "") + { + e = find(NULL, targetname, this.lodtarget1); + if(e) + { + this.lodmodel1 = e.model; + delete(e); + } + } + if(this.lodtarget2 != "") + { + e = find(NULL, targetname, this.lodtarget2); + if(e) + { + this.lodmodel2 = e.model; + delete(e); + } + } + + if(autocvar_loddebug < 0) + { + this.lodmodel1 = this.lodmodel2 = ""; // don't even initialize + } + + if(this.lodmodel1 != "") + { + vector mi, ma; + mi = this.mins; + ma = this.maxs; + + precache_model(this.lodmodel1); + _setmodel(this, this.lodmodel1); + this.lodmodelindex1 = this.modelindex; + + if(this.lodmodel2 != "") + { + precache_model(this.lodmodel2); + _setmodel(this, this.lodmodel2); + this.lodmodelindex2 = this.modelindex; + } + + this.modelindex = this.lodmodelindex0; + setsize(this, mi, ma); + } + + if(this.lodmodelindex1) + if (!getSendEntity(this)) + SetCustomizer(this, LOD_customize, LOD_uncustomize); +} + +/* +================ +InitTrigger +================ +*/ + +void SetMovedir(entity this) +{ + if(this.movedir != '0 0 0') + this.movedir = normalize(this.movedir); + else + { + makevectors(this.angles); + this.movedir = v_forward; + } + + this.angles = '0 0 0'; +} + +void InitTrigger(entity this) +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + SetMovedir(this); + this.solid = SOLID_TRIGGER; + SetBrushEntityModelNoLOD(this); + set_movetype(this, MOVETYPE_NONE); + this.modelindex = 0; + this.model = ""; +} + +void InitSolidBSPTrigger(entity this) +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + SetMovedir(this); + this.solid = SOLID_BSP; + SetBrushEntityModelNoLOD(this); + set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0 +// this.modelindex = 0; + this.model = ""; +} + +bool InitMovingBrushTrigger(entity this) +{ +// trigger angles are used for one-way touches. An angle of 0 is assumed +// to mean no restrictions, so use a yaw of 360 instead. + this.solid = SOLID_BSP; + SetBrushEntityModel(this); + set_movetype(this, MOVETYPE_PUSH); + if(this.modelindex == 0) + { + objerror(this, "InitMovingBrushTrigger: no brushes found!"); + return false; + } + return true; +} +#endif diff --git a/qcsrc/common/mapobjects/subs.qh b/qcsrc/common/mapobjects/subs.qh new file mode 100644 index 0000000000..861d73e72f --- /dev/null +++ b/qcsrc/common/mapobjects/subs.qh @@ -0,0 +1,171 @@ +#pragma once +#include "defs.qh" + +.float friction; +void SUB_Friction(entity this); + +void SUB_NullThink(entity this); + +/* +================== +SUB_VanishOrRemove + +Makes client invisible or removes non-client +================== +*/ +void SUB_VanishOrRemove(entity ent); + +void SUB_SetFade_Think(entity this); + +/* +================== +SUB_SetFade + +Fade 'ent' out when time >= 'when' +================== +*/ +.float fade_rate; +void SUB_SetFade(entity ent, float when, float fading_time); + +.vector finaldest, finalangle; //plat.qc stuff +.void(entity this) think1; +.float state; +.float t_length, t_width; + +.vector destvec; +.vector destvec2; + +.float delay; +.float wait; +.float lip; +.float speed; +.float sounds; +.string platmovetype; +.float platmovetype_start, platmovetype_end; + +//entity activator; + +.string killtarget; + +.vector pos1, pos2; +.vector mangle; + +.string target2; +.string target3; +.string target4; +.string curvetarget; +.float target_random; +.float trigger_reverse; + +// Keys player is holding +.float itemkeys; +// message delay for func_door locked by keys and key locks +// this field is used on player entities +.float key_door_messagetime; + +.vector dest1, dest2; + +.entity move_controller; + +const int TSPEED_TIME = -1; +const int TSPEED_LINEAR = 0; +const int TSPEED_START = 1; +const int TSPEED_END = 2; +// TODO average too? + +#ifdef CSQC +// this stuff is defined in the server side engine VM, so we must define it separately here +.float takedamage; +const int DAMAGE_NO = 0; +const int DAMAGE_YES = 1; +const int DAMAGE_AIM = 2; + +.string noise, noise1, noise2, noise3; // contains names of wavs to play + +.float max_health; // players maximum health is stored here +#endif + +#ifdef SVQC +spawnfunc(info_null); +#endif + +/* +============= +SUB_CalcMove + +calculate this.velocity and this.nextthink to reach dest from +this.origin traveling at speed +=============== +*/ +void SUB_CalcMoveDone(entity this); + +.float platmovetype_turn; +void SUB_CalcMove_controller_think (entity this); + +void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest); + +void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest); + +void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func); + +void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func); + +void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func); + +#ifdef SVQC +void ApplyMinMaxScaleAngles(entity e); + +void SetBrushEntityModel(entity this); + +void SetBrushEntityModelNoLOD(entity this); + +int autocvar_loddebug; +.string lodtarget1; +.string lodtarget2; +.string lodmodel1; +.string lodmodel2; +.float lodmodelindex0; +.float lodmodelindex1; +.float lodmodelindex2; +.float loddistance1; +.float loddistance2; + +bool LOD_customize(entity this, entity client); + +void LOD_uncustomize(entity this); + +void LODmodel_attach(entity this); +#endif + +/* +============= +SUB_CalcAngleMove + +calculate this.avelocity and this.nextthink to reach destangle from +this.angles rotating + +The calling function should make sure this.think is valid +=============== +*/ +void SUB_CalcAngleMoveDone (entity this); + +// FIXME: I fixed this function only for rotation around the main axes +void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func); + +void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func); + +/* +================ +InitTrigger +================ +*/ + +#ifdef SVQC +void SetMovedir(entity this); + +void InitTrigger(entity this); + +void InitSolidBSPTrigger(entity this); + +bool InitMovingBrushTrigger(entity this); +#endif diff --git a/qcsrc/common/mapobjects/target/_mod.inc b/qcsrc/common/mapobjects/target/_mod.inc new file mode 100644 index 0000000000..f9cc536eda --- /dev/null +++ b/qcsrc/common/mapobjects/target/_mod.inc @@ -0,0 +1,10 @@ +// generated file; do not modify +#include <common/mapobjects/target/changelevel.qc> +#include <common/mapobjects/target/kill.qc> +#include <common/mapobjects/target/levelwarp.qc> +#include <common/mapobjects/target/location.qc> +#include <common/mapobjects/target/music.qc> +#include <common/mapobjects/target/spawn.qc> +#include <common/mapobjects/target/spawnpoint.qc> +#include <common/mapobjects/target/speaker.qc> +#include <common/mapobjects/target/voicescript.qc> diff --git a/qcsrc/common/mapobjects/target/_mod.qh b/qcsrc/common/mapobjects/target/_mod.qh new file mode 100644 index 0000000000..e3f4cede99 --- /dev/null +++ b/qcsrc/common/mapobjects/target/_mod.qh @@ -0,0 +1,10 @@ +// generated file; do not modify +#include <common/mapobjects/target/changelevel.qh> +#include <common/mapobjects/target/kill.qh> +#include <common/mapobjects/target/levelwarp.qh> +#include <common/mapobjects/target/location.qh> +#include <common/mapobjects/target/music.qh> +#include <common/mapobjects/target/spawn.qh> +#include <common/mapobjects/target/spawnpoint.qh> +#include <common/mapobjects/target/speaker.qh> +#include <common/mapobjects/target/voicescript.qh> diff --git a/qcsrc/common/mapobjects/target/changelevel.qc b/qcsrc/common/mapobjects/target/changelevel.qc new file mode 100644 index 0000000000..114fd87181 --- /dev/null +++ b/qcsrc/common/mapobjects/target/changelevel.qc @@ -0,0 +1,55 @@ +#include "changelevel.qh" +#ifdef SVQC +.string chmap, gametype; +.entity chlevel_targ; + +void target_changelevel_use(entity this, entity actor, entity trigger) +{ + if(this.spawnflags & CHANGELEVEL_MULTIPLAYER) + { + // simply don't react if a non-player triggers it + if(!IS_PLAYER(actor)) { return; } + + actor.chlevel_targ = this; + + int plnum = 0; + int realplnum = 0; + // let's not count bots + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { + ++realplnum; + if(it.chlevel_targ == this) + ++plnum; + }); + if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players + return; + } + + if(this.gametype != "") + MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype)); + + if (this.chmap == "") + localcmd("endmatch\n"); + else + localcmd(strcat("changelevel ", this.chmap, "\n")); +} + +/*target_changelevel +Target to change/end level +KEYS: +chmap: map to switch to, leave empty for endmatch +gametype: gametype for the next map +count: fraction of real players that need to trigger this entity for levelchange +SPAWNFLAGS: +CHANGELEVEL_MULTIPLAYER: multiplayer support +*/ + +spawnfunc(target_changelevel) +{ + this.use = target_changelevel_use; + + if(!this.count) + { + this.count = 0.7; + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/changelevel.qh b/qcsrc/common/mapobjects/target/changelevel.qh new file mode 100644 index 0000000000..f6e206edc9 --- /dev/null +++ b/qcsrc/common/mapobjects/target/changelevel.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int CHANGELEVEL_MULTIPLAYER = BIT(1); diff --git a/qcsrc/common/mapobjects/target/kill.qc b/qcsrc/common/mapobjects/target/kill.qc new file mode 100644 index 0000000000..2751c600ea --- /dev/null +++ b/qcsrc/common/mapobjects/target/kill.qc @@ -0,0 +1,26 @@ +#include "kill.qh" +#include "location.qh" +#ifdef SVQC + +void target_kill_use(entity this, entity actor, entity trigger) +{ + if(actor.takedamage == DAMAGE_NO) + return; + + if(!actor.iscreature && !actor.damagedbytriggers) + return; + + Damage(actor, this, trigger, 1000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, actor.origin, '0 0 0'); +} + +spawnfunc(target_kill) +{ + this.classname = "target_kill"; + + if (this.message == "") + this.message = "was in the wrong place"; + + this.use = target_kill_use; +} + +#endif diff --git a/qcsrc/common/mapobjects/target/kill.qh b/qcsrc/common/mapobjects/target/kill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/target/kill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/levelwarp.qc b/qcsrc/common/mapobjects/target/levelwarp.qc new file mode 100644 index 0000000000..21419cf81a --- /dev/null +++ b/qcsrc/common/mapobjects/target/levelwarp.qc @@ -0,0 +1,21 @@ +#include "levelwarp.qh" + +#ifdef SVQC +void target_levelwarp_use(entity this, entity actor, entity trigger) +{ + if(!autocvar_g_campaign) + return; // only in campaign + + if(this.cnt) + CampaignLevelWarp(this.cnt - 1); // specific level + else + CampaignLevelWarp(-1); // next level +} + +spawnfunc(target_levelwarp) +{ + // this.cnt is index (starting from 1) of the campaign level to warp to + // 0 means next level + this.use = target_levelwarp_use; +} +#endif diff --git a/qcsrc/common/mapobjects/target/levelwarp.qh b/qcsrc/common/mapobjects/target/levelwarp.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/target/levelwarp.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/location.qc b/qcsrc/common/mapobjects/target/location.qc new file mode 100644 index 0000000000..5774f45f99 --- /dev/null +++ b/qcsrc/common/mapobjects/target/location.qc @@ -0,0 +1,25 @@ +#include "location.qh" +#ifdef SVQC +void target_push_init(entity this); + +spawnfunc(target_location) +{ + this.classname = "target_location"; + // location name in netname + // eventually support: count, teamgame selectors, line of sight? + + target_push_init(this); + + IL_PUSH(g_locations, this); +} + +spawnfunc(info_location) +{ + this.classname = "target_location"; + this.message = this.netname; + + target_push_init(this); + + IL_PUSH(g_locations, this); +} +#endif diff --git a/qcsrc/common/mapobjects/target/location.qh b/qcsrc/common/mapobjects/target/location.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/target/location.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/music.qc b/qcsrc/common/mapobjects/target/music.qc new file mode 100644 index 0000000000..5a63872dbd --- /dev/null +++ b/qcsrc/common/mapobjects/target/music.qc @@ -0,0 +1,344 @@ +#include "music.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <common/constants.qh> + #include <common/net_linked.qh> + #include <server/constants.qh> + #include <server/defs.qh> +#endif + +REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC) +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC) + +#ifdef SVQC + +IntrusiveList g_targetmusic_list; +STATIC_INIT(g_targetmusic_list) +{ + g_targetmusic_list = IL_NEW(); +} + +// values: +// volume +// noise +// targetname +// lifetime +// fade_time +// fade_rate +// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0) +// when targetname is not set, THIS ONE is default +void target_music_sendto(entity this, int to, bool is) +{ + WriteHeader(to, TE_CSQC_TARGET_MUSIC); + WriteShort(to, etof(this)); + WriteByte(to, this.volume * 255.0 * is); + WriteByte(to, this.fade_time * 16.0); + WriteByte(to, this.fade_rate * 16.0); + WriteByte(to, this.lifetime); + WriteString(to, this.noise); +} +void target_music_reset(entity this) +{ + if (this.targetname == "") + { + target_music_sendto(this, MSG_ALL, true); + } +} +void target_music_kill() +{ + IL_EACH(g_targetmusic_list, true, + { + it.volume = 0; + if (it.targetname == "") + target_music_sendto(it, MSG_ALL, true); + else + target_music_sendto(it, MSG_ALL, false); + }); +} +void target_music_use(entity this, entity actor, entity trigger) +{ + if(!actor) + return; + if(IS_REAL_CLIENT(actor)) + { + msg_entity = actor; + target_music_sendto(this, MSG_ONE, true); + } + FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, { + msg_entity = it; + target_music_sendto(this, MSG_ONE, true); + }); +} +spawnfunc(target_music) +{ + this.use = target_music_use; + this.reset = target_music_reset; + if(!this.volume) + this.volume = 1; + IL_PUSH(g_targetmusic_list, this); + if(this.targetname == "") + target_music_sendto(this, MSG_INIT, true); + else + target_music_sendto(this, MSG_INIT, false); +} +void TargetMusic_RestoreGame() +{ + IL_EACH(g_targetmusic_list, true, + { + if(it.targetname == "") + target_music_sendto(it, MSG_INIT, true); + else + target_music_sendto(it, MSG_INIT, false); + }); +} +// values: +// volume +// noise +// targetname +// fade_time +// spawnflags: +// START_DISABLED +// can be disabled/enabled for everyone with relays +bool trigger_music_SendEntity(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & SF_MUSIC_ORIGIN) + { + WriteVector(MSG_ENTITY, this.origin); + } + if(sendflags & SF_TRIGGER_INIT) + { + if(this.model != "null") + { + WriteShort(MSG_ENTITY, this.modelindex); + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + } + else + { + WriteShort(MSG_ENTITY, 0); + WriteVector(MSG_ENTITY, this.maxs); + } + WriteByte(MSG_ENTITY, this.volume * 255.0); + WriteByte(MSG_ENTITY, this.fade_time * 16.0); + WriteByte(MSG_ENTITY, this.fade_rate * 16.0); + WriteString(MSG_ENTITY, this.noise); + } + if(sendflags & SF_TRIGGER_UPDATE) + { + WriteByte(MSG_ENTITY, this.active); + } + return true; +} +void trigger_music_reset(entity this) +{ + if(this.spawnflags & START_DISABLED) + { + this.setactive(this, ACTIVE_NOT); + } + else + { + this.setactive(this, ACTIVE_ACTIVE); + } +} + +spawnfunc(trigger_music) +{ + if(this.model != "") + { + _setmodel(this, this.model); + } + if(!this.volume) + { + this.volume = 1; + } + if(!this.modelindex) + { + setorigin(this, this.origin + this.mins); + setsize(this, '0 0 0', this.maxs - this.mins); + } + + this.setactive = generic_netlinked_setactive; + this.use = generic_netlinked_legacy_use; // backwards compatibility + this.reset = trigger_music_reset; + this.reset(this); + + Net_LinkEntity(this, false, 0, trigger_music_SendEntity); +} +#elif defined(CSQC) + +entity TargetMusic_list; +STATIC_INIT(TargetMusic_list) +{ + TargetMusic_list = LL_NEW(); +} + +void TargetMusic_Advance() +{ + // run AFTER all the thinks! + entity best = music_default; + if (music_target && time < music_target.lifetime) + { + best = music_target; + } + if (music_trigger) + { + best = music_trigger; + } + LL_EACH(TargetMusic_list, it.noise, { + const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1; + if (it == best) + { + // increase volume + it.state = (it.fade_time > 0) ? bound(0, it.state + frametime / it.fade_time, 1) : 1; + } + else + { + // decrease volume + it.state = (it.fade_rate > 0) ? bound(0, it.state - frametime / it.fade_rate, 1) : 0; + } + const float vol = it.state * it.volume * autocvar_bgmvolume; + if (vol != vol0) + { + if(vol0 < 0) + sound7(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE, 0, BIT(4)); // restart + else + sound7(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE, 0, BIT(4)); + it.lastvol = vol; + } + }); + music_trigger = NULL; + bgmtime = (best) ? getsoundtime(best, CH_BGM_SINGLE) : gettime(GETTIME_CDTRACK); +} + +NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew) +{ + Net_TargetMusic(); + return true; +} + +void Net_TargetMusic() +{ + const int id = ReadShort(); + const float vol = ReadByte() / 255.0; + const float fai = ReadByte() / 16.0; + const float fao = ReadByte() / 16.0; + const float tim = ReadByte(); + const string noi = ReadString(); + + entity e = NULL; + LL_EACH(TargetMusic_list, it.count == id, { e = it; break; }); + if (!e) + { + LL_PUSH(TargetMusic_list, e = new_pure(TargetMusic)); + e.count = id; + } + if(e.noise != noi) + { + strcpy(e.noise, noi); + precache_sound(e.noise); + _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); + if(getsoundtime(e, CH_BGM_SINGLE) < 0) + { + LOG_TRACEF("Cannot initialize sound %s", e.noise); + strfree(e.noise); + } + } + e.volume = vol; + e.fade_time = fai; + e.fade_rate = fao; + if(vol > 0) + { + if(tim == 0) + { + music_default = e; + if(!music_disabled) + { + e.state = 2; + cvar_settemp("music_playlist_index", "-1"); // don't use playlists + localcmd("cd stop\n"); // just in case + music_disabled = 1; + } + } + else + { + music_target = e; + e.lifetime = time + tim; + } + } +} + +void Ent_TriggerMusic_Think(entity this) +{ + if(this.active == ACTIVE_NOT) + { + return; + } + vector org = (csqcplayer) ? csqcplayer.origin : view_origin; + if(WarpZoneLib_BoxTouchesBrush(org + STAT(PL_MIN), org + STAT(PL_MAX), this, NULL)) + { + music_trigger = this; + } +} + +void Ent_TriggerMusic_Remove(entity this) +{ + strfree(this.noise); +} + +NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) +{ + int sendflags = ReadByte(); + if(sendflags & SF_MUSIC_ORIGIN) + { + this.origin = ReadVector(); + } + if(sendflags & SF_TRIGGER_INIT) + { + this.modelindex = ReadShort(); + if(this.modelindex) + { + this.mins = ReadVector(); + this.maxs = ReadVector(); + } + else + { + this.mins = '0 0 0'; + this.maxs = ReadVector(); + } + + this.volume = ReadByte() / 255.0; + this.fade_time = ReadByte() / 16.0; + this.fade_rate = ReadByte() / 16.0; + string s = this.noise; + strcpy(this.noise, ReadString()); + if(this.noise != s) + { + precache_sound(this.noise); + sound7(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE, 0, BIT(4)); + if(getsoundtime(this, CH_BGM_SINGLE) < 0) + { + LOG_WARNF("Cannot initialize sound %s", this.noise); + strfree(this.noise); + } + } + } + if(sendflags & SF_TRIGGER_UPDATE) + { + this.active = ReadByte(); + } + + setorigin(this, this.origin); + setsize(this, this.mins, this.maxs); + this.draw = Ent_TriggerMusic_Think; + if(isnew) + { + LL_PUSH(TargetMusic_list, this); + IL_PUSH(g_drawables, this); + } + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/target/music.qh b/qcsrc/common/mapobjects/target/music.qh new file mode 100644 index 0000000000..fac00152a8 --- /dev/null +++ b/qcsrc/common/mapobjects/target/music.qh @@ -0,0 +1,30 @@ +#pragma once + +.float lifetime; + +const int SF_MUSIC_ORIGIN = BIT(2); + +#ifdef CSQC +float music_disabled; +entity music_default; +entity music_target; +entity music_trigger; +// FIXME also control bgmvolume here, to not require a target_music for the default track. + +entityclass(TargetMusic); +classfield(TargetMusic) .int state; +classfield(TargetMusic) .float lastvol; + +void TargetMusic_Advance(); + +void Net_TargetMusic(); + +void Ent_TriggerMusic_Think(entity this); + +void Ent_TriggerMusic_Remove(entity this); + +#elif defined(SVQC) +void target_music_kill(); + +void TargetMusic_RestoreGame(); +#endif diff --git a/qcsrc/common/mapobjects/target/spawn.qc b/qcsrc/common/mapobjects/target/spawn.qc new file mode 100644 index 0000000000..9c999ed4df --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawn.qc @@ -0,0 +1,340 @@ +#include "spawn.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <common/util.qh> + #include <server/defs.qh> +#endif + +#ifdef SVQC + +// spawner entity +// "classname" "target_spawn" +// "message" "fieldname value fieldname value ..." +// "spawnflags" +// ON_MAPLOAD = trigger on map load + +float target_spawn_initialized; +.void(entity this) target_spawn_spawnfunc; +float target_spawn_spawnfunc_field; +.entity target_spawn_activator; +.float target_spawn_id; +float target_spawn_count; + +void target_spawn_helper_setmodel(entity this) +{ + _setmodel(this, this.model); +} + +void target_spawn_helper_setsize(entity this) +{ + setsize(this, this.mins, this.maxs); +} + +void target_spawn_edit_entity(entity this, entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act, entity trigger) +{ + float i, n, valuefieldpos; + string key, value, valuefield, valueoffset, valueoffsetrandom; + entity valueent; + vector data, data2; + + n = tokenize_console(msg); + + for(i = 0; i < n-1; i += 2) + { + key = argv(i); + value = argv(i+1); + if(key == "$") + { + data.x = -1; + data.y = FIELD_STRING; + } + else + { + data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key))); + if(data.y == 0) // undefined field, i.e., invalid type + { + LOG_INFO("target_spawn: invalid/unknown entity key ", key, " specified, ignored!"); + continue; + } + } + if(substring(value, 0, 1) == "$") + { + value = substring(value, 1, strlen(value) - 1); + if(substring(value, 0, 1) == "$") + { + // deferred replacement + // do nothing + // useful for creating target_spawns with this! + } + else + { + // replace me! + valuefieldpos = strstrofs(value, "+", 0); + valueoffset = ""; + if(valuefieldpos != -1) + { + valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(valueoffset, "+", 0); + valueoffsetrandom = ""; + if(valuefieldpos != -1) + { + valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1); + valueoffset = substring(valueoffset, 0, valuefieldpos); + } + + valuefieldpos = strstrofs(value, ".", 0); + valuefield = ""; + if(valuefieldpos != -1) + { + valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); + value = substring(value, 0, valuefieldpos); + } + + if(value == "self") + { + valueent = this; + value = ""; + } + else if(value == "activator") + { + valueent = act; + value = ""; + } + else if(value == "other") + { + valueent = trigger; + value = ""; + } + else if(value == "pusher") + { + if(time < act.pushltime) + valueent = act.pusher; + else + valueent = NULL; + value = ""; + } + else if(value == "target") + { + valueent = e; + value = ""; + } + else if(value == "killtarget") + { + valueent = kt; + value = ""; + } + else if(value == "target2") + { + valueent = t2; + value = ""; + } + else if(value == "target3") + { + valueent = t3; + value = ""; + } + else if(value == "target4") + { + valueent = t4; + value = ""; + } + else if(value == "time") + { + valueent = NULL; + value = ftos(time); + } + else + { + LOG_INFO("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!"); + continue; + } + + if(valuefield == "") + { + if(value == "") + value = ftos(etof(valueent)); + } + else + { + if(value != "") + { + LOG_INFO("target_spawn: try to get a field of a non-entity, ignored!"); + continue; + } + data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); + if(data2_y == 0) // undefined field, i.e., invalid type + { + LOG_INFO("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!"); + continue; + } + value = getentityfieldstring(data2_x, valueent); + } + + if(valueoffset != "") + { + switch(data.y) + { + case FIELD_STRING: + value = strcat(value, valueoffset); + break; + case FIELD_FLOAT: + value = ftos(stof(value) + stof(valueoffset)); + break; + case FIELD_VECTOR: + value = vtos(stov(value) + stov(valueoffset)); + break; + default: + LOG_INFO("target_spawn: only string, float and vector fields can do calculations, calculation ignored!"); + break; + } + } + + if(valueoffsetrandom != "") + { + switch(data.y) + { + case FIELD_FLOAT: + value = ftos(stof(value) + random() * stof(valueoffsetrandom)); + break; + case FIELD_VECTOR: + data2 = stov(valueoffsetrandom); + value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1'); + break; + default: + LOG_INFO("target_spawn: only float and vector fields can do random calculations, calculation ignored!"); + break; + } + } + } + } + if(key == "$") + { + if(substring(value, 0, 1) == "_") + value = strcat("target_spawn_helper", value); + putentityfieldstring(target_spawn_spawnfunc_field, e, value); + + e.target_spawn_spawnfunc(e); + + // We called an external function, so we have to re-tokenize msg. + n = tokenize_console(msg); + } + else + { + if(data.y == FIELD_VECTOR) + value = strreplace("'", "", value); // why?!? + putentityfieldstring(data.x, e, value); + } + } +} + +void target_spawn_useon(entity e, entity this, entity actor, entity trigger) +{ + this.target_spawn_activator = actor; + target_spawn_edit_entity( + this, + e, + this.message, + find(NULL, targetname, this.killtarget), + find(NULL, targetname, this.target2), + find(NULL, targetname, this.target3), + find(NULL, targetname, this.target4), + actor, + trigger + ); +} + +bool target_spawn_cancreate(entity this) +{ + float c; + entity e; + + c = this.count; + if(c == 0) // no limit? + return true; + + ++c; // increase count to not include MYSELF + for(e = NULL; (e = findfloat(e, target_spawn_id, this.target_spawn_id)); --c) + ; + + // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more + if(c == 0) + return false; + return true; +} + +void target_spawn_use(entity this, entity actor, entity trigger) +{ + if(this.target == "") + { + // spawn new entity + if(!target_spawn_cancreate(this)) + return; + entity e = spawn(); + e.spawnfunc_checked = true; + target_spawn_useon(e, this, actor, trigger); + e.target_spawn_id = this.target_spawn_id; + } + else if(this.target == "*activator") + { + // edit entity + if(actor) + target_spawn_useon(actor, this, actor, trigger); + } + else + { + // edit entity + FOREACH_ENTITY_STRING(targetname, this.target, + { + target_spawn_useon(it, this, actor, trigger); + }); + } +} + +void target_spawn_spawnfirst(entity this) +{ + entity act = this.target_spawn_activator; + if(this.spawnflags & ON_MAPLOAD) + target_spawn_use(this, act, NULL); +} + +void initialize_field_db() +{ + if(!target_spawn_initialized) + { + float n, i; + string fn; + vector prev, next; + float ft; + + n = numentityfields(); + for(i = 0; i < n; ++i) + { + fn = entityfieldname(i); + ft = entityfieldtype(i); + next = i * '1 0 0' + ft * '0 1 0' + '0 0 1'; + prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn))); + if(prev.y == 0) + { + db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(next)); + if(fn == "target_spawn_spawnfunc") + target_spawn_spawnfunc_field = i; + } + } + + target_spawn_initialized = 1; + } +} + +spawnfunc(target_spawn) +{ + initialize_field_db(); + this.use = target_spawn_use; + this.message = strzone(strreplace("'", "\"", this.message)); + this.target_spawn_id = ++target_spawn_count; + InitializeEntity(this, target_spawn_spawnfirst, INITPRIO_LAST); +} +#endif diff --git a/qcsrc/common/mapobjects/target/spawn.qh b/qcsrc/common/mapobjects/target/spawn.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawn.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/target/spawnpoint.qc b/qcsrc/common/mapobjects/target/spawnpoint.qc new file mode 100644 index 0000000000..fe1538551e --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawnpoint.qc @@ -0,0 +1,24 @@ +#include "spawnpoint.qh" + +#ifdef SVQC +void target_spawnpoint_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + actor.spawnpoint_targ = this; +} + +void target_spawnpoint_reset(entity this) +{ + this.active = ACTIVE_ACTIVE; +} + +// TODO: persistent spawnflag? +spawnfunc(target_spawnpoint) +{ + this.active = ACTIVE_ACTIVE; + this.use = target_spawnpoint_use; + this.reset = target_spawnpoint_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/target/spawnpoint.qh b/qcsrc/common/mapobjects/target/spawnpoint.qh new file mode 100644 index 0000000000..2eeb8da625 --- /dev/null +++ b/qcsrc/common/mapobjects/target/spawnpoint.qh @@ -0,0 +1,5 @@ +#pragma once + +#ifdef SVQC +.entity spawnpoint_targ; +#endif diff --git a/qcsrc/common/mapobjects/target/speaker.qc b/qcsrc/common/mapobjects/target/speaker.qc new file mode 100644 index 0000000000..11c9ad7bae --- /dev/null +++ b/qcsrc/common/mapobjects/target/speaker.qc @@ -0,0 +1,138 @@ +#include "speaker.qh" +#ifdef SVQC +// TODO add a way to do looped sounds with sound(); then complete this entity +void target_speaker_use_off(entity this, entity actor, entity trigger); +void target_speaker_use_activator(entity this, entity actor, entity trigger) +{ + if (!IS_REAL_CLIENT(actor)) + return; + string snd; + if(substring(this.noise, 0, 1) == "*") + { + var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); + if(GetPlayerSoundSampleField_notFound) + snd = SND(Null); + else if(actor.(sample) == "") + snd = SND(Null); + else + { + tokenize_console(actor.(sample)); + float n; + n = stof(argv(1)); + if(n > 0) + snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization + else + snd = strcat(argv(0), ".wav"); // randomization + } + } + else + snd = this.noise; + msg_entity = actor; + soundto(MSG_ONE, this, CH_TRIGGER, snd, VOL_BASE * this.volume, this.atten); +} +void target_speaker_use_on(entity this, entity actor, entity trigger) +{ + string snd; + if(substring(this.noise, 0, 1) == "*") + { + var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); + if(GetPlayerSoundSampleField_notFound) + snd = SND(Null); + else if(actor.(sample) == "") + snd = SND(Null); + else + { + tokenize_console(actor.(sample)); + float n; + n = stof(argv(1)); + if(n > 0) + snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization + else + snd = strcat(argv(0), ".wav"); // randomization + } + } + else + snd = this.noise; + _sound(this, CH_TRIGGER_SINGLE, snd, VOL_BASE * this.volume, this.atten); + if(this.spawnflags & (SPEAKER_LOOPED_ON + SPEAKER_LOOPED_OFF)) + this.use = target_speaker_use_off; +} +void target_speaker_use_off(entity this, entity actor, entity trigger) +{ + sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASE * this.volume, this.atten); + this.use = target_speaker_use_on; +} +void target_speaker_reset(entity this) +{ + if(this.spawnflags & SPEAKER_LOOPED_ON) + { + if(this.use == target_speaker_use_on) + target_speaker_use_on(this, NULL, NULL); + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + if(this.use == target_speaker_use_off) + target_speaker_use_off(this, NULL, NULL); + } +} + +spawnfunc(target_speaker) +{ + // TODO: "*" prefix to sound file name + // TODO: wait and random (just, HOW? random is not a field) + if(this.noise) + precache_sound (this.noise); + + if(!this.atten && (this.spawnflags & SPEAKER_GLOBAL)) + { + LOG_WARN("target_speaker uses legacy spawnflag GLOBAL (BIT(2)), please set atten to -1 instead"); + this.atten = -1; + } + + if(!this.atten) + { + IFTARGETED + this.atten = ATTEN_NORM; + else + this.atten = ATTEN_STATIC; + } + else if(this.atten < 0) + this.atten = 0; + + if(!this.volume) + this.volume = 1; + + IFTARGETED + { + if(this.spawnflags & SPEAKER_ACTIVATOR) + this.use = target_speaker_use_activator; + else if(this.spawnflags & SPEAKER_LOOPED_ON) + { + target_speaker_use_on(this, NULL, NULL); + this.reset = target_speaker_reset; + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + this.use = target_speaker_use_on; + this.reset = target_speaker_reset; + } + else + this.use = target_speaker_use_on; + } + else if(this.spawnflags & SPEAKER_LOOPED_ON) + { + ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); + delete(this); + } + else if(this.spawnflags & SPEAKER_LOOPED_OFF) + { + objerror(this, "This sound entity can never be activated"); + } + else + { + // Quake/Nexuiz fallback + ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); + delete(this); + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/speaker.qh b/qcsrc/common/mapobjects/target/speaker.qh new file mode 100644 index 0000000000..53e0194be4 --- /dev/null +++ b/qcsrc/common/mapobjects/target/speaker.qh @@ -0,0 +1,7 @@ +#pragma once + + +const int SPEAKER_LOOPED_ON = BIT(0); +const int SPEAKER_LOOPED_OFF = BIT(1); +const int SPEAKER_GLOBAL = BIT(2); // legacy, set speaker atten to -1 instead +const int SPEAKER_ACTIVATOR = BIT(3); diff --git a/qcsrc/common/mapobjects/target/voicescript.qc b/qcsrc/common/mapobjects/target/voicescript.qc new file mode 100644 index 0000000000..6dfb568a8b --- /dev/null +++ b/qcsrc/common/mapobjects/target/voicescript.qc @@ -0,0 +1,102 @@ +#include "voicescript.qh" +#ifdef SVQC +.entity voicescript; // attached voice script +.float voicescript_index; // index of next voice, or -1 to use the randomized ones +.float voicescript_nextthink; // time to play next voice +.float voicescript_voiceend; // time when this voice ends + +void target_voicescript_clear(entity pl) +{ + pl.voicescript = NULL; +} + +void target_voicescript_use(entity this, entity actor, entity trigger) +{ + if(actor.voicescript != this) + { + actor.voicescript = this; + actor.voicescript_index = 0; + actor.voicescript_nextthink = time + this.delay; + } +} + +void target_voicescript_next(entity pl) +{ + entity vs; + float i, n, dt; + + vs = pl.voicescript; + if(!vs) + return; + if(vs.message == "") + return; + if (!IS_PLAYER(pl)) + return; + if(game_stopped) + return; + + if(time >= pl.voicescript_voiceend) + { + if(time >= pl.voicescript_nextthink) + { + // get the next voice... + n = tokenize_console(vs.message); + + if(pl.voicescript_index < vs.cnt) + i = pl.voicescript_index * 2; + else if(n > vs.cnt * 2) + i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1; + else + i = -1; + + if(i >= 0) + { + play2(pl, strcat(vs.netname, "/", argv(i), ".wav")); + dt = stof(argv(i + 1)); + if(dt >= 0) + { + pl.voicescript_voiceend = time + dt; + pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random()); + } + else + { + pl.voicescript_voiceend = time - dt; + pl.voicescript_nextthink = pl.voicescript_voiceend; + } + + pl.voicescript_index += 1; + } + else + { + pl.voicescript = NULL; // stop trying then + } + } + } +} + +spawnfunc(target_voicescript) +{ + // netname: directory of the sound files + // message: list of "sound file" duration "sound file" duration, a *, and again a list + // foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7 + // Here, a - in front of the duration means that no delay is to be + // added after this message + // wait: average time between messages + // delay: initial delay before the first message + + float i, n; + this.use = target_voicescript_use; + + n = tokenize_console(this.message); + this.cnt = n / 2; + for(i = 0; i+1 < n; i += 2) + { + if(argv(i) == "*") + { + this.cnt = i / 2; + ++i; + } + precache_sound(strcat(this.netname, "/", argv(i), ".wav")); + } +} +#endif diff --git a/qcsrc/common/mapobjects/target/voicescript.qh b/qcsrc/common/mapobjects/target/voicescript.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/target/voicescript.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/teleporters.qc b/qcsrc/common/mapobjects/teleporters.qc new file mode 100644 index 0000000000..403d956c59 --- /dev/null +++ b/qcsrc/common/mapobjects/teleporters.qc @@ -0,0 +1,315 @@ +#include "teleporters.qh" + +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <lib/warpzone/common.qh> + #include <lib/warpzone/util_server.qh> + #include <lib/warpzone/server.qh> + #include "../constants.qh" + #include "../mapobjects/subs.qh" + #include "../util.qh" + #include <server/weapons/csqcprojectile.qh> + #include <server/autocvars.qh> + #include <server/constants.qh> + #include <server/defs.qh> + #include "../deathtypes/all.qh" + #include "../turrets/sv_turrets.qh" + #include "../vehicles/all.qh" + #include "../mapinfo.qh" + #include <server/anticheat.qh> +#endif + +#ifdef SVQC +float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) +{ + if (IS_PLAYER(player) && !IS_DEAD(player)) + { + TDEATHLOOP(org) + { + #ifdef SVQC + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + #endif + if(IS_PLAYER(head)) + if(!IS_DEAD(head)) + return 1; + } + } + return 0; +} + +void trigger_teleport_link(entity this); + +void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax) +{ + TDEATHLOOP(player.origin) + { + if (IS_PLAYER(player) && GetResourceAmount(player, RESOURCE_HEALTH) >= 1) + { + if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) + { + if(IS_PLAYER(head)) + if(GetResourceAmount(head, RESOURCE_HEALTH) >= 1) + ++tdeath_hit; + Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, head.origin, '0 0 0'); + } + } + else // dead bodies and monsters gib themselves instead of telefragging + Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, telefragger.origin, '0 0 0'); + } +} + +void spawn_tdeath(vector v0, entity e, vector v) +{ + tdeath(e, e, e, '0 0 0', '0 0 0'); +} +#endif + +void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) +{ + entity telefragger; + vector from; + + if(teleporter.owner) + telefragger = teleporter.owner; + else + telefragger = player; + + makevectors (to_angles); + +#ifdef SVQC + if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers + { + if(teleporter.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps + { + if(tflags & TELEPORT_FLAG_SOUND) + { + string thesound = SND(TELEPORT); + if(teleporter.noise != "") + { + RandomSelection_Init(); + FOREACH_WORD(teleporter.noise, true, + { + RandomSelection_AddString(it, 1, 1); + }); + thesound = RandomSelection_chosen_string; + } + _sound (player, CH_TRIGGER, thesound, VOL_BASE, ATTEN_NORM); + } + if(tflags & TELEPORT_FLAG_PARTICLES) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + Send_Effect(EFFECT_TELEPORT, to + v_forward * 32, '0 0 0', 1); + } + teleporter.pushltime = time + 0.2; + } + } +#endif + + // Relocate the player + // assuming to allows PL_MIN to PL_MAX box and some more +#ifdef SVQC + from = player.origin; + setorigin(player, to); + player.oldorigin = to; // don't undo the teleport by unsticking + player.angles = to_angles; + player.fixangle = true; + player.velocity = to_velocity; + BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); + + makevectors(player.angles); + Reset_ArcBeam(player, v_forward); + UpdateCSQCProjectileAfterTeleport(player); + UpdateItemAfterTeleport(player); +#elif defined(CSQC) + from = player.origin; + setorigin(player, to); + player.angles = to_angles; + player.velocity = to_velocity; + UNSET_ONGROUND(player); + player.iflags |= IFLAG_TELEPORTED | IFLAG_V_ANGLE | IFLAG_ANGLES; + player.csqcmodel_teleported = 1; + player.v_angle = to_angles; + + if(player == csqcplayer) // not for anything but the main player + { + setproperty(VF_ANGLES, player.angles); + setproperty(VF_CL_VIEWANGLES, player.angles); + } +#endif + +#ifdef SVQC + if(IS_PLAYER(player)) + { + if(tflags & TELEPORT_FLAG_TDEATH) + if(player.takedamage && !IS_DEAD(player) && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) + tdeath(player, teleporter, telefragger, telefragmin, telefragmax); + + // player no longer is on ground + UNSET_ONGROUND(player); + + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + player.oldvelocity = player.velocity; + + // reset tracking of who pushed you into a hazard (for kill credit) + if(teleporter.owner) + { + player.pusher = teleporter.owner; + player.pushltime = time + autocvar_g_maxpushtime; + player.istypefrag = PHYS_INPUT_BUTTON_CHAT(player); + } + else + { + player.pushltime = 0; + player.istypefrag = 0; + } + + player.lastteleporttime = time; + player.lastteleport_origin = from; + } +#endif +} + +entity Simple_TeleportPlayer(entity teleporter, entity player) +{ + vector locout; + entity e = NULL; + + // Find the output teleporter + if(teleporter.enemy) + { + e = teleporter.enemy; + } + else + { + // sorry CSQC, random stuff ain't gonna happen +#ifdef SVQC + RandomSelection_Init(); + FOREACH_ENTITY_STRING(targetname, teleporter.target, + { + bool p = true; + if(STAT(TELEPORT_TELEFRAG_AVOID, player)) + { + #ifdef SVQC + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); + #elif defined(CSQC) + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); + #endif + if(check_tdeath(player, locout, '0 0 0', '0 0 0')) + p = false; + } + RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p); + }); + e = RandomSelection_chosen_ent; +#endif + } + +#ifdef SVQC + if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } +#elif defined(CSQC) + if(!e) { LOG_INFO("Teleport destination could not be found from CSQC."); } +#endif + + makevectors(e.mangle); + + if(e.speed) + if(vdist(player.velocity, >, e.speed)) + player.velocity = normalize(player.velocity) * max(0, e.speed); + + if(STAT(TELEPORT_MAXSPEED, player)) + if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player))) + player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); + + locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + + TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); + + return e; +} + +void teleport_findtarget(entity this) +{ + bool istrigger = (this.solid == SOLID_TRIGGER); + + int n = 0; + for(entity e = NULL; (e = find(e, targetname, this.target)); ) + { + ++n; +#ifdef SVQC + if(e.move_movetype == MOVETYPE_NONE) + { + entity tracetest_ent = spawn(); + setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); + tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + waypoint_spawnforteleporter(this, e.origin, 0, tracetest_ent); + delete(tracetest_ent); + } + if(e.classname != "info_teleport_destination") + LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work."); +#endif + } + + if(n == 0) + { + // no dest! + objerror (this, "Teleporter with nonexistant target"); + return; + } + else if(n == 1) + { + // exactly one dest - bots love that + this.enemy = find(NULL, targetname, this.target); + } + else + { + // have to use random selection every single time + this.enemy = NULL; + } + + // now enable touch + if(istrigger) + settouch(this, Teleport_Touch); +#ifdef SVQC + if(istrigger) + trigger_teleport_link(this); +#endif +} + +entity Teleport_Find(vector mi, vector ma) +{ + IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), + { + return it; + }); + return NULL; +} + +void WarpZone_PostTeleportPlayer_Callback(entity pl) +{ +#ifdef SVQC + makevectors(pl.angles); + Reset_ArcBeam(pl, v_forward); + UpdateCSQCProjectileAfterTeleport(pl); + UpdateItemAfterTeleport(pl); + if (IS_PLAYER(pl)) anticheat_fixangle(pl); +#endif + // "disown" projectiles after teleport + if(pl.owner) + if(pl.owner == pl.realowner) + { + #ifdef SVQC + if(!(pl.flags & FL_PROJECTILE)) + #elif defined(CSQC) + if(!(pl.flags & BIT(15))) // FL_PROJECTILE + #endif + LOG_INFO("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, "."); + pl.owner = NULL; + } + if(IS_PLAYER(pl)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + #ifdef SVQC + pl.oldvelocity = pl.velocity; + #endif + } +} diff --git a/qcsrc/common/mapobjects/teleporters.qh b/qcsrc/common/mapobjects/teleporters.qh new file mode 100644 index 0000000000..68c5114f4d --- /dev/null +++ b/qcsrc/common/mapobjects/teleporters.qh @@ -0,0 +1,72 @@ +#pragma once +#include "defs.qh" + +IntrusiveList g_teleporters; +STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); } + +.entity pusher; + +const int TELEPORT_FLAG_SOUND = BIT(0); +const int TELEPORT_FLAG_PARTICLES = BIT(1); +const int TELEPORT_FLAG_TDEATH = BIT(2); +const int TELEPORT_FLAG_FORCE_TDEATH = BIT(3); + +#define TELEPORT_FLAGS_WARPZONE 0 +#define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH) +#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH) + +// types for .teleportable entity setting +const int TELEPORT_NORMAL = 1; // play sounds/effects etc +const int TELEPORT_SIMPLE = 2; // only do teleport, nothing special + +entity Simple_TeleportPlayer(entity teleporter, entity player); + +void Teleport_Touch(entity this, entity toucher); + +void teleport_findtarget(entity this); + +entity Teleport_Find(vector mi, vector ma); + +void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags); + +#ifdef SVQC + +void trigger_teleport_use(entity this, entity actor, entity trigger); + +#define TDEATHLOOP(o) \ + entity head; \ + vector deathmin; \ + vector deathmax; \ + float deathradius; \ + deathmin = (o) + player.mins; \ + deathmax = (o) + player.maxs; \ + if(telefragmin != telefragmax) \ + { \ + if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \ + if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \ + if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \ + if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \ + if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \ + if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \ + } \ + deathradius = max(vlen(deathmin), vlen(deathmax)); \ + for(head = findradius(o, deathradius); head; head = head.chain) \ + if(head != player) \ + if(head.takedamage) \ + if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax)) + +float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax); +float tdeath_hit; +void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax); + +void spawn_tdeath(vector v0, entity e, vector v); + +void Reset_ArcBeam(entity player, vector forward); + +#endif + +void WarpZone_PostTeleportPlayer_Callback(entity pl); + +#ifdef CSQC +.entity realowner; +#endif diff --git a/qcsrc/common/mapobjects/trigger/_mod.inc b/qcsrc/common/mapobjects/trigger/_mod.inc new file mode 100644 index 0000000000..c2a663f055 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/_mod.inc @@ -0,0 +1,24 @@ +// generated file; do not modify +#include <common/mapobjects/trigger/counter.qc> +#include <common/mapobjects/trigger/delay.qc> +#include <common/mapobjects/trigger/disablerelay.qc> +#include <common/mapobjects/trigger/flipflop.qc> +#include <common/mapobjects/trigger/gamestart.qc> +#include <common/mapobjects/trigger/gravity.qc> +#include <common/mapobjects/trigger/heal.qc> +#include <common/mapobjects/trigger/hurt.qc> +#include <common/mapobjects/trigger/impulse.qc> +#include <common/mapobjects/trigger/jumppads.qc> +#include <common/mapobjects/trigger/keylock.qc> +#include <common/mapobjects/trigger/magicear.qc> +#include <common/mapobjects/trigger/monoflop.qc> +#include <common/mapobjects/trigger/multi.qc> +#include <common/mapobjects/trigger/multivibrator.qc> +#include <common/mapobjects/trigger/relay.qc> +#include <common/mapobjects/trigger/relay_activators.qc> +#include <common/mapobjects/trigger/relay_if.qc> +#include <common/mapobjects/trigger/relay_teamcheck.qc> +#include <common/mapobjects/trigger/secret.qc> +#include <common/mapobjects/trigger/swamp.qc> +#include <common/mapobjects/trigger/teleport.qc> +#include <common/mapobjects/trigger/viewloc.qc> diff --git a/qcsrc/common/mapobjects/trigger/_mod.qh b/qcsrc/common/mapobjects/trigger/_mod.qh new file mode 100644 index 0000000000..08a6e5a068 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/_mod.qh @@ -0,0 +1,24 @@ +// generated file; do not modify +#include <common/mapobjects/trigger/counter.qh> +#include <common/mapobjects/trigger/delay.qh> +#include <common/mapobjects/trigger/disablerelay.qh> +#include <common/mapobjects/trigger/flipflop.qh> +#include <common/mapobjects/trigger/gamestart.qh> +#include <common/mapobjects/trigger/gravity.qh> +#include <common/mapobjects/trigger/heal.qh> +#include <common/mapobjects/trigger/hurt.qh> +#include <common/mapobjects/trigger/impulse.qh> +#include <common/mapobjects/trigger/jumppads.qh> +#include <common/mapobjects/trigger/keylock.qh> +#include <common/mapobjects/trigger/magicear.qh> +#include <common/mapobjects/trigger/monoflop.qh> +#include <common/mapobjects/trigger/multi.qh> +#include <common/mapobjects/trigger/multivibrator.qh> +#include <common/mapobjects/trigger/relay.qh> +#include <common/mapobjects/trigger/relay_activators.qh> +#include <common/mapobjects/trigger/relay_if.qh> +#include <common/mapobjects/trigger/relay_teamcheck.qh> +#include <common/mapobjects/trigger/secret.qh> +#include <common/mapobjects/trigger/swamp.qh> +#include <common/mapobjects/trigger/teleport.qh> +#include <common/mapobjects/trigger/viewloc.qh> diff --git a/qcsrc/common/mapobjects/trigger/counter.qc b/qcsrc/common/mapobjects/trigger/counter.qc new file mode 100644 index 0000000000..9156439f99 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/counter.qc @@ -0,0 +1,75 @@ +#include "counter.qh" +#ifdef SVQC +void counter_reset(entity this); + +void counter_use(entity this, entity actor, entity trigger) +{ + entity store = this; + if(this.spawnflags & COUNTER_PER_PLAYER) + { + if(!IS_PLAYER(actor)) + return; + store = actor; + } + + store.counter_cnt += 1; + if (store.counter_cnt > this.count) + return; + + bool doactivate = (this.spawnflags & COUNTER_FIRE_AT_COUNT); + + if (store.counter_cnt == this.count) + { + if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COMPLETED); + + doactivate = true; + + if(this.respawntime) + { + setthink(this, counter_reset); + this.nextthink = time + this.respawntime; + } + } + else + { + if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) + { + if((this.count - store.counter_cnt) >= 4) + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER); + else + Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, this.count - store.counter_cnt); + } + } + + if(doactivate) + SUB_UseTargets(this, actor, trigger); +} + +void counter_reset(entity this) +{ + setthink(this, func_null); + this.nextthink = 0; + this.counter_cnt = 0; +} + +/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage COUNTER_FIRE_AT_COUNT +Acts as an intermediary for an action that takes multiple inputs. + +If nomessage is not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. +If COUNTER_FIRE_AT_COUNT is set, it will also fire all of its targets at countdown, making it behave like trigger_mulitple with limited shots + +If respawntime is set, it will re-enable itself after the time once the sequence has been completed + +After the counter has been triggered "count" times (default 2), it will fire all of its targets. +*/ +spawnfunc(trigger_counter) +{ + if (!this.count) + this.count = 2; + + this.counter_cnt = 0; + this.use = counter_use; + this.reset = counter_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/counter.qh b/qcsrc/common/mapobjects/trigger/counter.qh new file mode 100644 index 0000000000..d36bd0293c --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/counter.qh @@ -0,0 +1,10 @@ +#pragma once + +#ifdef SVQC +spawnfunc(trigger_counter); + +.float counter_cnt; +#endif + +const int COUNTER_FIRE_AT_COUNT = BIT(2); +const int COUNTER_PER_PLAYER = BIT(3); diff --git a/qcsrc/common/mapobjects/trigger/delay.qc b/qcsrc/common/mapobjects/trigger/delay.qc new file mode 100644 index 0000000000..2cd4cfd133 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/delay.qc @@ -0,0 +1,32 @@ +#include "delay.qh" +#ifdef SVQC +void delay_delayeduse(entity this) +{ + SUB_UseTargets(this, this.enemy, this.goalentity); + this.enemy = this.goalentity = NULL; +} + +void delay_use(entity this, entity actor, entity trigger) +{ + this.enemy = actor; + this.goalentity = trigger; + setthink(this, delay_delayeduse); + this.nextthink = time + this.wait; +} + +void delay_reset(entity this) +{ + this.enemy = this.goalentity = NULL; + setthink(this, func_null); + this.nextthink = 0; +} + +spawnfunc(trigger_delay) +{ + if(!this.wait) + this.wait = 1; + + this.use = delay_use; + this.reset = delay_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/delay.qh b/qcsrc/common/mapobjects/trigger/delay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/delay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/disablerelay.qc b/qcsrc/common/mapobjects/trigger/disablerelay.qc new file mode 100644 index 0000000000..eee61c9935 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/disablerelay.qc @@ -0,0 +1,29 @@ +#include "disablerelay.qh" +#ifdef SVQC +void trigger_disablerelay_use(entity this, entity actor, entity trigger) +{ + int a = 0, b = 0; + + for(entity e = NULL; (e = find(e, targetname, this.target)); ) + { + if(e.use == SUB_UseTargets) + { + e.use = SUB_DontUseTargets; + ++a; + } + else if(e.use == SUB_DontUseTargets) + { + e.use = SUB_UseTargets; + ++b; + } + } + + if((!a) == (!b)) + LOG_INFO("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!"); +} + +spawnfunc(trigger_disablerelay) +{ + this.use = trigger_disablerelay_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/disablerelay.qh b/qcsrc/common/mapobjects/trigger/disablerelay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/disablerelay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/flipflop.qc b/qcsrc/common/mapobjects/trigger/flipflop.qc new file mode 100644 index 0000000000..141f3ea9f1 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/flipflop.qc @@ -0,0 +1,23 @@ +#include "flipflop.qh" +#ifdef SVQC +/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED +"Flip-flop" trigger gate... lets only every second trigger event through +*/ +void flipflop_use(entity this, entity actor, entity trigger) +{ + this.state = !this.state; + if(this.state) + SUB_UseTargets(this, actor, trigger); +} + +spawnfunc(trigger_flipflop) +{ + if(this.spawnflags & START_ENABLED) + { + this.state = true; + } + this.use = flipflop_use; + this.reset = spawnfunc_trigger_flipflop; // perfect resetter +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/flipflop.qh b/qcsrc/common/mapobjects/trigger/flipflop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/flipflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/gamestart.qc b/qcsrc/common/mapobjects/trigger/gamestart.qc new file mode 100644 index 0000000000..72d76d1833 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gamestart.qc @@ -0,0 +1,28 @@ +#include "gamestart.qh" +#ifdef SVQC +void gamestart_use(entity this, entity actor, entity trigger) +{ + SUB_UseTargets(this, this, trigger); + delete(this); +} + +void gamestart_use_this(entity this) +{ + gamestart_use(this, NULL, NULL); +} + +spawnfunc(trigger_gamestart) +{ + this.use = gamestart_use; + this.reset2 = spawnfunc_trigger_gamestart; + + if(this.wait) + { + setthink(this, adaptor_think2use); + this.nextthink = game_starttime + this.wait; + } + else + InitializeEntity(this, gamestart_use_this, INITPRIO_FINDTARGET); +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/gamestart.qh b/qcsrc/common/mapobjects/trigger/gamestart.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gamestart.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/gravity.qc b/qcsrc/common/mapobjects/trigger/gravity.qc new file mode 100644 index 0000000000..1ac0f8768d --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gravity.qc @@ -0,0 +1,111 @@ +#include "gravity.qh" +#ifdef SVQC +.entity trigger_gravity_check; +void trigger_gravity_remove(entity own) +{ + if(own.trigger_gravity_check.owner == own) + { + UpdateCSQCProjectile(own); + own.gravity = own.trigger_gravity_check.gravity; + delete(own.trigger_gravity_check); + } + else + backtrace("Removing a trigger_gravity_check with no valid owner"); + own.trigger_gravity_check = NULL; +} +void trigger_gravity_check_think(entity this) +{ + // This spawns when a player enters the gravity zone and checks if he left. + // Each frame, this.count is set to 2 by trigger_gravity_touch() and decreased by 1 here. + // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that. + if(this.count <= 0) + { + if(this.owner.trigger_gravity_check == this) + trigger_gravity_remove(this.owner); + else + delete(this); + return; + } + else + { + this.count -= 1; + this.nextthink = time; + } +} + +// legacy +void trigger_gravity_use(entity this, entity actor, entity trigger) +{ + this.setactive(this, ACTIVE_TOGGLE); +} + +void trigger_gravity_touch(entity this, entity toucher) +{ + float g; + + if(this.active == ACTIVE_NOT) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + g = this.gravity; + + if (!(this.spawnflags & GRAVITY_STICKY)) + { + if(toucher.trigger_gravity_check) + { + if(this == toucher.trigger_gravity_check.enemy) + { + // same? + // NOTE: see explanation in trigger_gravity_check_think + toucher.trigger_gravity_check.count = 2; // gravity one more frame... + return; + } + + // compare prio + if(this.cnt > toucher.trigger_gravity_check.enemy.cnt) + trigger_gravity_remove(toucher); + else + return; + } + toucher.trigger_gravity_check = spawn(); + toucher.trigger_gravity_check.enemy = this; + toucher.trigger_gravity_check.owner = toucher; + toucher.trigger_gravity_check.gravity = toucher.gravity; + setthink(toucher.trigger_gravity_check, trigger_gravity_check_think); + toucher.trigger_gravity_check.nextthink = time; + toucher.trigger_gravity_check.count = 2; + if(toucher.gravity) + g *= toucher.gravity; + } + + if (toucher.gravity != g) + { + toucher.gravity = g; + if(this.noise != "") + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + UpdateCSQCProjectile(this.owner); + } +} + +spawnfunc(trigger_gravity) +{ + if(this.gravity == 1) + return; + + EXACTTRIGGER_INIT; + settouch(this, trigger_gravity_touch); + if(this.noise != "") + precache_sound(this.noise); + + this.active = ACTIVE_ACTIVE; + this.setactive = generic_setactive; + IFTARGETED + { + // legacy use + this.use = trigger_gravity_use; + if(this.spawnflags & GRAVITY_START_DISABLED) + this.active = ACTIVE_NOT; + } +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/gravity.qh b/qcsrc/common/mapobjects/trigger/gravity.qh new file mode 100644 index 0000000000..872f04ad9f --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/gravity.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int GRAVITY_STICKY = BIT(0); // keep gravity multiplier even after exiting the trigger_gravity +const int GRAVITY_START_DISABLED = BIT(1); diff --git a/qcsrc/common/mapobjects/trigger/heal.qc b/qcsrc/common/mapobjects/trigger/heal.qc new file mode 100644 index 0000000000..866fd88a56 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/heal.qc @@ -0,0 +1,60 @@ +#include "heal.qh" +#ifdef SVQC +.float triggerhealtime; +void trigger_heal_touch(entity this, entity toucher) +{ + if (this.active != ACTIVE_ACTIVE) + return; + + // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) + if (toucher.iscreature) + { + if (toucher.takedamage && !IS_DEAD(toucher) && toucher.triggerhealtime < time) + { + bool is_trigger = this.targetname == ""; + if(is_trigger) + EXACTTRIGGER_TOUCH(this, toucher); + if(this.delay > 0) + toucher.triggerhealtime = time + this.delay; + + bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS); + bool healed = Heal(toucher, this, GetResourceAmount(this, RESOURCE_HEALTH), this.max_health); + + if(playthesound || healed) + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + } + } +} + +void trigger_heal_use(entity this, entity actor, entity trigger) +{ + trigger_heal_touch(this, actor); +} + +void trigger_heal_init(entity this) +{ + this.active = ACTIVE_ACTIVE; + if(!this.delay) + this.delay = 1; + if(!GetResourceAmount(this, RESOURCE_HEALTH)) + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 10); // TODO: use a special field for this, it doesn't have actual health! + 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); +} + +spawnfunc(trigger_heal) +{ + EXACTTRIGGER_INIT; + settouch(this, trigger_heal_touch); + trigger_heal_init(this); +} + +spawnfunc(target_heal) +{ + this.use = trigger_heal_use; + trigger_heal_init(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/heal.qh b/qcsrc/common/mapobjects/trigger/heal.qh new file mode 100644 index 0000000000..8dbeea5456 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/heal.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int HEAL_SOUND_ALWAYS = BIT(2); diff --git a/qcsrc/common/mapobjects/trigger/hurt.qc b/qcsrc/common/mapobjects/trigger/hurt.qc new file mode 100644 index 0000000000..ccdf2c7d0b --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/hurt.qc @@ -0,0 +1,93 @@ +#include "hurt.qh" +#ifdef SVQC +void trigger_hurt_use(entity this, entity actor, entity trigger) +{ + if(IS_PLAYER(actor)) + this.enemy = actor; + else + this.enemy = NULL; // let's just destroy it, if taking over is too much work +} + +.float triggerhurttime; +void trigger_hurt_touch(entity this, entity toucher) +{ + if (this.active != ACTIVE_ACTIVE) + return; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) + return; + + // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) + if (toucher.iscreature) + { + if (toucher.takedamage) + if (toucher.triggerhurttime < time) + { + EXACTTRIGGER_TOUCH(this, toucher); + toucher.triggerhurttime = time + ((autocvar_sv_vq3compat && !(this.spawnflags & HURT_SLOW)) ? 0.1 : 1); + + entity own; + own = this.enemy; + if (!IS_PLAYER(own)) + { + own = this; + this.enemy = NULL; // I still hate you all + } + + Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); + } + } + else if(toucher.damagedbytriggers) + { + if(toucher.takedamage) + { + EXACTTRIGGER_TOUCH(this, toucher); + Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); + } + } + + return; +} + +/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? +Any object touching this will be hurt +set dmg to damage amount +default dmg = 10000 +*/ +.entity trigger_hurt_next; +entity trigger_hurt_last; +entity trigger_hurt_first; +spawnfunc(trigger_hurt) +{ + EXACTTRIGGER_INIT; + this.active = ACTIVE_ACTIVE; + settouch(this, trigger_hurt_touch); + this.use = trigger_hurt_use; + this.enemy = world; // I hate you all + if (!this.dmg) + this.dmg = ((autocvar_sv_vq3compat) ? 5 : 10000); + if (this.message == "") + this.message = "was in the wrong place"; + if (this.message2 == "") + this.message2 = "was thrown into a world of hurt by"; + // this.message = "someone like %s always gets wrongplaced"; + + if(!trigger_hurt_first) + trigger_hurt_first = this; + if(trigger_hurt_last) + trigger_hurt_last.trigger_hurt_next = this; + trigger_hurt_last = this; +} + +bool tracebox_hits_trigger_hurt(vector start, vector e_min, vector e_max, vector end) +{ + entity th; + + for(th = trigger_hurt_first; th; th = th.trigger_hurt_next) + if(tracebox_hits_box(start, e_min, e_max, end, th.absmin, th.absmax)) + return true; + + return false; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/hurt.qh b/qcsrc/common/mapobjects/trigger/hurt.qh new file mode 100644 index 0000000000..e992154aa9 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/hurt.qh @@ -0,0 +1,3 @@ +#pragma once + +const int HURT_SLOW = BIT(4); diff --git a/qcsrc/common/mapobjects/trigger/impulse.qc b/qcsrc/common/mapobjects/trigger/impulse.qc new file mode 100644 index 0000000000..c4e7ae287a --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/impulse.qc @@ -0,0 +1,243 @@ +#include "impulse.qh" +// targeted (directional) mode +void trigger_impulse_touch_directional(entity this, entity toucher) +{ + entity targ; + float pushdeltatime; + float str; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + targ = find(NULL, targetname, this.target); + if(!targ) + { + objerror(this, "trigger_force without a (valid) .target!\n"); + delete(this); + return; + } + + // falloff is not supported because radius is always 0 in directional mode + str = this.strength; + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + if(this.spawnflags & IMPULSE_DIRECTIONAL_SPEEDTARGET) + { + float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin); + if (addspeed > 0) + { + float accelspeed = min(IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR * pushdeltatime * str, addspeed); + toucher.velocity += accelspeed * normalize(targ.origin - this.origin); + } + } + else + toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; + + UNSET_ONGROUND(toucher); + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +// Directionless (accelerator/decelerator) mode +void trigger_impulse_touch_accel(entity this, entity toucher) +{ + float pushdeltatime; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + // div0: ticrate independent, 1 = identity (not 20) + toucher.velocity = toucher.velocity * (this.strength ** pushdeltatime); + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +// Spherical (gravity/repulsor) mode +void trigger_impulse_touch_radial(entity this, entity toucher) +{ + float pushdeltatime; + float str; + + if (this.active != ACTIVE_ACTIVE) + return; + + if (!isPushable(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + pushdeltatime = time - toucher.lastpushtime; + if (pushdeltatime > IMPULSE_MAX_PUSHDELTATIME) + { + pushdeltatime = 0; + } + toucher.lastpushtime = time; + if(!pushdeltatime) + { + return; + } + + setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); + + str = min(this.radius, vlen(this.origin - toucher.origin)); + + if(this.falloff == FALLOFF_LINEAR) + str = (1 - str / this.radius) * this.strength; // 1 in the inside + else if(this.falloff == FALLOFF_LINEAR_INV) + str = (str / this.radius) * this.strength; // 0 in the inside + else + str = this.strength; + + toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime; + +#ifdef SVQC + UpdateCSQCProjectile(toucher); +#endif +} + +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE) + +/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? +Force field +-------- KEYS -------- +target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed. + If not, this trigger acts like a damper/accelerator field. + +strength : This is how much force to add in the direction of .target each second + when .target is set. If not, this is how much to slow down/accelerate + something cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble) + +radius : If set, act as a spherical device rather then a linear one. + +falloff : 0 = none, 1 = liniar, 2 = inverted liniar + +-------- NOTES -------- +Use a brush textured with common/origin in the trigger entity to determine the origin of the force +in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect). +*/ +#ifdef SVQC +bool trigger_impulse_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); + + WriteByte(MSG_ENTITY, this.spawnflags); + WriteCoord(MSG_ENTITY, this.radius); + WriteCoord(MSG_ENTITY, this.strength); + WriteByte(MSG_ENTITY, this.falloff); + WriteByte(MSG_ENTITY, this.active); + + trigger_common_write(this, true); + + return true; +} + +void trigger_impulse_link(entity this) +{ + trigger_link(this, trigger_impulse_send); +} + +spawnfunc(trigger_impulse) +{ + this.active = ACTIVE_ACTIVE; + + trigger_init(this); + + if(this.radius) + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_RADIAL_STRENGTH * autocvar_g_triggerimpulse_radial_multiplier; + } + setorigin(this, this.origin); + setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); + settouch(this, trigger_impulse_touch_radial); + } + else + { + if(this.target) + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH * autocvar_g_triggerimpulse_directional_multiplier; + } + settouch(this, trigger_impulse_touch_directional); + } + else + { + if(!this.strength) + { + this.strength = IMPULSE_DEFAULT_ACCEL_STRENGTH; + } + this.strength = (this.strength ** autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier; + settouch(this, trigger_impulse_touch_accel); + } + } + + trigger_impulse_link(this); +} +#elif defined(CSQC) +NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew) +{ + this.spawnflags = ReadByte(); + this.radius = ReadCoord(); + this.strength = ReadCoord(); + this.falloff = ReadByte(); + this.active = ReadByte(); + + trigger_common_read(this, true); + return = true; + + this.classname = "trigger_impulse"; + this.solid = SOLID_TRIGGER; + this.entremove = trigger_remove_generic; + this.move_time = time; + + if (this.radius) + { + settouch(this, trigger_impulse_touch_radial); + } + else if (this.target) + { + settouch(this, trigger_impulse_touch_directional); + } + else + { + settouch(this, trigger_impulse_touch_accel); + } +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/impulse.qh b/qcsrc/common/mapobjects/trigger/impulse.qh new file mode 100644 index 0000000000..e86de4a497 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/impulse.qh @@ -0,0 +1,21 @@ +#pragma once + +// tZorks trigger impulse / gravity +.float radius; +.int falloff; +.float strength; +.float lastpushtime; + +const int FALLOFF_NO = 0; +const int FALLOFF_LINEAR = 1; +const int FALLOFF_LINEAR_INV = 2; + +const int IMPULSE_DIRECTIONAL_SPEEDTARGET = BIT(6); + +const float IMPULSE_DEFAULT_RADIAL_STRENGTH = 2000; +const float IMPULSE_DEFAULT_DIRECTIONAL_STRENGTH = 950; +const float IMPULSE_DEFAULT_ACCEL_STRENGTH = 0.9; + +const float IMPULSE_MAX_PUSHDELTATIME = 0.15; + +const float IMPULSE_DIRECTIONAL_MAX_ACCEL_FACTOR = 8; diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qc b/qcsrc/common/mapobjects/trigger/jumppads.qc new file mode 100644 index 0000000000..a61935eb8c --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/jumppads.qc @@ -0,0 +1,700 @@ +#include "jumppads.qh" +// TODO: split target_push and put it in the target folder +#ifdef SVQC +#include <common/physics/movetypes/movetypes.qh> + +void trigger_push_use(entity this, entity actor, entity trigger) +{ + if(teamplay) + { + this.team = actor.team; + this.SendFlags |= SF_TRIGGER_UPDATE; + } +} +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) +REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) + +/* + trigger_push_calculatevelocity + + Arguments: + org - origin of the object which is to be pushed + tgt - target entity (can be either a point or a model entity; if it is + the latter, its midpoint is used) + ht - jump height, measured from the higher one of org and tgt's midpoint + pushed_entity - object that is to be pushed + + Returns: velocity for the jump + */ +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity) +{ + float grav, sdist, zdist, vs, vz, jumpheight; + vector sdir, torg; + + torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; + + grav = PHYS_GRAVITY(NULL); + if(pushed_entity && PHYS_ENTGRAVITY(pushed_entity)) + grav *= PHYS_ENTGRAVITY(pushed_entity); + + zdist = torg.z - org.z; + sdist = vlen(torg - org - zdist * '0 0 1'); + sdir = normalize(torg - org - zdist * '0 0 1'); + + // how high do we need to push the player? + jumpheight = fabs(ht); + if(zdist > 0) + jumpheight = jumpheight + zdist; + + /* + STOP. + + You will not understand the following equations anyway... + But here is what I did to get them. + + I used the functions + + s(t) = t * vs + z(t) = t * vz - 1/2 grav t^2 + + and solved for: + + s(ti) = sdist + z(ti) = zdist + max(z, ti) = jumpheight + + From these three equations, you will find the three parameters vs, vz + and ti. + */ + + // push him so high... + vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)! + + // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! + if(ht < 0) + if(zdist < 0) + vz = -vz; + + vector solution; + solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" + // ALWAYS solvable because jumpheight >= zdist + if(!solution.z) + solution_y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) + if(zdist == 0) + solution_x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) + + float flighttime; + if(zdist < 0) + { + // down-jump + if(ht < 0) + { + // almost straight line type + // jump apex is before the jump + // we must take the larger one + flighttime = solution.y; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one too + flighttime = solution.y; + } + } + else + { + // up-jump + if(ht < 0) + { + // almost straight line type + // jump apex is after the jump + // we must take the smaller one + flighttime = solution.x; + } + else + { + // regular jump + // jump apex is during the jump + // we must take the larger one + flighttime = solution.y; + } + } + vs = sdist / flighttime; + + // finally calculate the velocity + return sdir * vs + '0 0 1' * vz; +} + +bool jumppad_push(entity this, entity targ) +{ + if (!isPushable(targ)) + return false; + + vector org = targ.origin; +#ifdef SVQC + if(autocvar_sv_vq3compat) +#elif defined(CSQC) + if(STAT(VQ3COMPAT)) +#endif + { + org.z += targ.mins_z; + org.z += 1; // off by 1! + } + + if(this.enemy) + { + targ.velocity = trigger_push_calculatevelocity(org, this.enemy, this.height, targ); + } + else if(this.target && this.target != "") + { + entity e; + RandomSelection_Init(); + for(e = NULL; (e = find(e, targetname, this.target)); ) + { + if(e.cnt) + RandomSelection_AddEnt(e, e.cnt, 1); + else + RandomSelection_AddEnt(e, 1, 1); + } + targ.velocity = trigger_push_calculatevelocity(org, RandomSelection_chosen_ent, this.height, targ); + } + else + { + targ.velocity = this.movedir; + } + + UNSET_ONGROUND(targ); + +#ifdef CSQC + if (targ.flags & FL_PROJECTILE) + { + targ.angles = vectoangles (targ.velocity); + switch(targ.move_movetype) + { + case MOVETYPE_FLY: + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; + break; + } + } +#endif + +#ifdef SVQC + if (IS_PLAYER(targ)) + { + // reset tracking of oldvelocity for impact damage (sudden velocity changes) + targ.oldvelocity = targ.velocity; + + if(this.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once + { + // flash when activated + Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1); + _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + this.pushltime = time + 0.2; + } + if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ)) + { + bool found = false; + for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i) + if(targ.(jumppadsused[i]) == this) + found = true; + if(!found) + { + targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this; + targ.jumppadcount = targ.jumppadcount + 1; + } + + if(IS_REAL_CLIENT(targ)) + { + if(this.message) + centerprint(targ, this.message); + } + else + { + targ.lastteleporttime = time; + targ.lastteleport_origin = targ.origin; + } + + if (!IS_DEAD(targ)) + animdecide_setaction(targ, ANIMACTION_JUMP, true); + } + else + targ.jumppadcount = 1; + + // reset tracking of who pushed you into a hazard (for kill credit) + targ.pushltime = 0; + targ.istypefrag = 0; + } + + if(this.enemy.target) + SUB_UseTargets(this.enemy, targ, this); + + if (targ.flags & FL_PROJECTILE) + { + targ.angles = vectoangles (targ.velocity); + targ.com_phys_gravity_factor = 1; + switch(targ.move_movetype) + { + case MOVETYPE_FLY: + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; + break; + case MOVETYPE_BOUNCEMISSILE: + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; + break; + } + UpdateCSQCProjectile(targ); + } +#endif + + return true; +} + +void trigger_push_touch(entity this, entity toucher) +{ + if (this.active == ACTIVE_NOT) + return; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, toucher))) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + noref bool success = jumppad_push(this, toucher); + +#ifdef SVQC + if (success && (this.spawnflags & PUSH_ONCE)) + { + settouch(this, func_null); + setthink(this, SUB_Remove); + this.nextthink = time; + } +#endif +} + +#ifdef SVQC +void trigger_push_link(entity this); +void trigger_push_updatelink(entity this); +bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org) +{ + setorigin(tracetest_ent, org); + tracetoss(tracetest_ent, tracetest_ent); + if(trace_startsolid) + return false; + + if (!jp.height) + { + // since tracetoss starting from jumppad's origin often fails when target + // is very close to real destination, start it directly from target's + // origin instead + vector ofs = '0 0 0'; + if (vdist(vec2(tracetest_ent.velocity), <, autocvar_sv_maxspeed)) + ofs = stepheightvec; + + tracetest_ent.velocity.z = 0; + setorigin(tracetest_ent, targ.origin + ofs); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid && ofs.z) + { + setorigin(tracetest_ent, targ.origin + ofs / 2); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid && ofs.z) + { + setorigin(tracetest_ent, targ.origin); + tracetoss(tracetest_ent, tracetest_ent); + if (trace_startsolid) + return false; + } + } + } + tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); + return true; +} + +bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org) +{ + setorigin(tracetest_ent, org); + tracetoss(tracetest_ent, tracetest_ent); + + if(trace_startsolid) + return false; + if (trace_ent == item) + return true; + + tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); + + if (trace_ent == item) + return true; + + return false; +} +#endif + +/// if (item != NULL) returns true if the item can be reached by using this jumppad, false otherwise +/// if (item == NULL) tests jumppad's trajectory and eventually spawns waypoints for it (return value doesn't matter) +bool trigger_push_test(entity this, entity item) +{ + // first calculate a typical start point for the jump + vector org = (this.absmin + this.absmax) * 0.5; + org.z = this.absmax.z - PL_MIN_CONST.z - 7; + + if (this.target) + { + int n = 0; +#ifdef SVQC + vector vel = '0 0 0'; +#endif + for(entity t = NULL; (t = find(t, targetname, this.target)); ) + { + ++n; +#ifdef SVQC + if(t.move_movetype != MOVETYPE_NONE) + continue; + + // bots can't tell teamed jumppads from normal ones + if (this.team) + continue; + + entity e = spawn(); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + e.velocity = trigger_push_calculatevelocity(org, t, this.height, e); + + vel = e.velocity; + vector best_target = '0 0 0'; + vector best_org = '0 0 0'; + vector best_vel = '0 0 0'; + bool valid_best_target = false; + if (item) + { + if (!trigger_push_testorigin_for_item(e, item, org)) + { + delete(e); + return false; + } + } + else + { + if (trigger_push_testorigin(e, t, this, org)) + { + best_target = trace_endpos; + best_org = org; + best_vel = e.velocity; + valid_best_target = true; + } + } + + vector new_org; + vector dist = t.origin - org; + if (dist.x || dist.y) // if not perfectly vertical + { + // test trajectory with different starting points, sometimes the trajectory + // starting from the jumppad origin can't reach the real destination + // and destination waypoint ends up near the jumppad itself + vector flatdir = normalize(dist - eZ * dist.z); + vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y)); + new_org = org + ofs; + + LABEL(new_test) + e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e); + if (item) + { + if (!trigger_push_testorigin_for_item(e, item, new_org)) + { + delete(e); + return false; + } + } + else + { + vel = e.velocity; + if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed)) + e.velocity = autocvar_sv_maxspeed * flatdir; + if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50)) + { + best_target = trace_endpos; + best_org = new_org; + best_vel = vel; + valid_best_target = true; + } + } + if (ofs && new_org != org - ofs) + { + new_org = org - ofs; + goto new_test; + } + } + + if (item) + { + delete(e); + return true; + } + + if (valid_best_target) + { + if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, best_target + PL_MIN_CONST, best_target + PL_MAX_CONST))) + { + float velxy = vlen(vec2(best_vel)); + float cost = vlen(vec2(t.origin - best_org)) / velxy; + if(velxy < autocvar_sv_maxspeed) + velxy = autocvar_sv_maxspeed; + cost += vlen(vec2(best_target - t.origin)) / velxy; + waypoint_spawnforteleporter(this, best_target, cost, e); + } + } + delete(e); +#endif + } + + if(item) + return false; + + if(!n) + { + // no dest! +#ifdef SVQC + objerror (this, "Jumppad with nonexistant target"); +#endif + return false; + } + else if(n == 1) + { + // exactly one dest - bots love that + if (!this.team) + this.enemy = find(NULL, targetname, this.target); + else // bots can't tell teamed jumppads from normal ones + this.enemy = NULL; + } + else + { + // have to use random selection every single time + this.enemy = NULL; + } + + } +#ifdef SVQC + else + { + if (!this.team) + { + entity e = spawn(); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + setorigin(e, org); + e.velocity = this.movedir; + tracetoss(e, e); + if (item) + { + bool r = (trace_ent == item); + delete(e); + return r; + } + if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, trace_endpos + PL_MIN_CONST, trace_endpos + PL_MAX_CONST))) + waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity), e); + delete(e); + } + else if (item) + return false; + } + + defer(this, 0.1, trigger_push_updatelink); +#endif + return true; +} + +void trigger_push_findtarget(entity this) +{ + trigger_push_test(this, NULL); +} + +#ifdef SVQC +float trigger_push_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); + + WriteByte(MSG_ENTITY, this.team); + WriteInt24_t(MSG_ENTITY, this.spawnflags); + WriteByte(MSG_ENTITY, this.active); + WriteCoord(MSG_ENTITY, this.height); + + WriteVector(MSG_ENTITY, this.movedir); + + trigger_common_write(this, true); + + return true; +} + +void trigger_push_updatelink(entity this) +{ + this.SendFlags |= SF_TRIGGER_INIT; +} + +void trigger_push_link(entity this) +{ + trigger_link(this, trigger_push_send); +} + +/* + * ENTITY PARAMETERS: + * + * target: target of jump + * height: the absolute value is the height of the highest point of the jump + * trajectory above the higher one of the player and the target. + * the sign indicates whether the highest point is INSIDE (positive) + * or OUTSIDE (negative) of the jump trajectory. General rule: use + * positive values for targets mounted on the floor, and use negative + * values to target a point on the ceiling. + * movedir: if target is not set, this * speed * 10 is the velocity to be reached. + */ +spawnfunc(trigger_push) +{ + SetMovedir(this); + + trigger_init(this); + + this.active = ACTIVE_ACTIVE; + this.use = trigger_push_use; + settouch(this, trigger_push_touch); + + // normal push setup + if (!this.speed) + this.speed = 1000; + this.movedir = this.movedir * this.speed * 10; + + if (!this.noise) + this.noise = "misc/jumppad.wav"; + precache_sound (this.noise); + + trigger_push_link(this); // link it now + + IL_PUSH(g_jumppads, this); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); +} + + +bool target_push_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); + + WriteByte(MSG_ENTITY, this.cnt); + WriteString(MSG_ENTITY, this.targetname); + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); + + return true; +} + +void target_push_use(entity this, entity actor, entity trigger) +{ + if(trigger.classname == "trigger_push" || trigger == this) + return; // WTF, why is this a thing + + jumppad_push(this, actor); +} + +void target_push_link(entity this) +{ + BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); + Net_LinkEntity(this, false, 0, target_push_send); + //this.SendFlags |= 1; // update +} + +void target_push_init(entity this) +{ + this.mangle = this.angles; + setorigin(this, this.origin); + target_push_link(this); +} + +void target_push_init2(entity this) +{ + if(this.target && this.target != "") // we have an old style pusher! + { + InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); + this.use = target_push_use; + } + + target_push_init(this); // normal push target behaviour can be combined with a legacy pusher? +} + +spawnfunc(target_push) +{ + target_push_init2(this); +} + +spawnfunc(info_notnull) +{ + target_push_init(this); +} +spawnfunc(target_position) +{ + target_push_init(this); +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) +{ + this.classname = "jumppad"; + int mytm = ReadByte(); + if(mytm) + { + this.team = mytm - 1; + } + this.spawnflags = ReadInt24_t(); + this.active = ReadByte(); + this.height = ReadCoord(); + + this.movedir = ReadVector(); + + trigger_common_read(this, true); + + this.entremove = trigger_remove_generic; + this.solid = SOLID_TRIGGER; + settouch(this, trigger_push_touch); + this.move_time = time; + defer(this, 0.25, trigger_push_findtarget); + + return true; +} + +void target_push_remove(entity this) +{ + // strfree(this.classname); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew) +{ + this.classname = "push_target"; + this.cnt = ReadByte(); + this.targetname = strzone(ReadString()); + this.origin = ReadVector(); + + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + this.angles_z = ReadAngle(); + + return = true; + + setorigin(this, this.origin); + + this.drawmask = MASK_NORMAL; + this.entremove = target_push_remove; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/jumppads.qh b/qcsrc/common/mapobjects/trigger/jumppads.qh new file mode 100644 index 0000000000..268134e806 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/jumppads.qh @@ -0,0 +1,63 @@ +#pragma once + + +const int PUSH_ONCE = BIT(0); // legacy, deactivate with relay instead +const int PUSH_SILENT = BIT(1); // not used? + +IntrusiveList g_jumppads; +STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } + +.float pushltime; +.bool istypefrag; +.float height; + +const int NUM_JUMPPADSUSED = 3; +.float jumppadcount; +.entity jumppadsused[NUM_JUMPPADSUSED]; + +#ifdef SVQC +void SUB_UseTargets(entity this, entity actor, entity trigger); +void trigger_push_use(entity this, entity actor, entity trigger); +bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org); +bool trigger_push_testorigin_for_item(entity tracetest_ent, entity item, vector org); +#endif + +/* + trigger_push_calculatevelocity + + Arguments: + org - origin of the object which is to be pushed + tgt - target entity (can be either a point or a model entity; if it is + the latter, its midpoint is used) + ht - jump height, measured from the higher one of org and tgt's midpoint + pushed_entity - object that is to be pushed + + Returns: velocity for the jump + */ +vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity); + +void trigger_push_touch(entity this, entity toucher); + +.vector dest; +bool trigger_push_test(entity this, entity item); +void trigger_push_findtarget(entity this); + +/* + * ENTITY PARAMETERS: + * + * target: target of jump + * height: the absolute value is the height of the highest point of the jump + * trajectory above the higher one of the player and the target. + * the sign indicates whether the highest point is INSIDE (positive) + * or OUTSIDE (negative) of the jump trajectory. General rule: use + * positive values for targets mounted on the floor, and use negative + * values to target a point on the ceiling. + * movedir: if target is not set, this * speed * 10 is the velocity to be reached. + */ +#ifdef SVQC +spawnfunc(trigger_push); + +spawnfunc(target_push); +spawnfunc(info_notnull); +spawnfunc(target_position); +#endif diff --git a/qcsrc/common/mapobjects/trigger/keylock.qc b/qcsrc/common/mapobjects/trigger/keylock.qc new file mode 100644 index 0000000000..f7ecd7c1f1 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/keylock.qc @@ -0,0 +1,194 @@ +#include "keylock.qh" +/** + * trigger given targets + */ +void trigger_keylock_trigger(entity this, entity actor, string s) +{ + for(entity t = NULL; (t = find(t, targetname, s)); ) + if(t.use) + t.use(t, actor, this); +} + +/** + * kill killtarget of trigger keylock. + */ +void trigger_keylock_kill(string s) +{ + entity t; + for(t = NULL; (t = find(t, targetname, s)); ) + delete(t); +} + +void trigger_keylock_touch(entity this, entity toucher) +{ + bool key_used = false; + bool started_delay = false; + + // only player may trigger the lock + if(!IS_PLAYER(toucher)) + return; + + // check silver key + if(this.itemkeys) + { +#ifdef SVQC + entity store = PS(toucher); +#elif defined(CSQC) + entity store = toucher; +#endif + key_used = item_keys_usekey(this, store); + } + + if(this.itemkeys) + { +#ifdef SVQC + // at least one of the keys is missing + if(key_used) + { + // one or more keys were given, but others are still missing! + play2(toucher, this.noise1); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys)); + toucher.key_door_messagetime = time + 2; + } + else if(toucher.key_door_messagetime <= time) + { + // no keys were given + play2(toucher, this.noise2); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys)); + toucher.key_door_messagetime = time + 2; + } +#endif + + // trigger target2 + if(this.delay <= time || started_delay == true) + if(this.target2) + { + trigger_keylock_trigger(this, toucher, this.target2); + started_delay = true; + this.delay = time + this.wait; + } + } + else + { +#ifdef SVQC + // all keys were given! + play2(toucher, this.noise); + centerprint(toucher, this.message); +#endif + + if(this.target) + trigger_keylock_trigger(this, toucher, this.target); + + if(this.killtarget) + trigger_keylock_kill(this.killtarget); + + delete(this); + } + +} + +REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK) + +#ifdef SVQC +bool trigger_keylock_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK); + + WriteInt24_t(MSG_ENTITY, this.itemkeys); + WriteByte(MSG_ENTITY, this.height); + + trigger_common_write(this, true); + + return true; +} + +void trigger_keylock_link(entity this) +{ + // uncomment to network keylocks + //Net_LinkEntity(this, false, 0, trigger_keylock_send); +} + +/*QUAKED trigger_keylock (.0 .5 .8) ? +Keylock trigger. Must target other entities. +This trigger will trigger target entities when all required keys are provided. +-------- KEYS -------- +itemkeys: A bit field with key IDs that are needed to open this lock. +sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default) +target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger +target2: trigger all entities with this targetname when triggered without giving it all the required keys. +killtarget: remove all entities with this targetname when triggered with all the needed keys. +message: print this message to the player who activated the trigger when all needed keys have been given. +message2: print this message to the player who activated the trigger when not all of the needed keys have been given. +noise: sound to play when lock gets unlocked (default: see sounds) +noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav) +noise2: sound to play when a key is missing (default: misc/talk.wav) +wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4. +---------NOTES---------- +If spawned without any key specified in itemkeys, this trigger will display an error and remove itself. +message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone. +*/ +spawnfunc(trigger_keylock) +{ + if(!this.itemkeys) { delete(this); return; } + + // set unlocked message + if(this.message == "") + this.message = "Unlocked!"; + + // set default unlock noise + if(this.noise == "") + { + if(this.sounds == 1) + this.noise = "misc/secret.wav"; + else if(this.sounds == 2) + this.noise = strzone(SND(TALK)); + else //if (this.sounds == 3) { + this.noise = "misc/trigger1.wav"; + } + + // set default use key sound + if(this.noise1 == "") + this.noise1 = "misc/decreasevalue.wav"; + + // set closed sourd + if(this.noise2 == "") + this.noise2 = SND(TALK); + + // delay between triggering message2 and trigger2 + if(!this.wait) { this.wait = 5; } + + // precache sounds + precache_sound(this.noise); + precache_sound(this.noise1); + precache_sound(this.noise2); + + EXACTTRIGGER_INIT; + + settouch(this, trigger_keylock_touch); + + trigger_keylock_link(this); +} +#elif defined(CSQC) +void keylock_remove(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.killtarget); + strfree(this.targetname); +} + +NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew) +{ + this.itemkeys = ReadInt24_t(); + this.height = ReadByte(); + + trigger_common_read(this, true); + + return = true; + + this.classname = "trigger_keylock"; + this.entremove = keylock_remove; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/keylock.qh b/qcsrc/common/mapobjects/trigger/keylock.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/keylock.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/magicear.qc b/qcsrc/common/mapobjects/trigger/magicear.qc new file mode 100644 index 0000000000..16118cb9d6 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/magicear.qc @@ -0,0 +1,200 @@ +#include "magicear.qh" +#ifdef SVQC +float magicear_matched; +float W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); +string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin) +{ + float domatch, dotrigger, matchstart, l; + string s, msg; + string savemessage; + + magicear_matched = false; + + dotrigger = ((IS_PLAYER(source)) && (!IS_DEAD(source)) && ((ear.radius == 0) || (vdist(source.origin - ear.origin, <=, ear.radius)))); + domatch = ((ear.spawnflags & MAGICEAR_REPLACE_OUTSIDE) || dotrigger); + + if (!domatch) + return msgin; + + if (!msgin) + { + // we are in TUBA mode! + if (!(ear.spawnflags & MAGICEAR_TUBA)) + return msgin; + + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & MAGICEAR_TUBA_EXACTPITCH), ear.movedir_y, ear.movedir_z)) + return msgin; + } + + magicear_matched = true; + + if(dotrigger) + { + savemessage = ear.message; + ear.message = string_null; + SUB_UseTargets(ear, source, NULL); + ear.message = savemessage; + } + + if(ear.netname != "") + return ear.netname; + + return msgin; + } + + if(ear.spawnflags & MAGICEAR_TUBA) // ENOTUBA + return msgin; + + if(privatesay) + { + if(ear.spawnflags & MAGICEAR_IGNORE_TELL) + return msgin; + } + else + { + if(!teamsay) + if(ear.spawnflags & MAGICEAR_IGNORE_SAY) + return msgin; + if(teamsay > 0) + if(ear.spawnflags & MAGICEAR_IGNORE_TEAMSAY) + return msgin; + if(teamsay < 0) + if(ear.spawnflags & MAGICEAR_IGNORE_INVALIDTELL) + return msgin; + } + + matchstart = -1; + l = strlen(ear.message); + + if(ear.spawnflags & MAGICEAR_NODECOLORIZE) + msg = msgin; + else + msg = strdecolorize(msgin); + + if(substring(ear.message, 0, 1) == "*") + { + if(substring(ear.message, -1, 1) == "*") + { + // two wildcards + // as we need multi-replacement here... + s = substring(ear.message, 1, -2); + l -= 2; + if(strstrofs(msg, s, 0) >= 0) + matchstart = -2; // we use strreplace on s + } + else + { + // match at start + s = substring(ear.message, 1, -1); + l -= 1; + if(substring(msg, -l, l) == s) + matchstart = strlen(msg) - l; + } + } + else + { + if(substring(ear.message, -1, 1) == "*") + { + // match at end + s = substring(ear.message, 0, -2); + l -= 1; + if(substring(msg, 0, l) == s) + matchstart = 0; + } + else + { + // full match + s = ear.message; + if(msg == ear.message) + matchstart = 0; + } + } + + if(matchstart == -1) // no match + return msgin; + + magicear_matched = true; + + if(dotrigger) + { + savemessage = ear.message; + ear.message = string_null; + SUB_UseTargets(ear, source, NULL); + ear.message = savemessage; + } + + if(ear.spawnflags & MAGICEAR_REPLACE_WHOLE_MESSAGE) + { + return ear.netname; + } + else if(ear.netname != "") + { + if(matchstart < 0) + return strreplace(s, ear.netname, msg); + else + return strcat( + substring(msg, 0, matchstart), + ear.netname, + substring(msg, matchstart + l, -1) + ); + } + else + return msgin; +} + +entity magicears; +string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin) +{ + entity ear; + string msgout; + for(ear = magicears; ear; ear = ear.enemy) + { + msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin); + if(!(ear.spawnflags & MAGICEAR_CONTINUE)) + if(magicear_matched) + return msgout; + msgin = msgout; + } + return msgin; +} + +spawnfunc(trigger_magicear) +{ + this.enemy = magicears; + magicears = this; + + // actually handled in "say" processing + // spawnflags: + // 1 = ignore say + // 2 = ignore teamsay + // 4 = ignore tell + // 8 = ignore tell to unknown player + // 16 = let netname replace the whole message (otherwise, netname is a word replacement if set) + // 32 = perform the replacement even if outside the radius or dead + // 64 = continue replacing/triggering even if this one matched + // 128 = don't decolorize message before matching + // 256 = message is a tuba note sequence (pitch.duration pitch.duration ...) + // 512 = tuba notes must be exact right pitch, no transposing + // message: either + // *pattern* + // or + // *pattern + // or + // pattern* + // or + // pattern + // netname: + // if set, replacement for the matched text + // radius: + // "hearing distance" + // target: + // what to trigger + // movedir: + // for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter) + + this.movedir_x -= 1; // map to tuba instrument numbers +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/magicear.qh b/qcsrc/common/mapobjects/trigger/magicear.qh new file mode 100644 index 0000000000..4e705868c3 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/magicear.qh @@ -0,0 +1,13 @@ +#pragma once + + +const int MAGICEAR_IGNORE_SAY = BIT(0); +const int MAGICEAR_IGNORE_TEAMSAY = BIT(1); +const int MAGICEAR_IGNORE_TELL = BIT(2); +const int MAGICEAR_IGNORE_INVALIDTELL = BIT(3); +const int MAGICEAR_REPLACE_WHOLE_MESSAGE = BIT(4); +const int MAGICEAR_REPLACE_OUTSIDE = BIT(5); +const int MAGICEAR_CONTINUE = BIT(6); +const int MAGICEAR_NODECOLORIZE = BIT(7); +const int MAGICEAR_TUBA = BIT(8); +const int MAGICEAR_TUBA_EXACTPITCH = BIT(9); diff --git a/qcsrc/common/mapobjects/trigger/monoflop.qc b/qcsrc/common/mapobjects/trigger/monoflop.qc new file mode 100644 index 0000000000..0c960ba8a1 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/monoflop.qc @@ -0,0 +1,49 @@ +#include "monoflop.qh" +#ifdef SVQC +/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8) +"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait" +*/ +void monoflop_use(entity this, entity actor, entity trigger) +{ + this.nextthink = time + this.wait; + this.enemy = actor; + if(this.state) + return; + this.state = 1; + SUB_UseTargets(this, actor, trigger); +} +void monoflop_fixed_use(entity this, entity actor, entity trigger) +{ + if(this.state) + return; + this.nextthink = time + this.wait; + this.state = 1; + this.enemy = actor; + SUB_UseTargets(this, actor, trigger); +} + +void monoflop_think(entity this) +{ + this.state = 0; + SUB_UseTargets(this, this.enemy, NULL); +} + +void monoflop_reset(entity this) +{ + this.state = 0; + this.nextthink = 0; +} + +spawnfunc(trigger_monoflop) +{ + if(!this.wait) + this.wait = 1; + if(this.spawnflags & MONOFLOP_FIXED) + this.use = monoflop_fixed_use; + else + this.use = monoflop_use; + setthink(this, monoflop_think); + this.state = 0; + this.reset = monoflop_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/monoflop.qh b/qcsrc/common/mapobjects/trigger/monoflop.qh new file mode 100644 index 0000000000..c64dffdee8 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/monoflop.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int MONOFLOP_FIXED = BIT(0); diff --git a/qcsrc/common/mapobjects/trigger/multi.qc b/qcsrc/common/mapobjects/trigger/multi.qc new file mode 100644 index 0000000000..bc6049e19f --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multi.qc @@ -0,0 +1,224 @@ +#include "multi.qh" +// NOTE: also contains trigger_once at bottom + +#ifdef SVQC +// the wait time has passed, so set back up for another activation +void multi_wait(entity this) +{ + if (this.max_health) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + } +} + + +// the trigger was just touched/killed/used +// this.enemy should be set to the activator so it can be held through a delay +// so wait for the delay time before firing +void multi_trigger(entity this) +{ + if (this.nextthink > time) + { + return; // allready been triggered + } + + if((this.spawnflags & ONLY_PLAYERS) && !IS_PLAYER(this.enemy)) + { + return; // only players + } + + // TODO: restructure this so that trigger_secret is more independent + if (this.classname == "trigger_secret") + { + if (!IS_PLAYER(this.enemy)) + return; + found_secrets = found_secrets + 1; + WriteByte (MSG_ALL, SVC_FOUNDSECRET); + } + + if (this.noise) + { + _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + } + + // don't trigger again until reset + this.takedamage = DAMAGE_NO; + + SUB_UseTargets(this, this.enemy, this.goalentity); + + if (this.wait > 0) + { + setthink(this, multi_wait); + this.nextthink = time + this.wait; + } + else if (this.wait == 0) + { + multi_wait(this); // waiting finished + } + else + { // we can't just delete(this) here, because this is a touch function + // called while C code is looping through area links... + settouch(this, func_null); + } +} + +void multi_use(entity this, entity actor, entity trigger) +{ + this.goalentity = trigger; + this.enemy = actor; + multi_trigger(this); +} + +void multi_touch(entity this, entity toucher) +{ + if(!(this.spawnflags & ALL_ENTITIES) && !toucher.iscreature) + { + return; + } + + if(this.team) + { + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != toucher.team)) + { + return; + } + } + + // if the trigger has an angles field, check player's facing direction + if (this.movedir != '0 0 0') + { + makevectors (toucher.angles); + if (v_forward * this.movedir < 0) + return; // not facing the right way + } + + // if the trigger has pressed keys, check that the player is pressing those keys + if(this.pressedkeys && IS_PLAYER(toucher)) // only for players + { + if(!(CS(toucher).pressedkeys & this.pressedkeys)) + { + return; + } + } + + EXACTTRIGGER_TOUCH(this, toucher); + + this.enemy = toucher; + this.goalentity = toucher; + multi_trigger(this); +} + +void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if(!this.takedamage) + return; + if(this.spawnflags & NOSPLASH) + if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) + return; + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (this.team != attacker.team)) + return; + TakeResource(this, RESOURCE_HEALTH, damage); + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + { + this.enemy = attacker; + this.goalentity = inflictor; + multi_trigger(this); + } +} + +void multi_reset(entity this) +{ + if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) + settouch(this, multi_touch); + if (this.max_health) + { + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + } + setthink(this, func_null); + this.nextthink = 0; + this.team = this.team_saved; +} + +/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch +Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. +If "delay" is set, the trigger waits some time after activating before firing. +"wait" : Seconds between triggerings. (.2 default) +If notouch is set, the trigger is only fired by other entities, not by touching. +NOTOUCH has been obsoleted by spawnfunc_trigger_relay! +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +spawnfunc(trigger_multiple) +{ + this.reset = multi_reset; + if (this.sounds == 1) + this.noise = "misc/secret.wav"; + else if (this.sounds == 2) + this.noise = strzone(SND(TALK)); + else if (this.sounds == 3) + this.noise = "misc/trigger1.wav"; + + if(this.noise) + precache_sound(this.noise); + + if (!this.wait) + this.wait = 0.2; + else if(this.wait < -1) + this.wait = 0; + this.use = multi_use; + + EXACTTRIGGER_INIT; + + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + + if (GetResourceAmount(this, RESOURCE_HEALTH)) + { + if (this.spawnflags & SPAWNFLAG_NOTOUCH) + objerror (this, "health and notouch don't make sense\n"); + this.canteamdamage = true; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); + this.event_damage = multi_eventdamage; + this.takedamage = DAMAGE_YES; + this.solid = SOLID_BBOX; + setorigin(this, this.origin); // make sure it links into the world + } + else + { + if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) + { + settouch(this, multi_touch); + setorigin(this, this.origin); // make sure it links into the world + } + } +} + + +/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch +Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching +"targetname". If "health" is set, the trigger must be killed to activate. +If notouch is set, the trigger is only fired by other entities, not by touching. +if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. +if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. +sounds +1) secret +2) beep beep +3) large switch +4) +set "message" to text string +*/ +spawnfunc(trigger_once) +{ + this.wait = -1; + spawnfunc_trigger_multiple(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/multi.qh b/qcsrc/common/mapobjects/trigger/multi.qh new file mode 100644 index 0000000000..43358c2744 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multi.qh @@ -0,0 +1,8 @@ +#pragma once + +#ifdef SVQC +void multi_trigger(entity this); +void multi_reset(entity this); + +spawnfunc(trigger_once); +#endif diff --git a/qcsrc/common/mapobjects/trigger/multivibrator.qc b/qcsrc/common/mapobjects/trigger/multivibrator.qc new file mode 100644 index 0000000000..932fda13ca --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multivibrator.qc @@ -0,0 +1,78 @@ +#include "multivibrator.qh" +#ifdef SVQC +void multivibrator_send(entity this) +{ + float newstate; + float cyclestart; + + cyclestart = floor((time + this.phase) / (this.wait + this.respawntime)) * (this.wait + this.respawntime) - this.phase; + + newstate = (time < cyclestart + this.wait); + + if(this.state != newstate) + SUB_UseTargets(this, this, NULL); + this.state = newstate; + + if(this.state) + this.nextthink = cyclestart + this.wait + 0.01; + else + this.nextthink = cyclestart + this.wait + this.respawntime + 0.01; +} + +void multivibrator_send_think(entity this) +{ + multivibrator_send(this); +} + +void multivibrator_toggle(entity this, entity actor, entity trigger) +{ + if(this.nextthink == 0) + { + multivibrator_send(this); + } + else + { + if(this.state) + { + SUB_UseTargets(this, actor, trigger); + this.state = 0; + } + this.nextthink = 0; + } +} + +void multivibrator_reset(entity this) +{ + if(!(this.spawnflags & START_ENABLED)) + this.nextthink = 0; // wait for a trigger event + else + this.nextthink = max(1, time); +} + +/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED +"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off. +-------- KEYS -------- +target: trigger all entities with this targetname when it goes off +targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state +phase: offset of the timing +wait: "on" cycle time (default: 1) +respawntime: "off" cycle time (default: same as wait) +-------- SPAWNFLAGS -------- +START_ENABLED: assume it is already turned on (when targeted) +*/ +spawnfunc(trigger_multivibrator) +{ + if(!this.wait) + this.wait = 1; + if(!this.respawntime) + this.respawntime = this.wait; + + this.state = 0; + this.use = multivibrator_toggle; + setthink(this, multivibrator_send_think); + this.nextthink = max(1, time); + + IFTARGETED + multivibrator_reset(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/multivibrator.qh b/qcsrc/common/mapobjects/trigger/multivibrator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/multivibrator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay.qc b/qcsrc/common/mapobjects/trigger/relay.qc new file mode 100644 index 0000000000..f99d364aec --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay.qc @@ -0,0 +1,26 @@ +#include "relay.qh" +#ifdef SVQC + +void relay_use(entity this, entity actor, entity trigger) +{ + if(this.active != ACTIVE_ACTIVE) + return; + + SUB_UseTargets(this, actor, trigger); +} + +/*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) +This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. +*/ +spawnfunc(trigger_relay) +{ + this.active = ACTIVE_ACTIVE; + this.use = relay_use; + this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully +} + +spawnfunc(target_relay) +{ + spawnfunc_trigger_relay(this); +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay.qh b/qcsrc/common/mapobjects/trigger/relay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay_activators.qc b/qcsrc/common/mapobjects/trigger/relay_activators.qc new file mode 100644 index 0000000000..18c2a40d01 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_activators.qc @@ -0,0 +1,34 @@ +#include "relay_activators.qh" +#ifdef SVQC +void relay_activators_use(entity this, entity actor, entity trigger) +{ + for(entity trg = NULL; (trg = find(trg, targetname, this.target)); ) + { + if (trg.setactive) + trg.setactive(trg, this.cnt); + else + { + //bprint("Not using setactive\n"); + generic_setactive(trg, this.cnt); + } + } +} + +spawnfunc(relay_activate) +{ + this.cnt = ACTIVE_ACTIVE; + this.use = relay_activators_use; +} + +spawnfunc(relay_deactivate) +{ + this.cnt = ACTIVE_NOT; + this.use = relay_activators_use; +} + +spawnfunc(relay_activatetoggle) +{ + this.cnt = ACTIVE_TOGGLE; + this.use = relay_activators_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_activators.qh b/qcsrc/common/mapobjects/trigger/relay_activators.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_activators.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/relay_if.qc b/qcsrc/common/mapobjects/trigger/relay_if.qc new file mode 100644 index 0000000000..9adcd666ec --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_if.qc @@ -0,0 +1,20 @@ +#include "relay_if.qh" +#ifdef SVQC +void trigger_relay_if_use(entity this, entity actor, entity trigger) +{ + int n = this.count; + + // TODO make this generic AND faster than nextent()ing through all, if somehow possible + n = (cvar_string(this.netname) == cvar_string(this.message)); + if(this.spawnflags & RELAYIF_NEGATE) + n = !n; + + if(n) + SUB_UseTargets(this, actor, trigger); +} + +spawnfunc(trigger_relay_if) +{ + this.use = trigger_relay_if_use; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_if.qh b/qcsrc/common/mapobjects/trigger/relay_if.qh new file mode 100644 index 0000000000..6f37aa71d9 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_if.qh @@ -0,0 +1,4 @@ +#pragma once + + +const int RELAYIF_NEGATE = BIT(0); diff --git a/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc new file mode 100644 index 0000000000..bf03b1542f --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qc @@ -0,0 +1,37 @@ +#include "relay_teamcheck.qh" +#ifdef SVQC +void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) +{ + if(actor.team) + { + if(this.spawnflags & RELAYTEAMCHECK_INVERT) + { + if(DIFF_TEAM(actor, this)) + SUB_UseTargets(this, actor, trigger); + } + else + { + if(SAME_TEAM(actor, this)) + SUB_UseTargets(this, actor, trigger); + } + } + else + { + if(this.spawnflags & RELAYTEAMCHECK_NOTEAM) + SUB_UseTargets(this, actor, trigger); + } +} + +void trigger_relay_teamcheck_reset(entity this) +{ + this.team = this.team_saved; +} + +spawnfunc(trigger_relay_teamcheck) +{ + this.team_saved = this.team; + IL_PUSH(g_saved_team, this); + this.use = trigger_relay_teamcheck_use; + this.reset = trigger_relay_teamcheck_reset; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh new file mode 100644 index 0000000000..602d253562 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/relay_teamcheck.qh @@ -0,0 +1,5 @@ +#pragma once + + +const int RELAYTEAMCHECK_NOTEAM = BIT(0); +const int RELAYTEAMCHECK_INVERT = BIT(1); diff --git a/qcsrc/common/mapobjects/trigger/secret.qc b/qcsrc/common/mapobjects/trigger/secret.qc new file mode 100644 index 0000000000..5d7c5b6f46 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/secret.qc @@ -0,0 +1,90 @@ +#include "secret.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <common/util.qh> + #include <server/defs.qh> +#endif + +#ifdef SVQC + +void secrets_setstatus(entity this) +{ + // TODO: use global stats! + STAT(SECRETS_TOTAL, this) = secrets_total; + STAT(SECRETS_FOUND, this) = secrets_found; +} + +/** + * A secret has been found (maybe :P) + */ +void trigger_secret_touch(entity this, entity toucher) +{ + // only a player can trigger this + if (!IS_PLAYER(toucher)) + return; + + // update secrets found counter + secrets_found += 1; + //print("Secret found: ", ftos(secret_counter.cnt), "/"); + //print(ftos(secret_counter.count), "\n"); + + // centerprint message (multi_touch() doesn't always call centerprint()) + centerprint(toucher, this.message); + this.message = ""; + + // handle normal trigger features + multi_touch(this, toucher); + // we can't just delete(this) here, because this is a touch function + // called while C code is looping through area links... + //delete(this); +} + +/*QUAKED trigger_secret (.5 .5 .5) ? +Variable sized secret trigger. Can be targeted at one or more entities. +Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found. +-------- KEYS -------- +sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1) +noise: path to sound file, if you want to play something else +target: trigger all entities with this targetname when triggered +message: print this message to the player who activated the trigger instead of the standard 'You found a secret!' +killtarget: remove all entities with this targetname when triggered +-------- NOTES -------- +You should create a common/trigger textured brush covering the entrance to a secret room/area. +Trigger secret can only be trigger by a player's touch and can not be a target itself. +*/ +spawnfunc(trigger_secret) +{ + // FIXME: should it be disabled in most modes? + + // update secrets count + secrets_total += 1; + + // add default message + if (this.message == "") + this.message = "You found a secret!"; + + // set default sound + if (this.noise == "") + if (!this.sounds) + this.sounds = 1; // misc/secret.wav + + // this entity can't be a target itself!!!! + this.targetname = ""; + + // you can't just shoot a room to find it, can you? + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); + + // a secret can not be delayed + this.delay = 0; + + // convert this trigger to trigger_once + //this.classname = "trigger_once"; + spawnfunc_trigger_once(this); + + // take over the touch() function, so we can mark secret as found + settouch(this, trigger_secret_touch); + // ignore triggering; + this.use = func_null; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/secret.qh b/qcsrc/common/mapobjects/trigger/secret.qh new file mode 100644 index 0000000000..fcc55c3959 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/secret.qh @@ -0,0 +1,19 @@ +#pragma once +#ifdef SVQC + +/** + * Total number of secrets on the map. + */ +float secrets_total; + +/** + * Total numbe of secrets found on the map. + */ +float secrets_found; + + +/** + * update secrets status. + */ +void secrets_setstatus(entity this); +#endif diff --git a/qcsrc/common/mapobjects/trigger/swamp.qc b/qcsrc/common/mapobjects/trigger/swamp.qc new file mode 100644 index 0000000000..8e3fd739de --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/swamp.qc @@ -0,0 +1,158 @@ +#include "swamp.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <lib/warpzone/util_server.qh> + #include <common/weapons/_all.qh> + #include <server/defs.qh> + #include <common/deathtypes/all.qh> +#endif + +/* +* t_swamp.c +* Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+ +* Author tZork (Jakob MG) +* jakob@games43.se +* 2005 11 29 +*/ + +.float swamp_interval; //Hurt players in swamp with this interval +.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.float swamp_lifetime; // holds the points remaining until slug dies (not quite health!) +.entity swampslug; + +#ifdef SVQC +spawnfunc(trigger_swamp); +#endif +void swamp_touch(entity this, entity toucher); +void swampslug_think(entity this); + + +/* +* Uses a entity calld swampslug to handle players in the swamp +* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp +* attaches a new "swampslug" to the player. As long as the plyer is inside +* the swamp the swamp gives the slug new health. But the slug slowly kills itself +* so when the player goes outside the swamp, it dies and releases the player from the +* swamps curses (dmg/slowdown) +* +* I do it this way becuz there is no "untouch" event. +*/ +void swampslug_think(entity this) +{ + //Slowly kill the slug + this.swamp_lifetime -= 1; + + //Slug dead? then remove curses. + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + { + this.owner.in_swamp = 0; + delete(this); + //centerprint(this.owner,"Killing slug...\n"); + return; + } + + // Slug still alive, so we are still in the swamp + // Or we have exited it very recently. + // Do the damage and renew the timer. +#ifdef SVQC + Damage (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, DMG_NOWEP, this.owner.origin, '0 0 0'); +#endif + + this.nextthink = time + this.swamp_interval; +} + +void swamp_touch(entity this, entity toucher) +{ + // If whatever thats touching the swamp is not a player + // or if its a dead player, just dont care abt it. + if(!IS_PLAYER(toucher) || IS_DEAD(toucher)) + return; + + EXACTTRIGGER_TOUCH(this, toucher); + + // Chech if player alredy got a swampslug. + if(toucher.in_swamp != 1) + { + // If not attach one. + //centerprint(toucher,"Entering swamp!\n"); + toucher.swampslug = spawn(); + toucher.swampslug.swamp_lifetime = 2; + setthink(toucher.swampslug, swampslug_think); + toucher.swampslug.nextthink = time; + toucher.swampslug.owner = toucher; + toucher.swampslug.dmg = this.dmg; + toucher.swampslug.swamp_interval = this.swamp_interval; + toucher.swamp_slowdown = this.swamp_slowdown; + toucher.in_swamp = 1; + return; + } + + //toucher.in_swamp = 1; + + //Revitalize players swampslug + toucher.swampslug.swamp_lifetime = 2; +} + +REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) + +#ifdef SVQC +float swamp_send(entity this, entity to, float sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP); + + WriteByte(MSG_ENTITY, this.dmg); // can probably get away with using a single byte here + WriteByte(MSG_ENTITY, this.swamp_slowdown); + WriteByte(MSG_ENTITY, this.swamp_interval); + + trigger_common_write(this, false); + + return true; +} + +void swamp_link(entity this) +{ + trigger_link(this, swamp_send); +} + +/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? +Players gettin into the swamp will +get slowd down and damaged +*/ +spawnfunc(trigger_swamp) +{ + // Init stuff + trigger_init(this); + settouch(this, swamp_touch); + + // Setup default keys, if missing + if(this.dmg <= 0) + this.dmg = 5; + if(this.swamp_interval <= 0) + this.swamp_interval = 1; + if(this.swamp_slowdown <= 0) + this.swamp_slowdown = 0.5; + + swamp_link(this); +} + +#elif defined(CSQC) + +NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew) +{ + this.dmg = ReadByte(); + this.swamp_slowdown = ReadByte(); + this.swamp_interval = ReadByte(); + + trigger_common_read(this, false); + + return = true; + + this.classname = "trigger_swamp"; + this.solid = SOLID_TRIGGER; + settouch(this, swamp_touch); + this.drawmask = MASK_NORMAL; + this.move_time = time; + this.entremove = trigger_remove_generic; +} +#endif diff --git a/qcsrc/common/mapobjects/trigger/swamp.qh b/qcsrc/common/mapobjects/trigger/swamp.qh new file mode 100644 index 0000000000..f4df98378d --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/swamp.qh @@ -0,0 +1,8 @@ +#pragma once + +.float swamp_interval; //Hurt players in swamp with this interval +.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.entity swampslug; + +.float in_swamp; // bool +.entity swampslug; // Uses this to release from swamp ("untouch" fix) diff --git a/qcsrc/common/mapobjects/trigger/teleport.qc b/qcsrc/common/mapobjects/trigger/teleport.qc new file mode 100644 index 0000000000..825dd01dde --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/teleport.qc @@ -0,0 +1,183 @@ +#include "teleport.qh" +REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) + +#ifdef SVQC +void trigger_teleport_use(entity this, entity actor, entity trigger) +{ + if(teamplay) + this.team = actor.team; +#ifdef SVQC + this.SendFlags |= SF_TRIGGER_UPDATE; +#endif +} +#endif + +bool Teleport_Active(entity this, entity player) +{ + if (this.active != ACTIVE_ACTIVE) + return false; + +#ifdef SVQC + if (!player.teleportable) + return false; + + if(player.vehicle) + if(!player.vehicle.teleportable) + return false; + + if(IS_TURRET(player)) + return false; +#elif defined(CSQC) + if(!IS_PLAYER(player)) + return false; +#endif + + if(IS_DEAD(player)) + return false; + + if(this.team) + if(((this.spawnflags & INVERT_TEAMS) == 0) == (DIFF_TEAM(this, player))) + return false; + + 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(player)) + RemoveGrapplingHooks(player); +#endif + + entity e; + e = Simple_TeleportPlayer(this, player); + +#ifdef SVQC + 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 +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) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_TELEPORT); + + WriteByte(MSG_ENTITY, this.team); + WriteInt24_t(MSG_ENTITY, this.spawnflags); + WriteByte(MSG_ENTITY, this.active); + WriteCoord(MSG_ENTITY, this.speed); + + trigger_common_write(this, true); + + return true; +} + +void trigger_teleport_link(entity this) +{ + //trigger_link(this, trigger_teleport_send); +} + +spawnfunc(trigger_teleport) +{ + this.angles = '0 0 0'; + + this.active = ACTIVE_ACTIVE; + //trigger_init(this); // only for predicted triggers? + EXACTTRIGGER_INIT; + this.use = trigger_teleport_use; + + if(this.noise != "") + FOREACH_WORD(this.noise, true, precache_sound(it)); + + // this must be called to spawn the teleport waypoints for bots + InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); + + if (this.target == "") + { + objerror (this, "Teleporter with no target"); + return; + } + + 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) +{ + this.classname = "trigger_teleport"; + if(isnew) + IL_PUSH(g_teleporters, this); + int mytm = ReadByte(); + if(mytm) + { + this.team = mytm - 1; + } + this.spawnflags = ReadInt24_t(); + this.active = ReadByte(); + this.speed = ReadCoord(); + + trigger_common_read(this, true); + + this.entremove = trigger_remove_generic; + this.solid = SOLID_TRIGGER; + //settouch(this, trigger_push_touch); + this.move_time = time; + defer(this, 0.25, teleport_findtarget); + + return true; +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/teleport.qh b/qcsrc/common/mapobjects/trigger/teleport.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/teleport.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mapobjects/trigger/viewloc.qc b/qcsrc/common/mapobjects/trigger/viewloc.qc new file mode 100644 index 0000000000..ba5dcbe443 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/viewloc.qc @@ -0,0 +1,213 @@ +#include "viewloc.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include <lib/warpzone/util_server.qh> + #include <server/defs.qh> +#endif + +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC) +REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER) + +#ifdef SVQC + +void viewloc_think(entity this) +{ + // we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities + + // set myself as current viewloc where possible +#if 1 + FOREACH_CLIENT(IS_PLAYER(it) && it.viewloc == this, + { + it.viewloc = NULL; + }); +#else + entity e; + for(e = NULL; (e = findentity(e, viewloc, this)); ) + e.viewloc = NULL; +#endif + +#if 1 + FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it), + { + vector emin = it.absmin; + vector emax = it.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + { + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate + it.viewloc = this; + } + }); +#else + + for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain) + if(!e.viewloc) + if(IS_PLAYER(e)) // should we support non-player entities with this? + //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet + { + vector emin = e.absmin; + vector emax = e.absmax; + if(this.solid == SOLID_BSP) + { + emin -= '1 1 1'; + emax += '1 1 1'; + } + if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick + if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate + e.viewloc = this; + } +#endif + + this.nextthink = time; +} + +bool trigger_viewloc_send(entity this, entity to, int sf) +{ + // CSQC doesn't need to know our origin (yet), as we're only available for referencing + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); + + WriteByte(MSG_ENTITY, this.spawnflags); + + WriteEntity(MSG_ENTITY, this.enemy); + WriteEntity(MSG_ENTITY, this.goalentity); + + WriteVector(MSG_ENTITY, this.origin); + + return true; +} + +void viewloc_init(entity this) +{ + entity e; + for(e = NULL; (e = find(e, targetname, this.target)); ) + if(e.classname == "target_viewlocation_start") + { + this.enemy = e; + break; + } + for(e = NULL; (e = find(e, targetname, this.target2)); ) + if(e.classname == "target_viewlocation_end") + { + this.goalentity = e; + break; + } + + if(!this.enemy) { LOG_INFO("^1FAIL!"); delete(this); return; } + + if(!this.goalentity) + this.goalentity = this.enemy; // make them match so CSQC knows what to do + + Net_LinkEntity(this, false, 0, trigger_viewloc_send); + + setthink(this, viewloc_think); + this.nextthink = time; +} + +spawnfunc(trigger_viewlocation) +{ + // we won't check target2 here yet, as it may not even need to exist + if(this.target == "") { LOG_INFO("^1FAIL!"); delete(this); return; } + + EXACTTRIGGER_INIT; + InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET); +} + +bool viewloc_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC); + + WriteByte(MSG_ENTITY, this.cnt); + + WriteVector(MSG_ENTITY, this.origin); + + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); + + return true; +} + +.float angle; +void viewloc_link(entity this) +{ + if(this.angle) + this.angles_y = this.angle; + Net_LinkEntity(this, false, 0, viewloc_send); +} + +spawnfunc(target_viewlocation_start) +{ + this.classname = "target_viewlocation_start"; + this.cnt = 1; + viewloc_link(this); +} +spawnfunc(target_viewlocation_end) +{ + this.classname = "target_viewlocation_end"; + this.cnt = 2; + viewloc_link(this); +} + +// compatibility +spawnfunc(target_viewlocation) +{ + spawnfunc_target_viewlocation_start(this); +} + +#elif defined(CSQC) + +void trigger_viewloc_updatelink(entity this) +{ + this.enemy = findfloat(NULL, entnum, this.cnt); + this.goalentity = findfloat(NULL, entnum, this.count); +} + +NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew) +{ + this.spawnflags = ReadByte(); + + float point1 = ReadShort(); + float point2 = ReadShort(); + + this.enemy = findfloat(NULL, entnum, point1); + this.goalentity = findfloat(NULL, entnum, point2); + + this.origin = ReadVector(); + + return = true; + + setorigin(this, this.origin); + + this.cnt = point1; + this.count = point2; + + setthink(this, trigger_viewloc_updatelink); + this.nextthink = time + 1; // we need to delay this or else + + this.classname = "trigger_viewlocation"; + this.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive +} + +NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) +{ + this.cnt = ReadByte(); + + this.origin = ReadVector(); + setorigin(this, this.origin); + + this.movedir_x = ReadAngle(); + this.movedir_y = ReadAngle(); + this.movedir_z = ReadAngle(); + + return = true; + + this.classname = ((this.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start"); + this.drawmask = MASK_NORMAL; // don't cull it +} + +#endif diff --git a/qcsrc/common/mapobjects/trigger/viewloc.qh b/qcsrc/common/mapobjects/trigger/viewloc.qh new file mode 100644 index 0000000000..3c393afd37 --- /dev/null +++ b/qcsrc/common/mapobjects/trigger/viewloc.qh @@ -0,0 +1,14 @@ +#pragma once + + +const int VIEWLOC_NOSIDESCROLL = BIT(0); // NOTE: currently unimplemented +const int VIEWLOC_FREEAIM = BIT(1); +const int VIEWLOC_FREEMOVE = BIT(2); + +.entity viewloc; + +#ifdef CSQC +.entity goalentity; +.entity enemy; +.vector movedir; +#endif diff --git a/qcsrc/common/mapobjects/triggers.qc b/qcsrc/common/mapobjects/triggers.qc new file mode 100644 index 0000000000..9a7181d3a2 --- /dev/null +++ b/qcsrc/common/mapobjects/triggers.qc @@ -0,0 +1,333 @@ +#include "triggers.qh" + +void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } + +void SUB_UseTargets(entity this, entity actor, entity trigger); + +void DelayThink(entity this) +{ + SUB_UseTargets (this, this.enemy, NULL); + delete(this); +} + +#ifdef SVQC +void generic_setactive(entity this, int act) +{ + if(act == ACTIVE_TOGGLE) + { + if(this.active == ACTIVE_ACTIVE) + { + this.active = ACTIVE_NOT; + } + else + { + this.active = ACTIVE_ACTIVE; + } + } + else + { + this.active = act; + } +} + +void generic_netlinked_setactive(entity this, int act) +{ + int old_status = this.active; + generic_setactive(this, act); + + if (this.active != old_status) + { + this.SendFlags |= SF_TRIGGER_UPDATE; + } +} + +void generic_netlinked_reset(entity this) +{ + IFTARGETED + { + if(this.spawnflags & START_ENABLED) + { + this.active = ACTIVE_ACTIVE; + } + else + { + this.active = ACTIVE_NOT; + } + } + else + { + this.active = ACTIVE_ACTIVE; + } + + this.SendFlags |= SF_TRIGGER_UPDATE; +} + +// Compatibility with old maps +void generic_netlinked_legacy_use(entity this, entity actor, entity trigger) +{ + //LOG_WARNF("Entity %s was (de)activated by a trigger, please update map to use relays", this.targetname); + this.setactive(this, ACTIVE_TOGGLE); +} + +bool autocvar_g_triggers_debug = true; + +void trigger_init(entity this) +{ + string m = this.model; + EXACTTRIGGER_INIT; + if(autocvar_g_triggers_debug) + { + if(m != "") + { + precache_model(m); + _setmodel(this, m); // no precision needed + } + setorigin(this, this.origin); + if(this.scale) + setsize(this, this.mins * this.scale, this.maxs * this.scale); + else + setsize(this, this.mins, this.maxs); + } + + if(autocvar_g_triggers_debug) + BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); +} + +void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc) +{ + setSendEntity(this, sendfunc); + this.SendFlags = 0xFFFFFF; +} + +void trigger_common_write(entity this, bool withtarget) +{ + int f = 0; + if(this.warpzone_isboxy) + BITSET_ASSIGN(f, 1); + if(this.origin != '0 0 0') + BITSET_ASSIGN(f, 4); + if(this.movedir != '0 0 0') + BITSET_ASSIGN(f, 8); + if(this.angles != '0 0 0') + BITSET_ASSIGN(f, 16); + WriteByte(MSG_ENTITY, f); + + if(withtarget) + { + // probably some way to clean this up... + int targbits = 0; + if(this.target && this.target != "") targbits |= BIT(0); + if(this.target2 && this.target2 != "") targbits |= BIT(1); + if(this.target3 && this.target3 != "") targbits |= BIT(2); + if(this.target4 && this.target4 != "") targbits |= BIT(3); + if(this.targetname && this.targetname != "") targbits |= BIT(4); + if(this.killtarget && this.killtarget != "") targbits |= BIT(5); + + WriteByte(MSG_ENTITY, targbits); + + if(targbits & BIT(0)) + WriteString(MSG_ENTITY, this.target); + if(targbits & BIT(1)) + WriteString(MSG_ENTITY, this.target2); + if(targbits & BIT(2)) + WriteString(MSG_ENTITY, this.target3); + if(targbits & BIT(3)) + WriteString(MSG_ENTITY, this.target4); + if(targbits & BIT(4)) + WriteString(MSG_ENTITY, this.targetname); + if(targbits & BIT(5)) + WriteString(MSG_ENTITY, this.killtarget); + } + + if(f & 4) + WriteVector(MSG_ENTITY, this.origin); + + if(f & 8) + WriteVector(MSG_ENTITY, this.movedir); + + if(f & 16) + WriteVector(MSG_ENTITY, this.angles); + + WriteShort(MSG_ENTITY, this.modelindex); + WriteVector(MSG_ENTITY, this.mins); + WriteVector(MSG_ENTITY, this.maxs); + WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255)); +} + +#elif defined(CSQC) + +void trigger_common_read(entity this, bool withtarget) +{ + int f = ReadByte(); + this.warpzone_isboxy = (f & 1); + + if(withtarget) + { + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.killtarget); + + int targbits = ReadByte(); + + this.target = ((targbits & BIT(0)) ? strzone(ReadString()) : string_null); + this.target2 = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null); + this.target3 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null); + this.target4 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null); + this.targetname = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null); + this.killtarget = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null); + } + + if(f & 4) + this.origin = ReadVector(); + else + this.origin = '0 0 0'; + setorigin(this, this.origin); + + if(f & 8) + this.movedir = ReadVector(); + else + this.movedir = '0 0 0'; + + if(f & 16) + this.angles = ReadVector(); + else + this.angles = '0 0 0'; + + this.modelindex = ReadShort(); + this.mins = ReadVector(); + this.maxs = ReadVector(); + this.scale = ReadByte() / 16; + setsize(this, this.mins, this.maxs); +} + +void trigger_remove_generic(entity this) +{ + strfree(this.target); + strfree(this.target2); + strfree(this.target3); + strfree(this.target4); + strfree(this.targetname); + strfree(this.killtarget); +} +#endif + + +/* +============================== +SUB_UseTargets + +the global "activator" should be set to the entity that initiated the firing. + +If this.delay is set, a DelayedUse entity will be created that will actually +do the SUB_UseTargets after that many seconds have passed. + +Centerprints any this.message to the activator. + +Removes all entities with a targetname that match this.killtarget, +and removes them, so some events can remove other triggers. + +Search for (string)targetname in all entities that +match (string)this.target and call their .use function + +============================== +*/ + +void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) +{ +// +// check for a delay +// + if (this.delay) + { + // create a temp object to fire at a later time + entity t = new(DelayedUse); + t.nextthink = time + this.delay; + setthink(t, DelayThink); + t.enemy = actor; + t.message = this.message; + t.killtarget = this.killtarget; + t.target = this.target; + t.target2 = this.target2; + t.target3 = this.target3; + t.target4 = this.target4; + t.antiwall_flag = this.antiwall_flag; + return; + } + + string s; + +// +// print the message +// +#ifdef SVQC + if(this) + if(IS_PLAYER(actor) && this.message != "") + if(IS_REAL_CLIENT(actor)) + { + centerprint(actor, this.message); + if (this.noise == "") + play2(actor, SND(TALK)); + } + +// +// kill the killtagets +// + s = this.killtarget; + if (s != "") + { + for(entity t = NULL; (t = find(t, targetname, s)); ) + delete(t); + } +#endif + +// +// fire targets +// + + if(this.target_random) + RandomSelection_Init(); + + for(int i = 0; i < 4; ++i) + { + switch(i) + { + default: + case 0: s = this.target; break; + case 1: s = this.target2; break; + case 2: s = this.target3; break; + case 3: s = this.target4; break; + } + if (s != "") + { + for(entity t = NULL; (t = find(t, targetname, s)); ) + { + if(t != this && t.use && (t.sub_target_used != time || !preventReuse)) + { + if(this.target_random) + { + RandomSelection_AddEnt(t, 1, 0); + } + else + { + t.use(t, actor, this); + if(preventReuse) + t.sub_target_used = time; + } + } + } + } + } + + if(this.target_random && RandomSelection_chosen_ent) + { + RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this); + if(preventReuse) + RandomSelection_chosen_ent.sub_target_used = time; + } +} + +void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); } +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); } diff --git a/qcsrc/common/mapobjects/triggers.qh b/qcsrc/common/mapobjects/triggers.qh new file mode 100644 index 0000000000..b9baf63f1c --- /dev/null +++ b/qcsrc/common/mapobjects/triggers.qh @@ -0,0 +1,50 @@ +#pragma once +#include "defs.qh" + +.bool pushable; + +.float antiwall_flag; // Variable to define what to do with func_clientwall +// 0 == do nothing, 1 == deactivate, 2 == activate + +.float height; + +#define IFTARGETED if(this.targetname && this.targetname != "") + +.float lip; + +// used elsewhere (will fix) +#ifdef SVQC +void trigger_common_write(entity this, bool withtarget); + +string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin); + +void target_voicescript_next(entity pl); +void target_voicescript_clear(entity pl); + +void SUB_DontUseTargets(entity this, entity actor, entity trigger); +void SUB_UseTargets(entity this, entity actor, entity trigger); + +void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger); + +void generic_setactive(entity this, int act); +// generic methods for netlinked entities +void generic_netlinked_reset(entity this); +void generic_netlinked_setactive(entity this, int act); +// WARNING: DON'T USE, ONLY TO KEEP COMPATIBILITY BECAUSE OF SWITCH FROM .state TO .alive!!!! +void generic_netlinked_legacy_use(entity this, entity actor, entity trigger); +#endif + +.float sub_target_used; + +.float volume, atten; + +.vector dest; + +#ifdef CSQC +void trigger_common_read(entity this, bool withtarget); +void trigger_remove_generic(entity this); + +.float active; +.string target; +.string targetname; +#endif diff --git a/qcsrc/common/minigames/cl_minigames.qh b/qcsrc/common/minigames/cl_minigames.qh index a0f6195d12..415984a2a8 100644 --- a/qcsrc/common/minigames/cl_minigames.qh +++ b/qcsrc/common/minigames/cl_minigames.qh @@ -1,5 +1,7 @@ #pragma once +#include "cl_minigames_hud.qh" + // Get a square in the center of the avaliable area // \note macro to pass by reference pos and mySize #define minigame_hud_fitsqare(pos, mySize) \ @@ -92,12 +94,6 @@ void minigame_cmd_workaround(float dummy, string...cmdargc); // (ie: it's their turn and they should get back to the minigame) void minigame_prompt(); -float HUD_MinigameMenu_IsOpened(); -void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger); - -// Adds a game-specific entry to the menu -void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_arg); - #define FOREACH_MINIGAME_ENTITY(entityvar) \ entityvar=NULL; \ diff --git a/qcsrc/common/minigames/cl_minigames_hud.qc b/qcsrc/common/minigames/cl_minigames_hud.qc index 2049dc9eae..f299af3c3a 100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qc +++ b/qcsrc/common/minigames/cl_minigames_hud.qc @@ -357,9 +357,6 @@ void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger) HUD_MinigameMenu_entries = NULL; HUD_MinigameMenu_last_entry = NULL; HUD_MinigameMenu_activeitem = NULL; - if(autocvar_hud_cursormode) - if ( !autocvar__hud_configure ) - setcursormode(0); } } @@ -420,8 +417,6 @@ void HUD_MinigameMenu_Open() HUD_MinigameMenu_last_entry ); HUD_MinigameMenu_CurrentButton(); HUD_MinigameMenu_activeitem = NULL; - if(autocvar_hud_cursormode) - setcursormode(1); } } @@ -683,11 +678,6 @@ void HUD_Minigame_Mouse() if( !HUD_MinigameMenu_IsOpened() || autocvar__hud_configure || mv_active ) return; - if (!autocvar_hud_cursormode) - update_mousepos(); - if ( HUD_MinigameMenu_IsOpened() && HUD_mouse_over(HUD_PANEL(MINIGAMEMENU)) ) HUD_MinigameMenu_MouseInput(); - - draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha); } diff --git a/qcsrc/common/minigames/cl_minigames_hud.qh b/qcsrc/common/minigames/cl_minigames_hud.qh index ef44ea025f..c14985f627 100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qh +++ b/qcsrc/common/minigames/cl_minigames_hud.qh @@ -2,3 +2,9 @@ float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary); void HUD_Minigame_Mouse(); + +float HUD_MinigameMenu_IsOpened(); +void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger); + +// Adds a game-specific entry to the menu +void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_arg); diff --git a/qcsrc/common/minigames/minigame/bd.qc b/qcsrc/common/minigames/minigame/bd.qc index 4790f1f1f7..91fa9cbda9 100644 --- a/qcsrc/common/minigames/minigame/bd.qc +++ b/qcsrc/common/minigames/minigame/bd.qc @@ -309,8 +309,7 @@ bool bd_move_dozer(entity minigame, entity dozer) case BD_TILE_BRICK1: return false; } - if(hit.netname) { strunzone(hit.netname); } - hit.netname = strzone(testpos); + strcpy(hit.netname, testpos); minigame_server_sendflags(hit,MINIG_SF_UPDATE); break; } @@ -330,8 +329,7 @@ bool bd_move_dozer(entity minigame, entity dozer) case BD_TILE_BRICK1: return false; } - if(dozer.netname) { strunzone(dozer.netname); } - dozer.netname = strzone(newpos); + strcpy(dozer.netname, newpos); return true; } @@ -422,7 +420,7 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st if(!piece) return; // how?! - if(piece.netname) { strunzone(piece.netname); } + strfree(piece.netname); delete(piece); minigame_server_sendflags(minigame,MINIG_SF_UPDATE); return; @@ -488,12 +486,12 @@ void bd_unfill_recurse(entity minigame, entity player, int thetype, int letter, if(targ && thetype == targ.bd_tiletype) { - if(targ.netname) { strunzone(targ.netname); } + strfree(targ.netname); delete(targ); } else if(piece && thetype == piece.bd_tiletype) { - if(piece.netname) { strunzone(piece.netname); } + strfree(piece.netname); delete(piece); } else return; @@ -568,7 +566,7 @@ void bd_setup_pieces(entity minigame) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } e = NULL; @@ -598,8 +596,7 @@ void bd_do_next_match(entity minigame, entity player) if(minigame.bd_nextlevel && minigame.bd_nextlevel != "") { - if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); } - minigame.bd_levelname = strzone(minigame.bd_nextlevel); + strcpy(minigame.bd_levelname, minigame.bd_nextlevel); } bd_setup_pieces(minigame); @@ -609,8 +606,7 @@ void bd_do_next_match(entity minigame, entity player) void bd_set_next_match(entity minigame, string next) { - if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); } - minigame.bd_nextlevel = strzone(next); + strcpy(minigame.bd_nextlevel, next); } void bd_next_match(entity minigame, entity player, string next) @@ -678,8 +674,7 @@ void bd_set_nextlevel(entity minigame, string s) { tokenize_console(s); - if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); } - minigame.bd_nextlevel = strzone(argv(2)); + strcpy(minigame.bd_nextlevel, argv(2)); } int bd_fix_dir(vector dir) @@ -854,8 +849,7 @@ int bd_server_event(entity minigame, string event, ...) { case "start": { - if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); } - minigame.bd_levelname = strzone(autocvar_sv_minigames_bulldozer_startlevel); + strcpy(minigame.bd_levelname, autocvar_sv_minigames_bulldozer_startlevel); bd_setup_pieces(minigame); minigame.minigame_flags = BD_TURN_MOVE; @@ -867,7 +861,7 @@ int bd_server_event(entity minigame, string event, ...) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } e = NULL; @@ -877,8 +871,8 @@ int bd_server_event(entity minigame, string event, ...) delete(e); } - if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); } - if(minigame.bd_levelname) { strunzone(minigame.bd_levelname); } + strfree(minigame.bd_nextlevel); + strfree(minigame.bd_levelname); return false; } case "join": @@ -1209,8 +1203,7 @@ void bd_editor_fill(entity minigame) void bd_set_curr_pos(string s) { - if ( bd_curr_pos ) - strunzone(bd_curr_pos); + strfree(bd_curr_pos); if ( s ) s = strzone(s); bd_curr_pos = s; @@ -1377,7 +1370,7 @@ int bd_client_event(entity minigame, string event, ...) { sent.message = bd_turn_to_string(sent.minigame_flags); //if ( sent.minigame_flags & minigame_self.team ) - minigame_prompt(); + //minigame_prompt(); } } else if(sent.classname == "minigame_board_piece") @@ -1386,8 +1379,7 @@ int bd_client_event(entity minigame, string event, ...) { int letter = ReadByte(); int number = ReadByte(); - if(sent.netname) { strunzone(sent.netname); } - sent.netname = strzone(minigame_tile_buildname(letter, number)); + strcpy(sent.netname, minigame_tile_buildname(letter, number)); sent.bd_tiletype = ReadByte(); diff --git a/qcsrc/common/minigames/minigame/c4.qc b/qcsrc/common/minigames/minigame/c4.qc index b3f5885c22..c8f8184f5b 100644 --- a/qcsrc/common/minigames/minigame/c4.qc +++ b/qcsrc/common/minigames/minigame/c4.qc @@ -201,7 +201,7 @@ int c4_server_event(entity minigame, string event, ...) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } return false; @@ -399,8 +399,7 @@ void c4_make_move(entity minigame) void c4_set_curr_pos(string s) { - if ( c4_curr_pos ) - strunzone(c4_curr_pos); + strfree(c4_curr_pos); if ( s ) s = strzone(s); c4_curr_pos = s; diff --git a/qcsrc/common/minigames/minigame/nmm.qc b/qcsrc/common/minigames/minigame/nmm.qc index 82e09c324a..65ac9dee01 100644 --- a/qcsrc/common/minigames/minigame/nmm.qc +++ b/qcsrc/common/minigames/minigame/nmm.qc @@ -126,9 +126,9 @@ void nmm_kill_tiles(entity minig) while ( ( e = findentity(e,owner,minig) ) ) if ( e.classname == "minigame_nmm_tile" ) { - strunzone(e.netname); - strunzone(e.nmm_tile_hmill); - strunzone(e.nmm_tile_vmill); + strfree(e.netname); + strfree(e.nmm_tile_hmill); + strfree(e.nmm_tile_vmill); delete(e); } } diff --git a/qcsrc/common/minigames/minigame/pp.qc b/qcsrc/common/minigames/minigame/pp.qc index 40ddcca08b..0b1d74344e 100644 --- a/qcsrc/common/minigames/minigame/pp.qc +++ b/qcsrc/common/minigames/minigame/pp.qc @@ -121,7 +121,7 @@ void pp_move(entity minigame, entity player, string pos ) if(existing) { - if(existing.netname) { strunzone(existing.netname); } + strfree(existing.netname); delete(existing); } @@ -215,7 +215,7 @@ int pp_server_event(entity minigame, string event, ...) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } return false; @@ -471,8 +471,7 @@ void pp_make_move(entity minigame) void pp_set_curr_pos(string s) { - if ( pp_curr_pos ) - strunzone(pp_curr_pos); + strfree(pp_curr_pos); if ( s ) s = strzone(s); pp_curr_pos = s; diff --git a/qcsrc/common/minigames/minigame/ps.qc b/qcsrc/common/minigames/minigame/ps.qc index cd5c001e7a..b5b900b8a1 100644 --- a/qcsrc/common/minigames/minigame/ps.qc +++ b/qcsrc/common/minigames/minigame/ps.qc @@ -139,11 +139,10 @@ bool ps_move_piece(entity minigame, entity piece, string pos, int leti, int numb if(!middle) return false; - if(middle.netname) { strunzone(middle.netname); } + strfree(middle.netname); delete(middle); - if(piece.netname) { strunzone(piece.netname); } - piece.netname = strzone(pos); + strcpy(piece.netname, pos); minigame_server_sendflags(piece,MINIG_SF_ALL); @@ -232,7 +231,7 @@ int ps_server_event(entity minigame, string event, ...) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } return false; @@ -514,8 +513,7 @@ void ps_make_move(entity minigame) void ps_set_curr_pos(string s) { - if ( ps_curr_pos ) - strunzone(ps_curr_pos); + strfree(ps_curr_pos); if ( s ) s = strzone(s); ps_curr_pos = s; @@ -608,8 +606,8 @@ int ps_client_event(entity minigame, string event, ...) if ( sf & MINIG_SF_UPDATE ) { sent.message = ps_turn_to_string(sent.minigame_flags); - if ( sent.minigame_flags & minigame_self.team ) - minigame_prompt(); + //if ( sent.minigame_flags & minigame_self.team ) + //minigame_prompt(); } } diff --git a/qcsrc/common/minigames/minigame/ttt.qc b/qcsrc/common/minigames/minigame/ttt.qc index c5a658054f..a6cc502377 100644 --- a/qcsrc/common/minigames/minigame/ttt.qc +++ b/qcsrc/common/minigames/minigame/ttt.qc @@ -147,7 +147,7 @@ int ttt_server_event(entity minigame, string event, ...) while( (e = findentity(e, owner, minigame)) ) if(e.classname == "minigame_board_piece") { - if(e.netname) { strunzone(e.netname); } + strfree(e.netname); delete(e); } return false; @@ -540,8 +540,7 @@ void ttt_make_move(entity minigame) void ttt_set_curr_pos(string s) { - if ( ttt_curr_pos ) - strunzone(ttt_curr_pos); + strfree(ttt_curr_pos); if ( s ) s = strzone(s); ttt_curr_pos = s; diff --git a/qcsrc/common/minigames/sv_minigames.qc b/qcsrc/common/minigames/sv_minigames.qc index 415417b465..af74e6a5a6 100644 --- a/qcsrc/common/minigames/sv_minigames.qc +++ b/qcsrc/common/minigames/sv_minigames.qc @@ -9,7 +9,7 @@ void player_clear_minigame(entity player) set_movetype(player, MOVETYPE_WALK); else set_movetype(player, MOVETYPE_FLY_WORLDONLY); - player.team_forced = 0; + Player_SetForcedTeamIndex(player, TEAM_FORCE_DEFAULT); } void minigame_rmplayer(entity minigame_session, entity player) @@ -150,7 +150,7 @@ int minigame_addplayer(entity minigame_session, entity player) PutObserverInServer(player); } if ( autocvar_sv_minigames_observer == 2 ) - player.team_forced = -1; + Player_SetForcedTeamIndex(player, TEAM_FORCE_SPECTATOR); minigame_resend(minigame_session); } @@ -248,7 +248,7 @@ void end_minigame(entity minigame_session) delete(e); } - strunzone(minigame_session.netname); + strfree(minigame_session.netname); delete(minigame_session); } diff --git a/qcsrc/common/models/model.qh b/qcsrc/common/models/model.qh index 7a1e7d73c2..38aa4e33ad 100644 --- a/qcsrc/common/models/model.qh +++ b/qcsrc/common/models/model.qh @@ -1,10 +1,9 @@ #pragma once -#define setmodel(e, m) _setmodel((e), (m).model_str()) - CLASS(Model, Object) ATTRIB(Model, m_id, int, 0); ATTRIB(Model, model_str, string()); + ATTRIB(Model, model_str_, string); CONSTRUCTOR(Model, string() path) { CONSTRUCT(Model); @@ -20,5 +19,12 @@ CLASS(Model, Object) } profile(sprintf("precache_model(\"%s\")", s)); precache_model(s); + strcpy(this.model_str_, s); } ENDCLASS(Model) + +#define setmodel(this, m) MACRO_BEGIN \ + Model _setmodel_model = (m); \ + string _setmodel_cached = _setmodel_model.model_str_; \ + _setmodel((this), _setmodel_cached ? _setmodel_cached : _setmodel_model.model_str()); \ +MACRO_END diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index beab98f0ac..88120a0ea7 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -87,23 +87,31 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ) { if(targ == NULL) return false; - if(targ.health <= 0) + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0) return false; if(DIFF_TEAM(targ, this) && targ != this.monster_follow) return false; if(STAT(FROZEN, targ)) return false; if(!IS_PLAYER(targ)) - return (IS_MONSTER(targ) && targ.health < targ.max_health); + return (IS_MONSTER(targ) && GetResourceAmount(targ, RESOURCE_HEALTH) < targ.max_health); if(targ.items & ITEM_Shield.m_itemid) return false; switch(this.skin) { - case 0: return (targ.health < autocvar_g_balance_health_regenstable); - case 1: return ((targ.ammo_cells && targ.ammo_cells < g_pickup_cells_max) || (targ.ammo_plasma && targ.ammo_plasma < g_pickup_plasma_max) || (targ.ammo_rockets && targ.ammo_rockets < g_pickup_rockets_max) || (targ.ammo_nails && targ.ammo_nails < g_pickup_nails_max) || (targ.ammo_shells && targ.ammo_shells < g_pickup_shells_max)); - case 2: return (targ.armorvalue < autocvar_g_balance_armor_regenstable); - case 3: return (targ.health > 0); + case 0: return (GetResourceAmount(targ, RESOURCE_HEALTH) < autocvar_g_balance_health_regenstable); + case 1: + { + return ((GetResourceAmount(targ, RESOURCE_CELLS) && GetResourceAmount(targ, RESOURCE_CELLS) < g_pickup_cells_max) + || (GetResourceAmount(targ, RESOURCE_PLASMA) && GetResourceAmount(targ, RESOURCE_PLASMA) < g_pickup_plasma_max) + || (GetResourceAmount(targ, RESOURCE_ROCKETS) && GetResourceAmount(targ, RESOURCE_ROCKETS) < g_pickup_rockets_max) + || (GetResourceAmount(targ, RESOURCE_BULLETS) && GetResourceAmount(targ, RESOURCE_BULLETS) < g_pickup_nails_max) + || (GetResourceAmount(targ, RESOURCE_SHELLS) && GetResourceAmount(targ, RESOURCE_SHELLS) < g_pickup_shells_max) + ); + } + case 2: return (GetResourceAmount(targ, RESOURCE_ARMOR) < autocvar_g_balance_armor_regenstable); + case 3: return (GetResourceAmount(targ, RESOURCE_HEALTH) > 0); } return false; @@ -118,7 +126,7 @@ void M_Mage_Attack_Spike_Explode(entity this, entity directhitentity) this.realowner.mage_spike = NULL; Send_Effect(EFFECT_EXPLOSION_SMALL, this.origin, '0 0 0', 1); - RadiusDamage (this, this.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), + RadiusDamage (this, this.realowner, (autocvar_g_monster_mage_attack_spike_damage), (autocvar_g_monster_mage_attack_spike_damage) * 0.5, (autocvar_g_monster_mage_attack_spike_radius), NULL, NULL, 0, DEATH_MONSTER_MAGE.m_id, DMG_NOWEP, directhitentity); delete(this); @@ -136,7 +144,7 @@ void M_Mage_Attack_Spike_Touch(entity this, entity toucher) // copied from W_Seeker_Think void M_Mage_Attack_Spike_Think(entity this) { - if (time > this.ltime || (this.enemy && this.enemy.health <= 0) || this.owner.health <= 0) { + if (time > this.ltime || (this.enemy && GetResourceAmount(this.enemy, RESOURCE_HEALTH) <= 0) || GetResourceAmount(this.owner, RESOURCE_HEALTH) <= 0) { this.projectiledeathtype |= HITTYPE_SPLASH; M_Mage_Attack_Spike_Explode(this, NULL); } @@ -226,26 +234,32 @@ void M_Mage_Defend_Heal(entity this) switch(this.skin) { case 0: - if(it.health < autocvar_g_balance_health_regenstable) it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable); + { + Heal(it, this, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_health_regenstable); fx = EFFECT_HEALING; break; + } case 1: - if(it.ammo_cells) it.ammo_cells = bound(it.ammo_cells, it.ammo_cells + 1, g_pickup_cells_max); - if(it.ammo_plasma) it.ammo_plasma = bound(it.ammo_plasma, it.ammo_plasma + 1, g_pickup_plasma_max); - if(it.ammo_rockets) it.ammo_rockets = bound(it.ammo_rockets, it.ammo_rockets + 1, g_pickup_rockets_max); - if(it.ammo_shells) it.ammo_shells = bound(it.ammo_shells, it.ammo_shells + 2, g_pickup_shells_max); - if(it.ammo_nails) it.ammo_nails = bound(it.ammo_nails, it.ammo_nails + 5, g_pickup_nails_max); + { + if(GetResourceAmount(this, RESOURCE_CELLS)) GiveResourceWithLimit(it, RESOURCE_CELLS, 1, g_pickup_cells_max); + if(GetResourceAmount(this, RESOURCE_PLASMA)) GiveResourceWithLimit(it, RESOURCE_PLASMA, 1, g_pickup_plasma_max); + if(GetResourceAmount(this, RESOURCE_ROCKETS)) GiveResourceWithLimit(it, RESOURCE_ROCKETS, 1, g_pickup_rockets_max); + if(GetResourceAmount(this, RESOURCE_SHELLS)) GiveResourceWithLimit(it, RESOURCE_SHELLS, 2, g_pickup_shells_max); + if(GetResourceAmount(this, RESOURCE_BULLETS)) GiveResourceWithLimit(it, RESOURCE_BULLETS, 5, g_pickup_nails_max); + // TODO: fuel? fx = EFFECT_AMMO_REGEN; break; + } case 2: - if(it.armorvalue < autocvar_g_balance_armor_regenstable) + if(GetResourceAmount(it, RESOURCE_ARMOR) < autocvar_g_balance_armor_regenstable) { - it.armorvalue = bound(0, it.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable); + GiveResourceWithLimit(it, RESOURCE_ARMOR, autocvar_g_monster_mage_heal_allies, autocvar_g_balance_armor_regenstable); fx = EFFECT_ARMOR_REPAIR; } break; case 3: - it.health = bound(0, it.health - ((it == this) ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable); + float hp = ((it == this) ? autocvar_g_monster_mage_heal_self : autocvar_g_monster_mage_heal_allies); + TakeResource(it, RESOURCE_HEALTH, hp); // TODO: use regular damage functions? needs a way to bypass friendly fire checks fx = EFFECT_RAGE; break; } @@ -255,9 +269,9 @@ void M_Mage_Defend_Heal(entity this) else { Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); - it.health = bound(0, it.health + (autocvar_g_monster_mage_heal_allies), it.max_health); + Heal(it, this, autocvar_g_monster_mage_heal_allies, RESOURCE_LIMIT_NONE); if(!(it.spawnflags & MONSTERFLAG_INVINCIBLE) && it.sprite) - WaypointSprite_UpdateHealth(it.sprite, it.health); + WaypointSprite_UpdateHealth(it.sprite, GetResourceAmount(it, RESOURCE_HEALTH)); } }); @@ -272,7 +286,7 @@ void M_Mage_Defend_Heal(entity this) void M_Mage_Attack_Push(entity this) { sound(this, CH_SHOTS, SND_TAGEXP1, 1, ATTEN_NORM); - RadiusDamage (this, this, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), + RadiusDamage (this, this, (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_damage), (autocvar_g_monster_mage_attack_push_radius), NULL, NULL, (autocvar_g_monster_mage_attack_push_force), DEATH_MONSTER_MAGE.m_id, DMG_NOWEP, this.enemy); Send_Effect(EFFECT_TE_EXPLOSION, this.origin, '0 0 0', 1); @@ -311,14 +325,14 @@ void M_Mage_Attack_Teleport(entity this, entity targ) void M_Mage_Defend_Shield_Remove(entity this) { this.effects &= ~(EF_ADDITIVE | EF_BLUE); - this.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); } void M_Mage_Defend_Shield(entity this) { this.effects |= (EF_ADDITIVE | EF_BLUE); this.mage_shield_delay = time + (autocvar_g_monster_mage_shield_delay); - this.armorvalue = (autocvar_g_monster_mage_shield_blockpercent); + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monster_mage_shield_blockpercent); this.mage_shield_time = time + (autocvar_g_monster_mage_shield_time); setanim(this, this.anim_shoot, true, true, true); this.attack_finished_single[0] = time + 1; @@ -405,16 +419,16 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor)) }); } - if(actor.health < (autocvar_g_monster_mage_heal_minhealth) || need_help) + if(GetResourceAmount(actor, RESOURCE_HEALTH) < (autocvar_g_monster_mage_heal_minhealth) || need_help) if(time >= actor.attack_finished_single[0]) if(random() < 0.5) M_Mage_Defend_Heal(actor); - if(time >= actor.mage_shield_time && actor.armorvalue) + if(time >= actor.mage_shield_time && GetResourceAmount(actor, RESOURCE_ARMOR)) M_Mage_Defend_Shield_Remove(actor); if(actor.enemy) - if(actor.health < actor.max_health) + if(GetResourceAmount(actor, RESOURCE_HEALTH) < actor.max_health) if(time >= actor.mage_shield_delay) if(random() < 0.5) M_Mage_Defend_Shield(actor); @@ -455,7 +469,7 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor)) METHOD(Mage, mr_setup, bool(Mage this, entity actor)) { TC(Mage, this); - if(!actor.health) actor.health = (autocvar_g_monster_mage_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_mage_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_mage_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_mage_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_mage_speed_stop); } diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index eeefeae8ca..9981474f9b 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -85,15 +85,15 @@ void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity t void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, adaptor_think2use); } @@ -136,7 +136,7 @@ void M_Shambler_Attack_Lightning(entity this) settouch(gren, M_Shambler_Attack_Lightning_Touch); gren.takedamage = DAMAGE_YES; - gren.health = 50; + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, 50); gren.damageforcescale = 0; gren.event_damage = M_Shambler_Attack_Lightning_Damage; gren.damagedbycontents = true; @@ -246,7 +246,7 @@ METHOD(Shambler, mr_anim, bool(Shambler this, entity actor)) METHOD(Shambler, mr_setup, bool(Shambler this, entity actor)) { TC(Shambler, this); - if(!actor.health) actor.health = (autocvar_g_monster_shambler_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_shambler_health); if(!actor.attack_range) actor.attack_range = 150; if(!actor.speed) { actor.speed = (autocvar_g_monster_shambler_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_shambler_speed_run); } diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 12277d1d64..d847cf4e6c 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -103,7 +103,7 @@ void M_Spider_Attack_Web_Explode(entity this) Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1); RadiusDamage(this, this.realowner, 0, 0, 25, NULL, NULL, 25, this.projectiledeathtype, DMG_NOWEP, NULL); - FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && it.monsterid != MON_SPIDER.monsterid, + FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && GetResourceAmount(it, RESOURCE_HEALTH) > 0 && it.monsterid != MON_SPIDER.monsterid, { it.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime); }); @@ -151,7 +151,7 @@ void M_Spider_Attack_Web(entity this) setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_NO; proj.damageforcescale = 0; - proj.health = 500; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, 500); proj.event_damage = func_null; proj.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, proj); @@ -168,9 +168,9 @@ void M_Spider_Attack_Web(entity this) bool M_Spider_Attack(int attack_type, entity actor, entity targ, .entity weaponentity) { + Weapon wep = WEP_SPIDER_ATTACK; switch(attack_type) { - Weapon wep = WEP_SPIDER_ATTACK; case MONSTER_ATTACK_MELEE: { wep.wr_think(wep, actor, weaponentity, 2); @@ -227,7 +227,7 @@ METHOD(Spider, mr_anim, bool(Spider this, entity actor)) METHOD(Spider, mr_setup, bool(Spider this, entity actor)) { TC(Spider, this); - if(!actor.health) actor.health = (autocvar_g_monster_spider_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_spider_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_spider_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_spider_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_spider_speed_stop); } diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index d596d7d33b..f6c905d6d1 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -71,7 +71,7 @@ void M_Wyvern_Attack_Fireball_Explode(entity this) entity own = this.realowner; - RadiusDamage(this, own, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force, + RadiusDamage(this, own, autocvar_g_monster_wyvern_attack_fireball_damage, autocvar_g_monster_wyvern_attack_fireball_edgedamage, autocvar_g_monster_wyvern_attack_fireball_force, NULL, NULL, autocvar_g_monster_wyvern_attack_fireball_radius, this.projectiledeathtype, DMG_NOWEP, NULL); FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM, @@ -152,7 +152,7 @@ METHOD(Wyvern, mr_anim, bool(Wyvern this, entity actor)) METHOD(Wyvern, mr_setup, bool(Wyvern this, entity actor)) { TC(Wyvern, this); - if(!actor.health) actor.health = (autocvar_g_monster_wyvern_health); + if(!GetResourceAmount(this, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_wyvern_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_wyvern_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_wyvern_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_wyvern_speed_stop); } diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index 297bab87dd..aaa27d21b2 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -51,7 +51,7 @@ const float zombie_anim_spawn = 30; void M_Zombie_Attack_Leap_Touch(entity this, entity toucher) { - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; vector angles_face; @@ -74,16 +74,16 @@ void M_Zombie_Attack_Leap_Touch(entity this, entity toucher) void M_Zombie_Defend_Block_End(entity this) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; setanim(this, this.anim_blockend, false, true, true); - this.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); } bool M_Zombie_Defend_Block(entity this) { - this.armorvalue = 0.9; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, 0.9); this.state = MONSTER_ATTACK_MELEE; // freeze monster this.attack_finished_single[0] = time + 2.1; this.anim_finished = this.attack_finished_single[0]; @@ -100,7 +100,7 @@ bool M_Zombie_Attack(int attack_type, entity actor, entity targ, .entity weapone { case MONSTER_ATTACK_MELEE: { - if(random() < 0.3 && actor.health < 75 && actor.enemy.health > 10) + if(random() < 0.3 && GetResourceAmount(actor, RESOURCE_HEALTH) < 75 && GetResourceAmount(actor.enemy, RESOURCE_HEALTH) > 10) return M_Zombie_Defend_Block(actor); float anim_chance = random(); @@ -148,7 +148,7 @@ METHOD(Zombie, mr_pain, float(Zombie this, entity actor, float damage_take, enti METHOD(Zombie, mr_death, bool(Zombie this, entity actor)) { TC(Zombie, this); - actor.armorvalue = autocvar_g_monsters_armor_blockpercent; + SetResourceAmountExplicit(actor, RESOURCE_ARMOR, autocvar_g_monsters_armor_blockpercent); setanim(actor, ((random() > 0.5) ? actor.anim_die1 : actor.anim_die2), false, true, true); return true; @@ -180,7 +180,7 @@ METHOD(Zombie, mr_anim, bool(Zombie this, entity actor)) METHOD(Zombie, mr_setup, bool(Zombie this, entity actor)) { TC(Zombie, this); - if(!actor.health) actor.health = (autocvar_g_monster_zombie_health); + if(!GetResourceAmount(actor, RESOURCE_HEALTH)) SetResourceAmountExplicit(actor, RESOURCE_HEALTH, autocvar_g_monster_zombie_health); if(!actor.speed) { actor.speed = (autocvar_g_monster_zombie_speed_walk); } if(!actor.speed2) { actor.speed2 = (autocvar_g_monster_zombie_speed_run); } if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_zombie_speed_stop); } diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 1197c26134..004fbf92b6 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1,6 +1,5 @@ #include "sv_monsters.qh" -#include <server/g_subs.qh> #include <lib/warpzone/common.qh> #include "../constants.qh" #include "../teams.qh" @@ -18,7 +17,7 @@ #include "../vehicles/all.qh" #include <server/campaign.qh> #include <server/command/_mod.qh> -#include "../triggers/triggers.qh" +#include "../mapobjects/triggers.qh" #include <lib/csqcmodel/sv_model.qh> #include <server/round_handler.qh> #include <server/weapons/_mod.qh> @@ -82,9 +81,10 @@ bool Monster_ValidTarget(entity this, entity targ) || (IS_VEHICLE(targ) && !((Monsters_from(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless || (time < game_starttime) // monsters do nothing before match has started || (targ.takedamage == DAMAGE_NO) + || (game_stopped) || (targ.items & IT_INVISIBILITY) || (IS_SPEC(targ) || IS_OBSERVER(targ)) // don't attack spectators - || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || targ.health <= 0 || this.health <= 0)) + || (!IS_VEHICLE(targ) && (IS_DEAD(targ) || IS_DEAD(this) || GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(this, RESOURCE_HEALTH) <= 0)) || (this.monster_follow == targ || targ.monster_follow == this) || (!IS_VEHICLE(targ) && (targ.flags & FL_NOTARGET)) || (!autocvar_g_monsters_typefrag && PHYS_INPUT_BUTTON_CHAT(targ)) @@ -275,7 +275,7 @@ void Monster_Sounds_Precache(entity this) void Monster_Sounds_Clear(entity this) { -#define _MSOUND(m) if(this.monstersound_##m) { strunzone(this.monstersound_##m); this.monstersound_##m = string_null; } +#define _MSOUND(m) strfree(this.monstersound_##m); ALLMONSTERSOUNDS #undef _MSOUND } @@ -310,9 +310,7 @@ bool Monster_Sounds_Load(entity this, string f, int first) field = Monster_Sound_SampleField(argv(0)); if(GetMonsterSoundSampleField_notFound) continue; - if (this.(field)) - strunzone(this.(field)); - this.(field) = strzone(strcat(argv(1), " ", argv(2))); + strcpy(this.(field), strcat(argv(1), " ", argv(2))); } fclose(fh); return true; @@ -377,7 +375,7 @@ bool Monster_Attack_Leap_Check(entity this, vector vel) return false; // already attacking if(!IS_ONGROUND(this)) return false; // not on the ground - if(this.health <= 0 || IS_DEAD(this)) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0 || IS_DEAD(this)) return false; // called when dead? if(time < this.attack_finished_single[0]) return false; // still attacking @@ -488,7 +486,7 @@ void Monster_Miniboss_Check(entity this) // g_monsters_miniboss_chance cvar or spawnflags 64 causes a monster to be a miniboss if ((this.spawnflags & MONSTERFLAG_MINIBOSS) || (chance < autocvar_g_monsters_miniboss_chance)) { - this.health += autocvar_g_monsters_miniboss_healthboost; + GiveResource(this, RESOURCE_HEALTH, autocvar_g_monsters_miniboss_healthboost); this.effects |= EF_RED; if(!this.weapon) this.weapon = WEP_VORTEX.m_id; @@ -529,10 +527,11 @@ void Monster_Dead_Fade(entity this) this.pos2 = this.angles; } this.event_damage = func_null; + this.event_heal = func_null; this.takedamage = DAMAGE_NO; setorigin(this, this.pos1); this.angles = this.pos2; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); setmodel(this, MDL_Null); } else @@ -561,7 +560,7 @@ vector Monster_Move_Target(entity this, entity targ) // cases where the enemy may have changed their state (don't need to check everything here) if((!this.enemy) - || (IS_DEAD(this.enemy) || this.enemy.health < 1) + || (IS_DEAD(this.enemy) || GetResourceAmount(this.enemy, RESOURCE_HEALTH) < 1) || (STAT(FROZEN, this.enemy)) || (this.enemy.flags & FL_NOTARGET) || (this.enemy.alpha < 0.5 && this.enemy.alpha != 0) @@ -696,7 +695,6 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra } .entity draggedby; -.entity target2; void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) { @@ -896,9 +894,9 @@ void Monster_Reset(entity this) setorigin(this, this.pos1); this.angles = this.pos2; - Unfreeze(this); // remove any icy remains + Unfreeze(this, false); // remove any icy remains - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.velocity = '0 0 0'; this.enemy = NULL; this.goalentity = NULL; @@ -908,11 +906,11 @@ void Monster_Reset(entity this) void Monster_Dead_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - if(this.health <= -50) // 100 health until gone? + if(GetResourceAmount(this, RESOURCE_HEALTH) <= -50) // 100 health until gone? { Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); @@ -932,10 +930,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) this.monster_lifetime = time + 5; if(STAT(FROZEN, this)) - { - Unfreeze(this); // remove any icy remains - this.health = 0; // reset by Unfreeze - } + Unfreeze(this, false); // remove any icy remains monster_dropitem(this, attacker); @@ -958,6 +953,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) _setmodel(this, this.mdl_dead); this.event_damage = ((gibbed) ? func_null : Monster_Dead_Damage); + this.event_heal = func_null; this.solid = SOLID_CORPSE; this.takedamage = DAMAGE_AIM; this.deadflag = DEAD_DEAD; @@ -1002,7 +998,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL) return; - vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); + vector v = healtharmor_applydamage(100, GetResourceAmount(this, RESOURCE_ARMOR) / 100, deathtype, damage); float take = v.x; //float save = v.y; @@ -1011,12 +1007,12 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(take) { - this.health -= take; + TakeResource(this, RESOURCE_HEALTH, take); Monster_Sound(this, monstersound_pain, 1.2, true, CH_PAIN); } if(this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); this.dmg_time = time; @@ -1034,7 +1030,7 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); } - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { if(deathtype == DEATH_KILL.m_id) this.candrop = false; // killed by mobkill command @@ -1043,13 +1039,13 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage SUB_UseTargets(this, attacker, this.enemy); this.target2 = this.oldtarget2; // reset to original target on death, incase we respawn - Monster_Dead(this, attacker, (this.health <= -100 || deathtype == DEATH_KILL.m_id)); + Monster_Dead(this, attacker, (GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id)); WaypointSprite_Kill(this.sprite); MUTATOR_CALLHOOK(MonsterDies, this, attacker, deathtype); - if(this.health <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed + if(GetResourceAmount(this, RESOURCE_HEALTH) <= -100 || deathtype == DEATH_KILL.m_id) // check if we're already gibbed { Violence_GibSplash(this, 1, 0.5, attacker); @@ -1059,6 +1055,18 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage } } +bool Monster_Heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.sprite) + WaypointSprite_UpdateHealth(targ.sprite, GetResourceAmount(targ, RESOURCE_HEALTH)); + return true; +} + // don't check for enemies, just keep walking in a straight line void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) { @@ -1149,36 +1157,34 @@ void Monster_Anim(entity this) void Monster_Frozen_Think(entity this) { - if(STAT(FROZEN, this) == 2) + if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + this.ticrate * this.revive_speed, 1); - this.health = max(1, STAT(REVIVE_PROGRESS, this) * this.max_health); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * this.max_health)); this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); if(STAT(REVIVE_PROGRESS, this) >= 1) - Unfreeze(this); + Unfreeze(this, false); } - else if(STAT(FROZEN, this) == 3) + else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - this.ticrate * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this) ); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (this.max_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE) && this.sprite) - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { - Unfreeze(this); - this.health = 0; + Unfreeze(this, false); if(this.event_damage) this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); } - else if ( STAT(REVIVE_PROGRESS, this) <= 0 ) - Unfreeze(this); + Unfreeze(this, false); } // otherwise, no revival! @@ -1212,7 +1218,7 @@ void Monster_Think(entity this) if(this.monster_lifetime && time >= this.monster_lifetime) { - Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, DMG_NOWEP, this.origin, this.origin); + Damage(this, this, this, GetResourceAmount(this, RESOURCE_HEALTH) + this.max_health, DEATH_KILL.m_id, DMG_NOWEP, this.origin, this.origin); return; } @@ -1244,8 +1250,8 @@ bool Monster_Spawn_Setup(entity this) mon.mr_setup(mon, this); // ensure some basic needs are met - if(!this.health) { this.health = 100; } - if(!this.armorvalue) { this.armorvalue = bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9); } + if(!GetResourceAmount(this, RESOURCE_HEALTH)) { SetResourceAmountExplicit(this, RESOURCE_HEALTH, 100); } + if(!GetResourceAmount(this, RESOURCE_ARMOR)) { SetResourceAmountExplicit(this, RESOURCE_ARMOR, bound(0.2, 0.5 * MONSTER_SKILLMOD(this), 0.9)); } if(!this.target_range) { this.target_range = autocvar_g_monsters_target_range; } if(!this.respawntime) { this.respawntime = autocvar_g_monsters_respawn_delay; } if(!this.monster_moveflags) { this.monster_moveflags = MONSTER_MOVE_WANDER; } @@ -1255,13 +1261,13 @@ bool Monster_Spawn_Setup(entity this) if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) { Monster_Miniboss_Check(this); - this.health *= MONSTER_SKILLMOD(this); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH) * MONSTER_SKILLMOD(this)); if(!this.skin) this.skin = rint(random() * 4); } - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.pain_finished = this.nextthink; if(IS_PLAYER(this.monster_follow)) @@ -1290,7 +1296,7 @@ bool Monster_Spawn_Setup(entity this) if(!(this.spawnflags & MONSTERFLAG_INVINCIBLE)) { WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateHealth(this.sprite, GetResourceAmount(this, RESOURCE_HEALTH)); } } @@ -1355,6 +1361,7 @@ bool Monster_Spawn(entity this, bool check_appear, int mon_id) this.damagedbycontents = true; this.monsterid = mon_id; this.event_damage = Monster_Damage; + this.event_heal = Monster_Heal; settouch(this, Monster_Touch); this.use = Monster_Use; this.solid = SOLID_BBOX; diff --git a/qcsrc/common/monsters/sv_spawn.qc b/qcsrc/common/monsters/sv_spawn.qc index d6989ad318..d456282d42 100644 --- a/qcsrc/common/monsters/sv_spawn.qc +++ b/qcsrc/common/monsters/sv_spawn.qc @@ -21,7 +21,7 @@ entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, if(monster == "random" || allow_any) { RandomSelection_Init(); - FOREACH(Monsters, it != MON_Null && (allow_any || (!(it.spawnflags & MONSTER_TYPE_PASSIVE) && !(it.spawnflags & MON_FLAG_HIDDEN))), + FOREACH(Monsters, it != MON_Null && (allow_any || !(it.spawnflags & MON_FLAG_HIDDEN)) && !(it.spawnflags & MONSTER_TYPE_PASSIVE), { RandomSelection_AddEnt(it, 1, 1); }); diff --git a/qcsrc/common/monsters/sv_spawner.qh b/qcsrc/common/monsters/sv_spawner.qh index 6f70f09bee..1fd3ec672f 100644 --- a/qcsrc/common/monsters/sv_spawner.qh +++ b/qcsrc/common/monsters/sv_spawner.qh @@ -1 +1,3 @@ #pragma once + +.string spawnmob; diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh index 4f940c42b3..163960ba5f 100644 --- a/qcsrc/common/mutators/base.qh +++ b/qcsrc/common/mutators/base.qh @@ -135,7 +135,7 @@ void RegisterCallbacks() {}; params(_MUTATOR_HANDLE_NOP, _MUTATOR_HANDLE_POPOUT) \ return ret; \ } \ - [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); } + ACCUMULATE void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); } #define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__) #ifdef __STDC__ @@ -171,6 +171,8 @@ void Mutator_Remove(Mutator mut); bool mutator_log = false; .bool m_added; +#define MUTATOR_IS_ENABLED(this) MUTATOR_##this.mutatorcheck() + #ifdef GAMEQC /** server mutators activate corresponding client mutators for all clients */ REGISTER_NET_LINKED(Mutator) @@ -265,7 +267,7 @@ void Mutator_Remove(Mutator mut) bool MUTATOR_##id##_check() { return dependence; } \ REGISTER(Mutators, MUTATOR, id, m_id, NEW(Mutator, #id, MUTATORFUNCTION_##id)) \ { this.mutatorcheck = MUTATOR_##id##_check; } \ - [[accumulate]] bool MUTATORFUNCTION_##id(int mode) + ACCUMULATE bool MUTATORFUNCTION_##id(int mode) STATIC_INIT(Mutators) { RegisterHooks(); @@ -301,7 +303,7 @@ STATIC_INIT_LATE(Mutators) { #define _MUTATOR_CALLBACK(name, func) \ Callback CALLBACK_##name; \ bool func(); \ - [[accumulate]] void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); } + ACCUMULATE void RegisterCallbacks() { CALLBACK_##name = NEW(Callback, func); } #define MUTATOR_HOOKFUNCTION(...) \ EVAL_MUTATOR_HOOKFUNCTION(OVERLOAD(MUTATOR_HOOKFUNCTION, __VA_ARGS__)) @@ -312,9 +314,9 @@ STATIC_INIT_LATE(Mutators) { #define MUTATOR_HOOKFUNCTION_3(mut, cb, order) \ _MUTATOR_CALLBACK(mut##_##cb, mut##_##cb) \ - [[accumulate]] bool MUTATORFUNCTION_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \ + ACCUMULATE bool MUTATORFUNCTION_##mut##_hooks(int mode) { MUTATOR_HOOK(cb, mut##_##cb, order); } \ bool mut##_##cb() { return = false; } \ - [[accumulate]] bool mut##_##cb() + ACCUMULATE bool mut##_##cb() #define MUTATOR_HOOK(cb, func, order) MACRO_BEGIN { \ MUTATOR_ONADD { \ diff --git a/qcsrc/common/mutators/mutator/buffs/all.inc b/qcsrc/common/mutators/mutator/buffs/all.inc index 877d5c4eac..0c9b889018 100644 --- a/qcsrc/common/mutators/mutator/buffs/all.inc +++ b/qcsrc/common/mutators/mutator/buffs/all.inc @@ -12,8 +12,8 @@ string Buff_UndeprecateName(string buffname) } REGISTER_BUFF(AMMO) { - this.m_prettyName = _("Ammo"); - this.m_name = "ammo"; + this.m_name = _("Ammo"); + this.netname = "ammo"; this.m_skin = 3; this.m_color = '0.76 1 0.1'; } @@ -21,8 +21,8 @@ BUFF_SPAWNFUNCS(ammo, BUFF_AMMO) BUFF_SPAWNFUNC_Q3TA_COMPAT(ammoregen, BUFF_AMMO) REGISTER_BUFF(RESISTANCE) { - this.m_prettyName = _("Resistance"); - this.m_name = "resistance"; + this.m_name = _("Resistance"); + this.netname = "resistance"; this.m_skin = 0; this.m_color = '0.36 1 0.07'; } @@ -30,8 +30,8 @@ BUFF_SPAWNFUNCS(resistance, BUFF_RESISTANCE) BUFF_SPAWNFUNC_Q3TA_COMPAT(resistance, BUFF_RESISTANCE) REGISTER_BUFF(SPEED) { - this.m_prettyName = _("Speed"); - this.m_name = "speed"; + this.m_name = _("Speed"); + this.netname = "speed"; this.m_skin = 9; this.m_color = '0.1 1 0.84'; } @@ -40,8 +40,8 @@ BUFF_SPAWNFUNC_Q3TA_COMPAT(haste, BUFF_SPEED) BUFF_SPAWNFUNC_Q3TA_COMPAT(scout, BUFF_SPEED) REGISTER_BUFF(MEDIC) { - this.m_prettyName = _("Medic"); - this.m_name = "medic"; + this.m_name = _("Medic"); + this.netname = "medic"; this.m_skin = 1; this.m_color = '1 0.12 0'; } @@ -51,40 +51,40 @@ BUFF_SPAWNFUNC_Q3TA_COMPAT(regen, BUFF_MEDIC) BUFF_SPAWNFUNC_Q3TA_COMPAT(revival, BUFF_MEDIC) REGISTER_BUFF(BASH) { - this.m_prettyName = _("Bash"); - this.m_name = "bash"; + this.m_name = _("Bash"); + this.netname = "bash"; this.m_skin = 5; this.m_color = '1 0.39 0'; } BUFF_SPAWNFUNCS(bash, BUFF_BASH) REGISTER_BUFF(VAMPIRE) { - this.m_prettyName = _("Vampire"); - this.m_name = "vampire"; + this.m_name = _("Vampire"); + this.netname = "vampire"; this.m_skin = 2; this.m_color = '1 0 0.24'; } BUFF_SPAWNFUNCS(vampire, BUFF_VAMPIRE) REGISTER_BUFF(DISABILITY) { - this.m_prettyName = _("Disability"); - this.m_name = "disability"; + this.m_name = _("Disability"); + this.netname = "disability"; this.m_skin = 7; this.m_color = '0.94 0.3 1'; } BUFF_SPAWNFUNCS(disability, BUFF_DISABILITY) REGISTER_BUFF(VENGEANCE) { - this.m_prettyName = _("Vengeance"); - this.m_name = "vengeance"; + this.m_name = _("Vengeance"); + this.netname = "vengeance"; this.m_skin = 15; this.m_color = '1 0.23 0.61'; } BUFF_SPAWNFUNCS(vengeance, BUFF_VENGEANCE) REGISTER_BUFF(JUMP) { - this.m_prettyName = _("Jump"); - this.m_name = "jump"; + this.m_name = _("Jump"); + this.netname = "jump"; this.m_skin = 10; this.m_color = '0.24 0.78 1'; } @@ -92,8 +92,8 @@ BUFF_SPAWNFUNCS(jump, BUFF_JUMP) BUFF_SPAWNFUNC_Q3TA_COMPAT(jumper, BUFF_JUMP) REGISTER_BUFF(INVISIBLE) { - this.m_prettyName = _("Invisible"); - this.m_name = "invisible"; + this.m_name = _("Invisible"); + this.netname = "invisible"; this.m_skin = 12; this.m_color = '0.5 0.5 1'; } @@ -101,40 +101,40 @@ BUFF_SPAWNFUNCS(invisible, BUFF_INVISIBLE) BUFF_SPAWNFUNC_Q3TA_COMPAT(invis, BUFF_INVISIBLE) REGISTER_BUFF(INFERNO) { - this.m_prettyName = _("Inferno"); - this.m_name = "inferno"; + this.m_name = _("Inferno"); + this.netname = "inferno"; this.m_skin = 16; this.m_color = '1 0.62 0'; } BUFF_SPAWNFUNCS(inferno, BUFF_INFERNO) REGISTER_BUFF(SWAPPER) { - this.m_prettyName = _("Swapper"); - this.m_name = "swapper"; + this.m_name = _("Swapper"); + this.netname = "swapper"; this.m_skin = 17; this.m_color = '0.63 0.36 1'; } BUFF_SPAWNFUNCS(swapper, BUFF_SWAPPER) REGISTER_BUFF(MAGNET) { - this.m_prettyName = _("Magnet"); - this.m_name = "magnet"; + this.m_name = _("Magnet"); + this.netname = "magnet"; this.m_skin = 18; this.m_color = '1 0.95 0.18'; } BUFF_SPAWNFUNCS(magnet, BUFF_MAGNET) REGISTER_BUFF(LUCK) { - this.m_prettyName = _("Luck"); - this.m_name = "luck"; + this.m_name = _("Luck"); + this.netname = "luck"; this.m_skin = 19; this.m_color = '1 0.23 0.44'; } BUFF_SPAWNFUNCS(luck, BUFF_LUCK) REGISTER_BUFF(FLIGHT) { - this.m_prettyName = _("Flight"); - this.m_name = "flight"; + this.m_name = _("Flight"); + this.netname = "flight"; this.m_skin = 11; this.m_color = '0.23 0.44 1'; } diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qc b/qcsrc/common/mutators/mutator/buffs/buffs.qc index 8ef69aad98..4a5dcc82aa 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qc @@ -3,7 +3,7 @@ string BUFF_NAME(int i) { Buff b = Buffs_from(i); - return strcat(rgb_to_hexcolor(b.m_color), b.m_prettyName); + return strcat(rgb_to_hexcolor(b.m_color), b.m_name); } entity buff_FirstFromFlags(int _buffs) diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh index d6cf1ca82e..7d4e583675 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qh @@ -20,13 +20,13 @@ REGISTRY_CHECK(Buffs) CLASS(Buff, Pickup) /** bit index */ ATTRIB(Buff, m_itemid, int, 0); - ATTRIB(Buff, m_name, string, "buff"); + ATTRIB(Buff, netname, string, "buff"); ATTRIB(Buff, m_color, vector, '1 1 1'); - ATTRIB(Buff, m_prettyName, string, "Buff"); + ATTRIB(Buff, m_name, string, "Buff"); ATTRIB(Buff, m_skin, int, 0); ATTRIB(Buff, m_sprite, string, ""); METHOD(Buff, display, void(entity this, void(string name, string icon) returns)) { - returns(this.m_prettyName, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.m_name)); + returns(this.m_name, sprintf("/gfx/hud/%s/buff_%s", cvar_string("menu_skin"), this.netname)); } #ifdef SVQC METHOD(Buff, m_time, float(Buff this)) @@ -36,9 +36,8 @@ ENDCLASS(Buff) STATIC_INIT(REGISTER_BUFFS) { FOREACH(Buffs, true, { - it.netname = it.m_name; \ it.m_itemid = BIT(it.m_id - 1); \ - it.m_sprite = strzone(strcat("buff-", it.m_name)); \ + it.m_sprite = strzone(strcat("buff-", it.netname)); \ }); } @@ -64,6 +63,7 @@ STATIC_INIT(REGISTER_BUFFS) { #endif string Buff_UndeprecateName(string buffname); +entity buff_FirstFromFlags(int _buffs); REGISTER_BUFF(Null); BUFF_SPAWNFUNCS(random, BUFF_Null) diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc index f751eecff0..790a10a86b 100644 --- a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc @@ -5,7 +5,7 @@ MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) { int allBuffs = STAT(BUFFS); FOREACH(Buffs, it.m_itemid & allBuffs, { - addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); + addPowerupItem(it.m_name, strcat("buff_", it.netname), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); }); } MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) @@ -16,8 +16,8 @@ MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) { Buff b = Buffs_from(this.wp_extra); M_ARGV(2, vector) = b.m_color; - M_ARGV(3, string) = b.m_prettyName; - M_ARGV(4, string) = strcat("buff_", b.m_name); + M_ARGV(3, string) = b.m_name; + M_ARGV(4, string) = strcat("buff_", b.netname); return true; } } diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc index 937f4cbde4..9338c986c9 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -1,6 +1,6 @@ #include "sv_buffs.qh" -#include <common/triggers/target/music.qh> +#include <common/mapobjects/target/music.qh> #include <common/gamemodes/_mod.qh> void buffs_DelayedInit(entity this); @@ -210,7 +210,7 @@ float buff_Available(entity buff) return false; if (buff == BUFF_VAMPIRE && cvar("g_vampire")) return false; - return cvar(strcat("g_buffs_", buff.m_name)); + return cvar(strcat("g_buffs_", buff.netname)); } .int buff_seencount; @@ -423,7 +423,7 @@ void buff_Medic_Heal(entity this) { FOREACH_CLIENT(IS_PLAYER(it) && it != this && vdist(it.origin - this.origin, <=, autocvar_g_buffs_medic_heal_range), { - if (!SAME_TEAM(it, this)) + if (DIFF_TEAM(it, this)) { continue; } @@ -544,7 +544,7 @@ MUTATOR_HOOKFUNCTION(buffs, Damage_Calculate) float amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, GetResourceAmount(frag_target, RESOURCE_HEALTH)); GiveResourceWithLimit(frag_attacker, RESOURCE_HEALTH, amount, g_pickup_healthsmall_max); - if (frag_target.armorvalue) + if (GetResourceAmount(frag_target, RESOURCE_ARMOR)) { amount = bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, GetResourceAmount(frag_target, RESOURCE_ARMOR)); @@ -926,7 +926,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) BUFF_ONADD(BUFF_INVISIBLE) { - if(time < player.strength_finished && g_instagib) + if(time < player.strength_finished && MUTATOR_IS_ENABLED(mutator_instagib)) player.buff_invisible_prev_alpha = default_player_alpha; // we don't want to save the powerup's alpha, as player may lose the powerup while holding the buff else player.buff_invisible_prev_alpha = player.alpha; @@ -935,7 +935,7 @@ MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) BUFF_ONREM(BUFF_INVISIBLE) { - if(time < player.strength_finished && g_instagib) + if(time < player.strength_finished && MUTATOR_IS_ENABLED(mutator_instagib)) player.alpha = autocvar_g_instagib_invis_alpha; else player.alpha = player.buff_invisible_prev_alpha; diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh index 4d9f107af7..671a524f9e 100644 --- a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh @@ -14,7 +14,7 @@ bool autocvar_g_buffs_random_location; int autocvar_g_buffs_random_location_attempts; int autocvar_g_buffs_spawn_count; bool autocvar_g_buffs_replace_powerups; -bool autocvar_g_buffs_drop = true; +bool autocvar_g_buffs_drop = false; float autocvar_g_buffs_cooldown_activate; float autocvar_g_buffs_cooldown_respawn; float autocvar_g_buffs_resistance_blockpercent; diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc index a1fe27a877..5f9de3b7cb 100644 --- a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc @@ -1,7 +1,7 @@ #include "sv_cloaked.qh" -string autocvar_g_cloaked; -REGISTER_MUTATOR(cloaked, expr_evaluate(autocvar_g_cloaked)); +//string autocvar_g_cloaked; +REGISTER_MUTATOR(cloaked, expr_evaluate(cvar_string("g_cloaked"))); float autocvar_g_balance_cloaked_alpha; diff --git a/qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qc index a26ecdfa96..c6cc40effe 100644 --- a/qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qc +++ b/qcsrc/common/mutators/mutator/damagetext/cl_damagetext.qc @@ -161,8 +161,7 @@ CLASS(DamageText, Object) ); } - if (this.text) strunzone(this.text); - this.text = strzone(s); + strcpy(this.text, s); this.m_size = map_bound_ranges(potential, autocvar_cl_damagetext_size_min_damage, autocvar_cl_damagetext_size_max_damage, @@ -186,7 +185,7 @@ CLASS(DamageText, Object) } DESTRUCTOR(DamageText) { - if (this.text) strunzone(this.text); + strfree(this.text); if (this == DamageText_screen_first) { // start from 0 offset again, hopefully, others (if any) will have faded away by now DamageText_screen_first = NULL; diff --git a/qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc index 4a18cc9308..0e0aa13fd0 100644 --- a/qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc +++ b/qcsrc/common/mutators/mutator/damagetext/sv_damagetext.qc @@ -4,18 +4,19 @@ AUTOCVAR(sv_damagetext, int, 2, "<= 0: disabled, >= 1: visible to spectators, >= REGISTER_MUTATOR(damagetext, true); -#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0 || autocvar_g_instagib) +#define SV_DAMAGETEXT_DISABLED() (autocvar_sv_damagetext <= 0) #define SV_DAMAGETEXT_SPECTATORS_ONLY() (autocvar_sv_damagetext >= 1) #define SV_DAMAGETEXT_PLAYERS() (autocvar_sv_damagetext >= 2) #define SV_DAMAGETEXT_ALL() (autocvar_sv_damagetext >= 3) MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) { if (SV_DAMAGETEXT_DISABLED()) return; - const entity attacker = M_ARGV(0, entity); - const entity hit = M_ARGV(1, entity); if (hit == attacker) return; - const float health = M_ARGV(2, float); - const float armor = M_ARGV(3, float); - const int deathtype = M_ARGV(5, int); - const float potential_damage = M_ARGV(6, float); + entity attacker = M_ARGV(0, entity); + entity hit = M_ARGV(1, entity); if (hit == attacker) return; + float health = M_ARGV(2, float); + float armor = M_ARGV(3, float); + int deathtype = M_ARGV(5, int); + float potential_damage = M_ARGV(6, float); + if(DEATH_WEAPONOF(deathtype) == WEP_VAPORIZER) return; FOREACH_CLIENT(IS_REAL_CLIENT(it), { if ( (SV_DAMAGETEXT_ALL()) || diff --git a/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc b/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc index d5d3ba40f8..4cfc0dd237 100644 --- a/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc +++ b/qcsrc/common/mutators/mutator/dynamic_handicap/sv_dynamic_handicap.qc @@ -32,13 +32,13 @@ float DynamicHandicap_ClampHandicap(float handicap); void DynamicHandicap_UpdateHandicap() { float total_score = 0; - float total_players = 0; + float totalplayers = 0; FOREACH_CLIENT(IS_PLAYER(it), { total_score += PlayerScore_Get(it, SP_SCORE); - ++total_players; + ++totalplayers; }); - float mean_score = total_score / total_players; + float mean_score = total_score / totalplayers; FOREACH_CLIENT(true, { float score = PlayerScore_Get(it, SP_SCORE); diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.qh b/qcsrc/common/mutators/mutator/instagib/_mod.qh index 7097eaf390..9989d8e3f3 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.qh +++ b/qcsrc/common/mutators/mutator/instagib/_mod.qh @@ -1,5 +1,8 @@ // generated file; do not modify #include <common/mutators/mutator/instagib/items.qh> +#ifdef SVQC + #include <common/mutators/mutator/instagib/sv_items.qh> +#endif #ifdef SVQC #include <common/mutators/mutator/instagib/sv_instagib.qh> #endif diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh index fe0070afcb..42e3adbd73 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qh +++ b/qcsrc/common/mutators/mutator/instagib/items.qh @@ -16,21 +16,21 @@ SOUND(VaporizerCells, Item_Sound("itempickup")); #ifdef SVQC int autocvar_g_instagib_ammo_drop; -void ammo_vaporizercells_init(entity item) +void ammo_vaporizercells_init(Pickup this, entity item) { - if(!item.ammo_cells) - item.ammo_cells = autocvar_g_instagib_ammo_drop; + if(!GetResourceAmount(item, RESOURCE_CELLS)) + SetResourceAmountExplicit(item, RESOURCE_CELLS, autocvar_g_instagib_ammo_drop); } #endif REGISTER_ITEM(VaporizerCells, Ammo) { this.m_canonical_spawnfunc = "item_vaporizer_cells"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_INSTAGIB | ITEM_FLAG_MUTATORBLOCKED; + this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; this.m_model = MDL_VaporizerCells_ITEM; this.m_sound = SND_VaporizerCells; #endif this.netname = "vaporizer_cells"; - this.m_name = "Vaporizer Ammo"; + this.m_name = _("Vaporizer ammo"); this.m_icon = "ammo_supercells"; #ifdef SVQC this.m_botvalue = 2000; @@ -52,12 +52,11 @@ SOUND(ExtraLife, Item_Sound("megahealth")); 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_model = MDL_ExtraLife_ITEM; this.m_sound = SND_ExtraLife; #endif this.netname = "extralife"; - this.m_name = "Extra life"; + this.m_name = _("Extra life"); this.m_icon = "item_mega_health"; this.m_color = '1 0 0'; this.m_waypoint = _("Extra life"); @@ -76,20 +75,20 @@ SOUND(Invisibility, Item_Sound("powerup")); /// \brief Initializes the invisibility powerup. /// \param[in,out] item Item to initialize. /// \return No return. -void powerup_invisibility_init(entity item); +void powerup_invisibility_init(Pickup this, entity item); #endif REGISTER_ITEM(Invisibility, Powerup) { this.m_canonical_spawnfunc = "item_invisibility"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_INSTAGIB | ITEM_FLAG_MUTATORBLOCKED; + this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; this.m_model = MDL_Invisibility_ITEM; this.m_sound = SND_Invisibility; this.m_glow = true; this.m_respawnsound = SND_STRENGTH_RESPAWN; #endif this.netname = "invisibility"; - this.m_name = "Invisibility"; + this.m_name = _("Invisibility"); this.m_icon = "strength"; this.m_color = '0 0 1'; this.m_waypoint = _("Invisibility"); @@ -111,20 +110,20 @@ SOUND(Speed, Item_Sound("powerup_shield")); /// \brief Initializes the speed powerup. /// \param[in,out] item Item to initialize. /// \return No return. -void powerup_speed_init(entity item); +void powerup_speed_init(Pickup this, entity item); #endif REGISTER_ITEM(Speed, Powerup) { this.m_canonical_spawnfunc = "item_speed"; #ifdef GAMEQC - this.spawnflags = ITEM_FLAG_INSTAGIB | ITEM_FLAG_MUTATORBLOCKED; + this.spawnflags = ITEM_FLAG_MUTATORBLOCKED; this.m_model = MDL_Speed_ITEM; this.m_sound = SND_Speed; this.m_glow = true; this.m_respawnsound = SND_SHIELD_RESPAWN; #endif this.netname = "speed"; - this.m_name = "Speed"; + this.m_name = _("Speed"); this.m_icon = "shield"; this.m_color = '1 0 1'; this.m_waypoint = _("Speed"); diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc index 616a05da3c..3cda4485f1 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -1,5 +1,9 @@ #include "sv_instagib.qh" +#include <server/client.qh> +#include <common/items/_mod.qh> +#include "../random_items/sv_random_items.qh" + bool autocvar_g_instagib_damagedbycontents = true; bool autocvar_g_instagib_blaster_keepdamage = false; bool autocvar_g_instagib_blaster_keepforce = false; @@ -13,24 +17,14 @@ bool autocvar_g_instagib_ammo_convert_bullets; int autocvar_g_instagib_extralives; float autocvar_g_instagib_speed_highspeed; -#include <server/client.qh> - -#include <common/items/_mod.qh> - -REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball) +IntrusiveList g_instagib_items; +STATIC_INIT() { - MUTATOR_ONADD - { - ITEM_VaporizerCells.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; - ITEM_Invisibility.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; - ITEM_Speed.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; - } - MUTATOR_ONROLLBACK_OR_REMOVE - { - ITEM_VaporizerCells.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; - ITEM_Invisibility.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; - ITEM_Speed.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; - } + g_instagib_items = IL_NEW(); + IL_PUSH(g_instagib_items, ITEM_VaporizerCells); + IL_PUSH(g_instagib_items, ITEM_ExtraLife); + IL_PUSH(g_instagib_items, ITEM_Invisibility); + IL_PUSH(g_instagib_items, ITEM_Speed); } void instagib_invisibility(entity this) @@ -50,6 +44,26 @@ void instagib_speed(entity this) StartItem(this, ITEM_Speed); } +/// \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) +{ + RandomSelection_Init(); + IL_EACH(g_instagib_items, Item_IsDefinitionAllowed(it), + { + 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; +} + .float instagib_nextthink; .float instagib_needammo; void instagib_stop_countdown(entity e) @@ -147,6 +161,13 @@ MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) FOREACH_CLIENT(IS_PLAYER(it), { instagib_stop_countdown(it); }); } +MUTATOR_HOOKFUNCTION(mutator_instagib, RandomItems_GetRandomItemClassName) +{ + M_ARGV(1, string) = RandomItems_GetRandomInstagibItemClassName( + M_ARGV(0, string)); + return true; +} + MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) { entity item = M_ARGV(1, entity); diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh index 9020b93124..56f4ac7daa 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh @@ -7,3 +7,19 @@ float autocvar_g_instagib_invis_alpha; void instagib_invisibility(entity this); void instagib_extralife(entity this); void instagib_speed(entity this); + +REGISTER_MUTATOR(mutator_instagib, autocvar_g_instagib && !g_nexball) +{ + MUTATOR_ONADD + { + ITEM_VaporizerCells.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; + ITEM_Invisibility.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; + ITEM_Speed.spawnflags &= ~ITEM_FLAG_MUTATORBLOCKED; + } + MUTATOR_ONROLLBACK_OR_REMOVE + { + ITEM_VaporizerCells.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; + ITEM_Invisibility.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; + ITEM_Speed.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; + } +} diff --git a/qcsrc/common/mutators/mutator/instagib/sv_items.qc b/qcsrc/common/mutators/mutator/instagib/sv_items.qc index ffd9bfb0ca..c944f56c33 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_items.qc +++ b/qcsrc/common/mutators/mutator/instagib/sv_items.qc @@ -1,3 +1,5 @@ +#include "sv_items.qh" + #include "items.qh" /// \brief Time of ivisibility powerup in seconds. @@ -5,7 +7,7 @@ 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) +void powerup_invisibility_init(Pickup this, entity item) { if(!item.strength_finished) { @@ -14,7 +16,7 @@ void powerup_invisibility_init(entity item) } -void powerup_speed_init(entity item) +void powerup_speed_init(Pickup this, entity item) { if(!item.invincible_finished) { diff --git a/qcsrc/common/mutators/mutator/instagib/sv_items.qh b/qcsrc/common/mutators/mutator/instagib/sv_items.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/sv_items.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc index e68c687bde..443fe24781 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc @@ -7,10 +7,10 @@ MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) { entity proj = M_ARGV(1, entity); - if(proj.health) + if(GetResourceAmount(proj, RESOURCE_HEALTH)) { // disable health which in effect disables damage calculations - proj.health = 0; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, 0); } } diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc index c2402ef460..1379d586f1 100644 --- a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc @@ -124,7 +124,7 @@ void Item_ItemsTime_SetTime(entity e, float t) { if (!item.instanceOfWeaponPickup) it_times[item.m_id] = t; - else if (e.weapons & WEPSET_SUPERWEAPONS) + else if (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) it_times[Items_MAX] = t; } } @@ -139,7 +139,7 @@ float Item_ItemsTime_UpdateTime(entity e, float t) bool isavailable = (t == 0); IL_EACH(g_items, it != e, { - if(!(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS)))) + if(!(it.itemdef == e.itemdef || ((STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) && (STAT(WEAPONS, it) & WEPSET_SUPERWEAPONS)))) continue; if (it.scheduledrespawntime <= time) isavailable = true; diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh index 98fb4815c1..0144c3147c 100644 --- a/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh +++ b/qcsrc/common/mutators/mutator/kick_teamkiller/_mod.qh @@ -1 +1,4 @@ // generated file; do not modify +#ifdef SVQC + #include <common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qh> +#endif diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc index a3b028f1c6..d91546af57 100644 --- a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc +++ b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qc @@ -1,3 +1,4 @@ +#include "sv_kick_teamkiller.qh" float autocvar_g_kick_teamkiller_rate; float autocvar_g_kick_teamkiller_lower_limit; @@ -24,7 +25,7 @@ MUTATOR_HOOKFUNCTION(kick_teamkiller, PlayerDies) // 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 && + 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); diff --git a/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qh b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/kick_teamkiller/sv_kick_teamkiller.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc index d6796fc059..a56efaf83c 100644 --- a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc @@ -1,7 +1,9 @@ #include "sv_melee_only.qh" +#include "../overkill/sv_overkill.qh" + string autocvar_g_melee_only; -REGISTER_MUTATOR(melee_only, expr_evaluate(autocvar_g_melee_only) && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball); +REGISTER_MUTATOR(melee_only, expr_evaluate(autocvar_g_melee_only) && !MUTATOR_IS_ENABLED(mutator_instagib) && !MUTATOR_IS_ENABLED(ok) && !g_nexball); MUTATOR_HOOKFUNCTION(melee_only, SetStartItems, CBC_ORDER_LAST) { @@ -25,11 +27,11 @@ MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) return true; } -MUTATOR_HOOKFUNCTION(melee_only, FilterItem) +MUTATOR_HOOKFUNCTION(melee_only, FilterItemDefinition) { - entity item = M_ARGV(0, entity); + entity definition = M_ARGV(0, entity); - switch (item.itemdef) + switch (definition) { case ITEM_HealthSmall: case ITEM_ArmorSmall: diff --git a/qcsrc/common/mutators/mutator/nades/effects.inc b/qcsrc/common/mutators/mutator/nades/effects.inc index cde0016f1b..83cf74247f 100644 --- a/qcsrc/common/mutators/mutator/nades/effects.inc +++ b/qcsrc/common/mutators/mutator/nades/effects.inc @@ -1,3 +1,5 @@ +#include <common/effects/all.qh> + EFFECT(0, NADE_EXPLODE_RED, "nade_red_explode") EFFECT(0, NADE_EXPLODE_BLUE, "nade_blue_explode") EFFECT(0, NADE_EXPLODE_YELLOW, "nade_yellow_explode") diff --git a/qcsrc/common/mutators/mutator/nades/nades.inc b/qcsrc/common/mutators/mutator/nades/nades.inc index bcdbe0cd92..0245cee640 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.inc +++ b/qcsrc/common/mutators/mutator/nades/nades.inc @@ -68,3 +68,12 @@ REGISTER_NADE(ENTRAP) { NADE_PROJECTILE(0, PROJECTILE_NADE_ENTRAP, EFFECT_NADE_TRAIL_YELLOW); NADE_PROJECTILE(1, PROJECTILE_NADE_ENTRAP_BURN, EFFECT_NADE_TRAIL_BURN_YELLOW); } + +REGISTER_NADE(VEIL) { + this.m_color = '0.65 0.85 0.65'; + this.m_name = _("Veil grenade"); + this.m_icon = "nade_veil"; + this.m_alpha = 0.45; + NADE_PROJECTILE(0, PROJECTILE_NADE_VEIL, EFFECT_NADE_TRAIL_NEUTRAL); + NADE_PROJECTILE(1, PROJECTILE_NADE_VEIL_BURN, EFFECT_NADE_TRAIL_BURN_NEUTRAL); +} diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 24149ad7fc..43de3b8cdc 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -1,5 +1,7 @@ #include "nades.qh" +#include "../overkill/okmachinegun.qh" + #ifdef SVQC bool autocvar_g_nades_nade_small; float autocvar_g_nades_spread = 0.04; @@ -36,6 +38,7 @@ entity Nade_TrailEffect(int proj, int nade_team) REGISTER_MUTATOR(cl_nades, true); MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay) { + // TODO: make a common orb state! if (STAT(HEALING_ORB) > time) { M_ARGV(0, vector) = NADE_TYPE_HEAL.m_color; @@ -48,6 +51,12 @@ MUTATOR_HOOKFUNCTION(cl_nades, HUD_Draw_overlay) M_ARGV(1, float) = STAT(ENTRAP_ORB_ALPHA); return true; } + if (STAT(VEIL_ORB) > time) + { + M_ARGV(0, vector) = NADE_TYPE_VEIL.m_color; + M_ARGV(1, float) = STAT(VEIL_ORB_ALPHA); + return true; + } return false; } MUTATOR_HOOKFUNCTION(cl_nades, Ent_Projectile) @@ -96,6 +105,7 @@ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile) settouch(proj, func_null); proj.scale = 1.5; proj.avelocity = randomvec() * 720; + proj.alphamod = nade_type.m_alpha; if (nade_type == NADE_TYPE_TRANSLOCATE || nade_type == NADE_TYPE_SPAWN) proj.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; @@ -148,7 +158,6 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan #include <common/gamemodes/_mod.qh> #include <common/monsters/sv_spawn.qh> #include <common/monsters/sv_monsters.qh> -#include <server/g_subs.qh> REGISTER_MUTATOR(nades, autocvar_g_nades); @@ -374,7 +383,7 @@ void nade_ice_freeze(entity freezefield, entity frost_target, float freezetime) { frost_target.frozen_by = freezefield.realowner; Send_Effect(EFFECT_ELECTRO_IMPACT, frost_target.origin, '0 0 0', 1); - Freeze(frost_target, 1 / freezetime, 3, false); + Freeze(frost_target, 1 / freezetime, FROZEN_TEMP_DYING, false); Drop_Special_Items(frost_target); } @@ -665,6 +674,35 @@ void nade_monster_boom(entity this) e.monster_skill = MONSTER_SKILL_INSANE; } +void nade_veil_touch(entity this, entity toucher) +{ + if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) ) + { + entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; + + float tint_alpha = 0.75; + if(SAME_TEAM(toucher, this.realowner)) + { + tint_alpha = 0.45; + if(!STAT(VEIL_ORB, show_tint)) + { + toucher.nade_veil_prevalpha = toucher.alpha; + toucher.alpha = -1; + } + } + STAT(VEIL_ORB, show_tint) = time + 0.1; + STAT(VEIL_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime; + } +} + +void nade_veil_boom(entity this) +{ + entity orb = nades_spawn_orb(this.owner, this.realowner, this.origin, autocvar_g_nades_veil_time, autocvar_g_nades_veil_radius); + + settouch(orb, nade_veil_touch); + orb.colormod = NADE_TYPE_VEIL.m_color; +} + void nade_boom(entity this) { entity expef = NULL; @@ -705,6 +743,11 @@ void nade_boom(entity this) expef = EFFECT_SPAWN_YELLOW; break; + case NADE_TYPE_VEIL: + nade_blast = false; + expef = EFFECT_SPAWN_NEUTRAL; + break; + default: case NADE_TYPE_NORMAL: expef = EFFECT_NADE_EXPLODE(this.realowner.team); @@ -736,6 +779,7 @@ void nade_boom(entity this) case NADE_TYPE_HEAL: nade_heal_boom(this); break; case NADE_TYPE_MONSTER: nade_monster_boom(this); break; case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break; + case NADE_TYPE_VEIL: nade_veil_boom(this); break; } IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, @@ -838,12 +882,12 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i force *= 0.5; // too much damage = 0; } - else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + else if(DEATH_ISWEAPON(deathtype, WEP_VORTEX) || DEATH_ISWEAPON(deathtype, WEP_VAPORIZER) || DEATH_ISWEAPON(deathtype, WEP_OVERKILL_NEX)) { force *= 6; damage = this.max_health * 0.55; } - else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN) || DEATH_ISWEAPON(deathtype, WEP_OVERKILL_MACHINEGUN)) damage = this.max_health * 0.1; else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO { @@ -934,7 +978,7 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time) settouch(_nade, nade_touch); _nade.spawnshieldtime = time + 0.1; // prevent instantly picking up again SetResourceAmount(_nade, RESOURCE_HEALTH, autocvar_g_nades_nade_health); - _nade.max_health = _nade.health; + _nade.max_health = GetResourceAmount(_nade, RESOURCE_HEALTH); _nade.takedamage = DAMAGE_AIM; _nade.event_damage = nade_damage; setcefc(_nade, func_null); @@ -977,7 +1021,7 @@ void nades_GiveBonus(entity player, float score) if (autocvar_g_nades_bonus) if (IS_REAL_CLIENT(player)) if (IS_PLAYER(player) && STAT(NADE_BONUS, player) < autocvar_g_nades_bonus_max) - if (STAT(FROZEN, player) == 0) + if (!STAT(FROZEN, player)) if (!IS_DEAD(player)) { if ( STAT(NADE_BONUS_SCORE, player) < 1 ) @@ -1056,9 +1100,11 @@ void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, strin n.projectiledeathtype = DEATH_NADE.m_id; n.weaponentity_fld = weaponentity; n.nade_lifetime = ntime; + n.alpha = Nades_from(STAT(NADE_BONUS_TYPE, n)).m_alpha; setmodel(fn, MDL_NADE_VIEW); - setattachment(fn, player.(weaponentity), ""); + //setattachment(fn, player.(weaponentity), ""); + fn.viewmodelforclient = player; fn.realowner = fn.owner = player; fn.colormod = Nades_from(STAT(NADE_BONUS_TYPE, n)).m_color; fn.colormap = player.colormap; @@ -1066,6 +1112,7 @@ void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, strin setthink(fn, SUB_Remove); fn.nextthink = n.wait; fn.weaponentity_fld = weaponentity; + fn.alpha = Nades_from(STAT(NADE_BONUS_TYPE, n)).m_alpha; player.nade = n; player.fake_nade = fn; @@ -1206,19 +1253,28 @@ MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) { entity player = M_ARGV(0, entity); - if (player.offhand != OFFHAND_NADE || (player.weapons & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { + if (player.offhand != OFFHAND_NADE || (STAT(WEAPONS, player) & WEPSET(HOOK)) || autocvar_g_nades_override_dropweapon) { nades_CheckThrow(player); return true; } } +#ifdef IS_REVIVING + #undef IS_REVIVING +#endif + +// returns true if player is reviving it +#define IS_REVIVING(player, it, revive_extra_size) \ + (it != player && !STAT(FROZEN, it) && !IS_DEAD(it) && SAME_TEAM(it, player) \ + && boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax)) + MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) { entity player = M_ARGV(0, entity); if (!IS_PLAYER(player)) { return; } - if (player.nade && (player.offhand != OFFHAND_NADE || (player.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, player, player.nade_altbutton); + if (player.nade && (player.offhand != OFFHAND_NADE || (STAT(WEAPONS, player) & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, player, player.nade_altbutton); entity held_nade = player.nade; if (held_nade) @@ -1271,46 +1327,55 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) { STAT(NADE_BONUS, player) = STAT(NADE_BONUS_SCORE, player) = 0; } + + if(STAT(VEIL_ORB, player) && STAT(VEIL_ORB, player) <= time) + { + STAT(VEIL_ORB, player) = 0; + if(player.vehicle) + player.vehicle.alpha = player.vehicle.nade_veil_prevalpha; + else + player.alpha = player.nade_veil_prevalpha; + } } int n = 0; - entity o = NULL; + + IntrusiveList reviving_players = NULL; + if(player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout) n = -1; - else if(STAT(FROZEN, player) == 3) + else if (STAT(FROZEN, player) == FROZEN_TEMP_DYING) { vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; n = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it != player, { - if(!IS_DEAD(it) && STAT(FROZEN, it) == 0 && SAME_TEAM(it, player)) - if(boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax)) - { - if(!o) - o = it; - it.reviving = true; - ++n; - } + FOREACH_CLIENT(IS_PLAYER(it) && IS_REVIVING(player, it, revive_extra_size), { + if (!reviving_players) + reviving_players = IL_NEW(); + IL_PUSH(reviving_players, it); + ++n; }); } - if(n > 0 && STAT(FROZEN, player) == 3) // OK, there is at least one teammate reviving us + if (n > 0 && STAT(FROZEN, player) == FROZEN_TEMP_DYING) // OK, there is at least one teammate reviving us { STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); SetResourceAmount(player, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, player) * start_health)); if(STAT(REVIVE_PROGRESS, player) >= 1) { - Unfreeze(player); + Unfreeze(player, false); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); - Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); + entity first = IL_FIRST(reviving_players); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, first.netname); + Send_Notification(NOTIF_ONE, first, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); } - FOREACH_CLIENT(IS_PLAYER(it) && it.reviving, { + IL_EACH(reviving_players, true, { STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player); - it.reviving = false; }); } + if (reviving_players) + IL_DELETE(reviving_players); } MUTATOR_HOOKFUNCTION(nades, PlayerPhysics_UpdateStats) @@ -1331,6 +1396,12 @@ MUTATOR_HOOKFUNCTION(nades, MonsterMove) M_ARGV(1, float) *= autocvar_g_nades_entrap_speed; // run speed M_ARGV(2, float) *= autocvar_g_nades_entrap_speed; // walk speed } + + if (STAT(VEIL_ORB, mon) && STAT(VEIL_ORB, mon) <= time) + { + mon.alpha = mon.nade_veil_prevalpha; + STAT(VEIL_ORB, mon) = 0; + } } MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) @@ -1404,13 +1475,10 @@ MUTATOR_HOOKFUNCTION(nades, Damage_Calculate) entity frag_target = M_ARGV(2, entity); float frag_deathtype = M_ARGV(3, float); - if(STAT(FROZEN, frag_target)) - if(autocvar_g_freezetag_revive_nade) - if(frag_attacker == frag_target) - if(frag_deathtype == DEATH_NADE.m_id) + if(autocvar_g_freezetag_revive_nade && STAT(FROZEN, frag_target) && frag_attacker == frag_target && frag_deathtype == DEATH_NADE.m_id) if(time - frag_inflictor.toss_time <= 0.1) { - Unfreeze(frag_target); + Unfreeze(frag_target, false); SetResourceAmount(frag_target, RESOURCE_HEALTH, autocvar_g_freezetag_revive_nade_health); Send_Effect(EFFECT_ICEORGLASS, frag_target.origin, '0 0 0', 3); M_ARGV(4, float) = 0; @@ -1469,6 +1537,8 @@ MUTATOR_HOOKFUNCTION(nades, SpectateCopy) STAT(HEALING_ORB_ALPHA, client) = STAT(HEALING_ORB_ALPHA, spectatee); STAT(ENTRAP_ORB, client) = STAT(ENTRAP_ORB, spectatee); STAT(ENTRAP_ORB_ALPHA, client) = STAT(ENTRAP_ORB_ALPHA, spectatee); + STAT(VEIL_ORB, client) = STAT(VEIL_ORB, spectatee); + STAT(VEIL_ORB_ALPHA, client) = STAT(VEIL_ORB_ALPHA, spectatee); } REPLICATE(cvar_cl_nade_type, int, "cl_nade_type"); diff --git a/qcsrc/common/mutators/mutator/nades/nades.qh b/qcsrc/common/mutators/mutator/nades/nades.qh index c5a1967e0b..2729316a88 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qh +++ b/qcsrc/common/mutators/mutator/nades/nades.qh @@ -18,6 +18,8 @@ const int PROJECTILE_NADE_MONSTER = 82; const int PROJECTILE_NADE_MONSTER_BURN = 83; const int PROJECTILE_NADE_ENTRAP = 84; const int PROJECTILE_NADE_ENTRAP_BURN = 85; +const int PROJECTILE_NADE_VEIL = 86; +const int PROJECTILE_NADE_VEIL_BURN = 87; REGISTRY(Nades, BITS(4)) #define Nades_from(i) _Nades_from(i, NADE_TYPE_Null) @@ -31,6 +33,7 @@ CLASS(Nade, Object) ATTRIB(Nade, m_color, vector, '0 0 0'); ATTRIB(Nade, m_name, string, _("Grenade")); ATTRIB(Nade, m_icon, string, "nade_normal"); + ATTRIB(Nade, m_alpha, float, 1); ATTRIBARRAY(Nade, m_projectile, int, 2); ATTRIBARRAY(Nade, m_trail, entity, 2); METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) { @@ -72,6 +75,7 @@ Nade Nade_FromProjectile(int proj) .string cvar_cl_pokenade_type; .float toss_time; .float nade_show_particles; +.float nade_veil_prevalpha; bool orb_send(entity this, entity to, int sf); @@ -95,3 +99,9 @@ void nades_GiveBonus(entity player, float score); MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); #endif + +#ifdef CSQC +bool Projectile_isnade(int proj); // TODO: remove + +void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expand_time); // TODO: mutator +#endif diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc index a691b866f7..1fdf5fd7aa 100644 --- a/qcsrc/common/mutators/mutator/nades/net.qc +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -32,7 +32,7 @@ void orb_setup(entity e) e.draw = orb_draw; IL_PUSH(g_drawables, e); - e.health = 255; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, 255); set_movetype(e, MOVETYPE_NONE); e.solid = SOLID_NOT; e.drawmask = MASK_NORMAL; diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc index ec2593215a..37dac8f931 100644 --- a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc @@ -70,11 +70,11 @@ roflsound "New toys, new toys!" sound. */ -string autocvar_g_new_toys; +//string autocvar_g_new_toys; bool nt_IsNewToy(int w); -REGISTER_MUTATOR(nt, expr_evaluate(autocvar_g_new_toys) && !cvar("g_instagib") && !cvar("g_overkill")) +REGISTER_MUTATOR(nt, expr_evaluate(cvar_string("g_new_toys")) && !MUTATOR_IS_ENABLED(mutator_instagib) && !MUTATOR_IS_ENABLED(ok)) { MUTATOR_ONADD { @@ -108,7 +108,7 @@ REGISTER_MUTATOR(nt, expr_evaluate(autocvar_g_new_toys) && !cvar("g_instagib") & .string new_toys; float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; +bool autocvar_g_new_toys_use_pickupsound = false; const float NT_AUTOREPLACE_NEVER = 0; const float NT_AUTOREPLACE_ALWAYS = 1; const float NT_AUTOREPLACE_RANDOM = 2; @@ -195,7 +195,7 @@ MUTATOR_HOOKFUNCTION(nt, SetStartItems) MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) { - if (autocvar_g_random_items) + if (MUTATOR_IS_ENABLED(random_items)) { // Do not replace weapons when random items are enabled. return; diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc index c174b530f0..586deda3ef 100644 --- a/qcsrc/common/mutators/mutator/nix/sv_nix.qc +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -1,6 +1,6 @@ #include "sv_nix.qh" -string autocvar_g_nix; +//string autocvar_g_nix; int autocvar_g_balance_nix_ammo_cells; int autocvar_g_balance_nix_ammo_plasma; int autocvar_g_balance_nix_ammo_fuel; @@ -36,7 +36,7 @@ float nix_nextweapon; bool NIX_CanChooseWeapon(int wpn); -REGISTER_MUTATOR(nix, expr_evaluate(autocvar_g_nix) && !cvar("g_instagib") && !cvar("g_overkill")) +REGISTER_MUTATOR(nix, expr_evaluate(cvar_string("g_nix")) && !MUTATOR_IS_ENABLED(mutator_instagib) && !MUTATOR_IS_ENABLED(ok)) { MUTATOR_ONADD { @@ -63,7 +63,7 @@ REGISTER_MUTATOR(nix, expr_evaluate(autocvar_g_nix) && !cvar("g_instagib") && !c SetResourceAmount(it, RESOURCE_CELLS, start_ammo_cells); SetResourceAmount(it, RESOURCE_PLASMA, start_ammo_plasma); SetResourceAmount(it, RESOURCE_FUEL, start_ammo_fuel); - it.weapons = start_weapons; + STAT(WEAPONS, it) = start_weapons; for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { .entity weaponentity = weaponentities[slot]; @@ -130,7 +130,7 @@ void NIX_GiveCurrentWeapon(entity this) } // get weapon info - entity e = Weapons_from(nix_weapon); + entity wpn = Weapons_from(nix_weapon); if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! { @@ -142,7 +142,7 @@ void NIX_GiveCurrentWeapon(entity this) SetResourceAmount(this, RESOURCE_FUEL, 0); if(this.items & IT_UNLIMITED_WEAPON_AMMO) { - switch (e.ammo_type) + switch (wpn.ammo_type) { 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; @@ -154,7 +154,7 @@ void NIX_GiveCurrentWeapon(entity this) } else { - switch (e.ammo_type) + switch (wpn.ammo_type) { 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; @@ -171,15 +171,15 @@ void NIX_GiveCurrentWeapon(entity this) else Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - e.wr_resetplayer(e, this); + wpn.wr_resetplayer(wpn, this); // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + if (wpn.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { .entity weaponentity = weaponentities[slot]; - this.(weaponentity).(weapon_load[nix_weapon]) = e.reloading_ammo; + this.(weaponentity).(weapon_load[nix_weapon]) = wpn.reloading_ammo; } } @@ -195,7 +195,7 @@ void NIX_GiveCurrentWeapon(entity this) if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) { - switch (e.ammo_type) + switch (wpn.ammo_type) { 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; @@ -208,23 +208,22 @@ void NIX_GiveCurrentWeapon(entity this) this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; } - this.weapons = '0 0 0'; + STAT(WEAPONS, this) = '0 0 0'; if(g_nix_with_blaster) - this.weapons |= WEPSET(BLASTER); - this.weapons |= e.m_wepset; - - Weapon w = Weapons_from(nix_weapon); - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(this.(weaponentity).m_weapon == WEP_Null && slot != 0) - continue; - - if(this.(weaponentity).m_switchweapon != w) - if(!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false)) + STAT(WEAPONS, this) |= WEPSET(BLASTER); + STAT(WEAPONS, this) |= wpn.m_wepset; + + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if (this.(weaponentity).m_weapon == WEP_Null && slot != 0) + continue; + + if (this.(weaponentity).m_switchweapon != wpn) + if (!client_hasweapon(this, this.(weaponentity).m_switchweapon, weaponentity, true, false)) { - if(client_hasweapon(this, w, weaponentity, true, false)) - W_SwitchWeapon(this, w, weaponentity); + if (client_hasweapon(this, wpn, weaponentity, true, false)) + W_SwitchWeapon(this, wpn, weaponentity); } } } @@ -244,15 +243,15 @@ MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX"); } -MUTATOR_HOOKFUNCTION(nix, FilterItem) +MUTATOR_HOOKFUNCTION(nix, FilterItemDefinition) { - entity item = M_ARGV(0, entity); + entity definition = M_ARGV(0, entity); - if(item.itemdef.instanceOfHealth || item.itemdef.instanceOfArmor) + if (definition.instanceOfHealth || definition.instanceOfArmor) { return !autocvar_g_nix_with_healtharmor; } - else if(item.itemdef.instanceOfPowerup) + else if (definition.instanceOfPowerup) { return !autocvar_g_nix_with_powerups; } diff --git a/qcsrc/common/mutators/mutator/offhand_blaster/_mod.qh b/qcsrc/common/mutators/mutator/offhand_blaster/_mod.qh index 98fb4815c1..5e11096a53 100644 --- a/qcsrc/common/mutators/mutator/offhand_blaster/_mod.qh +++ b/qcsrc/common/mutators/mutator/offhand_blaster/_mod.qh @@ -1 +1,4 @@ // generated file; do not modify +#ifdef SVQC + #include <common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qh> +#endif diff --git a/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qc b/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qc index b25175aea5..3a10055fe5 100644 --- a/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qc +++ b/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qc @@ -1,3 +1,5 @@ +#include "sv_offhand_blaster.qh" + string autocvar_g_offhand_blaster = "0"; REGISTER_MUTATOR(offhand_blaster, expr_evaluate(autocvar_g_offhand_blaster)); diff --git a/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qh b/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/offhand_blaster/sv_offhand_blaster.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/_mod.inc b/qcsrc/common/mutators/mutator/overkill/_mod.inc index 0552173c1a..017cc2082a 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.inc +++ b/qcsrc/common/mutators/mutator/overkill/_mod.inc @@ -1,10 +1,15 @@ // generated file; do not modify -#include <common/mutators/mutator/overkill/hmg.qc> -#include <common/mutators/mutator/overkill/overkill.qc> #ifdef CSQC #include <common/mutators/mutator/overkill/cl_overkill.qc> #endif #ifdef SVQC #include <common/mutators/mutator/overkill/sv_overkill.qc> #endif -#include <common/mutators/mutator/overkill/rpc.qc> +#include <common/mutators/mutator/overkill/okhmg.qc> +#include <common/mutators/mutator/overkill/okmachinegun.qc> +#include <common/mutators/mutator/overkill/oknex.qc> +#include <common/mutators/mutator/overkill/okrpc.qc> +#include <common/mutators/mutator/overkill/okshotgun.qc> +#ifdef SVQC + #include <common/mutators/mutator/overkill/sv_weapons.qc> +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/_mod.qh b/qcsrc/common/mutators/mutator/overkill/_mod.qh index 13e42431b3..7731f17e16 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.qh +++ b/qcsrc/common/mutators/mutator/overkill/_mod.qh @@ -1,10 +1,12 @@ // generated file; do not modify -#include <common/mutators/mutator/overkill/hmg.qh> -#include <common/mutators/mutator/overkill/overkill.qh> #ifdef CSQC #include <common/mutators/mutator/overkill/cl_overkill.qh> #endif #ifdef SVQC #include <common/mutators/mutator/overkill/sv_overkill.qh> #endif -#include <common/mutators/mutator/overkill/rpc.qh> +#include <common/mutators/mutator/overkill/okhmg.qh> +#include <common/mutators/mutator/overkill/okmachinegun.qh> +#include <common/mutators/mutator/overkill/oknex.qh> +#include <common/mutators/mutator/overkill/okrpc.qh> +#include <common/mutators/mutator/overkill/okshotgun.qh> diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc index eb21953955..4b14194be6 100644 --- a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc @@ -4,8 +4,5 @@ REGISTER_MUTATOR(ok, false) { MUTATOR_ONADD { cvar_settemp("g_overkill", "1"); - WEP_SHOTGUN.mdl = "ok_shotgun"; - WEP_MACHINEGUN.mdl = "ok_mg"; - WEP_VORTEX.mdl = "ok_sniper"; } } diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qc b/qcsrc/common/mutators/mutator/overkill/hmg.qc deleted file mode 100644 index b9e01bd984..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/hmg.qc +++ /dev/null @@ -1,132 +0,0 @@ -#include "hmg.qh" - -#ifdef SVQC - -REGISTER_MUTATOR(hmg_nadesupport, true); -MUTATOR_HOOKFUNCTION(hmg_nadesupport, Nade_Damage) -{ - if (M_ARGV(1, entity) != WEP_HMG) return; - return = true; - M_ARGV(3, float) /* damage */ = (M_ARGV(0, entity)).max_health * 0.1; -} - -void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) -{ - if (!PHYS_INPUT_BUTTON_ATCK(actor)) - { - w_ready(thiswep, actor, weaponentity, fire); - return; - } - - if((!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (!(actor.items & IT_SUPERWEAPON) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS))) - { - W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); - w_ready(thiswep, actor, weaponentity, fire); - return; - } - - W_DecreaseAmmo(WEP_HMG, actor, WEP_CVAR(hmg, ammo), weaponentity); - - W_SetupShot (actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage), WEP_HMG.m_id); - - if(!autocvar_g_norecoil) - { - actor.punchangle_x = random () - 0.5; - actor.punchangle_y = random () - 0.5; - } - - float hmg_spread = bound(WEP_CVAR(hmg, spread_min), WEP_CVAR(hmg, spread_min) + (WEP_CVAR(hmg, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR(hmg, spread_max)); - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, hmg_spread, WEP_CVAR(hmg, solidpenetration), WEP_CVAR(hmg, damage), WEP_CVAR(hmg, force), WEP_HMG.m_id, 0); - - actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; - - Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - - W_MachineGun_MuzzleFlash(actor, weaponentity); - W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); - - if (autocvar_g_casings >= 2) // casing code - { - makevectors(actor.v_angle); // for some reason, this is lost - SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); - } - - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(hmg, refire) * W_WeaponRateFactor(actor); - weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(hmg, refire), W_HeavyMachineGun_Attack_Auto); -} - -METHOD(HeavyMachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) -{ - if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200)) - PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); - else - PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); -} - -METHOD(HeavyMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) -{ - if(WEP_CVAR(hmg, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR(hmg, ammo)) { // forced reload - thiswep.wr_reload(thiswep, actor, weaponentity); - } else - { - if (fire & 1) - if (weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) - { - actor.(weaponentity).misc_bulletcounter = 0; - W_HeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire); - } - } -} - -METHOD(HeavyMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) -{ - 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); - - return ammo_amount; -} - -METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) -{ - 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); - - return ammo_amount; -} - -METHOD(HeavyMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) -{ - W_Reload(actor, weaponentity, WEP_CVAR(hmg, ammo), SND_RELOAD); -} - -METHOD(HeavyMachineGun, wr_suicidemessage, Notification(entity thiswep)) -{ - return WEAPON_THINKING_WITH_PORTALS; -} - -METHOD(HeavyMachineGun, wr_killmessage, Notification(entity thiswep)) -{ - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_HMG_MURDER_SNIPE; - else - return WEAPON_HMG_MURDER_SPRAY; -} - -#endif -#ifdef CSQC - -METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor)) -{ - vector org2; - org2 = w_org + w_backoff * 2; - pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1); - if(!w_issilent) - sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qh b/qcsrc/common/mutators/mutator/overkill/hmg.qh deleted file mode 100644 index 99c8093970..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/hmg.qh +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include <common/weapons/all.qh> - -CLASS(HeavyMachineGun, Weapon) -/* 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); -/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); -#ifdef GAMEQC -/* model */ ATTRIB(HeavyMachineGun, m_model, Model, MDL_HMG_ITEM); -#endif -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); -/* crosshair */ ATTRIB(HeavyMachineGun, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(HeavyMachineGun, model2, string, "weaponhmg"); -/* refname */ ATTRIB(HeavyMachineGun, netname, string, "hmg"); -/* wepname */ ATTRIB(HeavyMachineGun, m_name, string, _("Heavy Machine Gun")); - -#define X(BEGIN, P, END, class, prefix) \ - BEGIN(class) \ - P(class, prefix, ammo, float, NONE) \ - P(class, prefix, damage, float, NONE) \ - P(class, prefix, force, float, NONE) \ - P(class, prefix, refire, float, NONE) \ - P(class, prefix, reload_ammo, float, NONE) \ - P(class, prefix, reload_time, float, NONE) \ - P(class, prefix, solidpenetration, float, NONE) \ - P(class, prefix, spread_add, float, NONE) \ - P(class, prefix, spread_max, float, NONE) \ - P(class, prefix, spread_min, float, NONE) \ - P(class, prefix, switchdelay_drop, float, NONE) \ - P(class, prefix, switchdelay_raise, float, NONE) \ - P(class, prefix, weaponreplace, string, NONE) \ - P(class, prefix, weaponstartoverride, float, NONE) \ - P(class, prefix, weaponstart, float, NONE) \ - P(class, prefix, weaponthrowable, float, NONE) \ - END() - W_PROPS(X, HeavyMachineGun, hmg) -#undef X - -ENDCLASS(HeavyMachineGun) -REGISTER_WEAPON(HMG, hmg, NEW(HeavyMachineGun)); - -SPAWNFUNC_WEAPON(weapon_hmg, WEP_HMG) diff --git a/qcsrc/common/mutators/mutator/overkill/okhmg.qc b/qcsrc/common/mutators/mutator/overkill/okhmg.qc new file mode 100644 index 0000000000..688928ce14 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okhmg.qc @@ -0,0 +1,167 @@ +#include "okhmg.qh" + +#ifdef SVQC + +REGISTER_MUTATOR(okhmg_nadesupport, true); +MUTATOR_HOOKFUNCTION(okhmg_nadesupport, Nade_Damage) +{ + if (M_ARGV(1, entity) != WEP_OVERKILL_HMG) return; + return = true; + M_ARGV(3, float) /* damage */ = (M_ARGV(0, entity)).max_health * 0.1; +} + +void W_OverkillHeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) +{ + if (!PHYS_INPUT_BUTTON_ATCK(actor)) + { + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + if((!thiswep.wr_checkammo1(thiswep, actor, weaponentity) && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (!(actor.items & IT_SUPERWEAPON) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS))) + { + W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + W_DecreaseAmmo(WEP_OVERKILL_HMG, actor, WEP_CVAR_PRI(okhmg, ammo), weaponentity); + + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(okhmg, damage), WEP_OVERKILL_HMG.m_id); + + if(!autocvar_g_norecoil) + { + actor.punchangle_x = random () - 0.5; + actor.punchangle_y = random () - 0.5; + } + + float okhmg_spread = bound(WEP_CVAR_PRI(okhmg, spread_min), WEP_CVAR_PRI(okhmg, spread_min) + (WEP_CVAR_PRI(okhmg, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR_PRI(okhmg, spread_max)); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, okhmg_spread, WEP_CVAR_PRI(okhmg, solidpenetration), WEP_CVAR_PRI(okhmg, damage), WEP_CVAR_PRI(okhmg, force), WEP_OVERKILL_HMG.m_id, EFFECT_RIFLE); + + actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; + + Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); + + if (autocvar_g_casings >= 2) // casing code + { + makevectors(actor.v_angle); // for some reason, this is lost + SpawnCasing (((random () * 50 + 50) * v_right) - (v_forward * (random () * 25 + 25)) - ((random () * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); + } + + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okhmg, refire) * W_WeaponRateFactor(actor); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okhmg, refire), W_OverkillHeavyMachineGun_Attack_Auto); +} + +METHOD(OverkillHeavyMachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) +{ + if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200)) + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); + else + PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); +} + +METHOD(OverkillHeavyMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if ((WEP_CVAR_SEC(okhmg, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval)) + { + // Secondary uses it's own refire timer if refire_type is 1. + actor.jump_interval = time + WEP_CVAR_SEC(okhmg, refire) * W_WeaponRateFactor(actor); + BLASTER_SECONDARY_ATTACK(okhmg, actor, weaponentity); + if ((actor.(weaponentity).wframe == WFRAME_IDLE) || + (actor.(weaponentity).wframe == WFRAME_FIRE2)) + { + // Set secondary fire animation. + vector a = '0 0 0'; + actor.(weaponentity).wframe = WFRAME_FIRE2; + a = actor.(weaponentity).anim_fire2; + a.z *= g_weaponratefactor; + FOREACH_CLIENT(true, LAMBDA( + if (it == actor || (IS_SPEC(it) && it.enemy == actor)) + { + wframe_send(it, actor.(weaponentity), a, true); + } + )); + animdecide_setaction(actor, ANIMACTION_SHOOT, true); + } + } + if (WEP_CVAR(okhmg, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okhmg, ammo)) + { + // Forced reload. + thiswep.wr_reload(thiswep, actor, weaponentity); + return; + } + if (fire & 1) // Primary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) + { + return; + } + actor.(weaponentity).misc_bulletcounter = 0; + W_OverkillHeavyMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire); + return; + } + if ((fire & 2) && (WEP_CVAR_SEC(okhmg, refire_type) == 0)) // Secondary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(okhmg, refire))) + { + return; + } + BLASTER_SECONDARY_ATTACK(okhmg, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okhmg, animtime), w_ready); + } +} + +METHOD(OverkillHeavyMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okhmg, ammo); + if (autocvar_g_balance_okhmg_reload_ammo) + { + ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_HMG.m_id]) >= WEP_CVAR_PRI(okhmg, ammo); + } + return ammo_amount; +} + +METHOD(OverkillHeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(okhmg, ammo); + if (autocvar_g_balance_okhmg_reload_ammo) + { + ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_HMG.m_id]) >= WEP_CVAR_SEC(okhmg, ammo); + } + return ammo_amount; +} + +METHOD(OverkillHeavyMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, weaponentity, WEP_CVAR_PRI(okhmg, ammo), SND_RELOAD); +} + +METHOD(OverkillHeavyMachineGun, wr_suicidemessage, Notification(entity thiswep)) +{ + return WEAPON_THINKING_WITH_PORTALS; +} + +METHOD(OverkillHeavyMachineGun, wr_killmessage, Notification(entity thiswep)) +{ + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_OVERKILL_HMG_MURDER_SNIPE; + else + return WEAPON_OVERKILL_HMG_MURDER_SPRAY; +} + +#endif +#ifdef CSQC + +METHOD(OverkillHeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor)) +{ + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1); + if(!w_issilent) + sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/okhmg.qh b/qcsrc/common/mutators/mutator/overkill/okhmg.qh new file mode 100644 index 0000000000..b3c2664402 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okhmg.qh @@ -0,0 +1,61 @@ +#pragma once + +#include <common/weapons/all.qh> + +CLASS(OverkillHeavyMachineGun, Weapon) +/* spawnfunc */ ATTRIB(OverkillHeavyMachineGun, m_canonical_spawnfunc, string, "weapon_okhmg"); +/* ammotype */ ATTRIB(OverkillHeavyMachineGun, ammo_type, int, RESOURCE_BULLETS); +/* impulse */ ATTRIB(OverkillHeavyMachineGun, impulse, int, 3); +/* flags */ ATTRIB(OverkillHeavyMachineGun, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_HIDDEN | WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_SUPERWEAPON); +/* rating */ ATTRIB(OverkillHeavyMachineGun, bot_pickupbasevalue, float, 10000); +/* color */ ATTRIB(OverkillHeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(OverkillHeavyMachineGun, mdl, string, "ok_hmg"); +#ifdef GAMEQC +/* model */ ATTRIB(OverkillHeavyMachineGun, m_model, Model, MDL_HMG_ITEM); +#endif +/* crosshair */ ATTRIB(OverkillHeavyMachineGun, w_crosshair, string, "gfx/crosshairuzi"); +/* crosshair */ ATTRIB(OverkillHeavyMachineGun, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(OverkillHeavyMachineGun, model2, string, "weaponhmg"); +/* refname */ ATTRIB(OverkillHeavyMachineGun, netname, string, "okhmg"); +/* wepname */ ATTRIB(OverkillHeavyMachineGun, m_name, string, _("Overkill Heavy Machine Gun")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, PRI) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, force, float, PRI) \ + P(class, prefix, refire, float, PRI) \ + P(class, prefix, solidpenetration, float, PRI) \ + P(class, prefix, spread_add, float, PRI) \ + P(class, prefix, spread_max, float, PRI) \ + P(class, prefix, spread_min, float, PRI) \ + P(class, prefix, ammo, float, SEC) \ + P(class, prefix, animtime, float, SEC) \ + P(class, prefix, damage, float, SEC) \ + P(class, prefix, delay, float, SEC) \ + P(class, prefix, edgedamage, float, SEC) \ + P(class, prefix, force, float, SEC) \ + P(class, prefix, lifetime, float, SEC) \ + P(class, prefix, radius, float, SEC) \ + P(class, prefix, refire, float, SEC) \ + P(class, prefix, refire_type, float, SEC) \ + P(class, prefix, shotangle, float, SEC) \ + P(class, prefix, speed, float, SEC) \ + P(class, prefix, spread, float, SEC) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, OverkillHeavyMachineGun, okhmg) +#undef X + +ENDCLASS(OverkillHeavyMachineGun) +REGISTER_WEAPON(OVERKILL_HMG, okhmg, NEW(OverkillHeavyMachineGun)); + +//SPAWNFUNC_WEAPON(weapon_okhmg, WEP_OVERKILL_HMG) +//SPAWNFUNC_WEAPON(weapon_hmg, WEP_OVERKILL_HMG) diff --git a/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc new file mode 100644 index 0000000000..aa872f1541 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qc @@ -0,0 +1,154 @@ +#include "okmachinegun.qh" + +#ifdef SVQC + +void W_OverkillMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) +{ + float okmachinegun_spread; + + if(!(fire & 1)) + { + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + if(!thiswep.wr_checkammo1(thiswep, actor, weaponentity)) + if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + { + W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + W_DecreaseAmmo(WEP_OVERKILL_MACHINEGUN, actor, WEP_CVAR_PRI(okmachinegun, ammo), weaponentity); + + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(okmachinegun, damage), WEP_OVERKILL_MACHINEGUN.m_id); + if(!autocvar_g_norecoil) + { + actor.punchangle_x = random() - 0.5; + actor.punchangle_y = random() - 0.5; + } + + okmachinegun_spread = bound(WEP_CVAR_PRI(okmachinegun, spread_min), WEP_CVAR_PRI(okmachinegun, spread_min) + (WEP_CVAR_PRI(okmachinegun, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR_PRI(okmachinegun, spread_max)); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, okmachinegun_spread, WEP_CVAR_PRI(okmachinegun, solidpenetration), WEP_CVAR_PRI(okmachinegun, damage), WEP_CVAR_PRI(okmachinegun, force), WEP_OVERKILL_MACHINEGUN.m_id, EFFECT_RIFLE); + + actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; + + Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); + + if(autocvar_g_casings >= 2) // casing code + { + makevectors(actor.v_angle); // for some reason, this is lost + SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); + } + + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(okmachinegun, refire) * W_WeaponRateFactor(actor); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okmachinegun, refire), W_OverkillMachineGun_Attack_Auto); +} + +METHOD(OverkillMachineGun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) +{ + if(vdist(actor.origin - actor.enemy.origin, <, 3000 - bound(0, skill, 10) * 200)) + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); + else + PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); +} + +METHOD(OverkillMachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if ((WEP_CVAR_SEC(okmachinegun, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval)) + { + // Secondary uses it's own refire timer if refire_type is 1. + actor.jump_interval = time + WEP_CVAR_SEC(okmachinegun, refire) * W_WeaponRateFactor(actor); + BLASTER_SECONDARY_ATTACK(okmachinegun, actor, weaponentity); + if ((actor.(weaponentity).wframe == WFRAME_IDLE) || + (actor.(weaponentity).wframe == WFRAME_FIRE2)) + { + // Set secondary fire animation. + vector a = '0 0 0'; + actor.(weaponentity).wframe = WFRAME_FIRE2; + a = actor.(weaponentity).anim_fire2; + a.z *= g_weaponratefactor; + FOREACH_CLIENT(true, LAMBDA( + if (it == actor || (IS_SPEC(it) && it.enemy == actor)) + { + wframe_send(it, actor.(weaponentity), a, true); + } + )); + animdecide_setaction(actor, ANIMACTION_SHOOT, true); + } + } + if (WEP_CVAR(okmachinegun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okmachinegun, ammo)) + { + // Forced reload + thiswep.wr_reload(thiswep, actor, weaponentity); + return; + } + if (fire & 1) // Primary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) + { + return; + } + actor.(weaponentity).misc_bulletcounter = 0; + W_OverkillMachineGun_Attack_Auto(thiswep, actor, weaponentity, fire); + return; + } + if ((fire & 2) && (WEP_CVAR_SEC(okmachinegun, refire_type) == 0)) // Secondary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(okmachinegun, refire))) + { + return; + } + BLASTER_SECONDARY_ATTACK(okmachinegun, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okmachinegun, animtime), w_ready); + } +} + +METHOD(OverkillMachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount; + ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okmachinegun, ammo); + if (WEP_CVAR(okmachinegun, reload_ammo)) + { + ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_MACHINEGUN.m_id]) >= WEP_CVAR_PRI(okmachinegun, ammo); + } + return ammo_amount; +} + +METHOD(OverkillMachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + return true; // Blaster secondary is unlimited. +} + +METHOD(OverkillMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, weaponentity, WEP_CVAR_PRI(okmachinegun, ammo), SND_RELOAD); +} + +METHOD(OverkillMachineGun, wr_suicidemessage, Notification(entity thiswep)) +{ + return WEAPON_THINKING_WITH_PORTALS; +} + +METHOD(OverkillMachineGun, wr_killmessage, Notification(entity thiswep)) +{ + return WEAPON_OVERKILL_MACHINEGUN_MURDER; +} + +#endif +#ifdef CSQC + +METHOD(OverkillMachineGun, wr_impacteffect, void(entity thiswep, entity actor)) +{ + vector org2; + org2 = w_org + w_backoff * 2; + pointparticles(EFFECT_MACHINEGUN_IMPACT, org2, w_backoff * 1000, 1); + if(!w_issilent) + sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTN_NORM); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/okmachinegun.qh b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qh new file mode 100644 index 0000000000..32176da90a --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okmachinegun.qh @@ -0,0 +1,57 @@ +#pragma once + +CLASS(OverkillMachineGun, Weapon) +/* spawnfunc */ ATTRIB(OverkillMachineGun, m_canonical_spawnfunc, string, "weapon_okmachinegun"); +/* ammotype */ ATTRIB(OverkillMachineGun, ammo_type, int, RESOURCE_BULLETS); +/* impulse */ ATTRIB(OverkillMachineGun, impulse, int, 3); +/* flags */ ATTRIB(OverkillMachineGun, spawnflags, int, WEP_FLAG_HIDDEN | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS | WEP_FLAG_MUTATORBLOCKED); +/* rating */ ATTRIB(OverkillMachineGun, bot_pickupbasevalue, float, 7000); +/* color */ ATTRIB(OverkillMachineGun, wpcolor, vector, '1 1 0'); +/* modelname */ ATTRIB(OverkillMachineGun, mdl, string, "ok_mg"); +#ifdef GAMEQC +/* model */ ATTRIB(OverkillMachineGun, m_model, Model, MDL_OK_MG_ITEM); +#endif +/* crosshair */ ATTRIB(OverkillMachineGun, w_crosshair, string, "gfx/crosshairuzi"); +/* crosshair */ ATTRIB(OverkillMachineGun, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(OverkillMachineGun, model2, string, "ok_weapon_smg"); +/* refname */ ATTRIB(OverkillMachineGun, netname, string, "okmachinegun"); +/* wepname */ ATTRIB(OverkillMachineGun, m_name, string, _("Overkill MachineGun")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, PRI) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, force, float, PRI) \ + P(class, prefix, refire, float, PRI) \ + P(class, prefix, solidpenetration, float, PRI) \ + P(class, prefix, spread_add, float, PRI) \ + P(class, prefix, spread_max, float, PRI) \ + P(class, prefix, spread_min, float, PRI) \ + P(class, prefix, animtime, float, SEC) \ + P(class, prefix, damage, float, SEC) \ + P(class, prefix, delay, float, SEC) \ + P(class, prefix, edgedamage, float, SEC) \ + P(class, prefix, force, float, SEC) \ + P(class, prefix, lifetime, float, SEC) \ + P(class, prefix, radius, float, SEC) \ + P(class, prefix, refire, float, SEC) \ + P(class, prefix, refire_type, float, SEC) \ + P(class, prefix, shotangle, float, SEC) \ + P(class, prefix, speed, float, SEC) \ + P(class, prefix, spread, float, SEC) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, OverkillMachineGun, okmachinegun) +#undef X + +ENDCLASS(OverkillMachineGun) +REGISTER_WEAPON(OVERKILL_MACHINEGUN, okmachinegun, NEW(OverkillMachineGun)); + +//SPAWNFUNC_WEAPON(weapon_okmachinegun, WEP_OVERKILL_MACHINEGUN) diff --git a/qcsrc/common/mutators/mutator/overkill/oknex.qc b/qcsrc/common/mutators/mutator/overkill/oknex.qc new file mode 100644 index 0000000000..4fbd200b69 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/oknex.qc @@ -0,0 +1,361 @@ +#include "oknex.qh" + +#ifdef SVQC + +.float oknex_lasthit; +#endif + +#if defined(GAMEQC) + +METHOD(OverkillNex, wr_glow, vector(OverkillNex this, entity actor, entity wepent)) +{ + if (!WEP_CVAR(oknex, charge)) return '0 0 0'; + float charge = wepent.oknex_charge; + float animlimit = WEP_CVAR(oknex, charge_animlimit); + vector g; + g.x = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_half * min(1, charge / animlimit); + g.y = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_half * min(1, charge / animlimit); + g.z = autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_half * min(1, charge / animlimit); + if (charge > animlimit) + { + g.x += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_red_full * (charge - animlimit) / (1 - animlimit); + g.y += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_green_full * (charge - animlimit) / (1 - animlimit); + g.z += autocvar_g_weapon_charge_colormod_hdrmultiplier * autocvar_g_weapon_charge_colormod_blue_full * (charge - animlimit) / (1 - animlimit); + } + return g; +} +#endif + +#ifdef SVQC +REGISTER_MUTATOR(oknex_charge, true); + +MUTATOR_HOOKFUNCTION(oknex_charge, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + // WEAPONTODO + if(!WEP_CVAR(oknex, charge) || !WEP_CVAR(oknex, charge_velocity_rate)) + return; + + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + + if (player.(weaponentity).m_weapon == WEP_OVERKILL_NEX && WEP_CVAR(oknex, charge) && WEP_CVAR(oknex, charge_velocity_rate) && vdist(vec2(player.velocity), >, WEP_CVAR(oknex, charge_minspeed))) + { + float xyspeed = vlen(vec2(player.velocity)); + // add a maximum of charge_velocity_rate when going fast (f = 1), gradually increasing from minspeed (f = 0) to maxspeed + xyspeed = min(xyspeed, WEP_CVAR(oknex, charge_maxspeed)); + float f = (xyspeed - WEP_CVAR(oknex, charge_minspeed)) / (WEP_CVAR(oknex, charge_maxspeed) - WEP_CVAR(oknex, charge_minspeed)); + // add the extra charge + player.(weaponentity).oknex_charge = min(1, player.(weaponentity).oknex_charge + WEP_CVAR(oknex, charge_velocity_rate) * f * PHYS_INPUT_TIMELENGTH); + } + } +} + +void W_OverkillNex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float issecondary) +{ + float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge; + + mydmg = WEP_CVAR_BOTH(oknex, !issecondary, damage); + myforce = WEP_CVAR_BOTH(oknex, !issecondary, force); + mymindist = WEP_CVAR_BOTH(oknex, !issecondary, damagefalloff_mindist); + mymaxdist = WEP_CVAR_BOTH(oknex, !issecondary, damagefalloff_maxdist); + myhalflife = WEP_CVAR_BOTH(oknex, !issecondary, damagefalloff_halflife); + myforcehalflife = WEP_CVAR_BOTH(oknex, !issecondary, damagefalloff_forcehalflife); + myammo = WEP_CVAR_BOTH(oknex, !issecondary, ammo); + + float flying; + flying = IsFlying(actor); // do this BEFORE to make the trace values from FireRailgunBullet last + + if (WEP_CVAR(oknex, charge)) + { + charge = WEP_CVAR(oknex, charge_mindmg) / mydmg + (1 - WEP_CVAR(oknex, charge_mindmg) / mydmg) * actor.(weaponentity).oknex_charge; + actor.(weaponentity).oknex_charge *= WEP_CVAR(oknex, charge_shot_multiplier); // do this AFTER setting mydmg/myforce + // O RLY? -- divVerent + // YA RLY -- FruitieX + } + else + { + charge = 1; + } + mydmg *= charge; + myforce *= charge; + + W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg, thiswep.m_id); + if(charge > WEP_CVAR(oknex, charge_animlimit) && WEP_CVAR(oknex, charge_animlimit)) // if the OverkillNex is overcharged, we play an extra sound + { + sound(actor, CH_WEAPON_B, SND_NEXCHARGE, VOL_BASE * (charge - 0.5 * WEP_CVAR(oknex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(oknex, charge_animlimit)), ATTN_NORM); + } + + yoda = 0; + damage_goodhits = 0; + FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, thiswep.m_id); + + if(yoda && flying) + Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); + if(damage_goodhits && actor.oknex_lasthit) + { + Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_IMPRESSIVE); + damage_goodhits = 0; // only every second time + } + + actor.oknex_lasthit = damage_goodhits; + + //beam and muzzle flash done on client + SendCSQCVortexBeamParticle(charge); + + W_DecreaseAmmo(thiswep, actor, myammo, weaponentity); +} + +.float oknex_chargepool_pauseregen_finished; + +METHOD(OverkillNex, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) +{ + if(bot_aim(actor, weaponentity, 1000000, 0, 1, false)) + PHYS_INPUT_BUTTON_ATCK(actor) = true; + else + { + if(WEP_CVAR(oknex, charge)) + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + } +} + +METHOD(OverkillNex, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if (WEP_CVAR(oknex, charge) && actor.(weaponentity).oknex_charge < WEP_CVAR(oknex, charge_limit)) + { + actor.(weaponentity).oknex_charge = min(1, actor.(weaponentity).oknex_charge + WEP_CVAR(oknex, charge_rate) * frametime / W_TICSPERFRAME); + } + + if (WEP_CVAR_SEC(oknex, chargepool)) + if (actor.(weaponentity).oknex_chargepool_ammo < 1) + { + if (actor.oknex_chargepool_pauseregen_finished < time) + actor.(weaponentity).oknex_chargepool_ammo = min(1, actor.(weaponentity).oknex_chargepool_ammo + WEP_CVAR_SEC(oknex, chargepool_regen) * frametime / W_TICSPERFRAME); + actor.pauseregen_finished = max(actor.pauseregen_finished, time + WEP_CVAR_SEC(oknex, chargepool_pause_regen)); + } + + if ((WEP_CVAR_SEC(oknex, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval)) + { + // Secondary uses it's own refire timer if refire_type is 1. + actor.jump_interval = time + WEP_CVAR_SEC(oknex, refire) * W_WeaponRateFactor(actor); + BLASTER_SECONDARY_ATTACK(oknex, actor, weaponentity); + if ((actor.(weaponentity).wframe == WFRAME_IDLE) || + (actor.(weaponentity).wframe == WFRAME_FIRE2)) + { + // Set secondary fire animation. + vector a = '0 0 0'; + actor.(weaponentity).wframe = WFRAME_FIRE2; + a = actor.(weaponentity).anim_fire2; + a.z *= g_weaponratefactor; + FOREACH_CLIENT(true, LAMBDA( + if (it == actor || (IS_SPEC(it) && it.enemy == actor)) + { + wframe_send(it, actor.(weaponentity), a, true); + } + )); + animdecide_setaction(actor, ANIMACTION_SHOOT, true); + } + } + + if (autocvar_g_balance_oknex_reload_ammo && actor.(weaponentity).clip_load < WEP_CVAR_PRI(oknex, ammo)) + { + // Rorced reload + thiswep.wr_reload(thiswep, actor, weaponentity); + return; + } + if (fire & 1) // Primary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(oknex, refire))) + { + return; + } + W_OverkillNex_Attack(thiswep, actor, weaponentity, 0); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(oknex, animtime), w_ready); + return; + } + if ((fire & 2) && (WEP_CVAR(oknex, secondary) == 2) && (WEP_CVAR_SEC(oknex, refire_type) == 0)) + { + // Secondary attack + if (!weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(oknex, refire))) + { + return; + } + BLASTER_SECONDARY_ATTACK(oknex, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(oknex, animtime), w_ready); + return; + } + //if ((WEP_CVAR(oknex, charge) && (WEP_CVAR(oknex, secondary) == 1)) ? (PHYS_INPUT_BUTTON_ZOOM(actor) | PHYS_INPUT_BUTTON_ZOOMSCRIPT(actor)) : (fire & 2)) + //{ + // if(WEP_CVAR(oknex, charge)) + // { + // actor.(weaponentity).oknex_charge_rottime = time + WEP_CVAR(oknex, charge_rot_pause); + // float dt = frametime / W_TICSPERFRAME; + // + // if(actor.(weaponentity).oknex_charge < 1) + // { + // if(WEP_CVAR_SEC(oknex, chargepool)) + // { + // if(WEP_CVAR_SEC(oknex, ammo)) + // { + // // always deplete if secondary is held + // actor.(weaponentity).oknex_chargepool_ammo = max(0, actor.(weaponentity).oknex_chargepool_ammo - WEP_CVAR_SEC(oknex, ammo) * dt); + + // dt = min(dt, (1 - actor.(weaponentity).oknex_charge) / WEP_CVAR(oknex, charge_rate)); + // actor.oknex_chargepool_pauseregen_finished = time + WEP_CVAR_SEC(oknex, chargepool_pause_regen); + // dt = min(dt, actor.(weaponentity).oknex_chargepool_ammo); + // dt = max(0, dt); + + // actor.(weaponentity).oknex_charge += dt * WEP_CVAR(oknex, charge_rate); + // } + // } + + // else if(WEP_CVAR_SEC(oknex, ammo)) + // { + // if(fire & 2) // only eat ammo when the button is pressed + // { + // dt = min(dt, (1 - actor.(weaponentity).oknex_charge) / WEP_CVAR(oknex, charge_rate)); + // if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + // { + // // if this weapon is reloadable, decrease its load. Else decrease the player's ammo + // if(autocvar_g_balance_vortex_reload_ammo) + // { + // dt = min(dt, (actor.(weaponentity).clip_load - WEP_CVAR_PRI(oknex, ammo)) / WEP_CVAR_SEC(oknex, ammo)); + // dt = max(0, dt); + // if(dt > 0) + // { + // actor.(weaponentity).clip_load = max(WEP_CVAR_SEC(oknex, ammo), actor.(weaponentity).clip_load - WEP_CVAR_SEC(oknex, ammo) * dt); + // } + // actor.(weaponentity).(weapon_load[WEP_OVERKILL_NEX.m_id]) = actor.(weaponentity).clip_load; + // } + // else + // { + // dt = min(dt, (actor.(thiswep.ammo_field) - WEP_CVAR_PRI(oknex, ammo)) / WEP_CVAR_SEC(oknex, ammo)); + // dt = max(0, dt); + // if(dt > 0) + // { + // actor.(thiswep.ammo_field) = max(WEP_CVAR_SEC(oknex, ammo), actor.(thiswep.ammo_field) - WEP_CVAR_SEC(oknex, ammo) * dt); + // } + // } + // } + // actor.(weaponentity).oknex_charge += dt * WEP_CVAR(oknex, charge_rate); + // } + // } + + // else + // { + // dt = min(dt, (1 - actor.(weaponentity).oknex_charge) / WEP_CVAR(oknex, charge_rate)); + // actor.(weaponentity).oknex_charge += dt * WEP_CVAR(oknex, charge_rate); + // } + // } + // } + // else if(WEP_CVAR(oknex, secondary)) + // { + // if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(oknex, refire))) + // { + // W_OverkillNex_Attack(thiswep, actor, weaponentity, 1); + // weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(oknex, animtime), w_ready); + // } + // } + //} +} + +METHOD(OverkillNex, wr_setup, void(entity thiswep, entity actor, .entity weaponentity)) +{ + actor.oknex_lasthit = 0; +} + +METHOD(OverkillNex, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(oknex, ammo); + ammo_amount += (autocvar_g_balance_oknex_reload_ammo && actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(oknex, ammo)); + return ammo_amount; +} + +METHOD(OverkillNex, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + if (WEP_CVAR(oknex, secondary)) + { + // don't allow charging if we don't have enough ammo + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(oknex, ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(oknex, ammo); + return ammo_amount; + } + else + { + return false; // zoom is not a fire mode + } +} + +METHOD(OverkillNex, wr_resetplayer, void(entity thiswep, entity actor)) +{ + if (WEP_CVAR(oknex, charge)) { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + actor.(weaponentity).oknex_charge = WEP_CVAR(oknex, charge_start); + } + } + actor.oknex_lasthit = 0; +} + +METHOD(OverkillNex, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, weaponentity, WEP_CVAR_PRI(oknex, ammo), SND_RELOAD); +} + +METHOD(OverkillNex, wr_suicidemessage, Notification(entity thiswep)) +{ + return WEAPON_THINKING_WITH_PORTALS; +} + +METHOD(OverkillNex, wr_killmessage, Notification(entity thiswep)) +{ + return WEAPON_OVERKILL_NEX_MURDER; +} + +METHOD(OverkillNex, wr_zoom, bool(entity thiswep, entity actor)) +{ + return PHYS_INPUT_BUTTON_ATCK2(actor) && !WEP_CVAR(oknex, secondary); +} + +#endif +#ifdef CSQC + +METHOD(OverkillNex, wr_impacteffect, void(entity thiswep, entity actor)) +{ + entity this = actor; + vector org2 = w_org + w_backoff * 6; + pointparticles(EFFECT_VORTEX_IMPACT, org2, '0 0 0', 1); + if(!w_issilent) + sound(this, CH_SHOTS, SND_NEXIMPACT, VOL_BASE, ATTN_NORM); +} + +METHOD(OverkillNex, wr_init, void(entity thiswep)) +{ + if(autocvar_cl_reticle && autocvar_cl_reticle_weapon) + { + precache_pic("gfx/reticle_nex"); + } +} + +METHOD(OverkillNex, wr_zoom, bool(entity thiswep, entity actor)) +{ + if(button_zoom || zoomscript_caught || (!WEP_CVAR(oknex, secondary) && button_attack2)) + { + return true; + } + else + { + // no weapon specific image for this weapon + return false; + } +} + +METHOD(OverkillNex, wr_zoomdir, bool(entity thiswep)) +{ + return button_attack2 && !WEP_CVAR(oknex, secondary); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/oknex.qh b/qcsrc/common/mutators/mutator/overkill/oknex.qh new file mode 100644 index 0000000000..ec8ceeb390 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/oknex.qh @@ -0,0 +1,77 @@ +#pragma once + +CLASS(OverkillNex, Weapon) +/* spawnfunc */ ATTRIB(OverkillNex, m_canonical_spawnfunc, string, "weapon_oknex"); +/* ammotype */ ATTRIB(OverkillNex, ammo_type, int, RESOURCE_CELLS); +/* impulse */ ATTRIB(OverkillNex, impulse, int, 7); +/* flags */ ATTRIB(OverkillNex, spawnflags, int, WEP_FLAG_HIDDEN | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_MUTATORBLOCKED); +/* rating */ ATTRIB(OverkillNex, bot_pickupbasevalue, float, 8000); +/* color */ ATTRIB(OverkillNex, wpcolor, vector, '0.5 1 1'); +/* modelname */ ATTRIB(OverkillNex, mdl, string, "ok_sniper"); +#ifdef GAMEQC +/* model */ ATTRIB(OverkillNex, m_model, Model, MDL_OK_SNIPER_ITEM); +#endif +/* crosshair */ ATTRIB(OverkillNex, w_crosshair, string, "gfx/crosshairnex"); +/* crosshair */ ATTRIB(OverkillNex, w_crosshair_size, float, 0.65); +/* reticle */ ATTRIB(OverkillNex, w_reticle, string, "gfx/reticle_nex"); +/* wepimg */ ATTRIB(OverkillNex, model2, string, "ok_weapon_rail"); +/* refname */ ATTRIB(OverkillNex, netname, string, "oknex"); +/* wepname */ ATTRIB(OverkillNex, m_name, string, _("Overkill Nex")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, PRI) \ + P(class, prefix, animtime, float, PRI) \ + P(class, prefix, chargepool, float, SEC) \ + P(class, prefix, chargepool_pause_regen, float, SEC) \ + P(class, prefix, chargepool_regen, float, SEC) \ + P(class, prefix, charge, float, NONE) \ + P(class, prefix, charge_animlimit, float, NONE) \ + P(class, prefix, charge_limit, float, NONE) \ + P(class, prefix, charge_maxspeed, float, NONE) \ + P(class, prefix, charge_mindmg, float, NONE) \ + P(class, prefix, charge_minspeed, float, NONE) \ + P(class, prefix, charge_rate, float, NONE) \ + P(class, prefix, charge_rot_pause, float, NONE) \ + P(class, prefix, charge_rot_rate, float, NONE) \ + P(class, prefix, charge_shot_multiplier, float, NONE) \ + P(class, prefix, charge_start, float, NONE) \ + P(class, prefix, charge_velocity_rate, float, NONE) \ + P(class, prefix, damagefalloff_forcehalflife, float, BOTH) \ + P(class, prefix, damagefalloff_halflife, float, BOTH) \ + P(class, prefix, damagefalloff_maxdist, float, BOTH) \ + P(class, prefix, damagefalloff_mindist, float, BOTH) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, force, float, PRI) \ + P(class, prefix, refire, float, PRI) \ + P(class, prefix, secondary, float, NONE) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + P(class, prefix, ammo, float, SEC) \ + P(class, prefix, animtime, float, SEC) \ + P(class, prefix, damage, float, SEC) \ + P(class, prefix, delay, float, SEC) \ + P(class, prefix, edgedamage, float, SEC) \ + P(class, prefix, force, float, SEC) \ + P(class, prefix, lifetime, float, SEC) \ + P(class, prefix, radius, float, SEC) \ + P(class, prefix, refire, float, SEC) \ + P(class, prefix, refire_type, float, SEC) \ + P(class, prefix, shotangle, float, SEC) \ + P(class, prefix, speed, float, SEC) \ + P(class, prefix, spread, float, SEC) \ + END() + W_PROPS(X, OverkillNex, oknex) +#undef X + +ENDCLASS(OverkillNex) +REGISTER_WEAPON(OVERKILL_NEX, oknex, NEW(OverkillNex)); + + +//SPAWNFUNC_WEAPON(weapon_oknex, WEP_OVERKILL_NEX) diff --git a/qcsrc/common/mutators/mutator/overkill/okrpc.qc b/qcsrc/common/mutators/mutator/overkill/okrpc.qc new file mode 100644 index 0000000000..50c842d984 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okrpc.qc @@ -0,0 +1,242 @@ +#include "okrpc.qh" + +#ifdef SVQC + +.float m_chainsaw_damage; // accumulated damage of the missile as it passes trough enemies + +void W_OverkillRocketPropelledChainsaw_Explode(entity this, entity directhitentity) +{ + this.event_damage = func_null; + this.takedamage = DAMAGE_NO; + + float explosion_damage = RadiusDamage(this, this.realowner, WEP_CVAR_PRI(okrpc, damage), WEP_CVAR_PRI(okrpc, edgedamage), WEP_CVAR_PRI(okrpc, radius), NULL, NULL, WEP_CVAR_PRI(okrpc, force), this.projectiledeathtype, this.weaponentity_fld, directhitentity); + if (explosion_damage > 0 && this.m_chainsaw_damage > 0) + { + // if chainsaw hit something, it removed fired damage (so that direct hit is 100%) + // now that we also damaged something by explosion we'd go over 100% so let's add the fired damage back + accuracy_add(this.realowner, DEATH_WEAPONOF(this.projectiledeathtype), WEP_CVAR(okrpc, damage), 0); + } + + delete(this); +} + +void W_OverkillRocketPropelledChainsaw_Explode_think(entity this) +{ + W_OverkillRocketPropelledChainsaw_Explode(this, NULL); +} + +void W_OverkillRocketPropelledChainsaw_Touch (entity this, entity toucher) +{ + if(WarpZone_Projectile_Touch(this, toucher)) + if(wasfreed(this)) + return; + + W_OverkillRocketPropelledChainsaw_Explode(this, toucher); +} + +void W_OverkillRocketPropelledChainsaw_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +{ + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + return; + + if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions + return; // g_projectiles_damage says to halt + + TakeResource(this, RESOURCE_HEALTH, damage); + + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) + W_PrepareExplosionByDamage(this, attacker, W_OverkillRocketPropelledChainsaw_Explode_think); +} + +void W_OverkillRocketPropelledChainsaw_Think(entity this) +{ + if(this.cnt <= time) + { + delete(this); + return; + } + + float myspeed = vlen(this.velocity); + float myspeed_accel = myspeed * sys_frametime; + vector mydir = normalize(this.velocity); + + tracebox(this.origin, this.mins, this.maxs, this.origin + mydir * (2 * myspeed_accel), MOVE_NORMAL, this); + if (IS_PLAYER(trace_ent)) + { + if (accuracy_isgooddamage(this.realowner, trace_ent)) + { + if (this.m_chainsaw_damage == 0) // first hit + { + // The fired damage of the explosion is already counted in the statistics (when launching the chainsaw). + // We remove it here so that a direct hit that passes through and doesn't damage anything by the explosion later is still 100%. + float fired_damage = WEP_CVAR_PRI(okrpc, damage2) - WEP_CVAR_PRI(okrpc, damage); + float hit_damage = WEP_CVAR_PRI(okrpc, damage2); + accuracy_add(this.realowner, DEATH_WEAPONOF(this.projectiledeathtype), fired_damage, hit_damage); + } + this.m_chainsaw_damage += WEP_CVAR_PRI(okrpc, damage2); + } + Damage(trace_ent, this, this.realowner, WEP_CVAR_PRI(okrpc, damage2), this.projectiledeathtype, this.weaponentity_fld, this.origin, normalize(this.origin - trace_ent.origin) * WEP_CVAR_PRI(okrpc, force)); + } + + this.velocity = mydir * (myspeed + (WEP_CVAR_PRI(okrpc, speedaccel) * sys_frametime)); + + UpdateCSQCProjectile(this); + this.nextthink = time; +} + +void W_OverkillRocketPropelledChainsaw_Attack(Weapon thiswep, entity actor, .entity weaponentity) +{ + entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(actor); + entity flash = spawn (); + + W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(okrpc, ammo), weaponentity); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(okrpc, damage), thiswep.m_id); + Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); + PROJECTILE_MAKETRIGGER(missile); + + missile.owner = missile.realowner = actor; + missile.bot_dodge = true; + missile.bot_dodgerating = WEP_CVAR_PRI(okrpc, damage) * 2; + + missile.takedamage = DAMAGE_YES; + missile.damageforcescale = WEP_CVAR_PRI(okrpc, damageforcescale); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_PRI(okrpc, health)); + missile.event_damage = W_OverkillRocketPropelledChainsaw_Damage; + missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); + set_movetype(missile, MOVETYPE_FLY); + + missile.projectiledeathtype = thiswep.m_id; + missile.weaponentity_fld = weaponentity; + setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot + + setorigin(missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point + W_SetupProjVelocity_Basic(missile, WEP_CVAR_PRI(okrpc, speed), 0); + + settouch(missile, W_OverkillRocketPropelledChainsaw_Touch); + + setthink(missile, W_OverkillRocketPropelledChainsaw_Think); + missile.cnt = time + WEP_CVAR_PRI(okrpc, lifetime); + missile.nextthink = time; + missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); + + CSQCProjectile(missile, true, PROJECTILE_RPC, false); + + setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below + SUB_SetFade (flash, time, 0.1); + flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); + missile.m_chainsaw_damage = 0; + + MUTATOR_CALLHOOK(EditProjectile, actor, missile); +} + +METHOD(OverkillRocketPropelledChainsaw, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) +{ + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR_PRI(okrpc, speed), 0, WEP_CVAR_PRI(okrpc, lifetime), false); +} + +METHOD(OverkillRocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if ((WEP_CVAR_SEC(okrpc, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval)) + { + // Secondary uses it's own refire timer if refire_type is 1. + actor.jump_interval = time + WEP_CVAR_SEC(okrpc, refire) * W_WeaponRateFactor(actor); + BLASTER_SECONDARY_ATTACK(okrpc, actor, weaponentity); + if ((actor.(weaponentity).wframe == WFRAME_IDLE) || + (actor.(weaponentity).wframe == WFRAME_FIRE2)) + { + // Set secondary fire animation. + vector a = '0 0 0'; + actor.(weaponentity).wframe = WFRAME_FIRE2; + a = actor.(weaponentity).anim_fire2; + a.z *= g_weaponratefactor; + FOREACH_CLIENT(true, LAMBDA( + if (it == actor || (IS_SPEC(it) && it.enemy == actor)) + { + wframe_send(it, actor.(weaponentity), a, true); + } + )); + animdecide_setaction(actor, ANIMACTION_SHOOT, true); + } + } + if (WEP_CVAR(okrpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okrpc, ammo)) + { + // Forced reload + thiswep.wr_reload(thiswep, actor, weaponentity); + return; + } + if (fire & 1) // Primary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(okrpc, refire))) + { + return; + } + W_OverkillRocketPropelledChainsaw_Attack(thiswep, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okrpc, animtime), w_ready); + return; + } + if ((fire & 2) && (WEP_CVAR_SEC(okrpc, refire_type) == 0)) // Secondary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(okrpc, refire))) + { + return; + } + BLASTER_SECONDARY_ATTACK(okrpc, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okrpc, animtime), w_ready); + } +} + +METHOD(OverkillRocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okrpc, ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(okrpc, ammo); + return ammo_amount; +} + +METHOD(OverkillRocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(okrpc, ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(okrpc, ammo); + return ammo_amount; +} + +METHOD(OverkillRocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, weaponentity, WEP_CVAR_PRI(okrpc, ammo), SND_RELOAD); +} + +METHOD(OverkillRocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep)) +{ + if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_OVERKILL_RPC_SUICIDE_SPLASH; + else + return WEAPON_OVERKILL_RPC_SUICIDE_DIRECT; +} + +METHOD(OverkillRocketPropelledChainsaw, wr_killmessage, Notification(entity thiswep)) +{ + if(w_deathtype & HITTYPE_SECONDARY) + return WEAPON_BLASTER_MURDER; + else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) + return WEAPON_OVERKILL_RPC_MURDER_SPLASH; + else + return WEAPON_OVERKILL_RPC_MURDER_DIRECT; +} + +#endif + +#ifdef CSQC + +METHOD(OverkillRocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity actor)) +{ + vector org2; + org2 = w_org + w_backoff * 12; + pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); + if(!w_issilent) + sound(actor, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/okrpc.qh b/qcsrc/common/mutators/mutator/overkill/okrpc.qh new file mode 100644 index 0000000000..4f9bfe7b99 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okrpc.qh @@ -0,0 +1,66 @@ +#pragma once + +#include <common/weapons/all.qh> + +CLASS(OverkillRocketPropelledChainsaw, Weapon) +/* spawnfunc */ ATTRIB(OverkillRocketPropelledChainsaw, m_canonical_spawnfunc, string, "weapon_okrpc"); +/* ammotype */ ATTRIB(OverkillRocketPropelledChainsaw, ammo_type, int, RESOURCE_ROCKETS); +/* impulse */ ATTRIB(OverkillRocketPropelledChainsaw, impulse, int, 9); +/* flags */ ATTRIB(OverkillRocketPropelledChainsaw, 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(OverkillRocketPropelledChainsaw, bot_pickupbasevalue, float, 10000); +/* color */ ATTRIB(OverkillRocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); +/* modelname */ ATTRIB(OverkillRocketPropelledChainsaw, mdl, string, "ok_rl"); +#ifdef GAMEQC +/* model */ ATTRIB(OverkillRocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); +#endif +/* crosshair */ ATTRIB(OverkillRocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); +/* crosshair */ ATTRIB(OverkillRocketPropelledChainsaw, w_crosshair_size, float, 0.6); +/* wepimg */ ATTRIB(OverkillRocketPropelledChainsaw, model2, string, "weaponrpc"); +/* refname */ ATTRIB(OverkillRocketPropelledChainsaw, netname, string, "okrpc"); +/* wepname */ ATTRIB(OverkillRocketPropelledChainsaw, m_name, string, _("Overkill Rocket Propelled Chainsaw")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, PRI) \ + P(class, prefix, animtime, float, PRI) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, damage2, float, PRI) \ + P(class, prefix, damageforcescale, float, PRI) \ + P(class, prefix, edgedamage, float, PRI) \ + P(class, prefix, force, float, PRI) \ + P(class, prefix, health, float, PRI) \ + P(class, prefix, lifetime, float, PRI) \ + P(class, prefix, radius, float, PRI) \ + P(class, prefix, refire, float, PRI) \ + P(class, prefix, speedaccel, float, PRI) \ + P(class, prefix, speed, float, PRI) \ + P(class, prefix, ammo, float, SEC) \ + P(class, prefix, animtime, float, SEC) \ + P(class, prefix, damage, float, SEC) \ + P(class, prefix, delay, float, SEC) \ + P(class, prefix, edgedamage, float, SEC) \ + P(class, prefix, force, float, SEC) \ + P(class, prefix, lifetime, float, SEC) \ + P(class, prefix, radius, float, SEC) \ + P(class, prefix, refire, float, SEC) \ + P(class, prefix, refire_type, float, SEC) \ + P(class, prefix, shotangle, float, SEC) \ + P(class, prefix, speed, float, SEC) \ + P(class, prefix, spread, float, SEC) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string, NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, OverkillRocketPropelledChainsaw, okrpc) +#undef X + +ENDCLASS(OverkillRocketPropelledChainsaw) +REGISTER_WEAPON(OVERKILL_RPC, okrpc, NEW(OverkillRocketPropelledChainsaw)); + +//SPAWNFUNC_WEAPON(weapon_okrpc, WEP_OVERKILL_RPC) +//SPAWNFUNC_WEAPON(weapon_rpc, WEP_OVERKILL_RPC) diff --git a/qcsrc/common/mutators/mutator/overkill/okshotgun.qc b/qcsrc/common/mutators/mutator/overkill/okshotgun.qc new file mode 100644 index 0000000000..2461ba0cb0 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okshotgun.qc @@ -0,0 +1,117 @@ +#include "okshotgun.qh" + +#ifdef SVQC +METHOD(OverkillShotgun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) +{ + if (vdist(actor.origin - actor.enemy.origin, >, WEP_CVAR_PRI(okshotgun, bot_range))) + { + PHYS_INPUT_BUTTON_ATCK2(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); + } + else + { + PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); + } +} + +METHOD(OverkillShotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) +{ + if ((WEP_CVAR_SEC(okshotgun, refire_type) == 1) && (fire & 2) && (time >= actor.jump_interval)) + { + // Secondary uses it's own refire timer if refire_type is 1. + actor.jump_interval = time + WEP_CVAR_SEC(okshotgun, refire) * W_WeaponRateFactor(actor); + BLASTER_SECONDARY_ATTACK(okshotgun, actor, weaponentity); + if ((actor.(weaponentity).wframe == WFRAME_IDLE) || + (actor.(weaponentity).wframe == WFRAME_FIRE2)) + { + // Set secondary fire animation. + vector a = '0 0 0'; + actor.(weaponentity).wframe = WFRAME_FIRE2; + a = actor.(weaponentity).anim_fire2; + a.z *= g_weaponratefactor; + FOREACH_CLIENT(true, LAMBDA( + if (it == actor || (IS_SPEC(it) && it.enemy == actor)) + { + wframe_send(it, actor.(weaponentity), a, true); + } + )); + animdecide_setaction(actor, ANIMACTION_SHOOT, true); + } + } + if (WEP_CVAR(okshotgun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(okshotgun, ammo)) + { + // Forced reload + thiswep.wr_reload(thiswep, actor, weaponentity); + return; + } + if (fire & 1) // Primary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(okshotgun, refire))) + { + return; + } + W_Shotgun_Attack(thiswep, actor, weaponentity, true, + WEP_CVAR_PRI(okshotgun, ammo), + WEP_CVAR_PRI(okshotgun, damage), + WEP_CVAR_PRI(okshotgun, bullets), + WEP_CVAR_PRI(okshotgun, spread), + WEP_CVAR_PRI(okshotgun, solidpenetration), + WEP_CVAR_PRI(okshotgun, force), + EFFECT_RIFLE_WEAK); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(okshotgun, animtime), w_ready); + return; + } + if ((fire & 2) && (WEP_CVAR_SEC(okshotgun, refire_type) == 0)) // Secondary attack + { + if (!weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(okshotgun, refire))) + { + return; + } + BLASTER_SECONDARY_ATTACK(okshotgun, actor, weaponentity); + weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(okshotgun, animtime), w_ready); + } +} + +METHOD(OverkillShotgun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_PRI(okshotgun, ammo); + ammo_amount += actor.(weaponentity).(weapon_load[WEP_OVERKILL_SHOTGUN.m_id]) >= WEP_CVAR_PRI(okshotgun, ammo); + return ammo_amount; +} + +METHOD(OverkillShotgun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) +{ + return true; // Blaster secondary is unlimited. +} + +METHOD(OverkillShotgun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) +{ + W_Reload(actor, weaponentity, WEP_CVAR_PRI(okshotgun, ammo), SND_RELOAD); // WEAPONTODO +} + +METHOD(OverkillShotgun, wr_suicidemessage, Notification(entity thiswep)) +{ + return WEAPON_THINKING_WITH_PORTALS; +} + +METHOD(OverkillShotgun, wr_killmessage, Notification(entity thiswep)) +{ + return WEAPON_OVERKILL_SHOTGUN_MURDER; +} + +#endif +#ifdef CSQC +.float prevric; + +METHOD(OverkillShotgun, wr_impacteffect, void(entity thiswep, entity actor)) +{ + vector org2 = w_org + w_backoff * 2; + pointparticles(EFFECT_SHOTGUN_IMPACT, org2, w_backoff * 1000, 1); + if(!w_issilent && time - actor.prevric > 0.25) + { + if(w_random < 0.05) + sound(actor, CH_SHOTS, SND_RIC_RANDOM(), VOL_BASE, ATTEN_NORM); + actor.prevric = time; + } +} + +#endif diff --git a/qcsrc/common/mutators/mutator/overkill/okshotgun.qh b/qcsrc/common/mutators/mutator/overkill/okshotgun.qh new file mode 100644 index 0000000000..1124baa68b --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/okshotgun.qh @@ -0,0 +1,58 @@ +#pragma once + +CLASS(OverkillShotgun, Weapon) +/* spawnfunc */ ATTRIB(OverkillShotgun, m_canonical_spawnfunc, string, "weapon_okshotgun"); +/* ammotype */ ATTRIB(OverkillShotgun, ammo_type, int, RESOURCE_SHELLS); +/* impulse */ ATTRIB(OverkillShotgun, impulse, int, 2); +/* flags */ ATTRIB(OverkillShotgun, spawnflags, int, WEP_FLAG_HIDDEN | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_MUTATORBLOCKED); +/* rating */ ATTRIB(OverkillShotgun, bot_pickupbasevalue, float, 6000); +/* color */ ATTRIB(OverkillShotgun, wpcolor, vector, '0.5 0.25 0'); +/* modelname */ ATTRIB(OverkillShotgun, mdl, string, "ok_shotgun"); +#ifdef GAMEQC +/* model */ ATTRIB(OverkillShotgun, m_model, Model, MDL_OK_SHOTGUN_ITEM); +#endif +/* crosshair */ ATTRIB(OverkillShotgun, w_crosshair, string, "gfx/crosshairshotgun"); +/* crosshair */ ATTRIB(OverkillShotgun, w_crosshair_size, float, 0.65); +/* wepimg */ ATTRIB(OverkillShotgun, model2, string, "ok_weapon_shotgun"); +/* refname */ ATTRIB(OverkillShotgun, netname, string, "okshotgun"); +/* wepname */ ATTRIB(OverkillShotgun, m_name, string, _("Overkill Shotgun")); + +#define X(BEGIN, P, END, class, prefix) \ + BEGIN(class) \ + P(class, prefix, ammo, float, PRI) \ + P(class, prefix, animtime, float, PRI) \ + P(class, prefix, bot_range, float, PRI) \ + P(class, prefix, bullets, float, PRI) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, force, float, PRI) \ + P(class, prefix, refire, float, PRI) \ + P(class, prefix, solidpenetration, float, PRI) \ + P(class, prefix, spread, float, PRI) \ + P(class, prefix, animtime, float, SEC) \ + P(class, prefix, damage, float, SEC) \ + P(class, prefix, delay, float, SEC) \ + P(class, prefix, edgedamage, float, SEC) \ + P(class, prefix, force, float, SEC) \ + P(class, prefix, lifetime, float, SEC) \ + P(class, prefix, radius, float, SEC) \ + P(class, prefix, refire, float, SEC) \ + P(class, prefix, refire_type, float, SEC) \ + P(class, prefix, shotangle, float, SEC) \ + P(class, prefix, speed, float, SEC) \ + P(class, prefix, spread, float, SEC) \ + P(class, prefix, reload_ammo, float, NONE) \ + P(class, prefix, reload_time, float, NONE) \ + P(class, prefix, switchdelay_drop, float, NONE) \ + P(class, prefix, switchdelay_raise, float, NONE) \ + P(class, prefix, weaponreplace, string,NONE) \ + P(class, prefix, weaponstartoverride, float, NONE) \ + P(class, prefix, weaponstart, float, NONE) \ + P(class, prefix, weaponthrowable, float, NONE) \ + END() + W_PROPS(X, OverkillShotgun, okshotgun) +#undef X + +ENDCLASS(OverkillShotgun) +REGISTER_WEAPON(OVERKILL_SHOTGUN, okshotgun, NEW(OverkillShotgun)); + +//SPAWNFUNC_WEAPON(weapon_okshotgun, WEP_OVERKILL_SHOTGUN) diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qc b/qcsrc/common/mutators/mutator/overkill/overkill.qc deleted file mode 100644 index 3cb64ce923..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/overkill.qc +++ /dev/null @@ -1 +0,0 @@ -#include "overkill.qh" diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qh b/qcsrc/common/mutators/mutator/overkill/overkill.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/overkill.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qc b/qcsrc/common/mutators/mutator/overkill/rpc.qc deleted file mode 100644 index 09e6ce29e4..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/rpc.qc +++ /dev/null @@ -1,188 +0,0 @@ -#include "rpc.qh" - -#ifdef SVQC - -void W_RocketPropelledChainsaw_Explode(entity this, entity directhitentity) -{ - this.event_damage = func_null; - this.takedamage = DAMAGE_NO; - - RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, this.weaponentity_fld, directhitentity); - - delete(this); -} - -void W_RocketPropelledChainsaw_Explode_think(entity this) -{ - W_RocketPropelledChainsaw_Explode(this, NULL); -} - -void W_RocketPropelledChainsaw_Touch (entity this, entity toucher) -{ - if(WarpZone_Projectile_Touch(this, toucher)) - if(wasfreed(this)) - return; - - W_RocketPropelledChainsaw_Explode(this, toucher); -} - -void W_RocketPropelledChainsaw_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if (this.health <= 0) - return; - - if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions - return; // g_projectiles_damage says to halt - - this.health = this.health - damage; - - if (this.health <= 0) - W_PrepareExplosionByDamage(this, attacker, W_RocketPropelledChainsaw_Explode_think); -} - -void W_RocketPropelledChainsaw_Think(entity this) -{ - if(this.cnt <= time) - { - delete(this); - return; - } - - float myspeed = vlen(this.velocity); - float myspeed_accel = myspeed * sys_frametime; - vector mydir = normalize(this.velocity); - - tracebox(this.origin, this.mins, this.maxs, this.origin + mydir * (2 * myspeed_accel), MOVE_NORMAL, this); - if(IS_PLAYER(trace_ent)) - Damage (trace_ent, this, this.realowner, WEP_CVAR(rpc, damage2), this.projectiledeathtype, this.weaponentity_fld, this.origin, normalize(this.origin - trace_ent.origin) * WEP_CVAR(rpc, force)); - - this.velocity = mydir * (myspeed + (WEP_CVAR(rpc, speedaccel) * sys_frametime)); - - UpdateCSQCProjectile(this); - this.nextthink = time; -} - -void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor, .entity weaponentity) -{ - entity missile = spawn(); //WarpZone_RefSys_SpawnSameRefSys(actor); - entity flash = spawn (); - - W_DecreaseAmmo(thiswep, actor, WEP_CVAR(rpc, ammo), weaponentity); - W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage), WEP_RPC.m_id); - Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - PROJECTILE_MAKETRIGGER(missile); - - missile.owner = missile.realowner = actor; - missile.bot_dodge = true; - missile.bot_dodgerating = WEP_CVAR(rpc, damage) * 2; - - missile.takedamage = DAMAGE_YES; - missile.damageforcescale = WEP_CVAR(rpc, damageforcescale); - missile.health = WEP_CVAR(rpc, health); - missile.event_damage = W_RocketPropelledChainsaw_Damage; - missile.damagedbycontents = true; - IL_PUSH(g_damagedbycontents, missile); - set_movetype(missile, MOVETYPE_FLY); - - missile.projectiledeathtype = WEP_RPC.m_id; - missile.weaponentity_fld = weaponentity; - setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot - - setorigin(missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point - W_SetupProjVelocity_Basic(missile, WEP_CVAR(rpc, speed), 0); - - settouch(missile, W_RocketPropelledChainsaw_Touch); - - setthink(missile, W_RocketPropelledChainsaw_Think); - missile.cnt = time + WEP_CVAR(rpc, lifetime); - missile.nextthink = time; - missile.flags = FL_PROJECTILE; - IL_PUSH(g_projectiles, missile); - IL_PUSH(g_bot_dodge, missile); - - CSQCProjectile(missile, true, PROJECTILE_RPC, false); - - setmodel(flash, MDL_RPC_MUZZLEFLASH); // precision set below - SUB_SetFade (flash, time, 0.1); - flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); - - MUTATOR_CALLHOOK(EditProjectile, actor, missile); -} - -METHOD(RocketPropelledChainsaw, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) -{ - PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(rpc, speed), 0, WEP_CVAR(rpc, lifetime), false); -} - -METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) -{ - if(WEP_CVAR(rpc, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR(rpc, ammo)) { - thiswep.wr_reload(thiswep, actor, weaponentity); - } else - { - if (fire & 1) - { - if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire))) - { - W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity); - weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready); - } - } - - if (fire & 2) - { - // to-do - } - } -} - -METHOD(RocketPropelledChainsaw, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) -{ - 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; -} - -METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) -{ - return false; -} - -METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) -{ - W_Reload(actor, weaponentity, WEP_CVAR(rpc, ammo), SND_RELOAD); -} - -METHOD(RocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep)) -{ - if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_RPC_SUICIDE_SPLASH; - else - return WEAPON_RPC_SUICIDE_DIRECT; -} - -METHOD(RocketPropelledChainsaw, wr_killmessage, Notification(entity thiswep)) -{ - if(w_deathtype & HITTYPE_SECONDARY) - return WEAPON_BLASTER_MURDER; - else if((w_deathtype & HITTYPE_BOUNCE) || (w_deathtype & HITTYPE_SPLASH)) - return WEAPON_RPC_MURDER_SPLASH; - else - return WEAPON_RPC_MURDER_DIRECT; -} - -#endif - -#ifdef CSQC - -METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity actor)) -{ - vector org2; - org2 = w_org + w_backoff * 12; - pointparticles(EFFECT_ROCKET_EXPLODE, org2, '0 0 0', 1); - if(!w_issilent) - sound(actor, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qh b/qcsrc/common/mutators/mutator/overkill/rpc.qh deleted file mode 100644 index 78d5de51ae..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/rpc.qh +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include <common/weapons/all.qh> - -CLASS(RocketPropelledChainsaw, Weapon) -/* 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); -/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); -#ifdef GAMEQC -/* model */ ATTRIB(RocketPropelledChainsaw, m_model, Model, MDL_RPC_ITEM); -#endif -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair, string, "gfx/crosshairrocketlauncher"); -/* crosshair */ ATTRIB(RocketPropelledChainsaw, w_crosshair_size, float, 0.6); -/* wepimg */ ATTRIB(RocketPropelledChainsaw, model2, string, "weaponrpc"); -/* refname */ ATTRIB(RocketPropelledChainsaw, netname, string, "rpc"); -/* wepname */ ATTRIB(RocketPropelledChainsaw, m_name, string, _("Rocket Propelled Chainsaw")); - -#define X(BEGIN, P, END, class, prefix) \ - BEGIN(class) \ - P(class, prefix, ammo, float, NONE) \ - P(class, prefix, animtime, float, NONE) \ - P(class, prefix, damage2, float, NONE) \ - P(class, prefix, damageforcescale, float, NONE) \ - P(class, prefix, damage, float, NONE) \ - P(class, prefix, edgedamage, float, NONE) \ - P(class, prefix, force, float, NONE) \ - P(class, prefix, health, float, NONE) \ - P(class, prefix, lifetime, float, NONE) \ - P(class, prefix, radius, float, NONE) \ - P(class, prefix, refire, float, NONE) \ - P(class, prefix, reload_ammo, float, NONE) \ - P(class, prefix, reload_time, float, NONE) \ - P(class, prefix, speedaccel, float, NONE) \ - P(class, prefix, speed, float, NONE) \ - P(class, prefix, switchdelay_drop, float, NONE) \ - P(class, prefix, switchdelay_raise, float, NONE) \ - P(class, prefix, weaponreplace, string, NONE) \ - P(class, prefix, weaponstartoverride, float, NONE) \ - P(class, prefix, weaponstart, float, NONE) \ - P(class, prefix, weaponthrowable, float, NONE) \ - END() - W_PROPS(X, RocketPropelledChainsaw, rpc) -#undef X - -ENDCLASS(RocketPropelledChainsaw) -REGISTER_WEAPON(RPC, rpc, NEW(RocketPropelledChainsaw)); - -SPAWNFUNC_WEAPON(weapon_rpc, WEP_RPC) diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc index e910623946..c8e4398b0d 100644 --- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -1,9 +1,8 @@ #include "sv_overkill.qh" -#include "hmg.qh" -#include "rpc.qh" - -string autocvar_g_overkill; +#include "okshotgun.qh" +#include "okhmg.qh" +#include "okrpc.qh" bool autocvar_g_overkill_powerups_replace; @@ -11,50 +10,63 @@ bool autocvar_g_overkill_itemwaypoints = true; .Weapon ok_lastwep[MAX_WEAPONSLOTS]; -REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") +IntrusiveList g_overkill_items; +STATIC_INIT() { - MUTATOR_ONADD - { - precache_all_playermodels("models/ok_player/*.dpm"); + g_overkill_items = IL_NEW(); + IL_PUSH(g_overkill_items, ITEM_HealthMega); + IL_PUSH(g_overkill_items, ITEM_ArmorSmall); + IL_PUSH(g_overkill_items, ITEM_ArmorMedium); + IL_PUSH(g_overkill_items, ITEM_ArmorBig); + IL_PUSH(g_overkill_items, ITEM_ArmorMega); +} - 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) +/// \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) +{ + RandomSelection_Init(); + IL_EACH(g_overkill_items, !(it.spawnflags & ITEM_FLAG_MUTATORBLOCKED) && + Item_IsDefinitionAllowed(it), + { + string cvar_name = sprintf("g_%s_%s_probability", prefix, + it.m_canonical_spawnfunc); + if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS)) { - ITEM_ArmorMega.spawnflags |= ITEM_FLAG_MUTATORBLOCKED; + LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name); + continue; } - - 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"; + RandomSelection_AddString(it.m_canonical_spawnfunc, cvar(cvar_name), 1); + }); + string cvar_name = sprintf("g_%s_weapon_okhmg_probability", prefix); + if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS)) + { + LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name); } - - MUTATOR_ONREMOVE + else { - 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; + RandomSelection_AddString("weapon_okhmg", cvar(cvar_name), 1); + } + cvar_name = sprintf("g_%s_weapon_okrpc_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_okrpc", cvar(cvar_name), 1); } + return RandomSelection_chosen_string; } -void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float); + +MUTATOR_HOOKFUNCTION(ok, RandomItems_GetRandomItemClassName) +{ + M_ARGV(1, string) = RandomItems_GetRandomOverkillItemClassName( + M_ARGV(0, string)); + return true; +} MUTATOR_HOOKFUNCTION(ok, Damage_Calculate, CBC_ORDER_LAST) { @@ -90,7 +102,7 @@ MUTATOR_HOOKFUNCTION(ok, PlayerDies) entity frag_attacker = M_ARGV(1, entity); entity frag_target = M_ARGV(2, entity); - entity targ = ((frag_attacker) ? frag_attacker : frag_target); + entity targ = ((IS_PLAYER(frag_attacker)) ? frag_attacker : frag_target); ok_DropItem(frag_target, targ); @@ -122,32 +134,31 @@ MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) { - if(game_stopped) + if (game_stopped) + { return; - + } entity player = M_ARGV(0, entity); - - if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player)) + if (!IS_PLAYER(player) || IS_DEAD(player) || STAT(FROZEN, player)) + { return; - - if(PHYS_INPUT_BUTTON_ATCK2(player) && time >= player.jump_interval) - if( !forbidWeaponUse(player) - || (round_handler_IsActive() && !round_handler_IsRoundStarted()) ) + } + if (!PHYS_INPUT_BUTTON_ATCK2(player) || forbidWeaponUse(player) || + !(round_handler_IsActive() && !round_handler_IsRoundStarted())) { - player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player); - makevectors(player.v_angle); - - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + return; + } + // Allow secondary blaster during countdown. + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + Weapon weapon = player.(weaponentity).m_weapon; + if (weapon == WEP_Null && slot != 0) { - .entity weaponentity = weaponentities[slot]; - - if(player.(weaponentity).m_weapon == WEP_Null && slot != 0) - continue; - - BLASTER_SECONDARY_ATTACK(vaporizer, player, weaponentity); + continue; } + weapon.wr_think(weapon, player, weaponentity, 2); } - PHYS_INPUT_BUTTON_ATCK2(player) = false; } @@ -168,10 +179,10 @@ MUTATOR_HOOKFUNCTION(ok, PlayerWeaponSelect) if(player.ok_lastwep[slot] && player.ok_lastwep[slot] != WEP_Null) { Weapon newwep = player.ok_lastwep[slot]; - if(player.ok_lastwep[slot] == WEP_HMG) - newwep = WEP_MACHINEGUN; - if(player.ok_lastwep[slot] == WEP_RPC) - newwep = WEP_VORTEX; + if(player.ok_lastwep[slot] == WEP_OVERKILL_HMG) + newwep = WEP_OVERKILL_MACHINEGUN; + if(player.ok_lastwep[slot] == WEP_OVERKILL_RPC) + newwep = WEP_OVERKILL_NEX; thiswep.m_switchweapon = newwep; player.ok_lastwep[slot] = WEP_Null; } @@ -227,7 +238,7 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem) } if (item.classname == "item_strength") { - entity wep = new(weapon_hmg); + entity wep = new(weapon_okhmg); setorigin(wep, item.origin); wep.ok_item = true; wep.noalign = Item_ShouldKeepPosition(item); @@ -236,12 +247,12 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem) wep.respawntime = g_pickup_respawntime_superweapon; wep.pickup_anyway = true; wep.spawnfunc_checked = true; - Item_Initialize(wep, "weapon_hmg"); + Item_Initialize(wep, "weapon_okhmg"); // doesn't actually use spawnfunc return true; } else if (item.classname == "item_shield") { - entity wep = new(weapon_rpc); + entity wep = new(weapon_okrpc); setorigin(wep, item.origin); wep.ok_item = true; wep.noalign = Item_ShouldKeepPosition(item); @@ -250,7 +261,7 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem) wep.respawntime = g_pickup_respawntime_superweapon; wep.pickup_anyway = true; wep.spawnfunc_checked = true; - Item_Initialize(wep, "weapon_rpc"); + Item_Initialize(wep, "weapon_okrpc"); // doesn't actually use spawnfunc return true; } return true; @@ -258,10 +269,10 @@ MUTATOR_HOOKFUNCTION(ok, FilterItem) MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST) { - WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); + WepSet ok_start_items = (WEPSET(OVERKILL_MACHINEGUN) | WEPSET(OVERKILL_NEX) | WEPSET(OVERKILL_SHOTGUN)); - if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } - if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } + if(WEP_OVERKILL_RPC.weaponstart > 0) { ok_start_items |= WEPSET(OVERKILL_RPC); } + if(WEP_OVERKILL_HMG.weaponstart > 0) { ok_start_items |= WEPSET(OVERKILL_HMG); } start_items |= IT_UNLIMITED_WEAPON_AMMO; start_weapons = warmup_start_weapons = ok_start_items; @@ -288,3 +299,4 @@ MUTATOR_HOOKFUNCTION(ok, SetModname) M_ARGV(0, string) = "Overkill"; return true; } + diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh index 72324e6dbb..79e5dd3c5b 100644 --- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh @@ -1,8 +1,42 @@ #pragma once +string autocvar_g_overkill; 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; + +REGISTER_MUTATOR(ok, expr_evaluate(autocvar_g_overkill) && !MUTATOR_IS_ENABLED(mutator_instagib) && !g_nexball && cvar_string("g_mod_balance") == "Overkill") +{ + MUTATOR_ONADD + { + 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; + } + } + + 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; + } +} diff --git a/qcsrc/common/mutators/mutator/overkill/sv_weapons.qc b/qcsrc/common/mutators/mutator/overkill/sv_weapons.qc new file mode 100644 index 0000000000..4a131e3f03 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/sv_weapons.qc @@ -0,0 +1,22 @@ +string autocvar_g_overkill_weapons; + +REGISTER_MUTATOR(ok_weapons, expr_evaluate(autocvar_g_overkill_weapons) || MUTATOR_IS_ENABLED(ok)) +{ + MUTATOR_ONADD + { + WEP_OVERKILL_SHOTGUN.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_MACHINEGUN.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_NEX.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + } + + MUTATOR_ONREMOVE + { + WEP_OVERKILL_SHOTGUN.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_MACHINEGUN.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_NEX.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_OVERKILL_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } +} diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc index 1084ff7789..779e562b4a 100644 --- a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc @@ -1,7 +1,7 @@ #include "sv_pinata.qh" string autocvar_g_pinata; -REGISTER_MUTATOR(pinata, expr_evaluate(autocvar_g_pinata) && !cvar("g_instagib") && !cvar("g_overkill")); +REGISTER_MUTATOR(pinata, expr_evaluate(autocvar_g_pinata) && !MUTATOR_IS_ENABLED(mutator_instagib) && !MUTATOR_IS_ENABLED(ok)); MUTATOR_HOOKFUNCTION(pinata, PlayerDies) { @@ -15,7 +15,7 @@ MUTATOR_HOOKFUNCTION(pinata, PlayerDies) continue; FOREACH(Weapons, it != WEP_Null, { - if(frag_target.weapons & WepSet_FromWeapon(it)) + if(STAT(WEAPONS, frag_target) & WepSet_FromWeapon(it)) if(frag_target.(weaponentity).m_weapon != it) if(W_IsWeaponThrowable(frag_target, it.m_id)) W_ThrowNewWeapon(frag_target, it.m_id, false, CENTER_OR_VIEWOFS(frag_target), randomvec() * 175 + '0 0 325', weaponentity); diff --git a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc index 3d30574626..183808021b 100644 --- a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc +++ b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qc @@ -7,15 +7,6 @@ //============================ 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 @@ -33,8 +24,6 @@ enum // 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. @@ -64,162 +53,143 @@ string RandomItems_GetRandomItemClassNameWithProperty(string prefix, string RandomItems_GetRandomItemClassName(string prefix) { - if (autocvar_g_instagib) + if (MUTATOR_CALLHOOK(RandomItems_GetRandomItemClassName, prefix)) { - return RandomItems_GetRandomInstagibItemClassName(prefix); + return M_ARGV(1, string); } - if (expr_evaluate(autocvar_g_overkill)) - { - return RandomItems_GetRandomOverkillItemClassName(prefix); - } - return RandomItems_GetRandomVanillaItemClassName(prefix); + return RandomItems_GetRandomVanillaItemClassName(prefix, + RANDOM_ITEM_TYPE_ALL); } -string RandomItems_GetRandomVanillaItemClassName(string prefix) +string RandomItems_GetRandomVanillaItemClassName(string prefix, int types) { - 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)) + if (types == 0) { - 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); + return ""; } - int item_type = RandomSelection_chosen_float; - switch (item_type) + while (types != 0) { - case RANDOM_ITEM_TYPE_HEALTH: + string cvar_name; + RandomSelection_Init(); + if (types & RANDOM_ITEM_TYPE_HEALTH) { - return RandomItems_GetRandomItemClassNameWithProperty(prefix, - instanceOfHealth); + 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); + } } - case RANDOM_ITEM_TYPE_ARMOR: + if (types & RANDOM_ITEM_TYPE_ARMOR) { - return RandomItems_GetRandomItemClassNameWithProperty(prefix, - instanceOfArmor); + 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); + } } - case RANDOM_ITEM_TYPE_RESOURCE: + if (types & RANDOM_ITEM_TYPE_RESOURCE) { - return RandomItems_GetRandomItemClassNameWithProperty(prefix, - instanceOfAmmo); + 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); + } } - case RANDOM_ITEM_TYPE_WEAPON: + if (types & RANDOM_ITEM_TYPE_WEAPON) { - RandomSelection_Init(); - FOREACH(Weapons, it != WEP_Null && - !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED), + cvar_name = sprintf("g_%s_weapon_probability", prefix); + if (!(cvar_type(cvar_name) & CVAR_TYPEFLAG_EXISTS)) { - 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; + LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name); + } + else + { + RandomSelection_AddFloat(RANDOM_ITEM_TYPE_WEAPON, cvar(cvar_name), 1); + } } - case RANDOM_ITEM_TYPE_POWERUP: + if (types & RANDOM_ITEM_TYPE_POWERUP) { - return RandomItems_GetRandomItemClassNameWithProperty(prefix, - instanceOfPowerup); + 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); + } } - } - 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)) + int item_type = RandomSelection_chosen_float; + string class_name = ""; + switch (item_type) { - LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name); - continue; + case RANDOM_ITEM_TYPE_HEALTH: + { + class_name = RandomItems_GetRandomItemClassNameWithProperty( + prefix, instanceOfHealth); + break; + } + case RANDOM_ITEM_TYPE_ARMOR: + { + class_name = RandomItems_GetRandomItemClassNameWithProperty( + prefix, instanceOfArmor); + break; + } + case RANDOM_ITEM_TYPE_RESOURCE: + { + class_name = RandomItems_GetRandomItemClassNameWithProperty( + prefix, instanceOfAmmo); + break; + } + 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); + }); + class_name = RandomSelection_chosen_string; + break; + } + case RANDOM_ITEM_TYPE_POWERUP: + { + class_name = RandomItems_GetRandomItemClassNameWithProperty( + prefix, instanceOfPowerup); + break; + } } - 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)) + if (class_name != "") { - LOG_WARNF("Random items: cvar %s doesn't exist.", cvar_name); - continue; + return class_name; } - 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); + types &= ~item_type; } - 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; + return ""; } //========================= Free functions ==================================== @@ -242,7 +212,8 @@ string RandomItems_GetRandomItemClassNameWithProperty(string prefix, .bool item_property) { RandomSelection_Init(); - FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL), + FOREACH(Items, it.item_property && (it.spawnflags & ITEM_FLAG_NORMAL) && + Item_IsDefinitionAllowed(it), { string cvar_name = sprintf("g_%s_%s_probability", prefix, it.m_canonical_spawnfunc); @@ -296,7 +267,7 @@ entity RandomItems_ReplaceMapItem(entity item) } random_items_is_spawning = true; entity new_item; - if (!expr_evaluate(autocvar_g_overkill)) + if (!MUTATOR_IS_ENABLED(ok)) { new_item = Item_Create(strzone(new_classname), item.origin, Item_ShouldKeepPosition(item)); @@ -342,7 +313,7 @@ void RandomItems_SpawnLootItem(vector position) 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)) + if (!MUTATOR_IS_ENABLED(ok)) { Item_CreateLoot(class_name, position, spread, autocvar_g_random_loot_time); @@ -360,9 +331,6 @@ void RandomItems_SpawnLootItem(vector position) //============================= 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"); diff --git a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh index c9b4dbb901..c94375ceae 100644 --- a/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh +++ b/qcsrc/common/mutators/mutator/random_items/sv_random_items.qh @@ -6,6 +6,17 @@ /// \copyright GNU GPLv2 or any later version. bool autocvar_g_random_items; ///< Whether to enable random items. +bool autocvar_g_random_loot; ///< Whether to enable random loot. + +enum +{ + RANDOM_ITEM_TYPE_HEALTH = BIT(0), + RANDOM_ITEM_TYPE_ARMOR = BIT(1), + RANDOM_ITEM_TYPE_RESOURCE = BIT(2), + RANDOM_ITEM_TYPE_WEAPON = BIT(3), + RANDOM_ITEM_TYPE_POWERUP = BIT(4), + RANDOM_ITEM_TYPE_ALL = BITS(5) +}; /// \brief Returns a random classname of the item. /// \param[in] prefix Prefix of the cvars that hold probabilities. @@ -16,17 +27,19 @@ string RandomItems_GetRandomItemClassName(string prefix); /// \brief Returns a random classname of the vanilla item. /// \param[in] prefix Prefix of the cvars that hold probabilities. +/// \param[in] types Bitmask of the types. See RANDOM_ITEM_TYPE constants. /// \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); +string RandomItems_GetRandomVanillaItemClassName(string prefix, int types); -/// \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 Called when random item classname is requested. +#define EV_RandomItems_GetRandomItemClassName(i, o) \ + /** prefix */ i(string, MUTATOR_ARGV_0_string) \ + /** classname */ o(string, MUTATOR_ARGV_1_string) \ + /**/ +MUTATOR_HOOKABLE(RandomItems_GetRandomItemClassName, + EV_RandomItems_GetRandomItemClassName); -/// \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); +REGISTER_MUTATOR(random_items, (autocvar_g_random_items || + autocvar_g_random_loot)); diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc index 7edef9813d..c6def68e1b 100644 --- a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc +++ b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc @@ -3,7 +3,7 @@ #include <common/deathtypes/all.qh> #include <server/round_handler.qh> -REGISTER_MUTATOR(rm, cvar("g_instagib")); +REGISTER_MUTATOR(rm, autocvar_g_instagib); MUTATOR_HOOKFUNCTION(rm, Damage_Calculate) { diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc index d121cf1094..9458189fd0 100644 --- a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc @@ -212,11 +212,11 @@ void sandbox_ObjectRemove(entity e) // if the object being removed has been selected for attachment by a player, unset it FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.object_attach == e, { it.object_attach = NULL; }); - if(e.material) { strunzone(e.material); e.material = string_null; } - if(e.crypto_idfp) { strunzone(e.crypto_idfp); e.crypto_idfp = string_null; } - if(e.netname) { strunzone(e.netname); e.netname = string_null; } - if(e.message) { strunzone(e.message); e.message = string_null; } - if(e.message2) { strunzone(e.message2); e.message2 = string_null; } + strfree(e.material); + strfree(e.crypto_idfp); + strfree(e.netname); + strfree(e.message); + strfree(e.message2); delete(e); e = NULL; @@ -312,8 +312,9 @@ string sandbox_ObjectPort_Save(entity e, bool database) entity sandbox_ObjectPort_Load(entity this, string s, float database) { // load object properties, and spawn a new object with them - float n, i; + int n, i; entity e = NULL, parent = NULL; + string arg = string_null; // separate objects between the ; symbols n = tokenizebyseparator(s, "; "); @@ -323,9 +324,10 @@ entity sandbox_ObjectPort_Load(entity this, string s, float database) // now separate and apply the properties of each object for(i = 0; i < n; ++i) { - float argv_num; + #define SANDBOX_GETARG arg = argv(++argv_num); + int argv_num = -1; // starts at -1 so I don't need postincrement + string tagname = string_null; - argv_num = 0; tokenize_console(port_string[i]); e = sandbox_ObjectSpawn(this, database); @@ -333,38 +335,40 @@ entity sandbox_ObjectPort_Load(entity this, string s, float database) if(i) { // properties stored only for child objects - if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; + SANDBOX_GETARG; tagname = (arg != "") ? arg : string_null; } else { // properties stored only for parent objects if(database) { - setorigin(e, stov(argv(argv_num))); ++argv_num; - e.angles = stov(argv(argv_num)); ++argv_num; + SANDBOX_GETARG; setorigin(e, stov(arg)); + SANDBOX_GETARG; e.angles = stov(arg); } parent = e; // mark parent objects as such } // properties stored for all objects - _setmodel(e, argv(argv_num)); ++argv_num; - e.skin = stof(argv(argv_num)); ++argv_num; - e.alpha = stof(argv(argv_num)); ++argv_num; - e.colormod = stov(argv(argv_num)); ++argv_num; - e.glowmod = stov(argv(argv_num)); ++argv_num; - e.frame = stof(argv(argv_num)); ++argv_num; - sandbox_ObjectEdit_Scale(e, stof(argv(argv_num))); ++argv_num; - e.solid = e.old_solid = stof(argv(argv_num)); ++argv_num; - e.old_movetype = stof(argv(argv_num)); ++argv_num; + SANDBOX_GETARG; _setmodel(e, arg); + SANDBOX_GETARG; e.skin = stof(arg); + SANDBOX_GETARG; e.alpha = stof(arg); + SANDBOX_GETARG; e.colormod = stov(arg); + SANDBOX_GETARG; e.glowmod = stov(arg); + SANDBOX_GETARG; e.frame = stof(arg); + SANDBOX_GETARG; sandbox_ObjectEdit_Scale(e, stof(arg)); + SANDBOX_GETARG; e.solid = e.old_solid = stof(arg); + SANDBOX_GETARG; e.old_movetype = stof(arg); set_movetype(e, e.old_movetype); - e.damageforcescale = stof(argv(argv_num)); ++argv_num; - if(e.material) strunzone(e.material); if(argv(argv_num) != "") e.material = strzone(argv(argv_num)); else e.material = string_null; ++argv_num; + SANDBOX_GETARG; e.damageforcescale = stof(arg); + strfree(e.material); + SANDBOX_GETARG; e.material = (arg != "") ? strzone(arg) : string_null; if(database) { // properties stored only for the database - if(e.crypto_idfp) strunzone(e.crypto_idfp); if(argv(argv_num) != "") e.crypto_idfp = strzone(argv(argv_num)); else e.crypto_idfp = string_null; ++argv_num; - if(e.netname) strunzone(e.netname); e.netname = strzone(argv(argv_num)); ++argv_num; - if(e.message) strunzone(e.message); e.message = strzone(argv(argv_num)); ++argv_num; - if(e.message2) strunzone(e.message2); e.message2 = strzone(argv(argv_num)); ++argv_num; + strfree(e.crypto_idfp); + SANDBOX_GETARG; e.crypto_idfp = (arg != "") ? strzone(arg) : string_null; + SANDBOX_GETARG; strcpy(e.netname, arg); + SANDBOX_GETARG; strcpy(e.message, arg); + SANDBOX_GETARG; strcpy(e.message2, arg); } // attach last @@ -702,7 +706,7 @@ MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) e.damageforcescale = stof(argv(3)); break; case "material": - if(e.material) strunzone(e.material); + strfree(e.material); if(argv(3)) { for (j = 1; j <= 5; j++) // precache material sounds, 5 in total @@ -718,8 +722,7 @@ MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) } // update last editing time - if(e.message2) strunzone(e.message2); - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); + strcpy(e.message2, strftime(true, "%d-%m-%Y %H:%M:%S")); if(autocvar_g_sandbox_info > 1) LOG_INFO("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin)); @@ -745,8 +748,7 @@ MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) // also update the player's nickname if he changed it (but has the same player UID) if(e.netname != player.netname) { - if(e.netname) strunzone(e.netname); - e.netname = strzone(player.netname); + strcpy(e.netname, player.netname); print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated"); } @@ -756,8 +758,7 @@ MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) return true; } - if(e.crypto_idfp) strunzone(e.crypto_idfp); - e.crypto_idfp = strzone(player.crypto_idfp); + strcpy(e.crypto_idfp, player.crypto_idfp); print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully"); } diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc index 61c302c3e7..fdcc4beee5 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -90,8 +90,8 @@ MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_max && tested >= autocvar_g_spawn_near_teammate_ignore_spawnpoint_max) break; if (PHYS_INPUT_BUTTON_CHAT(it)) continue; - if (!SAME_TEAM(player, it)) continue; - if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && it.health < autocvar_g_balance_health_regenstable) continue; + if (DIFF_TEAM(player, it)) continue; + if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health && GetResourceAmount(it, RESOURCE_HEALTH) < autocvar_g_balance_health_regenstable) continue; if (IS_DEAD(it)) continue; if (time < it.msnt_timer) continue; if (time < it.spawnshieldtime) continue; diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc index 199b4e202a..56198186f1 100644 --- a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -1,7 +1,7 @@ #include "sv_vampire.qh" string autocvar_g_vampire; -REGISTER_MUTATOR(vampire, expr_evaluate(autocvar_g_vampire) && !cvar("g_instagib")); +REGISTER_MUTATOR(vampire, expr_evaluate(autocvar_g_vampire) && !MUTATOR_IS_ENABLED(mutator_instagib)); MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) { @@ -14,7 +14,7 @@ MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) if(!IS_DEAD(frag_target)) { GiveResource(frag_attacker, RESOURCE_HEALTH, - bound(0, damage_take, frag_target.health)); + bound(0, damage_take, GetResourceAmount(frag_target, RESOURCE_HEALTH))); } } diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc index d0f01a576a..e74cfb1152 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc @@ -21,18 +21,18 @@ MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) if(!STAT(FROZEN, thehook.aiment)) if(time >= game_starttime) if(DIFF_TEAM(thehook.owner, thehook.aiment) || autocvar_g_vampirehook_teamheal) - if(thehook.aiment.health > 0) + if(GetResourceAmount(thehook.aiment, RESOURCE_HEALTH) > 0) if(autocvar_g_vampirehook_damage) { thehook.last_dmg = time + autocvar_g_vampirehook_damagerate; thehook.owner.damage_dealt += autocvar_g_vampirehook_damage; Damage(dmgent, thehook, thehook.owner, autocvar_g_vampirehook_damage, WEP_HOOK.m_id, DMG_NOWEP, thehook.origin, '0 0 0'); - if(SAME_TEAM(thehook.owner, thehook.aiment)) - thehook.aiment.health = min(thehook.aiment.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); - else - thehook.owner.health = min(thehook.owner.health + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + entity targ = ((SAME_TEAM(thehook.owner, thehook.aiment)) ? thehook.aiment : thehook.owner); + // TODO: we can't do this due to an issue with globals and the mutator arguments + //Heal(targ, thehook.owner, autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max); + SetResourceAmountExplicit(targ, RESOURCE_HEALTH, min(GetResourceAmount(targ, RESOURCE_HEALTH) + autocvar_g_vampirehook_health_steal, g_pickup_healthsmall_max)); if(dmgent == thehook.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + TakeResource(dmgent, RESOURCE_HEALTH, autocvar_g_vampirehook_damage); // FIXME: friendly fire?! } } diff --git a/qcsrc/common/mutators/mutator/waypoints/all.inc b/qcsrc/common/mutators/mutator/waypoints/all.inc index b527bdc1a8..c8c4db546a 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.inc +++ b/qcsrc/common/mutators/mutator/waypoints/all.inc @@ -14,8 +14,8 @@ REGISTER_WAYPOINT(RaceFinish, _("Finish"), "", '1 0.5 0', 1); REGISTER_WAYPOINT(RaceStart, _("Start"), "", '1 0.5 0', 1); REGISTER_WAYPOINT(RaceStartFinish, _("Start"), "", '1 0.5 0', 1); -REGISTER_WAYPOINT(AssaultDefend, _("Defend"), "", '1 0.5 0', 1); -REGISTER_WAYPOINT(AssaultDestroy, _("Destroy"), "", '1 0.5 0', 1); +REGISTER_WAYPOINT(AssaultDefend, _("Defend"), "as_defend", '1 0.5 0', 1); +REGISTER_WAYPOINT(AssaultDestroy, _("Destroy"), "as_destroy", '1 0.5 0', 1); REGISTER_WAYPOINT(AssaultPush, _("Push"), "", '1 0.5 0', 1); REGISTER_WAYPOINT(FlagCarrier, _("Flag carrier"), "", '0.8 0.8 0', 1); diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index be5b40c14c..66904d0070 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -34,7 +34,7 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) { if (this.max_health) { - WriteByte(MSG_ENTITY, (this.health / this.max_health) * 191.0); + WriteByte(MSG_ENTITY, (GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 191.0); } else { @@ -106,9 +106,9 @@ NET_HANDLE(waypointsprites, bool isnew) { void Ent_RemoveWaypointSprite(entity this) { - if (this.netname) strunzone(this.netname); - if (this.netname2) strunzone(this.netname2); - if (this.netname3) strunzone(this.netname3); + strfree(this.netname); + strfree(this.netname2); + strfree(this.netname3); } /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */ @@ -134,7 +134,7 @@ void Ent_WaypointSprite(entity this, bool isnew) int t = ReadByte(); if (t < 192) { - this.health = t / 191.0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, t / 191.0); this.build_finished = 0; } else @@ -142,7 +142,7 @@ void Ent_WaypointSprite(entity this, bool isnew) t = (t - 192) * 256 + ReadByte(); this.build_started = servertime; if (this.build_finished) - this.build_starthealth = bound(0, this.health, 1); + this.build_starthealth = bound(0, GetResourceAmount(this, RESOURCE_HEALTH), 1); else this.build_starthealth = 0; this.build_finished = servertime + t / 32; @@ -150,7 +150,7 @@ void Ent_WaypointSprite(entity this, bool isnew) } else { - this.health = -1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, -1); this.build_finished = 0; } @@ -169,23 +169,17 @@ void Ent_WaypointSprite(entity this, bool isnew) if (sendflags & 2) { - if (this.netname) - strunzone(this.netname); - this.netname = strzone(ReadString()); + strcpy(this.netname, ReadString()); } if (sendflags & 4) { - if (this.netname2) - strunzone(this.netname2); - this.netname2 = strzone(ReadString()); + strcpy(this.netname2, ReadString()); } if (sendflags & 8) { - if (this.netname3) - strunzone(this.netname3); - this.netname3 = strzone(ReadString()); + strcpy(this.netname3, ReadString()); } if (sendflags & 16) @@ -558,11 +552,13 @@ void Draw_WaypointSprite(entity this) LOG_INFOF("WARNING: sprite of name %s has no color, using pink so you notice it", spriteimage); } - if (time - floor(time) > 0.5) + float health_val = GetResourceAmount(this, RESOURCE_HEALTH); + float blink_time = (health_val >= 0) ? (health_val * 10) : time; + if (blink_time - floor(blink_time) > 0.5) { if (this.helpme && time < this.helpme) a *= SPRITE_HELPME_BLINK; - else if (this.lifetime > 0) // fading out waypoints don't blink + else if (!this.lifetime) // fading out waypoints don't blink a *= spritelookupblinkvalue(this, spriteimage); } @@ -589,15 +585,16 @@ void Draw_WaypointSprite(entity this) { // scale it to be just in view vector d; - float f1, f2; d = o - '0.5 0 0' * vid_conwidth - '0 0.5 0' * vid_conheight; ang = atan2(-d.x, -d.y); if (o.z < 0) ang += M_PI; - f1 = d.x / vid_conwidth; - f2 = d.y / vid_conheight; + float f1 = d.x / vid_conwidth; + float f2 = d.y / vid_conheight; + if (f1 == 0) { f1 = 0.000001; } + if (f2 == 0) { f2 = 0.000001; } if (max(f1, -f1) > max(f2, -f2)) { if (d.z * f1 > 0) { @@ -659,14 +656,14 @@ void Draw_WaypointSprite(entity this) if (time < this.build_finished + 0.25) { if (time < this.build_started) - this.health = this.build_starthealth; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.build_starthealth); else if (time < this.build_finished) - this.health = (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, (time - this.build_started) / (this.build_finished - this.build_started) * (1 - this.build_starthealth) + this.build_starthealth); else - this.health = 1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 1); } else - this.health = -1; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, -1); } o = drawspritearrow(o, ang, rgb, a, SPRITE_ARROW_SCALE * t); @@ -714,7 +711,7 @@ void Draw_WaypointSprite(entity this) } draw_beginBoldFont(); - if (this.health >= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) >= 0) { float align = 0, marg; if (this.build_finished) @@ -731,7 +728,7 @@ void Draw_WaypointSprite(entity this) drawhealthbar( o, 0, - this.health, + GetResourceAmount(this, RESOURCE_HEALTH), '0 0 0', '0 0 0', SPRITE_HEALTHBAR_WIDTH * t, @@ -836,9 +833,9 @@ void WaypointSprite_UpdateSprites(entity e, entity _m1, entity _m2, entity _m3) void WaypointSprite_UpdateHealth(entity e, float f) { f = bound(0, f, e.max_health); - if (f != e.health || e.pain_finished) + if (f != GetResourceAmount(e, RESOURCE_HEALTH) || e.pain_finished) { - e.health = f; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, f); e.pain_finished = 0; e.SendFlags |= 0x80; } @@ -1106,7 +1103,7 @@ entity WaypointSprite_SpawnFixed( entity WaypointSprite_DeployFixed( entity spr, - float limited_range, + bool limited_range, entity player, vector ofs, entity icon // initial icon @@ -1138,7 +1135,7 @@ entity WaypointSprite_DeployPersonal( entity WaypointSprite_Attach( entity spr, entity player, - float limited_range, + bool limited_range, entity icon // initial icon ) { @@ -1165,10 +1162,10 @@ entity WaypointSprite_AttachCarrier( { WaypointSprite_Kill(carrier.waypointsprite_attached); // FC overrides attached entity e = WaypointSprite_Spawn(spr, 0, 0, carrier, '0 0 64', NULL, carrier.team, carrier, waypointsprite_attachedforcarrier, false, icon); - if (carrier.health) + if (GetResourceAmount(carrier, RESOURCE_HEALTH)) { WaypointSprite_UpdateMaxHealth(e, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2); - WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(carrier.health, carrier.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); + WaypointSprite_UpdateHealth(e, '1 0 0' * healtharmor_maxdamage(GetResourceAmount(carrier, RESOURCE_HEALTH), GetResourceAmount(carrier, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); } return e; } diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index 95b8890ccc..e2190b66a2 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -11,21 +11,21 @@ const int SPRITERULE_SPECTATOR = 2; #ifdef CSQC entityclass(WaypointSprite); -class(WaypointSprite) .float helpme; -class(WaypointSprite) .float rule; -class(WaypointSprite) .string netname; // primary picture -class(WaypointSprite) .string netname2; // secondary picture -class(WaypointSprite) .string netname3; // tertiary picture -class(WaypointSprite) .int team; // team that gets netname2 -class(WaypointSprite) .float lifetime; -class(WaypointSprite) .float fadetime; -class(WaypointSprite) .float maxdistance; -class(WaypointSprite) .int hideflags; -class(WaypointSprite) .float spawntime; -class(WaypointSprite) .float health; -class(WaypointSprite) .float build_started; -class(WaypointSprite) .float build_starthealth; -class(WaypointSprite) .float build_finished; +classfield(WaypointSprite) .float helpme; +classfield(WaypointSprite) .float rule; +classfield(WaypointSprite) .string netname; // primary picture +classfield(WaypointSprite) .string netname2; // secondary picture +classfield(WaypointSprite) .string netname3; // tertiary picture +classfield(WaypointSprite) .int team; // team that gets netname2 +classfield(WaypointSprite) .float lifetime; +classfield(WaypointSprite) .float fadetime; +classfield(WaypointSprite) .float maxdistance; +classfield(WaypointSprite) .int hideflags; +classfield(WaypointSprite) .float spawntime; +classfield(WaypointSprite) .float health; +classfield(WaypointSprite) .float build_started; +classfield(WaypointSprite) .float build_starthealth; +classfield(WaypointSprite) .float build_finished; float autocvar_g_waypointsprite_alpha; float autocvar_g_waypointsprite_crosshairfadealpha; @@ -52,6 +52,7 @@ int autocvar_g_waypointsprite_spam; float autocvar_g_waypointsprite_timealphaexponent; bool autocvar_g_waypointsprite_turrets = true; float autocvar_g_waypointsprite_turrets_maxdist = 5000; +bool autocvar_g_waypointsprite_turrets_text = false; bool autocvar_g_waypointsprite_uppercase; bool autocvar_g_waypointsprite_text; float autocvar_g_waypointsprite_iconsize = 32; @@ -203,7 +204,7 @@ entity WaypointSprite_SpawnFixed( .entity waypointsprite_deployed_fixed; entity WaypointSprite_DeployFixed( entity spr, - float limited_range, + bool limited_range, entity player, vector ofs, entity icon // initial icon @@ -222,7 +223,7 @@ entity WaypointSprite_DeployPersonal( entity WaypointSprite_Attach( entity spr, entity player, - float limited_range, + bool limited_range, entity icon // initial icon ); diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc index 7ac4504ec9..32b6f39305 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc @@ -3,12 +3,73 @@ // WEAPONTODO: rename the cvars REGISTER_MUTATOR(weaponarena_random, true); +MUTATOR_HOOKFUNCTION(weaponarena_random, SetStartItems) +{ + if(g_weaponarena) + g_weaponarena_random = cvar("g_weaponarena_random"); + else + g_weaponarena_random = 0; + + g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster"); +} + MUTATOR_HOOKFUNCTION(weaponarena_random, PlayerSpawn) { if (!g_weaponarena_random) return; entity player = M_ARGV(0, entity); - if (g_weaponarena_random_with_blaster) player.weapons &= ~WEPSET(BLASTER); - W_RandomWeapons(player, g_weaponarena_random); - if (g_weaponarena_random_with_blaster) player.weapons |= WEPSET(BLASTER); + if (g_weaponarena_random_with_blaster) STAT(WEAPONS, player) &= ~WEPSET(BLASTER); + STAT(WEAPONS, player) = W_RandomWeapons(player, STAT(WEAPONS, player), g_weaponarena_random); + if (g_weaponarena_random_with_blaster) STAT(WEAPONS, player) |= WEPSET(BLASTER); +} + +MUTATOR_HOOKFUNCTION(weaponarena_random, GiveFragsForKill) +{ + if(!g_weaponarena_random) return; + entity attacker = M_ARGV(0, entity); + entity targ = M_ARGV(1, entity); + float deathtype = M_ARGV(3, float); + entity wep_ent = M_ARGV(4, entity); + .entity weaponentity = wep_ent.weaponentity_fld; + + if(targ == attacker) return; // not for suicides + + // after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon + Weapon culprit = DEATH_WEAPONOF(deathtype); + if(!culprit) culprit = wep_ent.m_weapon; + else if(!(STAT(WEAPONS, attacker) & (culprit.m_wepset))) culprit = wep_ent.m_weapon; + + if(g_weaponarena_random_with_blaster && culprit == WEP_BLASTER) + { + // no exchange + } + else + { + if(!GiveFrags_randomweapons) + { + GiveFrags_randomweapons = new(GiveFrags_randomweapons); + } + + if(warmup_stage) + STAT(WEAPONS, GiveFrags_randomweapons) = WARMUP_START_WEAPONS; + else + STAT(WEAPONS, GiveFrags_randomweapons) = start_weapons; + + // all others (including the culprit): remove + STAT(WEAPONS, GiveFrags_randomweapons) &= ~STAT(WEAPONS, attacker); + STAT(WEAPONS, GiveFrags_randomweapons) &= ~(culprit.m_wepset); + + // among the remaining ones, choose one by random + STAT(WEAPONS, GiveFrags_randomweapons) = W_RandomWeapons(GiveFrags_randomweapons, STAT(WEAPONS, GiveFrags_randomweapons), 1); + + if(STAT(WEAPONS, GiveFrags_randomweapons)) + { + STAT(WEAPONS, attacker) |= STAT(WEAPONS, GiveFrags_randomweapons); + STAT(WEAPONS, attacker) &= ~(culprit.m_wepset); + } + } + + // after a frag, choose another random weapon set + if (!(STAT(WEAPONS, attacker) & WepSet_FromWeapon(wep_ent.m_weapon))) + W_SwitchWeapon_Force(attacker, w_getbestweapon(attacker, weaponentity), weaponentity); } diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh index 6f70f09bee..4af3cf0362 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh @@ -1 +1,5 @@ #pragma once + +float g_weaponarena_random_with_blaster; + +entity GiveFrags_randomweapons; diff --git a/qcsrc/common/net_linked.qh b/qcsrc/common/net_linked.qh index 7b69ad5030..6651c6cb9b 100644 --- a/qcsrc/common/net_linked.qh +++ b/qcsrc/common/net_linked.qh @@ -22,6 +22,7 @@ const int RACE_NET_SERVER_RANKINGS = 11; const int RACE_NET_SERVER_STATUS = 12; const int RACE_NET_CHECKPOINT_HIT_SELF_QUALIFYING = 13; // byte checkpoint, short time, short recordtime const int RACE_NET_CHECKPOINT_NEXT_SELF_QUALIFYING = 14; // byte nextcheckpoint, short recordtime +const int RACE_NET_RANKINGS_CNT = 15; REGISTER_NET_LINKED(_ENT_CLIENT_INIT) #ifdef CSQC @@ -55,3 +56,5 @@ REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT) REGISTER_NET_LINKED(ENT_CLIENT_WALL) #include <lib/csqcmodel/net.qh> + +REGISTER_NET_C2S(fpsreport) diff --git a/qcsrc/common/net_notice.qh b/qcsrc/common/net_notice.qh index 1134205185..0eecd6bdf3 100644 --- a/qcsrc/common/net_notice.qh +++ b/qcsrc/common/net_notice.qh @@ -11,4 +11,6 @@ void sv_notice_join(entity _to); #ifdef CSQC void cl_notice_read(); + +void cl_notice_run(); #endif diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index b4c6146bb6..1b93cbf359 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -380,7 +380,7 @@ MSG_INFO_NOTIF(ITEM_BUFF_GOT, N_CONSOLE, 0, 1, "item_buffname", "", "", _("^BGYou got the %s^BG buff!"), "") MSG_INFO_NOTIF(ITEM_WEAPON_DONTHAVE, N_DISABLE, 0, 1, "item_wepname", "", "", _("^BGYou do not have the ^F1%s"), "") - MSG_INFO_NOTIF(ITEM_WEAPON_DROP, N_DISABLE, 1, 1, "item_wepname item_wepammo", "", "", _("^BGYou dropped the ^F1%s^BG%s"), "") + MSG_INFO_NOTIF(ITEM_WEAPON_DROP, N_DISABLE, 0, 2, "item_wepname item_wepammo", "", "", _("^BGYou dropped the ^F1%s^BG%s"), "") MSG_INFO_NOTIF(ITEM_WEAPON_GOT, N_DISABLE, 0, 1, "item_wepname", "", "", _("^BGYou got the ^F1%s"), "") MSG_INFO_NOTIF(ITEM_WEAPON_NOAMMO, N_DISABLE, 0, 1, "item_wepname", "", "", _("^BGYou don't have enough ammo for the ^F1%s"), "") MSG_INFO_NOTIF(ITEM_WEAPON_PRIMORSEC, N_DISABLE, 0, 3, "item_wepname f2primsec f3primsec", "", "", _("^F1%s %s^BG is unable to fire, but its ^F1%s^BG can"), "") @@ -388,7 +388,6 @@ MSG_INFO_NOTIF(CONNECTING, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^BG is connecting..."), "") MSG_INFO_NOTIF(JOIN_CONNECT, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 connected"), "") - MULTITEAM_INFO(JOIN_CONNECT_TEAM, 4, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 connected and joined the ^TC^TT team"), "", NAME) MSG_INFO_NOTIF(JOIN_PLAY, N_CONSOLE, 1, 0, "s1", "", "", _("^BG%s^F3 is now playing"), "") MULTITEAM_INFO(JOIN_PLAY_TEAM, 4, N_CHATCON, 1, 0, "s1", "", "", _("^BG%s^F3 is now playing on the ^TC^TT team"), "", NAME) @@ -423,7 +422,7 @@ 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(QUIT_SPECTATE, N_CHATCON, 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(RACE_FAIL_RANKED, N_CONSOLE, 1, 3, "s1 race_col f1ord race_col f3race_time race_diff", "s1 f3race_time", "race_newfail", _("^BG%s^BG couldn't break their %s%s^BG place record of %s%s %s"), "") @@ -446,7 +445,7 @@ MSG_INFO_NOTIF(TEAMCHANGE_LARGERTEAM, N_CONSOLE, 0, 0, "", "", "", _("^BGYou cannot change to a larger team"), "") MSG_INFO_NOTIF(TEAMCHANGE_NOTALLOWED, N_CONSOLE, 0, 0, "", "", "", _("^BGYou are not allowed to change teams"), "") - MSG_INFO_NOTIF(VERSION_BETA, N_CHATCON, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "") + MSG_INFO_NOTIF(VERSION_BETA, N_CONSOLE, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s (beta)^BG, you have ^F2Xonotic %s"), "") MSG_INFO_NOTIF(VERSION_OLD, N_CHATCON, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^BGThe server is running ^F1Xonotic %s^BG, you have ^F2Xonotic %s"), "") MSG_INFO_NOTIF(VERSION_OUTDATED, N_CHATCON, 2, 0, "s1 s2", "", "", _("^F4NOTE: ^F1Xonotic %s^BG is out, and you still have ^F2Xonotic %s^BG - get the update from ^F3http://www.xonotic.org/^BG!"), "") @@ -477,8 +476,6 @@ MSG_INFO_NOTIF(WEAPON_HAGAR_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponhagar", _("^BG%s^K1 played with tiny Hagar rockets%s%s"), "") MSG_INFO_NOTIF(WEAPON_HLAC_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhlac", _("^BG%s%s^K1 was cut down with ^BG%s^K1's HLAC%s%s"), "") MSG_INFO_NOTIF(WEAPON_HLAC_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponhlac", _("^BG%s^K1 got a little jumpy with their HLAC%s%s"), "") - MSG_INFO_NOTIF(WEAPON_HMG_MURDER_SNIPE, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Heavy Machine Gun%s%s"), "") - MSG_INFO_NOTIF(WEAPON_HMG_MURDER_SPRAY, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was torn to bits by ^BG%s^K1's Heavy Machine Gun%s%s"), "") MSG_INFO_NOTIF(WEAPON_HOOK_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhook", _("^BG%s%s^K1 was caught in ^BG%s^K1's Hook gravity bomb%s%s"), "") MSG_INFO_NOTIF(WEAPON_KLEINBOTTLE_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapontuba", _("^BG%s%s^K1 died of ^BG%s^K1's great playing on the @!#%%'n Klein Bottle%s%s"), "") MSG_INFO_NOTIF(WEAPON_KLEINBOTTLE_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weapontuba", _("^BG%s^K1 hurt their own ears with the @!#%%'n Klein Bottle%s%s"), "") @@ -491,14 +488,20 @@ MSG_INFO_NOTIF(WEAPON_MORTAR_MURDER_EXPLODE, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weapongrenadelauncher", _("^BG%s%s^K1 ate ^BG%s^K1's Mortar grenade%s%s"), "") MSG_INFO_NOTIF(WEAPON_MORTAR_SUICIDE_BOUNCE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 didn't see their own Mortar grenade%s%s"), "") MSG_INFO_NOTIF(WEAPON_MORTAR_SUICIDE_EXPLODE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weapongrenadelauncher", _("^BG%s^K1 blew themself up with their own Mortar%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_HMG_MURDER_SNIPE, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was sniped by ^BG%s^K1's Overkill Heavy Machine Gun%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_HMG_MURDER_SPRAY, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponhmg", _("^BG%s%s^K1 was torn to bits by ^BG%s^K1's Overkill Heavy Machine Gun%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_MACHINEGUN_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponuzi", _("^BG%s%s^K1 was riddled full of holes by ^BG%s^K1's Overkill Machine Gun%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_NEX_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponnex", _("^BG%s%s^K1 has been vaporized by ^BG%s^K1's Overkill Nex%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_RPC_MURDER_DIRECT, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrpc", _("^BG%s%s^K1 was sawn in half by ^BG%s^K1's Overkill Rocket Propelled Chainsaw%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_RPC_MURDER_SPLASH, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrpc", _("^BG%s%s^K1 almost dodged ^BG%s^K1's Overkill Rocket Propelled Chainsaw%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_RPC_SUICIDE_DIRECT, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrpc", _("^BG%s^K1 was sawn in half by their own Overkill Rocket Propelled Chainsaw%s%s"), "") + MSG_INFO_NOTIF(WEAPON_OVERKILL_RPC_SUICIDE_SPLASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrpc", _("^BG%s^K1 blew themself up with their Overkill Rocket Propelled Chainsaw%s%s"), "") + + MSG_INFO_NOTIF(WEAPON_OVERKILL_SHOTGUN_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshotgun", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Overkill Shotgun%s%s"), "") MSG_INFO_NOTIF(WEAPON_RIFLE_MURDER, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 was sniped with a Rifle by ^BG%s^K1%s%s"), "") MSG_INFO_NOTIF(WEAPON_RIFLE_MURDER_HAIL, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 died in ^BG%s^K1's Rifle bullet hail%s%s"), "") MSG_INFO_NOTIF(WEAPON_RIFLE_MURDER_HAIL_PIERCING, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle bullet hail%s%s"), "") MSG_INFO_NOTIF(WEAPON_RIFLE_MURDER_PIERCING, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrifle", _("^BG%s%s^K1 failed to hide from ^BG%s^K1's Rifle%s%s"), "") - MSG_INFO_NOTIF(WEAPON_RPC_MURDER_DIRECT, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrpc", _("^BG%s%s^K1 was sawn in half by ^BG%s^K1's Rocket Propelled Chainsaw%s%s"), "") - MSG_INFO_NOTIF(WEAPON_RPC_MURDER_SPLASH, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponrpc", _("^BG%s%s^K1 almost dodged ^BG%s^K1's Rocket Propelled Chainsaw%s%s"), "") - MSG_INFO_NOTIF(WEAPON_RPC_SUICIDE_DIRECT, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrpc", _("^BG%s^K1 was sawn in half by their own Rocket Propelled Chainsaw%s%s"), "") - MSG_INFO_NOTIF(WEAPON_RPC_SUICIDE_SPLASH, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponrpc", _("^BG%s^K1 blew themself up with their Rocket Propelled Chainsaw%s%s"), "") MSG_INFO_NOTIF(WEAPON_SEEKER_MURDER_SPRAY, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponseeker", _("^BG%s%s^K1 was pummeled by ^BG%s^K1's Seeker rockets%s%s"), "") MSG_INFO_NOTIF(WEAPON_SEEKER_MURDER_TAG, N_CONSOLE, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponseeker", _("^BG%s%s^K1 was tagged by ^BG%s^K1's Seeker%s%s"), "") MSG_INFO_NOTIF(WEAPON_SEEKER_SUICIDE, N_CONSOLE, 2, 1, "s1 s2loc spree_lost", "s1", "weaponseeker", _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "") @@ -693,9 +696,9 @@ MSG_CENTER_NOTIF(ITEM_BUFF_DROP, N_ENABLE, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou dropped the %s^BG buff!"), "") MSG_CENTER_NOTIF(ITEM_BUFF_GOT, N_ENABLE, 0, 1, "item_buffname", CPID_ITEM, "item_centime 0", _("^BGYou got the %s^BG buff!"), "") MSG_CENTER_NOTIF(ITEM_FUELREGEN_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Fuel regenerator"), "") - MSG_CENTER_NOTIF(ITEM_JETPACK_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Jet pack"), "") + MSG_CENTER_NOTIF(ITEM_JETPACK_GOT, N_ENABLE, 0, 0, "", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1Jetpack"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_DONTHAVE, N_ENABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou do not have the ^F1%s"), "") - MSG_CENTER_NOTIF(ITEM_WEAPON_DROP, N_ENABLE, 1, 1, "item_wepname item_wepammo", CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") + MSG_CENTER_NOTIF(ITEM_WEAPON_DROP, N_ENABLE, 0, 2, "item_wepname item_wepammo", CPID_ITEM, "item_centime 0", _("^BGYou dropped the ^F1%s^BG%s"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_GOT, N_ENABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou got the ^F1%s"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_NOAMMO, N_ENABLE, 0, 1, "item_wepname", CPID_ITEM, "item_centime 0", _("^BGYou don't have enough ammo for the ^F1%s"), "") MSG_CENTER_NOTIF(ITEM_WEAPON_PRIMORSEC, N_ENABLE, 0, 3, "item_wepname f2primsec f3primsec", CPID_ITEM, "item_centime 0", _("^F1%s %s^BG is unable to fire, but its ^F1%s^BG can"), "") @@ -934,8 +937,6 @@ MSG_MULTI_NOTIF(WEAPON_HAGAR_SUICIDE, N_ENABLE, NULL, INFO_WEAPON_HAGAR_SUICIDE, CENTER_DEATH_SELF_GENERIC) MSG_MULTI_NOTIF(WEAPON_HLAC_MURDER, N_ENABLE, NULL, INFO_WEAPON_HLAC_MURDER, NULL) MSG_MULTI_NOTIF(WEAPON_HLAC_SUICIDE, N_ENABLE, NULL, INFO_WEAPON_HLAC_SUICIDE, CENTER_DEATH_SELF_GENERIC) - MSG_MULTI_NOTIF(WEAPON_HMG_MURDER_SNIPE, N_ENABLE, NULL, INFO_WEAPON_HMG_MURDER_SNIPE, NULL) - MSG_MULTI_NOTIF(WEAPON_HMG_MURDER_SPRAY, N_ENABLE, NULL, INFO_WEAPON_HMG_MURDER_SPRAY, NULL) MSG_MULTI_NOTIF(WEAPON_HOOK_MURDER, N_ENABLE, NULL, INFO_WEAPON_HOOK_MURDER, NULL) MSG_MULTI_NOTIF(WEAPON_KLEINBOTTLE_MURDER, N_ENABLE, NULL, INFO_WEAPON_KLEINBOTTLE_MURDER, NULL) MSG_MULTI_NOTIF(WEAPON_KLEINBOTTLE_SUICIDE, N_ENABLE, NULL, INFO_WEAPON_KLEINBOTTLE_SUICIDE, CENTER_DEATH_SELF_GENERIC) @@ -948,14 +949,19 @@ MSG_MULTI_NOTIF(WEAPON_MORTAR_MURDER_EXPLODE, N_ENABLE, NULL, INFO_WEAPON_MORTAR_MURDER_EXPLODE, NULL) MSG_MULTI_NOTIF(WEAPON_MORTAR_SUICIDE_BOUNCE, N_ENABLE, NULL, INFO_WEAPON_MORTAR_SUICIDE_BOUNCE, CENTER_DEATH_SELF_GENERIC) MSG_MULTI_NOTIF(WEAPON_MORTAR_SUICIDE_EXPLODE, N_ENABLE, NULL, INFO_WEAPON_MORTAR_SUICIDE_EXPLODE, CENTER_DEATH_SELF_GENERIC) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_HMG_MURDER_SNIPE, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_HMG_MURDER_SNIPE, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_HMG_MURDER_SPRAY, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_HMG_MURDER_SPRAY, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_MACHINEGUN_MURDER, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_MACHINEGUN_MURDER, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_NEX_MURDER, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_NEX_MURDER, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_RPC_MURDER_DIRECT, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_RPC_MURDER_DIRECT, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_RPC_MURDER_SPLASH, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_RPC_MURDER_SPLASH, NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_RPC_SUICIDE_DIRECT, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_RPC_SUICIDE_DIRECT,NULL) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_RPC_SUICIDE_SPLASH, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_RPC_SUICIDE_SPLASH,CENTER_DEATH_SELF_GENERIC) + MSG_MULTI_NOTIF(WEAPON_OVERKILL_SHOTGUN_MURDER, N_ENABLE, NULL, INFO_WEAPON_OVERKILL_SHOTGUN_MURDER, NULL) MSG_MULTI_NOTIF(WEAPON_RIFLE_MURDER, N_ENABLE, NULL, INFO_WEAPON_RIFLE_MURDER, NULL) MSG_MULTI_NOTIF(WEAPON_RIFLE_MURDER_HAIL, N_ENABLE, NULL, INFO_WEAPON_RIFLE_MURDER_HAIL, NULL) MSG_MULTI_NOTIF(WEAPON_RIFLE_MURDER_HAIL_PIERCING, N_ENABLE, NULL, INFO_WEAPON_RIFLE_MURDER_HAIL_PIERCING, NULL) MSG_MULTI_NOTIF(WEAPON_RIFLE_MURDER_PIERCING, N_ENABLE, NULL, INFO_WEAPON_RIFLE_MURDER_PIERCING, NULL) - MSG_MULTI_NOTIF(WEAPON_RPC_MURDER_DIRECT, N_ENABLE, NULL, INFO_WEAPON_RPC_MURDER_DIRECT, NULL) - MSG_MULTI_NOTIF(WEAPON_RPC_MURDER_SPLASH, N_ENABLE, NULL, INFO_WEAPON_RPC_MURDER_SPLASH, NULL) - MSG_MULTI_NOTIF(WEAPON_RPC_SUICIDE_DIRECT, N_ENABLE, NULL, INFO_WEAPON_RPC_SUICIDE_DIRECT, NULL) - MSG_MULTI_NOTIF(WEAPON_RPC_SUICIDE_SPLASH, N_ENABLE, NULL, INFO_WEAPON_RPC_SUICIDE_SPLASH, CENTER_DEATH_SELF_GENERIC) MSG_MULTI_NOTIF(WEAPON_SEEKER_MURDER_SPRAY, N_ENABLE, NULL, INFO_WEAPON_SEEKER_MURDER_SPRAY, NULL) MSG_MULTI_NOTIF(WEAPON_SEEKER_MURDER_TAG, N_ENABLE, NULL, INFO_WEAPON_SEEKER_MURDER_TAG, NULL) MSG_MULTI_NOTIF(WEAPON_SEEKER_SUICIDE, N_ENABLE, NULL, INFO_WEAPON_SEEKER_SUICIDE, CENTER_DEATH_SELF_GENERIC) @@ -999,9 +1005,9 @@ #define A_ALWAYS 2 // MSG_CHOICE_NOTIFICATIONS - MULTITEAM_CHOICE(CTF_CAPTURE_BROKEN, 4, N__NORMAL, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_BROKEN) - MULTITEAM_CHOICE(CTF_CAPTURE_TIME, 4, N__NORMAL, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_TIME) - MULTITEAM_CHOICE(CTF_CAPTURE_UNBROKEN, 4, N__NORMAL, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_UNBROKEN) + MULTITEAM_CHOICE(CTF_CAPTURE_BROKEN, 4, N_VERBOSE, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_BROKEN) + MULTITEAM_CHOICE(CTF_CAPTURE_TIME, 4, N_VERBOSE, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_TIME) + MULTITEAM_CHOICE(CTF_CAPTURE_UNBROKEN, 4, N_VERBOSE, A_ALWAYS, MSG_INFO, INFO_CTF_CAPTURE, INFO_CTF_CAPTURE_UNBROKEN) MULTITEAM_CHOICE(CTF_PICKUP_TEAM, 4, N__NORMAL, A_ALWAYS, MSG_CENTER, CENTER_CTF_PICKUP_TEAM, CENTER_CTF_PICKUP_TEAM_VERBOSE) MSG_CHOICE_NOTIF(CTF_PICKUP_TEAM_NEUTRAL, N__NORMAL, A_ALWAYS, MSG_CENTER, CENTER_CTF_PICKUP_TEAM_NEUTRAL, CENTER_CTF_PICKUP_TEAM_VERBOSE_NEUTRAL) MSG_CHOICE_NOTIF(CTF_PICKUP_ENEMY, N__NORMAL, A_ALWAYS, MSG_CENTER, CENTER_CTF_PICKUP_ENEMY, CENTER_CTF_PICKUP_ENEMY_VERBOSE) diff --git a/qcsrc/common/notifications/all.qc b/qcsrc/common/notifications/all.qc index b9350758a1..644d049d38 100644 --- a/qcsrc/common/notifications/all.qc +++ b/qcsrc/common/notifications/all.qc @@ -1063,8 +1063,7 @@ void Local_Notification_sound(int soundchannel, string soundfile, float soundvol _sound(NULL, soundchannel, AnnouncerFilename(soundfile), soundvolume, soundposition); - if (prev_soundfile) strunzone(prev_soundfile); - prev_soundfile = strzone(soundfile); + strcpy(prev_soundfile, soundfile); prev_soundtime = time; } else @@ -1467,7 +1466,7 @@ void Net_Notification_Remove(entity this) this.owner.nent_name )); #endif - for (int i = 0; i < this.nent_stringcount; ++i) { if (this.nent_strings[i]) strunzone(this.nent_strings[i]); } + for (int i = 0; i < this.nent_stringcount; ++i) { strfree(this.nent_strings[i]); } delete(this); } diff --git a/qcsrc/common/notifications/all.qh b/qcsrc/common/notifications/all.qh index 1721303ab5..6cbf91cc9b 100644 --- a/qcsrc/common/notifications/all.qh +++ b/qcsrc/common/notifications/all.qh @@ -5,6 +5,8 @@ #include <common/constants.qh> #include <common/teams.qh> #include <common/util.qh> +#include <common/sounds/sound.qh> +#include <common/weapons/all.qh> #ifdef CSQC #include <client/autocvars.qh> @@ -369,7 +371,7 @@ float autocvar_notification_show_sprees_center_specialonly = true; spree_end: placed at the end of murder messages to show ending of sprees spree_lost: placed at the end of suicide messages to show losing of sprees item_wepname: return full name of a weapon from weaponid - item_wepammo: ammo display for weapon from string + item_wepammo: ammo display for weapon from f1 and f2 item_centime: amount of time to display weapon message in centerprint item_buffname: return full name of a buff from buffid death_team: show the full name of the team a player is switching from @@ -432,7 +434,7 @@ string BUFF_NAME(int i); ARG_CASE(ARG_CS_SV, "item_wepname", Weapons_from(f1).m_name) \ ARG_CASE(ARG_CS_SV, "item_buffname", BUFF_NAME(f1)) \ ARG_CASE(ARG_CS_SV, "f3buffname", BUFF_NAME(f3)) \ - ARG_CASE(ARG_CS_SV, "item_wepammo", (s1 != "" ? sprintf(_(" with %s"), s1) : "")) \ + ARG_CASE(ARG_CS_SV, "item_wepammo", (f2 > 0 ? notif_arg_item_wepammo(f1, f2) : "")) \ ARG_CASE(ARG_DC, "item_centime", ftos(autocvar_notification_item_centerprinttime)) \ ARG_CASE(ARG_SV, "death_team", Team_ColoredFullName(f1)) \ ARG_CASE(ARG_CS, "death_team", Team_ColoredFullName(f1 - 1)) \ @@ -623,6 +625,23 @@ string notif_arg_spree_inf(float type, string input, string player, float spree) return ""; } +string notif_arg_item_wepammo(float f1, float f2) +{ + string ammoitems = ""; + Weapon wep = Weapons_from(f1); + switch (wep.ammo_type) + { + 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; + default: return ""; // doesn't use ammo + } + return sprintf(_(" with %d %s"), f2, ammoitems); +} + // ==================================== // Initialization/Create Declarations @@ -676,7 +695,8 @@ string notif_arg_spree_inf(float type, string input, string player, float spree) REGISTRY(Notifications, BITS(11)) REGISTER_REGISTRY(Notifications) -REGISTRY_SORT(Notifications); STATIC_INIT(Notifications) { FOREACH(Notifications, true, it.m_id = i); } +REGISTRY_SORT(Notifications); +STATIC_INIT(Notifications) { FOREACH(Notifications, true, it.m_id = i); } REGISTRY_CHECK(Notifications) const int NOTIF_CHOICE_MAX = 50; diff --git a/qcsrc/common/physics/movetypes/movetypes.qc b/qcsrc/common/physics/movetypes/movetypes.qc index 3fe2808583..6a53e57c26 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qc +++ b/qcsrc/common/physics/movetypes/movetypes.qc @@ -36,11 +36,15 @@ void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction vector planes[MAX_CLIP_PLANES]; int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight) // SV_FlyMove { + if(dt <= 0) + return 0; + int blocked = 0; int i, j, numplanes = 0; float time_left = dt, grav = 0; vector push; - vector primal_velocity, original_velocity, restore_velocity; + vector primal_velocity, original_velocity; + vector restore_velocity = this.velocity; for(i = 0; i < MAX_CLIP_PLANES; ++i) planes[i] = '0 0 0'; @@ -59,7 +63,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma } } - original_velocity = primal_velocity = restore_velocity = this.velocity; + original_velocity = primal_velocity = this.velocity; for(int bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++) { @@ -67,8 +71,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma break; push = this.velocity * time_left; - _Movetype_PushEntity(this, push, true); - if(trace_startsolid) + if(!_Movetype_PushEntity(this, push, true, false)) { // we got teleported by a touch function // let's abort the move @@ -113,22 +116,19 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma vector org = this.origin; vector steppush = '0 0 1' * stepheight; - _Movetype_PushEntity(this, steppush, true); - if(trace_startsolid) + if(!_Movetype_PushEntity(this, steppush, true, false)) { blocked |= 8; break; } - _Movetype_PushEntity(this, push, true); - if(trace_startsolid) + if(!_Movetype_PushEntity(this, push, true, false)) { blocked |= 8; break; } float trace2_fraction = trace_fraction; - steppush = '0 0 1' * (org.z - this.origin_z); - _Movetype_PushEntity(this, steppush, true); - if(trace_startsolid) + steppush = vec3(0, 0, org.z - this.origin_z); + if(!_Movetype_PushEntity(this, steppush, true, false)) { blocked |= 8; break; @@ -232,6 +232,9 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blocked & 8)) this.velocity = primal_velocity; + if(PHYS_WALLCLIP(this) && this.pm_time && !(this.flags & FL_WATERJUMP) && !(blocked & 8)) + this.velocity = primal_velocity; + if(applygravity) { if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this)) @@ -351,37 +354,50 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGr }); } +bool autocvar__movetype_debug = false; void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict { - vector mi, ma; - if(this.solid == SOLID_BSP) + if(autocvar__movetype_debug) { - // TODO set the absolute bbox - mi = this.mins; - ma = this.maxs; - } - else - { - mi = this.mins; - ma = this.maxs; - } - mi += this.origin; - ma += this.origin; + vector mi, ma; + if(this.solid == SOLID_BSP) + { + // TODO set the absolute bbox + mi = this.mins; + ma = this.maxs; + } + else + { + mi = this.mins; + ma = this.maxs; + } + mi += this.origin; + ma += this.origin; - if(this.flags & FL_ITEM) - { - mi -= '15 15 1'; - ma += '15 15 1'; + if(this.flags & FL_ITEM) + { + mi -= '15 15 1'; + ma += '15 15 1'; + } + else + { + mi -= '1 1 1'; + ma += '1 1 1'; + } + + this.absmin = mi; + this.absmax = ma; } else { - mi -= '1 1 1'; - ma += '1 1 1'; + setorigin(this, this.origin); // calls SV_LinkEdict + #ifdef CSQC + // NOTE: CSQC's version of setorigin doesn't expand + this.absmin -= '1 1 1'; + this.absmax += '1 1 1'; + #endif } - this.absmin = mi; - this.absmax = ma; - if(touch_triggers) _Movetype_LinkEdict_TouchAreaGrid(this); } @@ -390,11 +406,11 @@ entity _Movetype_TestEntityPosition_ent; bool _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition { entity this = _Movetype_TestEntityPosition_ent; -// vector org = this.origin + ofs; + vector org = this.origin + ofs; int cont = this.dphitcontentsmask; this.dphitcontentsmask = DPCONTENTS_SOLID; - tracebox(this.origin, this.mins, this.maxs, this.origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); + tracebox(org, this.mins, this.maxs, org, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); this.dphitcontentsmask = cont; if(trace_startsolid) @@ -405,11 +421,11 @@ bool _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition return false; } -bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity +int _Movetype_UnstickEntity(entity this) // SV_UnstickEntity { _Movetype_TestEntityPosition_ent = this; if (!_Movetype_TestEntityPosition(' 0 0 0')) { - return true; + return UNSTICK_FINE; } #define X(v) if (_Movetype_TestEntityPosition(v)) X('-1 0 0') X(' 1 0 0') @@ -421,20 +437,39 @@ bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity #define X(i) \ if (_Movetype_TestEntityPosition('0 0 -1' * i)) \ if (_Movetype_TestEntityPosition('0 0 1' * i)) - X(01) X(02) X(03) X(04) X(05) X(06) X(07) X(08) - X(09) X(10) X(11) X(12) X(13) X(14) X(15) X(16) + X(1) X(2) X(3) X(4) X(5) X(6) X(7) X(8) + X(9) X(10) X(11) X(12) X(13) X(14) X(15) X(16) X(17) #undef X { LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)", etof(this), this.classname, vtos(this.origin)); - return false; + return UNSTICK_STUCK; } } LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)", etof(this), this.classname, vtos(this.origin)); _Movetype_LinkEdict(this, true); - return true; + return UNSTICK_FIXED; +} + +void _Movetype_CheckStuck(entity this) // SV_CheckStuck +{ + int unstick = _Movetype_UnstickEntity(this); // sets test position entity + switch(unstick) + { + case UNSTICK_FINE: + this.oldorigin = this.origin; + break; + case UNSTICK_FIXED: + break; // already sorted + case UNSTICK_STUCK: + vector offset = this.oldorigin - this.origin; + if(!_Movetype_TestEntityPosition(offset)) + _Movetype_LinkEdict(this, false); + // couldn't unstick, should we warn about this? + break; + } } vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVelocity @@ -466,20 +501,32 @@ void _Movetype_PushEntityTrace(entity this, vector push) tracebox(this.origin, this.mins, this.maxs, end, type, this); } -float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity +bool _Movetype_PushEntity(entity this, vector push, bool failonstartsolid, bool dolink) // SV_PushEntity { _Movetype_PushEntityTrace(this, push); if(trace_startsolid && failonstartsolid) - return trace_fraction; + { + int oldtype = this.move_nomonsters; + this.move_nomonsters = MOVE_NOMONSTERS; + _Movetype_PushEntityTrace(this, push); + this.move_nomonsters = oldtype; + if(trace_startsolid) + return true; + } this.origin = trace_endpos; + vector last_origin = this.origin; + + if(dolink) + _Movetype_LinkEdict(this, true); + if(trace_fraction < 1) if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (this.groundentity != trace_ent))) _Movetype_Impact(this, trace_ent); - return trace_fraction; + return (this.origin == last_origin); // false if teleported by touch } @@ -519,6 +566,8 @@ void _Movetype_Physics_Frame(entity this, float movedt) case MOVETYPE_FLY: case MOVETYPE_FLY_WORLDONLY: _Movetype_Physics_Toss(this, movedt); + if(wasfreed(this)) + return; _Movetype_LinkEdict(this, true); break; case MOVETYPE_PHYSICS: diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh index 85912ee1c3..1eb2d95e47 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qh +++ b/qcsrc/common/physics/movetypes/movetypes.qh @@ -26,6 +26,8 @@ const int WATERLEVEL_SUBMERGED = 3; #define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP) #define PHYS_WALLFRICTION(s) STAT(MOVEVARS_WALLFRICTION) +#define PHYS_WALLCLIP(s) STAT(MOVEVARS_WALLCLIP) + #ifdef CSQC .float bouncestop; .float bouncefactor; @@ -55,6 +57,8 @@ const int WATERLEVEL_SUBMERGED = 3; void set_movetype(entity this, int mt); +.float pm_time; + .float move_movetype; .float move_time; //.vector move_origin; @@ -76,16 +80,22 @@ void set_movetype(entity this, int mt); .float move_suspendedinair; .float move_didgravity; +// unsticking +const int UNSTICK_FINE = 0; +const int UNSTICK_FIXED = 1; +const int UNSTICK_STUCK = 2; + void _Movetype_WallFriction(entity this, vector stepnormal); int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnormal, float stepheight); void _Movetype_CheckVelocity(entity this); void _Movetype_CheckWaterTransition(entity ent); +void _Movetype_CheckStuck(entity this); float _Movetype_CheckWater(entity ent); void _Movetype_LinkEdict_TouchAreaGrid(entity this); void _Movetype_LinkEdict(entity this, float touch_triggers); vector _Movetype_ClipVelocity(vector vel, vector norm, float f); void _Movetype_PushEntityTrace(entity this, vector push); -float _Movetype_PushEntity(entity this, vector push, float failonstartsolid); +bool _Movetype_PushEntity(entity this, vector push, float failonstartsolid, bool dolink); void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient); void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy); @@ -94,7 +104,7 @@ void Movetype_Physics_NoMatchServer(entity this); void _Movetype_LinkEdict(entity this, float touch_triggers); void _Movetype_LinkEdict_TouchAreaGrid(entity this); -float _Movetype_UnstickEntity(entity this); +int _Movetype_UnstickEntity(entity this); const int MAX_CLIP_PLANES = 5; @@ -122,6 +132,8 @@ const int MOVETYPE_ANGLENOCLIP = 1; const int MOVETYPE_ANGLECLIP = 2; #endif +const int MOVETYPE_QCPLAYER = 150; // QC-driven player physics, no think functions! + const int FL_ONSLICK = BIT(20); const int MOVETYPE_FAKEPUSH = 13; diff --git a/qcsrc/common/physics/movetypes/toss.qc b/qcsrc/common/physics/movetypes/toss.qc index 71e7fa9d08..fc3de0859b 100644 --- a/qcsrc/common/physics/movetypes/toss.qc +++ b/qcsrc/common/physics/movetypes/toss.qc @@ -44,14 +44,14 @@ void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump) { vector move = this.velocity * movetime; - _Movetype_PushEntity(this, move, true); + _Movetype_PushEntity(this, move, true, false); if (wasfreed(this)) return; if (trace_startsolid) { _Movetype_UnstickEntity(this); - _Movetype_PushEntity(this, move, false); + _Movetype_PushEntity(this, move, false, false); if (wasfreed(this)) return; } diff --git a/qcsrc/common/physics/movetypes/walk.qc b/qcsrc/common/physics/movetypes/walk.qc index c20e82e834..5c7ae9ee2a 100644 --- a/qcsrc/common/physics/movetypes/walk.qc +++ b/qcsrc/common/physics/movetypes/walk.qc @@ -8,7 +8,7 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove return; if (GAMEPLAYFIX_UNSTICKPLAYERS(this)) - _Movetype_UnstickEntity(this); + _Movetype_CheckStuck(this); bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.flags & FL_WATERJUMP)); @@ -20,6 +20,14 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove vector start_origin = this.origin; vector start_velocity = this.velocity; + if(PHYS_WALLCLIP(this) && this.pm_time) + { + if(dt >= this.pm_time || (this.flags & FL_WATERJUMP)) + this.pm_time = 0; + else + this.pm_time -= dt; + } + int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0); if (GAMEPLAYFIX_DOWNTRACEONGROUND(this) && !(clip & 1)) @@ -45,6 +53,8 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // if the move did not hit the ground at any point, we're not on ground if (!(clip & 1)) UNSET_ONGROUND(this); + else if(PHYS_WALLCLIP(this) && !this.groundentity && (PHYS_WALLCLIP(this) == 2 || start_velocity.z < -200)) // don't do landing time if we were just going down a slope + this.pm_time = 0.25; _Movetype_CheckVelocity(this); _Movetype_LinkEdict(this, true); @@ -90,10 +100,7 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // move up vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this); - _Movetype_PushEntity(this, upmove, true); - if(wasfreed(this)) - return; - if(trace_startsolid) + if(!_Movetype_PushEntity(this, upmove, true, true)) { // we got teleported when upstepping... must abort the move return; @@ -144,12 +151,9 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove } // move down - vector downmove = '0 0 1' * (-PHYS_STEPHEIGHT(this) + start_velocity.z * dt); - _Movetype_PushEntity(this, downmove, true); - if(wasfreed(this)) - return; - - if(trace_startsolid) + vector downmove = '0 0 0'; + downmove.z = -PHYS_STEPHEIGHT(this) + start_velocity.z * dt; + if(!_Movetype_PushEntity(this, downmove, true, true)) { // we got teleported when downstepping... must abort the move return; @@ -159,6 +163,13 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove { // this has been disabled so that you can't jump when you are stepping // up while already jumping (also known as the Quake2 double jump bug) + // LordHavoc: disabled this check so you can walk on monsters/players + //if (PRVM_serveredictfloat(ent, solid) == SOLID_BSP) + if(GAMEPLAYFIX_STEPDOWN(this) == 2) + { + SET_ONGROUND(this); + this.groundentity = trace_ent; + } } else { diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc index 7d3cab007b..87d456c018 100644 --- a/qcsrc/common/physics/player.qc +++ b/qcsrc/common/physics/player.qc @@ -1,28 +1,32 @@ #include "player.qh" -#include "../triggers/include.qh" +#include "../mapobjects/_mod.qh" #include "../viewloc.qh" #ifdef SVQC #include <server/miscfunctions.qh> -#include "../triggers/trigger/viewloc.qh" +#include "../mapobjects/trigger/viewloc.qh" // client side physics bool Physics_Valid(string thecvar) { - return autocvar_g_physics_clientselect && thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar); + return thecvar != "" && thecvar && thecvar != "default" && strhasword(autocvar_g_physics_clientselect_options, thecvar); } float Physics_ClientOption(entity this, string option, float defaultval) { + if(!autocvar_g_physics_clientselect) + return defaultval; + if(IS_REAL_CLIENT(this) && Physics_Valid(CS(this).cvar_cl_physics)) { string s = strcat("g_physics_", CS(this).cvar_cl_physics, "_", option); if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) return cvar(s); } - if(autocvar_g_physics_clientselect && autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "") + if(autocvar_g_physics_clientselect_default && autocvar_g_physics_clientselect_default != "" && autocvar_g_physics_clientselect_default != "default") { + // NOTE: not using Physics_Valid here, so the default can be forced to something normally unavailable string s = strcat("g_physics_", autocvar_g_physics_clientselect_default, "_", option); if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) return cvar(s); @@ -45,12 +49,13 @@ void Physics_UpdateStats(entity this) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw", autocvar_sv_airspeedlimit_nonqw) * maxspd_mod; STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed", autocvar_sv_maxspeed) * maxspd_mod; // also slow walking - STAT(PL_MIN, this) = autocvar_sv_player_mins; - STAT(PL_MAX, this) = autocvar_sv_player_maxs; - STAT(PL_VIEW_OFS, this) = autocvar_sv_player_viewoffset; - STAT(PL_CROUCH_MIN, this) = autocvar_sv_player_crouch_mins; - STAT(PL_CROUCH_MAX, this) = autocvar_sv_player_crouch_maxs; - STAT(PL_CROUCH_VIEW_OFS, this) = autocvar_sv_player_crouch_viewoffset; + bool vq3compat = autocvar_sv_vq3compat && autocvar_sv_vq3compat_changehitbox; // NOTE: these hitboxes are off by 1 due to engine differences + STAT(PL_MIN, this) = (vq3compat) ? '-15 -15 -24' : autocvar_sv_player_mins; + STAT(PL_MAX, this) = (vq3compat) ? '15 15 32' : autocvar_sv_player_maxs; + STAT(PL_VIEW_OFS, this) = (vq3compat) ? '0 0 26' : autocvar_sv_player_viewoffset; + STAT(PL_CROUCH_MIN, this) = (vq3compat) ? '-15 -15 -24' : autocvar_sv_player_crouch_mins; + STAT(PL_CROUCH_MAX, this) = (vq3compat) ? '15 15 16' : autocvar_sv_player_crouch_maxs; + STAT(PL_CROUCH_VIEW_OFS, this) = (vq3compat) ? '0 0 12' : autocvar_sv_player_crouch_viewoffset; // old stats // fix some new settings @@ -77,6 +82,8 @@ void Physics_UpdateStats(entity this) 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); + + MUTATOR_CALLHOOK(PlayerPhysics_PostUpdateStats, this, maxspd_mod); } #endif @@ -98,42 +105,53 @@ float GeomLerp(float a, float _lerp, float b) void PM_ClientMovement_UpdateStatus(entity this) { -#ifdef CSQC if(!IS_PLAYER(this)) return; - // set crouched - bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); + bool have_hook = false; for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { - entity wep = viewmodels[slot]; - if(wep.hook && !wasfreed(wep.hook)) + #if defined(CSQC) + entity wepent = viewmodels[slot]; + #elif defined(SVQC) + .entity weaponentity = weaponentities[slot]; + entity wepent = this.(weaponentity); + #endif + if(wepent.hook && !wasfreed(wepent.hook)) { - do_crouch = false; - break; // don't bother checking the others + have_hook = true; + break; } } - if(this.waterlevel >= WATERLEVEL_SWIMMING) + bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); + if(this.viewloc && !(this.viewloc.spawnflags & VIEWLOC_FREEMOVE) && PHYS_CS(this).movement.x < 0) + do_crouch = true; + if (have_hook) { do_crouch = false; - if(hud != HUD_NORMAL) + //} else if (this.waterlevel >= WATERLEVEL_SWIMMING) { + //do_crouch = false; + } else if (PHYS_INVEHICLE(this)) { do_crouch = false; - if(STAT(FROZEN, this)) + } else if (STAT(FROZEN, this)) { do_crouch = false; + } - if (do_crouch) - { - // wants to crouch, this always works - if (!IS_DUCKED(this)) SET_DUCKED(this); - } - else - { - // wants to stand, if currently crouching we need to check for a low ceiling first - if (IS_DUCKED(this)) - { - tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this); - if (!trace_startsolid) UNSET_DUCKED(this); + if (do_crouch) { + if (!IS_DUCKED(this)) { + SET_DUCKED(this); + this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this); + setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this)); + // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway } + } else if (IS_DUCKED(this)) { + tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this); + if (!trace_startsolid) { + UNSET_DUCKED(this); + this.view_ofs = STAT(PL_VIEW_OFS, this); + setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this)); + } } +#ifdef CSQC if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0) PHYS_WATERJUMP_TIME(this) = 0; @@ -781,7 +799,7 @@ void PM_jetpack(entity this, float maxspd_mod, float dt) #ifdef SVQC if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f; + TakeResource(this, RESOURCE_FUEL, PHYS_JETPACK_FUEL(this) * dt * fvel * f); ITEMS_STAT(this) |= IT_USING_JETPACK; @@ -798,7 +816,8 @@ bool IsFlying(entity this) return false; if(this.waterlevel >= WATERLEVEL_SWIMMING) return false; - traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this); + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 24', MOVE_NORMAL, this); + //traceline(this.origin, this.origin - '0 0 48', MOVE_NORMAL, this); if(trace_fraction < 1) return false; return true; diff --git a/qcsrc/common/physics/player.qh b/qcsrc/common/physics/player.qh index 4b2ffc55f9..9ecf7eab71 100644 --- a/qcsrc/common/physics/player.qh +++ b/qcsrc/common/physics/player.qh @@ -15,7 +15,7 @@ .float swamp_slowdown; .float lastflags; .float lastground; -.float wasFlying; +.bool wasFlying; .int buttons_old; .vector movement_old; @@ -101,15 +101,6 @@ bool IsFlying(entity a); #define PHYS_INPUT_BUTTON_ZOOM(s) PHYS_INPUT_BUTTON_BUTTON4(s) #define PHYS_INPUT_BUTTON_CROUCH(s) PHYS_INPUT_BUTTON_BUTTON5(s) #define PHYS_INPUT_BUTTON_HOOK(s) PHYS_INPUT_BUTTON_BUTTON6(s) - -#ifdef CSQC -STATIC_INIT(PHYS_INPUT_BUTTON_HOOK) -{ - localcmd("alias +hook +button6\n"); - localcmd("alias -hook -button6\n"); -} -#endif - #define PHYS_INPUT_BUTTON_INFO(s) PHYS_INPUT_BUTTON_BUTTON7(s) #define PHYS_INPUT_BUTTON_DRAG(s) PHYS_INPUT_BUTTON_BUTTON8(s) #define PHYS_INPUT_BUTTON_USE(s) PHYS_INPUT_BUTTON_BUTTON_USE(s) @@ -118,18 +109,19 @@ STATIC_INIT(PHYS_INPUT_BUTTON_HOOK) #define PHYS_INPUT_BUTTON_ZOOMSCRIPT(s) PHYS_INPUT_BUTTON_BUTTON9(s) #define PHYS_INPUT_BUTTON_JETPACK(s) PHYS_INPUT_BUTTON_BUTTON10(s) #define PHYS_INPUT_BUTTON_DODGE(s) PHYS_INPUT_BUTTON_BUTTON11(s) +#define PHYS_INPUT_BUTTON_MINIGAME(s) PHYS_INPUT_BUTTON_BUTTON14(s) #ifdef CSQC -STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) +STATIC_INIT(PHYS_INPUT_BUTTON) { + localcmd("alias +hook +button6\n"); + localcmd("alias -hook -button6\n"); + localcmd("alias +jetpack +button10\n"); - localcmd("alias -jetpack -button10\n"); -} + localcmd("alias -jetpack -button10\n"); -STATIC_INIT(PHYS_INPUT_BUTTON_DODGE) -{ - localcmd("alias +dodge +button11\n"); - localcmd("alias -dodge -button11\n"); + localcmd("alias +dodge +button11\n"); + localcmd("alias -dodge -button11\n"); } #endif @@ -198,8 +190,8 @@ STATIC_INIT(PHYS_INPUT_BUTTON_DODGE) .entity hook; // TODO - #define IS_CLIENT(s) ((s).isplayermodel || (s) == csqcplayer) - #define IS_PLAYER(s) ((s).isplayermodel) + #define IS_CLIENT(s) (((s).isplayermodel & ISPLAYER_CLIENT) || (s) == csqcplayer) + #define IS_PLAYER(s) ((s).isplayermodel & ISPLAYER_PLAYER) #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel && (s) != csqcplayer) #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE)) @@ -240,6 +232,8 @@ STATIC_INIT(PHYS_INPUT_BUTTON_DODGE) #define SET_DUCKED(s) ((s).flags |= FL_DUCKED) #define UNSET_DUCKED(s) ((s).flags &= ~FL_DUCKED) + #define PHYS_INVEHICLE(s) (boolean(hud != HUD_NORMAL)) + #define PHYS_JUMPSPEEDCAP_MIN autocvar_cl_jumpspeedcap_min #define PHYS_JUMPSPEEDCAP_MAX autocvar_cl_jumpspeedcap_max @@ -292,6 +286,8 @@ STATIC_INIT(PHYS_INPUT_BUTTON_DODGE) #define SET_DUCKED(s) ((s).crouch = true) #define UNSET_DUCKED(s) ((s).crouch = false) + #define PHYS_INVEHICLE(s) (boolean((s).vehicle != NULL)) + #define PHYS_JUMPSPEEDCAP_MIN autocvar_sv_jumpspeedcap_min #define PHYS_JUMPSPEEDCAP_MAX autocvar_sv_jumpspeedcap_max diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 0fcd70dfc0..2c9bbcde80 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -55,7 +55,7 @@ void PlayerStats_GameReport_AddPlayer(entity e) } } -void PlayerStats_GameReport_AddTeam(float t) +void PlayerStats_GameReport_AddTeam(int t) { if(PS_GR_OUT_DB < 0) { return; } @@ -154,11 +154,10 @@ void PlayerStats_GameReport_FinalizePlayer(entity p) } } - strunzone(p.playerstats_id); - p.playerstats_id = string_null; + strfree(p.playerstats_id); } -void PlayerStats_GameReport(float finished) +void PlayerStats_GameReport(bool finished) { if(PS_GR_OUT_DB < 0) { return; } @@ -259,6 +258,7 @@ void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that } // this... is a hack, a temporary one until we get a proper duel gametype +// TODO: remove duel hack after servers have migrated to the proper duel gametype! string PlayerStats_GetGametype() { if(IS_GAMETYPE(DEATHMATCH) && autocvar_g_maxplayers == 2) diff --git a/qcsrc/common/playerstats.qh b/qcsrc/common/playerstats.qh index 28f985e27d..d27dd0ed1f 100644 --- a/qcsrc/common/playerstats.qh +++ b/qcsrc/common/playerstats.qh @@ -54,7 +54,7 @@ const string PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD = "achievement-firstblood"; const string PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM = "achievement-firstvictim"; // delay map switch until this is set -float PlayerStats_GameReport_DelayMapVote; +bool PlayerStats_GameReport_DelayMapVote; // call at initialization void PlayerStats_GameReport_Init(); diff --git a/qcsrc/common/resources.qh b/qcsrc/common/resources.qh index a562292b40..8e33c649b3 100644 --- a/qcsrc/common/resources.qh +++ b/qcsrc/common/resources.qh @@ -5,6 +5,10 @@ /// \author Lyberta /// \copyright GNU GPLv2 or any later version. +/// \brief Unconditional maximum amount of resources the entity can have. +const int RESOURCE_AMOUNT_HARD_LIMIT = 999; +const int RESOURCE_LIMIT_NONE = -1; + /// \brief Describes the available resource types. enum { diff --git a/qcsrc/common/scores.qh b/qcsrc/common/scores.qh index 476d0dbbaa..377a780770 100644 --- a/qcsrc/common/scores.qh +++ b/qcsrc/common/scores.qh @@ -39,6 +39,8 @@ REGISTER_SP(FRAGS); REGISTER_SP(ELO); +REGISTER_SP(FPS); + // TODO: move to common mutators REGISTER_SP(RACE_TIME); diff --git a/qcsrc/common/sounds/all.qc b/qcsrc/common/sounds/all.qc index 328486d354..fcd4d9a459 100644 --- a/qcsrc/common/sounds/all.qc +++ b/qcsrc/common/sounds/all.qc @@ -8,6 +8,7 @@ bool autocvar_bot_sound_monopoly; .entity realowner; bool sound_allowed(int to, entity e) { + if(!e) return true; // save on a few checks for ( ; ; ) { if (e.classname == "body") e = e.enemy; diff --git a/qcsrc/common/sounds/sound.qh b/qcsrc/common/sounds/sound.qh index 8c4aecbda0..49cfb4488c 100644 --- a/qcsrc/common/sounds/sound.qh +++ b/qcsrc/common/sounds/sound.qh @@ -22,7 +22,7 @@ const int CH_PLAYER_SINGLE = 7; // const int CH_BGM_SINGLE = -8; const int CH_BGM_SINGLE = 8; const int CH_AMBIENT = -9; -// const int CH_AMBIENT_SINGLE = 9; +const int CH_AMBIENT_SINGLE = 9; const float ATTEN_NONE = 0; const float ATTEN_MIN = 0.015625; @@ -92,40 +92,55 @@ const float VOL_MUFFLED = 0.35; } \ } MACRO_END +string _Sound_fixpath(string base) +{ + if (base == "") return string_null; +#ifdef SVQC + return strcat(base, ".wav"); // let the client engine decide +#else +#define extensions(x) \ + x(wav) \ + x(ogg) \ + x(flac) \ + /**/ +#define tryext(ext) { \ + string s = strcat(base, "." #ext); \ + if (fexists(strcat("sound/", s))) { \ + return s; \ + } \ + } + extensions(tryext); + LOG_WARNF("Missing sound: \"%s\"", strcat("sound/", base)); +#undef tryext +#undef extensions + return string_null; +#endif +} + CLASS(Sound, Object) ATTRIB(Sound, m_id, int, 0); ATTRIB(Sound, sound_str, string()); + ATTRIB(Sound, sound_str_, string); CONSTRUCTOR(Sound, string() path) { CONSTRUCT(Sound); this.sound_str = path; } - #define Sound_fixpath(this) _Sound_fixpath((this).sound_str()) - string _Sound_fixpath(string base) - { - if (base == "") return string_null; -#ifdef SVQC - return strcat(base, ".wav"); // let the client engine decide -#else - #define extensions(x) \ - x(wav) \ - x(ogg) \ - x(flac) \ - /**/ - #define tryext(ext) { string s = strcat(base, "." #ext); if (fexists(strcat("sound/", s))) return s; } - extensions(tryext); - LOG_WARNF("Missing sound: \"%s\"", strcat("sound/", base)); - #undef tryext - #undef extensions - return string_null; -#endif - } METHOD(Sound, sound_precache, void(Sound this)) { - TC(Sound, this); - string s = Sound_fixpath(this); + TC(Sound, this); + string s = _Sound_fixpath(this.sound_str()); if (!s) return; profile(sprintf("precache_sound(\"%s\")", s)); precache_sound(s); + strcpy(this.sound_str_, s); } ENDCLASS(Sound) + +entity _Sound_fixpath_this; +string _Sound_fixpath_cached; +#define Sound_fixpath(this) ( \ + _Sound_fixpath_this = (this), \ + _Sound_fixpath_cached = _Sound_fixpath_this.sound_str_, \ + _Sound_fixpath_cached ? _Sound_fixpath_cached : _Sound_fixpath(_Sound_fixpath_this.sound_str()) \ +) diff --git a/qcsrc/common/state.qc b/qcsrc/common/state.qc index ea936185b9..2a1168eae0 100644 --- a/qcsrc/common/state.qc +++ b/qcsrc/common/state.qc @@ -1,13 +1,13 @@ #include "state.qh" -void Inventory_new(entity this); +void Inventory_new(PlayerState this); void Inventory_delete(entity this); void PlayerState_attach(entity this) { this._ps = NEW(PlayerState, this); - Inventory_new(this); + Inventory_new(PS(this)); } void PlayerState_detach(entity this) @@ -18,11 +18,10 @@ void PlayerState_detach(entity this) if (ps.m_client != this) return; // don't own state, spectator ps.ps_push(ps, this); + Inventory_delete(ps); FOREACH_CLIENT(PS(it) == ps, { PS(it) = NULL; }); delete(ps); - - Inventory_delete(this); } void GetCvars(entity this, entity store, int); @@ -31,7 +30,6 @@ void PlayerScore_Attach(entity this); void ClientData_Attach(entity this); void accuracy_init(entity this); void entcs_attach(entity this); -void playerdemo_init(entity this); void anticheat_init(entity this); void W_HitPlotOpen(entity this); void bot_clientconnect(entity this); @@ -51,17 +49,13 @@ void ClientState_attach(entity this) ClientData_Attach(this); accuracy_init(this); entcs_attach(this); - playerdemo_init(this); anticheat_init(this); W_HitPlotOpen(this); - - bot_clientconnect(this); } void bot_clientdisconnect(entity this); void W_HitPlotClose(entity this); void anticheat_report_to_eventlog(entity this); -void playerdemo_shutdown(entity this); void entcs_detach(entity this); void accuracy_free(entity this); void ClientData_Detach(entity this); @@ -74,13 +68,11 @@ void ClientState_detach(entity this) PlayerScore_Detach(this); // what ^they^ said W_HitPlotClose(this); ClientData_Detach(this); + entcs_detach(this); delete(CS(this)); this._cs = NULL; - bot_clientdisconnect(this); anticheat_report_to_eventlog(this); - playerdemo_shutdown(this); - entcs_detach(this); } diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index db681315a6..cf51ea66b3 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -120,6 +120,13 @@ REGISTER_STAT(ENTRAP_ORB, float) REGISTER_STAT(ENTRAP_ORB_ALPHA, float) REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime) REGISTER_STAT(KILL_TIME, float) +REGISTER_STAT(VEIL_ORB, float) +REGISTER_STAT(VEIL_ORB_ALPHA, float) + +#ifdef SVQC +float autocvar_sv_showfps = 5; +#endif +REGISTER_STAT(SHOWFPS, float, autocvar_sv_showfps) #ifdef SVQC bool autocvar_g_ctf_leaderboard; @@ -294,6 +301,11 @@ bool autocvar_sv_slick_applygravity; #endif REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity) +#ifdef SVQC +bool autocvar_sv_vq3compat; +#endif +REGISTER_STAT(VQ3COMPAT, bool, autocvar_sv_vq3compat) + #ifdef SVQC #include "physics/movetypes/movetypes.qh" float warmup_limit; @@ -353,6 +365,10 @@ REGISTER_STAT(MOVEVARS_STEPHEIGHT, float, autocvar_sv_stepheight) REGISTER_STAT(MOVEVARS_AIRACCEL_QW, float) REGISTER_STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, float) REGISTER_STAT(MOVEVARS_SPECIALCOMMAND, bool) +#ifdef SVQC +int autocvar_sv_wallclip; +#endif +REGISTER_STAT(MOVEVARS_WALLCLIP, int, autocvar_sv_wallclip) #ifdef CSQC diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index 249615d2d3..9675816cc5 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -15,7 +15,7 @@ #include "constants.qh" #include <common/deathtypes/all.qh> #include <common/notifications/all.qh> - #include "triggers/subs.qh" + #include "mapobjects/subs.qh" #include "util.qh" #include <common/monsters/_mod.qh> @@ -50,13 +50,8 @@ void Item_SetAlpha(entity this) } else { - if (autocvar_cl_ghost_items_color) - { - this.alpha = autocvar_cl_ghost_items; - this.colormod = this.glowmod = autocvar_cl_ghost_items_color; - } - else - this.alpha = -1; + this.alpha = autocvar_cl_ghost_items; + this.colormod = this.glowmod = autocvar_cl_ghost_items_color; } if((!veh_hud) && (this.ItemStatus & ITS_STAYWEP)) @@ -148,8 +143,17 @@ void Item_PreDraw(entity this) void ItemRemove(entity this) { - if(this.mdl) - strunzone(this.mdl); + strfree(this.mdl); +} + +HashMap ENT_CLIENT_ITEM_simple; +STATIC_INIT(ENT_CLIENT_ITEM_simple) +{ + HM_NEW(ENT_CLIENT_ITEM_simple); +} +SHUTDOWN(ENT_CLIENT_ITEM_simple) +{ + HM_DELETE(ENT_CLIENT_ITEM_simple); } NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) @@ -211,10 +215,8 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(!warpzone_warpzones_exist && this.fade_start && !autocvar_cl_items_nofade) setpredraw(this, Item_PreDraw); - if(this.mdl) - strunzone(this.mdl); + strfree(this.mdl); - this.mdl = ""; string _fn = ReadString(); this.item_simple = false; // reset it! @@ -223,27 +225,37 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) string _fn2 = substring(_fn, 0 , strlen(_fn) -4); this.item_simple = true; - if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".md3")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".dpm")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".iqm")); - else if(fexists(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl"))) - this.mdl = strzone(strcat(_fn2, autocvar_cl_simpleitems_postfix, ".mdl")); - else - { - this.item_simple = false; - LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it"); - } + #define extensions(x) \ + x(md3) \ + x(dpm) \ + x(iqm) \ + x(mdl) \ + /**/ + #define tryext(ext) { \ + string s = strcat(_fn2, autocvar_cl_simpleitems_postfix, "." #ext); \ + string cached = HM_gets(ENT_CLIENT_ITEM_simple, s); \ + if (cached == "") { \ + HM_sets(ENT_CLIENT_ITEM_simple, s, cached = fexists(s) ? "1" : "0"); \ + } \ + if (cached != "0") { \ + strcpy(this.mdl, s); \ + break; \ + } \ + } + do { + extensions(tryext); + this.item_simple = false; + LOG_TRACEF("Simple item requested for %s but no model exists for it", _fn); + } while (0); + #undef tryext + #undef extensions } if(!this.item_simple) - this.mdl = strzone(_fn); - + strcpy(this.mdl, _fn); if(this.mdl == "") - LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!"); + LOG_WARNF("this.mdl is unset for item %s", this.classname); precache_model(this.mdl); _setmodel(this, this.mdl); @@ -384,40 +396,13 @@ bool have_pickup_item(entity this) if(autocvar_g_pickup_items == 0) return false; if(g_weaponarena) - if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena + if(STAT(WEAPONS, this) || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena return false; } return true; } -/* -float Item_Customize() -{ - if(this.spawnshieldtime) - return true; - if(this.weapons & ~other.weapons) - { - this.colormod = '0 0 0'; - this.glowmod = this.colormod; - this.alpha = 0.5 + 0.5 * g_ghost_items; // halfway more alpha - return true; - } - else - { - if(g_ghost_items) - { - this.colormod = stov(autocvar_g_ghost_items_color); - this.glowmod = this.colormod; - this.alpha = g_ghost_items; - return true; - } - else - return false; - } -} -*/ - -void Item_Show (entity e, float mode) +void Item_Show (entity e, int mode) { e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST); e.ItemStatus &= ~ITS_STAYWEP; @@ -440,7 +425,7 @@ void Item_Show (entity e, float mode) } else { - bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons + bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.m_wepset & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons || e.team // weapon stay isn't supported for teamed weapons ; if(def.instanceOfWeaponPickup && !nostay && g_weapon_stay) @@ -500,7 +485,7 @@ void Item_Respawn (entity this) sound(this, CH_TRIGGER, this.itemdef.m_respawnsound, VOL_BASE, ATTEN_NORM); // play respawn sound setorigin(this, this.origin); - if (Item_ItemsTime_Allow(this.itemdef) || (this.weapons & WEPSET_SUPERWEAPONS)) + if (Item_ItemsTime_Allow(this.itemdef) || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) { float t = Item_ItemsTime_UpdateTime(this, 0); Item_ItemsTime_SetTime(this, t); @@ -585,13 +570,13 @@ void Item_RespawnThink(entity this) void Item_ScheduleRespawnIn(entity e, float t) { // if the respawn time is longer than 10 seconds, show a waypoint, otherwise, just respawn normally - if ((Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0) + if ((Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) || MUTATOR_CALLHOOK(Item_ScheduleRespawn, e, t)) && (t - ITEM_RESPAWN_TICKS) > 0) { setthink(e, Item_RespawnCountdown); e.nextthink = time + max(0, t - ITEM_RESPAWN_TICKS); e.scheduledrespawntime = e.nextthink + ITEM_RESPAWN_TICKS; e.item_respawncounter = 0; - if(Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS)) + if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)) { t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime); Item_ItemsTime_SetTime(e, t); @@ -605,7 +590,7 @@ void Item_ScheduleRespawnIn(entity e, float t) e.scheduledrespawntime = time + t; e.wait = time + t; - if(Item_ItemsTime_Allow(e.itemdef) || (e.weapons & WEPSET_SUPERWEAPONS)) + if(Item_ItemsTime_Allow(e.itemdef) || (STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS)) { t = Item_ItemsTime_UpdateTime(e, e.scheduledrespawntime); Item_ItemsTime_SetTime(e, t); @@ -628,14 +613,18 @@ float adjust_respawntime(float normal_respawntime) { return normal_respawntime; } - CheckAllowedTeams(NULL); - GetTeamCounts(NULL); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + TeamBalance_GetTeamCounts(balance, NULL); int players = 0; - if (c1 != -1) players += c1; - if (c2 != -1) players += c2; - if (c3 != -1) players += c3; - if (c4 != -1) players += c4; - + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (TeamBalance_IsTeamAllowed(balance, i)) + { + players += TeamBalance_GetNumberOfPlayers(balance, i); + } + } + TeamBalance_Destroy(balance); + if (players >= 2) { return normal_respawntime * (r / (players + o) + l); } else { @@ -663,8 +652,6 @@ void Item_ScheduleRespawn(entity e) AUTOCVAR(g_pickup_respawntime_initial_random, int, 1, "For items that don't start spawned: 0: spawn after their normal respawntime; 1: spawn after `random * respawntime` with the *same* random; 2: same as 1 but each item has separate random"); -float shared_random; -STATIC_INIT(shared_random) { shared_random = random(); } void Item_ScheduleInitialRespawn(entity e) { Item_Show(e, 0); @@ -675,18 +662,26 @@ void Item_ScheduleInitialRespawn(entity e) // range: respawntime .. respawntime + respawntimejitter spawn_in = e.respawntime + random() * e.respawntimejitter; } - else if (autocvar_g_pickup_respawntime_initial_random == 1) + else { + float rnd; + if (autocvar_g_pickup_respawntime_initial_random == 1) + { + static float shared_random = 0; + // NOTE this code works only if items are scheduled at the same time (normal case) + // NOTE2 random() can't return exactly 1 so this check always work as intended + if (!shared_random || floor(time) > shared_random) + shared_random = floor(time) + random(); + rnd = shared_random - floor(time); + } + else + rnd = random(); + // range: // if respawntime >= ITEM_RESPAWN_TICKS: ITEM_RESPAWN_TICKS .. respawntime + respawntimejitter // else: 0 .. ITEM_RESPAWN_TICKS // this is to prevent powerups spawning unexpectedly without waypoints - spawn_in = ITEM_RESPAWN_TICKS + shared_random * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS); - } - else - { - // range: same as 1 - spawn_in = ITEM_RESPAWN_TICKS + random() * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS); + spawn_in = ITEM_RESPAWN_TICKS + rnd * (e.respawntime + e.respawntimejitter - ITEM_RESPAWN_TICKS); } Item_ScheduleRespawnIn(e, max(0, game_starttime - time) + ((e.respawntimestart) ? e.respawntimestart : spawn_in)); @@ -710,7 +705,7 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, FOREACH(Weapons, it != WEP_Null, { // Finding a weapon which player doesn't have. - if (!(receiver.weapons & it.m_wepset) && (it.netname == weapon)) + if (!(STAT(WEAPONS, receiver) & it.m_wepset) && (it.netname == weapon)) { RandomSelection_AddEnt(it, 1, 1); break; @@ -721,7 +716,7 @@ void GiveRandomWeapons(entity receiver, int num_weapons, string weapon_names, { return; } - receiver.weapons |= RandomSelection_chosen_ent.m_wepset; + STAT(WEAPONS, receiver) |= RandomSelection_chosen_ent.m_wepset; if (RandomSelection_chosen_ent.ammo_type == RESOURCE_NONE) { continue; @@ -751,14 +746,14 @@ float Item_GiveAmmoTo(entity item, entity player, int resource_type, float ammom { return false; } - GiveResourceWithLimit(player, resource_type, amount, ammomax); + GiveOrTakeResourceWithLimit(player, resource_type, amount, ammomax); return true; } if (g_weapon_stay != 2) { return false; } - GiveResourceWithLimit(player, resource_type, amount, min(amount, ammomax)); + GiveOrTakeResourceWithLimit(player, resource_type, amount, min(amount, ammomax)); return true; } @@ -783,7 +778,7 @@ float Item_GiveTo(entity item, entity player) if(player.(weaponentity).m_switchweapon == w_getbestweapon(player, weaponentity)) _switchweapon |= BIT(slot); - if(!(player.weapons & WepSet_FromWeapon(player.(weaponentity).m_switchweapon))) + if(!(STAT(WEAPONS, player) & WepSet_FromWeapon(player.(weaponentity).m_switchweapon))) _switchweapon |= BIT(slot); } } @@ -799,8 +794,8 @@ float Item_GiveTo(entity item, entity player) if (item.itemdef.instanceOfWeaponPickup) { WepSet w; - w = item.weapons; - w &= ~player.weapons; + w = STAT(WEAPONS, item); + w &= ~STAT(WEAPONS, player); if (w || (item.spawnshieldtime && item.pickup_anyway > 0)) { @@ -833,7 +828,8 @@ float Item_GiveTo(entity item, entity player) { pickedup = true; player.items |= its; - Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); + // TODO: we probably want to show a message in the console, but not this one! + //Send_Notification(NOTIF_ONE, player, MSG_INFO, INFO_ITEM_WEAPON_GOT, item.netname); } if (item.strength_finished) @@ -860,7 +856,7 @@ float Item_GiveTo(entity item, entity player) return 0; // crude hack to enforce switching weapons - if(g_cts && item.itemdef.instanceOfWeaponPickup) + if(g_cts && item.itemdef.instanceOfWeaponPickup && !CS(player).cvar_cl_cts_noautoswitch) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -990,7 +986,7 @@ void Item_Reset(entity this) { WaypointSprite_Kill(this.waypointsprite_attached); } - if (this.itemdef.instanceOfPowerup || (this.weapons & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! + if (this.itemdef.instanceOfPowerup || (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) // do not spawn powerups initially! { Item_ScheduleInitialRespawn(this); } @@ -1049,7 +1045,7 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup float weapon_pickupevalfunc(entity player, entity item) { // See if I have it already - if(player.weapons & item.weapons) + if(STAT(WEAPONS, player) & STAT(WEAPONS, item)) { // If I can pick it up if(!item.spawnshieldtime) @@ -1060,7 +1056,7 @@ float weapon_pickupevalfunc(entity player, entity item) // reduce weapon value if bot already got a good arsenal float c = 1; int weapons_value = 0; - FOREACH(Weapons, it != WEP_Null && (player.weapons & it.m_wepset), { + FOREACH(Weapons, it != WEP_Null && (STAT(WEAPONS, player) & it.m_wepset), { weapons_value += it.bot_pickupbasevalue; }); c -= bound(0, weapons_value / 20000, 1) * 0.5; @@ -1079,12 +1075,12 @@ float ammo_pickupevalfunc(entity player, entity item) if(item.itemdef.instanceOfWeaponPickup) { entity ammo = NULL; - if(item.ammo_shells) { need_shells = true; ammo = ITEM_Shells; } - else if(item.ammo_nails) { need_nails = true; ammo = ITEM_Bullets; } - else if(item.ammo_rockets) { need_rockets = true; ammo = ITEM_Rockets; } - else if(item.ammo_cells) { need_cells = true; ammo = ITEM_Cells; } - else if(item.ammo_plasma) { need_plasma = true; ammo = ITEM_Plasma; } - else if(item.ammo_fuel) { need_fuel = true; ammo = ITEM_JetpackFuel; } + if(GetResourceAmount(item, RESOURCE_SHELLS)) { need_shells = true; ammo = ITEM_Shells; } + else if(GetResourceAmount(item, RESOURCE_BULLETS)) { need_nails = true; ammo = ITEM_Bullets; } + else if(GetResourceAmount(item, RESOURCE_ROCKETS)) { need_rockets = true; ammo = ITEM_Rockets; } + else if(GetResourceAmount(item, RESOURCE_CELLS)) { need_cells = true; ammo = ITEM_Cells; } + else if(GetResourceAmount(item, RESOURCE_PLASMA)) { need_plasma = true; ammo = ITEM_Plasma; } + else if(GetResourceAmount(item, RESOURCE_FUEL)) { need_fuel = true; ammo = ITEM_JetpackFuel; } if(!ammo) return 0; @@ -1094,7 +1090,7 @@ float ammo_pickupevalfunc(entity player, entity item) else { FOREACH(Weapons, it != WEP_Null, { - if(!(player.weapons & (it.m_wepset))) + if(!(STAT(WEAPONS, player) & (it.m_wepset))) continue; switch(it.ammo_type) @@ -1112,23 +1108,23 @@ float ammo_pickupevalfunc(entity player, entity item) float noammorating = 0.5; - if ((need_shells) && (item.ammo_shells) && (player.ammo_shells < g_pickup_shells_max)) - c = item.ammo_shells / max(noammorating, player.ammo_shells); + if ((need_shells) && GetResourceAmount(item, RESOURCE_SHELLS) && (GetResourceAmount(player, RESOURCE_SHELLS) < g_pickup_shells_max)) + c = GetResourceAmount(item, RESOURCE_SHELLS) / max(noammorating, GetResourceAmount(player, RESOURCE_SHELLS)); - if ((need_nails) && (item.ammo_nails) && (player.ammo_nails < g_pickup_nails_max)) - c = item.ammo_nails / max(noammorating, player.ammo_nails); + if ((need_nails) && GetResourceAmount(item, RESOURCE_BULLETS) && (GetResourceAmount(player, RESOURCE_BULLETS) < g_pickup_nails_max)) + c = GetResourceAmount(item, RESOURCE_BULLETS) / max(noammorating, GetResourceAmount(player, RESOURCE_BULLETS)); - if ((need_rockets) && (item.ammo_rockets) && (player.ammo_rockets < g_pickup_rockets_max)) - c = item.ammo_rockets / max(noammorating, player.ammo_rockets); + if ((need_rockets) && GetResourceAmount(item, RESOURCE_ROCKETS) && (GetResourceAmount(player, RESOURCE_ROCKETS) < g_pickup_rockets_max)) + c = GetResourceAmount(item, RESOURCE_ROCKETS) / max(noammorating, GetResourceAmount(player, RESOURCE_ROCKETS)); - if ((need_cells) && (item.ammo_cells) && (player.ammo_cells < g_pickup_cells_max)) - c = item.ammo_cells / max(noammorating, player.ammo_cells); + if ((need_cells) && GetResourceAmount(item, RESOURCE_CELLS) && (GetResourceAmount(player, RESOURCE_CELLS) < g_pickup_cells_max)) + c = GetResourceAmount(item, RESOURCE_CELLS) / max(noammorating, GetResourceAmount(player, RESOURCE_CELLS)); - if ((need_plasma) && (item.ammo_plasma) && (player.ammo_plasma < g_pickup_plasma_max)) - c = item.ammo_plasma / max(noammorating, player.ammo_plasma); + if ((need_plasma) && GetResourceAmount(item, RESOURCE_PLASMA) && (GetResourceAmount(player, RESOURCE_PLASMA) < g_pickup_plasma_max)) + c = GetResourceAmount(item, RESOURCE_PLASMA) / max(noammorating, GetResourceAmount(player, RESOURCE_PLASMA)); - if ((need_fuel) && (item.ammo_fuel) && (player.ammo_fuel < g_pickup_fuel_max)) - c = item.ammo_fuel / max(noammorating, player.ammo_fuel); + if ((need_fuel) && GetResourceAmount(item, RESOURCE_FUEL) && (GetResourceAmount(player, RESOURCE_FUEL) < g_pickup_fuel_max)) + c = GetResourceAmount(item, RESOURCE_FUEL) / max(noammorating, GetResourceAmount(player, RESOURCE_FUEL)); rating *= min(c, 2); if(wpn) @@ -1141,8 +1137,8 @@ float healtharmor_pickupevalfunc(entity player, entity item) float c = 0; float rating = item.bot_pickupbasevalue; - float itemarmor = item.armorvalue; - float itemhealth = item.health; + float itemarmor = GetResourceAmount(item, RESOURCE_ARMOR); + float itemhealth = GetResourceAmount(item, RESOURCE_HEALTH); if(item.item_group) { @@ -1150,11 +1146,11 @@ float healtharmor_pickupevalfunc(entity player, entity item) itemhealth *= min(4, item.item_group_count); } - if (itemarmor && (player.armorvalue < item.max_armorvalue)) - c = itemarmor / max(1, player.armorvalue * 2/3 + player.health * 1/3); + if (itemarmor && (GetResourceAmount(player, RESOURCE_ARMOR) < item.max_armorvalue)) + c = itemarmor / max(1, GetResourceAmount(player, RESOURCE_ARMOR) * 2/3 + GetResourceAmount(player, RESOURCE_HEALTH) * 1/3); - if (itemhealth && (player.health < item.max_health)) - c = itemhealth / max(1, player.health); + if (itemhealth && (GetResourceAmount(player, RESOURCE_HEALTH) < item.max_health)) + c = itemhealth / max(1, GetResourceAmount(player, RESOURCE_HEALTH)); rating *= min(2, c); return rating; @@ -1187,7 +1183,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default this.item_pickupsound_ent = pickupsound; if(def.m_iteminit) - def.m_iteminit(this); + def.m_iteminit(def, this); if(!this.respawntime) // both need to be set { @@ -1210,7 +1206,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default } if(weaponid) - this.weapons = WepSet_FromWeapon(Weapons_from(weaponid)); + STAT(WEAPONS, this) = WepSet_FromWeapon(Weapons_from(weaponid)); this.flags = FL_ITEM | itemflags; IL_PUSH(g_items, this); @@ -1321,7 +1317,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default || (def.instanceOfHealth && def != ITEM_HealthSmall) || (def.instanceOfArmor && def != ITEM_ArmorSmall) || (itemid & (IT_KEY1 | IT_KEY2)) - ) this.target = "###item###"; // for finding the nearest item using find() + ) this.target = "###item###"; // for finding the nearest item using findnearest Item_ItemsTime_SetTime(this, 0); } @@ -1343,7 +1339,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(def.instanceOfPowerup) this.ItemStatus |= ITS_ANIMATE1; - if(this.armorvalue || this.health) + if(GetResourceAmount(this, RESOURCE_ARMOR) || GetResourceAmount(this, RESOURCE_HEALTH)) this.ItemStatus |= ITS_ANIMATE2; } @@ -1405,7 +1401,7 @@ int group_count = 1; void setItemGroup(entity this) { - if(!IS_SMALL(this.itemdef)) + if(!IS_SMALL(this.itemdef) || Item_IsLoot(this)) return; FOREACH_ENTITY_RADIUS(this.origin, 120, (it != this) && IS_SMALL(it.itemdef), @@ -1519,7 +1515,7 @@ spawnfunc(target_items) s = W_UndeprecateName(argv(j)); if(s == it.netname) { - this.weapons |= (it.m_wepset); + STAT(WEAPONS, this) |= (it.m_wepset); if(this.spawnflags == 0 || this.spawnflags == 2) it.wr_init(it); break; @@ -1563,16 +1559,16 @@ spawnfunc(target_items) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, this.superweapons_finished * boolean(this.items & IT_SUPERWEAPON), "superweapons"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_Jetpack.m_itemid), "jetpack"); this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, boolean(this.items & ITEM_JetpackRegen.m_itemid), "fuel_regen"); - if(this.ammo_shells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_shells), "shells"); - if(this.ammo_nails != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_nails), "nails"); - if(this.ammo_rockets != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_rockets), "rockets"); - if(this.ammo_cells != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_cells), "cells"); - if(this.ammo_plasma != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_plasma), "plasma"); - if(this.ammo_fuel != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.ammo_fuel), "fuel"); - if(this.health != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.health), "health"); - if(this.armorvalue != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, this.armorvalue), "armor"); + if(GetResourceAmount(this, RESOURCE_SHELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_SHELLS)), "shells"); + if(GetResourceAmount(this, RESOURCE_BULLETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_BULLETS)), "nails"); + if(GetResourceAmount(this, RESOURCE_ROCKETS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_ROCKETS)), "rockets"); + if(GetResourceAmount(this, RESOURCE_CELLS) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_CELLS)), "cells"); + if(GetResourceAmount(this, RESOURCE_PLASMA) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_PLASMA)), "plasma"); + if(GetResourceAmount(this, RESOURCE_FUEL) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_FUEL)), "fuel"); + if(GetResourceAmount(this, RESOURCE_HEALTH) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_HEALTH)), "health"); + if(GetResourceAmount(this, RESOURCE_ARMOR) != 0) this.netname = sprintf("%s %s%d %s", this.netname, valueprefix, max(0, GetResourceAmount(this, RESOURCE_ARMOR)), "armor"); FOREACH(Buffs, it != BUFF_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(BUFFS, this) & (it.m_itemid)), it.m_name)); - FOREACH(Weapons, it != WEP_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(this.weapons & (it.m_wepset)), it.netname)); + FOREACH(Weapons, it != WEP_Null, this.netname = sprintf("%s %s%d %s", this.netname, itemprefix, !!(STAT(WEAPONS, this) & (it.m_wepset)), it.netname)); } this.netname = strzone(this.netname); //print(this.netname, "\n"); @@ -1591,30 +1587,30 @@ float GiveWeapon(entity e, float wpn, float op, float val) { WepSet v0, v1; WepSet s = WepSet_FromWeapon(Weapons_from(wpn)); - v0 = (e.weapons & s); + v0 = (STAT(WEAPONS, e) & s); switch(op) { case OP_SET: if(val > 0) - e.weapons |= s; + STAT(WEAPONS, e) |= s; else - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; case OP_MIN: case OP_PLUS: if(val > 0) - e.weapons |= s; + STAT(WEAPONS, e) |= s; break; case OP_MAX: if(val <= 0) - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; case OP_MINUS: if(val > 0) - e.weapons &= ~s; + STAT(WEAPONS, e) &= ~s; break; } - v1 = (e.weapons & s); + v1 = (STAT(WEAPONS, e) & s); return (v0 != v1); } @@ -1670,6 +1666,31 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa else if(v0 > v1) e.(regenfield) = max(e.(regenfield), time + regentime); } +bool GiveResourceValue(entity e, int resource_type, int op, int val) +{ + int v0 = GetResourceAmount(e, resource_type); + switch (op) + { + case OP_SET: + SetResourceAmount(e, resource_type, val); + break; + case OP_MIN: + SetResourceAmount(e, resource_type, max(v0, val)); // min 100 cells = at least 100 cells + break; + case OP_MAX: + SetResourceAmount(e, resource_type, min(v0, val)); + break; + case OP_PLUS: + SetResourceAmount(e, resource_type, v0 + val); + break; + case OP_MINUS: + SetResourceAmount(e, resource_type, v0 - val); + break; + } + int v1 = GetResourceAmount(e, resource_type); + return v0 != v1; +} + float GiveItems(entity e, float beginarg, float endarg) { float got, i, val, op; @@ -1702,14 +1723,14 @@ float GiveItems(entity e, float beginarg, float endarg) PREGIVE(e, strength_finished); PREGIVE(e, invincible_finished); PREGIVE(e, superweapons_finished); - PREGIVE(e, ammo_nails); - PREGIVE(e, ammo_cells); - PREGIVE(e, ammo_plasma); - PREGIVE(e, ammo_shells); - PREGIVE(e, ammo_rockets); - PREGIVE(e, ammo_fuel); - PREGIVE(e, armorvalue); - PREGIVE(e, health); + PREGIVE_RESOURCE(e, RESOURCE_BULLETS); + PREGIVE_RESOURCE(e, RESOURCE_CELLS); + PREGIVE_RESOURCE(e, RESOURCE_PLASMA); + PREGIVE_RESOURCE(e, RESOURCE_SHELLS); + PREGIVE_RESOURCE(e, RESOURCE_ROCKETS); + PREGIVE_RESOURCE(e, RESOURCE_FUEL); + PREGIVE_RESOURCE(e, RESOURCE_ARMOR); + PREGIVE_RESOURCE(e, RESOURCE_HEALTH); for(i = beginarg; i < endarg; ++i) { @@ -1746,19 +1767,19 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); case "all": got += GiveBit(e, items, ITEM_Jetpack.m_itemid, op, val); - got += GiveValue(e, health, op, val); - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RESOURCE_HEALTH, op, val); + got += GiveResourceValue(e, RESOURCE_ARMOR, op, val); case "allweapons": FOREACH(Weapons, it != WEP_Null && !(it.spawnflags & WEP_FLAG_MUTATORBLOCKED), got += GiveWeapon(e, it.m_id, op, val)); //case "allbuffs": // all buffs makes a player god, do not want! //FOREACH(Buffs, it != BUFF_Null, got += GiveBuff(e, it.m_itemid, op, val)); case "allammo": - got += GiveValue(e, ammo_cells, op, val); - got += GiveValue(e, ammo_plasma, op, val); - got += GiveValue(e, ammo_shells, op, val); - got += GiveValue(e, ammo_nails, op, val); - got += GiveValue(e, ammo_rockets, op, val); - got += GiveValue(e, ammo_fuel, op, val); + got += GiveResourceValue(e, RESOURCE_CELLS, op, val); + got += GiveResourceValue(e, RESOURCE_PLASMA, op, val); + got += GiveResourceValue(e, RESOURCE_SHELLS, op, val); + got += GiveResourceValue(e, RESOURCE_BULLETS, op, val); + got += GiveResourceValue(e, RESOURCE_ROCKETS, op, val); + got += GiveResourceValue(e, RESOURCE_FUEL, op, val); break; case "unlimited_ammo": got += GiveBit(e, items, IT_UNLIMITED_AMMO, op, val); @@ -1785,29 +1806,29 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveValue(e, superweapons_finished, op, val); break; case "cells": - got += GiveValue(e, ammo_cells, op, val); + got += GiveResourceValue(e, RESOURCE_CELLS, op, val); break; case "plasma": - got += GiveValue(e, ammo_plasma, op, val); + got += GiveResourceValue(e, RESOURCE_PLASMA, op, val); break; case "shells": - got += GiveValue(e, ammo_shells, op, val); + got += GiveResourceValue(e, RESOURCE_SHELLS, op, val); break; case "nails": case "bullets": - got += GiveValue(e, ammo_nails, op, val); + got += GiveResourceValue(e, RESOURCE_BULLETS, op, val); break; case "rockets": - got += GiveValue(e, ammo_rockets, op, val); + got += GiveResourceValue(e, RESOURCE_ROCKETS, op, val); break; case "health": - got += GiveValue(e, health, op, val); + got += GiveResourceValue(e, RESOURCE_HEALTH, op, val); break; case "armor": - got += GiveValue(e, armorvalue, op, val); + got += GiveResourceValue(e, RESOURCE_ARMOR, op, val); break; case "fuel": - got += GiveValue(e, ammo_fuel, op, val); + got += GiveResourceValue(e, RESOURCE_FUEL, op, val); break; default: FOREACH(Buffs, it != BUFF_Null && Buff_UndeprecateName(cmd) == it.m_name, @@ -1832,23 +1853,23 @@ float GiveItems(entity e, float beginarg, float endarg) FOREACH(Weapons, it != WEP_Null, { POSTGIVE_WEAPON(e, it, SND_WEAPONPICKUP, SND_Null); if(!(save_weapons & (it.m_wepset))) - if(e.weapons & (it.m_wepset)) + if(STAT(WEAPONS, e) & (it.m_wepset)) it.wr_init(it); }); POSTGIVE_VALUE(e, strength_finished, 1, SND_POWERUP, SND_POWEROFF); POSTGIVE_VALUE(e, invincible_finished, 1, SND_Shield, SND_POWEROFF); //POSTGIVE_VALUE(e, superweapons_finished, 1, SND_Null, SND_Null); - POSTGIVE_VALUE(e, ammo_nails, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_cells, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_plasma, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_shells, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE(e, ammo_rockets, 0, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE_ROT(e, ammo_fuel, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND_ITEMPICKUP, SND_Null); - POSTGIVE_VALUE_ROT(e, armorvalue, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_ARMOR25, SND_Null); - POSTGIVE_VALUE_ROT(e, health, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_MEGAHEALTH, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_BULLETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_CELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_PLASMA, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_SHELLS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE(e, RESOURCE_ROCKETS, 0, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_FUEL, 1, pauserotfuel_finished, autocvar_g_balance_pause_fuel_rot, pauseregen_finished, autocvar_g_balance_pause_fuel_regen, SND_ITEMPICKUP, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_ARMOR, 1, pauserotarmor_finished, autocvar_g_balance_pause_armor_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_ARMOR25, SND_Null); + POSTGIVE_RESOURCE_ROT(e, RESOURCE_HEALTH, 1, pauserothealth_finished, autocvar_g_balance_pause_health_rot, pauseregen_finished, autocvar_g_balance_pause_health_regen, SND_MEGAHEALTH, SND_Null); if(e.superweapons_finished <= 0) - if(e.weapons & WEPSET_SUPERWEAPONS) + if(STAT(WEAPONS, e) & WEPSET_SUPERWEAPONS) e.superweapons_finished = autocvar_g_balance_superweapons_time; if(e.strength_finished <= 0) @@ -1868,7 +1889,7 @@ float GiveItems(entity e, float beginarg, float endarg) { .entity weaponentity = weaponentities[slot]; if(e.(weaponentity).m_weapon != WEP_Null || slot == 0) - if(!(e.weapons & WepSet_FromWeapon(e.(weaponentity).m_switchweapon))) + if(!(STAT(WEAPONS, e) & WepSet_FromWeapon(e.(weaponentity).m_switchweapon))) _switchweapon |= BIT(slot); } diff --git a/qcsrc/common/t_items.qh b/qcsrc/common/t_items.qh index 315a100375..4d3f453800 100644 --- a/qcsrc/common/t_items.qh +++ b/qcsrc/common/t_items.qh @@ -62,7 +62,7 @@ const float ITEM_RESPAWN_TICKS = 10; .float item_respawncounter; -void Item_Show (entity e, float mode); +void Item_Show (entity e, int mode); void Item_Respawn (entity this); @@ -107,7 +107,7 @@ float weapon_pickupevalfunc(entity player, entity item); float ammo_pickupevalfunc(entity player, entity item); float healtharmor_pickupevalfunc(entity player, entity item); -.float is_item; +.bool is_item; .entity itemdef; void _StartItem(entity this, entity def, float defaultrespawntime, float defaultrespawntimejitter); @@ -124,10 +124,15 @@ void GiveSound(entity e, float v0, float v1, float t, Sound snd_incr, Sound snd_ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .float regenfield, float regentime); -#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = e.weapons +spawnfunc(target_items); + +#define PREGIVE_WEAPONS(e) WepSet save_weapons; save_weapons = STAT(WEAPONS, e) #define PREGIVE(e,f) float save_##f; save_##f = (e).f -#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(e.weapons & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr) +#define PREGIVE_RESOURCE(e,f) float save_##f = GetResourceAmount((e), (f)) +#define POSTGIVE_WEAPON(e,b,snd_incr,snd_decr) GiveSound((e), !!(save_weapons & WepSet_FromWeapon(b)), !!(STAT(WEAPONS, e) & WepSet_FromWeapon(b)), 0, snd_incr, snd_decr) #define POSTGIVE_BIT(e,f,b,snd_incr,snd_decr) GiveSound((e), save_##f & (b), (e).f & (b), 0, snd_incr, snd_decr) +#define POSTGIVE_RESOURCE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, GetResourceAmount((e), (f)), t, snd_incr, snd_decr) +#define POSTGIVE_RESOURCE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e),save_##f,GetResourceAmount((e),(f)),rotfield,rottime,regenfield,regentime);GiveSound((e),save_##f,GetResourceAmount((e),(f)),t,snd_incr,snd_decr) #define POSTGIVE_VALUE(e,f,t,snd_incr,snd_decr) GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) #define POSTGIVE_VALUE_ROT(e,f,t,rotfield,rottime,regenfield,regentime,snd_incr,snd_decr) GiveRot((e), save_##f, (e).f, rotfield, rottime, regenfield, regentime); GiveSound((e), save_##f, (e).f, t, snd_incr, snd_decr) diff --git a/qcsrc/common/teams.qh b/qcsrc/common/teams.qh index 57d644c044..62bb2db7cd 100644 --- a/qcsrc/common/teams.qh +++ b/qcsrc/common/teams.qh @@ -1,5 +1,7 @@ #pragma once +const int NUM_TEAMS = 4; ///< Number of teams in the game. + #ifdef TEAMNUMBERS_THAT_ARENT_STUPID const int NUM_TEAM_1 = 1; // red const int NUM_TEAM_2 = 2; // blue @@ -54,11 +56,11 @@ const string STATIC_NAME_TEAM_3 = "Yellow"; const string STATIC_NAME_TEAM_4 = "Pink"; #ifdef CSQC -float teamplay; -float myteam; +bool teamplay; +int myteam; #endif -string Team_ColorCode(float teamid) +string Team_ColorCode(int teamid) { switch(teamid) { @@ -71,7 +73,7 @@ string Team_ColorCode(float teamid) return "^7"; } -vector Team_ColorRGB(float teamid) +vector Team_ColorRGB(int teamid) { switch(teamid) { @@ -84,7 +86,7 @@ vector Team_ColorRGB(float teamid) return '0 0 0'; } -string Team_ColorName(float teamid) +string Team_ColorName(int teamid) { switch(teamid) { @@ -98,7 +100,7 @@ string Team_ColorName(float teamid) } // used for replacement in filenames or such where the name CANNOT be allowed to be translated -string Static_Team_ColorName(float teamid) +string Static_Team_ColorName(int teamid) { switch(teamid) { @@ -125,12 +127,12 @@ float Team_ColorToTeam(string team_color) return -1; } -/// \brief Returns whether team is valid. -/// \param[in] team_ Team to check. +/// \brief Returns whether team value is valid. +/// \param[in] team_num Team to check. /// \return True if team is valid, false otherwise. -bool Team_IsValidTeam(int team_) +bool Team_IsValidTeam(int team_num) { - switch (team_) + switch (team_num) { case NUM_TEAM_1: case NUM_TEAM_2: @@ -143,12 +145,12 @@ bool Team_IsValidTeam(int team_) return false; } -/// \brief Returns whether team number is valid. -/// \param[in] number Team number to check. -/// \return True if team number is valid, false otherwise. -bool Team_IsValidNumber(int number) +/// \brief Returns whether the team index is valid. +/// \param[in] index Team index to check. +/// \return True if team index is valid, false otherwise. +bool Team_IsValidIndex(int index) { - switch (number) + switch (index) { case 1: case 2: @@ -161,36 +163,60 @@ bool Team_IsValidNumber(int number) return false; } -float Team_NumberToTeam(float number) +/// \brief Converts team index into team value. +/// \param[in] index Team index to convert. +/// \return Team value. +int Team_IndexToTeam(int index) { - switch(number) + switch (index) { case 1: return NUM_TEAM_1; case 2: return NUM_TEAM_2; case 3: return NUM_TEAM_3; case 4: return NUM_TEAM_4; } - return -1; } -float Team_TeamToNumber(float teamid) +/// \brief Converts team value into team index. +/// \param[in] team_num Team value to convert. +/// \return Team index. +int Team_TeamToIndex(int team_num) { - switch(teamid) + switch (team_num) { case NUM_TEAM_1: return 1; case NUM_TEAM_2: return 2; case NUM_TEAM_3: return 3; case NUM_TEAM_4: return 4; } - return -1; } +/// \brief Converts team value into bit value that is used in team bitmasks. +/// \param[in] team_num Team value to convert. +/// \return Team bit. +int Team_TeamToBit(int team_num) +{ + if (!Team_IsValidTeam(team_num)) + { + return 0; + } + return BIT(Team_TeamToIndex(team_num) - 1); +} + +/// \brief Converts team index into bit value that is used in team bitmasks. +/// \param[in] index Team index to convert. +/// \return Team bit. +int Team_IndexToBit(int index) +{ + return BIT(index - 1); +} + // legacy aliases for shitty code -#define TeamByColor(teamid) (Team_TeamToNumber(teamid) - 1) -#define ColorByTeam(number) Team_NumberToTeam(number + 1) +#define TeamByColor(teamid) (Team_TeamToIndex(teamid) - 1) +#define ColorByTeam(number) Team_IndexToTeam(number + 1) // useful aliases #define Team_ColorName_Lower(teamid) strtolower(Team_ColorName(teamid)) @@ -203,8 +229,8 @@ float Team_TeamToNumber(float teamid) #define Team_FullName(teamid) strcat(Team_ColorName(teamid), " ", NAME_TEAM, "^7") #define Team_ColoredFullName(teamid) strcat(Team_ColorCode(teamid), Team_ColorName(teamid), " ", NAME_TEAM, "^7") -#define Team_NumberToFullName(number) Team_FullName(Team_NumberToTeam(number)) -#define Team_NumberToColoredFullName(number) Team_ColoredFullName(Team_NumberToTeam(number)) +#define Team_IndexToFullName(index) Team_FullName(Team_IndexToTeam(index)) +#define Team_IndexToColoredFullName(index) Team_ColoredFullName(Team_IndexToTeam(index)) // replace these flags in a string with the strings provided #define TCR(input,type,team) strreplace("^TC", COL_TEAM_##team, strreplace("^TT", strtoupper(type##_TEAM_##team), input)) diff --git a/qcsrc/common/triggers/_mod.inc b/qcsrc/common/triggers/_mod.inc deleted file mode 100644 index 9b327de55d..0000000000 --- a/qcsrc/common/triggers/_mod.inc +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include <common/triggers/include.qc> -#include <common/triggers/platforms.qc> -#include <common/triggers/subs.qc> -#include <common/triggers/teleporters.qc> -#include <common/triggers/triggers.qc> - -#include <common/triggers/func/_mod.inc> -#include <common/triggers/misc/_mod.inc> -#include <common/triggers/target/_mod.inc> -#include <common/triggers/trigger/_mod.inc> diff --git a/qcsrc/common/triggers/_mod.qh b/qcsrc/common/triggers/_mod.qh deleted file mode 100644 index d3bb2dc3ad..0000000000 --- a/qcsrc/common/triggers/_mod.qh +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include <common/triggers/include.qh> -#include <common/triggers/platforms.qh> -#include <common/triggers/subs.qh> -#include <common/triggers/teleporters.qh> -#include <common/triggers/triggers.qh> - -#include <common/triggers/func/_mod.qh> -#include <common/triggers/misc/_mod.qh> -#include <common/triggers/target/_mod.qh> -#include <common/triggers/trigger/_mod.qh> diff --git a/qcsrc/common/triggers/func/_mod.inc b/qcsrc/common/triggers/func/_mod.inc deleted file mode 100644 index 675e3689b3..0000000000 --- a/qcsrc/common/triggers/func/_mod.inc +++ /dev/null @@ -1,19 +0,0 @@ -// generated file; do not modify -#include <common/triggers/func/bobbing.qc> -#include <common/triggers/func/breakable.qc> -#include <common/triggers/func/button.qc> -#include <common/triggers/func/conveyor.qc> -#include <common/triggers/func/door.qc> -#include <common/triggers/func/door_rotating.qc> -#include <common/triggers/func/door_secret.qc> -#include <common/triggers/func/fourier.qc> -#include <common/triggers/func/include.qc> -#include <common/triggers/func/ladder.qc> -#include <common/triggers/func/pendulum.qc> -#include <common/triggers/func/plat.qc> -#include <common/triggers/func/pointparticles.qc> -#include <common/triggers/func/rainsnow.qc> -#include <common/triggers/func/rotating.qc> -#include <common/triggers/func/stardust.qc> -#include <common/triggers/func/train.qc> -#include <common/triggers/func/vectormamamam.qc> diff --git a/qcsrc/common/triggers/func/_mod.qh b/qcsrc/common/triggers/func/_mod.qh deleted file mode 100644 index fb179a42cc..0000000000 --- a/qcsrc/common/triggers/func/_mod.qh +++ /dev/null @@ -1,19 +0,0 @@ -// generated file; do not modify -#include <common/triggers/func/bobbing.qh> -#include <common/triggers/func/breakable.qh> -#include <common/triggers/func/button.qh> -#include <common/triggers/func/conveyor.qh> -#include <common/triggers/func/door.qh> -#include <common/triggers/func/door_rotating.qh> -#include <common/triggers/func/door_secret.qh> -#include <common/triggers/func/fourier.qh> -#include <common/triggers/func/include.qh> -#include <common/triggers/func/ladder.qh> -#include <common/triggers/func/pendulum.qh> -#include <common/triggers/func/plat.qh> -#include <common/triggers/func/pointparticles.qh> -#include <common/triggers/func/rainsnow.qh> -#include <common/triggers/func/rotating.qh> -#include <common/triggers/func/stardust.qh> -#include <common/triggers/func/train.qh> -#include <common/triggers/func/vectormamamam.qh> diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc deleted file mode 100644 index ff8841a570..0000000000 --- a/qcsrc/common/triggers/func/bobbing.qc +++ /dev/null @@ -1,85 +0,0 @@ -#include "bobbing.qh" -#ifdef SVQC -.float height; -void func_bobbing_controller_think(entity this) -{ - vector v; - this.nextthink = time + 0.1; - - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - // calculate sinewave using makevectors - makevectors((this.nextthink * this.owner.cnt + this.owner.phase * 360) * '0 1 0'); - v = this.owner.destvec + this.owner.movedir * v_forward_y; - if(this.owner.classname == "func_bobbing") // don't brake stuff if the func_bobbing was killtarget'ed - // * 10 so it will arrive in 0.1 sec - this.owner.velocity = (v - this.owner.origin) * 10; -} - -/*QUAKED spawnfunc_func_bobbing (0 .5 .8) ? X_AXIS Y_AXIS -Brush model that moves back and forth on one axis (default Z). -speed : how long one cycle takes in seconds (default 4) -height : how far the cycle moves (default 32) -phase : cycle timing adjustment (0-1 as a fraction of the cycle, default 0) -noise : path/name of looping .wav file to play. -dmg : Do this mutch dmg every .dmgtime intervall when blocked -dmgtime : See above. -*/ -spawnfunc(func_bobbing) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - if (!this.speed) - this.speed = 4; - if (!this.height) - this.height = 32; - // center of bobbing motion - this.destvec = this.origin; - // time scale to get degrees - this.cnt = 360 / this.speed; - - this.active = ACTIVE_ACTIVE; - - // damage when blocked - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - // how far to bob - if (this.spawnflags & 1) // X - this.movedir = '1 0 0' * this.height; - else if (this.spawnflags & 2) // Y - this.movedir = '0 1 0' * this.height; - else // Z - this.movedir = '0 0 1' * this.height; - - if (!InitMovingBrushTrigger(this)) - return; - - // wait for targets to spawn - controller = new(func_bobbing_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_bobbing_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/bobbing.qh b/qcsrc/common/triggers/func/bobbing.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/bobbing.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc deleted file mode 100644 index ec71bc3fc3..0000000000 --- a/qcsrc/common/triggers/func/breakable.qc +++ /dev/null @@ -1,373 +0,0 @@ -#include "breakable.qh" -#ifdef SVQC - -#include <server/g_subs.qh> -#include <server/g_damage.qh> -#include <server/bot/api.qh> -#include <common/csqcmodel_settings.qh> -#include <lib/csqcmodel/sv_model.qh> -#include <server/weapons/common.qh> - -.entity sprite; - -.float dmg; -.float dmg_edge; -.float dmg_radius; -.float dmg_force; -.float debrismovetype; -.float debrissolid; -.vector debrisvelocity; -.vector debrisvelocityjitter; -.vector debrisavelocityjitter; -.float debristime; -.float debristimejitter; -.float debrisfadetime; -.float debrisdamageforcescale; -.float debrisskin; - -.string mdl_dead; // or "" to hide when broken -.string debris; // space separated list of debris models -// other fields: -// mdl = particle effect name -// count = particle effect multiplier -// targetname = target to trigger to unbreak the model -// target = targets to trigger when broken -// health = amount of damage it can take -// spawnflags: -// 1 = start disabled (needs to be triggered to activate) -// 2 = indicate damage -// 4 = don't take direct damage (needs to be triggered to 'explode', then triggered again to restore) -// notes: -// for mdl_dead to work, origin must be set (using a common/origin brush). -// Otherwise mdl_dead will be displayed at the map origin, and nobody would -// want that! - -void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); - -// -// func_breakable -// - basically func_assault_destructible for general gameplay use -// -void LaunchDebris (entity this, string debrisname, vector force) -{ - entity dbr = spawn(); - vector org = this.absmin - + '1 0 0' * random() * (this.absmax.x - this.absmin.x) - + '0 1 0' * random() * (this.absmax.y - this.absmin.y) - + '0 0 1' * random() * (this.absmax.z - this.absmin.z); - setorigin(dbr, org); - _setmodel (dbr, debrisname ); - dbr.skin = this.debrisskin; - dbr.colormap = this.colormap; // inherit team colors - dbr.owner = this; // do not be affected by our own explosion - set_movetype(dbr, this.debrismovetype); - dbr.solid = this.debrissolid; - if(dbr.solid != SOLID_BSP) // SOLID_BSP has exact collision, MAYBE this works? TODO check this out - setsize(dbr, '0 0 0', '0 0 0'); // needed for performance, until engine can deal better with it - dbr.velocity_x = this.debrisvelocity.x + this.debrisvelocityjitter.x * crandom(); - dbr.velocity_y = this.debrisvelocity.y + this.debrisvelocityjitter.y * crandom(); - dbr.velocity_z = this.debrisvelocity.z + this.debrisvelocityjitter.z * crandom(); - dbr.velocity = dbr.velocity + force * this.debrisdamageforcescale; - dbr.angles = this.angles; - dbr.avelocity_x = random()*this.debrisavelocityjitter.x; - dbr.avelocity_y = random()*this.debrisavelocityjitter.y; - dbr.avelocity_z = random()*this.debrisavelocityjitter.z; - dbr.damageforcescale = this.debrisdamageforcescale; - if(dbr.damageforcescale) - dbr.takedamage = DAMAGE_YES; - SUB_SetFade(dbr, time + this.debristime + crandom() * this.debristimejitter, this.debrisfadetime); -} - -void func_breakable_colormod(entity this) -{ - float h; - if (!(this.spawnflags & 2)) - return; - h = this.health / this.max_health; - if(h < 0.25) - this.colormod = '1 0 0'; - else if(h <= 0.75) - this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); - else - this.colormod = '1 1 1'; -} - -void func_breakable_look_destroyed(entity this) -{ - float floorZ; - - if(this.solid == SOLID_BSP) // in case a misc_follow moved me, save the current origin first - this.dropped_origin = this.origin; - - if(this.mdl_dead == "") - this.effects |= EF_NODRAW; - else { - if (this.origin == '0 0 0') { // probably no origin brush, so don't spawn in the middle of the map.. - floorZ = this.absmin.z; - setorigin(this, ((this.absmax + this.absmin) * 0.5)); - this.origin_z = floorZ; - } - _setmodel(this, this.mdl_dead); - ApplyMinMaxScaleAngles(this); - this.effects &= ~EF_NODRAW; - } - - this.solid = SOLID_NOT; -} - -void func_breakable_look_restore(entity this) -{ - _setmodel(this, this.mdl); - ApplyMinMaxScaleAngles(this); - this.effects &= ~EF_NODRAW; - - if(this.mdl_dead != "") // only do this if we use mdl_dead, to behave better with misc_follow - setorigin(this, this.dropped_origin); - - this.solid = SOLID_BSP; -} - -void func_breakable_behave_destroyed(entity this) -{ - this.health = this.max_health; - this.takedamage = DAMAGE_NO; - if(this.bot_attack) - IL_REMOVE(g_bot_targets, this); - this.bot_attack = false; - this.event_damage = func_null; - this.state = 1; - if(this.spawnflags & 4) - this.use = func_null; - func_breakable_colormod(this); - if (this.noise1) - stopsound (this, CH_TRIGGER_SINGLE); -} - -void func_breakable_think(entity this) -{ - this.nextthink = time; - CSQCMODEL_AUTOUPDATE(this); -} - -void func_breakable_destroy(entity this, entity actor, entity trigger); -void func_breakable_behave_restore(entity this) -{ - this.health = this.max_health; - if(this.sprite) - { - WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); - } - if(!(this.spawnflags & 4)) - { - this.takedamage = DAMAGE_AIM; - if(!this.bot_attack) - IL_PUSH(g_bot_targets, this); - this.bot_attack = true; - this.event_damage = func_breakable_damage; - } - if(this.spawnflags & 4) - this.use = func_breakable_destroy; // don't need to set it usually, as .use isn't reset - this.state = 0; - //this.nextthink = 0; // cancel auto respawn - setthink(this, func_breakable_think); - this.nextthink = time + 0.1; - func_breakable_colormod(this); - if (this.noise1) - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); -} - -void func_breakable_init_for_player(entity this, entity player) -{ - 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); - } -} - -void func_breakable_destroyed(entity this) -{ - func_breakable_look_destroyed(this); - func_breakable_behave_destroyed(this); -} - -void func_breakable_restore(entity this, entity actor, entity trigger) -{ - func_breakable_look_restore(this); - func_breakable_behave_restore(this); -} - -void func_breakable_restore_self(entity this) -{ - func_breakable_restore(this, NULL, NULL); -} - -vector debrisforce; // global, set before calling this -void func_breakable_destroy(entity this, entity actor, entity trigger) -{ - float n, i; - string oldmsg; - - entity act = this.owner; - this.owner = NULL; // set by W_PrepareExplosionByDamage - - // now throw around the debris - n = tokenize_console(this.debris); - for(i = 0; i < n; ++i) - LaunchDebris(this, argv(i), debrisforce); - - func_breakable_destroyed(this); - - if(this.noise) - _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - - if(this.dmg) - RadiusDamage(this, act, this.dmg, this.dmg_edge, this.dmg_radius, this, NULL, this.dmg_force, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, NULL); - - if(this.cnt) // TODO - __pointparticles(this.cnt, this.absmin * 0.5 + this.absmax * 0.5, '0 0 0', this.count); - - if(this.respawntime) - { - CSQCMODEL_AUTOUPDATE(this); - setthink(this, func_breakable_restore_self); - this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter; - } - - oldmsg = this.message; - this.message = ""; - SUB_UseTargets(this, act, trigger); - this.message = oldmsg; -} - -void func_breakable_destroy_self(entity this) -{ - func_breakable_destroy(this, NULL, NULL); -} - -void func_breakable_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.state == 1) - return; - if(this.spawnflags & DOOR_NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if(this.team) - if(attacker.team == this.team) - return; - this.pain_finished = time; - this.health = this.health - damage; - if(this.sprite) - { - WaypointSprite_Ping(this.sprite); - WaypointSprite_UpdateHealth(this.sprite, this.health); - } - func_breakable_colormod(this); - - if(this.health <= 0) - { - debrisforce = force; - - this.takedamage = DAMAGE_NO; - this.event_damage = func_null; - - if(IS_CLIENT(attacker)) //&& this.classname == "func_assault_destructible") - { - this.owner = attacker; - this.realowner = attacker; - } - - // do not explode NOW but in the NEXT FRAME! - // because recursive calls to RadiusDamage are not allowed - this.nextthink = time; - CSQCMODEL_AUTOUPDATE(this); - setthink(this, func_breakable_destroy_self); - } -} - -void func_breakable_reset(entity this) -{ - this.team = this.team_saved; - func_breakable_look_restore(this); - if(this.spawnflags & 1) - func_breakable_behave_destroyed(this); - else - func_breakable_behave_restore(this); -} - -// destructible walls that can be used to trigger target_objective_decrease -spawnfunc(func_breakable) -{ - float n, i; - if(!this.health) - this.health = 100; - this.max_health = this.health; - - // yes, I know, MOVETYPE_NONE is not available here, not that one would want it here anyway - if(!this.debrismovetype) this.debrismovetype = MOVETYPE_BOUNCE; - if(!this.debrissolid) this.debrissolid = SOLID_NOT; - if(this.debrisvelocity == '0 0 0') this.debrisvelocity = '0 0 140'; - if(this.debrisvelocityjitter == '0 0 0') this.debrisvelocityjitter = '70 70 70'; - if(this.debrisavelocityjitter == '0 0 0') this.debrisavelocityjitter = '600 600 600'; - if(!this.debristime) this.debristime = 3.5; - if(!this.debristimejitter) this.debristime = 2.5; - - if(this.mdl != "") - this.cnt = _particleeffectnum(this.mdl); - if(this.count == 0) - this.count = 1; - - if(this.message == "") - this.message = "got too close to an explosion"; - if(this.message2 == "") - this.message2 = "was pushed into an explosion by"; - if(!this.dmg_radius) - this.dmg_radius = 150; - if(!this.dmg_force) - this.dmg_force = 200; - - this.mdl = this.model; - SetBrushEntityModel(this); - - if(this.spawnflags & 4) - this.use = func_breakable_destroy; - else - this.use = func_breakable_restore; - - if(this.spawnflags & 4) - { - this.takedamage = DAMAGE_NO; - this.event_damage = func_null; - this.bot_attack = false; - } - - // precache all the models - if (this.mdl_dead) - precache_model(this.mdl_dead); - n = tokenize_console(this.debris); - for(i = 0; i < n; ++i) - precache_model(argv(i)); - if(this.noise) - precache_sound(this.noise); - if(this.noise1) - precache_sound(this.noise1); - - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - this.dropped_origin = this.origin; - - this.reset = func_breakable_reset; - this.reset(this); - - IL_PUSH(g_initforplayer, this); - this.init_for_player = func_breakable_init_for_player; - - CSQCMODEL_AUTOINIT(this); -} - -// for use in maps with a "model" key set -spawnfunc(misc_breakablemodel) { - spawnfunc_func_breakable(this); -} -#endif diff --git a/qcsrc/common/triggers/func/breakable.qh b/qcsrc/common/triggers/func/breakable.qh deleted file mode 100644 index 761a2c7a98..0000000000 --- a/qcsrc/common/triggers/func/breakable.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef SVQC -spawnfunc(func_breakable); -#endif diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc deleted file mode 100644 index c51c034f16..0000000000 --- a/qcsrc/common/triggers/func/button.qc +++ /dev/null @@ -1,174 +0,0 @@ -#include "button.qh" -#ifdef SVQC -// button and multiple button - -void button_wait(entity this); -void button_return(entity this); - -void button_wait(entity this) -{ - this.state = STATE_TOP; - if(this.wait >= 0) - { - this.nextthink = this.ltime + this.wait; - setthink(this, button_return); - } - SUB_UseTargets(this, this.enemy, NULL); - this.frame = 1; // use alternate textures -} - -void button_done(entity this) -{ - this.state = STATE_BOTTOM; -} - -void button_return(entity this) -{ - this.state = STATE_DOWN; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, button_done); - this.frame = 0; // use normal textures - if (this.health) - this.takedamage = DAMAGE_YES; // can be shot again -} - - -void button_blocked(entity this, entity blocker) -{ - // do nothing, just don't come all the way back out -} - - -void button_fire(entity this) -{ - this.health = this.max_health; - this.takedamage = DAMAGE_NO; // will be reset upon return - - if (this.state == STATE_UP || this.state == STATE_TOP) - return; - - if (this.noise != "") - _sound (this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - - this.state = STATE_UP; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, button_wait); -} - -void button_reset(entity this) -{ - this.health = this.max_health; - setorigin(this, this.pos1); - this.frame = 0; // use normal textures - this.state = STATE_BOTTOM; - this.velocity = '0 0 0'; - setthink(this, func_null); - this.nextthink = 0; - if (this.health) - this.takedamage = DAMAGE_YES; // can be shot again -} - -void button_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - this.enemy = actor; - button_fire(this); -} - -void button_touch(entity this, entity toucher) -{ - if (!toucher) - return; - if (!toucher.iscreature) - return; - if(toucher.velocity * this.movedir < 0) - return; - this.enemy = toucher; - if (toucher.owner) - this.enemy = toucher.owner; - button_fire (this); -} - -void button_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.spawnflags & DOOR_NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if (this.spawnflags & BUTTON_DONTACCUMULATEDMG) - { - 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); - } - } -} - - -/*QUAKED spawnfunc_func_button (0 .5 .8) ? -When a button is touched, it moves some distance in the direction of it's angle, triggers all of it's targets, waits some time, then returns to it's original position where it can be triggered again. - -"angle" determines the opening direction -"target" all entities with a matching targetname will be used -"speed" override the default 40 speed -"wait" override the default 1 second wait (-1 = never return) -"lip" override the default 4 pixel lip remaining at end of move -"health" if set, the button must be killed instead of touched. If set to -1, the button will fire on ANY attack, even damageless ones like the InstaGib laser -"sounds" -0) steam metal -1) wooden clunk -2) metallic click -3) in-out -*/ -spawnfunc(func_button) -{ - SetMovedir(this); - - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - - setblocked(this, button_blocked); - this.use = button_use; - -// if (this.health == 0) // all buttons are now shootable -// this.health = 10; - if (this.health) - { - this.max_health = this.health; - this.event_damage = button_damage; - this.takedamage = DAMAGE_YES; - } - else - settouch(this, button_touch); - - if (!this.speed) - this.speed = 40; - if (!this.wait) - this.wait = 1; - if (!this.lip) - this.lip = 4; - - if(this.noise != "") - precache_sound(this.noise); - - this.active = ACTIVE_ACTIVE; - - this.pos1 = this.origin; - this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); - this.flags |= FL_NOTARGET; - - this.reset = button_reset; - - button_reset(this); -} -#endif diff --git a/qcsrc/common/triggers/func/button.qh b/qcsrc/common/triggers/func/button.qh deleted file mode 100644 index 75a6006eb5..0000000000 --- a/qcsrc/common/triggers/func/button.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -const int BUTTON_DONTACCUMULATEDMG = 128; diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc deleted file mode 100644 index 1802a75db4..0000000000 --- a/qcsrc/common/triggers/func/conveyor.qc +++ /dev/null @@ -1,192 +0,0 @@ -#include "conveyor.qh" -REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) - -void conveyor_think(entity this) -{ -#ifdef CSQC - // TODO: check if this is what is causing the glitchiness when switching between them - float dt = time - this.move_time; - this.move_time = time; - if(dt <= 0) { return; } -#endif - - // set myself as current conveyor where possible - IL_EACH(g_conveyed, it.conveyor == this, - { - it.conveyor = NULL; - IL_REMOVE(g_conveyed, it); - }); - - if(this.state) - { - FOREACH_ENTITY_RADIUS((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1, !it.conveyor.state && isPushable(it), - { - vector emin = it.absmin; - vector emax = it.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate - { - if(!it.conveyor) - IL_PUSH(g_conveyed, it); - it.conveyor = this; - } - }); - - IL_EACH(g_conveyed, it.conveyor == this, - { - if(IS_CLIENT(it)) // doing it via velocity has quite some advantages - continue; // done in SV_PlayerPhysics continue; - - setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME); - move_out_of_solid(it); -#ifdef SVQC - UpdateCSQCProjectile(it); -#endif - /* - // stupid conveyor code - tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it); - if(trace_fraction > 0) - setorigin(it, trace_endpos); - */ - }); - } - -#ifdef SVQC - this.nextthink = time; -#endif -} - -#ifdef SVQC - -void conveyor_use(entity this, entity actor, entity trigger) -{ - this.state = !this.state; - - this.SendFlags |= 2; -} - -void conveyor_reset(entity this) -{ - this.state = (this.spawnflags & 1); - - this.SendFlags |= 2; -} - -bool conveyor_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_CONVEYOR); - WriteByte(MSG_ENTITY, sf); - - if(sf & 1) - { - WriteByte(MSG_ENTITY, this.warpzone_isboxy); - WriteVector(MSG_ENTITY, this.origin); - - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - - WriteVector(MSG_ENTITY, this.movedir); - - WriteByte(MSG_ENTITY, this.speed); - WriteByte(MSG_ENTITY, this.state); - - WriteString(MSG_ENTITY, this.targetname); - WriteString(MSG_ENTITY, this.target); - } - - if(sf & 2) - WriteByte(MSG_ENTITY, this.state); - - return true; -} - -void conveyor_init(entity this) -{ - if (!this.speed) this.speed = 200; - this.movedir *= this.speed; - setthink(this, conveyor_think); - this.nextthink = time; - IFTARGETED - { - this.use = conveyor_use; - this.reset = conveyor_reset; - this.reset(this); - } - else - this.state = 1; - - FixSize(this); - - Net_LinkEntity(this, 0, false, conveyor_send); - - this.SendFlags |= 1; -} - -spawnfunc(trigger_conveyor) -{ - SetMovedir(this); - EXACTTRIGGER_INIT; - conveyor_init(this); -} - -spawnfunc(func_conveyor) -{ - SetMovedir(this); - InitMovingBrushTrigger(this); - set_movetype(this, MOVETYPE_NONE); - conveyor_init(this); -} - -#elif defined(CSQC) - -void conveyor_draw(entity this) { conveyor_think(this); } - -void conveyor_init(entity this, bool isnew) -{ - if(isnew) - IL_PUSH(g_drawables, this); - this.draw = conveyor_draw; - this.drawmask = MASK_NORMAL; - - set_movetype(this, MOVETYPE_NONE); - this.model = ""; - this.solid = SOLID_TRIGGER; - this.move_time = time; -} - -NET_HANDLE(ENT_CLIENT_CONVEYOR, bool isnew) -{ - int sf = ReadByte(); - - if(sf & 1) - { - this.warpzone_isboxy = ReadByte(); - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.mins = ReadVector(); - this.maxs = ReadVector(); - setsize(this, this.mins, this.maxs); - - this.movedir = ReadVector(); - - this.speed = ReadByte(); - this.state = ReadByte(); - - this.targetname = strzone(ReadString()); - this.target = strzone(ReadString()); - - conveyor_init(this, isnew); - } - - if(sf & 2) - this.state = ReadByte(); - - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh deleted file mode 100644 index c12b52d2dd..0000000000 --- a/qcsrc/common/triggers/func/conveyor.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -IntrusiveList g_conveyed; -STATIC_INIT(g_conveyed) { g_conveyed = IL_NEW(); } diff --git a/qcsrc/common/triggers/func/door.qc b/qcsrc/common/triggers/func/door.qc deleted file mode 100644 index f768717c6b..0000000000 --- a/qcsrc/common/triggers/func/door.qc +++ /dev/null @@ -1,829 +0,0 @@ -#include "door.qh" -/* - -Doors are similar to buttons, but can spawn a fat trigger field around them -to open without a touch, and they link together to form simultanious -double/quad doors. - -Door.owner is the master door. If there is only one door, it points to itself. -If multiple doors, all will point to a single one. - -Door.enemy chains from the master door through all doors linked in the chain. - -*/ - - -/* -============================================================================= - -THINK FUNCTIONS - -============================================================================= -*/ - -void door_go_down(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); - -void door_blocked(entity this, entity blocker) -{ - if((this.spawnflags & 8) -#ifdef SVQC - && (blocker.takedamage != DAMAGE_NO) -#elif defined(CSQC) - && !IS_DEAD(blocker) -#endif - ) - { // KIll Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { -#ifdef SVQC - if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - - // don't change direction for dead or dying stuff - if(IS_DEAD(blocker) -#ifdef SVQC - && (blocker.takedamage == DAMAGE_NO) -#endif - ) - { - if (this.wait >= 0) - { - if (this.state == STATE_DOWN) - if (this.classname == "door") - { - door_go_up (this, NULL, NULL); - } else - { - door_rotating_go_up(this, blocker); - } - else - if (this.classname == "door") - { - door_go_down (this); - } else - { - door_rotating_go_down (this); - } - } - } -#ifdef SVQC - else - { - //gib dying stuff just to make sure - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - } -} - -void door_hit_top(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_TOP; - if (this.spawnflags & DOOR_TOGGLE) - return; // don't come down automatically - if (this.classname == "door") - { - setthink(this, door_go_down); - } else - { - setthink(this, door_rotating_go_down); - } - this.nextthink = this.ltime + this.wait; -} - -void door_hit_bottom(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_BOTTOM; -} - -void door_go_down(entity this) -{ - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - if (this.max_health) - { - this.takedamage = DAMAGE_YES; - this.health = this.max_health; - } - - this.state = STATE_DOWN; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_hit_bottom); -} - -void door_go_up(entity this, entity actor, entity trigger) -{ - if (this.state == STATE_UP) - return; // already going up - - if (this.state == STATE_TOP) - { // reset top wait time - this.nextthink = this.ltime + this.wait; - return; - } - - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - this.state = STATE_UP; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_hit_top); - - string oldmessage; - oldmessage = this.message; - this.message = ""; - SUB_UseTargets(this, actor, trigger); - this.message = oldmessage; -} - - -/* -============================================================================= - -ACTIVATION FUNCTIONS - -============================================================================= -*/ - -bool door_check_keys(entity door, entity player) -{ - if(door.owner) - door = door.owner; - - // no key needed - if(!door.itemkeys) - return true; - - // this door require a key - // only a player can have a key - if(!IS_PLAYER(player)) - return false; - - entity store = player; -#ifdef SVQC - store = PS(player); -#endif - int valid = (door.itemkeys & store.itemkeys); - door.itemkeys &= ~valid; // only some of the needed keys were given - - if(!door.itemkeys) - { -#ifdef SVQC - play2(player, SND(TALK)); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_UNLOCKED); -#endif - return true; - } - - if(!valid) - { -#ifdef SVQC - if(player.key_door_messagetime <= time) - { - play2(player, door.noise3); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(door.itemkeys)); - player.key_door_messagetime = time + 2; - } -#endif - return false; - } - - // door needs keys the player doesn't have -#ifdef SVQC - if(player.key_door_messagetime <= time) - { - play2(player, door.noise3); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(door.itemkeys)); - player.key_door_messagetime = time + 2; - } -#endif - - return false; -} - -void door_fire(entity this, entity actor, entity trigger) -{ - if (this.owner != this) - objerror (this, "door_fire: this.owner != this"); - - if (this.spawnflags & DOOR_TOGGLE) - { - if (this.state == STATE_UP || this.state == STATE_TOP) - { - entity e = this; - do { - if (e.classname == "door") { - door_go_down(e); - } else { - door_rotating_go_down(e); - } - e = e.enemy; - } while ((e != this) && (e != NULL)); - return; - } - } - -// trigger all paired doors - entity e = this; - do { - if (e.classname == "door") { - 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) { - e.lip = 666; // e.lip is used to remember reverse opening direction for door_rotating - e.pos2 = '0 0 0' - e.pos2; - } - // if BIDIR_IN_DOWN (==8) is set, prevent the door from reoping during closing if it is triggered from the wrong side - if (!((e.spawnflags & 2) && (e.spawnflags & 8) && e.state == STATE_DOWN - && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0))))) - { - door_rotating_go_up(e, trigger); - } - } - e = e.enemy; - } while ((e != this) && (e != NULL)); -} - -void door_use(entity this, entity actor, entity trigger) -{ - //dprint("door_use (model: ");dprint(this.model);dprint(")\n"); - - if (this.owner) - door_fire(this.owner, actor, trigger); -} - -void door_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.spawnflags & DOOR_NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - this.health = this.health - damage; - - if (this.itemkeys) - { - // don't allow opening doors through damage if keys are required - return; - } - - if (this.health <= 0) - { - this.owner.health = this.owner.max_health; - this.owner.takedamage = DAMAGE_NO; // wil be reset upon return - door_use(this.owner, NULL, NULL); - } -} - -.float door_finished; - -/* -================ -door_touch - -Prints messages -================ -*/ - -void door_touch(entity this, entity toucher) -{ - if (!IS_PLAYER(toucher)) - return; - if (this.owner.door_finished > time) - return; - - this.owner.door_finished = time + 2; - -#ifdef SVQC - if (!(this.owner.dmg) && (this.owner.message != "")) - { - if (IS_CLIENT(toucher)) - centerprint(toucher, this.owner.message); - play2(toucher, this.owner.noise); - } -#endif -} - -void door_generic_plat_blocked(entity this, entity blocker) -{ - if((this.spawnflags & 8) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { - -#ifdef SVQC - if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - - //Dont chamge direction for dead or dying stuff - if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO)) - { - if (this.wait >= 0) - { - if (this.state == STATE_DOWN) - door_rotating_go_up (this, blocker); - else - door_rotating_go_down (this); - } - } -#ifdef SVQC - else - { - //gib dying stuff just to make sure - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - } -} - -void door_rotating_hit_top(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = STATE_TOP; - if (this.spawnflags & DOOR_TOGGLE) - return; // don't come down automatically - setthink(this, door_rotating_go_down); - this.nextthink = this.ltime + this.wait; -} - -void door_rotating_hit_bottom(entity this) -{ - if (this.noise1 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - if (this.lip==666) // this.lip is used to remember reverse opening direction for door_rotating - { - this.pos2 = '0 0 0' - this.pos2; - this.lip = 0; - } - this.state = STATE_BOTTOM; -} - -void door_rotating_go_down(entity this) -{ - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - if (this.max_health) - { - this.takedamage = DAMAGE_YES; - this.health = this.max_health; - } - - this.state = STATE_DOWN; - SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom); -} - -void door_rotating_go_up(entity this, entity oth) -{ - if (this.state == STATE_UP) - return; // already going up - - if (this.state == STATE_TOP) - { // reset top wait time - this.nextthink = this.ltime + this.wait; - return; - } - if (this.noise2 != "") - _sound (this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - this.state = STATE_UP; - SUB_CalcAngleMove (this, this.pos2, TSPEED_LINEAR, this.speed, door_rotating_hit_top); - - string oldmessage; - oldmessage = this.message; - this.message = ""; - SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here? - this.message = oldmessage; -} - - -/* -========================================= -door trigger - -Spawned if a door lacks a real activator -========================================= -*/ - -void door_trigger_touch(entity this, entity toucher) -{ - if (toucher.health < 1) -#ifdef SVQC - if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) -#elif defined(CSQC) - if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher))) -#endif - return; - - if (time < this.door_finished) - return; - - // check if door is locked - if (!door_check_keys(this, toucher)) - return; - - this.door_finished = time + 1; - - door_use(this.owner, toucher, NULL); -} - -void door_spawnfield(entity this, vector fmins, vector fmaxs) -{ - entity trigger; - vector t1 = fmins, t2 = fmaxs; - - trigger = new(doortriggerfield); - set_movetype(trigger, MOVETYPE_NONE); - trigger.solid = SOLID_TRIGGER; - trigger.owner = this; -#ifdef SVQC - settouch(trigger, door_trigger_touch); -#endif - - setsize (trigger, t1 - '60 60 8', t2 + '60 60 8'); -} - - -/* -============= -LinkDoors - - -============= -*/ - -entity LinkDoors_nextent(entity cur, entity near, entity pass) -{ - while((cur = find(cur, classname, pass.classname)) && ((cur.spawnflags & 4) || cur.enemy)) - { - } - return cur; -} - -bool LinkDoors_isconnected(entity e1, entity e2, entity pass) -{ - float DELTA = 4; - if((e1.absmin_x > e2.absmax_x + DELTA) - || (e1.absmin_y > e2.absmax_y + DELTA) - || (e1.absmin_z > e2.absmax_z + DELTA) - || (e2.absmin_x > e1.absmax_x + DELTA) - || (e2.absmin_y > e1.absmax_y + DELTA) - || (e2.absmin_z > e1.absmax_z + DELTA) - ) { return false; } - return true; -} - -#ifdef SVQC -void door_link(); -#endif -void LinkDoors(entity this) -{ - entity t; - vector cmins, cmaxs; - -#ifdef SVQC - door_link(); -#endif - - if (this.enemy) - return; // already linked by another door - if (this.spawnflags & 4) - { - this.owner = this.enemy = this; - - if (this.health) - return; - IFTARGETED - return; - if (this.items) - return; - - door_spawnfield(this, this.absmin, this.absmax); - - return; // don't want to link this door - } - - FindConnectedComponent(this, enemy, LinkDoors_nextent, LinkDoors_isconnected, this); - - // set owner, and make a loop of the chain - LOG_TRACE("LinkDoors: linking doors:"); - for(t = this; ; t = t.enemy) - { - LOG_TRACE(" ", etos(t)); - t.owner = this; - if(t.enemy == NULL) - { - t.enemy = this; - break; - } - } - LOG_TRACE(""); - - // collect health, targetname, message, size - cmins = this.absmin; - cmaxs = this.absmax; - for(t = this; ; t = t.enemy) - { - if(t.health && !this.health) - this.health = t.health; - if((t.targetname != "") && (this.targetname == "")) - this.targetname = t.targetname; - if((t.message != "") && (this.message == "")) - this.message = t.message; - if (t.absmin_x < cmins_x) - cmins_x = t.absmin_x; - if (t.absmin_y < cmins_y) - cmins_y = t.absmin_y; - if (t.absmin_z < cmins_z) - cmins_z = t.absmin_z; - if (t.absmax_x > cmaxs_x) - cmaxs_x = t.absmax_x; - if (t.absmax_y > cmaxs_y) - cmaxs_y = t.absmax_y; - if (t.absmax_z > cmaxs_z) - cmaxs_z = t.absmax_z; - if(t.enemy == this) - break; - } - - // distribute health, targetname, message - for(t = this; t; t = t.enemy) - { - t.health = this.health; - t.targetname = this.targetname; - t.message = this.message; - if(t.enemy == this) - break; - } - - // shootable, or triggered doors just needed the owner/enemy links, - // they don't spawn a field - - if (this.health) - return; - IFTARGETED - return; - if (this.items) - return; - - door_spawnfield(this, cmins, cmaxs); -} - -REGISTER_NET_LINKED(ENT_CLIENT_DOOR) - -#ifdef SVQC -/*QUAKED spawnfunc_func_door (0 .5 .8) ? START_OPEN x DOOR_DONT_LINK GOLD_KEY SILVER_KEY TOGGLE -if two doors touch, they are assumed to be connected and operate as a unit. - -TOGGLE causes the door to wait in both the start and end states for a trigger event. - -START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not useful for touch or takedamage doors). - -GOLD_KEY causes the door to open only if the activator holds a gold key. - -SILVER_KEY causes the door to open only if the activator holds a silver key. - -"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet -"angle" determines the opening direction -"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. -"health" if set, door must be shot open -"speed" movement speed (100 default) -"wait" wait before returning (3 default, -1 = never return) -"lip" lip remaining at end of move (8 default) -"dmg" damage to inflict when blocked (2 default) -"sounds" -0) no sound -1) stone -2) base -3) stone chain -4) screechy metal -FIXME: only one sound set available at the time being - -*/ - -float door_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_DOOR); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteString(MSG_ENTITY, this.classname); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteShort(MSG_ENTITY, this.wait); - WriteShort(MSG_ENTITY, this.speed); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - WriteCoord(MSG_ENTITY, this.ltime); - } - - if(sf & SF_TRIGGER_RESET) - { - // client makes use of this, we do not - } - - if(sf & SF_TRIGGER_UPDATE) - { - WriteVector(MSG_ENTITY, this.origin); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - } - - return true; -} - -void door_link() -{ - // set size now, as everything is loaded - //FixSize(this); - //Net_LinkEntity(this, false, 0, door_send); -} -#endif - -void door_init_startopen(entity this) -{ - setorigin(this, this.pos2); - this.pos2 = this.pos1; - this.pos1 = this.origin; - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_UPDATE; -#endif -} - -void door_reset(entity this) -{ - setorigin(this, this.pos1); - this.velocity = '0 0 0'; - this.state = STATE_BOTTOM; - setthink(this, func_null); - this.nextthink = 0; - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_RESET; -#endif -} - -#ifdef SVQC - -// spawnflags require key (for now only func_door) -spawnfunc(func_door) -{ - // Quake 1 keys compatibility - if (this.spawnflags & SPAWNFLAGS_GOLD_KEY) - this.itemkeys |= ITEM_KEY_BIT(0); - if (this.spawnflags & SPAWNFLAGS_SILVER_KEY) - this.itemkeys |= ITEM_KEY_BIT(1); - - SetMovedir(this); - - this.max_health = this.health; - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - this.classname = "door"; - - if(this.noise == "") - this.noise = "misc/talk.wav"; - if(this.noise3 == "") - this.noise3 = "misc/talk.wav"; - precache_sound(this.noise); - precache_sound(this.noise3); - - setblocked(this, door_blocked); - this.use = door_use; - - if(this.dmg && (this.message == "")) - this.message = "was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - - if (this.sounds > 0) - { - this.noise2 = "plats/medplat1.wav"; - this.noise1 = "plats/medplat2.wav"; - } - - if(this.noise1 && this.noise1 != "") { precache_sound(this.noise1); } - if(this.noise2 && this.noise2 != "") { precache_sound(this.noise2); } - - if (!this.speed) - this.speed = 100; - if (!this.wait) - this.wait = 3; - if (!this.lip) - this.lip = 8; - - this.pos1 = this.origin; - this.pos2 = this.pos1 + this.movedir*(fabs(this.movedir*this.size) - this.lip); - - if(this.spawnflags & DOOR_NONSOLID) - this.solid = SOLID_NOT; - -// DOOR_START_OPEN is to allow an entity to be lighted in the closed position -// but spawn in the open position - if (this.spawnflags & DOOR_START_OPEN) - InitializeEntity(this, door_init_startopen, INITPRIO_SETLOCATION); - - this.state = STATE_BOTTOM; - - if (this.health) - { - //this.canteamdamage = true; // TODO - this.takedamage = DAMAGE_YES; - this.event_damage = door_damage; - } - - if (this.items) - this.wait = -1; - - settouch(this, door_touch); - -// LinkDoors can't be done until all of the doors have been spawned, so -// the sizes can be detected properly. - InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); - - this.reset = door_reset; -} - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) -{ - int sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.classname = strzone(ReadString()); - this.spawnflags = ReadByte(); - - this.mdl = strzone(ReadString()); - _setmodel(this, this.mdl); - - trigger_common_read(this, true); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.wait = ReadShort(); - this.speed = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - this.ltime = ReadCoord(); - - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.use = door_use; - - LinkDoors(this); - - if(this.spawnflags & DOOR_START_OPEN) - door_init_startopen(this); - - this.move_time = time; - set_movetype(this, MOVETYPE_PUSH); - } - - if(sf & SF_TRIGGER_RESET) - { - door_reset(this); - } - - if(sf & SF_TRIGGER_UPDATE) - { - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - } - return true; -} - -#endif diff --git a/qcsrc/common/triggers/func/door.qh b/qcsrc/common/triggers/func/door.qh deleted file mode 100644 index 84a9d6aa23..0000000000 --- a/qcsrc/common/triggers/func/door.qh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -// door constants -const int DOOR_START_OPEN = 1; -const int DOOR_DONT_LINK = 4; -const int DOOR_TOGGLE = 32; - -const int DOOR_NOSPLASH = 256; // generic anti-splashdamage spawnflag - -const int DOOR_NONSOLID = 1024; - -const int SPAWNFLAGS_GOLD_KEY = 8; -const int SPAWNFLAGS_SILVER_KEY = 16; - -#ifdef CSQC -// stuff for preload - -.float door_finished; -#endif diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc deleted file mode 100644 index c61a026866..0000000000 --- a/qcsrc/common/triggers/func/door_rotating.qc +++ /dev/null @@ -1,128 +0,0 @@ -#include "door_rotating.qh" -#ifdef SVQC -/*QUAKED spawnfunc_func_door_rotating (0 .5 .8) ? START_OPEN BIDIR DOOR_DONT_LINK BIDIR_IN_DOWN x TOGGLE X_AXIS Y_AXIS -if two doors touch, they are assumed to be connected and operate as a unit. - -TOGGLE causes the door to wait in both the start and end states for a trigger event. - -BIDIR makes the door work bidirectional, so that the opening direction is always away from the requestor. -The usage of bidirectional doors requires two manually instantiated triggers (trigger_multiple), the one to open it in the other direction -must have set trigger_reverse to 1. -BIDIR_IN_DOWN will the door prevent from reopening while closing if it is triggered from the other side. - -START_OPEN causes the door to move to its destination when spawned, and operate in reverse. It is used to temporarily or permanently close off an area when triggered (not usefull for touch or takedamage doors). - -"message" is printed when the door is touched if it is a trigger door and it hasn't been fired yet -"angle" determines the destination angle for opening. negative values reverse the direction. -"targetname" if set, no touch field will be spawned and a remote button or trigger field activates the door. -"health" if set, door must be shot open -"speed" movement speed (100 default) -"wait" wait before returning (3 default, -1 = never return) -"dmg" damage to inflict when blocked (2 default) -"sounds" -0) no sound -1) stone -2) base -3) stone chain -4) screechy metal -FIXME: only one sound set available at the time being -*/ - -void door_rotating_reset(entity this) -{ - this.angles = this.pos1; - this.avelocity = '0 0 0'; - this.state = STATE_BOTTOM; - setthink(this, func_null); - this.nextthink = 0; -} - -void door_rotating_init_startopen(entity this) -{ - this.angles = this.movedir; - this.pos2 = '0 0 0'; - this.pos1 = this.movedir; -} - - -spawnfunc(func_door_rotating) -{ - - //if (!this.deathtype) // map makers can override this - // this.deathtype = " got in the way"; - - // I abuse "movedir" for denoting the axis for now - if (this.spawnflags & 64) // X (untested) - this.movedir = '0 0 1'; - else if (this.spawnflags & 128) // Y (untested) - this.movedir = '1 0 0'; - else // Z - this.movedir = '0 1 0'; - - if (this.angles_y==0) this.angles_y = 90; - - this.movedir = this.movedir * this.angles_y; - this.angles = '0 0 0'; - - this.max_health = this.health; - this.avelocity = this.movedir; - if (!InitMovingBrushTrigger(this)) - return; - this.velocity = '0 0 0'; - //this.effects |= EF_LOWPRECISION; - this.classname = "door_rotating"; - - setblocked(this, door_blocked); - this.use = door_use; - - if(this.spawnflags & 8) - this.dmg = 10000; - - if(this.dmg && (this.message == "")) - this.message = "was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - - if (this.sounds > 0) - { - precache_sound ("plats/medplat1.wav"); - precache_sound ("plats/medplat2.wav"); - this.noise2 = "plats/medplat1.wav"; - this.noise1 = "plats/medplat2.wav"; - } - - if (!this.speed) - this.speed = 50; - if (!this.wait) - this.wait = 1; - this.lip = 0; // this.lip is used to remember reverse opening direction for door_rotating - - this.pos1 = '0 0 0'; - this.pos2 = this.movedir; - -// DOOR_START_OPEN is to allow an entity to be lighted in the closed position -// but spawn in the open position - if (this.spawnflags & DOOR_START_OPEN) - InitializeEntity(this, door_rotating_init_startopen, INITPRIO_SETLOCATION); - - this.state = STATE_BOTTOM; - - if (this.health) - { - //this.canteamdamage = true; // TODO - this.takedamage = DAMAGE_YES; - this.event_damage = door_damage; - } - - if (this.items) - this.wait = -1; - - settouch(this, door_touch); - -// LinkDoors can't be done until all of the doors have been spawned, so -// the sizes can be detected properly. - InitializeEntity(this, LinkDoors, INITPRIO_LINKDOORS); - - this.reset = door_rotating_reset; -} -#endif diff --git a/qcsrc/common/triggers/func/door_rotating.qh b/qcsrc/common/triggers/func/door_rotating.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/door_rotating.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc deleted file mode 100644 index 5558269647..0000000000 --- a/qcsrc/common/triggers/func/door_secret.qc +++ /dev/null @@ -1,242 +0,0 @@ -#include "door_secret.qh" -#ifdef SVQC -void fd_secret_move1(entity this); -void fd_secret_move2(entity this); -void fd_secret_move3(entity this); -void fd_secret_move4(entity this); -void fd_secret_move5(entity this); -void fd_secret_move6(entity this); -void fd_secret_done(entity this); - -const float SECRET_OPEN_ONCE = 1; // stays open -const float SECRET_1ST_LEFT = 2; // 1st move is left of arrow -const float SECRET_1ST_DOWN = 4; // 1st move is down from arrow -const float SECRET_NO_SHOOT = 8; // only opened by trigger -const float SECRET_YES_SHOOT = 16; // shootable even if targeted - -void fd_secret_use(entity this, entity actor, entity trigger) -{ - float temp; - string message_save; - - this.health = 10000; - if(!this.bot_attack) - IL_PUSH(g_bot_targets, this); - this.bot_attack = true; - - // exit if still moving around... - if (this.origin != this.oldorigin) - return; - - message_save = this.message; - this.message = ""; // no more message - SUB_UseTargets(this, actor, trigger); // fire all targets / killtargets - this.message = message_save; - - this.velocity = '0 0 0'; - - // Make a sound, wait a little... - - if (this.noise1 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.nextthink = this.ltime + 0.1; - - temp = 1 - (this.spawnflags & SECRET_1ST_LEFT); // 1 or -1 - makevectors(this.mangle); - - if (!this.t_width) - { - if (this.spawnflags & SECRET_1ST_DOWN) - this.t_width = fabs(v_up * this.size); - else - this.t_width = fabs(v_right * this.size); - } - - if (!this.t_length) - this.t_length = fabs(v_forward * this.size); - - if (this.spawnflags & SECRET_1ST_DOWN) - this.dest1 = this.origin - v_up * this.t_width; - else - this.dest1 = this.origin + v_right * (this.t_width * temp); - - this.dest2 = this.dest1 + v_forward * this.t_length; - SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move1); - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); -} - -void fd_secret_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - fd_secret_use(this, NULL, NULL); -} - -// Wait after first movement... -void fd_secret_move1(entity this) -{ - this.nextthink = this.ltime + 1.0; - setthink(this, fd_secret_move2); - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -// Start moving sideways w/sound... -void fd_secret_move2(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.dest2, TSPEED_LINEAR, this.speed, fd_secret_move3); -} - -// Wait here until time to go back... -void fd_secret_move3(entity this) -{ - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); - if (!(this.spawnflags & SECRET_OPEN_ONCE)) - { - this.nextthink = this.ltime + this.wait; - setthink(this, fd_secret_move4); - } -} - -// Move backward... -void fd_secret_move4(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.dest1, TSPEED_LINEAR, this.speed, fd_secret_move5); -} - -// Wait 1 second... -void fd_secret_move5(entity this) -{ - this.nextthink = this.ltime + 1.0; - setthink(this, fd_secret_move6); - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -void fd_secret_move6(entity this) -{ - if (this.noise2 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise2, VOL_BASE, ATTEN_NORM); - SUB_CalcMove(this, this.oldorigin, TSPEED_LINEAR, this.speed, fd_secret_done); -} - -void fd_secret_done(entity this) -{ - if (this.spawnflags&SECRET_YES_SHOOT) - { - this.health = 10000; - this.takedamage = DAMAGE_YES; - //this.th_pain = fd_secret_use; - } - if (this.noise3 != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise3, VOL_BASE, ATTEN_NORM); -} - -.float door_finished; - -void secret_blocked(entity this, entity blocker) -{ - if (time < this.door_finished) - return; - this.door_finished = time + 0.5; - //T_Damage (other, this, this, this.dmg, this.dmg, this.deathtype, DT_IMPACT, (this.absmin + this.absmax) * 0.5, '0 0 0', Obituary_Generic); -} - -/* -============== -secret_touch - -Prints messages -================ -*/ -void secret_touch(entity this, entity toucher) -{ - if (!toucher.iscreature) - return; - if (this.door_finished > time) - return; - - this.door_finished = time + 2; - - if (this.message) - { - if (IS_CLIENT(toucher)) - centerprint(toucher, this.message); - play2(toucher, this.noise); - } -} - -void secret_reset(entity this) -{ - if (this.spawnflags & SECRET_YES_SHOOT) - { - this.health = 10000; - this.takedamage = DAMAGE_YES; - } - setorigin(this, this.oldorigin); - setthink(this, func_null); - this.nextthink = 0; -} - -/*QUAKED spawnfunc_func_door_secret (0 .5 .8) ? open_once 1st_left 1st_down no_shoot always_shoot -Basic secret door. Slides back, then to the side. Angle determines direction. -wait = # of seconds before coming back -1st_left = 1st move is left of arrow -1st_down = 1st move is down from arrow -always_shoot = even if targeted, keep shootable -t_width = override WIDTH to move back (or height if going down) -t_length = override LENGTH to move sideways -"dmg" damage to inflict when blocked (2 default) - -If a secret door has a targetname, it will only be opened by it's botton or trigger, not by damage. -"sounds" -1) medieval -2) metal -3) base -*/ - -spawnfunc(func_door_secret) -{ - /*if (!this.deathtype) // map makers can override this - this.deathtype = " got in the way";*/ - - if (!this.dmg) this.dmg = 2; - - // Magic formula... - this.mangle = this.angles; - this.angles = '0 0 0'; - this.classname = "door"; - if (!InitMovingBrushTrigger(this)) return; - this.effects |= EF_LOWPRECISION; - - if (this.noise == "") this.noise = "misc/talk.wav"; - precache_sound(this.noise); - - settouch(this, secret_touch); - setblocked(this, secret_blocked); - this.speed = 50; - this.use = fd_secret_use; - IFTARGETED - { - } - else - this.spawnflags |= SECRET_YES_SHOOT; - - if (this.spawnflags & SECRET_YES_SHOOT) - { - //this.canteamdamage = true; // TODO - this.health = 10000; - this.takedamage = DAMAGE_YES; - this.event_damage = fd_secret_damage; - } - this.oldorigin = this.origin; - if (!this.wait) this.wait = 5; // seconds before closing - - this.reset = secret_reset; - this.reset(this); -} -#endif diff --git a/qcsrc/common/triggers/func/door_secret.qh b/qcsrc/common/triggers/func/door_secret.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/door_secret.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc deleted file mode 100644 index 28e0f0f7c7..0000000000 --- a/qcsrc/common/triggers/func/fourier.qc +++ /dev/null @@ -1,89 +0,0 @@ -#include "fourier.qh" -#ifdef SVQC -/*QUAKED spawnfunc_func_fourier (0 .5 .8) ? -Brush model that moves in a pattern of added up sine waves, can be used e.g. for circular motions. -netname: list of <frequencymultiplier> <phase> <x> <y> <z> quadruples, separated by spaces; note that phase 0 represents a sine wave, and phase 0.25 a cosine wave (by default, it uses 1 0 0 0 1, to match func_bobbing's defaults -speed: how long one cycle of frequency multiplier 1 in seconds (default 4) -height: amplitude modifier (default 32) -phase: cycle timing adjustment (0-1 as a fraction of the cycle, default 0) -noise: path/name of looping .wav file to play. -dmg: Do this mutch dmg every .dmgtime intervall when blocked -dmgtime: See above. -*/ - -void func_fourier_controller_think(entity this) -{ - vector v; - float n, i, t; - - this.nextthink = time + 0.1; - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - - n = floor((tokenize_console(this.owner.netname)) / 5); - t = this.nextthink * this.owner.cnt + this.owner.phase * 360; - - v = this.owner.destvec; - - for(i = 0; i < n; ++i) - { - makevectors((t * stof(argv(i*5)) + stof(argv(i*5+1)) * 360) * '0 1 0'); - v = v + ('1 0 0' * stof(argv(i*5+2)) + '0 1 0' * stof(argv(i*5+3)) + '0 0 1' * stof(argv(i*5+4))) * this.owner.height * v_forward_y; - } - - if(this.owner.classname == "func_fourier") // don't brake stuff if the func_fourier was killtarget'ed - // * 10 so it will arrive in 0.1 sec - this.owner.velocity = (v - this.owner.origin) * 10; -} - -spawnfunc(func_fourier) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - - if (!this.speed) - this.speed = 4; - if (!this.height) - this.height = 32; - this.destvec = this.origin; - this.cnt = 360 / this.speed; - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if(this.netname == "") - this.netname = "1 0 0 0 1"; - - if (!InitMovingBrushTrigger(this)) - return; - - this.active = ACTIVE_ACTIVE; - - // wait for targets to spawn - controller = new(func_fourier_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_fourier_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/fourier.qh b/qcsrc/common/triggers/func/fourier.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/fourier.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/include.qc b/qcsrc/common/triggers/func/include.qc deleted file mode 100644 index 290c2e9849..0000000000 --- a/qcsrc/common/triggers/func/include.qc +++ /dev/null @@ -1,19 +0,0 @@ -#include "include.qh" - -#include "bobbing.qc" -#include "breakable.qc" -#include "button.qc" -#include "conveyor.qc" -#include "door.qc" -#include "door_rotating.qc" -#include "door_secret.qc" -#include "fourier.qc" -#include "ladder.qc" -#include "pendulum.qc" -#include "plat.qc" -#include "pointparticles.qc" -#include "rainsnow.qc" -#include "rotating.qc" -#include "stardust.qc" -#include "train.qc" -#include "vectormamamam.qc" diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh deleted file mode 100644 index 4e8b94cd1c..0000000000 --- a/qcsrc/common/triggers/func/include.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include "door.qh" -#include "ladder.qh" -#include "train.qh" diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc deleted file mode 100644 index 92f361a145..0000000000 --- a/qcsrc/common/triggers/func/ladder.qc +++ /dev/null @@ -1,149 +0,0 @@ -#include "ladder.qh" -REGISTER_NET_LINKED(ENT_CLIENT_LADDER) - -void func_ladder_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - if(IS_VEHICLE(toucher)) - return; -#elif defined(CSQC) - if(!toucher.isplayermodel) - return; -#endif - - EXACTTRIGGER_TOUCH(this, toucher); - - toucher.ladder_time = time + 0.1; - toucher.ladder_entity = this; -} - -#ifdef SVQC -bool func_ladder_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_LADDER); - - WriteString(MSG_ENTITY, this.classname); - WriteByte(MSG_ENTITY, this.skin); - WriteCoord(MSG_ENTITY, this.speed); - - trigger_common_write(this, false); - - return true; -} - -void func_ladder_link(entity this) -{ - trigger_link(this, func_ladder_send); - //this.model = "null"; -} - -void func_ladder_init(entity this) -{ - settouch(this, func_ladder_touch); - trigger_init(this); - func_ladder_link(this); - - if(min(this.absmax.x - this.absmin.x, this.absmax.y - this.absmin.y) > 100) - return; - - entity tracetest_ent = spawn(); - setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); - tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - - vector top_min = (this.absmin + this.absmax) / 2; - top_min.z = this.absmax.z; - vector top_max = top_min; - top_max.z += PL_MAX_CONST.z - PL_MIN_CONST.z; - tracebox(top_max + jumpstepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - tracebox(top_max + stepheightvec, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x - && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) - { - // move top on one side - top_max.y = top_min.y = this.absmin.y + (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; - } - else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y - && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) - { - // move top on one side - top_max.x = top_min.x = this.absmin.x + (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; - } - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - if(trace_startsolid) - { - if(this.absmax.x - this.absmin.x > PL_MAX_CONST.x - PL_MIN_CONST.x - && this.absmax.y - this.absmin.y < this.absmax.x - this.absmin.x) - { - // alternatively on the other side - top_max.y = top_min.y = this.absmax.y - (PL_MAX_CONST.y - PL_MIN_CONST.y) * 0.75; - } - else if(this.absmax.y - this.absmin.y > PL_MAX_CONST.y - PL_MIN_CONST.y - && this.absmax.x - this.absmin.x < this.absmax.y - this.absmin.y) - { - // alternatively on the other side - top_max.x = top_min.x = this.absmax.x - (PL_MAX_CONST.x - PL_MIN_CONST.x) * 0.75; - } - tracebox(top_max, PL_MIN_CONST, PL_MAX_CONST, top_min, MOVE_NOMONSTERS, tracetest_ent); - } - } - } - } - if(trace_startsolid || trace_endpos.z < this.absmax.z) - { - delete(tracetest_ent); - return; - } - - this.bot_pickup = true; // allow bots to make use of this ladder - float cost = waypoint_getlinearcost(trace_endpos.z - this.absmin.z); - top_min = trace_endpos; - waypoint_spawnforteleporter_boxes(this, WAYPOINTFLAG_LADDER, this.absmin, this.absmax, top_min, top_min, cost); -} - -spawnfunc(func_ladder) -{ - IL_PUSH(g_ladders, this); // TODO: also func_water? bots currently loop through func_ladder only - - func_ladder_init(this); -} - -spawnfunc(func_water) -{ - func_ladder_init(this); -} - -#elif defined(CSQC) -.float speed; - -void func_ladder_remove(entity this) -{ - if(this.classname) { strunzone(this.classname); } - this.classname = string_null; -} - -NET_HANDLE(ENT_CLIENT_LADDER, bool isnew) -{ - this.classname = strzone(ReadString()); - this.skin = ReadByte(); - this.speed = ReadCoord(); - - trigger_common_read(this, false); - - this.solid = SOLID_TRIGGER; - settouch(this, func_ladder_touch); - this.drawmask = MASK_NORMAL; - this.move_time = time; - this.entremove = func_ladder_remove; - - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/ladder.qh b/qcsrc/common/triggers/func/ladder.qh deleted file mode 100644 index 26cbbda032..0000000000 --- a/qcsrc/common/triggers/func/ladder.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -.float ladder_time; -.entity ladder_entity; diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc deleted file mode 100644 index a59f7a93ba..0000000000 --- a/qcsrc/common/triggers/func/pendulum.qc +++ /dev/null @@ -1,77 +0,0 @@ -#include "pendulum.qh" -#ifdef SVQC -.float freq; -void func_pendulum_controller_think(entity this) -{ - float v; - this.nextthink = time + 0.1; - - if (!(this.owner.active == ACTIVE_ACTIVE)) - { - this.owner.avelocity_x = 0; - return; - } - - // calculate sinewave using makevectors - makevectors((this.nextthink * this.owner.freq + this.owner.phase) * '0 360 0'); - v = this.owner.speed * v_forward_y + this.cnt; - if(this.owner.classname == "func_pendulum") // don't brake stuff if the func_bobbing was killtarget'ed - { - // * 10 so it will arrive in 0.1 sec - this.owner.avelocity_z = (remainder(v - this.owner.angles_z, 360)) * 10; - } -} - -spawnfunc(func_pendulum) -{ - entity controller; - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - - this.active = ACTIVE_ACTIVE; - - // keys: angle, speed, phase, noise, freq - - if(!this.speed) - this.speed = 30; - // not initializing this.dmg to 2, to allow damageless pendulum - - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - setblocked(this, generic_plat_blocked); - - this.avelocity_z = 0.0000001; - if (!InitMovingBrushTrigger(this)) - return; - - if(!this.freq) - { - // find pendulum length (same formula as Q3A) - this.freq = 1 / (M_PI * 2) * sqrt(autocvar_sv_gravity / (3 * max(8, fabs(this.mins_z)))); - } - - // copy initial angle - this.cnt = this.angles_z; - - // wait for targets to spawn - controller = new(func_pendulum_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_pendulum_controller_think); - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - //this.effects |= EF_LOWPRECISION; - - // TODO make a reset function for this one -} -#endif diff --git a/qcsrc/common/triggers/func/pendulum.qh b/qcsrc/common/triggers/func/pendulum.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/pendulum.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc deleted file mode 100644 index 9f97a1cea0..0000000000 --- a/qcsrc/common/triggers/func/plat.qc +++ /dev/null @@ -1,176 +0,0 @@ -#include "plat.qh" -REGISTER_NET_LINKED(ENT_CLIENT_PLAT) - -#ifdef SVQC -void plat_link(entity this); - -void plat_delayedinit(entity this) -{ - plat_link(this); - plat_spawn_inside_trigger(this); // the "start moving" trigger -} - -float plat_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_PLAT); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteByte(MSG_ENTITY, this.platmovetype_start); - WriteByte(MSG_ENTITY, this.platmovetype_turn); - WriteByte(MSG_ENTITY, this.platmovetype_end); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - - WriteShort(MSG_ENTITY, this.speed); - WriteShort(MSG_ENTITY, this.height); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - - WriteShort(MSG_ENTITY, this.dmg); - } - - if(sf & SF_TRIGGER_RESET) - { - // used on client - } - - return true; -} - -void plat_link(entity this) -{ - //Net_LinkEntity(this, 0, false, plat_send); -} - -spawnfunc(func_plat) -{ - if (this.sounds == 0) this.sounds = 2; - - if (this.spawnflags & 4) this.dmg = 10000; - - if (this.dmg && (this.message == "")) this.message = "was squished"; - if (this.dmg && (this.message2 == "")) this.message2 = "was squished by"; - - if (this.sounds == 1) - { - this.noise = "plats/plat1.wav"; - this.noise1 = "plats/plat2.wav"; - } - - if (this.sounds == 2) - { - this.noise = "plats/medplat1.wav"; - this.noise1 = "plats/medplat2.wav"; - } - - if (this.sound1) - this.noise = this.sound1; - if (this.sound2) - this.noise1 = this.sound2; - - if(this.noise && this.noise != "") { precache_sound(this.noise); } - if(this.noise1 && this.noise1 != "") { precache_sound(this.noise1); } - - this.mangle = this.angles; - this.angles = '0 0 0'; - - this.classname = "plat"; - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - setsize (this, this.mins , this.maxs); - - setblocked(this, plat_crush); - - if (!this.speed) this.speed = 150; - if (!this.lip) this.lip = 16; - if (!this.height) this.height = this.size.z - this.lip; - - this.pos1 = this.origin; - this.pos2 = this.origin; - this.pos2_z = this.origin.z - this.height; - - this.reset = plat_reset; - this.reset(this); - - InitializeEntity(this, plat_delayedinit, INITPRIO_FINDTARGET); -} -#elif defined(CSQC) -void plat_draw(entity this) -{ - Movetype_Physics_NoMatchServer(this); - //Movetype_Physics_MatchServer(autocvar_cl_projectiles_sloppy); -} - -NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) -{ - float sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.platmovetype_start = ReadByte(); - this.platmovetype_turn = ReadByte(); - this.platmovetype_end = ReadByte(); - this.spawnflags = ReadByte(); - - this.model = strzone(ReadString()); - _setmodel(this, this.model); - - trigger_common_read(this, true); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - this.speed = ReadShort(); - this.height = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - - this.dmg = ReadShort(); - - this.classname = "plat"; - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.drawmask = MASK_NORMAL; - this.draw = plat_draw; - if (isnew) IL_PUSH(g_drawables, this); - this.use = plat_use; - this.entremove = trigger_remove_generic; - - plat_reset(this); // also called here - - set_movetype(this, MOVETYPE_PUSH); - this.move_time = time; - - plat_spawn_inside_trigger(this); - } - - if(sf & SF_TRIGGER_RESET) - { - plat_reset(this); - - this.move_time = time; - } - return true; -} -#endif diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/plat.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc deleted file mode 100644 index 54fe212ae2..0000000000 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ /dev/null @@ -1,369 +0,0 @@ -#include "pointparticles.qh" -REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) - -#ifdef SVQC -// NOTE: also contains func_sparks - -bool pointparticles_SendEntity(entity this, entity to, float fl) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_POINTPARTICLES); - - // optional features to save space - fl = fl & 0x0F; - if(this.spawnflags & 2) - fl |= 0x10; // absolute count on toggle-on - if(this.movedir != '0 0 0' || this.velocity != '0 0 0') - fl |= 0x20; // 4 bytes - saves CPU - if(this.waterlevel || this.count != 1) - fl |= 0x40; // 4 bytes - obscure features almost never used - if(this.mins != '0 0 0' || this.maxs != '0 0 0') - fl |= 0x80; // 14 bytes - saves lots of space - - WriteByte(MSG_ENTITY, fl); - if(fl & 2) - { - if(this.state) - WriteCoord(MSG_ENTITY, this.impulse); - else - WriteCoord(MSG_ENTITY, 0); // off - } - if(fl & 4) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(fl & 1) - { - if(this.model != "null") - { - WriteShort(MSG_ENTITY, this.modelindex); - if(fl & 0x80) - { - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - } - } - else - { - WriteShort(MSG_ENTITY, 0); - if(fl & 0x80) - { - WriteVector(MSG_ENTITY, this.maxs); - } - } - WriteShort(MSG_ENTITY, this.cnt); - WriteString(MSG_ENTITY, this.mdl); - if(fl & 0x20) - { - WriteShort(MSG_ENTITY, compressShortVector(this.velocity)); - WriteShort(MSG_ENTITY, compressShortVector(this.movedir)); - } - if(fl & 0x40) - { - WriteShort(MSG_ENTITY, this.waterlevel * 16.0); - WriteByte(MSG_ENTITY, this.count * 16.0); - } - WriteString(MSG_ENTITY, this.noise); - if(this.noise != "") - { - WriteByte(MSG_ENTITY, floor(this.atten * 64)); - WriteByte(MSG_ENTITY, floor(this.volume * 255)); - } - WriteString(MSG_ENTITY, this.bgmscript); - if(this.bgmscript != "") - { - WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); - } - } - return 1; -} - -void pointparticles_use(entity this, entity actor, entity trigger) -{ - this.state = !this.state; - this.SendFlags |= 2; -} - -void pointparticles_think(entity this) -{ - if(this.origin != this.oldorigin) - { - this.SendFlags |= 4; - this.oldorigin = this.origin; - } - this.nextthink = time; -} - -void pointparticles_reset(entity this) -{ - if(this.spawnflags & 1) - this.state = 1; - else - this.state = 0; -} - -spawnfunc(func_pointparticles) -{ - if(this.model != "") { precache_model(this.model); _setmodel(this, this.model); } - if(this.noise != "") precache_sound(this.noise); - if(this.mdl != "") this.cnt = 0; // use a good handler - - if(!this.bgmscriptsustain) this.bgmscriptsustain = 1; - else if(this.bgmscriptsustain < 0) this.bgmscriptsustain = 0; - - if(!this.atten) this.atten = ATTEN_NORM; - else if(this.atten < 0) this.atten = 0; - if(!this.volume) this.volume = 1; - if(!this.count) this.count = 1; - if(!this.impulse) this.impulse = 1; - - if(!this.modelindex) - { - setorigin(this, this.origin + this.mins); - setsize(this, '0 0 0', this.maxs - this.mins); - } - //if(!this.cnt) this.cnt = _particleeffectnum(this.mdl); - - Net_LinkEntity(this, (this.spawnflags & 4), 0, pointparticles_SendEntity); - - IFTARGETED - { - this.use = pointparticles_use; - this.reset = pointparticles_reset; - this.reset(this); - } - else - this.state = 1; - setthink(this, pointparticles_think); - this.nextthink = time; -} - -spawnfunc(func_sparks) -{ - // this.cnt is the amount of sparks that one burst will spawn - if(this.cnt < 1) { - this.cnt = 25.0; // nice default value - } - - // this.wait is the probability that a sparkthink will spawn a spark shower - // range: 0 - 1, but 0 makes little sense, so... - if(this.wait < 0.05) { - this.wait = 0.25; // nice default value - } - - this.count = this.cnt; - this.mins = '0 0 0'; - this.maxs = '0 0 0'; - this.velocity = '0 0 -1'; - this.mdl = "TE_SPARK"; - this.impulse = 10 * this.wait; // by default 2.5/sec - this.wait = 0; - this.cnt = 0; // use mdl - - spawnfunc_func_pointparticles(this); -} -#elif defined(CSQC) - -.int dphitcontentsmask; - -entityclass(PointParticles); -class(PointParticles) .int cnt; // effect number -class(PointParticles) .vector velocity; // particle velocity -class(PointParticles) .float waterlevel; // direction jitter -class(PointParticles) .int count; // count multiplier -class(PointParticles) .int impulse; // density -class(PointParticles) .string noise; // sound -class(PointParticles) .float atten; -class(PointParticles) .float volume; -class(PointParticles) .float absolute; // 1 = count per second is absolute, 2 = only spawn at toggle -class(PointParticles) .vector movedir; // trace direction -class(PointParticles) .float glow_color; // palette index - -void Draw_PointParticles(entity this) -{ - float n, i, fail; - vector p; - vector sz; - vector o; - o = this.origin; - sz = this.maxs - this.mins; - n = doBGMScript(this); - if(this.absolute == 2) - { - if(n >= 0) - n = this.just_toggled ? this.impulse : 0; - else - n = this.impulse * drawframetime; - } - else - { - n *= this.impulse * drawframetime; - if(this.just_toggled) - if(n < 1) - n = 1; - } - if(n == 0) - return; - fail = 0; - for(i = random(); i <= n && fail <= 64*n; ++i) - { - p = o + this.mins; - p.x += random() * sz.x; - p.y += random() * sz.y; - p.z += random() * sz.z; - if(WarpZoneLib_BoxTouchesBrush(p, p, this, NULL)) - { - if(this.movedir != '0 0 0') - { - traceline(p, p + normalize(this.movedir) * 4096, 0, NULL); - p = trace_endpos; - int eff_num; - if(this.cnt) - eff_num = this.cnt; - else - eff_num = _particleeffectnum(this.mdl); - __pointparticles(eff_num, p, trace_plane_normal * vlen(this.movedir) + this.velocity + randomvec() * this.waterlevel, this.count); - } - else - { - int eff_num; - if(this.cnt) - eff_num = this.cnt; - else - eff_num = _particleeffectnum(this.mdl); - __pointparticles(eff_num, p, this.velocity + randomvec() * this.waterlevel, this.count); - } - if(this.noise != "") - { - setorigin(this, p); - _sound(this, CH_AMBIENT, this.noise, VOL_BASE * this.volume, this.atten); - } - this.just_toggled = 0; - } - else if(this.absolute) - { - ++fail; - --i; - } - } - setorigin(this, o); -} - -void Ent_PointParticles_Remove(entity this) -{ - if(this.noise) - strunzone(this.noise); - this.noise = string_null; - if(this.bgmscript) - strunzone(this.bgmscript); - this.bgmscript = string_null; - if(this.mdl) - strunzone(this.mdl); - this.mdl = string_null; -} - -NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) -{ - float i; - vector v; - int f = ReadByte(); - if(f & 2) - { - i = ReadCoord(); // density (<0: point, >0: volume) - if(i && !this.impulse && (this.cnt || this.mdl)) // this.cnt check is so it only happens if the ent already existed - this.just_toggled = 1; - this.impulse = i; - } - if(f & 4) - { - this.origin = ReadVector(); - } - if(f & 1) - { - this.modelindex = ReadShort(); - if(f & 0x80) - { - if(this.modelindex) - { - this.mins = ReadVector(); - this.maxs = ReadVector(); - } - else - { - this.mins = '0 0 0'; - this.maxs = ReadVector(); - } - } - else - { - this.mins = this.maxs = '0 0 0'; - } - - this.cnt = ReadShort(); // effect number - this.mdl = strzone(ReadString()); // effect string - - if(f & 0x20) - { - this.velocity = decompressShortVector(ReadShort()); - this.movedir = decompressShortVector(ReadShort()); - } - else - { - this.velocity = this.movedir = '0 0 0'; - } - if(f & 0x40) - { - this.waterlevel = ReadShort() / 16.0; - this.count = ReadByte() / 16.0; - } - else - { - this.waterlevel = 0; - this.count = 1; - } - if(this.noise) - strunzone(this.noise); - if(this.bgmscript) - strunzone(this.bgmscript); - this.noise = strzone(ReadString()); - if(this.noise != "") - { - this.atten = ReadByte() / 64.0; - this.volume = ReadByte() / 255.0; - } - this.bgmscript = strzone(ReadString()); - if(this.bgmscript != "") - { - this.bgmscriptattack = ReadByte() / 64.0; - this.bgmscriptdecay = ReadByte() / 64.0; - this.bgmscriptsustain = ReadByte() / 255.0; - this.bgmscriptrelease = ReadByte() / 64.0; - } - BGMScript_InitEntity(this); - } - - return = true; - - if(f & 2) - { - this.absolute = (this.impulse >= 0); - if(!this.absolute) - { - v = this.maxs - this.mins; - this.impulse *= -v.x * v.y * v.z / 262144; // relative: particles per 64^3 cube - } - } - - if(f & 0x10) - this.absolute = 2; - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.solid = SOLID_NOT; - this.draw = Draw_PointParticles; - if (isnew) IL_PUSH(g_drawables, this); - this.entremove = Ent_PointParticles_Remove; -} -#endif diff --git a/qcsrc/common/triggers/func/pointparticles.qh b/qcsrc/common/triggers/func/pointparticles.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/pointparticles.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc deleted file mode 100644 index 1817a9ce9a..0000000000 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ /dev/null @@ -1,138 +0,0 @@ -#include "rainsnow.qh" -REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) - -#ifdef SVQC -bool rainsnow_SendEntity(entity this, entity to, float sf) -{ - vector myorg = this.origin + this.mins; - vector mysize = this.maxs - this.mins; - WriteHeader(MSG_ENTITY, ENT_CLIENT_RAINSNOW); - WriteByte(MSG_ENTITY, this.state); - WriteVector(MSG_ENTITY, myorg); - WriteVector(MSG_ENTITY, mysize); - WriteShort(MSG_ENTITY, compressShortVector(this.dest)); - WriteShort(MSG_ENTITY, this.count); - WriteByte(MSG_ENTITY, this.cnt); - return true; -} - -/*QUAKED spawnfunc_func_rain (0 .5 .8) ? -This is an invisible area like a trigger, which rain falls inside of. - -Keys: -"velocity" - falling direction (should be something like '0 0 -700', use the X and Y velocity for wind) -"cnt" - sets color of rain (default 12 - white) -"count" - adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 -*/ -spawnfunc(func_rain) -{ - this.dest = this.velocity; - this.velocity = '0 0 0'; - if (!this.dest) - this.dest = '0 0 -700'; - this.angles = '0 0 0'; - set_movetype(this, MOVETYPE_NONE); - this.solid = SOLID_NOT; - SetBrushEntityModel(this); - if (!this.cnt) - this.cnt = 12; - if (!this.count) - this.count = 2000; - this.count = 0.01 * this.count * (this.size_x / 1024) * (this.size_y / 1024); - if (this.count < 1) - this.count = 1; - if(this.count > 65535) - this.count = 65535; - - this.state = 1; // 1 is rain, 0 is snow - this.Version = 1; - - Net_LinkEntity(this, false, 0, rainsnow_SendEntity); -} - - -/*QUAKED spawnfunc_func_snow (0 .5 .8) ? -This is an invisible area like a trigger, which snow falls inside of. - -Keys: -"velocity" - falling direction (should be something like '0 0 -300', use the X and Y velocity for wind) -"cnt" - sets color of rain (default 12 - white) -"count" - adjusts density, this many particles fall every second for a 1024x1024 area, default is 2000 -*/ -spawnfunc(func_snow) -{ - this.dest = this.velocity; - this.velocity = '0 0 0'; - if (!this.dest) - this.dest = '0 0 -300'; - this.angles = '0 0 0'; - set_movetype(this, MOVETYPE_NONE); - this.solid = SOLID_NOT; - SetBrushEntityModel(this); - if (!this.cnt) - this.cnt = 12; - if (!this.count) - this.count = 2000; - this.count = 0.01 * this.count * (this.size_x / 1024) * (this.size_y / 1024); - if (this.count < 1) - this.count = 1; - if(this.count > 65535) - this.count = 65535; - - this.state = 0; // 1 is rain, 0 is snow - this.Version = 1; - - Net_LinkEntity(this, false, 0, rainsnow_SendEntity); -} -#elif defined(CSQC) -float autocvar_cl_rainsnow_maxdrawdist = 2048; - -void Draw_Rain(entity this) -{ - vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; - maxdist.z = 5; - if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) - //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) - te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); -} - -void Draw_Snow(entity this) -{ - vector maxdist = '1 1 0' * autocvar_cl_rainsnow_maxdrawdist; - maxdist.z = 5; - if(boxesoverlap(vec2(view_origin) - maxdist, vec2(view_origin) + maxdist, vec2(this.absmin) - '0 0 5', vec2(this.absmax) + '0 0 5')) - //if(autocvar_cl_rainsnow_maxdrawdist <= 0 || vdist(vec2(this.origin) - vec2(this.absmin + this.absmax * 0.5), <=, autocvar_cl_rainsnow_maxdrawdist)) - te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); -} - -NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) -{ - this.impulse = ReadByte(); // Rain, Snow, or Whatever - this.origin = ReadVector(); - this.maxs = ReadVector(); - this.velocity = decompressShortVector(ReadShort()); - this.count = ReadShort() * 10; - this.glow_color = ReadByte(); // color - - return = true; - - this.mins = -0.5 * this.maxs; - this.maxs = 0.5 * this.maxs; - this.origin = this.origin - this.mins; - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.solid = SOLID_NOT; - if (isnew) IL_PUSH(g_drawables, this); - if(this.impulse) - this.draw = Draw_Rain; - else - this.draw = Draw_Snow; -} -#endif diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/rainsnow.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc deleted file mode 100644 index 6268dcfeb8..0000000000 --- a/qcsrc/common/triggers/func/rotating.qc +++ /dev/null @@ -1,103 +0,0 @@ -#include "rotating.qh" -#ifdef SVQC -const int FUNC_ROTATING_STARTOFF = BIT(4); - -void func_rotating_setactive(entity this, int astate) -{ - if (astate == ACTIVE_TOGGLE) - { - if(this.active == ACTIVE_ACTIVE) - this.active = ACTIVE_NOT; - else - this.active = ACTIVE_ACTIVE; - } - else - this.active = astate; - - if(this.active == ACTIVE_NOT) - this.avelocity = '0 0 0'; - else - this.avelocity = this.pos1; -} - -void func_rotating_reset(entity this) -{ - // TODO: reset angles as well? - - if(this.spawnflags & FUNC_ROTATING_STARTOFF) - { - this.avelocity = '0 0 0'; - this.active = ACTIVE_NOT; - } - else - { - this.avelocity = this.pos1; - this.active = ACTIVE_ACTIVE; - } -} - -/*QUAKED spawnfunc_func_rotating (0 .5 .8) ? - - X_AXIS Y_AXIS -Brush model that spins in place on one axis (default Z). -speed : speed to rotate (in degrees per second) -noise : path/name of looping .wav file to play. -dmg : Do this mutch dmg every .dmgtime intervall when blocked -dmgtime : See above. -*/ - -spawnfunc(func_rotating) -{ - if (this.noise != "") - { - precache_sound(this.noise); - ambientsound(this.origin, this.noise, VOL_BASE, ATTEN_IDLE); - } - - this.setactive = func_rotating_setactive; - - if (!this.speed) - this.speed = 100; - // FIXME: test if this turns the right way, then remove this comment (negate as needed) - if (this.spawnflags & BIT(2)) // X (untested) - this.avelocity = '0 0 1' * this.speed; - // FIXME: test if this turns the right way, then remove this comment (negate as needed) - else if (this.spawnflags & BIT(3)) // Y (untested) - this.avelocity = '1 0 0' * this.speed; - // FIXME: test if this turns the right way, then remove this comment (negate as needed) - else // Z - this.avelocity = '0 1 0' * this.speed; - - this.pos1 = this.avelocity; - - // do this after setting pos1, so we can safely reactivate the func_rotating - if(this.spawnflags & FUNC_ROTATING_STARTOFF) - { - this.avelocity = '0 0 0'; - this.active = ACTIVE_NOT; - } - else - this.active = ACTIVE_ACTIVE; - - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - - - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - - this.dmgtime2 = time; - - if (!InitMovingBrushTrigger(this)) - return; - // no EF_LOWPRECISION here, as rounding angles is bad - - setblocked(this, generic_plat_blocked); - - // wait for targets to spawn - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - this.reset = func_rotating_reset; -} -#endif diff --git a/qcsrc/common/triggers/func/rotating.qh b/qcsrc/common/triggers/func/rotating.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/rotating.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc deleted file mode 100644 index 9c2fba8ada..0000000000 --- a/qcsrc/common/triggers/func/stardust.qc +++ /dev/null @@ -1,9 +0,0 @@ -#include "stardust.qh" -#ifdef SVQC -spawnfunc(func_stardust) -{ - this.effects = EF_STARDUST; - - CSQCMODEL_AUTOINIT(this); -} -#endif diff --git a/qcsrc/common/triggers/func/stardust.qh b/qcsrc/common/triggers/func/stardust.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/stardust.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc deleted file mode 100644 index ead0867081..0000000000 --- a/qcsrc/common/triggers/func/train.qc +++ /dev/null @@ -1,351 +0,0 @@ -#include "train.qh" -.float train_wait_turning; -.entity future_target; -void train_next(entity this); -#ifdef SVQC -void train_use(entity this, entity actor, entity trigger); -#endif -void train_wait(entity this) -{ - SUB_UseTargets(this.enemy, NULL, NULL); - this.enemy = NULL; - - // if turning is enabled, the train will turn toward the next point while waiting - if(this.platmovetype_turn && !this.train_wait_turning) - { - entity targ, cp; - vector ang; - targ = this.future_target; - if((this.spawnflags & 1) && targ.curvetarget) - cp = find(NULL, targetname, targ.curvetarget); - else - cp = NULL; - - if(cp) // bezier curves movement - ang = cp.origin - (this.origin - this.view_ofs); // use the origin of the control point of the next path_corner - else // linear movement - ang = targ.origin - (this.origin - this.view_ofs); // use the origin of the next path_corner - ang = vectoangles(ang); - ang_x = -ang_x; // flip up / down orientation - - if(this.wait > 0) // slow turning - SUB_CalcAngleMove(this, ang, TSPEED_TIME, this.ltime - time + this.wait, train_wait); - else // instant turning - SUB_CalcAngleMove(this, ang, TSPEED_TIME, 0.0000001, train_wait); - this.train_wait_turning = true; - return; - } - -#ifdef SVQC - if(this.noise != "") - stopsoundto(MSG_BROADCAST, this, CH_TRIGGER_SINGLE); // send this as unreliable only, as the train will resume operation shortly anyway -#endif - -#ifdef SVQC - entity tg = this.future_target; - if(tg.spawnflags & 4) - { - this.use = train_use; - setthink(this, func_null); - this.nextthink = 0; - } - else -#endif - if(this.wait < 0 || this.train_wait_turning) // no waiting or we already waited while turning - { - this.train_wait_turning = false; - train_next(this); - } - else - { - setthink(this, train_next); - this.nextthink = this.ltime + this.wait; - } -} - -entity train_next_find(entity this) -{ - if(this.target_random) - { - RandomSelection_Init(); - for(entity t = NULL; (t = find(t, targetname, this.target));) - { - RandomSelection_AddEnt(t, 1, 0); - } - return RandomSelection_chosen_ent; - } - else - { - return find(NULL, targetname, this.target); - } -} - -void train_next(entity this) -{ - entity targ = NULL, cp = NULL; - vector cp_org = '0 0 0'; - - targ = this.future_target; - - this.target = targ.target; - this.target_random = targ.target_random; - this.future_target = train_next_find(targ); - - if (this.spawnflags & 1) - { - if(targ.curvetarget) - { - cp = find(NULL, targetname, targ.curvetarget); // get its second target (the control point) - cp_org = cp.origin - this.view_ofs; // no control point found, assume a straight line to the destination - } - } - if (this.target == "") - objerror(this, "train_next: no next target"); - this.wait = targ.wait; - if (!this.wait) - this.wait = 0.1; - - if(targ.platmovetype) - { - // this path_corner contains a movetype overrider, apply it - this.platmovetype_start = targ.platmovetype_start; - this.platmovetype_end = targ.platmovetype_end; - } - else - { - // this path_corner doesn't contain a movetype overrider, use the train's defaults - this.platmovetype_start = this.platmovetype_start_default; - this.platmovetype_end = this.platmovetype_end_default; - } - - if (targ.speed) - { - if (cp) - SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); - else - SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, targ.speed, train_wait); - } - else - { - if (cp) - SUB_CalcMove_Bezier(this, cp_org, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); - else - SUB_CalcMove(this, targ.origin - this.view_ofs, TSPEED_LINEAR, this.speed, train_wait); - } - - if(this.noise != "") - _sound(this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); -} - -REGISTER_NET_LINKED(ENT_CLIENT_TRAIN) - -#ifdef SVQC -float train_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRAIN); - WriteByte(MSG_ENTITY, sf); - - if(sf & SF_TRIGGER_INIT) - { - WriteString(MSG_ENTITY, this.platmovetype); - WriteByte(MSG_ENTITY, this.platmovetype_turn); - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteString(MSG_ENTITY, this.model); - - trigger_common_write(this, true); - - WriteString(MSG_ENTITY, this.curvetarget); - - WriteVector(MSG_ENTITY, this.pos1); - WriteVector(MSG_ENTITY, this.pos2); - - WriteVector(MSG_ENTITY, this.size); - - WriteVector(MSG_ENTITY, this.view_ofs); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - - WriteShort(MSG_ENTITY, this.speed); - WriteShort(MSG_ENTITY, this.height); - WriteByte(MSG_ENTITY, this.lip); - WriteByte(MSG_ENTITY, this.state); - WriteByte(MSG_ENTITY, this.wait); - - WriteShort(MSG_ENTITY, this.dmg); - WriteByte(MSG_ENTITY, this.dmgtime); - } - - if(sf & SF_TRIGGER_RESET) - { - // used on client - } - - return true; -} - -void train_link(entity this) -{ - //Net_LinkEntity(this, 0, false, train_send); -} - -void train_use(entity this, entity actor, entity trigger) -{ - this.nextthink = this.ltime + 1; - setthink(this, train_next); - this.use = func_null; // not again - if(trigger.target2 && trigger.target2 != "") - this.future_target = find(NULL, targetname, trigger.target2); -} - -void func_train_find(entity this) -{ - entity targ = train_next_find(this); - this.target = targ.target; - this.target_random = targ.target_random; - // save the future target for later - this.future_target = train_next_find(targ); - if (this.target == "") - objerror(this, "func_train_find: no next target"); - setorigin(this, targ.origin - this.view_ofs); - - if(!(this.spawnflags & 4)) - { - this.nextthink = this.ltime + 1; - setthink(this, train_next); - } - - train_link(this); -} - -#endif - -/*QUAKED spawnfunc_func_train (0 .5 .8) ? -Ridable platform, targets spawnfunc_path_corner path to follow. -speed : speed the train moves (can be overridden by each spawnfunc_path_corner) -target : targetname of first spawnfunc_path_corner (starts here) -*/ -#ifdef SVQC -spawnfunc(func_train) -{ - if (this.noise != "") - precache_sound(this.noise); - - if (this.target == "") - objerror(this, "func_train without a target"); - if (!this.speed) - this.speed = 100; - - if (!InitMovingBrushTrigger(this)) - return; - this.effects |= EF_LOWPRECISION; - - if(this.spawnflags & 4) - this.use = train_use; - - if (this.spawnflags & 2) - { - this.platmovetype_turn = true; - this.view_ofs = '0 0 0'; // don't offset a rotating train, origin works differently now - } - else - this.view_ofs = this.mins; - - // wait for targets to spawn - InitializeEntity(this, func_train_find, INITPRIO_FINDTARGET); - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message2 == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if(!set_platmovetype(this, this.platmovetype)) - return; - this.platmovetype_start_default = this.platmovetype_start; - this.platmovetype_end_default = this.platmovetype_end; - - // TODO make a reset function for this one -} -#elif defined(CSQC) -void train_draw(entity this) -{ - //Movetype_Physics_NoMatchServer(); - Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy); -} - -NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) -{ - float sf = ReadByte(); - - if(sf & SF_TRIGGER_INIT) - { - this.platmovetype = strzone(ReadString()); - this.platmovetype_turn = ReadByte(); - this.spawnflags = ReadByte(); - - this.model = strzone(ReadString()); - _setmodel(this, this.model); - - trigger_common_read(this, true); - - this.curvetarget = strzone(ReadString()); - - this.pos1 = ReadVector(); - this.pos2 = ReadVector(); - - this.size = ReadVector(); - - this.view_ofs = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - this.speed = ReadShort(); - this.height = ReadShort(); - this.lip = ReadByte(); - this.state = ReadByte(); - this.wait = ReadByte(); - - this.dmg = ReadShort(); - this.dmgtime = ReadByte(); - - this.classname = "func_train"; - this.solid = SOLID_BSP; - set_movetype(this, MOVETYPE_PUSH); - this.drawmask = MASK_NORMAL; - this.draw = train_draw; - if (isnew) IL_PUSH(g_drawables, this); - this.entremove = trigger_remove_generic; - - if(set_platmovetype(this, this.platmovetype)) - { - this.platmovetype_start_default = this.platmovetype_start; - this.platmovetype_end_default = this.platmovetype_end; - } - - // everything is set up by the time the train is linked, we shouldn't need this - //func_train_find(); - - // but we will need these - train_next(this); - - set_movetype(this, MOVETYPE_PUSH); - this.move_time = time; - } - - if(sf & SF_TRIGGER_RESET) - { - // TODO: make a reset function for trains - } - - return true; -} - -#endif diff --git a/qcsrc/common/triggers/func/train.qh b/qcsrc/common/triggers/func/train.qh deleted file mode 100644 index 8b6f7c02da..0000000000 --- a/qcsrc/common/triggers/func/train.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef CSQC -.float dmgtime; -#endif diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc deleted file mode 100644 index 951a740a20..0000000000 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ /dev/null @@ -1,159 +0,0 @@ -#include "vectormamamam.qh" -#ifdef SVQC -// reusing some fields havocbots declared -.entity wp00, wp01, wp02, wp03; - -.float targetfactor, target2factor, target3factor, target4factor; -.vector targetnormal, target2normal, target3normal, target4normal; - -vector func_vectormamamam_origin(entity o, float t) -{ - vector v, p; - float f; - entity e; - - f = o.spawnflags; - v = '0 0 0'; - - e = o.wp00; - if(e) - { - p = e.origin + t * e.velocity; - if(f & 1) - v = v + (p * o.targetnormal) * o.targetnormal * o.targetfactor; - else - v = v + (p - (p * o.targetnormal) * o.targetnormal) * o.targetfactor; - } - - e = o.wp01; - if(e) - { - p = e.origin + t * e.velocity; - if(f & 2) - v = v + (p * o.target2normal) * o.target2normal * o.target2factor; - else - v = v + (p - (p * o.target2normal) * o.target2normal) * o.target2factor; - } - - e = o.wp02; - if(e) - { - p = e.origin + t * e.velocity; - if(f & 4) - v = v + (p * o.target3normal) * o.target3normal * o.target3factor; - else - v = v + (p - (p * o.target3normal) * o.target3normal) * o.target3factor; - } - - e = o.wp03; - if(e) - { - p = e.origin + t * e.velocity; - if(f & 8) - v = v + (p * o.target4normal) * o.target4normal * o.target4factor; - else - v = v + (p - (p * o.target4normal) * o.target4normal) * o.target4factor; - } - - return v; -} - -void func_vectormamamam_controller_think(entity this) -{ - this.nextthink = time + 0.1; - - if(this.owner.active != ACTIVE_ACTIVE) - { - this.owner.velocity = '0 0 0'; - return; - } - - if(this.owner.classname == "func_vectormamamam") // don't brake stuff if the func_vectormamamam was killtarget'ed - this.owner.velocity = (this.owner.destvec + func_vectormamamam_origin(this.owner, 0.1) - this.owner.origin) * 10; -} - -void func_vectormamamam_findtarget(entity this) -{ - if(this.target != "") - this.wp00 = find(NULL, targetname, this.target); - - if(this.target2 != "") - this.wp01 = find(NULL, targetname, this.target2); - - if(this.target3 != "") - this.wp02 = find(NULL, targetname, this.target3); - - if(this.target4 != "") - this.wp03 = find(NULL, targetname, this.target4); - - if(!this.wp00 && !this.wp01 && !this.wp02 && !this.wp03) - objerror(this, "No reference entity found, so there is nothing to move. Aborting."); - - this.destvec = this.origin - func_vectormamamam_origin(this, 0); - - entity controller; - controller = new(func_vectormamamam_controller); - controller.owner = this; - controller.nextthink = time + 1; - setthink(controller, func_vectormamamam_controller_think); -} - -spawnfunc(func_vectormamamam) -{ - if (this.noise != "") - { - precache_sound(this.noise); - soundto(MSG_INIT, this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_IDLE); - } - - if(!this.targetfactor) - this.targetfactor = 1; - - if(!this.target2factor) - this.target2factor = 1; - - if(!this.target3factor) - this.target3factor = 1; - - if(!this.target4factor) - this.target4factor = 1; - - if(this.targetnormal) - this.targetnormal = normalize(this.targetnormal); - - if(this.target2normal) - this.target2normal = normalize(this.target2normal); - - if(this.target3normal) - this.target3normal = normalize(this.target3normal); - - if(this.target4normal) - this.target4normal = normalize(this.target4normal); - - setblocked(this, generic_plat_blocked); - if(this.dmg && (this.message == "")) - this.message = " was squished"; - if(this.dmg && (this.message == "")) - this.message2 = "was squished by"; - if(this.dmg && (!this.dmgtime)) - this.dmgtime = 0.25; - this.dmgtime2 = time; - - if(this.netname == "") - this.netname = "1 0 0 0 1"; - - if (!InitMovingBrushTrigger(this)) - return; - - // wait for targets to spawn - this.nextthink = this.ltime + 999999999; - setthink(this, SUB_NullThink); // for PushMove - - // Savage: Reduce bandwith, critical on e.g. nexdm02 - this.effects |= EF_LOWPRECISION; - - this.active = ACTIVE_ACTIVE; - - InitializeEntity(this, func_vectormamamam_findtarget, INITPRIO_FINDTARGET); -} -#endif diff --git a/qcsrc/common/triggers/func/vectormamamam.qh b/qcsrc/common/triggers/func/vectormamamam.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/func/vectormamamam.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/include.qc b/qcsrc/common/triggers/include.qc deleted file mode 100644 index 59da1f217b..0000000000 --- a/qcsrc/common/triggers/include.qc +++ /dev/null @@ -1,20 +0,0 @@ -#include "include.qh" - -// some required common stuff -#include "subs.qc" -#include "triggers.qc" -#include "platforms.qc" -#include "teleporters.qc" - -// func -#include "func/include.qc" - -// misc -#include "misc/include.qc" - -// target -#include "target/include.qc" - -// trigger -#include "trigger/include.qc" - diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh deleted file mode 100644 index 87c07c14df..0000000000 --- a/qcsrc/common/triggers/include.qh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -// some required common stuff -#ifdef SVQC - #include <server/item_key.qh> -#endif -#include "triggers.qh" -#include "subs.qh" -#include "platforms.qh" -#include "teleporters.qh" - -// func -#include "func/include.qh" - -// target -#include "target/include.qh" - -// trigger -#include "trigger/include.qh" diff --git a/qcsrc/common/triggers/misc/_mod.inc b/qcsrc/common/triggers/misc/_mod.inc deleted file mode 100644 index 4a8ec06ef5..0000000000 --- a/qcsrc/common/triggers/misc/_mod.inc +++ /dev/null @@ -1,6 +0,0 @@ -// generated file; do not modify -#include <common/triggers/misc/corner.qc> -#include <common/triggers/misc/follow.qc> -#include <common/triggers/misc/include.qc> -#include <common/triggers/misc/laser.qc> -#include <common/triggers/misc/teleport_dest.qc> diff --git a/qcsrc/common/triggers/misc/_mod.qh b/qcsrc/common/triggers/misc/_mod.qh deleted file mode 100644 index 98615ccb0c..0000000000 --- a/qcsrc/common/triggers/misc/_mod.qh +++ /dev/null @@ -1,6 +0,0 @@ -// generated file; do not modify -#include <common/triggers/misc/corner.qh> -#include <common/triggers/misc/follow.qh> -#include <common/triggers/misc/include.qh> -#include <common/triggers/misc/laser.qh> -#include <common/triggers/misc/teleport_dest.qh> diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc deleted file mode 100644 index 6c9093318f..0000000000 --- a/qcsrc/common/triggers/misc/corner.qc +++ /dev/null @@ -1,86 +0,0 @@ -#include "corner.qh" -REGISTER_NET_LINKED(ENT_CLIENT_CORNER) - -#ifdef SVQC -bool corner_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_CORNER); - - WriteString(MSG_ENTITY, this.platmovetype); - - WriteVector(MSG_ENTITY, this.origin); - - WriteString(MSG_ENTITY, this.target); - WriteString(MSG_ENTITY, this.target2); - WriteString(MSG_ENTITY, this.target3); - WriteString(MSG_ENTITY, this.target4); - WriteString(MSG_ENTITY, this.targetname); - WriteByte(MSG_ENTITY, this.target_random); - - WriteByte(MSG_ENTITY, this.wait); - - return true; -} - -void corner_link(entity this) -{ - //Net_LinkEntity(this, false, 0, corner_send); -} - -spawnfunc(path_corner) -{ - // setup values for overriding train movement - // if a second value does not exist, both start and end speeds are the single value specified - set_platmovetype(this, this.platmovetype); - - corner_link(this); -} -#elif defined(CSQC) - -void corner_remove(entity this) -{ - if(this.target) { strunzone(this.target); } - this.target = string_null; - - if(this.target2) { strunzone(this.target2); } - this.target2 = string_null; - - if(this.target3) { strunzone(this.target3); } - this.target3 = string_null; - - if(this.target4) { strunzone(this.target4); } - this.target4 = string_null; - - if(this.targetname) { strunzone(this.targetname); } - this.targetname = string_null; - - if(this.platmovetype) { strunzone(this.platmovetype); } - this.platmovetype = string_null; -} - -NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) -{ - this.platmovetype = strzone(ReadString()); - - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.target = strzone(ReadString()); - this.target2 = strzone(ReadString()); - this.target3 = strzone(ReadString()); - this.target4 = strzone(ReadString()); - this.targetname = strzone(ReadString()); - this.target_random = ReadByte(); - - this.wait = ReadByte(); - - return = true; - - this.classname = "path_corner"; - this.drawmask = MASK_NORMAL; - this.entremove = corner_remove; - - set_platmovetype(this, this.platmovetype); -} - -#endif diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/misc/corner.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc deleted file mode 100644 index 63db2c18fa..0000000000 --- a/qcsrc/common/triggers/misc/follow.qc +++ /dev/null @@ -1,70 +0,0 @@ -#include "follow.qh" -// the way this entity works makes it no use to CSQC, as it removes itself instantly - -#ifdef SVQC -void follow_init(entity this) -{ - entity src, dst; - src = NULL; - dst = NULL; - if(this.killtarget != "") - src = find(NULL, targetname, this.killtarget); - if(this.target != "") - dst = find(NULL, targetname, this.target); - - if(!src && !dst) - { - objerror(this, "follow: could not find target/killtarget"); - return; - } - - if(this.jointtype) - { - // already done :P entity must stay - this.aiment = src; - this.enemy = dst; - } - else if(!src || !dst) - { - objerror(this, "follow: could not find target/killtarget"); - return; - } - else if(this.spawnflags & 1) - { - // attach - if(this.spawnflags & 2) - { - setattachment(dst, src, this.message); - } - else - { - attach_sameorigin(dst, src, this.message); - } - - dst.solid = SOLID_NOT; // solid doesn't work with attachment - delete(this); - } - else - { - if(this.spawnflags & 2) - { - set_movetype(dst, MOVETYPE_FOLLOW); - dst.aiment = src; - // dst.punchangle = '0 0 0'; // keep unchanged - dst.view_ofs = dst.origin; - dst.v_angle = dst.angles; - } - else - { - follow_sameorigin(dst, src); - } - - delete(this); - } -} - -spawnfunc(misc_follow) -{ - InitializeEntity(this, follow_init, INITPRIO_FINDTARGET); -} -#endif diff --git a/qcsrc/common/triggers/misc/follow.qh b/qcsrc/common/triggers/misc/follow.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/misc/follow.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc deleted file mode 100644 index bbe5ce03d6..0000000000 --- a/qcsrc/common/triggers/misc/include.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "include.qh" -#include "corner.qc" -#include "follow.qc" -#include "laser.qc" -#include "teleport_dest.qc" diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/misc/include.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc deleted file mode 100644 index 3d5ef42d84..0000000000 --- a/qcsrc/common/triggers/misc/laser.qc +++ /dev/null @@ -1,375 +0,0 @@ -#include "laser.qh" -#if defined(CSQC) - #include <lib/csqcmodel/interpolate.qh> - #include <client/main.qh> - #include <lib/csqcmodel/cl_model.qh> -#elif defined(MENUQC) -#elif defined(SVQC) -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_LASER) - -#ifdef SVQC -.float modelscale; -void misc_laser_aim(entity this) -{ - vector a; - if(this.enemy) - { - if(this.spawnflags & 2) - { - if(this.enemy.origin != this.mangle) - { - this.mangle = this.enemy.origin; - this.SendFlags |= 2; - } - } - else - { - a = vectoangles(this.enemy.origin - this.origin); - a_x = -a_x; - if(a != this.mangle) - { - this.mangle = a; - this.SendFlags |= 2; - } - } - } - else - { - if(this.angles != this.mangle) - { - this.mangle = this.angles; - this.SendFlags |= 2; - } - } - if(this.origin != this.oldorigin) - { - this.SendFlags |= 1; - this.oldorigin = this.origin; - } -} - -void misc_laser_init(entity this) -{ - if(this.target != "") - this.enemy = find(NULL, targetname, this.target); -} - -.entity pusher; -void misc_laser_think(entity this) -{ - vector o; - entity hitent; - vector hitloc; - - this.nextthink = time; - - if(!this.state) - return; - - misc_laser_aim(this); - - if(this.enemy) - { - o = this.enemy.origin; - if (!(this.spawnflags & 2)) - o = this.origin + normalize(o - this.origin) * 32768; - } - else - { - makevectors(this.mangle); - o = this.origin + v_forward * 32768; - } - - if(this.dmg || this.enemy.target != "") - { - traceline(this.origin, o, MOVE_NORMAL, this); - } - hitent = trace_ent; - hitloc = trace_endpos; - - if(this.enemy.target != "") // DETECTOR laser - { - if(trace_ent.iscreature) - { - this.pusher = hitent; - if(!this.count) - { - this.count = 1; - - SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); - } - } - else - { - if(this.count) - { - this.count = 0; - - SUB_UseTargets(this.enemy, this.enemy.pusher, NULL); - } - } - } - - if(this.dmg) - { - if(this.team) - if(((this.spawnflags & 8) == 0) == (this.team != hitent.team)) - return; - if(hitent.takedamage) - Damage(hitent, this, this, ((this.dmg < 0) ? 100000 : (this.dmg * frametime)), DEATH_HURTTRIGGER.m_id, DMG_NOWEP, hitloc, '0 0 0'); - } -} - -bool laser_SendEntity(entity this, entity to, float fl) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_LASER); - fl = fl - (fl & 0xF0); // use that bit to indicate finite length laser - if(this.spawnflags & 2) - fl |= 0x80; - if(this.alpha) - fl |= 0x40; - if(this.scale != 1 || this.modelscale != 1) - fl |= 0x20; - if(this.spawnflags & 4) - fl |= 0x10; - WriteByte(MSG_ENTITY, fl); - if(fl & 1) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(fl & 8) - { - WriteByte(MSG_ENTITY, this.colormod_x * 255.0); - WriteByte(MSG_ENTITY, this.colormod_y * 255.0); - WriteByte(MSG_ENTITY, this.colormod_z * 255.0); - if(fl & 0x40) - WriteByte(MSG_ENTITY, this.alpha * 255.0); - if(fl & 0x20) - { - WriteByte(MSG_ENTITY, bound(0, this.scale * 16.0, 255)); - WriteByte(MSG_ENTITY, bound(0, this.modelscale * 16.0, 255)); - } - if((fl & 0x80) || !(fl & 0x10)) // effect doesn't need sending if the laser is infinite and has collision testing turned off - WriteShort(MSG_ENTITY, this.cnt + 1); - } - if(fl & 2) - { - if(fl & 0x80) - { - WriteVector(MSG_ENTITY, this.enemy.origin); - } - else - { - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - } - } - if(fl & 4) - WriteByte(MSG_ENTITY, this.state); - return 1; -} - -/*QUAKED spawnfunc_misc_laser (.5 .5 .5) ? START_ON DEST_IS_FIXED -Any object touching the beam will be hurt -Keys: -"target" - spawnfunc_target_position where the laser ends -"mdl" - name of beam end effect to use -"colormod" - color of the beam (default: red) -"dmg" - damage per second (-1 for a laser that kills immediately) -*/ -void laser_use(entity this, entity actor, entity trigger) -{ - this.state = !this.state; - this.SendFlags |= 4; - misc_laser_aim(this); -} - -void laser_reset(entity this) -{ - if(this.spawnflags & 1) - this.state = 1; - else - this.state = 0; -} - -spawnfunc(misc_laser) -{ - if(this.mdl) - { - if(this.mdl == "none") - this.cnt = -1; - else - { - this.cnt = _particleeffectnum(this.mdl); - if(this.cnt < 0 && this.dmg) - this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); - } - } - else if(!this.cnt) - { - if(this.dmg) - this.cnt = particleeffectnum(EFFECT_LASER_DEADLY); - else - this.cnt = -1; - } - if(this.cnt < 0) - this.cnt = -1; - - if(this.colormod == '0 0 0') - if(!this.alpha) - this.colormod = '1 0 0'; - if(this.message == "") this.message = "saw the light"; - if (this.message2 == "") this.message2 = "was pushed into a laser by"; - if(!this.scale) this.scale = 1; - if(!this.modelscale) this.modelscale = 1; - else if(this.modelscale < 0) this.modelscale = 0; - setthink(this, misc_laser_think); - this.nextthink = time; - InitializeEntity(this, misc_laser_init, INITPRIO_FINDTARGET); - - this.mangle = this.angles; - - Net_LinkEntity(this, false, 0, laser_SendEntity); - - IFTARGETED - { - this.reset = laser_reset; - this.reset(this); - this.use = laser_use; - } - else - this.state = 1; -} -#elif defined(CSQC) - -// a laser goes from origin in direction angles -// it has color 'colormod' -// and stops when something is in the way -entityclass(Laser); -class(Laser) .int cnt; // end effect -class(Laser) .vector colormod; -class(Laser) .int state; // on-off -class(Laser) .int count; // flags for the laser -class(Laser) .vector velocity; -class(Laser) .float alpha; -class(Laser) .float scale; // scaling factor of the thickness -class(Laser) .float modelscale; // scaling factor of the dlight - -void Draw_Laser(entity this) -{ - if(!this.state) - return; - InterpolateOrigin_Do(this); - if(this.count & 0x80) - { - if(this.count & 0x10) - { - trace_endpos = this.velocity; - trace_dphitq3surfaceflags = 0; - } - else - traceline(this.origin, this.velocity, 0, this); - } - else - { - if(this.count & 0x10) - { - makevectors(this.angles); - trace_endpos = this.origin + v_forward * 1048576; - trace_dphitq3surfaceflags = Q3SURFACEFLAG_SKY; - } - else - { - makevectors(this.angles); - traceline(this.origin, this.origin + v_forward * 32768, 0, this); - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - trace_endpos = this.origin + v_forward * 1048576; - } - } - if(this.scale != 0) - { - if(this.alpha) - { - Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.colormod, this.alpha, DRAWFLAG_NORMAL, view_origin); - } - else - { - Draw_CylindricLine(this.origin, trace_endpos, this.scale, "particles/laserbeam", 0, time * 3, this.colormod, 0.5, DRAWFLAG_ADDITIVE, view_origin); - } - } - if (!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) - { - if(this.cnt >= 0) - __pointparticles(this.cnt, trace_endpos, trace_plane_normal, drawframetime * 1000); - if(this.colormod != '0 0 0' && this.modelscale != 0) - adddynamiclight(trace_endpos + trace_plane_normal * 1, this.modelscale, this.colormod * 5); - } -} - -NET_HANDLE(ENT_CLIENT_LASER, bool isnew) -{ - InterpolateOrigin_Undo(this); - - // 30 bytes, or 13 bytes for just moving - int f = ReadByte(); - this.count = (f & 0xF0); - - if(this.count & 0x80) - this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; - else - this.iflags = IFLAG_ANGLES | IFLAG_ORIGIN; - - if(f & 1) - { - this.origin = ReadVector(); - setorigin(this, this.origin); - } - if(f & 8) - { - this.colormod_x = ReadByte() / 255.0; - this.colormod_y = ReadByte() / 255.0; - this.colormod_z = ReadByte() / 255.0; - if(f & 0x40) - this.alpha = ReadByte() / 255.0; - else - this.alpha = 0; - this.scale = 2; - this.modelscale = 50; - if(f & 0x20) - { - this.scale *= ReadByte() / 16.0; // beam radius - this.modelscale *= ReadByte() / 16.0; // dlight radius - } - if((f & 0x80) || !(f & 0x10)) - this.cnt = ReadShort() - 1; // effect number - else - this.cnt = 0; - } - if(f & 2) - { - if(f & 0x80) - { - this.velocity = ReadVector(); - } - else - { - this.angles_x = ReadAngle(); - this.angles_y = ReadAngle(); - } - } - if(f & 4) - this.state = ReadByte(); - - return = true; - - InterpolateOrigin_Note(this); - this.draw = Draw_Laser; - if (isnew) IL_PUSH(g_drawables, this); -} -#endif diff --git a/qcsrc/common/triggers/misc/laser.qh b/qcsrc/common/triggers/misc/laser.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/misc/laser.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc deleted file mode 100644 index 40c7d46c42..0000000000 --- a/qcsrc/common/triggers/misc/teleport_dest.qc +++ /dev/null @@ -1,94 +0,0 @@ -#include "teleport_dest.qh" -REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) - -#ifdef SVQC - -bool teleport_dest_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TELEPORT_DEST); - WriteByte(MSG_ENTITY, sf); - - if(sf & 1) - { - WriteByte(MSG_ENTITY, this.cnt); - WriteCoord(MSG_ENTITY, this.speed); - WriteString(MSG_ENTITY, this.targetname); - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.mangle_x); - WriteAngle(MSG_ENTITY, this.mangle_y); - WriteAngle(MSG_ENTITY, this.mangle_z); - } - - return true; -} - -void teleport_dest_link(entity this) -{ - Net_LinkEntity(this, false, 0, teleport_dest_send); - this.SendFlags |= 1; // update -} - -spawnfunc(info_teleport_destination) -{ - this.classname = "info_teleport_destination"; - - this.mangle = this.angles; - this.angles = '0 0 0'; - - //setorigin(this, this.origin + '0 0 27'); // To fix a mappers' habit as old as Quake - setorigin(this, this.origin); - - IFTARGETED - { - } - else - objerror (this, "^3Teleport destination without a targetname"); - - teleport_dest_link(this); -} - -spawnfunc(misc_teleporter_dest) -{ - spawnfunc_info_teleport_destination(this); -} - -#elif defined(CSQC) - -void teleport_dest_remove(entity this) -{ - //if(this.classname) - //strunzone(this.classname); - //this.classname = string_null; - - if(this.targetname) - strunzone(this.targetname); - this.targetname = string_null; -} - -NET_HANDLE(ENT_CLIENT_TELEPORT_DEST, bool isnew) -{ - int sf = ReadByte(); - - if(sf & 1) - { - this.classname = "info_teleport_destination"; - this.cnt = ReadByte(); - this.speed = ReadCoord(); - this.targetname = strzone(ReadString()); - this.origin = ReadVector(); - - this.mangle_x = ReadAngle(); - this.mangle_y = ReadAngle(); - this.mangle_z = ReadAngle(); - - setorigin(this, this.origin); - - this.drawmask = MASK_NORMAL; - this.entremove = teleport_dest_remove; - } - - return = true; -} - -#endif diff --git a/qcsrc/common/triggers/misc/teleport_dest.qh b/qcsrc/common/triggers/misc/teleport_dest.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/misc/teleport_dest.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc deleted file mode 100644 index 8ea48275d3..0000000000 --- a/qcsrc/common/triggers/platforms.qc +++ /dev/null @@ -1,225 +0,0 @@ -#include "platforms.qh" -void generic_plat_blocked(entity this, entity blocker) -{ -#ifdef SVQC - if(this.dmg && blocker.takedamage != DAMAGE_NO) - { - if(this.dmgtime2 < time) - { - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - this.dmgtime2 = time + this.dmgtime; - } - - // Gib dead/dying stuff - if(IS_DEAD(blocker)) - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif -} - -void plat_spawn_inside_trigger(entity this) -{ - entity trigger; - vector tmin, tmax; - - trigger = spawn(); - settouch(trigger, plat_center_touch); - set_movetype(trigger, MOVETYPE_NONE); - trigger.solid = SOLID_TRIGGER; - trigger.enemy = this; - - tmin = this.absmin + '25 25 0'; - tmax = this.absmax - '25 25 -8'; - tmin_z = tmax_z - (this.pos1_z - this.pos2_z + 8); - if (this.spawnflags & PLAT_LOW_TRIGGER) - tmax_z = tmin_z + 8; - - if (this.size_x <= 50) - { - tmin_x = (this.mins_x + this.maxs_x) / 2; - tmax_x = tmin_x + 1; - } - if (this.size_y <= 50) - { - tmin_y = (this.mins_y + this.maxs_y) / 2; - tmax_y = tmin_y + 1; - } - - if(tmin_x < tmax_x) - if(tmin_y < tmax_y) - if(tmin_z < tmax_z) - { - setsize (trigger, tmin, tmax); - return; - } - - // otherwise, something is fishy... - delete(trigger); - objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); -} - -void plat_hit_top(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = 1; - - setthink(this, plat_go_down); - this.nextthink = this.ltime + 3; -} - -void plat_hit_bottom(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise1, VOL_BASE, ATTEN_NORM); - this.state = 2; -} - -void plat_go_down(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); - this.state = 3; - SUB_CalcMove (this, this.pos2, TSPEED_LINEAR, this.speed, plat_hit_bottom); -} - -void plat_go_up(entity this) -{ - _sound (this, CH_TRIGGER_SINGLE, this.noise, VOL_BASE, ATTEN_NORM); - this.state = 4; - SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top); -} - -void plat_center_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - - if (toucher.health <= 0) - return; -#elif defined(CSQC) - if (!IS_PLAYER(toucher)) - return; - if(IS_DEAD(toucher)) - return; -#endif - - if (this.enemy.state == 2) { - plat_go_up(this.enemy); - } else if (this.enemy.state == 1) - this.enemy.nextthink = this.enemy.ltime + 1; -} - -void plat_outside_touch(entity this, entity toucher) -{ -#ifdef SVQC - if (!toucher.iscreature) - return; - - if (toucher.health <= 0) - return; -#elif defined(CSQC) - if (!IS_PLAYER(toucher)) - return; -#endif - - if (this.enemy.state == 1) { - entity e = this.enemy; - plat_go_down(e); - } -} - -void plat_trigger_use(entity this, entity actor, entity trigger) -{ - if (getthink(this)) - return; // already activated - plat_go_down(this); -} - - -void plat_crush(entity this, entity blocker) -{ - if((this.spawnflags & 4) && (blocker.takedamage != DAMAGE_NO)) - { // KIll Kill Kill!! -#ifdef SVQC - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); -#endif - } - else - { -#ifdef SVQC - if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) - { // Shall we bite? - Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - // Gib dead/dying stuff - if(IS_DEAD(blocker)) - Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, blocker.origin, '0 0 0'); - } -#endif - - if (this.state == 4) - plat_go_down (this); - else if (this.state == 3) - plat_go_up (this); - // when in other states, then the plat_crush event came delayed after - // plat state already had changed - // this isn't a bug per se! - } -} - -void plat_use(entity this, entity actor, entity trigger) -{ - this.use = func_null; - if (this.state != 4) - objerror (this, "plat_use: not in up state"); - plat_go_down(this); -} - -.string sound1, sound2; - -void plat_reset(entity this) -{ - IFTARGETED - { - setorigin(this, this.pos1); - this.state = 4; - this.use = plat_use; - } - else - { - setorigin(this, this.pos2); - this.state = 2; - this.use = plat_trigger_use; - } - -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_RESET; -#endif -} - -.float platmovetype_start_default, platmovetype_end_default; -bool set_platmovetype(entity e, string s) -{ - // sets platmovetype_start and platmovetype_end based on a string consisting of two values - - int n = tokenize_console(s); - if(n > 0) - e.platmovetype_start = stof(argv(0)); - else - e.platmovetype_start = 0; - - if(n > 1) - e.platmovetype_end = stof(argv(1)); - else - e.platmovetype_end = e.platmovetype_start; - - if(n > 2) - if(argv(2) == "force") - return true; // no checking, return immediately - - if(!cubic_speedfunc_is_sane(e.platmovetype_start, e.platmovetype_end)) - { - objerror(e, "Invalid platform move type; platform would go in reverse, which is not allowed."); - return false; - } - - return true; -} diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh deleted file mode 100644 index c40467494f..0000000000 --- a/qcsrc/common/triggers/platforms.qh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -.float dmgtime2; - -void plat_center_touch(entity this, entity toucher); -void plat_outside_touch(entity this, entity toucher); -void plat_trigger_use(entity this, entity actor, entity trigger); -void plat_go_up(entity this); -void plat_go_down(entity this); -void plat_crush(entity this, entity blocker); -const float PLAT_LOW_TRIGGER = 1; - -.float dmg; diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc deleted file mode 100644 index 5b6182e0ab..0000000000 --- a/qcsrc/common/triggers/subs.qc +++ /dev/null @@ -1,361 +0,0 @@ -#include "subs.qh" -void SUB_NullThink(entity this) { } - -void SUB_CalcMoveDone(entity this); -void SUB_CalcAngleMoveDone(entity this); - -/* -================== -SUB_Friction - -Applies some friction to this -================== -*/ -.float friction; -void SUB_Friction (entity this) -{ - this.nextthink = time; - if(IS_ONGROUND(this)) - this.velocity = this.velocity * (1 - frametime * this.friction); -} - -/* -================== -SUB_VanishOrRemove - -Makes client invisible or removes non-client -================== -*/ -void SUB_VanishOrRemove (entity ent) -{ - if (IS_CLIENT(ent)) - { - // vanish - ent.alpha = -1; - ent.effects = 0; -#ifdef SVQC - ent.glow_size = 0; - ent.pflags = 0; -#endif - } - else - { - // remove - delete(ent); - } -} - -void SUB_SetFade_Think (entity this) -{ - if(this.alpha == 0) - this.alpha = 1; - setthink(this, SUB_SetFade_Think); - this.nextthink = time; - this.alpha -= frametime * this.fade_rate; - if (this.alpha < 0.01) - SUB_VanishOrRemove(this); - else - this.nextthink = time; -} - -/* -================== -SUB_SetFade - -Fade 'ent' out when time >= 'when' -================== -*/ -void SUB_SetFade (entity ent, float when, float fading_time) -{ - ent.fade_rate = 1/fading_time; - setthink(ent, SUB_SetFade_Think); - ent.nextthink = when; -} - -/* -============= -SUB_CalcMove - -calculate this.velocity and this.nextthink to reach dest from -this.origin traveling at speed -=============== -*/ -void SUB_CalcMoveDone(entity this) -{ - // After moving, set origin to exact final destination - - setorigin (this, this.finaldest); - this.velocity = '0 0 0'; - this.nextthink = -1; - if (this.think1 && this.think1 != SUB_CalcMoveDone) - this.think1 (this); -} - -.float platmovetype_turn; -void SUB_CalcMove_controller_think (entity this) -{ - float traveltime; - float phasepos; - float nexttick; - vector delta; - vector delta2; - vector veloc; - vector angloc; - vector nextpos; - delta = this.destvec; - delta2 = this.destvec2; - if(time < this.animstate_endtime) - { - nexttick = time + PHYS_INPUT_FRAMETIME; - - traveltime = this.animstate_endtime - this.animstate_starttime; - phasepos = (nexttick - this.animstate_starttime) / traveltime; // range: [0, 1] - phasepos = cubic_speedfunc(this.platmovetype_start, this.platmovetype_end, phasepos); - nextpos = this.origin + (delta * phasepos) + (delta2 * phasepos * phasepos); - // derivative: delta + 2 * delta2 * phasepos (e.g. for angle positioning) - - if(this.owner.platmovetype_turn) - { - vector destangle; - destangle = delta + 2 * delta2 * phasepos; - destangle = vectoangles(destangle); - destangle_x = -destangle_x; // flip up / down orientation - - // take the shortest distance for the angles - vector v = this.owner.angles; - v.x -= 360 * floor((v.x - destangle_x) / 360 + 0.5); - v.y -= 360 * floor((v.y - destangle_y) / 360 + 0.5); - v.z -= 360 * floor((v.z - destangle_z) / 360 + 0.5); - this.owner.angles = v; - angloc = destangle - this.owner.angles; - angloc = angloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame - this.owner.avelocity = angloc; - } - if(nexttick < this.animstate_endtime) - veloc = nextpos - this.owner.origin; - else - veloc = this.finaldest - this.owner.origin; - veloc = veloc * (1 / PHYS_INPUT_FRAMETIME); // so it arrives for the next frame - - this.owner.velocity = veloc; - this.nextthink = nexttick; - } - else - { - // derivative: delta + 2 * delta2 (e.g. for angle positioning) - entity own = this.owner; - setthink(own, this.think1); - delete(this); - getthink(own)(own); - } -} - -void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector destin) -{ - // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t - // 2 * control * t - 2 * control * t * t + destin * t * t - // 2 * control * t + (destin - 2 * control) * t * t - - setorigin(controller, org); - control -= org; - destin -= org; - - controller.destvec = 2 * control; // control point - controller.destvec2 = destin - 2 * control; // quadratic part required to reach end point - // also: initial d/dphasepos origin = 2 * control, final speed = 2 * (destin - control) -} - -void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector destin) -{ - // 0 * (1-t) * (1-t) + 2 * control * t * (1-t) + destin * t * t - // 2 * control * t - 2 * control * t * t + destin * t * t - // 2 * control * t + (destin - 2 * control) * t * t - - setorigin(controller, org); - destin -= org; - - controller.destvec = destin; // end point - controller.destvec2 = '0 0 0'; -} - -float TSPEED_TIME = -1; -float TSPEED_LINEAR = 0; -float TSPEED_START = 1; -float TSPEED_END = 2; -// TODO average too? - -void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - float traveltime; - entity controller; - - if (!tspeed) - objerror (this, "No speed is defined!"); - - this.think1 = func; - this.finaldest = tdest; - setthink(this, SUB_CalcMoveDone); - - switch(tspeedtype) - { - default: - case TSPEED_START: - traveltime = 2 * vlen(tcontrol - this.origin) / tspeed; - break; - case TSPEED_END: - traveltime = 2 * vlen(tcontrol - tdest) / tspeed; - break; - case TSPEED_LINEAR: - traveltime = vlen(tdest - this.origin) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - if (traveltime < 0.1) // useless anim - { - this.velocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - controller = new(SUB_CalcMove_controller); - controller.owner = this; - controller.platmovetype = this.platmovetype; - controller.platmovetype_start = this.platmovetype_start; - controller.platmovetype_end = this.platmovetype_end; - SUB_CalcMove_controller_setbezier(controller, this.origin, tcontrol, tdest); - controller.finaldest = (tdest + '0 0 0.125'); // where do we want to end? Offset to overshoot a bit. - controller.animstate_starttime = time; - controller.animstate_endtime = time + traveltime; - setthink(controller, SUB_CalcMove_controller_think); - controller.think1 = getthink(this); - - // the thinking is now done by the controller - setthink(this, SUB_NullThink); // for PushMove - this.nextthink = this.ltime + traveltime; - - // invoke controller - getthink(controller)(controller); -} - -void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - vector delta; - float traveltime; - - if (!tspeed) - objerror (this, "No speed is defined!"); - - this.think1 = func; - this.finaldest = tdest; - setthink(this, SUB_CalcMoveDone); - - if (tdest == this.origin) - { - this.velocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - delta = tdest - this.origin; - - switch(tspeedtype) - { - default: - case TSPEED_START: - case TSPEED_END: - case TSPEED_LINEAR: - traveltime = vlen (delta) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - // Very short animations don't really show off the effect - // of controlled animation, so let's just use linear movement. - // Alternatively entities can choose to specify non-controlled movement. - // The only currently implemented alternative movement is linear (value 1) - if (traveltime < 0.15 || (this.platmovetype_start == 1 && this.platmovetype_end == 1)) // is this correct? - { - this.velocity = delta * (1/traveltime); // QuakeC doesn't allow vector/float division - this.nextthink = this.ltime + traveltime; - return; - } - - // now just run like a bezier curve... - SUB_CalcMove_Bezier(this, (this.origin + tdest) * 0.5, tdest, tspeedtype, tspeed, func); -} - -void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func) -{ - SUB_CalcMove(ent, tdest, tspeedtype, tspeed, func); -} - -/* -============= -SUB_CalcAngleMove - -calculate this.avelocity and this.nextthink to reach destangle from -this.angles rotating - -The calling function should make sure this.setthink is valid -=============== -*/ -void SUB_CalcAngleMoveDone(entity this) -{ - // After rotating, set angle to exact final angle - this.angles = this.finalangle; - this.avelocity = '0 0 0'; - this.nextthink = -1; - if (this.think1 && this.think1 != SUB_CalcAngleMoveDone) // avoid endless loops - this.think1 (this); -} - -// FIXME: I fixed this function only for rotation around the main axes -void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func) -{ - if (!tspeed) - objerror (this, "No speed is defined!"); - - // take the shortest distance for the angles - this.angles_x -= 360 * floor((this.angles_x - destangle_x) / 360 + 0.5); - this.angles_y -= 360 * floor((this.angles_y - destangle_y) / 360 + 0.5); - this.angles_z -= 360 * floor((this.angles_z - destangle_z) / 360 + 0.5); - vector delta = destangle - this.angles; - float traveltime; - - switch(tspeedtype) - { - default: - case TSPEED_START: - case TSPEED_END: - case TSPEED_LINEAR: - traveltime = vlen (delta) / tspeed; - break; - case TSPEED_TIME: - traveltime = tspeed; - break; - } - - this.think1 = func; - this.finalangle = destangle; - setthink(this, SUB_CalcAngleMoveDone); - - if (traveltime < 0.1) - { - this.avelocity = '0 0 0'; - this.nextthink = this.ltime + 0.1; - return; - } - - this.avelocity = delta * (1 / traveltime); - this.nextthink = this.ltime + traveltime; -} - -void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func) -{ - SUB_CalcAngleMove (ent, destangle, tspeedtype, tspeed, func); -} diff --git a/qcsrc/common/triggers/subs.qh b/qcsrc/common/triggers/subs.qh deleted file mode 100644 index 1b3bc5e690..0000000000 --- a/qcsrc/common/triggers/subs.qh +++ /dev/null @@ -1,59 +0,0 @@ -#pragma once - -void SUB_SetFade (entity ent, float when, float fading_time); -void SUB_VanishOrRemove (entity ent); - -.vector finaldest, finalangle; //plat.qc stuff -.void(entity this) think1; -.float state; -.float t_length, t_width; - -.vector destvec; -.vector destvec2; - -.float delay; -.float wait; -.float lip; -.float speed; -.float sounds; -.string platmovetype; -.float platmovetype_start, platmovetype_end; - -//entity activator; - -.string killtarget; - -.vector pos1, pos2; -.vector mangle; - -.string target2; -.string target3; -.string target4; -.string curvetarget; -.float target_random; -.float trigger_reverse; - -// Keys player is holding -.float itemkeys; -// message delay for func_door locked by keys and key locks -// this field is used on player entities -.float key_door_messagetime; - -.vector dest1, dest2; - -#ifdef CSQC -// this stuff is defined in the server side engine VM, so we must define it separately here -.float takedamage; -const float DAMAGE_NO = 0; -const float DAMAGE_YES = 1; -const float DAMAGE_AIM = 2; - -float STATE_TOP = 0; -float STATE_BOTTOM = 1; -float STATE_UP = 2; -float STATE_DOWN = 3; - -.string noise, noise1, noise2, noise3; // contains names of wavs to play - -.float max_health; // players maximum health is stored here -#endif diff --git a/qcsrc/common/triggers/target/_mod.inc b/qcsrc/common/triggers/target/_mod.inc deleted file mode 100644 index afd1050b90..0000000000 --- a/qcsrc/common/triggers/target/_mod.inc +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include <common/triggers/target/changelevel.qc> -#include <common/triggers/target/include.qc> -#include <common/triggers/target/kill.qc> -#include <common/triggers/target/levelwarp.qc> -#include <common/triggers/target/location.qc> -#include <common/triggers/target/music.qc> -#include <common/triggers/target/spawn.qc> -#include <common/triggers/target/spawnpoint.qc> -#include <common/triggers/target/speaker.qc> -#include <common/triggers/target/voicescript.qc> diff --git a/qcsrc/common/triggers/target/_mod.qh b/qcsrc/common/triggers/target/_mod.qh deleted file mode 100644 index 7c9fbeec54..0000000000 --- a/qcsrc/common/triggers/target/_mod.qh +++ /dev/null @@ -1,11 +0,0 @@ -// generated file; do not modify -#include <common/triggers/target/changelevel.qh> -#include <common/triggers/target/include.qh> -#include <common/triggers/target/kill.qh> -#include <common/triggers/target/levelwarp.qh> -#include <common/triggers/target/location.qh> -#include <common/triggers/target/music.qh> -#include <common/triggers/target/spawn.qh> -#include <common/triggers/target/spawnpoint.qh> -#include <common/triggers/target/speaker.qh> -#include <common/triggers/target/voicescript.qh> diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc deleted file mode 100644 index 6c006d42a9..0000000000 --- a/qcsrc/common/triggers/target/changelevel.qc +++ /dev/null @@ -1,42 +0,0 @@ -#include "changelevel.qh" -#ifdef SVQC -.string chmap, gametype; -.entity chlevel_targ; - -void target_changelevel_use(entity this, entity actor, entity trigger) -{ - if(this.spawnflags & 2) - { - // simply don't react if a non-player triggers it - if(!IS_PLAYER(actor)) { return; } - - actor.chlevel_targ = this; - - int plnum = 0; - int realplnum = 0; - // let's not count bots - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - ++realplnum; - if(it.chlevel_targ == this) - ++plnum; - }); - if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players - return; - } - - if(this.gametype != "") - MapInfo_SwitchGameType(MapInfo_Type_FromString(this.gametype)); - - if (this.chmap == "") - localcmd("endmatch\n"); - else - localcmd(strcat("changelevel ", this.chmap, "\n")); -} - -spawnfunc(target_changelevel) -{ - this.use = target_changelevel_use; - - if(!this.count) { this.count = 0.7; } -} -#endif diff --git a/qcsrc/common/triggers/target/changelevel.qh b/qcsrc/common/triggers/target/changelevel.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/changelevel.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/include.qc b/qcsrc/common/triggers/target/include.qc deleted file mode 100644 index a45c65ed05..0000000000 --- a/qcsrc/common/triggers/target/include.qc +++ /dev/null @@ -1,11 +0,0 @@ -#include "include.qh" - -#include "changelevel.qc" -#include "kill.qc" -#include "levelwarp.qc" -#include "location.qc" -#include "music.qc" -#include "spawn.qc" -#include "spawnpoint.qc" -#include "speaker.qc" -#include "voicescript.qc" diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh deleted file mode 100644 index c0f7cad443..0000000000 --- a/qcsrc/common/triggers/target/include.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -#include "music.qh" diff --git a/qcsrc/common/triggers/target/kill.qc b/qcsrc/common/triggers/target/kill.qc deleted file mode 100644 index 2751c600ea..0000000000 --- a/qcsrc/common/triggers/target/kill.qc +++ /dev/null @@ -1,26 +0,0 @@ -#include "kill.qh" -#include "location.qh" -#ifdef SVQC - -void target_kill_use(entity this, entity actor, entity trigger) -{ - if(actor.takedamage == DAMAGE_NO) - return; - - if(!actor.iscreature && !actor.damagedbytriggers) - return; - - Damage(actor, this, trigger, 1000, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, actor.origin, '0 0 0'); -} - -spawnfunc(target_kill) -{ - this.classname = "target_kill"; - - if (this.message == "") - this.message = "was in the wrong place"; - - this.use = target_kill_use; -} - -#endif diff --git a/qcsrc/common/triggers/target/kill.qh b/qcsrc/common/triggers/target/kill.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/kill.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/levelwarp.qc b/qcsrc/common/triggers/target/levelwarp.qc deleted file mode 100644 index 6cef53d6e5..0000000000 --- a/qcsrc/common/triggers/target/levelwarp.qc +++ /dev/null @@ -1,19 +0,0 @@ -#include "levelwarp.qh" - -#ifdef SVQC -void target_levelwarp_use(entity this, entity actor, entity trigger) -{ - if(!autocvar_g_campaign) - return; // only in campaign - - if(this.cnt) - CampaignLevelWarp(this.cnt - 1); // specific level - else - CampaignLevelWarp(-1); // next level -} - -spawnfunc(target_levelwarp) -{ - this.use = target_levelwarp_use; -} -#endif diff --git a/qcsrc/common/triggers/target/levelwarp.qh b/qcsrc/common/triggers/target/levelwarp.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/levelwarp.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/location.qc b/qcsrc/common/triggers/target/location.qc deleted file mode 100644 index 5774f45f99..0000000000 --- a/qcsrc/common/triggers/target/location.qc +++ /dev/null @@ -1,25 +0,0 @@ -#include "location.qh" -#ifdef SVQC -void target_push_init(entity this); - -spawnfunc(target_location) -{ - this.classname = "target_location"; - // location name in netname - // eventually support: count, teamgame selectors, line of sight? - - target_push_init(this); - - IL_PUSH(g_locations, this); -} - -spawnfunc(info_location) -{ - this.classname = "target_location"; - this.message = this.netname; - - target_push_init(this); - - IL_PUSH(g_locations, this); -} -#endif diff --git a/qcsrc/common/triggers/target/location.qh b/qcsrc/common/triggers/target/location.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/location.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc deleted file mode 100644 index 7ec3351381..0000000000 --- a/qcsrc/common/triggers/target/music.qc +++ /dev/null @@ -1,319 +0,0 @@ -#include "music.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <common/constants.qh> - #include <common/net_linked.qh> - #include <server/constants.qh> - #include <server/defs.qh> -#endif - -REGISTER_NET_TEMP(TE_CSQC_TARGET_MUSIC) -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_MUSIC) - -#ifdef SVQC - -IntrusiveList g_targetmusic_list; -STATIC_INIT(g_targetmusic_list) { g_targetmusic_list = IL_NEW(); } - -// values: -// volume -// noise -// targetname -// lifetime -// fade_time -// fade_rate -// when triggered, the music is overridden for activator until lifetime (or forever, if lifetime is 0) -// when targetname is not set, THIS ONE is default -void target_music_sendto(entity this, int to, bool is) -{ - WriteHeader(to, TE_CSQC_TARGET_MUSIC); - WriteShort(to, etof(this)); - WriteByte(to, this.volume * 255.0 * is); - WriteByte(to, this.fade_time * 16.0); - WriteByte(to, this.fade_rate * 16.0); - WriteByte(to, this.lifetime); - WriteString(to, this.noise); -} -void target_music_reset(entity this) -{ - if (this.targetname == "") target_music_sendto(this, MSG_ALL, 1); -} -void target_music_kill() -{ - IL_EACH(g_targetmusic_list, true, - { - it.volume = 0; - if (it.targetname == "") - target_music_sendto(it, MSG_ALL, 1); - else - target_music_sendto(it, MSG_ALL, 0); - }); -} -void target_music_use(entity this, entity actor, entity trigger) -{ - if(!actor) - return; - if(IS_REAL_CLIENT(actor)) - { - msg_entity = actor; - target_music_sendto(this, MSG_ONE, 1); - } - FOREACH_CLIENT(IS_SPEC(it) && it.enemy == actor, { - msg_entity = it; - target_music_sendto(this, MSG_ONE, 1); - }); -} -spawnfunc(target_music) -{ - this.use = target_music_use; - this.reset = target_music_reset; - if(!this.volume) - this.volume = 1; - IL_PUSH(g_targetmusic_list, this); - if(this.targetname == "") - target_music_sendto(this, MSG_INIT, 1); - else - target_music_sendto(this, MSG_INIT, 0); -} -void TargetMusic_RestoreGame() -{ - IL_EACH(g_targetmusic_list, true, - { - if(it.targetname == "") - target_music_sendto(it, MSG_INIT, 1); - else - target_music_sendto(it, MSG_INIT, 0); - }); -} -// values: -// volume -// noise -// targetname -// fade_time -// spawnflags: -// 1 = START_OFF -// when triggered, it is disabled/enabled for everyone -bool trigger_music_SendEntity(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_MUSIC); - sf &= ~0x80; - if(this.cnt) - sf |= 0x80; - WriteByte(MSG_ENTITY, sf); - if(sf & 4) - { - WriteVector(MSG_ENTITY, this.origin); - } - if(sf & 1) - { - if(this.model != "null") - { - WriteShort(MSG_ENTITY, this.modelindex); - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - } - else - { - WriteShort(MSG_ENTITY, 0); - WriteVector(MSG_ENTITY, this.maxs); - } - WriteByte(MSG_ENTITY, this.volume * 255.0); - WriteByte(MSG_ENTITY, this.fade_time * 16.0); - WriteByte(MSG_ENTITY, this.fade_rate * 16.0); - WriteString(MSG_ENTITY, this.noise); - } - return 1; -} -void trigger_music_reset(entity this) -{ - this.cnt = !(this.spawnflags & 1); - this.SendFlags |= 0x80; -} -void trigger_music_use(entity this, entity actor, entity trigger) -{ - this.cnt = !this.cnt; - this.SendFlags |= 0x80; -} -spawnfunc(trigger_music) -{ - if(this.model != "") _setmodel(this, this.model); - if(!this.volume) this.volume = 1; - if(!this.modelindex) - { - setorigin(this, this.origin + this.mins); - setsize(this, '0 0 0', this.maxs - this.mins); - } - trigger_music_reset(this); - - this.use = trigger_music_use; - this.reset = trigger_music_reset; - - Net_LinkEntity(this, false, 0, trigger_music_SendEntity); -} -#elif defined(CSQC) - -entity TargetMusic_list; -STATIC_INIT(TargetMusic_list) -{ - TargetMusic_list = LL_NEW(); -} - -void TargetMusic_Advance() -{ - // run AFTER all the thinks! - entity best = music_default; - if (music_target && time < music_target.lifetime) best = music_target; - if (music_trigger) best = music_trigger; - LL_EACH(TargetMusic_list, it.noise, { - const float vol0 = (getsoundtime(it, CH_BGM_SINGLE) >= 0) ? it.lastvol : -1; - if (it == best) - { - // increase volume - it.state = (it.fade_time > 0) ? bound(0, it.state + frametime / it.fade_time, 1) : 1; - } - else - { - // decrease volume - it.state = (it.fade_rate > 0) ? bound(0, it.state - frametime / it.fade_rate, 1) : 0; - } - const float vol = it.state * it.volume * autocvar_bgmvolume; - if (vol != vol0) - { - if(vol0 < 0) - _sound(it, CH_BGM_SINGLE, it.noise, vol, ATTEN_NONE); // restart - else - _sound(it, CH_BGM_SINGLE, "", vol, ATTEN_NONE); - it.lastvol = vol; - } - }); - music_trigger = NULL; - bgmtime = (best) ? getsoundtime(best, CH_BGM_SINGLE) : gettime(GETTIME_CDTRACK); -} - -NET_HANDLE(TE_CSQC_TARGET_MUSIC, bool isNew) -{ - Net_TargetMusic(); - return true; -} - -void Net_TargetMusic() -{ - const int id = ReadShort(); - const float vol = ReadByte() / 255.0; - const float fai = ReadByte() / 16.0; - const float fao = ReadByte() / 16.0; - const float tim = ReadByte(); - const string noi = ReadString(); - - entity e = NULL; - LL_EACH(TargetMusic_list, it.count == id, { e = it; break; }); - if (!e) - { - LL_PUSH(TargetMusic_list, e = new_pure(TargetMusic)); - e.count = id; - } - if(e.noise != noi) - { - if(e.noise) - strunzone(e.noise); - e.noise = strzone(noi); - precache_sound(e.noise); - _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); - if(getsoundtime(e, CH_BGM_SINGLE) < 0) - { - LOG_TRACEF("Cannot initialize sound %s", e.noise); - strunzone(e.noise); - e.noise = string_null; - } - } - e.volume = vol; - e.fade_time = fai; - e.fade_rate = fao; - if(vol > 0) - { - if(tim == 0) - { - music_default = e; - if(!music_disabled) - { - e.state = 2; - cvar_settemp("music_playlist_index", "-1"); // don't use playlists - localcmd("cd stop\n"); // just in case - music_disabled = 1; - } - } - else - { - music_target = e; - e.lifetime = time + tim; - } - } -} - -void Ent_TriggerMusic_Think(entity this) -{ - if(WarpZoneLib_BoxTouchesBrush(view_origin, view_origin, this, NULL)) - { - music_trigger = this; - } - this.nextthink = time; -} - -void Ent_TriggerMusic_Remove(entity this) -{ - if(this.noise) - strunzone(this.noise); - this.noise = string_null; -} - -NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) -{ - int f = ReadByte(); - if(f & 4) - { - this.origin = ReadVector(); - } - if(f & 1) - { - this.modelindex = ReadShort(); - if(this.modelindex) - { - this.mins = ReadVector(); - this.maxs = ReadVector(); - } - else - { - this.mins = '0 0 0'; - this.maxs = ReadVector(); - } - - this.volume = ReadByte() / 255.0; - this.fade_time = ReadByte() / 16.0; - this.fade_rate = ReadByte() / 16.0; - string s = this.noise; - if(this.noise) - strunzone(this.noise); - this.noise = strzone(ReadString()); - if(this.noise != s) - { - precache_sound(this.noise); - _sound(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE); - if(getsoundtime(this, CH_BGM_SINGLE) < 0) - { - LOG_TRACEF("Cannot initialize sound %s", this.noise); - strunzone(this.noise); - this.noise = string_null; - } - } - } - - setorigin(this, this.origin); - setsize(this, this.mins, this.maxs); - this.cnt = 1; - setthink(this, Ent_TriggerMusic_Think); - this.nextthink = time; - return true; -} - -#endif diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh deleted file mode 100644 index 80b3684a4e..0000000000 --- a/qcsrc/common/triggers/target/music.qh +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -.float lifetime; - -#ifdef CSQC -float music_disabled; -entity music_default; -entity music_target; -entity music_trigger; -// FIXME also control bgmvolume here, to not require a target_music for the default track. - -entityclass(TargetMusic); -class(TargetMusic) .int state; -class(TargetMusic) .float lastvol; - -void TargetMusic_Advance(); - -void Net_TargetMusic(); - -void Ent_TriggerMusic_Think(entity this); - -void Ent_TriggerMusic_Remove(entity this); - -#elif defined(SVQC) -void target_music_kill(); -#endif diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc deleted file mode 100644 index 1464da9551..0000000000 --- a/qcsrc/common/triggers/target/spawn.qc +++ /dev/null @@ -1,341 +0,0 @@ -#include "spawn.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <common/util.qh> - #include <server/defs.qh> -#endif - -#ifdef SVQC - -// spawner entity -// "classname" "target_spawn" -// "message" "fieldname value fieldname value ..." -// "spawnflags" -// 1 = call the spawn function -// 2 = trigger on map load - -float target_spawn_initialized; -.void(entity this) target_spawn_spawnfunc; -float target_spawn_spawnfunc_field; -.entity target_spawn_activator; -.float target_spawn_id; -float target_spawn_count; - -void target_spawn_helper_setmodel(entity this) -{ - _setmodel(this, this.model); -} - -void target_spawn_helper_setsize(entity this) -{ - setsize(this, this.mins, this.maxs); -} - -void target_spawn_edit_entity(entity this, entity e, string msg, entity kt, entity t2, entity t3, entity t4, entity act, entity trigger) -{ - float i, n, valuefieldpos; - string key, value, valuefield, valueoffset, valueoffsetrandom; - entity valueent; - vector data, data2; - - n = tokenize_console(msg); - - for(i = 0; i < n-1; i += 2) - { - key = argv(i); - value = argv(i+1); - if(key == "$") - { - data.x = -1; - data.y = FIELD_STRING; - } - else - { - data = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", key))); - if(data.y == 0) // undefined field, i.e., invalid type - { - LOG_INFO("target_spawn: invalid/unknown entity key ", key, " specified, ignored!"); - continue; - } - } - if(substring(value, 0, 1) == "$") - { - value = substring(value, 1, strlen(value) - 1); - if(substring(value, 0, 1) == "$") - { - // deferred replacement - // do nothing - // useful for creating target_spawns with this! - } - else - { - // replace me! - valuefieldpos = strstrofs(value, "+", 0); - valueoffset = ""; - if(valuefieldpos != -1) - { - valueoffset = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); - value = substring(value, 0, valuefieldpos); - } - - valuefieldpos = strstrofs(valueoffset, "+", 0); - valueoffsetrandom = ""; - if(valuefieldpos != -1) - { - valueoffsetrandom = substring(valueoffset, valuefieldpos + 1, strlen(valueoffset) - valuefieldpos - 1); - valueoffset = substring(valueoffset, 0, valuefieldpos); - } - - valuefieldpos = strstrofs(value, ".", 0); - valuefield = ""; - if(valuefieldpos != -1) - { - valuefield = substring(value, valuefieldpos + 1, strlen(value) - valuefieldpos - 1); - value = substring(value, 0, valuefieldpos); - } - - if(value == "self") - { - valueent = this; - value = ""; - } - else if(value == "activator") - { - valueent = act; - value = ""; - } - else if(value == "other") - { - valueent = trigger; - value = ""; - } - else if(value == "pusher") - { - if(time < act.pushltime) - valueent = act.pusher; - else - valueent = NULL; - value = ""; - } - else if(value == "target") - { - valueent = e; - value = ""; - } - else if(value == "killtarget") - { - valueent = kt; - value = ""; - } - else if(value == "target2") - { - valueent = t2; - value = ""; - } - else if(value == "target3") - { - valueent = t3; - value = ""; - } - else if(value == "target4") - { - valueent = t4; - value = ""; - } - else if(value == "time") - { - valueent = NULL; - value = ftos(time); - } - else - { - LOG_INFO("target_spawn: invalid/unknown variable replacement ", value, " specified, ignored!"); - continue; - } - - if(valuefield == "") - { - if(value == "") - value = ftos(etof(valueent)); - } - else - { - if(value != "") - { - LOG_INFO("target_spawn: try to get a field of a non-entity, ignored!"); - continue; - } - data2 = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", valuefield))); - if(data2_y == 0) // undefined field, i.e., invalid type - { - LOG_INFO("target_spawn: invalid/unknown entity key replacement ", valuefield, " specified, ignored!"); - continue; - } - value = getentityfieldstring(data2_x, valueent); - } - - if(valueoffset != "") - { - switch(data.y) - { - case FIELD_STRING: - value = strcat(value, valueoffset); - break; - case FIELD_FLOAT: - value = ftos(stof(value) + stof(valueoffset)); - break; - case FIELD_VECTOR: - value = vtos(stov(value) + stov(valueoffset)); - break; - default: - LOG_INFO("target_spawn: only string, float and vector fields can do calculations, calculation ignored!"); - break; - } - } - - if(valueoffsetrandom != "") - { - switch(data.y) - { - case FIELD_FLOAT: - value = ftos(stof(value) + random() * stof(valueoffsetrandom)); - break; - case FIELD_VECTOR: - data2 = stov(valueoffsetrandom); - value = vtos(stov(value) + random() * data2_x * '1 0 0' + random() * data2_y * '0 1 0' + random() * data2_z * '0 0 1'); - break; - default: - LOG_INFO("target_spawn: only float and vector fields can do random calculations, calculation ignored!"); - break; - } - } - } - } - if(key == "$") - { - if(substring(value, 0, 1) == "_") - value = strcat("target_spawn_helper", value); - putentityfieldstring(target_spawn_spawnfunc_field, e, value); - - e.target_spawn_spawnfunc(e); - - // We called an external function, so we have to re-tokenize msg. - n = tokenize_console(msg); - } - else - { - if(data.y == FIELD_VECTOR) - value = strreplace("'", "", value); // why?!? - putentityfieldstring(data.x, e, value); - } - } -} - -void target_spawn_useon(entity e, entity this, entity actor, entity trigger) -{ - this.target_spawn_activator = actor; - target_spawn_edit_entity( - this, - e, - this.message, - find(NULL, targetname, this.killtarget), - find(NULL, targetname, this.target2), - find(NULL, targetname, this.target3), - find(NULL, targetname, this.target4), - actor, - trigger - ); -} - -bool target_spawn_cancreate(entity this) -{ - float c; - entity e; - - c = this.count; - if(c == 0) // no limit? - return true; - - ++c; // increase count to not include MYSELF - for(e = NULL; (e = findfloat(e, target_spawn_id, this.target_spawn_id)); --c) - ; - - // if c now is 0, we have AT LEAST the given count (maybe more), so don't spawn any more - if(c == 0) - return false; - return true; -} - -void target_spawn_use(entity this, entity actor, entity trigger) -{ - if(this.target == "") - { - // spawn new entity - if(!target_spawn_cancreate(this)) - return; - entity e = spawn(); - e.spawnfunc_checked = true; - target_spawn_useon(e, this, actor, trigger); - e.target_spawn_id = this.target_spawn_id; - } - else if(this.target == "*activator") - { - // edit entity - if(actor) - target_spawn_useon(actor, this, actor, trigger); - } - else - { - // edit entity - FOREACH_ENTITY_STRING(targetname, this.target, - { - target_spawn_useon(it, this, actor, trigger); - }); - } -} - -void target_spawn_spawnfirst(entity this) -{ - entity act = this.target_spawn_activator; - if(this.spawnflags & 2) - target_spawn_use(this, act, NULL); -} - -void initialize_field_db() -{ - if(!target_spawn_initialized) - { - float n, i; - string fn; - vector prev, next; - float ft; - - n = numentityfields(); - for(i = 0; i < n; ++i) - { - fn = entityfieldname(i); - ft = entityfieldtype(i); - next = i * '1 0 0' + ft * '0 1 0' + '0 0 1'; - prev = stov(db_get(TemporaryDB, strcat("/target_spawn/field/", fn))); - if(prev.y == 0) - { - db_put(TemporaryDB, strcat("/target_spawn/field/", fn), vtos(next)); - if(fn == "target_spawn_spawnfunc") - target_spawn_spawnfunc_field = i; - } - } - - target_spawn_initialized = 1; - } -} - -spawnfunc(target_spawn) -{ - initialize_field_db(); - this.use = target_spawn_use; - this.message = strzone(strreplace("'", "\"", this.message)); - this.target_spawn_id = ++target_spawn_count; - InitializeEntity(this, target_spawn_spawnfirst, INITPRIO_LAST); -} -#endif diff --git a/qcsrc/common/triggers/target/spawn.qh b/qcsrc/common/triggers/target/spawn.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/spawn.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/spawnpoint.qc b/qcsrc/common/triggers/target/spawnpoint.qc deleted file mode 100644 index fe1538551e..0000000000 --- a/qcsrc/common/triggers/target/spawnpoint.qc +++ /dev/null @@ -1,24 +0,0 @@ -#include "spawnpoint.qh" - -#ifdef SVQC -void target_spawnpoint_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - actor.spawnpoint_targ = this; -} - -void target_spawnpoint_reset(entity this) -{ - this.active = ACTIVE_ACTIVE; -} - -// TODO: persistent spawnflag? -spawnfunc(target_spawnpoint) -{ - this.active = ACTIVE_ACTIVE; - this.use = target_spawnpoint_use; - this.reset = target_spawnpoint_reset; -} -#endif diff --git a/qcsrc/common/triggers/target/spawnpoint.qh b/qcsrc/common/triggers/target/spawnpoint.qh deleted file mode 100644 index 2eeb8da625..0000000000 --- a/qcsrc/common/triggers/target/spawnpoint.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#ifdef SVQC -.entity spawnpoint_targ; -#endif diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc deleted file mode 100644 index af327b443b..0000000000 --- a/qcsrc/common/triggers/target/speaker.qc +++ /dev/null @@ -1,132 +0,0 @@ -#include "speaker.qh" -#ifdef SVQC -// TODO add a way to do looped sounds with sound(); then complete this entity -void target_speaker_use_off(entity this, entity actor, entity trigger); -void target_speaker_use_activator(entity this, entity actor, entity trigger) -{ - if (!IS_REAL_CLIENT(actor)) - return; - string snd; - if(substring(this.noise, 0, 1) == "*") - { - var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); - if(GetPlayerSoundSampleField_notFound) - snd = SND(Null); - else if(actor.(sample) == "") - snd = SND(Null); - else - { - tokenize_console(actor.(sample)); - float n; - n = stof(argv(1)); - if(n > 0) - snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization - else - snd = strcat(argv(0), ".wav"); // randomization - } - } - else - snd = this.noise; - msg_entity = actor; - soundto(MSG_ONE, this, CH_TRIGGER, snd, VOL_BASE * this.volume, this.atten); -} -void target_speaker_use_on(entity this, entity actor, entity trigger) -{ - string snd; - if(substring(this.noise, 0, 1) == "*") - { - var .string sample = GetVoiceMessageSampleField(substring(this.noise, 1, -1)); - if(GetPlayerSoundSampleField_notFound) - snd = SND(Null); - else if(actor.(sample) == "") - snd = SND(Null); - else - { - tokenize_console(actor.(sample)); - float n; - n = stof(argv(1)); - if(n > 0) - snd = strcat(argv(0), ftos(floor(random() * n + 1)), ".wav"); // randomization - else - snd = strcat(argv(0), ".wav"); // randomization - } - } - else - snd = this.noise; - _sound(this, CH_TRIGGER_SINGLE, snd, VOL_BASE * this.volume, this.atten); - if(this.spawnflags & 3) - this.use = target_speaker_use_off; -} -void target_speaker_use_off(entity this, entity actor, entity trigger) -{ - sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASE * this.volume, this.atten); - this.use = target_speaker_use_on; -} -void target_speaker_reset(entity this) -{ - if(this.spawnflags & 1) // LOOPED_ON - { - if(this.use == target_speaker_use_on) - target_speaker_use_on(this, NULL, NULL); - } - else if(this.spawnflags & 2) - { - if(this.use == target_speaker_use_off) - target_speaker_use_off(this, NULL, NULL); - } -} - -spawnfunc(target_speaker) -{ - // TODO: "*" prefix to sound file name - // TODO: wait and random (just, HOW? random is not a field) - if(this.noise) - precache_sound (this.noise); - - if(!this.atten && !(this.spawnflags & 4)) - { - IFTARGETED - this.atten = ATTEN_NORM; - else - this.atten = ATTEN_STATIC; - } - else if(this.atten < 0) - this.atten = 0; - - if(!this.volume) - this.volume = 1; - - IFTARGETED - { - if(this.spawnflags & 8) // ACTIVATOR - this.use = target_speaker_use_activator; - else if(this.spawnflags & 1) // LOOPED_ON - { - target_speaker_use_on(this, NULL, NULL); - this.reset = target_speaker_reset; - } - else if(this.spawnflags & 2) // LOOPED_OFF - { - this.use = target_speaker_use_on; - this.reset = target_speaker_reset; - } - else - this.use = target_speaker_use_on; - } - else if(this.spawnflags & 1) // LOOPED_ON - { - ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - delete(this); - } - else if(this.spawnflags & 2) // LOOPED_OFF - { - objerror(this, "This sound entity can never be activated"); - } - else - { - // Quake/Nexuiz fallback - ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - delete(this); - } -} -#endif diff --git a/qcsrc/common/triggers/target/speaker.qh b/qcsrc/common/triggers/target/speaker.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/speaker.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc deleted file mode 100644 index 6dfb568a8b..0000000000 --- a/qcsrc/common/triggers/target/voicescript.qc +++ /dev/null @@ -1,102 +0,0 @@ -#include "voicescript.qh" -#ifdef SVQC -.entity voicescript; // attached voice script -.float voicescript_index; // index of next voice, or -1 to use the randomized ones -.float voicescript_nextthink; // time to play next voice -.float voicescript_voiceend; // time when this voice ends - -void target_voicescript_clear(entity pl) -{ - pl.voicescript = NULL; -} - -void target_voicescript_use(entity this, entity actor, entity trigger) -{ - if(actor.voicescript != this) - { - actor.voicescript = this; - actor.voicescript_index = 0; - actor.voicescript_nextthink = time + this.delay; - } -} - -void target_voicescript_next(entity pl) -{ - entity vs; - float i, n, dt; - - vs = pl.voicescript; - if(!vs) - return; - if(vs.message == "") - return; - if (!IS_PLAYER(pl)) - return; - if(game_stopped) - return; - - if(time >= pl.voicescript_voiceend) - { - if(time >= pl.voicescript_nextthink) - { - // get the next voice... - n = tokenize_console(vs.message); - - if(pl.voicescript_index < vs.cnt) - i = pl.voicescript_index * 2; - else if(n > vs.cnt * 2) - i = ((pl.voicescript_index - vs.cnt) % ((n - vs.cnt * 2 - 1) / 2)) * 2 + vs.cnt * 2 + 1; - else - i = -1; - - if(i >= 0) - { - play2(pl, strcat(vs.netname, "/", argv(i), ".wav")); - dt = stof(argv(i + 1)); - if(dt >= 0) - { - pl.voicescript_voiceend = time + dt; - pl.voicescript_nextthink = pl.voicescript_voiceend + vs.wait * (0.5 + random()); - } - else - { - pl.voicescript_voiceend = time - dt; - pl.voicescript_nextthink = pl.voicescript_voiceend; - } - - pl.voicescript_index += 1; - } - else - { - pl.voicescript = NULL; // stop trying then - } - } - } -} - -spawnfunc(target_voicescript) -{ - // netname: directory of the sound files - // message: list of "sound file" duration "sound file" duration, a *, and again a list - // foo1 4.1 foo2 4.0 foo3 -3.1 * fool1 1.1 fool2 7.1 fool3 9.1 fool4 3.7 - // Here, a - in front of the duration means that no delay is to be - // added after this message - // wait: average time between messages - // delay: initial delay before the first message - - float i, n; - this.use = target_voicescript_use; - - n = tokenize_console(this.message); - this.cnt = n / 2; - for(i = 0; i+1 < n; i += 2) - { - if(argv(i) == "*") - { - this.cnt = i / 2; - ++i; - } - precache_sound(strcat(this.netname, "/", argv(i), ".wav")); - } -} -#endif diff --git a/qcsrc/common/triggers/target/voicescript.qh b/qcsrc/common/triggers/target/voicescript.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/target/voicescript.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc deleted file mode 100644 index 25626e01b1..0000000000 --- a/qcsrc/common/triggers/teleporters.qc +++ /dev/null @@ -1,315 +0,0 @@ -#include "teleporters.qh" - -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <lib/warpzone/common.qh> - #include <lib/warpzone/util_server.qh> - #include <lib/warpzone/server.qh> - #include "../constants.qh" - #include "../triggers/subs.qh" - #include "../util.qh" - #include <server/weapons/csqcprojectile.qh> - #include <server/autocvars.qh> - #include <server/constants.qh> - #include <server/defs.qh> - #include "../deathtypes/all.qh" - #include "../turrets/sv_turrets.qh" - #include "../vehicles/all.qh" - #include "../mapinfo.qh" - #include <server/anticheat.qh> -#endif - -#ifdef SVQC -float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax) -{ - if (IS_PLAYER(player) && !IS_DEAD(player)) - { - TDEATHLOOP(org) - { - #ifdef SVQC - if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) - #endif - if(IS_PLAYER(head)) - if(!IS_DEAD(head)) - return 1; - } - } - return 0; -} - -void trigger_teleport_link(entity this); - -void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax) -{ - TDEATHLOOP(player.origin) - { - if (IS_PLAYER(player) && player.health >= 1) - { - if (!(teamplay && autocvar_g_telefrags_teamplay && head.team == player.team)) - { - if(IS_PLAYER(head)) - if(head.health >= 1) - ++tdeath_hit; - Damage (head, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, head.origin, '0 0 0'); - } - } - else // dead bodies and monsters gib themselves instead of telefragging - Damage (telefragger, teleporter, telefragger, 10000, DEATH_TELEFRAG.m_id, DMG_NOWEP, telefragger.origin, '0 0 0'); - } -} - -void spawn_tdeath(vector v0, entity e, vector v) -{ - tdeath(e, e, e, '0 0 0', '0 0 0'); -} -#endif - -void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags) -{ - entity telefragger; - vector from; - - if(teleporter.owner) - telefragger = teleporter.owner; - else - telefragger = player; - - makevectors (to_angles); - -#ifdef SVQC - if(player.teleportable == TELEPORT_NORMAL) // don't play sounds or show particles for anything that isn't a player, maybe change later to block only observers - { - if(teleporter.pushltime < time) // only show one teleport effect per teleporter per 0.2 seconds, for better fps - { - if(tflags & TELEPORT_FLAG_SOUND) - { - string thesound = SND(TELEPORT); - if(teleporter.noise != "") - { - RandomSelection_Init(); - FOREACH_WORD(teleporter.noise, true, - { - RandomSelection_AddString(it, 1, 1); - }); - thesound = RandomSelection_chosen_string; - } - _sound (player, CH_TRIGGER, thesound, VOL_BASE, ATTEN_NORM); - } - if(tflags & TELEPORT_FLAG_PARTICLES) - { - Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); - Send_Effect(EFFECT_TELEPORT, to + v_forward * 32, '0 0 0', 1); - } - teleporter.pushltime = time + 0.2; - } - } -#endif - - // Relocate the player - // assuming to allows PL_MIN to PL_MAX box and some more -#ifdef SVQC - from = player.origin; - setorigin(player, to); - player.oldorigin = to; // don't undo the teleport by unsticking - player.angles = to_angles; - player.fixangle = true; - player.velocity = to_velocity; - BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); - - makevectors(player.angles); - Reset_ArcBeam(player, v_forward); - UpdateCSQCProjectileAfterTeleport(player); - UpdateItemAfterTeleport(player); -#elif defined(CSQC) - from = player.origin; - setorigin(player, to); - player.angles = to_angles; - player.velocity = to_velocity; - UNSET_ONGROUND(player); - player.iflags |= IFLAG_TELEPORTED | IFLAG_V_ANGLE | IFLAG_ANGLES; - player.csqcmodel_teleported = 1; - player.v_angle = to_angles; - - if(player == csqcplayer) // not for anything but the main player - { - setproperty(VF_ANGLES, player.angles); - setproperty(VF_CL_VIEWANGLES, player.angles); - } -#endif - -#ifdef SVQC - if(IS_PLAYER(player)) - { - if(tflags & TELEPORT_FLAG_TDEATH) - if(player.takedamage && !IS_DEAD(player) && !g_race && !g_cts && (autocvar_g_telefrags || (tflags & TELEPORT_FLAG_FORCE_TDEATH))) - tdeath(player, teleporter, telefragger, telefragmin, telefragmax); - - // player no longer is on ground - UNSET_ONGROUND(player); - - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - player.oldvelocity = player.velocity; - - // reset tracking of who pushed you into a hazard (for kill credit) - if(teleporter.owner) - { - player.pusher = teleporter.owner; - player.pushltime = time + autocvar_g_maxpushtime; - player.istypefrag = PHYS_INPUT_BUTTON_CHAT(player); - } - else - { - player.pushltime = 0; - player.istypefrag = 0; - } - - player.lastteleporttime = time; - player.lastteleport_origin = from; - } -#endif -} - -entity Simple_TeleportPlayer(entity teleporter, entity player) -{ - vector locout; - entity e = NULL; - - // Find the output teleporter - if(teleporter.enemy) - { - e = teleporter.enemy; - } - else - { - // sorry CSQC, random stuff ain't gonna happen -#ifdef SVQC - RandomSelection_Init(); - FOREACH_ENTITY_STRING(targetname, teleporter.target, - { - bool p = true; - if(STAT(TELEPORT_TELEFRAG_AVOID, player)) - { - #ifdef SVQC - locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); - #elif defined(CSQC) - locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); - #endif - if(check_tdeath(player, locout, '0 0 0', '0 0 0')) - p = false; - } - RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p); - }); - e = RandomSelection_chosen_ent; -#endif - } - -#ifdef SVQC - if(!e) { sprint(player, "Teleport destination vanished. Sorry... please complain to the mapper.\n"); } -#elif defined(CSQC) - if(!e) { LOG_INFO("Teleport destination could not be found from CSQC."); } -#endif - - makevectors(e.mangle); - - if(e.speed) - if(vdist(player.velocity, >, e.speed)) - player.velocity = normalize(player.velocity) * max(0, e.speed); - - if(STAT(TELEPORT_MAXSPEED, player)) - if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player))) - player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); - - locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); - - TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); - - return e; -} - -void teleport_findtarget(entity this) -{ - bool istrigger = (this.solid == SOLID_TRIGGER); - - int n = 0; - for(entity e = NULL; (e = find(e, targetname, this.target)); ) - { - ++n; -#ifdef SVQC - if(e.move_movetype == MOVETYPE_NONE) - { - entity tracetest_ent = spawn(); - setsize(tracetest_ent, PL_MIN_CONST, PL_MAX_CONST); - tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - waypoint_spawnforteleporter(this, e.origin, 0, tracetest_ent); - delete(tracetest_ent); - } - if(e.classname != "info_teleport_destination") - LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work."); -#endif - } - - if(n == 0) - { - // no dest! - objerror (this, "Teleporter with nonexistant target"); - return; - } - else if(n == 1) - { - // exactly one dest - bots love that - this.enemy = find(NULL, targetname, this.target); - } - else - { - // have to use random selection every single time - this.enemy = NULL; - } - - // now enable touch - if(istrigger) - settouch(this, Teleport_Touch); -#ifdef SVQC - if(istrigger) - trigger_teleport_link(this); -#endif -} - -entity Teleport_Find(vector mi, vector ma) -{ - IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), - { - return it; - }); - return NULL; -} - -void WarpZone_PostTeleportPlayer_Callback(entity pl) -{ -#ifdef SVQC - makevectors(pl.angles); - Reset_ArcBeam(pl, v_forward); - UpdateCSQCProjectileAfterTeleport(pl); - UpdateItemAfterTeleport(pl); - if (IS_PLAYER(pl)) anticheat_fixangle(pl); -#endif - // "disown" projectiles after teleport - if(pl.owner) - if(pl.owner == pl.realowner) - { - #ifdef SVQC - if(!(pl.flags & FL_PROJECTILE)) - #elif defined(CSQC) - if(!(pl.flags & BIT(15))) // FL_PROJECTILE - #endif - LOG_INFO("A non-projectile got through a warpzone and its owner cleared. It's a ", pl.classname, "."); - pl.owner = NULL; - } - if(IS_PLAYER(pl)) - { - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - #ifdef SVQC - pl.oldvelocity = pl.velocity; - #endif - } -} diff --git a/qcsrc/common/triggers/teleporters.qh b/qcsrc/common/triggers/teleporters.qh deleted file mode 100644 index 6f5b2b595d..0000000000 --- a/qcsrc/common/triggers/teleporters.qh +++ /dev/null @@ -1,70 +0,0 @@ -#pragma once - -IntrusiveList g_teleporters; -STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); } - -.entity pusher; -const float TELEPORT_FLAG_SOUND = 1; -const float TELEPORT_FLAG_PARTICLES = 2; -const float TELEPORT_FLAG_TDEATH = 4; -const float TELEPORT_FLAG_FORCE_TDEATH = 8; - -#define TELEPORT_FLAGS_WARPZONE 0 -#define TELEPORT_FLAGS_PORTAL (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH | TELEPORT_FLAG_FORCE_TDEATH) -#define TELEPORT_FLAGS_TELEPORTER (TELEPORT_FLAG_SOUND | TELEPORT_FLAG_PARTICLES | TELEPORT_FLAG_TDEATH) - -// types for .teleportable entity setting -const float TELEPORT_NORMAL = 1; // play sounds/effects etc -const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special - -entity Simple_TeleportPlayer(entity teleporter, entity player); - -void Teleport_Touch(entity this, entity toucher); - -void teleport_findtarget(entity this); - -entity Teleport_Find(vector mi, vector ma); - -void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity, vector telefragmin, vector telefragmax, float tflags); - -#ifdef SVQC - -void trigger_teleport_use(entity this, entity actor, entity trigger); - -#define TDEATHLOOP(o) \ - entity head; \ - vector deathmin; \ - vector deathmax; \ - float deathradius; \ - deathmin = (o) + player.mins; \ - deathmax = (o) + player.maxs; \ - if(telefragmin != telefragmax) \ - { \ - if(deathmin.x > telefragmin.x) deathmin.x = telefragmin.x; \ - if(deathmin.y > telefragmin.y) deathmin.y = telefragmin.y; \ - if(deathmin.z > telefragmin.z) deathmin.z = telefragmin.z; \ - if(deathmax.x < telefragmax.x) deathmax.x = telefragmax.x; \ - if(deathmax.y < telefragmax.y) deathmax.y = telefragmax.y; \ - if(deathmax.z < telefragmax.z) deathmax.z = telefragmax.z; \ - } \ - deathradius = max(vlen(deathmin), vlen(deathmax)); \ - for(head = findradius(o, deathradius); head; head = head.chain) \ - if(head != player) \ - if(head.takedamage) \ - if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax)) - -float check_tdeath(entity player, vector org, vector telefragmin, vector telefragmax); -float tdeath_hit; -void tdeath(entity player, entity teleporter, entity telefragger, vector telefragmin, vector telefragmax); - -void spawn_tdeath(vector v0, entity e, vector v); - -void Reset_ArcBeam(entity player, vector forward); - -#endif - -void WarpZone_PostTeleportPlayer_Callback(entity pl); - -#ifdef CSQC -.entity realowner; -#endif diff --git a/qcsrc/common/triggers/trigger/_mod.inc b/qcsrc/common/triggers/trigger/_mod.inc deleted file mode 100644 index 05a496eb39..0000000000 --- a/qcsrc/common/triggers/trigger/_mod.inc +++ /dev/null @@ -1,25 +0,0 @@ -// generated file; do not modify -#include <common/triggers/trigger/counter.qc> -#include <common/triggers/trigger/delay.qc> -#include <common/triggers/trigger/disablerelay.qc> -#include <common/triggers/trigger/flipflop.qc> -#include <common/triggers/trigger/gamestart.qc> -#include <common/triggers/trigger/gravity.qc> -#include <common/triggers/trigger/heal.qc> -#include <common/triggers/trigger/hurt.qc> -#include <common/triggers/trigger/impulse.qc> -#include <common/triggers/trigger/include.qc> -#include <common/triggers/trigger/jumppads.qc> -#include <common/triggers/trigger/keylock.qc> -#include <common/triggers/trigger/magicear.qc> -#include <common/triggers/trigger/monoflop.qc> -#include <common/triggers/trigger/multi.qc> -#include <common/triggers/trigger/multivibrator.qc> -#include <common/triggers/trigger/relay.qc> -#include <common/triggers/trigger/relay_activators.qc> -#include <common/triggers/trigger/relay_if.qc> -#include <common/triggers/trigger/relay_teamcheck.qc> -#include <common/triggers/trigger/secret.qc> -#include <common/triggers/trigger/swamp.qc> -#include <common/triggers/trigger/teleport.qc> -#include <common/triggers/trigger/viewloc.qc> diff --git a/qcsrc/common/triggers/trigger/_mod.qh b/qcsrc/common/triggers/trigger/_mod.qh deleted file mode 100644 index 2c7477b1d5..0000000000 --- a/qcsrc/common/triggers/trigger/_mod.qh +++ /dev/null @@ -1,25 +0,0 @@ -// generated file; do not modify -#include <common/triggers/trigger/counter.qh> -#include <common/triggers/trigger/delay.qh> -#include <common/triggers/trigger/disablerelay.qh> -#include <common/triggers/trigger/flipflop.qh> -#include <common/triggers/trigger/gamestart.qh> -#include <common/triggers/trigger/gravity.qh> -#include <common/triggers/trigger/heal.qh> -#include <common/triggers/trigger/hurt.qh> -#include <common/triggers/trigger/impulse.qh> -#include <common/triggers/trigger/include.qh> -#include <common/triggers/trigger/jumppads.qh> -#include <common/triggers/trigger/keylock.qh> -#include <common/triggers/trigger/magicear.qh> -#include <common/triggers/trigger/monoflop.qh> -#include <common/triggers/trigger/multi.qh> -#include <common/triggers/trigger/multivibrator.qh> -#include <common/triggers/trigger/relay.qh> -#include <common/triggers/trigger/relay_activators.qh> -#include <common/triggers/trigger/relay_if.qh> -#include <common/triggers/trigger/relay_teamcheck.qh> -#include <common/triggers/trigger/secret.qh> -#include <common/triggers/trigger/swamp.qh> -#include <common/triggers/trigger/teleport.qh> -#include <common/triggers/trigger/viewloc.qh> diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc deleted file mode 100644 index 87c046b0db..0000000000 --- a/qcsrc/common/triggers/trigger/counter.qc +++ /dev/null @@ -1,66 +0,0 @@ -#include "counter.qh" -#ifdef SVQC -void counter_reset(entity this); - -void counter_use(entity this, entity actor, entity trigger) -{ - this.count -= 1; - if (this.count < 0) - return; - - bool doactivate = (this.spawnflags & 4); - - if (this.count == 0) - { - if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COMPLETED); - - doactivate = true; - - if(this.respawntime) - { - setthink(this, counter_reset); - this.nextthink = time + this.respawntime; - } - } - else - { - if(IS_PLAYER(actor) && !(this.spawnflags & SPAWNFLAG_NOMESSAGE)) - { - if(this.count >= 4) - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER); - else - Send_Notification(NOTIF_ONE, actor, MSG_CENTER, CENTER_SEQUENCE_COUNTER_FEWMORE, this.count); - } - } - - if(doactivate) - SUB_UseTargets(this, actor, trigger); -} - -void counter_reset(entity this) -{ - setthink(this, func_null); - this.nextthink = 0; - this.count = this.cnt; -} - -/*QUAKED spawnfunc_trigger_counter (.5 .5 .5) ? nomessage -Acts as an intermediary for an action that takes multiple inputs. - -If nomessage is not set, it will print "1 more.. " etc when triggered and "sequence complete" when finished. - -If respawntime is set, it will re-enable itself after the time once the sequence has been completed - -After the counter has been triggered "count" times (default 2), it will fire all of it's targets and remove itself. -*/ -spawnfunc(trigger_counter) -{ - if (!this.count) - this.count = 2; - this.cnt = this.count; - - this.use = counter_use; - this.reset = counter_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/counter.qh b/qcsrc/common/triggers/trigger/counter.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/counter.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc deleted file mode 100644 index 2cd4cfd133..0000000000 --- a/qcsrc/common/triggers/trigger/delay.qc +++ /dev/null @@ -1,32 +0,0 @@ -#include "delay.qh" -#ifdef SVQC -void delay_delayeduse(entity this) -{ - SUB_UseTargets(this, this.enemy, this.goalentity); - this.enemy = this.goalentity = NULL; -} - -void delay_use(entity this, entity actor, entity trigger) -{ - this.enemy = actor; - this.goalentity = trigger; - setthink(this, delay_delayeduse); - this.nextthink = time + this.wait; -} - -void delay_reset(entity this) -{ - this.enemy = this.goalentity = NULL; - setthink(this, func_null); - this.nextthink = 0; -} - -spawnfunc(trigger_delay) -{ - if(!this.wait) - this.wait = 1; - - this.use = delay_use; - this.reset = delay_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/delay.qh b/qcsrc/common/triggers/trigger/delay.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/delay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc deleted file mode 100644 index eee61c9935..0000000000 --- a/qcsrc/common/triggers/trigger/disablerelay.qc +++ /dev/null @@ -1,29 +0,0 @@ -#include "disablerelay.qh" -#ifdef SVQC -void trigger_disablerelay_use(entity this, entity actor, entity trigger) -{ - int a = 0, b = 0; - - for(entity e = NULL; (e = find(e, targetname, this.target)); ) - { - if(e.use == SUB_UseTargets) - { - e.use = SUB_DontUseTargets; - ++a; - } - else if(e.use == SUB_DontUseTargets) - { - e.use = SUB_UseTargets; - ++b; - } - } - - if((!a) == (!b)) - LOG_INFO("Invalid use of trigger_disablerelay: ", ftos(a), " relays were on, ", ftos(b), " relays were off!"); -} - -spawnfunc(trigger_disablerelay) -{ - this.use = trigger_disablerelay_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/disablerelay.qh b/qcsrc/common/triggers/trigger/disablerelay.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/disablerelay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc deleted file mode 100644 index af212ff5a4..0000000000 --- a/qcsrc/common/triggers/trigger/flipflop.qc +++ /dev/null @@ -1,21 +0,0 @@ -#include "flipflop.qh" -#ifdef SVQC -/*QUAKED spawnfunc_trigger_flipflop (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ENABLED -"Flip-flop" trigger gate... lets only every second trigger event through -*/ -void flipflop_use(entity this, entity actor, entity trigger) -{ - this.state = !this.state; - if(this.state) - SUB_UseTargets(this, actor, trigger); -} - -spawnfunc(trigger_flipflop) -{ - if(this.spawnflags & 1) - this.state = 1; - this.use = flipflop_use; - this.reset = spawnfunc_trigger_flipflop; // perfect resetter -} - -#endif diff --git a/qcsrc/common/triggers/trigger/flipflop.qh b/qcsrc/common/triggers/trigger/flipflop.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/flipflop.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc deleted file mode 100644 index 72d76d1833..0000000000 --- a/qcsrc/common/triggers/trigger/gamestart.qc +++ /dev/null @@ -1,28 +0,0 @@ -#include "gamestart.qh" -#ifdef SVQC -void gamestart_use(entity this, entity actor, entity trigger) -{ - SUB_UseTargets(this, this, trigger); - delete(this); -} - -void gamestart_use_this(entity this) -{ - gamestart_use(this, NULL, NULL); -} - -spawnfunc(trigger_gamestart) -{ - this.use = gamestart_use; - this.reset2 = spawnfunc_trigger_gamestart; - - if(this.wait) - { - setthink(this, adaptor_think2use); - this.nextthink = game_starttime + this.wait; - } - else - InitializeEntity(this, gamestart_use_this, INITPRIO_FINDTARGET); -} - -#endif diff --git a/qcsrc/common/triggers/trigger/gamestart.qh b/qcsrc/common/triggers/trigger/gamestart.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/gamestart.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc deleted file mode 100644 index 3ea1562f08..0000000000 --- a/qcsrc/common/triggers/trigger/gravity.qc +++ /dev/null @@ -1,107 +0,0 @@ -#include "gravity.qh" -#ifdef SVQC -.entity trigger_gravity_check; -void trigger_gravity_remove(entity own) -{ - if(own.trigger_gravity_check.owner == own) - { - UpdateCSQCProjectile(own); - own.gravity = own.trigger_gravity_check.gravity; - delete(own.trigger_gravity_check); - } - else - backtrace("Removing a trigger_gravity_check with no valid owner"); - own.trigger_gravity_check = NULL; -} -void trigger_gravity_check_think(entity this) -{ - // This spawns when a player enters the gravity zone and checks if he left. - // Each frame, this.count is set to 2 by trigger_gravity_touch() and decreased by 1 here. - // It the player has left the gravity trigger, this will be allowed to reach 0 and indicate that. - if(this.count <= 0) - { - if(this.owner.trigger_gravity_check == this) - trigger_gravity_remove(this.owner); - else - delete(this); - return; - } - else - { - this.count -= 1; - this.nextthink = time; - } -} - -void trigger_gravity_use(entity this, entity actor, entity trigger) -{ - this.state = !this.state; -} - -void trigger_gravity_touch(entity this, entity toucher) -{ - float g; - - if(this.state != true) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - g = this.gravity; - - if (!(this.spawnflags & 1)) - { - if(toucher.trigger_gravity_check) - { - if(this == toucher.trigger_gravity_check.enemy) - { - // same? - toucher.trigger_gravity_check.count = 2; // gravity one more frame... - return; - } - - // compare prio - if(this.cnt > toucher.trigger_gravity_check.enemy.cnt) - trigger_gravity_remove(toucher); - else - return; - } - toucher.trigger_gravity_check = spawn(); - toucher.trigger_gravity_check.enemy = this; - toucher.trigger_gravity_check.owner = toucher; - toucher.trigger_gravity_check.gravity = toucher.gravity; - setthink(toucher.trigger_gravity_check, trigger_gravity_check_think); - toucher.trigger_gravity_check.nextthink = time; - toucher.trigger_gravity_check.count = 2; - if(toucher.gravity) - g *= toucher.gravity; - } - - if (toucher.gravity != g) - { - toucher.gravity = g; - if(this.noise != "") - _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - UpdateCSQCProjectile(this.owner); - } -} - -spawnfunc(trigger_gravity) -{ - if(this.gravity == 1) - return; - - EXACTTRIGGER_INIT; - settouch(this, trigger_gravity_touch); - if(this.noise != "") - precache_sound(this.noise); - - this.state = true; - IFTARGETED - { - this.use = trigger_gravity_use; - if(this.spawnflags & 2) - this.state = false; - } -} -#endif diff --git a/qcsrc/common/triggers/trigger/gravity.qh b/qcsrc/common/triggers/trigger/gravity.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/gravity.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc deleted file mode 100644 index f2345e8f59..0000000000 --- a/qcsrc/common/triggers/trigger/heal.qc +++ /dev/null @@ -1,67 +0,0 @@ -#include "heal.qh" -#ifdef SVQC -.float triggerhealtime; -void trigger_heal_touch(entity this, entity toucher) -{ - if (this.active != ACTIVE_ACTIVE) - return; - - // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) - if (toucher.iscreature) - { - if (toucher.takedamage) - if (!IS_DEAD(toucher)) - if (toucher.triggerhealtime < time) - { - bool is_trigger = !boolean(!this.nottargeted && this.targetname != ""); - if(is_trigger) - EXACTTRIGGER_TOUCH(this, toucher); - if(this.delay > 0) - toucher.triggerhealtime = time + this.delay; - - bool playthesound = (this.spawnflags & 4); - if (toucher.health < this.max_health) - { - playthesound = true; - toucher.health = min(toucher.health + this.health, this.max_health); - toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - } - - if(playthesound) - _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - } - } -} - -void trigger_heal_use(entity this, entity actor, entity trigger) -{ - trigger_heal_touch(this, actor); -} - -void trigger_heal_init(entity this) -{ - this.active = ACTIVE_ACTIVE; - if(!this.delay) - this.delay = 1; - 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); -} - -spawnfunc(trigger_heal) -{ - EXACTTRIGGER_INIT; - settouch(this, trigger_heal_touch); - trigger_heal_init(this); -} - -spawnfunc(target_heal) -{ - this.use = trigger_heal_use; - trigger_heal_init(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/heal.qh b/qcsrc/common/triggers/trigger/heal.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/heal.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc deleted file mode 100644 index 9c09f923be..0000000000 --- a/qcsrc/common/triggers/trigger/hurt.qc +++ /dev/null @@ -1,93 +0,0 @@ -#include "hurt.qh" -#ifdef SVQC -void trigger_hurt_use(entity this, entity actor, entity trigger) -{ - if(IS_PLAYER(actor)) - this.enemy = actor; - else - this.enemy = NULL; // let's just destroy it, if taking over is too much work -} - -.float triggerhurttime; -void trigger_hurt_touch(entity this, entity toucher) -{ - if (this.active != ACTIVE_ACTIVE) - return; - - if(this.team) - if(((this.spawnflags & 4) == 0) == (this.team != toucher.team)) - return; - - // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) - if (toucher.iscreature) - { - if (toucher.takedamage) - if (toucher.triggerhurttime < time) - { - EXACTTRIGGER_TOUCH(this, toucher); - toucher.triggerhurttime = time + 1; - - entity own; - own = this.enemy; - if (!IS_PLAYER(own)) - { - own = this; - this.enemy = NULL; // I still hate you all - } - - Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); - } - } - else if(toucher.damagedbytriggers) - { - if(toucher.takedamage) - { - EXACTTRIGGER_TOUCH(this, toucher); - Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, toucher.origin, '0 0 0'); - } - } - - return; -} - -/*QUAKED spawnfunc_trigger_hurt (.5 .5 .5) ? -Any object touching this will be hurt -set dmg to damage amount -defalt dmg = 5 -*/ -.entity trigger_hurt_next; -entity trigger_hurt_last; -entity trigger_hurt_first; -spawnfunc(trigger_hurt) -{ - EXACTTRIGGER_INIT; - this.active = ACTIVE_ACTIVE; - settouch(this, trigger_hurt_touch); - this.use = trigger_hurt_use; - this.enemy = world; // I hate you all - if (!this.dmg) - this.dmg = 1000; - if (this.message == "") - this.message = "was in the wrong place"; - if (this.message2 == "") - this.message2 = "was thrown into a world of hurt by"; - // this.message = "someone like %s always gets wrongplaced"; - - if(!trigger_hurt_first) - trigger_hurt_first = this; - if(trigger_hurt_last) - trigger_hurt_last.trigger_hurt_next = this; - trigger_hurt_last = this; -} - -float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end) -{ - entity th; - - for(th = trigger_hurt_first; th; th = th.trigger_hurt_next) - if(tracebox_hits_box(start, mi, ma, end, th.absmin, th.absmax)) - return true; - - return false; -} -#endif diff --git a/qcsrc/common/triggers/trigger/hurt.qh b/qcsrc/common/triggers/trigger/hurt.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/hurt.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc deleted file mode 100644 index 4be6e86bca..0000000000 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ /dev/null @@ -1,212 +0,0 @@ -#include "impulse.qh" -// targeted (directional) mode -void trigger_impulse_touch1(entity this, entity toucher) -{ - entity targ; - float pushdeltatime; - float str; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - targ = find(NULL, targetname, this.target); - if(!targ) - { - objerror(this, "trigger_force without a (valid) .target!\n"); - delete(this); - return; - } - - str = min(this.radius, vlen(this.origin - toucher.origin)); - - if(this.falloff == 1) - str = (str / this.radius) * this.strength; - else if(this.falloff == 2) - str = (1 - (str / this.radius)) * this.strength; - else - str = this.strength; - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > 0.15) pushdeltatime = 0; - toucher.lastpushtime = time; - if(!pushdeltatime) return; - - if(this.spawnflags & 64) - { - float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin); - if (addspeed > 0) - { - float accelspeed = min(8 * pushdeltatime * str, addspeed); - toucher.velocity += accelspeed * normalize(targ.origin - this.origin); - } - } - else - toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; - - UNSET_ONGROUND(toucher); - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -// Directionless (accelerator/decelerator) mode -void trigger_impulse_touch2(entity this, entity toucher) -{ - float pushdeltatime; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > 0.15) pushdeltatime = 0; - toucher.lastpushtime = time; - if(!pushdeltatime) return; - - // div0: ticrate independent, 1 = identity (not 20) - toucher.velocity = toucher.velocity * (this.strength ** pushdeltatime); - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -// Spherical (gravity/repulsor) mode -void trigger_impulse_touch3(entity this, entity toucher) -{ - float pushdeltatime; - float str; - - if (this.active != ACTIVE_ACTIVE) - return; - - if (!isPushable(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - pushdeltatime = time - toucher.lastpushtime; - if (pushdeltatime > 0.15) pushdeltatime = 0; - toucher.lastpushtime = time; - if(!pushdeltatime) return; - - setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); - - str = min(this.radius, vlen(this.origin - toucher.origin)); - - if(this.falloff == 1) - str = (1 - str / this.radius) * this.strength; // 1 in the inside - else if(this.falloff == 2) - str = (str / this.radius) * this.strength; // 0 in the inside - else - str = this.strength; - - toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime; - -#ifdef SVQC - UpdateCSQCProjectile(toucher); -#endif -} - -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_IMPULSE) - -/*QUAKED spawnfunc_trigger_impulse (.5 .5 .5) ? --------- KEYS -------- -target : If this is set, this points to the spawnfunc_target_position to which the player will get pushed. - If not, this trigger acts like a damper/accelerator field. - -strength : This is how mutch force to add in the direction of .target each second - when .target is set. If not, this is hoe mutch to slow down/accelerate - someting cought inside this trigger. (1=no change, 0,5 half speed rougthly each tic, 2 = doubble) - -radius : If set, act as a spherical device rather then a liniar one. - -falloff : 0 = none, 1 = liniar, 2 = inverted liniar - --------- NOTES -------- -Use a brush textured with common/origin in the trigger entity to determine the origin of the force -in directional and sperical mode. For damper/accelerator mode this is not nessesary (and has no effect). -*/ -#ifdef SVQC -bool trigger_impulse_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_IMPULSE); - - WriteInt24_t(MSG_ENTITY, this.spawnflags); - WriteCoord(MSG_ENTITY, this.radius); - WriteCoord(MSG_ENTITY, this.strength); - WriteByte(MSG_ENTITY, this.falloff); - WriteByte(MSG_ENTITY, this.active); - - trigger_common_write(this, true); - - return true; -} - -void trigger_impulse_link(entity this) -{ - trigger_link(this, trigger_impulse_send); -} - -spawnfunc(trigger_impulse) -{ - this.active = ACTIVE_ACTIVE; - - trigger_init(this); - - if(this.radius) - { - if(!this.strength) this.strength = 2000 * autocvar_g_triggerimpulse_radial_multiplier; - setorigin(this, this.origin); - setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); - settouch(this, trigger_impulse_touch3); - } - else - { - if(this.target) - { - if(!this.strength) this.strength = 950 * autocvar_g_triggerimpulse_directional_multiplier; - settouch(this, trigger_impulse_touch1); - } - else - { - if(!this.strength) this.strength = 0.9; - this.strength = (this.strength ** autocvar_g_triggerimpulse_accel_power) * autocvar_g_triggerimpulse_accel_multiplier; - settouch(this, trigger_impulse_touch2); - } - } - - trigger_impulse_link(this); -} -#elif defined(CSQC) -NET_HANDLE(ENT_CLIENT_TRIGGER_IMPULSE, bool isnew) -{ - this.spawnflags = ReadInt24_t(); - this.radius = ReadCoord(); - this.strength = ReadCoord(); - this.falloff = ReadByte(); - this.active = ReadByte(); - - trigger_common_read(this, true); - return = true; - - this.classname = "trigger_impulse"; - this.solid = SOLID_TRIGGER; - this.entremove = trigger_remove_generic; - this.move_time = time; - - if (this.radius) { settouch(this, trigger_impulse_touch3); } - else if (this.target) { settouch(this, trigger_impulse_touch1); } - else { settouch(this, trigger_impulse_touch2); } -} -#endif diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh deleted file mode 100644 index a6961f5d2e..0000000000 --- a/qcsrc/common/triggers/trigger/impulse.qh +++ /dev/null @@ -1,7 +0,0 @@ -#pragma once - -// tZorks trigger impulse / gravity -.float radius; -.float falloff; -.float strength; -.float lastpushtime; diff --git a/qcsrc/common/triggers/trigger/include.qc b/qcsrc/common/triggers/trigger/include.qc deleted file mode 100644 index 1c762fc35f..0000000000 --- a/qcsrc/common/triggers/trigger/include.qc +++ /dev/null @@ -1,25 +0,0 @@ -#include "include.qh" - -#include "counter.qc" -#include "delay.qc" -#include "disablerelay.qc" -#include "flipflop.qc" -#include "gamestart.qc" -#include "gravity.qc" -#include "heal.qc" -#include "hurt.qc" -#include "impulse.qc" -#include "jumppads.qc" -#include "keylock.qc" -#include "magicear.qc" -#include "monoflop.qc" -#include "multi.qc" -#include "multivibrator.qc" -#include "relay.qc" -#include "relay_activators.qc" -#include "relay_if.qc" -#include "relay_teamcheck.qc" -#include "secret.qc" -#include "swamp.qc" -#include "teleport.qc" -#include "viewloc.qc" diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh deleted file mode 100644 index 8aa6b2b172..0000000000 --- a/qcsrc/common/triggers/trigger/include.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "multi.qh" -#include "jumppads.qh" -#include "secret.qh" -#include "swamp.qh" -#include "keylock.qh" -#include "impulse.qh" -#include "viewloc.qh" diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc deleted file mode 100644 index fde6e1fb9d..0000000000 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ /dev/null @@ -1,636 +0,0 @@ -#include "jumppads.qh" -// TODO: split target_push and put it in the target folder -#ifdef SVQC -#include "jumppads.qh" -#include <common/physics/movetypes/movetypes.qh> - -void trigger_push_use(entity this, entity actor, entity trigger) -{ - if(teamplay) - { - this.team = actor.team; - this.SendFlags |= 2; - } -} -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_PUSH) -REGISTER_NET_LINKED(ENT_CLIENT_TARGET_PUSH) - -/* - trigger_push_calculatevelocity - - Arguments: - org - origin of the object which is to be pushed - tgt - target entity (can be either a point or a model entity; if it is - the latter, its midpoint is used) - ht - jump height, measured from the higher one of org and tgt's midpoint - pushed_entity - object that is to be pushed - - Returns: velocity for the jump - */ -vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity) -{ - float grav, sdist, zdist, vs, vz, jumpheight; - vector sdir, torg; - - torg = tgt.origin + (tgt.mins + tgt.maxs) * 0.5; - - grav = PHYS_GRAVITY(NULL); - if(pushed_entity && PHYS_ENTGRAVITY(pushed_entity)) - grav *= PHYS_ENTGRAVITY(pushed_entity); - - zdist = torg.z - org.z; - sdist = vlen(torg - org - zdist * '0 0 1'); - sdir = normalize(torg - org - zdist * '0 0 1'); - - // how high do we need to push the player? - jumpheight = fabs(ht); - if(zdist > 0) - jumpheight = jumpheight + zdist; - - /* - STOP. - - You will not understand the following equations anyway... - But here is what I did to get them. - - I used the functions - - s(t) = t * vs - z(t) = t * vz - 1/2 grav t^2 - - and solved for: - - s(ti) = sdist - z(ti) = zdist - max(z, ti) = jumpheight - - From these three equations, you will find the three parameters vs, vz - and ti. - */ - - // push him so high... - vz = sqrt(fabs(2 * grav * jumpheight)); // NOTE: sqrt(positive)! - - // we start with downwards velocity only if it's a downjump and the jump apex should be outside the jump! - if(ht < 0) - if(zdist < 0) - vz = -vz; - - vector solution; - solution = solve_quadratic(0.5 * grav, -vz, zdist); // equation "z(ti) = zdist" - // ALWAYS solvable because jumpheight >= zdist - if(!solution.z) - solution_y = solution.x; // just in case it is not solvable due to roundoff errors, assume two equal solutions at their center (this is mainly for the usual case with ht == 0) - if(zdist == 0) - solution_x = solution.y; // solution_x is 0 in this case, so don't use it, but rather use solution_y (which will be sqrt(0.5 * jumpheight / grav), actually) - - float flighttime; - if(zdist < 0) - { - // down-jump - if(ht < 0) - { - // almost straight line type - // jump apex is before the jump - // we must take the larger one - flighttime = solution.y; - } - else - { - // regular jump - // jump apex is during the jump - // we must take the larger one too - flighttime = solution.y; - } - } - else - { - // up-jump - if(ht < 0) - { - // almost straight line type - // jump apex is after the jump - // we must take the smaller one - flighttime = solution.x; - } - else - { - // regular jump - // jump apex is during the jump - // we must take the larger one - flighttime = solution.y; - } - } - vs = sdist / flighttime; - - // finally calculate the velocity - return sdir * vs + '0 0 1' * vz; -} - -bool jumppad_push(entity this, entity targ) -{ - if (!isPushable(targ)) - return false; - - if(this.enemy) - { - targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height, targ); - } - else if(this.target && this.target != "") - { - entity e; - RandomSelection_Init(); - for(e = NULL; (e = find(e, targetname, this.target)); ) - { - if(e.cnt) - RandomSelection_AddEnt(e, e.cnt, 1); - else - RandomSelection_AddEnt(e, 1, 1); - } - targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height, targ); - } - else - { - targ.velocity = this.movedir; - } - - UNSET_ONGROUND(targ); - -#ifdef CSQC - if (targ.flags & FL_PROJECTILE) - { - targ.angles = vectoangles (targ.velocity); - switch(targ.move_movetype) - { - case MOVETYPE_FLY: - set_movetype(targ, MOVETYPE_TOSS); - targ.gravity = 1; - break; - case MOVETYPE_BOUNCEMISSILE: - set_movetype(targ, MOVETYPE_BOUNCE); - targ.gravity = 1; - break; - } - } -#endif - -#ifdef SVQC - if (IS_PLAYER(targ)) - { - // reset tracking of oldvelocity for impact damage (sudden velocity changes) - targ.oldvelocity = targ.velocity; - - if(this.pushltime < time) // prevent "snorring" sound when a player hits the jumppad more than once - { - // flash when activated - Send_Effect(EFFECT_JUMPPAD, targ.origin, targ.velocity, 1); - _sound (targ, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - this.pushltime = time + 0.2; - } - if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ)) - { - bool found = false; - for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i) - if(targ.(jumppadsused[i]) == this) - found = true; - if(!found) - { - targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this; - targ.jumppadcount = targ.jumppadcount + 1; - } - - if(IS_REAL_CLIENT(targ)) - { - if(this.message) - centerprint(targ, this.message); - } - else - { - targ.lastteleporttime = time; - targ.lastteleport_origin = targ.origin; - } - - if (!IS_DEAD(targ)) - animdecide_setaction(targ, ANIMACTION_JUMP, true); - } - else - targ.jumppadcount = 1; - - // reset tracking of who pushed you into a hazard (for kill credit) - targ.pushltime = 0; - targ.istypefrag = 0; - } - - if(this.enemy.target) - SUB_UseTargets(this.enemy, targ, this); - - if (targ.flags & FL_PROJECTILE) - { - targ.angles = vectoangles (targ.velocity); - targ.com_phys_gravity_factor = 1; - switch(targ.move_movetype) - { - case MOVETYPE_FLY: - set_movetype(targ, MOVETYPE_TOSS); - targ.gravity = 1; - break; - case MOVETYPE_BOUNCEMISSILE: - set_movetype(targ, MOVETYPE_BOUNCE); - targ.gravity = 1; - break; - } - UpdateCSQCProjectile(targ); - } -#endif - - return true; -} - -void trigger_push_touch(entity this, entity toucher) -{ - if (this.active == ACTIVE_NOT) - return; - - if(this.team) - if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher))) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - noref bool success = jumppad_push(this, toucher); - -#ifdef SVQC - if (success && (this.spawnflags & PUSH_ONCE)) - { - settouch(this, func_null); - setthink(this, SUB_Remove); - this.nextthink = time; - } -#endif -} - -#ifdef SVQC -void trigger_push_link(entity this); -void trigger_push_updatelink(entity this); -bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org) -{ - setorigin(tracetest_ent, org); - tracetoss(tracetest_ent, tracetest_ent); - if(trace_startsolid) - return false; - - if (!jp.height) - { - // since tracetoss starting from jumppad's origin often fails when target - // is very close to real destination, start it directly from target's - // origin instead - vector ofs = '0 0 0'; - if (vdist(vec2(tracetest_ent.velocity), <, autocvar_sv_maxspeed)) - ofs = stepheightvec; - - tracetest_ent.velocity.z = 0; - setorigin(tracetest_ent, targ.origin + ofs); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid && ofs.z) - { - setorigin(tracetest_ent, targ.origin + ofs / 2); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid && ofs.z) - { - setorigin(tracetest_ent, targ.origin); - tracetoss(tracetest_ent, tracetest_ent); - if (trace_startsolid) - return false; - } - } - } - tracebox(trace_endpos, tracetest_ent.mins, tracetest_ent.maxs, trace_endpos - eZ * 1500, true, tracetest_ent); - return true; -} -#endif - -/// if (item != NULL) returns true if the item can be reached by using this jumppad, false otherwise -/// if (item == NULL) tests jumppad's trajectory and eventually spawns waypoints for it (return value doesn't matter) -bool trigger_push_test(entity this, entity item) -{ - // first calculate a typical start point for the jump - vector org = (this.absmin + this.absmax) * 0.5; - org.z = this.absmax.z - PL_MIN_CONST.z - 10; - - if (this.target) - { - int n = 0; -#ifdef SVQC - vector vel = '0 0 0'; -#endif - for(entity t = NULL; (t = find(t, targetname, this.target)); ) - { - ++n; -#ifdef SVQC - if(t.move_movetype != MOVETYPE_NONE) - continue; - - entity e = spawn(); - setsize(e, PL_MIN_CONST, PL_MAX_CONST); - e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - e.velocity = trigger_push_calculatevelocity(org, t, this.height, e); - - if(item) - { - setorigin(e, org); - tracetoss(e, e); - bool r = (trace_ent == item); - delete(e); - return r; - } - - vel = e.velocity; - vector best_target = '0 0 0'; - vector best_org = '0 0 0'; - vector best_vel = '0 0 0'; - bool valid_best_target = false; - if (trigger_push_testorigin(e, t, this, org)) - { - best_target = trace_endpos; - best_org = org; - best_vel = e.velocity; - valid_best_target = true; - } - - vector new_org; - vector dist = t.origin - org; - if (dist.x || dist.y) // if not perfectly vertical - { - // test trajectory with different starting points, sometimes the trajectory - // starting from the jumppad origin can't reach the real destination - // and destination waypoint ends up near the jumppad itself - vector flatdir = normalize(dist - eZ * dist.z); - vector ofs = flatdir * 0.5 * min(fabs(this.absmax.x - this.absmin.x), fabs(this.absmax.y - this.absmin.y)); - new_org = org + ofs; - e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e); - vel = e.velocity; - if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed)) - e.velocity = autocvar_sv_maxspeed * flatdir; - if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50)) - { - best_target = trace_endpos; - best_org = new_org; - best_vel = vel; - valid_best_target = true; - } - new_org = org - ofs; - e.velocity = trigger_push_calculatevelocity(new_org, t, this.height, e); - vel = e.velocity; - if (vdist(vec2(e.velocity), <, autocvar_sv_maxspeed)) - e.velocity = autocvar_sv_maxspeed * flatdir; - if (trigger_push_testorigin(e, t, this, new_org) && (!valid_best_target || trace_endpos.z > best_target.z + 50)) - { - best_target = trace_endpos; - best_org = new_org; - best_vel = vel; - valid_best_target = true; - } - } - - if (valid_best_target) - { - if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, best_target + PL_MIN_CONST, best_target + PL_MAX_CONST))) - { - float velxy = vlen(vec2(best_vel)); - float cost = vlen(vec2(t.origin - best_org)) / velxy; - if(velxy < autocvar_sv_maxspeed) - velxy = autocvar_sv_maxspeed; - cost += vlen(vec2(best_target - t.origin)) / velxy; - waypoint_spawnforteleporter(this, best_target, cost, e); - } - } - delete(e); -#endif - } - - if(item) - return false; - - if(!n) - { - // no dest! -#ifdef SVQC - objerror (this, "Jumppad with nonexistant target"); -#endif - return false; - } - else if(n == 1) - { - // exactly one dest - bots love that - this.enemy = find(NULL, targetname, this.target); - } - else - { - // have to use random selection every single time - this.enemy = NULL; - } - } -#ifdef SVQC - else - { - entity e = spawn(); - setsize(e, PL_MIN_CONST, PL_MAX_CONST); - e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - setorigin(e, org); - e.velocity = this.movedir; - tracetoss(e, e); - if(item) - { - bool r = (trace_ent == item); - delete(e); - return r; - } - if (!(boxesoverlap(this.absmin, this.absmax + eZ * 50, trace_endpos + PL_MIN_CONST, trace_endpos + PL_MAX_CONST))) - waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity), e); - delete(e); - } - - defer(this, 0.1, trigger_push_updatelink); -#endif - return true; -} - -void trigger_push_findtarget(entity this) -{ - trigger_push_test(this, NULL); -} - -#ifdef SVQC -float trigger_push_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_PUSH); - - WriteByte(MSG_ENTITY, this.team); - WriteInt24_t(MSG_ENTITY, this.spawnflags); - WriteByte(MSG_ENTITY, this.active); - WriteCoord(MSG_ENTITY, this.height); - - WriteVector(MSG_ENTITY, this.movedir); - - trigger_common_write(this, true); - - return true; -} - -void trigger_push_updatelink(entity this) -{ - this.SendFlags |= 1; -} - -void trigger_push_link(entity this) -{ - trigger_link(this, trigger_push_send); -} - -/* - * ENTITY PARAMETERS: - * - * target: target of jump - * height: the absolute value is the height of the highest point of the jump - * trajectory above the higher one of the player and the target. - * the sign indicates whether the highest point is INSIDE (positive) - * or OUTSIDE (negative) of the jump trajectory. General rule: use - * positive values for targets mounted on the floor, and use negative - * values to target a point on the ceiling. - * movedir: if target is not set, this * speed * 10 is the velocity to be reached. - */ -spawnfunc(trigger_push) -{ - SetMovedir(this); - - trigger_init(this); - - this.active = ACTIVE_ACTIVE; - this.use = trigger_push_use; - settouch(this, trigger_push_touch); - - // normal push setup - if (!this.speed) - this.speed = 1000; - this.movedir = this.movedir * this.speed * 10; - - if (!this.noise) - this.noise = "misc/jumppad.wav"; - precache_sound (this.noise); - - trigger_push_link(this); // link it now - - IL_PUSH(g_jumppads, this); - - // this must be called to spawn the teleport waypoints for bots - InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); -} - - -bool target_push_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TARGET_PUSH); - - WriteByte(MSG_ENTITY, this.cnt); - WriteString(MSG_ENTITY, this.targetname); - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.angles_x); - WriteAngle(MSG_ENTITY, this.angles_y); - WriteAngle(MSG_ENTITY, this.angles_z); - - return true; -} - -void target_push_use(entity this, entity actor, entity trigger) -{ - if(trigger.classname == "trigger_push" || trigger == this) - return; // WTF, why is this a thing - - jumppad_push(this, actor); -} - -void target_push_link(entity this) -{ - BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); - Net_LinkEntity(this, false, 0, target_push_send); - //this.SendFlags |= 1; // update -} - -void target_push_init(entity this) -{ - this.mangle = this.angles; - setorigin(this, this.origin); - target_push_link(this); -} - -void target_push_init2(entity this) -{ - if(this.target && this.target != "") // we have an old style pusher! - { - InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); - this.use = target_push_use; - } - - target_push_init(this); // normal push target behaviour can be combined with a legacy pusher? -} - -spawnfunc(target_push) { target_push_init2(this); } -spawnfunc(info_notnull) { target_push_init(this); } -spawnfunc(target_position) { target_push_init(this); } - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_TRIGGER_PUSH, bool isnew) -{ - this.classname = "jumppad"; - int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; } - this.spawnflags = ReadInt24_t(); - this.active = ReadByte(); - this.height = ReadCoord(); - - this.movedir = ReadVector(); - - trigger_common_read(this, true); - - this.entremove = trigger_remove_generic; - this.solid = SOLID_TRIGGER; - settouch(this, trigger_push_touch); - this.move_time = time; - defer(this, 0.25, trigger_push_findtarget); - - return true; -} - -void target_push_remove(entity this) -{ - //if(this.classname) - //strunzone(this.classname); - //this.classname = string_null; - - if(this.targetname) - strunzone(this.targetname); - this.targetname = string_null; -} - -NET_HANDLE(ENT_CLIENT_TARGET_PUSH, bool isnew) -{ - this.classname = "push_target"; - this.cnt = ReadByte(); - this.targetname = strzone(ReadString()); - this.origin = ReadVector(); - - this.angles_x = ReadAngle(); - this.angles_y = ReadAngle(); - this.angles_z = ReadAngle(); - - return = true; - - setorigin(this, this.origin); - - this.drawmask = MASK_NORMAL; - this.entremove = target_push_remove; -} -#endif diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh deleted file mode 100644 index 50ed0a343c..0000000000 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once - -IntrusiveList g_jumppads; -STATIC_INIT(g_jumppads) { g_jumppads = IL_NEW(); } - -const float PUSH_ONCE = 1; -const float PUSH_SILENT = 2; - -.float pushltime; -.float istypefrag; -.float height; - -const int NUM_JUMPPADSUSED = 3; -.float jumppadcount; -.entity jumppadsused[NUM_JUMPPADSUSED]; - -#ifdef SVQC -void SUB_UseTargets(entity this, entity actor, entity trigger); -void trigger_push_use(entity this, entity actor, entity trigger); -bool trigger_push_testorigin(entity tracetest_ent, entity targ, entity jp, vector org); -#endif - -/* - trigger_push_calculatevelocity - - Arguments: - org - origin of the object which is to be pushed - tgt - target entity (can be either a point or a model entity; if it is - the latter, its midpoint is used) - ht - jump height, measured from the higher one of org and tgt's midpoint - pushed_entity - object that is to be pushed - - Returns: velocity for the jump - */ -vector trigger_push_calculatevelocity(vector org, entity tgt, float ht, entity pushed_entity); - -void trigger_push_touch(entity this, entity toucher); - -.vector dest; -bool trigger_push_test(entity this, entity item); -void trigger_push_findtarget(entity this); - -/* - * ENTITY PARAMETERS: - * - * target: target of jump - * height: the absolute value is the height of the highest point of the jump - * trajectory above the higher one of the player and the target. - * the sign indicates whether the highest point is INSIDE (positive) - * or OUTSIDE (negative) of the jump trajectory. General rule: use - * positive values for targets mounted on the floor, and use negative - * values to target a point on the ceiling. - * movedir: if target is not set, this * speed * 10 is the velocity to be reached. - */ -#ifdef SVQC -spawnfunc(trigger_push); - -spawnfunc(target_push); -spawnfunc(info_notnull); -spawnfunc(target_position); -#endif diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc deleted file mode 100644 index bf20d1e973..0000000000 --- a/qcsrc/common/triggers/trigger/keylock.qc +++ /dev/null @@ -1,198 +0,0 @@ -#include "keylock.qh" -/** - * trigger given targets - */ -void trigger_keylock_trigger(entity this, entity actor, string s) -{ - for(entity t = NULL; (t = find(t, targetname, s)); ) - if(t.use) - t.use(t, actor, this); -} - -/** - * kill killtarget of trigger keylock. - */ -void trigger_keylock_kill(string s) -{ - entity t; - for(t = NULL; (t = find(t, targetname, s)); ) - delete(t); -} - -void trigger_keylock_touch(entity this, entity toucher) -{ - bool key_used = false; - bool started_delay = false; - - // only player may trigger the lock - if(!IS_PLAYER(toucher)) - return; - - // check silver key - if(this.itemkeys) - key_used = item_keys_usekey(this, toucher); - - if(this.itemkeys) - { -#ifdef SVQC - // at least one of the keys is missing - if(key_used) - { - // one or more keys were given, but others are still missing! - play2(toucher, this.noise1); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys)); - toucher.key_door_messagetime = time + 2; - } - else if(toucher.key_door_messagetime <= time) - { - // no keys were given - play2(toucher, this.noise2); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys)); - toucher.key_door_messagetime = time + 2; - } -#endif - - // trigger target2 - if(this.delay <= time || started_delay == true) - if(this.target2) - { - trigger_keylock_trigger(this, toucher, this.target2); - started_delay = true; - this.delay = time + this.wait; - } - } - else - { -#ifdef SVQC - // all keys were given! - play2(toucher, this.noise); - centerprint(toucher, this.message); -#endif - - if(this.target) - trigger_keylock_trigger(this, toucher, this.target); - - if(this.killtarget) - trigger_keylock_kill(this.killtarget); - - delete(this); - } - -} - -REGISTER_NET_LINKED(ENT_CLIENT_KEYLOCK) - -#ifdef SVQC -bool trigger_keylock_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_KEYLOCK); - - WriteInt24_t(MSG_ENTITY, this.itemkeys); - WriteByte(MSG_ENTITY, this.height); - - trigger_common_write(this, true); - - return true; -} - -void trigger_keylock_link(entity this) -{ - // uncomment to network keylocks - //Net_LinkEntity(this, false, 0, trigger_keylock_send); -} - -/*QUAKED trigger_keylock (.0 .5 .8) ? -Keylock trigger. Must target other entities. -This trigger will trigger target entities when all required keys are provided. --------- KEYS -------- -itemkeys: A bit field with key IDs that are needed to open this lock. -sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (3 is default) -target: trigger all entities with this targetname when triggered and all keys have been given to it, then remove this trigger -target2: trigger all entities with this targetname when triggered without giving it all the required keys. -killtarget: remove all entities with this targetname when triggered with all the needed keys. -message: print this message to the player who activated the trigger when all needed keys have been given. -message2: print this message to the player who activated the trigger when not all of the needed keys have been given. -noise: sound to play when lock gets unlocked (default: see sounds) -noise1: sound to play when only some of the needed key were used but not all (default: misc/decreasevalue.wav) -noise2: sound to play when a key is missing (default: misc/talk.wav) -wait: prevent triggering again for this amount of time (default: 5) - applies to target2, target3, target4. ----------NOTES---------- -If spawned without any key specified in itemkeys, this trigger will display an error and remove itself. -message2 and noise2 will be resent to the player every 2 seconds while he is in the trigger zone. -*/ -spawnfunc(trigger_keylock) -{ - if(!this.itemkeys) { delete(this); return; } - - // set unlocked message - if(this.message == "") - this.message = "Unlocked!"; - - // set default unlock noise - if(this.noise == "") - { - if(this.sounds == 1) - this.noise = "misc/secret.wav"; - else if(this.sounds == 2) - this.noise = strzone(SND(TALK)); - else //if (this.sounds == 3) { - this.noise = "misc/trigger1.wav"; - } - - // set default use key sound - if(this.noise1 == "") - this.noise1 = "misc/decreasevalue.wav"; - - // set closed sourd - if(this.noise2 == "") - this.noise2 = SND(TALK); - - // delay between triggering message2 and trigger2 - if(!this.wait) { this.wait = 5; } - - // precache sounds - precache_sound(this.noise); - precache_sound(this.noise1); - precache_sound(this.noise2); - - EXACTTRIGGER_INIT; - - settouch(this, trigger_keylock_touch); - - trigger_keylock_link(this); -} -#elif defined(CSQC) -void keylock_remove(entity this) -{ - if(this.target) { strunzone(this.target); } - this.target = string_null; - - if(this.target2) { strunzone(this.target2); } - this.target2 = string_null; - - if(this.target3) { strunzone(this.target3); } - this.target3 = string_null; - - if(this.target4) { strunzone(this.target4); } - this.target4 = string_null; - - if(this.killtarget) { strunzone(this.killtarget); } - this.killtarget = string_null; - - if(this.targetname) { strunzone(this.targetname); } - this.targetname = string_null; -} - -NET_HANDLE(ENT_CLIENT_KEYLOCK, bool isnew) -{ - this.itemkeys = ReadInt24_t(); - this.height = ReadByte(); - - trigger_common_read(this, true); - - return = true; - - this.classname = "trigger_keylock"; - this.entremove = keylock_remove; -} -#endif diff --git a/qcsrc/common/triggers/trigger/keylock.qh b/qcsrc/common/triggers/trigger/keylock.qh deleted file mode 100644 index 904c3fa3d4..0000000000 --- a/qcsrc/common/triggers/trigger/keylock.qh +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once - -#ifdef CSQC -bool item_keys_usekey(entity l, entity p) -{ - int valid = (l.itemkeys & p.itemkeys); // TODO: itemkeys isn't networked or anything! - l.itemkeys &= ~valid; // only some of the needed keys were given - return valid != 0; -} -#endif diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc deleted file mode 100644 index 354ed1bfed..0000000000 --- a/qcsrc/common/triggers/trigger/magicear.qc +++ /dev/null @@ -1,200 +0,0 @@ -#include "magicear.qh" -#ifdef SVQC -float magicear_matched; -float W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); -string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin) -{ - float domatch, dotrigger, matchstart, l; - string s, msg; - string savemessage; - - magicear_matched = false; - - dotrigger = ((IS_PLAYER(source)) && (!IS_DEAD(source)) && ((ear.radius == 0) || (vdist(source.origin - ear.origin, <=, ear.radius)))); - domatch = ((ear.spawnflags & 32) || dotrigger); - - if (!domatch) - return msgin; - - if (!msgin) - { - // we are in TUBA mode! - if (!(ear.spawnflags & 256)) - return msgin; - - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(!W_Tuba_HasPlayed(source, weaponentity, ear.message, ear.movedir_x, !(ear.spawnflags & 512), ear.movedir_y, ear.movedir_z)) - return msgin; - } - - magicear_matched = true; - - if(dotrigger) - { - savemessage = ear.message; - ear.message = string_null; - SUB_UseTargets(ear, source, NULL); - ear.message = savemessage; - } - - if(ear.netname != "") - return ear.netname; - - return msgin; - } - - if(ear.spawnflags & 256) // ENOTUBA - return msgin; - - if(privatesay) - { - if(ear.spawnflags & 4) - return msgin; - } - else - { - if(!teamsay) - if(ear.spawnflags & 1) - return msgin; - if(teamsay > 0) - if(ear.spawnflags & 2) - return msgin; - if(teamsay < 0) - if(ear.spawnflags & 8) - return msgin; - } - - matchstart = -1; - l = strlen(ear.message); - - if(ear.spawnflags & 128) - msg = msgin; - else - msg = strdecolorize(msgin); - - if(substring(ear.message, 0, 1) == "*") - { - if(substring(ear.message, -1, 1) == "*") - { - // two wildcards - // as we need multi-replacement here... - s = substring(ear.message, 1, -2); - l -= 2; - if(strstrofs(msg, s, 0) >= 0) - matchstart = -2; // we use strreplace on s - } - else - { - // match at start - s = substring(ear.message, 1, -1); - l -= 1; - if(substring(msg, -l, l) == s) - matchstart = strlen(msg) - l; - } - } - else - { - if(substring(ear.message, -1, 1) == "*") - { - // match at end - s = substring(ear.message, 0, -2); - l -= 1; - if(substring(msg, 0, l) == s) - matchstart = 0; - } - else - { - // full match - s = ear.message; - if(msg == ear.message) - matchstart = 0; - } - } - - if(matchstart == -1) // no match - return msgin; - - magicear_matched = true; - - if(dotrigger) - { - savemessage = ear.message; - ear.message = string_null; - SUB_UseTargets(ear, source, NULL); - ear.message = savemessage; - } - - if(ear.spawnflags & 16) - { - return ear.netname; - } - else if(ear.netname != "") - { - if(matchstart < 0) - return strreplace(s, ear.netname, msg); - else - return strcat( - substring(msg, 0, matchstart), - ear.netname, - substring(msg, matchstart + l, -1) - ); - } - else - return msgin; -} - -entity magicears; -string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin) -{ - entity ear; - string msgout; - for(ear = magicears; ear; ear = ear.enemy) - { - msgout = trigger_magicear_processmessage(ear, source, teamsay, privatesay, msgin); - if(!(ear.spawnflags & 64)) - if(magicear_matched) - return msgout; - msgin = msgout; - } - return msgin; -} - -spawnfunc(trigger_magicear) -{ - this.enemy = magicears; - magicears = this; - - // actually handled in "say" processing - // spawnflags: - // 1 = ignore say - // 2 = ignore teamsay - // 4 = ignore tell - // 8 = ignore tell to unknown player - // 16 = let netname replace the whole message (otherwise, netname is a word replacement if set) - // 32 = perform the replacement even if outside the radius or dead - // 64 = continue replacing/triggering even if this one matched - // 128 = don't decolorize message before matching - // 256 = message is a tuba note sequence (pitch.duration pitch.duration ...) - // 512 = tuba notes must be exact right pitch, no transposing - // message: either - // *pattern* - // or - // *pattern - // or - // pattern* - // or - // pattern - // netname: - // if set, replacement for the matched text - // radius: - // "hearing distance" - // target: - // what to trigger - // movedir: - // for spawnflags 256, defines 'instrument+1 mintempo maxtempo' (zero component doesn't matter) - - this.movedir_x -= 1; // map to tuba instrument numbers -} -#endif diff --git a/qcsrc/common/triggers/trigger/magicear.qh b/qcsrc/common/triggers/trigger/magicear.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/magicear.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc deleted file mode 100644 index a67baca16a..0000000000 --- a/qcsrc/common/triggers/trigger/monoflop.qc +++ /dev/null @@ -1,49 +0,0 @@ -#include "monoflop.qh" -#ifdef SVQC -/*QUAKED spawnfunc_trigger_monoflop (.5 .5 .5) (-8 -8 -8) (8 8 8) -"Mono-flop" trigger gate... turns one trigger event into one "on" and one "off" event, separated by a delay of "wait" -*/ -void monoflop_use(entity this, entity actor, entity trigger) -{ - this.nextthink = time + this.wait; - this.enemy = actor; - if(this.state) - return; - this.state = 1; - SUB_UseTargets(this, actor, trigger); -} -void monoflop_fixed_use(entity this, entity actor, entity trigger) -{ - if(this.state) - return; - this.nextthink = time + this.wait; - this.state = 1; - this.enemy = actor; - SUB_UseTargets(this, actor, trigger); -} - -void monoflop_think(entity this) -{ - this.state = 0; - SUB_UseTargets(this, this.enemy, NULL); -} - -void monoflop_reset(entity this) -{ - this.state = 0; - this.nextthink = 0; -} - -spawnfunc(trigger_monoflop) -{ - if(!this.wait) - this.wait = 1; - if(this.spawnflags & 1) - this.use = monoflop_fixed_use; - else - this.use = monoflop_use; - setthink(this, monoflop_think); - this.state = 0; - this.reset = monoflop_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/monoflop.qh b/qcsrc/common/triggers/trigger/monoflop.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/monoflop.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc deleted file mode 100644 index 24b7d5f2ac..0000000000 --- a/qcsrc/common/triggers/trigger/multi.qc +++ /dev/null @@ -1,211 +0,0 @@ -#include "multi.qh" -// NOTE: also contains trigger_once at bottom - -#ifdef SVQC -// the wait time has passed, so set back up for another activation -void multi_wait(entity this) -{ - if (this.max_health) - { - this.health = this.max_health; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - } -} - - -// the trigger was just touched/killed/used -// this.enemy should be set to the activator so it can be held through a delay -// so wait for the delay time before firing -void multi_trigger(entity this) -{ - if (this.nextthink > time) - { - return; // allready been triggered - } - - if(this.spawnflags & 16384) - if(!IS_PLAYER(this.enemy)) - return; // only players - - if (this.classname == "trigger_secret") - { - if (!IS_PLAYER(this.enemy)) - return; - found_secrets = found_secrets + 1; - WriteByte (MSG_ALL, SVC_FOUNDSECRET); - } - - if (this.noise) - _sound (this.enemy, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - -// don't trigger again until reset - this.takedamage = DAMAGE_NO; - - SUB_UseTargets(this, this.enemy, this.goalentity); - - if (this.wait > 0) - { - setthink(this, multi_wait); - this.nextthink = time + this.wait; - } - else if (this.wait == 0) - { - multi_wait(this); // waiting finished - } - else - { // we can't just delete(this) here, because this is a touch function - // called while C code is looping through area links... - settouch(this, func_null); - } -} - -void multi_use(entity this, entity actor, entity trigger) -{ - this.goalentity = trigger; - this.enemy = actor; - multi_trigger(this); -} - -void multi_touch(entity this, entity toucher) -{ - if(!(this.spawnflags & 2)) - if(!toucher.iscreature) - return; - - if(this.team) - if(((this.spawnflags & 4) == 0) == (this.team != toucher.team)) - return; - -// if the trigger has an angles field, check player's facing direction - if (this.movedir != '0 0 0') - { - makevectors (toucher.angles); - if (v_forward * this.movedir < 0) - return; // not facing the right way - } - - // if the trigger has pressed keys, check that the player is pressing those keys - if(this.pressedkeys && IS_PLAYER(toucher)) // only for players - if(!(CS(toucher).pressedkeys & this.pressedkeys)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - this.enemy = toucher; - this.goalentity = toucher; - multi_trigger(this); -} - -void multi_eventdamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(!this.takedamage) - return; - if(this.spawnflags & DOOR_NOSPLASH) - if(!(DEATH_ISSPECIAL(deathtype)) && (deathtype & HITTYPE_SPLASH)) - return; - if(this.team) - if(((this.spawnflags & 4) == 0) == (this.team != attacker.team)) - return; - this.health = this.health - damage; - if (this.health <= 0) - { - this.enemy = attacker; - this.goalentity = inflictor; - multi_trigger(this); - } -} - -void multi_reset(entity this) -{ - if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) - settouch(this, multi_touch); - if (this.max_health) - { - this.health = this.max_health; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - } - setthink(this, func_null); - this.nextthink = 0; - this.team = this.team_saved; -} - -/*QUAKED spawnfunc_trigger_multiple (.5 .5 .5) ? notouch -Variable sized repeatable trigger. Must be targeted at one or more entities. If "health" is set, the trigger must be killed to activate each time. -If "delay" is set, the trigger waits some time after activating before firing. -"wait" : Seconds between triggerings. (.2 default) -If notouch is set, the trigger is only fired by other entities, not by touching. -NOTOUCH has been obsoleted by spawnfunc_trigger_relay! -sounds -1) secret -2) beep beep -3) large switch -4) -set "message" to text string -*/ -spawnfunc(trigger_multiple) -{ - this.reset = multi_reset; - if (this.sounds == 1) - this.noise = "misc/secret.wav"; - else if (this.sounds == 2) - this.noise = strzone(SND(TALK)); - else if (this.sounds == 3) - this.noise = "misc/trigger1.wav"; - - if(this.noise) - precache_sound(this.noise); - - if (!this.wait) - this.wait = 0.2; - else if(this.wait < -1) - this.wait = 0; - this.use = multi_use; - - EXACTTRIGGER_INIT; - - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - - if (this.health) - { - if (this.spawnflags & SPAWNFLAG_NOTOUCH) - objerror (this, "health and notouch don't make sense\n"); - this.canteamdamage = true; - this.max_health = this.health; - this.event_damage = multi_eventdamage; - this.takedamage = DAMAGE_YES; - this.solid = SOLID_BBOX; - setorigin(this, this.origin); // make sure it links into the world - } - else - { - if ( !(this.spawnflags & SPAWNFLAG_NOTOUCH) ) - { - settouch(this, multi_touch); - setorigin(this, this.origin); // make sure it links into the world - } - } -} - - -/*QUAKED spawnfunc_trigger_once (.5 .5 .5) ? notouch -Variable sized trigger. Triggers once, then removes itself. You must set the key "target" to the name of another object in the level that has a matching -"targetname". If "health" is set, the trigger must be killed to activate. -If notouch is set, the trigger is only fired by other entities, not by touching. -if "killtarget" is set, any objects that have a matching "target" will be removed when the trigger is fired. -if "angle" is set, the trigger will only fire when someone is facing the direction of the angle. Use "360" for an angle of 0. -sounds -1) secret -2) beep beep -3) large switch -4) -set "message" to text string -*/ -spawnfunc(trigger_once) -{ - this.wait = -1; - spawnfunc_trigger_multiple(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/multi.qh b/qcsrc/common/triggers/trigger/multi.qh deleted file mode 100644 index 43358c2744..0000000000 --- a/qcsrc/common/triggers/trigger/multi.qh +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -#ifdef SVQC -void multi_trigger(entity this); -void multi_reset(entity this); - -spawnfunc(trigger_once); -#endif diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc deleted file mode 100644 index d946efe5f1..0000000000 --- a/qcsrc/common/triggers/trigger/multivibrator.qc +++ /dev/null @@ -1,78 +0,0 @@ -#include "multivibrator.qh" -#ifdef SVQC -void multivibrator_send(entity this) -{ - float newstate; - float cyclestart; - - cyclestart = floor((time + this.phase) / (this.wait + this.respawntime)) * (this.wait + this.respawntime) - this.phase; - - newstate = (time < cyclestart + this.wait); - - if(this.state != newstate) - SUB_UseTargets(this, this, NULL); - this.state = newstate; - - if(this.state) - this.nextthink = cyclestart + this.wait + 0.01; - else - this.nextthink = cyclestart + this.wait + this.respawntime + 0.01; -} - -void multivibrator_send_think(entity this) -{ - multivibrator_send(this); -} - -void multivibrator_toggle(entity this, entity actor, entity trigger) -{ - if(this.nextthink == 0) - { - multivibrator_send(this); - } - else - { - if(this.state) - { - SUB_UseTargets(this, actor, trigger); - this.state = 0; - } - this.nextthink = 0; - } -} - -void multivibrator_reset(entity this) -{ - if(!(this.spawnflags & 1)) - this.nextthink = 0; // wait for a trigger event - else - this.nextthink = max(1, time); -} - -/*QUAKED trigger_multivibrator (.5 .5 .5) (-8 -8 -8) (8 8 8) START_ON -"Multivibrator" trigger gate... repeatedly sends trigger events. When triggered, turns on or off. --------- KEYS -------- -target: trigger all entities with this targetname when it goes off -targetname: name that identifies this entity so it can be triggered; when off, it always uses the OFF state -phase: offset of the timing -wait: "on" cycle time (default: 1) -respawntime: "off" cycle time (default: same as wait) --------- SPAWNFLAGS -------- -START_ON: assume it is already turned on (when targeted) -*/ -spawnfunc(trigger_multivibrator) -{ - if(!this.wait) - this.wait = 1; - if(!this.respawntime) - this.respawntime = this.wait; - - this.state = 0; - this.use = multivibrator_toggle; - setthink(this, multivibrator_send_think); - this.nextthink = max(1, time); - - IFTARGETED - multivibrator_reset(this); -} -#endif diff --git a/qcsrc/common/triggers/trigger/multivibrator.qh b/qcsrc/common/triggers/trigger/multivibrator.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/multivibrator.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc deleted file mode 100644 index e5d0018032..0000000000 --- a/qcsrc/common/triggers/trigger/relay.qc +++ /dev/null @@ -1,23 +0,0 @@ -#include "relay.qh" -#ifdef SVQC - -void relay_use(entity this, entity actor, entity trigger) -{ - if(this.active != ACTIVE_ACTIVE) - return; - - SUB_UseTargets(this, actor, trigger); -} - -/*QUAKED spawnfunc_trigger_relay (.5 .5 .5) (-8 -8 -8) (8 8 8) -This fixed size trigger cannot be touched, it can only be fired by other events. It can contain killtargets, targets, delays, and messages. -*/ -spawnfunc(trigger_relay) -{ - this.active = ACTIVE_ACTIVE; - this.use = relay_use; - this.reset = spawnfunc_trigger_relay; // this spawnfunc resets fully -} - -spawnfunc(target_relay) { spawnfunc_trigger_relay(this); } -#endif diff --git a/qcsrc/common/triggers/trigger/relay.qh b/qcsrc/common/triggers/trigger/relay.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/relay.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc deleted file mode 100644 index d713a05837..0000000000 --- a/qcsrc/common/triggers/trigger/relay_activators.qc +++ /dev/null @@ -1,40 +0,0 @@ -#include "relay_activators.qh" -#ifdef SVQC -void relay_activators_use(entity this, entity actor, entity trigger) -{ - for(entity trg = NULL; (trg = find(trg, targetname, this.target)); ) - { - if (trg.setactive) - trg.setactive(trg, this.cnt); - else - { - //bprint("Not using setactive\n"); - if(this.cnt == ACTIVE_TOGGLE) - if(trg.active == ACTIVE_ACTIVE) - trg.active = ACTIVE_NOT; - else - trg.active = ACTIVE_ACTIVE; - else - trg.active = this.cnt; - } - } -} - -spawnfunc(relay_activate) -{ - this.cnt = ACTIVE_ACTIVE; - this.use = relay_activators_use; -} - -spawnfunc(relay_deactivate) -{ - this.cnt = ACTIVE_NOT; - this.use = relay_activators_use; -} - -spawnfunc(relay_activatetoggle) -{ - this.cnt = ACTIVE_TOGGLE; - this.use = relay_activators_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_activators.qh b/qcsrc/common/triggers/trigger/relay_activators.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/relay_activators.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc deleted file mode 100644 index 728252c704..0000000000 --- a/qcsrc/common/triggers/trigger/relay_if.qc +++ /dev/null @@ -1,20 +0,0 @@ -#include "relay_if.qh" -#ifdef SVQC -void trigger_relay_if_use(entity this, entity actor, entity trigger) -{ - int n = this.count; - - // TODO make this generic AND faster than nextent()ing through all, if somehow possible - n = (cvar_string(this.netname) == cvar_string(this.message)); - if(this.spawnflags & 1) - n = !n; - - if(n) - SUB_UseTargets(this, actor, trigger); -} - -spawnfunc(trigger_relay_if) -{ - this.use = trigger_relay_if_use; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_if.qh b/qcsrc/common/triggers/trigger/relay_if.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/relay_if.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc deleted file mode 100644 index fee28df51a..0000000000 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qc +++ /dev/null @@ -1,37 +0,0 @@ -#include "relay_teamcheck.qh" -#ifdef SVQC -void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) -{ - if(actor.team) - { - if(this.spawnflags & 2) - { - if(DIFF_TEAM(actor, this)) - SUB_UseTargets(this, actor, trigger); - } - else - { - if(SAME_TEAM(actor, this)) - SUB_UseTargets(this, actor, trigger); - } - } - else - { - if(this.spawnflags & 1) - SUB_UseTargets(this, actor, trigger); - } -} - -void trigger_relay_teamcheck_reset(entity this) -{ - this.team = this.team_saved; -} - -spawnfunc(trigger_relay_teamcheck) -{ - this.team_saved = this.team; - IL_PUSH(g_saved_team, this); - this.use = trigger_relay_teamcheck_use; - this.reset = trigger_relay_teamcheck_reset; -} -#endif diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qh b/qcsrc/common/triggers/trigger/relay_teamcheck.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc deleted file mode 100644 index 9377332e2f..0000000000 --- a/qcsrc/common/triggers/trigger/secret.qc +++ /dev/null @@ -1,90 +0,0 @@ -#include "secret.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <common/util.qh> - #include <server/defs.qh> -#endif - -#ifdef SVQC - -void secrets_setstatus(entity this) -{ - // TODO: use global stats! - STAT(SECRETS_TOTAL, this) = secrets_total; - STAT(SECRETS_FOUND, this) = secrets_found; -} - -/** - * A secret has been found (maybe :P) - */ -void trigger_secret_touch(entity this, entity toucher) -{ - // only a player can trigger this - if (!IS_PLAYER(toucher)) - return; - - // update secrets found counter - secrets_found += 1; - //print("Secret found: ", ftos(secret_counter.cnt), "/"); - //print(ftos(secret_counter.count), "\n"); - - // centerprint message (multi_touch() doesn't always call centerprint()) - centerprint(toucher, this.message); - this.message = ""; - - // handle normal trigger features - multi_touch(this, toucher); - // we can't just delete(this) here, because this is a touch function - // called while C code is looping through area links... - //delete(this); -} - -/*QUAKED trigger_secret (.5 .5 .5) ? -Variable sized secret trigger. Can be targeted at one or more entities. -Basically, it's a trigger_once (with restrictions, see notes) that additionally updates the number of secrets found. --------- KEYS -------- -sounds: 1 to play misc/secret.wav, 2 to play misc/talk.wav, 3 to play misc/trigger1.wav (default: 1) -noise: path to sound file, if you want to play something else -target: trigger all entities with this targetname when triggered -message: print this message to the player who activated the trigger instead of the standard 'You found a secret!' -killtarget: remove all entities with this targetname when triggered --------- NOTES -------- -You should create a common/trigger textured brush covering the entrance to a secret room/area. -Trigger secret can only be trigger by a player's touch and can not be a target itself. -*/ -spawnfunc(trigger_secret) -{ - // FIXME: should it be disabled in most modes? - - // update secrets count - secrets_total += 1; - - // add default message - if (this.message == "") - this.message = "You found a secret!"; - - // set default sound - if (this.noise == "") - if (!this.sounds) - this.sounds = 1; // misc/secret.wav - - // this entity can't be a target itself!!!! - this.targetname = ""; - - // you can't just shoot a room to find it, can you? - this.health = 0; - - // a secret can not be delayed - this.delay = 0; - - // convert this trigger to trigger_once - //this.classname = "trigger_once"; - spawnfunc_trigger_once(this); - - // take over the touch() function, so we can mark secret as found - settouch(this, trigger_secret_touch); - // ignore triggering; - this.use = func_null; -} -#endif diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh deleted file mode 100644 index fcc55c3959..0000000000 --- a/qcsrc/common/triggers/trigger/secret.qh +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#ifdef SVQC - -/** - * Total number of secrets on the map. - */ -float secrets_total; - -/** - * Total numbe of secrets found on the map. - */ -float secrets_found; - - -/** - * update secrets status. - */ -void secrets_setstatus(entity this); -#endif diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc deleted file mode 100644 index 058e41ca27..0000000000 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ /dev/null @@ -1,157 +0,0 @@ -#include "swamp.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <lib/warpzone/util_server.qh> - #include <common/weapons/_all.qh> - #include <server/defs.qh> - #include <common/deathtypes/all.qh> -#endif - -/* -* t_swamp.c -* Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+ -* Author tZork (Jakob MG) -* jakob@games43.se -* 2005 11 29 -*/ - -.float swamp_interval; //Hurt players in swamp with this interval -.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) -.entity swampslug; - -#ifdef SVQC -spawnfunc(trigger_swamp); -#endif -void swamp_touch(entity this, entity toucher); -void swampslug_think(entity this); - - -/* -* Uses a entity calld swampslug to handle players in the swamp -* It works like this: When the plyer enters teh swamp the spawnfunc_trigger_swamp -* attaches a new "swampslug" to the player. As long as the plyer is inside -* the swamp the swamp gives the slug new health. But the slug slowly kills itself -* so when the player goes outside the swamp, it dies and releases the player from the -* swamps curses (dmg/slowdown) -* -* I do it this way becuz there is no "untouch" event. -*/ -void swampslug_think(entity this) -{ - //Slowly kill the slug - this.health = this.health - 1; - - //Slug dead? then remove curses. - if(this.health <= 0) - { - this.owner.in_swamp = 0; - delete(this); - //centerprint(this.owner,"Killing slug...\n"); - return; - } - - // Slug still alive, so we are still in the swamp - // Or we have exited it very recently. - // Do the damage and renew the timer. -#ifdef SVQC - Damage (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, DMG_NOWEP, this.owner.origin, '0 0 0'); -#endif - - this.nextthink = time + this.swamp_interval; -} - -void swamp_touch(entity this, entity toucher) -{ - // If whatever thats touching the swamp is not a player - // or if its a dead player, just dont care abt it. - if(!IS_PLAYER(toucher) || IS_DEAD(toucher)) - return; - - EXACTTRIGGER_TOUCH(this, toucher); - - // Chech if player alredy got a swampslug. - if(toucher.in_swamp != 1) - { - // If not attach one. - //centerprint(toucher,"Entering swamp!\n"); - toucher.swampslug = spawn(); - toucher.swampslug.health = 2; - setthink(toucher.swampslug, swampslug_think); - toucher.swampslug.nextthink = time; - toucher.swampslug.owner = toucher; - toucher.swampslug.dmg = this.dmg; - toucher.swampslug.swamp_interval = this.swamp_interval; - toucher.swamp_slowdown = this.swamp_slowdown; - toucher.in_swamp = 1; - return; - } - - //toucher.in_swamp = 1; - - //Revitalize players swampslug - toucher.swampslug.health = 2; -} - -REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) - -#ifdef SVQC -float swamp_send(entity this, entity to, float sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_SWAMP); - - WriteByte(MSG_ENTITY, this.dmg); // can probably get away with using a single byte here - WriteByte(MSG_ENTITY, this.swamp_slowdown); - WriteByte(MSG_ENTITY, this.swamp_interval); - - trigger_common_write(this, false); - - return true; -} - -void swamp_link(entity this) -{ - trigger_link(this, swamp_send); -} - -/*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? -Players gettin into the swamp will -get slowd down and damaged -*/ -spawnfunc(trigger_swamp) -{ - // Init stuff - trigger_init(this); - settouch(this, swamp_touch); - - // Setup default keys, if missing - if(this.dmg <= 0) - this.dmg = 5; - if(this.swamp_interval <= 0) - this.swamp_interval = 1; - if(this.swamp_slowdown <= 0) - this.swamp_slowdown = 0.5; - - swamp_link(this); -} - -#elif defined(CSQC) - -NET_HANDLE(ENT_CLIENT_SWAMP, bool isnew) -{ - this.dmg = ReadByte(); - this.swamp_slowdown = ReadByte(); - this.swamp_interval = ReadByte(); - - trigger_common_read(this, false); - - return = true; - - this.classname = "trigger_swamp"; - this.solid = SOLID_TRIGGER; - settouch(this, swamp_touch); - this.drawmask = MASK_NORMAL; - this.move_time = time; - this.entremove = trigger_remove_generic; -} -#endif diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh deleted file mode 100644 index f4df98378d..0000000000 --- a/qcsrc/common/triggers/trigger/swamp.qh +++ /dev/null @@ -1,8 +0,0 @@ -#pragma once - -.float swamp_interval; //Hurt players in swamp with this interval -.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) -.entity swampslug; - -.float in_swamp; // bool -.entity swampslug; // Uses this to release from swamp ("untouch" fix) diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc deleted file mode 100644 index 0330ce8d8c..0000000000 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ /dev/null @@ -1,176 +0,0 @@ -#include "teleport.qh" -REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) - -#ifdef SVQC -void trigger_teleport_use(entity this, entity actor, entity trigger) -{ - if(teamplay) - this.team = actor.team; -#ifdef SVQC - this.SendFlags |= SF_TRIGGER_UPDATE; -#endif -} -#endif - -bool Teleport_Active(entity this, entity player) -{ - if (this.active != ACTIVE_ACTIVE) - return false; - -#ifdef SVQC - if (!player.teleportable) - return false; - - if(player.vehicle) - if(!player.vehicle.teleportable) - return false; - - if(IS_TURRET(player)) - return false; -#elif defined(CSQC) - if(!IS_PLAYER(player)) - return false; -#endif - - if(IS_DEAD(player)) - return false; - - if(this.team) - if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, player))) - return false; - - 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(player)) - RemoveGrapplingHooks(player); -#endif - - entity e; - e = Simple_TeleportPlayer(this, player); - -#ifdef SVQC - 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 -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) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_TRIGGER_TELEPORT); - - WriteByte(MSG_ENTITY, this.team); - WriteInt24_t(MSG_ENTITY, this.spawnflags); - WriteByte(MSG_ENTITY, this.active); - WriteCoord(MSG_ENTITY, this.speed); - - trigger_common_write(this, true); - - return true; -} - -void trigger_teleport_link(entity this) -{ - //trigger_link(this, trigger_teleport_send); -} - -spawnfunc(trigger_teleport) -{ - this.angles = '0 0 0'; - - this.active = ACTIVE_ACTIVE; - //trigger_init(this); // only for predicted triggers? - EXACTTRIGGER_INIT; - this.use = trigger_teleport_use; - - if(this.noise != "") - FOREACH_WORD(this.noise, true, precache_sound(it)); - - // this must be called to spawn the teleport waypoints for bots - InitializeEntity(this, teleport_findtarget, INITPRIO_FINDTARGET); - - if (this.target == "") - { - objerror (this, "Teleporter with no target"); - return; - } - - 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) -{ - this.classname = "trigger_teleport"; - if(isnew) - IL_PUSH(g_teleporters, this); - int mytm = ReadByte(); if(mytm) { this.team = mytm - 1; } - this.spawnflags = ReadInt24_t(); - this.active = ReadByte(); - this.speed = ReadCoord(); - - trigger_common_read(this, true); - - this.entremove = trigger_remove_generic; - this.solid = SOLID_TRIGGER; - //settouch(this, trigger_push_touch); - this.move_time = time; - defer(this, 0.25, teleport_findtarget); - - return true; -} - -#endif diff --git a/qcsrc/common/triggers/trigger/teleport.qh b/qcsrc/common/triggers/trigger/teleport.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/common/triggers/trigger/teleport.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc deleted file mode 100644 index 8b985795da..0000000000 --- a/qcsrc/common/triggers/trigger/viewloc.qc +++ /dev/null @@ -1,210 +0,0 @@ -#include "viewloc.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include <lib/warpzone/util_server.qh> - #include <server/defs.qh> -#endif - -REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC) -REGISTER_NET_LINKED(ENT_CLIENT_VIEWLOC_TRIGGER) - -#ifdef SVQC - -void viewloc_think(entity this) -{ - // we abuse this method, rather than using normal .touch, because touch isn't reliable with multiple clients inside the same trigger, and can't "untouch" entities - - // set myself as current viewloc where possible -#if 1 - FOREACH_CLIENT(IS_PLAYER(it) && it.viewloc == this, - { - it.viewloc = NULL; - }); -#else - entity e; - for(e = NULL; (e = findentity(e, viewloc, this)); ) - e.viewloc = NULL; -#endif - -#if 1 - FOREACH_CLIENT(!it.viewloc && IS_PLAYER(it), - { - vector emin = it.absmin; - vector emax = it.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - { - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, it)) // accurate - it.viewloc = this; - } - }); -#else - - for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain) - if(!e.viewloc) - if(IS_PLAYER(e)) // should we support non-player entities with this? - //if(!IS_DEAD(e)) // death view is handled separately, we can't override this just yet - { - vector emin = e.absmin; - vector emax = e.absmax; - if(this.solid == SOLID_BSP) - { - emin -= '1 1 1'; - emax += '1 1 1'; - } - if(boxesoverlap(emin, emax, this.absmin, this.absmax)) // quick - if(WarpZoneLib_BoxTouchesBrush(emin, emax, this, e)) // accurate - e.viewloc = this; - } -#endif - - this.nextthink = time; -} - -bool trigger_viewloc_send(entity this, entity to, int sf) -{ - // CSQC doesn't need to know our origin (yet), as we're only available for referencing - WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC_TRIGGER); - - WriteByte(MSG_ENTITY, this.spawnflags); - - WriteEntity(MSG_ENTITY, this.enemy); - WriteEntity(MSG_ENTITY, this.goalentity); - - WriteVector(MSG_ENTITY, this.origin); - - return true; -} - -void viewloc_init(entity this) -{ - entity e; - for(e = NULL; (e = find(e, targetname, this.target)); ) - if(e.classname == "target_viewlocation_start") - { - this.enemy = e; - break; - } - for(e = NULL; (e = find(e, targetname, this.target2)); ) - if(e.classname == "target_viewlocation_end") - { - this.goalentity = e; - break; - } - - if(!this.enemy) { LOG_INFO("^1FAIL!"); delete(this); return; } - - if(!this.goalentity) - this.goalentity = this.enemy; // make them match so CSQC knows what to do - - Net_LinkEntity(this, false, 0, trigger_viewloc_send); - - setthink(this, viewloc_think); - this.nextthink = time; -} - -spawnfunc(trigger_viewlocation) -{ - // we won't check target2 here yet, as it may not even need to exist - if(this.target == "") { LOG_INFO("^1FAIL!"); delete(this); return; } - - EXACTTRIGGER_INIT; - InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET); -} - -bool viewloc_send(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_VIEWLOC); - - WriteByte(MSG_ENTITY, this.cnt); - - WriteVector(MSG_ENTITY, this.origin); - - WriteAngle(MSG_ENTITY, this.angles_x); - WriteAngle(MSG_ENTITY, this.angles_y); - WriteAngle(MSG_ENTITY, this.angles_z); - - return true; -} - -.float angle; -void viewloc_link(entity this) -{ - if(this.angle) - this.angles_y = this.angle; - Net_LinkEntity(this, false, 0, viewloc_send); -} - -spawnfunc(target_viewlocation_start) -{ - this.classname = "target_viewlocation_start"; - this.cnt = 1; - viewloc_link(this); -} -spawnfunc(target_viewlocation_end) -{ - this.classname = "target_viewlocation_end"; - this.cnt = 2; - viewloc_link(this); -} - -// compatibility -spawnfunc(target_viewlocation) { spawnfunc_target_viewlocation_start(this); } - -#elif defined(CSQC) - -void trigger_viewloc_updatelink(entity this) -{ - this.enemy = findfloat(NULL, entnum, this.cnt); - this.goalentity = findfloat(NULL, entnum, this.count); -} - -NET_HANDLE(ENT_CLIENT_VIEWLOC_TRIGGER, bool isnew) -{ - this.spawnflags = ReadByte(); - - float point1 = ReadShort(); - float point2 = ReadShort(); - - this.enemy = findfloat(NULL, entnum, point1); - this.goalentity = findfloat(NULL, entnum, point2); - - this.origin = ReadVector(); - - return = true; - - setorigin(this, this.origin); - - this.cnt = point1; - this.count = point2; - - setthink(this, trigger_viewloc_updatelink); - this.nextthink = time + 1; // we need to delay this or else - - this.classname = "trigger_viewlocation"; - this.drawmask = MASK_NORMAL; // not so concerned, but better keep it alive -} - -NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) -{ - this.cnt = ReadByte(); - - this.origin = ReadVector(); - setorigin(this, this.origin); - - this.movedir_x = ReadAngle(); - this.movedir_y = ReadAngle(); - this.movedir_z = ReadAngle(); - - return = true; - - this.classname = ((this.cnt == 2) ? "target_viewlocation_end" : "target_viewlocation_start"); - this.drawmask = MASK_NORMAL; // don't cull it -} - -#endif diff --git a/qcsrc/common/triggers/trigger/viewloc.qh b/qcsrc/common/triggers/trigger/viewloc.qh deleted file mode 100644 index 69c6c821ed..0000000000 --- a/qcsrc/common/triggers/trigger/viewloc.qh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -.entity viewloc; - -const int VIEWLOC_NOSIDESCROLL = BIT(0); // NOTE: currently unimplemented -const int VIEWLOC_FREEAIM = BIT(1); -const int VIEWLOC_FREEMOVE = BIT(2); - -#ifdef CSQC -.entity goalentity; -.entity enemy; -.vector movedir; -#endif diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc deleted file mode 100644 index fbd20c8401..0000000000 --- a/qcsrc/common/triggers/triggers.qc +++ /dev/null @@ -1,311 +0,0 @@ -#include "triggers.qh" -void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } - -void SUB_UseTargets(entity this, entity actor, entity trigger); - -void DelayThink(entity this) -{ - SUB_UseTargets (this, this.enemy, NULL); - delete(this); -} - -void FixSize(entity e) -{ - e.mins_x = rint(e.mins_x); - e.mins_y = rint(e.mins_y); - e.mins_z = rint(e.mins_z); - - e.maxs_x = rint(e.maxs_x); - e.maxs_y = rint(e.maxs_y); - e.maxs_z = rint(e.maxs_z); -} - -#ifdef SVQC - -bool autocvar_g_triggers_debug = true; - -void trigger_init(entity this) -{ - string m = this.model; - EXACTTRIGGER_INIT; - if(autocvar_g_triggers_debug) - { - if(m != "") - { - precache_model(m); - _setmodel(this, m); // no precision needed - } - setorigin(this, this.origin); - if(this.scale) - setsize(this, this.mins * this.scale, this.maxs * this.scale); - else - setsize(this, this.mins, this.maxs); - } - - if(autocvar_g_triggers_debug) - BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); -} - -void trigger_link(entity this, bool(entity this, entity to, int sendflags) sendfunc) -{ - setSendEntity(this, sendfunc); - this.SendFlags = 0xFFFFFF; -} - -void trigger_common_write(entity this, bool withtarget) -{ - int f = 0; - if(this.warpzone_isboxy) - BITSET_ASSIGN(f, 1); - if(this.origin != '0 0 0') - BITSET_ASSIGN(f, 4); - if(this.movedir != '0 0 0') - BITSET_ASSIGN(f, 8); - if(this.angles != '0 0 0') - BITSET_ASSIGN(f, 16); - WriteByte(MSG_ENTITY, f); - - if(withtarget) - { - // probably some way to clean this up... - int targbits = 0; - if(this.target && this.target != "") targbits |= BIT(0); - if(this.target2 && this.target2 != "") targbits |= BIT(1); - if(this.target3 && this.target3 != "") targbits |= BIT(2); - if(this.target4 && this.target4 != "") targbits |= BIT(3); - if(this.targetname && this.targetname != "") targbits |= BIT(4); - if(this.killtarget && this.killtarget != "") targbits |= BIT(5); - - WriteByte(MSG_ENTITY, targbits); - - if(targbits & BIT(0)) - WriteString(MSG_ENTITY, this.target); - if(targbits & BIT(1)) - WriteString(MSG_ENTITY, this.target2); - if(targbits & BIT(2)) - WriteString(MSG_ENTITY, this.target3); - if(targbits & BIT(3)) - WriteString(MSG_ENTITY, this.target4); - if(targbits & BIT(4)) - WriteString(MSG_ENTITY, this.targetname); - if(targbits & BIT(5)) - WriteString(MSG_ENTITY, this.killtarget); - } - - if(f & 4) - WriteVector(MSG_ENTITY, this.origin); - - if(f & 8) - WriteVector(MSG_ENTITY, this.movedir); - - if(f & 16) - WriteVector(MSG_ENTITY, this.angles); - - WriteShort(MSG_ENTITY, this.modelindex); - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - WriteByte(MSG_ENTITY, bound(1, this.scale * 16, 255)); -} - -#elif defined(CSQC) - -void trigger_common_read(entity this, bool withtarget) -{ - int f = ReadByte(); - this.warpzone_isboxy = (f & 1); - - if(withtarget) - { - if(this.target) { strunzone(this.target); } - if(this.target2) { strunzone(this.target2); } - if(this.target3) { strunzone(this.target3); } - if(this.target4) { strunzone(this.target4); } - if(this.targetname) { strunzone(this.targetname); } - if(this.killtarget) { strunzone(this.killtarget); } - - int targbits = ReadByte(); - - #define X(xs,b) MACRO_BEGIN { \ - if(targbits & BIT(b)) \ - xs = strzone(ReadString()); \ - else \ - xs = string_null; \ - } MACRO_END - - X(this.target, 0); - X(this.target2, 1); - X(this.target3, 2); - X(this.target4, 3); - X(this.targetname, 4); - X(this.killtarget, 5); - #undef X - } - - if(f & 4) - this.origin = ReadVector(); - else - this.origin = '0 0 0'; - setorigin(this, this.origin); - - if(f & 8) - this.movedir = ReadVector(); - else - this.movedir = '0 0 0'; - - if(f & 16) - this.angles = ReadVector(); - else - this.angles = '0 0 0'; - - this.modelindex = ReadShort(); - this.mins = ReadVector(); - this.maxs = ReadVector(); - this.scale = ReadByte() / 16; - setsize(this, this.mins, this.maxs); -} - -void trigger_remove_generic(entity this) -{ - if(this.target) { strunzone(this.target); } - this.target = string_null; - - if(this.target2) { strunzone(this.target2); } - this.target2 = string_null; - - if(this.target3) { strunzone(this.target3); } - this.target3 = string_null; - - if(this.target4) { strunzone(this.target4); } - this.target4 = string_null; - - if(this.targetname) { strunzone(this.targetname); } - this.target = string_null; - - if(this.killtarget) { strunzone(this.killtarget); } - this.killtarget = string_null; -} -#endif - - -/* -============================== -SUB_UseTargets - -the global "activator" should be set to the entity that initiated the firing. - -If this.delay is set, a DelayedUse entity will be created that will actually -do the SUB_UseTargets after that many seconds have passed. - -Centerprints any this.message to the activator. - -Removes all entities with a targetname that match this.killtarget, -and removes them, so some events can remove other triggers. - -Search for (string)targetname in all entities that -match (string)this.target and call their .use function - -============================== -*/ - -void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) -{ -// -// check for a delay -// - if (this.delay) - { - // create a temp object to fire at a later time - entity t = new(DelayedUse); - t.nextthink = time + this.delay; - setthink(t, DelayThink); - t.enemy = actor; - t.message = this.message; - t.killtarget = this.killtarget; - t.target = this.target; - t.target2 = this.target2; - t.target3 = this.target3; - t.target4 = this.target4; - t.antiwall_flag = this.antiwall_flag; - return; - } - - string s; - -// -// print the message -// -#ifdef SVQC - if(this) - if(IS_PLAYER(actor) && this.message != "") - if(IS_REAL_CLIENT(actor)) - { - centerprint(actor, this.message); - if (this.noise == "") - play2(actor, SND(TALK)); - } - -// -// kill the killtagets -// - s = this.killtarget; - if (s != "") - { - for(entity t = NULL; (t = find(t, targetname, s)); ) - delete(t); - } -#endif - -// -// fire targets -// - - if(this.target_random) - RandomSelection_Init(); - - for(int i = 0; i < 4; ++i) - { - switch(i) - { - default: - case 0: s = this.target; break; - case 1: s = this.target2; break; - case 2: s = this.target3; break; - case 3: s = this.target4; break; - } - if (s != "") - { - // Flag to set func_clientwall state - // 1 == deactivate, 2 == activate, 0 == do nothing - int aw_flag = this.antiwall_flag; - for(entity t = NULL; (t = find(t, targetname, s)); ) - { - if(t.use && (t.sub_target_used != time || !preventReuse)) - { - if(this.target_random) - { - RandomSelection_AddEnt(t, 1, 0); - } - else - { - if (t.classname == "func_clientwall" || t.classname == "func_clientillusionary") - t.antiwall_flag = aw_flag; - - t.use(t, actor, this); - if(preventReuse) - t.sub_target_used = time; - } - } - } - } - } - - if(this.target_random && RandomSelection_chosen_ent) - { - RandomSelection_chosen_ent.use(RandomSelection_chosen_ent, actor, this); - if(preventReuse) - RandomSelection_chosen_ent.sub_target_used = time; - } -} - -void SUB_UseTargets(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, false); } -void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger) { SUB_UseTargets_Ex(this, actor, trigger, true); } diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh deleted file mode 100644 index 2b8274f4b8..0000000000 --- a/qcsrc/common/triggers/triggers.qh +++ /dev/null @@ -1,55 +0,0 @@ -#pragma once - -const float SF_TRIGGER_INIT = 1; -const float SF_TRIGGER_UPDATE = 2; -const float SF_TRIGGER_RESET = 4; - -const float SPAWNFLAG_NOMESSAGE = 1; -const float SPAWNFLAG_NOTOUCH = 1; - -.bool pushable; - -.float antiwall_flag; // Variable to define what to do with func_clientwall -// 0 == do nothing, 1 == deactivate, 2 == activate - -.float height; - -.float nottargeted; -#define IFTARGETED if(!this.nottargeted && this.targetname != "") - -.float lip; - -// used elsewhere (will fix) -#ifdef SVQC -void trigger_common_write(entity this, bool withtarget); - -string trigger_magicear_processmessage_forallears(entity source, float teamsay, entity privatesay, string msgin); - -void target_voicescript_next(entity pl); -void target_voicescript_clear(entity pl); - -void SUB_UseTargets_PreventReuse(entity this, entity actor, entity trigger); -#endif - -.float sub_target_used; - -.float volume, atten; - -.vector dest; - -void FixSize(entity e); - -#ifdef CSQC -void trigger_common_read(entity this, bool withtarget); -void trigger_remove_generic(entity this); - -.float active; -.string target; -.string targetname; - -const int ACTIVE_NOT = 0; -const int ACTIVE_ACTIVE = 1; -const int ACTIVE_IDLE = 2; -const int ACTIVE_BUSY = 2; -const int ACTIVE_TOGGLE = 3; -#endif diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index ba7b5d01bb..6cdda51039 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -37,7 +37,7 @@ void turret_draw(entity this) this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 127) { dt = random(); @@ -45,11 +45,11 @@ void turret_draw(entity this) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - if(this.health < 85) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 85) if(dt < 0.01) pointparticles(EFFECT_SMOKE_LARGE, (this.origin + (randomvec() * 80)), '0 0 0', 1); - if(this.health < 32) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 32) if(dt < 0.015) pointparticles(EFFECT_SMOKE_SMALL, (this.origin + (randomvec() * 80)), '0 0 0', 1); @@ -176,11 +176,14 @@ void turret_draw2d(entity this) } o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); - o = drawsprite_TextOrIcon(true, o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); + if(autocvar_g_waypointsprite_turrets_text) + { + o = drawsprite_TextOrIcon(true, o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); + } drawhealthbar( o, 0, - this.health / 255, + GetResourceAmount(this, RESOURCE_HEALTH) / 255, '0 0 0', '0 0 0', 0.5 * SPRITE_HEALTHBAR_WIDTH * t, @@ -221,7 +224,7 @@ void turret_construct(entity this, bool isnew) set_movetype(this.tur_head, MOVETYPE_NOCLIP); set_movetype(this, MOVETYPE_NOCLIP); this.tur_head.angles = this.angles; - this.health = 255; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 255); this.solid = SOLID_BBOX; this.tur_head.solid = SOLID_NOT; set_movetype(this, MOVETYPE_NOCLIP); @@ -422,13 +425,15 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) } _tmp = ReadByte(); - if(_tmp == 0 && this.health != 0) + float myhp = GetResourceAmount(this, RESOURCE_HEALTH); + if(_tmp == 0 && myhp != 0) turret_die(this); - else if(this.health && this.health != _tmp) + else if(myhp && myhp > _tmp) this.helpme = servertime + 10; + else if(myhp && myhp < _tmp) + this.helpme = 0; // we're being healed, don't spam help me waypoints - this.health = _tmp; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, _tmp); } - //this.enemy.health = this.health / 255; return true; } diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index 2a7c41c98c..37fb5f0d52 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -182,9 +182,10 @@ void turret_die(entity this) this.tur_head.solid = this.solid; this.event_damage = func_null; + this.event_heal = func_null; this.takedamage = DAMAGE_NO; - this.health = 0; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 0); // Go boom //RadiusDamage (this,this, min(this.ammo,50),min(this.ammo,50) * 0.25,250,NULL,min(this.ammo,50)*5,DEATH_TURRET,NULL); @@ -230,7 +231,7 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, return; } - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); // thorw head slightly off aim when hit? if (this.damage_flags & TFL_DMG_HEADSHAKE) @@ -244,10 +245,12 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, if (this.turret_flags & TUR_FLAG_MOVE) this.velocity = this.velocity + vforce; - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.event_damage = func_null; this.tur_head.event_damage = func_null; + this.event_heal = func_null; + this.tur_head.event_heal = func_null; this.takedamage = DAMAGE_NO; this.nextthink = time; setthink(this, turret_die); @@ -256,6 +259,17 @@ void turret_damage(entity this, entity inflictor, entity attacker, float damage, this.SendFlags |= TNSF_STATUS; } +bool turret_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + targ.SendFlags |= TNSF_STATUS; + return true; +} + void turret_think(entity this); void turret_respawn(entity this) { @@ -268,10 +282,11 @@ void turret_respawn(entity this) this.solid = SOLID_BBOX; this.takedamage = DAMAGE_AIM; this.event_damage = turret_damage; + this.event_heal = turret_heal; this.avelocity = '0 0 0'; this.tur_head.avelocity = this.avelocity; this.tur_head.angles = this.idle_aim; - this.health = this.max_health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, this.max_health); this.enemy = NULL; this.volly_counter = this.shot_volly; this.ammo = this.ammo_max; @@ -350,10 +365,10 @@ bool turret_send(entity this, entity to, float sf) { WriteByte(MSG_ENTITY, this.team); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) WriteByte(MSG_ENTITY, 0); else - WriteByte(MSG_ENTITY, ceil((this.health / this.max_health) * 255)); + WriteByte(MSG_ENTITY, ceil((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 255)); } return true; @@ -384,7 +399,7 @@ void load_unit_settings(entity ent, bool is_reload) ent.tur_head.angles = '0 0 0'; } - ent.health = cvar(strcat(sbase,"_health")) * ent.turret_scale_health; + SetResourceAmountExplicit(ent, RESOURCE_HEALTH, cvar(strcat(sbase,"_health")) * ent.turret_scale_health); ent.respawntime = cvar(strcat(sbase,"_respawntime")) * ent.turret_scale_respawn; ent.shot_dmg = cvar(strcat(sbase,"_shot_dmg")) * ent.turret_scale_damage; @@ -451,15 +466,15 @@ void turret_projectile_touch(entity this, entity toucher) void turret_projectile_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector vforce) { this.velocity += vforce; - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); //this.realowner = attacker; // Dont change realowner, it does not make much sense for turrets - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.owner, turret_projectile_explode); } entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim) { - TC(Sound, _snd); + TC(Sound, _snd); entity proj; sound (actor, CH_WEAPON_A, _snd, VOL_BASE, ATTEN_NORM); @@ -483,7 +498,7 @@ entity turret_projectile(entity actor, Sound _snd, float _size, float _health, f PROJECTILE_MAKETRIGGER(proj); if(_health) { - proj.health = _health; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health); proj.takedamage = DAMAGE_YES; proj.event_damage = turret_projectile_damage; } @@ -673,6 +688,7 @@ void turret_track(entity this) + TFL_TARGETSELECT_LOS + TFL_TARGETSELECT_PLAYERS + TFL_TARGETSELECT_MISSILES + + TFL_TARGETSELECT_VEHICLES - TFL_TARGETSELECT_TRIGGERTARGET + TFL_TARGETSELECT_ANGLELIMITS + TFL_TARGETSELECT_RANGELIMITS @@ -701,7 +717,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl if(!checkpvs(e_target.origin, e_turret)) return -1; - if(e_target.alpha <= 0.3) + if(e_target.alpha != 0 && e_target.alpha <= 0.3) return -1; if(MUTATOR_CALLHOOK(TurretValidateTarget, e_turret, e_target, validate_flags)) @@ -715,15 +731,17 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl return -5; // Cant touch this + if (GetResourceAmount(e_target, RESOURCE_HEALTH) <= 0) + return -6; + else if (STAT(FROZEN, e_target)) + return -6; + + // vehicle if(IS_VEHICLE(e_target)) { - if (e_target.vehicle_health <= 0) - return -6; + if ((validate_flags & TFL_TARGETSELECT_VEHICLES) && !e_target.owner) + return -7; } - else if (e_target.health <= 0) - return -6; - else if(STAT(FROZEN, e_target) > 0) - return -6; // player if (IS_CLIENT(e_target)) @@ -1239,6 +1257,12 @@ void turret_initparams(entity tur) #undef TRY } +bool turret_closetotarget(entity this, vector targ) +{ + vector path_extra_size = '64 64 64'; + return boxesoverlap(targ - path_extra_size, targ + path_extra_size, this.absmin - path_extra_size, this.absmax + path_extra_size); +} + void turret_findtarget(entity this) { entity e = find(NULL, classname, "turret_manager"); @@ -1286,7 +1310,7 @@ bool turret_initialize(entity this, Turret tur) if(!this.team || !teamplay) { this.team = FLOAT_MAX; } if(!this.ticrate) { this.ticrate = ((this.turret_flags & TUR_FLAG_SUPPORT) ? 0.2 : 0.1); } - if(!this.health) { this.health = 1000; } + if(!GetResourceAmount(this, RESOURCE_HEALTH)) { SetResourceAmountExplicit(this, RESOURCE_HEALTH, 1000); } if(!this.shot_refire) { this.shot_refire = 1; } if(!this.tur_shotorg) { this.tur_shotorg = '50 0 50'; } if(!this.turret_flags) { this.turret_flags = TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER; } @@ -1343,7 +1367,7 @@ bool turret_initialize(entity this, Turret tur) this.effects = EF_NODRAW; this.netname = tur.turret_name; this.ticrate = bound(sys_frametime, this.ticrate, 60); - this.max_health = this.health; + this.max_health = GetResourceAmount(this, RESOURCE_HEALTH); this.target_validate_flags = this.target_select_flags; this.ammo = this.ammo_max; this.ammo_recharge *= this.ticrate; @@ -1354,6 +1378,7 @@ bool turret_initialize(entity this, Turret tur) this.idle_aim = '0 0 0'; this.turret_firecheckfunc = turret_firecheck; this.event_damage = turret_damage; + this.event_heal = turret_heal; this.use = turret_use; this.bot_attack = true; this.nextthink = time + 1; diff --git a/qcsrc/common/turrets/sv_turrets.qh b/qcsrc/common/turrets/sv_turrets.qh index 41a7bd962d..deee313ab1 100644 --- a/qcsrc/common/turrets/sv_turrets.qh +++ b/qcsrc/common/turrets/sv_turrets.qh @@ -89,6 +89,9 @@ void turrets_setframe(entity this, float _frame, float client_only); bool turret_initialize(entity this, Turret tur); +// returns true when box overlaps with a given location +bool turret_closetotarget(entity this, vector targ); + /// Function to use for target evaluation. usualy turret_targetscore_generic .float(entity _turret, entity _target) turret_score_target; diff --git a/qcsrc/common/turrets/turret.qh b/qcsrc/common/turrets/turret.qh index 7f9b746cd3..aa5a50c9e6 100644 --- a/qcsrc/common/turrets/turret.qh +++ b/qcsrc/common/turrets/turret.qh @@ -62,19 +62,20 @@ ENDCLASS(Turret) // target selection flags .int target_select_flags; .int target_validate_flags; -const int TFL_TARGETSELECT_NO = 2; // don't automatically find targets -const int TFL_TARGETSELECT_LOS = 4; // require line of sight to find targets -const int TFL_TARGETSELECT_PLAYERS = 8; // target players -const int TFL_TARGETSELECT_MISSILES = 16; // target projectiles -const int TFL_TARGETSELECT_TRIGGERTARGET = 32; // respond to turret_trigger_target events -const int TFL_TARGETSELECT_ANGLELIMITS = 64; // apply extra angular limits to target selection -const int TFL_TARGETSELECT_RANGELIMITS = 128; // limit target selection range -const int TFL_TARGETSELECT_TEAMCHECK = 256; // don't attack teammates -const int TFL_TARGETSELECT_NOBUILTIN = 512; // only attack targets when triggered -const int TFL_TARGETSELECT_OWNTEAM = 1024; // only attack teammates -const int TFL_TARGETSELECT_NOTURRETS = 2048; // don't attack other turrets -const int TFL_TARGETSELECT_FOV = 4096; // extra limits to attack range -const int TFL_TARGETSELECT_MISSILESONLY = 8192; // only attack missiles +const int TFL_TARGETSELECT_NO = BIT(1); // don't automatically find targets +const int TFL_TARGETSELECT_LOS = BIT(2); // require line of sight to find targets +const int TFL_TARGETSELECT_PLAYERS = BIT(3); // target players +const int TFL_TARGETSELECT_MISSILES = BIT(4); // target projectiles +const int TFL_TARGETSELECT_TRIGGERTARGET = BIT(5); // respond to turret_trigger_target events +const int TFL_TARGETSELECT_ANGLELIMITS = BIT(6); // apply extra angular limits to target selection +const int TFL_TARGETSELECT_RANGELIMITS = BIT(7); // limit target selection range +const int TFL_TARGETSELECT_TEAMCHECK = BIT(8); // don't attack teammates +const int TFL_TARGETSELECT_NOBUILTIN = BIT(9); // only attack targets when triggered +const int TFL_TARGETSELECT_OWNTEAM = BIT(10); // only attack teammates +const int TFL_TARGETSELECT_NOTURRETS = BIT(11); // don't attack other turrets +const int TFL_TARGETSELECT_FOV = BIT(12); // extra limits to attack range +const int TFL_TARGETSELECT_MISSILESONLY = BIT(13); // only attack missiles +const int TFL_TARGETSELECT_VEHICLES = BIT(14); // target manned vehicles // aim flags .int aim_flags; diff --git a/qcsrc/common/turrets/turret/ewheel.qc b/qcsrc/common/turrets/turret/ewheel.qc index 5625d23fc9..c0a0b177ee 100644 --- a/qcsrc/common/turrets/turret/ewheel.qc +++ b/qcsrc/common/turrets/turret/ewheel.qc @@ -17,7 +17,7 @@ const int ewheel_anim_bck_fast = 4; void ewheel_move_path(entity this) { // Are we close enough to a path node to switch to the next? - if(vdist(this.origin - this.pathcurrent.origin, <, 64)) + if(turret_closetotarget(this, this.pathcurrent.origin)) { #ifdef EWHEEL_FANCYPATH if (this.pathcurrent.path_next == NULL) @@ -49,7 +49,6 @@ void ewheel_move_path(entity this) if (this.pathcurrent) { - this.moveto = this.pathcurrent.origin; this.steerto = steerlib_attract2(this, this.moveto, 0.5, 500, 0.95); @@ -229,17 +228,17 @@ void ewheel_draw(entity this) setorigin(this, this.origin + this.velocity * dt); this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 127) if(random() < 0.05) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - METHOD(EWheel, tr_setup, void(EWheel this, entity it)) - { - it.gravity = 1; - set_movetype(it, MOVETYPE_BOUNCE); - it.move_time = time; - it.draw = ewheel_draw; - } +METHOD(EWheel, tr_setup, void(EWheel this, entity it)) +{ + it.gravity = 1; + set_movetype(it, MOVETYPE_BOUNCE); + it.move_time = time; + it.draw = ewheel_draw; +} #endif // CSQC diff --git a/qcsrc/common/turrets/turret/hk.qc b/qcsrc/common/turrets/turret/hk.qc index 811e386f5b..ba2e89c2c1 100644 --- a/qcsrc/common/turrets/turret/hk.qc +++ b/qcsrc/common/turrets/turret/hk.qc @@ -22,10 +22,10 @@ METHOD(HunterKiller, tr_setup, void(HunterKiller this, entity it)) { it.ammo_flags = TFL_AMMO_ROCKETS | TFL_AMMO_RECHARGE; it.aim_flags = TFL_AIM_SIMPLE; - it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; + it.target_select_flags = TFL_TARGETSELECT_LOS | TFL_TARGETSELECT_VEHICLES | TFL_TARGETSELECT_TRIGGERTARGET | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; it.firecheck_flags = TFL_FIRECHECK_DEAD | TFL_FIRECHECK_TEAMCHECK | TFL_FIRECHECK_REFIRE | TFL_FIRECHECK_AFF; it.shoot_flags = TFL_SHOOT_CLEARTARGET; - it.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_TEAMCHECK; + it.target_validate_flags = TFL_TARGETSELECT_VEHICLES | TFL_TARGETSELECT_TEAMCHECK; it.turret_addtarget = turret_hk_addtarget; } diff --git a/qcsrc/common/turrets/turret/hk_weapon.qc b/qcsrc/common/turrets/turret/hk_weapon.qc index 3141b3d10f..b68bfb7730 100644 --- a/qcsrc/common/turrets/turret/hk_weapon.qc +++ b/qcsrc/common/turrets/turret/hk_weapon.qc @@ -58,7 +58,7 @@ void turret_hk_missile_think(entity this) //if (this.cnt < time) // turret_hk_missile_explode(); - if (IS_DEAD(this.enemy)) + if (IS_DEAD(this.enemy) || IS_SPEC(this.enemy) || IS_OBSERVER(this.enemy)) this.enemy = NULL; // Pick the closest valid target. @@ -251,7 +251,7 @@ bool hk_is_valid_target(entity this, entity proj, entity targ) return false; // Cant touch this - if ((targ.takedamage == DAMAGE_NO) || (targ.health < 0)) + if ((targ.takedamage == DAMAGE_NO) || (GetResourceAmount(targ, RESOURCE_HEALTH) < 0)) return false; // player diff --git a/qcsrc/common/turrets/turret/machinegun_weapon.qc b/qcsrc/common/turrets/turret/machinegun_weapon.qc index 619d7a9072..a56e7de383 100644 --- a/qcsrc/common/turrets/turret/machinegun_weapon.qc +++ b/qcsrc/common/turrets/turret/machinegun_weapon.qc @@ -17,7 +17,7 @@ METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .ent actor.tur_head = actor; weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } - fireBullet (actor, weaponentity, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, 0); + fireBullet(actor, weaponentity, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, EFFECT_BULLET); W_MachineGun_MuzzleFlash(actor, weaponentity); setattachment(actor.(weaponentity).muzzle_flash, actor.tur_head, "tag_fire"); } diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc index 96fa81f8ee..89ddfbd4f1 100644 --- a/qcsrc/common/turrets/turret/plasma.qc +++ b/qcsrc/common/turrets/turret/plasma.qc @@ -6,7 +6,7 @@ spawnfunc(turret_plasma) { if (!turret_initialize(this, TUR_PLASMA)) delete(this METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this, entity it)) { - if(g_instagib) + if(MUTATOR_IS_ENABLED(mutator_instagib)) { .entity weaponentity = weaponentities[0]; // TODO: unhardcode FireRailgunBullet (it, weaponentity, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000, diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc index 2a6f997cf3..9fa10eefff 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qc +++ b/qcsrc/common/turrets/turret/plasma_dual.qc @@ -6,7 +6,7 @@ spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) d METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret thistur, entity it)) { - if (g_instagib) { + if (MUTATOR_IS_ENABLED(mutator_instagib)) { .entity weaponentity = weaponentities[0]; // TODO: unhardcode FireRailgunBullet (it, weaponentity, it.tur_shotorg, it.tur_shotorg + it.tur_shotdir_updated * max_shot_distance, 10000000000, 800, 0, 0, 0, 0, DEATH_TURRET_PLASMA.m_id); diff --git a/qcsrc/common/turrets/turret/plasma_dual.qh b/qcsrc/common/turrets/turret/plasma_dual.qh index 04436c47f8..6fdfd32e70 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qh +++ b/qcsrc/common/turrets/turret/plasma_dual.qh @@ -3,6 +3,7 @@ #include "plasma_weapon.qh" CLASS(PlasmaDualAttack, PlasmaAttack) +/* flags */ ATTRIB(PlasmaDualAttack, spawnflags, int, WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); /* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); /* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); ENDCLASS(PlasmaDualAttack) diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc index 8cf795a938..6aa0865e69 100644 --- a/qcsrc/common/turrets/turret/walker.qc +++ b/qcsrc/common/turrets/turret/walker.qc @@ -86,10 +86,10 @@ void walker_rocket_touch(entity this, entity toucher) void walker_rocket_damage(entity this, entity inflictor, entity attacker, float damage, float deathtype, .entity weaponentity, vector hitloc, vector vforce) { - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.velocity = this.velocity + vforce; - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.owner, walker_rocket_explode); } @@ -218,7 +218,7 @@ void walker_fire_rocket(entity this, vector org) rocket.bot_dodgerating = 50; rocket.takedamage = DAMAGE_YES; rocket.damageforcescale = 2; - rocket.health = 25; + SetResourceAmountExplicit(rocket, RESOURCE_HEALTH, 25); rocket.tur_shotorg = randomvec() * 512; rocket.cnt = time + 1; rocket.enemy = this.enemy; @@ -281,7 +281,8 @@ void walker_move_path(entity this) { #ifdef WALKER_FANCYPATHING // Are we close enougth to a path node to switch to the next? - if(vdist(this.origin - this.pathcurrent.origin, <, 64)) + if(turret_closetotarget(this, this.pathcurrent.origin)) + { if (this.pathcurrent.path_next == NULL) { // Path endpoint reached @@ -304,13 +305,14 @@ void walker_move_path(entity this) } else this.pathcurrent = this.pathcurrent.path_next; + } this.moveto = this.pathcurrent.origin; this.steerto = steerlib_attract2(this, this.moveto,0.5,500,0.95); walker_move_to(this, this.moveto, 0); #else - if(vdist(this.origin - this.pathcurrent.origin, <, 64)) + if(turret_closetotarget(this, this.pathcurrent.origin)) this.pathcurrent = this.pathcurrent.enemy; if(!this.pathcurrent) @@ -352,7 +354,7 @@ METHOD(WalkerTurret, tr_think, void(WalkerTurret thistur, entity it)) { fixedmakevectors(it.angles); - if (it.spawnflags & TSF_NO_PATHBREAK && it.pathcurrent) + if ((it.spawnflags & TSF_NO_PATHBREAK) && it.pathcurrent) walker_move_path(it); else if (it.enemy == NULL) { @@ -627,17 +629,17 @@ void walker_draw(entity this) setorigin(this, this.origin + this.velocity * dt); this.tur_head.angles += dt * this.tur_head.avelocity; - if (this.health < 127) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 127) if(random() < 0.15) te_spark(this.origin + '0 0 40', randomvec() * 256 + '0 0 256', 16); } - METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) - { - it.gravity = 1; - set_movetype(it, MOVETYPE_BOUNCE); - it.move_time = time; - it.draw = walker_draw; - } +METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) +{ + it.gravity = 1; + set_movetype(it, MOVETYPE_BOUNCE); + it.move_time = time; + it.draw = walker_draw; +} #endif // CSQC diff --git a/qcsrc/common/turrets/turret/walker_weapon.qc b/qcsrc/common/turrets/turret/walker_weapon.qc index d81b738ed4..bbe59aeac9 100644 --- a/qcsrc/common/turrets/turret/walker_weapon.qc +++ b/qcsrc/common/turrets/turret/walker_weapon.qc @@ -16,7 +16,7 @@ METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } sound (actor, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM); - fireBullet (actor, weaponentity, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_WALK_GUN.m_id, 0); + fireBullet(actor, weaponentity, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_WALK_GUN.m_id, EFFECT_BULLET); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, actor.tur_shotorg, actor.tur_shotdir_updated * 1000, 1); } } diff --git a/qcsrc/common/turrets/util.qh b/qcsrc/common/turrets/util.qh index 4c84f268d3..4e5cb0d5b9 100644 --- a/qcsrc/common/turrets/util.qh +++ b/qcsrc/common/turrets/util.qh @@ -5,4 +5,10 @@ float turret_tag_fire_update(entity this); void FireImoBeam(entity this, vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype); +#ifdef TURRET_DEBUG +void mark_error(vector where,float lifetime); +void mark_info(vector where,float lifetime); +entity mark_misc(vector where,float lifetime); +#endif + #endif diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index deba86c289..a7e9c42104 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -2,7 +2,7 @@ #if defined(CSQC) #include "constants.qh" - #include "../client/mutators/events.qh" + #include <client/mutators/_mod.qh> #include "mapinfo.qh" #include "notifications/all.qh" #include "scores.qh" @@ -10,14 +10,136 @@ #elif defined(MENUQC) #elif defined(SVQC) #include "constants.qh" - #include "../server/mutators/events.qh" + #include <server/mutators/_mod.qh> #include "notifications/all.qh" #include <common/deathtypes/all.qh> #include "scores.qh" #include "mapinfo.qh" #endif +#ifdef SVQC +float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking +{ + vector pos, dir, t; + float nudge; + entity stopentity; + + //nudge = 2 * cvar("collision_impactnudge"); // why not? + nudge = 0.5; + + dir = normalize(v2 - v1); + + pos = v1 + dir * nudge; + + float c; + c = 0; + + for (;;) + { + if(pos * dir >= v2 * dir) + { + // went too far + trace_fraction = 1; + trace_endpos = v2; + return c; + } + + tracebox(pos, mi, ma, v2, nomonsters, forent); + ++c; + + if(c == 50) + { + LOG_TRACE("When tracing from ", vtos(v1), " to ", vtos(v2)); + LOG_TRACE(" Nudging gets us nowhere at ", vtos(pos)); + LOG_TRACE(" trace_endpos is ", vtos(trace_endpos)); + LOG_TRACE(" trace distance is ", ftos(vlen(pos - trace_endpos))); + } + + stopentity = trace_ent; + + if(trace_startsolid) + { + // we started inside solid. + // then trace from endpos to pos + t = trace_endpos; + tracebox(t, mi, ma, pos, nomonsters, forent); + ++c; + if(trace_startsolid) + { + // t is still inside solid? bad + // force advance, then, and retry + pos = t + dir * nudge; + + // but if we hit an entity, stop RIGHT before it + if(stopatentity && stopentity && stopentity != ignorestopatentity) + { + trace_ent = stopentity; + trace_endpos = t; + trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); + return c; + } + } + else + { + // we actually LEFT solid! + trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); + return c; + } + } + else + { + // pos is outside solid?!? but why?!? never mind, just return it. + trace_endpos = pos; + trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); + return c; + } + } +} + +void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) +{ + tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity); +} +#endif + #ifdef GAMEQC +/* +================== +findbetterlocation + +Returns a point at least 12 units away from walls +(useful for explosion animations, although the blast is performed where it really happened) +Ripped from DPMod +================== +*/ +vector findbetterlocation (vector org, float mindist) +{ + vector vec = mindist * '1 0 0'; + int c = 0; + while (c < 6) + { + traceline (org, org + vec, true, NULL); + vec = vec * -1; + if (trace_fraction < 1) + { + vector loc = trace_endpos; + traceline (loc, loc + vec, true, NULL); + if (trace_fraction >= 1) + org = loc + vec; + } + if (c & 1) + { + float h = vec.y; + vec.y = vec.x; + vec.x = vec.z; + vec.z = h; + } + c = c + 1; + } + + return org; +} + /* * Get "real" origin, in worldspace, even if ent is attached to something else. */ @@ -336,19 +458,18 @@ STATIC_INIT(compressShortVector) if(cvar("developer")) { - LOG_INFO("Verifying vector compression table..."); + LOG_TRACE("Verifying vector compression table..."); for(i = 0x0F00; i < 0xFFFF; ++i) if(i != compressShortVector(decompressShortVector(i))) { - LOG_INFOF( + LOG_FATALF( "BROKEN vector compression: %s -> %s -> %s", ftos(i), vtos(decompressShortVector(i)), ftos(compressShortVector(decompressShortVector(i))) ); - error("b0rk"); } - LOG_INFO("Done."); + LOG_TRACE("Done."); } } @@ -453,14 +574,12 @@ void get_mi_min_max(float mode) { vector mi, ma; - if(mi_shortname) - strunzone(mi_shortname); - mi_shortname = mapname; - if(!strcasecmp(substring(mi_shortname, 0, 5), "maps/")) - mi_shortname = substring(mi_shortname, 5, strlen(mi_shortname) - 5); - if(!strcasecmp(substring(mi_shortname, strlen(mi_shortname) - 4, 4), ".bsp")) - mi_shortname = substring(mi_shortname, 0, strlen(mi_shortname) - 4); - mi_shortname = strzone(mi_shortname); + string s = mapname; + if(!strcasecmp(substring(s, 0, 5), "maps/")) + s = substring(s, 5, strlen(s) - 5); + if(!strcasecmp(substring(s, strlen(s) - 4, 4), ".bsp")) + s = substring(s, 0, strlen(s) - 4); + strcpy(mi_shortname, s); #ifdef CSQC mi = world.mins; @@ -1129,6 +1248,8 @@ vector healtharmor_applydamage(float a, float armorblock, int deathtype, float d vector v; if (DEATH_IS(deathtype, DEATH_DROWN)) // Why should armor help here... armorblock = 0; + if (deathtype & HITTYPE_ARMORPIERCE) + armorblock = 0; v.y = bound(0, damage * armorblock, a); // save v.x = bound(0, damage - v.y, damage); // take v.z = 0; @@ -1177,6 +1298,7 @@ float matchacl(string acl, string str) if(s == t) { r = d; + break; // if we found a killing case, apply it! other settings may be allowed in the future, but this one is caught } } return r; @@ -1397,8 +1519,7 @@ void execute_next_frame() if(to_execute_next_frame) { localcmd("\n", to_execute_next_frame, "\n"); - strunzone(to_execute_next_frame); - to_execute_next_frame = string_null; + strfree(to_execute_next_frame); } } void queue_to_execute_next_frame(string s) @@ -1406,9 +1527,8 @@ void queue_to_execute_next_frame(string s) if(to_execute_next_frame) { s = strcat(s, "\n", to_execute_next_frame); - strunzone(to_execute_next_frame); } - to_execute_next_frame = strzone(s); + strcpy(to_execute_next_frame, s); } .float FindConnectedComponent_processing; diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index 3304d0e7a4..a1c0d6785f 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -1,6 +1,27 @@ #pragma once +#ifdef SVQC + #include <server/autocvars.qh> +#endif + +#ifdef SVQC +float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); // returns the number of traces done, for benchmarking + +void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); +#endif + #ifdef GAMEQC +/* +================== +findbetterlocation + +Returns a point at least 12 units away from walls +(useful for explosion animations, although the blast is performed where it really happened) +Ripped from DPMod +================== +*/ +vector findbetterlocation (vector org, float mindist); + vector real_origin(entity ent); #endif diff --git a/qcsrc/common/vehicles/cl_vehicles.qc b/qcsrc/common/vehicles/cl_vehicles.qc index 51ac4bee45..06c8484ff8 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@ -21,7 +21,7 @@ float alarm2time; void vehicle_alarm(entity e, int ch, Sound s0und) { - TC(Sound, s0und); + TC(Sound, s0und); if(!autocvar_cl_vehicles_alarm) return; diff --git a/qcsrc/common/vehicles/sv_vehicles.qc b/qcsrc/common/vehicles/sv_vehicles.qc index 0eaf69eac6..be8d468fa7 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qc +++ b/qcsrc/common/vehicles/sv_vehicles.qc @@ -205,9 +205,9 @@ void vehicles_projectile_damage(entity this, entity inflictor, entity attacker, if(inflictor.owner == this.owner) return; - this.health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.velocity += force; - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { this.takedamage = DAMAGE_NO; this.event_damage = func_null; @@ -251,7 +251,7 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, int _deahtype, float _projtype, float _health, bool _cull, bool _clianim, entity _owner) { - TC(Sound, _mzlsound); + TC(Sound, _mzlsound); entity proj; proj = spawn(); @@ -282,7 +282,7 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, { proj.takedamage = DAMAGE_AIM; proj.event_damage = vehicles_projectile_damage; - proj.health = _health; + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, _health); } else proj.flags |= FL_NOTARGET; @@ -385,7 +385,7 @@ bool vehicle_addplayerslot( entity _owner, vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string _tagname, float _pichlimit_min, float _pichlimit_max, - float _rotlimit_min, float _rotlimit_max, float _aimspeed) + float _rotlimit_min, float _rotlimit_max, float _aimspeed, float dt) { vector vtmp, vtag; float ftmp; @@ -393,7 +393,7 @@ vector vehicle_aimturret(entity _vehic, vector _target, entity _turrret, string vtmp = vectoangles(normalize(_target - vtag)); vtmp = AnglesTransform_ToAngles(AnglesTransform_LeftDivide(AnglesTransform_FromAngles(_vehic.angles), AnglesTransform_FromAngles(vtmp))) - _turrret.angles; vtmp = AnglesTransform_Normalize(vtmp, true); - ftmp = _aimspeed * frametime; + ftmp = _aimspeed * dt; vtmp_y = bound(-ftmp, vtmp_y, ftmp); vtmp_x = bound(-ftmp, vtmp_x, ftmp); _turrret.angles_y = bound(_rotlimit_min, _turrret.angles_y + vtmp_y, _rotlimit_max); @@ -587,7 +587,7 @@ void vehicles_regen(entity this, float timer, .float regen_field, float field_ma if(timer + rpause < time) { if(_healthscale) - regen = regen * (this.vehicle_health / this.max_health); + regen = regen * (GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health); this.(regen_field) = min(this.(regen_field) + regen * delta_time, field_max); @@ -596,6 +596,23 @@ void vehicles_regen(entity this, float timer, .float regen_field, float field_ma } } +void vehicles_regen_resource(entity this, float timer, .float regen_field, float field_max, float rpause, float regen, float delta_time, float _healthscale, int resource) +{ + float resource_amount = GetResourceAmount(this, resource); + + if(resource_amount < field_max) + if(timer + rpause < time) + { + if(_healthscale) + regen = regen * (resource_amount / this.max_health); + + SetResourceAmount(this, resource, min(resource_amount + regen * delta_time, field_max)); + + if(this.owner) + this.owner.(regen_field) = (GetResourceAmount(this, resource) / field_max) * 100; + } +} + void shieldhit_think(entity this) { this.alpha -= 0.1; @@ -613,7 +630,7 @@ void shieldhit_think(entity this) void vehicles_painframe(entity this) { - int myhealth = ((this.owner) ? this.owner.vehicle_health : ((this.vehicle_health / this.max_health) * 100)); + int myhealth = ((this.owner) ? this.owner.vehicle_health : ((GetResourceAmount(this, RESOURCE_HEALTH) / this.max_health) * 100)); if(myhealth <= 50) if(this.pain_frame < time) @@ -684,7 +701,7 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag if(this.vehicle_shield < 0) { - this.vehicle_health -= fabs(this.vehicle_shield); + TakeResource(this, RESOURCE_HEALTH, fabs(this.vehicle_shield)); this.vehicle_shieldent.colormod = '2 0 0'; this.vehicle_shield = 0; this.vehicle_shieldent.alpha = 0.75; @@ -699,7 +716,7 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag } else { - this.vehicle_health -= damage; + TakeResource(this, RESOURCE_HEALTH, damage); if(sound_allowed(MSG_BROADCAST, attacker)) spamsound (this, CH_PAIN, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); // FIXME: PLACEHOLDER @@ -710,7 +727,7 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag else this.velocity += force; - if(this.vehicle_health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { if(this.owner) if(this.vehicle_flags & VHF_DEATHEJECT) @@ -727,6 +744,18 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag } } +bool vehicles_heal(entity targ, entity inflictor, float amount, float limit) +{ + float true_limit = ((limit != RESOURCE_LIMIT_NONE) ? limit : targ.max_health); + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= true_limit) + return false; + + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, true_limit); + if(targ.owner) + targ.owner.vehicle_health = (GetResourceAmount(targ, RESOURCE_HEALTH) / targ.max_health) * 100; + return true; +} + bool vehicles_crushable(entity e) { if(IS_PLAYER(e) && time >= e.vehicle_enter_delay) @@ -797,7 +826,6 @@ void vehicles_exit(entity vehic, bool eject) vehicles_exit_running = true; - // TODO: this was in an IS_CLIENT check, make sure it isn't actually needed! if(vehic.vehicle_flags & VHF_PLAYERSLOT) { vehic.vehicle_exit(vehic, eject); @@ -938,7 +966,7 @@ bool vehicle_impulse(entity this, int imp) void vehicles_enter(entity pl, entity veh) { - // Remove this when bots know how to use vehicles + // Remove this when bots know how to use vehicles if((IS_BOT_CLIENT(pl) && !autocvar_g_vehicles_allow_bots)) return; @@ -1005,6 +1033,7 @@ void vehicles_enter(entity pl, entity veh) setsize(pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl)); veh.event_damage = vehicles_damage; + veh.event_heal = vehicles_heal; veh.nextthink = 0; pl.items &= ~IT_USING_JETPACK; pl.angles = veh.angles; @@ -1118,6 +1147,7 @@ void vehicles_spawn(entity this) this.owner = NULL; settouch(this, vehicles_touch); this.event_damage = vehicles_damage; + this.event_heal = vehicles_heal; this.reset = vehicles_reset; this.iscreature = true; this.teleportable = false; // no teleporting for vehicles, too buggy @@ -1230,6 +1260,7 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) this.vehicleid = info.vehicleid; this.PlayerPhysplug = info.PlayerPhysplug; this.event_damage = func_null; + this.event_heal = func_null; settouch(this, vehicles_touch); setthink(this, vehicles_spawn); this.nextthink = time; diff --git a/qcsrc/common/vehicles/sv_vehicles.qh b/qcsrc/common/vehicles/sv_vehicles.qh index 0ddd02aa7e..a1f23c1dfc 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qh +++ b/qcsrc/common/vehicles/sv_vehicles.qh @@ -45,14 +45,14 @@ float autocvar_g_vehicles_weapon_damagerate = 2; .entity gunner1; .entity gunner2; -.float vehicle_health = _STAT(VEHICLESTAT_HEALTH); /// If ent is player this is 0..100 indicating precentage of health left on vehicle. If ent is vehile, this is the real health value. -.float vehicle_energy = _STAT(VEHICLESTAT_ENERGY); /// If ent is player this is 0..100 indicating precentage of energy left on vehicle. If ent is vehile, this is the real energy value. -.float vehicle_shield = _STAT(VEHICLESTAT_SHIELD); /// If ent is player this is 0..100 indicating precentage of shield left on vehicle. If ent is vehile, this is the real shield value. +.float vehicle_health = _STAT(VEHICLESTAT_HEALTH); /// If ent is player this is 0..100 indicating precentage of health left on vehicle. Vehicle's value is the health resource +.float vehicle_energy = _STAT(VEHICLESTAT_ENERGY); /// If ent is player this is 0..100 indicating precentage of energy left on vehicle. If ent is vehicle, this is the real energy value. +.float vehicle_shield = _STAT(VEHICLESTAT_SHIELD); /// If ent is player this is 0..100 indicating precentage of shield left on vehicle. If ent is vehicle, this is the real shield value. -.float vehicle_ammo1 = _STAT(VEHICLESTAT_AMMO1); /// If ent is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already stored in vehicle_energy. If ent is vehile, this is the real ammo1 value. -.float vehicle_reload1 = _STAT(VEHICLESTAT_RELOAD1); /// If ent is player this is 0..100 indicating percentage of primary reload status. If ent is vehile, this is the real reload1 value. -.float vehicle_ammo2 = _STAT(VEHICLESTAT_AMMO2); /// If ent is player this is 0..100 indicating percentage of secondary ammo left. If ent is vehile, this is the real ammo2 value. -.float vehicle_reload2 = _STAT(VEHICLESTAT_RELOAD2); /// If ent is player this is 0..100 indicating percentage of secondary reload status. If ent is vehile, this is the real reload2 value. +.float vehicle_ammo1 = _STAT(VEHICLESTAT_AMMO1); /// If ent is player this is 0..100 indicating percentage of primary ammo left UNLESS value is already stored in vehicle_energy. If ent is vehicle, this is the real ammo1 value. +.float vehicle_reload1 = _STAT(VEHICLESTAT_RELOAD1); /// If ent is player this is 0..100 indicating percentage of primary reload status. If ent is vehicle, this is the real reload1 value. +.float vehicle_ammo2 = _STAT(VEHICLESTAT_AMMO2); /// If ent is player this is 0..100 indicating percentage of secondary ammo left. If ent is vehicle, this is the real ammo2 value. +.float vehicle_reload2 = _STAT(VEHICLESTAT_RELOAD2); /// If ent is player this is 0..100 indicating percentage of secondary reload status. If ent is vehicle, this is the real reload2 value. .float sound_nexttime; const float VOL_VEHICLEENGINE = 1; @@ -79,7 +79,6 @@ const int MAX_AXH = 4; .float lock_strength; .float lock_time; .float lock_soundtime; -const float DAMAGE_TARGETDRONE = 10; // vehicle functions .void(int _spawnflag) vehicle_spawn; /// Vehicles custom fucntion to be efecuted when vehicle (re)spawns @@ -87,7 +86,7 @@ const float DAMAGE_TARGETDRONE = 10; .void(entity this, int exit_flags) vehicle_exit; .bool(entity this, entity player) vehicle_enter; const int VHEF_NORMAL = 0; /// User pressed exit key -const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehile is dying +const int VHEF_EJECT = 1; /// User pressed exit key 3 times fast (not implemented) or vehicle is dying const int VHEF_RELEASE = 2; /// Release ownership, client possibly allready dissconnected / went spec / changed team / used "kill" (not implemented) float force_fromtag_power; @@ -100,6 +99,9 @@ float vehicles_exit_running; #define VEHICLE_UPDATE_PLAYER(ply,vehi,fld,vhname) \ ply.vehicle_##fld = (vehi.vehicle_##fld / autocvar_g_vehicle_##vhname##_##fld) * 100 +#define VEHICLE_UPDATE_PLAYER_RESOURCE(ply,vehi,fld,vhname,res) \ + ply.vehicle_##fld = (GetResourceAmount(vehi, res) / autocvar_g_vehicle_##vhname##_##fld) * 100 + .float vehicle_enter_delay; // prevent players jumping to and from vehicles instantly void vehicles_exit(entity vehic, int eject); @@ -107,6 +109,7 @@ bool vehicle_initialize(entity this, Vehicle info, float nodrop); bool vehicle_impulse(entity this, int imp); bool vehicles_crushable(entity e); float vehicle_altitude(entity this, float amax); +void vehicles_enter(entity pl, entity veh); IntrusiveList g_vehicle_returners; STATIC_INIT(g_vehicle_returners) { g_vehicle_returners = IL_NEW(); } diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 4e842a865c..377b193087 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -15,7 +15,7 @@ float autocvar_g_vehicle_bumblebee_turnspeed = 120; float autocvar_g_vehicle_bumblebee_pitchspeed = 60; float autocvar_g_vehicle_bumblebee_pitchlimit = 60; float autocvar_g_vehicle_bumblebee_friction = 0.5; -bool autocvar_g_vehicle_bumblebee_swim = false; +bool autocvar_g_vehicle_bumblebee_swim = true; float autocvar_g_vehicle_bumblebee_energy = 500; float autocvar_g_vehicle_bumblebee_energy_regen = 50; @@ -33,9 +33,9 @@ float autocvar_g_vehicle_bumblebee_cannon_ammo = 100; float autocvar_g_vehicle_bumblebee_cannon_ammo_regen = 100; float autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause = 1; -float autocvar_g_vehicle_bumblebee_cannon_lock = 0; +float autocvar_g_vehicle_bumblebee_cannon_lock = 1; -float autocvar_g_vehicle_bumblebee_cannon_turnspeed = 160; +float autocvar_g_vehicle_bumblebee_cannon_turnspeed = 260; float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down = 60; float autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up = 60; float autocvar_g_vehicle_bumblebee_cannon_turnlimit_in = 20; @@ -105,20 +105,28 @@ bool bumblebee_gunner_frame(entity this, float dt) if(autocvar_g_vehicle_bumblebee_cannon_lock) { - if(gun.lock_time < time) + if(gun.lock_time < time || IS_DEAD(gun.enemy) || STAT(FROZEN, gun.enemy)) gun.enemy = NULL; if(trace_ent) - if(trace_ent.move_movetype) - if(trace_ent.takedamage) - if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent)) - { - if(DIFF_TEAM(trace_ent, this)) - { - gun.enemy = trace_ent; - gun.lock_time = time + 5; - } - } + if(trace_ent.move_movetype) + if(trace_ent.takedamage) + if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent)) + { + if(teamplay) + { + if(DIFF_TEAM(trace_ent, this)) + { + gun.enemy = trace_ent; + gun.lock_time = time + 2.5; + } + } + else + { + gun.enemy = trace_ent; + gun.lock_time = time + 0.5; + } + } } if(gun.enemy) @@ -141,13 +149,13 @@ bool bumblebee_gunner_frame(entity this, float dt) UpdateAuxiliaryXhair(this, ad, '1 0 1', 1); vehicle_aimturret(vehic, trace_endpos, gun, "fire", autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, - _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed, dt); } else vehicle_aimturret(vehic, _ct, gun, "fire", autocvar_g_vehicle_bumblebee_cannon_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_cannon_pitchlimit_up, - _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed); + _out * -1, _in, autocvar_g_vehicle_bumblebee_cannon_turnspeed, dt); if(!forbidWeaponUse(this)) if(PHYS_INPUT_BUTTON_ATCK(this)) @@ -160,7 +168,7 @@ bool bumblebee_gunner_frame(entity this, float dt) gun.attack_finished_single[0] = time + autocvar_g_vehicle_bumblebee_cannon_refire; } - VEHICLE_UPDATE_PLAYER(this, vehic, health, bumblebee); + VEHICLE_UPDATE_PLAYER_RESOURCE(this, vehic, health, bumblebee, RESOURCE_HEALTH); if(vehic.vehicle_flags & VHF_HASSHIELD) VEHICLE_UPDATE_PLAYER(this, vehic, shield, bumblebee); @@ -389,7 +397,7 @@ void bumblebee_regen(entity this, float dt) vehicles_regen(this, this.dmg_time, vehicle_shield, autocvar_g_vehicle_bumblebee_shield, autocvar_g_vehicle_bumblebee_shield_regen_pause, autocvar_g_vehicle_bumblebee_shield_regen, dt, true); if(this.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, dt, false); + vehicles_regen_resource(this, this.dmg_time, vehicle_health, autocvar_g_vehicle_bumblebee_health, autocvar_g_vehicle_bumblebee_health_regen_pause, autocvar_g_vehicle_bumblebee_health_regen, dt, false, RESOURCE_HEALTH); if(this.vehicle_flags & VHF_ENERGYREGEN) vehicles_regen(this, this.wait, vehicle_energy, autocvar_g_vehicle_bumblebee_energy, autocvar_g_vehicle_bumblebee_energy_regen_pause, autocvar_g_vehicle_bumblebee_energy_regen, dt, false); @@ -521,7 +529,7 @@ bool bumblebee_pilot_frame(entity this, float dt) vang = vehicle_aimturret(vehic, trace_endpos, vehic.gun3, "fire", autocvar_g_vehicle_bumblebee_raygun_pitchlimit_down * -1, autocvar_g_vehicle_bumblebee_raygun_pitchlimit_up, - autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1, autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides, autocvar_g_vehicle_bumblebee_raygun_turnspeed); + autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1, autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides, autocvar_g_vehicle_bumblebee_raygun_turnspeed, dt); if(!forbidWeaponUse(this)) if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME || autocvar_g_vehicle_bumblebee_raygun == 0)) @@ -544,36 +552,27 @@ bool bumblebee_pilot_frame(entity this, float dt) else { if(!IS_DEAD(trace_ent)) + { if((teamplay && trace_ent.team == this.team) || !teamplay) { + if(autocvar_g_vehicle_bumblebee_healgun_hps) + { + float hplimit = ((IS_PLAYER(trace_ent)) ? autocvar_g_vehicle_bumblebee_healgun_hmax : RESOURCE_LIMIT_NONE); + Heal(trace_ent, this, autocvar_g_vehicle_bumblebee_healgun_hps * dt, hplimit); + } if(IS_VEHICLE(trace_ent)) { - if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health) + if(autocvar_g_vehicle_bumblebee_healgun_sps && GetResourceAmount(trace_ent, RESOURCE_HEALTH) <= trace_ent.max_health) trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * dt, trace_ent.tur_head.max_health); - - if(autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.vehicle_health = min(trace_ent.vehicle_health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health); } else if(IS_CLIENT(trace_ent)) { - if(trace_ent.health <= autocvar_g_vehicle_bumblebee_healgun_hmax && autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax); - - if(trace_ent.armorvalue <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) - trace_ent.armorvalue = min(trace_ent.armorvalue + autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax); - - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, autocvar_g_vehicle_bumblebee_healgun_hmax); - } - else if(IS_TURRET(trace_ent)) - { - if(trace_ent.health <= trace_ent.max_health && autocvar_g_vehicle_bumblebee_healgun_hps) - trace_ent.health = min(trace_ent.health + autocvar_g_vehicle_bumblebee_healgun_hps * dt, trace_ent.max_health); - //else ..hmmm what? ammo? - - trace_ent.SendFlags |= TNSF_STATUS; + if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= autocvar_g_vehicle_bumblebee_healgun_amax && autocvar_g_vehicle_bumblebee_healgun_aps) + GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, autocvar_g_vehicle_bumblebee_healgun_aps * dt, autocvar_g_vehicle_bumblebee_healgun_amax); } } + } } } @@ -593,7 +592,7 @@ bool bumblebee_pilot_frame(entity this, float dt) } */ - VEHICLE_UPDATE_PLAYER(this, vehic, health, bumblebee); + VEHICLE_UPDATE_PLAYER_RESOURCE(this, vehic, health, bumblebee, RESOURCE_HEALTH); VEHICLE_UPDATE_PLAYER(this, vehic, energy, bumblebee); this.vehicle_ammo1 = (vehic.gun1.vehicle_energy / autocvar_g_vehicle_bumblebee_cannon_ammo) * 100; @@ -803,7 +802,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance)) Send_Effect(EFFECT_EXPLOSION_MEDIUM, findbetterlocation(instance.origin, 16), '0 0 0', 1); - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_NOT; instance.takedamage = DAMAGE_NO; @@ -888,7 +887,7 @@ METHOD(Bumblebee, vr_spawn, void(Bumblebee thisveh, entity instance)) if(!autocvar_g_vehicle_bumblebee_swim) instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK; - instance.vehicle_health = autocvar_g_vehicle_bumblebee_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_bumblebee_health); instance.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; instance.solid = SOLID_BBOX; set_movetype(instance, MOVETYPE_TOSS); @@ -915,8 +914,8 @@ METHOD(Bumblebee, vr_setup, void(Bumblebee thisveh, entity instance)) instance.vehicle_exit = bumblebee_exit; instance.respawntime = autocvar_g_vehicle_bumblebee_respawntime; - instance.vehicle_health = autocvar_g_vehicle_bumblebee_health; - instance.max_health = instance.vehicle_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_bumblebee_health); + instance.max_health = GetResourceAmount(instance, RESOURCE_HEALTH); instance.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; } @@ -939,7 +938,7 @@ METHOD(Bumblebee, vr_hud, void(Bumblebee thisveh)) float hudAlpha = autocvar_hud_panel_fg_alpha; float blinkValue = 0.55 + sin(time * 7) * 0.45; vector tmpPos = '0 0 0'; - vector tmpSize = '1 1 1' * hud_fontsize; + vector tmpSize = hud_fontsize; tmpPos.x = vehicleHud_Pos.x + vehicleHud_Size.x * (520/768); if(!AuxiliaryXhair[1].draw2d) diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh index e9c5bf41d0..f6909fb42a 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh @@ -8,7 +8,7 @@ float autocvar_g_vehicle_bumblebee_cannon_damage = 60; float autocvar_g_vehicle_bumblebee_cannon_radius = 225; float autocvar_g_vehicle_bumblebee_cannon_refire = 0.2; float autocvar_g_vehicle_bumblebee_cannon_speed = 20000; -float autocvar_g_vehicle_bumblebee_cannon_spread = 0.02; +float autocvar_g_vehicle_bumblebee_cannon_spread = 0; float autocvar_g_vehicle_bumblebee_cannon_force = -35; #endif diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 70ee753e0e..c7f7af8ac6 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -1,7 +1,7 @@ #include "racer.qh" #ifdef SVQC -#include <common/triggers/trigger/impulse.qh> +#include <common/mapobjects/trigger/impulse.qh> bool autocvar_g_vehicle_racer = true; @@ -137,16 +137,16 @@ void racer_align4point(entity this, float _delta) this.angles_z *= 1 - (autocvar_g_vehicle_racer_anglestabilizer * _delta); } -void racer_fire_rocket_aim(entity player, string tagname, entity trg) +void racer_fire_rocket_aim(entity this, entity player, string tagname, entity trg) { - entity racer = player.vehicle; - vector v = gettaginfo(racer, gettagindex(racer, tagname)); + vector v = gettaginfo(this, gettagindex(this, tagname)); racer_fire_rocket(player, v, v_forward, trg); } bool racer_frame(entity this, float dt) { - entity vehic = this.vehicle; + entity player = this; + entity vehic = player.vehicle; return = true; if(game_stopped) @@ -157,28 +157,27 @@ bool racer_frame(entity this, float dt) return; } - vehicles_frame(vehic, this); + vehicles_frame(vehic, player); - traceline(vehic.origin, vehic.origin + '0 0 1', MOVE_NOMONSTERS, this); - int cont = trace_dpstartcontents; + int cont = Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(vehic.origin)); if(!(cont & DPCONTENTS_WATER)) vehic.air_finished = time + autocvar_g_vehicle_racer_water_time; if(IS_DEAD(vehic)) { - PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; + PHYS_INPUT_BUTTON_ATCK(player) = PHYS_INPUT_BUTTON_ATCK2(player) = false; return; } racer_align4point(vehic, dt); - PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false; + PHYS_INPUT_BUTTON_ZOOM(player) = PHYS_INPUT_BUTTON_CROUCH(player) = false; vehic.angles_x *= -1; // Yaw float ftmp = autocvar_g_vehicle_racer_turnspeed * dt; - ftmp = bound(-ftmp, shortangle_f(this.v_angle_y - vehic.angles_y, vehic.angles_y), ftmp); + ftmp = bound(-ftmp, shortangle_f(player.v_angle_y - vehic.angles_y, vehic.angles_y), ftmp); vehic.angles_y = anglemods(vehic.angles_y + ftmp); // Roll @@ -186,7 +185,7 @@ bool racer_frame(entity this, float dt) // Pitch ftmp = autocvar_g_vehicle_racer_pitchspeed * dt; - ftmp = bound(-ftmp, shortangle_f(this.v_angle_x - vehic.angles_x, vehic.angles_x), ftmp); + ftmp = bound(-ftmp, shortangle_f(player.v_angle_x - vehic.angles_x, vehic.angles_x), ftmp); vehic.angles_x = bound(-autocvar_g_vehicle_racer_pitchlimit, anglemods(vehic.angles_x + ftmp), autocvar_g_vehicle_racer_pitchlimit); makevectors(vehic.angles); @@ -196,17 +195,17 @@ bool racer_frame(entity this, float dt) vector df = vehic.velocity * -autocvar_g_vehicle_racer_friction; //vehic.velocity_z = ftmp; - if(CS(this).movement) + if(CS(player).movement) { if(cont & DPCONTENTS_LIQUIDSMASK) { - if(CS(this).movement_x) { df += v_forward * ((CS(this).movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); } - if(CS(this).movement_y) { df += v_right * ((CS(this).movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); } + if(CS(player).movement_x) { df += v_forward * ((CS(player).movement_x > 0) ? autocvar_g_vehicle_racer_water_speed_forward : -autocvar_g_vehicle_racer_water_speed_forward); } + if(CS(player).movement_y) { df += v_right * ((CS(player).movement_y > 0) ? autocvar_g_vehicle_racer_water_speed_strafe : -autocvar_g_vehicle_racer_water_speed_strafe); } } else { - if(CS(this).movement_x) { df += v_forward * ((CS(this).movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); } - if(CS(this).movement_y) { df += v_right * ((CS(this).movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); } + if(CS(player).movement_x) { df += v_forward * ((CS(player).movement_x > 0) ? autocvar_g_vehicle_racer_speed_forward : -autocvar_g_vehicle_racer_speed_forward); } + if(CS(player).movement_y) { df += v_right * ((CS(player).movement_y > 0) ? autocvar_g_vehicle_racer_speed_strafe : -autocvar_g_vehicle_racer_speed_strafe); } } #ifdef SVQC @@ -231,7 +230,7 @@ bool racer_frame(entity this, float dt) #endif // Afterburn - if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * dt)) + if (PHYS_INPUT_BUTTON_JUMP(player) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * dt)) { #ifdef SVQC if(time - vehic.wait > 0.2) @@ -282,14 +281,14 @@ bool racer_frame(entity this, float dt) dforce = autocvar_g_vehicle_racer_water_downforce; df -= v_up * (vlen(vehic.velocity) * dforce); - CS(this).movement = vehic.velocity += df * dt; + CS(player).movement = vehic.velocity += df * dt; #ifdef SVQC Weapon wep1 = WEP_RACER; .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if (!forbidWeaponUse(this)) - if (PHYS_INPUT_BUTTON_ATCK(this)) + if (!forbidWeaponUse(player)) + if (PHYS_INPUT_BUTTON_ATCK(player)) if (wep1.wr_checkammo1(wep1, vehic, weaponentity)) { string tagname = (vehic.cnt) @@ -299,7 +298,7 @@ bool racer_frame(entity this, float dt) w_shotorg = org; w_shotdir = v_forward; // Fix z-aim (for chase mode) - crosshair_trace(this); + crosshair_trace(player); w_shotdir.z = normalize(trace_endpos - org).z * 0.5; wep1.wr_think(wep1, vehic, weaponentity, 1); } @@ -308,7 +307,7 @@ bool racer_frame(entity this, float dt) { if(time >= vehic.vehicle_last_trace) { - crosshair_trace(this); + crosshair_trace(player); vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * dt, (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * dt, @@ -320,63 +319,63 @@ bool racer_frame(entity this, float dt) if(vehic.lock_target) { if(vehic.lock_strength == 1) - UpdateAuxiliaryXhair(this, real_origin(vehic.lock_target), '1 0 0', 0); + UpdateAuxiliaryXhair(player, real_origin(vehic.lock_target), '1 0 0', 0); else if(vehic.lock_strength > 0.5) - UpdateAuxiliaryXhair(this, real_origin(vehic.lock_target), '0 1 0', 0); + UpdateAuxiliaryXhair(player, real_origin(vehic.lock_target), '0 1 0', 0); else if(vehic.lock_strength < 0.5) - UpdateAuxiliaryXhair(this, real_origin(vehic.lock_target), '0 0 1', 0); + UpdateAuxiliaryXhair(player, real_origin(vehic.lock_target), '0 0 1', 0); } } - if(!forbidWeaponUse(this)) + if(!forbidWeaponUse(player)) if(time > vehic.delay) - if(PHYS_INPUT_BUTTON_ATCK2(this)) + if(PHYS_INPUT_BUTTON_ATCK2(player)) { vehic.misc_bulletcounter += 1; vehic.delay = time + 0.3; if(vehic.misc_bulletcounter == 1) { - racer_fire_rocket_aim(this, "tag_rocket_r", (vehic.lock_strength == 1 && vehic.lock_target) ? vehic.lock_target : NULL); - this.vehicle_ammo2 = 50; + racer_fire_rocket_aim(vehic, player, "tag_rocket_r", (vehic.lock_strength == 1 && vehic.lock_target) ? vehic.lock_target : NULL); + player.vehicle_ammo2 = 50; } else if(vehic.misc_bulletcounter == 2) { - racer_fire_rocket_aim(this, "tag_rocket_l", (vehic.lock_strength == 1 && vehic.lock_target) ? vehic.lock_target : NULL); + racer_fire_rocket_aim(vehic, player, "tag_rocket_l", (vehic.lock_strength == 1 && vehic.lock_target) ? vehic.lock_target : NULL); vehic.lock_strength = 0; vehic.lock_target = NULL; vehic.misc_bulletcounter = 0; vehic.delay = time + autocvar_g_vehicle_racer_rocket_refire; vehic.lip = time; - this.vehicle_ammo2 = 0; + player.vehicle_ammo2 = 0; } } else if(vehic.misc_bulletcounter == 0) - this.vehicle_ammo2 = 100; + player.vehicle_ammo2 = 100; - this.vehicle_reload2 = bound(0, 100 * ((time - vehic.lip) / (vehic.delay - vehic.lip)), 100); + player.vehicle_reload2 = bound(0, 100 * ((time - vehic.lip) / (vehic.delay - vehic.lip)), 100); - if(vehic.vehicle_flags & VHF_SHIELDREGEN) + if(vehic.vehicle_flags & VHF_SHIELDREGEN) vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_racer_shield, autocvar_g_vehicle_racer_shield_regen_pause, autocvar_g_vehicle_racer_shield_regen, dt, true); - if(vehic.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, dt, false); + if(vehic.vehicle_flags & VHF_HEALTHREGEN) + vehicles_regen_resource(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_racer_health, autocvar_g_vehicle_racer_health_regen_pause, autocvar_g_vehicle_racer_health_regen, dt, false, RESOURCE_HEALTH); - if(vehic.vehicle_flags & VHF_ENERGYREGEN) + if(vehic.vehicle_flags & VHF_ENERGYREGEN) vehicles_regen(vehic, vehic.wait, vehicle_energy, autocvar_g_vehicle_racer_energy, autocvar_g_vehicle_racer_energy_regen_pause, autocvar_g_vehicle_racer_energy_regen, dt, false); - VEHICLE_UPDATE_PLAYER(this, vehic, health, racer); - VEHICLE_UPDATE_PLAYER(this, vehic, energy, racer); + VEHICLE_UPDATE_PLAYER_RESOURCE(player, vehic, health, racer, RESOURCE_HEALTH); + VEHICLE_UPDATE_PLAYER(player, vehic, energy, racer); if(vehic.vehicle_flags & VHF_HASSHIELD) - VEHICLE_UPDATE_PLAYER(this, vehic, shield, racer); + VEHICLE_UPDATE_PLAYER(player, vehic, shield, racer); - PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; + PHYS_INPUT_BUTTON_ATCK(player) = PHYS_INPUT_BUTTON_ATCK2(player) = false; #endif - setorigin(this, vehic.origin + '0 0 32'); - this.oldorigin = this.origin; // negate fall damage - this.velocity = vehic.velocity; + setorigin(player, vehic.origin + '0 0 32'); + player.oldorigin = player.origin; // negate fall damage + player.velocity = vehic.velocity; } void racer_think(entity this) @@ -515,7 +514,7 @@ METHOD(Racer, vr_enter, void(Racer thisveh, entity instance)) { #ifdef SVQC set_movetype(instance, MOVETYPE_BOUNCE); - instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_racer_health) * 100; + instance.owner.vehicle_health = (GetResourceAmount(instance, RESOURCE_HEALTH) / autocvar_g_vehicle_racer_health) * 100; instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_racer_shield) * 100; if(instance.owner.flagcarried) @@ -545,7 +544,7 @@ METHOD(Racer, vr_spawn, void(Racer thisveh, entity instance)) setthink(instance, racer_think); instance.nextthink = time; - instance.vehicle_health = autocvar_g_vehicle_racer_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_racer_health); instance.vehicle_shield = autocvar_g_vehicle_racer_shield; set_movetype(instance, MOVETYPE_TOSS); @@ -558,7 +557,7 @@ METHOD(Racer, vr_spawn, void(Racer thisveh, entity instance)) instance.bouncefactor = autocvar_g_vehicle_racer_bouncefactor; instance.bouncestop = autocvar_g_vehicle_racer_bouncestop; instance.damageforcescale = 0.5; - instance.vehicle_health = autocvar_g_vehicle_racer_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_racer_health); instance.vehicle_shield = autocvar_g_vehicle_racer_shield; #endif } @@ -567,7 +566,7 @@ METHOD(Racer, vr_death, void(Racer thisveh, entity instance)) { #ifdef SVQC setSendEntity(instance, func_null); // stop networking this racer (for now) - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; @@ -626,9 +625,9 @@ METHOD(Racer, vr_setup, void(Racer thisveh, entity instance)) instance.vehicle_flags |= VHF_HEALTHREGEN; instance.respawntime = autocvar_g_vehicle_racer_respawntime; - instance.vehicle_health = autocvar_g_vehicle_racer_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_racer_health); instance.vehicle_shield = autocvar_g_vehicle_racer_shield; - instance.max_health = instance.vehicle_health; + instance.max_health = GetResourceAmount(instance, RESOURCE_HEALTH); #endif #ifdef CSQC diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index bf3e443620..0150ee98f9 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -343,11 +343,11 @@ bool raptor_frame(entity this, float dt) vehicle_aimturret(vehic, trace_endpos, vehic.gun1, "fire1", autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, - autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed, dt); vehicle_aimturret(vehic, trace_endpos, vehic.gun2, "fire1", autocvar_g_vehicle_raptor_cannon_pitchlimit_down * -1, autocvar_g_vehicle_raptor_cannon_pitchlimit_up, - autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed); + autocvar_g_vehicle_raptor_cannon_turnlimit * -1, autocvar_g_vehicle_raptor_cannon_turnlimit, autocvar_g_vehicle_raptor_cannon_turnspeed, dt); /* ad = ad * 0.5; @@ -369,7 +369,7 @@ bool raptor_frame(entity this, float dt) vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true); if(vehic.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false); + vehicles_regen_resource(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false, RESOURCE_HEALTH); if(vehic.vehicle_flags & VHF_ENERGYREGEN) vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false); @@ -427,7 +427,7 @@ bool raptor_frame(entity this, float dt) } - VEHICLE_UPDATE_PLAYER(this, vehic, health, raptor); + VEHICLE_UPDATE_PLAYER_RESOURCE(this, vehic, health, raptor, RESOURCE_HEALTH); VEHICLE_UPDATE_PLAYER(this, vehic, energy, raptor); if(vehic.vehicle_flags & VHF_HASSHIELD) VEHICLE_UPDATE_PLAYER(this, vehic, shield, raptor); @@ -471,7 +471,7 @@ bool raptor_takeoff(entity this, float dt) vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_raptor_shield, autocvar_g_vehicle_raptor_shield_regen_pause, autocvar_g_vehicle_raptor_shield_regen, dt, true); if(vehic.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false); + vehicles_regen_resource(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_raptor_health, autocvar_g_vehicle_raptor_health_regen_pause, autocvar_g_vehicle_raptor_health_regen, dt, false, RESOURCE_HEALTH); if(vehic.vehicle_flags & VHF_ENERGYREGEN) vehicles_regen(vehic, vehic.cnt, vehicle_energy, autocvar_g_vehicle_raptor_energy, autocvar_g_vehicle_raptor_energy_regen_pause, autocvar_g_vehicle_raptor_energy_regen, dt, false); @@ -481,7 +481,7 @@ bool raptor_takeoff(entity this, float dt) this.vehicle_reload2 = bound(0, vehic.bomb1.alpha * 100, 100); this.vehicle_ammo2 = (this.vehicle_reload2 == 100) ? 100 : 0; - VEHICLE_UPDATE_PLAYER(this, vehic, health, raptor); + VEHICLE_UPDATE_PLAYER_RESOURCE(this, vehic, health, raptor, RESOURCE_HEALTH); VEHICLE_UPDATE_PLAYER(this, vehic, energy, raptor); if(vehic.vehicle_flags & VHF_HASSHIELD) VEHICLE_UPDATE_PLAYER(this, vehic, shield, raptor); @@ -594,7 +594,7 @@ METHOD(Raptor, vr_enter, void(Raptor thisveh, entity instance)) instance.owner.PlayerPhysplug = raptor_takeoff; set_movetype(instance, MOVETYPE_BOUNCEMISSILE); instance.solid = SOLID_SLIDEBOX; - instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_raptor_health) * 100; + instance.owner.vehicle_health = (GetResourceAmount(instance, RESOURCE_HEALTH) / autocvar_g_vehicle_raptor_health) * 100; instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100; instance.velocity = '0 0 1'; // nudge upwards so takeoff sequence can work instance.tur_head.exteriormodeltoclient = instance.owner; @@ -609,7 +609,7 @@ METHOD(Raptor, vr_enter, void(Raptor thisveh, entity instance)) } METHOD(Raptor, vr_death, void(Raptor thisveh, entity instance)) { - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; @@ -701,7 +701,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance)) } instance.frame = 0; - instance.vehicle_health = autocvar_g_vehicle_raptor_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_raptor_health); instance.vehicle_shield = autocvar_g_vehicle_raptor_shield; set_movetype(instance, MOVETYPE_TOSS); instance.solid = SOLID_SLIDEBOX; @@ -720,7 +720,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance)) instance.bouncefactor = autocvar_g_vehicle_raptor_bouncefactor; instance.bouncestop = autocvar_g_vehicle_raptor_bouncestop; instance.damageforcescale = 0.25; - instance.vehicle_health = autocvar_g_vehicle_raptor_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_raptor_health); instance.vehicle_shield = autocvar_g_vehicle_raptor_shield; } METHOD(Raptor, vr_setup, void(Raptor thisveh, entity instance)) @@ -739,9 +739,9 @@ METHOD(Raptor, vr_setup, void(Raptor thisveh, entity instance)) instance.vehicle_exit = raptor_exit; instance.respawntime = autocvar_g_vehicle_raptor_respawntime; - instance.vehicle_health = autocvar_g_vehicle_raptor_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_raptor_health); instance.vehicle_shield = autocvar_g_vehicle_raptor_shield; - instance.max_health = instance.vehicle_health; + instance.max_health = GetResourceAmount(instance, RESOURCE_HEALTH); if(!autocvar_g_vehicle_raptor_swim) instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK; diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc index 37c4fc391f..53475d6cfd 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc @@ -74,7 +74,7 @@ METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weapone _flare.solid = SOLID_CORPSE; _flare.takedamage = DAMAGE_YES; _flare.event_damage = raptor_flare_damage; - _flare.health = 20; + SetResourceAmountExplicit(_flare, RESOURCE_HEALTH, 20); _flare.tur_impacttime = time + autocvar_g_vehicle_raptor_flare_lifetime; settouch(_flare, raptor_flare_touch); } @@ -191,8 +191,8 @@ void raptor_flare_touch(entity this, entity toucher) void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - this.health -= damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) delete(this); } diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index 994a642d73..323b025e0e 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -264,7 +264,7 @@ bool spiderbot_frame(entity this, float dt) .entity weaponentity = weaponentities[0]; // TODO: unhardcode fireBullet(this, weaponentity, v, v_forward, autocvar_g_vehicle_spiderbot_minigun_spread, autocvar_g_vehicle_spiderbot_minigun_solidpenetration, - autocvar_g_vehicle_spiderbot_minigun_damage, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_VH_SPID_MINIGUN.m_id, 0); + autocvar_g_vehicle_spiderbot_minigun_damage, autocvar_g_vehicle_spiderbot_minigun_force, DEATH_VH_SPID_MINIGUN.m_id, EFFECT_BULLET); sound (gun, CH_WEAPON_A, SND_UZI_FIRE, VOL_BASE, ATTEN_NORM); //trailparticles(this, _particleeffectnum("spiderbot_minigun_trail"), v, trace_endpos); @@ -294,7 +294,7 @@ bool spiderbot_frame(entity this, float dt) vehicles_regen(vehic, vehic.dmg_time, vehicle_shield, autocvar_g_vehicle_spiderbot_shield, autocvar_g_vehicle_spiderbot_shield_regen_pause, autocvar_g_vehicle_spiderbot_shield_regen, dt, true); if(vehic.vehicle_flags & VHF_HEALTHREGEN) - vehicles_regen(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, dt, false); + vehicles_regen_resource(vehic, vehic.dmg_time, vehicle_health, autocvar_g_vehicle_spiderbot_health, autocvar_g_vehicle_spiderbot_health_regen_pause, autocvar_g_vehicle_spiderbot_health_regen, dt, false, RESOURCE_HEALTH); PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; //this.vehicle_ammo2 = vehic.tur_head.frame; @@ -309,7 +309,7 @@ bool spiderbot_frame(entity this, float dt) this.oldorigin = this.origin; // negate fall damage this.velocity = vehic.velocity; - VEHICLE_UPDATE_PLAYER(this, vehic, health, spiderbot); + VEHICLE_UPDATE_PLAYER_RESOURCE(this, vehic, health, spiderbot, RESOURCE_HEALTH); if(vehic.vehicle_flags & VHF_HASSHIELD) VEHICLE_UPDATE_PLAYER(this, vehic, shield, spiderbot); @@ -524,7 +524,7 @@ METHOD(Spiderbot, vr_enter, void(Spiderbot thisveh, entity instance)) STAT(VEHICLESTAT_W2MODE, instance) = SBRM_GUIDE; set_movetype(instance, MOVETYPE_WALK); CSQCVehicleSetup(instance.owner, 0); - instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_spiderbot_health) * 100; + instance.owner.vehicle_health = (GetResourceAmount(instance, RESOURCE_HEALTH) / autocvar_g_vehicle_spiderbot_health) * 100; instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100; if(instance.owner.flagcarried) @@ -540,7 +540,7 @@ METHOD(Spiderbot, vr_think, void(Spiderbot thisveh, entity instance)) } METHOD(Spiderbot, vr_death, void(Spiderbot thisveh, entity instance)) { - instance.health = 0; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, 0); instance.event_damage = func_null; instance.takedamage = DAMAGE_NO; settouch(instance, func_null); @@ -582,7 +582,7 @@ METHOD(Spiderbot, vr_spawn, void(Spiderbot thisveh, entity instance)) setorigin(instance, instance.pos1 + '0 0 128'); instance.angles = instance.pos2; instance.damageforcescale = 0.03; - instance.vehicle_health = autocvar_g_vehicle_spiderbot_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_spiderbot_health); instance.vehicle_shield = autocvar_g_vehicle_spiderbot_shield; instance.PlayerPhysplug = spiderbot_frame; @@ -599,9 +599,9 @@ METHOD(Spiderbot, vr_setup, void(Spiderbot thisveh, entity instance)) instance.vehicle_flags |= VHF_HEALTHREGEN; instance.respawntime = autocvar_g_vehicle_spiderbot_respawntime; - instance.vehicle_health = autocvar_g_vehicle_spiderbot_health; + SetResourceAmountExplicit(instance, RESOURCE_HEALTH, autocvar_g_vehicle_spiderbot_health); instance.vehicle_shield = autocvar_g_vehicle_spiderbot_shield; - instance.max_health = instance.vehicle_health; + instance.max_health = GetResourceAmount(instance, RESOURCE_HEALTH); instance.pushable = true; // spiderbot can use jumppads } diff --git a/qcsrc/common/viewloc.qc b/qcsrc/common/viewloc.qc index e4e5ba991b..e1be43af0d 100644 --- a/qcsrc/common/viewloc.qc +++ b/qcsrc/common/viewloc.qc @@ -48,7 +48,7 @@ void viewloc_PlayerPhysics(entity this) if(PHYS_CS(this).movement_x > 0) // right this.angles_y = forward.y; } - + #if 0 //if(!PHYS_INPUT_BUTTON_CROUCH(this) && !IS_DUCKED(this)) if(!(this.viewloc.spawnflags & VIEWLOC_FREEMOVE)) { @@ -65,6 +65,7 @@ void viewloc_PlayerPhysics(entity this) //else { input_buttons &= ~16; this.flags &= ~FL_DUCKED; } #endif } + #endif } } @@ -199,4 +200,10 @@ void viewloc_SetViewLocation() } } +STATIC_INIT_LATE(viewloc_cursor) +{ + // fix the mouse position on init so it isn't in the corner + viewloc_mousepos = '0.5 0 0' * autocvar_vid_conwidth + '0 0.5 0' * autocvar_vid_conheight; +} + #endif diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index dca20d0cdb..f118a04667 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -123,6 +123,8 @@ string W_UndeprecateName(string s) case "minstanex": return "vaporizer"; case "grenadelauncher": return "mortar"; case "uzi": return "machinegun"; + case "hmg": return "okhmg"; + case "rpc": return "okrpc"; default: return s; } } @@ -192,9 +194,8 @@ string W_FixWeaponOrder_ForceComplete(string order) return W_FixWeaponOrder(order, 1); } -void W_RandomWeapons(entity e, int n) +WepSet W_RandomWeapons(entity e, WepSet remaining, int n) { - WepSet remaining = e.weapons; WepSet result = '0 0 0'; for (int j = 0; j < n; ++j) { @@ -207,7 +208,7 @@ void W_RandomWeapons(entity e, int n) result |= WepSet_FromWeapon(w); remaining &= ~WepSet_FromWeapon(w); } - e.weapons = result; + return result; } string GetAmmoPicture(int ammotype) diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh index 0af4759007..9a32b42778 100644 --- a/qcsrc/common/weapons/all.qh +++ b/qcsrc/common/weapons/all.qh @@ -130,7 +130,7 @@ REGISTER_WEAPON(Null, NEW(Weapon)); Weapon Weapons_fromstr(string s) { FOREACH(Weapons, it != WEP_Null && it.netname == s, return it); - return NULL; + return WEP_Null; } @@ -304,7 +304,6 @@ STATIC_INIT(register_weapons_done) WepSet set = it.m_wepset = _WepSet_FromWeapon(it.m_id = i); WEPSET_ALL |= set; if ((it.spawnflags) & WEP_FLAG_SUPERWEAPON) WEPSET_SUPERWEAPONS |= set; - it.weapons = set; if (it == WEP_Null) continue; int imp = WEP_IMPULSE_BEGIN + it.m_id - 1; if (imp <= WEP_IMPULSE_END) @@ -368,4 +367,8 @@ ENUMCLASS_END(WFRAME) vector shotorg_adjust_values(vector vecs, bool y_is_right, bool visual, int algn); void CL_WeaponEntity_SetModel(entity this, string name, bool _anim); + +#ifdef SVQC +void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim); +#endif #endif diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc index d2443f1670..b53e25fde9 100644 --- a/qcsrc/common/weapons/config.qc +++ b/qcsrc/common/weapons/config.qc @@ -25,7 +25,10 @@ float W_Config_Queue_Compare(int root, int child, entity pass) void Dump_Weapon_Settings() { int totalweapons = 0, totalsettings = 0; + int wepcount = 1; FOREACH(Weapons, it != WEP_Null, { + if((it.spawnflags & WEP_FLAG_HIDDEN) && (it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_FLAG_NORMAL)) + continue; // never include the attacks // step 1: clear the queue WEP_CONFIG_COUNT = 0; for (int x = 0; x <= MAX_CONFIG_SETTINGS; ++x) @@ -40,7 +43,7 @@ void Dump_Weapon_Settings() // step 4: write queue WEP_CONFIG_WRITETOFILE(sprintf( "// {{{ #%d: %s%s\n", - i, + wepcount, it.m_name, ((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) ? " (MUTATOR WEAPON)" : "") )); @@ -51,6 +54,7 @@ void Dump_Weapon_Settings() LOG_INFOF("#%d: %s: %d settings...", i, it.m_name, WEP_CONFIG_COUNT); totalweapons += 1; totalsettings += WEP_CONFIG_COUNT; + wepcount += 1; }); // clear queue now that we're finished diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index 460d95af65..cdf5748e67 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -44,8 +44,6 @@ CLASS(Weapon, Object) 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 type */ ATTRIB(Weapon, ammo_type, int, RESOURCE_NONE); /** M: impulse : weapon impulse */ @@ -164,9 +162,9 @@ CLASS(WeaponPickup, Pickup) METHOD(WeaponPickup, giveTo, bool(entity this, entity item, entity player)) { bool b = Item_GiveTo(item, player); - if (b) { - LOG_TRACEF("entity %i picked up %s", player, this.m_name); - } + //if (b) { + //LOG_TRACEF("entity %i picked up %s", player, this.m_name); + //} return b; } #endif @@ -211,7 +209,7 @@ string W_NumberWeaponOrder(string order); string W_FixWeaponOrder_BuildImpulseList(string o); string W_FixWeaponOrder_AllowIncomplete(entity this, string order); string W_FixWeaponOrder_ForceComplete(string order); -void W_RandomWeapons(entity e, int n); +WepSet W_RandomWeapons(entity e, WepSet remaining, int n); string GetAmmoPicture(int ammotype); diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index 21ca117f4c..ef4e3eb7d7 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -1,6 +1,8 @@ #include "arc.qh" #ifdef SVQC +#include <common/gamemodes/gamemode/onslaught/sv_onslaught.qh> +#include <common/gamemodes/gamemode/onslaught/sv_generator.qh> bool W_Arc_Beam_Send(entity this, entity to, int sf) { @@ -102,16 +104,16 @@ void W_Arc_Bolt_Explode_use(entity this, entity actor, entity trigger) void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, getthink(this)); } @@ -138,7 +140,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR(arc, bolt_damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR(arc, bolt_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(arc, bolt_health)); missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale); missile.event_damage = W_Arc_Bolt_Damage; missile.damagedbycontents = true; @@ -189,11 +191,9 @@ void W_Arc_Beam_Think(entity this) if( !IS_PLAYER(own) || - (!thiswep.wr_checkammo1(thiswep, own, weaponentity) && !(own.items & IT_UNLIMITED_WEAPON_AMMO)) - || IS_DEAD(own) || - forbidWeaponUse(own) + !weapon_prepareattack_check(thiswep, own, weaponentity, this.beam_bursting, -1) || own.(weaponentity).m_switchweapon != WEP_ARC || @@ -236,8 +236,8 @@ void W_Arc_Beam_Think(entity this) { // note: this doesn't force the switch W_SwitchToOtherWeapon(own, weaponentity); - own.(weaponentity).arc_BUTTON_ATCK_prev = false; // hax } + own.(weaponentity).arc_BUTTON_ATCK_prev = false; // allow switching weapons delete(this); return; } @@ -265,14 +265,14 @@ void W_Arc_Beam_Think(entity this) W_SetupShot_Range( own, - weaponentity, // TODO + weaponentity, true, 0, SND_Null, 0, WEP_CVAR(arc, beam_damage) * coefficient, WEP_CVAR(arc, beam_range), - WEP_ARC.m_id + thiswep.m_id ); // After teleport, "lock" the beam until the teleport is confirmed. @@ -322,7 +322,10 @@ void W_Arc_Beam_Think(entity this) (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)), min(WEP_CVAR(arc, beam_maxangle) / angle, 1) ); - this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor)); + if(vdist(this.beam_dir - w_shotdir, <, 0.01)) + this.beam_dir = w_shotdir; + else + this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor)); } else { @@ -332,7 +335,10 @@ void W_Arc_Beam_Think(entity this) (1 - (WEP_CVAR(arc, beam_returnspeed) * frametime)), 1 ); - this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor)); + if(vdist(this.beam_dir - w_shotdir, <, 0.01)) + this.beam_dir = w_shotdir; + else + this.beam_dir = normalize((w_shotdir * (1 - blendfactor)) + (this.beam_dir * blendfactor)); } // network information: beam direction @@ -408,7 +414,7 @@ void W_Arc_Beam_Think(entity this) beam_endpos = WarpZone_TransformOrigin(WarpZone_trace_transform, beam_endpos); new_dir = WarpZone_TransformVelocity(WarpZone_trace_transform, new_dir); - float is_player = ( + bool is_player = ( IS_PLAYER(trace_ent) || trace_ent.classname == "body" @@ -416,65 +422,42 @@ void W_Arc_Beam_Think(entity this) IS_MONSTER(trace_ent) ); - if(trace_ent && trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage))) + if(trace_ent) { - // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) - // NO. trace_endpos should be just fine. If not, - // that's an engine bug that needs proper debugging. - vector hitorigin = trace_endpos; - - float falloff = ExponentialFalloff( - WEP_CVAR(arc, beam_falloff_mindist), - WEP_CVAR(arc, beam_falloff_maxdist), - WEP_CVAR(arc, beam_falloff_halflifedist), - vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg) - ); - - if(is_player && SAME_TEAM(own, trace_ent)) + if(SAME_TEAM(own, trace_ent)) { - float roothealth, rootarmor; - if(burst) + float roothealth = ((burst) ? WEP_CVAR(arc, burst_healing_hps) : WEP_CVAR(arc, beam_healing_hps)); + float rootarmor = ((burst) ? WEP_CVAR(arc, burst_healing_aps) : WEP_CVAR(arc, beam_healing_aps)); + float hplimit = ((IS_PLAYER(trace_ent)) ? WEP_CVAR(arc, beam_healing_hmax) : RESOURCE_LIMIT_NONE); + Heal(trace_ent, own, (roothealth * coefficient), hplimit); + if(IS_PLAYER(trace_ent) && rootarmor) { - roothealth = WEP_CVAR(arc, burst_healing_hps); - rootarmor = WEP_CVAR(arc, burst_healing_aps); + if(GetResourceAmount(trace_ent, RESOURCE_ARMOR) <= WEP_CVAR(arc, beam_healing_amax)) + { + GiveResourceWithLimit(trace_ent, RESOURCE_ARMOR, (rootarmor * coefficient), WEP_CVAR(arc, beam_healing_amax)); + trace_ent.pauserotarmor_finished = max( + trace_ent.pauserotarmor_finished, + time + autocvar_g_balance_pause_armor_rot + ); + } } - else - { - roothealth = WEP_CVAR(arc, beam_healing_hps); - rootarmor = WEP_CVAR(arc, beam_healing_aps); - } - - if(trace_ent.health <= WEP_CVAR(arc, beam_healing_hmax) && roothealth) - { - trace_ent.health = min( - trace_ent.health + (roothealth * coefficient), - WEP_CVAR(arc, beam_healing_hmax) - ); - } - if(trace_ent.armorvalue <= WEP_CVAR(arc, beam_healing_amax) && rootarmor) - { - trace_ent.armorvalue = min( - trace_ent.armorvalue + (rootarmor * coefficient), - WEP_CVAR(arc, beam_healing_amax) - ); - } - - // stop rot, set visual effect if(roothealth || rootarmor) - { - trace_ent.pauserothealth_finished = max( - trace_ent.pauserothealth_finished, - time + autocvar_g_balance_pause_health_rot - ); - trace_ent.pauserotarmor_finished = max( - trace_ent.pauserotarmor_finished, - time + autocvar_g_balance_pause_armor_rot - ); new_beam_type = ARC_BT_HEAL; - } } - else + else if(trace_ent.takedamage && (is_player || WEP_CVAR(arc, beam_nonplayerdamage))) { + // calculate our own hit origin as trace_endpos tends to jump around annoyingly (to player origin?) + // NO. trace_endpos should be just fine. If not, + // that's an engine bug that needs proper debugging. + vector hitorigin = trace_endpos; + + float falloff = ExponentialFalloff( + WEP_CVAR(arc, beam_falloff_mindist), + WEP_CVAR(arc, beam_falloff_maxdist), + WEP_CVAR(arc, beam_falloff_halflifedist), + vlen(WarpZone_UnTransformOrigin(WarpZone_trace_transform, hitorigin) - w_shotorg) + ); + float rootdamage; if(is_player) { @@ -490,7 +473,7 @@ void W_Arc_Beam_Think(entity this) { accuracy_add( own, - WEP_ARC.m_id, + WEP_ARC, 0, rootdamage * coefficient * falloff ); @@ -556,17 +539,33 @@ void W_Arc_Beam(float burst, entity actor, .entity weaponentity) getthink(beam)(beam); } -void Arc_Smoke(entity actor, .entity weaponentity) +void W_Arc_Attack(Weapon thiswep, entity actor, .entity weaponentity, int fire) +{ + if(!actor.(weaponentity).arc_beam || wasfreed(actor.(weaponentity).arc_beam)) + { + w_ready(thiswep, actor, weaponentity, fire); + return; + } + + // attack handled by the beam itself, this is just a loop to keep the attack happening! + + // NOTE: arc doesn't use a refire + //ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(arc, refire) * W_WeaponRateFactor(actor); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack); +} +void Arc_Smoke(Weapon thiswep, entity actor, .entity weaponentity, int fire) { + // TODO: spamming this without checking any refires is asking for trouble! makevectors(actor.v_angle); - W_SetupShot_Range(actor,weaponentity,true,0,SND_Null,0,0,0,WEP_ARC.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect + W_SetupShot_Range(actor,weaponentity,false,0,SND_Null,0,0,0,thiswep.m_id); // TODO: probably doesn't need deathtype, since this is just a prefire effect vector smoke_origin = w_shotorg + actor.velocity*frametime; if ( actor.arc_overheat > time ) { if ( random() < actor.(weaponentity).arc_heat_percent ) Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 ); - if ( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) + if ( (fire & 1) || (fire & 2) ) { Send_Effect(EFFECT_ARC_OVERHEAT_FIRE, smoke_origin, w_shotdir, 1 ); if ( !actor.arc_smoke_sound ) @@ -585,7 +584,7 @@ void Arc_Smoke(entity actor, .entity weaponentity) } if ( actor.arc_smoke_sound && ( actor.arc_overheat <= time || - !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != WEP_ARC ) + !( PHYS_INPUT_BUTTON_ATCK(actor) || PHYS_INPUT_BUTTON_ATCK2(actor) ) ) || actor.(weaponentity).m_switchweapon != thiswep ) { actor.arc_smoke_sound = 0; sound(actor, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); @@ -620,14 +619,14 @@ METHOD(Arc, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { Arc_Player_SetHeat(actor, weaponentity); - Arc_Smoke(actor, weaponentity); + Arc_Smoke(thiswep, actor, weaponentity, fire); bool beam_fire2 = ((fire & 2) && !WEP_CVAR(arc, bolt)); if (time >= actor.arc_overheat) if ((fire & 1) || beam_fire2 || actor.(weaponentity).arc_beam.beam_bursting) { - + #if 0 if(actor.(weaponentity).arc_BUTTON_ATCK_prev) { #if 0 @@ -637,6 +636,7 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i #endif weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), w_ready); } + #endif if((!actor.(weaponentity).arc_beam) || wasfreed(actor.(weaponentity).arc_beam)) { @@ -646,7 +646,8 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i if(!actor.(weaponentity).arc_BUTTON_ATCK_prev) { - weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), W_Arc_Attack); actor.(weaponentity).arc_BUTTON_ATCK_prev = true; } } @@ -656,7 +657,7 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i } else if(fire & 2) { - if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(arc, bolt_refire))) + if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(arc, bolt_refire))) { W_Arc_Attack_Bolt(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready); @@ -665,10 +666,9 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i if(actor.(weaponentity).arc_BUTTON_ATCK_prev) { - int slot = weaponslot(weaponentity); sound(actor, CH_WEAPON_A, SND_ARC_STOP, VOL_BASE, ATTN_NORM); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor); } actor.(weaponentity).arc_BUTTON_ATCK_prev = false; @@ -752,6 +752,9 @@ METHOD(Arc, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponent #endif #ifdef CSQC bool autocvar_cl_arcbeam_teamcolor = true; +bool autocvar_cl_arcbeam_simple = true; + +.int beam_slot; METHOD(Arc, wr_impacteffect, void(entity thiswep, entity actor)) { @@ -802,32 +805,37 @@ void Draw_ArcBeam_callback(vector start, vector hit, vector end) vector last_top = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_top); vector last_bottom = WarpZone_TransformOrigin(WarpZone_trace_transform, Draw_ArcBeam_callback_last_bottom); - R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE - R_PolygonVertex( - top, - '0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)), - beam.beam_color, - beam.beam_alpha - ); - R_PolygonVertex( - last_top, - '0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), - beam.beam_color, - beam.beam_alpha - ); - R_PolygonVertex( - last_bottom, - '0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), - beam.beam_color, - beam.beam_alpha - ); - R_PolygonVertex( - bottom, - '0 0.5 0' * (1 - (thickness / beam.beam_thickness)), - beam.beam_color, - beam.beam_alpha - ); - R_EndPolygon(); + if(autocvar_cl_arcbeam_simple) + Draw_CylindricLine(start, end, thickness, beam.beam_image, 0.25, -time * 3, beam.beam_color, beam.beam_alpha, DRAWFLAG_NORMAL, transformed_view_org); + else + { + R_BeginPolygon(beam.beam_image, DRAWFLAG_NORMAL); // DRAWFLAG_ADDITIVE + R_PolygonVertex( + top, + '0 0.5 0' + ('0 0.5 0' * (thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + last_top, + '0 0.5 0' + ('0 0.5 0' * (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + last_bottom, + '0 0.5 0' * (1 - (Draw_ArcBeam_callback_last_thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_PolygonVertex( + bottom, + '0 0.5 0' * (1 - (thickness / beam.beam_thickness)), + beam.beam_color, + beam.beam_alpha + ); + R_EndPolygon(); + } // draw trailing particles // NOTES: @@ -884,17 +892,30 @@ void Draw_ArcBeam(entity this) // into a weapon system for client code. // find where we are aiming - makevectors(warpzone_save_view_angles); + makevectors(((autocvar_chase_active) ? warpzone_save_view_angles : view_angles)); vector forward = v_forward; vector right = v_right; vector up = v_up; + entity wepent = viewmodels[this.beam_slot]; + + if(autocvar_chase_active) + this.beam_usevieworigin = 1; + else + this.beam_usevieworigin = 2; // decide upon start position if(this.beam_usevieworigin == 2) { start_pos = warpzone_save_view_origin; } + else if(csqcplayer) + { start_pos = csqcplayer.origin + csqcplayer.view_ofs; } else { start_pos = this.origin; } + int v_shot_idx; // used later + (v_shot_idx = gettagindex(wepent, "shot")) || (v_shot_idx = gettagindex(wepent, "tag_shot")); + if(v_shot_idx && this.beam_usevieworigin == 2) + start_pos = gettaginfo(wepent, v_shot_idx) - '0 0 2'; + // trace forward with an estimation WarpZone_TraceLine( start_pos, @@ -911,9 +932,16 @@ void Draw_ArcBeam(entity this) end_pos = start_pos + (forward * g_trueaim_minrange); // move shot origin to the actual gun muzzle origin - vector origin_offset = - right * -this.beam_shotorigin.y - + up * this.beam_shotorigin.z; + vector origin_offset = '0 0 0'; + if(!v_shot_idx || this.beam_usevieworigin != 2) + { + this.beam_shotorigin = wepent.movedir; + origin_offset = + right * -this.beam_shotorigin.y + + up * this.beam_shotorigin.z; + } + else + this.beam_shotorigin = '0 0 0'; start_pos = start_pos + origin_offset; @@ -1119,7 +1147,7 @@ void Draw_ArcBeam(entity this) ) ); } - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { pointparticles( this.beam_muzzleeffect, @@ -1160,11 +1188,13 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) int slot = ReadByte(); entity flash; + this.beam_slot = slot; + if(isnew) { int gunalign = W_GunAlign(viewmodels[slot], STAT(GUNALIGN)) - 1; - this.beam_shotorigin = arc_shotorigin[gunalign]; + this.beam_shotorigin = arc_shotorigin[gunalign]; // get a starting point // set other main attributes of the beam this.draw = Draw_ArcBeam; @@ -1271,18 +1301,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1297,18 +1327,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; // (EFFECT_GRENADE_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; // (EFFECT_GRENADE_MUZZLEFLASH); this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1323,18 +1353,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1349,18 +1379,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 0; this.beam_hitlight[3] = 0; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 50; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 0; this.beam_muzzlelight[3] = 0; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1375,18 +1405,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1401,18 +1431,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1427,18 +1457,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1453,18 +1483,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } @@ -1481,18 +1511,18 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) this.beam_hitlight[1] = 1; this.beam_hitlight[2] = 1; this.beam_hitlight[3] = 1; - this.beam_muzzleeffect = NULL; //(EFFECT_VORTEX_MUZZLEFLASH); + this.beam_muzzleeffect = EFFECT_Null; this.beam_muzzlelight[0] = 0; this.beam_muzzlelight[1] = 1; this.beam_muzzlelight[2] = 1; this.beam_muzzlelight[3] = 1; this.beam_image = "particles/lgbeam"; - if(this.beam_muzzleeffect) + if(this.beam_muzzleeffect && autocvar_r_drawviewmodel) { setmodel(flash, MDL_ARC_MUZZLEFLASH); flash.alpha = this.beam_alpha; flash.colormod = this.beam_color; - flash.scale = 0.5; + flash.scale = 0.35; } break; } diff --git a/qcsrc/common/weapons/weapon/crylink.qc b/qcsrc/common/weapons/weapon/crylink.qc index 2710768668..064668ca6c 100644 --- a/qcsrc/common/weapons/weapon/crylink.qc +++ b/qcsrc/common/weapons/weapon/crylink.qc @@ -247,7 +247,7 @@ void W_Crylink_Touch(entity this, entity toucher) if(a) f *= a; - float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), + float totaldamage = RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * f, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * f, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, this.weaponentity_fld, toucher); if(totaldamage && ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 2) || ((WEP_CVAR_BOTH(crylink, isprimary, linkexplode) == 1) && !W_Crylink_Touch_WouldHitFriendly(this, WEP_CVAR_BOTH(crylink, isprimary, radius))))) @@ -297,7 +297,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity) if(WEP_CVAR_PRI(crylink, joinexplode)) maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage); - W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg, WEP_CRYLINK.m_id); + W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg, thiswep.m_id); forward = v_forward; right = v_right; up = v_up; @@ -336,7 +336,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity) set_movetype(proj, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_CRYLINK.m_id; + proj.projectiledeathtype = thiswep.m_id; //proj.gravity = 0.001; setorigin(proj, w_shotorg); @@ -409,7 +409,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity) if(WEP_CVAR_SEC(crylink, joinexplode)) maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage); - W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg, WEP_CRYLINK.m_id | HITTYPE_SECONDARY); + W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg, thiswep.m_id | HITTYPE_SECONDARY); forward = v_forward; right = v_right; up = v_up; @@ -448,7 +448,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity) set_movetype(proj, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_CRYLINK.m_id | HITTYPE_SECONDARY; + proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; //proj.gravity = 0.001; setorigin(proj, w_shotorg); @@ -571,7 +571,7 @@ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentit if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { // ran out of ammo! - actor.cnt = WEP_CRYLINK.m_id; + actor.cnt = thiswep.m_id; actor.(weaponentity).m_switchweapon = w_getbestweapon(actor, weaponentity); } } @@ -584,7 +584,7 @@ METHOD(Crylink, wr_checkammo1, bool(entity thiswep, entity actor, .entity weapon return true; 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(crylink, ammo); return ammo_amount; } METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) @@ -594,7 +594,7 @@ METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor, .entity weapon return true; 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(crylink, ammo); return ammo_amount; } METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) diff --git a/qcsrc/common/weapons/weapon/crylink.qh b/qcsrc/common/weapons/weapon/crylink.qh index e48bf5fb34..77e0b734e2 100644 --- a/qcsrc/common/weapons/weapon/crylink.qh +++ b/qcsrc/common/weapons/weapon/crylink.qh @@ -71,4 +71,6 @@ SPAWNFUNC_WEAPON(weapon_crylink, WEP_CRYLINK) .entity queuenext; .entity queueprev; + +void W_Crylink_Dequeue(entity e); #endif diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index 0a046389c7..f8539b14c3 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -49,9 +49,8 @@ void W_Devastator_Explode(entity this, entity directhitentity) 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; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(this.realowner, slot) = time; + this.realowner.cnt = thiswep.m_id; + ATTACK_FINISHED(this.realowner, weaponentity) = time; this.realowner.(weaponentity).m_switchweapon = w_getbestweapon(this.realowner, weaponentity); } } @@ -143,9 +142,8 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) 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; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(this.realowner, slot) = time; + this.realowner.cnt = thiswep.m_id; + ATTACK_FINISHED(this.realowner, weaponentity) = time; this.realowner.(weaponentity).m_switchweapon = w_getbestweapon(this.realowner, weaponentity); } } @@ -235,11 +233,19 @@ void W_Devastator_Think(entity this) else f = 1; + vector md = this.realowner.(weaponentity).movedir; + vector vecs = ((md.x > 0) ? md : '0 0 0'); + + vector dv = v_right * -vecs.y + v_up * vecs.z; + + if(!W_DualWielding(this.realowner)) + dv = '0 0 0'; // don't override! + velspeed = vlen(this.velocity); makevectors(this.realowner.v_angle); desireddir = WarpZone_RefSys_TransformVelocity(this.realowner, this, v_forward); - desiredorigin = WarpZone_RefSys_TransformOrigin(this.realowner, this, this.realowner.origin + this.realowner.view_ofs); + desiredorigin = WarpZone_RefSys_TransformOrigin(this.realowner, this, this.realowner.origin + this.realowner.view_ofs + dv); olddir = normalize(this.velocity); // now it gets tricky... we want to move like some curve to approximate the target direction @@ -281,24 +287,24 @@ void W_Devastator_Touch(entity this, entity toucher) void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode_think); } -void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) +void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity, int fire) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR(devastator, ammo), weaponentity); - W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage), WEP_DEVASTATOR.m_id); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage), thiswep.m_id); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); entity missile = WarpZone_RefSys_SpawnSameRefSys(actor); @@ -316,14 +322,14 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) missile.takedamage = DAMAGE_YES; missile.damageforcescale = WEP_CVAR(devastator, damageforcescale); - missile.health = WEP_CVAR(devastator, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(devastator, health)); missile.event_damage = W_Devastator_Damage; missile.damagedbycontents = true; IL_PUSH(g_damagedbycontents, missile); set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_DEVASTATOR.m_id; + missile.projectiledeathtype = thiswep.m_id; setsize(missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot setorigin(missile, w_shotorg - v_forward * 3); // move it back so it hits the wall at the right point @@ -334,6 +340,7 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) setthink(missile, W_Devastator_Think); missile.nextthink = time; missile.cnt = time + WEP_CVAR(devastator, lifetime); + missile.rl_detonate_later = (fire & 2); // allow instant detonation missile.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, missile); IL_PUSH(g_bot_dodge, missile); @@ -350,6 +357,11 @@ void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) // common properties MUTATOR_CALLHOOK(EditProjectile, actor, missile); + + if (time >= missile.nextthink) + { + getthink(missile)(missile); + } } METHOD(Devastator, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) @@ -423,7 +435,7 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor, .entity weaponenti // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events PHYS_INPUT_BUTTON_ATCK2(actor) = true; - if((skill > 6.5) && (selfdamage > actor.health)) + if((skill > 6.5) && (selfdamage > GetResourceAmount(actor, RESOURCE_HEALTH))) PHYS_INPUT_BUTTON_ATCK2(actor) = false; //if(PHYS_INPUT_BUTTON_ATCK2(actor) == true) // dprint(ftos(desirabledamage),"\n"); @@ -441,7 +453,7 @@ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponen if(actor.(weaponentity).rl_release || WEP_CVAR(devastator, guidestop)) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) { - W_Devastator_Attack(thiswep, actor, weaponentity); + W_Devastator_Attack(thiswep, actor, weaponentity, fire); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); actor.(weaponentity).rl_release = 0; } @@ -450,7 +462,7 @@ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponen actor.(weaponentity).rl_release = 1; if(fire & 2) - if(actor.(weaponentity).m_switchweapon == WEP_DEVASTATOR) + if(actor.(weaponentity).m_switchweapon == thiswep) { bool rockfound = false; IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket", @@ -474,7 +486,7 @@ METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor, .entity wea { #if 0 // don't switch while guiding a missile - if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_DEVASTATOR) + if(ATTACK_FINISHED(actor, weaponentity) <= time || PS(actor).m_weapon != WEP_DEVASTATOR) { ammo_amount = false; if(WEP_CVAR(devastator, reload_ammo)) @@ -502,7 +514,7 @@ METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor, .entity wea } #else 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(devastator, ammo); return ammo_amount; #endif } diff --git a/qcsrc/common/weapons/weapon/devastator.qh b/qcsrc/common/weapons/weapon/devastator.qh index 0e8d8b2fbc..e858d54e42 100644 --- a/qcsrc/common/weapons/weapon/devastator.qh +++ b/qcsrc/common/weapons/weapon/devastator.qh @@ -4,7 +4,7 @@ CLASS(Devastator, Weapon) /* 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); +/* flags */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Devastator, bot_pickupbasevalue, float, 8000); /* color */ ATTRIB(Devastator, wpcolor, vector, '1 1 0'); /* modelname */ ATTRIB(Devastator, mdl, string, "rl"); diff --git a/qcsrc/common/weapons/weapon/electro.qc b/qcsrc/common/weapons/weapon/electro.qc index 2276b6042a..e22b5e9412 100644 --- a/qcsrc/common/weapons/weapon/electro.qc +++ b/qcsrc/common/weapons/weapon/electro.qc @@ -205,7 +205,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) SND_ELECTRO_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(electro, damage), - WEP_ELECTRO.m_id + thiswep.m_id ); Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -219,7 +219,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) proj.nextthink = time; proj.ltime = time + WEP_CVAR_PRI(electro, lifetime); PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_ELECTRO.m_id; + proj.projectiledeathtype = thiswep.m_id; proj.weaponentity_fld = weaponentity; setorigin(proj, w_shotorg); @@ -258,7 +258,7 @@ void W_Electro_Orb_Stick(entity this, entity to) newproj.takedamage = this.takedamage; newproj.damageforcescale = this.damageforcescale; - newproj.health = this.health; + SetResourceAmountExplicit(newproj, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); newproj.event_damage = this.event_damage; newproj.spawnshieldtime = this.spawnshieldtime; newproj.damagedbycontents = true; @@ -279,14 +279,14 @@ void W_Electro_Orb_Stick(entity this, entity to) delete(this); if(to) - SetMovetypeFollow(this, to); + SetMovetypeFollow(newproj, to); } void W_Electro_Orb_Touch(entity this, entity toucher) { PROJECTILE_TOUCH(this, toucher); - if(toucher.takedamage == DAMAGE_AIM) - { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(this, toucher); } } + if(toucher.takedamage == DAMAGE_AIM && WEP_CVAR_SEC(electro, touchexplode)) + { W_Electro_Explode(this, toucher); } else if(toucher.owner != this.owner && toucher.classname != this.classname) // don't stick to player's other projectiles! { //UpdateCSQCProjectile(this); @@ -300,7 +300,7 @@ void W_Electro_Orb_Touch(entity this, entity toucher) void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; // note: combos are usually triggered by W_Electro_TriggerCombo, not damage @@ -309,8 +309,8 @@ void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_combo ? 1 : -1))) return; // g_projectiles_damage says to halt - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.takedamage = DAMAGE_NO; this.nextthink = time; @@ -354,7 +354,7 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) SND_ELECTRO_FIRE2, CH_WEAPON_A, WEP_CVAR_SEC(electro, damage), - WEP_ELECTRO.m_id | HITTYPE_SECONDARY + thiswep.m_id | HITTYPE_SECONDARY ); w_shotdir = v_forward; // no TrueAim for grenades please @@ -369,7 +369,7 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) proj.bot_dodgerating = WEP_CVAR_SEC(electro, damage); proj.nextthink = time + WEP_CVAR_SEC(electro, lifetime); PROJECTILE_MAKETRIGGER(proj); - proj.projectiledeathtype = WEP_ELECTRO.m_id | HITTYPE_SECONDARY; + proj.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; proj.weaponentity_fld = weaponentity; setorigin(proj, w_shotorg); @@ -381,7 +381,7 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) setsize(proj, '-4 -4 -4', '4 4 4'); proj.takedamage = DAMAGE_YES; proj.damageforcescale = WEP_CVAR_SEC(electro, damageforcescale); - proj.health = WEP_CVAR_SEC(electro, health); + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, WEP_CVAR_SEC(electro, health)); proj.event_damage = W_Electro_Orb_Damage; proj.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, proj); @@ -394,14 +394,6 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop); proj.missile_flags = MIF_SPLASH | MIF_ARC; -#if 0 - entity p2; - p2 = spawn(); - copyentity(proj, p2); - setmodel(p2, MDL_PROJECTILE_ELECTRO); - setsize(p2, proj.mins, proj.maxs); -#endif - CSQCProjectile(proj, true, PROJECTILE_ELECTRO, false); // no culling, it has sound MUTATOR_CALLHOOK(EditProjectile, actor, proj); @@ -413,7 +405,7 @@ void W_Electro_CheckAttack(Weapon thiswep, entity actor, .entity weaponentity, i if(PHYS_INPUT_BUTTON_ATCK2(actor)) if(weapon_prepareattack(thiswep, actor, weaponentity, true, -1)) { - W_Electro_Attack_Orb(WEP_ELECTRO, actor, weaponentity); + W_Electro_Attack_Orb(thiswep, actor, weaponentity); actor.(weaponentity).electro_count -= 1; weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); return; @@ -492,7 +484,7 @@ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentit METHOD(Electro, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(electro, ammo); return ammo_amount; } METHOD(Electro, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) @@ -501,12 +493,12 @@ METHOD(Electro, wr_checkammo2, bool(entity thiswep, entity actor, .entity weapon 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 = 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(electro, ammo) + WEP_CVAR_PRI(electro, ammo); } else { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(electro, ammo); } return ammo_amount; } diff --git a/qcsrc/common/weapons/weapon/fireball.qc b/qcsrc/common/weapons/weapon/fireball.qc index ef2eb91cad..ea7f5b8aea 100644 --- a/qcsrc/common/weapons/weapon/fireball.qc +++ b/qcsrc/common/weapons/weapon/fireball.qc @@ -14,9 +14,9 @@ void W_Fireball_Explode(entity this, entity directhitentity) this.takedamage = DAMAGE_NO; // 1. dist damage - d = (this.realowner.health + this.realowner.armorvalue); + d = (GetResourceAmount(this.realowner, RESOURCE_HEALTH) + GetResourceAmount(this.realowner, RESOURCE_ARMOR)); RadiusDamage(this, this.realowner, WEP_CVAR_PRI(fireball, damage), WEP_CVAR_PRI(fireball, edgedamage), WEP_CVAR_PRI(fireball, radius), NULL, NULL, WEP_CVAR_PRI(fireball, force), this.projectiledeathtype, this.weaponentity_fld, directhitentity); - if(this.realowner.health + this.realowner.armorvalue >= d) + if(GetResourceAmount(this.realowner, RESOURCE_HEALTH) + GetResourceAmount(this.realowner, RESOURCE_ARMOR) >= d) if(!this.cnt) { modeleffect_spawn("models/sphere/sphere.md3", 0, 0, this.origin, '0 0 0', '0 0 0', '0 0 0', 0, WEP_CVAR_PRI(fireball, bfgradius), 0.2, 0.05, 0.25); @@ -42,7 +42,7 @@ void W_Fireball_Explode(entity this, entity directhitentity) dir = normalize(e.origin + e.view_ofs - this.origin); if(accuracy_isgooddamage(this.realowner, e)) - accuracy_add(this.realowner, WEP_FIREBALL.m_id, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points); + accuracy_add(this.realowner, WEP_FIREBALL, 0, WEP_CVAR_PRI(fireball, bfgdamage) * points); Damage(e, this, this.realowner, WEP_CVAR_PRI(fireball, bfgdamage) * points, this.projectiledeathtype | HITTYPE_BOUNCE | HITTYPE_SPLASH, this.weaponentity_fld, e.origin + e.view_ofs, WEP_CVAR_PRI(fireball, bfgforce) * dir); Send_Effect(EFFECT_FIREBALL_BFGDAMAGE, e.origin, -1 * dir, 1); @@ -119,14 +119,14 @@ void W_Fireball_Think(entity this) void W_Fireball_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { this.cnt = 1; W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode_think); @@ -147,7 +147,7 @@ void W_Fireball_Attack1(entity actor, .entity weaponentity) proj.use = W_Fireball_Explode_use; setthink(proj, W_Fireball_Think); proj.nextthink = time; - proj.health = WEP_CVAR_PRI(fireball, health); + SetResourceAmountExplicit(proj, RESOURCE_HEALTH, WEP_CVAR_PRI(fireball, health)); proj.team = actor.team; proj.event_damage = W_Fireball_Damage; proj.takedamage = DAMAGE_YES; @@ -155,7 +155,6 @@ void W_Fireball_Attack1(entity actor, .entity weaponentity) PROJECTILE_MAKETRIGGER(proj); proj.projectiledeathtype = WEP_FIREBALL.m_id; proj.weaponentity_fld = weaponentity; - proj.weaponentity_fld = weaponentity; setorigin(proj, w_shotorg); set_movetype(proj, MOVETYPE_FLY); diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index d9164dc19a..58be8f835f 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -32,7 +32,7 @@ void W_Hagar_Explode2_use(entity this, entity actor, entity trigger) void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; float is_linkexplode = ( ((inflictor.owner != NULL) ? (inflictor.owner == this.owner) : true) @@ -47,10 +47,10 @@ void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, is_linkexplode)) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, getthink(this)); } @@ -81,7 +81,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity) W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hagar, ammo), weaponentity); - W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage), WEP_HAGAR.m_id); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage), thiswep.m_id); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -91,7 +91,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_PRI(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_PRI(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_PRI(hagar, health)); missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -102,7 +102,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity) setthink(missile, adaptor_think2use_hittype_splash); missile.nextthink = time + WEP_CVAR_PRI(hagar, lifetime); PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR.m_id; + missile.projectiledeathtype = thiswep.m_id; missile.weaponentity_fld = weaponentity; setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); @@ -127,7 +127,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo), weaponentity); - W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage), WEP_HAGAR.m_id | HITTYPE_SECONDARY); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage), thiswep.m_id | HITTYPE_SECONDARY); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -137,7 +137,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_SEC(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_SEC(hagar, health)); missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -149,7 +149,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) setthink(missile, adaptor_think2use_hittype_splash); missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand); PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY; + missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; missile.weaponentity_fld = weaponentity; setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); @@ -169,7 +169,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) } .float hagar_loadstep, hagar_loadblock, hagar_loadbeep, hagar_warning; -void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) +void W_Hagar_Attack2_Load_Release(Weapon thiswep, entity actor, .entity weaponentity) { // time to release the rockets we've loaded @@ -184,7 +184,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire)); shots = actor.(weaponentity).hagar_load; - W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage) * shots, WEP_HAGAR.m_id | HITTYPE_SECONDARY); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage) * shots, thiswep.m_id | HITTYPE_SECONDARY); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); forward = v_forward; @@ -200,7 +200,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) missile.bot_dodgerating = WEP_CVAR_SEC(hagar, damage); missile.takedamage = DAMAGE_YES; - missile.health = WEP_CVAR_SEC(hagar, health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR_SEC(hagar, health)); missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; @@ -211,7 +211,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) setthink(missile, adaptor_think2use_hittype_splash); missile.nextthink = time + WEP_CVAR_SEC(hagar, lifetime_min) + random() * WEP_CVAR_SEC(hagar, lifetime_rand); PROJECTILE_MAKETRIGGER(missile); - missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY; + missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; missile.weaponentity_fld = weaponentity; setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); @@ -255,9 +255,9 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) { // loadable hagar secondary attack, must always run each frame - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) + if(time < game_starttime || time < actor.race_penalty || timeout_status == TIMEOUT_ACTIVE) return; - if(time < game_starttime) + if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return; bool loaded = actor.(weaponentity).hagar_load >= WEP_CVAR_SEC(hagar, load_max); @@ -267,7 +267,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) if(actor.items & IT_UNLIMITED_WEAPON_AMMO) enough_ammo = true; else if(autocvar_g_balance_hagar_reload_ammo) - enough_ammo = actor.(weaponentity).(weapon_load[WEP_HAGAR.m_id]) >= WEP_CVAR_SEC(hagar, ammo); + enough_ammo = actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(hagar, ammo); else enough_ammo = GetResourceAmount(actor, thiswep.ammo_type) >= WEP_CVAR_SEC(hagar, ammo); @@ -342,7 +342,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) if(!PHYS_INPUT_BUTTON_ATCK2(actor) || (stopped && actor.(weaponentity).hagar_loadstep < time && WEP_CVAR_SEC(hagar, load_hold) >= 0)) { actor.(weaponentity).state = WS_READY; - W_Hagar_Attack2_Load_Release(actor, weaponentity); + W_Hagar_Attack2_Load_Release(thiswep, actor, weaponentity); } } else @@ -363,7 +363,7 @@ void W_Hagar_Attack2_Load(Weapon thiswep, entity actor, .entity weaponentity) void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - if(!(fire & 1) || actor.(weaponentity).hagar_load || actor.(weaponentity).hagar_loadblock || actor.(weaponentity).m_switchweapon != WEP_HAGAR) + if(!(fire & 1) || actor.(weaponentity).hagar_load || actor.(weaponentity).hagar_loadblock || actor.(weaponentity).m_switchweapon != thiswep || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1)) { w_ready(thiswep, actor, weaponentity, fire); return; @@ -379,16 +379,9 @@ void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int W_Hagar_Attack(thiswep, actor, weaponentity); - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor); - int theframe = WFRAME_FIRE1; - entity this = actor.(weaponentity); - if(this) - { - if(this.wframe == WFRAME_FIRE1) - theframe = WFRAME_DONTCHANGE; - } - weapon_thinkf(actor, weaponentity, theframe, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor); + actor.(weaponentity).wframe = WFRAME_FIRE1; + weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR_PRI(hagar, refire), W_Hagar_Attack_Auto); } METHOD(Hagar, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) @@ -428,7 +421,7 @@ METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor, .entity weaponent if(actor.(weaponentity).hagar_load) { actor.(weaponentity).state = WS_READY; - W_Hagar_Attack2_Load_Release(actor, weaponentity); + W_Hagar_Attack2_Load_Release(thiswep, actor, weaponentity); } } METHOD(Hagar, wr_setup, void(entity thiswep, entity actor, .entity weaponentity)) @@ -443,13 +436,13 @@ METHOD(Hagar, wr_setup, void(entity thiswep, entity actor, .entity weaponentity) METHOD(Hagar, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(hagar, ammo); return ammo_amount; } METHOD(Hagar, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(hagar, ammo); return ammo_amount; } METHOD(Hagar, wr_resetplayer, void(entity thiswep, entity actor)) @@ -464,7 +457,7 @@ METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor, .entity weapone { // if we have any rockets loaded when we die, release them if(actor.(weaponentity).hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath)) - W_Hagar_Attack2_Load_Release(actor, weaponentity); + W_Hagar_Attack2_Load_Release(thiswep, actor, weaponentity); } METHOD(Hagar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { diff --git a/qcsrc/common/weapons/weapon/hlac.qc b/qcsrc/common/weapons/weapon/hlac.qc index 49ad1c7394..816ddae365 100644 --- a/qcsrc/common/weapons/weapon/hlac.qc +++ b/qcsrc/common/weapons/weapon/hlac.qc @@ -12,7 +12,7 @@ void W_HLAC_Touch(entity this, entity toucher) isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY); - RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), + RadiusDamage(this, this.realowner, WEP_CVAR_BOTH(hlac, isprimary, damage), WEP_CVAR_BOTH(hlac, isprimary, edgedamage), WEP_CVAR_BOTH(hlac, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(hlac, isprimary, force), this.projectiledeathtype, this.weaponentity_fld, toucher); delete(this); @@ -30,7 +30,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity) if(actor.crouch) spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod); - W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage), WEP_HLAC.m_id); + W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage), thiswep.m_id); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); if(!autocvar_g_norecoil) { @@ -61,7 +61,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity) missile.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, missile); IL_PUSH(g_bot_dodge, missile); - missile.projectiledeathtype = WEP_HLAC.m_id; + missile.projectiledeathtype = thiswep.m_id; missile.weaponentity_fld = weaponentity; CSQCProjectile(missile, true, PROJECTILE_HLAC, true); @@ -69,7 +69,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity) MUTATOR_CALLHOOK(EditProjectile, actor, missile); } -void W_HLAC_Attack2(entity actor, .entity weaponentity) +void W_HLAC_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; float spread; @@ -80,7 +80,7 @@ void W_HLAC_Attack2(entity actor, .entity weaponentity) if(actor.crouch) spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod); - W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage), WEP_HLAC.m_id | HITTYPE_SECONDARY); + W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage), thiswep.m_id | HITTYPE_SECONDARY); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = new(hlacbolt); @@ -107,7 +107,7 @@ void W_HLAC_Attack2(entity actor, .entity weaponentity) IL_PUSH(g_projectiles, missile); IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; - missile.projectiledeathtype = WEP_HLAC.m_id | HITTYPE_SECONDARY; + missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; missile.weaponentity_fld = weaponentity; CSQCProjectile(missile, true, PROJECTILE_HLAC, true); @@ -134,9 +134,8 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int return; } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor); - W_HLAC_Attack(WEP_HLAC, actor, weaponentity); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor); + W_HLAC_Attack(thiswep, actor, weaponentity); actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); } @@ -153,7 +152,7 @@ void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor, .entity weaponentity) W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hlac, ammo), weaponentity); for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i) - W_HLAC_Attack2(actor, weaponentity); + W_HLAC_Attack2(thiswep, actor, weaponentity); if(!autocvar_g_norecoil) { @@ -192,13 +191,13 @@ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, METHOD(HLAC, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(hlac, ammo); return ammo_amount; } METHOD(HLAC, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(hlac, ammo); return ammo_amount; } METHOD(HLAC, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index 8d569d04e2..e17e976208 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -48,15 +48,15 @@ void W_Hook_Explode2_use(entity this, entity actor, entity trigger) void W_Hook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, this.realowner, W_Hook_Explode2); } @@ -88,7 +88,7 @@ void W_Hook_Attack2(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Hook_Touch2); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_SEC(hook, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_SEC(hook, health)); gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale); gren.event_damage = W_Hook_Damage; gren.damagedbycontents = true; @@ -165,7 +165,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, { if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) { - if( actor.ammo_fuel >= (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel ) + if( GetResourceAmount(actor, RESOURCE_FUEL) >= (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel ) { W_DecreaseAmmo(thiswep, actor, (time - actor.(weaponentity).hook_time_fueldecrease) * hooked_fuel, weaponentity); actor.(weaponentity).hook_time_fueldecrease = time; @@ -173,7 +173,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, } else { - actor.ammo_fuel = 0; + SetResourceAmount(actor, RESOURCE_FUEL, 0); actor.(weaponentity).hook_state |= HOOK_REMOVING; if(actor.(weaponentity).m_weapon != WEP_Null) // offhand W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); @@ -214,9 +214,9 @@ METHOD(Hook, wr_checkammo1, bool(Hook thiswep, entity actor, .entity weaponentit if (!thiswep.ammo_factor) return true; if(actor.(weaponentity).hook) - return actor.ammo_fuel > 0; + return GetResourceAmount(actor, RESOURCE_FUEL) > 0; - return actor.ammo_fuel >= WEP_CVAR_PRI(hook, ammo); + return GetResourceAmount(actor, RESOURCE_FUEL) >= WEP_CVAR_PRI(hook, ammo); } METHOD(Hook, wr_checkammo2, bool(Hook thiswep, entity actor, .entity weaponentity)) { @@ -261,11 +261,11 @@ float autocvar_cl_grapplehook_alpha = 1; void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg); entityclass(Hook); -class(Hook) .entity HookType; // ENT_CLIENT_* -class(Hook) .vector origin; -class(Hook) .vector velocity; -class(Hook) .float HookSilent; -class(Hook) .float HookRange; +classfield(Hook) .entity HookType; // ENT_CLIENT_* +classfield(Hook) .vector origin; +classfield(Hook) .vector velocity; +classfield(Hook) .float HookSilent; +classfield(Hook) .float HookRange; string Draw_GrapplingHook_trace_callback_tex; float Draw_GrapplingHook_trace_callback_rnd; @@ -281,7 +281,7 @@ void Draw_GrapplingHook_trace_callback(vector start, vector hit, vector end) Draw_GrapplingHook_trace_callback_rnd += 0.25 * vlen(hit - start) / 8; } -class(Hook) .float teleport_time; +classfield(Hook) .float teleport_time; void Draw_GrapplingHook(entity this) { vector a, b, atrans; diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index b5c3bf2fa4..607f1fcddf 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -56,14 +56,13 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we actor.punchangle_x = random() - 0.5; actor.punchangle_y = random() - 0.5; } - int slot = weaponslot(weaponentity); // this attack_finished just enforces a cooldown at the end of a burst - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); if(actor.(weaponentity).misc_bulletcounter == 1) - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, 0); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, first_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, first_damage), WEP_CVAR(machinegun, first_force), deathtype, EFFECT_BULLET); else - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), deathtype, 0); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, sustained_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), deathtype, EFFECT_BULLET); Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -86,7 +85,7 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we // weapon frames void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon) // abort immediately if switching + if(actor.(weaponentity).m_weapon != actor.(weaponentity).m_switchweapon || !weapon_prepareattack_check(thiswep, actor, weaponentity, (fire & 2), -1)) // abort immediately if switching { w_ready(thiswep, actor, weaponentity, fire); return; @@ -101,7 +100,7 @@ void W_MachineGun_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentit return; } actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity); + W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); } else @@ -113,7 +112,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity { float machinegun_spread; - if(!(fire & 1)) + if(!(fire & 1) || !weapon_prepareattack_check(thiswep, actor, weaponentity, false, -1)) { w_ready(thiswep, actor, weaponentity, fire); return; @@ -127,9 +126,9 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity return; } - W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity); + W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, sustained_ammo), weaponentity); - W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), WEP_MACHINEGUN.m_id); + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id); if(!autocvar_g_norecoil) { actor.punchangle_x = random() - 0.5; @@ -137,7 +136,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity } machinegun_spread = bound(WEP_CVAR(machinegun, spread_min), WEP_CVAR(machinegun, spread_min) + (WEP_CVAR(machinegun, spread_add) * actor.(weaponentity).misc_bulletcounter), WEP_CVAR(machinegun, spread_max)); - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, machinegun_spread, WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET); actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; @@ -152,21 +151,20 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 70) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 3, actor, weaponentity); } - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, first_refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Auto); } void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), WEP_MACHINEGUN.m_id); + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage), thiswep.m_id); if(!autocvar_g_norecoil) { actor.punchangle_x = random() - 0.5; actor.punchangle_y = random() - 0.5; } - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), WEP_MACHINEGUN.m_id, 0); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET); Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -182,8 +180,7 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit actor.(weaponentity).misc_bulletcounter = actor.(weaponentity).misc_bulletcounter + 1; if(actor.(weaponentity).misc_bulletcounter == 0) { - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = time + WEP_CVAR(machinegun, burst_refire2) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, burst_animtime), w_ready); } else @@ -238,7 +235,7 @@ METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponen if(weapon_prepareattack(thiswep, actor, weaponentity, false, 0)) { actor.(weaponentity).misc_bulletcounter = 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id, actor, weaponentity); // sets attack_finished + W_MachineGun_Attack(thiswep, thiswep.m_id, actor, weaponentity); // sets attack_finished weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(machinegun, sustained_refire), W_MachineGun_Attack_Frame); } @@ -246,7 +243,7 @@ METHOD(MachineGun, wr_think, void(entity thiswep, entity actor, .entity weaponen if(weapon_prepareattack(thiswep, actor, weaponentity, true, 0)) { actor.(weaponentity).misc_bulletcounter = 1; - W_MachineGun_Attack(WEP_MACHINEGUN, WEP_MACHINEGUN.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished + W_MachineGun_Attack(thiswep, thiswep.m_id | HITTYPE_SECONDARY, actor, weaponentity); // sets attack_finished weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(machinegun, first_refire), w_ready); } } @@ -262,9 +259,9 @@ METHOD(MachineGun, wr_checkammo1, bool(entity thiswep, entity actor, .entity wea if(WEP_CVAR(machinegun, reload_ammo)) { if(WEP_CVAR(machinegun, mode) == 1) - ammo_amount += actor.(weaponentity).(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, sustained_ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, sustained_ammo); else - ammo_amount += actor.(weaponentity).(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo); } return ammo_amount; } @@ -279,9 +276,9 @@ METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor, .entity wea if(WEP_CVAR(machinegun, reload_ammo)) { if(WEP_CVAR(machinegun, mode) == 1) - ammo_amount += actor.(weaponentity).(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, burst_ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, burst_ammo); else - ammo_amount += actor.(weaponentity).(weapon_load[WEP_MACHINEGUN.m_id]) >= WEP_CVAR(machinegun, first_ammo); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(machinegun, first_ammo); } return ammo_amount; } diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index 25bad729fd..6063c666eb 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -27,7 +27,7 @@ void W_MineLayer_Stick(entity this, entity to) newmine.takedamage = this.takedamage; newmine.damageforcescale = this.damageforcescale; - newmine.health = this.health; + SetResourceAmountExplicit(newmine, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); newmine.event_damage = this.event_damage; newmine.spawnshieldtime = this.spawnshieldtime; newmine.damagedbycontents = true; @@ -67,19 +67,17 @@ void W_MineLayer_Explode(entity this, entity directhitentity) RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), NULL, NULL, WEP_CVAR(minelayer, force), this.projectiledeathtype, this.weaponentity_fld, directhitentity); .entity weaponentity = this.weaponentity_fld; - if(this.realowner.(weaponentity).m_weapon == WEP_MINE_LAYER) + Weapon thiswep = WEP_MINE_LAYER; + if(this.realowner.(weaponentity).m_weapon == thiswep) { entity own = this.realowner; - Weapon w = WEP_MINE_LAYER; - if(!w.wr_checkammo1(w, own, weaponentity)) + if(!thiswep.wr_checkammo1(thiswep, own, weaponentity)) { - own.cnt = WEP_MINE_LAYER.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(own, slot) = time; + own.cnt = thiswep.m_id; + ATTACK_FINISHED(own, weaponentity) = time; own.(weaponentity).m_switchweapon = w_getbestweapon(own, weaponentity); } } - this.realowner.(weaponentity).minelayer_mines -= 1; delete(this); } @@ -96,23 +94,21 @@ void W_MineLayer_DoRemoteExplode(entity this) if(this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FOLLOW) this.velocity = this.mine_orientation; // particle fx and decals need .velocity - RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), + RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, remote_damage), WEP_CVAR(minelayer, remote_edgedamage), WEP_CVAR(minelayer, remote_radius), NULL, NULL, WEP_CVAR(minelayer, remote_force), this.projectiledeathtype | HITTYPE_BOUNCE, this.weaponentity_fld, NULL); .entity weaponentity = this.weaponentity_fld; - if(this.realowner.(weaponentity).m_weapon == WEP_MINE_LAYER) + Weapon thiswep = WEP_MINE_LAYER; + if(this.realowner.(weaponentity).m_weapon == thiswep) { entity own = this.realowner; - Weapon w = WEP_MINE_LAYER; - if(!w.wr_checkammo1(w, own, weaponentity)) + if(!thiswep.wr_checkammo1(thiswep, own, weaponentity)) { - own.cnt = WEP_MINE_LAYER.m_id; - int slot = weaponslot(weaponentity); - ATTACK_FINISHED(own, slot) = time; + own.cnt = thiswep.m_id; + ATTACK_FINISHED(own, weaponentity) = time; own.(weaponentity).m_switchweapon = w_getbestweapon(own, weaponentity); } } - this.realowner.(weaponentity).minelayer_mines -= 1; delete(this); } @@ -226,15 +222,7 @@ void W_MineLayer_Touch(entity this, entity toucher) if(this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FOLLOW) return; // we're already a stuck mine, why do we get called? TODO does this even happen? - if(WarpZone_Projectile_Touch(this, toucher)) - { - if(wasfreed(this)) - { - .entity weaponentity = this.weaponentity_fld; - this.realowner.(weaponentity).minelayer_mines -= 1; - } - return; - } + PROJECTILE_TOUCH(this, toucher); if((toucher && IS_PLAYER(toucher) && !IS_DEAD(toucher)) || toucher.owner == this.owner) { @@ -249,7 +237,7 @@ void W_MineLayer_Touch(entity this, entity toucher) void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; float is_from_enemy = (inflictor.realowner != this.realowner); @@ -257,10 +245,10 @@ void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float da if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, (is_from_enemy ? 1 : -1))) return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); this.angles = vectoangles(this.velocity); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode_think); } @@ -272,7 +260,8 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) // scan how many mines we placed, and return if we reached our limit if(WEP_CVAR(minelayer, limit)) { - if(actor.(weaponentity).minelayer_mines >= WEP_CVAR(minelayer, limit)) + int minecount = W_MineLayer_Count(actor, weaponentity); + if(minecount >= WEP_CVAR(minelayer, limit)) { // the refire delay keeps this message from being spammed Send_Notification(NOTIF_ONE, actor, MSG_MULTI, WEAPON_MINELAYER_LIMIT, WEP_CVAR(minelayer, limit)); @@ -283,7 +272,7 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) W_DecreaseAmmo(thiswep, actor, WEP_CVAR(minelayer, ammo), weaponentity); - W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage), WEP_MINE_LAYER.m_id); + W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage), thiswep.m_id); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); mine = WarpZone_RefSys_SpawnSameRefSys(actor); @@ -300,14 +289,14 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) mine.takedamage = DAMAGE_YES; mine.damageforcescale = WEP_CVAR(minelayer, damageforcescale); - mine.health = WEP_CVAR(minelayer, health); + SetResourceAmountExplicit(mine, RESOURCE_HEALTH, WEP_CVAR(minelayer, health)); mine.event_damage = W_MineLayer_Damage; mine.damagedbycontents = true; IL_PUSH(g_damagedbycontents, mine); set_movetype(mine, MOVETYPE_TOSS); PROJECTILE_MAKETRIGGER(mine); - mine.projectiledeathtype = WEP_MINE_LAYER.m_id; + mine.projectiledeathtype = thiswep.m_id; mine.weaponentity_fld = weaponentity; setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot @@ -338,8 +327,6 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) // common properties MUTATOR_CALLHOOK(EditProjectile, actor, mine); - - actor.(weaponentity).minelayer_mines = W_MineLayer_Count(actor, weaponentity); } bool W_MineLayer_PlacedMines(entity this, .entity weaponentity, bool detonate) @@ -365,7 +352,8 @@ bool W_MineLayer_PlacedMines(entity this, .entity weaponentity, bool detonate) METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor, .entity weaponentity)) { // aim and decide to fire if appropriate - if(actor.(weaponentity).minelayer_mines >= WEP_CVAR(minelayer, limit)) + int minecount = W_MineLayer_Count(actor, weaponentity); + if(minecount >= WEP_CVAR(minelayer, limit)) PHYS_INPUT_BUTTON_ATCK(actor) = false; else PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, WEP_CVAR(minelayer, speed), 0, WEP_CVAR(minelayer, lifetime), false); @@ -439,7 +427,7 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor, .entity weaponentit // but don't fire a new shot at the same time! if(desirabledamage >= 0.75 * coredamage) //this should do group damage in rare fortunate events PHYS_INPUT_BUTTON_ATCK2(actor) = true; - if((skill > 6.5) && (selfdamage > actor.health)) + if((skill > 6.5) && (selfdamage > GetResourceAmount(actor, RESOURCE_HEALTH))) PHYS_INPUT_BUTTON_ATCK2(actor) = false; //if(PHYS_INPUT_BUTTON_ATCK2(actor) == true) // dprint(ftos(desirabledamage),"\n"); @@ -448,6 +436,8 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor, .entity weaponentit } METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { + actor.(weaponentity).minelayer_mines = W_MineLayer_Count(actor, weaponentity); + 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 @@ -472,12 +462,11 @@ METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponent } METHOD(MineLayer, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { - //int slot = 0; // TODO: unhardcode // actually do // don't switch while placing a mine - //if(ATTACK_FINISHED(actor, slot) <= time || PS(actor).m_weapon != WEP_MINE_LAYER) + //if(ATTACK_FINISHED(actor, weaponentity) <= time || PS(actor).m_weapon != WEP_MINE_LAYER) //{ 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(minelayer, ammo); return ammo_amount; //} //return true; diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index 2dcde20d04..6e2bc91cf9 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -54,15 +54,15 @@ void W_Mortar_Grenade_Explode2_use(entity this, entity actor, entity trigger) void W_Mortar_Grenade_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, adaptor_think2use); } @@ -151,7 +151,7 @@ void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(mortar, ammo), weaponentity); - W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage), WEP_MORTAR.m_id); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage), thiswep.m_id); w_shotdir = v_forward; // no TrueAim for grenades please Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -164,7 +164,7 @@ void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity) gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); gren.bouncestop = WEP_CVAR(mortar, bouncestop); PROJECTILE_MAKETRIGGER(gren); - gren.projectiledeathtype = WEP_MORTAR.m_id; + gren.projectiledeathtype = thiswep.m_id; gren.weaponentity_fld = weaponentity; setorigin(gren, w_shotorg); setsize(gren, '-3 -3 -3', '3 3 3'); @@ -176,7 +176,7 @@ void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Mortar_Grenade_Touch1); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_PRI(mortar, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_PRI(mortar, health)); gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; @@ -203,7 +203,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(mortar, ammo), weaponentity); - W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage), WEP_MORTAR.m_id | HITTYPE_SECONDARY); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage), thiswep.m_id | HITTYPE_SECONDARY); w_shotdir = v_forward; // no TrueAim for grenades please Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -216,7 +216,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); gren.bouncestop = WEP_CVAR(mortar, bouncestop); PROJECTILE_MAKETRIGGER(gren); - gren.projectiledeathtype = WEP_MORTAR.m_id | HITTYPE_SECONDARY; + gren.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; gren.weaponentity_fld = weaponentity; setorigin(gren, w_shotorg); setsize(gren, '-3 -3 -3', '3 3 3'); @@ -227,7 +227,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) settouch(gren, W_Mortar_Grenade_Touch2); gren.takedamage = DAMAGE_YES; - gren.health = WEP_CVAR_SEC(mortar, health); + SetResourceAmountExplicit(gren, RESOURCE_HEALTH, WEP_CVAR_SEC(mortar, health)); gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; @@ -325,13 +325,13 @@ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity METHOD(Mortar, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(mortar, ammo); return ammo_amount; } METHOD(Mortar, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(mortar, ammo); return ammo_amount; } METHOD(Mortar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh index 2161d468bf..affec0dbca 100644 --- a/qcsrc/common/weapons/weapon/mortar.qh +++ b/qcsrc/common/weapons/weapon/mortar.qh @@ -4,7 +4,7 @@ CLASS(Mortar, Weapon) /* 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 | WEP_FLAG_NODUAL); +/* flags */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Mortar, bot_pickupbasevalue, float, 7000); /* color */ ATTRIB(Mortar, wpcolor, vector, '1 0 0'); /* modelname */ ATTRIB(Mortar, mdl, string, "gl"); diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index f778e164bd..722171b958 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -1,7 +1,8 @@ #include "porto.qh" #ifdef SVQC -#include <common/triggers/trigger/jumppads.qh> +#include <common/mapobjects/trigger/jumppads.qh> +#include <server/weapons/throwing.qh> REGISTER_MUTATOR(porto_ticker, true); MUTATOR_HOOKFUNCTION(porto_ticker, SV_StartFrame) { @@ -20,7 +21,6 @@ void W_Porto_Success(entity this) delete(this); } -string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity); void W_Porto_Fail(entity this, float failhard) { if(this.realowner == NULL) @@ -37,7 +37,7 @@ void W_Porto_Fail(entity this, float failhard) this.realowner.porto_current = NULL; - if(this.cnt < 0 && !failhard && this.realowner.playerid == this.playerid && !IS_DEAD(this.realowner) && !(this.realowner.weapons & WEPSET(PORTO))) + if(this.cnt < 0 && !failhard && this.realowner.playerid == this.playerid && !IS_DEAD(this.realowner) && !(STAT(WEAPONS, this.realowner) & WEPSET(PORTO))) { setsize(this, '-16 -16 0', '16 16 32'); setorigin(this, this.origin + trace_plane_normal); @@ -198,11 +198,11 @@ void W_Porto_Touch(entity this, entity toucher) } } -void W_Porto_Attack(entity actor, .entity weaponentity, float type) +void W_Porto_Attack(Weapon thiswep, entity actor, .entity weaponentity, float type) { entity gren; - W_SetupShot(actor, weaponentity, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0, WEP_PORTO.m_id); // TODO: does the deathtype even need to be set here? porto can't hurt people + W_SetupShot(actor, weaponentity, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0, thiswep.m_id); // TODO: does the deathtype even need to be set here? porto can't hurt people // always shoot from the eye w_shotdir = v_forward; w_shotorg = actor.origin + actor.view_ofs + ((w_shotorg - actor.origin - actor.view_ofs) * v_forward) * v_forward; @@ -270,7 +270,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone if(!actor.porto_forbidden) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire))) { - W_Porto_Attack(actor, weaponentity, 0); + W_Porto_Attack(thiswep, actor, weaponentity, 0); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } @@ -279,7 +279,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone if(!actor.porto_forbidden) if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(porto, refire))) { - W_Porto_Attack(actor, weaponentity, 1); + W_Porto_Attack(thiswep, actor, weaponentity, 1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready); } } @@ -306,7 +306,7 @@ METHOD(PortoLaunch, wr_think, void(entity thiswep, entity actor, .entity weapone if(!actor.porto_forbidden) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(porto, refire))) { - W_Porto_Attack(actor, weaponentity, -1); + W_Porto_Attack(thiswep, actor, weaponentity, -1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } } diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh index 93b3a6e9f7..a77093944b 100644 --- a/qcsrc/common/weapons/weapon/porto.qh +++ b/qcsrc/common/weapons/weapon/porto.qh @@ -44,4 +44,6 @@ SPAWNFUNC_WEAPON(weapon_porto, WEP_PORTO) .float porto_v_angle_held; .vector right_vector; .float porto_forbidden; + +void W_Porto_Fail(entity this, float failhard); #endif diff --git a/qcsrc/common/weapons/weapon/rifle.qc b/qcsrc/common/weapons/weapon/rifle.qc index 1740c45d84..d4d692ec70 100644 --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@ -19,7 +19,7 @@ void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, flo } for(i = 0; i < pShots; ++i) - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE)); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EFFECT_RIFLE : EFFECT_RIFLE_WEAK)); if(autocvar_g_casings >= 2) { @@ -28,17 +28,17 @@ void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, flo } } -void W_Rifle_Attack(entity actor, .entity weaponentity) +void W_Rifle_Attack(Weapon thiswep, entity actor, .entity weaponentity) { - W_Rifle_FireBullet(WEP_RIFLE, weaponentity, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), WEP_RIFLE.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND_CAMPINGRIFLE_FIRE, actor); + W_Rifle_FireBullet(thiswep, weaponentity, WEP_CVAR_PRI(rifle, spread), WEP_CVAR_PRI(rifle, damage), WEP_CVAR_PRI(rifle, force), WEP_CVAR_PRI(rifle, solidpenetration), WEP_CVAR_PRI(rifle, ammo), thiswep.m_id, WEP_CVAR_PRI(rifle, tracer), WEP_CVAR_PRI(rifle, shots), SND_CAMPINGRIFLE_FIRE, actor); } -void W_Rifle_Attack2(entity actor, .entity weaponentity) +void W_Rifle_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { - W_Rifle_FireBullet(WEP_RIFLE, weaponentity, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), WEP_RIFLE.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND_CAMPINGRIFLE_FIRE2, actor); + W_Rifle_FireBullet(thiswep, weaponentity, WEP_CVAR_SEC(rifle, spread), WEP_CVAR_SEC(rifle, damage), WEP_CVAR_SEC(rifle, force), WEP_CVAR_SEC(rifle, solidpenetration), WEP_CVAR_SEC(rifle, ammo), thiswep.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(rifle, tracer), WEP_CVAR_SEC(rifle, shots), SND_CAMPINGRIFLE_FIRE2, actor); } -.void(entity actor, .entity weaponentity) rifle_bullethail_attackfunc; +.void(Weapon thiswep, entity actor, .entity weaponentity) rifle_bullethail_attackfunc; .WFRAME rifle_bullethail_frame; .float rifle_bullethail_animtime; .float rifle_bullethail_refire; @@ -47,28 +47,27 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent float r, af; Weapon sw = actor.(weaponentity).m_switchweapon; // make it not detect weapon changes as reason to abort firing - int slot = weaponslot(weaponentity); - af = ATTACK_FINISHED(actor, slot); + af = ATTACK_FINISHED(actor, weaponentity); actor.(weaponentity).m_switchweapon = actor.(weaponentity).m_weapon; - ATTACK_FINISHED(actor, slot) = time; + ATTACK_FINISHED(actor, weaponentity) = time; r = weapon_prepareattack(thiswep, actor, weaponentity, actor.rifle_bullethail_frame == WFRAME_FIRE2, actor.rifle_bullethail_refire); if(actor.(weaponentity).m_switchweapon == actor.(weaponentity).m_weapon) actor.(weaponentity).m_switchweapon = sw; if(r) { - actor.rifle_bullethail_attackfunc(actor, weaponentity); + actor.rifle_bullethail_attackfunc(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); } else { - ATTACK_FINISHED(actor, slot) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time + ATTACK_FINISHED(actor, weaponentity) = af; // reset attack_finished if we didn't fire, so the last shot enforces the refire time } } -void W_Rifle_BulletHail(entity actor, .entity weaponentity, float mode, void(entity actor, .entity weaponentity) AttackFunc, WFRAME fr, float animtime, float refire) +void W_Rifle_BulletHail(Weapon thiswep, entity actor, .entity weaponentity, float mode, void(Weapon thiswep, entity actor, .entity weaponentity) AttackFunc, WFRAME fr, float animtime, float refire) { // if we get here, we have at least one bullet to fire - AttackFunc(actor, weaponentity); + AttackFunc(thiswep, actor, weaponentity); if(mode) { // continue hail @@ -122,7 +121,7 @@ METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, if(time >= actor.(weaponentity).rifle_accumulator + WEP_CVAR_PRI(rifle, burstcost)) { weapon_prepareattack_do(actor, weaponentity, false, WEP_CVAR_PRI(rifle, refire)); - W_Rifle_BulletHail(actor, weaponentity, WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + W_Rifle_BulletHail(thiswep, actor, weaponentity, WEP_CVAR_PRI(rifle, bullethail), W_Rifle_Attack, WFRAME_FIRE1, WEP_CVAR_PRI(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); actor.(weaponentity).rifle_accumulator += WEP_CVAR_PRI(rifle, burstcost); } if(fire & 2) @@ -137,7 +136,7 @@ METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, if(time >= actor.(weaponentity).rifle_accumulator + WEP_CVAR_SEC(rifle, burstcost)) { weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(rifle, refire)); - W_Rifle_BulletHail(actor, weaponentity, WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); + W_Rifle_BulletHail(thiswep, actor, weaponentity, WEP_CVAR_SEC(rifle, bullethail), W_Rifle_Attack2, WFRAME_FIRE2, WEP_CVAR_SEC(rifle, animtime), WEP_CVAR_PRI(rifle, refire)); actor.(weaponentity).rifle_accumulator += WEP_CVAR_SEC(rifle, burstcost); } } @@ -148,18 +147,22 @@ METHOD(Rifle, wr_think, void(entity thiswep, entity actor, .entity weaponentity, METHOD(Rifle, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(rifle, ammo); return ammo_amount; } METHOD(Rifle, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(rifle, ammo); return ammo_amount; } METHOD(Rifle, wr_resetplayer, void(entity thiswep, entity actor)) { - actor.rifle_accumulator = time - WEP_CVAR(rifle, bursttime); + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + actor.(weaponentity).rifle_accumulator = time - WEP_CVAR(rifle, bursttime); + } } METHOD(Rifle, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { diff --git a/qcsrc/common/weapons/weapon/rifle.qh b/qcsrc/common/weapons/weapon/rifle.qh index 87bc4d7ec1..560354c052 100644 --- a/qcsrc/common/weapons/weapon/rifle.qh +++ b/qcsrc/common/weapons/weapon/rifle.qh @@ -4,7 +4,7 @@ CLASS(Rifle, Weapon) /* 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 | WEP_FLAG_NODUAL); +/* flags */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_FLAG_PENETRATEWALLS); /* rating */ ATTRIB(Rifle, bot_pickupbasevalue, float, 7000); /* color */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0'); /* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle"); diff --git a/qcsrc/common/weapons/weapon/seeker.qc b/qcsrc/common/weapons/weapon/seeker.qc index 0f0c426ecc..784276ef46 100644 --- a/qcsrc/common/weapons/weapon/seeker.qc +++ b/qcsrc/common/weapons/weapon/seeker.qc @@ -124,18 +124,18 @@ void W_Seeker_Missile_Think(entity this) void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if(!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_projectiles_damage says to halt if(this.realowner == attacker) - this.health = this.health - (damage * 0.25); + TakeResource(this, RESOURCE_HEALTH, (damage * 0.25)); else - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode_think); } @@ -169,7 +169,7 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, v W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, missile_ammo), weaponentity); makevectors(actor.v_angle); - W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0, ((m_target != NULL) ? WEP_SEEKER.m_id | HITTYPE_SECONDARY : WEP_SEEKER.m_id)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0, ((m_target != NULL) ? thiswep.m_id | HITTYPE_SECONDARY : thiswep.m_id)); w_shotorg += f_diff; Send_Effect(EFFECT_SEEKER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -190,16 +190,16 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, v missile.scale = 2; missile.takedamage = DAMAGE_YES; missile.weaponentity_fld = weaponentity; - missile.health = WEP_CVAR(seeker, missile_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(seeker, missile_health)); missile.damageforcescale = WEP_CVAR(seeker, missile_damageforcescale); missile.damagedbycontents = true; IL_PUSH(g_damagedbycontents, missile); //missile.think = W_Seeker_Missile_Animate; // csqc projectiles. if(missile.enemy != NULL) - missile.projectiledeathtype = WEP_SEEKER.m_id | HITTYPE_SECONDARY; + missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; else - missile.projectiledeathtype = WEP_SEEKER.m_id; + missile.projectiledeathtype = thiswep.m_id; setorigin(missile, w_shotorg); @@ -266,7 +266,7 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity) f_diff = '+1.25 +3.75 0'; break; } - W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage), WEP_SEEKER.m_id | HITTYPE_SECONDARY); + W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage), thiswep.m_id | HITTYPE_SECONDARY); w_shotorg += f_diff; Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -281,7 +281,7 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity) missile.nextthink = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand); missile.solid = SOLID_BBOX; set_movetype(missile, MOVETYPE_FLY); - missile.projectiledeathtype = WEP_SEEKER.m_id | HITTYPE_SECONDARY; + missile.projectiledeathtype = thiswep.m_id | HITTYPE_SECONDARY; missile.weaponentity_fld = weaponentity; missile.flags = FL_PROJECTILE; IL_PUSH(g_projectiles, missile); @@ -315,7 +315,7 @@ entity W_Seeker_Tagged_Info(entity isowner, .entity weaponentity, entity istarge return NULL; } -void W_Seeker_Attack(entity actor, .entity weaponentity) +void W_Seeker_Attack(Weapon thiswep, entity actor, .entity weaponentity) { entity closest_target = NULL; @@ -337,7 +337,7 @@ void W_Seeker_Attack(entity actor, .entity weaponentity) closest_target = NULL; } - W_Seeker_Fire_Missile(WEP_SEEKER, actor, weaponentity, '0 0 0', closest_target); + W_Seeker_Fire_Missile(thiswep, actor, weaponentity, '0 0 0', closest_target); } void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack @@ -348,7 +348,7 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek Weapon thiswep = WEP_SEEKER; .entity weaponentity = this.weaponentity_fld; - 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)) + 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 != thiswep)) { delete(this); return; @@ -365,17 +365,17 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek switch(c) { case 0: - W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO + W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO break; case 1: - W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO + W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO break; case 2: - W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO + W_Seeker_Fire_Missile(thiswep, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO break; case 3: default: - W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO + W_Seeker_Fire_Missile(thiswep, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO break; } @@ -415,10 +415,10 @@ void W_Seeker_Tag_Explode(entity this) void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; - this.health = this.health - damage; - if(this.health <= 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) W_Seeker_Tag_Explode(this); } @@ -491,7 +491,7 @@ void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, tag_ammo), weaponentity); - W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count), WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY); + W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count), thiswep.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY); entity missile = new(seeker_tag); missile.weaponentity_fld = weaponentity; @@ -506,7 +506,7 @@ void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity) missile.takedamage = DAMAGE_YES; missile.event_damage = W_Seeker_Tag_Damage; - missile.health = WEP_CVAR(seeker, tag_health); + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, WEP_CVAR(seeker, tag_health)); missile.damageforcescale = WEP_CVAR(seeker, tag_damageforcescale); setorigin(missile, w_shotorg); @@ -550,7 +550,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, missile_refire))) { - W_Seeker_Attack(actor, weaponentity); + W_Seeker_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready); } } @@ -590,12 +590,12 @@ METHOD(Seeker, wr_checkammo1, bool(entity thiswep, entity actor, .entity weapone if(WEP_CVAR(seeker, type) == 1) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, missile_ammo); } else { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, tag_ammo); } return ammo_amount; } @@ -605,12 +605,12 @@ METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor, .entity weapone if(WEP_CVAR(seeker, type) == 1) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, tag_ammo); } else { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR(seeker, flac_ammo); } return ammo_amount; } diff --git a/qcsrc/common/weapons/weapon/shockwave.qc b/qcsrc/common/weapons/weapon/shockwave.qc index e9d55b2423..7e60667e53 100644 --- a/qcsrc/common/weapons/weapon/shockwave.qc +++ b/qcsrc/common/weapons/weapon/shockwave.qc @@ -106,7 +106,7 @@ void W_Shockwave_Melee_Think(entity this) // handle accuracy if(accuracy_isgooddamage(this.realowner, target_victim)) - { accuracy_add(this.realowner, WEP_SHOCKWAVE.m_id, 0, swing_damage); } + { accuracy_add(this.realowner, WEP_SHOCKWAVE, 0, swing_damage); } #ifdef DEBUG_SHOCKWAVE LOG_INFOF( @@ -157,7 +157,7 @@ void W_Shockwave_Melee(Weapon thiswep, entity actor, .entity weaponentity, int f setthink(meleetemp, W_Shockwave_Melee_Think); meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor(actor); meleetemp.weaponentity_fld = weaponentity; - W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range), WEP_SHOCKWAVE.m_id | HITTYPE_SECONDARY); + W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range), thiswep.m_id | HITTYPE_SECONDARY); } // SHOCKWAVE ATTACK MODE @@ -265,7 +265,7 @@ void W_Shockwave_Send(entity actor) WriteByte(MSG_BROADCAST, etof(actor)); } -void W_Shockwave_Attack(entity actor, .entity weaponentity) +void W_Shockwave_Attack(Weapon thiswep, entity actor, .entity weaponentity) { // declarations float multiplier, multiplier_from_accuracy, multiplier_from_distance; @@ -276,7 +276,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity) float i, queue = 0; // set up the shot direction - W_SetupShot(actor, weaponentity, true, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage), WEP_SHOCKWAVE.m_id); + W_SetupShot(actor, weaponentity, true, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage), thiswep.m_id); vector attack_endpos = (w_shotorg + (w_shotdir * WEP_CVAR(shockwave, blast_distance))); WarpZone_TraceLine(w_shotorg, attack_endpos, MOVE_NOMONSTERS, actor); vector attack_hitpos = trace_endpos; @@ -292,7 +292,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity) WEP_CVAR(shockwave, blast_splash_edgedamage), WEP_CVAR(shockwave, blast_splash_radius), w_shotdir * WEP_CVAR(shockwave, blast_splash_force), - WEP_SHOCKWAVE.m_id, + thiswep.m_id, 0, actor ); @@ -381,7 +381,7 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity) actor, actor, final_damage, - WEP_SHOCKWAVE.m_id, + thiswep.m_id, weaponentity, head.origin, final_force @@ -566,14 +566,14 @@ void W_Shockwave_Attack(entity actor, .entity weaponentity) actor, actor, final_damage, - WEP_SHOCKWAVE.m_id, + thiswep.m_id, weaponentity, head.origin, final_force ); if(accuracy_isgooddamage(actor, head)) - accuracy_add(actor, WEP_SHOCKWAVE.m_id, 0, final_damage); + accuracy_add(actor, thiswep, 0, final_damage); #ifdef DEBUG_SHOCKWAVE LOG_INFOF( @@ -608,7 +608,7 @@ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponent { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(shockwave, blast_animtime))) { - W_Shockwave_Attack(actor, weaponentity); + W_Shockwave_Attack(thiswep, actor, weaponentity); actor.(weaponentity).shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready); } diff --git a/qcsrc/common/weapons/weapon/shotgun.qc b/qcsrc/common/weapons/weapon/shotgun.qc index 7cabd77103..8faac3d5d0 100644 --- a/qcsrc/common/weapons/weapon/shotgun.qc +++ b/qcsrc/common/weapons/weapon/shotgun.qc @@ -2,15 +2,15 @@ #ifdef SVQC -void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary) +void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary, float ammocount, float damage, float bullets, float spread, float solidpenetration, float force, entity bullet_trail_effect) { - W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(shotgun, ammo), weaponentity); + W_DecreaseAmmo(thiswep, actor, ammocount, weaponentity); - W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets), WEP_SHOTGUN.m_id); - for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1) - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR_PRI(shotgun, spread), WEP_CVAR_PRI(shotgun, solidpenetration), WEP_CVAR_PRI(shotgun, damage), WEP_CVAR_PRI(shotgun, force), WEP_SHOTGUN.m_id, 0); + W_SetupShot(actor, weaponentity, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), damage * bullets, thiswep.m_id); + for(int sc = 0;sc < bullets;sc = sc + 1) + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, spread, solidpenetration, damage, force, thiswep.m_id, bullet_trail_effect); - Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo)); + Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, ammocount); // casing code if(autocvar_g_casings >= 1) @@ -68,7 +68,7 @@ void W_Shotgun_Melee_Think(entity this) + (v_up * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_up)) + (v_right * swing_factor * WEP_CVAR_SEC(shotgun, melee_swing_side))); - WarpZone_traceline_antilag(this, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ((IS_CLIENT(this.realowner)) ? ANTILAG_LATENCY(this.realowner) : 0)); + WarpZone_traceline_antilag(this.realowner, this.realowner.origin + this.realowner.view_ofs, targpos, false, this.realowner, ((IS_CLIENT(this.realowner)) ? ANTILAG_LATENCY(this.realowner) : 0)); // draw lightning beams for debugging //te_lightning2(NULL, targpos, this.realowner.origin + this.realowner.view_ofs + v_forward * 5 - v_up * 5); @@ -77,7 +77,7 @@ void W_Shotgun_Melee_Think(entity this) is_player = (IS_PLAYER(trace_ent) || trace_ent.classname == "body" || IS_MONSTER(trace_ent)); if((trace_fraction < 1) // if trace is good, apply the damage and remove this - && (trace_ent.takedamage == DAMAGE_AIM) + && (trace_ent.takedamage != DAMAGE_NO) && (trace_ent != this.swing_alreadyhit) && (is_player || WEP_CVAR_SEC(shotgun, melee_nonplayerdamage))) { @@ -95,7 +95,7 @@ void W_Shotgun_Melee_Think(entity this) this.realowner.origin + this.realowner.view_ofs, v_forward * WEP_CVAR_SEC(shotgun, force)); - if(accuracy_isgooddamage(this.realowner, target_victim)) { accuracy_add(this.realowner, WEP_SHOTGUN.m_id, 0, swing_damage); } + if(accuracy_isgooddamage(this.realowner, target_victim)) { accuracy_add(this.realowner, WEP_SHOTGUN, 0, swing_damage); } // draw large red flash for debugging //te_customflash(targpos, 200, 2, '15 0 0'); @@ -152,7 +152,14 @@ void W_Shotgun_Attack3_Frame2(Weapon thiswep, entity actor, .entity weaponentity } sound(actor, CH_WEAPON_SINGLE, SND_Null, VOL_BASE, ATTN_NORM); // kill previous sound - W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true); // actually is secondary, but we trick the last shot into playing full reload sound + W_Shotgun_Attack(thiswep, actor, weaponentity, true, + WEP_CVAR_PRI(shotgun, ammo), + WEP_CVAR_PRI(shotgun, damage), + WEP_CVAR_PRI(shotgun, bullets), + WEP_CVAR_PRI(shotgun, spread), + WEP_CVAR_PRI(shotgun, solidpenetration), + WEP_CVAR_PRI(shotgun, force), + EFFECT_BULLET_WEAK); // actually is secondary, but we trick the last shot into playing full reload sound weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), w_ready); } void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire) @@ -165,7 +172,14 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity return; } - W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false); + W_Shotgun_Attack(thiswep, actor, weaponentity, false, + WEP_CVAR_PRI(shotgun, ammo), + WEP_CVAR_PRI(shotgun, damage), + WEP_CVAR_PRI(shotgun, bullets), + WEP_CVAR_PRI(shotgun, spread), + WEP_CVAR_PRI(shotgun, solidpenetration), + WEP_CVAR_PRI(shotgun, force), + EFFECT_BULLET_WEAK); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2); } @@ -178,6 +192,7 @@ METHOD(Shotgun, wr_aim, void(entity thiswep, entity actor, .entity weaponentity) else PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, weaponentity, 1000000, 0, 0.001, false); } + METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(shotgun, reload_ammo) && actor.(weaponentity).clip_load < WEP_CVAR_PRI(shotgun, ammo)) // forced reload @@ -195,7 +210,14 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime))) { - W_Shotgun_Attack(thiswep, actor, weaponentity, true); + W_Shotgun_Attack(thiswep, actor, weaponentity, true, + WEP_CVAR_PRI(shotgun, ammo), + WEP_CVAR_PRI(shotgun, damage), + WEP_CVAR_PRI(shotgun, bullets), + WEP_CVAR_PRI(shotgun, spread), + WEP_CVAR_PRI(shotgun, solidpenetration), + WEP_CVAR_PRI(shotgun, force), + EFFECT_BULLET_WEAK); actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready); } @@ -207,7 +229,14 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime))) { - W_Shotgun_Attack(thiswep, actor, weaponentity, false); + W_Shotgun_Attack(thiswep, actor, weaponentity, false, + WEP_CVAR_PRI(shotgun, ammo), + WEP_CVAR_PRI(shotgun, damage), + WEP_CVAR_PRI(shotgun, bullets), + WEP_CVAR_PRI(shotgun, spread), + WEP_CVAR_PRI(shotgun, solidpenetration), + WEP_CVAR_PRI(shotgun, force), + EFFECT_BULLET_WEAK); actor.(weaponentity).shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame1); } @@ -226,7 +255,7 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit METHOD(Shotgun, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(shotgun, ammo); return ammo_amount; } METHOD(Shotgun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) @@ -240,7 +269,7 @@ METHOD(Shotgun, wr_checkammo2, bool(entity thiswep, entity actor, .entity weapon case 2: // secondary triple shot { 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(shotgun, ammo); return ammo_amount; } default: return false; // secondary unavailable diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh index ffa1dd6e2d..d932d98bd1 100644 --- a/qcsrc/common/weapons/weapon/tuba.qh +++ b/qcsrc/common/weapons/weapon/tuba.qh @@ -45,9 +45,9 @@ SPAWNFUNC_WEAPON(weapon_tuba, WEP_TUBA) #ifdef CSQC entityclass(Tuba); -class(Tuba) .int note; -class(Tuba) .bool tuba_attenuate; -class(Tuba) .float tuba_volume; -class(Tuba) .float tuba_volume_initial; -class(Tuba) .int tuba_instrument; +classfield(Tuba) .int note; +classfield(Tuba) .bool tuba_attenuate; +classfield(Tuba) .float tuba_volume; +classfield(Tuba) .float tuba_volume_initial; +classfield(Tuba) .int tuba_instrument; #endif diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 06562f68a8..cefa4558f2 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -105,7 +105,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew) void W_RocketMinsta_Explosion(entity actor, .entity weaponentity, vector loc) { if(accuracy_canbegooddamage(actor)) - accuracy_add(actor, WEP_DEVASTATOR.m_id, autocvar_g_rm_damage, 0); + accuracy_add(actor, WEP_DEVASTATOR, autocvar_g_rm_damage, 0); entity dmgent = spawn(); dmgent.owner = dmgent.realowner = actor; setorigin(dmgent, loc); @@ -118,14 +118,14 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity) bool flying = IsFlying(actor); // do this BEFORE to make the trace values from FireRailgunBullet last float vaporizer_damage = ((WEP_CVAR_PRI(vaporizer, damage) > 0) ? WEP_CVAR_PRI(vaporizer, damage) : 10000); - W_SetupShot(actor, weaponentity, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage, WEP_VAPORIZER.m_id); + W_SetupShot(actor, weaponentity, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage, thiswep.m_id); // handle sound separately so we can change the volume // added bonus: no longer plays the strength sound (strength gives no bonus to instakill anyway) sound (actor, CH_WEAPON_A, SND_MINSTANEXFIRE, VOL_BASE * 0.8, ATTEN_NORM); yoda = 0; damage_goodhits = 0; - FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, vaporizer_damage, 800, 0, 0, 0, 0, WEP_VAPORIZER.m_id); + FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, vaporizer_damage, WEP_CVAR_PRI(vaporizer, force), 0, 0, 0, 0, thiswep.m_id); // do this now, as goodhits is disabled below SendCSQCVaporizerBeamParticle(actor, damage_goodhits); @@ -144,7 +144,7 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor, .entity weaponentity) if(!(trace_dphitq3surfaceflags & (Q3SURFACEFLAG_SKY | Q3SURFACEFLAG_NOIMPACT))) W_RocketMinsta_Explosion(actor, weaponentity, trace_endpos); - W_DecreaseAmmo(thiswep, actor, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)), weaponentity); + W_DecreaseAmmo(thiswep, actor, ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)), weaponentity); } void W_RocketMinsta_Laser_Explode (entity this, entity directhitentity) @@ -288,14 +288,14 @@ METHOD(Vaporizer, wr_aim, void(entity thiswep, entity actor, .entity weaponentit } METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); + float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); // if the laser uses load, we also consider its ammo for reloading if(WEP_CVAR(vaporizer, reload_ammo) && WEP_CVAR_SEC(vaporizer, ammo) && actor.(weaponentity).clip_load < min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo))) { // forced reload thiswep.wr_reload(thiswep, actor, weaponentity); } else if(WEP_CVAR(vaporizer, reload_ammo) && actor.(weaponentity).clip_load < vaporizer_ammo) { // forced reload thiswep.wr_reload(thiswep, actor, weaponentity); } - if((fire & 1) && (actor.ammo_cells || !autocvar_g_rm) && !forbidWeaponUse(actor)) + if((fire & 1) && (GetResourceAmount(actor, RESOURCE_CELLS) || !autocvar_g_rm) && !forbidWeaponUse(actor)) { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire))) { @@ -303,7 +303,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); } } - if((fire & 2) || ((fire & 1) && !actor.ammo_cells && autocvar_g_rm)) + if((fire & 2) || ((fire & 1) && !GetResourceAmount(actor, RESOURCE_CELLS) && autocvar_g_rm)) { if((autocvar_g_rm && autocvar_g_rm_laser) || autocvar_g_rm_laser == 2) { @@ -349,9 +349,9 @@ METHOD(Vaporizer, wr_setup, void(entity thiswep, entity actor, .entity weaponent } METHOD(Vaporizer, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { - float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); + float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); float ammo_amount = GetResourceAmount(actor, thiswep.ammo_type) >= vaporizer_ammo; - ammo_amount += actor.(weaponentity).(weapon_load[WEP_VAPORIZER.m_id]) >= vaporizer_ammo; + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= vaporizer_ammo; return ammo_amount; } METHOD(Vaporizer, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) @@ -359,7 +359,7 @@ METHOD(Vaporizer, wr_checkammo2, bool(entity thiswep, entity actor, .entity weap if(!WEP_CVAR_SEC(vaporizer, ammo)) return true; 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(vaporizer, ammo); return ammo_amount; } METHOD(Vaporizer, wr_resetplayer, void(entity thiswep, entity actor)) @@ -368,7 +368,7 @@ METHOD(Vaporizer, wr_resetplayer, void(entity thiswep, entity actor)) } METHOD(Vaporizer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - float vaporizer_ammo = ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); + float vaporizer_ammo = ((autocvar_g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo)); float used_ammo; if(WEP_CVAR_SEC(vaporizer, ammo)) used_ammo = min(vaporizer_ammo, WEP_CVAR_SEC(vaporizer, ammo)); diff --git a/qcsrc/common/weapons/weapon/vaporizer.qh b/qcsrc/common/weapons/weapon/vaporizer.qh index ea9f8dd2ba..9bbc5e9bdd 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qh +++ b/qcsrc/common/weapons/weapon/vaporizer.qh @@ -22,7 +22,8 @@ CLASS(Vaporizer, Weapon) BEGIN(class) \ P(class, prefix, ammo, float, PRI) \ P(class, prefix, animtime, float, PRI) \ - P(class, prefix, damage, float, PRI) \ + P(class, prefix, damage, float, PRI) \ + P(class, prefix, force, float, PRI) \ P(class, prefix, refire, float, PRI) \ P(class, prefix, ammo, float, SEC) \ P(class, prefix, animtime, float, SEC) \ diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index 8c590bf6e2..2e9a60ab85 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -105,6 +105,10 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float i myforcehalflife = WEP_CVAR_BOTH(vortex, !issecondary, damagefalloff_forcehalflife); myammo = WEP_CVAR_BOTH(vortex, !issecondary, ammo); + float dtype = thiswep.m_id; + if(WEP_CVAR_BOTH(vortex, !issecondary, armorpierce)) + dtype |= HITTYPE_ARMORPIERCE; + float flying; flying = IsFlying(actor); // do this BEFORE to make the trace values from FireRailgunBullet last @@ -120,7 +124,7 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float i mydmg *= charge; myforce *= charge; - W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg, WEP_VORTEX.m_id); + W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg, dtype); if(charge > WEP_CVAR(vortex, charge_animlimit) && WEP_CVAR(vortex, charge_animlimit)) // if the Vortex is overcharged, we play an extra sound { sound(actor, CH_WEAPON_B, SND_NEXCHARGE, VOL_BASE * (charge - 0.5 * WEP_CVAR(vortex, charge_animlimit)) / (1 - 0.5 * WEP_CVAR(vortex, charge_animlimit)), ATTN_NORM); @@ -128,7 +132,7 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float i yoda = 0; damage_goodhits = 0; - FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, WEP_VORTEX.m_id); + FireRailgunBullet(actor, weaponentity, w_shotorg, w_shotorg + w_shotdir * max_shot_distance, mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, dtype); if(yoda && flying) Send_Notification(NOTIF_ONE, actor, MSG_ANNCE, ANNCE_ACHIEVEMENT_YODA); @@ -224,7 +228,7 @@ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity { actor.(weaponentity).clip_load = max(WEP_CVAR_SEC(vortex, ammo), actor.(weaponentity).clip_load - WEP_CVAR_SEC(vortex, ammo) * dt); } - actor.(weaponentity).(weapon_load[WEP_VORTEX.m_id]) = actor.(weaponentity).clip_load; + actor.(weaponentity).(weapon_load[thiswep.m_id]) = actor.(weaponentity).clip_load; } else { @@ -265,7 +269,7 @@ METHOD(Vortex, wr_setup, void(entity thiswep, entity actor, .entity weaponentity METHOD(Vortex, wr_checkammo1, bool(entity thiswep, entity actor, .entity weaponentity)) { 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)); + ammo_amount += (autocvar_g_balance_vortex_reload_ammo && actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_PRI(vortex, ammo)); return ammo_amount; } METHOD(Vortex, wr_checkammo2, bool(entity thiswep, entity actor, .entity weaponentity)) @@ -274,7 +278,7 @@ METHOD(Vortex, wr_checkammo2, bool(entity thiswep, entity actor, .entity weapone { // don't allow charging if we don't have enough 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); + ammo_amount += actor.(weaponentity).(weapon_load[thiswep.m_id]) >= WEP_CVAR_SEC(vortex, ammo); return ammo_amount; } else diff --git a/qcsrc/common/weapons/weapon/vortex.qh b/qcsrc/common/weapons/weapon/vortex.qh index 59152e2759..8a11b2e13e 100644 --- a/qcsrc/common/weapons/weapon/vortex.qh +++ b/qcsrc/common/weapons/weapon/vortex.qh @@ -4,7 +4,7 @@ CLASS(Vortex, Weapon) /* 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); +/* flags */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN); /* rating */ ATTRIB(Vortex, bot_pickupbasevalue, float, 8000); /* color */ ATTRIB(Vortex, wpcolor, vector, '0.5 1 1'); /* modelname */ ATTRIB(Vortex, mdl, string, "nex"); @@ -22,6 +22,7 @@ CLASS(Vortex, Weapon) BEGIN(class) \ P(class, prefix, ammo, float, BOTH) \ P(class, prefix, animtime, float, BOTH) \ + P(class, prefix, armorpierce, float, BOTH) \ P(class, prefix, chargepool, float, SEC) \ P(class, prefix, chargepool_pause_regen, float, SEC) \ P(class, prefix, chargepool_regen, float, SEC) \ diff --git a/qcsrc/common/wepent.qc b/qcsrc/common/wepent.qc index 1b9abdbf0e..6b1797c664 100644 --- a/qcsrc/common/wepent.qc +++ b/qcsrc/common/wepent.qc @@ -20,12 +20,16 @@ MACRO_END \ PROP(false, m_alpha, WEPENT_SET_NORMAL, \ { WriteByte(chan, rint(bound(-1, 254 * this.m_alpha, 254) - -1)); }, \ - { (viewmodels[this.m_wepent_slot]).alpha = (ReadByte() + -1) / 254; }) \ + { (viewmodels[this.m_wepent_slot]).m_alpha = (ReadByte() + -1) / 254; }) \ \ PROP(false, vortex_charge, WEPENT_SET_NORMAL, \ { WriteByte(chan, this.vortex_charge * 255); }, \ { (viewmodels[this.m_wepent_slot]).vortex_charge = ReadByte() / 255; }) \ \ + PROP(false, oknex_charge, WEPENT_SET_NORMAL, \ + { WriteByte(chan, this.oknex_charge * 16); }, \ + { (viewmodels[this.m_wepent_slot]).oknex_charge = ReadByte() / 16; }) \ + \ PROP(false, m_gunalign, WEPENT_SET_NORMAL, \ { WriteByte(chan, this.m_gunalign); }, \ { (viewmodels[this.m_wepent_slot]).m_gunalign = ReadByte(); }) \ @@ -58,7 +62,11 @@ MACRO_END { WriteByte(chan, this.vortex_chargepool_ammo * 16); }, \ { (viewmodels[this.m_wepent_slot]).vortex_chargepool_ammo = ReadByte() / 16; }) \ \ - PROP(false, clip_load, WEPENT_SET_NORMAL, \ + PROP(false, oknex_chargepool_ammo, WEPENT_SET_NORMAL, \ + { WriteByte(chan, this.oknex_chargepool_ammo * 16); }, \ + { (viewmodels[this.m_wepent_slot]).oknex_chargepool_ammo = ReadByte() / 16; }) \ + \ + PROP(false, clip_load, WEPENT_SET_NORMAL, \ { WriteShort(chan, this.clip_load); }, \ { (viewmodels[this.m_wepent_slot]).clip_load = ReadShort(); }) \ \ diff --git a/qcsrc/common/wepent.qh b/qcsrc/common/wepent.qh index dbd38fc10f..556f58194f 100644 --- a/qcsrc/common/wepent.qh +++ b/qcsrc/common/wepent.qh @@ -5,6 +5,8 @@ REGISTER_NET_TEMP(CLIENT_WEPENT) .float vortex_charge; .float vortex_chargepool_ammo; +.float oknex_charge; +.float oknex_chargepool_ammo; .int tuba_instrument; .int minelayer_mines; .float arc_heat_percent; @@ -33,6 +35,8 @@ REGISTER_NET_TEMP(CLIENT_WEPENT) .Weapon switchingweapon; .Weapon switchweapon; + .float m_alpha; + // only for Porto .bool angles_held_status; .vector angles_held; diff --git a/qcsrc/ecs/systems/physics.qc b/qcsrc/ecs/systems/physics.qc index c8b459962e..ae5f119e7f 100644 --- a/qcsrc/ecs/systems/physics.qc +++ b/qcsrc/ecs/systems/physics.qc @@ -6,6 +6,8 @@ void sys_phys_simulate(entity this, float dt); void sys_phys_simulate_simple(entity this, float dt); +void sys_phys_postupdate(entity this); + void sys_phys_update(entity this, float dt) { if (!IS_CLIENT(this)) { @@ -43,7 +45,7 @@ void sys_phys_update(entity this, float dt) float maxspeed_mod = (!this.in_swamp) ? 1 : this.swamp_slowdown; // cvar("g_balance_swamp_moverate"); // conveyors: first fix velocity - if (this.conveyor.state) { this.velocity -= this.conveyor.movedir; } + if (this.conveyor.active) { this.velocity -= this.conveyor.movedir; } MUTATOR_CALLHOOK(PlayerPhysics, this, dt); if (!IS_PLAYER(this)) { @@ -63,7 +65,8 @@ void sys_phys_update(entity this, float dt) // if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER) // { this.velocity_z = 70; } } - goto end; + sys_phys_postupdate(this); + return; } PM_check_slick(this); @@ -153,10 +156,14 @@ void sys_phys_update(entity this, float dt) this.com_phys_air = false; } - LABEL(end) + sys_phys_postupdate(this); +} + +void sys_phys_postupdate(entity this) +{ if (IS_ONGROUND(this)) { this.lastground = time; } // conveyors: then break velocity again - if (this.conveyor.state) { this.velocity += this.conveyor.movedir; } + if (this.conveyor.active) { this.velocity += this.conveyor.movedir; } this.lastflags = this.flags; this.lastclassname = this.classname; diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc index 5ae47c394d..45128393ba 100644 --- a/qcsrc/ecs/systems/sv_physics.qc +++ b/qcsrc/ecs/systems/sv_physics.qc @@ -4,6 +4,7 @@ void sys_phys_fix(entity this, float dt) { WarpZone_PlayerPhysics_FixVAngle(this); Physics_UpdateStats(this); + PM_ClientMovement_UpdateStatus(this); } bool sys_phys_override(entity this, float dt) @@ -33,7 +34,6 @@ void sys_phys_monitor(entity this, float dt) void sys_phys_ai(entity this) { if (!IS_BOT_CLIENT(this)) { return; } - if (playerdemo_read(this)) { return; } bot_think(this); } diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index d44c5e89cd..0bed40bbf0 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -24,12 +24,26 @@ #define bool float #endif +#ifndef QCC_SUPPORT_ACCUMULATE + #warning "QCC does not support accumulate, may not compile correctly" + #define ACCUMULATE +#else + #define ACCUMULATE [[accumulate]] +#endif + #ifndef QCC_SUPPORT_ERASEABLE #define ERASEABLE #else #define ERASEABLE [[eraseable]] #endif +#ifndef QCC_SUPPORT_ALIAS + #warning "QCC does not support alias, may not compile correctly" + #define ALIAS(var) +#else + #define ALIAS(var) [[alias(var)]] +#endif + #include <dpdefs/pre.qh> #if defined(CSQC) @@ -47,6 +61,12 @@ #include <dpdefs/post.qh> +#ifndef QCC_SUPPORT_POW + #define pow(a, b) pow(a, b) +#else + #define pow(a, b) ((a) ** (b)) +#endif + #include "self.qh" #define USING(name, T) typedef T name @@ -113,6 +133,7 @@ #include "oo.qh" #include "p2mathlib.qc" #include "progname.qh" +#include "promise.qc" #include "random.qc" #include "registry.qh" #include "registry_net.qh" diff --git a/qcsrc/lib/_mod.inc b/qcsrc/lib/_mod.inc index 115a6a070f..8df43c95f4 100644 --- a/qcsrc/lib/_mod.inc +++ b/qcsrc/lib/_mod.inc @@ -2,6 +2,7 @@ #include <lib/angle.qc> #include <lib/json.qc> #include <lib/p2mathlib.qc> +#include <lib/promise.qc> #include <lib/random.qc> #include <lib/sortlist.qc> #include <lib/test.qc> diff --git a/qcsrc/lib/_mod.qh b/qcsrc/lib/_mod.qh index fd97f51b65..8645347d69 100644 --- a/qcsrc/lib/_mod.qh +++ b/qcsrc/lib/_mod.qh @@ -2,6 +2,7 @@ #include <lib/angle.qh> #include <lib/json.qh> #include <lib/p2mathlib.qh> +#include <lib/promise.qh> #include <lib/random.qh> #include <lib/sortlist.qh> #include <lib/test.qh> diff --git a/qcsrc/lib/accumulate.qh b/qcsrc/lib/accumulate.qh index c265325f8f..2d1e40212f 100644 --- a/qcsrc/lib/accumulate.qh +++ b/qcsrc/lib/accumulate.qh @@ -2,7 +2,7 @@ #ifdef QCC_SUPPORT_ACCUMULATE #define ACCUMULATE_FUNCTION(func, otherfunc) \ - [[accumulate]] void func() \ + ACCUMULATE void func() \ { \ otherfunc(); \ } diff --git a/qcsrc/lib/color.qh b/qcsrc/lib/color.qh index 5f9297f2da..6f7a7326de 100644 --- a/qcsrc/lib/color.qh +++ b/qcsrc/lib/color.qh @@ -25,13 +25,13 @@ vector colormapPaletteColor_(int c, bool isPants, float t) case 14: return '1.000000 0.666667 0.000000'; case 15: if (isPants) - return '1 0 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 0.0000000000)) - + '0 1 0' * (0.502 + 0.498 * sin(t / 2.7182818285 + 2.0943951024)) - + '0 0 1' * (0.502 + 0.498 * sin(t / 2.7182818285 + 4.1887902048)); + return '1 0 0' * (0.502 + 0.498 * sin(t / M_E + 0)) + + '0 1 0' * (0.502 + 0.498 * sin(t / M_E + M_PI * 2 / 3)) + + '0 0 1' * (0.502 + 0.498 * sin(t / M_E + M_PI * 4 / 3)); else - return '1 0 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 5.2359877560)) - + '0 1 0' * (0.502 + 0.498 * sin(t / 3.1415926536 + 3.1415926536)) - + '0 0 1' * (0.502 + 0.498 * sin(t / 3.1415926536 + 1.0471975512)); + return '1 0 0' * (0.502 + 0.498 * sin(t / M_PI + M_PI * 5 / 3)) + + '0 1 0' * (0.502 + 0.498 * sin(t / M_PI + M_PI)) + + '0 0 1' * (0.502 + 0.498 * sin(t / M_PI + M_PI * 1 / 3)); default: return '0.000 0.000 0.000'; } } diff --git a/qcsrc/lib/compiler.qh b/qcsrc/lib/compiler.qh index d1bdc4fe5b..5ca0ed5652 100644 --- a/qcsrc/lib/compiler.qh +++ b/qcsrc/lib/compiler.qh @@ -18,6 +18,18 @@ #endif #endif +#ifndef QCC_SUPPORT_ALIAS + #ifdef GMQCC + #define QCC_SUPPORT_ALIAS + #endif +#endif + +#ifndef QCC_SUPPORT_POW + #ifdef GMQCC + #define QCC_SUPPORT_POW + #endif +#endif + #ifdef GMQCC #define LABEL(id) :id #else diff --git a/qcsrc/lib/csqcmodel/cl_model.qc b/qcsrc/lib/csqcmodel/cl_model.qc index 05aba388c4..12be0dbaa4 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qc +++ b/qcsrc/lib/csqcmodel/cl_model.qc @@ -179,7 +179,7 @@ void CSQCModel_InterpolateAnimation_Do(entity this) void CSQCModel_Draw(entity this) { // some nice flags for CSQCMODEL_IF and the hooks - bool isplayer = (this.entnum >= 1 && this.entnum <= maxclients); + bool isplayer = (this.isplayermodel & ISPLAYER_CLIENT); noref bool islocalplayer = (this.entnum == player_localnum + 1); noref bool isnolocalplayer = (isplayer && (this.entnum != player_localnum + 1)); @@ -223,9 +223,10 @@ void CSQCModel_remove(entity this) NET_HANDLE(ENT_CLIENT_MODEL, bool isnew) { int sf = ReadInt24_t(); + int psf = ReadByte(); // some nice flags for CSQCMODEL_IF and the hooks - bool isplayer = ReadByte() || (this.entnum >= 1 && this.entnum <= maxclients); + bool isplayer = (psf & ISPLAYER_CLIENT) || (this.entnum >= 1 && this.entnum <= maxclients); if (isnew && isplayer) { CSQCModel_players[this.entnum - 1] = this; @@ -234,6 +235,10 @@ NET_HANDLE(ENT_CLIENT_MODEL, bool isnew) bool islocalplayer = (this.entnum == player_localnum + 1); noref bool isnolocalplayer = (isplayer && !islocalplayer); + this.isplayermodel = BITSET(this.isplayermodel, ISPLAYER_CLIENT, isplayer); + this.isplayermodel = BITSET(this.isplayermodel, ISPLAYER_LOCAL, islocalplayer); + this.isplayermodel = BITSET(this.isplayermodel, ISPLAYER_PLAYER, (psf & ISPLAYER_PLAYER)); + this.classname = "csqcmodel"; this.iflags |= IFLAG_ORIGIN; // interpolate origin too this.iflags |= IFLAG_ANGLES; // interpolate angles too diff --git a/qcsrc/lib/csqcmodel/cl_player.qc b/qcsrc/lib/csqcmodel/cl_player.qc index 225c7307de..268b591264 100644 --- a/qcsrc/lib/csqcmodel/cl_player.qc +++ b/qcsrc/lib/csqcmodel/cl_player.qc @@ -210,11 +210,11 @@ bool CSQCPlayer_IsLocalPlayer(entity this) /** Called once per CSQC_UpdateView() */ void CSQCPlayer_SetCamera() { - const vector v0 = ((intermission && !autocvar_cl_movement_intermissionrunning) ? '0 0 0' : pmove_vel); // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity - const float vh = PHYS_VIEWHEIGHT(NULL); - const vector pl_viewofs = PHYS_PL_VIEWOFS(NULL); - const vector pl_viewofs_crouch = PHYS_PL_CROUCH_VIEWOFS(NULL); - const entity e = csqcplayer; + vector v0 = ((intermission && !autocvar_cl_movement_intermissionrunning) ? '0 0 0' : pmove_vel); // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity + float vh = PHYS_VIEWHEIGHT(NULL); + vector pl_viewofs = PHYS_PL_VIEWOFS(NULL); + vector pl_viewofs_crouch = PHYS_PL_CROUCH_VIEWOFS(NULL); + entity e = csqcplayer; if (e) { if (servercommandframe == 0 || clientcommandframe == 0) @@ -239,13 +239,13 @@ void CSQCPlayer_SetCamera() } else { - const int flg = e.iflags; e.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES); + int flg = e.iflags; e.iflags &= ~(IFLAG_ORIGIN | IFLAG_ANGLES); InterpolateOrigin_Do(e); e.iflags = flg; if (csqcplayer_status == CSQCPLAYERSTATUS_FROMSERVER) { - const vector o = e.origin; + vector o = e.origin; csqcplayer_status = CSQCPLAYERSTATUS_PREDICTED; CSQCPlayer_PredictTo(e, servercommandframe + 1, false); CSQCPlayer_SetPredictionError(e.origin - o, e.velocity - v0, pmove_onground - IS_ONGROUND(e)); @@ -291,7 +291,7 @@ void CSQCPlayer_SetCamera() // note: these two only work in WIP2, but are harmless in WIP1 if (PHYS_HEALTH(NULL) <= 0 && PHYS_HEALTH(NULL) != -666 && PHYS_HEALTH(NULL) != -2342) refdefflags |= REFDEFFLAG_DEAD; if (intermission) refdefflags |= REFDEFFLAG_INTERMISSION; - V_CalcRefdef(view, refdefflags); + V_CalcRefdef(view, refdefflags); // TODO? uses .health stat in the engine when this isn't called here, may be broken! } else { diff --git a/qcsrc/lib/csqcmodel/common.qh b/qcsrc/lib/csqcmodel/common.qh index f8375d09e8..a2c9e68e55 100644 --- a/qcsrc/lib/csqcmodel/common.qh +++ b/qcsrc/lib/csqcmodel/common.qh @@ -53,6 +53,11 @@ IN THE SOFTWARE.\ .float frame2time; .float lerpfrac; +const int ISPLAYER_MODEL = BIT(0); // using a player model +const int ISPLAYER_CLIENT = BIT(1); // is a client +const int ISPLAYER_LOCAL = BIT(2); // is the local player +const int ISPLAYER_PLAYER = BIT(3); // is a player in the match + const int CSQCMODEL_PROPERTY_FRAME = BIT(23); const int CSQCMODEL_PROPERTY_TELEPORTED = BIT(22); // the "teleport bit" cancelling interpolation const int CSQCMODEL_PROPERTY_MODELINDEX = BIT(21); diff --git a/qcsrc/lib/csqcmodel/sv_model.qc b/qcsrc/lib/csqcmodel/sv_model.qc index 0ff4389944..584bfc23ff 100644 --- a/qcsrc/lib/csqcmodel/sv_model.qc +++ b/qcsrc/lib/csqcmodel/sv_model.qc @@ -32,9 +32,14 @@ bool CSQCModel_Send(entity this, entity to, int sf) noref bool islocalplayer = (this == to); noref bool isnolocalplayer = (isplayer && (this != to)); + int psf = 0; + psf = BITSET(psf, ISPLAYER_CLIENT, isplayer); + psf = BITSET(psf, ISPLAYER_LOCAL, islocalplayer); + psf = BITSET(psf, ISPLAYER_PLAYER, IS_PLAYER(this)); + WriteHeader(MSG_ENTITY, ENT_CLIENT_MODEL); WriteInt24_t(MSG_ENTITY, sf); - WriteByte(MSG_ENTITY, isplayer); + WriteByte(MSG_ENTITY, psf); #define CSQCMODEL_IF(cond) if(cond) { #define CSQCMODEL_ENDIF } diff --git a/qcsrc/lib/cvar.qh b/qcsrc/lib/cvar.qh index a17f2bad72..e8e2c54880 100644 --- a/qcsrc/lib/cvar.qh +++ b/qcsrc/lib/cvar.qh @@ -87,7 +87,7 @@ const noref vector default_vector = '0 0 0'; // e.g.: AUTOCVAR(mycvar, float, 2.5, "cvar description") #define __AUTOCVAR(file, archive, var, type, desc, default) \ - [[accumulate]] void RegisterCvars(void(string, string, string, bool, string) f) \ + ACCUMULATE void RegisterCvars(void(string, string, string, bool, string) f) \ { \ f( #var, repr_cvar_##type(default), desc, archive, file); \ } \ diff --git a/qcsrc/lib/defer.qh b/qcsrc/lib/defer.qh index 4f34bb4853..5d6473080a 100644 --- a/qcsrc/lib/defer.qh +++ b/qcsrc/lib/defer.qh @@ -6,8 +6,8 @@ #include "self.qh" entityclass(Defer); - class(Defer).entity owner; - class(Defer).void(entity) defer_func; + classfield(Defer).entity owner; + classfield(Defer).void(entity) defer_func; /** Remove entity */ void SUB_Remove(entity this) diff --git a/qcsrc/lib/i18n.qh b/qcsrc/lib/i18n.qh index 3dfac62246..841486f583 100644 --- a/qcsrc/lib/i18n.qh +++ b/qcsrc/lib/i18n.qh @@ -45,14 +45,14 @@ ERASEABLE string CTX(string s) { #if CTX_CACHE - string c = HM_gets(CTX_cache, s); - if (c != "") return c; + string c = HM_gets(CTX_cache, s); + if (c != "") return c; #endif int p = strstrofs(s, "^", 0); string ret = (p < 0) ? s : substring(s, p + 1, -1); #if CTX_CACHE - LOG_DEBUGF("CTX(\"%s\")", s); - HM_sets(CTX_cache, s, ret); + LOG_DEBUGF("CTX(\"%s\")", s); + HM_sets(CTX_cache, s, ret); #endif return ret; } diff --git a/qcsrc/lib/math.qh b/qcsrc/lib/math.qh index f20b1c66e5..d8f19906a3 100644 --- a/qcsrc/lib/math.qh +++ b/qcsrc/lib/math.qh @@ -324,9 +324,9 @@ vector solve_quadratic(float a, float b, float c) } /// Maps values between the src and dest range: src_min to dest_min, src_max to dest_max, values between them -/// to the curresponding values between and extrapolates for values outside the range. +/// to the corresponding values between and extrapolates for values outside the range. /// -/// src_min and src_max must not be the same or division by zero accurs. +/// src_min and src_max must not be the same or division by zero occurs. /// /// dest_max can be smaller than dest_min if you want the resulting range to be inverted, all values can be negative. ERASEABLE diff --git a/qcsrc/lib/matrix/command.qc b/qcsrc/lib/matrix/command.qc index 449aa373b3..1b58eb1b9f 100644 --- a/qcsrc/lib/matrix/command.qc +++ b/qcsrc/lib/matrix/command.qc @@ -5,12 +5,10 @@ GENERIC_COMMAND(mx, "Send a matrix command") { switch (argv(1)) { case "user": - if (matrix_user) strunzone(matrix_user); - matrix_user = strzone(substring(command, argv_start_index(2), -1)); + strcpy(matrix_user, substring(command, argv_start_index(2), -1)); break; case "token": - if (matrix_access_token) strunzone(matrix_access_token); - matrix_access_token = strzone(substring(command, argv_start_index(2), -1)); + strcpy(matrix_access_token, substring(command, argv_start_index(2), -1)); break; case "messages": MX_Messages(string_null); diff --git a/qcsrc/lib/matrix/matrix.qc b/qcsrc/lib/matrix/matrix.qc index c399c2aa6c..57754d04e4 100644 --- a/qcsrc/lib/matrix/matrix.qc +++ b/qcsrc/lib/matrix/matrix.qc @@ -204,7 +204,8 @@ void MX_Say_(entity fh, entity pass, int status) fh.url_verb = "PUT"; fh.url_content_type = "application/json"; url_fputs(fh, sprintf("{\"msgtype\": \"m.text\", \"body\": \"%s\"}", pass.message)); - strunzone(pass.message); delete(pass); + strfree(pass.message); + delete(pass); url_fclose(fh); break; } diff --git a/qcsrc/lib/misc.qh b/qcsrc/lib/misc.qh index 6c29a4b88d..cbb1079244 100644 --- a/qcsrc/lib/misc.qh +++ b/qcsrc/lib/misc.qh @@ -10,13 +10,15 @@ #include "p99.qh" #define OVERLOAD(F, ...) P99_IF_EMPTY(__VA_ARGS__)(P99_PASTE2(F, _00)())(P99_PASTE3(F, _, P00_NARG(__VA_ARGS__))(__VA_ARGS__)) - /** for use within a macro */ + /** for use within macros */ #define OVERLOAD_(F, ...) P99_IF_EMPTY(__VA_ARGS__)(P99_PASTE2(F, _00)())(P99_PASTE3(F, _, P00_NARG(__VA_ARGS__))(__VA_ARGS__)) + #define OVERLOAD__(F, ...) P99_IF_EMPTY(__VA_ARGS__)(P99_PASTE2(F, _00)())(P99_PASTE3(F, _, P00_NARG(__VA_ARGS__))(__VA_ARGS__)) #else #define EVAL(...) __VA_ARGS__ - #define OVERLOAD_(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) #define OVERLOAD(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) + #define OVERLOAD_(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) + #define OVERLOAD__(F, ...) F##_##__VA_COUNT__(__VA_ARGS__) #endif #if defined(CSQC) diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index b1f5326a9b..7b3f581b38 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -50,7 +50,7 @@ STATIC_INIT(RegisterTempEntities_renumber) { FOREACH(TempEntities, true, it.m_id #ifdef CSQC #define REGISTER_NET_LINKED(id) \ - [[accumulate]] NET_HANDLE(id, bool isnew) \ + ACCUMULATE NET_HANDLE(id, bool isnew) \ { \ this = __self; \ this.sourceLoc = __FILE__ ":" STR(__LINE__); \ @@ -107,7 +107,6 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } #ifdef SVQC const int MSG_ENTITY = 5; - .int Version; // deprecated, use SendFlags .int SendFlags; IntrusiveList g_uncustomizables; @@ -202,8 +201,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } { if (g_buf == "") return; localcmd("\ncmd c2s \"", strreplace("$", "$$", g_buf), "\"\n"); - strunzone(g_buf); - g_buf = string_null; + strfree(g_buf); } #endif @@ -301,8 +299,12 @@ MACRO_END string s = string_null; yenc_single(b, s); string tmp = strcat(g_buf, s); - if (g_buf) strunzone(g_buf); - g_buf = strzone(tmp); + strcpy(g_buf, tmp); + } + void WriteShort(int to, int b) + { + WriteByte(to, (b >> 8) & 0xFF); + WriteByte(to, b & 0xFF); } #elif defined(SVQC) int ReadByte() @@ -311,6 +313,10 @@ MACRO_END ydec_single(g_buf, ret); return ret; } + int ReadShort() + { + return (ReadByte() << 8) | (ReadByte()); + } void WriteByte(int to, int b); #endif diff --git a/qcsrc/lib/noise.qh b/qcsrc/lib/noise.qh index 782798c9fe..5ef9cf8b6b 100644 --- a/qcsrc/lib/noise.qh +++ b/qcsrc/lib/noise.qh @@ -2,11 +2,11 @@ // noises "usually" start in the range -1..1 entityclass(Noise); -class(Noise).float noise_baccum; -class(Noise).float noise_paccum; -class(Noise).float noise_paccum2; -class(Noise).float noise_paccum3; -class(Noise).float noise_bstate; +classfield(Noise).float noise_baccum; +classfield(Noise).float noise_paccum; +classfield(Noise).float noise_paccum2; +classfield(Noise).float noise_paccum3; +classfield(Noise).float noise_bstate; ERASEABLE float Noise_Brown(entity e, float dt) diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index f57bf8e909..e482d7d9ac 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -5,19 +5,16 @@ #include "static.qh" .vector origin; + .bool pure_data; -/** @deprecated use new_pure or NEW(class) */ -#define make_pure(e) \ - MACRO_BEGIN \ - { \ - (e).pure_data = true; \ - } MACRO_END -#define make_impure(e) \ - MACRO_BEGIN \ - { \ - (e).pure_data = false; \ - } MACRO_END #define is_pure(e) ((e).pure_data) +/** @deprecated use new_pure or NEW(class) */ +#define make_pure(e) MACRO_BEGIN \ + (e).pure_data = true; \ +MACRO_END +#define make_impure(e) MACRO_BEGIN \ + (e).pure_data = false; \ +MACRO_END .string classname; /** Location entity was spawned from in source */ @@ -58,11 +55,11 @@ entity __spawn(string _classname, string _sourceLoc, bool pure) #define entityclass_1(name) entityclass_2(name, Object) #ifndef QCC_SUPPORT_ENTITYCLASS #define entityclass_2(name, base) USING(name, entity) - #define class(name) + #define classfield(name) #define _new(class, pure) __spawn( #class, __FILE__ ":" STR(__LINE__), pure) #else #define entityclass_2(name, base) entityclass name : base {} - #define class(name) [[class(name)]] + #define classfield(name) [[class(name)]] #define _new(class, pure) ((class) __spawn( #class, __FILE__ ":" STR(__LINE__), pure)) #endif /** entities you care about seeing (.origin works) */ @@ -71,12 +68,13 @@ entity __spawn(string _classname, string _sourceLoc, bool pure) #define new_pure(class) _new(class, true) #define spawn() __spawn("entity", __FILE__ ":" STR(__LINE__), false) -[[accumulate]] void ONREMOVE(entity this) {} +ACCUMULATE void ONREMOVE(entity this) {} #ifndef SVQC #define delete_fn builtin_remove #endif +.void(entity this) dtor; #define delete(this) MACRO_BEGIN { \ entity _this = (this); \ void(entity) _dtor = _this.dtor; \ @@ -104,7 +102,7 @@ void clearentity(entity e) } // Classes have a `spawn##cname(entity)` constructor -// The parameter is used across [[accumulate]] functions +// The parameter is used across ACCUMULATE functions .bool transmute; @@ -138,12 +136,149 @@ void clearentity(entity e) } \ MACRO_END -#define CONSTRUCTOR(cname, ...) \ - cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) \ - { \ - return = this; \ - } \ - [[accumulate]] cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) +#define CLASS(...) EVAL_CLASS(OVERLOAD__(CLASS, __VA_ARGS__)) +#define EVAL_CLASS(...) __VA_ARGS__ + +#define ATTRIB(...) EVAL_ATTRIB(OVERLOAD_(ATTRIB, __VA_ARGS__)) +#define EVAL_ATTRIB(...) __VA_ARGS__ + +#ifdef QCC_SUPPORT_CLASS + +#warning "QCC_SUPPORT_CLASS not implemented" + +#define CLASS_1(name) CLASS_2(name, entity) +#define CLASS_2(name, base) class name : base { + +#define INIT(class) void class::class() +#define CONSTRUCTOR(class, ...) void class::class(__VA_ARGS__) +#define DESTRUCTOR(class) class::~class() + +#define SUPER(class) super + +#define ATTRIB_3(class, name, T) T name +#define ATTRIB_4(class, name, T, val) ATTRIB_3(class, name, T) = val +#define STATIC_ATTRIB(class, name, T, val) static T name = val + +#define ATTRIB_STRZONE(class, name, T, val) T name = val +#define STATIC_ATTRIB_STRZONE(class, name, T, val) static T name = val + +#define ATTRIBARRAY(class, name, T, val) T name[val] + +#define METHOD(class, name, prototype) virtual void class::name() +#define STATIC_METHOD(class, name, prototype) static void class::name() + +#define ENDCLASS(class) }; + +#else + +#define CLASS_1(cname) CLASS_2(cname, ) +#define CLASS_2(cname, base) \ + entityclass(cname, base); \ + classfield(cname).bool instanceOf##cname; \ + DEBUG_STUFF(cname) \ + VTBL(cname, base) \ + _INIT_STATIC(cname) \ + { \ + if (cname##_vtbl && !this.transmute) \ + { \ + copyentity(cname##_vtbl, this); \ + return; \ + } \ + spawn##base##_static(this); \ + this.instanceOf##cname = true; \ + } \ + INIT(cname) \ + { \ + /* Only statically initialize the current class, it contains everything it inherits */ \ + if (cname##_vtbl.vtblname == this.classname) \ + { \ + spawn##cname##_static(this); \ + this.transmute = false; \ + this.classname = #cname; \ + this.vtblname = string_null; \ + this.vtblbase = cname##_vtbl; \ + } \ + spawn##base##_1(this); \ + } + +#define INIT(cname) \ + ACCUMULATE cname spawn##cname##_1(cname this) + +#define CONSTRUCTOR(cname, ...) \ + cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) \ + { \ + return = this; \ + } \ + ACCUMULATE cname OVERLOAD(spawn##cname, cname this, __VA_ARGS__) + +#define DESTRUCTOR(cname) \ + STATIC_METHOD(cname, dtorimpl, void(cname this)); \ + METHOD(cname, dtor, void(cname this)) \ + { \ + METHOD_REFERENCE(cname, dtorimpl)(this); \ + this.instanceOf##cname = false; \ + entity super = SUPER(cname); \ + if (super != cname##_vtbl) super.dtor(this); \ + } \ + STATIC_METHOD(cname, dtorimpl, void(cname this)) + +#define SUPER(cname) (cname##_vtbl.vtblbase) + +#define ATTRIB_3(cname, name, type) classfield(cname) .type name +#define ATTRIB_4(cname, name, type, val) \ + ATTRIB_3(cname, name, type); \ + INIT(cname) \ + { \ + noref bool strzone; /* Error on strzone() calls. */ \ + this.name = val; \ + } \ + ATTRIB_3(cname, name, type) + +#define STATIC_ATTRIB(cname, name, type, val) \ + type cname##_##name; \ + _INIT_STATIC(cname) \ + { \ + noref bool strzone; /* Error on strzone() calls. */ \ + cname##_##name = val; \ + } + +// cleanup potentially zoned strings from base classes +#define ATTRIB_STRZONE(cname, name, type, val) \ + classfield(cname).type name; \ + INIT(cname) \ + { \ + strcpy(this.name, val); \ + } + +#define STATIC_ATTRIB_STRZONE(cname, name, type, val) \ + type cname##_##name; \ + _INIT_STATIC(cname) \ + { \ + strcpy(cname##_##name, val); \ + } + +#define ATTRIBARRAY(cname, name, type, cnt) \ + classfield(cname) .type name[cnt] + +#define METHOD(cname, name, prototype) \ + STATIC_METHOD(cname, name, prototype); \ + classfield(cname) .prototype name; \ + _INIT_STATIC(cname) \ + { \ + this.name = METHOD_REFERENCE(cname, name); \ + } \ + STATIC_METHOD(cname, name, prototype) + +#define STATIC_METHOD(cname, name, prototype) \ + prototype METHOD_REFERENCE(cname, name) + +#define ENDCLASS(cname) \ + INIT(cname) \ + { \ + return this; \ + } + +// impl .string vtblname; .entity vtblbase; @@ -168,133 +303,30 @@ STATIC_INIT(RegisterClasses) } \ ACCUMULATE_FUNCTION(RegisterClasses, cname##_vtbl_init) -#define _INIT_STATIC(cname) [[accumulate]] void spawn##cname##_static(cname this) -#define INIT(cname) [[accumulate]] cname spawn##cname##_1(cname this) +#define _INIT_STATIC(cname) ACCUMULATE void spawn##cname##_static(cname this) #if NDEBUG #define DEBUG_STUFF(cname) #else #define DEBUG_STUFF(cname) \ - bool is_##cname(entity e) { return e.instanceOf##cname; } \ - void isnt_##cname(entity e) { eprint(e); } + ERASEABLE bool is_##cname(entity e) { return e.instanceOf##cname; } \ + ERASEABLE void isnt_##cname(entity e) { eprint(e); } #endif - -#define CLASS(cname, base) \ - entityclass(cname, base); \ - class(cname).bool instanceOf##cname; \ - DEBUG_STUFF(cname) \ - VTBL(cname, base) \ - _INIT_STATIC(cname) \ - { \ - if (cname##_vtbl && !this.transmute)\ - { \ - copyentity(cname##_vtbl, this); \ - return; \ - } \ - spawn##base##_static(this); \ - this.instanceOf##cname = true; \ - } \ - INIT(cname) \ - { \ - /* Only statically initialize the current class, it contains everything it inherits */ \ - if (cname##_vtbl.vtblname == this.classname) \ - { \ - spawn##cname##_static(this); \ - this.transmute = false; \ - this.classname = #cname; \ - this.vtblname = string_null; \ - this.vtblbase = cname##_vtbl; \ - } \ - spawn##base##_1(this); \ - } - #define METHOD_REFERENCE(cname, name) \ cname##_##name -#define STATIC_METHOD(cname, name, prototype) \ - prototype METHOD_REFERENCE(cname, name) - -#define METHOD(cname, name, prototype) \ - STATIC_METHOD(cname, name, prototype); \ - class(cname) .prototype name; \ - _INIT_STATIC(cname) \ - { \ - this.name = METHOD_REFERENCE(cname, name); \ - } \ - STATIC_METHOD(cname, name, prototype) - -#define DESTRUCTOR(cname) \ - STATIC_METHOD(cname, dtorimpl, void(cname this)); \ - METHOD(cname, dtor, void(cname this)) \ - { \ - METHOD_REFERENCE(cname, dtorimpl)(this); \ - this.instanceOf##cname = false; \ - entity super = SUPER(cname); \ - if (super != cname##_vtbl) super.dtor(this); \ - } \ - STATIC_METHOD(cname, dtorimpl, void(cname this)) - -#define ATTRIB(...) EVAL_ATTRIB(OVERLOAD_(ATTRIB, __VA_ARGS__)) -#define EVAL_ATTRIB(...) __VA_ARGS__ -#define ATTRIB_3(cname, name, type) INIT(cname) {} class(cname) .type name -#define ATTRIB_4(cname, name, type, val) \ - ATTRIB_3(cname, name, type); \ - INIT(cname) \ - { \ - noref bool strzone; /* Error on strzone() calls. */ \ - this.name = val; \ - } \ - ATTRIB_3(cname, name, type) - -#define STATIC_ATTRIB(cname, name, type, val) \ - type cname##_##name; \ - _INIT_STATIC(cname) \ - { \ - noref bool strzone; /* Error on strzone() calls. */ \ - cname##_##name = val; \ - } - -// cleanup potentially zoned strings from base classes - -#define ATTRIB_STRZONE(cname, name, type, val) \ - class(cname).type name; \ - INIT(cname) \ - { \ - if (this.name) \ - strunzone(this.name); \ - this.name = strzone(val); \ - } - -#define STATIC_ATTRIB_STRZONE(cname, name, type, val) \ - type cname##_##name; \ - _INIT_STATIC(cname) \ - { \ - if (cname##_##name) \ - strunzone(cname##_##name); \ - cname##_##name = val; \ - } - -#define ATTRIBARRAY(cname, name, type, cnt) \ - class(cname) .type name[cnt] - -#define ENDCLASS(cname) \ - INIT(cname) \ - { \ - return this; \ - } - -#define SUPER(cname) (cname##_vtbl.vtblbase) +#endif #define spawn_static(this) #define spawn_1(this) #define _vtbl NULL -CLASS(Object, ); +CLASS(Object) DESTRUCTOR(Object) { builtin_remove(this); } #define remove(this) delete(this) METHOD(Object, describe, string(Object this)) { - TC(Object, this); + TC(Object, this); string s = _("No description"); if (cvar("developer")) { @@ -308,7 +340,7 @@ CLASS(Object, ); } METHOD(Object, display, void(Object this, void(string name, string icon) returns)) { - TC(Object, this); + TC(Object, this); returns(sprintf("entity %i", this), "nopreview_map"); } ENDCLASS(Object) diff --git a/qcsrc/lib/promise.qc b/qcsrc/lib/promise.qc new file mode 100644 index 0000000000..475208e34a --- /dev/null +++ b/qcsrc/lib/promise.qc @@ -0,0 +1,214 @@ +#include "promise.qh" + +.int _ref_count; +.void(entity this) _ref_finalize; + +void ref_init(entity this, int init, void(entity this) finalize) +{ + this._ref_count = init; + this._ref_finalize = finalize; +} + +// todo: rename to `ref` +entity REF(entity this) +{ + this._ref_count += 1; + return this; +} + +entity unref(Promise this) +{ + this._ref_count -= 1; + if (!this._ref_count) { + LOG_DEBUGF("Finalize entity %i (from %s)", this, this.sourceLoc); + this._ref_finalize(this); + return NULL; + } + return this; +} + +enum { + PROMISE_PENDING, + PROMISE_RESOLVED, + PROMISE_REJECTED, +}; + +classfield(Promise) .int _promise_state; +classfield(Promise) .entity _promise_result; +classfield(Promise) .IntrusiveList _promise_handlers; + +entityclass(PromiseHandler); +classfield(PromiseHandler) .Promise _promise_handler_ret; +classfield(PromiseHandler) .entity _promise_handler_data; +classfield(PromiseHandler) .Promise(Promise ret, entity result, entity userdata) _promise_handler_resolve; +classfield(PromiseHandler) .Promise(Promise ret, entity err, entity userdata) _promise_handler_reject; + +void _Promise_finalize(Promise this) +{ + delete(this); +} + +Promise Promise_new_(Promise this) +{ + ref_init(this, 2, _Promise_finalize); + this._promise_result = this; // promises default to being their own result to save on entities + return this; +} + +void _Promise_handle(Promise this, PromiseHandler h); + +void Promise_resolve(Promise this) +{ + if (!this) { + LOG_SEVERE("Attempted to resolve a null promise"); + return; + } + if (this._promise_state != PROMISE_PENDING) { + LOG_SEVEREF("Resolved non-pending promise %i", this); + return; + } + this._promise_state = PROMISE_RESOLVED; + if (this._promise_handlers) { + IL_EACH(this._promise_handlers, true, _Promise_handle(this, it)); + IL_DELETE(this._promise_handlers); + } + unref(this); + return; +} + +void Promise_reject(Promise this) +{ + if (!this) { + LOG_SEVERE("Attempted to reject a null promise"); + return; + } + if (this._promise_state != PROMISE_PENDING) { + LOG_SEVEREF("Rejected non-pending promise %i", this); + return; + } + this._promise_state = PROMISE_REJECTED; + if (this._promise_handlers) { + IL_EACH(this._promise_handlers, true, _Promise_handle(this, it)); + IL_DELETE(this._promise_handlers); + } + unref(this); + return; +} + +Promise _Promise_then( + Promise this, + Promise ret, + Promise(Promise ret, entity result, entity userdata) onResolve, + Promise(Promise ret, entity result, entity userdata) onReject, + entity userdata +); + +void _Promise_handle(Promise this, PromiseHandler h) +{ + switch (this._promise_state) { + case PROMISE_PENDING: + if (!this._promise_handlers) { + this._promise_handlers = IL_NEW(); + } + IL_PUSH(this._promise_handlers, h); + break; + case PROMISE_RESOLVED: { + Promise ret = h._promise_handler_ret; + Promise p = h._promise_handler_resolve(ret, this._promise_result, h._promise_handler_data); + if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret + delete(h); + break; + } + case PROMISE_REJECTED: { + Promise ret = h._promise_handler_ret; + Promise p = h._promise_handler_reject(ret, this._promise_result, h._promise_handler_data); + if (p != ret) _Promise_then(p, ret, func_null, func_null, NULL); // bind p -> ret + delete(h); + break; + } + } +} + +void _Promise_done( + Promise this, + Promise(Promise ret, entity result, entity userdata) onResolve, + Promise(Promise ret, entity err, entity userdata) onReject, + Promise ret, + entity userdata +) +{ + PromiseHandler h = new_pure(PromiseHandler); + h._promise_handler_ret = ret; + h._promise_handler_data = userdata; + h._promise_handler_resolve = onResolve; + h._promise_handler_reject = onReject; + _Promise_handle(this, h); +} + +Promise _Promise_onResolve_default(Promise ret, entity result, entity userdata) +{ + ret._promise_result = result; + Promise_resolve(ret); + return ret; +} + +Promise _Promise_onReject_default(Promise ret, entity err, entity userdata) +{ + ret._promise_result = err; + Promise_reject(ret); + return ret; +} + +Promise _Promise_then( + Promise this, + Promise ret, + Promise(Promise ret, entity result, entity userdata) onResolve, + Promise(Promise ret, entity result, entity userdata) onReject, + entity userdata +) +{ + _Promise_done( + this, + (onResolve ? onResolve : _Promise_onResolve_default), + (onReject ? onReject : _Promise_onReject_default), + ret, + userdata + ); + return ret; +} + +Promise Promise_then_( + Promise this, + Promise ret, + Promise(Promise ret, entity result, entity userdata) onResolve, + entity userdata +) +{ + unref(ret); // ret is a temporary + return _Promise_then(this, ret, onResolve, func_null, userdata); +} + +Promise Promise_catch_( + Promise this, + Promise ret, + Promise(Promise ret, entity result, entity userdata) onReject, + entity userdata +) +{ + unref(ret); // ret is a temporary + return _Promise_then(this, ret, func_null, onReject, userdata); +} + +// utils + +#ifndef MENUQC + +Promise Promise_sleep(float n) +{ + Promise p = unref(Promise_new()); + setthink(p, Promise_resolve); + p.nextthink = time + n; + return p; +} + +#endif diff --git a/qcsrc/lib/promise.qh b/qcsrc/lib/promise.qh new file mode 100644 index 0000000000..a793f58583 --- /dev/null +++ b/qcsrc/lib/promise.qh @@ -0,0 +1,31 @@ +#pragma once + +entityclass(Promise); + +#define Promise_new() Promise_new_(new_pure(Promise)) +Promise Promise_new_(Promise this); + +/** + * notify all Promise_then subscribers that this promise has resolved + */ +void Promise_resolve(Promise this); + +#define Promise_then(this, handler, userdata) Promise_then_(this, Promise_new(), handler, userdata) +Promise Promise_then_(Promise this, Promise ret, Promise(Promise ret, entity result, entity userdata) handler, entity userdata); + +/** + * notify all Promise_catch subscribers that this promise has rejected + */ +void Promise_reject(Promise this); + +#define Promise_catch(this, handler, userdata) Promise_catch_(this, Promise_new(), handler, userdata) +Promise Promise_catch_(Promise this, Promise ret, Promise(Promise ret, entity err, entity userdata) handler, entity userdata); + +// utils + +#ifndef MENUQC + +// TODO: support menu +Promise Promise_sleep(float n); + +#endif diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index 2d41e5d431..d8f18a02c9 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -22,9 +22,9 @@ */ #define REGISTRY(id, max) \ void Register##id(); \ - [[accumulate]] void REGISTRY_DEPENDS_(id) {} \ - [[accumulate]] REGISTRY_BEGIN(id) {} \ - [[accumulate]] REGISTRY_END(id) {} \ + ACCUMULATE void REGISTRY_DEPENDS_(id) {} \ + REGISTRY_BEGIN(id) {} \ + REGISTRY_END(id) {} \ void _Register##id() {} \ int id##_state = 0; \ void Register##id() { if (id##_state) return; id##_state = 1; REGISTRY_DEPENDS_(id); REGISTRY_BEGIN_(id); _Register##id(); id##_state = 2; REGISTRY_END_(id); } \ @@ -40,11 +40,11 @@ #define REGISTRY_DEPENDS_(id) Register##id##_Depends() /** Called before initializing a registry. */ -#define REGISTRY_BEGIN(id) [[accumulate]] void REGISTRY_BEGIN_(id) { noref void() f = Register##id; } void REGISTRY_BEGIN_(id) +#define REGISTRY_BEGIN(id) ACCUMULATE void REGISTRY_BEGIN_(id) { noref void() f = Register##id; } void REGISTRY_BEGIN_(id) #define REGISTRY_BEGIN_(id) Register##id##_First() /** Called after initializing a registry. */ -#define REGISTRY_END(id) [[accumulate]] void REGISTRY_END_(id) { noref void() f = Register##id; } void REGISTRY_END_(id) +#define REGISTRY_END(id) ACCUMULATE void REGISTRY_END_(id) { noref void() f = Register##id; } void REGISTRY_END_(id) #define REGISTRY_END_(id) Register##id##_Done() REGISTRY(Registries, BITS(8)) @@ -104,7 +104,7 @@ REGISTRY(Registries, BITS(8)) REGISTRY_PUSH(registry, fld, e); \ } MACRO_END -#define REGISTER_INIT(id) [[accumulate]] void Register_##id##_init(entity this) +#define REGISTER_INIT(id) ACCUMULATE void Register_##id##_init(entity this) /** internal next pointer */ #define REGISTRY_NEXT enemy @@ -147,9 +147,9 @@ REGISTRY(Registries, BITS(8)) #define REGISTRY_HASH(id) Registry_hash_##id ERASEABLE -[[accumulate]] void Registry_check(string r, string server) { } +ACCUMULATE void Registry_check(string r, string server) { } ERASEABLE -[[accumulate]] void Registry_send_all() { } +ACCUMULATE void Registry_send_all() { } #ifdef SVQC void Registry_send(string id, string hash); diff --git a/qcsrc/lib/replicate.qh b/qcsrc/lib/replicate.qh index c7a42042de..6569897826 100644 --- a/qcsrc/lib/replicate.qh +++ b/qcsrc/lib/replicate.qh @@ -12,21 +12,20 @@ #define REPLICATE(...) EVAL_REPLICATE(OVERLOAD(REPLICATE, __VA_ARGS__)) #define EVAL_REPLICATE(...) __VA_ARGS__ - [[accumulate]] void ReplicateVars(entity this, entity store, string thisname, int i) {} + ACCUMULATE void ReplicateVars(entity this, entity store, string thisname, int i) {} #define REPLICATE_3(fld, type, var) REPLICATE_4(fld, type, var, ) #define REPLICATE_4(fld, type, var, func) REPLICATE_##type(fld, var, func) #define REPLICATE_string(fld, var, func) \ REPLICATE_7(fld, string, var, , \ - { if (field) strunzone(field); field = strzone(it); }, \ - { if (field) strunzone(field); field = string_null; }, \ + { strcpy(field, it); }, \ + { strfree(field); }, \ { \ /* also initialize to the default value of func when requesting cvars */ \ string s = func(field); \ if (s != field) \ { \ - strunzone(field); \ - field = strzone(s); \ + strcpy(field, s); \ } \ }) #define REPLICATE_float(fld, var, func) REPLICATE_7(fld, float, var, func, { field = stof(it); }, , ) diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index 0a61cc003d..4299c19cd3 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -12,7 +12,7 @@ // Step 2: const self #if 1 #define self (RVALUE, self) - [[alias("self")]] entity __self; + ALIAS("self") entity __self; #define setself(s) (__self = s) #define WITHSELF(value, block) WITH(entity, __self, value, (RVALUE, block)) #endif diff --git a/qcsrc/lib/sortlist.qh b/qcsrc/lib/sortlist.qh index af4b47ab80..8c362d4305 100644 --- a/qcsrc/lib/sortlist.qh +++ b/qcsrc/lib/sortlist.qh @@ -2,7 +2,7 @@ entityclass(Sort); // .float(entity,entity) sort_cmp; -class(Sort).entity chain, sort_next, sort_prev; +classfield(Sort).entity chain, sort_next, sort_prev; entity Sort_Spawn(); diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index ac9ad50580..d3198b3ce4 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -19,13 +19,13 @@ noref bool require_spawnfunc_prefix; } #define _spawnfunc_checktypes(fld) \ - if (fieldname == #fld) \ - if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", fieldname); + if (s == #fld) \ + if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", s); #else #define _spawnfunc_checktypes(fld) #endif #define _spawnfunc_check(fld) \ - if (fieldname == #fld) continue; + if (s == #fld) continue; noref int __spawnfunc_expecting; noref entity __spawnfunc_expect; @@ -86,7 +86,7 @@ noref bool require_spawnfunc_prefix; #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) \ + ACCUMULATE void spawnfunc_##id(entity this) \ { \ if (!__spawnfunc_first) { \ __spawnfunc_first = true; \ @@ -111,13 +111,13 @@ noref bool require_spawnfunc_prefix; if (!this.spawnfunc_checked) { \ for (int i = 0, n = numentityfields(); i < n; ++i) { \ string value = getentityfieldstring(i, this); \ - string fieldname = entityfieldname(i); \ + string s = entityfieldname(i); \ whitelist(_spawnfunc_checktypes) \ if (value == "") continue; \ - if (fieldname == "") continue; \ + if (s == "") continue; \ FIELDS_COMMON(_spawnfunc_check) \ whitelist(_spawnfunc_check) \ - LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), #id, fieldname, value); \ + LOG_WARNF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue."), #id, s, value); \ } \ this.spawnfunc_checked = true; \ if (this) { \ @@ -163,6 +163,7 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, bgmscriptsustain) \ FIELD_SCALAR(fld, bgmscript) \ FIELD_SCALAR(fld, button0) \ + FIELD_SCALAR(fld, chmap) \ FIELD_SCALAR(fld, cnt) \ FIELD_SCALAR(fld, colormap) \ FIELD_SCALAR(fld, count) \ @@ -182,10 +183,12 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, dmg_force) \ FIELD_SCALAR(fld, dmg_radius) \ FIELD_SCALAR(fld, effects) \ + FIELD_SCALAR(fld, falloff) \ FIELD_SCALAR(fld, flags) \ FIELD_SCALAR(fld, fog) \ FIELD_SCALAR(fld, frags) \ FIELD_SCALAR(fld, frame) \ + FIELD_SCALAR(fld, gametype) \ FIELD_SCALAR(fld, gametypefilter) \ FIELD_SCALAR(fld, geomtype) \ FIELD_SCALAR(fld, gravity) \ @@ -216,6 +219,7 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, noalign) \ FIELD_SCALAR(fld, noise1) \ FIELD_SCALAR(fld, noise2) \ + FIELD_SCALAR(fld, noise3) \ FIELD_SCALAR(fld, noise) \ FIELD_SCALAR(fld, phase) \ FIELD_SCALAR(fld, platmovetype) \ @@ -243,6 +247,7 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, target_random) \ FIELD_SCALAR(fld, target_range) \ FIELD_SCALAR(fld, team) \ + FIELD_SCALAR(fld, trigger_reverse) \ FIELD_SCALAR(fld, turret_scale_health) \ FIELD_SCALAR(fld, turret_scale_range) \ FIELD_SCALAR(fld, turret_scale_respawn) \ @@ -256,13 +261,13 @@ noref bool require_spawnfunc_prefix; FIELD_VEC(fld, absmin) \ FIELD_VEC(fld, angles) \ FIELD_VEC(fld, avelocity) \ + FIELD_VEC(fld, beam_color)\ FIELD_VEC(fld, debrisavelocityjitter) \ FIELD_VEC(fld, debrisvelocity) \ FIELD_VEC(fld, debrisvelocityjitter) \ FIELD_VEC(fld, color) \ FIELD_VEC(fld, mangle) \ FIELD_VEC(fld, maxs) \ - FIELD_VEC(fld, maxs) \ FIELD_VEC(fld, mins) \ FIELD_VEC(fld, modelscale_vec) \ FIELD_VEC(fld, velocity) \ diff --git a/qcsrc/lib/static.qh b/qcsrc/lib/static.qh index 6f511fcecf..e0ec96b8ec 100644 --- a/qcsrc/lib/static.qh +++ b/qcsrc/lib/static.qh @@ -17,7 +17,9 @@ void profile(string s) } #define _STATIC_INIT(func, where) \ - [[accumulate]] void _static_##func() { profile(#func); } \ + ACCUMULATE void _static_##func##profile() { profile(#func); } \ + ACCUMULATE_FUNCTION(where, _static_##func##profile) \ + ACCUMULATE void _static_##func(); \ ACCUMULATE_FUNCTION(where, _static_##func) \ void _static_##func() diff --git a/qcsrc/lib/stats.qh b/qcsrc/lib/stats.qh index 1100c474cb..03bd34b813 100644 --- a/qcsrc/lib/stats.qh +++ b/qcsrc/lib/stats.qh @@ -53,7 +53,7 @@ int g_magic_stats_hole = 0; REGISTRY_RESERVE(Stats, m_id, STAT_##id, z); \ } \ } \ - [[accumulate]] void stats_get() \ + ACCUMULATE void stats_get() \ { \ T it = getstat_##T(STAT_##id.m_id); \ /* if (it != CAT(_STAT(id), _prev)) \ @@ -111,7 +111,7 @@ int g_magic_stats_hole = 0; REGISTRY_RESERVE(Stats, m_id, STAT_##id, z); \ } \ } \ - [[accumulate]] void stats_add() \ + ACCUMULATE void stats_add() \ { \ .T fld = _STAT(id); \ addstat_##T(STAT_##id.m_id, fld); \ @@ -120,7 +120,7 @@ int g_magic_stats_hole = 0; /** TODO: do we want the global copy to update? */ #define REGISTER_STAT_3(id, T, expr) \ REGISTER_STAT_2(id, T); \ - [[accumulate]] void GlobalStats_update(entity this) { STAT(id, this) = (expr); } \ + ACCUMULATE void GlobalStats_update(entity this) { STAT(id, this) = (expr); } \ STATIC_INIT(worldstat_##id) { entity this = STATS; STAT(id, this) = (expr); } #else #define REGISTER_STAT_2(id, type) diff --git a/qcsrc/lib/string.qh b/qcsrc/lib/string.qh index 0a3fb085c1..96e8a3a276 100644 --- a/qcsrc/lib/string.qh +++ b/qcsrc/lib/string.qh @@ -4,6 +4,25 @@ #include "sort.qh" #include "oo.qh" +// string logic +// +// true: is truthy +// == "": is equal to "" +// is "": has the same string index as the string constant "" +// strunzone: can be strunzoned +// +// | | true | == "" | is "" | strunzone | +// | :----------: | :--: | :---: | :---: | :-------: | +// | nil | | yes | | | +// | strcat(nil) | yes | yes | | | +// | strzone(nil) | yes | yes | | yes | +// | "" | yes | yes | yes | | +// | strcat("") | yes | yes | | | +// | strzone("") | yes | yes | | yes | +// | "s" | yes | | | | +// | strcat("s") | yes | | | | +// | strzone("s") | yes | | | yes | + #ifdef CSQC float stringwidth_colors(string s, vector theSize) { @@ -27,6 +46,20 @@ } #endif +#define strcpy(this, s) MACRO_BEGIN \ + if (this) { \ + strunzone(this); \ + } \ + this = strzone(s); \ +MACRO_END + +#define strfree(this) MACRO_BEGIN \ + if (this) { \ + strunzone(this); \ + } \ + this = string_null; \ +MACRO_END + ERASEABLE string seconds_tostring(float sec) { @@ -161,6 +194,14 @@ string cons(string a, string b) return strcat(a, " ", b); } +ERASEABLE +string cons_mid(string a, string mid, string b) +{ + if (a == "") return b; + if (b == "") return a; + return strcat(a, mid, b); +} + ERASEABLE string substring_range(string s, float b, float e) { diff --git a/qcsrc/lib/test.qh b/qcsrc/lib/test.qh index ff6f2d23d5..8d8de76ec6 100644 --- a/qcsrc/lib/test.qh +++ b/qcsrc/lib/test.qh @@ -5,7 +5,7 @@ /** Use UpperCamelCase for suite and test only */ #define TEST(suite, test) \ void _TEST_##suite##_##test(); \ - [[accumulate]] int TEST_RunAll_accumulated(int f) { \ + ACCUMULATE int TEST_RunAll_accumulated(int f) { \ if (!TEST_Run(#suite "_" #test)) ++f; \ return = f; \ } \ diff --git a/qcsrc/lib/urllib.qc b/qcsrc/lib/urllib.qc index 1572fec07c..2ad7bda246 100644 --- a/qcsrc/lib/urllib.qc +++ b/qcsrc/lib/urllib.qc @@ -55,7 +55,7 @@ float url_URI_Get_Callback(int id, float status, string data) { LOG_INFO("url_URI_Get_Callback: out of memory in buf_create"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return 1; } @@ -64,7 +64,7 @@ float url_URI_Get_Callback(int id, float status, string data) { LOG_INFO("url_URI_Get_Callback: out of memory in buf_create"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return 1; } @@ -77,7 +77,7 @@ float url_URI_Get_Callback(int id, float status, string data) { // an ERROR e.url_ready(e, e.url_ready_pass, -fabs(status)); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return 1; } @@ -108,7 +108,7 @@ void url_single_fopen(string url, int mode, url_ready_func rdy, entity pass) { LOG_INFO("url_single_fopen: out of memory in buf_create"); rdy(e, pass, URL_READY_ERROR); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return; } @@ -231,7 +231,7 @@ void url_fclose(entity e) LOG_INFO("url_fclose: too many concurrent requests"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); buf_del(e.url_wbuf); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return; } @@ -243,7 +243,7 @@ void url_fclose(entity e) LOG_INFO("url_fclose: failure in crypto_uri_postbuf"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); buf_del(e.url_wbuf); - strunzone(e.url_url); + strfree(e.url_url); delete(e); return; } @@ -264,7 +264,7 @@ void url_fclose(entity e) // we have READ all data, just close e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); buf_del(e.url_rbuf); - strunzone(e.url_url); + strfree(e.url_url); delete(e); } } @@ -341,7 +341,7 @@ void url_multi_ready(entity fh, entity me, float status) { LOG_INFO("uri_multi_ready: got HTTP error 422, data is in unusable format - not continuing"); me.url_ready(fh, me.url_ready_pass, status); - strunzone(me.url_url); + strfree(me.url_url); delete(me); return; } @@ -350,7 +350,7 @@ void url_multi_ready(entity fh, entity me, float status) if (n <= me.url_attempt) { me.url_ready(fh, me.url_ready_pass, status); - strunzone(me.url_url); + strfree(me.url_url); delete(me); return; } diff --git a/qcsrc/lib/warpzone/client.qc b/qcsrc/lib/warpzone/client.qc index 3a6765e9d4..7f8b0cdc83 100644 --- a/qcsrc/lib/warpzone/client.qc +++ b/qcsrc/lib/warpzone/client.qc @@ -28,6 +28,8 @@ void WarpZone_Fade_PreDraw(entity this) void WarpZone_Touch(entity this, entity toucher); NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) { + if(!warpzone_warpzones_exist) + cvar_settemp("r_water", "1"); // HACK for DarkPlaces: always enable reflections when a map has warpzones warpzone_warpzones_exist = 1; if (!this.enemy) { @@ -84,6 +86,8 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) NET_HANDLE(ENT_CLIENT_WARPZONE_CAMERA, bool isnew) { + if(!warpzone_cameras_exist) + cvar_settemp("r_water", "1"); // HACK for DarkPlaces: always enable reflections when a map has cameras warpzone_cameras_exist = 1; this.classname = "func_warpzone_camera"; diff --git a/qcsrc/lib/warpzone/mathlib.qc b/qcsrc/lib/warpzone/mathlib.qc index acbc1c61d4..4a7c886106 100644 --- a/qcsrc/lib/warpzone/mathlib.qc +++ b/qcsrc/lib/warpzone/mathlib.qc @@ -63,11 +63,11 @@ float tanh(float e) float exp(float e) { - return (M_E ** e); + return pow(M_E, e); } float exp2(float e) { - return (2 ** e); + return pow(2, e); } float expm1(float e) { @@ -79,16 +79,16 @@ vector frexp(float e) vector v; v.z = 0; v.y = ilogb(e) + 1; - v.x = e / (2 ** v.y); + v.x = e / pow(2, v.y); return v; } int ilogb(float e) { return floor(log2(fabs(e))); } -float ldexp(float e, int e) +float ldexp(float x, int e) { - return e * (2 ** e); + return x * pow(2, e); } float logn(float e, float base) { @@ -117,12 +117,12 @@ vector modf(float f) float scalbn(float e, int n) { - return e * (2 ** n); + return e * pow(2, n); } float cbrt(float e) { - return copysign((fabs(e) ** (1.0/3.0)), e); + return copysign(pow(fabs(e), (1.0/3.0)), e); } float hypot(float e, float f) { diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index ce4535452d..2805c00509 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -6,7 +6,7 @@ #elif defined(SVQC) #include <common/constants.qh> #include <common/net_linked.qh> - #include <common/triggers/subs.qh> + #include <common/mapobjects/subs.qh> #include <common/util.qh> #include <server/constants.qh> #include <server/defs.qh> diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index e727b39e64..a06ac8e231 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -3,7 +3,7 @@ #include "../menu.qh" #include "../item.qh" -#include "../mutators/events.qh" +#include <menu/mutators/_mod.qh> #include <common/command/_mod.qh> @@ -49,6 +49,7 @@ void GameCommand(string theCommand) LOG_INFO(_(" sync - reloads all cvars on the current menu page")); LOG_INFO(_(" directmenu ITEM - select a menu item as main item")); LOG_INFO(_(" dumptree - dump the state of the menu as a tree to the console")); + LOG_INFO("\n"); LOG_INFO("Generic commands shared by all programs:"); GenericCommand_macro_help(); diff --git a/qcsrc/menu/item.qc b/qcsrc/menu/item.qc index a5c7cfa854..6d725259d2 100644 --- a/qcsrc/menu/item.qc +++ b/qcsrc/menu/item.qc @@ -3,26 +3,26 @@ #include "item/container.qh" #include "item/borderimage.qh" - METHOD(Item, destroy, void(Item this)) + METHOD(MenuItem, destroy, void(MenuItem this)) { // free memory associated with this } - METHOD(Item, relinquishFocus, void(Item this)) + METHOD(MenuItem, relinquishFocus, void(MenuItem this)) { entity par = this.parent; if (!par) return; if (par.instanceOfContainer) par.setFocus(par, NULL); } - METHOD(Item, resizeNotify, void(Item this, vector relOrigin, vector relSize, vector absOrigin, vector absSize)) + METHOD(MenuItem, resizeNotify, void(MenuItem this, vector relOrigin, vector relSize, vector absOrigin, vector absSize)) { this.origin = absOrigin; this.size = absSize; } int autocvar_menu_showboxes; - METHOD(Item, draw, void(Item this)) + METHOD(MenuItem, draw, void(MenuItem this)) { if (!autocvar_menu_showboxes) return; vector rgb = '1 0 1'; @@ -53,53 +53,53 @@ } } - METHOD(Item, showNotify, void(Item this)) + METHOD(MenuItem, showNotify, void(MenuItem this)) {} - METHOD(Item, hideNotify, void(Item this)) + METHOD(MenuItem, hideNotify, void(MenuItem this)) {} - METHOD(Item, keyDown, float(Item this, float scan, float ascii, float shift)) + METHOD(MenuItem, keyDown, float(MenuItem this, float scan, float ascii, float shift)) { return 0; // unhandled } - METHOD(Item, keyUp, float(Item this, float scan, float ascii, float shift)) + METHOD(MenuItem, keyUp, float(MenuItem this, float scan, float ascii, float shift)) { return 0; // unhandled } - METHOD(Item, mouseMove, float(Item this, vector pos)) + METHOD(MenuItem, mouseMove, float(MenuItem this, vector pos)) { return 0; // unhandled } - METHOD(Item, mousePress, bool(Item this, vector pos)) + METHOD(MenuItem, mousePress, bool(MenuItem this, vector pos)) { return false; // unhandled } - METHOD(Item, mouseDrag, float(Item this, vector pos)) + METHOD(MenuItem, mouseDrag, float(MenuItem this, vector pos)) { return 0; // unhandled } - METHOD(Item, mouseRelease, float(Item this, vector pos)) + METHOD(MenuItem, mouseRelease, float(MenuItem this, vector pos)) { return 0; // unhandled } void m_play_focus_sound(); - METHOD(Item, focusEnter, void(Item this)) + METHOD(MenuItem, focusEnter, void(MenuItem this)) { if (this.allowFocusSound) m_play_focus_sound(); } - METHOD(Item, focusLeave, void(Item this)) + METHOD(MenuItem, focusLeave, void(MenuItem this)) {} - METHOD(Item, toString, string(Item this)) + METHOD(MenuItem, toString, string(MenuItem this)) { return string_null; } diff --git a/qcsrc/menu/item.qh b/qcsrc/menu/item.qh index 6cee17b3fd..dd1d0679be 100644 --- a/qcsrc/menu/item.qh +++ b/qcsrc/menu/item.qh @@ -5,28 +5,28 @@ #include "draw.qh" #include "menu.qh" -CLASS(Item, Object) - METHOD(Item, draw, void(Item)); - METHOD(Item, keyDown, float(Item, float, float, float)); - METHOD(Item, keyUp, float(Item, float, float, float)); - METHOD(Item, mouseMove, float(Item, vector)); - METHOD(Item, mousePress, bool(Item this, vector pos)); - METHOD(Item, mouseDrag, float(Item, vector)); - METHOD(Item, mouseRelease, float(Item, vector)); - METHOD(Item, focusEnter, void(Item)); - METHOD(Item, focusLeave, void(Item)); - METHOD(Item, resizeNotify, void(Item, vector, vector, vector, vector)); - METHOD(Item, relinquishFocus, void(Item)); - METHOD(Item, showNotify, void(Item)); - METHOD(Item, hideNotify, void(Item)); - METHOD(Item, toString, string(Item)); - METHOD(Item, destroy, void(Item)); - ATTRIB(Item, focused, float, 0); - ATTRIB(Item, focusable, float, 0); - ATTRIB(Item, allowFocusSound, float, 0); - ATTRIB(Item, parent, entity); - ATTRIB(Item, preferredFocusPriority, float, 0); - ATTRIB(Item, origin, vector, '0 0 0'); - ATTRIB(Item, size, vector, '0 0 0'); - ATTRIB(Item, tooltip, string); -ENDCLASS(Item) +CLASS(MenuItem, Object) + METHOD(MenuItem, draw, void(MenuItem)); + METHOD(MenuItem, keyDown, float(MenuItem, float, float, float)); + METHOD(MenuItem, keyUp, float(MenuItem, float, float, float)); + METHOD(MenuItem, mouseMove, float(MenuItem, vector)); + METHOD(MenuItem, mousePress, bool(MenuItem this, vector pos)); + METHOD(MenuItem, mouseDrag, float(MenuItem, vector)); + METHOD(MenuItem, mouseRelease, float(MenuItem, vector)); + METHOD(MenuItem, focusEnter, void(MenuItem)); + METHOD(MenuItem, focusLeave, void(MenuItem)); + METHOD(MenuItem, resizeNotify, void(MenuItem, vector, vector, vector, vector)); + METHOD(MenuItem, relinquishFocus, void(MenuItem)); + METHOD(MenuItem, showNotify, void(MenuItem)); + METHOD(MenuItem, hideNotify, void(MenuItem)); + METHOD(MenuItem, toString, string(MenuItem)); + METHOD(MenuItem, destroy, void(MenuItem)); + ATTRIB(MenuItem, focused, float, 0); + ATTRIB(MenuItem, focusable, float, 0); + ATTRIB(MenuItem, allowFocusSound, float, 0); + ATTRIB(MenuItem, parent, entity); + ATTRIB(MenuItem, preferredFocusPriority, float, 0); + ATTRIB(MenuItem, origin, vector, '0 0 0'); + ATTRIB(MenuItem, size, vector, '0 0 0'); + ATTRIB(MenuItem, tooltip, string); +ENDCLASS(MenuItem) diff --git a/qcsrc/menu/item/container.qh b/qcsrc/menu/item/container.qh index b737526850..bc2d8e6d0b 100644 --- a/qcsrc/menu/item/container.qh +++ b/qcsrc/menu/item/container.qh @@ -2,7 +2,7 @@ #include <menu/item.qh> -CLASS(Container, Item) +CLASS(Container, MenuItem) METHOD(Container, draw, void(entity)); METHOD(Container, keyUp, float(entity, float, float, float)); METHOD(Container, keyDown, float(entity, float, float, float)); diff --git a/qcsrc/menu/item/image.qh b/qcsrc/menu/item/image.qh index 726f328600..0db9c3719a 100644 --- a/qcsrc/menu/item/image.qh +++ b/qcsrc/menu/item/image.qh @@ -1,7 +1,7 @@ #pragma once #include "../item.qh" -CLASS(Image, Item) +CLASS(Image, MenuItem) METHOD(Image, configureImage, void(entity, string)); METHOD(Image, draw, void(entity)); METHOD(Image, toString, string(entity)); diff --git a/qcsrc/menu/item/inputbox.qc b/qcsrc/menu/item/inputbox.qc index a418dc2ce0..3272ed54f3 100644 --- a/qcsrc/menu/item/inputbox.qc +++ b/qcsrc/menu/item/inputbox.qc @@ -22,7 +22,7 @@ void InputBox_setText(entity me, string txt) { - if (me.text) strunzone(me.text); + strfree(me.text); SUPER(InputBox).setText(me, strzone(txt)); } @@ -356,7 +356,7 @@ } // skipping SUPER(InputBox).draw(me); - Item_draw(me); + MenuItem_draw(me); } void InputBox_showNotify(entity me) diff --git a/qcsrc/menu/item/label.qc b/qcsrc/menu/item/label.qc index d21b5676bd..f7a782dabb 100644 --- a/qcsrc/menu/item/label.qc +++ b/qcsrc/menu/item/label.qc @@ -9,8 +9,7 @@ me.text = txt; if (txt != me.currentText) { - if (me.currentText) strunzone(me.currentText); - me.currentText = strzone(txt); + strcpy(me.currentText, txt); me.recalcPos = 1; } } @@ -112,8 +111,7 @@ t = me.textEntity.toString(me.textEntity); if (t != me.currentText) { - if (me.currentText) strunzone(me.currentText); - me.currentText = strzone(t); + strcpy(me.currentText, t); me.recalcPos = 1; } } diff --git a/qcsrc/menu/item/label.qh b/qcsrc/menu/item/label.qh index f91ae8ad3c..2439d1d5be 100644 --- a/qcsrc/menu/item/label.qh +++ b/qcsrc/menu/item/label.qh @@ -1,7 +1,7 @@ #pragma once #include "../item.qh" -CLASS(Label, Item) +CLASS(Label, MenuItem) METHOD(Label, configureLabel, void(entity, string, float, float)); METHOD(Label, draw, void(entity)); METHOD(Label, resizeNotify, void(entity, vector, vector, vector, vector)); diff --git a/qcsrc/menu/item/listbox.qc b/qcsrc/menu/item/listbox.qc index 97f08c9811..c455d2f5ab 100644 --- a/qcsrc/menu/item/listbox.qc +++ b/qcsrc/menu/item/listbox.qc @@ -331,7 +331,7 @@ } } AUTOCVAR(menu_scroll_averaging_time, float, 0.16, "smooth scroll averaging time"); -// scroll faster while dragging the scrollbar + // scroll faster while dragging the scrollbar AUTOCVAR(menu_scroll_averaging_time_pressed, float, 0.06, "smooth scroll averaging time when dragging the scrollbar"); void ListBox_draw(entity me) { @@ -348,7 +348,7 @@ if (me.scrollPos != me.scrollPosTarget) { float averaging_time = (me.pressed == 1) - ? autocvar_menu_scroll_averaging_time_pressed + ? autocvar_menu_scroll_averaging_time_pressed : autocvar_menu_scroll_averaging_time; // this formula works with whatever framerate float f = averaging_time ? exp(-frametime / averaging_time) : 0; diff --git a/qcsrc/menu/item/listbox.qh b/qcsrc/menu/item/listbox.qh index b3f5164a4d..f60066cd7b 100644 --- a/qcsrc/menu/item/listbox.qh +++ b/qcsrc/menu/item/listbox.qh @@ -1,7 +1,7 @@ #pragma once #include "../item.qh" -CLASS(ListBox, Item) +CLASS(ListBox, MenuItem) METHOD(ListBox, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(ListBox, configureListBox, void(entity, float, float)); METHOD(ListBox, draw, void(entity)); diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index af14e0842b..fb6c4aeeda 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -217,8 +217,7 @@ void m_init_delayed() if (m_goto_buffer) { m_goto(m_goto_buffer); - strunzone(m_goto_buffer); - m_goto_buffer = string_null; + strfree(m_goto_buffer); } if (Menu_Active) m_display(); // delayed menu display @@ -550,8 +549,7 @@ void m_tooltip(vector pos) { // fade out if tooltip of a certain item has changed menuTooltipState = 3; - if (prev_tooltip) strunzone(prev_tooltip); - prev_tooltip = strzone(it.tooltip); + strcpy(prev_tooltip, it.tooltip); } else if (menuTooltipItem && !m_testmousetooltipbox(pos)) { @@ -584,8 +582,7 @@ void m_tooltip(vector pos) menuTooltipOrigin.x = -1; // unallocated - if (menuTooltipText) strunzone(menuTooltipText); - menuTooltipText = strzone(gettooltip()); + strcpy(menuTooltipText, gettooltip()); int i = 0; float w = 0; @@ -635,11 +632,7 @@ void m_tooltip(vector pos) if (menuTooltipItem == NULL) { - if (menuTooltipText) - { - strunzone(menuTooltipText); - menuTooltipText = string_null; - } + strfree(menuTooltipText); return; } else @@ -953,8 +946,7 @@ void m_goto(string itemname) { if (!menuInitialized) { - if (m_goto_buffer) strunzone(m_goto_buffer); - m_goto_buffer = strzone(itemname); + strcpy(m_goto_buffer, itemname); return; } if (itemname == "") // this can be called by GameCommand diff --git a/qcsrc/menu/mutators/_mod.inc b/qcsrc/menu/mutators/_mod.inc index 98fb4815c1..2698092690 100644 --- a/qcsrc/menu/mutators/_mod.inc +++ b/qcsrc/menu/mutators/_mod.inc @@ -1 +1,2 @@ // generated file; do not modify +#include <menu/mutators/events.qc> diff --git a/qcsrc/menu/mutators/_mod.qh b/qcsrc/menu/mutators/_mod.qh index 98fb4815c1..93432be42c 100644 --- a/qcsrc/menu/mutators/_mod.qh +++ b/qcsrc/menu/mutators/_mod.qh @@ -1 +1,2 @@ // generated file; do not modify +#include <menu/mutators/events.qh> diff --git a/qcsrc/menu/mutators/events.qc b/qcsrc/menu/mutators/events.qc new file mode 100644 index 0000000000..c2dbb70215 --- /dev/null +++ b/qcsrc/menu/mutators/events.qc @@ -0,0 +1 @@ +#include "events.qh" diff --git a/qcsrc/menu/mutators/events.qh b/qcsrc/menu/mutators/events.qh index 1df38f5af0..af1b7f6324 100644 --- a/qcsrc/menu/mutators/events.qh +++ b/qcsrc/menu/mutators/events.qh @@ -2,6 +2,11 @@ #include <common/mutators/base.qh> +// register all possible hooks here + +// to use a hook, first register your mutator using REGISTER_MUTATOR +// then create your function using MUTATOR_HOOKFUNCTION + // globals string cmd_name; diff --git a/qcsrc/menu/xonotic/campaign.qc b/qcsrc/menu/xonotic/campaign.qc index cb418a4e18..a3ad36355d 100644 --- a/qcsrc/menu/xonotic/campaign.qc +++ b/qcsrc/menu/xonotic/campaign.qc @@ -14,8 +14,7 @@ void rewrapCampaign(float w, float l0, float emptyheight, vector theFontSize) for(i = 0; i < campaign_entries; ++i) { l = l0; - if(campaign_longdesc_wrapped[i]) - strunzone(campaign_longdesc_wrapped[i]); + strfree(campaign_longdesc_wrapped[i]); n = tokenizebyseparator(campaign_longdesc[i], "\n"); r = ""; for(j = 0; j < n; ++j) @@ -58,7 +57,7 @@ void XonoticCampaignList_configureXonoticCampaignList(entity me) me.configureXonoticListBox(me); me.campaignGlob = search_begin("maps/campaign*.txt", true, true); me.loadCvars(me); - me.campaignGo(me, 0); // takes care of enabling/disabling buttons too + me.campaignGo(me, 0); // it makes work buttons too } void XonoticCampaignList_destroy(entity me) @@ -70,12 +69,8 @@ void XonoticCampaignList_destroy(entity me) void XonoticCampaignList_loadCvars(entity me) { // read campaign cvars - if(campaign_name) - strunzone(campaign_name); - if(me.cvarName) - strunzone(me.cvarName); - campaign_name = strzone(cvar_string("g_campaign_name")); - me.cvarName = strzone(strcat("g_campaign", campaign_name, "_index")); + strcpy(campaign_name, cvar_string("g_campaign_name")); + strcpy(me.cvarName, strcat("g_campaign", campaign_name, "_index")); registercvar(me.cvarName, "", 0); // saved by server QC anyway CampaignFile_Unload(); CampaignFile_Load(0, CAMPAIGN_MAX_ENTRIES); @@ -101,12 +96,9 @@ void XonoticCampaignList_saveCvars(entity me) void XonoticCampaignList_campaignGo(entity me, float step) { - float canNext, canPrev; string s; float i, j, n; - canNext = canPrev = 0; - if(me.campaignGlob >= 0) { n = search_getsize(me.campaignGlob); @@ -141,15 +133,10 @@ void XonoticCampaignList_campaignGo(entity me, float step) s = substring(s, 13, strlen(s) - 17); cvar_set("g_campaign_name", s); me.loadCvars(me); - canNext = (j != n - 1); - canPrev = (j != 0); + me.hasNextCampaign = (j != n - 1); + me.hasPrevCampaign = (j != 0); } } - - if(me.buttonNext) - me.buttonNext.disabled = !canNext; - if(me.buttonPrev) - me.buttonPrev.disabled = !canPrev; } void MultiCampaign_Next(entity btn, entity me) @@ -163,6 +150,11 @@ void MultiCampaign_Prev(entity btn, entity me) void XonoticCampaignList_draw(entity me) { + if(me.buttonNext) + me.buttonNext.disabled = !me.hasNextCampaign; + if(me.buttonPrev) + me.buttonPrev.disabled = !me.hasPrevCampaign; + if(cvar(me.cvarName) != me.campaignIndex || cvar_string("g_campaign_name") != campaign_name) me.loadCvars(me); SUPER(XonoticCampaignList).draw(me); @@ -173,8 +165,10 @@ void XonoticCampaignList_resizeNotify(entity me, vector relOrigin, vector relSiz me.itemAbsSize = '0 0 0'; SUPER(XonoticCampaignList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin1 = 0.5 * me.realFontSize.y; me.realUpperMargin2 = me.realUpperMargin1 + 2 * me.realFontSize.y; diff --git a/qcsrc/menu/xonotic/campaign.qh b/qcsrc/menu/xonotic/campaign.qh index 784926f7df..298e275874 100644 --- a/qcsrc/menu/xonotic/campaign.qh +++ b/qcsrc/menu/xonotic/campaign.qh @@ -34,6 +34,8 @@ CLASS(XonoticCampaignList, XonoticListBox) ATTRIB(XonoticCampaignList, cvarName, string); METHOD(XonoticCampaignList, loadCvars, void(entity)); METHOD(XonoticCampaignList, saveCvars, void(entity)); + ATTRIB(XonoticCampaignList, hasNextCampaign, bool, false); + ATTRIB(XonoticCampaignList, hasPrevCampaign, bool, false); ATTRIB(XonoticCampaignList, buttonNext, entity); ATTRIB(XonoticCampaignList, buttonPrev, entity); diff --git a/qcsrc/menu/xonotic/crosshairpreview.qc b/qcsrc/menu/xonotic/crosshairpreview.qc index e11d7dcc0f..f3c3fe24fa 100644 --- a/qcsrc/menu/xonotic/crosshairpreview.qc +++ b/qcsrc/menu/xonotic/crosshairpreview.qc @@ -25,9 +25,7 @@ void XonoticCrosshairPreview_draw(entity me) float a; rgb = stov(cvar_string("crosshair_color")); a = cvar("crosshair_alpha"); - if(me.src) - strunzone(me.src); - me.src = strzone(strcat("/gfx/crosshair", cvar_string("crosshair"))); + strcpy(me.src, strcat("/gfx/crosshair", cvar_string("crosshair"))); sz = draw_PictureSize(me.src); sz = globalToBoxSize(sz, me.size); diff --git a/qcsrc/menu/xonotic/crosshairpreview.qh b/qcsrc/menu/xonotic/crosshairpreview.qh index 7bf363def7..7499f4462d 100644 --- a/qcsrc/menu/xonotic/crosshairpreview.qh +++ b/qcsrc/menu/xonotic/crosshairpreview.qh @@ -1,7 +1,7 @@ #pragma once #include "../item.qh" -CLASS(XonoticCrosshairPreview, Item) +CLASS(XonoticCrosshairPreview, MenuItem) METHOD(XonoticCrosshairPreview, configureXonoticCrosshairPreview, void(entity)); METHOD(XonoticCrosshairPreview, draw, void(entity)); ATTRIB(XonoticCrosshairPreview, src, string); diff --git a/qcsrc/menu/xonotic/cvarlist.qc b/qcsrc/menu/xonotic/cvarlist.qc index 12bb2810ba..9587d5432d 100644 --- a/qcsrc/menu/xonotic/cvarlist.qc +++ b/qcsrc/menu/xonotic/cvarlist.qc @@ -107,17 +107,10 @@ void XonoticCvarList_setSelected(entity me, float i) if(me.nItems == 0) return; - if(me.cvarName) - strunzone(me.cvarName); - if(me.cvarDescription) - strunzone(me.cvarDescription); - if(me.cvarType) - strunzone(me.cvarType); - if(me.cvarDefault) - strunzone(me.cvarDefault); - me.cvarName = strzone(bufstr_get(me.handle, me.selectedItem)); - me.cvarDescription = strzone(cvar_description(me.cvarName)); - me.cvarDefault = strzone(cvar_defstring(me.cvarName)); + strfree(me.cvarType); + strcpy(me.cvarName, bufstr_get(me.handle, me.selectedItem)); + strcpy(me.cvarDescription, cvar_description(me.cvarName)); + strcpy(me.cvarDefault, cvar_defstring(me.cvarName)); me.cvarNameBox.setText(me.cvarNameBox, me.cvarName); me.cvarDescriptionBox.setText(me.cvarDescriptionBox, me.cvarDescription); float needsForcing = me.updateCvarType(me); diff --git a/qcsrc/menu/xonotic/demolist.qc b/qcsrc/menu/xonotic/demolist.qc index b527542a59..0addd6cbed 100644 --- a/qcsrc/menu/xonotic/demolist.qc +++ b/qcsrc/menu/xonotic/demolist.qc @@ -92,8 +92,10 @@ void XonoticDemoList_resizeNotify(entity me, vector relOrigin, vector relSize, v me.itemAbsSize = '0 0 0'; SUPER(XonoticDemoList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnNameOrigin = me.realFontSize.x; @@ -129,8 +131,7 @@ void DemoList_Refresh_Click(entity btn, entity me) void DemoList_Filter_Change(entity box, entity me) { - if(me.filterString) - strunzone(me.filterString); + strfree(me.filterString); if(box.text != "") { diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc index 2e67e7acf8..611ef4a45e 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qc @@ -10,7 +10,9 @@ void XonoticHUDAmmoDialog_fill(entity me) entity e; string panelname = "ammo"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Ammunition display:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh index 9113a02794..3af373e438 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDAmmoDialog, XonoticRootDialog) ATTRIB(XonoticHUDAmmoDialog, title, string, _("Ammo Panel")); ATTRIB(XonoticHUDAmmoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDAmmoDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDAmmoDialog, rows, float, 15); + ATTRIB(XonoticHUDAmmoDialog, rows, float, 15.5); ATTRIB(XonoticHUDAmmoDialog, columns, float, 4); ATTRIB(XonoticHUDAmmoDialog, name, string, "HUDammo"); ATTRIB(XonoticHUDAmmoDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc index 332f6753f7..938f69a8c5 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qc @@ -10,7 +10,9 @@ void XonoticHUDCenterprintDialog_fill(entity me) entity e; string panelname = "centerprint"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TDempty(me, 0.2); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh index 9fc6846eb9..2b952580ec 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDCenterprintDialog, XonoticRootDialog) ATTRIB(XonoticHUDCenterprintDialog, title, string, _("Centerprint Panel")); ATTRIB(XonoticHUDCenterprintDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDCenterprintDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDCenterprintDialog, rows, float, 15); + ATTRIB(XonoticHUDCenterprintDialog, rows, float, 15.5); ATTRIB(XonoticHUDCenterprintDialog, columns, float, 4); ATTRIB(XonoticHUDCenterprintDialog, name, string, "HUDcenterprint"); ATTRIB(XonoticHUDCenterprintDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qc b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qc index 1ffa41f68c..d1612412e6 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qc @@ -9,7 +9,9 @@ void XonoticHUDChatDialog_fill(entity me) entity e; string panelname = "chat"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 2, e = makeXonoticTextLabel(0, _("Chat entries:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh index 570c2d616c..da2f329bba 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDChatDialog, XonoticRootDialog) ATTRIB(XonoticHUDChatDialog, title, string, _("Chat Panel")); ATTRIB(XonoticHUDChatDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDChatDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDChatDialog, rows, float, 15); + ATTRIB(XonoticHUDChatDialog, rows, float, 15.5); ATTRIB(XonoticHUDChatDialog, columns, float, 4); ATTRIB(XonoticHUDChatDialog, name, string, "HUDchat"); ATTRIB(XonoticHUDChatDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc index b46d3aa02e..ef63756870 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qc @@ -8,7 +8,9 @@ void XonoticHUDEngineInfoDialog_fill(entity me) entity e; string panelname = "engineinfo"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Engine info:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh index b741465024..706767146c 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDEngineInfoDialog, XonoticRootDialog) ATTRIB(XonoticHUDEngineInfoDialog, title, string, _("Engine Info Panel")); ATTRIB(XonoticHUDEngineInfoDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDEngineInfoDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDEngineInfoDialog, rows, float, 15); + ATTRIB(XonoticHUDEngineInfoDialog, rows, float, 15.5); ATTRIB(XonoticHUDEngineInfoDialog, columns, float, 4); ATTRIB(XonoticHUDEngineInfoDialog, name, string, "HUDengineinfo"); ATTRIB(XonoticHUDEngineInfoDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc index ac305b8325..e67f63ffcd 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qc @@ -9,7 +9,9 @@ void XonoticHUDHealthArmorDialog_fill(entity me) entity e; string panelname = "healtharmor"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_healtharmor_combined", _("Combine health and armor"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh index b37f41b76e..9342e29330 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDHealthArmorDialog, XonoticRootDialog) ATTRIB(XonoticHUDHealthArmorDialog, title, string, _("Health/Armor Panel")); ATTRIB(XonoticHUDHealthArmorDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDHealthArmorDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDHealthArmorDialog, rows, float, 16); + ATTRIB(XonoticHUDHealthArmorDialog, rows, float, 16.5); ATTRIB(XonoticHUDHealthArmorDialog, columns, float, 4); ATTRIB(XonoticHUDHealthArmorDialog, name, string, "HUDhealtharmor"); ATTRIB(XonoticHUDHealthArmorDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc index d758ad2066..6b11a69151 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qc @@ -8,7 +8,9 @@ void XonoticHUDInfoMessagesDialog_fill(entity me) entity e; string panelname = "infomessages"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Info messages:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh index 5d9032ffb0..a6370d33e3 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDInfoMessagesDialog, XonoticRootDialog) ATTRIB(XonoticHUDInfoMessagesDialog, title, string, _("Info Messages Panel")); ATTRIB(XonoticHUDInfoMessagesDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDInfoMessagesDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDInfoMessagesDialog, rows, float, 15); + ATTRIB(XonoticHUDInfoMessagesDialog, rows, float, 15.5); ATTRIB(XonoticHUDInfoMessagesDialog, columns, float, 4); ATTRIB(XonoticHUDInfoMessagesDialog, name, string, "HUDinfomessages"); ATTRIB(XonoticHUDInfoMessagesDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc index aeb8c8c774..32264c5bcd 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc @@ -13,12 +13,12 @@ void XonoticHUDItemsTimeDialog_fill(entity me) me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextSlider("hud_panel_itemstime")); - e.addValue(e, ZCTX(_("PNL^Disabled")), "0"); - e.addValue(e, ZCTX(_("PNL^Enabled spectating")), "1"); - e.addValue(e, ZCTX(_("PNL^Enabled even playing in warmup")), "2"); + e.addValue(e, _("Disable"), "0"); + e.addValue(e, _("Enable spectating"), "1"); + e.addValue(e, _("Enable even playing in warmup"), "2"); e.configureXonoticTextSliderValues(e); - DIALOG_HUDPANEL_COMMON_NOTOGGLE(); + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 1.4, e = makeXonoticTextLabel(0, _("Align icon:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh index 507dedb7c9..0cb74ea677 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDItemsTimeDialog, XonoticRootDialog) ATTRIB(XonoticHUDItemsTimeDialog, title, string, _("Items Time Panel")); ATTRIB(XonoticHUDItemsTimeDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDItemsTimeDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDItemsTimeDialog, rows, float, 15); + ATTRIB(XonoticHUDItemsTimeDialog, rows, float, 15.5); ATTRIB(XonoticHUDItemsTimeDialog, columns, float, 4); ATTRIB(XonoticHUDItemsTimeDialog, name, string, "HUDitemstime"); ENDCLASS(XonoticHUDItemsTimeDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qc b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qc index 53cad1a930..5023d7a2d9 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qc @@ -4,8 +4,10 @@ void XonoticHUDModIconsDialog_fill(entity me) { - entity e; + //entity e; string panelname = "modicons"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); } diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh index 3a3b72f6c9..a59d09af4b 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDModIconsDialog, XonoticRootDialog) ATTRIB(XonoticHUDModIconsDialog, title, string, _("Mod Icons Panel")); ATTRIB(XonoticHUDModIconsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDModIconsDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDModIconsDialog, rows, float, 15); + ATTRIB(XonoticHUDModIconsDialog, rows, float, 15.5); ATTRIB(XonoticHUDModIconsDialog, columns, float, 4); ATTRIB(XonoticHUDModIconsDialog, name, string, "HUDmodicons"); ATTRIB(XonoticHUDModIconsDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qc b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qc index 490051846e..1dc0a15b70 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qc @@ -9,7 +9,9 @@ void XonoticHUDNotificationDialog_fill(entity me) entity e; string panelname = "notify"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Notifications:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh index f816e4ffe0..be51051f6c 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDNotificationDialog, XonoticRootDialog) ATTRIB(XonoticHUDNotificationDialog, title, string, _("Notification Panel")); ATTRIB(XonoticHUDNotificationDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDNotificationDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDNotificationDialog, rows, float, 15); + ATTRIB(XonoticHUDNotificationDialog, rows, float, 15.5); ATTRIB(XonoticHUDNotificationDialog, columns, float, 4); ATTRIB(XonoticHUDNotificationDialog, name, string, "HUDnotify"); ATTRIB(XonoticHUDNotificationDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qc b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qc index eabc93dab5..3a1e0bee18 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qc @@ -12,13 +12,13 @@ void XonoticHUDPhysicsDialog_fill(entity me) me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextSlider("hud_panel_physics")); - e.addValue(e, _("Panel disabled"), "0"); - e.addValue(e, _("Panel enabled"), "1"); - e.addValue(e, _("Panel enabled even observing"), "2"); - e.addValue(e, _("Panel enabled only in Race/CTS"), "3"); + e.addValue(e, _("Disable"), "0"); + e.addValue(e, _("Enable"), "1"); + e.addValue(e, _("Enable even observing"), "2"); + e.addValue(e, _("Enable only in Race/CTS"), "3"); e.configureXonoticTextSliderValues(e); - DIALOG_HUDPANEL_COMMON_NOTOGGLE(); + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 1.4, e = makeXonoticCheckBox(0, "hud_panel_physics_progressbar", _("Status bar"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh index f6f19135d1..2a52bef91b 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDPhysicsDialog, XonoticRootDialog) ATTRIB(XonoticHUDPhysicsDialog, title, string, _("Physics Panel")); ATTRIB(XonoticHUDPhysicsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDPhysicsDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDPhysicsDialog, rows, float, 15); + ATTRIB(XonoticHUDPhysicsDialog, rows, float, 15.5); ATTRIB(XonoticHUDPhysicsDialog, columns, float, 4); ATTRIB(XonoticHUDPhysicsDialog, name, string, "HUDphysics"); ATTRIB(XonoticHUDPhysicsDialog, sliderTopspeedTime, entity); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc index 1b490d1027..7e0ffae1a4 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qc @@ -9,7 +9,9 @@ void XonoticHUDPowerupsDialog_fill(entity me) entity e; string panelname = "powerups"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_powerups_progressbar", _("Enable status bar"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh index 7f67fa6007..c82f982780 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDPowerupsDialog, XonoticRootDialog) ATTRIB(XonoticHUDPowerupsDialog, title, string, _("Powerups Panel")); ATTRIB(XonoticHUDPowerupsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDPowerupsDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDPowerupsDialog, rows, float, 14); + ATTRIB(XonoticHUDPowerupsDialog, rows, float, 15.5); ATTRIB(XonoticHUDPowerupsDialog, columns, float, 4); ATTRIB(XonoticHUDPowerupsDialog, name, string, "HUDpowerups"); ATTRIB(XonoticHUDPowerupsDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc index 4e24ff998e..30d9572109 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qc @@ -11,12 +11,12 @@ void XonoticHUDPressedKeysDialog_fill(entity me) me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextSlider("hud_panel_pressedkeys")); - e.addValue(e, _("Panel disabled"), "0"); - e.addValue(e, _("Panel enabled when spectating"), "1"); - e.addValue(e, _("Panel always enabled"), "2"); + e.addValue(e, ("Disable"), "0"); + e.addValue(e, ("Enable when spectating"), "1"); + e.addValue(e, ("Always enable"), "2"); e.configureXonoticTextSliderValues(e); - DIALOG_HUDPANEL_COMMON_NOTOGGLE(); + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TDempty(me, 0.2); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh index cc82959e64..46452c696d 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDPressedKeysDialog, XonoticRootDialog) ATTRIB(XonoticHUDPressedKeysDialog, title, string, _("Pressed Keys Panel")); ATTRIB(XonoticHUDPressedKeysDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDPressedKeysDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDPressedKeysDialog, rows, float, 15); + ATTRIB(XonoticHUDPressedKeysDialog, rows, float, 15.5); ATTRIB(XonoticHUDPressedKeysDialog, columns, float, 4); ATTRIB(XonoticHUDPressedKeysDialog, name, string, "HUDpressedkeys"); ATTRIB(XonoticHUDPressedKeysDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc index 4012bc61ac..128951c0c8 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qc @@ -9,7 +9,10 @@ void XonoticHUDQuickMenuDialog_fill(entity me) entity e; string panelname = "quickmenu"; - DIALOG_HUDPANEL_COMMON_NOTOGGLE(); + // this panel has no main cvar + //dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Text alignment:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh index 16f93c13d5..ac6693a648 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDQuickMenuDialog, XonoticRootDialog) ATTRIB(XonoticHUDQuickMenuDialog, title, string, _("Quick Menu Panel")); ATTRIB(XonoticHUDQuickMenuDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDQuickMenuDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDQuickMenuDialog, rows, float, 15); + ATTRIB(XonoticHUDQuickMenuDialog, rows, float, 15.5); ATTRIB(XonoticHUDQuickMenuDialog, columns, float, 4); ATTRIB(XonoticHUDQuickMenuDialog, name, string, "HUDquickmenu"); ENDCLASS(XonoticHUDQuickMenuDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qc b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qc index 2673e54603..dec6fe3644 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qc @@ -4,8 +4,10 @@ void XonoticHUDRaceTimerDialog_fill(entity me) { - entity e; + //entity e; string panelname = "racetimer"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); } diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh index 7c814e3823..81a8f00534 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDRaceTimerDialog, XonoticRootDialog) ATTRIB(XonoticHUDRaceTimerDialog, title, string, _("Race Timer Panel")); ATTRIB(XonoticHUDRaceTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDRaceTimerDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDRaceTimerDialog, rows, float, 15); + ATTRIB(XonoticHUDRaceTimerDialog, rows, float, 15.5); ATTRIB(XonoticHUDRaceTimerDialog, columns, float, 4); ATTRIB(XonoticHUDRaceTimerDialog, name, string, "HUDracetimer"); ATTRIB(XonoticHUDRaceTimerDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qc b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qc index de9ed6898b..3e8db6686c 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qc @@ -12,12 +12,12 @@ void XonoticHUDRadarDialog_fill(entity me) me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextSlider("hud_panel_radar")); - e.addValue(e, _("Panel disabled"), "0"); - e.addValue(e, _("Panel enabled in teamgames"), "1"); - e.addValue(e, _("Panel always enabled"), "2"); + e.addValue(e, _("Disable"), "0"); + e.addValue(e, _("Enable in team games"), "1"); + e.addValue(e, _("Always enable"), "2"); e.configureXonoticTextSliderValues(e); - DIALOG_HUDPANEL_COMMON_NOTOGGLE(); + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Radar:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh index 04617b677f..d02f5cd592 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDRadarDialog, XonoticRootDialog) ATTRIB(XonoticHUDRadarDialog, title, string, _("Radar Panel")); ATTRIB(XonoticHUDRadarDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDRadarDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDRadarDialog, rows, float, 15); + ATTRIB(XonoticHUDRadarDialog, rows, float, 15.5); ATTRIB(XonoticHUDRadarDialog, columns, float, 4); ATTRIB(XonoticHUDRadarDialog, name, string, "HUDradar"); ATTRIB(XonoticHUDRadarDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_score.qc b/qcsrc/menu/xonotic/dialog_hudpanel_score.qc index f04ca30da9..afb97e5fd4 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_score.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_score.qc @@ -9,7 +9,9 @@ void XonoticHUDScoreDialog_fill(entity me) entity e; string panelname = "score"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Score:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_score.qh b/qcsrc/menu/xonotic/dialog_hudpanel_score.qh index d97787404e..20fa6cf967 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_score.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_score.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDScoreDialog, XonoticRootDialog) ATTRIB(XonoticHUDScoreDialog, title, string, _("Score Panel")); ATTRIB(XonoticHUDScoreDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDScoreDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDScoreDialog, rows, float, 15); + ATTRIB(XonoticHUDScoreDialog, rows, float, 15.5); ATTRIB(XonoticHUDScoreDialog, columns, float, 4); ATTRIB(XonoticHUDScoreDialog, name, string, "HUDscore"); ATTRIB(XonoticHUDScoreDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qc b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qc index dd9a7b8788..84c6d862db 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qc @@ -8,7 +8,9 @@ void XonoticHUDTimerDialog_fill(entity me) entity e; string panelname = "timer"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 4, e = makeXonoticTextLabel(0, _("Timer:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh index 61955495ed..dac91cc2a6 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDTimerDialog, XonoticRootDialog) ATTRIB(XonoticHUDTimerDialog, title, string, _("Timer Panel")); ATTRIB(XonoticHUDTimerDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDTimerDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDTimerDialog, rows, float, 15); + ATTRIB(XonoticHUDTimerDialog, rows, float, 15.5); ATTRIB(XonoticHUDTimerDialog, columns, float, 4); ATTRIB(XonoticHUDTimerDialog, name, string, "HUDtimer"); ATTRIB(XonoticHUDTimerDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qc b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qc index e8afa7ca70..39c9a69f70 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qc @@ -9,7 +9,9 @@ void XonoticHUDVoteDialog_fill(entity me) entity e; string panelname = "vote"; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TD(me, 1, 1.4, e = makeXonoticTextLabel(0, _("Alpha after voting:"))); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh index 061c69e56c..b07b189e5b 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDVoteDialog, XonoticRootDialog) ATTRIB(XonoticHUDVoteDialog, title, string, _("Vote Panel")); ATTRIB(XonoticHUDVoteDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDVoteDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDVoteDialog, rows, float, 15); + ATTRIB(XonoticHUDVoteDialog, rows, float, 15.5); ATTRIB(XonoticHUDVoteDialog, columns, float, 4); ATTRIB(XonoticHUDVoteDialog, name, string, "HUDvote"); ATTRIB(XonoticHUDVoteDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc index 61339b6b93..116b8ae2f0 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qc @@ -13,7 +13,9 @@ void XonoticHUDWeaponsDialog_fill(entity me) string panelname = "weapons"; float i; - DIALOG_HUDPANEL_COMMON(); + dialog_hudpanel_main_checkbox(me, panelname); + + dialog_hudpanel_main_settings(me, panelname); me.TR(me); me.TDempty(me, 0.2); diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh index fad14749e6..4f877d4d3f 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh @@ -6,7 +6,7 @@ CLASS(XonoticHUDWeaponsDialog, XonoticRootDialog) ATTRIB(XonoticHUDWeaponsDialog, title, string, _("Weapons Panel")); ATTRIB(XonoticHUDWeaponsDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); ATTRIB(XonoticHUDWeaponsDialog, intendedWidth, float, 0.4); - ATTRIB(XonoticHUDWeaponsDialog, rows, float, 21); + ATTRIB(XonoticHUDWeaponsDialog, rows, float, 21.5); ATTRIB(XonoticHUDWeaponsDialog, columns, float, 4); ATTRIB(XonoticHUDWeaponsDialog, name, string, "HUDweapons"); ATTRIB(XonoticHUDWeaponsDialog, requiresConnection, float, true); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc index 481914200a..ba09c311e4 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc @@ -27,16 +27,8 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax, // clear old values for(i = 0; i < e.nValues; ++i); { - if(e.(valueStrings[i])) - { - strunzone(e.(valueStrings[i])); - e.(valueStrings[i]) = string_null; - } - if(e.(valueIdentifiers[i])) - { - strunzone(e.(valueIdentifiers[i])); - e.(valueIdentifiers[i]) = string_null; - } + strfree(e.(valueStrings[i])); + strfree(e.(valueIdentifiers[i])); } e.clearValues(e); @@ -60,23 +52,8 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax, void GameType_ConfigureSliders_for_CurrentGametype(entity me) { - switch(MapInfo_CurrentGametype()) - { - case MAPINFO_TYPE_CA: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_ca_teams_override", _("The amount of frags needed before the match will end")); break; - case MAPINFO_TYPE_FREEZETAG: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", "g_freezetag_teams_override", _("The amount of frags needed before the match will end")); break; - case MAPINFO_TYPE_CTF: GameType_ConfigureSliders(me, _("Capture limit:"), 1, 20, 1, "capturelimit_override", string_null, _("The amount of captures needed before the match will end")); break; - case MAPINFO_TYPE_DOMINATION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, "g_domination_point_limit", "g_domination_teams_override", _("The amount of points needed before the match will end")); break; - case MAPINFO_TYPE_KEYHUNT: GameType_ConfigureSliders(me, _("Point limit:"), 200, 1500, 50, "g_keyhunt_point_limit", "g_keyhunt_teams_override", _("The amount of points needed before the match will end")); break; - case MAPINFO_TYPE_LMS: GameType_ConfigureSliders(me, _("Lives:"), 3, 50, 1, "g_lms_lives_override", string_null, string_null); break; - case MAPINFO_TYPE_RACE: GameType_ConfigureSliders(me, _("Laps:"), 1, 25, 1, "g_race_laps_limit", string_null, string_null); break; - case MAPINFO_TYPE_NEXBALL: GameType_ConfigureSliders(me, _("Goals:"), 1, 50, 1, "g_nexball_goallimit", string_null, _("The amount of goals needed before the match will end")); break; - case MAPINFO_TYPE_ASSAULT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break; - case MAPINFO_TYPE_ONSLAUGHT: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break; - case MAPINFO_TYPE_CTS: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break; - case MAPINFO_TYPE_INVASION: GameType_ConfigureSliders(me, _("Point limit:"), 50, 500, 10, string_null, string_null, string_null); break; - case MAPINFO_TYPE_TEAM_DEATHMATCH: GameType_ConfigureSliders(me, _("Point limit:"), 5, 100, 5, "g_tdm_point_limit", "g_tdm_teams_override", _("The amount of points needed before the match will end")); break; - default: GameType_ConfigureSliders(me, _("Frag limit:"), 5, 100, 5, "fraglimit_override", string_null, _("The amount of frags needed before the match will end")); break; - } + Gametype gt = MapInfo_CurrentGametype(); + gt.m_configuremenu(gt, me, GameType_ConfigureSliders); } entity makeXonoticServerCreateTab() diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc index 2f2ab901a8..87ffadf383 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc @@ -12,19 +12,11 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, int i, entity mlb) me.startButton.onClickEntity = mlb; MapInfo_Get_ByID(i); - if(me.currentMapBSPName) - { - strunzone(me.currentMapBSPName); - strunzone(me.currentMapTitle); - strunzone(me.currentMapAuthor); - strunzone(me.currentMapDescription); - strunzone(me.currentMapPreviewImage); - } - me.currentMapBSPName = strzone(MapInfo_Map_bspname); - me.currentMapTitle = strzone(strdecolorize(MapInfo_Map_title)); - me.currentMapAuthor = strzone(strdecolorize(MapInfo_Map_author)); - me.currentMapDescription = strzone(MapInfo_Map_description); - me.currentMapPreviewImage = strzone(strcat("/maps/", MapInfo_Map_bspname)); + strcpy(me.currentMapBSPName, MapInfo_Map_bspname); + strcpy(me.currentMapTitle, strdecolorize(MapInfo_Map_title)); + strcpy(me.currentMapAuthor, strdecolorize(MapInfo_Map_author)); + strcpy(me.currentMapDescription, MapInfo_Map_description); + strcpy(me.currentMapPreviewImage, strcat("/maps/", MapInfo_Map_bspname)); me.frame.setText(me.frame, me.currentMapBSPName); me.titleLabel.setText(me.titleLabel, me.currentMapTitle); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index 0ea24a8891..8b6dd4b731 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -20,7 +20,7 @@ string weaponarenastring_cvar; string WeaponArenaString() { string s; - float n, i; + float n; s = cvar_string("g_weaponarena"); if(s == "0") return ""; @@ -30,81 +30,81 @@ string WeaponArenaString() return _("Most Weapons Arena"); if(s == weaponarenastring_cvar) return weaponarenastring; - if(weaponarenastring) - strunzone(weaponarenastring); - if(weaponarenastring_cvar) - strunzone(weaponarenastring_cvar); - weaponarenastring_cvar = strzone(s); + strcpy(weaponarenastring_cvar, s); n = tokenize_console(s); s = ""; - for(i = 0; i < n; ++i) + for(int j = 0; j < n; ++j) { - FOREACH(Weapons, it != WEP_Null, { - if(argv(i) == it.netname) - s = strcat(s, " & ", it.m_name); - }); + Weapon wep = Weapons_fromstr(argv(j)); + if(wep != WEP_Null) + { + s = cons_mid(s, " & ", wep.m_name); + } } - s = sprintf(_("%s Arena"), substring(s, 3, strlen(s) - 3)); + s = sprintf(_("%s Arena"), s); - weaponarenastring = strzone(s); + strcpy(weaponarenastring, s); return weaponarenastring; } string XonoticMutatorsDialog_toString(entity me) { - string s; - s = ""; + string s = ""; if(cvar("g_dodging")) - s = strcat(s, ", ", _("Dodging")); + s = cons_mid(s, ", ", _("Dodging")); if(cvar("g_instagib")) - s = strcat(s, ", ", _("InstaGib")); + s = cons_mid(s, ", ", _("InstaGib")); if(cvar("g_new_toys")) - s = strcat(s, ", ", _("New Toys")); + s = cons_mid(s, ", ", _("New Toys")); if(cvar("g_nix")) - s = strcat(s, ", ", _("NIX")); + s = cons_mid(s, ", ", _("NIX")); if(cvar("g_rocket_flying")) - s = strcat(s, ", ", _("Rocket Flying")); + s = cons_mid(s, ", ", _("Rocket Flying")); if(cvar("g_invincible_projectiles")) - s = strcat(s, ", ", _("Invincible Projectiles")); + s = cons_mid(s, ", ", _("Invincible Projectiles")); if(cvar_string("g_weaponarena") != "0") - s = strcat(s, ", ", WeaponArenaString()); + s = cons_mid(s, ", ", WeaponArenaString()); else if(cvar("g_balance_blaster_weaponstartoverride") == 0) - s = strcat(s, ", ", _("No start weapons")); + s = cons_mid(s, ", ", _("No start weapons")); if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) - s = strcat(s, ", ", _("Low gravity")); + s = cons_mid(s, ", ", _("Low gravity")); if(cvar("g_cloaked")) - s = strcat(s, ", ", _("Cloaked")); + s = cons_mid(s, ", ", _("Cloaked")); if(cvar("g_grappling_hook")) - s = strcat(s, ", ", _("Hook")); + s = cons_mid(s, ", ", _("Hook")); if(cvar("g_midair")) - s = strcat(s, ", ", _("Midair")); + s = cons_mid(s, ", ", _("Midair")); + if(cvar("g_melee_only")) + s = cons_mid(s, ", ", _("Melee only")); if(cvar("g_vampire")) - s = strcat(s, ", ", _("Vampire")); + s = cons_mid(s, ", ", _("Vampire")); if(cvar("g_pinata")) - s = strcat(s, ", ", _("Piñata")); + s = cons_mid(s, ", ", _("Piñata")); if(cvar("g_weapon_stay")) - s = strcat(s, ", ", _("Weapons stay")); + s = cons_mid(s, ", ", _("Weapons stay")); if(cvar("g_bloodloss") > 0) - s = strcat(s, ", ", _("Blood loss")); + s = cons_mid(s, ", ", _("Blood loss")); if(cvar("g_jetpack")) - s = strcat(s, ", ", _("Jet pack")); + s = cons_mid(s, ", ", _("Jetpack")); if(cvar("g_buffs") > 0) - s = strcat(s, ", ", _("Buffs")); + s = cons_mid(s, ", ", _("Buffs")); if(cvar("g_overkill")) - s = strcat(s, ", ", _("Overkill")); + s = cons_mid(s, ", ", _("Overkill")); if(cvar("g_powerups") == 0) - s = strcat(s, ", ", _("No powerups")); + s = cons_mid(s, ", ", _("No powerups")); if(cvar("g_powerups") > 0) - s = strcat(s, ", ", _("Powerups")); + s = cons_mid(s, ", ", _("Powerups")); if(cvar("g_touchexplode") > 0) - s = strcat(s, ", ", _("Touch explode")); + s = cons_mid(s, ", ", _("Touch explode")); + if(cvar("g_walljump")) + s = cons_mid(s, ", ", _("Wall jumping")); if(s == "") return ZCTX(_("MUT^None")); else - return substring(s, 2, strlen(s) - 2); + return s; } float checkCompatibility_pinata(entity me) @@ -113,6 +113,10 @@ float checkCompatibility_pinata(entity me) return 0; if(cvar("g_nix")) return 0; + if(cvar("g_overkill")) + return 0; + if(cvar("g_melee_only")) + return 0; if(cvar_string("g_weaponarena") != "0") return 0; return 1; @@ -168,10 +172,11 @@ void XonoticMutatorsDialog_fill(entity me) me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticCheckBox(0, "g_buffs", _("Buffs"))); + e.cvarOffValue = "-1"; // TODO: make this a radio button? me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_midair", _("Midair"), - _("Only possible to inflict damage on your enemy while he's airborne"))); + _("Only possible to inflict damage on your enemy while they're airborne"))); me.TR(me); me.TDempty(me, 0.2); me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_vampire", _("Vampire"), @@ -205,7 +210,7 @@ void XonoticMutatorsDialog_fill(entity me) _("Players spawn with the grappling hook"))); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_jetpack", _("Jet pack"), + me.TD(me, 1, 1.8, e = makeXonoticCheckBox_T(0, "g_jetpack", _("Jetpack"), _("Players spawn with the jetpack"))); me.TR(me); me.TDempty(me, 0.2); @@ -233,32 +238,38 @@ void XonoticMutatorsDialog_fill(entity me) me.gotoRC(me, 0, 2); me.setFirstColumn(me, me.currentColumn); me.TD(me, 1, 2, e = makeXonoticRadioButton(1, string_null, string_null, _("Regular (no arena)"))); + string weaponarena_tooltip = strzone(_("Players will be given a set of weapons at spawn as well as unlimited ammo, without weapon pickups")); me.TR(me); - me.TD(me, 1, 2, e = makeXonoticRadioButton_T(1, "g_weaponarena", "menu_weaponarena", _("Weapon arenas:"), - _("Selecting a weapon arena will give all players that weapon at spawn as well as unlimited ammo, and disable all other weapon pickups."))); + me.TD(me, 1, 2, makeXonoticTextLabel(0, _("Weapon arenas:"))); + me.TR(me); + me.TDempty(me, 0.2); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_weaponarena", "menu_weaponarena", _("Custom weapons"), weaponarena_tooltip)); e.cvarValueIsAnotherCvar = true; e.cvarOffValue = "0"; + + me.TDempty(me, 0.1); // fix initial position for(i = WEP_FIRST, j = 0; i <= WEP_LAST; ++i) { w = Weapons_from(i); if(w.spawnflags & WEP_FLAG_HIDDEN) continue; - if((j & 1) == 0) + if ((j % 3) == 0) + { me.TR(me); - me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); + me.TDempty(me, 0.4); + } + me.TD(me, 1, 1.2, e = makeXonoticWeaponarenaCheckBox(strzone(w.netname), strzone(w.m_name))); setDependentWeird(e, checkCompatibility_weaponarena_weapon); ++j; } + me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_weaponarena", "most", _("Most weapons"), - _("Selecting a weapon arena will give all players that weapon at spawn as well as unlimited ammo, and disable all other weapon pickups."))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_weaponarena", "most", _("Most weapons"), weaponarena_tooltip)); e.cvarOffValue = "0"; me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_weaponarena", "all", _("All weapons"), - _("Selecting a weapon arena will give all players that weapon at spawn as well as unlimited ammo, and disable all other weapon pickups."))); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_weaponarena", "all", _("All weapons"), weaponarena_tooltip)); e.cvarOffValue = "0"; me.TR(me); me.TD(me, 1, 4, makeXonoticTextLabel(0, _("Special arenas:"))); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join.qc index a34d0d80a1..1792ec635b 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join.qc @@ -5,6 +5,7 @@ #include "textlabel.qh" #include "inputbox.qh" #include "checkbox.qh" +#include "commandbutton.qh" #include "button.qh" entity makeXonoticServerListTab() @@ -81,7 +82,10 @@ void XonoticServerListTab_fill(entity me) e.onClickEntity = slist; slist.infoButton = e; me.TR(me); - me.TD(me, 1, me.columns, e = makeXonoticButton(_("Join!"), '0 0 0')); + me.TD(me, 1, 1, e = makeXonoticCommandButton_T(_("Disconnect"), '0 0 0', "disconnect", 0, + _("Disconnect from the server"))); + slist.disconnectButton = e; + me.TD(me, 1, me.columns-1, e = makeXonoticButton(_("Join!"), '0 0 0')); e.onClick = ServerList_Connect_Click; e.onClickEntity = slist; slist.connectButton = e; diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc index baeb9a51bc..5745ce072e 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc @@ -16,61 +16,21 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) // ==================================== // First clear and unzone the strings // ==================================== - if(me.currentServerName) - strunzone(me.currentServerName); - me.currentServerName = string_null; - - if(me.currentServerCName) - strunzone(me.currentServerCName); - me.currentServerCName = string_null; - - if(me.currentServerType) - strunzone(me.currentServerType); - me.currentServerType = string_null; - - if(me.currentServerMap) - strunzone(me.currentServerMap); - me.currentServerMap = string_null; - - if(me.currentServerPlayers) - strunzone(me.currentServerPlayers); - me.currentServerPlayers = string_null; - - if(me.currentServerNumPlayers) - strunzone(me.currentServerNumPlayers); - me.currentServerNumPlayers = string_null; - - if(me.currentServerNumBots) - strunzone(me.currentServerNumBots); - me.currentServerNumBots = string_null; - - if(me.currentServerNumFreeSlots) - strunzone(me.currentServerNumFreeSlots); - me.currentServerNumFreeSlots = string_null; - - if(me.currentServerMod) - strunzone(me.currentServerMod); - me.currentServerMod = string_null; - - if(me.currentServerVersion) - strunzone(me.currentServerVersion); - me.currentServerVersion = string_null; - + strfree(me.currentServerName); + strfree(me.currentServerCName); + strfree(me.currentServerType); + strfree(me.currentServerMap); + strfree(me.currentServerPlayers); + strfree(me.currentServerNumPlayers); + strfree(me.currentServerNumBots); + strfree(me.currentServerNumFreeSlots); + strfree(me.currentServerMod); + strfree(me.currentServerVersion); // not zoned! - //if(me.currentServerEncrypt) - // strunzone(me.currentServerEncrypt); - //me.currentServerEncrypt = string_null; - if(me.currentServerPure) - strunzone(me.currentServerPure); - me.currentServerPure = string_null; - - if(me.currentServerKey) - strunzone(me.currentServerKey); - me.currentServerKey = string_null; - - if(me.currentServerID) - strunzone(me.currentServerID); - me.currentServerID = string_null; + // strfree(me.currentServerEncrypt); + strfree(me.currentServerPure); + strfree(me.currentServerKey); + strfree(me.currentServerID); // ========================== // Now, fill in the strings diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc index 1229be7144..afde0914a4 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qc @@ -19,9 +19,7 @@ void XonoticScreenshotBrowserTab_loadPreviewScreenshot(entity me, string scrImag { if (me.currentScrPath == scrImage) return; - if (me.currentScrPath) - strunzone(me.currentScrPath); - me.currentScrPath = strzone(scrImage); + strcpy(me.currentScrPath, scrImage); me.screenshotImage.load(me.screenshotImage, me.currentScrPath); } void XonoticScreenshotBrowserTab_fill(entity me) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc index d6d545eeea..57dd75679e 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qc @@ -24,9 +24,7 @@ void XonoticScreenshotViewerDialog_loadScreenshot(entity me, string scrImage) if (me.currentScrPath == scrImage) return; - if (me.currentScrPath) - strunzone(me.currentScrPath); - me.currentScrPath = strzone(scrImage); + strcpy(me.currentScrPath, scrImage); me.screenshotImage.load(me.screenshotImage, me.currentScrPath); me.frame.setText(me.frame, me.screenshotImage.screenshotTitle); } diff --git a/qcsrc/menu/xonotic/dialog_settings_game_messages.qc b/qcsrc/menu/xonotic/dialog_settings_game_messages.qc index 31cc98215d..069b22b02d 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_messages.qc +++ b/qcsrc/menu/xonotic/dialog_settings_game_messages.qc @@ -80,7 +80,7 @@ void XonoticGameMessageSettingsTab_fill(entity me) me.TD(me, 1, 3, e = makeXonoticCheckBoxEx_T(2, 1, "notification_allow_chatboxprint", _("Display all info messages in the chatbox"), "-")); me.TR(me); me.TD(me, 1, 3, e = makeXonoticCheckBoxEx_T(2, 1, "notification_INFO_QUIT_DISCONNECT", _("Display player statuses in the chatbox"), "-")); - makeMulti(e, "notification_INFO_QUIT_KICK_IDLING notification_INFO_JOIN_CONNECT_TEAM"); + makeMulti(e, "notification_INFO_QUIT_KICK_IDLING notification_INFO_JOIN_CONNECT"); me.TR(me); me.TR(me); me.TD(me, 1, 3, e = makeXonoticCheckBox_T(0, "notification_CENTER_POWERUP_INVISIBILITY", _("Powerup notifications"), "-")); diff --git a/qcsrc/menu/xonotic/dialog_settings_misc.qc b/qcsrc/menu/xonotic/dialog_settings_misc.qc index 594b581fe4..1c7da72919 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc.qc +++ b/qcsrc/menu/xonotic/dialog_settings_misc.qc @@ -42,7 +42,7 @@ void XonoticMiscSettingsTab_fill(entity me) e.configureXonoticTextSliderValues(e); me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Input packets/s:"))); - me.TD(me, 1, 2, e = makeXonoticSlider_T(20, 100, 5, "cl_netfps", + me.TD(me, 1, 2, e = makeXonoticSlider_T(30, 180, 5, "cl_netfps", _("How many input packets to send to the server each second"))); me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Server queries/s:"))); diff --git a/qcsrc/menu/xonotic/gametypelist.qc b/qcsrc/menu/xonotic/gametypelist.qc index a08e4dbbc5..3703393789 100644 --- a/qcsrc/menu/xonotic/gametypelist.qc +++ b/qcsrc/menu/xonotic/gametypelist.qc @@ -101,8 +101,10 @@ void XonoticGametypeList_resizeNotify(entity me, vector relOrigin, vector relSiz me.itemAbsSize = '0 0 0'; SUPER(XonoticGametypeList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnIconOrigin = 0; me.columnIconSize = me.itemAbsSize.y / me.itemAbsSize.x; diff --git a/qcsrc/menu/xonotic/hudskinlist.qc b/qcsrc/menu/xonotic/hudskinlist.qc index 66c96046d8..92dee8d85e 100644 --- a/qcsrc/menu/xonotic/hudskinlist.qc +++ b/qcsrc/menu/xonotic/hudskinlist.qc @@ -146,8 +146,10 @@ void XonoticHUDSkinList_resizeNotify(entity me, vector relOrigin, vector relSize me.itemAbsSize = '0 0 0'; SUPER(XonoticHUDSkinList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnNameOrigin = me.realFontSize.x; @@ -188,19 +190,15 @@ void HUDSkinList_Refresh_Click(entity btn, entity me) void HUDSkinList_SavedName_Change(entity box, entity me) { - if(me.savedName) - strunzone(me.savedName); + strfree(me.savedName); if(box.text != "") me.savedName = strzone(box.text); - else - me.savedName = string_null; } void HUDSkinList_Filter_Change(entity box, entity me) { - if(me.filterString) - strunzone(me.filterString); + strfree(me.filterString); if(box.text != "") { @@ -209,8 +207,6 @@ void HUDSkinList_Filter_Change(entity box, entity me) else me.filterString = strzone(strcat("*", box.text, "*")); } - else - me.filterString = string_null; me.getHUDSkins(me); } diff --git a/qcsrc/menu/xonotic/keybinder.qc b/qcsrc/menu/xonotic/keybinder.qc index 1f28a1bdfe..f4417171ff 100644 --- a/qcsrc/menu/xonotic/keybinder.qc +++ b/qcsrc/menu/xonotic/keybinder.qc @@ -34,7 +34,7 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("+jump" , _("jump / swim")); KEYBIND_DEF("+crouch" , _("crouch / sink")); KEYBIND_DEF("+hook" , _("off-hand hook")); - KEYBIND_DEF("+jetpack" , _("jet pack")); + KEYBIND_DEF("+jetpack" , _("jetpack")); KEYBIND_DEF("" , ""); KEYBIND_DEF("" , _("Attacking")); KEYBIND_DEF("+fire" , _("primary fire")); @@ -103,6 +103,7 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("+use" , _("drop key / drop flag")); KEYBIND_DEF("" , ""); KEYBIND_DEF("" , _("Misc")); + KEYBIND_DEF("kill" , _("respawn")); KEYBIND_DEF("quickmenu" , _("quick menu")); KEYBIND_DEF("menu_showsandboxtools" , _("sandbox menu")); KEYBIND_DEF("+button8" , _("drag object")); @@ -246,12 +247,8 @@ void XonoticKeyBinder_destroy(entity me) for(int i = 0; i < MAX_KEYBINDS; ++i) { - if(Xonotic_KeyBinds_Functions[i]) - strunzone(Xonotic_KeyBinds_Functions[i]); - Xonotic_KeyBinds_Functions[i] = string_null; - if(Xonotic_KeyBinds_Descriptions[i]) - strunzone(Xonotic_KeyBinds_Descriptions[i]); - Xonotic_KeyBinds_Descriptions[i] = string_null; + strfree(Xonotic_KeyBinds_Functions[i]); + strfree(Xonotic_KeyBinds_Descriptions[i]); } Xonotic_KeyBinds_Count = 0; } diff --git a/qcsrc/menu/xonotic/mainwindow.qc b/qcsrc/menu/xonotic/mainwindow.qc index 0afd27c8d6..110b796be6 100644 --- a/qcsrc/menu/xonotic/mainwindow.qc +++ b/qcsrc/menu/xonotic/mainwindow.qc @@ -1,6 +1,6 @@ #include "mainwindow.qh" -#include "../mutators/events.qh" +#include <menu/mutators/_mod.qh> #include "nexposee.qh" #include "inputbox.qh" diff --git a/qcsrc/menu/xonotic/maplist.qc b/qcsrc/menu/xonotic/maplist.qc index 111744e966..5184ee1fd5 100644 --- a/qcsrc/menu/xonotic/maplist.qc +++ b/qcsrc/menu/xonotic/maplist.qc @@ -88,8 +88,10 @@ void XonoticMapList_resizeNotify(entity me, vector relOrigin, vector relSize, ve me.itemAbsSize = '0 0 0'; SUPER(XonoticMapList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin1 = 0.5 * (1 - 2.5 * me.realFontSize.y); me.realUpperMargin2 = me.realUpperMargin1 + 1.5 * me.realFontSize.y; @@ -181,8 +183,6 @@ void XonoticMapList_refilter(entity me) for(i = 0; i < MapInfo_count; ++i) draw_PreloadPicture(strcat("/maps/", MapInfo_BSPName_ByID(i))); - if(me.g_maplistCache) - strunzone(me.g_maplistCache); s = "0"; for(i = 1; i < MapInfo_count; i *= 2) s = strcat(s, s); @@ -201,7 +201,7 @@ void XonoticMapList_refilter(entity me) ); } } - me.g_maplistCache = strzone(s); + strcpy(me.g_maplistCache, s); if(gt != me.lastGametype || f != me.lastFeatures) { me.lastGametype = gt; @@ -217,12 +217,9 @@ void XonoticMapList_refilterCallback(entity me, entity cb) void MapList_StringFilterBox_Change(entity box, entity me) { - if(me.stringFilter) - strunzone(me.stringFilter); + strfree(me.stringFilter); if(box.text != "") me.stringFilter = strzone(box.text); - else - me.stringFilter = string_null; me.refilter(me); } @@ -345,9 +342,7 @@ float XonoticMapList_keyDown(entity me, float scan, float ascii, float shift) if(time < me.typeToSearchTime) { save = substring(me.typeToSearchString, 0, strlen(me.typeToSearchString) - 1); - if(me.typeToSearchString) - strunzone(me.typeToSearchString); - me.typeToSearchString = strzone(save); + strcpy(me.typeToSearchString, save); me.typeToSearchTime = time + 0.5; if(strlen(me.typeToSearchString)) { @@ -364,9 +359,7 @@ float XonoticMapList_keyDown(entity me, float scan, float ascii, float shift) save = ch; else save = strcat(me.typeToSearchString, ch); - if(me.typeToSearchString) - strunzone(me.typeToSearchString); - me.typeToSearchString = strzone(save); + strcpy(me.typeToSearchString, save); me.typeToSearchTime = time + 0.5; MapInfo_FindName(me.typeToSearchString); if(MapInfo_FindName_firstResult >= 0) diff --git a/qcsrc/menu/xonotic/picker.qh b/qcsrc/menu/xonotic/picker.qh index c98e9fc9b6..bbff7d551b 100644 --- a/qcsrc/menu/xonotic/picker.qh +++ b/qcsrc/menu/xonotic/picker.qh @@ -1,7 +1,7 @@ #pragma once #include "../item.qh" -CLASS(XonoticPicker, Item) +CLASS(XonoticPicker, MenuItem) METHOD(XonoticPicker, configureXonoticPicker, void(entity)); METHOD(XonoticPicker, mousePress, bool(XonoticPicker this, vector pos)); METHOD(XonoticPicker, mouseRelease, float(entity, vector)); diff --git a/qcsrc/menu/xonotic/playerlist.qc b/qcsrc/menu/xonotic/playerlist.qc index c6033050ae..e90eef23d3 100644 --- a/qcsrc/menu/xonotic/playerlist.qc +++ b/qcsrc/menu/xonotic/playerlist.qc @@ -61,8 +61,10 @@ void XonoticPlayerList_resizeNotify(entity me, vector relOrigin, vector relSize, me.itemAbsSize = '0 0 0'; SUPER(XonoticPlayerList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); // this list does 1 char left and right margin diff --git a/qcsrc/menu/xonotic/playermodel.qc b/qcsrc/menu/xonotic/playermodel.qc index c679d4449d..1ad651bdb5 100644 --- a/qcsrc/menu/xonotic/playermodel.qc +++ b/qcsrc/menu/xonotic/playermodel.qc @@ -113,21 +113,12 @@ void XonoticPlayerModelSelector_go(entity me, float d) { me.idxModels = mod(me.idxModels + d + me.numModels, me.numModels); - if(me.currentModel) - strunzone(me.currentModel); - if(me.currentModelTitle) - strunzone(me.currentModelTitle); - if(me.currentModelImage) - strunzone(me.currentModelImage); - if(me.currentModelDescription) - strunzone(me.currentModelDescription); - // select model #i! - me.currentModelTitle = strzone(bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_TITLE)); - me.currentModelImage = strzone(bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_IMAGE)); + strcpy(me.currentModelTitle, bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_TITLE)); + strcpy(me.currentModelImage, bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_IMAGE)); me.currentSkin = stof(bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_SKIN)); - me.currentModel = strzone(bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_MODEL)); - me.currentModelDescription = strzone(bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_DESC)); + strcpy(me.currentModel, bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_MODEL)); + strcpy(me.currentModelDescription, bufstr_get(me.bufModels, BUFMODELS_COUNT*me.idxModels+BUFMODELS_DESC)); // fix the image if(draw_PictureSize(me.currentModelImage) == '0 0 0') diff --git a/qcsrc/menu/xonotic/playlist.qc b/qcsrc/menu/xonotic/playlist.qc index c912ba3a72..da5fd486ea 100644 --- a/qcsrc/menu/xonotic/playlist.qc +++ b/qcsrc/menu/xonotic/playlist.qc @@ -19,8 +19,10 @@ void XonoticPlayList_resizeNotify(entity me, vector relOrigin, vector relSize, v me.itemAbsSize = '0 0 0'; SUPER(XonoticPlayList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnNumberOrigin = 0; diff --git a/qcsrc/menu/xonotic/screenshotimage.qc b/qcsrc/menu/xonotic/screenshotimage.qc index 8e8f3ccf73..af987e7b58 100644 --- a/qcsrc/menu/xonotic/screenshotimage.qc +++ b/qcsrc/menu/xonotic/screenshotimage.qc @@ -19,9 +19,7 @@ void XonoticScreenshotImage_load(entity me, string theImage) { me.screenshotTime = time; me.src = theImage; - if (me.screenshotTitle) - strunzone(me.screenshotTitle); - me.screenshotTitle = strzone(substring(me.src, 13, strlen(theImage) - 13)); //strip "/screenshots/" + strcpy(me.screenshotTitle, substring(me.src, 13, strlen(theImage) - 13)); //strip "/screenshots/" me.initZoom(me); // this image may have a different size me.setZoom(me, 0, 0); diff --git a/qcsrc/menu/xonotic/screenshotlist.qc b/qcsrc/menu/xonotic/screenshotlist.qc index a948ce86f8..9f5ba787aa 100644 --- a/qcsrc/menu/xonotic/screenshotlist.qc +++ b/qcsrc/menu/xonotic/screenshotlist.qc @@ -97,8 +97,10 @@ void XonoticScreenshotList_resizeNotify(entity me, vector relOrigin, vector relS me.itemAbsSize = '0 0 0'; SUPER(XonoticScreenshotList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnNameOrigin = me.realFontSize.x; @@ -153,8 +155,7 @@ void ScreenshotList_Refresh_Click(entity btn, entity me) void ScreenshotList_Filter_Change(entity box, entity me) { - if(me.filterString) - strunzone(me.filterString); + strfree(me.filterString); if(box.text != "") { @@ -163,8 +164,6 @@ void ScreenshotList_Filter_Change(entity box, entity me) else me.filterString = strzone(strcat("*", box.text, "*")); } - else - me.filterString = string_null; ScreenshotList_Refresh_Click(NULL, me); } diff --git a/qcsrc/menu/xonotic/serverlist.qc b/qcsrc/menu/xonotic/serverlist.qc index 5e6a567b6a..eec56ca5fd 100644 --- a/qcsrc/menu/xonotic/serverlist.qc +++ b/qcsrc/menu/xonotic/serverlist.qc @@ -37,8 +37,7 @@ void RegisterSLCategories() } } \ if(catnum) \ { \ - strunzone(categories[i].override_string); \ - categories[i].override_string = string_null; \ + strfree(categories[i].override_string); \ categories[i].override_field = catnum; \ continue; \ } \ @@ -51,8 +50,7 @@ void RegisterSLCategories() ); \ } \ } \ - strunzone(categories[i].override_string); \ - categories[i].override_string = string_null; \ + strfree(categories[i].override_string); \ categories[i].override_field = 0; \ } PROCESS_OVERRIDE(cat_enoverride_string, cat_enoverride) @@ -308,9 +306,7 @@ void XonoticServerList_setSelected(entity me, int i) if(gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT) != me.nItems) return; // sorry, it would be wrong - if(me.selectedServer) - strunzone(me.selectedServer); - me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); + strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); me.ipAddressBox.setText(me.ipAddressBox, me.selectedServer); me.ipAddressBox.cursorPos = strlen(me.selectedServer); @@ -549,9 +545,10 @@ void XonoticServerList_draw(entity me) } else { me.nItems = gethostcachevalue(SLIST_HOSTCACHEVIEWCOUNT); } - me.connectButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == "")); - me.infoButton.disabled = ((me.nItems == 0) || !owned); - me.favoriteButton.disabled = ((me.nItems == 0) && (me.ipAddressBox.text == "")); + me.connectButton.disabled = (me.lockedSelectedItem || (me.nItems == 0 && me.ipAddressBox.text == "")); + me.disconnectButton.disabled = (!(gamestatus & (GAME_ISSERVER | GAME_CONNECTED))); + me.infoButton.disabled = (me.lockedSelectedItem || me.nItems == 0 || !owned); + me.favoriteButton.disabled = (me.lockedSelectedItem || (me.nItems == 0 && me.ipAddressBox.text == "")); if(me.lockedSelectedItem) { @@ -559,9 +556,7 @@ void XonoticServerList_draw(entity me) { if(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem) != me.selectedServer) { - if(me.selectedServer) - strunzone(me.selectedServer); - me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); + strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); } found = true; } @@ -586,9 +581,7 @@ void XonoticServerList_draw(entity me) // selected server disappeared, select the last server (scrolling to it) if(me.selectedItem >= me.nItems) SUPER(XonoticServerList).setSelected(me, me.nItems - 1); - if(me.selectedServer) - strunzone(me.selectedServer); - me.selectedServer = strzone(gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); + strcpy(me.selectedServer, gethostcachestring(SLIST_FIELD_CNAME, me.selectedItem)); } } @@ -666,12 +659,9 @@ void ServerList_TypeSort_Click(entity btn, entity me) } void ServerList_Filter_Change(entity box, entity me) { - if(me.filterString) - strunzone(me.filterString); + strfree(me.filterString); if(box.text != "") me.filterString = strzone(box.text); - else - me.filterString = string_null; me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); me.ipAddressBox.setText(me.ipAddressBox, ""); @@ -717,9 +707,7 @@ void XonoticServerList_setSortOrder(entity me, int fld, int direction) me.sortButton4.forcePressed = 0; me.sortButton5.forcePressed = (fld == SLIST_FIELD_NUMHUMANS); me.selectedItem = 0; - if(me.selectedServer) - strunzone(me.selectedServer); - me.selectedServer = string_null; + strfree(me.selectedServer); me.refreshServerList(me, REFRESHSERVERLIST_REFILTER); } void XonoticServerList_positionSortButton(entity me, entity btn, float theOrigin, float theSize, string theTitle, void(entity, entity) theFunc) @@ -775,11 +763,10 @@ void XonoticServerList_resizeNotify(entity me, vector relOrigin, vector relSize, } void ServerList_Connect_Click(entity btn, entity me) { - localcmd(sprintf("connect %s\n", - ((me.ipAddressBox.text != "") ? - me.ipAddressBox.text : me.selectedServer - ) - )); + if (me.lockedSelectedItem) + return; + string sv = (me.ipAddressBox.text != "") ? me.ipAddressBox.text : me.selectedServer; + localcmd(sprintf("connect %s\n", sv)); } void ServerList_Favorite_Click(entity btn, entity this) { @@ -857,7 +844,7 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is } } - if(isSelected) + if(isSelected && !me.lockedSelectedItem) draw_Fill('0 0 0', '1 1 0', SKINCOLOR_LISTBOX_SELECTED, SKINALPHA_LISTBOX_SELECTED); else if(isFocused) { diff --git a/qcsrc/menu/xonotic/serverlist.qh b/qcsrc/menu/xonotic/serverlist.qh index e45abfda33..74f3bd570d 100644 --- a/qcsrc/menu/xonotic/serverlist.qh +++ b/qcsrc/menu/xonotic/serverlist.qh @@ -30,7 +30,7 @@ CLASS(XonoticServerList, XonoticListBox) ATTRIB(XonoticServerList, columnTypeSize, float, 0); ATTRIB(XonoticServerList, columnPlayersOrigin, float, 0); ATTRIB(XonoticServerList, columnPlayersSize, float, 0); - ATTRIB(XonoticServerList, lockedSelectedItem, bool, true); // initially keep selected the first item of the list, avoiding an unwanted scrolling + ATTRIB(XonoticServerList, lockedSelectedItem, bool, true); // initially keep selected the first item of the list to avoid unwanted scrolling ATTRIB(XonoticServerList, selectedServer, string); // to restore selected server when needed METHOD(XonoticServerList, setSelected, void(entity, float)); @@ -52,6 +52,7 @@ CLASS(XonoticServerList, XonoticListBox) ATTRIB(XonoticServerList, sortButton4, entity); ATTRIB(XonoticServerList, sortButton5, entity); ATTRIB(XonoticServerList, connectButton, entity); + ATTRIB(XonoticServerList, disconnectButton, entity); ATTRIB(XonoticServerList, infoButton, entity); ATTRIB(XonoticServerList, currentSortOrder, float, 0); ATTRIB(XonoticServerList, currentSortField, float, -1); diff --git a/qcsrc/menu/xonotic/skinlist.qc b/qcsrc/menu/xonotic/skinlist.qc index e72ca12e24..4683c45207 100644 --- a/qcsrc/menu/xonotic/skinlist.qc +++ b/qcsrc/menu/xonotic/skinlist.qc @@ -111,8 +111,10 @@ void XonoticSkinList_resizeNotify(entity me, vector relOrigin, vector relSize, v me.itemAbsSize = '0 0 0'; SUPER(XonoticSkinList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin1 = 0.5 * (1 - 2.5 * me.realFontSize.y); me.realUpperMargin2 = me.realUpperMargin1 + 1.5 * me.realFontSize.y; diff --git a/qcsrc/menu/xonotic/soundlist.qc b/qcsrc/menu/xonotic/soundlist.qc index 13e6ba34c0..99094b0af2 100644 --- a/qcsrc/menu/xonotic/soundlist.qc +++ b/qcsrc/menu/xonotic/soundlist.qc @@ -55,8 +55,10 @@ void XonoticSoundList_resizeNotify(entity me, vector relOrigin, vector relSize, me.itemAbsSize = '0 0 0'; SUPER(XonoticSoundList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); me.columnNumberOrigin = 0; @@ -104,13 +106,10 @@ void SoundList_Menu_Track_Reset(entity box, entity me) void SoundList_Filter_Change(entity box, entity me) { - if(me.filterString) - strunzone(me.filterString); + strfree(me.filterString); if(box.text != "") me.filterString = strzone(box.text); - else - me.filterString = string_null; me.getSounds(me); } diff --git a/qcsrc/menu/xonotic/statslist.qc b/qcsrc/menu/xonotic/statslist.qc index 3f9dc410b3..2c600b6f52 100644 --- a/qcsrc/menu/xonotic/statslist.qc +++ b/qcsrc/menu/xonotic/statslist.qc @@ -100,7 +100,7 @@ void XonoticStatsList_getStats(entity me) case "overall/last_seen_dt": { order = 1; - outstr = _("Last seen:"); + outstr = _("Last match:"); data = XonoticStatsList_convertDate(car(data)); break; } @@ -270,8 +270,10 @@ void XonoticStatsList_resizeNotify(entity me, vector relOrigin, vector relSize, me.itemAbsSize = '0 0 0'; SUPER(XonoticStatsList).resizeNotify(me, relOrigin, relSize, absOrigin, absSize); - me.realFontSize_y = me.fontSize / (me.itemAbsSize_y = (absSize.y * me.itemHeight)); - me.realFontSize_x = me.fontSize / (me.itemAbsSize_x = (absSize.x * (1 - me.controlWidth))); + me.itemAbsSize.y = absSize.y * me.itemHeight; + me.itemAbsSize.x = absSize.x * (1 - me.controlWidth); + me.realFontSize.y = me.fontSize / me.itemAbsSize.y; + me.realFontSize.x = me.fontSize / me.itemAbsSize.x; me.realUpperMargin = 0.5 * (1 - me.realFontSize.y); #if 0 diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 7b381b5445..e994491121 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -1,4 +1,5 @@ #include "util.qh" +#include "dialog.qh" #include "../item.qh" @@ -266,8 +267,7 @@ void setZonedTooltip(entity e, string theTooltip, string theCvar) theTooltip = string_null; } - if(e.tooltip) - strunzone(e.tooltip); + strfree(e.tooltip); e.tooltip = (theTooltip != "") ? strzone(theTooltip) : string_null; } @@ -458,21 +458,18 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data) void updateCheck() { - if(cvar("menu_updatecheck")) + if(!_Nex_ExtResponseSystem_Queried) { - if(!_Nex_ExtResponseSystem_Queried) - { - _Nex_ExtResponseSystem_Queried = 1; - float startcnt; - string uri; + _Nex_ExtResponseSystem_Queried = 1; + float startcnt; + string uri; - cvar_set("cl_startcount", ftos(startcnt = cvar("cl_startcount") + 1)); + cvar_set("cl_startcount", ftos(startcnt = cvar("cl_startcount") + 1)); - // for privacy, munge the start count a little - startcnt = floor((floor(startcnt / 10) + random()) * 10); - uri = sprintf("http://update.xonotic.org/checkupdate.txt?version=%s&cnt=%d", uri_escape(cvar_string("g_xonoticversion")), startcnt); - uri_get(uri, URI_GET_UPDATENOTIFICATION); - } + // for privacy, munge the start count a little + startcnt = floor((floor(startcnt / 10) + random()) * 10); + uri = sprintf("http://update.xonotic.org/checkupdate.txt?version=%s&cnt=%d", uri_escape(cvar_string("g_xonoticversion")), startcnt); + uri_get(uri, URI_GET_UPDATENOTIFICATION); } if(_Nex_ExtResponseSystem_PacksStep > 0) @@ -565,7 +562,7 @@ void preMenuDraw() updateCheck(); - if(_Nex_ExtResponseSystem_UpdateTo != "") + if(_Nex_ExtResponseSystem_UpdateTo != "" && !(gamestatus & (GAME_CONNECTED | GAME_ISSERVER))) { // TODO rather turn this into a dialog fs = ((1/draw_scale.x) * eX + (1/draw_scale.y) * eY) * 12; @@ -611,8 +608,7 @@ void preMenuDraw() } else { - strunzone(campaign_name_previous); - campaign_name_previous = strzone(campaign_name); + strcpy(campaign_name_previous, campaign_name); campaign_won_previous = cvar(strcat("g_campaign", campaign_name, "_won")); } } @@ -693,6 +689,7 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_NEXBALL) \ GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \ GAMETYPE(MAPINFO_TYPE_ASSAULT) \ + /* GAMETYPE(MAPINFO_TYPE_DUEL) */ \ /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \ /**/ @@ -749,6 +746,7 @@ string GameType_GetIcon(int cnt) .void(entity) TR; .void(entity, float, float, entity) TD; .void(entity, float) TDempty; +.void(entity, float, float) gotoRC; entity makeXonoticTextLabel(float theAlign, string theText); entity makeXonoticTextSlider(string); .void(entity, string, string) addValue; @@ -758,12 +756,21 @@ entity makeXonoticCheckBoxString(string, string, string, string); entity makeXonoticCheckBox(float, string, string); .bool sendCvars; -void dialog_hudpanel_common_notoggle(entity me, string panelname) +void dialog_hudpanel_main_checkbox(entity me, string panelname) { - float i; entity e; me.TR(me); + me.TDempty(me, 1.5); + me.TD(me, 1, 2.5, e = makeXonoticCheckBox(0, strzone(strcat("hud_panel_", panelname)), _("Enable"))); +} + +void dialog_hudpanel_main_settings(entity me, string panelname) +{ + float i; + entity e; + + me.gotoRC(me, me.currentRow + 1.5, 0); me.TD(me, 1, 1.4, e = makeXonoticTextLabel(0, _("Background:"))); me.TD(me, 1, 2.6, e = makeXonoticTextSlider(strzone(strcat("hud_panel_", panelname, "_bg")))); e.addValue(e, _("Default"), ""); diff --git a/qcsrc/menu/xonotic/util.qh b/qcsrc/menu/xonotic/util.qh index 96fef2ad40..f5bd636d81 100644 --- a/qcsrc/menu/xonotic/util.qh +++ b/qcsrc/menu/xonotic/util.qh @@ -36,13 +36,8 @@ string GameType_GetIcon(int cnt); int GameType_GetCount(); int GameType_GetTotalCount(); -void dialog_hudpanel_common_notoggle(entity me, string panelname); -#define DIALOG_HUDPANEL_COMMON_NOTOGGLE() \ - dialog_hudpanel_common_notoggle(me, panelname) -#define DIALOG_HUDPANEL_COMMON() \ - me.TR(me); \ - me.TD(me, 1, 4, e = makeXonoticCheckBox(0, strzone(strcat("hud_panel_", panelname)), _("Enable panel"))); \ - DIALOG_HUDPANEL_COMMON_NOTOGGLE() +void dialog_hudpanel_main_checkbox(entity me, string panelname); +void dialog_hudpanel_main_settings(entity me, string panelname); float getFadedAlpha(float currentAlpha, float startAlpha, float targetAlpha); diff --git a/qcsrc/menu/xonotic/weaponarenacheckbox.qc b/qcsrc/menu/xonotic/weaponarenacheckbox.qc index fd4f51385b..695b2d7873 100644 --- a/qcsrc/menu/xonotic/weaponarenacheckbox.qc +++ b/qcsrc/menu/xonotic/weaponarenacheckbox.qc @@ -21,9 +21,8 @@ void XonoticWeaponarenaCheckBox_setChecked(entity me, float foo) } void XonoticWeaponarenaCheckBox_loadCvars(entity me) { - float n = tokenize_console(cvar_string("menu_weaponarena")); - float i; - for(i=0; i<n; ++i) + int n = tokenize_console(cvar_string("menu_weaponarena")); + for (int i = 0; i < n; i++) { if(argv(i) == me.netname) { diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index 569301c5d6..2ec8386955 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -4,22 +4,18 @@ #include <server/campaign.qc> #include <server/cheats.qc> #include <server/client.qc> +#include <server/clientkill.qc> #include <server/g_damage.qc> #include <server/g_hook.qc> -#include <server/g_lights.qc> -#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/player.qc> -#include <server/playerdemo.qc> #include <server/portals.qc> #include <server/race.qc> #include <server/resources.qc> diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index 2013fd6bb5..cc27baf120 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -4,22 +4,18 @@ #include <server/campaign.qh> #include <server/cheats.qh> #include <server/client.qh> +#include <server/clientkill.qh> #include <server/g_damage.qh> #include <server/g_hook.qh> -#include <server/g_lights.qh> -#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> #include <server/player.qh> -#include <server/playerdemo.qh> #include <server/portals.qh> #include <server/race.qh> #include <server/resources.qh> diff --git a/qcsrc/server/antilag.qc b/qcsrc/server/antilag.qc index 4062f7f660..c6e26e09e8 100644 --- a/qcsrc/server/antilag.qc +++ b/qcsrc/server/antilag.qc @@ -5,6 +5,7 @@ #include <server/defs.qh> #include <common/state.qh> #include <common/vehicles/all.qh> + #include <lib/warpzone/common.qh> #include "antilag.qh" #endif @@ -146,3 +147,78 @@ void antilag_restore_all(entity ignore) antilag_restore(it, it); }); } + +/* +================== +traceline_antilag + +A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack +Additionally it moves players back into the past before the trace and restores them afterward. +================== +*/ +void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz) +{ + // check whether antilagged traces are enabled + if (lag < 0.001) + lag = 0; + if (!IS_REAL_CLIENT(forent)) + lag = 0; // only antilag for clients + + // change shooter to SOLID_BBOX so the shot can hit corpses + int oldsolid = source.dphitcontentsmask; + if(source) + source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + + if (lag) + antilag_takeback_all(forent, lag); + + // do the trace + if(wz) + WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent); + else + tracebox (v1, mi, ma, v2, nomonst, forent); + + // restore players to current positions + if (lag) + antilag_restore_all(forent); + + // restore shooter solid type + if(source) + source.dphitcontentsmask = oldsolid; +} +void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) +{ + tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, false); +} +void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) +{ + bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); + if (autocvar_g_antilag != 2 || noantilag) + lag = 0; + traceline_antilag_force(source, v1, v2, nomonst, forent, lag); +} +void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag) +{ + bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); + if (autocvar_g_antilag != 2 || noantilag) + lag = 0; + tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, false); +} +void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) +{ + tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, true); +} +void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) +{ + bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); + if (autocvar_g_antilag != 2 || noantilag) + lag = 0; + WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag); +} +void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag) +{ + bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); + if (autocvar_g_antilag != 2 || noantilag) + lag = 0; + tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, true); +} diff --git a/qcsrc/server/antilag.qh b/qcsrc/server/antilag.qh index d57762ccd3..c3be5553a9 100644 --- a/qcsrc/server/antilag.qh +++ b/qcsrc/server/antilag.qh @@ -13,3 +13,19 @@ void antilag_restore_all(entity ignore); #define ANTILAG_LATENCY(e) min(0.4, CS(e).ping * 0.001) // add one ticrate? + +/* +================== +traceline_antilag + +A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack +Additionally it moves players back into the past before the trace and restores them afterward. +================== +*/ +void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz); +void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); +void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); +void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); +void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); +void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); +void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index a2f9ab49b4..b23f6f1481 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -88,9 +88,6 @@ float autocvar_g_balance_powerup_strength_selfforce; //float autocvar_g_balance_powerup_strength_time; float autocvar_g_balance_superweapons_time; 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_ballistics_density_corpse; float autocvar_g_ballistics_density_player; float autocvar_g_ballistics_mindistance; @@ -102,6 +99,7 @@ float autocvar_g_ban_sync_timeout; string autocvar_g_ban_sync_trusted_servers; bool autocvar_g_ban_sync_trusted_servers_verify; string autocvar_g_ban_sync_uri; +bool autocvar_g_ban_telluser = true; string autocvar_g_banned_list; bool autocvar_g_banned_list_idmode; bool autocvar_g_botclip_collisions; @@ -109,7 +107,6 @@ bool autocvar_g_campaign; #define autocvar_g_campaign_forceteam cvar("g_campaign_forceteam") int autocvar_g_campaign_skill; int autocvar_g_casings; -bool autocvar_g_changeteam_banned; float autocvar_g_chat_flood_burst; float autocvar_g_chat_flood_burst_team; float autocvar_g_chat_flood_burst_tell; @@ -124,11 +121,7 @@ int autocvar_g_chat_nospectators; bool autocvar_g_chat_teamcolors; bool autocvar_g_chat_tellprivacy; bool autocvar_g_forced_respawn; -string autocvar_g_forced_team_blue; -string autocvar_g_forced_team_otherwise; -string autocvar_g_forced_team_pink; -string autocvar_g_forced_team_red; -string autocvar_g_forced_team_yellow; +string autocvar_g_forced_team_otherwise; // TODO: Move to teamplay.qc #define autocvar_g_friendlyfire cvar("g_friendlyfire") #define autocvar_g_friendlyfire_virtual cvar("g_friendlyfire_virtual") #define autocvar_g_friendlyfire_virtual_force cvar("g_friendlyfire_virtual_force") @@ -167,7 +160,7 @@ int autocvar_g_maxplayers; float autocvar_g_maxplayers_spectator_blocktime; float autocvar_g_maxpushtime; float autocvar_g_maxspeed; -bool autocvar_g_instagib; +#define autocvar_g_instagib cvar("g_instagib") #define autocvar_g_mirrordamage cvar("g_mirrordamage") #define autocvar_g_mirrordamage_virtual cvar("g_mirrordamage_virtual") bool autocvar_g_mirrordamage_onlyweapons; @@ -240,6 +233,7 @@ float autocvar_g_turrets_targetscan_mindelay; bool autocvar_g_use_ammunition; bool autocvar_g_waypointeditor; bool autocvar_g_waypointeditor_symmetrical; +bool autocvar_g_waypointeditor_symmetrical_allowload = true; vector autocvar_g_waypointeditor_symmetrical_origin; int autocvar_g_waypointeditor_symmetrical_order; vector autocvar_g_waypointeditor_symmetrical_axis; @@ -256,7 +250,6 @@ bool autocvar_lastlevel; //int autocvar_leadlimit; int autocvar_leadlimit_and_fraglimit; int autocvar_leadlimit_override; -int autocvar_loddebug; int autocvar_minplayers; string autocvar_nextmap; string autocvar_quit_and_redirect; @@ -321,7 +314,6 @@ float autocvar_sv_maxairspeed; float autocvar_sv_maxspeed; string autocvar_sv_motd; bool autocvar_sv_precacheplayermodels; -//float autocvar_sv_precacheweapons; // WEAPONTODO? bool autocvar_sv_q3acompat_machineshotgunswap; bool autocvar_sv_servermodelsonly; int autocvar_sv_spectate; @@ -366,8 +358,6 @@ bool autocvar_sv_vote_gamestart; string autocvar_sv_weaponstats_file; float autocvar_sv_gibhealth; float autocvar_sys_ticrate; -bool autocvar_teamplay_lockonrestart; -int autocvar_teamplay_mode; #define autocvar_timelimit cvar("timelimit") #define autocvar_timelimit_override cvar("timelimit_override") float autocvar_timelimit_increment; @@ -470,6 +460,8 @@ float autocvar_g_nades_entrap_strength = 0.01; float autocvar_g_nades_entrap_speed = 0.5; float autocvar_g_nades_entrap_radius = 500; float autocvar_g_nades_entrap_time = 10; +float autocvar_g_nades_veil_time = 8; +float autocvar_g_nades_veil_radius = 300; string autocvar_g_nades_pokenade_monster_type; float autocvar_g_nades_pokenade_monster_lifetime; bool autocvar_g_jump_grunt; @@ -526,4 +518,6 @@ float autocvar_sv_airstopaccelerate; float autocvar_sv_track_canjump; bool autocvar_sv_showspectators; bool autocvar_g_weaponswitch_debug; +bool autocvar_g_weaponswitch_debug_alternate; bool autocvar_g_allow_checkpoints; +bool autocvar_sv_vq3compat_changehitbox = false; diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh index 51ac148fc1..11e0707e98 100644 --- a/qcsrc/server/bot/api.qh +++ b/qcsrc/server/bot/api.qh @@ -13,6 +13,7 @@ const int WAYPOINTFLAG_PROTECTED = BIT(18); // Useless WP detection never kills const int WAYPOINTFLAG_USEFUL = BIT(17); // Useless WP detection temporary flag. const int WAYPOINTFLAG_DEAD_END = BIT(16); // Useless WP detection temporary flag. const int WAYPOINTFLAG_LADDER = BIT(15); +const int WAYPOINTFLAG_JUMP = BIT(14); entity kh_worldkeylist; .entity kh_worldkeynext; @@ -79,7 +80,9 @@ bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, en vector havocbot_middlepoint; float havocbot_middlepoint_radius; -vector havocbot_symmetryaxis_equation; +float havocbot_symmetry_axis_m; +float havocbot_symmetry_axis_q; +float havocbot_symmetry_origin_order; .float goalentity_lock_timeout; .float ignoregoaltime; @@ -88,7 +91,7 @@ vector havocbot_symmetryaxis_equation; .entity bot_basewaypoint; .bool navigation_dynamicgoal; void navigation_dynamicgoal_init(entity this, bool initially_static); -void navigation_dynamicgoal_set(entity this); +void navigation_dynamicgoal_set(entity this, entity dropper); void navigation_dynamicgoal_unset(entity this); entity navigation_findnearestwaypoint(entity ent, float walkfromwp); void navigation_goalrating_end(entity this); @@ -116,7 +119,7 @@ void waypoint_schedulerelink(entity wp); void waypoint_spawnforitem(entity e); void waypoint_spawnforitem_force(entity e, vector org); void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent); -void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent); +void waypoint_spawnforteleporter_wz(entity e, entity tracetest_ent); void waypoint_spawn_fromeditor(entity pl); entity waypoint_spawn(vector m1, vector m2, float f); void waypoint_unreachable(entity pl); diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc index 5ad1295f99..e7a2e6a7be 100644 --- a/qcsrc/server/bot/default/aim.qc +++ b/qcsrc/server/bot/default/aim.qc @@ -11,7 +11,7 @@ #include "../../weapons/weaponsystem.qh" -#include "../../mutators/_mod.qh" +#include <server/mutators/_mod.qh> // traces multiple trajectories to find one that will impact the target // 'end' vector is the place it aims for, @@ -95,11 +95,11 @@ float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, f void lag_update(entity this) { - if (this.lag1_time) if (time > this.lag1_time) {this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4);this.lag1_time = 0;} - if (this.lag2_time) if (time > this.lag2_time) {this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4);this.lag2_time = 0;} - if (this.lag3_time) if (time > this.lag3_time) {this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4);this.lag3_time = 0;} - if (this.lag4_time) if (time > this.lag4_time) {this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4);this.lag4_time = 0;} - if (this.lag5_time) if (time > this.lag5_time) {this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4);this.lag5_time = 0;} + if (this.lag1_time && time > this.lag1_time) { this.lag_func(this, this.lag1_time, this.lag1_float1, this.lag1_float2, this.lag1_entity1, this.lag1_vec1, this.lag1_vec2, this.lag1_vec3, this.lag1_vec4); this.lag1_time = 0; } + if (this.lag2_time && time > this.lag2_time) { this.lag_func(this, this.lag2_time, this.lag2_float1, this.lag2_float2, this.lag2_entity1, this.lag2_vec1, this.lag2_vec2, this.lag2_vec3, this.lag2_vec4); this.lag2_time = 0; } + if (this.lag3_time && time > this.lag3_time) { this.lag_func(this, this.lag3_time, this.lag3_float1, this.lag3_float2, this.lag3_entity1, this.lag3_vec1, this.lag3_vec2, this.lag3_vec3, this.lag3_vec4); this.lag3_time = 0; } + if (this.lag4_time && time > this.lag4_time) { this.lag_func(this, this.lag4_time, this.lag4_float1, this.lag4_float2, this.lag4_entity1, this.lag4_vec1, this.lag4_vec2, this.lag4_vec3, this.lag4_vec4); this.lag4_time = 0; } + if (this.lag5_time && time > this.lag5_time) { this.lag_func(this, this.lag5_time, this.lag5_float1, this.lag5_float2, this.lag5_entity1, this.lag5_vec1, this.lag5_vec2, this.lag5_vec3, this.lag5_vec4); this.lag5_time = 0; } } float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) @@ -139,10 +139,12 @@ bool bot_shouldattack(entity this, entity targ) return false; if (IS_DEAD(targ)) return false; - if (PHYS_INPUT_BUTTON_CHAT(targ)) + if (PHYS_INPUT_BUTTON_CHAT(targ) && !autocvar_bot_typefrag) return false; if(targ.flags & FL_NOTARGET) return false; + if(targ.alpha <= 0.1 && targ.alpha != 0) + return false; // invisible via alpha if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ)) return false; @@ -152,11 +154,6 @@ bool bot_shouldattack(entity this, entity targ) void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) { - if(this.flags & FL_INWATER) - { - this.bot_aimtarg = NULL; - return; - } this.bot_aimtarg = e1; this.bot_aimlatency = CS(this).ping; // FIXME? Shouldn't this be in the lag item? //this.bot_aimorigin = v1; @@ -178,6 +175,8 @@ void bot_aimdir(entity this, vector v, float maxfiredeviation) float dist, delta_t, blend; vector desiredang, diffang; + this.bot_aimdir_executed = true; + //dprint("aim ", this.netname, ": old:", vtos(this.v_angle)); // make sure v_angle is sane first this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360; @@ -243,6 +242,7 @@ void bot_aimdir(entity this, vector v, float maxfiredeviation) + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th ); + desiredang.x = bound(-90, desiredang.x, 90); // calculate turn angles diffang = desiredang - this.bot_mouseaim; diff --git a/qcsrc/server/bot/default/aim.qh b/qcsrc/server/bot/default/aim.qh index b8b35f1c20..1eb71bc7ff 100644 --- a/qcsrc/server/bot/default/aim.qh +++ b/qcsrc/server/bot/default/aim.qh @@ -59,6 +59,7 @@ vector shotdir; .vector lag5_vec3; .vector lag5_vec4; +.bool bot_aimdir_executed; .float bot_badaimtime; .float bot_aimthinktime; .float bot_prevaimtime; diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc index a605fc0689..0c4668f2f3 100644 --- a/qcsrc/server/bot/default/bot.qc +++ b/qcsrc/server/bot/default/bot.qc @@ -21,7 +21,7 @@ #include "../../race.qh" #include <common/t_items.qh> -#include "../../mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "../../weapons/accuracy.qh" @@ -74,12 +74,6 @@ void bot_think(entity this) this.bot_nextthink = max(time, this.bot_nextthink) + max(0.01, autocvar_bot_ai_thinkinterval * (0.5 ** this.bot_aiskill) * min(14 / (skill + 14), 1)); - //if (this.bot_painintensity > 0) - // this.bot_painintensity = this.bot_painintensity - (skill + 1) * 40 * frametime; - - //this.bot_painintensity = this.bot_painintensity + this.bot_oldhealth - this.health; - //this.bot_painintensity = bound(0, this.bot_painintensity, 100); - if (!IS_PLAYER(this) || (autocvar_g_campaign && !campaign_bots_may_start)) { CS(this).movement = '0 0 0'; @@ -129,6 +123,9 @@ void bot_think(entity this) // if dead, just wait until we can respawn if (IS_DEAD(this)) { + if (bot_waypoint_queue_owner == this) + bot_waypoint_queue_owner = NULL; + this.aistatus = 0; CS(this).movement = '0 0 0'; if (this.deadflag == DEAD_DEAD) { @@ -244,7 +241,7 @@ void bot_setnameandstuff(entity this) this.bot_config_loaded = true; - // this is really only a default, JoinBestTeam is called later + // this is really only a default, TeamBalance_JoinBestTeam is called later setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants)); this.bot_preferredcolors = this.clientcolors; @@ -255,16 +252,23 @@ void bot_setnameandstuff(entity this) name = bot_name; // number bots with identical names - int j = 0; - FOREACH_CLIENT(IS_BOT_CLIENT(it), { - if(it.cleanname == name) - ++j; - }); - if (j) - this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix)); - else + if (name == "") + { + name = ftos(etof(this)); this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix)); - + } + else + { + int j = 0; + FOREACH_CLIENT(IS_BOT_CLIENT(it), { + if(it.cleanname == name) + ++j; + }); + if (j) + this.netname = this.netname_freeme = strzone(strcat(prefix, name, "(", ftos(j), ")", suffix)); + else + this.netname = this.netname_freeme = strzone(strcat(prefix, name, suffix)); + } this.cleanname = strzone(name); // pick the model and skin @@ -396,18 +400,10 @@ void bot_clientdisconnect(entity this) if (!IS_BOT_CLIENT(this)) return; bot_clearqueue(this); - if(this.cleanname) - strunzone(this.cleanname); - if(this.netname_freeme) - strunzone(this.netname_freeme); - if(this.playermodel_freeme) - strunzone(this.playermodel_freeme); - if(this.playerskin_freeme) - strunzone(this.playerskin_freeme); - this.cleanname = string_null; - this.netname_freeme = string_null; - this.playermodel_freeme = string_null; - this.playerskin_freeme = string_null; + strfree(this.cleanname); + strfree(this.netname_freeme); + strfree(this.playermodel_freeme); + strfree(this.playerskin_freeme); if(this.bot_cmd_current) delete(this.bot_cmd_current); if(bot_waypoint_queue_owner == this) @@ -438,15 +434,15 @@ void bot_clientconnect(entity this) else if(this.bot_forced_team==4) this.team = NUM_TEAM_4; else - JoinBestTeam(this, true); + TeamBalance_JoinBestTeam(this); havocbot_setupbot(this); } void bot_removefromlargestteam() { - CheckAllowedTeams(NULL); - GetTeamCounts(NULL); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + TeamBalance_GetTeamCounts(balance, NULL); entity best = NULL; float besttime = 0; @@ -465,12 +461,10 @@ void bot_removefromlargestteam() int thiscount = 0; - switch(it.team) + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: thiscount = c1; break; - case NUM_TEAM_2: thiscount = c2; break; - case NUM_TEAM_3: thiscount = c3; break; - case NUM_TEAM_4: thiscount = c4; break; + thiscount = TeamBalance_GetNumberOfPlayers(balance, + Team_TeamToIndex(it.team)); } if(thiscount > bestcount) @@ -485,6 +479,7 @@ void bot_removefromlargestteam() best = it; } }); + TeamBalance_Destroy(balance); if(!bcount) return; // no bots to remove currentbots = currentbots - 1; @@ -599,8 +594,6 @@ float bot_fixcount() } int bots; - // add/remove bots if needed to make sure there are at least - // minplayers+bot_number, or remove all bots if no one is playing // But don't remove bots immediately on level change, as the real players // usually haven't rejoined yet bots_would_leave = false; @@ -608,15 +601,17 @@ float bot_fixcount() bots = min(ceil(fabs(autocvar_bot_vs_human) * activerealplayers), maxclients - realplayers); else if ((realplayers || autocvar_bot_join_empty || (currentbots > 0 && time < 5))) { - float realminplayers, minplayers; - realminplayers = autocvar_minplayers; - minplayers = max(0, floor(realminplayers)); + int minplayers = max(0, floor(autocvar_minplayers)); + int minbots = max(0, floor(autocvar_bot_number)); - float realminbots, minbots; - realminbots = autocvar_bot_number; - minbots = max(0, floor(realminbots)); + // add bots to reach minplayers if needed + bots = max(minbots, minplayers - activerealplayers); + // cap bots to the max players allowed by the server + int player_limit = GetPlayerLimit(); + if(player_limit) + bots = min(bots, player_limit - activerealplayers); + bots = min(bots, maxclients - realplayers); - bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers); if(bots > minbots) bots_would_leave = true; } diff --git a/qcsrc/server/bot/default/bot.qh b/qcsrc/server/bot/default/bot.qh index ca567181bb..c4da14a4be 100644 --- a/qcsrc/server/bot/default/bot.qh +++ b/qcsrc/server/bot/default/bot.qh @@ -16,7 +16,7 @@ const int AI_STATUS_JETPACK_FLYING = BIT(9); const int AI_STATUS_JETPACK_LANDING = BIT(10); const int AI_STATUS_STUCK = BIT(11); // Cannot reach any goal -.float isbot; // true if this client is actually a bot +.bool isbot; // true if this client is actually a bot .int aistatus; // Skill system diff --git a/qcsrc/server/bot/default/cvars.qh b/qcsrc/server/bot/default/cvars.qh index 16b2aaf431..aea112d9ed 100644 --- a/qcsrc/server/bot/default/cvars.qh +++ b/qcsrc/server/bot/default/cvars.qh @@ -54,6 +54,7 @@ bool autocvar_bot_usemodelnames; bool autocvar_bot_debug_tracewalk; bool autocvar_bot_debug_goalstack; bool autocvar_bot_wander_enable; +bool autocvar_bot_typefrag; bool autocvar_g_debug_bot_commands; int autocvar_g_waypointeditor_auto; float autocvar_skill_auto; diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index 2a0d0c8503..17a7c29ef8 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -18,8 +18,9 @@ #include <common/items/_mod.qh> #include <common/wepent.qh> -#include <common/triggers/teleporters.qh> -#include <common/triggers/trigger/jumppads.qh> +#include <common/mapobjects/func/ladder.qh> +#include <common/mapobjects/teleporters.qh> +#include <common/mapobjects/trigger/jumppads.qh> #include <lib/warpzone/common.qh> @@ -104,12 +105,15 @@ void havocbot_ai(entity this) } havocbot_aim(this); lag_update(this); + + this.bot_aimdir_executed = false; + if (this.bot_aimtarg) { this.aistatus |= AI_STATUS_ATTACKING; this.aistatus &= ~AI_STATUS_ROAMING; - if(this.weapons) + if(STAT(WEAPONS, this)) { if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this)) { @@ -140,48 +144,17 @@ void havocbot_ai(entity this) { this.aistatus |= AI_STATUS_ROAMING; this.aistatus &= ~AI_STATUS_ATTACKING; - - vector now, next; - float aimdistance,skillblend,distanceblend,blend; - - vector v = get_closer_dest(this.goalcurrent, this.origin); - if(this.goalcurrent.wpisbox) - { - // avoid a glitch when bot is teleported but teleport waypoint isn't removed yet - if(this.goalstack02 && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT - && this.lastteleporttime > 0 && time - this.lastteleporttime < 0.15) - v = (this.goalstack02.absmin + this.goalstack02.absmax) * 0.5; - // aim to teleport origin if bot is inside teleport waypoint but hasn't touched the real teleport yet - else if(boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin, this.origin)) - v = this.goalcurrent.origin; - } - next = now = v - (this.origin + this.view_ofs); - aimdistance = vlen(now); - - //dprint(this.goalstack01.classname,etos(this.goalstack01),"\n"); - if( - this.goalstack01 != this && this.goalstack01 && !wasfreed(this.goalstack01) && ((this.aistatus & AI_STATUS_RUNNING) == 0) && - !(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) - ) - next = ((this.goalstack01.absmin + this.goalstack01.absmax) * 0.5) - (this.origin + this.view_ofs); - - skillblend=bound(0,(skill+this.bot_moveskill-2.5)*0.5,1); //lower skill player can't preturn - distanceblend=bound(0,aimdistance/autocvar_bot_ai_keyboard_distance,1); - blend = skillblend * (1-distanceblend); - //v = (now * (distanceblend) + next * (1-distanceblend)) * (skillblend) + now * (1-skillblend); - //v = now * (distanceblend) * (skillblend) + next * (1-distanceblend) * (skillblend) + now * (1-skillblend); - //v = now * ((1-skillblend) + (distanceblend) * (skillblend)) + next * (1-distanceblend) * (skillblend); - v = now + blend * (next - now); - //dprint(etos(this), " "); - //dprint(vtos(now), ":", vtos(next), "=", vtos(v), " (blend ", ftos(blend), ")\n"); - //v = now * (distanceblend) + next * (1-distanceblend); - if (this.waterlevel < WATERLEVEL_SWIMMING) - v.z = 0; - //dprint("walk at:", vtos(v), "\n"); - //te_lightning2(NULL, this.origin, this.goalcurrent.origin); - bot_aimdir(this, v, -1); } + havocbot_movetogoal(this); + if (!this.bot_aimdir_executed && this.goalcurrent) + { + // Heading + vector dir = get_closer_dest(this.goalcurrent, this.origin); + dir -= this.origin + this.view_ofs; + dir.z = 0; + bot_aimdir(this, dir, -1); + } // if the bot is not attacking, consider reloading weapons if (!(this.aistatus & AI_STATUS_ATTACKING)) @@ -204,7 +177,7 @@ void havocbot_ai(entity this) if(this.(weaponentity).clip_load >= 0) // only if we're not reloading a weapon already { FOREACH(Weapons, it != WEP_Null, { - if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo)) + if((STAT(WEAPONS, this) & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.(weaponentity).weapon_load[it.m_id] < it.reloading_ammo)) { this.(weaponentity).m_switchweapon = it; break; @@ -488,6 +461,7 @@ void havocbot_movetogoal(entity this) vector flatdir; vector evadeobstacle; vector evadelava; + float dodge_enemy_factor = 1; float maxspeed; //float dist; vector dodge; @@ -500,7 +474,7 @@ void havocbot_movetogoal(entity this) // Jetpack navigation if(this.navigation_jetpack_goal) if(this.goalcurrent==this.navigation_jetpack_goal) - if(this.ammo_fuel) + if(GetResourceAmount(this, RESOURCE_FUEL)) { if(autocvar_bot_debug_goalstack) { @@ -525,13 +499,13 @@ void havocbot_movetogoal(entity this) { // Calculate brake distance in xy float d = vlen(vec2(this.origin - (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5)); - float v = vlen(vec2(this.velocity)); - float db = ((v ** 2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100; + float vel2 = vlen2(vec2(this.velocity)); + float db = (vel2 / (autocvar_g_jetpack_acceleration_side * 2)) + 100; //LOG_INFOF("distance %d, velocity %d, brake at %d ", ceil(d), ceil(v), ceil(db)); if(d < db || d < 500) { // Brake - if(v > maxspeed * 0.3) + if (vel2 > (maxspeed * 0.3) ** 2) { CS(this).movement_x = dir * v_forward * -maxspeed; return; @@ -567,8 +541,8 @@ void havocbot_movetogoal(entity this) if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) { this.aistatus |= AI_STATUS_OUT_JUMPPAD; - navigation_poptouchedgoals(this); - return; + if(navigation_poptouchedgoals(this)) + return; } else if(this.aistatus & AI_STATUS_OUT_JUMPPAD) { @@ -695,7 +669,8 @@ void havocbot_movetogoal(entity this) return; } - else if(this.health + this.armorvalue > ROCKETJUMP_DAMAGE()) + else if(!this.jumppadcount && !this.goalcurrent.wphardwired + && GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR) > ROCKETJUMP_DAMAGE()) { if(this.velocity.z < 0) { @@ -748,9 +723,10 @@ void havocbot_movetogoal(entity this) else PHYS_INPUT_BUTTON_JUMP(this) = false; makevectors(this.v_angle.y * '0 1 0'); - CS(this).movement_x = dir * v_forward * maxspeed; - CS(this).movement_y = dir * v_right * maxspeed; - CS(this).movement_z = dir * v_up * maxspeed; + vector v = dir * maxspeed; + CS(this).movement.x = v * v_forward; + CS(this).movement.y = v * v_right; + CS(this).movement.z = v * v_up; } // if there is nowhere to go, exit @@ -799,19 +775,30 @@ void havocbot_movetogoal(entity this) if (this.goalcurrent == this.goalentity && this.goalentity_lock_timeout > time) locked_goal = true; - navigation_shortenpath(this); + if (navigation_shortenpath(this)) + { + if (vdist(this.origin - this.goalcurrent_prev.origin, <, 50) + && navigation_goalrating_timeout_can_be_anticipated(this)) + { + navigation_goalrating_timeout_force(this); + } + } - if (IS_MOVABLE(this.goalcurrent)) + bool goalcurrent_can_be_removed = false; + if (IS_PLAYER(this.goalcurrent) || IS_MONSTER(this.goalcurrent)) { - if (IS_DEAD(this.goalcurrent)) + bool freeze_state_changed = (boolean(STAT(FROZEN, this.goalentity)) != this.goalentity_shouldbefrozen); + if (IS_DEAD(this.goalcurrent) || (this.goalentity == this.goalcurrent && freeze_state_changed)) { + goalcurrent_can_be_removed = true; + // don't remove if not visible if (checkpvs(this.origin + this.view_ofs, this.goalcurrent)) { navigation_goalrating_timeout_force(this); return; } } - else if (this.bot_tracewalk_time < time) + else if (!(STAT(FROZEN, this.goalentity)) && this.bot_tracewalk_time < time) { set_tracewalk_dest(this.goalcurrent, this.origin, true); if (!(trace_ent == this || tracewalk(this, this.origin, this.mins, this.maxs, @@ -823,6 +810,7 @@ void havocbot_movetogoal(entity this) this.bot_tracewalk_time = max(time, this.bot_tracewalk_time) + 0.25; } } + if(!locked_goal) { // optimize path finding by anticipating goalrating when bot is near a waypoint; @@ -832,7 +820,7 @@ void havocbot_movetogoal(entity this) { if (this.goalcurrent) { - if (IS_MOVABLE(this.goalcurrent) && IS_DEAD(this.goalcurrent)) + if (goalcurrent_can_be_removed) { // remove even if not visible navigation_goalrating_timeout_force(this); @@ -872,30 +860,53 @@ void havocbot_movetogoal(entity this) bool bunnyhop_forbidden = false; vector destorg = get_closer_dest(this.goalcurrent, this.origin); - - // in case bot ends up inside the teleport waypoint without touching - // the teleport itself, head to the teleport origin - if(this.goalcurrent.wpisbox && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin + eZ * this.mins.z, this.origin + eZ * this.maxs.z)) + if (this.jumppadcount && this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) { - bunnyhop_forbidden = true; + // if bot used the jumppad, push towards jumppad origin until jumppad waypoint gets removed destorg = this.goalcurrent.origin; - if(destorg.z > this.origin.z) - PHYS_INPUT_BUTTON_JUMP(this) = true; + } + else if (this.goalcurrent.wpisbox) + { + // if bot is inside the teleport waypoint, head to teleport origin until teleport gets used + // do it even if bot is on a ledge above a teleport/jumppad so it doesn't get stuck + if (boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin + eZ * this.mins.z, this.origin + eZ * this.maxs.z) + || (this.absmin.z > destorg.z && destorg.x == this.origin.x && destorg.y == this.origin.y)) + { + bunnyhop_forbidden = true; + destorg = this.goalcurrent.origin; + if(destorg.z > this.origin.z) + PHYS_INPUT_BUTTON_JUMP(this) = true; + } } diff = destorg - this.origin; - if (fabs(diff.x) < 10 && fabs(diff.y) < 10 - && this.goalcurrent == this.goalentity && time < this.goalentity_lock_timeout) + if (this.goalcurrent == this.goalentity && time < this.goalentity_lock_timeout && vdist(diff, <, 10)) { + // stop if the locked goal has been reached destorg = this.origin; - diff.x = 0; - diff.y = 0; + diff = dir = '0 0 0'; } - - dir = normalize(diff); - flatdir = diff;flatdir.z = 0; - flatdir = normalize(flatdir); + else if (IS_PLAYER(this.goalcurrent) || IS_MONSTER(this.goalcurrent)) + { + if (vdist(diff, <, 80)) + { + // stop if too close to target player (even if frozen) + destorg = this.origin; + diff = dir = '0 0 0'; + } + else + { + // move destorg out of target players, otherwise bot will consider them + // an obstacle that needs to be jumped (especially if frozen) + dir = normalize(diff); + destorg -= dir * PL_MAX_CONST.x * M_SQRT2; + diff = destorg - this.origin; + } + } + else + dir = normalize(diff); + flatdir = (diff.z == 0) ? dir : normalize(vec2(diff)); //if (this.bot_dodgevector_time < time) { @@ -906,9 +917,9 @@ void havocbot_movetogoal(entity this) this.aistatus &= ~AI_STATUS_DANGER_AHEAD; makevectors(this.v_angle.y * '0 1 0'); - if (this.waterlevel) + if (this.waterlevel > WATERLEVEL_WETFEET) { - if(this.waterlevel>WATERLEVEL_SWIMMING) + if (this.waterlevel > WATERLEVEL_SWIMMING) { if(!this.goalcurrent) this.aistatus |= AI_STATUS_OUT_WATER; @@ -919,7 +930,7 @@ void havocbot_movetogoal(entity this) { dir = flatdir; if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && destorg.z < this.origin.z) && - ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER)) + (this.aistatus & AI_STATUS_OUT_WATER)) PHYS_INPUT_BUTTON_JUMP(this) = true; else PHYS_INPUT_BUTTON_JUMP(this) = false; @@ -935,21 +946,33 @@ void havocbot_movetogoal(entity this) // jump if going toward an obstacle that doesn't look like stairs we // can walk up directly vector deviation = '0 0 0'; - if (this.velocity) + float current_speed = vlen(vec2(this.velocity)); + if (current_speed < maxspeed * 0.2) + current_speed = maxspeed * 0.2; + else { deviation = vectoangles(diff) - vectoangles(this.velocity); while (deviation.y < -180) deviation.y += 360; while (deviation.y > 180) deviation.y -= 360; } + float turning = false; vector flat_diff = vec2(diff); - offset = max(32, vlen(vec2(this.velocity)) * cos(deviation.y * DEG2RAD) * 0.2) * flatdir; + offset = max(32, current_speed * cos(deviation.y * DEG2RAD) * 0.3) * flatdir; vector actual_destorg = this.origin + offset; - if (!this.goalstack01 || this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) + if (!this.goalstack01 || this.goalcurrent.wpflags & (WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_LADDER)) { if (vlen2(flat_diff) < vlen2(offset)) { - actual_destorg.x = destorg.x; - actual_destorg.y = destorg.y; + if (this.goalcurrent.wpflags & WAYPOINTFLAG_JUMP && this.goalstack01) + { + // oblique warpzones need a jump otherwise bots gets stuck + PHYS_INPUT_BUTTON_JUMP(this) = true; + } + else + { + actual_destorg.x = destorg.x; + actual_destorg.y = destorg.y; + } } } else if (vdist(flat_diff, <, 32) && diff.z < -16) // destination is under the bot @@ -961,24 +984,53 @@ void havocbot_movetogoal(entity this) { vector next_goal_org = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; vector next_dir = normalize(vec2(next_goal_org - destorg)); - float next_dist = vlen(vec2(this.origin + offset - destorg)); - actual_destorg = vec2(destorg) + next_dist * next_dir; + float dist = vlen(vec2(this.origin + offset - destorg)); + // if current and next goal are close to each other make sure + // actual_destorg isn't set beyond next_goal_org + if (dist ** 2 > vlen2(vec2(next_goal_org - destorg))) + actual_destorg = next_goal_org; + else + actual_destorg = vec2(destorg) + dist * next_dir; actual_destorg.z = this.origin.z; + turning = true; } - tracebox(this.origin, this.mins, this.maxs, actual_destorg, false, this); - if (trace_fraction < 1) - if (trace_plane_normal.z < 0.7) + LABEL(jump_check); + dir = flatdir = normalize(actual_destorg - this.origin); + + if (turning || fabs(deviation.y) < 50) // don't even try to jump if deviation is too high { - s = trace_fraction; - tracebox(this.origin + stepheightvec, this.mins, this.maxs, actual_destorg + stepheightvec, false, this); - if (trace_fraction < s + 0.01) - if (trace_plane_normal.z < 0.7) + tracebox(this.origin, this.mins, this.maxs, actual_destorg, false, this); + if (trace_fraction < 1 && trace_plane_normal.z < 0.7) { s = trace_fraction; - tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, actual_destorg + jumpstepheightvec, false, this); - if (trace_fraction > s) - PHYS_INPUT_BUTTON_JUMP(this) = true; + tracebox(this.origin + stepheightvec, this.mins, this.maxs, actual_destorg + stepheightvec, false, this); + if (trace_fraction < s + 0.01 && trace_plane_normal.z < 0.7) + { + // found an obstacle + if (turning && fabs(deviation.y) > 5) + { + // check if the obstacle is still there without turning + actual_destorg = destorg; + turning = false; + this.bot_tracewalk_time = time + 0.25; + goto jump_check; + } + s = trace_fraction; + // don't artificially reduce max jump height in real-time + // (jumpstepheightvec is reduced a bit to make the jumps easy in tracewalk) + vector jump_height = (IS_ONGROUND(this)) ? stepheightvec + jumpheight_vec : jumpstepheightvec; + tracebox(this.origin + jump_height, this.mins, this.maxs, actual_destorg + jump_height, false, this); + if (trace_fraction > s) + PHYS_INPUT_BUTTON_JUMP(this) = true; + else + { + jump_height = stepheightvec + jumpheight_vec / 2; + tracebox(this.origin + jump_height, this.mins, this.maxs, actual_destorg + jump_height, false, this); + if (trace_fraction > s) + PHYS_INPUT_BUTTON_JUMP(this) = true; + } + } } } @@ -1065,6 +1117,18 @@ void havocbot_movetogoal(entity this) if(IS_PLAYER(this.goalcurrent)) unreachable = true; } + + // slow down if bot is in the air and goal is under it + if (!this.goalcurrent.wphardwired + && vdist(flat_diff, <, 250) && this.origin.z - destorg.z > 120 + && (!IS_ONGROUND(this) || vdist(vec2(this.velocity), >, maxspeed * 0.3))) + { + // tracebox wouldn't work when bot is still on the ledge + traceline(this.origin, this.origin - '0 0 200', true, this); + if (this.origin.z - trace_endpos.z > 120) + evadeobstacle = normalize(this.velocity) * -1; + } + if(unreachable) { navigation_clearroute(this); @@ -1075,31 +1139,64 @@ void havocbot_movetogoal(entity this) } dodge = havocbot_dodge(this); - dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1); + if (dodge) + dodge *= bound(0, 0.5 + (skill + this.bot_dodgeskill) * 0.1, 1); + dodge += evadeobstacle + evadelava; evadelava = evadelava * bound(1,3-(skill+this.bot_dodgeskill),3); //Noobs fear lava a lot and take more distance from it - traceline(this.origin, ( ( this.enemy.absmin + this.enemy.absmax ) * 0.5 ), true, NULL); - if(IS_PLAYER(trace_ent)) - dir = dir * bound(0,(skill+this.bot_dodgeskill)/7,1); - - dir = normalize(dir + dodge + evadeobstacle + evadelava); + if (this.enemy) + { + traceline(this.origin, (this.enemy.absmin + this.enemy.absmax) * 0.5, true, NULL); + if (IS_PLAYER(trace_ent)) + dodge_enemy_factor = bound(0, (skill + this.bot_dodgeskill) / 7, 1); + } // this.bot_dodgevector = dir; // this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this); } + float ladder_zdir = 0; if(time < this.ladder_time) { if(this.goalcurrent.origin.z + this.goalcurrent.mins.z > this.origin.z + this.mins.z) { if(this.origin.z + this.mins.z < this.ladder_entity.origin.z + this.ladder_entity.maxs.z) - dir.z = 1; + ladder_zdir = 1; } else { if(this.origin.z + this.mins.z > this.ladder_entity.origin.z + this.ladder_entity.mins.z) - dir.z = -1; + ladder_zdir = -1; + } + if (ladder_zdir) + { + if (vdist(flatdir, <, 15)) + dir = ladder_zdir * '0 0 1'; + else + { + dir.z = ladder_zdir * 1.3; + dir = normalize(dir); + } } } + if (this.goalcurrent.wpisbox + && boxesoverlap(this.goalcurrent.absmin, this.goalcurrent.absmax, this.origin, this.origin)) + { + // bot is inside teleport waypoint but hasn't touched the real teleport yet + // head to teleport origin + dir = (this.goalcurrent.origin - this.origin); + dir.z = 0; + dir = normalize(dir); + } + + if (!this.bot_aimdir_executed) + bot_aimdir(this, dir, -1); + + if (!ladder_zdir) + { + dir *= dodge_enemy_factor; + dir = normalize(dir + dodge); + } + //dir = this.bot_dodgevector; //if (this.bot_dodgevector_jumpbutton) // PHYS_INPUT_BUTTON_JUMP(this) = true; @@ -1167,7 +1264,7 @@ void havocbot_chooseenemy(entity this) traceline(this.origin+this.view_ofs, ( this.enemy.absmin + this.enemy.absmax ) * 0.5,false,NULL); if (trace_ent == this.enemy || trace_fraction == 1) if (vdist(((this.enemy.absmin + this.enemy.absmax) * 0.5) - this.origin, <, 1000)) - if (this.health > 30) + if (GetResourceAmount(this, RESOURCE_HEALTH) > 30) { // remain tracking him for a shot while (case he went after a small corner or pilar this.havocbot_chooseenemy_finished = time + 0.5; @@ -1238,7 +1335,7 @@ LABEL(scan_targets) // I want to do a second scan if no enemy was found or I don't have weapons // TODO: Perform the scan when using the rifle (requires changes on the rifle code) - if(best || this.weapons) // || this.weapon == WEP_RIFLE.m_id + if(best || STAT(WEAPONS, this)) // || this.weapon == WEP_RIFLE.m_id break; if(scan_transparent) break; @@ -1315,7 +1412,7 @@ void havocbot_chooseweapon(entity this, .entity weaponentity) // Should it do a weapon combo? float af, ct, combo_time, combo; - af = ATTACK_FINISHED(this, 0); + af = ATTACK_FINISHED(this, weaponentity); ct = autocvar_bot_ai_weapon_combo_threshold; // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos @@ -1453,15 +1550,19 @@ float havocbot_moveto(entity this, vector pos) if(autocvar_bot_debug_goalstack) debuggoalstack(this); - // Heading - vector dir = get_closer_dest(this.goalcurrent, this.origin); - dir = dir - (this.origin + this.view_ofs); - dir.z = 0; - bot_aimdir(this, dir, -1); // Go! havocbot_movetogoal(this); + if (!this.bot_aimdir_executed && this.goalcurrent) + { + // Heading + vector dir = get_closer_dest(this.goalcurrent, this.origin); + dir -= this.origin + this.view_ofs; + dir.z = 0; + bot_aimdir(this, dir, -1); + } + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED) { // Step 5: Waypoint reached @@ -1535,6 +1636,7 @@ void havocbot_setupbot(entity this) this.cmd_moveto = havocbot_moveto; this.cmd_resetgoal = havocbot_resetgoal; + // NOTE: bot is not player yet havocbot_chooserole(this); } diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qh b/qcsrc/server/bot/default/havocbot/havocbot.qh index 2f987f674e..b3c0c3ea79 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qh +++ b/qcsrc/server/bot/default/havocbot/havocbot.qh @@ -61,5 +61,3 @@ void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrat */ .entity draggedby; -.float ladder_time; -.entity ladder_entity; diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc index e469436014..1d66df0966 100644 --- a/qcsrc/server/bot/default/havocbot/roles.qc +++ b/qcsrc/server/bot/default/havocbot/roles.qc @@ -3,6 +3,7 @@ #include <server/defs.qh> #include <server/miscfunctions.qh> #include <server/items.qh> +#include <server/resources.qh> #include "havocbot.qh" #include "../cvars.qh" @@ -49,14 +50,14 @@ void havocbot_goalrating_waypoints(entity this, float ratingscale, vector org, f bool havocbot_goalrating_item_can_be_left_to_teammate(entity this, entity player, entity item) { - if (item.health && player.health <= this.health) {return true;} - if (item.armorvalue && player.armorvalue <= this.armorvalue) {return true;} - if (item.weapons && !(player.weapons & item.weapons)) {return true;} - if (item.ammo_shells && player.ammo_shells <= this.ammo_shells) {return true;} - if (item.ammo_nails && player.ammo_nails <= this.ammo_nails) {return true;} - if (item.ammo_rockets && player.ammo_rockets <= this.ammo_rockets) {return true;} - if (item.ammo_cells && player.ammo_cells <= this.ammo_cells) {return true;} - if (item.ammo_plasma && player.ammo_plasma <= this.ammo_plasma) {return true;} + if (GetResourceAmount(item, RESOURCE_HEALTH) && GetResourceAmount(player, RESOURCE_HEALTH) <= GetResourceAmount(this, RESOURCE_HEALTH)) {return true;} + if (GetResourceAmount(item, RESOURCE_ARMOR) && GetResourceAmount(player, RESOURCE_ARMOR) <= GetResourceAmount(this, RESOURCE_ARMOR)) {return true;} + if (STAT(WEAPONS, item) && !(STAT(WEAPONS, player) & STAT(WEAPONS, item))) {return true;} + if (GetResourceAmount(item, RESOURCE_SHELLS) && GetResourceAmount(player, RESOURCE_SHELLS) <= GetResourceAmount(this, RESOURCE_SHELLS)) {return true;} + if (GetResourceAmount(item, RESOURCE_BULLETS) && GetResourceAmount(player, RESOURCE_BULLETS) <= GetResourceAmount(this, RESOURCE_BULLETS)) {return true;} + if (GetResourceAmount(item, RESOURCE_ROCKETS) && GetResourceAmount(player, RESOURCE_ROCKETS) <= GetResourceAmount(this, RESOURCE_ROCKETS)) {return true;} + if (GetResourceAmount(item, RESOURCE_CELLS) && GetResourceAmount(player, RESOURCE_CELLS) <= GetResourceAmount(this, RESOURCE_CELLS)) {return true;} + if (GetResourceAmount(item, RESOURCE_PLASMA) && GetResourceAmount(player, RESOURCE_PLASMA) <= GetResourceAmount(this, RESOURCE_PLASMA)) {return true;} if (item.itemdef.instanceOfPowerup) {return true;} return false; @@ -72,7 +73,7 @@ bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, en float enemy_distance = FLOAT_MAX; float dist; - FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), + FOREACH_CLIENT(IS_PLAYER(it) && it != this && !(IS_DEAD(it) || STAT(FROZEN, it)), { if (it.team == this.team) { @@ -112,7 +113,7 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float { float rating; vector o; - ratingscale = ratingscale * 0.0001; // items are rated around 10000 already + ratingscale = ratingscale * 0.0001; IL_EACH(g_items, it.bot_pickup, { @@ -120,8 +121,6 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float // NOTE: this code assumes each bot rates items in a different frame if(it.bot_ratingscale_time == time && ratingscale < it.bot_ratingscale) continue; - it.bot_ratingscale_time = time; - it.bot_ratingscale = ratingscale; if(!it.solid) { @@ -174,6 +173,8 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float if(!havocbot_goalrating_item_pickable_check_players(this, org, it, o)) continue; + it.bot_ratingscale_time = time; + it.bot_ratingscale = ratingscale; rating = it.bot_pickupevalfunc(this, it); if(rating > 0) navigation_routerating(this, it, rating * ratingscale, 2000); @@ -190,7 +191,7 @@ void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org if(this.waterlevel>WATERLEVEL_WETFEET) return; - ratingscale = ratingscale * 0.00005; // enemies are rated around 20000 already + ratingscale = ratingscale * 0.0001; float t; FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), { @@ -207,7 +208,7 @@ void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org continue; */ - t = ((this.health + this.armorvalue) - (it.health + it.armorvalue)) / 150; + t = ((GetResourceAmount(this, RESOURCE_HEALTH) + GetResourceAmount(this, RESOURCE_ARMOR)) - (GetResourceAmount(it, RESOURCE_HEALTH) + GetResourceAmount(it, RESOURCE_ARMOR))) / 150; t = bound(0, 1 + t, 3); if (skill > 3) { @@ -234,7 +235,7 @@ void havocbot_role_generic(entity this) { navigation_goalrating_start(this); havocbot_goalrating_items(this, 10000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 10000, this.origin, 10000); havocbot_goalrating_waypoints(this, 1, this.origin, 3000); navigation_goalrating_end(this); diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc index 7b80a1a6f2..8fe72ff637 100644 --- a/qcsrc/server/bot/default/navigation.qc +++ b/qcsrc/server/bot/default/navigation.qc @@ -13,7 +13,8 @@ #include <common/constants.qh> #include <common/net_linked.qh> -#include <common/triggers/trigger/jumppads.qh> +#include <common/mapobjects/func/ladder.qh> +#include <common/mapobjects/trigger/jumppads.qh> .float speed; @@ -49,12 +50,15 @@ bool navigation_goalrating_timeout(entity this) #define MAX_CHASE_DISTANCE 700 bool navigation_goalrating_timeout_can_be_anticipated(entity this) { - if(time > this.bot_strategytime - (IS_MOVABLE(this.goalentity) ? 3 : 2)) + vector gco = (this.goalentity.absmin + this.goalentity.absmax) * 0.5; + if (vdist(gco - this.origin, >, autocvar_sv_maxspeed * 1.5) + && time > this.bot_strategytime - (IS_MOVABLE(this.goalentity) ? 3 : 2)) + { return true; + } if (this.goalentity.bot_pickup && time > this.bot_strategytime - 5) { - vector gco = (this.goalentity.absmin + this.goalentity.absmax) * 0.5; if(!havocbot_goalrating_item_pickable_check_players(this, this.origin, this.goalentity, gco)) { this.ignoregoal = this.goalentity; @@ -75,9 +79,11 @@ void navigation_dynamicgoal_init(entity this, bool initially_static) this.nearestwaypointtimeout = time; } -void navigation_dynamicgoal_set(entity this) +void navigation_dynamicgoal_set(entity this, entity dropper) { this.nearestwaypointtimeout = time; + if (dropper && dropper.nearestwaypointtimeout && dropper.nearestwaypointtimeout < time + 2) + this.nearestwaypoint = dropper.nearestwaypoint; if (this.nearestwaypoint) this.nearestwaypointtimeout += 2; } @@ -123,8 +129,18 @@ void set_tracewalk_dest(entity ent, vector org, bool fix_player_dest) // z coord is set to ent's min height tracewalk_dest.x = bound(wm1.x, org.x, wm2.x); tracewalk_dest.y = bound(wm1.y, org.y, wm2.y); - tracewalk_dest.z = wm1.z; - tracewalk_dest_height = wm2.z - wm1.z; // destination height + if ((IS_PLAYER(ent) || IS_MONSTER(ent)) + && org.x == tracewalk_dest.x && org.y == tracewalk_dest.y && org.z > tracewalk_dest.z) + { + tracewalk_dest.z = wm2.z - PL_MIN_CONST.z; + tracewalk_dest_height = 0; + fix_player_dest = false; + } + else + { + tracewalk_dest.z = wm1.z; + tracewalk_dest_height = wm2.z - wm1.z; + } } else { @@ -246,6 +262,7 @@ vector resurface_limited(vector org, float lim, vector m1) // rough simulation of walking from one point to another to test if a path // can be traveled, used for waypoint linking and havocbot // if end_height is > 0 destination is any point in the vertical segment [end, end + end_height * eZ] +// INFO: the command sv_cmd trace walk is useful to test this function in game bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float end_height, float movemode) { if(autocvar_bot_debug_tracewalk) @@ -264,8 +281,7 @@ bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float e int nav_action; // Analyze starting point - traceline(start, start, MOVE_NORMAL, e); - if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) + if (IN_LAVA(start)) ignorehazards = true; tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e); @@ -731,6 +747,7 @@ void navigation_clearroute(entity this) this.goalcurrent_distance_z = FLOAT_MAX; this.goalcurrent_distance_time = 0; this.goalentity_lock_timeout = 0; + this.goalentity_shouldbefrozen = false; this.goalentity = NULL; this.goalcurrent = NULL; this.goalstack01 = NULL; @@ -899,7 +916,7 @@ entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfrom vector pm2 = ent.origin + ent.maxs; // do two scans, because box test is cheaper - IL_EACH(g_waypoints, it != ent && it != except, + IL_EACH(g_waypoints, it != ent && it != except && !(it.wpflags & WAYPOINTFLAG_TELEPORT), { if(boxesoverlap(pm1, pm2, it.absmin, it.absmax)) { @@ -1201,11 +1218,7 @@ void navigation_markroutes_inverted(entity fixed_source_waypoint) // updates the best goal according to a weighted calculation of travel cost and item value of a new proposed item void navigation_routerating(entity this, entity e, float f, float rangebias) { - if (!e) - return; - - if(e.blacklisted) - return; + if (!e || e.blacklisted) { return; } rangebias = waypoint_getlinearcost(rangebias); f = waypoint_getlinearcost(f); @@ -1213,8 +1226,11 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) if (IS_PLAYER(e)) { bool rate_wps = false; - if((e.flags & FL_INWATER) || (e.flags & FL_PARTIALGROUND)) + if (e.watertype < CONTENT_WATER || (e.waterlevel > WATERLEVEL_WETFEET && !STAT(FROZEN, e)) + || (e.flags & FL_PARTIALGROUND)) + { rate_wps = true; + } if(!IS_ONGROUND(e)) { @@ -1233,12 +1249,13 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) { entity theEnemy = e; entity best_wp = NULL; - float best_dist = 10000; - IL_EACH(g_waypoints, vdist(it.origin - theEnemy.origin, <, 500) + float best_dist = FLOAT_MAX; + IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_TELEPORT) + && vdist(it.origin - theEnemy.origin, <, 500) && vdist(it.origin - this.origin, >, 100) - && !(it.wpflags & WAYPOINTFLAG_TELEPORT), + && vdist(it.origin - this.origin, <, 10000), { - float dist = vlen(it.origin - theEnemy.origin); + float dist = vlen2(it.origin - theEnemy.origin); if (dist < best_dist) { best_wp = it; @@ -1256,7 +1273,6 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) //print("routerating ", etos(e), " = ", ftos(f), " - ", ftos(rangebias), "\n"); // Evaluate path using jetpack - if(g_jetpack) if(this.items & IT_JETPACK) if(autocvar_bot_ai_navigation_jetpack) if(vdist(this.origin - goal_org, >, autocvar_bot_ai_navigation_jetpack_mindistance)) @@ -1315,10 +1331,10 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) t += xydistance / autocvar_g_jetpack_maxspeed_side; fuel = t * autocvar_g_jetpack_fuel * 0.8; - LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel)); + LOG_DEBUG("jetpack ai: required fuel ", ftos(fuel), ", have ", ftos(GetResourceAmount(this, RESOURCE_FUEL))); // enough fuel ? - if(this.ammo_fuel>fuel) + if(GetResourceAmount(this, RESOURCE_FUEL) > fuel || (this.items & IT_UNLIMITED_WEAPON_AMMO)) { // Estimate cost // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship @@ -1387,7 +1403,6 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) nwp = e.nearestwaypoint; } - LOG_DEBUG("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")"); if (nwp && nwp.wpcost < 10000000) { //te_wizspike(nwp.wpnearestpoint); @@ -1397,12 +1412,12 @@ void navigation_routerating(entity this, entity e, float f, float rangebias) else nwptoitem_cost = waypoint_gettravelcost(nwp.wpnearestpoint, goal_org, nwp, e); float cost = nwp.wpcost + nwptoitem_cost; - LOG_DEBUG(e.classname, " ", ftos(f), "/(1+", ftos(cost), "/", ftos(rangebias), ") = "); + LOG_DEBUG("checking ^5", e.classname, "^7 with base rating ^xf04", ftos(f), "^7 and rangebias ^xf40", ftos(rangebias)); f = f * rangebias / (rangebias + cost); - LOG_DEBUG("considering ", e.classname, " (with rating ", ftos(f), ")"); + LOG_DEBUG(" ^5", e.classname, "^7 with cost ^6", ftos(cost), "^7 and final rating ^2", ftos(f)); if (navigation_bestrating < f) { - LOG_DEBUG("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")"); + LOG_DEBUG(" ground path: ^3added goal ^5", e.classname); navigation_bestrating = f; navigation_bestgoal = e; } @@ -1509,12 +1524,12 @@ bool navigation_routetogoal(entity this, entity e, vector startposition) } // shorten path by removing intermediate goals -void navigation_shortenpath(entity this) +bool navigation_shortenpath(entity this) { if (!this.goalstack01 || wasfreed(this.goalstack01)) - return; + return false; if (this.bot_tracewalk_time > time) - return; + return false; this.bot_tracewalk_time = max(time, this.bot_tracewalk_time) + 0.25; bool cut_allowed = false; @@ -1553,8 +1568,9 @@ void navigation_shortenpath(entity this) navigation_poproute(this); } while (this.goalcurrent != next); + return true; } - return; + return false; } } @@ -1573,10 +1589,12 @@ void navigation_shortenpath(entity this) if (trace_ent == this || tracewalk(this, this.origin, this.mins, this.maxs, tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { - LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue"); + LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue"); navigation_poproute(this); + return true; } } + return false; } // removes any currently touching waypoints from the goal stack @@ -1590,6 +1608,16 @@ int navigation_poptouchedgoals(entity this) if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) { + if (!this.goalcurrent.wpisbox // warpzone + && vlen2(this.origin - this.goalstack01.origin) < vlen2(this.origin - this.goalcurrent.origin)) + { + navigation_poproute(this); + ++removed_goals; + navigation_poproute(this); + ++removed_goals; + return removed_goals; + } + // make sure jumppad is really hit, don't rely on distance based checks // as they may report a touch even if it didn't really happen if(this.lastteleporttime > 0 && TELEPORT_USED(this, this.goalcurrent)) @@ -1600,6 +1628,16 @@ int navigation_poptouchedgoals(entity this) this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; } + if(this.jumppadcount) + { + // remove jumppad waypoint after a random delay to prevent bots getting + // stuck on certain jumppads that require an extra initial horizontal speed + float max_delay = 0.1; + if (vdist(vec2(this.velocity), >, 2 * autocvar_sv_maxspeed)) + max_delay = 0.05; + if (time - this.lastteleporttime < random() * max_delay) + return removed_goals; + } navigation_poproute(this); this.lastteleporttime = 0; ++removed_goals; @@ -1674,8 +1712,16 @@ int navigation_poptouchedgoals(entity this) gc_min = this.goalcurrent.origin - '1 1 1' * 12; gc_max = this.goalcurrent.origin + '1 1 1' * 12; } - if(!boxesoverlap(this.absmin, this.absmax, gc_min, gc_max)) - break; + if (time < this.ladder_time) + { + if (!boxesoverlap(this.absmin, this.absmax - eZ * STAT(PL_MAX, this).z, gc_min, gc_max)) + break; + } + else + { + if (!boxesoverlap(this.absmin, this.absmax, gc_min, gc_max)) + break; + } // Detect personal waypoints if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) @@ -1770,6 +1816,7 @@ void navigation_goalrating_end(entity this) this.aistatus |= AI_STATUS_STUCK; } } + this.goalentity_shouldbefrozen = boolean(STAT(FROZEN, this.goalentity)); } void botframe_updatedangerousobjects(float maxupdate) @@ -1839,7 +1886,7 @@ void navigation_unstuck(entity this) float d = vlen2(this.origin - bot_waypoint_queue_goal.origin); LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d)); set_tracewalk_dest(bot_waypoint_queue_goal, this.origin, false); - if (tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), + if (tracewalk(this, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), tracewalk_dest, tracewalk_dest_height, bot_navigation_movemode)) { if( d > bot_waypoint_queue_bestgoalrating) @@ -1848,6 +1895,17 @@ void navigation_unstuck(entity this) bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal; } } + + // move to a random waypoint while bot is searching for a walkable path; + // this is usually sufficient to unstuck bots from bad spots or when other + // bots of the same team block all their ways + if (!bot_waypoint_queue_bestgoal && (!this.goalentity || random() < 0.1)) + { + navigation_clearroute(this); + navigation_routetogoal(this, bot_waypoint_queue_goal, this.origin); + navigation_goalrating_timeout_expire(this, 1 + random() * 2); + } + bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal; if (!bot_waypoint_queue_goal) @@ -1855,6 +1913,7 @@ void navigation_unstuck(entity this) if (bot_waypoint_queue_bestgoal) { LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it"); + navigation_clearroute(this); navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin); navigation_goalrating_timeout_set(this); this.aistatus &= ~AI_STATUS_STUCK; diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh index f3103cc4fc..b8b067c3b9 100644 --- a/qcsrc/server/bot/default/navigation.qh +++ b/qcsrc/server/bot/default/navigation.qh @@ -30,6 +30,7 @@ entity navigation_bestgoal; .float goalcurrent_distance_time; .float goalentity_lock_timeout; +.bool goalentity_shouldbefrozen; .entity nearestwaypoint; .float nearestwaypointtimeout; @@ -82,7 +83,7 @@ float bot_waypoint_queue_bestgoalrating; .entity bot_basewaypoint; .bool navigation_dynamicgoal; void navigation_dynamicgoal_init(entity this, bool initially_static); -void navigation_dynamicgoal_set(entity this); +void navigation_dynamicgoal_set(entity this, entity dropper); void navigation_dynamicgoal_unset(entity this); .int nav_submerged_state; @@ -114,7 +115,7 @@ void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vecto void navigation_markroutes(entity this, entity fixed_source_waypoint); void navigation_markroutes_inverted(entity fixed_source_waypoint); void navigation_routerating(entity this, entity e, float f, float rangebias); -void navigation_shortenpath(entity this); +bool navigation_shortenpath(entity this); int navigation_poptouchedgoals(entity this); void navigation_goalrating_start(entity this); void navigation_goalrating_end(entity this); diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc index e69050beb8..555f6fc58a 100644 --- a/qcsrc/server/bot/default/scripting.qc +++ b/qcsrc/server/bot/default/scripting.qc @@ -308,9 +308,7 @@ float bot_decodecommand(string cmdstring) bot_cmd.bot_cmd_parm_float = stof(parm); break; case BOT_CMD_PARAMETER_STRING: - if(bot_cmd.bot_cmd_parm_string) - strunzone(bot_cmd.bot_cmd_parm_string); - bot_cmd.bot_cmd_parm_string = strzone(parm); + strcpy(bot_cmd.bot_cmd_parm_string, parm); break; case BOT_CMD_PARAMETER_VECTOR: if(substring(parm, 0, 1) != "\'") @@ -622,10 +620,11 @@ float bot_cmd_eval(entity this, string expr) return cvar(substring(expr, 5, strlen(expr))); // Search for fields + // TODO: expand with support for more fields (key carrier, ball carrier, armor etc) switch(expr) { case "health": - return this.health; + return GetResourceAmount(this, RESOURCE_HEALTH); case "speed": return vlen(this.velocity); case "flagcarrier": @@ -1085,12 +1084,12 @@ float bot_cmd_debug_assert_canfire(entity this) LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by weaponentity state"); } } - else if(ATTACK_FINISHED(this, slot) > time) + else if(ATTACK_FINISHED(this, weaponentity) > time) { if(f) { this.colormod = '8 0 8'; - LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)"); + LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left)"); } } else if(this.(weaponentity).tuba_note) @@ -1106,7 +1105,7 @@ float bot_cmd_debug_assert_canfire(entity this) if(!f) { this.colormod = '8 8 0'; - LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left"); + LOG_INFO("Bot ", this.netname, " using ", this.(weaponentity).weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, weaponentity) - time), " seconds left"); } } @@ -1170,8 +1169,7 @@ void bot_resetqueues() it.bot_barrier = 0; for(int i = 0; i < it.bot_places_count; ++i) { - strunzone(it.(bot_placenames[i])); - it.(bot_placenames[i]) = string_null; + strfree(it.(bot_placenames[i])); } it.bot_places_count = 0; }); diff --git a/qcsrc/server/bot/default/waypoints.qc b/qcsrc/server/bot/default/waypoints.qc index fa82d926b6..182b1b3d39 100644 --- a/qcsrc/server/bot/default/waypoints.qc +++ b/qcsrc/server/bot/default/waypoints.qc @@ -128,7 +128,7 @@ void waypoint_unreachable(entity pl) if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j); } -vector waypoint_getSymmetricalOrigin(vector org, int ctf_flags) +vector waypoint_getSymmetricalPoint(vector org, int ctf_flags) { vector new_org = org; if (fabs(autocvar_g_waypointeditor_symmetrical) == 1) @@ -141,8 +141,8 @@ vector waypoint_getSymmetricalOrigin(vector org, int ctf_flags) } else if (fabs(autocvar_g_waypointeditor_symmetrical) == 2) { - float m = havocbot_symmetryaxis_equation.x; - float q = havocbot_symmetryaxis_equation.y; + float m = havocbot_symmetry_axis_m; + float q = havocbot_symmetry_axis_q; if (autocvar_g_waypointeditor_symmetrical == -2) { m = autocvar_g_waypointeditor_symmetrical_axis.x; @@ -190,6 +190,16 @@ entity waypoint_spawn(vector m1, vector m2, float f) return it; }); } + // spawn only one destination waypoint for teleports teleporting player to the exact same spot + // otherwise links loaded from file would be applied only to the first destination + // waypoint since link format doesn't specify waypoint entities but just positions + if((f & WAYPOINTFLAG_GENERATED) && !(f & WAYPOINTFLAG_NORELINK) && m1 == m2) + { + IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax), + { + return it; + }); + } entity w = new(waypoint); IL_PUSH(g_waypoints, w); @@ -236,15 +246,14 @@ void waypoint_spawn_fromeditor(entity pl) { entity e; vector org = pl.origin; - int ctf_flags = havocbot_symmetryaxis_equation.z; + int ctf_flags = havocbot_symmetry_origin_order; bool sym = ((autocvar_g_waypointeditor_symmetrical > 0 && ctf_flags >= 2) || (autocvar_g_waypointeditor_symmetrical < 0)); - int order = ctf_flags; if(autocvar_g_waypointeditor_symmetrical_order >= 2) - { - order = autocvar_g_waypointeditor_symmetrical_order; - ctf_flags = order; - } + ctf_flags = autocvar_g_waypointeditor_symmetrical_order; + if (sym && ctf_flags < 2) + ctf_flags = 2; + int wp_num = ctf_flags; if(!PHYS_INPUT_BUTTON_CROUCH(pl)) { @@ -253,7 +262,7 @@ void waypoint_spawn_fromeditor(entity pl) { vector item_org = (it.absmin + it.absmax) * 0.5; item_org.z = it.absmin.z - PL_MIN_CONST.z; - if(vlen(item_org - org) < 30) + if (vlen(item_org - org) < 20) { org = item_org; break; @@ -272,11 +281,11 @@ void waypoint_spawn_fromeditor(entity pl) bprint(strcat("Waypoint spawned at ", vtos(e.origin), "\n")); if(sym) { - org = waypoint_getSymmetricalOrigin(e.origin, ctf_flags); + org = waypoint_getSymmetricalPoint(e.origin, ctf_flags); if (vdist(org - pl.origin, >, 32)) { - if(order > 2) - order--; + if(wp_num > 2) + wp_num--; else sym = false; goto add_wp; @@ -286,7 +295,6 @@ void waypoint_spawn_fromeditor(entity pl) void waypoint_remove(entity wp) { - // tell all waypoints linked to wp that they need to relink IL_EACH(g_waypoints, it != wp, { if (waypoint_islinked(it, wp)) @@ -299,15 +307,14 @@ void waypoint_remove_fromeditor(entity pl) { entity e = navigation_findnearestwaypoint(pl, false); - int ctf_flags = havocbot_symmetryaxis_equation.z; + int ctf_flags = havocbot_symmetry_origin_order; bool sym = ((autocvar_g_waypointeditor_symmetrical > 0 && ctf_flags >= 2) || (autocvar_g_waypointeditor_symmetrical < 0)); - int order = ctf_flags; if(autocvar_g_waypointeditor_symmetrical_order >= 2) - { - order = autocvar_g_waypointeditor_symmetrical_order; - ctf_flags = order; - } + ctf_flags = autocvar_g_waypointeditor_symmetrical_order; + if (sym && ctf_flags < 2) + ctf_flags = 2; + int wp_num = ctf_flags; LABEL(remove_wp); if (!e) return; @@ -322,7 +329,7 @@ void waypoint_remove_fromeditor(entity pl) entity wp_sym = NULL; if (sym) { - vector org = waypoint_getSymmetricalOrigin(e.origin, ctf_flags); + vector org = waypoint_getSymmetricalPoint(e.origin, ctf_flags); FOREACH_ENTITY_CLASS("waypoint", !(it.wpflags & WAYPOINTFLAG_GENERATED), { if(vdist(org - it.origin, <, 3)) { @@ -338,8 +345,8 @@ void waypoint_remove_fromeditor(entity pl) if (sym && wp_sym) { e = wp_sym; - if(order > 2) - order--; + if(wp_num > 2) + wp_num--; else sym = false; goto remove_wp; @@ -351,52 +358,65 @@ void waypoint_removelink(entity from, entity to) if (from == to || (from.wpflags & WAYPOINTFLAG_NORELINK)) return; - bool found = false; - if (!found && from.wp00 == to) found = true; if (found) {from.wp00 = from.wp01; from.wp00mincost = from.wp01mincost;} - if (!found && from.wp01 == to) found = true; if (found) {from.wp01 = from.wp02; from.wp01mincost = from.wp02mincost;} - if (!found && from.wp02 == to) found = true; if (found) {from.wp02 = from.wp03; from.wp02mincost = from.wp03mincost;} - if (!found && from.wp03 == to) found = true; if (found) {from.wp03 = from.wp04; from.wp03mincost = from.wp04mincost;} - if (!found && from.wp04 == to) found = true; if (found) {from.wp04 = from.wp05; from.wp04mincost = from.wp05mincost;} - if (!found && from.wp05 == to) found = true; if (found) {from.wp05 = from.wp06; from.wp05mincost = from.wp06mincost;} - if (!found && from.wp06 == to) found = true; if (found) {from.wp06 = from.wp07; from.wp06mincost = from.wp07mincost;} - if (!found && from.wp07 == to) found = true; if (found) {from.wp07 = from.wp08; from.wp07mincost = from.wp08mincost;} - if (!found && from.wp08 == to) found = true; if (found) {from.wp08 = from.wp09; from.wp08mincost = from.wp09mincost;} - if (!found && from.wp09 == to) found = true; if (found) {from.wp09 = from.wp10; from.wp09mincost = from.wp10mincost;} - if (!found && from.wp10 == to) found = true; if (found) {from.wp10 = from.wp11; from.wp10mincost = from.wp11mincost;} - if (!found && from.wp11 == to) found = true; if (found) {from.wp11 = from.wp12; from.wp11mincost = from.wp12mincost;} - if (!found && from.wp12 == to) found = true; if (found) {from.wp12 = from.wp13; from.wp12mincost = from.wp13mincost;} - if (!found && from.wp13 == to) found = true; if (found) {from.wp13 = from.wp14; from.wp13mincost = from.wp14mincost;} - if (!found && from.wp14 == to) found = true; if (found) {from.wp14 = from.wp15; from.wp14mincost = from.wp15mincost;} - if (!found && from.wp15 == to) found = true; if (found) {from.wp15 = from.wp16; from.wp15mincost = from.wp16mincost;} - if (!found && from.wp16 == to) found = true; if (found) {from.wp16 = from.wp17; from.wp16mincost = from.wp17mincost;} - if (!found && from.wp17 == to) found = true; if (found) {from.wp17 = from.wp18; from.wp17mincost = from.wp18mincost;} - if (!found && from.wp18 == to) found = true; if (found) {from.wp18 = from.wp19; from.wp18mincost = from.wp19mincost;} - if (!found && from.wp19 == to) found = true; if (found) {from.wp19 = from.wp20; from.wp19mincost = from.wp20mincost;} - if (!found && from.wp20 == to) found = true; if (found) {from.wp20 = from.wp21; from.wp20mincost = from.wp21mincost;} - if (!found && from.wp21 == to) found = true; if (found) {from.wp21 = from.wp22; from.wp21mincost = from.wp22mincost;} - if (!found && from.wp22 == to) found = true; if (found) {from.wp22 = from.wp23; from.wp22mincost = from.wp23mincost;} - if (!found && from.wp23 == to) found = true; if (found) {from.wp23 = from.wp24; from.wp23mincost = from.wp24mincost;} - if (!found && from.wp24 == to) found = true; if (found) {from.wp24 = from.wp25; from.wp24mincost = from.wp25mincost;} - if (!found && from.wp25 == to) found = true; if (found) {from.wp25 = from.wp26; from.wp25mincost = from.wp26mincost;} - if (!found && from.wp26 == to) found = true; if (found) {from.wp26 = from.wp27; from.wp26mincost = from.wp27mincost;} - if (!found && from.wp27 == to) found = true; if (found) {from.wp27 = from.wp28; from.wp27mincost = from.wp28mincost;} - if (!found && from.wp28 == to) found = true; if (found) {from.wp28 = from.wp29; from.wp28mincost = from.wp29mincost;} - if (!found && from.wp29 == to) found = true; if (found) {from.wp29 = from.wp30; from.wp29mincost = from.wp30mincost;} - if (!found && from.wp30 == to) found = true; if (found) {from.wp30 = from.wp31; from.wp30mincost = from.wp31mincost;} - if (found) {from.wp31 = NULL; from.wp31mincost = 10000000;} + entity fromwp31_prev = from.wp31; + + switch (waypoint_getlinknum(from, to)) + { + // fallthrough all the way + case 0: from.wp00 = from.wp01; from.wp00mincost = from.wp01mincost; + case 1: from.wp01 = from.wp02; from.wp01mincost = from.wp02mincost; + case 2: from.wp02 = from.wp03; from.wp02mincost = from.wp03mincost; + case 3: from.wp03 = from.wp04; from.wp03mincost = from.wp04mincost; + case 4: from.wp04 = from.wp05; from.wp04mincost = from.wp05mincost; + case 5: from.wp05 = from.wp06; from.wp05mincost = from.wp06mincost; + case 6: from.wp06 = from.wp07; from.wp06mincost = from.wp07mincost; + case 7: from.wp07 = from.wp08; from.wp07mincost = from.wp08mincost; + case 8: from.wp08 = from.wp09; from.wp08mincost = from.wp09mincost; + case 9: from.wp09 = from.wp10; from.wp09mincost = from.wp10mincost; + case 10: from.wp10 = from.wp11; from.wp10mincost = from.wp11mincost; + case 11: from.wp11 = from.wp12; from.wp11mincost = from.wp12mincost; + case 12: from.wp12 = from.wp13; from.wp12mincost = from.wp13mincost; + case 13: from.wp13 = from.wp14; from.wp13mincost = from.wp14mincost; + case 14: from.wp14 = from.wp15; from.wp14mincost = from.wp15mincost; + case 15: from.wp15 = from.wp16; from.wp15mincost = from.wp16mincost; + case 16: from.wp16 = from.wp17; from.wp16mincost = from.wp17mincost; + case 17: from.wp17 = from.wp18; from.wp17mincost = from.wp18mincost; + case 18: from.wp18 = from.wp19; from.wp18mincost = from.wp19mincost; + case 19: from.wp19 = from.wp20; from.wp19mincost = from.wp20mincost; + case 20: from.wp20 = from.wp21; from.wp20mincost = from.wp21mincost; + case 21: from.wp21 = from.wp22; from.wp21mincost = from.wp22mincost; + case 22: from.wp22 = from.wp23; from.wp22mincost = from.wp23mincost; + case 23: from.wp23 = from.wp24; from.wp23mincost = from.wp24mincost; + case 24: from.wp24 = from.wp25; from.wp24mincost = from.wp25mincost; + case 25: from.wp25 = from.wp26; from.wp25mincost = from.wp26mincost; + case 26: from.wp26 = from.wp27; from.wp26mincost = from.wp27mincost; + case 27: from.wp27 = from.wp28; from.wp27mincost = from.wp28mincost; + case 28: from.wp28 = from.wp29; from.wp28mincost = from.wp29mincost; + case 29: from.wp29 = from.wp30; from.wp29mincost = from.wp30mincost; + case 30: from.wp30 = from.wp31; from.wp30mincost = from.wp31mincost; + case 31: from.wp31 = NULL; from.wp31mincost = 10000000; + } + + if (fromwp31_prev && !from.wp31) + waypoint_schedulerelink(from); +} + +int waypoint_getlinknum(entity from, entity to) +{ + if (from.wp00 == to) return 0; if (from.wp01 == to) return 1; if (from.wp02 == to) return 2; if (from.wp03 == to) return 3; + if (from.wp04 == to) return 4; if (from.wp05 == to) return 5; if (from.wp06 == to) return 6; if (from.wp07 == to) return 7; + if (from.wp08 == to) return 8; if (from.wp09 == to) return 9; if (from.wp10 == to) return 10; if (from.wp11 == to) return 11; + if (from.wp12 == to) return 12; if (from.wp13 == to) return 13; if (from.wp14 == to) return 14; if (from.wp15 == to) return 15; + if (from.wp16 == to) return 16; if (from.wp17 == to) return 17; if (from.wp18 == to) return 18; if (from.wp19 == to) return 19; + if (from.wp20 == to) return 20; if (from.wp21 == to) return 21; if (from.wp22 == to) return 22; if (from.wp23 == to) return 23; + if (from.wp24 == to) return 24; if (from.wp25 == to) return 25; if (from.wp26 == to) return 26; if (from.wp27 == to) return 27; + if (from.wp28 == to) return 28; if (from.wp29 == to) return 29; if (from.wp30 == to) return 30; if (from.wp31 == to) return 31; + return -1; } bool waypoint_islinked(entity from, entity to) { - if (from.wp00 == to) return true;if (from.wp01 == to) return true;if (from.wp02 == to) return true;if (from.wp03 == to) return true; - if (from.wp04 == to) return true;if (from.wp05 == to) return true;if (from.wp06 == to) return true;if (from.wp07 == to) return true; - if (from.wp08 == to) return true;if (from.wp09 == to) return true;if (from.wp10 == to) return true;if (from.wp11 == to) return true; - if (from.wp12 == to) return true;if (from.wp13 == to) return true;if (from.wp14 == to) return true;if (from.wp15 == to) return true; - if (from.wp16 == to) return true;if (from.wp17 == to) return true;if (from.wp18 == to) return true;if (from.wp19 == to) return true; - if (from.wp20 == to) return true;if (from.wp21 == to) return true;if (from.wp22 == to) return true;if (from.wp23 == to) return true; - if (from.wp24 == to) return true;if (from.wp25 == to) return true;if (from.wp26 == to) return true;if (from.wp27 == to) return true; - if (from.wp28 == to) return true;if (from.wp29 == to) return true;if (from.wp30 == to) return true;if (from.wp31 == to) return true; - return false; + return (waypoint_getlinknum(from, to) >= 0); } void waypoint_updatecost_foralllinks() @@ -446,7 +466,7 @@ float waypoint_getlinearcost(float dist) } float waypoint_getlinearcost_underwater(float dist) { - // NOTE: this value is hardcoded on the engine too, see SV_WaterMove + // NOTE: underwater speed factor is hardcoded in the engine too, see SV_WaterMove return dist / (autocvar_sv_maxspeed * 0.7); } @@ -557,6 +577,9 @@ void waypoint_think(entity this) bot_calculate_stepheightvec(); + int dphitcontentsmask_save = this.dphitcontentsmask; + this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n"); @@ -609,7 +632,7 @@ void waypoint_think(entity this) relink_walkculled += 0.5; else { - if (tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv2, sv2_height, MOVE_NOMONSTERS)) + if (tracewalk(this, ev, PL_MIN_CONST, PL_MAX_CONST, sv2, sv2_height, MOVE_NOMONSTERS)) waypoint_addlink(it, this); else relink_walkculled += 0.5; @@ -618,6 +641,7 @@ void waypoint_think(entity this) }); navigation_testtracewalk = 0; this.wplinked = true; + this.dphitcontentsmask = dphitcontentsmask_save; } void waypoint_clearlinks(entity wp) @@ -710,6 +734,7 @@ bool waypoint_load_links() bool parse_comments = true; float ver = 0; + string links_time = string_null; while ((s = fgets(file))) { @@ -719,13 +744,18 @@ bool waypoint_load_links() { if(substring(s, 2, 17) == "WAYPOINT_VERSION ") ver = stof(substring(s, 19, -1)); + else if(substring(s, 2, 14) == "WAYPOINT_TIME ") + links_time = substring(s, 16, -1); continue; } else { - if(ver < WAYPOINT_VERSION) + if(ver < WAYPOINT_VERSION || links_time != waypoint_time) { - LOG_TRACE("waypoint links for this map are outdated."); + if (links_time != waypoint_time) + LOG_TRACE("waypoint links for this map are not made for these waypoints."); + else + LOG_TRACE("waypoint links for this map are outdated."); if (g_assault) { LOG_TRACE("Assault waypoint links need to be manually updated in the editor"); @@ -885,7 +915,7 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode) if(!found) { if(!removal_mode) - LOG_INFO("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped"); + LOG_INFO("NOTICE: Can not find origin waypoint for the hardwired link ", s, ". Path skipped"); continue; } } @@ -907,7 +937,7 @@ void waypoint_load_or_remove_links_hardwired(bool removal_mode) if(!found) { if(!removal_mode) - LOG_INFO("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped"); + LOG_INFO("NOTICE: Can not find destination waypoint for the hardwired link ", s, ". Path skipped"); continue; } @@ -988,6 +1018,8 @@ void waypoint_save_links() } fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n")); + if (waypoint_time != "") + fputs(file, strcat("//", "WAYPOINT_TIME ", waypoint_time, "\n")); int c = 0; IL_EACH(g_waypoints, true, @@ -997,6 +1029,7 @@ void waypoint_save_links() entity link = waypoint_get_link(it, j); if(link) { + // NOTE: vtos rounds vector components to 1 decimal place string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n"); fputs(file, s); ++c; @@ -1028,11 +1061,39 @@ void waypoint_saveall() return; } - // add 3 comments to not break compatibility with older Xonotic versions + float sym = autocvar_g_waypointeditor_symmetrical; + string sym_str = ftos(sym); + if (sym == -1 || (sym == 1 && autocvar_g_waypointeditor_symmetrical_order >= 2)) + { + if (sym == 1) + { + sym_str = cons(sym_str, "-"); + sym_str = cons(sym_str, "-"); + } + else + { + sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.x)); + sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_origin.y)); + } + if (autocvar_g_waypointeditor_symmetrical_order >= 2) + sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_order)); + } + else if (autocvar_g_waypointeditor_symmetrical == -2) + { + sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.x)); + sym_str = cons(sym_str, ftos(autocvar_g_waypointeditor_symmetrical_axis.y)); + } + + // a group of 3 comments doesn't break compatibility with older Xonotic versions // (they are read as a waypoint with origin '0 0 0' and flag 0 though) fputs(file, strcat("//", "WAYPOINT_VERSION ", ftos_decimals(WAYPOINT_VERSION, 2), "\n")); - fputs(file, strcat("//", "\n")); - fputs(file, strcat("//", "\n")); + fputs(file, strcat("//", "WAYPOINT_SYMMETRY ", sym_str, "\n")); + + strcpy(waypoint_time, strftime(true, "%Y-%m-%d %H:%M:%S")); + fputs(file, strcat("//", "WAYPOINT_TIME ", waypoint_time, "\n")); + //fputs(file, strcat("//", "\n")); + //fputs(file, strcat("//", "\n")); + //fputs(file, strcat("//", "\n")); int c = 0; IL_EACH(g_waypoints, true, @@ -1041,6 +1102,7 @@ void waypoint_saveall() continue; string s; + // NOTE: vtos rounds vector components to 1 decimal place s = strcat(vtos(it.origin + it.mins), "\n"); s = strcat(s, vtos(it.origin + it.maxs)); s = strcat(s, "\n"); @@ -1085,6 +1147,8 @@ float waypoint_loadall() bool parse_comments = true; float ver = 0; + float sym = 0; + float sym_param1 = 0, sym_param2 = 0, sym_param3 = 0; while ((s = fgets(file))) { @@ -1094,6 +1158,16 @@ float waypoint_loadall() { if(substring(s, 2, 17) == "WAYPOINT_VERSION ") ver = stof(substring(s, 19, -1)); + else if(substring(s, 2, 18) == "WAYPOINT_SYMMETRY ") + { + int tokens = tokenizebyseparator(substring(s, 20, -1), " "); + if (tokens) { sym = stof(argv(0)); } + if (tokens > 1) { sym_param1 = stof(argv(1)); } + if (tokens > 2) { sym_param2 = stof(argv(2)); } + if (tokens > 3) { sym_param3 = stof(argv(3)); } + } + else if(substring(s, 2, 14) == "WAYPOINT_TIME ") + strcpy(waypoint_time, substring(s, 16, -1)); continue; } else @@ -1124,6 +1198,35 @@ float waypoint_loadall() fclose(file); LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints"); + if (autocvar_g_waypointeditor && autocvar_g_waypointeditor_symmetrical_allowload) + { + cvar_set("g_waypointeditor_symmetrical", ftos(sym)); + if (sym == 1 && sym_param3 < 2) + cvar_set("g_waypointeditor_symmetrical_order", "0"); // make sure this is reset if not loaded + if (sym == -1 || (sym == 1 && sym_param3 >= 2)) + { + string params; + if (sym == 1) + params = cons("-", "-"); + else + { + params = cons(ftos(sym_param1), ftos(sym_param2)); + cvar_set("g_waypointeditor_symmetrical_origin", params); + } + cvar_set("g_waypointeditor_symmetrical_order", ftos(sym_param3)); + LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with origin ", params, " and order ", ftos(sym_param3)); + } + else if (sym == -2) + { + string params = strcat(ftos(sym_param1), " ", ftos(sym_param2)); + cvar_set("g_waypointeditor_symmetrical_axis", params); + LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym), " with axis ", params); + } + else + LOG_INFO("Waypoint editor: loaded symmetry ", ftos(sym)); + LOG_INFO(strcat("g_waypointeditor_symmetrical", " has been set to ", cvar_string("g_waypointeditor_symmetrical"))); + } + return cwp + cwb; } @@ -1132,11 +1235,12 @@ float waypoint_loadall() vector waypoint_fixorigin_down_dir(vector position, entity tracetest_ent, vector down_dir) { - tracebox(position + '0 0 1', PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent); + vector endpos = position + down_dir * 3000; + tracebox(position + '0 0 1', PL_MIN_CONST, PL_MAX_CONST, endpos, MOVE_NOMONSTERS, tracetest_ent); if(trace_startsolid) - tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z / 2), PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent); + tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z / 2), PL_MIN_CONST, PL_MAX_CONST, endpos, MOVE_NOMONSTERS, tracetest_ent); if(trace_startsolid) - tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + down_dir * 3000, MOVE_NOMONSTERS, tracetest_ent); + tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, endpos, MOVE_NOMONSTERS, tracetest_ent); if(trace_fraction < 1) position = trace_endpos; return position; @@ -1194,19 +1298,42 @@ void waypoint_spawnforteleporter_boxes(entity e, int teleport_flag, vector org1, e.nearestwaypointtimeout = -1; } -void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent) +void waypoint_spawnforteleporter_wz(entity e, entity tracetest_ent) { - // warpzones with oblique warp plane rely on down_dir to snap waypoints - // to the ground without leaving the warp plane - // warpzones with horizontal warp plane (down_dir.x == -1) generate - // destination waypoint snapped to the ground (leaving warpzone), source - // waypoint in the center of the warp plane - if(down_dir.x != -1) - org = waypoint_fixorigin_down_dir(org, tracetest_ent, down_dir); - if(down_dir.x == -1) - down_dir = '0 0 -1'; - destination = waypoint_fixorigin_down_dir(destination, tracetest_ent, down_dir); - waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT, org, org, destination, destination, timetaken); + float src_angle = e.warpzone_angles.x; + while (src_angle < -180) src_angle += 360; + while (src_angle > 180) src_angle -= 360; + + float dest_angle = e.enemy.warpzone_angles.x; + while (dest_angle < -180) dest_angle += 360; + while (dest_angle > 180) dest_angle -= 360; + + // no waypoints for warpzones pointing upwards, they can't be used by the bots + if (src_angle == -90 || dest_angle == -90) + return; + + makevectors(e.warpzone_angles); + vector src = (e.absmin + e.absmax) * 0.5; + src += ((e.warpzone_origin - src) * v_forward) * v_forward + 16 * v_right; + vector down_dir_src = -v_up; + + makevectors(e.enemy.warpzone_angles); + vector dest = (e.enemy.absmin + e.enemy.absmax) * 0.5; + dest += ((e.enemy.warpzone_origin - dest) * v_forward) * v_forward - 16 * v_right; + vector down_dir_dest = -v_up; + + int extra_flag = 0; + // don't snap to the ground waypoints for source warpzones pointing downwards + if (src_angle != 90) + { + src = waypoint_fixorigin_down_dir(src, tracetest_ent, down_dir_src); + dest = waypoint_fixorigin_down_dir(dest, tracetest_ent, down_dir_dest); + // oblique warpzones need a jump otherwise bots gets stuck + if (src_angle != 0) + extra_flag = WAYPOINTFLAG_JUMP; + } + + waypoint_spawnforteleporter_boxes(e, WAYPOINTFLAG_TELEPORT | extra_flag, src, src, dest, dest, 0); } void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent) @@ -1283,6 +1410,8 @@ void botframe_showwaypointlinks() { int display_type = 0; entity head = navigation_findnearestwaypoint(it, false); + it.nearestwaypoint = head; // mainly useful for debug + it.nearestwaypointtimeout = time + 2; // while I'm at it... if (IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE) display_type = 1; // default else if(head && (head.wphardwired)) diff --git a/qcsrc/server/bot/default/waypoints.qh b/qcsrc/server/bot/default/waypoints.qh index bea11e9299..1de3d44901 100644 --- a/qcsrc/server/bot/default/waypoints.qh +++ b/qcsrc/server/bot/default/waypoints.qh @@ -6,7 +6,8 @@ // increase by 0.01 when changes require only waypoint relinking // increase by 1 when changes require to manually edit waypoints // max 2 decimal places, always specified -#define WAYPOINT_VERSION 1.00 +const float WAYPOINT_VERSION = 1.02; +string waypoint_time; // fields you can query using prvm_global server to get some statistics about waypoint linking culling float relink_total, relink_walkculled, relink_pvsculled, relink_lengthculled; @@ -36,6 +37,7 @@ float botframe_cachedwaypointlinks; spawnfunc(waypoint); void waypoint_removelink(entity from, entity to); +int waypoint_getlinknum(entity from, entity to); bool waypoint_islinked(entity from, entity to); void waypoint_addlink_customcost(entity from, entity to, float c); void waypoint_addlink(entity from, entity to); @@ -57,7 +59,7 @@ void waypoint_saveall(); void waypoint_spawnforitem_force(entity e, vector org); void waypoint_spawnforitem(entity e); void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent); -void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent); +void waypoint_spawnforteleporter_wz(entity e, entity tracetest_ent); void botframe_showwaypointlinks(); float waypoint_loadall(); diff --git a/qcsrc/server/bot/null/bot_null.qc b/qcsrc/server/bot/null/bot_null.qc index f8738f80fb..bdca146c2e 100644 --- a/qcsrc/server/bot/null/bot_null.qc +++ b/qcsrc/server/bot/null/bot_null.qc @@ -38,7 +38,7 @@ void waypoint_schedulerelink(entity wp) { } void waypoint_spawnforitem(entity e) { } void waypoint_spawnforitem_force(entity e, vector org) { } void waypoint_spawnforteleporter(entity e, vector destination, float timetaken, entity tracetest_ent) { } -void waypoint_spawnforteleporter_wz(entity e, vector org, vector destination, float timetaken, vector down_dir, entity tracetest_ent) { } +void waypoint_spawnforteleporter_wz(entity e, entity tracetest_ent) { } void waypoint_spawn_fromeditor(entity pl) { } entity waypoint_spawn(vector m1, vector m2, float f) { return NULL; } #endif diff --git a/qcsrc/server/campaign.qc b/qcsrc/server/campaign.qc index e80769cc27..e90d666035 100644 --- a/qcsrc/server/campaign.qc +++ b/qcsrc/server/campaign.qc @@ -52,8 +52,7 @@ void cvar_set_campaignwrapper(string theCvar, string theValue) { if(cvar_string_campaignwrapper(theCvar) == theValue) return; - string s; - s = cvar_campaignwrapper_list; + string s = cvar_campaignwrapper_list; cvar_campaignwrapper_list = strzone(strcat("; ", theCvar, " ", theValue, s)); strunzone(s); //print(cvar_campaignwrapper_list, "\n"); diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 976d5a7b94..5ca5270d61 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -3,12 +3,15 @@ #include <server/defs.qh> #include <server/miscfunctions.qh> #include <common/effects/all.qh> +#include <server/resources.qh> #include "g_damage.qh" +#include "clientkill.qh" +#include "player.qh" #include "race.qh" -#include "../common/triggers/teleporters.qh" +#include "../common/mapobjects/teleporters.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "weapons/tracing.qh" @@ -22,17 +25,16 @@ #include <common/weapons/_all.qh> -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" +#include <common/mapobjects/triggers.qh> -#include "../common/triggers/func/breakable.qh" +#include "../common/mapobjects/func/breakable.qh" #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/anglestransform.qh" #include "../lib/warpzone/util_server.qh" -void CopyBody(entity this, float keepvelocity); - #ifdef NOCHEATS float CheatImpulse(entity this, int imp) { return 0; } @@ -44,7 +46,7 @@ void Drag_MoveDrag(entity from, entity to) { } #else -.float maycheat; +.bool maycheat; float gamestart_sv_cheats; @@ -58,7 +60,7 @@ void CheatShutdown() { } -float CheatsAllowed(entity this, float i, float argc, float fr) // the cheat gets passed as argument for possible future ACL checking +float CheatsAllowed(entity this, float i, int argc, float fr) // the cheat gets passed as argument for possible future ACL checking { // dead people cannot cheat if(IS_DEAD(this)) @@ -151,15 +153,15 @@ float CheatImpulse(entity this, int imp) this.personal.origin = this.origin; this.personal.v_angle = this.v_angle; this.personal.velocity = this.velocity; - this.personal.ammo_rockets = this.ammo_rockets; - this.personal.ammo_nails = this.ammo_nails; - this.personal.ammo_cells = this.ammo_cells; - this.personal.ammo_plasma = this.ammo_plasma; - this.personal.ammo_shells = this.ammo_shells; - this.personal.ammo_fuel = this.ammo_fuel; - this.personal.health = max(1, this.health); - this.personal.armorvalue = this.armorvalue; - this.personal.weapons = this.weapons; + SetResourceAmount(this.personal, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS)); + SetResourceAmount(this.personal, RESOURCE_BULLETS, GetResourceAmount(this, RESOURCE_BULLETS)); + SetResourceAmount(this.personal, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS)); + SetResourceAmount(this.personal, RESOURCE_PLASMA, GetResourceAmount(this, RESOURCE_PLASMA)); + SetResourceAmount(this.personal, RESOURCE_SHELLS, GetResourceAmount(this, RESOURCE_SHELLS)); + SetResourceAmount(this.personal, RESOURCE_FUEL, GetResourceAmount(this, RESOURCE_FUEL)); + SetResourceAmount(this.personal, RESOURCE_HEALTH, max(1, GetResourceAmount(this, RESOURCE_HEALTH))); + SetResourceAmount(this.personal, RESOURCE_ARMOR, GetResourceAmount(this, RESOURCE_ARMOR)); + STAT(WEAPONS, this.personal) = STAT(WEAPONS, this); this.personal.items = this.items; this.personal.pauserotarmor_finished = this.pauserotarmor_finished; this.personal.pauserothealth_finished = this.pauserothealth_finished; @@ -210,15 +212,15 @@ float CheatImpulse(entity this, int imp) MUTATOR_CALLHOOK(AbortSpeedrun, this); } - this.ammo_rockets = this.personal.ammo_rockets; - this.ammo_nails = this.personal.ammo_nails; - this.ammo_cells = this.personal.ammo_cells; - this.ammo_plasma = this.personal.ammo_plasma; - this.ammo_shells = this.personal.ammo_shells; - this.ammo_fuel = this.personal.ammo_fuel; - this.health = this.personal.health; - this.armorvalue = this.personal.armorvalue; - this.weapons = this.personal.weapons; + SetResourceAmount(this, RESOURCE_ROCKETS, GetResourceAmount(this.personal, RESOURCE_ROCKETS)); + SetResourceAmount(this, RESOURCE_BULLETS, GetResourceAmount(this.personal, RESOURCE_BULLETS)); + SetResourceAmount(this, RESOURCE_CELLS, GetResourceAmount(this.personal, RESOURCE_CELLS)); + SetResourceAmount(this, RESOURCE_PLASMA, GetResourceAmount(this.personal, RESOURCE_PLASMA)); + SetResourceAmount(this, RESOURCE_SHELLS, GetResourceAmount(this.personal, RESOURCE_SHELLS)); + SetResourceAmount(this, RESOURCE_FUEL, GetResourceAmount(this.personal, RESOURCE_FUEL)); + SetResourceAmount(this, RESOURCE_HEALTH, GetResourceAmount(this.personal, RESOURCE_HEALTH)); + SetResourceAmount(this, RESOURCE_ARMOR, GetResourceAmount(this.personal, RESOURCE_ARMOR)); + STAT(WEAPONS, this) = STAT(WEAPONS, this.personal); this.items = this.personal.items; this.pauserotarmor_finished = time + this.personal.pauserotarmor_finished - this.personal.teleport_time; this.pauserothealth_finished = time + this.personal.pauserothealth_finished - this.personal.teleport_time; @@ -290,7 +292,6 @@ float CheatImpulse(entity this, int imp) END_CHEAT_FUNCTION(); } -void DragBox_Think(entity this); float drag_lastcnt; float CheatCommand(entity this, int argc) { @@ -355,7 +356,7 @@ float CheatCommand(entity this, int argc) entity e = spawn(); e.model = strzone(argv(1)); e.mdl = "rocket_explode"; - e.health = 1000; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, 1000); setorigin(e, trace_endpos); e.effects = EF_NOMODELFLAGS; if(f == 1) @@ -710,18 +711,6 @@ float CheatCommand(entity this, int argc) END_CHEAT_FUNCTION(); } -float Drag(entity this, float force_allow_pick, float ischeat); -void Drag_Begin(entity dragger, entity draggee, vector touchpoint); -void Drag_Finish(entity dragger); -float Drag_IsDraggable(entity draggee); -float Drag_MayChangeAngles(entity draggee); -void Drag_MoveForward(entity dragger); -void Drag_SetSpeed(entity dragger, float s); -void Drag_MoveBackward(entity dragger); -void Drag_Update(entity dragger); -float Drag_CanDrag(entity dragger); -float Drag_IsDragging(entity dragger); -void Drag_MoveDrag(entity from, entity to); .entity dragentity; float CheatFrame(entity this) @@ -850,7 +839,7 @@ float Drag(entity this, float force_allow_pick, float ischeat) } // Find e and pick if(e && pick) - if(Drag_IsDraggable(e)) + if(Drag_IsDraggable(e, this)) { if(ischeat) IS_CHEAT(this, 0, 0, CHRAME_DRAG); @@ -926,31 +915,24 @@ void Drag_Finish(entity dragger) } } -float Drag_IsDraggable(entity draggee) +bool drag_undraggable(entity draggee, entity dragger) +{ + // stuff probably shouldn't need this, we should figure out why they do! + // exceptions of course are observers and weapon entities, where things mess up + return false; +} + +float Drag_IsDraggable(entity draggee, entity dragger) { // TODO add more checks for bad stuff here if(draggee == NULL) return false; - if(draggee.classname == "func_bobbing") - return false; if(draggee.classname == "door") // FIXME find out why these must be excluded, or work around the problem (trying to drag these causes like 4 fps) - return false; - if(draggee.classname == "plat") - return false; - if(draggee.classname == "func_button") - return false; + return false; // probably due to BSP collision // if(draggee.model == "") // return false; - if(IS_SPEC(draggee)) - return false; - if(IS_OBSERVER(draggee)) - return false; - if(draggee.classname == "exteriorweaponentity") - return false; - if(draggee.classname == "weaponentity") - return false; - return true; + return ((draggee.draggable) ? draggee.draggable(draggee, dragger) : true); } float Drag_MayChangeAngles(entity draggee) @@ -1028,7 +1010,7 @@ float Drag_IsDragging(entity dragger) dragger.dragentity = NULL; return false; } - if(!Drag_CanDrag(dragger) || !Drag_IsDraggable(dragger.dragentity)) + if(!Drag_CanDrag(dragger) || !Drag_IsDraggable(dragger.dragentity, dragger)) { Drag_Finish(dragger); return false; diff --git a/qcsrc/server/cheats.qh b/qcsrc/server/cheats.qh index 1bf0806410..962e017a19 100644 --- a/qcsrc/server/cheats.qh +++ b/qcsrc/server/cheats.qh @@ -14,4 +14,20 @@ float CheatFrame(entity this); const float CHRAME_DRAG = 8; +bool drag_undraggable(entity draggee, entity dragger); + +.bool(entity this, entity dragger) draggable; void Drag_MoveDrag(entity from, entity to); // call this from CopyBody +void DragBox_Think(entity this); +float Drag(entity this, float force_allow_pick, float ischeat); +void Drag_Begin(entity dragger, entity draggee, vector touchpoint); +void Drag_Finish(entity dragger); +bool Drag_IsDraggable(entity draggee, entity dragger); +float Drag_MayChangeAngles(entity draggee); +void Drag_MoveForward(entity dragger); +void Drag_SetSpeed(entity dragger, float s); +void Drag_MoveBackward(entity dragger); +void Drag_Update(entity dragger); +float Drag_CanDrag(entity dragger); +float Drag_IsDragging(entity dragger); +void Drag_MoveDrag(entity from, entity to); diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 5a199e06dc..01c8222e62 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -10,13 +10,14 @@ #include "miscfunctions.qh" #include "portals.qh" #include "teamplay.qh" -#include "playerdemo.qh" #include "spawnpoints.qh" #include "resources.qh" #include "g_damage.qh" #include "handicap.qh" #include "g_hook.qh" #include "command/common.qh" +#include "command/vote.qh" +#include "clientkill.qh" #include "cheats.qh" #include "g_world.qh" #include "race.qh" @@ -31,11 +32,14 @@ #include "../common/wepent.qh" #include <common/state.qh> +#include "compat/quake3.qh" + #include <common/effects/qc/globalsound.qh> -#include "../common/triggers/func/conveyor.qh" -#include "../common/triggers/teleporters.qh" -#include "../common/triggers/target/spawnpoint.qh" +#include "../common/mapobjects/func/conveyor.qh" +#include "../common/mapobjects/teleporters.qh" +#include "../common/mapobjects/target/spawnpoint.qh" +#include <common/mapobjects/trigger/counter.qh> #include "../common/vehicles/all.qh" @@ -46,13 +50,17 @@ #include "../common/net_linked.qh" #include "../common/physics/player.qh" +#include <common/vehicles/sv_vehicles.qh> + #include "../common/items/_mod.qh" #include "../common/mutators/mutator/waypoints/all.qh" +#include "../common/mutators/mutator/instagib/sv_instagib.qh" +#include <common/gamemodes/_mod.qh> -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" -#include "../common/triggers/trigger/secret.qh" +#include "../common/mapobjects/subs.qh" +#include "../common/mapobjects/triggers.qh" +#include "../common/mapobjects/trigger/secret.qh" #include "../common/minigames/sv_minigames.qh" @@ -62,6 +70,8 @@ #include "../lib/warpzone/server.qh" +#include <common/mutators/mutator/overkill/oknex.qh> + STATIC_METHOD(Client, Add, void(Client this, int _team)) { ClientConnect(this); @@ -71,8 +81,6 @@ STATIC_METHOD(Client, Add, void(Client this, int _team)) PutClientInServer(this); } -void PutObserverInServer(entity this); - STATIC_METHOD(Client, Remove, void(Client this)) { TRANSMUTE(Observer, this); @@ -152,15 +160,17 @@ void ClientData_Detach(entity this) void ClientData_Touch(entity e) { - CS(e).clientdata.SendFlags = 1; + entity cd = CS(e).clientdata; + if (cd) { cd.SendFlags = 1; } // make it spectatable - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, { CS(it).clientdata.SendFlags = 1; }); + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, + { + entity cd = CS(it).clientdata; + if (cd) { cd.SendFlags = 1; } + }); } -void SetSpectatee(entity this, entity spectatee); -void SetSpectatee_status(entity this, int spectatee_num); - /* ============= @@ -214,7 +224,6 @@ void setplayermodel(entity e, string modelname) UpdatePlayerSounds(e); } -void FixPlayermodel(entity player); /** putting a client as observer in the server */ void PutObserverInServer(entity this) { @@ -223,7 +232,7 @@ void PutObserverInServer(entity this) if (IS_PLAYER(this)) { - if(this.health >= 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) >= 1) { // despawn effect Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); @@ -265,7 +274,7 @@ void PutObserverInServer(entity this) RemoveGrapplingHooks(this); Portal_ClearAll(this); - Unfreeze(this); + Unfreeze(this, false); SetSpectatee(this, NULL); if (this.alivetime) @@ -279,27 +288,11 @@ void PutObserverInServer(entity this) WaypointSprite_PlayerDead(this); - 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 - } - if (CS(this).killcount != FRAGS_SPECTATOR) { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname); if(!game_stopped) if(autocvar_g_chat_nospectators == 1 || (!warmup_stage && autocvar_g_chat_nospectators == 2)) Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS); - - if(!CS(this).just_joined) - LogTeamchange(this.playerid, -1, 4); - else - CS(this).just_joined = false; } accuracy_resend(this); @@ -308,6 +301,9 @@ void PutObserverInServer(entity this) if(this.bot_attack) IL_REMOVE(g_bot_targets, this); this.bot_attack = false; + if(this.monster_attack) + IL_REMOVE(g_monster_targets, this); + this.monster_attack = false; STAT(HUD, this) = HUD_NORMAL; TRANSMUTE(Observer, this); this.iscreature = false; @@ -315,15 +311,14 @@ void PutObserverInServer(entity this) if(this.damagedbycontents) IL_REMOVE(g_damagedbycontents, this); this.damagedbycontents = false; - this.health = FRAGS_SPECTATOR; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, FRAGS_SPECTATOR); SetSpectatee_status(this, etof(this)); this.takedamage = DAMAGE_NO; this.solid = SOLID_NOT; set_movetype(this, MOVETYPE_FLY_WORLDONLY); // user preference is controlled by playerprethink this.flags = FL_CLIENT | FL_NOTARGET; - this.armorvalue = 666; this.effects = 0; - this.armorvalue = autocvar_g_balance_armor_start; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, autocvar_g_balance_armor_start); // was 666?! this.pauserotarmor_finished = 0; this.pauserothealth_finished = 0; this.pauseregen_finished = 0; @@ -340,6 +335,10 @@ void PutObserverInServer(entity this) this.strength_finished = 0; this.invincible_finished = 0; this.superweapons_finished = 0; + //this.dphitcontentsmask = 0; + this.dphitcontentsmask = DPCONTENTS_SOLID; + if (autocvar_g_playerclip_collisions) + this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; this.pushltime = 0; this.istypefrag = 0; setthink(this, func_null); @@ -348,9 +347,10 @@ void PutObserverInServer(entity this) this.crouch = false; STAT(REVIVE_PROGRESS, this) = 0; this.revival_time = 0; + this.draggable = drag_undraggable; this.items = 0; - this.weapons = '0 0 0'; + STAT(WEAPONS, this) = '0 0 0'; this.drawonlytoclient = this; this.viewloc = NULL; @@ -371,6 +371,7 @@ void PutObserverInServer(entity this) this.oldvelocity = this.velocity; this.fire_endtime = -1; this.event_damage = func_null; + this.event_heal = func_null; for(int slot = 0; slot < MAX_AXH; ++slot) { @@ -380,6 +381,16 @@ void PutObserverInServer(entity this) if(axh.owner == this && axh != NULL && !wasfreed(axh)) delete(axh); } + + if (mutator_returnvalue) + { + // mutator prevents resetting teams+score + } + else + { + SetPlayerTeam(this, -1, TEAM_CHANGE_SPECTATOR); + this.frags = FRAGS_SPECTATOR; + } } int player_getspecies(entity this) @@ -512,7 +523,7 @@ void PutPlayerInServer(entity this) accuracy_resend(this); if (this.team < 0) - JoinBestTeam(this, true); + TeamBalance_JoinBestTeam(this); entity spot = SelectSpawnPoint(this, false); if (!spot) { @@ -544,25 +555,25 @@ void PutPlayerInServer(entity this) this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; if (warmup_stage) { - this.ammo_shells = warmup_start_ammo_shells; - this.ammo_nails = warmup_start_ammo_nails; - this.ammo_rockets = warmup_start_ammo_rockets; - this.ammo_cells = warmup_start_ammo_cells; - this.ammo_plasma = warmup_start_ammo_plasma; - this.ammo_fuel = warmup_start_ammo_fuel; - this.health = warmup_start_health; - this.armorvalue = warmup_start_armorvalue; - this.weapons = WARMUP_START_WEAPONS; + SetResourceAmount(this, RESOURCE_SHELLS, warmup_start_ammo_shells); + SetResourceAmount(this, RESOURCE_BULLETS, warmup_start_ammo_nails); + SetResourceAmount(this, RESOURCE_ROCKETS, warmup_start_ammo_rockets); + SetResourceAmount(this, RESOURCE_CELLS, warmup_start_ammo_cells); + SetResourceAmount(this, RESOURCE_PLASMA, warmup_start_ammo_plasma); + SetResourceAmount(this, RESOURCE_FUEL, warmup_start_ammo_fuel); + SetResourceAmount(this, RESOURCE_HEALTH, warmup_start_health); + SetResourceAmount(this, RESOURCE_ARMOR, warmup_start_armorvalue); + STAT(WEAPONS, this) = WARMUP_START_WEAPONS; } else { - this.ammo_shells = start_ammo_shells; - this.ammo_nails = start_ammo_nails; - this.ammo_rockets = start_ammo_rockets; - this.ammo_cells = start_ammo_cells; - this.ammo_plasma = start_ammo_plasma; - this.ammo_fuel = start_ammo_fuel; - this.health = start_health; - this.armorvalue = start_armorvalue; - this.weapons = start_weapons; + SetResourceAmount(this, RESOURCE_SHELLS, start_ammo_shells); + SetResourceAmount(this, RESOURCE_BULLETS, start_ammo_nails); + SetResourceAmount(this, RESOURCE_ROCKETS, start_ammo_rockets); + SetResourceAmount(this, RESOURCE_CELLS, start_ammo_cells); + SetResourceAmount(this, RESOURCE_PLASMA, start_ammo_plasma); + SetResourceAmount(this, RESOURCE_FUEL, start_ammo_fuel); + SetResourceAmount(this, RESOURCE_HEALTH, start_health); + SetResourceAmount(this, RESOURCE_ARMOR, start_armorvalue); + STAT(WEAPONS, this) = start_weapons; if (MUTATOR_CALLHOOK(ForbidRandomStartWeapons, this) == false) { GiveRandomWeapons(this, random_start_weapons_count, @@ -573,7 +584,7 @@ void PutPlayerInServer(entity this) PS(this).dual_weapons = '0 0 0'; - this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; + this.superweapons_finished = (STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; this.items = start_items; @@ -665,6 +676,9 @@ void PutPlayerInServer(entity this) STAT(HUD, this) = HUD_NORMAL; this.event_damage = PlayerDamage; + this.event_heal = PlayerHeal; + + this.draggable = func_null; if(!this.bot_attack) IL_PUSH(g_bot_targets, this); @@ -697,6 +711,9 @@ void PutPlayerInServer(entity this) this.speedrunning = false; + this.counter_cnt = 0; + this.fragsfilter_cnt = 0; + target_voicescript_clear(this); // reset fields the weapons may use @@ -713,13 +730,13 @@ void PutPlayerInServer(entity this) }); { - string s = spot.target; - spot.target = string_null; + //string s = spot.target; + //spot.target = string_null; SUB_UseTargets(spot, this, NULL); - spot.target = s; + //spot.target = s; } - Unfreeze(this); + Unfreeze(this, false); MUTATOR_CALLHOOK(PlayerSpawn, this, spot); @@ -744,6 +761,14 @@ void PutPlayerInServer(entity this) MUTATOR_CALLHOOK(PlayerWeaponSelect, this); + if (CS(this).impulse) ImpulseCommands(this); + + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + W_WeaponFrame(this, weaponentity); + } + if (!warmup_stage && !this.alivetime) this.alivetime = time; @@ -778,8 +803,6 @@ void PutClientInServer(entity this) } } -void ClientInit_misc(entity this); - // TODO do we need all these fields, or should we stop autodetecting runtime // changes and just have a console command to update this? bool ClientInit_SendEntity(entity this, entity to, int sf) @@ -886,202 +909,6 @@ void DecodeLevelParms(entity this) MUTATOR_CALLHOOK(DecodeLevelParms); } -/* -============= -ClientKill - -Called when a client types 'kill' in the console -============= -*/ - -.float clientkill_nexttime; -void ClientKill_Now_TeamChange(entity this) -{ - if(this.killindicator_teamchange == -1) - { - JoinBestTeam( this, true ); - } - 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, this.killindicator_teamchange - 1); - this.killindicator_teamchange = 0; -} - -void ClientKill_Now(entity this) -{ - if(this.vehicle) - { - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(!this.killindicator_teamchange) - { - this.vehicle_health = -1; - Damage(this, this, this, 1 , DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); - } - } - - if(this.killindicator && !wasfreed(this.killindicator)) - delete(this.killindicator); - - this.killindicator = NULL; - - if(this.killindicator_teamchange) - ClientKill_Now_TeamChange(this); - - if (!IS_SPEC(this) && !IS_OBSERVER(this) && MUTATOR_CALLHOOK(ClientKill_Now, this) == false) - { - Damage(this, this, this, 100000, DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); - } - - // now I am sure the player IS dead -} -void KillIndicator_Think(entity this) -{ - if (game_stopped) - { - this.owner.killindicator = NULL; - delete(this); - return; - } - - if (this.owner.alpha < 0 && !this.owner.vehicle) - { - this.owner.killindicator = NULL; - delete(this); - return; - } - - if(this.cnt <= 0) - { - ClientKill_Now(this.owner); - return; - } - else if(this.health == 1) // health == 1 means that it's silent - { - this.nextthink = time + 1; - this.cnt -= 1; - } - else - { - if(this.cnt <= 10) - setmodel(this, MDL_NUM(this.cnt)); - if(IS_REAL_CLIENT(this.owner)) - { - if(this.cnt <= 10) - { Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); } - } - this.nextthink = time + 1; - this.cnt -= 1; - } -} - -float clientkilltime; -void ClientKill_TeamChange (entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec -{ - float killtime; - float starttime; - - if (game_stopped) - return; - - killtime = autocvar_g_balance_kill_delay; - - if(MUTATOR_CALLHOOK(ClientKill, this, killtime)) - return; - killtime = M_ARGV(1, float); - - this.killindicator_teamchange = targetteam; - - if(!this.killindicator) - { - if(!IS_DEAD(this)) - { - killtime = max(killtime, this.clientkill_nexttime - time); - this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam; - } - - if(killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this)) - { - ClientKill_Now(this); - } - else - { - starttime = max(time, clientkilltime); - - this.killindicator = spawn(); - this.killindicator.owner = this; - this.killindicator.scale = 0.5; - setattachment(this.killindicator, this, ""); - setorigin(this.killindicator, '0 0 52'); - setthink(this.killindicator, KillIndicator_Think); - this.killindicator.nextthink = starttime + (this.lip) * 0.05; - clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05); - this.killindicator.cnt = ceil(killtime); - this.killindicator.count = bound(0, ceil(killtime), 10); - //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n")); - - IL_EACH(g_clones, it.enemy == this && !(it.effects & CSQCMODEL_EF_RESPAWNGHOST), - { - it.killindicator = spawn(); - it.killindicator.owner = it; - it.killindicator.scale = 0.5; - setattachment(it.killindicator, it, ""); - setorigin(it.killindicator, '0 0 52'); - setthink(it.killindicator, KillIndicator_Think); - it.killindicator.nextthink = starttime + (it.lip) * 0.05; - //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05); - it.killindicator.cnt = ceil(killtime); - }); - this.lip = 0; - } - } - if(this.killindicator) - { - if(targetteam == 0) // just die - { - this.killindicator.colormod = '0 0 0'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SUICIDE, this.killindicator.cnt); - } - else if(targetteam == -1) // auto - { - this.killindicator.colormod = '0 1 0'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_AUTO, this.killindicator.cnt); - } - else if(targetteam == -2) // spectate - { - this.killindicator.colormod = '0.5 0.5 0.5'; - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_TEAMCHANGE_SPECTATE, this.killindicator.cnt); - } - else - { - this.killindicator.colormod = Team_ColorRGB(targetteam); - if(IS_REAL_CLIENT(this)) - if(this.killindicator.cnt > 0) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE), this.killindicator.cnt); - } - } - -} - -void ClientKill (entity this) -{ - if(game_stopped) return; - if(this.player_blocked) return; - if(STAT(FROZEN, this)) return; - - ClientKill_TeamChange(this, 0); -} - void FixClientCvars(entity e) { // send prediction settings to the client @@ -1157,6 +984,76 @@ void ClientPreConnect(entity this) } #endif +string GetClientVersionMessage(entity this) +{ + if (CS(this).version_mismatch) { + if(CS(this).version < autocvar_gameversion) { + return strcat("This is Xonotic ", autocvar_g_xonoticversion, + "\n^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8"); + } else { + return strcat("This is Xonotic ", autocvar_g_xonoticversion, + "\n^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8"); + } + } else { + return strcat("Welcome to Xonotic ", autocvar_g_xonoticversion); + } +} + +string getwelcomemessage(entity this) +{ + MUTATOR_CALLHOOK(BuildMutatorsPrettyString, ""); + string modifications = M_ARGV(0, string); + + if(g_weaponarena) + { + if(g_weaponarena_random) + modifications = strcat(modifications, ", ", ftos(g_weaponarena_random), " of ", g_weaponarena_list, " Arena"); + else + modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena"); + } + else if(cvar("g_balance_blaster_weaponstartoverride") == 0) + modifications = strcat(modifications, ", No start weapons"); + if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) + modifications = strcat(modifications, ", Low gravity"); + if(g_weapon_stay && !g_cts) + modifications = strcat(modifications, ", Weapons stay"); + if(g_jetpack) + modifications = strcat(modifications, ", Jet pack"); + if(autocvar_g_powerups == 0) + modifications = strcat(modifications, ", No powerups"); + if(autocvar_g_powerups > 0) + modifications = strcat(modifications, ", Powerups"); + modifications = substring(modifications, 2, strlen(modifications) - 2); + + string versionmessage = GetClientVersionMessage(this); + string s = strcat(versionmessage, "^8\n^8\nmatch type is ^1", gamemode_name, "^8\n"); + + if(modifications != "") + s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n"); + + if(cache_lastmutatormsg != autocvar_g_mutatormsg) + { + strcpy(cache_lastmutatormsg, autocvar_g_mutatormsg); + strcpy(cache_mutatormsg, cache_lastmutatormsg); + } + + if (cache_mutatormsg != "") { + s = strcat(s, "\n\n^8special gameplay tips: ^7", cache_mutatormsg); + } + + string mutator_msg = ""; + MUTATOR_CALLHOOK(BuildGameplayTipsString, mutator_msg); + mutator_msg = M_ARGV(0, string); + + s = strcat(s, mutator_msg); // trust that the mutator will do proper formatting + + string motd = autocvar_sv_motd; + if (motd != "") { + s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd)); + } + return s; +} + /** ============= ClientConnect @@ -1177,54 +1074,13 @@ void ClientConnect(entity this) TRANSMUTE(Client, this); CS(this).version_nagtime = time + 10 + random() * 10; - // identify the right forced team - if (autocvar_g_campaign) - { - if (IS_REAL_CLIENT(this)) // only players, not bots - { - switch (autocvar_g_campaign_forceteam) - { - case 1: this.team_forced = NUM_TEAM_1; break; - case 2: this.team_forced = NUM_TEAM_2; break; - case 3: this.team_forced = NUM_TEAM_3; break; - case 4: this.team_forced = NUM_TEAM_4; break; - default: this.team_forced = 0; - } - } - } - else if (PlayerInList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; - else if (PlayerInList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; - else if (PlayerInList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; - else if (PlayerInList(this, autocvar_g_forced_team_pink)) this.team_forced = NUM_TEAM_4; - else switch (autocvar_g_forced_team_otherwise) - { - default: this.team_forced = 0; break; - case "red": this.team_forced = NUM_TEAM_1; break; - case "blue": this.team_forced = NUM_TEAM_2; break; - case "yellow": this.team_forced = NUM_TEAM_3; break; - case "pink": this.team_forced = NUM_TEAM_4; break; - case "spectate": - case "spectator": - this.team_forced = -1; - break; - } - if (!teamplay && this.team_forced > 0) this.team_forced = 0; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname); - int playerid_save = this.playerid; - this.playerid = 0; // silent - JoinBestTeam(this, false); // if the team number is valid, keep it - this.playerid = playerid_save; + bot_clientconnect(this); - if (autocvar_sv_spectate || autocvar_g_campaign || this.team_forced < 0) { - TRANSMUTE(Observer, this); - } else { - if (!teamplay || autocvar_g_balance_teams) { - TRANSMUTE(Player, this); - campaign_bots_may_start = true; - } else { - TRANSMUTE(Observer, this); // do it anyway - } - } + Player_DetermineForcedTeam(this); + + TRANSMUTE(Observer, this); PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); @@ -1237,15 +1093,8 @@ void ClientConnect(entity this) if (autocvar_sv_eventlog) GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", playername(this, false))); - LogTeamchange(this.playerid, this.team, 1); - CS(this).just_joined = true; // stop spamming the eventlog with additional lines when the client connects - if(teamplay && IS_PLAYER(this)) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname); - else - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_CONNECT, this.netname); - stuffcmd(this, clientstuff, "\n"); stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? @@ -1257,12 +1106,9 @@ void ClientConnect(entity this) // notify about available teams if (teamplay) { - CheckAllowedTeams(this); - int t = 0; - if (c1 >= 0) t |= BIT(0); - if (c2 >= 0) t |= BIT(1); - if (c3 >= 0) t |= BIT(2); - if (c4 >= 0) t |= BIT(3); + entity balance = TeamBalance_CheckAllowedTeams(this); + int t = TeamBalance_GetAllowedTeams(balance); + TeamBalance_Destroy(balance); stuffcmd(this, sprintf("set _teams_available %d\n", t)); } else @@ -1300,6 +1146,8 @@ void ClientConnect(entity this) if (IS_REAL_CLIENT(this)) sv_notice_join(this); + this.move_qcphysics = true; + // update physics stats (players can spawn before physics runs) Physics_UpdateStats(this); @@ -1328,7 +1176,6 @@ Called when a client disconnects from the server ============= */ .entity chatbubbleentity; -void ReadyCount(); void ClientDisconnect(entity this) { assert(IS_CLIENT(this), return); @@ -1348,13 +1195,13 @@ void ClientDisconnect(entity this) MUTATOR_CALLHOOK(ClientDisconnect, this); - if (CS(this).netname_previous) strunzone(CS(this).netname_previous); // needs to be before the CS entity is removed! - if (CS(this).weaponorder_byimpulse) strunzone(CS(this).weaponorder_byimpulse); + strfree(CS(this).netname_previous); // needs to be before the CS entity is removed! + strfree(CS(this).weaponorder_byimpulse); ClientState_detach(this); Portal_ClearAll(this); - Unfreeze(this); + Unfreeze(this, false); RemoveGrapplingHooks(this); @@ -1369,7 +1216,7 @@ void ClientDisconnect(entity this) bot_relinkplayerlist(); - if (this.clientstatus) strunzone(this.clientstatus); + strfree(this.clientstatus); if (this.personal) delete(this.personal); this.playerid = 0; @@ -1394,7 +1241,7 @@ void ChatBubbleThink(entity this) if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) { - if ( CS(this.owner).active_minigame ) + if ( CS(this.owner).active_minigame && PHYS_INPUT_BUTTON_MINIGAME(this.owner) ) this.mdl = "models/sprites/minigame_busy.iqm"; else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) this.mdl = "models/misc/chatbubble.spr"; @@ -1465,9 +1312,57 @@ void respawn(entity this) PutClientInServer(this); } +void PrintToChat(entity client, string text) +{ + text = strcat("\{1}^7", text, "\n"); + sprint(client, text); +} + +void DebugPrintToChat(entity client, string text) +{ + if (autocvar_developer) + { + PrintToChat(client, text); + } +} + +void PrintToChatAll(string text) +{ + text = strcat("\{1}^7", text, "\n"); + bprint(text); +} + +void DebugPrintToChatAll(string text) +{ + if (autocvar_developer) + { + PrintToChatAll(text); + } +} + +void PrintToChatTeam(int team_num, string text) +{ + text = strcat("\{1}^7", text, "\n"); + FOREACH_CLIENT(IS_REAL_CLIENT(it), + { + if (it.team == team_num) + { + sprint(it, text); + } + }); +} + +void DebugPrintToChatTeam(int team_num, string text) +{ + if (autocvar_developer) + { + PrintToChatTeam(team_num, text); + } +} + void play_countdown(entity this, float finished, Sound samp) { - TC(Sound, samp); + TC(Sound, samp); if(IS_REAL_CLIENT(this)) if(floor(finished - time - frametime) != floor(finished - time)) if(finished - time < 6) @@ -1492,7 +1387,7 @@ void player_powerups(entity this) Fire_ApplyDamage(this); Fire_ApplyEffect(this); - if (!g_instagib) + if (!MUTATOR_IS_ENABLED(mutator_instagib)) { if (this.items & ITEM_Strength.m_itemid) { @@ -1538,7 +1433,7 @@ void player_powerups(entity this) } if (this.items & IT_SUPERWEAPON) { - if (!(this.weapons & WEPSET_SUPERWEAPONS)) + if (!(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS)) { this.superweapons_finished = 0; this.items = this.items - (this.items & IT_SUPERWEAPON); @@ -1555,25 +1450,28 @@ void player_powerups(entity this) if (time > this.superweapons_finished) { this.items = this.items - (this.items & IT_SUPERWEAPON); - this.weapons &= ~WEPSET_SUPERWEAPONS; + STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS; //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_BROKEN, this.netname); Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_BROKEN); } } } - else if(this.weapons & WEPSET_SUPERWEAPONS) + else if(STAT(WEAPONS, this) & WEPSET_SUPERWEAPONS) { if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS)) { this.items = this.items | IT_SUPERWEAPON; - if(!g_cts) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP); + if(!(this.items & IT_UNLIMITED_SUPERWEAPONS)) + { + if(!g_cts) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_PICKUP, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_PICKUP); + } } else { this.superweapons_finished = 0; - this.weapons &= ~WEPSET_SUPERWEAPONS; + STAT(WEAPONS, this) &= ~WEPSET_SUPERWEAPONS; } } else @@ -1678,13 +1576,17 @@ void player_regen(entity this) limith = limith * limit_mod; limita = limita * limit_mod; - this.armorvalue = CalcRotRegen(this.armorvalue, mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, rot_mod * frametime * (time > this.pauserotarmor_finished), limita); - this.health = CalcRotRegen(this.health, regen_health_stable, regen_health, regen_health_linear, regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, rot_mod * frametime * (time > this.pauserothealth_finished), limith); + SetResourceAmount(this, RESOURCE_ARMOR, CalcRotRegen(GetResourceAmount(this, RESOURCE_ARMOR), mina, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, + regen_mod * frametime * (time > this.pauseregen_finished), maxa, autocvar_g_balance_armor_rot, autocvar_g_balance_armor_rotlinear, + rot_mod * frametime * (time > this.pauserotarmor_finished), limita)); + SetResourceAmount(this, RESOURCE_HEALTH, CalcRotRegen(GetResourceAmount(this, RESOURCE_HEALTH), regen_health_stable, regen_health, regen_health_linear, + regen_mod * frametime * (time > this.pauseregen_finished), regen_health_rotstable, regen_health_rot, regen_health_rotlinear, + rot_mod * frametime * (time > this.pauserothealth_finished), limith)); } // if player rotted to death... die! // check this outside above checks, as player may still be able to rot to death - if(this.health < 1) + if(GetResourceAmount(this, RESOURCE_HEALTH) < 1) { if(this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -1700,20 +1602,10 @@ void player_regen(entity this) minf = autocvar_g_balance_fuel_regenstable; limitf = GetResourceLimit(this, RESOURCE_FUEL); - this.ammo_fuel = CalcRotRegen(this.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf); + SetResourceAmount(this, RESOURCE_FUEL, CalcRotRegen(GetResourceAmount(this, RESOURCE_FUEL), minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, + frametime * (time > this.pauseregen_finished) * ((this.items & ITEM_JetpackRegen.m_itemid) != 0), + maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > this.pauserotfuel_finished), limitf)); } - // Ugly hack to make sure the health and armor don't go beyond hard limit. - // TODO: Remove this hack when all code uses GivePlayerHealth and - // GivePlayerArmor. - if (this.health > RESOURCE_AMOUNT_HARD_LIMIT) - { - this.health = RESOURCE_AMOUNT_HARD_LIMIT; - } - if (this.armorvalue > RESOURCE_AMOUNT_HARD_LIMIT) - { - this.armorvalue = RESOURCE_AMOUNT_HARD_LIMIT; - } - // End hack. } bool zoomstate_set; @@ -1737,7 +1629,7 @@ void GetPressedKeys(entity this) keys = BITSET(keys, KEY_LEFT, CS(this).movement.y < 0); keys = BITSET(keys, KEY_JUMP, PHYS_INPUT_BUTTON_JUMP(this)); - keys = BITSET(keys, KEY_CROUCH, PHYS_INPUT_BUTTON_CROUCH(this)); + keys = BITSET(keys, KEY_CROUCH, IS_DUCKED(this)); // workaround: player can't un-crouch until their path is clear, so we keep the button held here keys = BITSET(keys, KEY_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); CS(this).pressedkeys = keys; // store for other users @@ -1753,20 +1645,20 @@ spectate mode routines void SpectateCopy(entity this, entity spectatee) { - TC(Client, this); TC(Client, spectatee); + TC(Client, this); TC(Client, spectatee); MUTATOR_CALLHOOK(SpectateCopy, spectatee, this); PS(this) = PS(spectatee); this.armortype = spectatee.armortype; - this.armorvalue = spectatee.armorvalue; - this.ammo_cells = spectatee.ammo_cells; - this.ammo_plasma = spectatee.ammo_plasma; - this.ammo_shells = spectatee.ammo_shells; - this.ammo_nails = spectatee.ammo_nails; - this.ammo_rockets = spectatee.ammo_rockets; - this.ammo_fuel = spectatee.ammo_fuel; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, GetResourceAmount(spectatee, RESOURCE_ARMOR)); + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(spectatee, RESOURCE_CELLS)); + SetResourceAmountExplicit(this, RESOURCE_PLASMA, GetResourceAmount(spectatee, RESOURCE_PLASMA)); + SetResourceAmountExplicit(this, RESOURCE_SHELLS, GetResourceAmount(spectatee, RESOURCE_SHELLS)); + SetResourceAmountExplicit(this, RESOURCE_BULLETS, GetResourceAmount(spectatee, RESOURCE_BULLETS)); + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(spectatee, RESOURCE_ROCKETS)); + SetResourceAmountExplicit(this, RESOURCE_FUEL, GetResourceAmount(spectatee, RESOURCE_FUEL)); this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance - this.health = spectatee.health; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, GetResourceAmount(spectatee, RESOURCE_HEALTH)); CS(this).impulse = 0; this.items = spectatee.items; STAT(LAST_PICKUP, this) = STAT(LAST_PICKUP, spectatee); @@ -1775,7 +1667,7 @@ void SpectateCopy(entity this, entity spectatee) this.invincible_finished = spectatee.invincible_finished; this.superweapons_finished = spectatee.superweapons_finished; STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee); - this.weapons = spectatee.weapons; + STAT(WEAPONS, this) = STAT(WEAPONS, spectatee); this.punchangle = spectatee.punchangle; this.view_ofs = spectatee.view_ofs; this.velocity = spectatee.velocity; @@ -1995,7 +1887,7 @@ void ShowRespawnCountdown(entity this) .bool team_selected; bool ShowTeamSelection(entity this) { - if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (CS(this).wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0) + if (!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (CS(this).wasplayer && autocvar_g_changeteam_banned) || Player_HasRealForcedTeam(this)) return false; stuffcmd(this, "menu_showteamselect\n"); return true; @@ -2006,7 +1898,7 @@ void Join(entity this) if(!this.team_selected) if(autocvar_g_campaign || autocvar_g_balance_teams) - JoinBestTeam(this, true); + TeamBalance_JoinBestTeam(this); if(autocvar_g_campaign) campaign_bots_may_start = true; @@ -2017,12 +1909,21 @@ void Join(entity this) if(IS_PLAYER(this)) if(teamplay && this.team != -1) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_PLAY_TEAM), this.netname); + { + } else Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_JOIN_PLAY, this.netname); this.team_selected = false; } +int GetPlayerLimit() +{ + int player_limit = autocvar_g_maxplayers; + MUTATOR_CALLHOOK(GetPlayerLimit, player_limit); + player_limit = M_ARGV(0, int); + return player_limit; +} + /** * Determines whether the player is allowed to join. This depends on cvar * g_maxplayers, if it isn't used this function always return true, otherwise @@ -2041,7 +1942,7 @@ int nJoinAllowed(entity this, entity ignore) return 0; } - if(this && this.team_forced < 0) + if(this && (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR)) return 0; // forced spectators can never join // TODO simplify this @@ -2055,11 +1956,13 @@ int nJoinAllowed(entity this, entity ignore) ++currentlyPlaying; }); + int player_limit = GetPlayerLimit(); + float free_slots = 0; - if (!autocvar_g_maxplayers) + if (!player_limit) free_slots = maxclients - totalClients; - else if(currentlyPlaying < autocvar_g_maxplayers) - free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); + else if(currentlyPlaying < player_limit) + free_slots = min(maxclients - totalClients, player_limit - currentlyPlaying); static float join_prevent_msg_time = 0; if(this && ignore && !free_slots && time > join_prevent_msg_time) @@ -2135,13 +2038,15 @@ void PrintWelcomeMessage(entity this) } } +const int MIN_SPEC_TIME = 1; bool joinAllowed(entity this) { if (CS(this).version_mismatch) return false; + if (time < CS(this).jointime + MIN_SPEC_TIME) return false; if (!nJoinAllowed(this, this)) return false; if (teamplay && lockteams) return false; - if (ShowTeamSelection(this)) return false; if (MUTATOR_CALLHOOK(ForbidSpawn, this)) return false; + if (ShowTeamSelection(this)) return false; return true; } @@ -2242,43 +2147,6 @@ bool PlayerThink(entity this) return false; } - bool have_hook = false; - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - if(this.(weaponentity).hook.state) - { - have_hook = true; - break; - } - } - bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); - if (have_hook) { - do_crouch = false; - } else if (this.waterlevel >= WATERLEVEL_SWIMMING) { - do_crouch = false; - } else if (this.vehicle) { - do_crouch = false; - } else if (STAT(FROZEN, this)) { - do_crouch = false; - } - - if (do_crouch) { - if (!this.crouch) { - this.crouch = true; - this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this); - setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this)); - // setanim(this, this.anim_duck, false, true, true); // this anim is BROKEN anyway - } - } else if (this.crouch) { - tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, false, this); - if (!trace_startsolid) { - this.crouch = false; - this.view_ofs = STAT(PL_VIEW_OFS, this); - setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this)); - } - } - FixPlayermodel(this); if (this.shootfromfixedorigin != autocvar_g_shootfromfixedorigin) { @@ -2298,7 +2166,7 @@ bool PlayerThink(entity this) } this.items_added = 0; - if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || this.ammo_fuel >= 0.01)) + if ((this.items & ITEM_Jetpack.m_itemid) && ((this.items & ITEM_JetpackRegen.m_itemid) || GetResourceAmount(this, RESOURCE_FUEL) >= 0.01)) this.items_added |= IT_FUEL; this.items |= this.items_added; @@ -2328,6 +2196,7 @@ bool PlayerThink(entity this) return true; } +.bool would_spectate; void ObserverThink(entity this) { if ( CS(this).impulse ) @@ -2340,7 +2209,7 @@ void ObserverThink(entity this) if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) { this.flags &= ~FL_JUMPRELEASED; this.flags |= FL_SPAWNING; - } else if(PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch) { + } else if(PHYS_INPUT_BUTTON_ATCK(this) && !CS(this).version_mismatch || this.would_spectate) { this.flags &= ~FL_JUMPRELEASED; if(SpectateNext(this)) { TRANSMUTE(Spectator, this); @@ -2400,12 +2269,19 @@ void SpectatorThink(entity this) } CS(this).impulse = 0; } else if (PHYS_INPUT_BUTTON_ATCK2(this)) { + this.would_spectate = false; this.flags &= ~FL_JUMPRELEASED; TRANSMUTE(Observer, this); PutClientInServer(this); } else { if(!SpectateUpdate(this)) - PutObserverInServer(this); + { + if(!SpectateNext(this)) + { + PutObserverInServer(this); + this.would_spectate = true; + } + } } } else { if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) { @@ -2424,7 +2300,6 @@ void SpectatorThink(entity this) this.flags |= FL_CLIENT | FL_NOTARGET; } -void vehicles_enter (entity pl, entity veh); void PlayerUseKey(entity this) { if (!IS_PLAYER(this)) @@ -2514,8 +2389,7 @@ void PlayerPreThink (entity this) } 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); + strcpy(CS(this).netname_previous, this.netname); } // version nagging @@ -2545,21 +2419,21 @@ void PlayerPreThink (entity this) if(IS_PLAYER(this)) { - if (STAT(FROZEN, this) == 2) + if (STAT(FROZEN, this) == FROZEN_TEMP_REVIVING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) + frametime * this.revive_speed, 1); - this.health = max(1, STAT(REVIVE_PROGRESS, this) * start_health); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(1, STAT(REVIVE_PROGRESS, this) * start_health)); this.iceblock.alpha = bound(0.2, 1 - STAT(REVIVE_PROGRESS, this), 1); if (STAT(REVIVE_PROGRESS, this) >= 1) - Unfreeze(this); + Unfreeze(this, false); } - else if (STAT(FROZEN, this) == 3) + else if (STAT(FROZEN, this) == FROZEN_TEMP_DYING) { STAT(REVIVE_PROGRESS, this) = bound(0, STAT(REVIVE_PROGRESS, this) - frametime * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this) ); + SetResourceAmountExplicit(this, RESOURCE_HEALTH, max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * STAT(REVIVE_PROGRESS, this))); - if (this.health < 1) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 1) { if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); @@ -2567,7 +2441,7 @@ void PlayerPreThink (entity this) this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, DMG_NOWEP, this.origin, '0 0 0'); } else if (STAT(REVIVE_PROGRESS, this) <= 0) - Unfreeze(this); + Unfreeze(this, false); } } @@ -2606,6 +2480,8 @@ void PlayerPreThink (entity this) PrintWelcomeMessage(this); if (IS_PLAYER(this)) { + if (IS_REAL_CLIENT(this) && time < CS(this).jointime + MIN_SPEC_TIME) + error("Client can't be spawned as player on connection!"); if(!PlayerThink(this)) return; } @@ -2614,6 +2490,20 @@ void PlayerPreThink (entity this) IntermissionThink(this); return; } + else if (IS_REAL_CLIENT(this) && !CS(this).autojoin_checked && time >= CS(this).jointime + MIN_SPEC_TIME) + { + CS(this).autojoin_checked = true; + // don't do this in ClientConnect + // many things can go wrong if a client is spawned as player on connection + if (MUTATOR_CALLHOOK(AutoJoinOnConnection, this) + || (!(autocvar_sv_spectate || autocvar_g_campaign || (Player_GetForcedTeamIndex(this) == TEAM_FORCE_SPECTATOR)) + && (!teamplay || autocvar_g_balance_teams))) + { + campaign_bots_may_start = true; + Join(this); + return; + } + } else if (IS_OBSERVER(this)) { ObserverThink(this); } @@ -2687,7 +2577,7 @@ void DrownPlayer(entity this) void Player_Physics(entity this) { - set_movetype(this, this.move_movetype); + this.movetype = (this.move_qcphysics) ? MOVETYPE_QCPLAYER : this.move_movetype; if(!this.move_qcphysics) return; @@ -2785,15 +2675,318 @@ void PlayerPostThink (entity this) } if (this.waypointsprite_attachedforcarrier) { - vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); + vector v = healtharmor_maxdamage(GetResourceAmount(this, RESOURCE_HEALTH), GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); } - playerdemo_write(this); - CSQCMODEL_AUTOUPDATE(this); } +/** + * message "": do not say, just test flood control + * return value: + * 1 = accept + * 0 = reject + * -1 = fake accept + */ +int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) +{ + if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ") + msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) + + if (source) + msgin = formatmessage(source, msgin); + + string colorstr; + if (!(IS_PLAYER(source) || source.caplayer)) + colorstr = "^0"; // black for spectators + else if(teamplay) + colorstr = Team_ColorCode(source.team); + else + { + colorstr = ""; + teamsay = false; + } + + if(game_stopped) + teamsay = false; + + if (!source) { + colorstr = ""; + teamsay = false; + } + + if(msgin != "") + msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); + + /* + * using bprint solves this... me stupid + // how can we prevent the message from appearing in a listen server? + // for now, just give "say" back and only handle say_team + if(!teamsay) + { + clientcommand(source, strcat("say ", msgin)); + return; + } + */ + + string namestr = ""; + if (source) + namestr = playername(source, autocvar_g_chat_teamcolors); + + string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; + + string msgstr = "", cmsgstr = ""; + string privatemsgprefix = string_null; + int privatemsgprefixlen = 0; + if (msgin != "") + { + if(privatesay) + { + msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); + privatemsgprefixlen = strlen(msgstr); + msgstr = strcat(msgstr, msgin); + cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7"); + } + else if(teamsay) + { + if(strstrofs(msgin, "/me", 0) >= 0) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin); + msgstr = strcat("\{1}\{13}^4* ", "^7", msgin); + } + else + msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin); + cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin); + } + else + { + if(strstrofs(msgin, "/me", 0) >= 0) + { + //msgin = strreplace("/me", "", msgin); + //msgin = substring(msgin, 3, strlen(msgin)); + msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); + msgstr = strcat("\{1}^4* ", "^7", msgin); + } + else { + msgstr = "\{1}"; + msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); + msgstr = strcat(msgstr, msgin); + } + cmsgstr = ""; + } + msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint + } + + string fullmsgstr = msgstr; + string fullcmsgstr = cmsgstr; + + // FLOOD CONTROL + int flood = 0; + var .float flood_field = floodcontrol_chat; + if(floodcontrol && source) + { + float flood_spl; + float flood_burst; + float flood_lmax; + float lines; + if(privatesay) + { + flood_spl = autocvar_g_chat_flood_spl_tell; + flood_burst = autocvar_g_chat_flood_burst_tell; + flood_lmax = autocvar_g_chat_flood_lmax_tell; + flood_field = floodcontrol_chattell; + } + else if(teamsay) + { + flood_spl = autocvar_g_chat_flood_spl_team; + flood_burst = autocvar_g_chat_flood_burst_team; + flood_lmax = autocvar_g_chat_flood_lmax_team; + flood_field = floodcontrol_chatteam; + } + else + { + flood_spl = autocvar_g_chat_flood_spl; + flood_burst = autocvar_g_chat_flood_burst; + flood_lmax = autocvar_g_chat_flood_lmax; + flood_field = floodcontrol_chat; + } + flood_burst = max(0, flood_burst - 1); + // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four! + + // do flood control for the default line size + if(msgstr != "") + { + getWrappedLine_remaining = msgstr; + msgstr = ""; + lines = 0; + while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax)) + { + msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width + ++lines; + } + msgstr = substring(msgstr, 1, strlen(msgstr) - 1); + + if(getWrappedLine_remaining != "") + { + msgstr = strcat(msgstr, "\n"); + flood = 2; + } + + if (time >= source.(flood_field)) + { + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; + } + else + { + flood = 1; + msgstr = fullmsgstr; + } + } + else + { + if (time >= source.(flood_field)) + source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; + else + flood = 1; + } + + if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection + source.(flood_field) = flood = 0; + } + + string sourcemsgstr, sourcecmsgstr; + if(flood == 2) // cannot happen for empty msgstr + { + if(autocvar_g_chat_flood_notify_flooder) + { + sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n"); + sourcecmsgstr = ""; + } + else + { + sourcemsgstr = fullmsgstr; + sourcecmsgstr = fullcmsgstr; + } + cmsgstr = ""; + } + else + { + sourcemsgstr = msgstr; + sourcecmsgstr = cmsgstr; + } + + if (!privatesay && source && !(IS_PLAYER(source) || source.caplayer)) + { + if (!game_stopped) + if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage)) + teamsay = -1; // spectators + } + + if(flood) + LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding."); + + // build sourcemsgstr by cutting off a prefix and replacing it by the other one + if(privatesay) + sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); + + int ret; + if(source && CS(source).muted) + { + // always fake the message + ret = -1; + } + else if(flood == 1) + { + if (autocvar_g_chat_flood_notify_flooder) + { + sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); + ret = 0; + } + else + ret = -1; + } + else + { + ret = 1; + } + + if (privatesay && source && !(IS_PLAYER(source) || source.caplayer)) + { + if (!game_stopped) + if ((privatesay && (IS_PLAYER(privatesay) || privatesay.caplayer)) && ((autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage))) + ret = -1; // just hide the message completely + } + + MUTATOR_CALLHOOK(ChatMessage, source, ret); + ret = M_ARGV(1, int); + + if(sourcemsgstr != "" && ret != 0) + { + if(ret < 0) // faked message, because the player is muted + { + sprint(source, sourcemsgstr); + if(sourcecmsgstr != "" && !privatesay) + centerprint(source, sourcecmsgstr); + } + else if(privatesay) // private message, between 2 people only + { + sprint(source, sourcemsgstr); + if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled + if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source)) + { + sprint(privatesay, msgstr); + if(cmsgstr != "") + centerprint(privatesay, cmsgstr); + } + } + else if ( teamsay && CS(source).active_minigame ) + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + } + else if(teamsay > 0) // team message, only sent to team mates + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + if(sourcecmsgstr != "") + centerprint(source, sourcecmsgstr); + FOREACH_CLIENT((IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + if(cmsgstr != "") + centerprint(it, cmsgstr); + }); + } + else if(teamsay < 0) // spectator message, only sent to spectators + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(!(IS_PLAYER(it) || it.caplayer) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + } + else + { + if (source) { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + MX_Say(strcat(playername(source, true), "^7: ", msgin)); + } + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { + sprint(it, msgstr); + }); + } + } + + return ret; +} + // hack to copy the button fields from the client entity to the Client State void PM_UpdateButtons(entity this, entity store) { @@ -2801,11 +2994,14 @@ void PM_UpdateButtons(entity this, entity store) store.impulse = this.impulse; this.impulse = 0; - store.button0 = this.button0; - store.button2 = this.button2; - store.button3 = this.button3; + bool typing = this.buttonchat || this.button14; + + store.button0 = (typing) ? 0 : this.button0; + //button1?! + store.button2 = (typing) ? 0 : this.button2; + store.button3 = (typing) ? 0 : this.button3; store.button4 = this.button4; - store.button5 = this.button5; + store.button5 = (typing) ? 0 : this.button5; store.button6 = this.button6; store.button7 = this.button7; store.button8 = this.button8; @@ -2831,5 +3027,12 @@ void PM_UpdateButtons(entity this, entity store) store.ping_movementloss = this.ping_movementloss; store.v_angle = this.v_angle; - store.movement = this.movement; + store.movement = (typing) ? '0 0 0' : this.movement; +} + +NET_HANDLE(fpsreport, bool) +{ + int fps = ReadShort(); + PlayerScore_Set(sender, SP_FPS, fps); + return true; } diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh index 48d42da009..012e040f81 100644 --- a/qcsrc/server/client.qh +++ b/qcsrc/server/client.qh @@ -17,7 +17,7 @@ CLASS(Client, Object) /** Client IP */ ATTRIB(Client, netaddress, string, this.netaddress); ATTRIB(Client, playermodel, string, this.playermodel); - ATTRIB(Client, playerskin, int, this.playerskin); + ATTRIB(Client, playerskin, string, this.playerskin); /** fingerprint of CA key the player used to authenticate */ ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp); @@ -114,6 +114,7 @@ CLASS(Client, Object) ATTRIB(Client, cmd_floodtime, float, this.cmd_floodtime); ATTRIB(Client, wasplayer, bool, this.wasplayer); ATTRIB(Client, weaponorder_byimpulse, string, this.weaponorder_byimpulse); + ATTRIB(Client, autojoin_checked, bool, this.wasplayer); // networked cvars @@ -144,6 +145,9 @@ CLASS(Client, Object) ATTRIB(Client, cvar_cl_accuracy_data_receive, bool, this.cvar_cl_accuracy_data_receive); ATTRIBARRAY(Client, cvar_cl_weaponpriorities, string, 10); ATTRIB(Client, cvar_cl_weaponpriority, string, this.cvar_cl_weaponpriority); + ATTRIB(Client, cvar_cl_cts_noautoswitch, bool, this.cvar_cl_cts_noautoswitch); + ATTRIB(Client, cvar_cl_weapon_switch_reload, bool, this.cvar_cl_weapon_switch_reload); + ATTRIB(Client, cvar_cl_weapon_switch_fallback_to_impulse, bool, this.cvar_cl_weapon_switch_fallback_to_impulse); METHOD(Client, m_unwind, bool(Client this)); @@ -225,11 +229,62 @@ METHOD(Client, m_unwind, bool(Client this)) return false; } +bool PlayerInList(entity player, string list); + +/// \brief Print the string to the client's chat. +/// \param[in] client Client to print to. +/// \param[in] text Text to print. +void PrintToChat(entity client, string text); + +/// \brief Print the string to the client's chat if the server cvar "developer" +/// is not 0. +/// \param[in] client Client to print to. +/// \param[in] text Text to print. +void DebugPrintToChat(entity client, string text); + +/// \brief Prints the string to all clients' chat. +/// \param[in] text Text to print. +void PrintToChatAll(string text); + +/// \brief Prints the string to all clients' chat if the server cvar "developer" +/// is not 0. +/// \param[in] text Text to print. +void DebugPrintToChatAll(string text); + +/// \brief Print the string to chat of all clients of the specified team. +/// \param[in] team_num Team to print to. See NUM_TEAM constants. +/// \param[in] text Text to print. +void PrintToChatTeam(int team_num, string text); + +/// \brief Print the string to chat of all clients of the specified team if the +/// server cvar "developer" is not 0. +/// \param[in] team_num Team to print to. See NUM_TEAM constants. +/// \param[in] text Text to print. +void DebugPrintToChatTeam(int team_num, string text); + 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); bool Spectate(entity this, entity pl); -#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee) +void ClientInit_Spawn(); + +void PutObserverInServer(entity this); + +void SetSpectatee(entity this, entity spectatee); +void SetSpectatee_status(entity this, int spectatee_num); + +void FixPlayermodel(entity player); + +void ClientInit_misc(entity this); + +int GetPlayerLimit(); + +bool joinAllowed(entity this); +void Join(entity this); + +#define SPECTATE_COPY() ACCUMULATE void SpectateCopy(entity this, entity spectatee) #define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } + +int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol); diff --git a/qcsrc/server/clientkill.qc b/qcsrc/server/clientkill.qc new file mode 100644 index 0000000000..08758a0653 --- /dev/null +++ b/qcsrc/server/clientkill.qc @@ -0,0 +1,202 @@ +#include "clientkill.qh" + +#include <server/defs.qh> + +#include "g_damage.qh" +#include "teamplay.qh" + +#include <common/vehicles/sv_vehicles.qh> +#include <common/notifications/all.qh> +#include <common/stats.qh> + +void ClientKill_Now_TeamChange(entity this) +{ + if (this.killindicator_teamchange == -1) + { + TeamBalance_JoinBestTeam(this); + } + 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 + { + Player_SetTeamIndexChecked(this, Team_TeamToIndex( + this.killindicator_teamchange)); + } + this.killindicator_teamchange = 0; +} + +void ClientKill_Now(entity this) +{ + if (this.vehicle) + { + vehicles_exit(this.vehicle, VHEF_RELEASE); + if (!this.killindicator_teamchange) + { + this.vehicle_health = -1; + Damage(this, this, this, 1 , DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); + } + } + + if (this.killindicator && !wasfreed(this.killindicator)) + delete(this.killindicator); + + this.killindicator = NULL; + + if (this.killindicator_teamchange) + ClientKill_Now_TeamChange(this); + + if (!IS_SPEC(this) && !IS_OBSERVER(this) && MUTATOR_CALLHOOK(ClientKill_Now, this) == false) + { + Damage(this, this, this, 100000, DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); + } + + // now I am sure the player IS dead +} +void KillIndicator_Think(entity this) +{ + if (game_stopped || (this.owner.alpha < 0 && !this.owner.vehicle)) + { + this.owner.killindicator = NULL; + delete(this); + return; + } + + if (this.cnt <= 0) + { + ClientKill_Now(this.owner); + return; + } + + // count == 1 means that it's silent + if (this.count != 1) + { + if (this.cnt <= 10) + setmodel(this, MDL_NUM(this.cnt)); + if (IS_REAL_CLIENT(this.owner)) + { + if (this.cnt <= 10) + Send_Notification(NOTIF_ONE, this.owner, MSG_ANNCE, Announcer_PickNumber(CNT_KILL, this.cnt)); + } + } + this.nextthink = time + 1; + this.cnt -= 1; +} + +.float lip; +float clientkilltime; +.float clientkill_nexttime; +void ClientKill_TeamChange(entity this, float targetteam) // 0 = don't change, -1 = auto, -2 = spec +{ + if (game_stopped) + return; + + float killtime = autocvar_g_balance_kill_delay; + + if (MUTATOR_CALLHOOK(ClientKill, this, killtime)) + return; + killtime = M_ARGV(1, float); + + this.killindicator_teamchange = targetteam; + + // this.killindicator.count == 1 means that the kill indicator was spawned by ClientKill_Silent + if(killtime <= 0 && this.killindicator && this.killindicator.count == 1) + { + ClientKill_Now(this); // allow instant kill in this case + return; + } + + if (!this.killindicator) + { + if (!IS_DEAD(this)) + { + killtime = max(killtime, this.clientkill_nexttime - time); + this.clientkill_nexttime = time + killtime + autocvar_g_balance_kill_antispam; + } + + if (killtime <= 0 || !IS_PLAYER(this) || IS_DEAD(this)) + { + ClientKill_Now(this); + } + else + { + float starttime = max(time, clientkilltime); + + this.killindicator = spawn(); + this.killindicator.owner = this; + this.killindicator.scale = 0.5; + setattachment(this.killindicator, this, ""); + setorigin(this.killindicator, '0 0 52'); + setthink(this.killindicator, KillIndicator_Think); + this.killindicator.nextthink = starttime + (this.lip) * 0.05; + clientkilltime = max(clientkilltime, this.killindicator.nextthink + 0.05); + this.killindicator.cnt = ceil(killtime); + this.killindicator.count = bound(0, ceil(killtime), 10); + //sprint(this, strcat("^1You'll be dead in ", ftos(this.killindicator.cnt), " seconds\n")); + + IL_EACH(g_clones, it.enemy == this && !(it.effects & CSQCMODEL_EF_RESPAWNGHOST), + { + it.killindicator = spawn(); + it.killindicator.owner = it; + it.killindicator.scale = 0.5; + setattachment(it.killindicator, it, ""); + setorigin(it.killindicator, '0 0 52'); + setthink(it.killindicator, KillIndicator_Think); + it.killindicator.nextthink = starttime + (it.lip) * 0.05; + //clientkilltime = max(clientkilltime, it.killindicator.nextthink + 0.05); + it.killindicator.cnt = ceil(killtime); + }); + this.lip = 0; + } + } + if (this.killindicator) + { + Notification notif; + if (targetteam == 0) // just die + { + this.killindicator.colormod = '0 0 0'; + notif = CENTER_TEAMCHANGE_SUICIDE; + } + else if (targetteam == -1) // auto + { + this.killindicator.colormod = '0 1 0'; + notif = CENTER_TEAMCHANGE_AUTO; + } + else if (targetteam == -2) // spectate + { + this.killindicator.colormod = '0.5 0.5 0.5'; + notif = CENTER_TEAMCHANGE_SPECTATE; + } + else + { + this.killindicator.colormod = Team_ColorRGB(targetteam); + notif = APP_TEAM_NUM(targetteam, CENTER_TEAMCHANGE); + } + if (IS_REAL_CLIENT(this) && this.killindicator.cnt > 0) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, notif, this.killindicator.cnt); + } + +} + +void ClientKill_Silent(entity this, float _delay) +{ + this.killindicator = spawn(); + this.killindicator.owner = this; + setthink(this.killindicator, KillIndicator_Think); + this.killindicator.nextthink = time + (this.lip) * 0.05; + this.killindicator.cnt = ceil(_delay); + this.killindicator.count = 1; // this is used to indicate that it should be silent + this.lip = 0; +} + +// Called when a client types 'kill' in the console +void ClientKill(entity this) +{ + if (game_stopped || this.player_blocked || STAT(FROZEN, this)) + return; + + ClientKill_TeamChange(this, 0); +} diff --git a/qcsrc/server/clientkill.qh b/qcsrc/server/clientkill.qh new file mode 100644 index 0000000000..5b26adf915 --- /dev/null +++ b/qcsrc/server/clientkill.qh @@ -0,0 +1,11 @@ + +// set when showing a kill countdown +.entity killindicator; +.int killindicator_teamchange; + +void ClientKill_Now_TeamChange(entity this); +void ClientKill_Now(entity this); +void KillIndicator_Think(entity this); +void ClientKill_TeamChange(entity this, float targetteam); // 0 = don't change, -1 = auto, -2 = spec +void ClientKill_Silent(entity this, float _delay); +void ClientKill(entity this); diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index da495f7962..fb63b057e2 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -18,7 +18,7 @@ // Last updated: December 29th, 2011 // ===================================================== -void BanCommand_ban(float request, float argc, string command) +void BanCommand_ban(int request, int argc, string command) { switch (request) { @@ -54,7 +54,7 @@ void BanCommand_ban(float request, float argc, string command) } } -void BanCommand_banlist(float request) +void BanCommand_banlist(int request) { switch (request) { @@ -75,7 +75,7 @@ void BanCommand_banlist(float request) } } -void BanCommand_kickban(float request, float argc, string command) +void BanCommand_kickban(int request, int argc, string command) { switch (request) { @@ -122,7 +122,7 @@ void BanCommand_kickban(float request, float argc, string command) } } -void BanCommand_mute(float request, float argc, string command) // TODO: Add a sort of mute-"ban" which allows players to be muted based on IP/cryptokey +void BanCommand_mute(int request, int argc, string command) // TODO: Add a sort of mute-"ban" which allows players to be muted based on IP/cryptokey { switch (request) { @@ -157,7 +157,7 @@ void BanCommand_mute(float request, float argc, string command) // TODO: Add a } } -void BanCommand_unban(float request, float argc) +void BanCommand_unban(int request, int argc) { switch (request) { @@ -203,7 +203,7 @@ void BanCommand_unban(float request, float argc) } } -void BanCommand_unmute(float request, float argc) +void BanCommand_unmute(int request, int argc) { switch (request) { @@ -240,7 +240,7 @@ void BanCommand_unmute(float request, float argc) /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void BanCommand_(float request) +void BanCommand_(int request) { switch(request) { @@ -285,7 +285,7 @@ void BanCommand_macro_help() #undef BAN_COMMAND } -float BanCommand_macro_command(float argc, string command) +float BanCommand_macro_command(int argc, string command) { #define BAN_COMMAND(name, function, description) \ { if (name == strtolower(argv(0))) { function; return true; } } @@ -296,7 +296,7 @@ float BanCommand_macro_command(float argc, string command) return false; } -float BanCommand_macro_usage(float argc) +float BanCommand_macro_usage(int argc) { #define BAN_COMMAND(name, function, description) \ { if (name == strtolower(argv(1))) { function; return true; } } @@ -318,7 +318,7 @@ void BanCommand_macro_write_aliases(float fh) float BanCommand(string command) { - float argc = tokenize_console(command); + int argc = tokenize_console(command); // Guide for working with argc arguments by example: // argc: 1 - 2 - 3 - 4 diff --git a/qcsrc/server/command/banning.qh b/qcsrc/server/command/banning.qh index d6b1ae60f0..ae16532ecf 100644 --- a/qcsrc/server/command/banning.qh +++ b/qcsrc/server/command/banning.qh @@ -8,13 +8,8 @@ #define GET_BAN_ARG(v, d) if (argc > reason_arg) { if ((v = stof(argv(reason_arg))) != 0) ++reason_arg; else v = d; } else { v = d; } #define GET_BAN_REASON(v, d) if (argc > reason_arg) v = substring(command, argv_start_index(reason_arg), strlen(command) - argv_start_index(reason_arg)); else v = d; -void Ban_KickBanClient(entity client, float bantime, float masksize, string reason); -void Ban_View(); -float Ban_Insert(string ip, float bantime, string reason, float dosync); -float Ban_Delete(float i); - // used by common/command/generic.qc:GenericCommand_dumpcommands to list all commands into a .txt file void BanCommand_macro_write_aliases(float fh); void BanCommand_macro_help(); -float BanCommand_macro_usage(float argc); +float BanCommand_macro_usage(int argc); diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 088925f100..80716d811c 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -8,15 +8,20 @@ #include "common.qh" #include "vote.qh" +#include "../bot/api.qh" + #include "../campaign.qh" #include "../cheats.qh" +#include "../client.qh" +#include "../clientkill.qh" #include "../player.qh" #include "../ipban.qh" #include "../mapvoting.qh" #include "../scores.qh" #include "../teamplay.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> +#include <common/gamemodes/_mod.qh> #ifdef SVQC #include <common/vehicles/all.qh> @@ -30,7 +35,7 @@ #include <common/physics/player.qh> #include <common/teams.qh> #include <common/util.qh> -#include <common/triggers/triggers.qh> +#include <common/mapobjects/triggers.qh> #include <common/minigames/sv_minigames.qh> @@ -40,8 +45,6 @@ #include <lib/warpzone/common.qh> -void ClientKill_TeamChange(entity this, float targetteam); // 0 = don't change, -1 = auto, -2 = spec - // ========================================================= // Server side networked commands code, reworked by Samual // Last updated: December 28th, 2011 @@ -72,7 +75,7 @@ bool SV_ParseClientCommand_floodcheck(entity this) // Command Sub-Functions // ======================= -void ClientCommand_autoswitch(entity caller, float request, float argc) +void ClientCommand_autoswitch(entity caller, int request, int argc) { switch (request) { @@ -97,7 +100,7 @@ void ClientCommand_autoswitch(entity caller, float request, float argc) } } -void ClientCommand_clientversion(entity caller, float request, float argc) // internal command, used only by code +void ClientCommand_clientversion(entity caller, int request, int argc) // internal command, used only by code { switch (request) { @@ -118,7 +121,7 @@ void ClientCommand_clientversion(entity caller, float request, float argc) // i { // JoinBestTeam(caller, false, true); } - else if (teamplay && !autocvar_sv_spectate && !(caller.team_forced > 0)) + else if (teamplay && !autocvar_sv_spectate && !(Player_GetForcedTeamIndex(caller) > 0)) { TRANSMUTE(Observer, caller); // really? stuffcmd(caller, "menu_showteamselect\n"); @@ -140,7 +143,7 @@ void ClientCommand_clientversion(entity caller, float request, float argc) // i } } -void ClientCommand_mv_getpicture(entity caller, float request, float argc) // internal command, used only by code +void ClientCommand_mv_getpicture(entity caller, int request, int argc) // internal command, used only by code { switch (request) { @@ -165,9 +168,62 @@ void ClientCommand_mv_getpicture(entity caller, float request, float argc) // i } } -bool joinAllowed(entity this); -void Join(entity this); -void ClientCommand_join(entity caller, float request) +void ClientCommand_wpeditor(entity caller, int request, int argc) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if (!autocvar_g_waypointeditor) + { + sprint(caller, "ERROR: this command works only if the waypoint editor is on\n"); + return; + } + + if (argv(1) != "") + { + if (argv(1) == "spawn") + { + if (!IS_PLAYER(caller)) + sprint(caller, "ERROR: this command works only if you are player\n"); + else + waypoint_spawn_fromeditor(caller); + } + else if (argv(1) == "remove") + { + if (!IS_PLAYER(caller)) + sprint(caller, "ERROR: this command works only if you are player\n"); + else + waypoint_remove_fromeditor(caller); + } + else if (argv(1) == "unreachable") + { + if (!IS_PLAYER(caller)) + sprint(caller, "ERROR: this command works only if you are player\n"); + else + waypoint_unreachable(caller); + } + else if (argv(1) == "saveall") + waypoint_saveall(); + else if (argv(1) == "relinkall") + waypoint_schedulerelinkall(); + + return; + } + } + + default: + sprint(caller, "Incorrect parameters for ^2wpeditor^7\n"); + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd wpeditor action\n"); + sprint(caller, " Where 'action' can be: spawn, remove, unreachable, saveall, relinkall\n"); + return; + } + } +} + +void ClientCommand_join(entity caller, int request) { switch (request) { @@ -191,7 +247,37 @@ void ClientCommand_join(entity caller, float request) } } -void ClientCommand_physics(entity caller, float request, float argc) +void ClientCommand_kill(entity caller, int request) +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + if(IS_SPEC(caller) || IS_OBSERVER(caller)) + return; // no point warning about this, command does nothing + + if(GetResourceAmount(caller, RESOURCE_HEALTH) <= 0) + { + sprint(caller, "Can't die - you are already dead!\n"); + return; + } + + ClientKill(caller); + + return; // never fall through to usage + } + + default: + case CMD_REQUEST_USAGE: + { + sprint(caller, "\nUsage:^3 cmd kill\n"); + sprint(caller, " No arguments required.\n"); + return; + } + } +} + +void ClientCommand_physics(entity caller, int request, int argc) { switch (request) { @@ -231,7 +317,7 @@ void ClientCommand_physics(entity caller, float request, float argc) } } -void ClientCommand_ready(entity caller, float request) // todo: anti-spam for toggling readyness +void ClientCommand_ready(entity caller, int request) // todo: anti-spam for toggling readyness { switch (request) { @@ -248,12 +334,14 @@ void ClientCommand_ready(entity caller, float request) // todo: anti-spam for t if (caller.ready) // toggle { caller.ready = false; - bprint(playername(caller, false), "^2 is ^1NOT^2 ready\n"); + if(IS_PLAYER(caller) || caller.caplayer == 1) + bprint(playername(caller, false), "^2 is ^1NOT^2 ready\n"); } else { caller.ready = true; - bprint(playername(caller, false), "^2 is ready\n"); + if(IS_PLAYER(caller) || caller.caplayer == 1) + bprint(playername(caller, false), "^2 is ready\n"); } // cannot reset the game while a timeout is active! @@ -278,7 +366,7 @@ void ClientCommand_ready(entity caller, float request) // todo: anti-spam for t } } -void ClientCommand_say(entity caller, float request, float argc, string command) +void ClientCommand_say(entity caller, int request, int argc, string command) { switch (request) { @@ -298,13 +386,14 @@ void ClientCommand_say(entity caller, float request, float argc, string command) } } -void ClientCommand_say_team(entity caller, float request, float argc, string command) +void ClientCommand_say_team(entity caller, int request, int argc, string command) { switch (request) { case CMD_REQUEST_COMMAND: { - if (argc >= 2) Say(caller, true, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); + if (argc >= 2) + Say(caller, true, NULL, substring(command, argv_start_index(1), argv_end_index(-1) - argv_start_index(1)), 1); return; // never fall through to usage } @@ -319,7 +408,7 @@ void ClientCommand_say_team(entity caller, float request, float argc, string com } .bool team_selected; -void ClientCommand_selectteam(entity caller, float request, float argc) +void ClientCommand_selectteam(entity caller, int request, int argc) { switch (request) { @@ -338,7 +427,7 @@ void ClientCommand_selectteam(entity caller, float request, float argc) sprint(caller, "^7selectteam can only be used in teamgames\n"); return; } - if (caller.team_forced > 0) + if (Player_GetForcedTeamIndex(caller) > 0) { sprint(caller, "^7selectteam can not be used as your team is forced\n"); return; @@ -394,13 +483,16 @@ void ClientCommand_selectteam(entity caller, float request, float argc) 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) + entity balance = TeamBalance_CheckAllowedTeams(caller); + TeamBalance_GetTeamCounts(balance, caller); + if ((Team_IndexToBit(Team_TeamToIndex(selection)) & + TeamBalance_FindBestTeams(balance, caller, false)) == 0) { Send_Notification(NOTIF_ONE, caller, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM); + TeamBalance_Destroy(balance); return; } + TeamBalance_Destroy(balance); } ClientKill_TeamChange(caller, selection); if (!IS_PLAYER(caller)) @@ -421,7 +513,7 @@ void ClientCommand_selectteam(entity caller, float request, float argc) } } -void ClientCommand_selfstuff(entity caller, float request, string command) +void ClientCommand_selfstuff(entity caller, int request, string command) { switch (request) { @@ -445,7 +537,7 @@ void ClientCommand_selfstuff(entity caller, float request, string command) } } -void ClientCommand_sentcvar(entity caller, float request, float argc, string command) +void ClientCommand_sentcvar(entity caller, int request, int argc, string command) { switch (request) { @@ -479,7 +571,7 @@ void ClientCommand_sentcvar(entity caller, float request, float argc, string com } } -void ClientCommand_spectate(entity caller, float request) +void ClientCommand_spectate(entity caller, int request) { switch (request) { @@ -519,7 +611,7 @@ void ClientCommand_spectate(entity caller, float request) } } -void ClientCommand_suggestmap(entity caller, float request, float argc) +void ClientCommand_suggestmap(entity caller, int request, int argc) { switch (request) { @@ -543,7 +635,7 @@ void ClientCommand_suggestmap(entity caller, float request, float argc) } } -void ClientCommand_tell(entity caller, float request, float argc, string command) +void ClientCommand_tell(entity caller, int request, int argc, string command) { switch (request) { @@ -600,7 +692,7 @@ void ClientCommand_tell(entity caller, float request, float argc, string command } } -void ClientCommand_voice(entity caller, float request, float argc, string command) +void ClientCommand_voice(entity caller, int request, int argc, string command) { switch (request) { @@ -614,8 +706,10 @@ void ClientCommand_voice(entity caller, float request, float argc, string comman sprint(caller, sprintf("Invalid voice. Use one of: %s\n", allvoicesamples)); return; } - if (argc >= 3) VoiceMessage(caller, e, substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2))); - else VoiceMessage(caller, e, ""); + string msg = ""; + if (argc >= 3) + msg = substring(command, argv_start_index(2), argv_end_index(-1) - argv_start_index(2)); + VoiceMessage(caller, e, msg); return; } @@ -635,7 +729,7 @@ void ClientCommand_voice(entity caller, float request, float argc, string comman /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void ClientCommand_(entity caller, float request) +void ClientCommand_(entity caller, int request) { switch(request) { @@ -665,8 +759,10 @@ void ClientCommand_(entity caller, float request) #define CLIENT_COMMANDS(ent, request, arguments, command) \ CLIENT_COMMAND("autoswitch", ClientCommand_autoswitch(ent, request, arguments), "Whether or not to switch automatically when getting a better weapon") \ CLIENT_COMMAND("clientversion", ClientCommand_clientversion(ent, request, arguments), "Release version of the game") \ - CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \ CLIENT_COMMAND("join", ClientCommand_join(ent, request), "Become a player in the game") \ + CLIENT_COMMAND("kill", ClientCommand_kill(ent, request), "Become a member of the dead") \ + CLIENT_COMMAND("minigame", ClientCommand_minigame(ent, request, arguments, command), "Start a minigame") \ + CLIENT_COMMAND("mv_getpicture", ClientCommand_mv_getpicture(ent, request, arguments), "Retrieve mapshot picture from the server") \ CLIENT_COMMAND("physics", ClientCommand_physics(ent, request, arguments), "Change physics set") \ CLIENT_COMMAND("ready", ClientCommand_ready(ent, request), "Qualify as ready to end warmup stage (or restart server if allowed)") \ CLIENT_COMMAND("say", ClientCommand_say(ent, request, arguments, command), "Print a message to chat to all players") \ @@ -678,7 +774,7 @@ void ClientCommand_(entity caller, float request) CLIENT_COMMAND("suggestmap", ClientCommand_suggestmap(ent, request, arguments), "Suggest a map to the mapvote at match end") \ CLIENT_COMMAND("tell", ClientCommand_tell(ent, request, arguments, command), "Send a message directly to a player") \ CLIENT_COMMAND("voice", ClientCommand_voice(ent, request, arguments, command), "Send voice message via sound") \ - CLIENT_COMMAND("minigame", ClientCommand_minigame(ent, request, arguments, command), "Start a minigame") \ + CLIENT_COMMAND("wpeditor", ClientCommand_wpeditor(ent, request, arguments), "Waypoint editor commands") \ /* nothing */ void ClientCommand_macro_help(entity caller) @@ -690,7 +786,7 @@ void ClientCommand_macro_help(entity caller) #undef CLIENT_COMMAND } -float ClientCommand_macro_command(float argc, entity caller, string command) +float ClientCommand_macro_command(int argc, entity caller, string command) { #define CLIENT_COMMAND(name, function, description) \ { if (name == strtolower(argv(0))) { function; return true; } } @@ -701,7 +797,7 @@ float ClientCommand_macro_command(float argc, entity caller, string command) return false; } -float ClientCommand_macro_usage(float argc, entity caller) +float ClientCommand_macro_usage(int argc, entity caller) { #define CLIENT_COMMAND(name, function, description) \ { if (name == strtolower(argv(1))) { function; return true; } } @@ -739,7 +835,7 @@ void SV_ParseClientCommand(entity this, string command) // if we're banned, don't even parse the command if (Ban_MaybeEnforceBanOnce(this)) return; - float argc = tokenize_console(command); + int argc = tokenize_console(command); // Guide for working with argc arguments by example: // argc: 1 - 2 - 3 - 4 @@ -753,6 +849,7 @@ void SV_ParseClientCommand(entity this, string command) case "begin": break; // handled by engine in host_cmd.c case "download": break; // handled by engine in cl_parse.c case "mv_getpicture": break; // handled by server in this file + case "wpeditor": break; // handled by server in this file case "pause": break; // handled by engine in host_cmd.c case "prespawn": break; // handled by engine in host_cmd.c case "sentcvar": break; // handled by server in this file diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index 2f7467eab7..643afa3f17 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -76,7 +76,7 @@ float VerifyClientNumber(float tmp_number) else return true; } -entity GetIndexedEntity(float argc, float start_index) +entity GetIndexedEntity(int argc, float start_index) { entity selection; float tmp_number, index; @@ -280,7 +280,7 @@ void timeout_handler_think(entity this) // Common commands used in both sv_cmd.qc and cmd.qc // =================================================== -void CommonCommand_cvar_changes(float request, entity caller) +void CommonCommand_cvar_changes(int request, entity caller) { switch (request) { @@ -301,7 +301,7 @@ void CommonCommand_cvar_changes(float request, entity caller) } } -void CommonCommand_cvar_purechanges(float request, entity caller) +void CommonCommand_cvar_purechanges(int request, entity caller) { switch (request) { @@ -391,7 +391,7 @@ void CommonCommand_editmob(int request, entity caller, int argc) break; }); - if (!found && arg_lower != "random") { print_to(caller, "Invalid monster"); return; } + if (!found && arg_lower != "random" && arg_lower != "anyrandom") { print_to(caller, "Invalid monster"); return; } totalspawned += 1; WarpZone_TraceBox(CENTER_OR_VIEWOFS(caller), caller.mins, caller.maxs, CENTER_OR_VIEWOFS(caller) + v_forward * 150, true, caller); @@ -405,7 +405,7 @@ void CommonCommand_editmob(int request, entity caller, int argc) if (mon.realowner != caller && autocvar_g_monsters_edit < 2) { print_to(caller, "This monster does not belong to you"); return; } if (!is_visible) { print_to(caller, "You must look at your monster to edit it"); return; } - Damage(mon, NULL, NULL, mon.health + mon.max_health + 200, DEATH_KILL.m_id, DMG_NOWEP, mon.origin, '0 0 0'); + Damage(mon, NULL, NULL, GetResourceAmount(mon, RESOURCE_HEALTH) + mon.max_health + 200, DEATH_KILL.m_id, DMG_NOWEP, mon.origin, '0 0 0'); print_to(caller, strcat("Your pet '", mon.monster_name, "' has been brutally mutilated")); return; } @@ -469,7 +469,7 @@ void CommonCommand_editmob(int request, entity caller, int argc) } } -void CommonCommand_info(float request, entity caller, float argc) +void CommonCommand_info(int request, entity caller, int argc) { switch (request) { @@ -493,7 +493,7 @@ void CommonCommand_info(float request, entity caller, float argc) } } -void CommonCommand_ladder(float request, entity caller) +void CommonCommand_ladder(int request, entity caller) { switch (request) { @@ -513,7 +513,7 @@ void CommonCommand_ladder(float request, entity caller) } } -void CommonCommand_lsmaps(float request, entity caller) +void CommonCommand_lsmaps(int request, entity caller) { switch (request) { @@ -533,7 +533,7 @@ void CommonCommand_lsmaps(float request, entity caller) } } -void CommonCommand_printmaplist(float request, entity caller) +void CommonCommand_printmaplist(int request, entity caller) { switch (request) { @@ -553,7 +553,7 @@ void CommonCommand_printmaplist(float request, entity caller) } } -void CommonCommand_rankings(float request, entity caller) +void CommonCommand_rankings(int request, entity caller) { switch (request) { @@ -573,7 +573,7 @@ void CommonCommand_rankings(float request, entity caller) } } -void CommonCommand_records(float request, entity caller) +void CommonCommand_records(int request, entity caller) { switch (request) { @@ -601,7 +601,7 @@ void CommonCommand_records(float request, entity caller) } } -void CommonCommand_teamstatus(float request, entity caller) +void CommonCommand_teamstatus(int request, entity caller) { switch (request) { @@ -621,7 +621,7 @@ void CommonCommand_teamstatus(float request, entity caller) } } -void CommonCommand_time(float request, entity caller) +void CommonCommand_time(int request, entity caller) { switch (request) { @@ -632,8 +632,8 @@ void CommonCommand_time(float request, entity caller) print_to(caller, strcat("realtime = ", ftos(gettime(GETTIME_REALTIME)))); print_to(caller, strcat("hires = ", ftos(gettime(GETTIME_HIRES)))); print_to(caller, strcat("uptime = ", ftos(gettime(GETTIME_UPTIME)))); - print_to(caller, strcat("localtime = ", strftime(true, "%a %b %e %H:%M:%S %Z %Y"))); - print_to(caller, strcat("gmtime = ", strftime(false, "%a %b %e %H:%M:%S %Z %Y"))); + print_to(caller, strcat("localtime = ", strftime(true, "%a %b %d %H:%M:%S %Z %Y"))); + print_to(caller, strcat("gmtime = ", strftime(false, "%a %b %d %H:%M:%S %Z %Y"))); return; } @@ -647,7 +647,7 @@ void CommonCommand_time(float request, entity caller) } } -void CommonCommand_timein(float request, entity caller) +void CommonCommand_timein(int request, entity caller) { switch (request) { @@ -702,7 +702,7 @@ void CommonCommand_timein(float request, entity caller) } } -void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE. +void CommonCommand_timeout(int request, entity caller) // DEAR GOD THIS COMMAND IS TERRIBLE. { switch (request) { @@ -771,7 +771,7 @@ void CommonCommand_timeout(float request, entity caller) // DEAR GOD THIS COMMA } } -void CommonCommand_who(float request, entity caller, float argc) +void CommonCommand_who(int request, entity caller, int argc) { switch (request) { @@ -836,7 +836,7 @@ void CommonCommand_who(float request, entity caller, float argc) /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void CommonCommand_(float request, entity caller) +void CommonCommand_(int request, entity caller) { switch(request) { diff --git a/qcsrc/server/command/common.qh b/qcsrc/server/command/common.qh index 13cbfe49e1..f03a815de2 100644 --- a/qcsrc/server/command/common.qh +++ b/qcsrc/server/command/common.qh @@ -81,7 +81,7 @@ string GetClientErrorString_color(float clienterror, string original_input, stri // is this entity number even in the possible range of entities? float VerifyClientNumber(float tmp_number); -entity GetIndexedEntity(float argc, float start_index); +entity GetIndexedEntity(int argc, float start_index); // find a player which matches the input string, and return their entity entity GetFilteredEntity(string input); @@ -105,33 +105,33 @@ void timeout_handler_think(entity this); // Common commands used in both sv_cmd.qc and cmd.qc // =================================================== -void CommonCommand_cvar_changes(float request, entity caller); +void CommonCommand_cvar_changes(int request, entity caller); -void CommonCommand_cvar_purechanges(float request, entity caller); +void CommonCommand_cvar_purechanges(int request, entity caller); -void CommonCommand_editmob(float request, entity caller, float argc); +void CommonCommand_editmob(int request, entity caller, int argc); -void CommonCommand_info(float request, entity caller, float argc); +void CommonCommand_info(int request, entity caller, int argc); -void CommonCommand_ladder(float request, entity caller); +void CommonCommand_ladder(int request, entity caller); -void CommonCommand_lsmaps(float request, entity caller); +void CommonCommand_lsmaps(int request, entity caller); -void CommonCommand_printmaplist(float request, entity caller); +void CommonCommand_printmaplist(int request, entity caller); -void CommonCommand_rankings(float request, entity caller); +void CommonCommand_rankings(int request, entity caller); -void CommonCommand_records(float request, entity caller); +void CommonCommand_records(int request, entity caller); -void CommonCommand_teamstatus(float request, entity caller); +void CommonCommand_teamstatus(int request, entity caller); -void CommonCommand_time(float request, entity caller); +void CommonCommand_time(int request, entity caller); -void CommonCommand_timein(float request, entity caller); +void CommonCommand_timein(int request, entity caller); -void CommonCommand_timeout(float request, entity caller); +void CommonCommand_timeout(int request, entity caller); -void CommonCommand_who(float request, entity caller, float argc); +void CommonCommand_who(int request, entity caller, int argc); // ================================== @@ -160,7 +160,7 @@ void CommonCommand_macro_help(entity caller) FOREACH(COMMON_COMMANDS, true, { print_to(caller, sprintf(" ^2%s^7: %s", it.m_name, it.m_description)); }); } -float CommonCommand_macro_command(float argc, entity caller, string command) +float CommonCommand_macro_command(int argc, entity caller, string command) { string c = strtolower(argv(0)); FOREACH(COMMON_COMMANDS, it.m_name == c, { @@ -170,7 +170,7 @@ float CommonCommand_macro_command(float argc, entity caller, string command) return false; } -float CommonCommand_macro_usage(float argc, entity caller) +float CommonCommand_macro_usage(int argc, entity caller) { string c = strtolower(argv(1)); FOREACH(COMMON_COMMANDS, it.m_name == c, { diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index 6c31af72c8..e3dbf795f6 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -4,7 +4,6 @@ #include <common/command/_mod.qh> #include "../g_world.qh" -#include "../g_subs.qh" #include <common/util.qh> @@ -367,7 +366,7 @@ void RadarMap_Think(entity this) } } -bool RadarMap_Make(float argc) +bool RadarMap_Make(int argc) { float i; diff --git a/qcsrc/server/command/radarmap.qh b/qcsrc/server/command/radarmap.qh index ffa82e5c02..44bd8fb4ba 100644 --- a/qcsrc/server/command/radarmap.qh +++ b/qcsrc/server/command/radarmap.qh @@ -1,6 +1,6 @@ #pragma once #ifndef RADARMAP -bool RadarMap_Make(float argc) { LOG_INFO("radarmap is disabled, compile with -DRADARMAP to enable it."); return true; } +bool RadarMap_Make(int argc) { LOG_INFO("radarmap is disabled, compile with -DRADARMAP to enable it."); return true; } #else // =========================================== @@ -17,6 +17,6 @@ string doublehex = "000102030405060708090A0B0C0D0E0F101112131415161718191A1B1C1D // FF is contained twice, to map 256 to FF too // removes the need to bound() -bool RadarMap_Make(float argc); +bool RadarMap_Make(int argc); #endif diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 6de4507b17..c3e5bf9015 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -15,12 +15,12 @@ #include "../player.qh" #include "../g_world.qh" #include "../ipban.qh" -#include "../playerdemo.qh" #include "../teamplay.qh" #include "../bot/api.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> +#include <common/gamemodes/_mod.qh> #include <common/constants.qh> #include <common/net_linked.qh> @@ -31,13 +31,6 @@ #include <common/monsters/sv_monsters.qh> - -void PutObserverInServer(entity this); - -// ===================================================== -// Server side game commands code, reworked by Samual -// ===================================================== - // used by GameCommand_make_mapinfo() void make_mapinfo_Think(entity this) { @@ -95,7 +88,7 @@ void changematchtime(float delta, float mi, float ma) // Command Sub-Functions // ======================= -void GameCommand_adminmsg(float request, float argc) +void GameCommand_adminmsg(int request, int argc) { switch (request) { @@ -167,7 +160,7 @@ void GameCommand_adminmsg(float request, float argc) } } -void GameCommand_allready(float request) +void GameCommand_allready(int request) { switch (request) { @@ -187,7 +180,7 @@ void GameCommand_allready(float request) } } -void GameCommand_allspec(float request, float argc) +void GameCommand_allspec(int request, int argc) { switch (request) { @@ -216,7 +209,7 @@ void GameCommand_allspec(float request, float argc) } } -void GameCommand_anticheat(float request, float argc) +void GameCommand_anticheat(int request, int argc) { switch (request) { @@ -247,7 +240,7 @@ void GameCommand_anticheat(float request, float argc) } } -void GameCommand_bbox(float request) +void GameCommand_bbox(int request) { switch (request) { @@ -320,7 +313,7 @@ void GameCommand_bbox(float request) } } -void GameCommand_bot_cmd(float request, float argc, string command) +void GameCommand_bot_cmd(int request, int argc, string command) { switch (request) { @@ -452,7 +445,7 @@ void GameCommand_bot_cmd(float request, float argc, string command) } } -void GameCommand_cointoss(float request, float argc) +void GameCommand_cointoss(int request, int argc) { switch (request) { @@ -476,7 +469,7 @@ void GameCommand_cointoss(float request, float argc) } } -void GameCommand_database(float request, float argc) +void GameCommand_database(int request, int argc) { switch (request) { @@ -519,7 +512,7 @@ void GameCommand_database(float request, float argc) } } -void GameCommand_defer_clear(float request, float argc) +void GameCommand_defer_clear(int request, int argc) { switch (request) { @@ -556,14 +549,14 @@ void GameCommand_defer_clear(float request, float argc) } } -void GameCommand_defer_clear_all(float request) +void GameCommand_defer_clear_all(int request) { switch (request) { case CMD_REQUEST_COMMAND: { int n = 0; - float argc; + int argc; FOREACH_CLIENT(true, { argc = tokenize_console(strcat("defer_clear ", ftos(etof(it)))); @@ -585,7 +578,7 @@ void GameCommand_defer_clear_all(float request) } } -void GameCommand_delrec(float request, float argc) // perhaps merge later with records and printstats and such? +void GameCommand_delrec(int request, int argc) // perhaps merge later with records and printstats and such? { switch (request) { @@ -612,7 +605,7 @@ void GameCommand_delrec(float request, float argc) // perhaps merge later with } } -void GameCommand_effectindexdump(float request) +void GameCommand_effectindexdump(int request) { switch (request) { @@ -726,7 +719,7 @@ void GameCommand_effectindexdump(float request) } } -void GameCommand_extendmatchtime(float request) +void GameCommand_extendmatchtime(int request) { switch (request) { @@ -747,7 +740,7 @@ void GameCommand_extendmatchtime(float request) } } -void GameCommand_gametype(float request, float argc) +void GameCommand_gametype(int request, int argc) { switch (request) { @@ -797,7 +790,7 @@ void GameCommand_gametype(float request, float argc) } } -void GameCommand_gettaginfo(float request, float argc) +void GameCommand_gettaginfo(int request, int argc) { switch (request) { @@ -862,7 +855,7 @@ void GameCommand_gettaginfo(float request, float argc) } } -void GameCommand_animbench(float request, float argc) +void GameCommand_animbench(int request, int argc) { switch (request) { @@ -921,7 +914,7 @@ void GameCommand_animbench(float request, float argc) } } -void GameCommand_gotomap(float request, float argc) +void GameCommand_gotomap(int request, int argc) { switch (request) { @@ -946,7 +939,7 @@ void GameCommand_gotomap(float request, float argc) } } -void GameCommand_lockteams(float request) +void GameCommand_lockteams(int request) { switch (request) { @@ -975,7 +968,7 @@ void GameCommand_lockteams(float request) } } -void GameCommand_make_mapinfo(float request) +void GameCommand_make_mapinfo(int request) { switch (request) { @@ -1001,7 +994,7 @@ void GameCommand_make_mapinfo(float request) } } -void GameCommand_moveplayer(float request, float argc) +void GameCommand_moveplayer(int request, int argc) { switch (request) { @@ -1059,11 +1052,12 @@ void GameCommand_moveplayer(float request, float argc) { // set up float team_id; - float save = client.team_forced; - client.team_forced = 0; + int save = Player_GetForcedTeamIndex(client); + Player_SetForcedTeamIndex(client, TEAM_FORCE_DEFAULT); // find the team to move the player to team_id = Team_ColorToTeam(destination); + entity balance; if (team_id == client.team) // already on the destination team { // keep the forcing undone @@ -1072,30 +1066,72 @@ void GameCommand_moveplayer(float request, float argc) } else if (team_id == 0) // auto team { - CheckAllowedTeams(client); - team_id = Team_NumberToTeam(FindSmallestTeam(client, false)); + balance = TeamBalance_CheckAllowedTeams(client); + team_id = Team_IndexToTeam(TeamBalance_FindBestTeam(balance, client, false)); } else { - CheckAllowedTeams(client); + balance = TeamBalance_CheckAllowedTeams(client); } - client.team_forced = save; + Player_SetForcedTeamIndex(client, save); // Check to see if the destination team is even available switch (team_id) { - case NUM_TEAM_1: if (c1 == -1) { LOG_INFO("Sorry, can't move player to red team if it doesn't exist."); return; } break; - case NUM_TEAM_2: if (c2 == -1) { LOG_INFO("Sorry, can't move player to blue team if it doesn't exist."); return; } break; - case NUM_TEAM_3: if (c3 == -1) { LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist."); return; } break; - case NUM_TEAM_4: if (c4 == -1) { LOG_INFO("Sorry, can't move player to pink team if it doesn't exist."); return; } break; - - default: LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist."); + case NUM_TEAM_1: + { + if (!TeamBalance_IsTeamAllowed(balance, 1)) + { + LOG_INFO("Sorry, can't move player to red team if it doesn't exist."); + TeamBalance_Destroy(balance); + return; + } + TeamBalance_Destroy(balance); + break; + } + case NUM_TEAM_2: + { + if (!TeamBalance_IsTeamAllowed(balance, 2)) + { + LOG_INFO("Sorry, can't move player to blue team if it doesn't exist."); + TeamBalance_Destroy(balance); + return; + } + TeamBalance_Destroy(balance); + break; + } + case NUM_TEAM_3: + { + if (!TeamBalance_IsTeamAllowed(balance, 3)) + { + LOG_INFO("Sorry, can't move player to yellow team if it doesn't exist."); + TeamBalance_Destroy(balance); + return; + } + TeamBalance_Destroy(balance); + break; + } + case NUM_TEAM_4: + { + if (!TeamBalance_IsTeamAllowed(balance, 4)) + { + LOG_INFO("Sorry, can't move player to pink team if it doesn't exist."); + TeamBalance_Destroy(balance); + return; + } + TeamBalance_Destroy(balance); + break; + } + default: + { + LOG_INFO("Sorry, can't move player here if team ", destination, " doesn't exist."); return; + } } // If so, lets continue and finally move the player - client.team_forced = 0; - if (MoveToTeam(client, team_id, 6)) + Player_SetForcedTeamIndex(client, TEAM_FORCE_DEFAULT); + if (MoveToTeam(client, Team_TeamToIndex(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."); @@ -1143,7 +1179,7 @@ void GameCommand_moveplayer(float request, float argc) } } -void GameCommand_nospectators(float request) +void GameCommand_nospectators(int request) { switch (request) { @@ -1172,87 +1208,7 @@ void GameCommand_nospectators(float request) } } -void GameCommand_playerdemo(float request, float argc) -{ - switch (request) - { - case CMD_REQUEST_COMMAND: - { - if (argv(2) && argv(3)) - { - entity client; - float i, n, accepted; - - switch (argv(1)) - { - case "read": - { - client = GetIndexedEntity(argc, 2); - accepted = VerifyClientEntity(client, false, true); - - if (accepted <= 0) - { - LOG_INFO("playerdemo: read: ", GetClientErrorString(accepted, argv(2)), "."); - return; - } - - playerdemo_open_read(client, argv(next_token)); - return; - } - - case "write": - { - client = GetIndexedEntity(argc, 2); - accepted = VerifyClientEntity(client, false, false); - - if (accepted <= 0) - { - LOG_INFO("playerdemo: write: ", GetClientErrorString(accepted, argv(2)), "."); - return; - } - - playerdemo_open_write(client, argv(next_token)); - return; - } - - case "auto_read_and_write": - { - n = GetFilteredNumber(argv(3)); - cvar_set("bot_number", ftos(n)); - - localcmd("wait; wait; wait\n"); - for (i = 0; i < n; ++i) - localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); - localcmd("sv_cmd playerdemo write 1 ", ftos(n + 1), "\n"); - return; - } - - case "auto_read": - { - n = GetFilteredNumber(argv(3)); - cvar_set("bot_number", ftos(n)); - - localcmd("wait; wait; wait\n"); - for (i = 0; i < n; ++i) - localcmd("sv_cmd playerdemo read ", ftos(i + 2), " ", argv(2), ftos(i + 1), "\n"); - return; - } - } - } - } - - default: - LOG_INFO("Incorrect parameters for ^2playerdemo^7"); - case CMD_REQUEST_USAGE: - { - LOG_INFO("Usage:^3 sv_cmd playerdemo command (entitynumber filename | entitynumber botnumber)"); - LOG_INFO(" Full list of commands here: \"read, write, auto_read_and_write, auto_read.\""); - return; - } - } -} - -void GameCommand_printstats(float request) +void GameCommand_printstats(int request) { switch (request) { @@ -1273,7 +1229,7 @@ void GameCommand_printstats(float request) } } -void GameCommand_radarmap(float request, float argc) +void GameCommand_radarmap(int request, int argc) { switch (request) { @@ -1295,7 +1251,7 @@ void GameCommand_radarmap(float request, float argc) } } -void GameCommand_reducematchtime(float request) +void GameCommand_reducematchtime(int request) { switch (request) { @@ -1316,7 +1272,7 @@ void GameCommand_reducematchtime(float request) } } -void GameCommand_setbots(float request, float argc) +void GameCommand_setbots(int request, int argc) { switch (request) { @@ -1343,7 +1299,7 @@ void GameCommand_setbots(float request, float argc) } } -void GameCommand_shuffleteams(float request) +void GameCommand_shuffleteams(int request) { switch (request) { @@ -1356,7 +1312,7 @@ void GameCommand_shuffleteams(float request) } FOREACH_CLIENT(IS_PLAYER(it) || it.caplayer, { - if (it.team_forced) { + if (Player_HasRealForcedTeam(it)) { // we could theoretically assign forced players to their teams // and shuffle the rest to fill the empty spots but in practise // either all players or none are gonna have forced teams @@ -1366,16 +1322,23 @@ void GameCommand_shuffleteams(float request) }); int number_of_teams = 0; - CheckAllowedTeams(NULL); - if (c1 >= 0) number_of_teams = max(1, number_of_teams); - if (c2 >= 0) number_of_teams = max(2, number_of_teams); - if (c3 >= 0) number_of_teams = max(3, number_of_teams); - if (c4 >= 0) number_of_teams = max(4, number_of_teams); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (TeamBalance_IsTeamAllowed(balance, i)) + { + number_of_teams = max(i, number_of_teams); + } + } + TeamBalance_Destroy(balance); int team_index = 0; FOREACH_CLIENT_RANDOM(IS_PLAYER(it) || it.caplayer, { - int target_team_number = Team_NumberToTeam(team_index + 1); - if (it.team != target_team_number) MoveToTeam(it, target_team_number, 6); + int target_team_index = team_index + 1; + if (Entity_GetTeamIndex(it) != target_team_index) + { + MoveToTeam(it, target_team_index, 6); + } team_index = (team_index + 1) % number_of_teams; }); @@ -1394,7 +1357,7 @@ void GameCommand_shuffleteams(float request) } } -void GameCommand_stuffto(float request, float argc) +void GameCommand_stuffto(int request, int argc) { // This... is a fairly dangerous and powerful command... - It allows any arguments to be sent to a client via rcon. // Because of this, it is disabled by default and must be enabled by the server owner when doing compilation. That way, @@ -1443,7 +1406,7 @@ void GameCommand_stuffto(float request, float argc) #endif } -void GameCommand_trace(float request, float argc) +void GameCommand_trace(int request, int argc) { switch (request) { @@ -1577,10 +1540,13 @@ void GameCommand_trace(float request, float argc) if (argc == 4 || argc == 5) { e = nextent(NULL); + int dphitcontentsmask_save = e.dphitcontentsmask; + e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; if (tracewalk(e, stov(argv(2)), e.mins, e.maxs, stov(argv(3)), stof(argv(4)), MOVE_NORMAL)) LOG_INFO("can walk"); else LOG_INFO("cannot walk"); + e.dphitcontentsmask = dphitcontentsmask_save; return; } } @@ -1616,7 +1582,7 @@ void GameCommand_trace(float request, float argc) } } -void GameCommand_unlockteams(float request) +void GameCommand_unlockteams(int request) { switch (request) { @@ -1645,7 +1611,7 @@ void GameCommand_unlockteams(float request) } } -void GameCommand_warp(float request, float argc) +void GameCommand_warp(int request, int argc) { switch (request) { @@ -1684,7 +1650,7 @@ void GameCommand_warp(float request, float argc) /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void GameCommand_(float request) +void GameCommand_(int request) { switch(request) { @@ -1732,7 +1698,6 @@ SERVER_COMMAND(lockteams, "Disable the ability for players to switch or enter te SERVER_COMMAND(make_mapinfo, "Automatically rebuild mapinfo files") { GameCommand_make_mapinfo(request); } SERVER_COMMAND(moveplayer, "Change the team/status of a player") { GameCommand_moveplayer(request, arguments); } SERVER_COMMAND(nospectators, "Automatically remove spectators from a match") { GameCommand_nospectators(request); } -SERVER_COMMAND(playerdemo, "Control the ability to save demos of players") { GameCommand_playerdemo(request, arguments); } SERVER_COMMAND(printstats, "Dump eventlog player stats and other score information") { GameCommand_printstats(request); } SERVER_COMMAND(radarmap, "Generate a radar image of the map") { GameCommand_radarmap(request, arguments); } SERVER_COMMAND(reducematchtime, "Decrease the timelimit value incrementally") { GameCommand_reducematchtime(request); } @@ -1748,7 +1713,7 @@ void GameCommand_macro_help() FOREACH(SERVER_COMMANDS, true, { LOG_INFOF(" ^2%s^7: %s", it.m_name, it.m_description); }); } -float GameCommand_macro_command(float argc, string command) +float GameCommand_macro_command(int argc, string command) { string c = strtolower(argv(0)); FOREACH(SERVER_COMMANDS, it.m_name == c, { @@ -1758,7 +1723,7 @@ float GameCommand_macro_command(float argc, string command) return false; } -float GameCommand_macro_usage(float argc) +float GameCommand_macro_usage(int argc) { string c = strtolower(argv(1)); FOREACH(SERVER_COMMANDS, it.m_name == c, { @@ -1781,7 +1746,7 @@ void GameCommand_macro_write_aliases(float fh) void GameCommand(string command) { - float argc = tokenize_console(command); + int argc = tokenize_console(command); // Guide for working with argc arguments by example: // argc: 1 - 2 - 3 - 4 diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 6d037473dd..51cf55ce3a 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -10,11 +10,13 @@ #include "../g_damage.qh" #include "../g_world.qh" +#include "../teamplay.qh" #include "../race.qh" #include "../round_handler.qh" #include "../scores.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> +#include <common/gamemodes/_mod.qh> #include <common/constants.qh> #include <common/net_linked.qh> @@ -128,19 +130,15 @@ void VoteReset() if (vote_called) { - strunzone(vote_called_command); - strunzone(vote_called_display); - strunzone(vote_caller_name); + strfree(vote_called_command); + strfree(vote_called_display); + strfree(vote_caller_name); } vote_called = VOTE_NULL; vote_caller = NULL; - vote_caller_name = string_null; vote_endtime = 0; - vote_called_command = string_null; - vote_called_display = string_null; - vote_parsed_command = string_null; vote_parsed_display = string_null; @@ -367,7 +365,7 @@ void reset_map(bool dorespawn) if (it.reset2) it.reset2(it); }); - FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it); }); + FOREACH_CLIENT(IS_PLAYER(it) && STAT(FROZEN, it), { Unfreeze(it, false); }); // Moving the player reset code here since the player-reset depends // on spawnpoint entities which have to be reset first --blub @@ -517,7 +515,7 @@ float Votecommand_check_assignment(entity caller, float assignment) return false; } -string VoteCommand_extractcommand(string input, float startpos, float argc) +string VoteCommand_extractcommand(string input, float startpos, int argc) { string output; @@ -574,7 +572,7 @@ string ValidateMap(string validated_map, entity caller) return validated_map; } -float VoteCommand_checkargs(float startpos, float argc) +float VoteCommand_checkargs(float startpos, int argc) { float p, q, check, minargs; string cvarname = strcat("sv_vote_command_restriction_", argv(startpos)); @@ -648,7 +646,7 @@ float VoteCommand_checkargs(float startpos, float argc) return true; } -int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, float argc) +int VoteCommand_parse(entity caller, string vote_command, string vote_list, float startpos, int argc) { string first_command = argv(startpos); int missing_chars = argv_start_index(startpos); @@ -660,6 +658,14 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa if (!VoteCommand_checkargs(startpos, argc)) return 0; + switch (MUTATOR_CALLHOOK(VoteCommand_Parse, caller, first_command, vote_command, startpos, argc)) + { + case MUT_VOTEPARSE_CONTINUE: { break; } + case MUT_VOTEPARSE_SUCCESS: { return 1; } + case MUT_VOTEPARSE_INVALID: { return -1; } + case MUT_VOTEPARSE_UNACCEPTABLE: { return 0; } + } + switch (first_command) // now go through and parse the proper commands to adjust as needed. { case "kick": @@ -708,6 +714,16 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa break; } + case "restart": + { + // add a delay so that vote result can be seen and announcer can be heard + // if the vote is accepted + vote_parsed_command = strcat("defer 1 ", vote_command); + vote_parsed_display = strzone(strcat("^1", vote_command)); + + break; + } + default: { vote_parsed_command = vote_command; @@ -725,7 +741,7 @@ int VoteCommand_parse(entity caller, string vote_command, string vote_list, floa // Command Sub-Functions // ======================= -void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY +void VoteCommand_abstain(int request, entity caller) // CLIENT ONLY { switch (request) { @@ -757,7 +773,7 @@ void VoteCommand_abstain(float request, entity caller) // CLIENT ONLY } } -void VoteCommand_call(float request, entity caller, float argc, string vote_command) // BOTH +void VoteCommand_call(int request, entity caller, int argc, string vote_command) // BOTH { switch (request) { @@ -819,14 +835,15 @@ void VoteCommand_call(float request, entity caller, float argc, string vote_comm } FOREACH_CLIENT(IS_REAL_CLIENT(it), { ++tmp_playercount; }); - 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)); Nagger_VoteChanged(); VoteCount(true); // needed if you are the only one + + if (tmp_playercount > 1 && vote_called != VOTE_NULL) + Send_Notification(NOTIF_ALL, NULL, MSG_ANNCE, ANNCE_VOTE_CALL); } return; @@ -844,7 +861,7 @@ void VoteCommand_call(float request, entity caller, float argc, string vote_comm } } -void VoteCommand_master(float request, entity caller, float argc, string vote_command) // CLIENT ONLY +void VoteCommand_master(int request, entity caller, int argc, string vote_command) // CLIENT ONLY { switch (request) { @@ -959,7 +976,7 @@ void VoteCommand_master(float request, entity caller, float argc, string vote_co } } -void VoteCommand_no(float request, entity caller) // CLIENT ONLY +void VoteCommand_no(int request, entity caller) // CLIENT ONLY { switch (request) { @@ -997,7 +1014,7 @@ void VoteCommand_no(float request, entity caller) // CLIENT ONLY } } -void VoteCommand_status(float request, entity caller) // BOTH +void VoteCommand_status(int request, entity caller) // BOTH { switch (request) { @@ -1019,7 +1036,7 @@ void VoteCommand_status(float request, entity caller) // BOTH } } -void VoteCommand_stop(float request, entity caller) // BOTH +void VoteCommand_stop(int request, entity caller) // BOTH { switch (request) { @@ -1041,7 +1058,7 @@ void VoteCommand_stop(float request, entity caller) // BOTH } } -void VoteCommand_yes(float request, entity caller) // CLIENT ONLY +void VoteCommand_yes(int request, entity caller) // CLIENT ONLY { switch (request) { @@ -1076,7 +1093,7 @@ void VoteCommand_yes(float request, entity caller) // CLIENT ONLY /* use this when creating a new command, making sure to place it in alphabetical order... also, ** ADD ALL NEW COMMANDS TO commands.cfg WITH PROPER ALIASES IN THE SAME FASHION! -void VoteCommand_(float request) +void VoteCommand_(int request) { switch(request) { @@ -1114,7 +1131,7 @@ void VoteCommand_(float request) VOTE_COMMAND("yes", VoteCommand_yes(request, caller), "Select yes in current vote", VC_ASGNMNT_CLIENTONLY) \ /* nothing */ -void VoteCommand_macro_help(entity caller, float argc) +void VoteCommand_macro_help(entity caller, int argc) { string command_origin = GetCommandPrefix(caller); @@ -1147,7 +1164,7 @@ void VoteCommand_macro_help(entity caller, float argc) } } -float VoteCommand_macro_command(entity caller, float argc, string vote_command) +float VoteCommand_macro_command(entity caller, int argc, string vote_command) { #define VOTE_COMMAND(name, function, description, assignment) \ { if (Votecommand_check_assignment(caller, assignment)) { if (name == strtolower(argv(1))) { function; return true; } } } @@ -1163,7 +1180,7 @@ float VoteCommand_macro_command(entity caller, float argc, string vote_command) // Main function handling vote commands // ====================================== -void VoteCommand(float request, entity caller, float argc, string vote_command) +void VoteCommand(int request, entity caller, int argc, string vote_command) { // Guide for working with argc arguments by example: // argc: 1 - 2 - 3 - 4 diff --git a/qcsrc/server/command/vote.qh b/qcsrc/server/command/vote.qh index 7b10b37eb7..988e91bbce 100644 --- a/qcsrc/server/command/vote.qh +++ b/qcsrc/server/command/vote.qh @@ -41,7 +41,7 @@ string vote_parsed_display; // visual string which is fixed after being parsed // allow functions to be used in other code like g_world.qc and teamplay.qc void VoteThink(); void VoteReset(); -void VoteCommand(float request, entity caller, float argc, string vote_command); +void VoteCommand(int request, entity caller, int argc, string vote_command); // warmup and nagger stuff const float RESTART_COUNTDOWN = 10; @@ -54,3 +54,4 @@ void reset_map(float dorespawn); void ReadyCount(); void ReadyRestart_force(); void VoteCount(float first_count); +void Nagger_Init(); diff --git a/qcsrc/server/compat/quake.qc b/qcsrc/server/compat/quake.qc index e830fe6c4f..c80da0af37 100644 --- a/qcsrc/server/compat/quake.qc +++ b/qcsrc/server/compat/quake.qc @@ -15,4 +15,4 @@ 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(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);} +SPAWNFUNC_ITEM_COND(item_health, (this.spawnflags & 2), ITEM_HealthMega, ITEM_HealthMedium) diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc index a1e1294027..65f231374f 100644 --- a/qcsrc/server/compat/quake3.qc +++ b/qcsrc/server/compat/quake3.qc @@ -4,10 +4,11 @@ #include <server/miscfunctions.qh> #include <server/items.qh> #include <server/resources.qh> +#include <common/t_items.qh> +#include <common/mapobjects/triggers.qh> +#include <common/mapobjects/trigger/counter.qh> #include <common/weapons/_all.qh> -spawnfunc(target_items); - //*********************** //QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons //*********************** @@ -107,7 +108,7 @@ void target_init_use(entity this, entity actor, entity trigger) SetResourceAmount(actor, RESOURCE_PLASMA, start_ammo_plasma); SetResourceAmount(actor, RESOURCE_FUEL, start_ammo_fuel); - actor.weapons = start_weapons; + STAT(WEAPONS, actor) = start_weapons; if (this.spawnflags & 32) { // TODO @@ -141,33 +142,33 @@ void target_give_init(entity this) IL_EACH(g_items, it.targetname == this.target, { if (it.classname == "weapon_devastator") { - this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo); + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(devastator, ammo)); // WEAPONTODO this.netname = cons(this.netname, "devastator"); } else if (it.classname == "weapon_vortex") { - this.ammo_cells += it.count * WEP_CVAR_PRI(vortex, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_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 + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS) + it.count * WEP_CVAR_PRI(electro, ammo)); // WEAPONTODO this.netname = cons(this.netname, "electro"); } else if (it.classname == "weapon_hagar") { - this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(hagar, ammo)); // WEAPONTODO this.netname = cons(this.netname, "hagar"); } else if (it.classname == "weapon_crylink") { - this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo); + SetResourceAmountExplicit(this, RESOURCE_CELLS, GetResourceAmount(this, RESOURCE_CELLS) + it.count * WEP_CVAR_PRI(crylink, ammo)); // WEAPONTODO this.netname = cons(this.netname, "crylink"); } else if (it.classname == "weapon_mortar") { - this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO + SetResourceAmountExplicit(this, RESOURCE_ROCKETS, GetResourceAmount(this, RESOURCE_ROCKETS) + it.count * WEP_CVAR_PRI(mortar, ammo)); // WEAPONTODO this.netname = cons(this.netname, "mortar"); } else if (it.classname == "item_armor_mega") - this.armorvalue = 100; + SetResourceAmountExplicit(this, RESOURCE_ARMOR, 100); else if (it.classname == "item_health_mega") - this.health = 200; + SetResourceAmountExplicit(this, RESOURCE_HEALTH, 200); //remove(it); // removing ents in init functions causes havoc, workaround: setthink(it, SUB_Remove); it.nextthink = time; @@ -183,6 +184,37 @@ spawnfunc(target_give) InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET); } +void score_use(entity this, entity actor, entity trigger) +{ + if(!IS_PLAYER(actor)) + return; + actor.fragsfilter_cnt += this.count; +} +spawnfunc(target_score) +{ + if(!g_cts) { delete(this); return; } + + if(!this.count) + this.count = 1; + this.use = score_use; +} + +void fragsfilter_use(entity this, entity actor, entity trigger) +{ + if(!IS_PLAYER(actor)) + return; + if(actor.fragsfilter_cnt >= this.frags) + SUB_UseTargets(this, actor, trigger); +} +spawnfunc(target_fragsFilter) +{ + if(!g_cts) { delete(this); return; } + + if(!this.frags) + this.frags = 1; + this.use = fragsfilter_use; +} + //spawnfunc(item_flight) /* handled by buffs mutator */ //spawnfunc(item_haste) /* handled by buffs mutator */ //spawnfunc(item_health) /* handled in t_quake.qc */ @@ -233,6 +265,8 @@ bool DoesQ3ARemoveThisEntity(entity this) gametypename = "team"; if(g_ctf) gametypename = "ctf"; + if(g_duel) + gametypename = "tournament"; if(maxclients == 1) gametypename = "single"; // we do not have the other types (oneflag, obelisk, harvester, teamtournament) diff --git a/qcsrc/server/compat/quake3.qh b/qcsrc/server/compat/quake3.qh index 6f70f09bee..20e4879d9a 100644 --- a/qcsrc/server/compat/quake3.qh +++ b/qcsrc/server/compat/quake3.qh @@ -1 +1,5 @@ #pragma once + +bool DoesQ3ARemoveThisEntity(entity this); + +.int fragsfilter_cnt; diff --git a/qcsrc/server/constants.qh b/qcsrc/server/constants.qh index 86ed86da1d..f98d586fb5 100644 --- a/qcsrc/server/constants.qh +++ b/qcsrc/server/constants.qh @@ -17,5 +17,3 @@ const int RESPAWN_DENY = 4; #define EFMASK_CHEAP (EF_ADDITIVE | EF_DOUBLESIDED | EF_FULLBRIGHT | EF_NODEPTHTEST | EF_NODRAW | EF_NOGUNBOB | EF_NOSHADOW | EF_LOWPRECISION | EF_SELECTABLE | EF_TELEPORT_BIT) const int NUM_PLAYERSKINS_TEAMPLAY = 3; - -const int ASSAULT_VALUE_INACTIVE = 1000; diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 4b600e3fce..5b5d8a66b7 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -7,7 +7,7 @@ // Globals -float g_footsteps, g_grappling_hook, g_instagib; +float g_footsteps, g_grappling_hook; float g_warmup_allguns; float g_warmup_allow_timeout; float warmup_stage; @@ -25,9 +25,7 @@ float currentbots; float bots_would_leave; void UpdateFrags(entity player, int f); -.float totalfrags; - -float team1_score, team2_score, team3_score, team4_score; +.int totalfrags; // flag set on worldspawn so that the code knows if it is dedicated or not float server_is_dedicated; @@ -36,12 +34,14 @@ float server_is_dedicated; .void(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) event_damage; +.bool(entity targ, entity inflictor, float amount, float limit) event_heal; + //.string wad; //.string map; //.float worldtype; // Needed for dynamic clientwalls -.float inactive; // Clientwall disappears when inactive +.bool inactive; // Clientwall disappears when inactive .float alpha_max, alpha_min; .float fade_start, fade_end, fade_vertical_offset; .float default_solid; // Variable to store default .solid for clientwalls @@ -168,6 +168,9 @@ float default_weapon_alpha; .float cvar_cl_jetpack_jump; .float cvar_cl_movement_track_canjump; .float cvar_cl_newusekeysupported; +.float cvar_cl_cts_noautoswitch; +.bool cvar_cl_weapon_switch_reload; +.bool cvar_cl_weapon_switch_fallback_to_impulse; .string cvar_g_xonoticversion; .string cvar_cl_weaponpriority; @@ -182,8 +185,6 @@ float default_weapon_alpha; string gamemode_name; -float startitem_failed; - string W_Apply_Weaponreplace(string in); void FixIntermissionClient(entity e); @@ -192,8 +193,6 @@ void FixClientCvars(entity e); // WEAPONTODO: remove this //WepSet weaponsInMap; -#define weapons _STAT(WEAPONS) - .float respawn_countdown; // next number to count float bot_waypoints_for_items; @@ -205,10 +204,7 @@ float bot_waypoints_for_items; #else #define ATTACK_FINISHED_FOR(ent, w, slot) ((ent).attack_finished_single[slot]) #endif -#define ATTACK_FINISHED(ent, slot) ATTACK_FINISHED_FOR(ent, ent.(weaponentity).m_weapon.m_id, slot) - -// assault game mode: Which team is attacking in this round? -float assault_attacker_team; +#define ATTACK_FINISHED(ent, w) ATTACK_FINISHED_FOR(ent, ent.(w).m_weapon.m_id, weaponslot(w)) // speedrun: when 1, player auto teleports back when capture timeout happens .float speedrunning; @@ -217,15 +213,12 @@ float assault_attacker_team; float ServerProgsDB; float TemporaryDB; -.float team_saved; +.int team_saved; bool some_spawn_has_been_used; int have_team_spawns; // 0 = no team spawns requested, -1 = team spawns requested but none found, 1 = team spawns requested and found int have_team_spawns_forteams; // if Xth bit is 1 then team X has spawns else it has no spawns; team 0 is the "no-team" -// set when showing a kill countdown -.entity killindicator; - .bool canteamdamage; void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); @@ -233,8 +226,6 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d // WEAPONTODO #define DMG_NOWEP (weaponentities[0]) -float lockteams; - float sv_maxidle; float sv_maxidle_spectatorsareidle; int sv_maxidle_slots; @@ -281,9 +272,6 @@ void W_Porto_Remove (entity p); // Nexball float g_nexball_meter_period; -void SUB_DontUseTargets(entity this, entity actor, entity trigger); -void SUB_UseTargets(entity this, entity actor, entity trigger); - .void(entity this) reset; // if set, an entity is reset using this .void(entity this) reset2; // if set, an entity is reset using this (after calling ALL the reset functions for other entities) @@ -317,14 +305,17 @@ float client_cefc_accumulatortime; .float weapon_load[Weapons_MAX]; .int ammo_none; // used by the reloading system, must always be 0 -.float clip_load; -.float old_clip_load; -.float clip_size; +.int clip_load; +.int old_clip_load; +.int clip_size; .int minelayer_mines; .float vortex_charge; .float vortex_charge_rottime; .float vortex_chargepool_ammo; +.float oknex_charge; +.float oknex_charge_rottime; +.float oknex_chargepool_ammo; .int hagar_load; .int grab; // 0 = can't grab, 1 = owner can grab, 2 = owner and team mates can grab, 3 = anyone can grab @@ -333,9 +324,9 @@ float client_cefc_accumulatortime; // when doing this, hagar can go through clones // #define PROJECTILE_MAKETRIGGER(e) (e).solid = SOLID_BBOX -.float spectatee_status; -.float zoomstate; -.float restriction; +.int spectatee_status; +.bool zoomstate; +.int restriction; .entity clientdata; .entity personal; @@ -345,24 +336,27 @@ string deathmessage; .bool just_joined; .float cvar_cl_weaponimpulsemode; -.float selectweapon; // last selected weapon of the player +.int selectweapon; // last selected weapon of the player .float ballistics_density; // wall piercing factor, larger = bullet can pass through more -const float ACTIVE_NOT = 0; -const float ACTIVE_ACTIVE = 1; -const float ACTIVE_IDLE = 2; -const float ACTIVE_BUSY = 2; -const float ACTIVE_TOGGLE = 3; -.float active; +//const int FROZEN_NOT = 0; +const int FROZEN_NORMAL = 1; +const int FROZEN_TEMP_REVIVING = 2; +const int FROZEN_TEMP_DYING = 3; + +const int ACTIVE_NOT = 0; +const int ACTIVE_ACTIVE = 1; +const int ACTIVE_IDLE = 2; +const int ACTIVE_BUSY = 2; +const int ACTIVE_TOGGLE = 3; +.int active; .void (entity this, int act_state) setactive; .entity realowner; //float serverflags; -.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator - -.float player_blocked; +.bool player_blocked; .float revival_time; // time at which player was last revived .float revive_speed; // NOTE: multiplier (anything above 1 is instaheal) @@ -373,8 +367,6 @@ const float ACTIVE_TOGGLE = 3; .entity muzzle_flash; .float misc_bulletcounter; // replaces uzi & hlac bullet counter. -.int killindicator_teamchange; - void PlayerUseKey(entity this); USING(spawn_evalfunc_t, vector(entity this, entity player, entity spot, vector current)); @@ -382,7 +374,7 @@ USING(spawn_evalfunc_t, vector(entity this, entity player, entity spot, vector c string modname; -.float missile_flags; +.int missile_flags; const int MIF_SPLASH = BIT(1); const int MIF_ARC = BIT(2); const int MIF_PROXY = BIT(3); @@ -408,67 +400,43 @@ const int MIF_GUIDED_CONFUSABLE = MIF_GUIDED_HEAT | MIF_GUIDED_AI; .WepSet dual_weapons; IntrusiveList g_monsters; -STATIC_INIT(g_monsters) { g_monsters = IL_NEW(); } - IntrusiveList g_waypoints; -STATIC_INIT(g_waypoints) { g_waypoints = IL_NEW(); } - IntrusiveList g_vehicles; -STATIC_INIT(g_vehicles) { g_vehicles = IL_NEW(); } - IntrusiveList g_turrets; -STATIC_INIT(g_turrets) { g_turrets = IL_NEW(); } - IntrusiveList g_mines; -STATIC_INIT(g_mines) { g_mines = IL_NEW(); } - IntrusiveList g_projectiles; -STATIC_INIT(g_projectiles) { g_projectiles = IL_NEW(); } - IntrusiveList g_items; -STATIC_INIT(g_items) { g_items = IL_NEW(); } - IntrusiveList g_initforplayer; -STATIC_INIT(g_initforplayer) { g_initforplayer = IL_NEW(); } - IntrusiveList g_clones; -STATIC_INIT(g_clones) { g_clones = IL_NEW(); } - -IntrusiveList g_assault_destructibles; -STATIC_INIT(g_assault_destructibles) { g_assault_destructibles = IL_NEW(); } - -IntrusiveList g_assault_objectivedecreasers; -STATIC_INIT(g_assault_objectivedecreasers) { g_assault_objectivedecreasers = IL_NEW(); } - -IntrusiveList g_assault_objectives; -STATIC_INIT(g_assault_objectives) { g_assault_objectives = IL_NEW(); } - IntrusiveList g_spawnpoints; -STATIC_INIT(g_spawnpoints) { g_spawnpoints = IL_NEW(); } - IntrusiveList g_bot_targets; -STATIC_INIT(g_bot_targets) { g_bot_targets = IL_NEW(); } - IntrusiveList g_bot_dodge; -STATIC_INIT(g_bot_dodge) { g_bot_dodge = IL_NEW(); } - IntrusiveList g_damagedbycontents; -STATIC_INIT(g_damagedbycontents) { g_damagedbycontents = IL_NEW(); } - IntrusiveList g_railgunhit; -STATIC_INIT(g_railgunhit) { g_railgunhit = IL_NEW(); } - IntrusiveList g_ladders; -STATIC_INIT(g_ladders) { g_ladders = IL_NEW(); } - IntrusiveList g_locations; -STATIC_INIT(g_locations) { g_locations = IL_NEW(); } - IntrusiveList g_saved_team; -STATIC_INIT(g_saved_team) { g_saved_team = IL_NEW(); } - IntrusiveList g_monster_targets; -STATIC_INIT(g_monster_targets) { g_monster_targets = IL_NEW(); } - IntrusiveList g_pathlib_nodes; -STATIC_INIT(g_pathlib_nodes) { g_pathlib_nodes = IL_NEW(); } +STATIC_INIT(defs) +{ + g_monsters = IL_NEW(); + g_waypoints = IL_NEW(); + g_vehicles = IL_NEW(); + g_turrets = IL_NEW(); + g_mines = IL_NEW(); + g_projectiles = IL_NEW(); + g_items = IL_NEW(); + g_initforplayer = IL_NEW(); + g_clones = IL_NEW(); + g_spawnpoints = IL_NEW(); + g_bot_targets = IL_NEW(); + g_bot_dodge = IL_NEW(); + g_damagedbycontents = IL_NEW(); + g_railgunhit = IL_NEW(); + g_ladders = IL_NEW(); + g_locations = IL_NEW(); + g_saved_team = IL_NEW(); + g_monster_targets = IL_NEW(); + g_pathlib_nodes = IL_NEW(); +} diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 3fb000e708..1b8f838aa4 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -3,7 +3,8 @@ #include <common/effects/all.qh> #include "bot/api.qh" #include "g_hook.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> +#include "teamplay.qh" #include "scores.qh" #include "spawnpoints.qh" #include "../common/state.qh" @@ -13,6 +14,8 @@ #include "../common/vehicles/all.qh" #include "../common/items/_mod.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" +#include "../common/mutators/mutator/instagib/sv_instagib.qh" +#include "../common/mutators/mutator/buffs/buffs.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" @@ -23,6 +26,7 @@ #include "../common/playerstats.qh" #include "../common/teams.qh" #include "../common/util.qh" +#include <common/gamemodes/rules.qh> #include <common/weapons/_all.qh> #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/common.qh" @@ -32,7 +36,7 @@ void UpdateFrags(entity player, int f) GameRules_scoring_add_team(player, SCORE, f); } -void GiveFrags (entity attacker, entity targ, float f, int deathtype, .entity weaponentity) +void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity weaponentity) { // TODO route through PlayerScores instead if(game_stopped) return; @@ -60,51 +64,8 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype, .entity we GameRules_scoring_add(targ, DEATHS, 1); - if(targ != attacker) // not for suicides - if(g_weaponarena_random) - { - // after a frag, exchange the current weapon (or the culprit, if detectable) by a new random weapon - Weapon culprit = DEATH_WEAPONOF(deathtype); - if(!culprit) culprit = attacker.(weaponentity).m_weapon; - else if(!(attacker.weapons & (culprit.m_wepset))) culprit = attacker.(weaponentity).m_weapon; - - if(g_weaponarena_random_with_blaster && culprit == WEP_BLASTER) // WEAPONTODO: Shouldn't this be in a mutator? - { - // no exchange - } - else - { - if(!GiveFrags_randomweapons) - { - GiveFrags_randomweapons = new(GiveFrags_randomweapons); - } - - if(warmup_stage) - GiveFrags_randomweapons.weapons = WARMUP_START_WEAPONS; - else - GiveFrags_randomweapons.weapons = start_weapons; - - // all others (including the culprit): remove - GiveFrags_randomweapons.weapons &= ~attacker.weapons; - GiveFrags_randomweapons.weapons &= ~(culprit.m_wepset); - - // among the remaining ones, choose one by random - W_RandomWeapons(GiveFrags_randomweapons, 1); - - if(GiveFrags_randomweapons.weapons) - { - attacker.weapons |= GiveFrags_randomweapons.weapons; - attacker.weapons &= ~(culprit.m_wepset); - } - } - - // after a frag, choose another random weapon set - if (!(attacker.weapons & WepSet_FromWeapon(attacker.(weaponentity).m_weapon))) - W_SwitchWeapon_Force(attacker, w_getbestweapon(attacker, weaponentity), weaponentity); - } - // FIXME fix the mess this is (we have REAL points now!) - if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f)) + if(MUTATOR_CALLHOOK(GiveFragsForKill, attacker, targ, f, deathtype, attacker.(weaponentity))) f = M_ARGV(2, float); attacker.totalfrags += f; @@ -262,7 +223,6 @@ bool frag_centermessage_override(entity attacker, entity targ, int deathtype, in return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target); } -entity buff_FirstFromFlags(int _buffs); void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .entity weaponentity) { // Sanity check @@ -518,9 +478,9 @@ void Ice_Think(entity this) this.nextthink = time; } -void Freeze (entity targ, float revivespeed, float frozen_type, float show_waypoint) +void Freeze(entity targ, float revivespeed, int frozen_type, bool show_waypoint) { - if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // only specified entities can be freezed + if(!IS_PLAYER(targ) && !IS_MONSTER(targ)) // TODO: only specified entities can be freezed return; if(STAT(FROZEN, targ)) @@ -529,8 +489,8 @@ void Freeze (entity targ, float revivespeed, float frozen_type, float show_waypo float targ_maxhealth = ((IS_MONSTER(targ)) ? targ.max_health : start_health); STAT(FROZEN, targ) = frozen_type; - STAT(REVIVE_PROGRESS, targ) = ((frozen_type == 3) ? 1 : 0); - SetResourceAmount(targ, RESOURCE_HEALTH, ((frozen_type == 3) ? targ_maxhealth : 1)); + STAT(REVIVE_PROGRESS, targ) = ((frozen_type == FROZEN_TEMP_DYING) ? 1 : 0); + SetResourceAmount(targ, RESOURCE_HEALTH, ((frozen_type == FROZEN_TEMP_DYING) ? targ_maxhealth : 1)); targ.revive_speed = revivespeed; if(targ.bot_attack) IL_REMOVE(g_bot_targets, targ); @@ -565,20 +525,19 @@ void Freeze (entity targ, float revivespeed, float frozen_type, float show_waypo }); // add waypoint - if(show_waypoint) + if(MUTATOR_CALLHOOK(Freeze, targ, revivespeed, frozen_type) || show_waypoint) WaypointSprite_Spawn(WP_Frozen, 0, 0, targ, '0 0 64', NULL, targ.team, targ, waypointsprite_attached, true, RADARICON_WAYPOINT); } -void Unfreeze (entity targ) +void Unfreeze(entity targ, bool reset_health) { if(!STAT(FROZEN, targ)) return; - if(STAT(FROZEN, targ) && STAT(FROZEN, targ) != 3) // only reset health if target was frozen - { + if (reset_health && STAT(FROZEN, targ) != FROZEN_TEMP_DYING) SetResourceAmount(targ, RESOURCE_HEALTH, ((IS_PLAYER(targ)) ? start_health : targ.max_health)); - targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen; - } + + targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen; STAT(FROZEN, targ) = 0; STAT(REVIVE_PROGRESS, targ) = 0; @@ -603,9 +562,11 @@ void Unfreeze (entity targ) if(targ.iceblock) delete(targ.iceblock); targ.iceblock = NULL; + + MUTATOR_CALLHOOK(Unfreeze, targ); } -void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) +void Damage(entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { float complainteamdamage = 0; float mirrordamage = 0; @@ -634,9 +595,9 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d // These are ALWAYS lethal // No damage modification here // Instead, prepare the victim for his death... - SetResourceAmount(targ, RESOURCE_ARMOR, 0); + SetResourceAmountExplicit(targ, RESOURCE_ARMOR, 0); targ.spawnshieldtime = 0; - SetResourceAmount(targ, RESOURCE_HEALTH, 0.9); // this is < 1 + SetResourceAmountExplicit(targ, RESOURCE_HEALTH, 0.9); // this is < 1 targ.flags -= targ.flags & FL_GODMODE; damage = 100000; } @@ -677,7 +638,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d if(autocvar_g_mirrordamage_virtual) { - vector v = healtharmor_applydamage(attacker.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage); + vector v = healtharmor_applydamage(GetResourceAmount(attacker, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, mirrordamage); attacker.dmg_take += v.x; attacker.dmg_save += v.y; attacker.dmg_inflictor = inflictor; @@ -687,7 +648,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d if(autocvar_g_friendlyfire_virtual) { - vector v = healtharmor_applydamage(targ.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + vector v = healtharmor_applydamage(GetResourceAmount(targ, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); targ.dmg_take += v.x; targ.dmg_save += v.y; targ.dmg_inflictor = inflictor; @@ -713,7 +674,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d } // should this be changed at all? If so, in what way? - MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force); + MUTATOR_CALLHOOK(Damage_Calculate, inflictor, attacker, targ, deathtype, damage, mirrordamage, force, attacker.(weaponentity)); damage = M_ARGV(4, float); mirrordamage = M_ARGV(5, float); force = M_ARGV(6, vector); @@ -728,14 +689,11 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d } } - if(STAT(FROZEN, targ)) - if(deathtype != DEATH_HURTTRIGGER.m_id && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id) + if(deathtype != DEATH_HURTTRIGGER.m_id && deathtype != DEATH_TEAMCHANGE.m_id && deathtype != DEATH_AUTOTEAMCHANGE.m_id && STAT(FROZEN, targ)) { - if(autocvar_g_frozen_revive_falldamage > 0) - if(deathtype == DEATH_FALL.m_id) - if(damage >= autocvar_g_frozen_revive_falldamage) + if(autocvar_g_frozen_revive_falldamage > 0 && deathtype == DEATH_FALL.m_id && damage >= autocvar_g_frozen_revive_falldamage) { - Unfreeze(targ); + Unfreeze(targ, false); SetResourceAmount(targ, RESOURCE_HEALTH, autocvar_g_frozen_revive_falldamage_health); Send_Effect(EFFECT_ICEORGLASS, targ.origin, '0 0 0', 3); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED_FALL, targ.netname); @@ -746,7 +704,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d force *= autocvar_g_frozen_force; } - if(STAT(FROZEN, targ) && deathtype == DEATH_HURTTRIGGER.m_id && !autocvar_g_frozen_damage_trigger) + if(IS_PLAYER(targ) && STAT(FROZEN, targ) && deathtype == DEATH_HURTTRIGGER.m_id && !autocvar_g_frozen_damage_trigger) { Send_Effect(EFFECT_TELEPORT, targ.origin, '0 0 0', 1); @@ -779,7 +737,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d } } - if(!g_instagib) + if(!MUTATOR_IS_ENABLED(mutator_instagib)) { // apply strength multiplier if (attacker.items & ITEM_Strength.m_itemid) @@ -907,7 +865,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d } } -float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, +float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector inflictorvelocity, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float inflictorselfdamage, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity) // Returns total damage applies to creatures { @@ -1079,9 +1037,9 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in } if(targ == directhitentity || DEATH_ISSPECIAL(deathtype)) - Damage (targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force); + Damage(targ, inflictor, attacker, finaldmg, deathtype, weaponentity, nearest, force); else - Damage (targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force); + Damage(targ, inflictor, attacker, finaldmg, deathtype | HITTYPE_SPLASH, weaponentity, nearest, force); } } } @@ -1092,14 +1050,28 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in RadiusDamage_running = 0; if(!DEATH_ISSPECIAL(deathtype)) - accuracy_add(attacker, DEATH_WEAPONOF(deathtype).m_id, 0, min(coredamage, stat_damagedone)); + accuracy_add(attacker, DEATH_WEAPONOF(deathtype), 0, min(coredamage, stat_damagedone)); return total_damage_to_creatures; } -float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity) +float RadiusDamage(entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity) { - return RadiusDamageForSource (inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, false, forceintensity, deathtype, weaponentity, directhitentity); + return RadiusDamageForSource(inflictor, (inflictor.origin + (inflictor.mins + inflictor.maxs) * 0.5), inflictor.velocity, attacker, coredamage, edgedamage, rad, cantbe, mustbe, false, forceintensity, deathtype, weaponentity, directhitentity); +} + +bool Heal(entity targ, entity inflictor, float amount, float limit) +{ + if(game_stopped || (IS_CLIENT(targ) && CS(targ).killcount == FRAGS_SPECTATOR) || STAT(FROZEN, targ) || IS_DEAD(targ)) + return false; + + bool healed = false; + if(targ.event_heal) + healed = targ.event_heal(targ, inflictor, amount, limit); + // TODO: additional handling? what if the healing kills them? should this abort if healing would do so etc + // TODO: healing fx! + // TODO: armor healing? + return healed; } float Fire_IsBurning(entity e) @@ -1207,7 +1179,7 @@ float Fire_AddDamage(entity e, entity o, float d, float t, float dt) } } if(accuracy_isgooddamage(o, e)) - accuracy_add(o, DEATH_WEAPONOF(dt).m_id, 0, max(0, totaldamage - mindamage)); + accuracy_add(o, DEATH_WEAPONOF(dt), 0, max(0, totaldamage - mindamage)); return max(0, totaldamage - mindamage); // can never be negative, but to make sure } else @@ -1221,7 +1193,7 @@ float Fire_AddDamage(entity e, entity o, float d, float t, float dt) e.fire_owner = o; e.fire_hitsound = false; if(accuracy_isgooddamage(o, e)) - accuracy_add(o, DEATH_WEAPONOF(dt).m_id, 0, d); + accuracy_add(o, DEATH_WEAPONOF(dt), 0, d); return d; } } @@ -1260,11 +1232,11 @@ void Fire_ApplyDamage(entity e) } e.fire_hitsound = true; - if(!IS_INDEPENDENT_PLAYER(e)) - if(!STAT(FROZEN, e)) - FOREACH_CLIENT(IS_PLAYER(it) && it != e, { - if(!IS_DEAD(it)) - if(!IS_INDEPENDENT_PLAYER(it)) + if(!IS_INDEPENDENT_PLAYER(e) && !STAT(FROZEN, e)) + { + IL_EACH(g_damagedbycontents, it.damagedbycontents && it != e, + { + if(!IS_DEAD(it) && it.takedamage && !IS_INDEPENDENT_PLAYER(it)) if(boxesoverlap(e.absmin, e.absmax, it.absmin, it.absmax)) { t = autocvar_g_balance_firetransfer_time * (e.fire_endtime - time); @@ -1272,6 +1244,7 @@ void Fire_ApplyDamage(entity e) Fire_AddDamage(it, o, d, t, DEATH_FIRE.m_id); } }); + } } void Fire_ApplyEffect(entity e) diff --git a/qcsrc/server/g_damage.qh b/qcsrc/server/g_damage.qh index 4f68c2ea00..1d100e7a5a 100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@ -19,7 +19,7 @@ #include "defs.qh" #include <common/notifications/all.qh> #include <common/deathtypes/all.qh> - #include "mutators/_mod.qh" + #include <server/mutators/_mod.qh> #include <common/turrets/sv_turrets.qh> #include <common/vehicles/all.qh> #include <lib/csqcmodel/sv_model.qh> @@ -49,7 +49,7 @@ float damage_gooddamage; .float teamkill_soundtime; .entity teamkill_soundsource; .entity pusher; -.float istypefrag; +.bool istypefrag; .float taunt_soundtime; float IsFlying(entity a); @@ -58,7 +58,6 @@ void UpdateFrags(entity player, int f); // NOTE: f=0 means still count as a (positive) kill, but count no frags for it void W_SwitchWeapon_Force(Player this, Weapon w, .entity weaponentity); -entity GiveFrags_randomweapons; void GiveFrags (entity attacker, entity targ, float f, int deathtype, .entity weaponentity); string AppendItemcodes(string s, entity player); @@ -84,9 +83,9 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype, .en void Ice_Think(entity this); -void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypoint); +void Freeze(entity targ, float freeze_time, int frozen_type, bool show_waypoint); -void Unfreeze (entity targ); +void Unfreeze(entity targ, bool reset_health); // NOTE: the .weaponentity parameter can be set to DMG_NOWEP if the attack wasn't caused by a weapon or player void Damage (entity targ, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force); @@ -97,6 +96,10 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in float RadiusDamage (entity inflictor, entity attacker, float coredamage, float edgedamage, float rad, entity cantbe, entity mustbe, float forceintensity, int deathtype, .entity weaponentity, entity directhitentity); +// Calls .event_heal on the target so that they can handle healing themselves +// a limit of RESOURCE_LIMIT_NONE should be handled by the entity as its max health (if applicable) +bool Heal(entity targ, entity inflictor, float amount, float limit); + .float fire_damagepersec; .float fire_endtime; .float fire_deathtype; diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 0d732e0ca4..68aa7154ec 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -110,7 +110,6 @@ void GrapplingHookReset(entity this) RemoveHook(this); } -void GrapplingHookThink(entity this); void GrapplingHook_Stop(entity this) { Send_Effect(EFFECT_HOOK_IMPACT, this.origin, '0 0 0', 1); @@ -328,7 +327,7 @@ void GrapplingHookTouch(entity this, entity toucher) GrapplingHook_Stop(this); if(toucher) - if(toucher.move_movetype != MOVETYPE_NONE) + //if(toucher.move_movetype != MOVETYPE_NONE) { SetMovetypeFollow(this, toucher); WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment); @@ -339,15 +338,15 @@ void GrapplingHookTouch(entity this, entity toucher) void GrapplingHook_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) { - if(this.health <= 0) + if(GetResourceAmount(this, RESOURCE_HEALTH) <= 0) return; if (!W_CheckProjectileDamage(inflictor.realowner, this.realowner, deathtype, -1)) // no exceptions return; // g_balance_projectiledamage says to halt - this.health = this.health - damage; + TakeResource(this, RESOURCE_HEALTH, damage); - if (this.health <= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= 0) { if(attacker != this.realowner) { @@ -364,19 +363,9 @@ void FireGrapplingHook(entity actor, .entity weaponentity) if(forbidWeaponUse(actor)) return; if(actor.vehicle) return; - makevectors(actor.v_angle); - - int s = W_GunAlign(actor.(weaponentity), STAT(GUNALIGN, actor)) - 1; - vector vs = hook_shotorigin[s]; - - // UGLY WORKAROUND: play this on CH_WEAPON_B so it can't cut off fire sounds - sound (actor, CH_WEAPON_B, SND_HOOK_FIRE, VOL_BASE, ATTEN_NORM); - vector org = actor.origin + actor.view_ofs + v_forward * vs.x + v_right * -vs.y + v_up * vs.z; - - tracebox(actor.origin + actor.view_ofs, '-3 -3 -3', '3 3 3', org, MOVE_NORMAL, actor); - org = trace_endpos; - - Send_Effect(EFFECT_HOOK_MUZZLEFLASH, org, '0 0 0', 1); + // TODO: offhand hook shoots from eye + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', true, 0, SND_HOOK_FIRE, CH_WEAPON_B, 0, WEP_HOOK.m_id); + Send_Effect(EFFECT_HOOK_MUZZLEFLASH, w_shotorg, '0 0 0', 1); entity missile = WarpZone_RefSys_SpawnSameRefSys(actor); missile.owner = missile.realowner = actor; @@ -393,11 +382,11 @@ void FireGrapplingHook(entity actor, .entity weaponentity) //setmodel (missile, MDL_HOOK); // precision set below setsize (missile, '-3 -3 -3', '3 3 3'); - setorigin(missile, org); + setorigin(missile, w_shotorg); missile.state = 0; // not latched onto anything - W_SetupProjVelocity_Explicit(missile, v_forward, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false); + W_SetupProjVelocity_Explicit(missile, w_shotdir, v_up, autocvar_g_balance_grapplehook_speed_fly, 0, 0, 0, false); missile.angles = vectoangles (missile.velocity); //missile.glow_color = 250; // 244, 250 @@ -408,7 +397,7 @@ void FireGrapplingHook(entity actor, .entity weaponentity) missile.effects = /*EF_FULLBRIGHT | EF_ADDITIVE |*/ EF_LOWPRECISION; - missile.health = autocvar_g_balance_grapplehook_health;//120 + SetResourceAmountExplicit(missile, RESOURCE_HEALTH, autocvar_g_balance_grapplehook_health); missile.event_damage = GrapplingHook_Damage; missile.takedamage = DAMAGE_AIM; missile.damageforcescale = 0; diff --git a/qcsrc/server/g_hook.qh b/qcsrc/server/g_hook.qh index 719bf5b605..c0df31662a 100644 --- a/qcsrc/server/g_hook.qh +++ b/qcsrc/server/g_hook.qh @@ -2,6 +2,7 @@ // Wazat's grappling hook .entity hook; +void GrapplingHookThink(entity this); void RemoveGrapplingHooks(entity pl); void RemoveHook(entity this); // (note: you can change the hook impulse #'s to whatever you please) diff --git a/qcsrc/server/g_lights.qc b/qcsrc/server/g_lights.qc deleted file mode 100644 index 852f1efc01..0000000000 --- a/qcsrc/server/g_lights.qc +++ /dev/null @@ -1,135 +0,0 @@ -#include "g_lights.qh" - -#include <server/defs.qh> -#include <server/miscfunctions.qh> - -void train_next(entity this); - -const float LOOP = 1; - -.float speed; - -const float DNOSHADOW = 2; -const float DFOLLOW = 4; -.float light_lev; -.float lefty; -.vector color; -.string dtagname; - -/*QUAKED dynlight (0 1 0) (-8 -8 -8) (8 8 8) START_OFF NOSHADOW FOLLOW -Dynamic spawnfunc_light. -Can do one of these things: sit still and be just a silly spawnfunc_light, travel along a path, follow an entity around, attach to a tag on an entity. -It can spin around it's own axis in all the above cases. -If targeted, it will toggle between on or off. -keys: -"light_lev" spawnfunc_light radius, default 200 -"color" spawnfunc_light color in rgb and brightness, 1 1 1 produces bright white, up to 255 255 255 (nuclear blast), recommended values up to 1 1 1, default 1 1 1 -"style" lightstyle, same as for static lights -"angles" initial orientation -"avelocity" a vector value, the direction and speed it rotates in -"skin" cubemap number, must be 16 or above -"dtagname" will attach to this tag on the entity which "targetname" matches "target". If the "target" is either not an md3 model or is missing tags, it will attach to the targets origin. Note that the "target" must be visible to the spawnfunc_light -"targetname" will toggle on and off when triggered -"target" if issued with a target, preferrably spawnfunc_path_corner, it will move along the path. If also issued with the FOLLOW spawnflag, then this is the entity it will follow. If issued with the "tagname" key it will attach it to this targets tag called "tagname", does not work together with FOLLOW or path movement -"speed" the speed it will travel along the path, default 100 -flags: -"START_OFF" spawnfunc_light will be in off state until targeted -"NOSHADOW" will not cast shadows in realtime lighting mode -"FOLLOW" will follow the entity which "targetname" matches "target" -*/ -void dynlight_think(entity this) -{ - if(!this.owner) - delete(this); - - this.nextthink = time + 0.1; -} -void dynlight_find_aiment(entity this) -{ - entity targ; - if (!this.target) - objerror (this, "dynlight: no target to follow"); - - targ = find(NULL, targetname, this.target); - set_movetype(this, MOVETYPE_FOLLOW); - this.aiment = targ; - this.owner = targ; - this.punchangle = targ.angles; - this.view_ofs = this.origin - targ.origin; - this.v_angle = this.angles - targ.angles; - setthink(this, dynlight_think); - this.nextthink = time + 0.1; -} -void dynlight_find_path(entity this) -{ - entity targ; - if (!this.target) - objerror (this, "dynlight: no target to follow"); - - targ = find(NULL, targetname, this.target); - this.target = targ.target; - setorigin(this, targ.origin); - setthink(this, train_next); - this.nextthink = time + 0.1; -} -void dynlight_find_target(entity this) -{ - entity targ; - if (!this.target) - objerror (this, "dynlight: no target to follow"); - - targ = find(NULL, targetname, this.target); - setattachment(this, targ, this.dtagname); - this.owner = targ; - setthink(this, dynlight_think); - this.nextthink = time + 0.1; -} -void dynlight_use(entity this, entity actor, entity trigger) -{ - if (this.light_lev == 0) - this.light_lev = this.lefty; - else - this.light_lev = 0; -} -spawnfunc(dynlight) -{ - if (!this.light_lev) - this.light_lev = 200; - if (!this.color) - this.color = '1 1 1'; - this.lefty = this.light_lev; - this.use = dynlight_use; - setsize (this, '0 0 0', '0 0 0'); - setorigin(this, this.origin); - //this.pflags = PFLAGS_FULLDYNAMIC; - this.solid = SOLID_NOT; - //this.blocked = func_null; - //if (this.spawnflags & DNOSHADOW) - // this.pflags = this.pflags + PFLAGS_NOSHADOW; - //if (this.spawnflags & START_OFF) - // this.light_lev = 0; - -//tag attaching - if (this.dtagname) - { - InitializeEntity(this, dynlight_find_target, INITPRIO_FINDTARGET); - return; - } - -// entity following - if (this.spawnflags & DFOLLOW) - { - InitializeEntity(this, dynlight_find_aiment, INITPRIO_FINDTARGET); - return; - } -// path following - if (this.target) -// if (!(this.spawnflags & DFOLLOW)) - { - set_movetype(this, MOVETYPE_NOCLIP); - if (!this.speed) - this.speed = 100; - InitializeEntity(this, dynlight_find_path, INITPRIO_FINDTARGET); - return; - } -} diff --git a/qcsrc/server/g_lights.qh b/qcsrc/server/g_lights.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/server/g_lights.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/server/g_models.qc b/qcsrc/server/g_models.qc deleted file mode 100644 index 1026e3aa7a..0000000000 --- a/qcsrc/server/g_models.qc +++ /dev/null @@ -1,194 +0,0 @@ -#include "g_models.qh" - -#include <server/defs.qh> -#include <server/miscfunctions.qh> -#include "g_subs.qh" -#include <common/net_linked.qh> -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" - -entityclass(BGMScript); -class(BGMScript) .string bgmscript; -class(BGMScript) .float bgmscriptattack; -class(BGMScript) .float bgmscriptdecay; -class(BGMScript) .float bgmscriptsustain; -class(BGMScript) .float bgmscriptrelease; - -#include "../common/constants.qh" -#include "../lib/csqcmodel/sv_model.qh" - -.float modelscale; - -void g_model_setcolormaptoactivator(entity this, entity actor, entity trigger) -{ - if(teamplay) - { - if(actor.team) - this.colormap = (actor.team - 1) * 0x11; - else - this.colormap = 0x00; - } - else - this.colormap = floor(random() * 256); - this.colormap |= BIT(10); // RENDER_COLORMAPPED -} - -void g_clientmodel_setcolormaptoactivator(entity this, entity actor, entity trigger) -{ - g_model_setcolormaptoactivator(this, actor, trigger); - this.SendFlags |= (BIT(3) | BIT(0)); -} - -void g_clientmodel_use(entity this, entity actor, entity trigger) -{ - if (this.antiwall_flag == 1) - { - this.inactive = 1; - this.solid = SOLID_NOT; - } - else if (this.antiwall_flag == 2) - { - this.inactive = 0; - this.solid = this.default_solid; - } - g_clientmodel_setcolormaptoactivator(this, actor, trigger); -} - -void g_model_dropbyspawnflags(entity this) -{ - if((this.spawnflags & 3) == 1) // ALIGN_ORIGIN - { - traceline(this.origin, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); - setorigin(this, trace_endpos); - } - else if((this.spawnflags & 3) == 2) // ALIGN_BOTTOM - { - tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); - setorigin(this, trace_endpos); - } - else if((this.spawnflags & 3) == 3) // ALIGN_ORIGIN | ALIGN_BOTTOM - { - traceline(this.origin, this.origin - '0 0 4096', MOVE_NOMONSTERS, this); - setorigin(this, trace_endpos - '0 0 1' * this.mins.z); - } -} - -void g_clientmodel_dropbyspawnflags(entity this) -{ - vector o0; - o0 = this.origin; - g_model_dropbyspawnflags(this); - if(this.origin != o0) - this.SendFlags |= 2; -} - -bool g_clientmodel_genericsendentity(entity this, entity to, int sf) -{ - sf = sf & 0x0F; - if(this.angles != '0 0 0') - sf |= 0x10; - if(this.mins != '0 0 0' || this.maxs != '0 0 0') - sf |= 0x20; - if(this.colormap != 0) - sf |= 0x40; - if(this.lodmodelindex1) - sf |= 0x80; - - WriteHeader(MSG_ENTITY, ENT_CLIENT_WALL); - WriteByte(MSG_ENTITY, sf); - - if(sf & BIT(0)) - { - if(sf & 0x40) - WriteShort(MSG_ENTITY, this.colormap); - WriteByte(MSG_ENTITY, this.skin); - } - - if(sf & BIT(1)) - { - WriteVector(MSG_ENTITY, this.origin); - } - - if(sf & BIT(2)) - { - if(sf & 0x10) - { - WriteAngle(MSG_ENTITY, this.angles.x); - WriteAngle(MSG_ENTITY, this.angles.y); - WriteAngle(MSG_ENTITY, this.angles.z); - } - } - - if(sf & BIT(3)) - { - if(sf & 0x80) - { - WriteShort(MSG_ENTITY, this.lodmodelindex0); - WriteShort(MSG_ENTITY, bound(0, this.loddistance1, 65535)); - WriteShort(MSG_ENTITY, this.lodmodelindex1); - WriteShort(MSG_ENTITY, bound(0, this.loddistance2, 65535)); - WriteShort(MSG_ENTITY, this.lodmodelindex2); - } - else - WriteShort(MSG_ENTITY, this.modelindex); - WriteByte(MSG_ENTITY, this.solid); - WriteShort(MSG_ENTITY, floor(this.scale * 256)); - if(sf & 0x20) - { - WriteVector(MSG_ENTITY, this.mins); - WriteVector(MSG_ENTITY, this.maxs); - } - WriteString(MSG_ENTITY, this.bgmscript); - if(this.bgmscript != "") - { - WriteByte(MSG_ENTITY, floor(this.bgmscriptattack * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptdecay * 64)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptsustain * 255)); - WriteByte(MSG_ENTITY, floor(this.bgmscriptrelease * 64)); - WriteVector(MSG_ENTITY, this.movedir); - WriteByte(MSG_ENTITY, floor(this.lip * 255)); - } - WriteByte(MSG_ENTITY, this.fade_start); - WriteByte(MSG_ENTITY, this.fade_end); - WriteByte(MSG_ENTITY, this.alpha_max); - WriteByte(MSG_ENTITY, this.alpha_min); - WriteByte(MSG_ENTITY, this.inactive); - WriteShort(MSG_ENTITY, this.fade_vertical_offset); - } - - return true; -} - - -#define G_MODEL_INIT(ent,sol) \ - if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \ - if(!ent.scale) ent.scale = ent.modelscale; \ - SetBrushEntityModel(ent); \ - ent.use = g_model_setcolormaptoactivator; \ - InitializeEntity(ent, g_model_dropbyspawnflags, INITPRIO_DROPTOFLOOR); \ - if(!ent.solid) ent.solid = (sol); else if(ent.solid < 0) ent.solid = SOLID_NOT; - -#define G_CLIENTMODEL_INIT(ent,sol) \ - if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) set_movetype(ent, MOVETYPE_PHYSICS); \ - if(!ent.scale) ent.scale = ent.modelscale; \ - SetBrushEntityModel(ent); \ - ent.use = g_clientmodel_use; \ - InitializeEntity(ent, g_clientmodel_dropbyspawnflags, INITPRIO_DROPTOFLOOR); \ - if(!ent.solid) ent.solid = (sol); else if(ent.solid < 0) ent.solid = SOLID_NOT; \ - if(!ent.bgmscriptsustain) ent.bgmscriptsustain = 1; else if(ent.bgmscriptsustain < 0) ent.bgmscriptsustain = 0; \ - Net_LinkEntity(ent, true, 0, g_clientmodel_genericsendentity); \ - ent.default_solid = sol; - -// non-solid model entities: -spawnfunc(misc_gamemodel) { this.angles_x = -this.angles.x; G_MODEL_INIT (this, SOLID_NOT) } // model entity -spawnfunc(misc_clientmodel) { this.angles_x = -this.angles.x; G_CLIENTMODEL_INIT(this, SOLID_NOT) } // model entity -spawnfunc(misc_models) { this.angles_x = -this.angles.x; G_MODEL_INIT (this, SOLID_NOT) } // DEPRECATED old compat entity with confusing name, do not use - -// non-solid brush entities: -spawnfunc(func_illusionary) { G_MODEL_INIT (this, SOLID_NOT) } // Q1 name (WARNING: MISPREDICTED) -spawnfunc(func_clientillusionary) { G_CLIENTMODEL_INIT(this, SOLID_NOT) } // brush entity -spawnfunc(func_static) { G_MODEL_INIT (this, SOLID_NOT) } // DEPRECATED old alias name from some other game - -// solid brush entities -spawnfunc(func_wall) { G_MODEL_INIT (this, SOLID_BSP) } // Q1 name -spawnfunc(func_clientwall) { G_CLIENTMODEL_INIT(this, SOLID_BSP) } // brush entity (WARNING: MISPREDICTED) diff --git a/qcsrc/server/g_models.qh b/qcsrc/server/g_models.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/server/g_models.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc deleted file mode 100644 index d9372e0aa5..0000000000 --- a/qcsrc/server/g_subs.qc +++ /dev/null @@ -1,439 +0,0 @@ -#include "g_subs.qh" - -#include <server/defs.qh> -#include <server/miscfunctions.qh> -#include "antilag.qh" -#include "command/common.qh" -#include "../common/state.qh" -#include "../lib/warpzone/common.qh" -#include "../common/triggers/subs.qh" - -spawnfunc(info_null) -{ - delete(this); - // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately. -} - -/* -================== -main - -unused but required by the engine -================== -*/ -void main () -{ - -} - -// Misc - -/* -================== -traceline_antilag - -A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack -Additionally it moves players back into the past before the trace and restores them afterward. -================== -*/ -void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz) -{ - // check whether antilagged traces are enabled - if (lag < 0.001) - lag = 0; - if (!IS_REAL_CLIENT(forent)) - lag = 0; // only antilag for clients - - // change shooter to SOLID_BBOX so the shot can hit corpses - int oldsolid = source.dphitcontentsmask; - if(source) - source.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - - if (lag) - antilag_takeback_all(forent, lag); - - // do the trace - if(wz) - WarpZone_TraceBox (v1, mi, ma, v2, nomonst, forent); - else - tracebox (v1, mi, ma, v2, nomonst, forent); - - // restore players to current positions - if (lag) - antilag_restore_all(forent); - - // restore shooter solid type - if(source) - source.dphitcontentsmask = oldsolid; -} -void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) -{ - tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, false); -} -void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) -{ - bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); - if (autocvar_g_antilag != 2 || noantilag) - lag = 0; - traceline_antilag_force(source, v1, v2, nomonst, forent, lag); -} -void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag) -{ - bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); - if (autocvar_g_antilag != 2 || noantilag) - lag = 0; - tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, false); -} -void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) -{ - tracebox_antilag_force_wz(source, v1, '0 0 0', '0 0 0', v2, nomonst, forent, lag, true); -} -void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag) -{ - bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); - if (autocvar_g_antilag != 2 || noantilag) - lag = 0; - WarpZone_traceline_antilag_force(source, v1, v2, nomonst, forent, lag); -} -void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag) -{ - bool noantilag = ((IS_CLIENT(source)) ? CS(source).cvar_cl_noantilag : false); - if (autocvar_g_antilag != 2 || noantilag) - lag = 0; - tracebox_antilag_force_wz(source, v1, mi, ma, v2, nomonst, forent, lag, true); -} - -float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) // returns the number of traces done, for benchmarking -{ - vector pos, dir, t; - float nudge; - entity stopentity; - - //nudge = 2 * cvar("collision_impactnudge"); // why not? - nudge = 0.5; - - dir = normalize(v2 - v1); - - pos = v1 + dir * nudge; - - float c; - c = 0; - - for (;;) - { - if(pos * dir >= v2 * dir) - { - // went too far - trace_fraction = 1; - trace_endpos = v2; - return c; - } - - tracebox(pos, mi, ma, v2, nomonsters, forent); - ++c; - - if(c == 50) - { - LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2)); - LOG_TRACE(" Nudging gets us nowhere at ", vtos(pos)); - LOG_TRACE(" trace_endpos is ", vtos(trace_endpos)); - LOG_TRACE(" trace distance is ", ftos(vlen(pos - trace_endpos))); - } - - stopentity = trace_ent; - - if(trace_startsolid) - { - // we started inside solid. - // then trace from endpos to pos - t = trace_endpos; - tracebox(t, mi, ma, pos, nomonsters, forent); - ++c; - if(trace_startsolid) - { - // t is still inside solid? bad - // force advance, then, and retry - pos = t + dir * nudge; - - // but if we hit an entity, stop RIGHT before it - if(stopatentity && stopentity && stopentity != ignorestopatentity) - { - trace_ent = stopentity; - trace_endpos = t; - trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); - return c; - } - } - else - { - // we actually LEFT solid! - trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); - return c; - } - } - else - { - // pos is outside solid?!? but why?!? never mind, just return it. - trace_endpos = pos; - trace_fraction = ((trace_endpos - v1) * dir) / ((v2 - v1) * dir); - return c; - } - } -} - -void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity) -{ - tracebox_inverted(v1, '0 0 0', '0 0 0', v2, nomonsters, forent, stopatentity, ignorestopatentity); -} - -/* -================== -findbetterlocation - -Returns a point at least 12 units away from walls -(useful for explosion animations, although the blast is performed where it really happened) -Ripped from DPMod -================== -*/ -vector findbetterlocation (vector org, float mindist) -{ - vector vec = mindist * '1 0 0'; - int c = 0; - while (c < 6) - { - traceline (org, org + vec, true, NULL); - vec = vec * -1; - if (trace_fraction < 1) - { - vector loc = trace_endpos; - traceline (loc, loc + vec, true, NULL); - if (trace_fraction >= 1) - org = loc + vec; - } - if (c & 1) - { - float h = vec.y; - vec.y = vec.x; - vec.x = vec.z; - vec.z = h; - } - c = c + 1; - } - - return org; -} - -bool LOD_customize(entity this, entity client) -{ - if(autocvar_loddebug) - { - int d = autocvar_loddebug; - if(d == 1) - this.modelindex = this.lodmodelindex0; - else if(d == 2 || !this.lodmodelindex2) - this.modelindex = this.lodmodelindex1; - else // if(d == 3) - this.modelindex = this.lodmodelindex2; - return true; - } - - // TODO csqc network this so it only gets sent once - vector near_point = NearestPointOnBox(this, client.origin); - if(vdist(near_point - client.origin, <, this.loddistance1)) - this.modelindex = this.lodmodelindex0; - else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2)) - this.modelindex = this.lodmodelindex1; - else - this.modelindex = this.lodmodelindex2; - - return true; -} - -void LOD_uncustomize(entity this) -{ - this.modelindex = this.lodmodelindex0; -} - -void LODmodel_attach(entity this) -{ - entity e; - - if(!this.loddistance1) - this.loddistance1 = 1000; - if(!this.loddistance2) - this.loddistance2 = 2000; - this.lodmodelindex0 = this.modelindex; - - if(this.lodtarget1 != "") - { - e = find(NULL, targetname, this.lodtarget1); - if(e) - { - this.lodmodel1 = e.model; - delete(e); - } - } - if(this.lodtarget2 != "") - { - e = find(NULL, targetname, this.lodtarget2); - if(e) - { - this.lodmodel2 = e.model; - delete(e); - } - } - - if(autocvar_loddebug < 0) - { - this.lodmodel1 = this.lodmodel2 = ""; // don't even initialize - } - - if(this.lodmodel1 != "") - { - vector mi, ma; - mi = this.mins; - ma = this.maxs; - - precache_model(this.lodmodel1); - _setmodel(this, this.lodmodel1); - this.lodmodelindex1 = this.modelindex; - - if(this.lodmodel2 != "") - { - precache_model(this.lodmodel2); - _setmodel(this, this.lodmodel2); - this.lodmodelindex2 = this.modelindex; - } - - this.modelindex = this.lodmodelindex0; - setsize(this, mi, ma); - } - - if(this.lodmodelindex1) - if (!getSendEntity(this)) - SetCustomizer(this, LOD_customize, LOD_uncustomize); -} - -void ApplyMinMaxScaleAngles(entity e) -{ - if(e.angles.x != 0 || e.angles.z != 0 || e.avelocity.x != 0 || e.avelocity.z != 0) // "weird" rotation - { - e.maxs = '1 1 1' * vlen( - '1 0 0' * max(-e.mins.x, e.maxs.x) + - '0 1 0' * max(-e.mins.y, e.maxs.y) + - '0 0 1' * max(-e.mins.z, e.maxs.z) - ); - e.mins = -e.maxs; - } - else if(e.angles.y != 0 || e.avelocity.y != 0) // yaw only is a bit better - { - e.maxs_x = vlen( - '1 0 0' * max(-e.mins.x, e.maxs.x) + - '0 1 0' * max(-e.mins.y, e.maxs.y) - ); - e.maxs_y = e.maxs.x; - e.mins_x = -e.maxs.x; - e.mins_y = -e.maxs.x; - } - if(e.scale) - setsize(e, e.mins * e.scale, e.maxs * e.scale); - else - setsize(e, e.mins, e.maxs); -} - -void SetBrushEntityModel(entity this) -{ - if(this.model != "") - { - precache_model(this.model); - if(this.mins != '0 0 0' || this.maxs != '0 0 0') - { - vector mi = this.mins; - vector ma = this.maxs; - _setmodel(this, this.model); // no precision needed - setsize(this, mi, ma); - } - else - _setmodel(this, this.model); // no precision needed - InitializeEntity(this, LODmodel_attach, INITPRIO_FINDTARGET); - } - setorigin(this, this.origin); - ApplyMinMaxScaleAngles(this); -} - -void SetBrushEntityModelNoLOD(entity this) -{ - if(this.model != "") - { - precache_model(this.model); - if(this.mins != '0 0 0' || this.maxs != '0 0 0') - { - vector mi = this.mins; - vector ma = this.maxs; - _setmodel(this, this.model); // no precision needed - setsize(this, mi, ma); - } - else - _setmodel(this, this.model); // no precision needed - } - setorigin(this, this.origin); - ApplyMinMaxScaleAngles(this); -} - -/* -================ -InitTrigger -================ -*/ - -void SetMovedir(entity this) -{ - if(this.movedir != '0 0 0') - this.movedir = normalize(this.movedir); - else - { - makevectors(this.angles); - this.movedir = v_forward; - } - - this.angles = '0 0 0'; -} - -void InitTrigger(entity this) -{ -// trigger angles are used for one-way touches. An angle of 0 is assumed -// to mean no restrictions, so use a yaw of 360 instead. - SetMovedir(this); - this.solid = SOLID_TRIGGER; - SetBrushEntityModel(this); - set_movetype(this, MOVETYPE_NONE); - this.modelindex = 0; - this.model = ""; -} - -void InitSolidBSPTrigger(entity this) -{ -// trigger angles are used for one-way touches. An angle of 0 is assumed -// to mean no restrictions, so use a yaw of 360 instead. - SetMovedir(this); - this.solid = SOLID_BSP; - SetBrushEntityModel(this); - set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0 -// this.modelindex = 0; - this.model = ""; -} - -bool InitMovingBrushTrigger(entity this) -{ -// trigger angles are used for one-way touches. An angle of 0 is assumed -// to mean no restrictions, so use a yaw of 360 instead. - this.solid = SOLID_BSP; - SetBrushEntityModel(this); - set_movetype(this, MOVETYPE_PUSH); - if(this.modelindex == 0) - { - objerror(this, "InitMovingBrushTrigger: no brushes found!"); - return false; - } - return true; -} diff --git a/qcsrc/server/g_subs.qh b/qcsrc/server/g_subs.qh deleted file mode 100644 index 1f5537cea5..0000000000 --- a/qcsrc/server/g_subs.qh +++ /dev/null @@ -1,165 +0,0 @@ -#pragma once - -void SUB_NullThink(entity this); - -void SUB_CalcMoveDone(entity this); -void SUB_CalcAngleMoveDone(entity this); - -spawnfunc(info_null); - -/* -================== -SUB_Friction - -Applies some friction to this -================== -*/ -.float friction; -void SUB_Friction (entity this); - -/* -================== -SUB_VanishOrRemove - -Makes client invisible or removes non-client -================== -*/ -void SUB_VanishOrRemove (entity ent); - -void SUB_SetFade_Think (entity this); - -/* -================== -SUB_SetFade - -Fade 'ent' out when time >= 'when' -================== -*/ -void SUB_SetFade (entity ent, float when, float fadetime); - -/* -============= -SUB_CalcMove - -calculate this.velocity and this.nextthink to reach dest from -this.origin traveling at speed -=============== -*/ -void SUB_CalcMoveDone(entity this); - -.float platmovetype_turn; -void SUB_CalcMove_controller_think (entity this); - -void SUB_CalcMove_controller_setbezier (entity controller, vector org, vector control, vector dest); - -void SUB_CalcMove_controller_setlinear (entity controller, vector org, vector dest); - -void SUB_CalcMove_Bezier (entity this, vector tcontrol, vector tdest, float tspeedtype, float tspeed, void(entity this) func); - -void SUB_CalcMove (entity this, vector tdest, float tspeedtype, float tspeed, void(entity this) func); - -void SUB_CalcMoveEnt (entity ent, vector tdest, float tspeedtype, float tspeed, void(entity this) func); - -/* -============= -SUB_CalcAngleMove - -calculate this.avelocity and this.nextthink to reach destangle from -this.angles rotating - -The calling function should make sure this.think is valid -=============== -*/ -void SUB_CalcAngleMoveDone (entity this); - -// FIXME: I fixed this function only for rotation around the main axes -void SUB_CalcAngleMove (entity this, vector destangle, float tspeedtype, float tspeed, void(entity this) func); - -void SUB_CalcAngleMoveEnt (entity ent, vector destangle, float tspeedtype, float tspeed, void(entity this) func); - -/* -================== -main - -unused but required by the engine -================== -*/ -void main (); - -// Misc - -/* -================== -traceline_antilag - -A version of traceline that must be used by SOLID_SLIDEBOX things that want to hit SOLID_CORPSE things with a trace attack -Additionally it moves players back into the past before the trace and restores them afterward. -================== -*/ -void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag, float wz); -void traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); -void traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); -void tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); -void WarpZone_traceline_antilag_force (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); -void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomonst, entity forent, float lag); -void WarpZone_tracebox_antilag (entity source, vector v1, vector mi, vector ma, vector v2, float nomonst, entity forent, float lag); - -float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); // returns the number of traces done, for benchmarking - -void traceline_inverted (vector v1, vector v2, float nomonsters, entity forent, float stopatentity, entity ignorestopatentity); - -/* -================== -findbetterlocation - -Returns a point at least 12 units away from walls -(useful for explosion animations, although the blast is performed where it really happened) -Ripped from DPMod -================== -*/ -vector findbetterlocation (vector org, float mindist); - -/* -================== -Angc used for animations -================== -*/ - - -float angc (float a1, float a2); - -.string lodtarget1; -.string lodtarget2; -.string lodmodel1; -.string lodmodel2; -.float lodmodelindex0; -.float lodmodelindex1; -.float lodmodelindex2; -.float loddistance1; -.float loddistance2; - -bool LOD_customize(entity this, entity client); - -void LOD_uncustomize(entity this); - -void LODmodel_attach(entity this); - -void ApplyMinMaxScaleAngles(entity e); - -void SetBrushEntityModel(entity this); - -void SetBrushEntityModelNoLOD(entity this); - -/* -================ -InitTrigger -================ -*/ - -void SetMovedir(entity this); - -void InitTrigger(entity this); - -void InitSolidBSPTrigger(entity this); - -bool InitMovingBrushTrigger(entity this); diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index a3bb6eb216..522f4f041c 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -13,14 +13,16 @@ #include "g_hook.qh" #include "ipban.qh" #include "mapvoting.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "race.qh" #include "scores.qh" +#include "scores_rules.qh" #include "teamplay.qh" #include "weapons/weaponstats.qh" #include "../common/constants.qh" #include <common/net_linked.qh> #include "../common/deathtypes/all.qh" +#include "../common/gamemodes/sv_rules.qh" #include "../common/mapinfo.qh" #include "../common/monsters/_mod.qh" #include "../common/monsters/sv_monsters.qh" @@ -30,8 +32,8 @@ #include "../common/playerstats.qh" #include "../common/stats.qh" #include "../common/teams.qh" -#include "../common/triggers/trigger/secret.qh" -#include "../common/triggers/target/music.qh" +#include "../common/mapobjects/trigger/secret.qh" +#include "../common/mapobjects/target/music.qh" #include "../common/util.qh" #include "../common/items/_mod.qh" #include <common/weapons/_all.qh> @@ -91,9 +93,6 @@ const float SPAWNFLAG_NO_WAYPOINTS_FOR_ITEMS = 1; string redirection_target; float world_initialized; -string GetGametype(); -void ShuffleMaplist(); - void SetDefaultAlpha() { if (!MUTATOR_CALLHOOK(SetDefaultAlpha)) @@ -145,12 +144,8 @@ void cvar_changes_init() string k, v, d; float n, i, adding, pureadding; - if(cvar_changes) - strunzone(cvar_changes); - cvar_changes = string_null; - if(cvar_purechanges) - strunzone(cvar_purechanges); - cvar_purechanges = string_null; + strfree(cvar_changes); + strfree(cvar_purechanges); cvar_purechanges_count = 0; h = buf_create(); @@ -253,6 +248,7 @@ void cvar_changes_init() // these can contain player IDs, so better hide BADPREFIX("g_forced_team_"); BADCVAR("sv_muteban_list"); + BADCVAR("sv_voteban_list"); BADCVAR("sv_allow_customplayermodels_idlist"); BADCVAR("sv_allow_customplayermodels_speciallist"); @@ -269,6 +265,7 @@ void cvar_changes_init() BADCVAR("g_dm"); BADCVAR("g_domination"); BADCVAR("g_domination_default_teams"); + BADCVAR("g_duel"); BADCVAR("g_freezetag"); BADCVAR("g_freezetag_teams"); BADCVAR("g_invasion_teams"); @@ -361,10 +358,12 @@ void cvar_changes_init() BADCVAR("sv_stepheight"); BADCVAR("sv_timeout"); BADCVAR("sv_weapons_modeloverride"); + BADCVAR("w_prop_interval"); BADPREFIX("crypto_"); BADPREFIX("gameversion_"); BADPREFIX("g_chat_"); BADPREFIX("g_ctf_captimerecord_"); + BADPREFIX("g_hats_"); BADPREFIX("g_maplist_"); BADPREFIX("g_mod_"); BADPREFIX("g_respawn_"); @@ -508,38 +507,6 @@ void cvar_changes_init() cvar_purechanges = strzone(cvar_purechanges); } -void detect_maptype() -{ -#if 0 - vector o, v; - float i; - - for (;;) - { - o = world.mins; - o.x += random() * (world.maxs.x - world.mins.x); - o.y += random() * (world.maxs.y - world.mins.y); - o.z += random() * (world.maxs.z - world.mins.z); - - tracebox(o, STAT(PL_MIN), STAT(PL_MAX), o - '0 0 32768', MOVE_WORLDONLY, NULL); - if(trace_fraction == 1) - continue; - - v = trace_endpos; - - for(i = 0; i < 64; i += 4) - { - tracebox(o, '-1 -1 -1' * i, '1 1 1' * i, o - '0 0 32768', MOVE_WORLDONLY, NULL); - if(trace_fraction == 1) - continue; - LOG_INFO(ftos(i), " -> ", vtos(trace_endpos)); - } - - break; - } -#endif -} - entity randomseed; bool RandomSeed_Send(entity this, entity to, int sf) { @@ -613,12 +580,59 @@ STATIC_INIT_EARLY(maxclients) } } +void default_delayedinit(entity this) +{ + if(!scores_initialized) + ScoreRules_generic(); +} + +void InitGameplayMode() +{ + VoteReset(); + + // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds + get_mi_min_max(1); + // assign reflectively to avoid "assignment to world" warning + 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("%v", v)); + if (++done == 2) break; + } + } + // currently, NetRadiant's limit is 131072 qu for each side + // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu + // set the distance according to map size but don't go over the limit to avoid issues with float precision + // in case somebody makes extremely large maps + max_shot_distance = min(230000, vlen(world.maxs - world.mins)); + + MapInfo_LoadMapSettings(mapname); + GameRules_teams(false); + + if (!cvar_value_issafe(world.fog)) + { + LOG_INFO("The current map contains a potentially harmful fog setting, ignored"); + world.fog = string_null; + } + if(MapInfo_Map_fog != "") + if(MapInfo_Map_fog == "none") + world.fog = string_null; + else + world.fog = strzone(MapInfo_Map_fog); + clientstuff = strzone(MapInfo_Map_clientstuff); + + MapInfo_ClearTemps(); + + gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype); + + cache_mutatormsg = strzone(""); + cache_lastmutatormsg = strzone(""); + + InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK); +} + void Map_MarkAsRecent(string m); float world_already_spawned; -void Nagger_Init(); -void ClientInit_Spawn(); -void WeaponStats_Init(); -void WeaponStats_Shutdown(); spawnfunc(worldspawn) { server_is_dedicated = boolean(stof(cvar_defstring("is_dedicated"))); @@ -872,8 +886,6 @@ spawnfunc(worldspawn) next_pingtime = time + 5; - detect_maptype(); - // set up information replies for clients and server to use maplist_reply = strzone(getmaplist()); lsmaps_reply = strzone(getlsmaps()); @@ -1094,7 +1106,7 @@ void Map_Goto(float reinit) // return codes of map selectors: // -1 = temporary failure (that is, try some method that is guaranteed to succeed) // -2 = permanent failure -float() MaplistMethod_Iterate = // usual method +float MaplistMethod_Iterate() // usual method { float pass, i; @@ -1113,7 +1125,7 @@ float() MaplistMethod_Iterate = // usual method return -1; } -float() MaplistMethod_Repeat = // fallback method +float MaplistMethod_Repeat() // fallback method { LOG_TRACE("Trying MaplistMethod_Repeat"); @@ -1122,7 +1134,7 @@ float() MaplistMethod_Repeat = // fallback method return -2; } -float() MaplistMethod_Random = // random map selection +float MaplistMethod_Random() // random map selection { float i, imax; @@ -1140,7 +1152,7 @@ float() MaplistMethod_Random = // random map selection return -1; } -float(float exponent) MaplistMethod_Shuffle = // more clever shuffling +float MaplistMethod_Shuffle(float exponent) // more clever shuffling // the exponent sets a bias on the map selection: // the higher the exponent, the less likely "shortly repeated" same maps are { @@ -1199,9 +1211,7 @@ void Maplist_Init() error("empty maplist, cannot select a new map"); Map_Current = bound(0, GetMaplistPosition(), Map_Count - 1); - if(Map_Current_Name) - strunzone(Map_Current_Name); - Map_Current_Name = strzone(argv(Map_Current)); // will be automatically freed on exit thanks to DP + strcpy(Map_Current_Name, argv(Map_Current)); // will be automatically freed on exit thanks to DP // this may or may not be correct, but who cares, in the worst case a map // isn't chosen in the first pass that should have been } @@ -1511,7 +1521,7 @@ void FixIntermissionClient(entity e) if(!e.autoscreenshot) // initial call { e.autoscreenshot = time + 0.8; // used for autoscreenshot - e.health = -2342; + SetResourceAmountExplicit(e, RESOURCE_HEALTH, -2342); // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not) for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { @@ -1602,7 +1612,9 @@ float InitiateSuddenDeath() // - for this timelimit_overtime needs to be >0 of course // - also check the winning condition calculated in the previous frame and only add normal overtime // again, if at the point at which timelimit would be extended again, still no winner was found - if (!autocvar_g_campaign && (checkrules_overtimesadded >= 0) && (checkrules_overtimesadded < autocvar_timelimit_overtimes || autocvar_timelimit_overtimes < 0) && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying)) + if (!autocvar_g_campaign && checkrules_overtimesadded >= 0 + && (checkrules_overtimesadded < autocvar_timelimit_overtimes || autocvar_timelimit_overtimes < 0) + && autocvar_timelimit_overtime && !(g_race && !g_race_qualifying)) { return 1; // need to call InitiateOvertime later } @@ -1625,11 +1637,7 @@ void InitiateOvertime() // ONLY call this if InitiateSuddenDeath returned true { ++checkrules_overtimesadded; //add one more overtime by simply extending the timelimit - float tl; - tl = autocvar_timelimit; - tl += autocvar_timelimit_overtime; - cvar_set("timelimit", ftos(tl)); - + cvar_set("timelimit", ftos(autocvar_timelimit + autocvar_timelimit_overtime)); Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_TIME, autocvar_timelimit_overtime * 60); } @@ -1690,10 +1698,11 @@ float WinningCondition_Scores(float limit, float leadlimit) if(teamplay) { - team1_score = TeamScore_GetCompareValue(NUM_TEAM_1); - team2_score = TeamScore_GetCompareValue(NUM_TEAM_2); - team3_score = TeamScore_GetCompareValue(NUM_TEAM_3); - team4_score = TeamScore_GetCompareValue(NUM_TEAM_4); + for (int i = 1; i < 5; ++i) + { + Team_SetTeamScore(Team_GetTeamFromIndex(i), + TeamScore_GetCompareValue(Team_IndexToTeam(i))); + } } ClearWinners(); @@ -1763,30 +1772,32 @@ float WinningCondition_RanOutOfSpawns() if(!some_spawn_has_been_used) return WINNING_NO; - team1_score = team2_score = team3_score = team4_score = 0; + for (int i = 1; i < 5; ++i) + { + Team_SetTeamScore(Team_GetTeamFromIndex(i), 0); + } - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - switch(it.team) + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), + { + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: team1_score = 1; break; - case NUM_TEAM_2: team2_score = 1; break; - case NUM_TEAM_3: team3_score = 1; break; - case NUM_TEAM_4: team4_score = 1; break; + Team_SetTeamScore(Team_GetTeam(it.team), 1); } }); IL_EACH(g_spawnpoints, true, { - switch(it.team) + if (Team_IsValidTeam(it.team)) { - case NUM_TEAM_1: team1_score = 1; break; - case NUM_TEAM_2: team2_score = 1; break; - case NUM_TEAM_3: team3_score = 1; break; - case NUM_TEAM_4: team4_score = 1; break; + Team_SetTeamScore(Team_GetTeam(it.team), 1); } }); ClearWinners(); + float team1_score = Team_GetTeamScore(Team_GetTeamFromIndex(1)); + float team2_score = Team_GetTeamScore(Team_GetTeamFromIndex(2)); + float team3_score = Team_GetTeamScore(Team_GetTeamFromIndex(3)); + float team4_score = Team_GetTeamScore(Team_GetTeamFromIndex(4)); if(team1_score + team2_score + team3_score + team4_score == 0) { checkrules_equality = true; @@ -1796,20 +1807,28 @@ float WinningCondition_RanOutOfSpawns() { float t, i; if(team1_score) - t = NUM_TEAM_1; + t = 1; else if(team2_score) - t = NUM_TEAM_2; + t = 2; else if(team3_score) - t = NUM_TEAM_3; + t = 3; else // if(team4_score) - t = NUM_TEAM_4; - CheckAllowedTeams(NULL); + t = 4; + entity balance = TeamBalance_CheckAllowedTeams(NULL); for(i = 0; i < MAX_TEAMSCORE; ++i) { - if(t != NUM_TEAM_1) if(c1 >= 0) TeamScore_AddToTeam(NUM_TEAM_1, i, -1000); - if(t != NUM_TEAM_2) if(c2 >= 0) TeamScore_AddToTeam(NUM_TEAM_2, i, -1000); - if(t != NUM_TEAM_3) if(c3 >= 0) TeamScore_AddToTeam(NUM_TEAM_3, i, -1000); - if(t != NUM_TEAM_4) if(c4 >= 0) TeamScore_AddToTeam(NUM_TEAM_4, i, -1000); + for (int j = 1; j <= NUM_TEAMS; ++j) + { + if (t == j) + { + continue; + } + if (!TeamBalance_IsTeamAllowed(balance, j)) + { + continue; + } + TeamScore_AddToTeam(Team_IndexToTeam(j), i, -1000); + } } AddWinners(team, t); @@ -2147,7 +2166,6 @@ float RedirectionThink() return true; } -void TargetMusic_RestoreGame(); void RestoreGame() { // Loaded from a save game diff --git a/qcsrc/server/g_world.qh b/qcsrc/server/g_world.qh index 034407bc1f..da950f1857 100644 --- a/qcsrc/server/g_world.qh +++ b/qcsrc/server/g_world.qh @@ -5,6 +5,9 @@ float checkrules_suddendeathwarning; float checkrules_suddendeathend; float checkrules_overtimesadded; //how many overtimes have been already added +string cache_mutatormsg; +string cache_lastmutatormsg; + const int WINNING_NO = 0; // no winner, but time limits may terminate the game const int WINNING_YES = 1; // winner found const int WINNING_NEVER = 2; // no winner, enter overtime if time limit is reached @@ -16,11 +19,15 @@ void IntermissionThink(entity this); void GotoNextMap(float reinit); void ReadyRestart(); +string GetGametype(); + void DumpStats(float final); float Map_IsRecent(string m); string GetNextMap(); void ShuffleMaplist(); void Map_Goto_SetStr(string nextmapname); void Map_Goto(float reinit); +void Map_MarkAsRecent(string m); float DoNextMapOverride(float reinit); void CheckRules_World(); +float RedirectionThink(); diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc index 5010d28371..8a17ef6a1d 100644 --- a/qcsrc/server/impulse.qc +++ b/qcsrc/server/impulse.qc @@ -1,11 +1,10 @@ #include "impulse.qh" #include "round_handler.qh" -#include "bot/api.qh" - #include "weapons/throwing.qh" #include "command/common.qh" #include "cheats.qh" +#include "clientkill.qh" #include "weapons/selection.qh" #include "weapons/tracing.qh" #include "weapons/weaponsystem.qh" @@ -150,7 +149,7 @@ X(9, next) for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) \ { \ .entity weaponentity = weaponentities[slot]; \ - W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i), weaponentity); \ + W_SwitchWeapon_TryOthers(this, Weapons_from(WEP_FIRST + i), weaponentity); \ if(slot == 0 && autocvar_g_weaponswitch_debug != 1) \ break; \ } \ @@ -571,33 +570,3 @@ IMPULSE(waypoint_clear) } sprint(this, "all waypoints cleared\n"); } - -IMPULSE(navwaypoint_spawn) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_spawn_fromeditor(this); -} - -IMPULSE(navwaypoint_remove) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_remove_fromeditor(this); -} - -IMPULSE(navwaypoint_relink) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_schedulerelinkall(); -} - -IMPULSE(navwaypoint_save) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_saveall(); -} - -IMPULSE(navwaypoint_unreachable) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_unreachable(this); -} diff --git a/qcsrc/server/ipban.qc b/qcsrc/server/ipban.qc index 98dbf5c55b..7c6fcbafef 100644 --- a/qcsrc/server/ipban.qc +++ b/qcsrc/server/ipban.qc @@ -31,8 +31,6 @@ #define MAX_IPBAN_URIS (URI_GET_IPBAN_END - URI_GET_IPBAN + 1) -float Ban_Insert(string ip, float bantime, string reason, float dosync); - void OnlineBanList_SendBan(string ip, float bantime, string reason) { string uri; @@ -199,7 +197,7 @@ LABEL(skip) void OnlineBanList_Think(entity this) { - float argc; + int argc; string uri; float i, n; @@ -211,12 +209,8 @@ void OnlineBanList_Think(entity this) if(argc == 0) goto killme; - if(OnlineBanList_Servers) - strunzone(OnlineBanList_Servers); - OnlineBanList_Servers = argv(0); - for(i = 1; i < argc; ++i) - OnlineBanList_Servers = strcat(OnlineBanList_Servers, ";", argv(i)); - OnlineBanList_Servers = strzone(OnlineBanList_Servers); + string s = argv(0); for(i = 1; i < argc; ++i) s = strcat(s, ";", argv(i)); + strcpy(OnlineBanList_Servers, s); uri = strcat( "action=list&hostname=", uri_escape(autocvar_hostname)); uri = strcat(uri, "&servers=", uri_escape(OnlineBanList_Servers)); @@ -452,6 +446,8 @@ bool Ban_MaybeEnforceBan(entity client) if (Ban_IsClientBanned(client, -1)) { string s = sprintf("^1NOTE:^7 banned client %s just tried to enter\n", client.netaddress); + if(autocvar_g_ban_telluser) + sprint(client, "You are banned from this server.\n"); dropclient(client); bprint(s); return true; diff --git a/qcsrc/server/ipban.qh b/qcsrc/server/ipban.qh index 946f44f935..330d8b7dff 100644 --- a/qcsrc/server/ipban.qh +++ b/qcsrc/server/ipban.qh @@ -6,4 +6,9 @@ float Ban_MaybeEnforceBan(entity client); float Ban_MaybeEnforceBanOnce(entity client); float BanCommand(string command); +float Ban_Insert(string ip, float bantime, string reason, float dosync); +void Ban_KickBanClient(entity client, float bantime, float masksize, string reason); +void Ban_View(); +float Ban_Delete(float i); + void OnlineBanList_URI_Get_Callback(float id, float status, string data); diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc deleted file mode 100644 index c645c7facd..0000000000 --- a/qcsrc/server/item_key.qc +++ /dev/null @@ -1,286 +0,0 @@ -#include "item_key.qh" - -#include "../common/triggers/subs.qh" -#include "../common/monsters/_mod.qh" -#include "../common/notifications/all.qh" -#include "../common/util.qh" -#include "../lib/warpzone/util_server.qh" - -/* -TODO: -- add an unlock sound (here to trigger_keylock and to func_door) -- display available keys on the HUD -- make more tests -- think about adding NOT_EASY/NOT_NORMAL/NOT_HARD for Q1 compatibility -- should keys have a trigger? -*/ - -bool item_keys_usekey(entity l, entity p) -{ - int valid = l.itemkeys & PS(p).itemkeys; - - if (!valid) { - // player has none of the needed keys - return false; - } else if (l.itemkeys == valid) { - // ALL needed keys were given - l.itemkeys = 0; - return true; - } else { - // only some of the needed keys were given - l.itemkeys &= ~valid; - return true; - } -} - -string item_keys_keylist(float keylist) { - // no keys - if (!keylist) - return ""; - - // one key - if ((keylist & (keylist-1)) == 0) - return strcat("the ", item_keys_names[lowestbit(keylist)]); - - string n = ""; - int base = 0; - while (keylist) { - int l = lowestbit(keylist); - if (n) - n = strcat(n, ", the ", item_keys_names[base + l]); - else - n = strcat("the ", item_keys_names[base + l]); - - keylist = bitshift(keylist, -(l + 1)); - base+= l + 1; - } - - return n; -} - - -/* -================================ -item_key -================================ -*/ - -/** - * Key touch handler. - */ -void item_key_touch(entity this, entity toucher) -{ - if (!IS_PLAYER(toucher)) - return; - - // player already picked up this key - if (PS(toucher).itemkeys & this.itemkeys) - return; - - PS(toucher).itemkeys |= this.itemkeys; - play2(toucher, this.noise); - - centerprint(toucher, this.message); - - string oldmsg = this.message; - this.message = ""; - SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for the trigger here? - this.message = oldmsg; -}; - -/** - * Spawn a key with given model, key code and color. - */ -void spawn_item_key(entity this) -{ - precache_model(this.model); - - if (this.spawnflags & 1) // FLOATING - this.noalign = 1; - - if (this.noalign) - set_movetype(this, MOVETYPE_NONE); - else - set_movetype(this, MOVETYPE_TOSS); - - precache_sound(this.noise); - - this.mdl = this.model; - this.effects = EF_LOWPRECISION; - _setmodel(this, this.model); - //setsize(this, '-16 -16 -24', '16 16 32'); - setorigin(this, this.origin + '0 0 32'); - setsize(this, '-16 -16 -56', '16 16 0'); - this.modelflags |= MF_ROTATE; - this.solid = SOLID_TRIGGER; - - if (!this.noalign) - { - // first nudge it off the floor a little bit to avoid math errors - setorigin(this, this.origin + '0 0 1'); - // note droptofloor returns false if stuck/or would fall too far - droptofloor(this); - } - - settouch(this, item_key_touch); -}; - - -/*QUAKED item_key (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING -A key entity. -The itemkeys should contain one of the following key IDs: -1 - GOLD key - -2 - SILVER key -4 - BRONZE key -8 - RED keycard -16 - BLUE keycard -32 - GREEN keycard -Custom keys: -... - last key is 1<<23 -Keys with bigger Id than 32 don't have a default netname and model, if you use one of them, you MUST provide those. ------------KEYS------------ -colormod: color of the key (default: '.9 .9 .9'). -itemkeys: a key Id. -message: message to print when player picks up this key. -model: custom key model to use. -netname: the display name of the key. -noise: custom sound to play when player picks up the key. --------- SPAWNFLAGS -------- -FLOATING: the item will float in air, instead of aligning to the floor by falling ----------NOTES---------- -This is the only correct way to put keys on the map! - -itemkeys MUST always have exactly one bit set. -*/ -spawnfunc(item_key) -{ - string _netname; - vector _colormod; - - // reject this entity if more than one key was set! - if (this.itemkeys>0 && (this.itemkeys & (this.itemkeys-1)) != 0) { - objerror(this, "item_key.itemkeys must contain only 1 bit set specifying the key it represents!"); - delete(this); - return; - } - - // find default netname and colormod - switch(this.itemkeys) { - case BIT(0): - _netname = "GOLD key"; - _colormod = '1 .9 0'; - break; - - case BIT(1): - _netname = "SILVER key"; - _colormod = '.9 .9 .9'; - break; - - case BIT(2): - _netname = "BRONZE key"; - _colormod = '.6 .25 0'; - break; - - case BIT(3): - _netname = "RED keycard"; - _colormod = '.9 0 0'; - break; - - case BIT(4): - _netname = "BLUE keycard"; - _colormod = '0 0 .9'; - break; - - case BIT(5): - _netname = "GREEN keycard"; - _colormod = '0 .9 0'; - break; - - default: - _netname = "FLUFFY PINK keycard"; - _colormod = '1 1 1'; - - if (this.netname == "") { - objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!"); - delete(this); - return; - } - break; - - } - - // find default model - string _model = string_null; - if (this.itemkeys <= ITEM_KEY_BIT(2)) { - _model = "models/keys/key.md3"; - } else if (this.itemkeys >= ITEM_KEY_BIT(3) && this.itemkeys <= ITEM_KEY_BIT(5)) { - _model = "models/keys/key.md3"; // FIXME: replace it by a keycard model! - } else if (this.model == "") { - objerror(this, "item_key doesn't have a default model for this key and a custom one was not specified!"); - delete(this); - return; - } - - // set defailt netname - if (this.netname == "") - this.netname = _netname; - - // set default colormod - if (!this.colormod) - this.colormod = _colormod; - - // set default model - if (this.model == "") - this.model = _model; - - // set default pickup message - if (this.message == "") - this.message = strzone(strcat("You've picked up the ", this.netname, "!")); - - if (this.noise == "") - this.noise = strzone(SND(ITEMPICKUP)); - - // save the name for later - item_keys_names[lowestbit(this.itemkeys)] = this.netname; - - // put the key on the map - spawn_item_key(this); -} - -/*QUAKED item_key1 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING -SILVER key. ------------KEYS------------ -colormod: color of the key (default: '.9 .9 .9'). -message: message to print when player picks up this key. -model: custom model to use. -noise: custom sound to play when player picks up the key. --------- SPAWNFLAGS -------- -FLOATING: the item will float in air, instead of aligning to the floor by falling ----------NOTES---------- -Don't use this entity on new maps! Use item_key instead. -*/ -spawnfunc(item_key1) -{ - this.classname = "item_key"; - this.itemkeys = ITEM_KEY_BIT(1); - spawnfunc_item_key(this); -}; - -/*QUAKED item_key2 (0 .5 .8) (-16 -16 -24) (16 16 32) FLOATING -GOLD key. ------------KEYS------------ -colormod: color of the key (default: '1 .9 0'). -message: message to print when player picks up this key. -model: custom model to use. -noise: custom sound to play when player picks up the key. --------- SPAWNFLAGS -------- -FLOATING: the item will float in air, instead of aligning to the floor by falling ----------NOTES---------- -Don't use this entity on new maps! Use item_key instead. -*/ -spawnfunc(item_key2) -{ - this.classname = "item_key"; - this.itemkeys = ITEM_KEY_BIT(0); - spawnfunc_item_key(this); -}; diff --git a/qcsrc/server/item_key.qh b/qcsrc/server/item_key.qh deleted file mode 100644 index 50be5f8dba..0000000000 --- a/qcsrc/server/item_key.qh +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -/** - * Returns the bit ID of a key - */ -#define ITEM_KEY_BIT(n) ( bitshift(1, n) ) - -#define ITEM_KEY_MAX 24 - -/** - * list of key names. - */ -#ifdef SVQC -string item_keys_names[ITEM_KEY_MAX]; - -/** - * Use keys from p on l. - * Returns true if any new keys were given, false otherwise. - */ -float item_keys_usekey(entity l, entity p); - -/** - * Returns a string with a comma separated list of key names, as specified in keylist. - */ -string item_keys_keylist(float keylist); -#endif diff --git a/qcsrc/server/items.qc b/qcsrc/server/items.qc index 7d248834f7..b21df78e3f 100644 --- a/qcsrc/server/items.qc +++ b/qcsrc/server/items.qc @@ -5,14 +5,43 @@ /// game items. /// \copyright GNU GPLv2 or any later version. -#include "g_subs.qh" +#include <server/mutators/_mod.qh> #include <common/weapons/all.qh> +#include <common/mapobjects/subs.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_FindDefinition(string class_name) +{ + FOREACH(Items, it.m_canonical_spawnfunc == class_name, + { + return it; + }); + FOREACH(Weapons, it.m_canonical_spawnfunc == class_name, + { + return it.m_pickup; + }); + return NULL; +} + +bool Item_IsAllowed(string class_name) +{ + entity definition = Item_FindDefinition(class_name); + if (definition == NULL) + { + return false; + } + return Item_IsDefinitionAllowed(definition); +} + +bool Item_IsDefinitionAllowed(entity definition) +{ + return !MUTATOR_CALLHOOK(FilterItemDefinition, definition); +} + entity Item_Create(string class_name, vector position, bool no_align) { entity item = spawn(); diff --git a/qcsrc/server/items.qh b/qcsrc/server/items.qh index 1abcf64e08..b52449e715 100644 --- a/qcsrc/server/items.qh +++ b/qcsrc/server/items.qh @@ -4,6 +4,28 @@ /// \brief Header file that describes the functions related to game items. /// \copyright GNU GPLv2 or any later version. +bool startitem_failed; + +/// \brief Returns the item definition corresponding to the given class name. +/// \param[in] class_name Class name to search for. +/// \return Item definition corresponding to the given class name or NULL is not +/// found. +entity Item_FindDefinition(string class_name); + +/// \brief Checks whether the items with the specified class name are allowed to +/// spawn. +/// \param[in] class_name Item class name to check. +/// \return True items with the specified class name are allowed to spawn, false +/// otherwise. +bool Item_IsAllowed(string class_name); + +/// \brief Checks whether the items with the specified definition are allowed to +/// spawn. +/// \param[in] definition Item definition to check. +/// \return True items with the specified definition are allowed to spawn, false +/// otherwise. +bool Item_IsDefinitionAllowed(entity definition); + /// \brief Creates a new item. /// \param[in] class_name Class name of the item. /// \param[in] position Position of the item. @@ -12,7 +34,7 @@ /// \return Item on success, NULL otherwise. entity Item_Create(string class_name, vector position, bool no_align); -/// \brief Initializes the item according to classname. +/// \brief Initializes the item according to class name. /// \param[in,out] item Item to initialize. /// \param[in] class_name Class name to use. /// \return No return. diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc index 209ac7af98..5c564d56db 100644 --- a/qcsrc/server/mapvoting.qc +++ b/qcsrc/server/mapvoting.qc @@ -109,16 +109,8 @@ void MapVote_UnzoneStrings() { for(int j = 0; j < mapvote_count; ++j) { - if ( mapvote_maps[j] ) - { - strunzone(mapvote_maps[j]); - mapvote_maps[j] = string_null; - } - if ( mapvote_maps_pakfile[j] ) - { - strunzone(mapvote_maps_pakfile[j]); - mapvote_maps_pakfile[j] = string_null; - } + strfree(mapvote_maps[j]); + strfree(mapvote_maps_pakfile[j]); } } @@ -599,9 +591,9 @@ void MapVote_Tick() int totalvotes = 0; FOREACH_CLIENT(IS_REAL_CLIENT(it), { // hide scoreboard again - if(it.health != 2342) + if(GetResourceAmount(it, RESOURCE_HEALTH) != 2342) { - it.health = 2342; + SetResourceAmountExplicit(it, RESOURCE_HEALTH, 2342); CS(it).impulse = 0; msg_entity = it; diff --git a/qcsrc/server/matrix.qc b/qcsrc/server/matrix.qc index 4d235da694..b495b4b0d8 100644 --- a/qcsrc/server/matrix.qc +++ b/qcsrc/server/matrix.qc @@ -1,6 +1,6 @@ #include "matrix.qh" -#include "player.qh" +#include "client.qh" var void MX_Handle(int buf, string ancestor) { diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index ff762e9046..035891e5b1 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -5,22 +5,24 @@ #include "constants.qh" #include "g_hook.qh" #include "ipban.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "../common/t_items.qh" #include "resources.qh" #include "items.qh" +#include "player.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" #include "../common/command/_mod.qh" #include "../common/constants.qh" #include <common/net_linked.qh> +#include <common/weapons/weapon/crylink.qh> #include "../common/deathtypes/all.qh" #include "../common/mapinfo.qh" #include "../common/notifications/all.qh" #include "../common/playerstats.qh" #include "../common/teams.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../common/util.qh" #include "../common/turrets/sv_turrets.qh" #include <common/weapons/_all.qh> @@ -38,34 +40,47 @@ void crosshair_trace(entity pl) { traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } -.bool ctrace_solidchanged; + void crosshair_trace_plusvisibletriggers(entity pl) +{ + crosshair_trace_plusvisibletriggers__is_wz(pl, false); +} + +void WarpZone_crosshair_trace_plusvisibletriggers(entity pl) +{ + crosshair_trace_plusvisibletriggers__is_wz(pl, true); +} + +void crosshair_trace_plusvisibletriggers__is_wz(entity pl, bool is_wz) { FOREACH_ENTITY_FLOAT(solid, SOLID_TRIGGER, { if(it.model != "") { it.solid = SOLID_BSP; - it.ctrace_solidchanged = true; IL_PUSH(g_ctrace_changed, it); } }); - crosshair_trace(pl); + if (is_wz) + WarpZone_crosshair_trace(pl); + else + crosshair_trace(pl); - IL_EACH(g_ctrace_changed, it.ctrace_solidchanged, - { - it.solid = SOLID_TRIGGER; - it.ctrace_solidchanged = false; - }); + IL_EACH(g_ctrace_changed, true, { it.solid = SOLID_TRIGGER; }); IL_CLEAR(g_ctrace_changed); } + void WarpZone_crosshair_trace(entity pl) { WarpZone_traceline_antilag(pl, CS(pl).cursor_trace_start, CS(pl).cursor_trace_start + normalize(CS(pl).cursor_trace_endpos - CS(pl).cursor_trace_start) * max_shot_distance, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } +void dedicated_print(string input) +{ + if (server_is_dedicated) print(input); +} void GameLogEcho(string s) { @@ -198,10 +213,10 @@ string NearestLocation(vector p) return ret; } -string AmmoNameFromWeaponentity(entity wpn) +string AmmoNameFromWeaponentity(Weapon wep) { string ammoitems = "batteries"; - switch ((wpn.m_weapon).ammo_type) + switch (wep.ammo_type) { case RESOURCE_SHELLS: ammoitems = ITEM_Shells.m_name; break; case RESOURCE_BULLETS: ammoitems = ITEM_Bullets.m_name; break; @@ -217,16 +232,13 @@ string formatmessage(entity this, string msg) { float p, p1, p2; float n; - vector cursor; - entity cursor_ent; + vector cursor = '0 0 0'; + entity cursor_ent = NULL; string escape; string replacement; p = 0; n = 7; - - WarpZone_crosshair_trace(this); - cursor = trace_endpos; - cursor_ent = trace_ent; + bool traced = false; MUTATOR_CALLHOOK(PreFormatMessage, this, msg); msg = M_ARGV(1, string); @@ -250,6 +262,14 @@ string formatmessage(entity this, string msg) if (p < 0) break; + if(!traced) + { + WarpZone_crosshair_trace_plusvisibletriggers(this); + cursor = trace_endpos; + cursor_ent = trace_ent; + traced = true; + } + replacement = substring(msg, p, 2); escape = substring(msg, p + 1, 1); @@ -260,13 +280,13 @@ string formatmessage(entity this, string msg) case "%": replacement = "%"; break; case "\\":replacement = "\\"; break; case "n": replacement = "\n"; break; - case "a": replacement = ftos(floor(this.armorvalue)); break; - case "h": replacement = ftos(floor(this.health)); break; + case "a": replacement = ftos(floor(GetResourceAmount(this, RESOURCE_ARMOR))); break; + case "h": replacement = ftos(floor(GetResourceAmount(this, RESOURCE_HEALTH))); break; case "l": replacement = NearestLocation(this.origin); break; case "y": replacement = NearestLocation(cursor); break; case "d": replacement = NearestLocation(this.death_origin); break; case "w": replacement = ((this.(weaponentity).m_weapon == WEP_Null) ? ((this.(weaponentity).m_switchweapon == WEP_Null) ? Weapons_from(this.(weaponentity).cnt) : this.(weaponentity).m_switchweapon) : this.(weaponentity).m_weapon).m_name; break; - case "W": replacement = AmmoNameFromWeaponentity(this.(weaponentity)); break; + case "W": replacement = AmmoNameFromWeaponentity(this.(weaponentity).m_weapon); break; case "x": replacement = ((cursor_ent.netname == "" || !cursor_ent) ? "nothing" : cursor_ent.netname); break; case "s": replacement = ftos(vlen(this.velocity - this.velocity_z * '0 0 1')); break; case "S": replacement = ftos(vlen(this.velocity)); break; @@ -298,17 +318,13 @@ void GetCvars_handleString(entity this, entity store, string thisname, float f, { if (f < 0) { - if (store.(field)) - strunzone(store.(field)); - store.(field) = string_null; + strfree(store.(field)); } else if (f > 0) { if (thisname == name) { - if (store.(field)) - strunzone(store.(field)); - store.(field) = strzone(argv(f + 1)); + strcpy(store.(field), argv(f + 1)); } } else @@ -323,8 +339,7 @@ void GetCvars_handleString_Fixup(entity this, entity store, string thisname, flo string s = func(this, strcat1(store.(field))); if (s != store.(field)) { - strunzone(store.(field)); - store.(field) = strzone(s); + strcpy(store.(field), s); } } } @@ -366,14 +381,8 @@ void GetCvars_handleFloatOnce(entity this, entity store, string thisname, float } string W_FixWeaponOrder_ForceComplete_AndBuildImpulseList(entity this, string wo) { - string o; - o = W_FixWeaponOrder_ForceComplete(wo); - if(CS(this).weaponorder_byimpulse) - { - strunzone(CS(this).weaponorder_byimpulse); - CS(this).weaponorder_byimpulse = string_null; - } - CS(this).weaponorder_byimpulse = strzone(W_FixWeaponOrder_BuildImpulseList(o)); + string o = W_FixWeaponOrder_ForceComplete(wo); + strcpy(CS(this).weaponorder_byimpulse, W_FixWeaponOrder_BuildImpulseList(o)); return o; } @@ -407,6 +416,12 @@ REPLICATE(cvar_cl_weaponimpulsemode, int, "cl_weaponimpulsemode"); REPLICATE(cvar_g_xonoticversion, string, "g_xonoticversion"); +REPLICATE(cvar_cl_cts_noautoswitch, bool, "cl_cts_noautoswitch"); + +REPLICATE(cvar_cl_weapon_switch_reload, bool, "cl_weapon_switch_reload"); + +REPLICATE(cvar_cl_weapon_switch_fallback_to_impulse, bool, "cl_weapon_switch_fallback_to_impulse"); + /** * @param f -1: cleanup, 0: request, 1: receive */ @@ -591,24 +606,16 @@ void readplayerstartcvars() for (i = 0; i < t; ++i) { s = argv(i); - FOREACH(Weapons, it != WEP_Null, { - if(it.netname == s) - { - g_weaponarena_weapons |= (it.m_wepset); - g_weaponarena_list = strcat(g_weaponarena_list, it.m_name, " & "); - break; - } - }); + Weapon wep = Weapons_fromstr(s); + if(wep != WEP_Null) + { + g_weaponarena_weapons |= (wep.m_wepset); + g_weaponarena_list = strcat(g_weaponarena_list, wep.m_name, " & "); + } } g_weaponarena_list = strzone(substring(g_weaponarena_list, 0, strlen(g_weaponarena_list) - 3)); } - if(g_weaponarena) - g_weaponarena_random = cvar("g_weaponarena_random"); - else - g_weaponarena_random = 0; - g_weaponarena_random_with_blaster = cvar("g_weaponarena_random_with_blaster"); - if (g_weaponarena) { g_weapon_stay = 0; // incompatible @@ -629,6 +636,9 @@ void readplayerstartcvars() }); } + if(cvar("g_balance_superweapons_time") < 0) + start_items |= IT_UNLIMITED_SUPERWEAPONS; + if(!cvar("g_use_ammunition")) start_items |= IT_UNLIMITED_AMMO; @@ -886,7 +896,7 @@ void remove_safely(entity e) builtin_remove(e); } -void InitializeEntity(entity e, void(entity this) func, float order) +void InitializeEntity(entity e, void(entity this) func, int order) { entity prev, cur; @@ -1071,7 +1081,7 @@ bool SUB_NoImpactCheck(entity this, entity toucher) if(trace_dphitcontents == 0) { LOG_TRACEF("A hit from a projectile happened with no hit contents! DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct. (edict: %i, classname: %s, origin: %v)", this, this.classname, this.origin); - checkclient(this); + checkclient(this); // TODO: .health is checked in the engine with this, possibly replace with a QC function? } if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) return true; @@ -1097,7 +1107,6 @@ bool SUB_NoImpactCheck(entity this, entity toucher) #define SUB_OwnerCheck(ent,oth) ((oth) && ((oth) == (ent).owner)) -void W_Crylink_Dequeue(entity e); bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher) { if(SUB_OwnerCheck(this, toucher)) diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index 8a32b9b5bd..2374b4869b 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -1,10 +1,11 @@ #pragma once #include <server/defs.qh> +#include <server/g_world.qh> #include <common/t_items.qh> -#include "mutators/events.qh" +#include <server/mutators/_mod.qh> #include <common/constants.qh> #include <common/mapinfo.qh> @@ -29,7 +30,6 @@ float cvar_normal(string n) #define cvar_set_normal builtin_cvar_set .vector dropped_origin; -.float nottargeted; entity eliminatedPlayers; void EliminatedPlayers_Init(float(entity) isEliminated_func); @@ -62,6 +62,8 @@ void attach_sameorigin(entity e, entity to, string tag); void crosshair_trace(entity pl); void crosshair_trace_plusvisibletriggers(entity pl); +void WarpZone_crosshair_trace_plusvisibletriggers(entity pl); +void crosshair_trace_plusvisibletriggers__is_wz(entity pl, bool is_wz); void detach_sameorigin(entity e); @@ -69,6 +71,9 @@ void follow_sameorigin(entity e, entity to); string formatmessage(entity this, string msg); +/** print(), but only print if the server is not local */ +void dedicated_print(string input); + void GameLogEcho(string s); void GameLogInit(); @@ -91,6 +96,8 @@ float MoveToRandomMapLocation(entity e, float goodcontents, float badcontents, f string NearestLocation(vector p); +string AmmoNameFromWeaponentity(Weapon wep); + void play2(entity e, string filename); string playername(entity p, bool team_colorize); @@ -169,8 +176,7 @@ float g_pickup_fuel_max; float g_pickup_weapons_anyway; float g_weaponarena; WepSet g_weaponarena_weapons; -float g_weaponarena_random; -float g_weaponarena_random_with_blaster; +float g_weaponarena_random; // TODO string g_weaponarena_list; float g_weaponspeedfactor; float g_weaponratefactor; @@ -217,14 +223,11 @@ void readplayerstartcvars(); float sv_autotaunt; float sv_taunt; -string GetGametype(); // g_world.qc void readlevelcvars() { if(cvar("sv_allow_fullbright")) serverflags |= SERVERFLAG_ALLOW_FULLBRIGHT; - g_instagib = cvar("g_instagib"); - sv_clones = cvar("sv_clones"); sv_foginterval = cvar("sv_foginterval"); g_footsteps = cvar("g_footsteps"); @@ -325,17 +328,17 @@ void readlevelcvars() //#NO AUTOCVARS END -const float INITPRIO_FIRST = 0; -const float INITPRIO_GAMETYPE = 0; -const float INITPRIO_GAMETYPE_FALLBACK = 1; -const float INITPRIO_FINDTARGET = 10; -const float INITPRIO_DROPTOFLOOR = 20; -const float INITPRIO_SETLOCATION = 90; -const float INITPRIO_LINKDOORS = 91; -const float INITPRIO_LAST = 99; +const int INITPRIO_FIRST = 0; +const int INITPRIO_GAMETYPE = 0; +const int INITPRIO_GAMETYPE_FALLBACK = 1; +const int INITPRIO_FINDTARGET = 10; +const int INITPRIO_DROPTOFLOOR = 20; +const int INITPRIO_SETLOCATION = 90; +const int INITPRIO_LINKDOORS = 91; +const int INITPRIO_LAST = 99; .void(entity this) initialize_entity; -.float initialize_entity_order; +.int initialize_entity_order; .entity initialize_entity_next; entity initialize_entity_first; @@ -343,8 +346,8 @@ entity initialize_entity_first; -float sound_allowed(float dest, entity e); -void InitializeEntity(entity e, void(entity this) func, float order); +bool sound_allowed(int dest, entity e); +void InitializeEntity(entity e, void(entity this) func, int order); IntrusiveList g_ctrace_changed; STATIC_INIT(g_ctrace_changed) { g_ctrace_changed = IL_NEW(); } diff --git a/qcsrc/server/mutators/_mod.inc b/qcsrc/server/mutators/_mod.inc index f0108dec37..7b7cdf33df 100644 --- a/qcsrc/server/mutators/_mod.inc +++ b/qcsrc/server/mutators/_mod.inc @@ -1,4 +1,3 @@ // generated file; do not modify +#include <server/mutators/events.qc> #include <server/mutators/loader.qc> - -#include <server/mutators/mutator/_mod.inc> diff --git a/qcsrc/server/mutators/_mod.qh b/qcsrc/server/mutators/_mod.qh index 9888c94666..6adf8e0db5 100644 --- a/qcsrc/server/mutators/_mod.qh +++ b/qcsrc/server/mutators/_mod.qh @@ -1,4 +1,3 @@ // generated file; do not modify +#include <server/mutators/events.qh> #include <server/mutators/loader.qh> - -#include <server/mutators/mutator/_mod.qh> diff --git a/qcsrc/server/mutators/events.qc b/qcsrc/server/mutators/events.qc new file mode 100644 index 0000000000..c2dbb70215 --- /dev/null +++ b/qcsrc/server/mutators/events.qc @@ -0,0 +1 @@ +#include "events.qh" diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 6853c04a15..864623bbae 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -4,6 +4,9 @@ // register all possible hooks here +// to use a hook, first register your mutator using REGISTER_MUTATOR +// then create your function using MUTATOR_HOOKFUNCTION + /** called when a player becomes observer, after shared setup */ #define EV_MakePlayerObserver(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -24,6 +27,12 @@ MUTATOR_HOOKABLE(PutClientInServer, EV_PutClientInServer); /**/ MUTATOR_HOOKABLE(ForbidSpawn, EV_ForbidSpawn); +/** returns true if client should be put as player on connection */ +#define EV_AutoJoinOnConnection(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(AutoJoinOnConnection, EV_AutoJoinOnConnection); + /** called when player spawns to determine whether to give them random start weapons. Return true to forbid giving them. */ #define EV_ForbidRandomStartWeapons(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -120,46 +129,59 @@ MUTATOR_HOOKABLE(ItemSound, EV_ItemSound); /** target */ i(entity, MUTATOR_ARGV_1_entity) \ /** frag score */ i(float, MUTATOR_ARGV_2_float) \ /** */ o(float, MUTATOR_ARGV_2_float) \ + /** deathtype */ i(float, MUTATOR_ARGV_3_float) \ + /** wep entity */ i(entity, MUTATOR_ARGV_4_entity) \ /**/ MUTATOR_HOOKABLE(GiveFragsForKill, EV_GiveFragsForKill); /** called when the match ends */ MUTATOR_HOOKABLE(MatchEnd, EV_NO_ARGS); -/** allows adjusting allowed teams */ -#define EV_CheckAllowedTeams(i, o) \ +/** Allows adjusting allowed teams. Return true to use the bitmask value and set + * non-empty string to use team entity name. Both behaviors can be active at the + * same time and will stack allowed teams. + */ +#define EV_TeamBalance_CheckAllowedTeams(i, o) \ /** mask of teams */ i(float, MUTATOR_ARGV_0_float) \ /**/ o(float, MUTATOR_ARGV_0_float) \ /** team entity name */ i(string, MUTATOR_ARGV_1_string) \ /**/ o(string, MUTATOR_ARGV_1_string) \ /** player checked */ i(entity, MUTATOR_ARGV_2_entity) \ /**/ -MUTATOR_HOOKABLE(CheckAllowedTeams, EV_CheckAllowedTeams); +MUTATOR_HOOKABLE(TeamBalance_CheckAllowedTeams, + EV_TeamBalance_CheckAllowedTeams); /** return true to manually override team counts */ -MUTATOR_HOOKABLE(GetTeamCounts, EV_NO_ARGS); +MUTATOR_HOOKABLE(TeamBalance_GetTeamCounts, EV_NO_ARGS); -/** allow overriding of team counts */ -#define EV_GetTeamCount(i, o) \ - /** team to count */ i(float, MUTATOR_ARGV_0_float) \ +/** allows overriding of team counts */ +#define EV_TeamBalance_GetTeamCount(i, o) \ + /** team index 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) \ + /** number of players in a team */ o(float, MUTATOR_ARGV_2_float) \ + /** number of bots in a team */ o(float, MUTATOR_ARGV_3_float) \ + /**/ +MUTATOR_HOOKABLE(TeamBalance_GetTeamCount, EV_TeamBalance_GetTeamCount); + +/** allows overriding the teams that will make the game most balanced if the + * player joins any of them. + */ +#define EV_TeamBalance_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); +MUTATOR_HOOKABLE(TeamBalance_FindBestTeams, EV_TeamBalance_FindBestTeams); + +/** Called during autobalance. Return true to override the player that will be +switched. */ +#define EV_TeamBalance_GetPlayerForTeamSwitch(i, o) \ + /** source team index */ i(int, MUTATOR_ARGV_0_int) \ + /** destination team index */ i(int, MUTATOR_ARGV_1_int) \ + /** is looking for bot */ i(bool, MUTATOR_ARGV_2_bool) \ + /** player to switch */ o(entity, MUTATOR_ARGV_3_entity) \ + /**/ +MUTATOR_HOOKABLE(TeamBalance_GetPlayerForTeamSwitch, + EV_TeamBalance_GetPlayerForTeamSwitch); /** copies variables for spectating "spectatee" to "this" */ #define EV_SpectateCopy(i, o) \ @@ -229,8 +251,16 @@ MUTATOR_HOOKABLE(SetStartItems, EV_NO_ARGS); /**/ MUTATOR_HOOKABLE(CustomizeWaypoint, EV_CustomizeWaypoint); +/** Check if items having the given definition are allowed to spawn. + * Return true to disallow spawning. + */ +#define EV_FilterItemDefinition(i, o) \ + /** item */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(FilterItemDefinition, EV_FilterItemDefinition); + /** - * checks if the current item may be spawned (.items and .weapons may be read and written to, as well as the ammo_ fields) + * checks if the current item may be spawned (.items may be read and written to, as well as the ammo_ fields) * return error to request removal */ #define EV_FilterItem(i, o) \ @@ -400,7 +430,8 @@ MUTATOR_HOOKABLE(PlayerDamage_SplitHealthArmor, EV_PlayerDamage_SplitHealthArmor /** mirrordamage */ i(float, MUTATOR_ARGV_5_float) \ /** mirrordamage */ o(float, MUTATOR_ARGV_5_float) \ /** force */ i(vector, MUTATOR_ARGV_6_vector) \ - /** force */ o(vector, MUTATOR_ARGV_6_vector) \ + /** force */ o(vector, MUTATOR_ARGV_6_vector) \ + /** weapon entity */ i(entity, MUTATOR_ARGV_7_entity) \ /**/ MUTATOR_HOOKABLE(Damage_Calculate, EV_Damage_Calculate); @@ -424,6 +455,8 @@ MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged); #define EV_W_DecreaseAmmo(i, o) \ /** actor */ i(entity, MUTATOR_ARGV_0_entity) \ /** weapon entity */ i(entity, MUTATOR_ARGV_1_entity) \ + /** ammo to take */ i(float, MUTATOR_ARGV_2_float) \ + /**/ o(float, MUTATOR_ARGV_2_float) \ /**/ MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo); @@ -679,6 +712,26 @@ constants for resource types. Return true to forbid the change. */ /**/ MUTATOR_HOOKABLE(SetResourceAmount, EV_SetResourceAmount); +/** Called after the amount of resource of an entity has changed. See RESOURCE_* +constants for resource types. Amount wasted is the amount of resource that is +above resource limit so it was not given. */ +#define EV_ResourceAmountChanged(i, o) \ + /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \ + /** resource type */ i(int, MUTATOR_ARGV_1_int) \ + /** amount */ i(float, MUTATOR_ARGV_2_float) \ + /**/ +MUTATOR_HOOKABLE(ResourceAmountChanged, EV_ResourceAmountChanged); + +/** Called when there was an attempt to set entity resources higher than their +limit. See RESOURCE_* constants for resource types. Amount wasted is the amount +of resource that is above resource limit so it was not given. */ +#define EV_ResourceWasted(i, o) \ + /** checked entity */ i(entity, MUTATOR_ARGV_0_entity) \ + /** resource type */ i(int, MUTATOR_ARGV_1_int) \ + /** amount wasted */ i(float, MUTATOR_ARGV_2_float) \ + /**/ +MUTATOR_HOOKABLE(ResourceWasted, EV_ResourceWasted); + /** Called when entity is being given some resource. See RESOURCE_* constants for resource types. Return true to forbid giving. */ #define EV_GiveResource(i, o) \ @@ -690,6 +743,43 @@ for resource types. Return true to forbid giving. */ /**/ MUTATOR_HOOKABLE(GiveResource, EV_GiveResource); +/** Called when entity is being given some resource with specified limit. See +RESOURCE_* constants for resource types. Return true to forbid giving. */ +#define EV_GiveResourceWithLimit(i, o) \ + /** receiver */ i(entity, MUTATOR_ARGV_0_entity) \ + /** resource type */ i(int, MUTATOR_ARGV_1_int) \ + /**/ o(int, MUTATOR_ARGV_1_int) \ + /** amount */ i(float, MUTATOR_ARGV_2_float) \ + /**/ o(float, MUTATOR_ARGV_2_float) \ + /** limit */ i(float, MUTATOR_ARGV_3_float) \ + /**/ o(float, MUTATOR_ARGV_3_float) \ + /**/ +MUTATOR_HOOKABLE(GiveResourceWithLimit, EV_GiveResourceWithLimit); + +/** Called when some resource is being taken from an entity. See RESOURCE_* constants +for resource types. Return true to forbid giving. */ +#define EV_TakeResource(i, o) \ + /** receiver */ i(entity, MUTATOR_ARGV_0_entity) \ + /** resource type */ i(int, MUTATOR_ARGV_1_int) \ + /**/ o(int, MUTATOR_ARGV_1_int) \ + /** amount */ i(float, MUTATOR_ARGV_2_float) \ + /**/ o(float, MUTATOR_ARGV_2_float) \ + /**/ +MUTATOR_HOOKABLE(TakeResource, EV_TakeResource); + +/** Called when some resource is being taken from an entity, with a limit. See +RESOURCE_* constants for resource types. Return true to forbid giving. */ +#define EV_TakeResourceWithLimit(i, o) \ + /** receiver */ i(entity, MUTATOR_ARGV_0_entity) \ + /** resource type */ i(int, MUTATOR_ARGV_1_int) \ + /**/ o(int, MUTATOR_ARGV_1_int) \ + /** amount */ i(float, MUTATOR_ARGV_2_float) \ + /**/ o(float, MUTATOR_ARGV_2_float) \ + /** limit */ i(float, MUTATOR_ARGV_3_float) \ + /**/ o(float, MUTATOR_ARGV_3_float) \ + /**/ +MUTATOR_HOOKABLE(TakeResourceWithLimit, EV_TakeResourceWithLimit); + /** called at when a player connect */ #define EV_ClientConnect(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -962,9 +1052,9 @@ 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) \ - /** new team */ i(float, MUTATOR_ARGV_2_float) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** current team index */ i(float, MUTATOR_ARGV_1_float) \ + /** new team index */ i(float, MUTATOR_ARGV_2_float) \ /**/ MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam); @@ -972,9 +1062,9 @@ 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) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** old team index */ i(float, MUTATOR_ARGV_1_float) \ + /** current team index */ i(float, MUTATOR_ARGV_2_float) \ /**/ MUTATOR_HOOKABLE(Player_ChangedTeam, EV_Player_ChangedTeam); @@ -1060,6 +1150,13 @@ MUTATOR_HOOKABLE(Item_ScheduleRespawn, EV_Item_ScheduleRespawn); /**/ MUTATOR_HOOKABLE(PlayerPhysics_UpdateStats, EV_PlayerPhysics_UpdateStats); +/** called after physics stats are set on a player, allows post-initialization modifications */ +#define EV_PlayerPhysics_PostUpdateStats(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** maxspeed_mod */ i(float, MUTATOR_ARGV_1_float) \ + /**/ +MUTATOR_HOOKABLE(PlayerPhysics_PostUpdateStats, EV_PlayerPhysics_PostUpdateStats); + /** return true to use your own aim target (or none at all) */ #define EV_HavocBot_Aim(i, o) \ /** bot */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -1071,3 +1168,47 @@ MUTATOR_HOOKABLE(HavocBot_Aim, EV_HavocBot_Aim); /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /**/ MUTATOR_HOOKABLE(CalculateRespawnTime, EV_CalculateRespawnTime); + +/** called when parsing a vote command. */ +#define EV_VoteCommand_Parse(i, o) \ + /** caller */ i(entity, MUTATOR_ARGV_0_entity) \ + /** first command */ i(string, MUTATOR_ARGV_1_string) \ + /** vote command */ i(string, MUTATOR_ARGV_2_string) \ + /** start position of vote command */ i(float, MUTATOR_ARGV_3_float) \ + /** argument count */ i(float, MUTATOR_ARGV_4_float) \ + /**/ +MUTATOR_HOOKABLE(VoteCommand_Parse, EV_VoteCommand_Parse); + +enum { + MUT_VOTEPARSE_CONTINUE, // return this flag to make the function continue as normal + MUT_VOTEPARSE_SUCCESS, // return 1 (vote parsed) + MUT_VOTEPARSE_INVALID, // return -1 (vote parsed but counted as invalid, no action or vote) + MUT_VOTEPARSE_UNACCEPTABLE // return 0 (vote parameter counted as unacceptable, warns caller) +}; + +/** + * Called when freezing an entity (monster or player), return true to force showing a waypoint + */ +#define EV_Freeze(i, o) \ + /** targ */ i(entity, MUTATOR_ARGV_0_entity) \ + /** revive speed */ i(float, MUTATOR_ARGV_1_float) \ + /** frozen type */ i(int, MUTATOR_ARGV_2_int) \ + /**/ +MUTATOR_HOOKABLE(Freeze, EV_Freeze); + +/** + * Called when an entity (monster or player) is defrosted + */ +#define EV_Unfreeze(i, o) \ + /** targ */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(Unfreeze, EV_Unfreeze); + +/** + * Called when a player is trying to join, argument is the number of players allowed to join the match + */ +#define EV_GetPlayerLimit(i, o) \ + /** g_maxplayers */ i(int, MUTATOR_ARGV_0_int) \ + /**/ o(int, MUTATOR_ARGV_0_int) \ + /**/ +MUTATOR_HOOKABLE(GetPlayerLimit, EV_GetPlayerLimit); diff --git a/qcsrc/server/mutators/gamemode.qh b/qcsrc/server/mutators/gamemode.qh deleted file mode 100644 index b0f42f59e5..0000000000 --- a/qcsrc/server/mutators/gamemode.qh +++ /dev/null @@ -1,108 +0,0 @@ -#pragma once - -#include <server/miscfunctions.qh> -#include <server/g_world.qh> -#include <server/round_handler.qh> -#include <server/scores.qh> -#include <server/scores_rules.qh> -#include <server/teamplay.qh> -#include <common/gamemodes/rules.qh> - -#include "mutator.qh" - -// TODO: trim - -#include <lib/warpzone/anglestransform.qh> -#include <lib/warpzone/common.qh> -#include <lib/warpzone/util_server.qh> -#include <lib/warpzone/server.qh> -#include <common/constants.qh> -#include <common/scores.qh> -#include <common/stats.qh> -#include <common/teams.qh> -#include <common/util.qh> -#include <common/command/_mod.qh> -#include <common/net_notice.qh> -#include <common/animdecide.qh> -#include <common/monsters/_mod.qh> -#include <common/monsters/sv_monsters.qh> -#include <common/monsters/sv_spawn.qh> -#include <common/weapons/config.qh> -#include <common/weapons/_all.qh> -#include <server/weapons/accuracy.qh> -#include <server/weapons/common.qh> -#include <server/weapons/csqcprojectile.qh> -#include <server/weapons/hitplot.qh> -#include <server/weapons/selection.qh> -#include <server/weapons/spawning.qh> -#include <server/weapons/throwing.qh> -#include <server/weapons/tracing.qh> -#include <server/weapons/weaponstats.qh> -#include <server/weapons/weaponsystem.qh> -#include <common/t_items.qh> -#include <server/autocvars.qh> -#include <server/constants.qh> -#include <server/defs.qh> -#include <common/notifications/all.qh> -#include <common/deathtypes/all.qh> -#include <common/turrets/sv_turrets.qh> -#include <common/vehicles/all.qh> -#include <server/campaign.qh> -#include <common/campaign_common.qh> -#include <common/mapinfo.qh> -#include <server/command/common.qh> -#include <server/command/banning.qh> -#include <server/command/radarmap.qh> -#include <server/command/vote.qh> -#include <server/command/getreplies.qh> -#include <server/command/cmd.qh> -#include <server/command/sv_cmd.qh> -#include <common/csqcmodel_settings.qh> -#include <lib/csqcmodel/common.qh> -#include <lib/csqcmodel/sv_model.qh> -#include <server/anticheat.qh> -#include <server/cheats.qh> -#include <common/playerstats.qh> -#include <server/portals.qh> -#include <server/g_hook.qh> -#include <server/spawnpoints.qh> -#include <server/mapvoting.qh> -#include <server/ipban.qh> -#include <server/antilag.qh> -#include <server/playerdemo.qh> -#include <server/item_key.qh> -#include <server/pathlib/pathlib.qh> -#include <common/vehicles/all.qh> - -#include <common/mutators/mutator/waypoints/waypointsprites.qh> - -#include <server/client.qh> -#include <server/player.qh> -#include <server/impulse.qh> -#include <server/cheats.qh> -#include <server/g_damage.qh> - -#include <server/bot/api.qh> - -#include <server/command/_mod.qh> - -#include <common/monsters/_mod.qh> - -#include <server/weapons/tracing.qh> -#include <server/weapons/weaponsystem.qh> - -#include <common/physics/player.qh> -#include <common/effects/qc/_mod.qh> -#include <common/deathtypes/all.qh> -#include <common/notifications/all.qh> -#include <common/triggers/teleporters.qh> -#include <common/triggers/subs.qh> -#include <common/stats.qh> -#include <common/teams.qh> - -#include <lib/warpzone/server.qh> -#include <lib/warpzone/util_server.qh> - -.float lastground; -float total_players; -float redalive, bluealive, yellowalive, pinkalive; diff --git a/qcsrc/server/mutators/mutator.qh b/qcsrc/server/mutators/mutator.qh deleted file mode 100644 index e051cd6978..0000000000 --- a/qcsrc/server/mutators/mutator.qh +++ /dev/null @@ -1,34 +0,0 @@ -#pragma once - -#include <common/mutators/base.qh> - -#include <server/client.qh> -#include <server/player.qh> -#include <server/impulse.qh> -#include <server/cheats.qh> -#include <server/g_damage.qh> -#include <server/round_handler.qh> -#include <server/scores.qh> -#include <server/scores_rules.qh> - -#include <server/bot/api.qh> - -#include <server/command/_mod.qh> - -#include <server/weapons/common.qh> -#include <server/weapons/tracing.qh> -#include <server/weapons/throwing.qh> -#include <server/weapons/weaponsystem.qh> - -#include <common/deathtypes/all.qh> -#include <common/notifications/all.qh> -#include <common/triggers/teleporters.qh> -#include <common/triggers/subs.qh> -#include <common/stats.qh> -#include <common/teams.qh> - -#include <common/monsters/_mod.qh> - -#include <lib/warpzone/anglestransform.qh> -#include <lib/warpzone/server.qh> -#include <lib/warpzone/util_server.qh> diff --git a/qcsrc/server/mutators/mutator/_mod.inc b/qcsrc/server/mutators/mutator/_mod.inc deleted file mode 100644 index 6835f5d560..0000000000 --- a/qcsrc/server/mutators/mutator/_mod.inc +++ /dev/null @@ -1,14 +0,0 @@ -// generated file; do not modify -#include <server/mutators/mutator/gamemode_assault.qc> -#include <server/mutators/mutator/gamemode_ca.qc> -#include <server/mutators/mutator/gamemode_ctf.qc> -#include <server/mutators/mutator/gamemode_cts.qc> -#include <server/mutators/mutator/gamemode_deathmatch.qc> -#include <server/mutators/mutator/gamemode_domination.qc> -#include <server/mutators/mutator/gamemode_freezetag.qc> -#include <server/mutators/mutator/gamemode_invasion.qc> -#include <server/mutators/mutator/gamemode_keepaway.qc> -#include <server/mutators/mutator/gamemode_keyhunt.qc> -#include <server/mutators/mutator/gamemode_lms.qc> -#include <server/mutators/mutator/gamemode_race.qc> -#include <server/mutators/mutator/gamemode_tdm.qc> diff --git a/qcsrc/server/mutators/mutator/_mod.qh b/qcsrc/server/mutators/mutator/_mod.qh deleted file mode 100644 index aef0b332ab..0000000000 --- a/qcsrc/server/mutators/mutator/_mod.qh +++ /dev/null @@ -1,14 +0,0 @@ -// generated file; do not modify -#include <server/mutators/mutator/gamemode_assault.qh> -#include <server/mutators/mutator/gamemode_ca.qh> -#include <server/mutators/mutator/gamemode_ctf.qh> -#include <server/mutators/mutator/gamemode_cts.qh> -#include <server/mutators/mutator/gamemode_deathmatch.qh> -#include <server/mutators/mutator/gamemode_domination.qh> -#include <server/mutators/mutator/gamemode_freezetag.qh> -#include <server/mutators/mutator/gamemode_invasion.qh> -#include <server/mutators/mutator/gamemode_keepaway.qh> -#include <server/mutators/mutator/gamemode_keyhunt.qh> -#include <server/mutators/mutator/gamemode_lms.qh> -#include <server/mutators/mutator/gamemode_race.qh> -#include <server/mutators/mutator/gamemode_tdm.qh> diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc deleted file mode 100644 index 70e2669184..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ /dev/null @@ -1,617 +0,0 @@ -#include "gamemode_assault.qh" - -#include <lib/float.qh> - -.entity sprite; -#define AS_ROUND_DELAY 5 - -// random functions -void assault_objective_use(entity this, entity actor, entity trigger) -{ - // activate objective - this.health = 100; - //print("^2Activated objective ", this.targetname, "=", etos(this), "\n"); - //print("Activator is ", actor.classname, "\n"); - - IL_EACH(g_assault_objectivedecreasers, it.target == this.targetname, - { - target_objective_decrease_activate(it); - }); -} - -vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current) -{ - if(this.health < 0 || this.health >= ASSAULT_VALUE_INACTIVE) - return '-1 0 0'; - return current; -} - -// reset this objective. Used when spawning an objective -// and when a new round starts -void assault_objective_reset(entity this) -{ - this.health = ASSAULT_VALUE_INACTIVE; -} - -// decrease the health of targeted objectives -void assault_objective_decrease_use(entity this, entity actor, entity trigger) -{ - if(actor.team != assault_attacker_team) - { - // wrong team triggered decrease - return; - } - - if(trigger.assault_sprite) - { - WaypointSprite_Disown(trigger.assault_sprite, waypointsprite_deadlifetime); - if(trigger.classname == "func_assault_destructible") - trigger.sprite = NULL; // TODO: just unsetting it?! - } - else - return; // already activated! cannot activate again! - - if(this.enemy.health < ASSAULT_VALUE_INACTIVE) - { - if(this.enemy.health - this.dmg > 0.5) - { - GameRules_scoring_add_team(actor, SCORE, this.dmg); - this.enemy.health = this.enemy.health - this.dmg; - } - else - { - GameRules_scoring_add_team(actor, SCORE, this.enemy.health); - GameRules_scoring_add_team(actor, ASSAULT_OBJECTIVES, 1); - this.enemy.health = -1; - - if(this.enemy.message) - FOREACH_CLIENT(IS_PLAYER(it), { centerprint(it, this.enemy.message); }); - - SUB_UseTargets(this.enemy, this, trigger); - } - } -} - -void assault_setenemytoobjective(entity this) -{ - IL_EACH(g_assault_objectives, it.targetname == this.target, - { - if(this.enemy == NULL) - this.enemy = it; - else - objerror(this, "more than one objective as target - fix the map!"); - break; - }); - - if(this.enemy == NULL) - objerror(this, "no objective as target - fix the map!"); -} - -bool assault_decreaser_sprite_visible(entity this, entity player, entity view) -{ - if(this.assault_decreaser.enemy.health >= ASSAULT_VALUE_INACTIVE) - return false; - - return true; -} - -void target_objective_decrease_activate(entity this) -{ - entity spr; - this.owner = NULL; - FOREACH_ENTITY_STRING(target, this.targetname, - { - if(it.assault_sprite != NULL) - { - WaypointSprite_Disown(it.assault_sprite, waypointsprite_deadlifetime); - if(it.classname == "func_assault_destructible") - it.sprite = NULL; // TODO: just unsetting it?! - } - - spr = WaypointSprite_SpawnFixed(WP_AssaultDefend, 0.5 * (it.absmin + it.absmax), it, assault_sprite, RADARICON_OBJECTIVE); - spr.assault_decreaser = this; - spr.waypointsprite_visible_for_player = assault_decreaser_sprite_visible; - spr.classname = "sprite_waypoint"; - WaypointSprite_UpdateRule(spr, assault_attacker_team, SPRITERULE_TEAMPLAY); - if(it.classname == "func_assault_destructible") - { - WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy); - WaypointSprite_UpdateMaxHealth(spr, it.max_health); - WaypointSprite_UpdateHealth(spr, it.health); - it.sprite = spr; - } - else - WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultPush, WP_AssaultPush); - }); -} - -void target_objective_decrease_findtarget(entity this) -{ - assault_setenemytoobjective(this); -} - -void target_assault_roundend_reset(entity this) -{ - //print("round end reset\n"); - ++this.cnt; // up round counter - this.winning = false; // up round -} - -void target_assault_roundend_use(entity this, entity actor, entity trigger) -{ - this.winning = 1; // round has been won by attackers -} - -void assault_roundstart_use(entity this, entity actor, entity trigger) -{ - SUB_UseTargets(this, this, trigger); - - //(Re)spawn all turrets - IL_EACH(g_turrets, true, - { - // Swap turret teams - if(it.team == NUM_TEAM_1) - it.team = NUM_TEAM_2; - else - it.team = NUM_TEAM_1; - - // Doubles as teamchange - turret_respawn(it); - }); -} -void assault_roundstart_use_this(entity this) -{ - assault_roundstart_use(this, NULL, NULL); -} - -void assault_wall_think(entity this) -{ - if(this.enemy.health < 0) - { - this.model = ""; - this.solid = SOLID_NOT; - } - else - { - this.model = this.mdl; - this.solid = SOLID_BSP; - } - - this.nextthink = time + 0.2; -} - -// trigger new round -// reset objectives, toggle spawnpoints, reset triggers, ... -void assault_new_round(entity this) -{ - //bprint("ASSAULT: new round\n"); - - // up round counter - this.winning = this.winning + 1; - - // swap attacker/defender roles - if(assault_attacker_team == NUM_TEAM_1) - assault_attacker_team = NUM_TEAM_2; - else - assault_attacker_team = NUM_TEAM_1; - - IL_EACH(g_saved_team, !IS_CLIENT(it), - { - if(it.team_saved == NUM_TEAM_1) - it.team_saved = NUM_TEAM_2; - else if(it.team_saved == NUM_TEAM_2) - it.team_saved = NUM_TEAM_1; - }); - - // reset the level with a countdown - cvar_set("timelimit", ftos(ceil(time - AS_ROUND_DELAY - game_starttime) / 60)); - ReadyRestart_force(); // sets game_starttime -} - -entity as_round; -.entity ent_winning; -void as_round_think() -{ - game_stopped = false; - assault_new_round(as_round.ent_winning); - delete(as_round); - as_round = NULL; -} - -// Assault winning condition: If the attackers triggered a round end (by fulfilling all objectives) -// they win. Otherwise the defending team wins once the timelimit passes. -int WinningCondition_Assault() -{ - if(as_round) - return WINNING_NO; - - WinningConditionHelper(NULL); // set worldstatus - - int status = WINNING_NO; - // as the timelimit has not yet passed just assume the defending team will win - if(assault_attacker_team == NUM_TEAM_1) - { - SetWinners(team, NUM_TEAM_2); - } - else - { - SetWinners(team, NUM_TEAM_1); - } - - entity ent; - ent = find(NULL, classname, "target_assault_roundend"); - if(ent) - { - if(ent.winning) // round end has been triggered by attacking team - { - bprint("Assault: round completed.\n"); - SetWinners(team, assault_attacker_team); - - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 666 - TeamScore_AddToTeam(assault_attacker_team, ST_ASSAULT_OBJECTIVES, 0)); - - if(ent.cnt == 1 || autocvar_g_campaign) // this was the second round - { - status = WINNING_YES; - } - else - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ASSAULT_OBJ_DESTROYED, ceil(time - game_starttime)); - as_round = new(as_round); - as_round.think = as_round_think; - as_round.ent_winning = ent; - as_round.nextthink = time + AS_ROUND_DELAY; - game_stopped = true; - - // make sure timelimit isn't hit while the game is blocked - if(autocvar_timelimit > 0) - if(time + AS_ROUND_DELAY >= game_starttime + autocvar_timelimit * 60) - cvar_set("timelimit", ftos(autocvar_timelimit + AS_ROUND_DELAY / 60)); - } - } - } - - return status; -} - -// spawnfuncs -spawnfunc(info_player_attacker) -{ - if (!g_assault) { delete(this); return; } - - this.team = NUM_TEAM_1; // red, gets swapped every round - spawnfunc_info_player_deathmatch(this); -} - -spawnfunc(info_player_defender) -{ - if (!g_assault) { delete(this); return; } - - this.team = NUM_TEAM_2; // blue, gets swapped every round - spawnfunc_info_player_deathmatch(this); -} - -spawnfunc(target_objective) -{ - if (!g_assault) { delete(this); return; } - - this.classname = "target_objective"; - IL_PUSH(g_assault_objectives, this); - this.use = assault_objective_use; - this.reset = assault_objective_reset; - this.reset(this); - this.spawn_evalfunc = target_objective_spawn_evalfunc; -} - -spawnfunc(target_objective_decrease) -{ - if (!g_assault) { delete(this); return; } - - this.classname = "target_objective_decrease"; - IL_PUSH(g_assault_objectivedecreasers, this); - - if(!this.dmg) - this.dmg = 101; - - this.use = assault_objective_decrease_use; - this.health = ASSAULT_VALUE_INACTIVE; - this.max_health = ASSAULT_VALUE_INACTIVE; - this.enemy = NULL; - - InitializeEntity(this, target_objective_decrease_findtarget, INITPRIO_FINDTARGET); -} - -// destructible walls that can be used to trigger target_objective_decrease -spawnfunc(func_breakable); -spawnfunc(func_assault_destructible) -{ - if (!g_assault) { delete(this); return; } - - this.spawnflags = 3; - this.classname = "func_assault_destructible"; - IL_PUSH(g_assault_destructibles, this); - - if(assault_attacker_team == NUM_TEAM_1) - this.team = NUM_TEAM_2; - else - this.team = NUM_TEAM_1; - - spawnfunc_func_breakable(this); -} - -spawnfunc(func_assault_wall) -{ - if (!g_assault) { delete(this); return; } - - this.classname = "func_assault_wall"; - this.mdl = this.model; - _setmodel(this, this.mdl); - this.solid = SOLID_BSP; - setthink(this, assault_wall_think); - this.nextthink = time; - InitializeEntity(this, assault_setenemytoobjective, INITPRIO_FINDTARGET); -} - -spawnfunc(target_assault_roundend) -{ - if (!g_assault) { delete(this); return; } - - this.winning = 0; // round not yet won by attackers - this.classname = "target_assault_roundend"; - this.use = target_assault_roundend_use; - this.cnt = 0; // first round - this.reset = target_assault_roundend_reset; -} - -spawnfunc(target_assault_roundstart) -{ - if (!g_assault) { delete(this); return; } - - assault_attacker_team = NUM_TEAM_1; - this.classname = "target_assault_roundstart"; - this.use = assault_roundstart_use; - this.reset2 = assault_roundstart_use_this; - InitializeEntity(this, assault_roundstart_use_this, INITPRIO_FINDTARGET); -} - -// legacy bot code -void havocbot_goalrating_ast_targets(entity this, float ratingscale) -{ - IL_EACH(g_assault_destructibles, it.bot_attack, - { - if (it.target == "") - continue; - - bool found = false; - entity destr = it; - IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target, - { - if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE) - { - found = true; - break; - } - }); - - if(!found) - continue; - - vector p = 0.5 * (it.absmin + it.absmax); - - // Find and rate waypoints around it - found = false; - entity best = NULL; - float bestvalue = 99999999999; - entity des = it; - for(float radius = 0; radius < 1500 && !found; radius += 500) - { - FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED), - { - if(checkpvs(it.origin, des)) - { - found = true; - if(it.cnt < bestvalue) - { - best = it; - bestvalue = it.cnt; - } - } - }); - } - - if(best) - { - /// dprint("waypoints around target were found\n"); - // te_lightning2(NULL, '0 0 0', best.origin); - // te_knightspike(best.origin); - - navigation_routerating(this, best, ratingscale, 4000); - best.cnt += 1; - - this.havocbot_attack_time = 0; - - if(checkpvs(this.origin + this.view_ofs, it)) - if(checkpvs(this.origin + this.view_ofs, best)) - { - // dprint("increasing attack time for this target\n"); - this.havocbot_attack_time = time + 2; - } - } - }); -} - -void havocbot_role_ast_offense(entity this) -{ - if(IS_DEAD(this)) - { - this.havocbot_attack_time = 0; - havocbot_ast_reset_role(this); - return; - } - - // Set the role timeout if necessary - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 120; - - if (time > this.havocbot_role_timeout) - { - havocbot_ast_reset_role(this); - return; - } - - if(this.havocbot_attack_time>time) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 650); - havocbot_goalrating_ast_targets(this, 20000); - havocbot_goalrating_items(this, 15000, this.origin, 10000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ast_defense(entity this) -{ - if(IS_DEAD(this)) - { - this.havocbot_attack_time = 0; - havocbot_ast_reset_role(this); - return; - } - - // Set the role timeout if necessary - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 120; - - if (time > this.havocbot_role_timeout) - { - havocbot_ast_reset_role(this); - return; - } - - if(this.havocbot_attack_time>time) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 3000); - havocbot_goalrating_ast_targets(this, 20000); - havocbot_goalrating_items(this, 15000, this.origin, 10000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ast_setrole(entity this, float role) -{ - switch(role) - { - case HAVOCBOT_AST_ROLE_DEFENSE: - this.havocbot_role = havocbot_role_ast_defense; - this.havocbot_role_flags = HAVOCBOT_AST_ROLE_DEFENSE; - this.havocbot_role_timeout = 0; - break; - case HAVOCBOT_AST_ROLE_OFFENSE: - this.havocbot_role = havocbot_role_ast_offense; - this.havocbot_role_flags = HAVOCBOT_AST_ROLE_OFFENSE; - this.havocbot_role_timeout = 0; - break; - } -} - -void havocbot_ast_reset_role(entity this) -{ - if(IS_DEAD(this)) - return; - - if(this.team == assault_attacker_team) - havocbot_role_ast_setrole(this, HAVOCBOT_AST_ROLE_OFFENSE); - else - havocbot_role_ast_setrole(this, HAVOCBOT_AST_ROLE_DEFENSE); -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(as, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(player.team == assault_attacker_team) - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_ATTACKING); - else - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_ASSAULT_DEFENDING); -} - -MUTATOR_HOOKFUNCTION(as, TurretSpawn) -{ - entity turret = M_ARGV(0, entity); - - if(!turret.team || turret.team == FLOAT_MAX) - turret.team = 5; // this gets reversed when match starts? -} - -MUTATOR_HOOKFUNCTION(as, VehicleInit) -{ - entity veh = M_ARGV(0, entity); - - veh.nextthink = time + 0.5; -} - -MUTATOR_HOOKFUNCTION(as, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - havocbot_ast_reset_role(bot); - return true; -} - -MUTATOR_HOOKFUNCTION(as, PlayHitsound) -{ - entity frag_victim = M_ARGV(0, entity); - - return (frag_victim.classname == "func_assault_destructible"); -} - -MUTATOR_HOOKFUNCTION(as, CheckAllowedTeams) -{ - // assault always has 2 teams - c1 = c2 = 0; - return true; -} - -MUTATOR_HOOKFUNCTION(as, CheckRules_World) -{ - M_ARGV(0, float) = WinningCondition_Assault(); - return true; -} - -MUTATOR_HOOKFUNCTION(as, ReadLevelCvars) -{ - // incompatible - warmup_stage = 0; - sv_ready_restart_after_countdown = 0; -} - -MUTATOR_HOOKFUNCTION(as, OnEntityPreSpawn) -{ - entity ent = M_ARGV(0, entity); - - switch(ent.classname) - { - case "info_player_team1": - case "info_player_team2": - case "info_player_team3": - case "info_player_team4": - return true; - } -} - -MUTATOR_HOOKFUNCTION(as, ReadyRestart_Deny) -{ - // readyrestart not supported (yet) - return true; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qh b/qcsrc/server/mutators/mutator/gamemode_assault.qh deleted file mode 100644 index ea714e6a23..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qh +++ /dev/null @@ -1,43 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -const int ST_ASSAULT_OBJECTIVES = 1; - -REGISTER_MUTATOR(as, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - int teams = BITS(2); // always red vs blue - GameRules_scoring(teams, SFL_SORT_PRIO_SECONDARY, SFL_SORT_PRIO_SECONDARY, { - field_team(ST_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); - field(SP_ASSAULT_OBJECTIVES, "objectives", SFL_SORT_PRIO_PRIMARY); - }); - } - return 0; -} - -// sprites -.entity assault_decreaser; -.entity assault_sprite; - -// legacy bot defs -const int HAVOCBOT_AST_ROLE_NONE = 0; -const int HAVOCBOT_AST_ROLE_DEFENSE = 2; -const int HAVOCBOT_AST_ROLE_OFFENSE = 4; - -.int havocbot_role_flags; -.float havocbot_attack_time; - -void(entity this) havocbot_role_ast_defense; -void(entity this) havocbot_role_ast_offense; - -void(entity bot) havocbot_ast_reset_role; - -void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_items; -void(entity this, float ratingscale, vector org, float sradius) havocbot_goalrating_enemyplayers; - -// predefined spawnfuncs -void target_objective_decrease_activate(entity this); diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qc b/qcsrc/server/mutators/mutator/gamemode_ca.qc deleted file mode 100644 index 43ed39aea4..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qc +++ /dev/null @@ -1,487 +0,0 @@ -#include "gamemode_ca.qh" - -float autocvar_g_ca_damage2score_multiplier; -bool autocvar_g_ca_spectate_enemies; - -void CA_count_alive_players() -{ - total_players = redalive = bluealive = yellowalive = pinkalive = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - switch(it.team) - { - case NUM_TEAM_1: ++total_players; if(!IS_DEAD(it)) ++redalive; break; - case NUM_TEAM_2: ++total_players; if(!IS_DEAD(it)) ++bluealive; break; - case NUM_TEAM_3: ++total_players; if(!IS_DEAD(it)) ++yellowalive; break; - case NUM_TEAM_4: ++total_players; if(!IS_DEAD(it)) ++pinkalive; break; - } - }); - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - STAT(REDALIVE, it) = redalive; - STAT(BLUEALIVE, it) = bluealive; - STAT(YELLOWALIVE, it) = yellowalive; - STAT(PINKALIVE, it) = pinkalive; - }); -} - -float CA_GetWinnerTeam() -{ - float winner_team = 0; - if(redalive >= 1) - winner_team = NUM_TEAM_1; - if(bluealive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowalive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkalive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) - return winner_team; - return -1; // no player left -} - -void nades_Clear(entity player); - -#define CA_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) -#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == NumTeams(ca_teams)) -float CA_CheckWinner() -{ - if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); - FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); - - allowed_to_spawn = false; - game_stopped = true; - round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - return 1; - } - - CA_count_alive_players(); - if(CA_ALIVE_TEAMS() > 1) - return 0; - - int winner_team = CA_GetWinnerTeam(); - if(winner_team > 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); - TeamScore_AddToTeam(winner_team, ST_CA_ROUNDS, +1); - } - else if(winner_team == -1) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); - } - - allowed_to_spawn = false; - game_stopped = true; - round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - - FOREACH_CLIENT(IS_PLAYER(it), { nades_Clear(it); }); - - return 1; -} - -void CA_RoundStart() -{ - allowed_to_spawn = boolean(warmup_stage); -} - -bool CA_CheckTeams() -{ - static int prev_missing_teams_mask; - allowed_to_spawn = true; - CA_count_alive_players(); - if(CA_ALIVE_TEAMS_OK()) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - return true; - } - if(total_players == 0) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - return false; - } - int missing_teams_mask = 0; - if(ca_teams & BIT(0)) - missing_teams_mask += (!redalive) * 1; - if(ca_teams & BIT(1)) - missing_teams_mask += (!bluealive) * 2; - if(ca_teams & BIT(2)) - missing_teams_mask += (!yellowalive) * 4; - if(ca_teams & BIT(3)) - missing_teams_mask += (!pinkalive) * 8; - if(prev_missing_teams_mask != missing_teams_mask) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); - prev_missing_teams_mask = missing_teams_mask; - } - return false; -} - -bool ca_isEliminated(entity e) -{ - if(e.caplayer == 1 && (IS_DEAD(e) || e.frags == FRAGS_LMS_LOSER)) - return true; - if(e.caplayer == 0.5) - return true; - return false; -} - -/** Returns next available player to spectate if g_ca_spectate_enemies == 0 */ -entity CA_SpectateNext(entity player, entity start) -{ - if (SAME_TEAM(start, player)) return start; - // continue from current player - for (entity e = start; (e = find(e, classname, STR_PLAYER)); ) - { - if (SAME_TEAM(player, e)) return e; - } - // restart from begining - for (entity e = NULL; (e = find(e, classname, STR_PLAYER)); ) - { - if (SAME_TEAM(player, e)) return e; - } - return start; -} - - -MUTATOR_HOOKFUNCTION(ca, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.caplayer = 1; - if (!warmup_stage) - eliminatedPlayers.SendFlags |= 1; -} - -MUTATOR_HOOKFUNCTION(ca, ForbidSpawn) -{ - entity player = M_ARGV(0, entity); - - // spectators / observers that weren't playing can join; they are - // immediately forced to observe in the PutClientInServer hook - // this way they are put in a team and can play in the next round - if (!allowed_to_spawn && player.caplayer) - return true; - return false; -} - -MUTATOR_HOOKFUNCTION(ca, PutClientInServer) -{ - entity player = M_ARGV(0, entity); - - if (!allowed_to_spawn && IS_PLAYER(player)) // this is true even when player is trying to join - { - TRANSMUTE(Observer, player); - if (CS(player).jointime != time && !player.caplayer) // not when connecting - { - player.caplayer = 0.5; - Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_JOIN_LATE); - } - } -} - -MUTATOR_HOOKFUNCTION(ca, reset_map_players) -{ - FOREACH_CLIENT(true, { - CS(it).killcount = 0; - if (!it.caplayer && IS_BOT_CLIENT(it)) - { - it.team = -1; - it.caplayer = 1; - } - if (it.caplayer) - { - TRANSMUTE(Player, it); - it.caplayer = 1; - PutClientInServer(it); - } - }); - bot_relinkplayerlist(); - return true; -} - -MUTATOR_HOOKFUNCTION(ca, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - TRANSMUTE(Observer, player); - return true; -} - -MUTATOR_HOOKFUNCTION(ca, reset_map_global) -{ - allowed_to_spawn = true; - return true; -} - -MUTATOR_HOOKFUNCTION(ca, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(0, float) = ca_teams; -} - -entity ca_LastPlayerForTeam(entity this) -{ - entity last_pl = NULL; - FOREACH_CLIENT(IS_PLAYER(it) && it != this, { - if (!IS_DEAD(it)) - if (SAME_TEAM(this, it)) - if (!last_pl) - last_pl = it; - else - return NULL; - }); - return last_pl; -} - -void ca_LastPlayerForTeam_Notify(entity this) -{ - if (round_handler_IsActive()) - if (round_handler_IsRoundStarted()) - { - entity pl = ca_LastPlayerForTeam(this); - if (pl) - Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); - } -} - -MUTATOR_HOOKFUNCTION(ca, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - ca_LastPlayerForTeam_Notify(frag_target); - if (!allowed_to_spawn) - { - 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)) - bot_clear(frag_target); - return true; -} - -MUTATOR_HOOKFUNCTION(ca, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - if (player.caplayer == 1) - ca_LastPlayerForTeam_Notify(player); - return true; -} - -MUTATOR_HOOKFUNCTION(ca, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - if (!IS_DEAD(player)) - ca_LastPlayerForTeam_Notify(player); - if (player.killindicator_teamchange == -2) // player wants to spectate - player.caplayer = 0; - if (player.caplayer) - player.frags = FRAGS_LMS_LOSER; - if (!warmup_stage) - eliminatedPlayers.SendFlags |= 1; - if (!player.caplayer) - return false; // allow team reset - return true; // prevent team reset -} - -MUTATOR_HOOKFUNCTION(ca, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(ca, GiveFragsForKill, CBC_ORDER_FIRST) -{ - M_ARGV(2, float) = 0; // score will be given to the winner team when the round ends - return true; -} - -MUTATOR_HOOKFUNCTION(ca, SetStartItems) -{ - start_items &= ~IT_UNLIMITED_AMMO; - start_health = warmup_start_health = cvar("g_lms_start_health"); - start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); - start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); - start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); - start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); - start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); - start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); - start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); -} - -MUTATOR_HOOKFUNCTION(ca, Damage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - float frag_damage = M_ARGV(4, float); - float frag_mirrordamage = M_ARGV(5, float); - - if (IS_PLAYER(frag_target)) - if (!IS_DEAD(frag_target)) - if (frag_target == frag_attacker || SAME_TEAM(frag_target, frag_attacker) || frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; - - frag_mirrordamage = 0; - - M_ARGV(4, float) = frag_damage; - M_ARGV(5, float) = frag_mirrordamage; -} - -MUTATOR_HOOKFUNCTION(ca, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if (autocvar_g_powerups <= 0) - if (item.flags & FL_POWERUP) - return true; - - if (autocvar_g_pickup_items <= 0) - return true; -} - -MUTATOR_HOOKFUNCTION(ca, PlayerDamage_SplitHealthArmor) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_damage = M_ARGV(7, float); - float damage_take = M_ARGV(4, float); - float damage_save = M_ARGV(5, float); - - float excess = max(0, frag_damage - damage_take - damage_save); - - if (frag_target != frag_attacker && IS_PLAYER(frag_attacker)) - GameRules_scoring_add_team(frag_attacker, SCORE, (frag_damage - excess) * autocvar_g_ca_damage2score_multiplier); -} - -MUTATOR_HOOKFUNCTION(ca, CalculateRespawnTime) -{ - // no respawn calculations needed, player is forced to spectate anyway - return true; -} - -MUTATOR_HOOKFUNCTION(ca, PlayerRegen) -{ - // no regeneration in CA - return true; -} - -MUTATOR_HOOKFUNCTION(ca, Scores_CountFragsRemaining) -{ - // announce remaining frags - return true; -} - -MUTATOR_HOOKFUNCTION(ca, SpectateSet) -{ - entity client = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); - - if (!autocvar_g_ca_spectate_enemies && client.caplayer) - if (DIFF_TEAM(targ, client)) - return true; -} - -MUTATOR_HOOKFUNCTION(ca, SpectateNext) -{ - entity client = M_ARGV(0, entity); - - if (!autocvar_g_ca_spectate_enemies && client.caplayer) - { - entity targ = M_ARGV(1, entity); - M_ARGV(1, entity) = CA_SpectateNext(client, targ); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ca, SpectatePrev) -{ - entity client = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); - entity first = M_ARGV(2, entity); - - if (!autocvar_g_ca_spectate_enemies && client.caplayer) - { - do { targ = targ.chain; } - while(targ && DIFF_TEAM(targ, client)); - - if (!targ) - { - for (targ = first; targ && DIFF_TEAM(targ, client); targ = targ.chain); - - if (targ == client.enemy) - return MUT_SPECPREV_RETURN; - } - } - - M_ARGV(1, entity) = targ; - - return MUT_SPECPREV_FOUND; -} - -MUTATOR_HOOKFUNCTION(ca, Bot_FixCount, CBC_ORDER_EXCLUSIVE) -{ - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - if (IS_PLAYER(it) || it.caplayer == 1) - ++M_ARGV(0, int); - ++M_ARGV(1, int); - }); - return true; -} - -MUTATOR_HOOKFUNCTION(ca, ClientCommand_Spectate) -{ - entity player = M_ARGV(0, entity); - - if (player.caplayer) - { - // they're going to spec, we can do other checks - if (autocvar_sv_spectate && (IS_SPEC(player) || IS_OBSERVER(player))) - Send_Notification(NOTIF_ONE_ONLY, player, MSG_INFO, INFO_CA_LEAVE); - return MUT_SPECCMD_FORCE; - } - - return MUT_SPECCMD_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(ca, WantWeapon) -{ - M_ARGV(2, bool) = true; // all weapons -} - -MUTATOR_HOOKFUNCTION(ca, HideTeamNagger) -{ - return true; // doesn't work well with the whole spectator as player thing -} - -MUTATOR_HOOKFUNCTION(ca, GetPlayerStatus) -{ - entity player = M_ARGV(0, entity); - - return player.caplayer == 1; -} - -MUTATOR_HOOKFUNCTION(ca, SetWeaponArena) -{ - // most weapons arena - if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most"; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qh b/qcsrc/server/mutators/mutator/gamemode_ca.qh deleted file mode 100644 index 0982fcca8f..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qh +++ /dev/null @@ -1,52 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -int autocvar_g_ca_point_limit; -int autocvar_g_ca_point_leadlimit; -float autocvar_g_ca_round_timelimit; -bool autocvar_g_ca_team_spawns; -//int autocvar_g_ca_teams; -int autocvar_g_ca_teams_override; -float autocvar_g_ca_warmup; - - -int ca_teams; -bool allowed_to_spawn; - -const int ST_CA_ROUNDS = 1; - -bool CA_CheckTeams(); -bool CA_CheckWinner(); -void CA_RoundStart(); -bool ca_isEliminated(entity e); - -REGISTER_MUTATOR(ca, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - GameRules_spawning_teams(autocvar_g_ca_team_spawns); - GameRules_limit_score(autocvar_g_ca_point_limit); - GameRules_limit_lead(autocvar_g_ca_point_leadlimit); - - ca_teams = autocvar_g_ca_teams_override; - if (ca_teams < 2) - ca_teams = cvar("g_ca_teams"); // read the cvar directly as it gets written earlier in the same frame - - ca_teams = BITS(bound(2, ca_teams, 4)); - GameRules_scoring(ca_teams, SFL_SORT_PRIO_PRIMARY, 0, { - field_team(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); - }); - - allowed_to_spawn = true; - round_handler_Spawn(CA_CheckTeams, CA_CheckWinner, CA_RoundStart); - round_handler_Init(5, autocvar_g_ca_warmup, autocvar_g_ca_round_timelimit); - EliminatedPlayers_Init(ca_isEliminated); - } - return 0; -} - -// should be removed in the future, as other code should not have to care -.float caplayer; // 0.5 if scheduled to join the next round diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qc b/qcsrc/server/mutators/mutator/gamemode_ctf.qc deleted file mode 100644 index b34e3f59f5..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc +++ /dev/null @@ -1,2775 +0,0 @@ -#include "gamemode_ctf.qh" - -#include <common/effects/all.qh> -#include <common/vehicles/all.qh> -#include <server/teamplay.qh> - -#include <lib/warpzone/common.qh> - -bool autocvar_g_ctf_allow_vehicle_carry; -bool autocvar_g_ctf_allow_vehicle_touch; -bool autocvar_g_ctf_allow_monster_touch; -bool autocvar_g_ctf_throw; -float autocvar_g_ctf_throw_angle_max; -float autocvar_g_ctf_throw_angle_min; -int autocvar_g_ctf_throw_punish_count; -float autocvar_g_ctf_throw_punish_delay; -float autocvar_g_ctf_throw_punish_time; -float autocvar_g_ctf_throw_strengthmultiplier; -float autocvar_g_ctf_throw_velocity_forward; -float autocvar_g_ctf_throw_velocity_up; -float autocvar_g_ctf_drop_velocity_up; -float autocvar_g_ctf_drop_velocity_side; -bool autocvar_g_ctf_oneflag_reverse; -bool autocvar_g_ctf_portalteleport; -bool autocvar_g_ctf_pass; -float autocvar_g_ctf_pass_arc; -float autocvar_g_ctf_pass_arc_max; -float autocvar_g_ctf_pass_directional_max; -float autocvar_g_ctf_pass_directional_min; -float autocvar_g_ctf_pass_radius; -float autocvar_g_ctf_pass_wait; -bool autocvar_g_ctf_pass_request; -float autocvar_g_ctf_pass_turnrate; -float autocvar_g_ctf_pass_timelimit; -float autocvar_g_ctf_pass_velocity; -bool autocvar_g_ctf_dynamiclights; -float autocvar_g_ctf_flag_collect_delay; -float autocvar_g_ctf_flag_damageforcescale; -bool autocvar_g_ctf_flag_dropped_waypoint; -bool autocvar_g_ctf_flag_dropped_floatinwater; -bool autocvar_g_ctf_flag_glowtrails; -int autocvar_g_ctf_flag_health; -bool autocvar_g_ctf_flag_return; -bool autocvar_g_ctf_flag_return_carrying; -float autocvar_g_ctf_flag_return_carried_radius; -float autocvar_g_ctf_flag_return_time; -bool autocvar_g_ctf_flag_return_when_unreachable; -float autocvar_g_ctf_flag_return_damage; -float autocvar_g_ctf_flag_return_damage_delay; -float autocvar_g_ctf_flag_return_dropped; -float autocvar_g_ctf_flagcarrier_auto_helpme_damage; -float autocvar_g_ctf_flagcarrier_auto_helpme_time; -float autocvar_g_ctf_flagcarrier_selfdamagefactor; -float autocvar_g_ctf_flagcarrier_selfforcefactor; -float autocvar_g_ctf_flagcarrier_damagefactor; -float autocvar_g_ctf_flagcarrier_forcefactor; -//float autocvar_g_ctf_flagcarrier_waypointforenemy_spotting; -bool autocvar_g_ctf_fullbrightflags; -bool autocvar_g_ctf_ignore_frags; -bool autocvar_g_ctf_score_ignore_fields; -int autocvar_g_ctf_score_capture; -int autocvar_g_ctf_score_capture_assist; -int autocvar_g_ctf_score_kill; -int autocvar_g_ctf_score_penalty_drop; -int autocvar_g_ctf_score_penalty_returned; -int autocvar_g_ctf_score_pickup_base; -int autocvar_g_ctf_score_pickup_dropped_early; -int autocvar_g_ctf_score_pickup_dropped_late; -int autocvar_g_ctf_score_return; -float autocvar_g_ctf_shield_force; -float autocvar_g_ctf_shield_max_ratio; -int autocvar_g_ctf_shield_min_negscore; -bool autocvar_g_ctf_stalemate; -int autocvar_g_ctf_stalemate_endcondition; -float autocvar_g_ctf_stalemate_time; -bool autocvar_g_ctf_reverse; -float autocvar_g_ctf_dropped_capture_delay; -float autocvar_g_ctf_dropped_capture_radius; - -void ctf_FakeTimeLimit(entity e, float t) -{ - msg_entity = e; - WriteByte(MSG_ONE, 3); // svc_updatestat - WriteByte(MSG_ONE, 236); // STAT_TIMELIMIT - if(t < 0) - WriteCoord(MSG_ONE, autocvar_timelimit); - else - WriteCoord(MSG_ONE, (t + 1) / 60); -} - -void ctf_EventLog(string mode, int flagteam, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(sprintf(":ctf:%s:%d:%d:%s", mode, flagteam, actor.team, ((actor != NULL) ? ftos(actor.playerid) : ""))); - //GameLogEcho(strcat(":ctf:", mode, ":", ftos(flagteam), ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); -} - -void ctf_CaptureRecord(entity flag, entity player) -{ - float cap_record = ctf_captimerecord; - float cap_time = (time - flag.ctf_pickuptime); - string refername = db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname")); - - // notify about shit - if(ctf_oneflag) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CTF_CAPTURE_NEUTRAL, player.netname); - else if(!ctf_captimerecord) - Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_TIME), player.netname, TIME_ENCODE(cap_time)); - else if(cap_time < cap_record) - Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, TIME_ENCODE(cap_time), TIME_ENCODE(cap_record)); - else - Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, TIME_ENCODE(cap_time), TIME_ENCODE(cap_record)); - - // write that shit in the database - if(!ctf_oneflag) // but not in 1-flag mode - if((!ctf_captimerecord) || (cap_time < cap_record)) - { - ctf_captimerecord = cap_time; - db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"), ftos(cap_time)); - db_put(ServerProgsDB, strcat(GetMapname(), "/captimerecord/netname"), player.netname); - write_recordmarker(player, flag.ctf_pickuptime, cap_time); - } - - if(autocvar_g_ctf_leaderboard && !ctf_oneflag) - race_setTime(GetMapname(), TIME_ENCODE(cap_time), player.crypto_idfp, player.netname, player, false); -} - -bool ctf_Immediate_Return_Allowed(entity flag, entity toucher) -{ - int num_perteam = 0; - FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), { ++num_perteam; }); - - // automatically return if there's only 1 player on the team - return ((autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried)) - && flag.team); -} - -bool ctf_Return_Customize(entity this, entity client) -{ - // only to the carrier - return boolean(client == this.owner); -} - -void ctf_FlagcarrierWaypoints(entity player) -{ - WaypointSprite_Spawn(WP_FlagCarrier, 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_flagcarrier, true, RADARICON_FLAG); - WaypointSprite_UpdateMaxHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(start_health, start_armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id) * 2); - WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); - WaypointSprite_UpdateTeamRadar(player.wps_flagcarrier, RADARICON_FLAGCARRIER, WPCOLOR_FLAGCARRIER(player.team)); - - if(player.flagcarried && CTF_SAMETEAM(player, player.flagcarried)) - { - if(!player.wps_enemyflagcarrier) - { - entity wp = WaypointSprite_Spawn(((ctf_oneflag) ? WP_FlagCarrier : WP_FlagCarrierEnemy), 0, 0, player, FLAG_WAYPOINT_OFFSET, NULL, 0, player, wps_enemyflagcarrier, true, RADARICON_FLAG); - wp.colormod = WPCOLOR_ENEMYFC(player.team); - setcefc(wp, ctf_Stalemate_Customize); - - if(IS_REAL_CLIENT(player) && !ctf_stalemate) - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_VISIBLE); - } - - if(!player.wps_flagreturn) - { - entity owp = WaypointSprite_SpawnFixed(WP_FlagReturn, player.flagcarried.ctf_spawnorigin + FLAG_WAYPOINT_OFFSET, player, wps_flagreturn, RADARICON_FLAG); - owp.colormod = '0 0.8 0.8'; - //WaypointSprite_UpdateTeamRadar(player.wps_flagreturn, RADARICON_FLAG, ((player.team) ? colormapPaletteColor(player.team - 1, false) : '1 1 1')); - setcefc(owp, ctf_Return_Customize); - } - } -} - -void ctf_CalculatePassVelocity(entity flag, vector to, vector from, float turnrate) -{ - float current_distance = vlen((('1 0 0' * to.x) + ('0 1 0' * to.y)) - (('1 0 0' * from.x) + ('0 1 0' * from.y))); // for the sake of this check, exclude Z axis - float initial_height = min(autocvar_g_ctf_pass_arc_max, (flag.pass_distance * tanh(autocvar_g_ctf_pass_arc))); - float current_height = (initial_height * min(1, (current_distance / flag.pass_distance))); - //print("current_height = ", ftos(current_height), ", initial_height = ", ftos(initial_height), ".\n"); - - vector targpos; - if(current_height) // make sure we can actually do this arcing path - { - targpos = (to + ('0 0 1' * current_height)); - WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); - if(trace_fraction < 1) - { - //print("normal arc line failed, trying to find new pos..."); - WarpZone_TraceLine(to, targpos, MOVE_NOMONSTERS, flag); - targpos = (trace_endpos + FLAG_PASS_ARC_OFFSET); - WarpZone_TraceLine(flag.origin, targpos, MOVE_NOMONSTERS, flag); - if(trace_fraction < 1) { targpos = to; /* print(" ^1FAILURE^7, reverting to original direction.\n"); */ } - /*else { print(" ^3SUCCESS^7, using new arc line.\n"); } */ - } - } - else { targpos = to; } - - //flag.angles = normalize(('0 1 0' * to_y) - ('0 1 0' * from_y)); - - vector desired_direction = normalize(targpos - from); - if(turnrate) { flag.velocity = (normalize(normalize(flag.velocity) + (desired_direction * autocvar_g_ctf_pass_turnrate)) * autocvar_g_ctf_pass_velocity); } - else { flag.velocity = (desired_direction * autocvar_g_ctf_pass_velocity); } -} - -bool ctf_CheckPassDirection(vector head_center, vector passer_center, vector passer_angle, vector nearest_to_passer) -{ - if(autocvar_g_ctf_pass_directional_max || autocvar_g_ctf_pass_directional_min) - { - // directional tracing only - float spreadlimit; - makevectors(passer_angle); - - // find the closest point on the enemy to the center of the attack - float h; // hypotenuse, which is the distance between attacker to head - float a; // adjacent side, which is the distance between attacker and the point on w_shotdir that is closest to head.origin - - h = vlen(head_center - passer_center); - a = h * (normalize(head_center - passer_center) * v_forward); - - vector nearest_on_line = (passer_center + a * v_forward); - float distance_from_line = vlen(nearest_to_passer - nearest_on_line); - - spreadlimit = (autocvar_g_ctf_pass_radius ? min(1, (vlen(passer_center - nearest_on_line) / autocvar_g_ctf_pass_radius)) : 1); - spreadlimit = (autocvar_g_ctf_pass_directional_min * (1 - spreadlimit) + autocvar_g_ctf_pass_directional_max * spreadlimit); - - if(spreadlimit && (distance_from_line <= spreadlimit) && ((vlen(normalize(head_center - passer_center) - v_forward) * RAD2DEG) <= 90)) - { return true; } - else - { return false; } - } - else { return true; } -} - - -// ======================= -// CaptureShield Functions -// ======================= - -bool ctf_CaptureShield_CheckStatus(entity p) -{ - int s, s2, s3, s4, se, se2, se3, se4, sr, ser; - int players_worseeq, players_total; - - if(ctf_captureshield_max_ratio <= 0) - return false; - - s = GameRules_scoring_add(p, CTF_CAPS, 0); - s2 = GameRules_scoring_add(p, CTF_PICKUPS, 0); - s3 = GameRules_scoring_add(p, CTF_RETURNS, 0); - s4 = GameRules_scoring_add(p, CTF_FCKILLS, 0); - - sr = ((s - s2) + (s3 + s4)); - - if(sr >= -ctf_captureshield_min_negscore) - return false; - - players_total = players_worseeq = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - if(DIFF_TEAM(it, p)) - continue; - se = GameRules_scoring_add(it, CTF_CAPS, 0); - se2 = GameRules_scoring_add(it, CTF_PICKUPS, 0); - se3 = GameRules_scoring_add(it, CTF_RETURNS, 0); - se4 = GameRules_scoring_add(it, CTF_FCKILLS, 0); - - ser = ((se - se2) + (se3 + se4)); - - if(ser <= sr) - ++players_worseeq; - ++players_total; - }); - - // player is in the worse half, if >= half the players are better than him, or consequently, if < half of the players are worse - // use this rule here - - if(players_worseeq >= players_total * ctf_captureshield_max_ratio) - return false; - - return true; -} - -void ctf_CaptureShield_Update(entity player, bool wanted_status) -{ - bool updated_status = ctf_CaptureShield_CheckStatus(player); - if((wanted_status == player.ctf_captureshielded) && (updated_status != wanted_status)) // 0: shield only, 1: unshield only - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((updated_status) ? CENTER_CTF_CAPTURESHIELD_SHIELDED : CENTER_CTF_CAPTURESHIELD_FREE)); - player.ctf_captureshielded = updated_status; - } -} - -bool ctf_CaptureShield_Customize(entity this, entity client) -{ - if(!client.ctf_captureshielded) { return false; } - if(CTF_SAMETEAM(this, client)) { return false; } - - return true; -} - -void ctf_CaptureShield_Touch(entity this, entity toucher) -{ - if(!toucher.ctf_captureshielded) { return; } - if(CTF_SAMETEAM(this, toucher)) { return; } - - vector mymid = (this.absmin + this.absmax) * 0.5; - vector theirmid = (toucher.absmin + toucher.absmax) * 0.5; - - Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, DMG_NOWEP, mymid, normalize(theirmid - mymid) * ctf_captureshield_force); - if(IS_REAL_CLIENT(toucher)) { Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); } -} - -void ctf_CaptureShield_Spawn(entity flag) -{ - entity shield = new(ctf_captureshield); - - shield.enemy = flag; - shield.team = flag.team; - settouch(shield, ctf_CaptureShield_Touch); - setcefc(shield, ctf_CaptureShield_Customize); - shield.effects = EF_ADDITIVE; - set_movetype(shield, MOVETYPE_NOCLIP); - shield.solid = SOLID_TRIGGER; - shield.avelocity = '7 0 11'; - shield.scale = 0.5; - - setorigin(shield, flag.origin); - setmodel(shield, MDL_CTF_SHIELD); - setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); -} - - -// ==================== -// Drop/Pass/Throw Code -// ==================== - -void ctf_Handle_Drop(entity flag, entity player, int droptype) -{ - // declarations - player = (player ? player : flag.pass_sender); - - // main - set_movetype(flag, MOVETYPE_TOSS); - flag.takedamage = DAMAGE_YES; - flag.angles = '0 0 0'; - flag.health = flag.max_flag_health; - flag.ctf_droptime = time; - flag.ctf_dropper = player; - flag.ctf_status = FLAG_DROPPED; - - // messages and sounds - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_LOST), player.netname); - _sound(flag, CH_TRIGGER, flag.snd_flag_dropped, VOL_BASE, ATTEN_NONE); - ctf_EventLog("dropped", player.team, player); - - // scoring - GameRules_scoring_add_team(player, SCORE, -((flag.score_drop) ? flag.score_drop : autocvar_g_ctf_score_penalty_drop)); - GameRules_scoring_add(player, CTF_DROPS, 1); - - // waypoints - if(autocvar_g_ctf_flag_dropped_waypoint) { - entity wp = WaypointSprite_Spawn(WP_FlagDropped, 0, 0, flag, FLAG_WAYPOINT_OFFSET, NULL, ((autocvar_g_ctf_flag_dropped_waypoint == 2) ? 0 : player.team), flag, wps_flagdropped, true, RADARICON_FLAG); - wp.colormod = WPCOLOR_DROPPEDFLAG(flag.team); - } - - if(autocvar_g_ctf_flag_return_time || (autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health)) - { - WaypointSprite_UpdateMaxHealth(flag.wps_flagdropped, flag.max_flag_health); - WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); - } - - player.throw_antispam = time + autocvar_g_ctf_pass_wait; - - if(droptype == DROP_PASS) - { - flag.pass_distance = 0; - flag.pass_sender = NULL; - flag.pass_target = NULL; - } -} - -void ctf_Handle_Retrieve(entity flag, entity player) -{ - entity sender = flag.pass_sender; - - // transfer flag to player - flag.owner = player; - flag.owner.flagcarried = flag; - GameRules_scoring_vip(player, true); - - // reset flag - if(player.vehicle) - { - setattachment(flag, player.vehicle, ""); - setorigin(flag, VEHICLE_FLAG_OFFSET); - flag.scale = VEHICLE_FLAG_SCALE; - } - else - { - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); - } - set_movetype(flag, MOVETYPE_NONE); - flag.takedamage = DAMAGE_NO; - flag.solid = SOLID_NOT; - flag.angles = '0 0 0'; - flag.ctf_status = FLAG_CARRY; - - // messages and sounds - _sound(player, CH_TRIGGER, flag.snd_flag_pass, VOL_BASE, ATTEN_NORM); - ctf_EventLog("receive", flag.team, player); - - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - if(it == sender) - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_SENT), player.netname); - else if(it == player) - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_RECEIVED), sender.netname); - else if(SAME_TEAM(it, sender)) - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_OTHER), sender.netname, player.netname); - }); - - // create new waypoint - ctf_FlagcarrierWaypoints(player); - - sender.throw_antispam = time + autocvar_g_ctf_pass_wait; - player.throw_antispam = sender.throw_antispam; - - flag.pass_distance = 0; - flag.pass_sender = NULL; - flag.pass_target = NULL; -} - -void ctf_Handle_Throw(entity player, entity receiver, int droptype) -{ - entity flag = player.flagcarried; - vector targ_origin, flag_velocity; - - if(!flag) { return; } - if((droptype == DROP_PASS) && !receiver) { return; } - - if(flag.speedrunning) { ctf_RespawnFlag(flag); return; } - - // reset the flag - setattachment(flag, NULL, ""); - setorigin(flag, player.origin + FLAG_DROP_OFFSET); - flag.owner.flagcarried = NULL; - GameRules_scoring_vip(flag.owner, false); - flag.owner = NULL; - flag.solid = SOLID_TRIGGER; - flag.ctf_dropper = player; - flag.ctf_droptime = time; - navigation_dynamicgoal_set(flag); - - flag.flags = FL_ITEM | FL_NOTARGET; // clear FL_ONGROUND for MOVETYPE_TOSS - - switch(droptype) - { - case DROP_PASS: - { - // warpzone support: - // for the examples, we assume player -> wz1 -> ... -> wzn -> receiver - // findradius has already put wzn ... wz1 into receiver's warpzone parameters! - WarpZone_RefSys_Copy(flag, receiver); - WarpZone_RefSys_AddInverse(flag, receiver); // wz1^-1 ... wzn^-1 receiver - targ_origin = WarpZone_RefSys_TransformOrigin(receiver, flag, (0.5 * (receiver.absmin + receiver.absmax))); // this is target origin as seen by the flag - - flag.pass_distance = vlen((('1 0 0' * targ_origin.x) + ('0 1 0' * targ_origin.y)) - (('1 0 0' * player.origin.x) + ('0 1 0' * player.origin.y))); // for the sake of this check, exclude Z axis - ctf_CalculatePassVelocity(flag, targ_origin, player.origin, false); - - // main - set_movetype(flag, MOVETYPE_FLY); - flag.takedamage = DAMAGE_NO; - flag.pass_sender = player; - flag.pass_target = receiver; - flag.ctf_status = FLAG_PASSING; - - // other - _sound(player, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); - WarpZone_TrailParticles(NULL, _particleeffectnum(flag.passeffect), player.origin, targ_origin); - ctf_EventLog("pass", flag.team, player); - break; - } - - case DROP_THROW: - { - makevectors((player.v_angle.y * '0 1 0') + (bound(autocvar_g_ctf_throw_angle_min, player.v_angle.x, autocvar_g_ctf_throw_angle_max) * '1 0 0')); - - flag_velocity = (('0 0 1' * autocvar_g_ctf_throw_velocity_up) + ((v_forward * autocvar_g_ctf_throw_velocity_forward) * ((player.items & ITEM_Strength.m_itemid) ? autocvar_g_ctf_throw_strengthmultiplier : 1))); - flag.velocity = W_CalculateProjectileVelocity(player, player.velocity, flag_velocity, false); - ctf_Handle_Drop(flag, player, droptype); - break; - } - - case DROP_RESET: - { - flag.velocity = '0 0 0'; // do nothing - break; - } - - default: - case DROP_NORMAL: - { - flag.velocity = W_CalculateProjectileVelocity(player, player.velocity, (('0 0 1' * autocvar_g_ctf_drop_velocity_up) + ((('0 1 0' * crandom()) + ('1 0 0' * crandom())) * autocvar_g_ctf_drop_velocity_side)), false); - ctf_Handle_Drop(flag, player, droptype); - break; - } - } - - // kill old waypointsprite - WaypointSprite_Ping(player.wps_flagcarrier); - WaypointSprite_Kill(player.wps_flagcarrier); - - if(player.wps_enemyflagcarrier) - WaypointSprite_Kill(player.wps_enemyflagcarrier); - - if(player.wps_flagreturn) - WaypointSprite_Kill(player.wps_flagreturn); - - // captureshield - ctf_CaptureShield_Update(player, 0); // shield player from picking up flag -} - -void shockwave_spawn(string m, vector org, float sz, float t1, float t2) -{ - return modeleffect_spawn(m, 0, 0, org, '0 0 0', '0 0 0', '0 0 0', 0, sz, 1, t1, t2); -} - -// ============== -// Event Handlers -// ============== - -void nades_GiveBonus(entity player, float score); - -void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) -{ - entity enemy_flag = ((capturetype == CAPTURE_NORMAL) ? toucher.flagcarried : toucher); - entity player = ((capturetype == CAPTURE_NORMAL) ? toucher : enemy_flag.ctf_dropper); - entity player_team_flag = NULL, tmp_entity; - float old_time, new_time; - - if(!player) { return; } // without someone to give the reward to, we can't possibly cap - if(CTF_DIFFTEAM(player, flag)) { return; } - if((flag.cnt || enemy_flag.cnt) && flag.cnt != enemy_flag.cnt) { return; } // this should catch some edge cases (capturing grouped flag at ungrouped flag disallowed etc) - - if (toucher.goalentity == flag.bot_basewaypoint) - toucher.goalentity_lock_timeout = 0; - - if(ctf_oneflag) - for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) - if(SAME_TEAM(tmp_entity, player)) - { - player_team_flag = tmp_entity; - break; - } - - nades_GiveBonus(player, autocvar_g_nades_bonus_score_high ); - - player.throw_prevtime = time; - player.throw_count = 0; - - // messages and sounds - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_NUM(enemy_flag.team, CENTER_CTF_CAPTURE)); - ctf_CaptureRecord(enemy_flag, player); - _sound(player, CH_TRIGGER, ((ctf_oneflag) ? player_team_flag.snd_flag_capture : ((DIFF_TEAM(player, flag)) ? enemy_flag.snd_flag_capture : flag.snd_flag_capture)), VOL_BASE, ATTEN_NONE); - - switch(capturetype) - { - case CAPTURE_NORMAL: ctf_EventLog("capture", enemy_flag.team, player); break; - case CAPTURE_DROPPED: ctf_EventLog("droppedcapture", enemy_flag.team, player); break; - default: break; - } - - // scoring - float pscore = 0; - if(enemy_flag.score_capture || flag.score_capture) - pscore = floor((max(1, enemy_flag.score_capture) + max(1, flag.score_capture)) * 0.5); - GameRules_scoring_add_team(player, SCORE, ((pscore) ? pscore : autocvar_g_ctf_score_capture)); - float capscore = 0; - if(enemy_flag.score_team_capture || flag.score_team_capture) - capscore = floor((max(1, enemy_flag.score_team_capture) + max(1, flag.score_team_capture)) * 0.5); - GameRules_scoring_add_team(player, CTF_CAPS, ((capscore) ? capscore : 1)); - - old_time = GameRules_scoring_add(player, CTF_CAPTIME, 0); - new_time = TIME_ENCODE(time - enemy_flag.ctf_pickuptime); - if(!old_time || new_time < old_time) - GameRules_scoring_add(player, CTF_CAPTIME, new_time - old_time); - - // effects - Send_Effect_(flag.capeffect, flag.origin, '0 0 0', 1); - //shockwave_spawn("models/ctf/shockwavetransring.md3", flag.origin - '0 0 15', -0.8, 0, 1); - - // other - if(capturetype == CAPTURE_NORMAL) - { - WaypointSprite_Kill(player.wps_flagcarrier); - if(flag.speedrunning) { ctf_FakeTimeLimit(player, -1); } - - if((enemy_flag.ctf_dropper) && (player != enemy_flag.ctf_dropper)) - { GameRules_scoring_add_team(enemy_flag.ctf_dropper, SCORE, ((enemy_flag.score_assist) ? enemy_flag.score_assist : autocvar_g_ctf_score_capture_assist)); } - } - - flag.enemy = toucher; - - // reset the flag - player.next_take_time = time + autocvar_g_ctf_flag_collect_delay; - ctf_RespawnFlag(enemy_flag); -} - -void ctf_Handle_Return(entity flag, entity player) -{ - // messages and sounds - if(IS_MONSTER(player)) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN_MONSTER), player.monster_name); - } - else if(flag.team) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_RETURN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(flag.team, INFO_CTF_RETURN), player.netname); - } - _sound(player, CH_TRIGGER, flag.snd_flag_returned, VOL_BASE, ATTEN_NONE); - ctf_EventLog("return", flag.team, player); - - // scoring - if(IS_PLAYER(player)) - { - GameRules_scoring_add_team(player, SCORE, ((flag.score_return) ? flag.score_return : autocvar_g_ctf_score_return)); // reward for return - GameRules_scoring_add(player, CTF_RETURNS, 1); // add to count of returns - - nades_GiveBonus(player,autocvar_g_nades_bonus_score_medium); - } - - TeamScore_AddToTeam(flag.team, ST_SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the team who was last carrying it - - if(flag.ctf_dropper) - { - GameRules_scoring_add(flag.ctf_dropper, SCORE, -autocvar_g_ctf_score_penalty_returned); // punish the player who dropped the flag - ctf_CaptureShield_Update(flag.ctf_dropper, 0); // shield player from picking up flag - flag.ctf_dropper.next_take_time = time + autocvar_g_ctf_flag_collect_delay; // set next take time - } - - // other - if(player.flagcarried == flag) - WaypointSprite_Kill(player.wps_flagcarrier); - - flag.enemy = player; - - // reset the flag - ctf_RespawnFlag(flag); -} - -void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) -{ - // declarations - float pickup_dropped_score; // used to calculate dropped pickup score - - // attach the flag to the player - flag.owner = player; - player.flagcarried = flag; - GameRules_scoring_vip(player, true); - if(player.vehicle) - { - setattachment(flag, player.vehicle, ""); - setorigin(flag, VEHICLE_FLAG_OFFSET); - flag.scale = VEHICLE_FLAG_SCALE; - } - else - { - setattachment(flag, player, ""); - setorigin(flag, FLAG_CARRY_OFFSET); - } - - // flag setup - set_movetype(flag, MOVETYPE_NONE); - flag.takedamage = DAMAGE_NO; - flag.solid = SOLID_NOT; - flag.angles = '0 0 0'; - flag.ctf_status = FLAG_CARRY; - - switch(pickuptype) - { - case PICKUP_BASE: flag.ctf_pickuptime = time; break; // used for timing runs - case PICKUP_DROPPED: flag.health = flag.max_flag_health; break; // reset health/return timelimit - default: break; - } - - // messages and sounds - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_PICKUP), player.netname); - if(ctf_stalemate) - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_STALEMATE_CARRIER); - if(!flag.team) - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PICKUP_NEUTRAL); - else if(CTF_DIFFTEAM(player, flag)) - Send_Notification(NOTIF_ONE, player, MSG_CENTER, APP_TEAM_NUM(flag.team, CENTER_CTF_PICKUP)); - else - Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_RETURN : CENTER_CTF_PICKUP_RETURN_ENEMY), Team_ColorCode(flag.team)); - - Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, APP_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); - - if(!flag.team) - FOREACH_CLIENT(IS_PLAYER(it) && it != player && DIFF_TEAM(it, player), { Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname); }); - - if(flag.team) - FOREACH_CLIENT(IS_PLAYER(it) && it != player, { - if(CTF_SAMETEAM(flag, it)) - if(SAME_TEAM(player, it)) - Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); - else - Send_Notification(NOTIF_ONE, it, MSG_CHOICE, ((SAME_TEAM(flag, player)) ? CHOICE_CTF_PICKUP_ENEMY_TEAM : CHOICE_CTF_PICKUP_ENEMY), Team_ColorCode(player.team), player.netname); - }); - - _sound(player, CH_TRIGGER, flag.snd_flag_taken, VOL_BASE, ATTEN_NONE); - - // scoring - GameRules_scoring_add(player, CTF_PICKUPS, 1); - nades_GiveBonus(player, autocvar_g_nades_bonus_score_minor); - switch(pickuptype) - { - case PICKUP_BASE: - { - GameRules_scoring_add_team(player, SCORE, ((flag.score_pickup) ? flag.score_pickup : autocvar_g_ctf_score_pickup_base)); - ctf_EventLog("steal", flag.team, player); - break; - } - - case PICKUP_DROPPED: - { - pickup_dropped_score = (autocvar_g_ctf_flag_return_time ? bound(0, ((flag.ctf_droptime + autocvar_g_ctf_flag_return_time) - time) / autocvar_g_ctf_flag_return_time, 1) : 1); - pickup_dropped_score = floor((autocvar_g_ctf_score_pickup_dropped_late * (1 - pickup_dropped_score) + autocvar_g_ctf_score_pickup_dropped_early * pickup_dropped_score) + 0.5); - LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score)); - GameRules_scoring_add_team(player, SCORE, pickup_dropped_score); - ctf_EventLog("pickup", flag.team, player); - break; - } - - default: break; - } - - // speedrunning - if(pickuptype == PICKUP_BASE) - { - flag.speedrunning = player.speedrunning; // if speedrunning, flag will flag-return and teleport the owner back after the record - if((player.speedrunning) && (ctf_captimerecord)) - ctf_FakeTimeLimit(player, time + ctf_captimerecord); - } - - // effects - Send_Effect_(flag.toucheffect, player.origin, '0 0 0', 1); - - // waypoints - if(pickuptype == PICKUP_DROPPED) { WaypointSprite_Kill(flag.wps_flagdropped); } - ctf_FlagcarrierWaypoints(player); - WaypointSprite_Ping(player.wps_flagcarrier); -} - - -// =================== -// Main Flag Functions -// =================== - -void ctf_CheckFlagReturn(entity flag, int returntype) -{ - if((flag.ctf_status == FLAG_DROPPED) || (flag.ctf_status == FLAG_PASSING)) - { - if(flag.wps_flagdropped) { WaypointSprite_UpdateHealth(flag.wps_flagdropped, flag.health); } - - if((flag.health <= 0) || (time >= flag.ctf_droptime + autocvar_g_ctf_flag_return_time)) - { - switch(returntype) - { - case RETURN_DROPPED: - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DROPPED)); break; - case RETURN_DAMAGE: - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_DAMAGED)); break; - case RETURN_SPEEDRUN: - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_SPEEDRUN), TIME_ENCODE(ctf_captimerecord)); break; - case RETURN_NEEDKILL: - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_NEEDKILL)); break; - default: - case RETURN_TIMEOUT: - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(flag.team, INFO_CTF_FLAGRETURN_TIMEOUT)); break; - } - _sound(flag, CH_TRIGGER, flag.snd_flag_respawn, VOL_BASE, ATTEN_NONE); - ctf_EventLog("returned", flag.team, NULL); - flag.enemy = NULL; - ctf_RespawnFlag(flag); - } - } -} - -bool ctf_Stalemate_Customize(entity this, entity client) -{ - // make spectators see what the player would see - entity e = WaypointSprite_getviewentity(client); - entity wp_owner = this.owner; - - // team waypoints - //if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; } - if(SAME_TEAM(wp_owner, e)) { return false; } - if(!IS_PLAYER(e)) { return false; } - - return true; -} - -void ctf_CheckStalemate() -{ - // declarations - int stale_flags = 0, stale_red_flags = 0, stale_blue_flags = 0, stale_yellow_flags = 0, stale_pink_flags = 0, stale_neutral_flags = 0; - entity tmp_entity; - - entity ctf_staleflaglist = NULL; // reset the list, we need to build the list each time this function runs - - // build list of stale flags - for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) - { - if(autocvar_g_ctf_stalemate) - if(tmp_entity.ctf_status != FLAG_BASE) - if(time >= tmp_entity.ctf_pickuptime + autocvar_g_ctf_stalemate_time || !tmp_entity.team) // instant stalemate in oneflag - { - tmp_entity.ctf_staleflagnext = ctf_staleflaglist; // link flag into staleflaglist - ctf_staleflaglist = tmp_entity; - - switch(tmp_entity.team) - { - case NUM_TEAM_1: ++stale_red_flags; break; - case NUM_TEAM_2: ++stale_blue_flags; break; - case NUM_TEAM_3: ++stale_yellow_flags; break; - case NUM_TEAM_4: ++stale_pink_flags; break; - default: ++stale_neutral_flags; break; - } - } - } - - if(ctf_oneflag) - stale_flags = (stale_neutral_flags >= 1); - else - stale_flags = (stale_red_flags >= 1) + (stale_blue_flags >= 1) + (stale_yellow_flags >= 1) + (stale_pink_flags >= 1); - - if(ctf_oneflag && stale_flags == 1) - ctf_stalemate = true; - else if(stale_flags >= 2) - ctf_stalemate = true; - else if(stale_flags == 0 && autocvar_g_ctf_stalemate_endcondition == 2) - { ctf_stalemate = false; wpforenemy_announced = false; } - else if(stale_flags < 2 && autocvar_g_ctf_stalemate_endcondition == 1) - { ctf_stalemate = false; wpforenemy_announced = false; } - - // if sufficient stalemate, then set up the waypointsprite and announce the stalemate if necessary - if(ctf_stalemate) - { - for(tmp_entity = ctf_staleflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_staleflagnext) - { - if((tmp_entity.owner) && (!tmp_entity.owner.wps_enemyflagcarrier)) - { - entity wp = WaypointSprite_Spawn(((ctf_oneflag) ? WP_FlagCarrier : WP_FlagCarrierEnemy), 0, 0, tmp_entity.owner, FLAG_WAYPOINT_OFFSET, NULL, 0, tmp_entity.owner, wps_enemyflagcarrier, true, RADARICON_FLAG); - wp.colormod = WPCOLOR_ENEMYFC(tmp_entity.owner.team); - setcefc(tmp_entity.owner.wps_enemyflagcarrier, ctf_Stalemate_Customize); - } - } - - if (!wpforenemy_announced) - { - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((it.flagcarried) ? CENTER_CTF_STALEMATE_CARRIER : CENTER_CTF_STALEMATE_OTHER)); }); - - wpforenemy_announced = true; - } - } -} - -void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - if(autocvar_g_ctf_flag_return_damage_delay) - this.ctf_flagdamaged_byworld = true; - else - { - this.health = 0; - ctf_CheckFlagReturn(this, RETURN_NEEDKILL); - } - return; - } - if(autocvar_g_ctf_flag_return_damage) - { - // reduce health and check if it should be returned - this.health = this.health - damage; - ctf_CheckFlagReturn(this, RETURN_DAMAGE); - return; - } -} - -void ctf_FlagThink(entity this) -{ - // declarations - entity tmp_entity; - - this.nextthink = time + FLAG_THINKRATE; // only 5 fps, more is unnecessary. - - // captureshield - if(this == ctf_worldflaglist) // only for the first flag - FOREACH_CLIENT(true, { ctf_CaptureShield_Update(it, 1); }); // release shield only - - // sanity checks - if(this.mins != this.m_mins || this.maxs != this.m_maxs) { // reset the flag boundaries in case it got squished - LOG_TRACE("wtf the flag got squashed?"); - tracebox(this.origin, this.m_mins, this.m_maxs, this.origin, MOVE_NOMONSTERS, this); - if(!trace_startsolid || this.noalign) // can we resize it without getting stuck? - setsize(this, this.m_mins, this.m_maxs); - } - - // main think method - switch(this.ctf_status) - { - case FLAG_BASE: - { - if(autocvar_g_ctf_dropped_capture_radius) - { - for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) - if(tmp_entity.ctf_status == FLAG_DROPPED) - if(vdist(this.origin - tmp_entity.origin, <, autocvar_g_ctf_dropped_capture_radius)) - if(time > tmp_entity.ctf_droptime + autocvar_g_ctf_dropped_capture_delay) - ctf_Handle_Capture(this, tmp_entity, CAPTURE_DROPPED); - } - return; - } - - case FLAG_DROPPED: - { - this.angles = '0 0 0'; // reset flag angles in case warpzones adjust it - - if(autocvar_g_ctf_flag_dropped_floatinwater) - { - vector midpoint = ((this.absmin + this.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - this.velocity = this.velocity * 0.5; - - if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER) - { this.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; } - else - { set_movetype(this, MOVETYPE_FLY); } - } - else if(this.move_movetype == MOVETYPE_FLY) { set_movetype(this, MOVETYPE_TOSS); } - } - if(autocvar_g_ctf_flag_return_dropped) - { - if((vdist(this.origin - this.ctf_spawnorigin, <=, autocvar_g_ctf_flag_return_dropped)) || (autocvar_g_ctf_flag_return_dropped == -1)) - { - this.health = 0; - ctf_CheckFlagReturn(this, RETURN_DROPPED); - return; - } - } - if(this.ctf_flagdamaged_byworld) - { - this.health -= ((this.max_flag_health / autocvar_g_ctf_flag_return_damage_delay) * FLAG_THINKRATE); - ctf_CheckFlagReturn(this, RETURN_NEEDKILL); - return; - } - else if(autocvar_g_ctf_flag_return_time) - { - this.health -= ((this.max_flag_health / autocvar_g_ctf_flag_return_time) * FLAG_THINKRATE); - ctf_CheckFlagReturn(this, RETURN_TIMEOUT); - return; - } - return; - } - - case FLAG_CARRY: - { - if(this.speedrunning && ctf_captimerecord && (time >= this.ctf_pickuptime + ctf_captimerecord)) - { - this.health = 0; - ctf_CheckFlagReturn(this, RETURN_SPEEDRUN); - - CS(this.owner).impulse = CHIMPULSE_SPEEDRUN.impulse; // move the player back to the waypoint they set - ImpulseCommands(this.owner); - } - if(autocvar_g_ctf_stalemate) - { - if(time >= wpforenemy_nextthink) - { - ctf_CheckStalemate(); - wpforenemy_nextthink = time + WPFE_THINKRATE; // waypoint for enemy think rate (to reduce unnecessary spam of this check) - } - } - if(CTF_SAMETEAM(this, this.owner) && this.team) - { - if(autocvar_g_ctf_flag_return) // drop the flag if reverse status has changed - ctf_Handle_Throw(this.owner, NULL, DROP_THROW); - else if(vdist(this.owner.origin - this.ctf_spawnorigin, <=, autocvar_g_ctf_flag_return_carried_radius)) - ctf_Handle_Return(this, this.owner); - } - return; - } - - case FLAG_PASSING: - { - vector targ_origin = ((this.pass_target.absmin + this.pass_target.absmax) * 0.5); - targ_origin = WarpZone_RefSys_TransformOrigin(this.pass_target, this, targ_origin); // origin of target as seen by the flag (us) - WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this); - - if((this.pass_target == NULL) - || (IS_DEAD(this.pass_target)) - || (this.pass_target.flagcarried) - || (vdist(this.origin - targ_origin, >, autocvar_g_ctf_pass_radius)) - || ((trace_fraction < 1) && (trace_ent != this.pass_target)) - || (time > this.ctf_droptime + autocvar_g_ctf_pass_timelimit)) - { - // give up, pass failed - ctf_Handle_Drop(this, NULL, DROP_PASS); - } - else - { - // still a viable target, go for it - ctf_CalculatePassVelocity(this, targ_origin, this.origin, true); - } - return; - } - - default: // this should never happen - { - LOG_TRACE("ctf_FlagThink(): Flag exists with no status?"); - return; - } - } -} - -METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) -{ - return = false; - if(game_stopped) return; - if(trace_dphitcontents & (DPCONTENTS_PLAYERCLIP | DPCONTENTS_MONSTERCLIP)) { return; } - - bool is_not_monster = (!IS_MONSTER(toucher)); - - // automatically kill the flag and return it if it touched lava/slime/nodrop surfaces - if(ITEM_TOUCH_NEEDKILL()) - { - if(!autocvar_g_ctf_flag_return_damage_delay) - { - flag.health = 0; - ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); - } - if(!flag.ctf_flagdamaged_byworld) { return; } - } - - // special touch behaviors - if(STAT(FROZEN, toucher)) { return; } - else if(IS_VEHICLE(toucher)) - { - if(autocvar_g_ctf_allow_vehicle_touch && toucher.owner) - toucher = toucher.owner; // the player is actually the vehicle owner, not other - else - return; // do nothing - } - else if(IS_MONSTER(toucher)) - { - if(!autocvar_g_ctf_allow_monster_touch) - return; // do nothing - } - else if (!IS_PLAYER(toucher)) // The flag just touched an object, most likely the world - { - if(time > flag.wait) // if we haven't in a while, play a sound/effect - { - Send_Effect_(flag.toucheffect, flag.origin, '0 0 0', 1); - _sound(flag, CH_TRIGGER, flag.snd_flag_touch, VOL_BASE, ATTEN_NORM); - flag.wait = time + FLAG_TOUCHRATE; - } - return; - } - else if(IS_DEAD(toucher)) { return; } - - switch(flag.ctf_status) - { - case FLAG_BASE: - { - if(ctf_oneflag) - { - if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && !toucher.flagcarried.team && is_not_monster) - ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the neutral flag to enemy base - else if(!flag.team && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the neutral flag - } - else if(CTF_SAMETEAM(toucher, flag) && (toucher.flagcarried) && DIFF_TEAM(toucher.flagcarried, flag) && is_not_monster) - ctf_Handle_Capture(flag, toucher, CAPTURE_NORMAL); // toucher just captured the enemies flag to his base - else if(CTF_DIFFTEAM(toucher, flag) && (toucher.flagcarried) && CTF_SAMETEAM(toucher.flagcarried, toucher) && (!toucher.ctf_captureshielded) && autocvar_g_ctf_flag_return_carrying && (time > toucher.next_take_time) && is_not_monster) - { - ctf_Handle_Return(toucher.flagcarried, toucher); // return their current flag - ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // now pickup the flag - } - else if(CTF_DIFFTEAM(toucher, flag) && (!toucher.flagcarried) && (!toucher.ctf_captureshielded) && (time > toucher.next_take_time) && is_not_monster) - ctf_Handle_Pickup(flag, toucher, PICKUP_BASE); // toucher just stole the enemies flag - break; - } - - case FLAG_DROPPED: - { - if(CTF_SAMETEAM(toucher, flag) && ctf_Immediate_Return_Allowed(flag, toucher)) - ctf_Handle_Return(flag, toucher); // toucher just returned his own flag - else if(is_not_monster && (!toucher.flagcarried) && ((toucher != flag.ctf_dropper) || (time > flag.ctf_droptime + autocvar_g_ctf_flag_collect_delay))) - ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); // toucher just picked up a dropped enemy flag - break; - } - - case FLAG_CARRY: - { - LOG_TRACE("Someone touched a flag even though it was being carried?"); - break; - } - - case FLAG_PASSING: - { - if((IS_PLAYER(toucher)) && !IS_DEAD(toucher) && (toucher != flag.pass_sender)) - { - if(DIFF_TEAM(toucher, flag.pass_sender)) - { - if(ctf_Immediate_Return_Allowed(flag, toucher)) - ctf_Handle_Return(flag, toucher); - else if(is_not_monster && (!toucher.flagcarried)) - ctf_Handle_Pickup(flag, toucher, PICKUP_DROPPED); - } - else if(!toucher.flagcarried) - ctf_Handle_Retrieve(flag, toucher); - } - break; - } - } -} - -.float last_respawn; -void ctf_RespawnFlag(entity flag) -{ - // check for flag respawn being called twice in a row - if(flag.last_respawn > time - 0.5) - { backtrace("flag respawn called twice quickly! please notify Samual about this..."); } - - flag.last_respawn = time; - - // reset the player (if there is one) - if((flag.owner) && (flag.owner.flagcarried == flag)) - { - WaypointSprite_Kill(flag.owner.wps_enemyflagcarrier); - WaypointSprite_Kill(flag.owner.wps_flagreturn); - WaypointSprite_Kill(flag.wps_flagcarrier); - - flag.owner.flagcarried = NULL; - GameRules_scoring_vip(flag.owner, false); - - if(flag.speedrunning) - ctf_FakeTimeLimit(flag.owner, -1); - } - - if((flag.owner) && (flag.owner.vehicle)) - flag.scale = FLAG_SCALE; - - if(flag.ctf_status == FLAG_DROPPED) - { WaypointSprite_Kill(flag.wps_flagdropped); } - - // reset the flag - setattachment(flag, NULL, ""); - setorigin(flag, flag.ctf_spawnorigin); - - set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS)); - flag.takedamage = DAMAGE_NO; - flag.health = flag.max_flag_health; - flag.solid = SOLID_TRIGGER; - flag.velocity = '0 0 0'; - flag.angles = flag.mangle; - flag.flags = FL_ITEM | FL_NOTARGET; - - flag.ctf_status = FLAG_BASE; - flag.owner = NULL; - flag.pass_distance = 0; - flag.pass_sender = NULL; - flag.pass_target = NULL; - flag.ctf_dropper = NULL; - flag.ctf_pickuptime = 0; - flag.ctf_droptime = 0; - flag.ctf_flagdamaged_byworld = false; - navigation_dynamicgoal_unset(flag); - - ctf_CheckStalemate(); -} - -void ctf_Reset(entity this) -{ - if(this.owner && IS_PLAYER(this.owner)) - ctf_Handle_Throw(this.owner, NULL, DROP_RESET); - - this.enemy = NULL; - ctf_RespawnFlag(this); -} - -bool ctf_FlagBase_Customize(entity this, entity client) -{ - entity e = WaypointSprite_getviewentity(client); - entity wp_owner = this.owner; - entity flag = e.flagcarried; - if(flag && CTF_SAMETEAM(e, flag)) - return false; - if(flag && (flag.cnt || wp_owner.cnt) && wp_owner.cnt != flag.cnt) - return false; - return true; -} - -void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map by ctf_FlagSetup() -{ - // bot waypoints - waypoint_spawnforitem_force(this, this.origin); - navigation_dynamicgoal_init(this, true); - - // waypointsprites - entity basename; - switch (this.team) - { - case NUM_TEAM_1: basename = WP_FlagBaseRed; break; - case NUM_TEAM_2: basename = WP_FlagBaseBlue; break; - case NUM_TEAM_3: basename = WP_FlagBaseYellow; break; - case NUM_TEAM_4: basename = WP_FlagBasePink; break; - default: basename = WP_FlagBaseNeutral; break; - } - - entity wp = WaypointSprite_SpawnFixed(basename, this.origin + FLAG_WAYPOINT_OFFSET, this, wps_flagbase, RADARICON_FLAG); - wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 1 1'); - WaypointSprite_UpdateTeamRadar(this.wps_flagbase, RADARICON_FLAG, ((this.team) ? colormapPaletteColor(this.team - 1, false) : '1 1 1')); - setcefc(wp, ctf_FlagBase_Customize); - - // captureshield setup - ctf_CaptureShield_Spawn(this); -} - -.bool pushable; - -void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag entity on the map as a spawnfunc -{ - // main setup - flag.ctf_worldflagnext = ctf_worldflaglist; // link flag into ctf_worldflaglist - ctf_worldflaglist = flag; - - setattachment(flag, NULL, ""); - - flag.netname = strzone(sprintf("%s%s^7 flag", Team_ColorCode(teamnumber), Team_ColorName_Upper(teamnumber))); - flag.team = teamnumber; - flag.classname = "item_flag_team"; - flag.target = "###item###"; // wut? - flag.flags = FL_ITEM | FL_NOTARGET; - IL_PUSH(g_items, flag); - flag.solid = SOLID_TRIGGER; - flag.takedamage = DAMAGE_NO; - flag.damageforcescale = autocvar_g_ctf_flag_damageforcescale; - flag.max_flag_health = ((autocvar_g_ctf_flag_return_damage && autocvar_g_ctf_flag_health) ? autocvar_g_ctf_flag_health : 100); - flag.health = flag.max_flag_health; - flag.event_damage = ctf_FlagDamage; - flag.pushable = true; - flag.teleportable = TELEPORT_NORMAL; - flag.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP; - flag.damagedbytriggers = autocvar_g_ctf_flag_return_when_unreachable; - flag.damagedbycontents = autocvar_g_ctf_flag_return_when_unreachable; - if(flag.damagedbycontents) - IL_PUSH(g_damagedbycontents, flag); - flag.velocity = '0 0 0'; - flag.mangle = flag.angles; - flag.reset = ctf_Reset; - settouch(flag, ctf_FlagTouch); - setthink(flag, ctf_FlagThink); - flag.nextthink = time + FLAG_THINKRATE; - flag.ctf_status = FLAG_BASE; - - // crudely force them all to 0 - if(autocvar_g_ctf_score_ignore_fields) - flag.cnt = flag.score_assist = flag.score_team_capture = flag.score_capture = flag.score_drop = flag.score_pickup = flag.score_return = 0; - - string teamname = Static_Team_ColorName_Lower(teamnumber); - // appearence - if(!flag.scale) { flag.scale = FLAG_SCALE; } - if(flag.skin == 0) { flag.skin = cvar(sprintf("g_ctf_flag_%s_skin", teamname)); } - if(flag.model == "") { flag.model = cvar_string(sprintf("g_ctf_flag_%s_model", teamname)); } - if (flag.toucheffect == "") { flag.toucheffect = EFFECT_FLAG_TOUCH(teamnumber).eent_eff_name; } - if (flag.passeffect == "") { flag.passeffect = EFFECT_PASS(teamnumber).eent_eff_name; } - if (flag.capeffect == "") { flag.capeffect = EFFECT_CAP(teamnumber).eent_eff_name; } - - // sounds -#define X(s,b) \ - if(flag.s == "") flag.s = b; \ - precache_sound(flag.s); - - X(snd_flag_taken, strzone(SND(CTF_TAKEN(teamnumber)))) - X(snd_flag_returned, strzone(SND(CTF_RETURNED(teamnumber)))) - X(snd_flag_capture, strzone(SND(CTF_CAPTURE(teamnumber)))) - X(snd_flag_dropped, strzone(SND(CTF_DROPPED(teamnumber)))) - X(snd_flag_respawn, strzone(SND(CTF_RESPAWN))) - X(snd_flag_touch, strzone(SND(CTF_TOUCH))) - X(snd_flag_pass, strzone(SND(CTF_PASS))) -#undef X - - // precache - precache_model(flag.model); - - // appearence - _setmodel(flag, flag.model); // precision set below - setsize(flag, CTF_FLAG.m_mins * flag.scale, CTF_FLAG.m_maxs * flag.scale); - flag.m_mins = flag.mins; // store these for squash checks - flag.m_maxs = flag.maxs; - setorigin(flag, (flag.origin + FLAG_SPAWN_OFFSET)); - - if(autocvar_g_ctf_flag_glowtrails) - { - switch(teamnumber) - { - case NUM_TEAM_1: flag.glow_color = 251; break; - case NUM_TEAM_2: flag.glow_color = 210; break; - case NUM_TEAM_3: flag.glow_color = 110; break; - case NUM_TEAM_4: flag.glow_color = 145; break; - default: flag.glow_color = 254; break; - } - flag.glow_size = 25; - flag.glow_trail = 1; - } - - flag.effects |= EF_LOWPRECISION; - if(autocvar_g_ctf_fullbrightflags) { flag.effects |= EF_FULLBRIGHT; } - if(autocvar_g_ctf_dynamiclights) - { - switch(teamnumber) - { - case NUM_TEAM_1: flag.effects |= EF_RED; break; - case NUM_TEAM_2: flag.effects |= EF_BLUE; break; - case NUM_TEAM_3: flag.effects |= EF_DIMLIGHT; break; - case NUM_TEAM_4: flag.effects |= EF_RED; break; - default: flag.effects |= EF_DIMLIGHT; break; - } - } - - // flag placement - if((flag.spawnflags & 1) || flag.noalign) // don't drop to floor, just stay at fixed location - { - flag.dropped_origin = flag.origin; - flag.noalign = true; - set_movetype(flag, MOVETYPE_NONE); - } - else // drop to floor, automatically find a platform and set that as spawn origin - { - flag.noalign = false; - droptofloor(flag); - set_movetype(flag, MOVETYPE_NONE); - } - - InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION); -} - - -// ================ -// Bot player logic -// ================ - -// NOTE: LEGACY CODE, needs to be re-written! - -void havocbot_ctf_calculate_middlepoint() -{ - entity f; - vector s = '0 0 0'; - vector fo = '0 0 0'; - int n = 0; - - f = ctf_worldflaglist; - while (f) - { - fo = f.origin; - s = s + fo; - f = f.ctf_worldflagnext; - n++; - } - if(!n) - return; - - havocbot_middlepoint = s / n; - havocbot_middlepoint_radius = vlen(fo - havocbot_middlepoint); - - havocbot_symmetryaxis_equation = '0 0 0'; - if(n == 2) - { - // for symmetrical editing of waypoints - entity f1 = ctf_worldflaglist; - entity f2 = f1.ctf_worldflagnext; - float m = -(f1.origin.y - f2.origin.y) / (f1.origin.x - f2.origin.x); - float q = havocbot_middlepoint.y - m * havocbot_middlepoint.x; - havocbot_symmetryaxis_equation.x = m; - havocbot_symmetryaxis_equation.y = q; - } - // store number of flags in this otherwise unused vector component - havocbot_symmetryaxis_equation.z = n; -} - - -entity havocbot_ctf_find_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if (CTF_SAMETEAM(bot, f)) - return f; - f = f.ctf_worldflagnext; - } - return NULL; -} - -entity havocbot_ctf_find_enemy_flag(entity bot) -{ - entity f; - f = ctf_worldflaglist; - while (f) - { - if(ctf_oneflag) - { - if(CTF_DIFFTEAM(bot, f)) - { - if(f.team) - { - if(bot.flagcarried) - return f; - } - else if(!bot.flagcarried) - return f; - } - } - else if (CTF_DIFFTEAM(bot, f)) - return f; - f = f.ctf_worldflagnext; - } - return NULL; -} - -int havocbot_ctf_teamcount(entity bot, vector org, float tc_radius) -{ - if (!teamplay) - return 0; - - int c = 0; - - FOREACH_CLIENT(IS_PLAYER(it), { - if(DIFF_TEAM(it, bot) || IS_DEAD(it) || it == bot) - continue; - - if(vdist(it.origin - org, <, tc_radius)) - ++c; - }); - - return c; -} - -// unused -#if 0 -void havocbot_goalrating_ctf_ourflag(entity this, float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if (CTF_SAMETEAM(this, head)) - break; - head = head.ctf_worldflagnext; - } - if (head) - navigation_routerating(this, head, ratingscale, 10000); -} -#endif - -void havocbot_goalrating_ctf_ourbase(entity this, float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if (CTF_SAMETEAM(this, head)) - { - if (this.flagcarried) - if ((this.flagcarried.cnt || head.cnt) && this.flagcarried.cnt != head.cnt) - { - head = head.ctf_worldflagnext; // skip base if it has a different group - continue; - } - break; - } - head = head.ctf_worldflagnext; - } - if (!head) - return; - - navigation_routerating(this, head.bot_basewaypoint, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_enemyflag(entity this, float ratingscale) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - if(ctf_oneflag) - { - if(CTF_DIFFTEAM(this, head)) - { - if(head.team) - { - if(this.flagcarried) - break; - } - else if(!this.flagcarried) - break; - } - } - else if(CTF_DIFFTEAM(this, head)) - break; - head = head.ctf_worldflagnext; - } - if (head) - navigation_routerating(this, head, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_enemybase(entity this, float ratingscale) -{ - if (!bot_waypoints_for_items) - { - havocbot_goalrating_ctf_enemyflag(this, ratingscale); - return; - } - - entity head; - - head = havocbot_ctf_find_enemy_flag(this); - - if (!head) - return; - - navigation_routerating(this, head.bot_basewaypoint, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_ourstolenflag(entity this, float ratingscale) -{ - entity mf; - - mf = havocbot_ctf_find_flag(this); - - if(mf.ctf_status == FLAG_BASE) - return; - - if(mf.tag_entity) - navigation_routerating(this, mf.tag_entity, ratingscale, 10000); -} - -void havocbot_goalrating_ctf_droppedflags(entity this, float ratingscale, vector org, float df_radius) -{ - entity head; - head = ctf_worldflaglist; - while (head) - { - // flag is out in the field - if(head.ctf_status != FLAG_BASE) - if(head.tag_entity==NULL) // dropped - { - if(df_radius) - { - if(vdist(org - head.origin, <, df_radius)) - navigation_routerating(this, head, ratingscale, 10000); - } - else - navigation_routerating(this, head, ratingscale, 10000); - } - - head = head.ctf_worldflagnext; - } -} - -void havocbot_goalrating_ctf_carrieritems(entity this, float ratingscale, vector org, float sradius) -{ - IL_EACH(g_items, it.bot_pickup, - { - // gather health and armor only - if (it.solid) - if (it.health || it.armorvalue) - if (vdist(it.origin - org, <, sradius)) - { - // get the value of the item - float t = it.bot_pickupevalfunc(this, it) * 0.0001; - if (t > 0) - navigation_routerating(this, it, t * ratingscale, 500); - } - }); -} - -void havocbot_ctf_reset_role(entity this) -{ - float cdefense, cmiddle, coffense; - entity mf, ef; - float c; - - if(IS_DEAD(this)) - return; - - // Check ctf flags - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - mf = havocbot_ctf_find_flag(this); - ef = havocbot_ctf_find_enemy_flag(this); - - // Retrieve stolen flag - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - // If enemy flag is taken go to the middle to intercept pursuers - if(ef.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); - return; - } - - // if there is only me on the team switch to offense - c = 0; - FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, this), { ++c; }); - - if(c==1) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_OFFENSE); - return; - } - - // Evaluate best position to take - // Count mates on middle position - cmiddle = havocbot_ctf_teamcount(this, havocbot_middlepoint, havocbot_middlepoint_radius * 0.5); - - // Count mates on defense position - cdefense = havocbot_ctf_teamcount(this, mf.dropped_origin, havocbot_middlepoint_radius * 0.5); - - // Count mates on offense position - coffense = havocbot_ctf_teamcount(this, ef.dropped_origin, havocbot_middlepoint_radius); - - if(cdefense<=coffense) - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_DEFENSE); - else if(coffense<=cmiddle) - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_OFFENSE); - else - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); -} - -void havocbot_role_ctf_carrier(entity this) -{ - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried == NULL) - { - havocbot_ctf_reset_role(this); - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - if(ctf_oneflag) - havocbot_goalrating_ctf_enemybase(this, 50000); - else - havocbot_goalrating_ctf_ourbase(this, 50000); - - if(this.health<100) - havocbot_goalrating_ctf_carrieritems(this, 1000, this.origin, 1000); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - - entity head = ctf_worldflaglist; - while (head) - { - if (this.goalentity == head.bot_basewaypoint) - { - this.goalentity_lock_timeout = time + 5; - break; - } - head = head.ctf_worldflagnext; - } - - if (this.goalentity) - this.havocbot_cantfindflag = time + 10; - else if (time > this.havocbot_cantfindflag) - { - // Can't navigate to my own base, suicide! - // TODO: drop it and wander around - Damage(this, this, this, 100000, DEATH_KILL.m_id, DMG_NOWEP, this.origin, '0 0 0'); - return; - } - } -} - -void havocbot_role_ctf_escort(entity this) -{ - entity mf, ef; - - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If enemy flag is back on the base switch to previous role - ef = havocbot_ctf_find_enemy_flag(this); - if(ef.ctf_status==FLAG_BASE) - { - this.havocbot_role = this.havocbot_previous_role; - this.havocbot_role_timeout = 0; - return; - } - - // If the flag carrier reached the base switch to defense - mf = havocbot_ctf_find_flag(this); - if(mf.ctf_status!=FLAG_BASE) - if(vdist(ef.origin - mf.dropped_origin, <, 300)) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_DEFENSE); - return; - } - - // Set the role timeout if necessary - if (!this.havocbot_role_timeout) - { - this.havocbot_role_timeout = time + random() * 30 + 60; - } - - // If nothing happened just switch to previous role - if (time > this.havocbot_role_timeout) - { - this.havocbot_role = this.havocbot_previous_role; - this.havocbot_role_timeout = 0; - return; - } - - // Chase the flag carrier - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - havocbot_goalrating_ctf_enemyflag(this, 30000); - havocbot_goalrating_ctf_ourstolenflag(this, 40000); - havocbot_goalrating_items(this, 10000, this.origin, 10000); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ctf_offense(entity this) -{ - entity mf, ef; - vector pos; - - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // Check flags - mf = havocbot_ctf_find_flag(this); - ef = havocbot_ctf_find_enemy_flag(this); - - // Own flag stolen - if(mf.ctf_status!=FLAG_BASE) - { - if(mf.tag_entity) - pos = mf.tag_entity.origin; - else - pos = mf.origin; - - // Try to get it if closer than the enemy base - if(vlen2(this.origin-ef.dropped_origin)>vlen2(this.origin-pos)) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - } - - // Escort flag carrier - if(ef.ctf_status!=FLAG_BASE) - { - if(ef.tag_entity) - pos = ef.tag_entity.origin; - else - pos = ef.origin; - - if(vdist(pos - mf.dropped_origin, >, 700)) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_ESCORT); - return; - } - } - - // About to fail, switch to middlefield - if(this.health<50) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_MIDDLE); - return; - } - - // Set the role timeout if necessary - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 120; - - if (time > this.havocbot_role_timeout) - { - havocbot_ctf_reset_role(this); - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - havocbot_goalrating_ctf_ourstolenflag(this, 50000); - havocbot_goalrating_ctf_enemybase(this, 20000); - havocbot_goalrating_items(this, 5000, this.origin, 1000); - havocbot_goalrating_items(this, 1000, this.origin, 10000); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -// Retriever (temporary role): -void havocbot_role_ctf_retriever(entity this) -{ - entity mf; - - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If flag is back on the base switch to previous role - mf = havocbot_ctf_find_flag(this); - if(mf.ctf_status==FLAG_BASE) - { - if (mf.enemy == this) // did this bot return the flag? - navigation_goalrating_timeout_force(this); - havocbot_ctf_reset_role(this); - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 20; - - if (time > this.havocbot_role_timeout) - { - havocbot_ctf_reset_role(this); - return; - } - - if (navigation_goalrating_timeout(this)) - { - float rt_radius; - rt_radius = 10000; - - navigation_goalrating_start(this); - - havocbot_goalrating_ctf_ourstolenflag(this, 50000); - havocbot_goalrating_ctf_droppedflags(this, 40000, this.origin, rt_radius); - havocbot_goalrating_ctf_enemybase(this, 30000); - havocbot_goalrating_items(this, 500, this.origin, rt_radius); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ctf_middle(entity this) -{ - entity mf; - - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - mf = havocbot_ctf_find_flag(this); - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 10; - - if (time > this.havocbot_role_timeout) - { - havocbot_ctf_reset_role(this); - return; - } - - if (navigation_goalrating_timeout(this)) - { - vector org; - - org = havocbot_middlepoint; - org.z = this.origin.z; - - navigation_goalrating_start(this); - - havocbot_goalrating_ctf_ourstolenflag(this, 50000); - havocbot_goalrating_ctf_droppedflags(this, 30000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 10000, org, havocbot_middlepoint_radius * 0.5); - havocbot_goalrating_items(this, 5000, org, havocbot_middlepoint_radius * 0.5); - havocbot_goalrating_items(this, 2500, this.origin, 10000); - havocbot_goalrating_ctf_enemybase(this, 2500); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ctf_defense(entity this) -{ - entity mf; - - if(IS_DEAD(this)) - { - havocbot_ctf_reset_role(this); - return; - } - - if (this.flagcarried) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_CARRIER); - return; - } - - // If own flag was captured - mf = havocbot_ctf_find_flag(this); - if(mf.ctf_status!=FLAG_BASE) - { - havocbot_role_ctf_setrole(this, HAVOCBOT_CTF_ROLE_RETRIEVER); - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + 30; - - if (time > this.havocbot_role_timeout) - { - havocbot_ctf_reset_role(this); - return; - } - if (navigation_goalrating_timeout(this)) - { - vector org = mf.dropped_origin; - - navigation_goalrating_start(this); - - // if enemies are closer to our base, go there - entity closestplayer = NULL; - float distance, bestdistance = 10000; - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - distance = vlen(org - it.origin); - if(distance<bestdistance) - { - closestplayer = it; - bestdistance = distance; - } - }); - - if(closestplayer) - if(DIFF_TEAM(closestplayer, this)) - if(vdist(org - this.origin, >, 1000)) - if(checkpvs(this.origin,closestplayer)||random()<0.5) - havocbot_goalrating_ctf_ourbase(this, 30000); - - havocbot_goalrating_ctf_ourstolenflag(this, 20000); - havocbot_goalrating_ctf_droppedflags(this, 20000, org, havocbot_middlepoint_radius); - havocbot_goalrating_enemyplayers(this, 15000, org, havocbot_middlepoint_radius); - havocbot_goalrating_items(this, 10000, org, havocbot_middlepoint_radius); - havocbot_goalrating_items(this, 5000, this.origin, 10000); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ctf_setrole(entity bot, int role) -{ - string s = "(null)"; - switch(role) - { - case HAVOCBOT_CTF_ROLE_CARRIER: - s = "carrier"; - bot.havocbot_role = havocbot_role_ctf_carrier; - bot.havocbot_role_timeout = 0; - bot.havocbot_cantfindflag = time + 10; - if (bot.havocbot_previous_role != bot.havocbot_role) - navigation_goalrating_timeout_force(bot); - break; - case HAVOCBOT_CTF_ROLE_DEFENSE: - s = "defense"; - bot.havocbot_role = havocbot_role_ctf_defense; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_MIDDLE: - s = "middle"; - bot.havocbot_role = havocbot_role_ctf_middle; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_OFFENSE: - s = "offense"; - bot.havocbot_role = havocbot_role_ctf_offense; - bot.havocbot_role_timeout = 0; - break; - case HAVOCBOT_CTF_ROLE_RETRIEVER: - s = "retriever"; - bot.havocbot_previous_role = bot.havocbot_role; - bot.havocbot_role = havocbot_role_ctf_retriever; - bot.havocbot_role_timeout = time + 10; - if (bot.havocbot_previous_role != bot.havocbot_role) - navigation_goalrating_timeout_expire(bot, 2); - break; - case HAVOCBOT_CTF_ROLE_ESCORT: - s = "escort"; - bot.havocbot_previous_role = bot.havocbot_role; - bot.havocbot_role = havocbot_role_ctf_escort; - bot.havocbot_role_timeout = time + 30; - if (bot.havocbot_previous_role != bot.havocbot_role) - navigation_goalrating_timeout_expire(bot, 2); - break; - } - LOG_TRACE(bot.netname, " switched to ", s); -} - - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - int t = 0, t2 = 0, t3 = 0; - bool b1 = false, b2 = false, b3 = false, b4 = false, b5 = false; // TODO: kill this, we WANT to show the other flags, somehow! (note: also means you don't see if you're FC) - - // initially clear items so they can be set as necessary later. - STAT(CTF_FLAGSTATUS, player) &= ~(CTF_RED_FLAG_CARRYING | CTF_RED_FLAG_TAKEN | CTF_RED_FLAG_LOST - | CTF_BLUE_FLAG_CARRYING | CTF_BLUE_FLAG_TAKEN | CTF_BLUE_FLAG_LOST - | CTF_YELLOW_FLAG_CARRYING | CTF_YELLOW_FLAG_TAKEN | CTF_YELLOW_FLAG_LOST - | CTF_PINK_FLAG_CARRYING | CTF_PINK_FLAG_TAKEN | CTF_PINK_FLAG_LOST - | CTF_NEUTRAL_FLAG_CARRYING | CTF_NEUTRAL_FLAG_TAKEN | CTF_NEUTRAL_FLAG_LOST - | CTF_FLAG_NEUTRAL | CTF_SHIELDED | CTF_STALEMATE); - - // scan through all the flags and notify the client about them - for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) - { - if(flag.team == NUM_TEAM_1 && !b1) { b1 = true; t = CTF_RED_FLAG_CARRYING; t2 = CTF_RED_FLAG_TAKEN; t3 = CTF_RED_FLAG_LOST; } - if(flag.team == NUM_TEAM_2 && !b2) { b2 = true; t = CTF_BLUE_FLAG_CARRYING; t2 = CTF_BLUE_FLAG_TAKEN; t3 = CTF_BLUE_FLAG_LOST; } - if(flag.team == NUM_TEAM_3 && !b3) { b3 = true; t = CTF_YELLOW_FLAG_CARRYING; t2 = CTF_YELLOW_FLAG_TAKEN; t3 = CTF_YELLOW_FLAG_LOST; } - if(flag.team == NUM_TEAM_4 && !b4) { b4 = true; t = CTF_PINK_FLAG_CARRYING; t2 = CTF_PINK_FLAG_TAKEN; t3 = CTF_PINK_FLAG_LOST; } - if(flag.team == 0 && !b5) { b5 = true; t = CTF_NEUTRAL_FLAG_CARRYING; t2 = CTF_NEUTRAL_FLAG_TAKEN; t3 = CTF_NEUTRAL_FLAG_LOST; STAT(CTF_FLAGSTATUS, player) |= CTF_FLAG_NEUTRAL; } - - switch(flag.ctf_status) - { - case FLAG_PASSING: - case FLAG_CARRY: - { - if((flag.owner == player) || (flag.pass_sender == player)) - STAT(CTF_FLAGSTATUS, player) |= t; // carrying: player is currently carrying the flag - else - STAT(CTF_FLAGSTATUS, player) |= t2; // taken: someone else is carrying the flag - break; - } - case FLAG_DROPPED: - { - STAT(CTF_FLAGSTATUS, player) |= t3; // lost: the flag is dropped somewhere on the map - break; - } - } - } - - // item for stopping players from capturing the flag too often - if(player.ctf_captureshielded) - STAT(CTF_FLAGSTATUS, player) |= CTF_SHIELDED; - - if(ctf_stalemate) - STAT(CTF_FLAGSTATUS, player) |= CTF_STALEMATE; - - // update the health of the flag carrier waypointsprite - if(player.wps_flagcarrier) - WaypointSprite_UpdateHealth(player.wps_flagcarrier, '1 0 0' * healtharmor_maxdamage(player.health, player.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id)); -} - -MUTATOR_HOOKFUNCTION(ctf, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_damage = M_ARGV(4, float); - vector frag_force = M_ARGV(6, vector); - - if(frag_attacker.flagcarried) // if the attacker is a flagcarrier - { - if(frag_target == frag_attacker) // damage done to yourself - { - frag_damage *= autocvar_g_ctf_flagcarrier_selfdamagefactor; - frag_force *= autocvar_g_ctf_flagcarrier_selfforcefactor; - } - else // damage done to everyone else - { - frag_damage *= autocvar_g_ctf_flagcarrier_damagefactor; - frag_force *= autocvar_g_ctf_flagcarrier_forcefactor; - } - - M_ARGV(4, float) = frag_damage; - M_ARGV(6, vector) = frag_force; - } - else if(frag_target.flagcarried && !IS_DEAD(frag_target) && CTF_DIFFTEAM(frag_target, frag_attacker)) // if the target is a flagcarrier - { - if(autocvar_g_ctf_flagcarrier_auto_helpme_damage > ('1 0 0' * healtharmor_maxdamage(frag_target.health, frag_target.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id))) - if(time > frag_target.wps_helpme_time + autocvar_g_ctf_flagcarrier_auto_helpme_time) - { - frag_target.wps_helpme_time = time; - WaypointSprite_HelpMePing(frag_target.wps_flagcarrier); - } - // todo: add notification for when flag carrier needs help? - } -} - -MUTATOR_HOOKFUNCTION(ctf, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker)) && (frag_target.flagcarried)) - { - GameRules_scoring_add_team(frag_attacker, SCORE, ((SAME_TEAM(frag_attacker, frag_target)) ? -autocvar_g_ctf_score_kill : autocvar_g_ctf_score_kill)); - GameRules_scoring_add(frag_attacker, CTF_FCKILLS, 1); - } - - if(frag_target.flagcarried) - { - entity tmp_entity = frag_target.flagcarried; - ctf_Handle_Throw(frag_target, NULL, DROP_NORMAL); - tmp_entity.ctf_dropper = NULL; - } -} - -MUTATOR_HOOKFUNCTION(ctf, GiveFragsForKill) -{ - M_ARGV(2, float) = 0; // frag score - return (autocvar_g_ctf_ignore_frags); // no frags counted in ctf if this is true -} - -void ctf_RemovePlayer(entity player) -{ - if(player.flagcarried) - { ctf_Handle_Throw(player, NULL, DROP_NORMAL); } - - for(entity flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) - { - if(flag.pass_sender == player) { flag.pass_sender = NULL; } - if(flag.pass_target == player) { flag.pass_target = NULL; } - if(flag.ctf_dropper == player) { flag.ctf_dropper = NULL; } - } -} - -MUTATOR_HOOKFUNCTION(ctf, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - ctf_RemovePlayer(player); -} - -MUTATOR_HOOKFUNCTION(ctf, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - ctf_RemovePlayer(player); -} - -MUTATOR_HOOKFUNCTION(ctf, ClientConnect) -{ - if(!autocvar_g_ctf_leaderboard) - return; - - entity player = M_ARGV(0, entity); - - if(IS_REAL_CLIENT(player)) - { - for(int i = 1; i <= RANKINGS_CNT; ++i) - { - race_SendRankings(i, 0, 0, MSG_ONE); - } - } -} - -MUTATOR_HOOKFUNCTION(ctf, GetPressedKeys) -{ - if(!autocvar_g_ctf_leaderboard) - return; - - entity player = M_ARGV(0, entity); - - if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) - { - if (!player.stored_netname) - player.stored_netname = strzone(uid2name(player.crypto_idfp)); - if(player.stored_netname != player.netname) - { - db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); - strunzone(player.stored_netname); - player.stored_netname = strzone(player.netname); - } - } -} - -MUTATOR_HOOKFUNCTION(ctf, PortalTeleport) -{ - entity player = M_ARGV(0, entity); - - if(player.flagcarried) - if(!autocvar_g_ctf_portalteleport) - { ctf_Handle_Throw(player, NULL, DROP_NORMAL); } -} - -MUTATOR_HOOKFUNCTION(ctf, PlayerUseKey) -{ - if(MUTATOR_RETURNVALUE || game_stopped) return; - - entity player = M_ARGV(0, entity); - - if((time > player.throw_antispam) && !IS_DEAD(player) && !player.speedrunning && (!player.vehicle || autocvar_g_ctf_allow_vehicle_touch)) - { - // pass the flag to a team mate - if(autocvar_g_ctf_pass) - { - entity head, closest_target = NULL; - head = WarpZone_FindRadius(player.origin, autocvar_g_ctf_pass_radius, true); - - while(head) // find the closest acceptable target to pass to - { - if(IS_PLAYER(head) && !IS_DEAD(head)) - if(head != player && SAME_TEAM(head, player)) - if(!head.speedrunning && !head.vehicle) - { - // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) - vector head_center = WarpZone_UnTransformOrigin(head, CENTER_OR_VIEWOFS(head)); - vector passer_center = CENTER_OR_VIEWOFS(player); - - if(ctf_CheckPassDirection(head_center, passer_center, player.v_angle, head.WarpZone_findradius_nearest)) - { - if(autocvar_g_ctf_pass_request && !player.flagcarried && head.flagcarried) - { - if(IS_BOT_CLIENT(head)) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname); - ctf_Handle_Throw(head, player, DROP_PASS); - } - else - { - Send_Notification(NOTIF_ONE, head, MSG_CENTER, CENTER_CTF_PASS_REQUESTED, player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_PASS_REQUESTING, head.netname); - } - player.throw_antispam = time + autocvar_g_ctf_pass_wait; - return true; - } - else if(player.flagcarried && !head.flagcarried) - { - if(closest_target) - { - vector closest_target_center = WarpZone_UnTransformOrigin(closest_target, CENTER_OR_VIEWOFS(closest_target)); - if(vlen2(passer_center - head_center) < vlen2(passer_center - closest_target_center)) - { closest_target = head; } - } - else { closest_target = head; } - } - } - } - head = head.chain; - } - - if(closest_target) { ctf_Handle_Throw(player, closest_target, DROP_PASS); return true; } - } - - // throw the flag in front of you - if(autocvar_g_ctf_throw && player.flagcarried) - { - if(player.throw_count == -1) - { - if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - { - player.throw_prevtime = time; - player.throw_count = 1; - ctf_Handle_Throw(player, NULL, DROP_THROW); - return true; - } - else - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CTF_FLAG_THROW_PUNISH, rint((player.throw_prevtime + autocvar_g_ctf_throw_punish_delay) - time)); - return false; - } - } - else - { - if(time > player.throw_prevtime + autocvar_g_ctf_throw_punish_time) { player.throw_count = 1; } - else { player.throw_count += 1; } - if(player.throw_count >= autocvar_g_ctf_throw_punish_count) { player.throw_count = -1; } - - player.throw_prevtime = time; - ctf_Handle_Throw(player, NULL, DROP_THROW); - return true; - } - } - } -} - -MUTATOR_HOOKFUNCTION(ctf, HelpMePing) -{ - entity player = M_ARGV(0, entity); - - if(player.wps_flagcarrier) // update the flagcarrier waypointsprite with "NEEDING HELP" notification - { - player.wps_helpme_time = time; - WaypointSprite_HelpMePing(player.wps_flagcarrier); - } - else // create a normal help me waypointsprite - { - WaypointSprite_Spawn(WP_Helpme, waypointsprite_deployed_lifetime, waypointsprite_limitedrange, player, FLAG_WAYPOINT_OFFSET, NULL, player.team, player, wps_helpme, false, RADARICON_HELPME); - WaypointSprite_Ping(player.wps_helpme); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ctf, VehicleEnter) -{ - entity player = M_ARGV(0, entity); - entity veh = M_ARGV(1, entity); - - if(player.flagcarried) - { - if(!autocvar_g_ctf_allow_vehicle_carry && !autocvar_g_ctf_allow_vehicle_touch) - { - ctf_Handle_Throw(player, NULL, DROP_NORMAL); - } - else - { - player.flagcarried.nodrawtoclient = player; // hide the flag from the driver - setattachment(player.flagcarried, veh, ""); - setorigin(player.flagcarried, VEHICLE_FLAG_OFFSET); - player.flagcarried.scale = VEHICLE_FLAG_SCALE; - //player.flagcarried.angles = '0 0 0'; - } - return true; - } -} - -MUTATOR_HOOKFUNCTION(ctf, VehicleExit) -{ - entity player = M_ARGV(0, entity); - - if(player.flagcarried) - { - setattachment(player.flagcarried, player, ""); - setorigin(player.flagcarried, FLAG_CARRY_OFFSET); - player.flagcarried.scale = FLAG_SCALE; - player.flagcarried.angles = '0 0 0'; - player.flagcarried.nodrawtoclient = NULL; - return true; - } -} - -MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun) -{ - entity player = M_ARGV(0, entity); - - if(player.flagcarried) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(player.flagcarried.team, INFO_CTF_FLAGRETURN_ABORTRUN)); - ctf_RespawnFlag(player.flagcarried); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ctf, MatchEnd) -{ - entity flag; // temporary entity for the search method - - for(flag = ctf_worldflaglist; flag; flag = flag.ctf_worldflagnext) - { - switch(flag.ctf_status) - { - case FLAG_DROPPED: - case FLAG_PASSING: - { - // lock the flag, game is over - set_movetype(flag, MOVETYPE_NONE); - flag.takedamage = DAMAGE_NO; - flag.solid = SOLID_NOT; - flag.nextthink = false; // stop thinking - - //dprint("stopping the ", flag.netname, " from moving.\n"); - break; - } - - default: - case FLAG_BASE: - case FLAG_CARRY: - { - // do nothing for these flags - break; - } - } - } -} - -MUTATOR_HOOKFUNCTION(ctf, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - havocbot_ctf_reset_role(bot); - return true; -} - -MUTATOR_HOOKFUNCTION(ctf, CheckAllowedTeams) -{ - //M_ARGV(0, float) = ctf_teams; - M_ARGV(1, string) = "ctf_team"; - return true; -} - -MUTATOR_HOOKFUNCTION(ctf, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - STAT(CTF_FLAGSTATUS, client) = STAT(CTF_FLAGSTATUS, spectatee); -} - -MUTATOR_HOOKFUNCTION(ctf, GetRecords) -{ - int record_page = M_ARGV(0, int); - string ret_string = M_ARGV(1, string); - - for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) - { - if (MapInfo_Get_ByID(i)) - { - float r = stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/time"))); - - if(!r) - continue; - - // TODO: uid2name - string h = db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, "/captimerecord/netname")); - ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-6, ftos_decimals(r, 2)), " ", h, "\n"); - } - } - - M_ARGV(1, string) = ret_string; -} - -bool superspec_Spectate(entity this, entity targ); // TODO -void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel); // TODO -MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) -{ - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if(IS_PLAYER(player) || MUTATOR_RETURNVALUE || !cvar("g_superspectate")) { return false; } - - if(cmd_name == "followfc") - { - if(!g_ctf) - return true; - - int _team = 0; - bool found = false; - - if(cmd_argc == 2) - { - switch(argv(1)) - { - case "red": if(ctf_teams & BIT(0)) _team = NUM_TEAM_1; break; - case "blue": if(ctf_teams & BIT(1)) _team = NUM_TEAM_2; break; - case "yellow": if(ctf_teams & BIT(2)) _team = NUM_TEAM_3; break; - case "pink": if(ctf_teams & BIT(3)) _team = NUM_TEAM_4; break; - } - } - - FOREACH_CLIENT(IS_PLAYER(it), { - if(it.flagcarried && (it.team == _team || _team == 0)) - { - found = true; - if(_team == 0 && IS_SPEC(player) && player.enemy == it) - continue; // already spectating this fc, try another - return superspec_Spectate(player, it); - } - }); - - if(!found) - superspec_msg("", "", player, "No active flag carrier\n", 1); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems) -{ - entity frag_target = M_ARGV(0, entity); - - if(frag_target.flagcarried) - ctf_Handle_Throw(frag_target, NULL, DROP_THROW); -} - - -// ========== -// Spawnfuncs -// ========== - -/*QUAKED spawnfunc_item_flag_team1 (0 0.5 0.8) (-48 -48 -37) (48 48 37) -CTF flag for team one (Red). -Keys: -"angle" Angle the flag will point (minus 90 degrees)... -"model" model to use, note this needs red and blue as skins 0 and 1... -"noise" sound played when flag is picked up... -"noise1" sound played when flag is returned by a teammate... -"noise2" sound played when flag is captured... -"noise3" sound played when flag is lost in the field and respawns itself... -"noise4" sound played when flag is dropped by a player... -"noise5" sound played when flag touches the ground... */ -spawnfunc(item_flag_team1) -{ - if(!g_ctf) { delete(this); return; } - - ctf_FlagSetup(NUM_TEAM_1, this); -} - -/*QUAKED spawnfunc_item_flag_team2 (0 0.5 0.8) (-48 -48 -37) (48 48 37) -CTF flag for team two (Blue). -Keys: -"angle" Angle the flag will point (minus 90 degrees)... -"model" model to use, note this needs red and blue as skins 0 and 1... -"noise" sound played when flag is picked up... -"noise1" sound played when flag is returned by a teammate... -"noise2" sound played when flag is captured... -"noise3" sound played when flag is lost in the field and respawns itself... -"noise4" sound played when flag is dropped by a player... -"noise5" sound played when flag touches the ground... */ -spawnfunc(item_flag_team2) -{ - if(!g_ctf) { delete(this); return; } - - ctf_FlagSetup(NUM_TEAM_2, this); -} - -/*QUAKED spawnfunc_item_flag_team3 (0 0.5 0.8) (-48 -48 -37) (48 48 37) -CTF flag for team three (Yellow). -Keys: -"angle" Angle the flag will point (minus 90 degrees)... -"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... -"noise" sound played when flag is picked up... -"noise1" sound played when flag is returned by a teammate... -"noise2" sound played when flag is captured... -"noise3" sound played when flag is lost in the field and respawns itself... -"noise4" sound played when flag is dropped by a player... -"noise5" sound played when flag touches the ground... */ -spawnfunc(item_flag_team3) -{ - if(!g_ctf) { delete(this); return; } - - ctf_FlagSetup(NUM_TEAM_3, this); -} - -/*QUAKED spawnfunc_item_flag_team4 (0 0.5 0.8) (-48 -48 -37) (48 48 37) -CTF flag for team four (Pink). -Keys: -"angle" Angle the flag will point (minus 90 degrees)... -"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... -"noise" sound played when flag is picked up... -"noise1" sound played when flag is returned by a teammate... -"noise2" sound played when flag is captured... -"noise3" sound played when flag is lost in the field and respawns itself... -"noise4" sound played when flag is dropped by a player... -"noise5" sound played when flag touches the ground... */ -spawnfunc(item_flag_team4) -{ - if(!g_ctf) { delete(this); return; } - - ctf_FlagSetup(NUM_TEAM_4, this); -} - -/*QUAKED spawnfunc_item_flag_neutral (0 0.5 0.8) (-48 -48 -37) (48 48 37) -CTF flag (Neutral). -Keys: -"angle" Angle the flag will point (minus 90 degrees)... -"model" model to use, note this needs red, blue yellow and pink as skins 0, 1, 2 and 3... -"noise" sound played when flag is picked up... -"noise1" sound played when flag is returned by a teammate... -"noise2" sound played when flag is captured... -"noise3" sound played when flag is lost in the field and respawns itself... -"noise4" sound played when flag is dropped by a player... -"noise5" sound played when flag touches the ground... */ -spawnfunc(item_flag_neutral) -{ - if(!g_ctf) { delete(this); return; } - if(!cvar("g_ctf_oneflag")) { delete(this); return; } - - ctf_FlagSetup(0, this); -} - -/*QUAKED spawnfunc_ctf_team (0 .5 .8) (-16 -16 -24) (16 16 32) -Team declaration for CTF gameplay, this allows you to decide what team names and control point models are used in your map. -Note: If you use spawnfunc_ctf_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too. -Keys: -"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)... -"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ -spawnfunc(ctf_team) -{ - if(!g_ctf) { delete(this); return; } - - this.classname = "ctf_team"; - this.team = this.cnt + 1; -} - -// compatibility for quake maps -spawnfunc(team_CTF_redflag) { spawnfunc_item_flag_team1(this); } -spawnfunc(team_CTF_blueflag) { spawnfunc_item_flag_team2(this); } -spawnfunc(info_player_team1); -spawnfunc(team_CTF_redplayer) { spawnfunc_info_player_team1(this); } -spawnfunc(team_CTF_redspawn) { spawnfunc_info_player_team1(this); } -spawnfunc(info_player_team2); -spawnfunc(team_CTF_blueplayer) { spawnfunc_info_player_team2(this); } -spawnfunc(team_CTF_bluespawn) { spawnfunc_info_player_team2(this); } - -spawnfunc(team_CTF_neutralflag) { spawnfunc_item_flag_neutral(this); } -spawnfunc(team_neutralobelisk) { spawnfunc_item_flag_neutral(this); } - -// compatibility for wop maps -spawnfunc(team_redplayer) { spawnfunc_info_player_team1(this); } -spawnfunc(team_blueplayer) { spawnfunc_info_player_team2(this); } -spawnfunc(team_ctl_redlolly) { spawnfunc_item_flag_team1(this); } -spawnfunc(team_CTL_redlolly) { spawnfunc_item_flag_team1(this); } -spawnfunc(team_ctl_bluelolly) { spawnfunc_item_flag_team2(this); } -spawnfunc(team_CTL_bluelolly) { spawnfunc_item_flag_team2(this); } - - -// ============== -// Initialization -// ============== - -// scoreboard setup -void ctf_ScoreRules(int teams) -{ - CheckAllowedTeams(NULL); - GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { - field_team(ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); - field(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - field(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); - field(SP_CTF_PICKUPS, "pickups", 0); - field(SP_CTF_FCKILLS, "fckills", 0); - field(SP_CTF_RETURNS, "returns", 0); - field(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); - }); -} - -// code from here on is just to support maps that don't have flag and team entities -void ctf_SpawnTeam (string teamname, int teamcolor) -{ - entity this = new_pure(ctf_team); - this.netname = teamname; - this.cnt = teamcolor - 1; - this.spawnfunc_checked = true; - this.team = teamcolor; -} - -void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. -{ - ctf_teams = 0; - - entity tmp_entity; - for(tmp_entity = ctf_worldflaglist; tmp_entity; tmp_entity = tmp_entity.ctf_worldflagnext) - { - //if(tmp_entity.team == NUM_TEAM_3) { ctf_teams = max(3, ctf_teams); } - //if(tmp_entity.team == NUM_TEAM_4) { ctf_teams = max(4, ctf_teams); } - - switch(tmp_entity.team) - { - case NUM_TEAM_1: BITSET_ASSIGN(ctf_teams, BIT(0)); break; - case NUM_TEAM_2: BITSET_ASSIGN(ctf_teams, BIT(1)); break; - case NUM_TEAM_3: BITSET_ASSIGN(ctf_teams, BIT(2)); break; - case NUM_TEAM_4: BITSET_ASSIGN(ctf_teams, BIT(3)); break; - } - if(tmp_entity.team == 0) { ctf_oneflag = true; } - } - - havocbot_ctf_calculate_middlepoint(); - - if(NumTeams(ctf_teams) < 2) // somehow, there's not enough flags! - { - ctf_teams = 0; // so set the default red and blue teams - BITSET_ASSIGN(ctf_teams, BIT(0)); - BITSET_ASSIGN(ctf_teams, BIT(1)); - } - - //ctf_teams = bound(2, ctf_teams, 4); - - // if no teams are found, spawn defaults - if(find(NULL, classname, "ctf_team") == NULL) - { - LOG_TRACE("No \"ctf_team\" entities found on this map, creating them anyway."); - if(ctf_teams & BIT(0)) - ctf_SpawnTeam("Red", NUM_TEAM_1); - if(ctf_teams & BIT(1)) - ctf_SpawnTeam("Blue", NUM_TEAM_2); - if(ctf_teams & BIT(2)) - ctf_SpawnTeam("Yellow", NUM_TEAM_3); - if(ctf_teams & BIT(3)) - ctf_SpawnTeam("Pink", NUM_TEAM_4); - } - - ctf_ScoreRules(ctf_teams); -} - -void ctf_Initialize() -{ - ctf_captimerecord = stof(db_get(ServerProgsDB, strcat(GetMapname(), "/captimerecord/time"))); - - ctf_captureshield_min_negscore = autocvar_g_ctf_shield_min_negscore; - ctf_captureshield_max_ratio = autocvar_g_ctf_shield_max_ratio; - ctf_captureshield_force = autocvar_g_ctf_shield_force; - - InitializeEntity(NULL, ctf_DelayedInit, INITPRIO_GAMETYPE); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qh b/qcsrc/server/mutators/mutator/gamemode_ctf.qh deleted file mode 100644 index 14bf281e71..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qh +++ /dev/null @@ -1,190 +0,0 @@ -#pragma once - -#ifdef SVQC - -#include "../gamemode.qh" - -void ctf_Initialize(); - -REGISTER_MUTATOR(ctf, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - GameRules_limit_score(autocvar_capturelimit_override); - GameRules_limit_lead(autocvar_captureleadlimit_override); - - ctf_Initialize(); - } - return 0; -} - -// used in cheats.qc -void ctf_RespawnFlag(entity flag); - -// score rule declarations -const int ST_CTF_CAPS = 1; - -CLASS(Flag, Pickup) - ATTRIB(Flag, m_mins, vector, (PL_MIN_CONST + '0 0 -13') * 1.4); // scaling be damned - ATTRIB(Flag, m_maxs, vector, (PL_MAX_CONST + '0 0 -13') * 1.4); -ENDCLASS(Flag) -Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } -void ctf_FlagTouch(entity this, entity toucher) { ITEM_HANDLE(Pickup, CTF_FLAG, this, toucher); } - -// flag constants // for most of these, there is just one question to be asked: WHYYYYY? - -const float FLAG_SCALE = 0.6; - -const float FLAG_THINKRATE = 0.2; -const float FLAG_TOUCHRATE = 0.5; -const float WPFE_THINKRATE = 0.5; - -const vector FLAG_DROP_OFFSET = ('0 0 32'); -const vector FLAG_CARRY_OFFSET = ('-16 0 8'); -#define FLAG_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) -const vector FLAG_WAYPOINT_OFFSET = ('0 0 64'); -const vector FLAG_FLOAT_OFFSET = ('0 0 32'); -const vector FLAG_PASS_ARC_OFFSET = ('0 0 -10'); - -const vector VEHICLE_FLAG_OFFSET = ('0 0 96'); -const float VEHICLE_FLAG_SCALE = 1.0; - -// waypoint colors -#define WPCOLOR_ENEMYFC(t) ((t) ? colormapPaletteColor(t - 1, false) * 0.75 : '1 1 1') -#define WPCOLOR_FLAGCARRIER(t) (WP_FlagCarrier.m_color) -#define WPCOLOR_DROPPEDFLAG(t) ((t) ? ('0.25 0.25 0.25' + colormapPaletteColor(t - 1, false)) * 0.5 : '1 1 1') - -// sounds -#define snd_flag_taken noise -#define snd_flag_returned noise1 -#define snd_flag_capture noise2 -#define snd_flag_respawn noise3 -.string snd_flag_dropped; -.string snd_flag_touch; -.string snd_flag_pass; - -// score fields -.float score_assist; -.float score_capture; -.float score_drop; // note: negated -.float score_pickup; -.float score_return; -.float score_team_capture; // shouldn't be too high - -// effects -.string toucheffect; -.string passeffect; -.string capeffect; - -// list of flags on the map -entity ctf_worldflaglist; -.entity ctf_worldflagnext; -.entity ctf_staleflagnext; - -// waypoint sprites -.entity wps_helpme; -.entity wps_flagbase; -.entity wps_flagcarrier; -.entity wps_flagdropped; -.entity wps_flagreturn; -.entity wps_enemyflagcarrier; -.float wps_helpme_time; -bool wpforenemy_announced; -float wpforenemy_nextthink; - -// statuses -const int FLAG_BASE = 1; -const int FLAG_DROPPED = 2; -const int FLAG_CARRY = 3; -const int FLAG_PASSING = 4; - -const int DROP_NORMAL = 1; -const int DROP_THROW = 2; -const int DROP_PASS = 3; -const int DROP_RESET = 4; - -const int PICKUP_BASE = 1; -const int PICKUP_DROPPED = 2; - -const int CAPTURE_NORMAL = 1; -const int CAPTURE_DROPPED = 2; - -const int RETURN_TIMEOUT = 1; -const int RETURN_DROPPED = 2; -const int RETURN_DAMAGE = 3; -const int RETURN_SPEEDRUN = 4; -const int RETURN_NEEDKILL = 5; - -bool ctf_Stalemate_Customize(entity this, entity client); - -void ctf_Handle_Throw(entity player, entity receiver, float droptype); - -// flag properties -#define ctf_spawnorigin dropped_origin -bool ctf_stalemate; // indicates that a stalemate is active -float ctf_captimerecord; // record time for capturing the flag -.float ctf_pickuptime; -.float ctf_droptime; -.int ctf_status; // status of the flag (FLAG_BASE, FLAG_DROPPED, FLAG_CARRY declared globally) -.entity ctf_dropper; // don't allow spam of dropping the flag -.int max_flag_health; -.float next_take_time; -.bool ctf_flagdamaged_byworld; -int ctf_teams; -.entity enemy; // when flag is back in the base, it remembers last player who carried/touched the flag, useful to bots - -// passing/throwing properties -.float pass_distance; -.entity pass_sender; -.entity pass_target; -.float throw_antispam; -.float throw_prevtime; -.int throw_count; - -// CaptureShield: If the player is too bad to be allowed to capture, shield them from taking the flag. -.bool ctf_captureshielded; // set to 1 if the player is too bad to be allowed to capture -float ctf_captureshield_min_negscore; // punish at -20 points -float ctf_captureshield_max_ratio; // punish at most 30% of each team -float ctf_captureshield_force; // push force of the shield - -// 1 flag ctf -bool ctf_oneflag; // indicates whether or not a neutral flag has been found - -// bot player logic -const int HAVOCBOT_CTF_ROLE_NONE = 0; -const int HAVOCBOT_CTF_ROLE_DEFENSE = 2; -const int HAVOCBOT_CTF_ROLE_MIDDLE = 4; -const int HAVOCBOT_CTF_ROLE_OFFENSE = 8; -const int HAVOCBOT_CTF_ROLE_CARRIER = 16; -const int HAVOCBOT_CTF_ROLE_RETRIEVER = 32; -const int HAVOCBOT_CTF_ROLE_ESCORT = 64; - -.bool havocbot_cantfindflag; - -void havocbot_role_ctf_setrole(entity bot, int role); - -// team checking -#define CTF_SAMETEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? DIFF_TEAM(a,b) : SAME_TEAM(a,b)) -#define CTF_DIFFTEAM(a,b) ((autocvar_g_ctf_reverse || (ctf_oneflag && autocvar_g_ctf_oneflag_reverse)) ? SAME_TEAM(a,b) : DIFF_TEAM(a,b)) -#endif - -const int CTF_RED_FLAG_TAKEN = 1; -const int CTF_RED_FLAG_LOST = 2; -const int CTF_RED_FLAG_CARRYING = 3; -const int CTF_BLUE_FLAG_TAKEN = 4; -const int CTF_BLUE_FLAG_LOST = 8; -const int CTF_BLUE_FLAG_CARRYING = 12; -const int CTF_YELLOW_FLAG_TAKEN = 16; -const int CTF_YELLOW_FLAG_LOST = 32; -const int CTF_YELLOW_FLAG_CARRYING = 48; -const int CTF_PINK_FLAG_TAKEN = 64; -const int CTF_PINK_FLAG_LOST = 128; -const int CTF_PINK_FLAG_CARRYING = 192; -const int CTF_NEUTRAL_FLAG_TAKEN = 256; -const int CTF_NEUTRAL_FLAG_LOST = 512; -const int CTF_NEUTRAL_FLAG_CARRYING = 768; -const int CTF_FLAG_NEUTRAL = 2048; -const int CTF_SHIELDED = 4096; -const int CTF_STALEMATE = 8192; diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qc b/qcsrc/server/mutators/mutator/gamemode_cts.qc deleted file mode 100644 index daaf8a9695..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qc +++ /dev/null @@ -1,431 +0,0 @@ -#include "gamemode_cts.qh" - -#include <server/race.qh> -#include <server/items.qh> - -float autocvar_g_cts_finish_kill_delay; -bool autocvar_g_cts_selfdamage; - -// legacy bot roles -.float race_checkpoint; -void havocbot_role_cts(entity this) -{ - if(IS_DEAD(this)) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - bool raw_touch_check = true; - int cp = this.race_checkpoint; - - LABEL(search_racecheckpoints) - IL_EACH(g_racecheckpoints, true, - { - if(it.cnt == cp || cp == -1) - { - // redirect bot to next goal if it touched the waypoint of an untouchable checkpoint - // e.g. checkpoint in front of Stormkeep's warpzone - // the same workaround is applied in Race game mode - if (raw_touch_check && vdist(this.origin - it.nearestwaypoint.origin, <, 30)) - { - cp = race_NextCheckpoint(cp); - raw_touch_check = false; - goto search_racecheckpoints; - } - navigation_routerating(this, it, 1000000, 5000); - } - }); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void cts_ScoreRules() -{ - GameRules_score_enabled(false); - GameRules_scoring(0, 0, 0, { - if (g_race_qualifying) { - field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); - } else { - field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); - } - }); -} - -void cts_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":cts:", mode, ":", ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); -} - -void KillIndicator_Think(entity this); -void CTS_ClientKill(entity e) // silent version of ClientKill, used when player finishes a CTS run. Useful to prevent cheating by running back to the start line and starting out with more speed -{ - e.killindicator = spawn(); - e.killindicator.owner = e; - setthink(e.killindicator, KillIndicator_Think); - e.killindicator.nextthink = time + (e.lip) * 0.05; - e.killindicator.cnt = ceil(autocvar_g_cts_finish_kill_delay); - e.killindicator.health = 1; // this is used to indicate that it should be silent - e.lip = 0; -} - -MUTATOR_HOOKFUNCTION(cts, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - float dt = M_ARGV(1, float); - - player.race_movetime_frac += dt; - float f = floor(player.race_movetime_frac); - player.race_movetime_frac -= f; - player.race_movetime_count += f; - player.race_movetime = player.race_movetime_frac + player.race_movetime_count; - -#ifdef SVQC - if(IS_PLAYER(player)) - { - if (player.race_penalty) - if (time > player.race_penalty) - player.race_penalty = 0; - if(player.race_penalty) - { - player.velocity = '0 0 0'; - set_movetype(player, MOVETYPE_NONE); - player.disableclientprediction = 2; - } - } -#endif - - // force kbd movement for fairness - float wishspeed; - vector wishvel; - - // if record times matter - // ensure nothing EVIL is being done (i.e. div0_evade) - // this hinders joystick users though - // but it still gives SOME analog control - wishvel.x = fabs(CS(player).movement.x); - wishvel.y = fabs(CS(player).movement.y); - if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y) - { - wishvel.z = 0; - wishspeed = vlen(wishvel); - if(wishvel.x >= 2 * wishvel.y) - { - // pure X motion - if(CS(player).movement.x > 0) - CS(player).movement_x = wishspeed; - else - CS(player).movement_x = -wishspeed; - CS(player).movement_y = 0; - } - else if(wishvel.y >= 2 * wishvel.x) - { - // pure Y motion - CS(player).movement_x = 0; - if(CS(player).movement.y > 0) - CS(player).movement_y = wishspeed; - else - CS(player).movement_y = -wishspeed; - } - else - { - // diagonal - if(CS(player).movement.x > 0) - CS(player).movement_x = M_SQRT1_2 * wishspeed; - else - CS(player).movement_x = -M_SQRT1_2 * wishspeed; - if(CS(player).movement.y > 0) - CS(player).movement_y = M_SQRT1_2 * wishspeed; - else - CS(player).movement_y = -M_SQRT1_2 * wishspeed; - } - } -} - -MUTATOR_HOOKFUNCTION(cts, reset_map_global) -{ - float s; - - Score_NicePrint(NULL); - - race_ClearRecords(); - PlayerScore_Sort(race_place, 0, 1, 0); - - FOREACH_CLIENT(true, { - if(it.race_place) - { - s = GameRules_scoring_add(it, RACE_FASTEST, 0); - if(!s) - it.race_place = 0; - } - cts_EventLog(ftos(it.race_place), it); - }); - - if(g_race_qualifying == 2) - { - g_race_qualifying = 0; - independent_players = 0; - cvar_set("fraglimit", ftos(race_fraglimit)); - cvar_set("leadlimit", ftos(race_leadlimit)); - cvar_set("timelimit", ftos(race_timelimit)); - cts_ScoreRules(); - } -} - -MUTATOR_HOOKFUNCTION(cts, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - race_PreparePlayer(player); - player.race_checkpoint = -1; - - if(IS_REAL_CLIENT(player)) - { - string rr = CTS_RECORD; - - msg_entity = player; - race_send_recordtime(MSG_ONE); - race_send_speedaward(MSG_ONE); - - speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); - speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); - race_send_speedaward_alltimebest(MSG_ONE); - - float i; - for (i = 1; i <= RANKINGS_CNT; ++i) - { - race_SendRankings(i, 0, 0, MSG_ONE); - } - } -} - -MUTATOR_HOOKFUNCTION(cts, AbortSpeedrun) -{ - entity player = M_ARGV(0, entity); - - if(autocvar_g_allow_checkpoints) - race_PreparePlayer(player); // nice try -} - -MUTATOR_HOOKFUNCTION(cts, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - if(GameRules_scoring_add(player, RACE_FASTEST, 0)) - player.frags = FRAGS_LMS_LOSER; - else - player.frags = FRAGS_SPECTATOR; - - race_PreparePlayer(player); - player.race_checkpoint = -1; -} - -MUTATOR_HOOKFUNCTION(cts, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - - if(spawn_spot.target == "") - // Emergency: this wasn't a real spawnpoint. Can this ever happen? - race_PreparePlayer(player); - - // if we need to respawn, do it right - player.race_respawn_checkpoint = player.race_checkpoint; - player.race_respawn_spotref = spawn_spot; - - player.race_place = 0; -} - -MUTATOR_HOOKFUNCTION(cts, PutClientInServer) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - if(!game_stopped) - { - if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn - race_PreparePlayer(player); - else // respawn - race_RetractPlayer(player); - - race_AbandonRaceCheck(player); - } -} - -MUTATOR_HOOKFUNCTION(cts, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - frag_target.respawn_flags |= RESPAWN_FORCE; - race_AbandonRaceCheck(frag_target); -} - -MUTATOR_HOOKFUNCTION(cts, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - bot.havocbot_role = havocbot_role_cts; - return true; -} - -MUTATOR_HOOKFUNCTION(cts, GetPressedKeys) -{ - entity player = M_ARGV(0, entity); - - if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) - { - if (!player.stored_netname) - player.stored_netname = strzone(uid2name(player.crypto_idfp)); - if(player.stored_netname != player.netname) - { - db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); - strunzone(player.stored_netname); - player.stored_netname = strzone(player.netname); - } - } - - if (!IS_OBSERVER(player)) - { - if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed)) - { - speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1'); - speedaward_holder = player.netname; - speedaward_uid = player.crypto_idfp; - speedaward_lastupdate = time; - } - if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) - { - string rr = CTS_RECORD; - race_send_speedaward(MSG_ALL); - speedaward_lastsent = speedaward_speed; - if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") - { - speedaward_alltimebest = speedaward_speed; - speedaward_alltimebest_holder = speedaward_holder; - speedaward_alltimebest_uid = speedaward_uid; - db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest)); - db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid); - race_send_speedaward_alltimebest(MSG_ALL); - } - } - } -} - -MUTATOR_HOOKFUNCTION(cts, ForbidThrowCurrentWeapon) -{ - // no weapon dropping in CTS - return true; -} - -MUTATOR_HOOKFUNCTION(cts, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if (Item_IsLoot(item)) - { - return true; - } -} - -MUTATOR_HOOKFUNCTION(cts, Damage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - float frag_damage = M_ARGV(4, float); - - if(frag_target == frag_attacker || frag_deathtype == DEATH_FALL.m_id) - if(!autocvar_g_cts_selfdamage) - { - frag_damage = 0; - M_ARGV(4, float) = frag_damage; - } -} - -MUTATOR_HOOKFUNCTION(cts, ForbidPlayerScore_Clear) -{ - return true; // in CTS, you don't lose score by observing -} - -MUTATOR_HOOKFUNCTION(cts, GetRecords) -{ - int record_page = M_ARGV(0, int); - string ret_string = M_ARGV(1, string); - - for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) - { - if(MapInfo_Get_ByID(i)) - { - float r = race_readTime(MapInfo_Map_bspname, 1); - - if(!r) - continue; - - string h = race_readName(MapInfo_Map_bspname, 1); - ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n"); - } - } - - M_ARGV(1, string) = ret_string; -} - -void ClientKill_Now(entity this); -MUTATOR_HOOKFUNCTION(cts, ClientKill) -{ - entity player = M_ARGV(0, entity); - - M_ARGV(1, float) = 0; // kill delay - - if(player.killindicator && player.killindicator.health == 1) // player.killindicator.health == 1 means that the kill indicator was spawned by CTS_ClientKill - { - delete(player.killindicator); - player.killindicator = NULL; - - ClientKill_Now(player); // allow instant kill in this case - return; - } -} - -MUTATOR_HOOKFUNCTION(cts, Race_FinalCheckpoint) -{ - entity player = M_ARGV(0, entity); - - if(autocvar_g_cts_finish_kill_delay) - CTS_ClientKill(player); -} - -MUTATOR_HOOKFUNCTION(cts, HideTeamNagger) -{ - return true; // doesn't work so well (but isn't cts a teamless mode?) -} - -MUTATOR_HOOKFUNCTION(cts, FixClientCvars) -{ - entity player = M_ARGV(0, entity); - - stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n"); -} - -MUTATOR_HOOKFUNCTION(cts, WantWeapon) -{ - M_ARGV(1, float) = (M_ARGV(0, entity) == WEP_SHOTGUN); // want weapon = weapon info - M_ARGV(3, bool) = true; // want mutator blocked - return true; -} - -MUTATOR_HOOKFUNCTION(cts, ForbidDropCurrentWeapon) -{ - return true; -} - -void cts_Initialize() -{ - cts_ScoreRules(); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qh b/qcsrc/server/mutators/mutator/gamemode_cts.qh deleted file mode 100644 index c90919e6f4..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qh +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include "../gamemode.qh" -#include <server/race.qh> - -void cts_Initialize(); - -REGISTER_MUTATOR(cts, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - g_race_qualifying = true; - independent_players = 1; - GameRules_limit_score(0); - GameRules_limit_lead(0); - - cts_Initialize(); - } - return 0; -} - -// scores -const float ST_CTS_LAPS = 1; diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc deleted file mode 100644 index 9590027d3f..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc +++ /dev/null @@ -1,7 +0,0 @@ -#include "gamemode_deathmatch.qh" - -MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining) -{ - // announce remaining frags - return true; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh deleted file mode 100644 index f45b0800f3..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -REGISTER_MUTATOR(dm, false) -{ - MUTATOR_STATIC(); - return 0; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc deleted file mode 100644 index 38ef58b6c5..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ /dev/null @@ -1,670 +0,0 @@ -#include "gamemode_domination.qh" - -#include <server/teamplay.qh> - -bool g_domination; - -int autocvar_g_domination_default_teams; -bool autocvar_g_domination_disable_frags; -int autocvar_g_domination_point_amt; -bool autocvar_g_domination_point_fullbright; -float autocvar_g_domination_round_timelimit; -float autocvar_g_domination_warmup; -float autocvar_g_domination_point_rate; -int autocvar_g_domination_teams_override; - -void dom_EventLog(string mode, float team_before, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":dom:", mode, ":", ftos(team_before), ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); -} - -void set_dom_state(entity e) -{ - STAT(DOM_TOTAL_PPS, e) = total_pps; - STAT(DOM_PPS_RED, e) = pps_red; - STAT(DOM_PPS_BLUE, e) = pps_blue; - if(domination_teams >= 3) - STAT(DOM_PPS_YELLOW, e) = pps_yellow; - if(domination_teams >= 4) - STAT(DOM_PPS_PINK, e) = pps_pink; -} - -void dompoint_captured(entity this) -{ - float old_delay, old_team, real_team; - - // now that the delay has expired, switch to the latest team to lay claim to this point - entity head = this.owner; - - real_team = this.cnt; - this.cnt = -1; - - dom_EventLog("taken", this.team, this.dmg_inflictor); - this.dmg_inflictor = NULL; - - this.goalentity = head; - this.model = head.mdl; - this.modelindex = head.dmg; - this.skin = head.skin; - - float points, wait_time; - if (autocvar_g_domination_point_amt) - points = autocvar_g_domination_point_amt; - else - points = this.frags; - if (autocvar_g_domination_point_rate) - wait_time = autocvar_g_domination_point_rate; - else - wait_time = this.wait; - - if(domination_roundbased) - bprint(sprintf("^3%s^3%s\n", head.netname, this.message)); - else - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_DOMINATION_CAPTURE_TIME, head.netname, this.message, points, wait_time); - - if(this.enemy.playerid == this.enemy_playerid) - GameRules_scoring_add(this.enemy, DOM_TAKES, 1); - else - this.enemy = NULL; - - if (head.noise != "") - if(this.enemy) - _sound(this.enemy, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM); - else - _sound(this, CH_TRIGGER, head.noise, VOL_BASE, ATTEN_NORM); - if (head.noise1 != "") - play2all(head.noise1); - - this.delay = time + wait_time; - - // do trigger work - old_delay = this.delay; - old_team = this.team; - this.team = real_team; - this.delay = 0; - SUB_UseTargets (this, this, NULL); - this.delay = old_delay; - this.team = old_team; - - entity msg = WP_DomNeut; - switch(real_team) - { - case NUM_TEAM_1: msg = WP_DomRed; break; - case NUM_TEAM_2: msg = WP_DomBlue; break; - case NUM_TEAM_3: msg = WP_DomYellow; break; - case NUM_TEAM_4: msg = WP_DomPink; break; - } - - WaypointSprite_UpdateSprites(this.sprite, msg, WP_Null, WP_Null); - - total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; - IL_EACH(g_dompoints, true, - { - if (autocvar_g_domination_point_amt) - points = autocvar_g_domination_point_amt; - else - points = it.frags; - if (autocvar_g_domination_point_rate) - wait_time = autocvar_g_domination_point_rate; - else - wait_time = it.wait; - switch(it.goalentity.team) - { - case NUM_TEAM_1: pps_red += points/wait_time; break; - case NUM_TEAM_2: pps_blue += points/wait_time; break; - case NUM_TEAM_3: pps_yellow += points/wait_time; break; - case NUM_TEAM_4: pps_pink += points/wait_time; break; - } - total_pps += points/wait_time; - }); - - WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DOMPOINT, colormapPaletteColor(this.goalentity.team - 1, 0)); - WaypointSprite_Ping(this.sprite); - - this.captime = time; - - FOREACH_CLIENT(IS_REAL_CLIENT(it), { set_dom_state(it); }); -} - -void AnimateDomPoint(entity this) -{ - if(this.pain_finished > time) - return; - this.pain_finished = time + this.t_width; - if(this.nextthink > this.pain_finished) - this.nextthink = this.pain_finished; - - this.frame = this.frame + 1; - if(this.frame > this.t_length) - this.frame = 0; -} - -void dompointthink(entity this) -{ - float fragamt; - - this.nextthink = time + 0.1; - - //this.frame = this.frame + 1; - //if(this.frame > 119) - // this.frame = 0; - AnimateDomPoint(this); - - // give points - - if (game_stopped || this.delay > time || time < game_starttime) // game has ended, don't keep giving points - return; - - if(autocvar_g_domination_point_rate) - this.delay = time + autocvar_g_domination_point_rate; - else - this.delay = time + this.wait; - - // give credit to the team - // NOTE: this defaults to 0 - if (!domination_roundbased) - if (this.goalentity.netname != "") - { - if(autocvar_g_domination_point_amt) - fragamt = autocvar_g_domination_point_amt; - else - fragamt = this.frags; - TeamScore_AddToTeam(this.goalentity.team, ST_SCORE, fragamt); - TeamScore_AddToTeam(this.goalentity.team, ST_DOM_TICKS, fragamt); - - // give credit to the individual player, if he is still there - if (this.enemy.playerid == this.enemy_playerid) - { - GameRules_scoring_add(this.enemy, SCORE, fragamt); - GameRules_scoring_add(this.enemy, DOM_TICKS, fragamt); - } - else - this.enemy = NULL; - } -} - -void dompointtouch(entity this, entity toucher) -{ - if (!IS_PLAYER(toucher)) - return; - if (toucher.health < 1) - return; - - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) - return; - - if(time < this.captime + 0.3) - return; - - // only valid teams can claim it - entity head = find(NULL, classname, "dom_team"); - while (head && head.team != toucher.team) - head = find(head, classname, "dom_team"); - if (!head || head.netname == "" || head == this.goalentity) - return; - - // delay capture - - this.team = this.goalentity.team; // this stores the PREVIOUS team! - - this.cnt = toucher.team; - this.owner = head; // team to switch to after the delay - this.dmg_inflictor = toucher; - - // this.state = 1; - // this.delay = time + cvar("g_domination_point_capturetime"); - //this.nextthink = time + cvar("g_domination_point_capturetime"); - //this.think = dompoint_captured; - - // go to neutral team in the mean time - head = find(NULL, classname, "dom_team"); - while (head && head.netname != "") - head = find(head, classname, "dom_team"); - if(head == NULL) - return; - - WaypointSprite_UpdateSprites(this.sprite, WP_DomNeut, WP_Null, WP_Null); - WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DOMPOINT, '0 1 1'); - WaypointSprite_Ping(this.sprite); - - this.goalentity = head; - this.model = head.mdl; - this.modelindex = head.dmg; - this.skin = head.skin; - - this.enemy = toucher; // individual player scoring - this.enemy_playerid = toucher.playerid; - dompoint_captured(this); -} - -void dom_controlpoint_setup(entity this) -{ - entity head; - // find the spawnfunc_dom_team representing unclaimed points - head = find(NULL, classname, "dom_team"); - while(head && head.netname != "") - head = find(head, classname, "dom_team"); - if (!head) - objerror(this, "no spawnfunc_dom_team with netname \"\" found\n"); - - // copy important properties from spawnfunc_dom_team entity - this.goalentity = head; - _setmodel(this, head.mdl); // precision already set - this.skin = head.skin; - - this.cnt = -1; - - if(this.message == "") - this.message = " has captured a control point"; - - if(this.frags <= 0) - this.frags = 1; - if(this.wait <= 0) - this.wait = 5; - - float points, waittime; - if (autocvar_g_domination_point_amt) - points = autocvar_g_domination_point_amt; - else - points = this.frags; - if (autocvar_g_domination_point_rate) - waittime = autocvar_g_domination_point_rate; - else - waittime = this.wait; - - total_pps += points/waittime; - - if(!this.t_width) - this.t_width = 0.02; // frame animation rate - if(!this.t_length) - this.t_length = 239; // maximum frame - - setthink(this, dompointthink); - this.nextthink = time; - settouch(this, dompointtouch); - this.solid = SOLID_TRIGGER; - if(!this.flags & FL_ITEM) - IL_PUSH(g_items, this); - this.flags = FL_ITEM; - setsize(this, '-32 -32 -32', '32 32 32'); - setorigin(this, this.origin + '0 0 20'); - droptofloor(this); - - waypoint_spawnforitem(this); - WaypointSprite_SpawnFixed(WP_DomNeut, this.origin + '0 0 32', this, sprite, RADARICON_DOMPOINT); -} - -float total_controlpoints; -void Domination_count_controlpoints() -{ - total_controlpoints = redowned = blueowned = yellowowned = pinkowned = 0; - IL_EACH(g_dompoints, true, - { - ++total_controlpoints; - redowned += (it.goalentity.team == NUM_TEAM_1); - blueowned += (it.goalentity.team == NUM_TEAM_2); - yellowowned += (it.goalentity.team == NUM_TEAM_3); - pinkowned += (it.goalentity.team == NUM_TEAM_4); - }); -} - -float Domination_GetWinnerTeam() -{ - float winner_team = 0; - if(redowned == total_controlpoints) - winner_team = NUM_TEAM_1; - if(blueowned == total_controlpoints) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowowned == total_controlpoints) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkowned == total_controlpoints) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) - return winner_team; - return -1; // no control points left? -} - -#define DOM_OWNED_CONTROLPOINTS() ((redowned > 0) + (blueowned > 0) + (yellowowned > 0) + (pinkowned > 0)) -#define DOM_OWNED_CONTROLPOINTS_OK() (DOM_OWNED_CONTROLPOINTS() < total_controlpoints) -float Domination_CheckWinner() -{ - if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); - - game_stopped = true; - round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); - return 1; - } - - Domination_count_controlpoints(); - - float winner_team = Domination_GetWinnerTeam(); - - if(winner_team == -1) - return 0; - - if(winner_team > 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); - TeamScore_AddToTeam(winner_team, ST_DOM_CAPS, +1); - } - else if(winner_team == -1) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); - } - - game_stopped = true; - round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); - - return 1; -} - -float Domination_CheckPlayers() -{ - return 1; -} - -void Domination_RoundStart() -{ - FOREACH_CLIENT(IS_PLAYER(it), { it.player_blocked = false; }); -} - -//go to best items, or control points you don't own -void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) -{ - IL_EACH(g_dompoints, vdist((((it.absmin + it.absmax) * 0.5) - org), <, sradius), - { - if(it.cnt > -1) // this is just being fought - navigation_routerating(this, it, ratingscale, 5000); - else if(it.goalentity.cnt == 0) // unclaimed - navigation_routerating(this, it, ratingscale * 0.5, 5000); - else if(it.goalentity.team != this.team) // other team's point - navigation_routerating(this, it, ratingscale * 0.2, 5000); - }); -} - -void havocbot_role_dom(entity this) -{ - if(IS_DEAD(this)) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_controlpoints(this, 10000, this.origin, 15000); - havocbot_goalrating_items(this, 8000, this.origin, 8000); - //havocbot_goalrating_enemyplayers(this, 3000, this.origin, 2000); - havocbot_goalrating_waypoints(this, 1, this.origin, 3000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -MUTATOR_HOOKFUNCTION(dom, CheckAllowedTeams) -{ - // fallback? - M_ARGV(0, float) = domination_teams; - string ret_string = "dom_team"; - - entity head = find(NULL, classname, ret_string); - while(head) - { - if(head.netname != "") - { - switch(head.team) - { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; - } - } - - head = find(head, classname, ret_string); - } - - M_ARGV(1, string) = string_null; - - return true; -} - -MUTATOR_HOOKFUNCTION(dom, reset_map_players) -{ - total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - PutClientInServer(it); - if(domination_roundbased) - it.player_blocked = 1; - if(IS_REAL_CLIENT(it)) - set_dom_state(it); - }); - return true; -} - -MUTATOR_HOOKFUNCTION(dom, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(domination_roundbased) - if(!round_handler_IsRoundStarted()) - player.player_blocked = 1; - else - player.player_blocked = 0; -} - -MUTATOR_HOOKFUNCTION(dom, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - set_dom_state(player); -} - -MUTATOR_HOOKFUNCTION(dom, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - bot.havocbot_role = havocbot_role_dom; - return true; -} - -/*QUAKED spawnfunc_dom_controlpoint (0 .5 .8) (-16 -16 -24) (16 16 32) -Control point for Domination gameplay. -*/ -spawnfunc(dom_controlpoint) -{ - if(!g_domination) - { - delete(this); - return; - } - setthink(this, dom_controlpoint_setup); - this.nextthink = time + 0.1; - this.reset = dom_controlpoint_setup; - - if(!this.scale) - this.scale = 0.6; - - this.effects = this.effects | EF_LOWPRECISION; - if (autocvar_g_domination_point_fullbright) - this.effects |= EF_FULLBRIGHT; - - IL_PUSH(g_dompoints, this); -} - -/*QUAKED spawnfunc_dom_team (0 .5 .8) (-32 -32 -24) (32 32 32) -Team declaration for Domination gameplay, this allows you to decide what team -names and control point models are used in your map. - -Note: If you use spawnfunc_dom_team entities you must define at least 3 and only two -can have netname set! The nameless team owns all control points at start. - -Keys: -"netname" - Name of the team (for example Red Team, Blue Team, Green Team, Yellow Team, Life, Death, etc) -"cnt" - Scoreboard color of the team (for example 4 is red and 13 is blue) -"model" - Model to use for control points owned by this team (for example - "progs/b_g_key.mdl" is a gold keycard, and "progs/b_s_key.mdl" is a silver - keycard) -"skin" - Skin of the model to use (for team skins on a single model) -"noise" - Sound to play when this team captures a point. - (this is a localized sound, like a small alarm or other effect) -"noise1" - Narrator speech to play when this team captures a point. - (this is a global sound, like "Red team has captured a control point") -*/ - -spawnfunc(dom_team) -{ - if(!g_domination || autocvar_g_domination_teams_override >= 2) - { - delete(this); - return; - } - precache_model(this.model); - if (this.noise != "") - precache_sound(this.noise); - if (this.noise1 != "") - precache_sound(this.noise1); - this.classname = "dom_team"; - _setmodel(this, this.model); // precision not needed - this.mdl = this.model; - this.dmg = this.modelindex; - this.model = ""; - this.modelindex = 0; - // this would have to be changed if used in quakeworld - if(this.cnt) - this.team = this.cnt + 1; // WHY are these different anyway? -} - -// scoreboard setup -void ScoreRules_dom(int teams) -{ - if(domination_roundbased) - { - GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, 0, { - field_team(ST_DOM_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); - field(SP_DOM_TAKES, "takes", 0); - }); - } - else - { - float sp_domticks, sp_score; - sp_score = sp_domticks = 0; - if(autocvar_g_domination_disable_frags) - sp_domticks = SFL_SORT_PRIO_PRIMARY; - else - sp_score = SFL_SORT_PRIO_PRIMARY; - GameRules_scoring(teams, sp_score, sp_score, { - field_team(ST_DOM_TICKS, "ticks", sp_domticks); - field(SP_DOM_TICKS, "ticks", sp_domticks); - field(SP_DOM_TAKES, "takes", 0); - }); - } -} - -// code from here on is just to support maps that don't have control point and team entities -void dom_spawnteam (string teamname, float teamcolor, string pointmodel, float pointskin, Sound capsound, string capnarration, string capmessage) -{ - TC(Sound, capsound); - entity e = new_pure(dom_team); - e.netname = strzone(teamname); - e.cnt = teamcolor; - e.model = pointmodel; - e.skin = pointskin; - e.noise = strzone(Sound_fixpath(capsound)); - e.noise1 = strzone(capnarration); - e.message = strzone(capmessage); - - // this code is identical to spawnfunc_dom_team - _setmodel(e, e.model); // precision not needed - e.mdl = e.model; - e.dmg = e.modelindex; - e.model = ""; - e.modelindex = 0; - // this would have to be changed if used in quakeworld - e.team = e.cnt + 1; - - //eprint(e); -} - -void dom_spawnpoint(vector org) -{ - entity e = spawn(); - e.classname = "dom_controlpoint"; - setthink(e, spawnfunc_dom_controlpoint); - e.nextthink = time; - setorigin(e, org); - spawnfunc_dom_controlpoint(e); -} - -// spawn some default teams if the map is not set up for domination -void dom_spawnteams(int teams) -{ - TC(int, teams); - dom_spawnteam(Team_ColoredFullName(NUM_TEAM_1), NUM_TEAM_1-1, "models/domination/dom_red.md3", 0, SND_DOM_CLAIM, "", "Red team has captured a control point"); - dom_spawnteam(Team_ColoredFullName(NUM_TEAM_2), NUM_TEAM_2-1, "models/domination/dom_blue.md3", 0, SND_DOM_CLAIM, "", "Blue team has captured a control point"); - if(teams >= 3) - dom_spawnteam(Team_ColoredFullName(NUM_TEAM_3), NUM_TEAM_3-1, "models/domination/dom_yellow.md3", 0, SND_DOM_CLAIM, "", "Yellow team has captured a control point"); - if(teams >= 4) - dom_spawnteam(Team_ColoredFullName(NUM_TEAM_4), NUM_TEAM_4-1, "models/domination/dom_pink.md3", 0, SND_DOM_CLAIM, "", "Pink team has captured a control point"); - dom_spawnteam("", 0, "models/domination/dom_unclaimed.md3", 0, SND_Null, "", ""); -} - -void dom_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. -{ - // if no teams are found, spawn defaults - if(find(NULL, classname, "dom_team") == NULL || autocvar_g_domination_teams_override >= 2) - { - LOG_TRACE("No \"dom_team\" entities found on this map, creating them anyway."); - domination_teams = autocvar_g_domination_teams_override; - if (domination_teams < 2) - domination_teams = autocvar_g_domination_default_teams; - domination_teams = bound(2, domination_teams, 4); - dom_spawnteams(domination_teams); - } - - CheckAllowedTeams(NULL); - //domination_teams = ((c4>=0) ? 4 : (c3>=0) ? 3 : 2); - - int teams = 0; - if(c1 >= 0) teams |= BIT(0); - if(c2 >= 0) teams |= BIT(1); - if(c3 >= 0) teams |= BIT(2); - if(c4 >= 0) teams |= BIT(3); - domination_teams = teams; - - domination_roundbased = autocvar_g_domination_roundbased; - - ScoreRules_dom(domination_teams); - - if(domination_roundbased) - { - round_handler_Spawn(Domination_CheckPlayers, Domination_CheckWinner, Domination_RoundStart); - round_handler_Init(5, autocvar_g_domination_warmup, autocvar_g_domination_round_timelimit); - } -} - -void dom_Initialize() -{ - g_domination = true; - InitializeEntity(NULL, dom_DelayedInit, INITPRIO_GAMETYPE); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qh b/qcsrc/server/mutators/mutator/gamemode_domination.qh deleted file mode 100644 index 95311c98a3..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qh +++ /dev/null @@ -1,53 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -#define autocvar_g_domination_point_limit cvar("g_domination_point_limit") -bool autocvar_g_domination_roundbased; -int autocvar_g_domination_roundbased_point_limit; -int autocvar_g_domination_point_leadlimit; - -void dom_Initialize(); - -REGISTER_MUTATOR(dom, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - int fraglimit_override = autocvar_g_domination_point_limit; - if (autocvar_g_domination_roundbased && autocvar_g_domination_roundbased_point_limit) - fraglimit_override = autocvar_g_domination_roundbased_point_limit; - - GameRules_teams(true); - GameRules_limit_score(fraglimit_override); - GameRules_limit_lead(autocvar_g_domination_point_leadlimit); - - dom_Initialize(); - } - return 0; -} - -// score rule declarations -const float ST_DOM_TICKS = 1; -const float ST_DOM_CAPS = 1; - -// pps: points per second -float total_pps; -float pps_red; -float pps_blue; -float pps_yellow; -float pps_pink; - -// capture declarations -.float enemy_playerid; -.entity sprite; -.float captime; - -// misc globals -float domination_roundbased; -float domination_teams; - -void AnimateDomPoint(entity this); - -IntrusiveList g_dompoints; -STATIC_INIT(g_dompoints) { g_dompoints = IL_NEW(); } diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc deleted file mode 100644 index 36546c43a0..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc +++ /dev/null @@ -1,585 +0,0 @@ -#include "gamemode_freezetag.qh" - -float autocvar_g_freezetag_frozen_maxtime; -float autocvar_g_freezetag_revive_clearspeed; -float autocvar_g_freezetag_round_timelimit; -//int autocvar_g_freezetag_teams; -int autocvar_g_freezetag_teams_override; -float autocvar_g_freezetag_warmup; - -void freezetag_count_alive_players() -{ - total_players = redalive = bluealive = yellowalive = pinkalive = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - switch(it.team) - { - case NUM_TEAM_1: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++redalive; break; - case NUM_TEAM_2: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++bluealive; break; - case NUM_TEAM_3: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++yellowalive; break; - case NUM_TEAM_4: ++total_players; if(it.health >= 1 && STAT(FROZEN, it) != 1) ++pinkalive; break; - } - }); - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - STAT(REDALIVE, it) = redalive; - STAT(BLUEALIVE, it) = bluealive; - STAT(YELLOWALIVE, it) = yellowalive; - STAT(PINKALIVE, it) = pinkalive; - }); - - eliminatedPlayers.SendFlags |= 1; -} -#define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) -#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == NumTeams(freezetag_teams)) - -float freezetag_CheckTeams() -{ - static float prev_missing_teams_mask; - if(FREEZETAG_ALIVE_TEAMS_OK()) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - return 1; - } - if(total_players == 0) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - return 0; - } - int missing_teams_mask = 0; - if(freezetag_teams & BIT(0)) - missing_teams_mask += (!redalive) * 1; - if(freezetag_teams & BIT(1)) - missing_teams_mask += (!bluealive) * 2; - if(freezetag_teams & BIT(2)) - missing_teams_mask += (!yellowalive) * 4; - if(freezetag_teams & BIT(3)) - missing_teams_mask += (!pinkalive) * 8; - if(prev_missing_teams_mask != missing_teams_mask) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); - prev_missing_teams_mask = missing_teams_mask; - } - return 0; -} - -float freezetag_getWinnerTeam() -{ - float winner_team = 0; - if(redalive >= 1) - winner_team = NUM_TEAM_1; - if(bluealive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowalive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkalive >= 1) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_4; - } - if(winner_team) - return winner_team; - return -1; // no player left -} - -void nades_Clear(entity); -void nades_GiveBonus(entity player, float score); - -float freezetag_CheckWinner() -{ - if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); - FOREACH_CLIENT(IS_PLAYER(it), { - it.freezetag_frozen_timeout = 0; - nades_Clear(it); - }); - game_stopped = true; - round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); - return 1; - } - - if(FREEZETAG_ALIVE_TEAMS() > 1) - return 0; - - int winner_team = freezetag_getWinnerTeam(); - if(winner_team > 0) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); - TeamScore_AddToTeam(winner_team, ST_SCORE, +1); - } - else if(winner_team == -1) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_TIED); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_TIED); - } - - FOREACH_CLIENT(IS_PLAYER(it), { - it.freezetag_frozen_timeout = 0; - nades_Clear(it); - }); - - game_stopped = true; - round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); - return 1; -} - -entity freezetag_LastPlayerForTeam(entity this) -{ - entity last_pl = NULL; - FOREACH_CLIENT(IS_PLAYER(it) && it != this, { - if(it.health >= 1) - if(!STAT(FROZEN, it)) - if(SAME_TEAM(it, this)) - if(!last_pl) - last_pl = it; - else - return NULL; - }); - return last_pl; -} - -void freezetag_LastPlayerForTeam_Notify(entity this) -{ - if(round_handler_IsActive()) - if(round_handler_IsRoundStarted()) - { - entity pl = freezetag_LastPlayerForTeam(this); - if(pl) - Send_Notification(NOTIF_ONE, pl, MSG_CENTER, CENTER_ALONE); - } -} - -void freezetag_Add_Score(entity targ, entity attacker) -{ - if(attacker == targ) - { - // you froze your own dumb targ - // counted as "suicide" already - GameRules_scoring_add(targ, SCORE, -1); - } - else if(IS_PLAYER(attacker)) - { - // got frozen by an enemy - // counted as "kill" and "death" already - GameRules_scoring_add(targ, SCORE, -1); - GameRules_scoring_add(attacker, SCORE, +1); - } - // else nothing - got frozen by the game type rules themselves -} - -void freezetag_Freeze(entity targ, entity attacker) -{ - if(STAT(FROZEN, targ)) - return; - - if(autocvar_g_freezetag_frozen_maxtime > 0) - targ.freezetag_frozen_timeout = time + autocvar_g_freezetag_frozen_maxtime; - - Freeze(targ, 0, 1, true); - - freezetag_count_alive_players(); - - freezetag_Add_Score(targ, attacker); -} - -void freezetag_Unfreeze(entity this) -{ - this.freezetag_frozen_time = 0; - this.freezetag_frozen_timeout = 0; - - Unfreeze(this); -} - -float freezetag_isEliminated(entity e) -{ - if(IS_PLAYER(e) && (STAT(FROZEN, e) == 1 || IS_DEAD(e))) - return true; - return false; -} - - -// ================ -// Bot player logic -// ================ - -void(entity this) havocbot_role_ft_freeing; -void(entity this) havocbot_role_ft_offense; - -void havocbot_goalrating_freeplayers(entity this, float ratingscale, vector org, float sradius) -{ - float t; - FOREACH_CLIENT(IS_PLAYER(it) && it != this && SAME_TEAM(it, this), { - if (STAT(FROZEN, it) == 1) - { - if(vdist(it.origin - org, >, sradius)) - continue; - navigation_routerating(this, it, ratingscale, 2000); - } - else if(vdist(it.origin - org, >, 400)) // avoid gathering all teammates in one place - { - // If teamate is not frozen still seek them out as fight better - // in a group. - t = 0.2 * 150 / (this.health + this.armorvalue); - navigation_routerating(this, it, t * ratingscale, 2000); - } - }); -} - -void havocbot_role_ft_offense(entity this) -{ - if(IS_DEAD(this)) - return; - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + random() * 10 + 20; - - // Count how many players on team are unfrozen. - int unfrozen = 0; - FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(it, this) && !(STAT(FROZEN, it) != 1), { unfrozen++; }); - - // If only one left on team or if role has timed out then start trying to free players. - if (((unfrozen == 0) && (!STAT(FROZEN, this))) || (time > this.havocbot_role_timeout)) - { - LOG_TRACE("changing role to freeing"); - this.havocbot_role = havocbot_role_ft_freeing; - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_items(this, 10000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000); - havocbot_goalrating_freeplayers(this, 9000, this.origin, 10000); - havocbot_goalrating_waypoints(this, 1, this.origin, 3000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_ft_freeing(entity this) -{ - if(IS_DEAD(this)) - return; - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + random() * 10 + 20; - - if (time > this.havocbot_role_timeout) - { - LOG_TRACE("changing role to offense"); - this.havocbot_role = havocbot_role_ft_offense; - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_items(this, 8000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 10000, this.origin, 10000); - havocbot_goalrating_freeplayers(this, 20000, this.origin, 10000); - havocbot_goalrating_waypoints(this, 1, this.origin, 3000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - - -// ============== -// Hook Functions -// ============== - -void ft_RemovePlayer(entity this) -{ - this.health = 0; // neccessary to update correctly alive stats - if(!STAT(FROZEN, this)) - freezetag_LastPlayerForTeam_Notify(this); - freezetag_Unfreeze(this); - freezetag_count_alive_players(); -} - -MUTATOR_HOOKFUNCTION(ft, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - ft_RemovePlayer(player); - return true; -} - -MUTATOR_HOOKFUNCTION(ft, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - ft_RemovePlayer(player); -} - -MUTATOR_HOOKFUNCTION(ft, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - - if(round_handler_IsActive()) - if(round_handler_CountdownRunning()) - { - if(STAT(FROZEN, frag_target)) - freezetag_Unfreeze(frag_target); - freezetag_count_alive_players(); - return true; // let the player die so that he can respawn whenever he wants - } - - // Cases DEATH_TEAMCHANGE and DEATH_AUTOTEAMCHANGE are needed to fix a bug whe - // you succeed changing team through the menu: you both really die (gibbing) and get frozen - if(ITEM_DAMAGE_NEEDKILL(frag_deathtype) - || frag_deathtype == DEATH_TEAMCHANGE.m_id || frag_deathtype == DEATH_AUTOTEAMCHANGE.m_id) - { - // let the player die, he will be automatically frozen when he respawns - if(STAT(FROZEN, frag_target) != 1) - { - freezetag_Add_Score(frag_target, frag_attacker); - freezetag_count_alive_players(); - freezetag_LastPlayerForTeam_Notify(frag_target); - } - else - freezetag_Unfreeze(frag_target); // remove ice - frag_target.health = 0; // Unfreeze resets health - frag_target.freezetag_frozen_timeout = -2; // freeze on respawn - return true; - } - - if(STAT(FROZEN, frag_target)) - return true; - - freezetag_Freeze(frag_target, frag_attacker); - freezetag_LastPlayerForTeam_Notify(frag_target); - - if(frag_attacker == frag_target || frag_attacker == NULL) - { - if(IS_PLAYER(frag_target)) - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_SELF); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_SELF, frag_target.netname); - } - else - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ft, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(player.freezetag_frozen_timeout == -1) // if PlayerSpawn is called by reset_map_players - return true; // do nothing, round is starting right now - - if(player.freezetag_frozen_timeout == -2) // player was dead - { - freezetag_Freeze(player, NULL); - return true; - } - - freezetag_count_alive_players(); - - if(round_handler_IsActive()) - if(round_handler_IsRoundStarted()) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_SPAWN_LATE); - freezetag_Freeze(player, NULL); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ft, reset_map_players) -{ - FOREACH_CLIENT(IS_PLAYER(it), { - CS(it).killcount = 0; - it.freezetag_frozen_timeout = -1; - PutClientInServer(it); - it.freezetag_frozen_timeout = 0; - }); - freezetag_count_alive_players(); - return true; -} - -MUTATOR_HOOKFUNCTION(ft, GiveFragsForKill, CBC_ORDER_FIRST) -{ - M_ARGV(2, float) = 0; // no frags counted in Freeze Tag - return true; -} - -MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) -{ - if(game_stopped) - return true; - - if(round_handler_IsActive()) - if(!round_handler_IsRoundStarted()) - return true; - - int n; - entity o = NULL; - entity player = M_ARGV(0, entity); - //if(STAT(FROZEN, player)) - //if(player.freezetag_frozen_timeout > 0 && time < player.freezetag_frozen_timeout) - //player.iceblock.alpha = ICE_MIN_ALPHA + (ICE_MAX_ALPHA - ICE_MIN_ALPHA) * (player.freezetag_frozen_timeout - time) / (player.freezetag_frozen_timeout - player.freezetag_frozen_time); - - if(player.freezetag_frozen_timeout > 0 && time >= player.freezetag_frozen_timeout) - n = -1; - else - { - vector revive_extra_size = '1 1 1' * autocvar_g_freezetag_revive_extra_size; - n = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it != player, { - if(STAT(FROZEN, it) == 0) - if(!IS_DEAD(it)) - if(SAME_TEAM(it, player)) - if(boxesoverlap(player.absmin - revive_extra_size, player.absmax + revive_extra_size, it.absmin, it.absmax)) - { - if(!o) - o = it; - if(STAT(FROZEN, player) == 1) - it.reviving = true; - ++n; - } - }); - - } - - if(n && STAT(FROZEN, player) == 1) // OK, there is at least one teammate reviving us - { - STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) + frametime * max(1/60, autocvar_g_freezetag_revive_speed), 1); - player.health = max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)); - - if(STAT(REVIVE_PROGRESS, player) >= 1) - { - freezetag_Unfreeze(player); - freezetag_count_alive_players(); - - if(n == -1) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_AUTO_REVIVED, autocvar_g_freezetag_frozen_maxtime); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_AUTO_REVIVED, player.netname, autocvar_g_freezetag_frozen_maxtime); - return true; - } - - // EVERY team mate nearby gets a point (even if multiple!) - FOREACH_CLIENT(IS_PLAYER(it) && it.reviving, { - GameRules_scoring_add(it, FREEZETAG_REVIVALS, +1); - GameRules_scoring_add(it, SCORE, +1); - nades_GiveBonus(it,autocvar_g_nades_bonus_score_low); - }); - - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_FREEZETAG_REVIVED, o.netname); - Send_Notification(NOTIF_ONE, o, MSG_CENTER, CENTER_FREEZETAG_REVIVE, player.netname); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_REVIVED, player.netname, o.netname); - } - - FOREACH_CLIENT(IS_PLAYER(it) && it.reviving, { - STAT(REVIVE_PROGRESS, it) = STAT(REVIVE_PROGRESS, player); - it.reviving = false; - }); - } - else if(!n && STAT(FROZEN, player) == 1) // only if no teammate is nearby will we reset - { - STAT(REVIVE_PROGRESS, player) = bound(0, STAT(REVIVE_PROGRESS, player) - frametime * autocvar_g_freezetag_revive_clearspeed, 1); - player.health = max(1, STAT(REVIVE_PROGRESS, player) * ((warmup_stage) ? warmup_start_health : start_health)); - } - else if(!n && !STAT(FROZEN, player)) - { - STAT(REVIVE_PROGRESS, player) = 0; // thawing nobody - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ft, SetStartItems) -{ - start_items &= ~IT_UNLIMITED_AMMO; - //start_health = warmup_start_health = cvar("g_lms_start_health"); - //start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); - start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); - start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); - start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); - start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); - start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); - start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); -} - -MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - if (!IS_DEAD(bot)) - { - if (random() < 0.5) - bot.havocbot_role = havocbot_role_ft_freeing; - else - bot.havocbot_role = havocbot_role_ft_offense; - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ft, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(0, float) = freezetag_teams; -} - -MUTATOR_HOOKFUNCTION(ft, SetWeaponArena) -{ - // most weapons arena - if(M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") - M_ARGV(0, string) = "most"; -} - -MUTATOR_HOOKFUNCTION(ft, FragCenterMessage) -{ - entity frag_attacker = M_ARGV(0, entity); - entity frag_target = M_ARGV(1, entity); - //float frag_deathtype = M_ARGV(2, float); - int kill_count_to_attacker = M_ARGV(3, int); - int kill_count_to_target = M_ARGV(4, int); - - if(STAT(FROZEN, frag_target)) - return; // target was already frozen, so this is just pushing them off the cliff - - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CHOICE, CHOICE_FRAG_FREEZE, frag_target.netname, kill_count_to_attacker, (IS_BOT_CLIENT(frag_target) ? -1 : CS(frag_target).ping)); - Send_Notification(NOTIF_ONE, frag_target, MSG_CHOICE, CHOICE_FRAGGED_FREEZE, frag_attacker.netname, kill_count_to_target, frag_attacker.health, frag_attacker.armorvalue, (IS_BOT_CLIENT(frag_attacker) ? -1 : CS(frag_attacker).ping)); - - return true; -} - -void freezetag_Initialize() -{ - freezetag_teams = autocvar_g_freezetag_teams_override; - if(freezetag_teams < 2) - freezetag_teams = cvar("g_freezetag_teams"); // read the cvar directly as it gets written earlier in the same frame - - freezetag_teams = BITS(bound(2, freezetag_teams, 4)); - GameRules_scoring(freezetag_teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, { - field(SP_FREEZETAG_REVIVALS, "revivals", 0); - }); - - round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null); - round_handler_Init(5, autocvar_g_freezetag_warmup, autocvar_g_freezetag_round_timelimit); - - EliminatedPlayers_Init(freezetag_isEliminated); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh b/qcsrc/server/mutators/mutator/gamemode_freezetag.qh deleted file mode 100644 index a258d82eab..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -int autocvar_g_freezetag_point_limit; -int autocvar_g_freezetag_point_leadlimit; -bool autocvar_g_freezetag_team_spawns; -void freezetag_Initialize(); - -REGISTER_MUTATOR(ft, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - GameRules_spawning_teams(autocvar_g_freezetag_team_spawns); - GameRules_limit_score(autocvar_g_freezetag_point_limit); - GameRules_limit_lead(autocvar_g_freezetag_point_leadlimit); - - freezetag_Initialize(); - } - return 0; -} - -.float freezetag_frozen_time; -.float freezetag_frozen_timeout; -const float ICE_MAX_ALPHA = 1; -const float ICE_MIN_ALPHA = 0.1; -float freezetag_teams; - -.float reviving; // temp var - -float autocvar_g_freezetag_revive_extra_size; -float autocvar_g_freezetag_revive_speed; -bool autocvar_g_freezetag_revive_nade; -float autocvar_g_freezetag_revive_nade_health; diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qc b/qcsrc/server/mutators/mutator/gamemode_invasion.qc deleted file mode 100644 index 1b8b77ae07..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc +++ /dev/null @@ -1,603 +0,0 @@ -#include "gamemode_invasion.qh" - -#include <common/monsters/sv_spawn.qh> -#include <common/monsters/sv_monsters.qh> - -#include <server/teamplay.qh> - -IntrusiveList g_invasion_roundends; -STATIC_INIT(g_invasion_roundends) { g_invasion_roundends = IL_NEW(); } - -IntrusiveList g_invasion_waves; -STATIC_INIT(g_invasion_waves) { g_invasion_waves = IL_NEW(); } - -IntrusiveList g_invasion_spawns; -STATIC_INIT(g_invasion_spawns) { g_invasion_spawns = IL_NEW(); } - -float autocvar_g_invasion_round_timelimit; -float autocvar_g_invasion_spawnpoint_spawn_delay; -float autocvar_g_invasion_warmup; -int autocvar_g_invasion_monster_count; -bool autocvar_g_invasion_zombies_only; -float autocvar_g_invasion_spawn_delay; - -bool victent_present; -.bool inv_endreached; - -bool inv_warning_shown; // spammy - -.string spawnmob; - -void target_invasion_roundend_use(entity this, entity actor, entity trigger) -{ - if(!IS_PLAYER(actor)) { return; } - - actor.inv_endreached = true; - - int plnum = 0; - int realplnum = 0; - // let's not count bots - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), { - ++realplnum; - if(it.inv_endreached) - ++plnum; - }); - if(plnum < ceil(realplnum * min(1, this.count))) // 70% of players - return; - - this.winning = true; -} - -spawnfunc(target_invasion_roundend) -{ - if(!g_invasion) { delete(this); return; } - - victent_present = true; // a victory entity is present, we don't need to rely on monster count TODO: merge this with the intrusive list (can check empty) - - if(!this.count) { this.count = 0.7; } // require at least 70% of the players to reach the end before triggering victory - - this.use = target_invasion_roundend_use; - - IL_PUSH(g_invasion_roundends, this); -} - -spawnfunc(invasion_wave) -{ - if(!g_invasion) { delete(this); return; } - - IL_PUSH(g_invasion_waves, this); -} - -spawnfunc(invasion_spawnpoint) -{ - if(!g_invasion) { delete(this); return; } - - this.classname = "invasion_spawnpoint"; - IL_PUSH(g_invasion_spawns, this); -} - -void ClearWinners(); - -// Invasion stage mode winning condition: If the attackers triggered a round end (by fulfilling all objectives) -// they win. -int WinningCondition_Invasion() -{ - WinningConditionHelper(NULL); // set worldstatus - - int status = WINNING_NO; - - if(autocvar_g_invasion_type == INV_TYPE_STAGE) - { - SetWinners(inv_endreached, true); - - int found = 0; - IL_EACH(g_invasion_roundends, true, - { - ++found; - if(it.winning) - { - bprint("Invasion: round completed.\n"); - // winners already set (TODO: teamplay support) - - status = WINNING_YES; - break; - } - }); - - if(!found) - status = WINNING_YES; // just end it? TODO: should warn mapper! - } - else if(autocvar_g_invasion_type == INV_TYPE_HUNT) - { - ClearWinners(); - - int found = 0; // NOTE: this ends the round if no monsters are placed - IL_EACH(g_monsters, !(it.spawnflags & MONSTERFLAG_RESPAWNED), - { - ++found; - }); - - if(found <= 0) - { - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), - { - it.winning = true; - }); - status = WINNING_YES; - } - } - - return status; -} - -Monster invasion_PickMonster(int supermonster_count) -{ - RandomSelection_Init(); - - FOREACH(Monsters, it != MON_Null, - { - if((it.spawnflags & MON_FLAG_HIDDEN) || (it.spawnflags & MONSTER_TYPE_PASSIVE) || (it.spawnflags & MONSTER_TYPE_FLY) || (it.spawnflags & MONSTER_TYPE_SWIM) || - (it.spawnflags & MONSTER_SIZE_QUAKE) || ((it.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1)) - continue; - if(autocvar_g_invasion_zombies_only && !(it.spawnflags & MONSTER_TYPE_UNDEAD)) - continue; - RandomSelection_AddEnt(it, 1, 1); - }); - - return RandomSelection_chosen_ent; -} - -entity invasion_PickSpawn() -{ - RandomSelection_Init(); - - IL_EACH(g_invasion_spawns, true, - { - RandomSelection_AddEnt(it, 1, ((time < it.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating - it.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay; - }); - - return RandomSelection_chosen_ent; -} - -entity invasion_GetWaveEntity(int wavenum) -{ - IL_EACH(g_invasion_waves, it.cnt == wavenum, - { - return it; // found one - }); - - // if no specific one is found, find the last existing wave ent - entity best = NULL; - IL_EACH(g_invasion_waves, it.cnt <= wavenum, - { - if(!best || it.cnt > best.cnt) - best = it; - }); - - return best; -} - -void invasion_SpawnChosenMonster(Monster mon) -{ - entity monster; - entity spawn_point = invasion_PickSpawn(); - entity wave_ent = invasion_GetWaveEntity(inv_roundcnt); - - string tospawn = ""; - if(wave_ent && wave_ent.spawnmob && wave_ent.spawnmob != "") - { - RandomSelection_Init(); - FOREACH_WORD(wave_ent.spawnmob, true, - { - RandomSelection_AddString(it, 1, 1); - }); - - tospawn = RandomSelection_chosen_string; - } - - if(spawn_point == NULL) - { - if(!inv_warning_shown) - { - inv_warning_shown = true; - LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations"); - } - entity e = spawn(); - setsize(e, mon.m_mins, mon.m_maxs); - - if(MoveToRandomMapLocation(e, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) - monster = spawnmonster(e, tospawn, mon.monsterid, NULL, NULL, e.origin, false, false, 2); - else - { - delete(e); - return; - } - } - else // if spawnmob field falls through (unset), fallback to mon (relying on spawnmonster for that behaviour) - monster = spawnmonster(spawn(), ((spawn_point.spawnmob && spawn_point.spawnmob != "") ? spawn_point.spawnmob : tospawn), mon.monsterid, spawn_point, spawn_point, spawn_point.origin, false, false, 2); - - if(!monster) - return; - - monster.spawnshieldtime = time; - - if(spawn_point) - { - if(spawn_point.target_range) - monster.target_range = spawn_point.target_range; - monster.target2 = spawn_point.target2; - } - - if(teamplay) - { - if(spawn_point && spawn_point.team && inv_monsters_perteam[spawn_point.team] > 0) - monster.team = spawn_point.team; - else - { - RandomSelection_Init(); - if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_AddFloat(NUM_TEAM_1, 1, 1); - if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_AddFloat(NUM_TEAM_2, 1, 1); - if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_AddFloat(NUM_TEAM_3, 1, 1); } - if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_AddFloat(NUM_TEAM_4, 1, 1); } - - monster.team = RandomSelection_chosen_float; - } - - monster_setupcolors(monster); - - if(monster.sprite) - { - WaypointSprite_UpdateTeamRadar(monster.sprite, RADARICON_DANGER, ((monster.team) ? Team_ColorRGB(monster.team) : '1 0 0')); - - monster.sprite.team = 0; - monster.sprite.SendFlags |= 1; - } - } - - if(monster.monster_attack) - IL_REMOVE(g_monster_targets, monster); - monster.monster_attack = false; // it's the player's job to kill all the monsters - - if(inv_roundcnt >= inv_maxrounds) - monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses -} - -void invasion_SpawnMonsters(int supermonster_count) -{ - Monster chosen_monster = invasion_PickMonster(supermonster_count); - - invasion_SpawnChosenMonster(chosen_monster); -} - -bool Invasion_CheckWinner() -{ - if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) - { - IL_EACH(g_monsters, true, - { - Monster_Remove(it); - }); - IL_CLEAR(g_monsters); - - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_OVER); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_OVER); - round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - return 1; - } - - float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0; - - IL_EACH(g_monsters, it.health > 0, - { - if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) - ++supermonster_count; - ++total_alive_monsters; - - if(teamplay) - switch(it.team) - { - case NUM_TEAM_1: ++red_alive; break; - case NUM_TEAM_2: ++blue_alive; break; - case NUM_TEAM_3: ++yellow_alive; break; - case NUM_TEAM_4: ++pink_alive; break; - } - }); - - if((total_alive_monsters + inv_numkilled) < inv_maxspawned && inv_maxcurrent < inv_maxspawned) - { - if(time >= inv_lastcheck) - { - invasion_SpawnMonsters(supermonster_count); - inv_lastcheck = time + autocvar_g_invasion_spawn_delay; - } - - return 0; - } - - if(inv_numspawned < 1) - return 0; // nothing has spawned yet - - if(teamplay) - { - if(((red_alive > 0) + (blue_alive > 0) + (yellow_alive > 0) + (pink_alive > 0)) > 1) - return 0; - } - else if(inv_numkilled < inv_maxspawned) - return 0; - - entity winner = NULL; - float winning_score = 0, winner_team = 0; - - - if(teamplay) - { - if(red_alive > 0) { winner_team = NUM_TEAM_1; } - if(blue_alive > 0) - if(winner_team) { winner_team = 0; } - else { winner_team = NUM_TEAM_2; } - if(yellow_alive > 0) - if(winner_team) { winner_team = 0; } - else { winner_team = NUM_TEAM_3; } - if(pink_alive > 0) - if(winner_team) { winner_team = 0; } - else { winner_team = NUM_TEAM_4; } - } - else - { - FOREACH_CLIENT(IS_PLAYER(it), { - float cs = GameRules_scoring_add(it, KILLS, 0); - if(cs > winning_score) - { - winning_score = cs; - winner = it; - } - }); - } - - IL_EACH(g_monsters, true, - { - Monster_Remove(it); - }); - IL_CLEAR(g_monsters); - - if(teamplay) - { - if(winner_team) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_ROUND_TEAM_WIN)); - } - } - else if(winner) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_ROUND_PLAYER_WIN, winner.netname); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ROUND_PLAYER_WIN, winner.netname); - } - - round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - - return 1; -} - -bool Invasion_CheckPlayers() -{ - return true; -} - -void Invasion_RoundStart() -{ - int numplayers = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - it.player_blocked = false; - ++numplayers; - }); - - if(inv_roundcnt < inv_maxrounds) - inv_roundcnt += 1; // a limiter to stop crazy counts - - inv_monsterskill = inv_roundcnt + max(1, numplayers * 0.3); - - inv_maxcurrent = 0; - inv_numspawned = 0; - inv_numkilled = 0; - - inv_maxspawned = rint(max(autocvar_g_invasion_monster_count, autocvar_g_invasion_monster_count * (inv_roundcnt * 0.5))); - - if(teamplay) - { - DistributeEvenly_Init(inv_maxspawned, invasion_teams); - inv_monsters_perteam[NUM_TEAM_1] = DistributeEvenly_Get(1); - inv_monsters_perteam[NUM_TEAM_2] = DistributeEvenly_Get(1); - if(invasion_teams >= 3) inv_monsters_perteam[NUM_TEAM_3] = DistributeEvenly_Get(1); - if(invasion_teams >= 4) inv_monsters_perteam[NUM_TEAM_4] = DistributeEvenly_Get(1); - } -} - -MUTATOR_HOOKFUNCTION(inv, MonsterDies) -{ - entity frag_target = M_ARGV(0, entity); - entity frag_attacker = M_ARGV(1, entity); - - if(!(frag_target.spawnflags & MONSTERFLAG_RESPAWNED)) - { - if(autocvar_g_invasion_type == INV_TYPE_ROUND) - { - inv_numkilled += 1; - inv_maxcurrent -= 1; - } - if(teamplay) { inv_monsters_perteam[frag_target.team] -= 1; } - - if(IS_PLAYER(frag_attacker)) - if(SAME_TEAM(frag_attacker, frag_target)) // in non-teamplay modes, same team = same player, so this works - GameRules_scoring_add(frag_attacker, KILLS, -1); - else - { - GameRules_scoring_add(frag_attacker, KILLS, +1); - if(teamplay) - TeamScore_AddToTeam(frag_attacker.team, ST_INV_KILLS, +1); - } - } -} - -MUTATOR_HOOKFUNCTION(inv, MonsterSpawn) -{ - entity mon = M_ARGV(0, entity); - mon.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP; - - if(autocvar_g_invasion_type == INV_TYPE_HUNT) - return false; // allowed - - if(!(mon.spawnflags & MONSTERFLAG_SPAWNED)) - return true; - - if(!(mon.spawnflags & MONSTERFLAG_RESPAWNED)) - { - inv_numspawned += 1; - inv_maxcurrent += 1; - } - - mon.monster_skill = inv_monsterskill; - - if((get_monsterinfo(mon.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_INVASION_SUPERMONSTER, mon.monster_name); -} - -MUTATOR_HOOKFUNCTION(inv, SV_StartFrame) -{ - if(autocvar_g_invasion_type != INV_TYPE_ROUND) - return; // uses map spawned monsters - - monsters_total = inv_maxspawned; // TODO: make sure numspawned never exceeds maxspawned - monsters_killed = inv_numkilled; -} - -MUTATOR_HOOKFUNCTION(inv, PlayerRegen) -{ - // no regeneration in invasion, regardless of the game type - return true; -} - -MUTATOR_HOOKFUNCTION(inv, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(player.bot_attack) - IL_REMOVE(g_bot_targets, player); - player.bot_attack = false; -} - -MUTATOR_HOOKFUNCTION(inv, Damage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_damage = M_ARGV(4, float); - vector frag_force = M_ARGV(6, vector); - - if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target) && frag_attacker != frag_target) - { - frag_damage = 0; - frag_force = '0 0 0'; - - M_ARGV(4, float) = frag_damage; - M_ARGV(6, vector) = frag_force; - } -} - -MUTATOR_HOOKFUNCTION(inv, BotShouldAttack) -{ - entity targ = M_ARGV(1, entity); - - if(!IS_MONSTER(targ)) - return true; -} - -MUTATOR_HOOKFUNCTION(inv, SetStartItems) -{ - if(autocvar_g_invasion_type == INV_TYPE_ROUND) - { - start_health = 200; - start_armorvalue = 200; - } -} - -MUTATOR_HOOKFUNCTION(inv, AccuracyTargetValid) -{ - entity frag_target = M_ARGV(1, entity); - - if(IS_MONSTER(frag_target)) - return MUT_ACCADD_INVALID; - return MUT_ACCADD_INDIFFERENT; -} - -MUTATOR_HOOKFUNCTION(inv, AllowMobSpawning) -{ - // monster spawning disabled during an invasion - M_ARGV(1, string) = "You cannot spawn monsters during an invasion!"; - return true; -} - -MUTATOR_HOOKFUNCTION(inv, CheckRules_World) -{ - if(autocvar_g_invasion_type == INV_TYPE_ROUND) - return false; - - M_ARGV(0, float) = WinningCondition_Invasion(); - return true; -} - -MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(0, float) = invasion_teams; -} - -MUTATOR_HOOKFUNCTION(inv, AllowMobButcher) -{ - M_ARGV(0, string) = "This command does not work during an invasion!"; - return true; -} - -void invasion_ScoreRules(int inv_teams) -{ - if(inv_teams) { CheckAllowedTeams(NULL); } - GameRules_score_enabled(false); - GameRules_scoring(inv_teams, 0, 0, { - if (inv_teams) { - field_team(ST_INV_KILLS, "frags", SFL_SORT_PRIO_PRIMARY); - } - field(SP_KILLS, "frags", ((inv_teams) ? SFL_SORT_PRIO_SECONDARY : SFL_SORT_PRIO_PRIMARY)); - }); -} - -void invasion_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. -{ - if(autocvar_g_invasion_type == INV_TYPE_HUNT || autocvar_g_invasion_type == INV_TYPE_STAGE) - cvar_set("fraglimit", "0"); - - if(autocvar_g_invasion_teams) - { - invasion_teams = BITS(bound(2, autocvar_g_invasion_teams, 4)); - } - else - invasion_teams = 0; - - independent_players = 1; // to disable extra useless scores - - invasion_ScoreRules(invasion_teams); - - independent_players = 0; - - if(autocvar_g_invasion_type == INV_TYPE_ROUND) - { - round_handler_Spawn(Invasion_CheckPlayers, Invasion_CheckWinner, Invasion_RoundStart); - round_handler_Init(5, autocvar_g_invasion_warmup, autocvar_g_invasion_round_timelimit); - - inv_roundcnt = 0; - inv_maxrounds = 15; // 15? - } -} - -void invasion_Initialize() -{ - InitializeEntity(NULL, invasion_DelayedInit, INITPRIO_GAMETYPE); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qh b/qcsrc/server/mutators/mutator/gamemode_invasion.qh deleted file mode 100644 index 0ea0e82c4c..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qh +++ /dev/null @@ -1,47 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") -int autocvar_g_invasion_teams; -int autocvar_g_invasion_type; -bool autocvar_g_invasion_team_spawns; -bool g_invasion; -void invasion_Initialize(); - -REGISTER_MUTATOR(inv, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - if (autocvar_g_invasion_teams >= 2) { - GameRules_teams(true); - GameRules_spawning_teams(autocvar_g_invasion_team_spawns); - } - GameRules_limit_score(autocvar_g_invasion_point_limit); - - g_invasion = true; - cvar_settemp("g_monsters", "1"); - invasion_Initialize(); - } - return 0; -} - -float inv_numspawned; -float inv_maxspawned; -float inv_roundcnt; -float inv_maxrounds; -float inv_numkilled; -float inv_lastcheck; -float inv_maxcurrent; - -float invasion_teams; -float inv_monsters_perteam[17]; - -float inv_monsterskill; - -const float ST_INV_KILLS = 1; - -const int INV_TYPE_ROUND = 0; // round-based waves of enemies -const int INV_TYPE_HUNT = 1; // clear the map of placed enemies -const int INV_TYPE_STAGE = 2; // reach the end of the level diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc deleted file mode 100644 index 567f24b478..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc +++ /dev/null @@ -1,472 +0,0 @@ -#include "gamemode_keepaway.qh" - -#include <common/effects/all.qh> - -.entity ballcarried; - -int autocvar_g_keepaway_ballcarrier_effects; -float autocvar_g_keepaway_ballcarrier_damage; -float autocvar_g_keepaway_ballcarrier_force; -float autocvar_g_keepaway_ballcarrier_highspeed; -float autocvar_g_keepaway_ballcarrier_selfdamage; -float autocvar_g_keepaway_ballcarrier_selfforce; -float autocvar_g_keepaway_noncarrier_damage; -float autocvar_g_keepaway_noncarrier_force; -float autocvar_g_keepaway_noncarrier_selfdamage; -float autocvar_g_keepaway_noncarrier_selfforce; -bool autocvar_g_keepaway_noncarrier_warn; -int autocvar_g_keepaway_score_bckill; -int autocvar_g_keepaway_score_killac; -int autocvar_g_keepaway_score_timepoints; -float autocvar_g_keepaway_score_timeinterval; -float autocvar_g_keepawayball_damageforcescale; -int autocvar_g_keepawayball_effects; -float autocvar_g_keepawayball_respawntime; -int autocvar_g_keepawayball_trail_color; - -bool ka_ballcarrier_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs on waypoints which are attached to ballcarriers, updates once per frame -{ - if(view.ballcarried) - if(IS_SPEC(player)) - return false; // we don't want spectators of the ballcarrier to see the attached waypoint on the top of their screen - - // TODO: Make the ballcarrier lack a waypointsprite whenever they have the invisibility powerup - - return true; -} - -void ka_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":ka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); -} - -void ka_TouchEvent(entity this, entity toucher); -void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated -{ - if(game_stopped) return; - vector oldballorigin = this.origin; - - if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, 10, 1024, 256)) - { - entity spot = SelectSpawnPoint(this, true); - setorigin(this, spot.origin); - this.angles = spot.angles; - } - - makevectors(this.angles); - set_movetype(this, MOVETYPE_BOUNCE); - this.velocity = '0 0 200'; - this.angles = '0 0 0'; - this.effects = autocvar_g_keepawayball_effects; - settouch(this, ka_TouchEvent); - setthink(this, ka_RespawnBall); - this.nextthink = time + autocvar_g_keepawayball_respawntime; - navigation_dynamicgoal_set(this); - - Send_Effect(EFFECT_ELECTRO_COMBO, oldballorigin, '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, this.origin, '0 0 0', 1); - - WaypointSprite_Spawn(WP_KaBall, 0, 0, this, '0 0 64', NULL, this.team, this, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); - WaypointSprite_Ping(this.waypointsprite_attachedforcarrier); - - sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) -} - -void ka_TimeScoring(entity this) -{ - if(this.owner.ballcarried) - { // add points for holding the ball after a certain amount of time - if(autocvar_g_keepaway_score_timepoints) - GameRules_scoring_add(this.owner, SCORE, autocvar_g_keepaway_score_timepoints); - - GameRules_scoring_add(this.owner, KEEPAWAY_BCTIME, (autocvar_g_keepaway_score_timeinterval / 1)); // interval is divided by 1 so that time always shows "seconds" - this.nextthink = time + autocvar_g_keepaway_score_timeinterval; - } -} - -void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something -{ - if(game_stopped) return; - if(!this) return; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { // The ball fell off the map, respawn it since players can't get to it - ka_RespawnBall(this); - return; - } - if(IS_DEAD(toucher)) { return; } - if(STAT(FROZEN, toucher)) { return; } - if (!IS_PLAYER(toucher)) - { // The ball just touched an object, most likely the world - Send_Effect(EFFECT_BALL_SPARKS, this.origin, '0 0 0', 1); - sound(this, CH_TRIGGER, SND_KA_TOUCH, VOL_BASE, ATTEN_NORM); - return; - } - else if(this.wait > time) { return; } - - // attach the ball to the player - this.owner = toucher; - toucher.ballcarried = this; - GameRules_scoring_vip(toucher, true); - setattachment(this, toucher, ""); - setorigin(this, '0 0 0'); - - // make the ball invisible/unable to do anything/set up time scoring - this.velocity = '0 0 0'; - set_movetype(this, MOVETYPE_NONE); - this.effects |= EF_NODRAW; - settouch(this, func_null); - setthink(this, ka_TimeScoring); - this.nextthink = time + autocvar_g_keepaway_score_timeinterval; - this.takedamage = DAMAGE_NO; - navigation_dynamicgoal_unset(this); - - // apply effects to player - toucher.glow_color = autocvar_g_keepawayball_trail_color; - toucher.glow_trail = true; - toucher.effects |= autocvar_g_keepaway_ballcarrier_effects; - - // messages and sounds - ka_EventLog("pickup", toucher); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, toucher.netname); - Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, toucher.netname); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF); - sound(this.owner, CH_TRIGGER, SND_KA_PICKEDUP, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) - - // scoring - GameRules_scoring_add(toucher, KEEPAWAY_PICKUPS, 1); - - // waypoints - WaypointSprite_AttachCarrier(WP_KaBallCarrier, toucher, RADARICON_FLAGCARRIER); - toucher.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player; - WaypointSprite_UpdateRule(toucher.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); - WaypointSprite_Ping(toucher.waypointsprite_attachedforcarrier); - WaypointSprite_Kill(this.waypointsprite_attachedforcarrier); -} - -void ka_DropEvent(entity plyr) // runs any time that a player is supposed to lose the ball -{ - entity ball; - ball = plyr.ballcarried; - - if(!ball) { return; } - - // reset the ball - setattachment(ball, NULL, ""); - set_movetype(ball, MOVETYPE_BOUNCE); - ball.wait = time + 1; - settouch(ball, ka_TouchEvent); - setthink(ball, ka_RespawnBall); - ball.nextthink = time + autocvar_g_keepawayball_respawntime; - ball.takedamage = DAMAGE_YES; - ball.effects &= ~EF_NODRAW; - setorigin(ball, plyr.origin + '0 0 10'); - ball.velocity = '0 0 200' + '0 100 0'*crandom() + '100 0 0'*crandom(); - entity e = ball.owner; ball.owner = NULL; - e.ballcarried = NULL; - GameRules_scoring_vip(e, false); - navigation_dynamicgoal_set(ball); - - // reset the player effects - plyr.glow_trail = false; - plyr.effects &= ~autocvar_g_keepaway_ballcarrier_effects; - - // messages and sounds - ka_EventLog("dropped", plyr); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_DROPPED, plyr.netname); - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEEPAWAY_DROPPED, plyr.netname); - sound(NULL, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) - - // scoring - // GameRules_scoring_add(plyr, KEEPAWAY_DROPS, 1); Not anymore, this is 100% the same as pickups and is useless. - - // waypoints - WaypointSprite_Spawn(WP_KaBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); - WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); - WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); - WaypointSprite_Kill(plyr.waypointsprite_attachedforcarrier); -} - -/** used to clear the ballcarrier whenever the match switches from warmup to normal */ -void ka_Reset(entity this) -{ - if((this.owner) && (IS_PLAYER(this.owner))) - ka_DropEvent(this.owner); - - if(time < game_starttime) - { - setthink(this, ka_RespawnBall); - settouch(this, func_null); - this.nextthink = game_starttime; - } - else - ka_RespawnBall(this); -} - - -// ================ -// Bot player logic -// ================ - -void havocbot_goalrating_ball(entity this, float ratingscale, vector org) -{ - float t; - entity ball_owner; - ball_owner = ka_ball.owner; - - if (ball_owner == this) - return; - - // If ball is carried by player then hunt them down. - if (ball_owner) - { - t = (this.health + this.armorvalue) / (ball_owner.health + ball_owner.armorvalue); - navigation_routerating(this, ball_owner, t * ratingscale, 2000); - } - else // Ball has been dropped so collect. - navigation_routerating(this, ka_ball, ratingscale, 2000); -} - -void havocbot_role_ka_carrier(entity this) -{ - if (IS_DEAD(this)) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_items(this, 10000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000); - havocbot_goalrating_waypoints(this, 1, this.origin, 3000); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } - - if (!this.ballcarried) - { - this.havocbot_role = havocbot_role_ka_collector; - navigation_goalrating_timeout_expire(this, 2); - } -} - -void havocbot_role_ka_collector(entity this) -{ - if (IS_DEAD(this)) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - havocbot_goalrating_items(this, 10000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 1000, this.origin, 10000); - havocbot_goalrating_ball(this, 20000, this.origin); - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } - - if (this.ballcarried) - { - this.havocbot_role = havocbot_role_ka_carrier; - navigation_goalrating_timeout_expire(this, 2); - } -} - - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ka, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if((frag_attacker != frag_target) && (IS_PLAYER(frag_attacker))) - { - if(frag_target.ballcarried) { // add to amount of times killing carrier - GameRules_scoring_add(frag_attacker, KEEPAWAY_CARRIERKILLS, 1); - if(autocvar_g_keepaway_score_bckill) // add bckills to the score - GameRules_scoring_add(frag_attacker, SCORE, autocvar_g_keepaway_score_bckill); - } - else if(!frag_attacker.ballcarried) - if(autocvar_g_keepaway_noncarrier_warn) - Send_Notification(NOTIF_ONE_ONLY, frag_attacker, MSG_CENTER, CENTER_KEEPAWAY_WARN); - - if(frag_attacker.ballcarried) // add to amount of kills while ballcarrier - GameRules_scoring_add(frag_attacker, SCORE, autocvar_g_keepaway_score_killac); - } - - if(frag_target.ballcarried) { ka_DropEvent(frag_target); } // a player with the ball has died, drop it -} - -MUTATOR_HOOKFUNCTION(ka, GiveFragsForKill) -{ - M_ARGV(2, float) = 0; // no frags counted in keepaway - return true; // you deceptive little bugger ;3 This needs to be true in order for this function to even count. -} - -MUTATOR_HOOKFUNCTION(ka, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - // clear the item used for the ball in keepaway - player.items &= ~IT_KEY1; - - // if the player has the ball, make sure they have the item for it (Used for HUD primarily) - if(player.ballcarried) - player.items |= IT_KEY1; -} - -MUTATOR_HOOKFUNCTION(ka, PlayerUseKey) -{ - entity player = M_ARGV(0, entity); - - if(MUTATOR_RETURNVALUE == 0) - if(player.ballcarried) - { - ka_DropEvent(player); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ka, Damage_Calculate) // for changing damage and force values that are applied to players in g_damage.qc -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_damage = M_ARGV(4, float); - vector frag_force = M_ARGV(6, vector); - - if(frag_attacker.ballcarried) // if the attacker is a ballcarrier - { - if(frag_target == frag_attacker) // damage done to yourself - { - frag_damage *= autocvar_g_keepaway_ballcarrier_selfdamage; - frag_force *= autocvar_g_keepaway_ballcarrier_selfforce; - } - else // damage done to noncarriers - { - frag_damage *= autocvar_g_keepaway_ballcarrier_damage; - frag_force *= autocvar_g_keepaway_ballcarrier_force; - } - } - else if (!frag_target.ballcarried) // if the target is a noncarrier - { - if(frag_target == frag_attacker) // damage done to yourself - { - frag_damage *= autocvar_g_keepaway_noncarrier_selfdamage; - frag_force *= autocvar_g_keepaway_noncarrier_selfforce; - } - else // damage done to other noncarriers - { - frag_damage *= autocvar_g_keepaway_noncarrier_damage; - frag_force *= autocvar_g_keepaway_noncarrier_force; - } - } - - M_ARGV(4, float) = frag_damage; - M_ARGV(6, vector) = frag_force; -} - -MUTATOR_HOOKFUNCTION(ka, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it -} - -MUTATOR_HOOKFUNCTION(ka, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - if(player.ballcarried) { ka_DropEvent(player); } // a player with the ball has left the match, drop it -} - -MUTATOR_HOOKFUNCTION(ka, PlayerPowerups) -{ - entity player = M_ARGV(0, entity); - - // In the future this hook is supposed to allow me to do some extra stuff with waypointsprites and invisibility powerup - // So bare with me until I can fix a certain bug with ka_ballcarrier_waypointsprite_visible_for_player() - - player.effects &= ~autocvar_g_keepaway_ballcarrier_effects; - - if(player.ballcarried) - player.effects |= autocvar_g_keepaway_ballcarrier_effects; -} - - -MUTATOR_HOOKFUNCTION(ka, PlayerPhysics_UpdateStats) -{ - entity player = M_ARGV(0, entity); - // these automatically reset, no need to worry - - if(player.ballcarried) - STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_keepaway_ballcarrier_highspeed; -} - -MUTATOR_HOOKFUNCTION(ka, BotShouldAttack) -{ - entity bot = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); - - // if neither player has ball then don't attack unless the ball is on the ground - if(!targ.ballcarried && !bot.ballcarried && ka_ball.owner) - return true; -} - -MUTATOR_HOOKFUNCTION(ka, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - if (bot.ballcarried) - bot.havocbot_role = havocbot_role_ka_carrier; - else - bot.havocbot_role = havocbot_role_ka_collector; - return true; -} - -MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) -{ - entity frag_target = M_ARGV(0, entity); - - if(frag_target.ballcarried) - ka_DropEvent(frag_target); -} - -.bool pushable; - -// ============== -// Initialization -// ============== - -MODEL(KA_BALL, "models/orbs/orbblue.md3"); - -void ka_SpawnBall() // loads various values for the ball, runs only once at start of match -{ - entity e = new(keepawayball); - setmodel(e, MDL_KA_BALL); - setsize(e, '-16 -16 -20', '16 16 20'); // 20 20 20 was too big, player is only 16 16 24... gotta cheat with the Z (20) axis so that the particle isn't cut off - e.damageforcescale = autocvar_g_keepawayball_damageforcescale; - e.takedamage = DAMAGE_YES; - e.solid = SOLID_TRIGGER; - set_movetype(e, MOVETYPE_BOUNCE); - e.glow_color = autocvar_g_keepawayball_trail_color; - e.glow_trail = true; - e.flags = FL_ITEM; - IL_PUSH(g_items, e); - e.pushable = true; - e.reset = ka_Reset; - settouch(e, ka_TouchEvent); - e.owner = NULL; - ka_ball = e; - navigation_dynamicgoal_init(ka_ball, false); - - InitializeEntity(e, ka_RespawnBall, INITPRIO_SETLOCATION); // is this the right priority? Neh, I have no idea.. Well-- it works! So. -} - -void ka_Initialize() // run at the start of a match, initiates game mode -{ - ka_SpawnBall(); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh b/qcsrc/server/mutators/mutator/gamemode_keepaway.qh deleted file mode 100644 index abbabbd5bb..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -void ka_Initialize(); - -REGISTER_MUTATOR(ka, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_scoring(0, SFL_SORT_PRIO_PRIMARY, 0, { - field(SP_KEEPAWAY_PICKUPS, "pickups", 0); - field(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); - field(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); - }); - - ka_Initialize(); - } - return false; -} - - -entity ka_ball; - -void(entity this) havocbot_role_ka_carrier; -void(entity this) havocbot_role_ka_collector; - -void ka_DropEvent(entity plyr); diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc deleted file mode 100644 index 04576486b7..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ /dev/null @@ -1,1321 +0,0 @@ -#include "gamemode_keyhunt.qh" - -float autocvar_g_balance_keyhunt_damageforcescale; -float autocvar_g_balance_keyhunt_delay_collect; -float autocvar_g_balance_keyhunt_delay_damage_return; -float autocvar_g_balance_keyhunt_delay_return; -float autocvar_g_balance_keyhunt_delay_round; -float autocvar_g_balance_keyhunt_delay_tracking; -float autocvar_g_balance_keyhunt_return_when_unreachable; -float autocvar_g_balance_keyhunt_dropvelocity; -float autocvar_g_balance_keyhunt_maxdist; -float autocvar_g_balance_keyhunt_protecttime; - -int autocvar_g_balance_keyhunt_score_capture; -int autocvar_g_balance_keyhunt_score_carrierfrag; -int autocvar_g_balance_keyhunt_score_collect; -int autocvar_g_balance_keyhunt_score_destroyed; -int autocvar_g_balance_keyhunt_score_destroyed_ownfactor; -int autocvar_g_balance_keyhunt_score_push; -float autocvar_g_balance_keyhunt_throwvelocity; - -//int autocvar_g_keyhunt_teams; -int autocvar_g_keyhunt_teams_override; - -// #define KH_PLAYER_USE_ATTACHMENT -// #define KH_PLAYER_USE_CARRIEDMODEL - -#ifdef KH_PLAYER_USE_ATTACHMENT -const vector KH_PLAYER_ATTACHMENT_DIST_ROTATED = '0 -4 0'; -const vector KH_PLAYER_ATTACHMENT_DIST = '4 0 0'; -const vector KH_PLAYER_ATTACHMENT = '0 0 0'; -const vector KH_PLAYER_ATTACHMENT_ANGLES = '0 0 0'; -const string KH_PLAYER_ATTACHMENT_BONE = ""; -#else -const float KH_KEY_ZSHIFT = 22; -const float KH_KEY_XYDIST = 24; -const float KH_KEY_XYSPEED = 45; -#endif -const float KH_KEY_WP_ZSHIFT = 20; - -const vector KH_KEY_MIN = '-10 -10 -46'; -const vector KH_KEY_MAX = '10 10 3'; -const float KH_KEY_BRIGHTNESS = 2; - -bool kh_no_radar_circles; - -// kh_state -// bits 0- 4: team of key 1, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 5- 9: team of key 2, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 10-14: team of key 3, or 0 for no such key, or 30 for dropped, or 31 for self -// bits 15-19: team of key 4, or 0 for no such key, or 30 for dropped, or 31 for self -.float siren_time; // time delay the siren -//.float stuff_time; // time delay to stuffcmd a cvar - -int kh_keystatus[17]; -//kh_keystatus[0] = status of dropped keys, kh_keystatus[1 - 16] = player # -//replace 17 with cvar("maxplayers") or similar !!!!!!!!! -//for(i = 0; i < maxplayers; ++i) -// kh_keystatus[i] = "0"; - -int kh_Team_ByID(int t) -{ - if(t == 0) return NUM_TEAM_1; - if(t == 1) return NUM_TEAM_2; - if(t == 2) return NUM_TEAM_3; - if(t == 3) return NUM_TEAM_4; - return 0; -} - -//entity kh_worldkeylist; -.entity kh_worldkeynext; -entity kh_controller; -//bool kh_tracking_enabled; -int kh_teams; -int kh_interferemsg_team; -float kh_interferemsg_time; -.entity kh_next, kh_prev; // linked list -.float kh_droptime; -.int kh_dropperteam; -.entity kh_previous_owner; -.int kh_previous_owner_playerid; - -int kh_key_dropped, kh_key_carried; - -int kh_Key_AllOwnedByWhichTeam(); - -const int ST_KH_CAPS = 1; -void kh_ScoreRules(int teams) -{ - GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, { - field_team(ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - field(SP_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - field(SP_KH_PUSHES, "pushes", 0); - field(SP_KH_DESTROYS, "destroyed", SFL_LOWER_IS_BETTER); - field(SP_KH_PICKUPS, "pickups", 0); - field(SP_KH_KCKILLS, "kckills", 0); - field(SP_KH_LOSSES, "losses", SFL_LOWER_IS_BETTER); - }); -} - -bool kh_KeyCarrier_waypointsprite_visible_for_player(entity this, entity player, entity view) // runs all the time -{ - if(!IS_PLAYER(view) || DIFF_TEAM(this, view)) - if(!kh_tracking_enabled) - return false; - - return true; -} - -bool kh_Key_waypointsprite_visible_for_player(entity this, entity player, entity view) -{ - if(!kh_tracking_enabled) - return false; - if(!this.owner) - return true; - if(!this.owner.owner) - return true; - return false; // draw only when key is not owned -} - -void kh_update_state() -{ - entity key; - int f; - int s = 0; - FOR_EACH_KH_KEY(key) - { - if(key.owner) - f = key.team; - else - f = 30; - s |= (32 ** key.count) * f; - } - - FOREACH_CLIENT(true, { STAT(KH_KEYS, it) = s; }); - - FOR_EACH_KH_KEY(key) - { - if(key.owner) - STAT(KH_KEYS, key.owner) |= (32 ** key.count) * 31; - } - //print(ftos((nextent(NULL)).kh_state), "\n"); -} - - - - -var kh_Think_t kh_Controller_Thinkfunc; -void kh_Controller_SetThink(float t, kh_Think_t func) // runs occasionaly -{ - kh_Controller_Thinkfunc = func; - kh_controller.cnt = ceil(t); - if(t == 0) - kh_controller.nextthink = time; // force -} -void kh_WaitForPlayers(); -void kh_Controller_Think(entity this) // called a lot -{ - if(game_stopped) - return; - if(this.cnt > 0) - { - if(getthink(this) != kh_WaitForPlayers) - this.cnt -= 1; - } - else if(this.cnt == 0) - { - this.cnt -= 1; - kh_Controller_Thinkfunc(); - } - this.nextthink = time + 1; -} - -// frags f: take from cvar * f -// frags 0: no frags -void kh_Scores_Event(entity player, entity key, string what, float frags_player, float frags_owner) // update the score when a key is captured -{ - string s; - if(game_stopped) - return; - - if(frags_player) - UpdateFrags(player, frags_player); - - if(key && key.owner && frags_owner) - UpdateFrags(key.owner, frags_owner); - - if(!autocvar_sv_eventlog) //output extra info to the console or text file - return; - - s = strcat(":keyhunt:", what, ":", ftos(player.playerid), ":", ftos(frags_player)); - - if(key && key.owner) - s = strcat(s, ":", ftos(key.owner.playerid)); - else - s = strcat(s, ":0"); - - s = strcat(s, ":", ftos(frags_owner), ":"); - - if(key) - s = strcat(s, key.netname); - - GameLogEcho(s); -} - -vector kh_AttachedOrigin(entity e) // runs when a team captures the flag, it can run 2 or 3 times. -{ - if(e.tag_entity) - { - makevectors(e.tag_entity.angles); - return e.tag_entity.origin + e.origin.x * v_forward - e.origin.y * v_right + e.origin.z * v_up; - } - else - return e.origin; -} - -void kh_Key_Attach(entity key) // runs when a player picks up a key and several times when a key is assigned to a player at the start of a round -{ -#ifdef KH_PLAYER_USE_ATTACHMENT - entity first = key.owner.kh_next; - if(key == first) - { - setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE); - if(key.kh_next) - { - setattachment(key.kh_next, key, ""); - setorigin(key, key.kh_next.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); - setorigin(key.kh_next, KH_PLAYER_ATTACHMENT_DIST_ROTATED); - key.kh_next.angles = '0 0 0'; - } - else - setorigin(key, KH_PLAYER_ATTACHMENT); - key.angles = KH_PLAYER_ATTACHMENT_ANGLES; - } - else - { - setattachment(key, key.kh_prev, ""); - if(key.kh_next) - setattachment(key.kh_next, key, ""); - setorigin(key, KH_PLAYER_ATTACHMENT_DIST_ROTATED); - setorigin(first, first.origin - 0.5 * KH_PLAYER_ATTACHMENT_DIST); - key.angles = '0 0 0'; - } -#else - setattachment(key, key.owner, ""); - setorigin(key, '0 0 1' * KH_KEY_ZSHIFT); // fixing x, y in think - key.angles_y -= key.owner.angles.y; -#endif - key.flags = 0; - if(IL_CONTAINS(g_items, key)) - IL_REMOVE(g_items, key); - key.solid = SOLID_NOT; - set_movetype(key, MOVETYPE_NONE); - key.team = key.owner.team; - key.nextthink = time; - key.damageforcescale = 0; - key.takedamage = DAMAGE_NO; - key.modelindex = kh_key_carried; - navigation_dynamicgoal_unset(key); -} - -void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs several times times when all the keys are captured -{ -#ifdef KH_PLAYER_USE_ATTACHMENT - entity first = key.owner.kh_next; - if(key == first) - { - if(key.kh_next) - { - setattachment(key.kh_next, key.owner, KH_PLAYER_ATTACHMENT_BONE); - setorigin(key.kh_next, key.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); - key.kh_next.angles = KH_PLAYER_ATTACHMENT_ANGLES; - } - } - else - { - if(key.kh_next) - setattachment(key.kh_next, key.kh_prev, ""); - setorigin(first, first.origin + 0.5 * KH_PLAYER_ATTACHMENT_DIST); - } - // in any case: - setattachment(key, NULL, ""); - setorigin(key, key.owner.origin + '0 0 1' * (STAT(PL_MIN, key.owner).z - KH_KEY_MIN_z)); - key.angles = key.owner.angles; -#else - setorigin(key, key.owner.origin + key.origin.z * '0 0 1'); - setattachment(key, NULL, ""); - key.angles_y += key.owner.angles.y; -#endif - key.flags = FL_ITEM; - if(!IL_CONTAINS(g_items, key)) - IL_PUSH(g_items, key); - key.solid = SOLID_TRIGGER; - set_movetype(key, MOVETYPE_TOSS); - key.pain_finished = time + autocvar_g_balance_keyhunt_delay_return; - key.damageforcescale = autocvar_g_balance_keyhunt_damageforcescale; - key.takedamage = DAMAGE_YES; - // let key.team stay - key.modelindex = kh_key_dropped; - navigation_dynamicgoal_set(key); - key.kh_previous_owner = key.owner; - key.kh_previous_owner_playerid = key.owner.playerid; -} - -void kh_Key_AssignTo(entity key, entity player) // runs every time a key is picked up or assigned. Runs prior to kh_key_attach -{ - if(key.owner == player) - return; - - int ownerteam0 = kh_Key_AllOwnedByWhichTeam(); - - if(key.owner) - { - kh_Key_Detach(key); - - // remove from linked list - if(key.kh_next) - key.kh_next.kh_prev = key.kh_prev; - key.kh_prev.kh_next = key.kh_next; - key.kh_next = NULL; - key.kh_prev = NULL; - - if(key.owner.kh_next == NULL) - { - // No longer a key carrier - if(!kh_no_radar_circles) - WaypointSprite_Ping(key.owner.waypointsprite_attachedforcarrier); - WaypointSprite_DetachCarrier(key.owner); - } - } - - key.owner = player; - - if(player) - { - // insert into linked list - key.kh_next = player.kh_next; - key.kh_prev = player; - player.kh_next = key; - if(key.kh_next) - key.kh_next.kh_prev = key; - - float i; - i = kh_keystatus[key.owner.playerid]; - if(key.netname == "^1red key") - i += 1; - if(key.netname == "^4blue key") - i += 2; - if(key.netname == "^3yellow key") - i += 4; - if(key.netname == "^6pink key") - i += 8; - kh_keystatus[key.owner.playerid] = i; - - kh_Key_Attach(key); - - if(key.kh_next == NULL) - { - // player is now a key carrier - entity wp = WaypointSprite_AttachCarrier(WP_Null, player, RADARICON_FLAGCARRIER); - wp.colormod = colormapPaletteColor(player.team - 1, 0); - player.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_KeyCarrier_waypointsprite_visible_for_player; - WaypointSprite_UpdateRule(player.waypointsprite_attachedforcarrier, player.team, SPRITERULE_TEAMPLAY); - if(player.team == NUM_TEAM_1) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierRed, WP_KeyCarrierFriend, WP_KeyCarrierRed); - else if(player.team == NUM_TEAM_2) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierBlue, WP_KeyCarrierFriend, WP_KeyCarrierBlue); - else if(player.team == NUM_TEAM_3) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierYellow, WP_KeyCarrierFriend, WP_KeyCarrierYellow); - else if(player.team == NUM_TEAM_4) - WaypointSprite_UpdateSprites(player.waypointsprite_attachedforcarrier, WP_KeyCarrierPink, WP_KeyCarrierFriend, WP_KeyCarrierPink); - if(!kh_no_radar_circles) - WaypointSprite_Ping(player.waypointsprite_attachedforcarrier); - } - } - - // moved that here, also update if there's no player - kh_update_state(); - - key.pusher = NULL; - - int ownerteam = kh_Key_AllOwnedByWhichTeam(); - if(ownerteam != ownerteam0) - { - entity k; - if(ownerteam != -1) - { - kh_interferemsg_time = time + 0.2; - kh_interferemsg_team = player.team; - - // audit all key carrier sprites, update them to "Run here" - FOR_EACH_KH_KEY(k) - { - if (!k.owner) continue; - entity first = WP_Null; - FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, { first = it; break; }); - entity third = WP_Null; - FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, { third = it; break; }); - WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFinish, third); - } - } - else - { - kh_interferemsg_time = 0; - - // audit all key carrier sprites, update them to "Key Carrier" - FOR_EACH_KH_KEY(k) - { - if (!k.owner) continue; - entity first = WP_Null; - FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model1, { first = it; break; }); - entity third = WP_Null; - FOREACH(Waypoints, it.netname == k.owner.waypointsprite_attachedforcarrier.model3, { third = it; break; }); - WaypointSprite_UpdateSprites(k.owner.waypointsprite_attachedforcarrier, first, WP_KeyCarrierFriend, third); - } - } - } -} - -void kh_Key_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, .entity weaponentity, vector hitloc, vector force) -{ - if(this.owner) - return; - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - this.pain_finished = bound(time, time + autocvar_g_balance_keyhunt_delay_damage_return, this.pain_finished); - return; - } - if(force == '0 0 0') - return; - if(time > this.pushltime) - if(IS_PLAYER(attacker)) - this.team = attacker.team; -} - -void kh_Key_Collect(entity key, entity player) //a player picks up a dropped key -{ - sound(player, CH_TRIGGER, SND_KH_COLLECT, VOL_BASE, ATTEN_NORM); - - if(key.kh_dropperteam != player.team) - { - kh_Scores_Event(player, key, "collect", autocvar_g_balance_keyhunt_score_collect, 0); - GameRules_scoring_add(player, KH_PICKUPS, 1); - } - key.kh_dropperteam = 0; - int realteam = kh_Team_ByID(key.count); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_PICKUP), player.netname); - - kh_Key_AssignTo(key, player); // this also updates .kh_state -} - -void kh_Key_Touch(entity this, entity toucher) // runs many, many times when a key has been dropped and can be picked up -{ - if(game_stopped) - return; - - if(this.owner) // already carried - return; - - if(ITEM_TOUCH_NEEDKILL()) - { - this.pain_finished = bound(time, time + autocvar_g_balance_keyhunt_delay_damage_return, this.pain_finished); - return; - } - - if (!IS_PLAYER(toucher)) - return; - if(IS_DEAD(toucher)) - return; - if(toucher == this.enemy) - if(time < this.kh_droptime + autocvar_g_balance_keyhunt_delay_collect) - return; // you just dropped it! - kh_Key_Collect(this, toucher); -} - -void kh_Key_Remove(entity key) // runs after when all the keys have been collected or when a key has been dropped for more than X seconds -{ - entity o = key.owner; - kh_Key_AssignTo(key, NULL); - if(o) // it was attached - WaypointSprite_Kill(key.waypointsprite_attachedforcarrier); - else // it was dropped - WaypointSprite_DetachCarrier(key); - - // remove key from key list - if (kh_worldkeylist == key) - kh_worldkeylist = kh_worldkeylist.kh_worldkeynext; - else - { - o = kh_worldkeylist; - while (o) - { - if (o.kh_worldkeynext == key) - { - o.kh_worldkeynext = o.kh_worldkeynext.kh_worldkeynext; - break; - } - o = o.kh_worldkeynext; - } - } - - delete(key); - - kh_update_state(); -} - -void kh_FinishRound() // runs when a team captures the keys -{ - // prepare next round - kh_interferemsg_time = 0; - entity key; - - kh_no_radar_circles = true; - FOR_EACH_KH_KEY(key) - kh_Key_Remove(key); - kh_no_radar_circles = false; - - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); -} - -void nades_GiveBonus(entity player, float score); - -void kh_WinnerTeam(int winner_team) // runs when a team wins -{ - // all key carriers get some points - entity key; - float score = (NumTeams(kh_teams) - 1) * autocvar_g_balance_keyhunt_score_capture; - DistributeEvenly_Init(score, NumTeams(kh_teams)); - // twice the score for 3 team games, three times the score for 4 team games! - // note: for a win by destroying the key, this should NOT be applied - FOR_EACH_KH_KEY(key) - { - float f = DistributeEvenly_Get(1); - kh_Scores_Event(key.owner, key, "capture", f, 0); - GameRules_scoring_add_team(key.owner, KH_CAPS, 1); - nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high); - } - - bool first = true; - string keyowner = ""; - FOR_EACH_KH_KEY(key) - if(key.owner.kh_next == key) - { - if(!first) - keyowner = strcat(keyowner, ", "); - keyowner = key.owner.netname; - first = false; - } - - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(winner_team, CENTER_ROUND_TEAM_WIN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_KEYHUNT_CAPTURE), keyowner); - - first = true; - vector firstorigin = '0 0 0', lastorigin = '0 0 0', midpoint = '0 0 0'; - FOR_EACH_KH_KEY(key) - { - vector thisorigin = kh_AttachedOrigin(key); - //dprint("Key origin: ", vtos(thisorigin), "\n"); - midpoint += thisorigin; - - if(!first) - te_lightning2(NULL, lastorigin, thisorigin); - lastorigin = thisorigin; - if(first) - firstorigin = thisorigin; - first = false; - } - if(NumTeams(kh_teams) > 2) - { - te_lightning2(NULL, lastorigin, firstorigin); - } - midpoint = midpoint * (1 / NumTeams(kh_teams)); - te_customflash(midpoint, 1000, 1, Team_ColorRGB(winner_team) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component - - play2all(SND(KH_CAPTURE)); - kh_FinishRound(); -} - -void kh_LoserTeam(int loser_team, entity lostkey) // runs when a player pushes a flag carrier off the map -{ - float f; - entity attacker = NULL; - if(lostkey.pusher) - if(lostkey.pusher.team != loser_team) - if(IS_PLAYER(lostkey.pusher)) - attacker = lostkey.pusher; - - if(attacker) - { - if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, NULL, "pushed", 0, -autocvar_g_balance_keyhunt_score_push); - // don't actually GIVE him the -nn points, just log - kh_Scores_Event(attacker, NULL, "push", autocvar_g_balance_keyhunt_score_push, 0); - GameRules_scoring_add(attacker, KH_PUSHES, 1); - //centerprint(attacker, "Your push is the best!"); // does this really need to exist? - } - else - { - int players = 0; - float of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; - - FOREACH_CLIENT(IS_PLAYER(it) && it.team != loser_team, { ++players; }); - - entity key; - int keys = 0; - FOR_EACH_KH_KEY(key) - if(key.owner && key.team != loser_team) - ++keys; - - if(lostkey.kh_previous_owner) - kh_Scores_Event(lostkey.kh_previous_owner, NULL, "destroyed", 0, -autocvar_g_balance_keyhunt_score_destroyed); - // don't actually GIVE him the -nn points, just log - - if(lostkey.kh_previous_owner.playerid == lostkey.kh_previous_owner_playerid) - GameRules_scoring_add(lostkey.kh_previous_owner, KH_DESTROYS, 1); - - DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players); - - FOR_EACH_KH_KEY(key) - if(key.owner && key.team != loser_team) - { - f = DistributeEvenly_Get(of); - kh_Scores_Event(key.owner, NULL, "destroyed_holdingkey", f, 0); - } - - int fragsleft = DistributeEvenly_Get(players); - - // Now distribute these among all other teams... - int j = NumTeams(kh_teams) - 1; - for(int i = 0; i < NumTeams(kh_teams); ++i) - { - int thisteam = kh_Team_ByID(i); - if(thisteam == loser_team) // bad boy, no cookie - this WILL happen - continue; - - players = 0; - FOREACH_CLIENT(IS_PLAYER(it) && it.team == thisteam, { ++players; }); - - DistributeEvenly_Init(fragsleft, j); - fragsleft = DistributeEvenly_Get(j - 1); - DistributeEvenly_Init(DistributeEvenly_Get(1), players); - - FOREACH_CLIENT(IS_PLAYER(it) && it.team == thisteam, { - f = DistributeEvenly_Get(1); - kh_Scores_Event(it, NULL, "destroyed", f, 0); - }); - - --j; - } - } - - int realteam = kh_Team_ByID(lostkey.count); - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, APP_TEAM_NUM(loser_team, CENTER_ROUND_TEAM_LOSS)); - if(attacker) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_PUSHED), attacker.netname, lostkey.kh_previous_owner.netname); - else - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_DESTROYED), lostkey.kh_previous_owner.netname); - - play2all(SND(KH_DESTROY)); - te_tarexplosion(lostkey.origin); - - kh_FinishRound(); -} - -void kh_Key_Think(entity this) // runs all the time -{ - if(game_stopped) - return; - - if(this.owner) - { -#ifndef KH_PLAYER_USE_ATTACHMENT - makevectors('0 1 0' * (this.cnt + (time % 360) * KH_KEY_XYSPEED)); - setorigin(this, v_forward * KH_KEY_XYDIST + '0 0 1' * this.origin.z); -#endif - } - - // if in nodrop or time over, end the round - if(!this.owner) - if(time > this.pain_finished) - kh_LoserTeam(this.team, this); - - if(this.owner) - if(kh_Key_AllOwnedByWhichTeam() != -1) - { - if(this.siren_time < time) - { - sound(this.owner, CH_TRIGGER, SND_KH_ALARM, VOL_BASE, ATTEN_NORM); // play a simple alarm - this.siren_time = time + 2.5; // repeat every 2.5 seconds - } - - entity key; - vector p = this.owner.origin; - FOR_EACH_KH_KEY(key) - if(vdist(key.owner.origin - p, >, autocvar_g_balance_keyhunt_maxdist)) - goto not_winning; - kh_WinnerTeam(this.team); -LABEL(not_winning) - } - - if(kh_interferemsg_time && time > kh_interferemsg_time) - { - kh_interferemsg_time = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - if(it.team == kh_interferemsg_team) - if(it.kh_next) - Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_KEYHUNT_MEET); - else - Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_KEYHUNT_HELP); - else - Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(kh_interferemsg_team, CENTER_KEYHUNT_INTERFERE)); - }); - } - - this.nextthink = time + 0.05; -} - -void key_reset(entity this) -{ - kh_Key_AssignTo(this, NULL); - kh_Key_Remove(this); -} - -const string STR_ITEM_KH_KEY = "item_kh_key"; -void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every time a new flag is created, ie after all the keys have been collected -{ - entity key = spawn(); - key.count = i; - key.classname = STR_ITEM_KH_KEY; - settouch(key, kh_Key_Touch); - setthink(key, kh_Key_Think); - key.nextthink = time; - key.items = IT_KEY1 | IT_KEY2; - key.cnt = _angle; - key.angles = '0 360 0' * random(); - key.event_damage = kh_Key_Damage; - key.takedamage = DAMAGE_YES; - key.damagedbytriggers = autocvar_g_balance_keyhunt_return_when_unreachable; - key.damagedbycontents = autocvar_g_balance_keyhunt_return_when_unreachable; - key.modelindex = kh_key_dropped; - key.model = "key"; - key.kh_dropperteam = 0; - key.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - setsize(key, KH_KEY_MIN, KH_KEY_MAX); - key.colormod = Team_ColorRGB(initial_owner.team) * KH_KEY_BRIGHTNESS; - key.reset = key_reset; - navigation_dynamicgoal_init(key, false); - - switch(initial_owner.team) - { - case NUM_TEAM_1: - key.netname = "^1red key"; - break; - case NUM_TEAM_2: - key.netname = "^4blue key"; - break; - case NUM_TEAM_3: - key.netname = "^3yellow key"; - break; - case NUM_TEAM_4: - key.netname = "^6pink key"; - break; - default: - key.netname = "NETGIER key"; - break; - } - - // link into key list - key.kh_worldkeynext = kh_worldkeylist; - kh_worldkeylist = key; - - Send_Notification(NOTIF_ONE, initial_owner, MSG_CENTER, APP_TEAM_NUM(initial_owner.team, CENTER_KEYHUNT_START)); - - WaypointSprite_Spawn(WP_KeyDropped, 0, 0, key, '0 0 1' * KH_KEY_WP_ZSHIFT, NULL, key.team, key, waypointsprite_attachedforcarrier, false, RADARICON_FLAG); - key.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = kh_Key_waypointsprite_visible_for_player; - - kh_Key_AssignTo(key, initial_owner); -} - -// -1 when no team completely owns all keys yet -int kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team -{ - entity key; - int teem = -1; - int keys = NumTeams(kh_teams); - FOR_EACH_KH_KEY(key) - { - if(!key.owner) - return -1; - if(teem == -1) - teem = key.team; - else if(teem != key.team) - return -1; - --keys; - } - if(keys != 0) - return -1; - return teem; -} - -void kh_Key_DropOne(entity key) -{ - // prevent collecting this one for some time - entity player = key.owner; - - key.kh_droptime = time; - key.enemy = player; - - kh_Scores_Event(player, key, "dropkey", 0, 0); - GameRules_scoring_add(player, KH_LOSSES, 1); - int realteam = kh_Team_ByID(key.count); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_DROP), player.netname); - - kh_Key_AssignTo(key, NULL); - makevectors(player.v_angle); - key.velocity = W_CalculateProjectileVelocity(player, player.velocity, autocvar_g_balance_keyhunt_throwvelocity * v_forward, false); - key.pusher = NULL; - key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; - key.kh_dropperteam = key.team; - - sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM); -} - -void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies -{ - if(player.kh_next) - { - entity mypusher = NULL; - if(player.pusher) - if(time < player.pushltime) - mypusher = player.pusher; - - entity key; - while((key = player.kh_next)) - { - kh_Scores_Event(player, key, "losekey", 0, 0); - GameRules_scoring_add(player, KH_LOSSES, 1); - int realteam = kh_Team_ByID(key.count); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_LOST), player.netname); - kh_Key_AssignTo(key, NULL); - makevectors('-1 0 0' * (45 + 45 * random()) + '0 360 0' * random()); - key.velocity = W_CalculateProjectileVelocity(player, player.velocity, autocvar_g_balance_keyhunt_dropvelocity * v_forward, false); - key.pusher = mypusher; - key.pushltime = time + autocvar_g_balance_keyhunt_protecttime; - if(suicide) - key.kh_dropperteam = player.team; - } - sound(player, CH_TRIGGER, SND_KH_DROP, VOL_BASE, ATTEN_NORM); - } -} - -int kh_GetMissingTeams() -{ - int missing_teams = 0; - for(int i = 0; i < NumTeams(kh_teams); ++i) - { - int teem = kh_Team_ByID(i); - int players = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) - ++players; - }); - if (!players) - missing_teams |= (2 ** i); - } - return missing_teams; -} - -void kh_WaitForPlayers() // delay start of the round until enough players are present -{ - static int prev_missing_teams_mask; - if(time < game_starttime) - { - if (prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); - return; - } - - int missing_teams_mask = kh_GetMissingTeams(); - if(!missing_teams_mask) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_ROUNDSTART, autocvar_g_balance_keyhunt_delay_round); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); - } - else - { - if(player_count == 0) - { - if(prev_missing_teams_mask > 0) - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_MISSING_TEAMS); - prev_missing_teams_mask = -1; - } - else - { - if(prev_missing_teams_mask != missing_teams_mask) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); - prev_missing_teams_mask = missing_teams_mask; - } - } - kh_Controller_SetThink(1, kh_WaitForPlayers); - } -} - -void kh_EnableTrackingDevice() // runs after each round -{ - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT); - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER); - - kh_tracking_enabled = true; -} - -void kh_StartRound() // runs at the start of each round -{ - if(time < game_starttime) - { - kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); - return; - } - - if(kh_GetMissingTeams()) - { - kh_Controller_SetThink(1, kh_WaitForPlayers); - return; - } - - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT); - Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER); - - for(int i = 0; i < NumTeams(kh_teams); ++i) - { - int teem = kh_Team_ByID(i); - int players = 0; - entity my_player = NULL; - FOREACH_CLIENT(IS_PLAYER(it), { - if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) - { - ++players; - if(random() * players <= 1) - my_player = it; - } - }); - kh_Key_Spawn(my_player, 360 * i / NumTeams(kh_teams), i); - } - - kh_tracking_enabled = false; - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_KEYHUNT_SCAN, autocvar_g_balance_keyhunt_delay_tracking); - kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_tracking, kh_EnableTrackingDevice); -} - -float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the player score -{ - if(attacker == targ) - return f; - - if(targ.kh_next) - { - if(attacker.team == targ.team) - { - int nk = 0; - for(entity k = targ.kh_next; k != NULL; k = k.kh_next) - ++nk; - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", -nk * autocvar_g_balance_keyhunt_score_collect, 0); - } - else - { - kh_Scores_Event(attacker, targ.kh_next, "carrierfrag", autocvar_g_balance_keyhunt_score_carrierfrag-1, 0); - GameRules_scoring_add(attacker, KH_KCKILLS, 1); - // the frag gets added later - } - } - - return f; -} - -void kh_Initialize() // sets up th KH environment -{ - // setup variables - kh_teams = autocvar_g_keyhunt_teams_override; - if(kh_teams < 2) - kh_teams = cvar("g_keyhunt_teams"); // read the cvar directly as it gets written earlier in the same frame - kh_teams = BITS(bound(2, kh_teams, 4)); - - // make a KH entity for controlling the game - kh_controller = spawn(); - setthink(kh_controller, kh_Controller_Think); - kh_Controller_SetThink(0, kh_WaitForPlayers); - - setmodel(kh_controller, MDL_KH_KEY); - kh_key_dropped = kh_controller.modelindex; - /* - dprint(vtos(kh_controller.mins)); - dprint(vtos(kh_controller.maxs)); - dprint("\n"); - */ -#ifdef KH_PLAYER_USE_CARRIEDMODEL - setmodel(kh_controller, MDL_KH_KEY_CARRIED); - kh_key_carried = kh_controller.modelindex; -#else - kh_key_carried = kh_key_dropped; -#endif - - kh_controller.model = ""; - kh_controller.modelindex = 0; - - kh_ScoreRules(kh_teams); -} - -void kh_finalize() -{ - // to be called before intermission - kh_FinishRound(); - delete(kh_controller); - kh_controller = NULL; -} - -// legacy bot role - -void(entity this) havocbot_role_kh_carrier; -void(entity this) havocbot_role_kh_defense; -void(entity this) havocbot_role_kh_offense; -void(entity this) havocbot_role_kh_freelancer; - - -void havocbot_goalrating_kh(entity this, float ratingscale_team, float ratingscale_dropped, float ratingscale_enemy) -{ - entity head; - for (head = kh_worldkeylist; head; head = head.kh_worldkeynext) - { - if(head.owner == this) - continue; - if(!kh_tracking_enabled) - { - // if it's carried by our team we know about it - // otherwise we have to see it to know about it - if(!head.owner || head.team != this.team) - { - traceline(this.origin + this.view_ofs, head.origin, MOVE_NOMONSTERS, this); - if (trace_fraction < 1 && trace_ent != head) - continue; // skip what I can't see - } - } - if(!head.owner) - navigation_routerating(this, head, ratingscale_dropped * 10000, 100000); - else if(head.team == this.team) - navigation_routerating(this, head.owner, ratingscale_team * 10000, 100000); - else - navigation_routerating(this, head.owner, ratingscale_enemy * 10000, 100000); - } - - havocbot_goalrating_items(this, 1, this.origin, 10000); -} - -void havocbot_role_kh_carrier(entity this) -{ - if(IS_DEAD(this)) - return; - - if (!(this.kh_next)) - { - LOG_TRACE("changing role to freelancer"); - this.havocbot_role = havocbot_role_kh_freelancer; - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - if(kh_Key_AllOwnedByWhichTeam() == this.team) - havocbot_goalrating_kh(this, 10, 0.1, 0.1); // bring home - else - havocbot_goalrating_kh(this, 4, 4, 1); // play defensively - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_kh_defense(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.kh_next) - { - LOG_TRACE("changing role to carrier"); - this.havocbot_role = havocbot_role_kh_carrier; - this.havocbot_role_timeout = 0; - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + random() * 10 + 20; - if (time > this.havocbot_role_timeout) - { - LOG_TRACE("changing role to freelancer"); - this.havocbot_role = havocbot_role_kh_freelancer; - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - float key_owner_team; - navigation_goalrating_start(this); - - key_owner_team = kh_Key_AllOwnedByWhichTeam(); - if(key_owner_team == this.team) - havocbot_goalrating_kh(this, 10, 0.1, 0.1); // defend key carriers - else if(key_owner_team == -1) - havocbot_goalrating_kh(this, 4, 1, 0.1); // play defensively - else - havocbot_goalrating_kh(this, 0.1, 0.1, 10); // ATTACK ANYWAY - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_kh_offense(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.kh_next) - { - LOG_TRACE("changing role to carrier"); - this.havocbot_role = havocbot_role_kh_carrier; - this.havocbot_role_timeout = 0; - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + random() * 10 + 20; - if (time > this.havocbot_role_timeout) - { - LOG_TRACE("changing role to freelancer"); - this.havocbot_role = havocbot_role_kh_freelancer; - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - float key_owner_team; - - navigation_goalrating_start(this); - - key_owner_team = kh_Key_AllOwnedByWhichTeam(); - if(key_owner_team == this.team) - havocbot_goalrating_kh(this, 10, 0.1, 0.1); // defend anyway - else if(key_owner_team == -1) - havocbot_goalrating_kh(this, 0.1, 1, 4); // play offensively - else - havocbot_goalrating_kh(this, 0.1, 0.1, 10); // ATTACK! EMERGENCY! - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void havocbot_role_kh_freelancer(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.kh_next) - { - LOG_TRACE("changing role to carrier"); - this.havocbot_role = havocbot_role_kh_carrier; - this.havocbot_role_timeout = 0; - return; - } - - if (!this.havocbot_role_timeout) - this.havocbot_role_timeout = time + random() * 10 + 10; - if (time > this.havocbot_role_timeout) - { - if (random() < 0.5) - { - LOG_TRACE("changing role to offense"); - this.havocbot_role = havocbot_role_kh_offense; - } - else - { - LOG_TRACE("changing role to defense"); - this.havocbot_role = havocbot_role_kh_defense; - } - this.havocbot_role_timeout = 0; - return; - } - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - int key_owner_team = kh_Key_AllOwnedByWhichTeam(); - if(key_owner_team == this.team) - havocbot_goalrating_kh(this, 10, 0.1, 0.1); // defend anyway - else if(key_owner_team == -1) - havocbot_goalrating_kh(this, 1, 10, 4); // prefer dropped keys - else - havocbot_goalrating_kh(this, 0.1, 0.1, 10); // ATTACK ANYWAY - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - - -// register this as a mutator - -MUTATOR_HOOKFUNCTION(kh, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - kh_Key_DropAll(player, true); -} - -MUTATOR_HOOKFUNCTION(kh, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - kh_Key_DropAll(player, true); -} - -MUTATOR_HOOKFUNCTION(kh, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(frag_target == frag_attacker) - kh_Key_DropAll(frag_target, true); - else if(IS_PLAYER(frag_attacker)) - kh_Key_DropAll(frag_target, false); - else - kh_Key_DropAll(frag_target, true); -} - -MUTATOR_HOOKFUNCTION(kh, GiveFragsForKill, CBC_ORDER_FIRST) -{ - entity frag_attacker = M_ARGV(0, entity); - entity frag_target = M_ARGV(1, entity); - float frag_score = M_ARGV(2, float); - M_ARGV(2, float) = kh_HandleFrags(frag_attacker, frag_target, frag_score); -} - -MUTATOR_HOOKFUNCTION(kh, MatchEnd) -{ - kh_finalize(); -} - -MUTATOR_HOOKFUNCTION(kh, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(0, float) = kh_teams; -} - -MUTATOR_HOOKFUNCTION(kh, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - STAT(KH_KEYS, client) = STAT(KH_KEYS, spectatee); -} - -MUTATOR_HOOKFUNCTION(kh, PlayerUseKey) -{ - entity player = M_ARGV(0, entity); - - if(MUTATOR_RETURNVALUE == 0) - { - entity k = player.kh_next; - if(k) - { - kh_Key_DropOne(k); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - if(IS_DEAD(bot)) - return true; - - float r = random() * 3; - if (r < 1) - bot.havocbot_role = havocbot_role_kh_offense; - else if (r < 2) - bot.havocbot_role = havocbot_role_kh_defense; - else - bot.havocbot_role = havocbot_role_kh_freelancer; - - return true; -} - -MUTATOR_HOOKFUNCTION(kh, DropSpecialItems) -{ - entity frag_target = M_ARGV(0, entity); - - kh_Key_DropAll(frag_target, false); -} - -MUTATOR_HOOKFUNCTION(kh, reset_map_global) -{ - kh_WaitForPlayers(); // takes care of killing the "missing teams" message -} diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh deleted file mode 100644 index 77d7c06fcf..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -#define autocvar_g_keyhunt_point_limit cvar("g_keyhunt_point_limit") -int autocvar_g_keyhunt_point_leadlimit; -bool autocvar_g_keyhunt_team_spawns; -void kh_Initialize(); - -REGISTER_MUTATOR(kh, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - GameRules_spawning_teams(autocvar_g_keyhunt_team_spawns); - GameRules_limit_score(autocvar_g_keyhunt_point_limit); - GameRules_limit_lead(autocvar_g_keyhunt_point_leadlimit); - - kh_Initialize(); - } - return 0; -} - -#define FOR_EACH_KH_KEY(v) for(v = kh_worldkeylist; v; v = v.kh_worldkeynext ) - -// ALL OF THESE should be removed in the future, as other code should not have to care - -// used by bots: -bool kh_tracking_enabled; -.entity kh_next; - -USING(kh_Think_t, void()); -void kh_StartRound(); -void kh_Controller_SetThink(float t, kh_Think_t func); diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qc b/qcsrc/server/mutators/mutator/gamemode_lms.qc deleted file mode 100644 index 94c4a998c2..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qc +++ /dev/null @@ -1,429 +0,0 @@ -#include "gamemode_lms.qh" - -#include <common/mutators/mutator/instagib/items.qh> -#include <server/campaign.qh> -#include <server/command/_mod.qh> - -int autocvar_g_lms_extra_lives; -bool autocvar_g_lms_join_anytime; -int autocvar_g_lms_last_join; -bool autocvar_g_lms_regenerate; - -// main functions -float LMS_NewPlayerLives() -{ - float fl; - fl = autocvar_fraglimit; - if(fl == 0) - fl = 999; - - // first player has left the game for dying too much? Nobody else can get in. - if(lms_lowest_lives < 1) - return 0; - - if(!autocvar_g_lms_join_anytime) - if(lms_lowest_lives < fl - autocvar_g_lms_last_join) - return 0; - - return bound(1, lms_lowest_lives, fl); -} - -void ClearWinners(); - -// LMS winning condition: game terminates if and only if there's at most one -// one player who's living lives. Top two scores being equal cancels the time -// limit. -int WinningCondition_LMS() -{ - entity first_player = NULL; - int total_players = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - if (!total_players) - first_player = it; - ++total_players; - }); - - if (total_players) - { - if (total_players > 1) - { - // two or more active players - continue with the game - - if (autocvar_g_campaign) - { - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - float pl_lives = GameRules_scoring_add(it, LMS_LIVES, 0); - if (!pl_lives) - return WINNING_YES; // human player lost, game over - break; - }); - } - } - else - { - // exactly one player? - - ClearWinners(); - SetWinners(winning, 0); // NOTE: exactly one player is still "player", so this works out - - if (LMS_NewPlayerLives()) - { - // game still running (that is, nobody got removed from the game by a frag yet)? then continue - return WINNING_NO; - } - else - { - // a winner! - // and assign him his first place - GameRules_scoring_add(first_player, LMS_RANK, 1); - if(warmup_stage) - return WINNING_NO; - else - return WINNING_YES; - } - } - } - else - { - // nobody is playing at all... - if (LMS_NewPlayerLives()) - { - // wait for players... - } - else - { - // SNAFU (maybe a draw game?) - ClearWinners(); - LOG_TRACE("No players, ending game."); - return WINNING_YES; - } - } - - // When we get here, we have at least two players who are actually LIVING, - // now check if the top two players have equal score. - WinningConditionHelper(NULL); - - ClearWinners(); - if(WinningConditionHelper_winner) - WinningConditionHelper_winner.winning = true; - if(WinningConditionHelper_topscore == WinningConditionHelper_secondscore) - return WINNING_NEVER; - - // Top two have different scores? Way to go for our beloved TIMELIMIT! - return WINNING_NO; -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(lms, reset_map_global) -{ - lms_lowest_lives = 999; -} - -MUTATOR_HOOKFUNCTION(lms, reset_map_players) -{ - FOREACH_CLIENT(true, { - TRANSMUTE(Player, it); - it.frags = FRAGS_PLAYER; - GameRules_scoring_add(it, LMS_LIVES, LMS_NewPlayerLives()); - PutClientInServer(it); - }); -} - -MUTATOR_HOOKFUNCTION(lms, PutClientInServer) -{ - entity player = M_ARGV(0, entity); - - if(player.frags == FRAGS_SPECTATOR) - TRANSMUTE(Observer, player); - else - { - float tl = GameRules_scoring_add(player, LMS_LIVES, 0); - if(tl < lms_lowest_lives) - lms_lowest_lives = tl; - if(tl <= 0) - TRANSMUTE(Observer, player); - if(warmup_stage) - GameRules_scoring_add(player, LMS_RANK, -GameRules_scoring_add(player, LMS_RANK, 0)); - } -} - -MUTATOR_HOOKFUNCTION(lms, ForbidSpawn) -{ - entity player = M_ARGV(0, entity); - - if(warmup_stage) - return false; - if(player.frags == FRAGS_SPECTATOR) - return true; - if(GameRules_scoring_add(player, LMS_LIVES, 0) <= 0) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_LMS_NOLIVES); - return true; - } - return false; -} - -MUTATOR_HOOKFUNCTION(lms, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - frag_target.respawn_flags |= RESPAWN_FORCE; -} - -void lms_RemovePlayer(entity player) -{ - static int quitters = 0; - float player_rank = GameRules_scoring_add(player, LMS_RANK, 0); - if (!player_rank) - { - int pl_cnt = 0; - FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; }); - if (player.lms_spectate_warning != 2) - { - if(IS_BOT_CLIENT(player)) - bot_clear(player); - player.frags = FRAGS_LMS_LOSER; - GameRules_scoring_add(player, LMS_RANK, pl_cnt + 1); - } - else - { - lms_lowest_lives = 999; - FOREACH_CLIENT(true, { - if (it.frags == FRAGS_LMS_LOSER) - { - float it_rank = GameRules_scoring_add(it, LMS_RANK, 0); - if (it_rank > player_rank && it_rank <= 256) - GameRules_scoring_add(it, LMS_RANK, -1); - lms_lowest_lives = 0; - } - else if (it.frags != FRAGS_SPECTATOR) - { - float tl = GameRules_scoring_add(it, LMS_LIVES, 0); - if(tl < lms_lowest_lives) - lms_lowest_lives = tl; - } - }); - GameRules_scoring_add(player, LMS_RANK, 665 - quitters); // different from 666 - if(!warmup_stage) - { - GameRules_scoring_add(player, LMS_LIVES, -GameRules_scoring_add(player, LMS_LIVES, 0)); - ++quitters; - } - player.frags = FRAGS_LMS_LOSER; - TRANSMUTE(Observer, player); - } - if (pl_cnt == 2 && !warmup_stage) // a player is forfeiting leaving only one player - lms_lowest_lives = 0; // end the game now! - } - - if(CS(player).killcount != FRAGS_SPECTATOR) - if(GameRules_scoring_add(player, LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_LMS_NOLIVES, player.netname); - else - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_LMS_FORFEIT, player.netname); -} - -MUTATOR_HOOKFUNCTION(lms, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - lms_RemovePlayer(player); -} - -MUTATOR_HOOKFUNCTION(lms, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - lms_RemovePlayer(player); - return true; // prevent team reset -} - -MUTATOR_HOOKFUNCTION(lms, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - TRANSMUTE(Player, player); - campaign_bots_may_start = true; - - if(GameRules_scoring_add(player, LMS_LIVES, LMS_NewPlayerLives()) <= 0) - { - GameRules_scoring_add(player, LMS_RANK, 666); // mark as forced spectator for the hud code - player.frags = FRAGS_SPECTATOR; - } -} - -MUTATOR_HOOKFUNCTION(lms, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(player.deadflag == DEAD_DYING) - player.deadflag = DEAD_RESPAWNING; -} - -MUTATOR_HOOKFUNCTION(lms, PlayerRegen) -{ - if(autocvar_g_lms_regenerate) - return false; - return true; -} - -MUTATOR_HOOKFUNCTION(lms, ForbidThrowCurrentWeapon) -{ - // forbode! - return true; -} - -MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) -{ - entity frag_target = M_ARGV(1, entity); - - if (!warmup_stage) - { - // remove a life - int tl = GameRules_scoring_add(frag_target, LMS_LIVES, -1); - if(tl < lms_lowest_lives) - lms_lowest_lives = tl; - if(tl <= 0) - { - int pl_cnt = 0; - FOREACH_CLIENT(IS_PLAYER(it), { pl_cnt++; }); - if(IS_BOT_CLIENT(frag_target)) - bot_clear(frag_target); - frag_target.frags = FRAGS_LMS_LOSER; - GameRules_scoring_add(frag_target, LMS_RANK, pl_cnt); - } - } - M_ARGV(2, float) = 0; // frag score - - return true; -} - -MUTATOR_HOOKFUNCTION(lms, SetStartItems) -{ - start_items &= ~IT_UNLIMITED_AMMO; - start_health = warmup_start_health = cvar("g_lms_start_health"); - start_armorvalue = warmup_start_armorvalue = cvar("g_lms_start_armor"); - start_ammo_shells = warmup_start_ammo_shells = cvar("g_lms_start_ammo_shells"); - start_ammo_nails = warmup_start_ammo_nails = cvar("g_lms_start_ammo_nails"); - start_ammo_rockets = warmup_start_ammo_rockets = cvar("g_lms_start_ammo_rockets"); - start_ammo_cells = warmup_start_ammo_cells = cvar("g_lms_start_ammo_cells"); - start_ammo_plasma = warmup_start_ammo_plasma = cvar("g_lms_start_ammo_plasma"); - start_ammo_fuel = warmup_start_ammo_fuel = cvar("g_lms_start_ammo_fuel"); -} - -MUTATOR_HOOKFUNCTION(lms, ForbidPlayerScore_Clear) -{ - // don't clear player score - return true; -} - -MUTATOR_HOOKFUNCTION(lms, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(autocvar_g_lms_extra_lives) - if(item.itemdef == ITEM_ExtraLife) - return false; - - return true; -} - -void lms_extralife(entity this) -{ - StartItem(this, ITEM_ExtraLife); -} - -MUTATOR_HOOKFUNCTION(lms, OnEntityPreSpawn) -{ - if (!autocvar_g_powerups) return false; - if (!autocvar_g_lms_extra_lives) return false; - - entity ent = M_ARGV(0, entity); - - // Can't use .itemdef here - if (ent.classname != "item_health_mega") return false; - - entity e = spawn(); - setthink(e, lms_extralife); - - e.nextthink = time + 0.1; - e.spawnflags = ent.spawnflags; - e.noalign = ent.noalign; - setorigin(e, ent.origin); - - return true; -} - -MUTATOR_HOOKFUNCTION(lms, ItemTouch) -{ - entity item = M_ARGV(0, entity); - entity toucher = M_ARGV(1, entity); - - if(item.itemdef == ITEM_ExtraLife) - { - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); - GameRules_scoring_add(toucher, LMS_LIVES, autocvar_g_lms_extra_lives); - return MUT_ITEMTOUCH_PICKUP; - } - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE) -{ - FOREACH_CLIENT(IS_REAL_CLIENT(it), { - ++M_ARGV(0, int); // activerealplayers - ++M_ARGV(1, int); // realplayers - }); - - return true; -} - -MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate) -{ - entity player = M_ARGV(0, entity); - - if(warmup_stage || player.lms_spectate_warning) - { - // for the forfeit message... - player.lms_spectate_warning = 2; - } - else - { - if(player.frags != FRAGS_SPECTATOR && player.frags != FRAGS_LMS_LOSER) - { - player.lms_spectate_warning = 1; - sprint(player, "WARNING: you won't be able to enter the game again after spectating in LMS. Use the same command again to spectate anyway.\n"); - } - return MUT_SPECCMD_RETURN; - } - return MUT_SPECCMD_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(lms, CheckRules_World) -{ - M_ARGV(0, float) = WinningCondition_LMS(); - return true; -} - -MUTATOR_HOOKFUNCTION(lms, WantWeapon) -{ - M_ARGV(2, bool) = true; // all weapons -} - -MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(lms, AddPlayerScore) -{ - if(game_stopped) - if(M_ARGV(0, entity) == SP_LMS_RANK) // score field - return true; // allow writing to this field in intermission as it is needed for newly joining players -} - -void lms_Initialize() -{ - lms_lowest_lives = 9999; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qh b/qcsrc/server/mutators/mutator/gamemode_lms.qh deleted file mode 100644 index c69113a0c2..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -.float lms_spectate_warning; -#define autocvar_g_lms_lives_override cvar("g_lms_lives_override") -void lms_Initialize(); - -REGISTER_MUTATOR(lms, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_limit_score(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override)); - GameRules_limit_lead(0); - GameRules_score_enabled(false); - GameRules_scoring(0, 0, 0, { - field(SP_LMS_LIVES, "lives", SFL_SORT_PRIO_SECONDARY); - field(SP_LMS_RANK, "rank", SFL_LOWER_IS_BETTER | SFL_RANK | SFL_SORT_PRIO_PRIMARY | SFL_ALLOW_HIDE); - }); - - lms_Initialize(); - } - return 0; -} - -// lives related defs -float lms_lowest_lives; -float LMS_NewPlayerLives(); diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qc b/qcsrc/server/mutators/mutator/gamemode_race.qc deleted file mode 100644 index aa6d12a83e..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_race.qc +++ /dev/null @@ -1,488 +0,0 @@ -#include "gamemode_race.qh" - -#include <server/race.qh> - -#define autocvar_g_race_laps_limit cvar("g_race_laps_limit") -float autocvar_g_race_qualifying_timelimit; -float autocvar_g_race_qualifying_timelimit_override; -int autocvar_g_race_teams; - -// legacy bot roles -.float race_checkpoint; -void havocbot_role_race(entity this) -{ - if(IS_DEAD(this)) - return; - - if (navigation_goalrating_timeout(this)) - { - navigation_goalrating_start(this); - - bool raw_touch_check = true; - int cp = this.race_checkpoint; - - LABEL(search_racecheckpoints) - IL_EACH(g_racecheckpoints, true, - { - if(it.cnt == cp || cp == -1) - { - // redirect bot to next goal if it touched the waypoint of an untouchable checkpoint - // e.g. checkpoint in front of Stormkeep's warpzone - // the same workaround is applied in CTS game mode - if (raw_touch_check && vdist(this.origin - it.nearestwaypoint.origin, <, 30)) - { - cp = race_NextCheckpoint(cp); - raw_touch_check = false; - goto search_racecheckpoints; - } - navigation_routerating(this, it, 1000000, 5000); - } - }); - - navigation_goalrating_end(this); - - navigation_goalrating_timeout_set(this); - } -} - -void race_ScoreRules() -{ - GameRules_score_enabled(false); - GameRules_scoring(race_teams, 0, 0, { - if (race_teams) { - field_team(ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); - } else if (g_race_qualifying) { - field(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); - } else { - field(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - field(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - field(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); - } - }); -} - -void race_EventLog(string mode, entity actor) // use an alias for easy changing and quick editing later -{ - if(autocvar_sv_eventlog) - GameLogEcho(strcat(":race:", mode, ":", ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); -} - -float WinningCondition_Race(float fraglimit) -{ - float wc; - float n, c; - - n = 0; - c = 0; - FOREACH_CLIENT(IS_PLAYER(it), { - ++n; - if(CS(it).race_completed) - ++c; - }); - if(n && (n == c)) - return WINNING_YES; - wc = WinningCondition_Scores(fraglimit, 0); - - // ALWAYS initiate overtime, unless EVERYONE has finished the race! - if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME) - // do NOT support equality when the laps are all raced! - return WINNING_STARTSUDDENDEATHOVERTIME; - else - return WINNING_NEVER; -} - -float WinningCondition_QualifyingThenRace(float limit) -{ - float wc; - wc = WinningCondition_Scores(limit, 0); - - // NEVER initiate overtime - if(wc == WINNING_YES || wc == WINNING_STARTSUDDENDEATHOVERTIME) - { - return WINNING_YES; - } - - return wc; -} - -MUTATOR_HOOKFUNCTION(rc, ClientKill) -{ - if(g_race_qualifying) - M_ARGV(1, float) = 0; // killtime -} - -MUTATOR_HOOKFUNCTION(rc, AbortSpeedrun) -{ - entity player = M_ARGV(0, entity); - - if(autocvar_g_allow_checkpoints) - race_PreparePlayer(player); // nice try -} - -MUTATOR_HOOKFUNCTION(rc, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - float dt = M_ARGV(1, float); - - player.race_movetime_frac += dt; - float f = floor(player.race_movetime_frac); - player.race_movetime_frac -= f; - player.race_movetime_count += f; - player.race_movetime = player.race_movetime_frac + player.race_movetime_count; - -#ifdef SVQC - if(IS_PLAYER(player)) - { - if (player.race_penalty) - if (time > player.race_penalty) - player.race_penalty = 0; - if(player.race_penalty) - { - player.velocity = '0 0 0'; - set_movetype(player, MOVETYPE_NONE); - player.disableclientprediction = 2; - } - } -#endif - - // force kbd movement for fairness - float wishspeed; - vector wishvel; - - // if record times matter - // ensure nothing EVIL is being done (i.e. div0_evade) - // this hinders joystick users though - // but it still gives SOME analog control - wishvel.x = fabs(CS(player).movement.x); - wishvel.y = fabs(CS(player).movement.y); - if(wishvel.x != 0 && wishvel.y != 0 && wishvel.x != wishvel.y) - { - wishvel.z = 0; - wishspeed = vlen(wishvel); - if(wishvel.x >= 2 * wishvel.y) - { - // pure X motion - if(CS(player).movement.x > 0) - CS(player).movement_x = wishspeed; - else - CS(player).movement_x = -wishspeed; - CS(player).movement_y = 0; - } - else if(wishvel.y >= 2 * wishvel.x) - { - // pure Y motion - CS(player).movement_x = 0; - if(CS(player).movement.y > 0) - CS(player).movement_y = wishspeed; - else - CS(player).movement_y = -wishspeed; - } - else - { - // diagonal - if(CS(player).movement.x > 0) - CS(player).movement_x = M_SQRT1_2 * wishspeed; - else - CS(player).movement_x = -M_SQRT1_2 * wishspeed; - if(CS(player).movement.y > 0) - CS(player).movement_y = M_SQRT1_2 * wishspeed; - else - CS(player).movement_y = -M_SQRT1_2 * wishspeed; - } - } -} - -MUTATOR_HOOKFUNCTION(rc, reset_map_global) -{ - float s; - - Score_NicePrint(NULL); - - race_ClearRecords(); - PlayerScore_Sort(race_place, 0, 1, 0); - - FOREACH_CLIENT(true, { - if(it.race_place) - { - s = GameRules_scoring_add(it, RACE_FASTEST, 0); - if(!s) - it.race_place = 0; - } - race_EventLog(ftos(it.race_place), it); - }); - - if(g_race_qualifying == 2) - { - g_race_qualifying = 0; - independent_players = 0; - cvar_set("fraglimit", ftos(race_fraglimit)); - cvar_set("leadlimit", ftos(race_leadlimit)); - cvar_set("timelimit", ftos(race_timelimit)); - race_ScoreRules(); - } -} - -MUTATOR_HOOKFUNCTION(rc, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - race_PreparePlayer(player); - player.race_checkpoint = -1; - - string rr = RACE_RECORD; - - if(IS_REAL_CLIENT(player)) - { - msg_entity = player; - race_send_recordtime(MSG_ONE); - race_send_speedaward(MSG_ONE); - - speedaward_alltimebest = stof(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"))); - speedaward_alltimebest_holder = uid2name(db_get(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"))); - race_send_speedaward_alltimebest(MSG_ONE); - - float i; - for (i = 1; i <= RANKINGS_CNT; ++i) - { - race_SendRankings(i, 0, 0, MSG_ONE); - } - } -} - -MUTATOR_HOOKFUNCTION(rc, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - if(g_race_qualifying) - if(GameRules_scoring_add(player, RACE_FASTEST, 0)) - player.frags = FRAGS_LMS_LOSER; - else - player.frags = FRAGS_SPECTATOR; - - race_PreparePlayer(player); - player.race_checkpoint = -1; -} - -MUTATOR_HOOKFUNCTION(rc, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - - if(spawn_spot.target == "") - // Emergency: this wasn't a real spawnpoint. Can this ever happen? - race_PreparePlayer(player); - - // if we need to respawn, do it right - player.race_respawn_checkpoint = player.race_checkpoint; - player.race_respawn_spotref = spawn_spot; - - player.race_place = 0; -} - -MUTATOR_HOOKFUNCTION(rc, PutClientInServer) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - if(!game_stopped) - { - if(CS(player).killcount == FRAGS_SPECTATOR /* initial spawn */ || g_race_qualifying) // spawn - race_PreparePlayer(player); - else // respawn - race_RetractPlayer(player); - - race_AbandonRaceCheck(player); - } -} - -MUTATOR_HOOKFUNCTION(rc, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - frag_target.respawn_flags |= RESPAWN_FORCE; - race_AbandonRaceCheck(frag_target); -} - -MUTATOR_HOOKFUNCTION(rc, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); - - bot.havocbot_role = havocbot_role_race; - return true; -} - -MUTATOR_HOOKFUNCTION(rc, GetPressedKeys) -{ - entity player = M_ARGV(0, entity); - - if(CS(player).cvar_cl_allow_uidtracking == 1 && CS(player).cvar_cl_allow_uid2name == 1) - { - if (!player.stored_netname) - player.stored_netname = strzone(uid2name(player.crypto_idfp)); - if(player.stored_netname != player.netname) - { - db_put(ServerProgsDB, strcat("/uid2name/", player.crypto_idfp), player.netname); - strunzone(player.stored_netname); - player.stored_netname = strzone(player.netname); - } - } - - if (!IS_OBSERVER(player)) - { - if(vdist(player.velocity - player.velocity_z * '0 0 1', >, speedaward_speed)) - { - speedaward_speed = vlen(player.velocity - player.velocity_z * '0 0 1'); - speedaward_holder = player.netname; - speedaward_uid = player.crypto_idfp; - speedaward_lastupdate = time; - } - if (speedaward_speed > speedaward_lastsent && time - speedaward_lastupdate > 1) - { - string rr = RACE_RECORD; - race_send_speedaward(MSG_ALL); - speedaward_lastsent = speedaward_speed; - if (speedaward_speed > speedaward_alltimebest && speedaward_uid != "") - { - speedaward_alltimebest = speedaward_speed; - speedaward_alltimebest_holder = speedaward_holder; - speedaward_alltimebest_uid = speedaward_uid; - db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/speed"), ftos(speedaward_alltimebest)); - db_put(ServerProgsDB, strcat(GetMapname(), rr, "speed/crypto_idfp"), speedaward_alltimebest_uid); - race_send_speedaward_alltimebest(MSG_ALL); - } - } - } -} - -MUTATOR_HOOKFUNCTION(rc, ForbidPlayerScore_Clear) -{ - if(g_race_qualifying) - return true; // in qualifying, you don't lose score by observing -} - -MUTATOR_HOOKFUNCTION(rc, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(0, float) = race_teams; -} - -MUTATOR_HOOKFUNCTION(rc, Scores_CountFragsRemaining) -{ - // announce remaining frags if not in qualifying mode - if(!g_race_qualifying) - return true; -} - -MUTATOR_HOOKFUNCTION(rc, GetRecords) -{ - int record_page = M_ARGV(0, int); - string ret_string = M_ARGV(1, string); - - for(int i = record_page * 200; i < MapInfo_count && i < record_page * 200 + 200; ++i) - { - if(MapInfo_Get_ByID(i)) - { - float r = race_readTime(MapInfo_Map_bspname, 1); - - if(!r) - continue; - - string h = race_readName(MapInfo_Map_bspname, 1); - ret_string = strcat(ret_string, strpad(32, MapInfo_Map_bspname), " ", strpad(-8, TIME_ENCODED_TOSTRING(r)), " ", h, "\n"); - } - } - - M_ARGV(1, string) = ret_string; -} - -MUTATOR_HOOKFUNCTION(rc, HideTeamNagger) -{ - return true; // doesn't work so well -} - -MUTATOR_HOOKFUNCTION(rc, FixClientCvars) -{ - entity player = M_ARGV(0, entity); - - stuffcmd(player, "cl_cmd settemp cl_movecliptokeyboard 2\n"); -} - -MUTATOR_HOOKFUNCTION(rc, CheckRules_World) -{ - float checkrules_timelimit = M_ARGV(1, float); - float checkrules_fraglimit = M_ARGV(2, float); - - if(checkrules_timelimit >= 0) - { - if(!g_race_qualifying) - { - M_ARGV(0, float) = WinningCondition_Race(checkrules_fraglimit); - return true; - } - else if(g_race_qualifying == 2) - { - M_ARGV(0, float) = WinningCondition_QualifyingThenRace(checkrules_fraglimit); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(rc, ReadLevelCvars) -{ - if(g_race_qualifying == 2) - warmup_stage = 0; -} - -void race_Initialize() -{ - race_ScoreRules(); - if(g_race_qualifying == 2) - warmup_stage = 0; -} - -void rc_SetLimits() -{ - int fraglimit_override, leadlimit_override; - float timelimit_override, qualifying_override; - - if(autocvar_g_race_teams) - { - GameRules_teams(true); - race_teams = BITS(bound(2, autocvar_g_race_teams, 4)); - } - else - race_teams = 0; - - qualifying_override = autocvar_g_race_qualifying_timelimit_override; - fraglimit_override = autocvar_g_race_laps_limit; - leadlimit_override = 0; // currently not supported by race - timelimit_override = autocvar_timelimit_override; - - float want_qualifying = ((qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit) > 0; - - if(autocvar_g_campaign) - { - g_race_qualifying = 1; - independent_players = 1; - } - else if(want_qualifying) - { - g_race_qualifying = 2; - independent_players = 1; - race_fraglimit = (fraglimit_override >= 0) ? fraglimit_override : autocvar_fraglimit; - race_leadlimit = (leadlimit_override >= 0) ? leadlimit_override : autocvar_leadlimit; - race_timelimit = (timelimit_override >= 0) ? timelimit_override : autocvar_timelimit; - qualifying_override = (qualifying_override >= 0) ? qualifying_override : autocvar_g_race_qualifying_timelimit; - fraglimit_override = 0; - leadlimit_override = 0; - timelimit_override = qualifying_override; - } - else - g_race_qualifying = 0; - GameRules_limit_score(fraglimit_override); - GameRules_limit_lead(leadlimit_override); - GameRules_limit_time(timelimit_override); - GameRules_limit_time_qualifying(qualifying_override); -} diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qh b/qcsrc/server/mutators/mutator/gamemode_race.qh deleted file mode 100644 index 1e475e3ce6..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_race.qh +++ /dev/null @@ -1,18 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -void rc_SetLimits(); -void race_Initialize(); - -REGISTER_MUTATOR(rc, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - rc_SetLimits(); - - race_Initialize(); - } - return 0; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qc b/qcsrc/server/mutators/mutator/gamemode_tdm.qc deleted file mode 100644 index aad3193288..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qc +++ /dev/null @@ -1,63 +0,0 @@ -#include "gamemode_tdm.qh" - -int autocvar_g_tdm_teams; -int autocvar_g_tdm_teams_override; - -/*QUAKED spawnfunc_tdm_team (0 .5 .8) (-16 -16 -24) (16 16 32) -Team declaration for TDM gameplay, this allows you to decide what team names and control point models are used in your map. -Note: If you use spawnfunc_tdm_team entities you must define at least 2! However, unlike domination, you don't need to make a blank one too. -Keys: -"netname" Name of the team (for example Red, Blue, Green, Yellow, Life, Death, Offense, Defense, etc)... -"cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ -spawnfunc(tdm_team) -{ - if(!g_tdm || !this.cnt) { delete(this); return; } - - this.classname = "tdm_team"; - this.team = this.cnt + 1; -} - -// code from here on is just to support maps that don't have team entities -void tdm_SpawnTeam (string teamname, int teamcolor) -{ - entity this = new_pure(tdm_team); - this.netname = teamname; - this.cnt = teamcolor - 1; - this.team = teamcolor; - this.spawnfunc_checked = true; - //spawnfunc_tdm_team(this); -} - -void tdm_DelayedInit(entity this) -{ - // if no teams are found, spawn defaults - if(find(NULL, classname, "tdm_team") == NULL) - { - LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway."); - - int numteams = autocvar_g_tdm_teams_override; - if(numteams < 2) { numteams = autocvar_g_tdm_teams; } - - int teams = BITS(bound(2, numteams, 4)); - if(teams & BIT(0)) - tdm_SpawnTeam("Red", NUM_TEAM_1); - if(teams & BIT(1)) - tdm_SpawnTeam("Blue", NUM_TEAM_2); - if(teams & BIT(2)) - tdm_SpawnTeam("Yellow", NUM_TEAM_3); - if(teams & BIT(3)) - tdm_SpawnTeam("Pink", NUM_TEAM_4); - } -} - -MUTATOR_HOOKFUNCTION(tdm, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) -{ - M_ARGV(1, string) = "tdm_team"; - return true; -} - -MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining) -{ - // announce remaining frags - return true; -} diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qh b/qcsrc/server/mutators/mutator/gamemode_tdm.qh deleted file mode 100644 index c163962faf..0000000000 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qh +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../gamemode.qh" - -int autocvar_g_tdm_point_limit; -int autocvar_g_tdm_point_leadlimit; -bool autocvar_g_tdm_team_spawns; -void tdm_DelayedInit(entity this); - -REGISTER_MUTATOR(tdm, false) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - GameRules_teams(true); - GameRules_spawning_teams(autocvar_g_tdm_team_spawns); - GameRules_limit_score(autocvar_g_tdm_point_limit); - GameRules_limit_lead(autocvar_g_tdm_point_leadlimit); - - InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); - } - return 0; -} diff --git a/qcsrc/server/pathlib/debug.qc b/qcsrc/server/pathlib/debug.qc index b84ae6414e..13dbda5200 100644 --- a/qcsrc/server/pathlib/debug.qc +++ b/qcsrc/server/pathlib/debug.qc @@ -8,9 +8,7 @@ MODEL(SQUARE_BAD, "models/pathlib/badsquare.md3"); MODEL(EDGE, "models/pathlib/edge.md3"); #ifdef TURRET_DEBUG -void mark_error(vector where,float lifetime); -void mark_info(vector where,float lifetime); -entity mark_misc(vector where,float lifetime); +#include <common/turrets/util.qh> #endif void pathlib_showpath(entity start) diff --git a/qcsrc/server/pathlib/debug.qh b/qcsrc/server/pathlib/debug.qh index 811c031aff..6f1bd98950 100644 --- a/qcsrc/server/pathlib/debug.qh +++ b/qcsrc/server/pathlib/debug.qh @@ -1,2 +1,8 @@ #pragma once #include "pathlib.qh" + +#if DEBUGPATHING +void pathlib_showpath(entity start); +void pathlib_showpath2(entity path); +void pathlib_showsquare(vector where,float goodsquare,float _lifetime); +#endif diff --git a/qcsrc/server/pathlib/main.qc b/qcsrc/server/pathlib/main.qc index c2f3326030..1aeae109c3 100644 --- a/qcsrc/server/pathlib/main.qc +++ b/qcsrc/server/pathlib/main.qc @@ -30,9 +30,7 @@ void dumpnode(entity n) } #if DEBUGPATHING -void pathlib_showpath(entity start); -void pathlib_showpath2(entity path); -void pathlib_showsquare(vector where,float goodsquare,float _lifetime); +#include "debug.qh" #endif diff --git a/qcsrc/server/pathlib/pathlib.qh b/qcsrc/server/pathlib/pathlib.qh index 21ef8b3cbc..d1bafe392a 100644 --- a/qcsrc/server/pathlib/pathlib.qh +++ b/qcsrc/server/pathlib/pathlib.qh @@ -15,11 +15,6 @@ const vector PLIB_FORWARD = '0 1 0'; const vector PLIB_RIGHT = '1 0 0'; //#define PLIB_LEFT '-1 0 0' -#if DEBUGPATHING -void pathlib_showpath(entity start); -void pathlib_showpath2(entity path); -#endif - entity openlist; entity closedlist; diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index 06bdb1428f..3805df3f26 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -3,9 +3,9 @@ #include <common/effects/all.qh> #include "bot/api.qh" #include "cheats.qh" +#include "clientkill.qh" #include "g_damage.qh" #include "handicap.qh" -#include "g_subs.qh" #include "miscfunctions.qh" #include "portals.qh" #include "teamplay.qh" @@ -15,17 +15,20 @@ #include "../common/anim.qh" #include "../common/animdecide.qh" #include "../common/csqcmodel_settings.qh" +#include "../common/gamemodes/sv_rules.qh" #include "../common/deathtypes/all.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/subs.qh" #include "../common/playerstats.qh" #include "../lib/csqcmodel/sv_model.qh" #include "../common/minigames/sv_minigames.qh" +#include <common/gamemodes/_mod.qh> + #include "../common/physics/player.qh" #include "../common/effects/qc/_mod.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" -#include "../common/triggers/include.qh" +#include "../common/mapobjects/_mod.qh" #include "../common/wepent.qh" #include "weapons/weaponstats.qh" @@ -74,6 +77,7 @@ void CopyBody(entity this, float keepvelocity) clone.effects = this.effects; clone.glowmod = this.glowmod; clone.event_damage = this.event_damage; + clone.event_heal = this.event_heal; clone.anim_state = this.anim_state; clone.anim_time = this.anim_time; clone.anim_lower_action = this.anim_lower_action; @@ -89,8 +93,8 @@ void CopyBody(entity this, float keepvelocity) clone.dphitcontentsmask = this.dphitcontentsmask; clone.death_time = this.death_time; clone.pain_finished = this.pain_finished; - clone.health = this.health; - clone.armorvalue = this.armorvalue; + SetResourceAmountExplicit(clone, RESOURCE_HEALTH, GetResourceAmount(this, RESOURCE_HEALTH)); + SetResourceAmountExplicit(clone, RESOURCE_ARMOR, GetResourceAmount(this, RESOURCE_ARMOR)); clone.armortype = this.armortype; clone.model = this.model; clone.modelindex = this.modelindex; @@ -173,7 +177,7 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da vector v; Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + v = healtharmor_applydamage(GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); take = v.x; save = v.y; @@ -192,8 +196,8 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da if (take > 100) Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; + TakeResource(this, RESOURCE_ARMOR, save); + TakeResource(this, RESOURCE_HEALTH, take); // pause regeneration for 5 seconds this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); @@ -201,7 +205,7 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da this.dmg_take = this.dmg_take + take;//max(take - 10, 0); this.dmg_inflictor = inflictor; - if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) + if (GetResourceAmount(this, RESOURCE_HEALTH) <= -autocvar_sv_gibhealth && this.alpha >= 0) { // don't use any animations as a gib this.frame = 0; @@ -310,8 +314,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, vector v; float excess; - dh = max(this.health, 0); - da = max(this.armorvalue, 0); + dh = max(GetResourceAmount(this, RESOURCE_HEALTH), 0); + da = max(GetResourceAmount(this, RESOURCE_ARMOR), 0); if(!DEATH_ISSPECIAL(deathtype)) { @@ -359,7 +363,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, else Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + v = healtharmor_applydamage(GetResourceAmount(this, RESOURCE_ARMOR), autocvar_g_balance_armor_blockpercent, deathtype, damage); take = v.x; save = v.y; @@ -388,8 +392,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, } MUTATOR_CALLHOOK(PlayerDamage_SplitHealthArmor, inflictor, attacker, this, force, take, save, deathtype, damage); - take = bound(0, M_ARGV(4, float), this.health); - save = bound(0, M_ARGV(5, float), this.armorvalue); + take = bound(0, M_ARGV(4, float), GetResourceAmount(this, RESOURCE_HEALTH)); + save = bound(0, M_ARGV(5, float), GetResourceAmount(this, RESOURCE_ARMOR)); excess = max(0, damage - take - save); if(sound_allowed(MSG_BROADCAST, attacker)) @@ -411,8 +415,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, { if (!(this.flags & FL_GODMODE)) { - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; + TakeResource(this, RESOURCE_ARMOR, save); + TakeResource(this, RESOURCE_HEALTH, take); // pause regeneration for 5 seconds if(take) this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); @@ -432,19 +436,19 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, animdecide_setaction(this, ANIMACTION_PAIN2, true); } } - + float myhp = GetResourceAmount(this, RESOURCE_HEALTH); + if(myhp > 1) + if(myhp < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this) if(sound_allowed(MSG_BROADCAST, attacker)) - if(this.health < 25 || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || take > 20 || attacker != this) - if(this.health > 1) // exclude pain sounds for laserjumps as long as you aren't REALLY low on health and would die of the next two { if(deathtype == DEATH_FALL.m_id) PlayerSound(this, playersound_fall, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 75) + else if(myhp > 75) PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 50) + else if(myhp > 50) PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); - else if(this.health > 25) + else if(myhp > 25) PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); else PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); @@ -454,13 +458,23 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // throw off bot aim temporarily float shake; - if(IS_BOT_CLIENT(this) && this.health >= 1) + if(IS_BOT_CLIENT(this) && GetResourceAmount(this, RESOURCE_HEALTH) >= 1) { shake = damage * 5 / (bound(0,skill,100) + 1); this.v_angle_x = this.v_angle.x + (random() * 2 - 1) * shake; this.v_angle_y = this.v_angle.y + (random() * 2 - 1) * shake; this.v_angle_x = bound(-90, this.v_angle.x, 90); } + + if (this != attacker) { + float realdmg = damage - excess; + if (IS_PLAYER(attacker) && DIFF_TEAM(attacker, this)) { + GameRules_scoring_add(attacker, DMG, realdmg); + } + if (IS_PLAYER(this)) { + GameRules_scoring_add(this, DMGTAKEN, realdmg); + } + } } else this.max_armorvalue += (save + take); @@ -469,16 +483,6 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, this.dmg_take = this.dmg_take + take;//max(take - 10, 0); this.dmg_inflictor = inflictor; - if (this != attacker) { - float realdmg = damage - excess; - if (IS_PLAYER(attacker)) { - GameRules_scoring_add(attacker, DMG, realdmg); - } - if (IS_PLAYER(this)) { - GameRules_scoring_add(this, DMGTAKEN, realdmg); - } - } - bool abot = (IS_BOT_CLIENT(attacker)); bool vbot = (IS_BOT_CLIENT(this)); @@ -497,8 +501,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, valid_damage_for_weaponstats = true; } - dh = dh - max(this.health, 0); - da = da - max(this.armorvalue, 0); + dh = dh - max(GetResourceAmount(this, RESOURCE_HEALTH), 0); + da = da - max(GetResourceAmount(this, RESOURCE_ARMOR), 0); if(valid_damage_for_weaponstats) { WeaponStats_LogDamage(awep.m_id, abot, this.(weaponentity).m_weapon.m_id, vbot, dh + da); @@ -506,7 +510,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage); - if (this.health < 1) + if (GetResourceAmount(this, RESOURCE_HEALTH) < 1) { float defer_ClientKill_Now_TeamChange; defer_ClientKill_Now_TeamChange = false; @@ -549,8 +553,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, if(this.classname != "body") Obituary (attacker, inflictor, this, deathtype, weaponentity); - // increment frag counter for used weapon type - Weapon w = DEATH_WEAPONOF(deathtype); + // increment frag counter for used weapon type + Weapon w = DEATH_WEAPONOF(deathtype); if(w != WEP_Null && accuracy_isgooddamage(attacker, this)) CS(attacker).accuracy.(accuracy_frags[w.m_id-1]) += 1; @@ -580,7 +584,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // player could have been miraculously resuscitated ;) // e.g. players in freezetag get frozen, they don't really die - if(this.health >= 1 || !(IS_PLAYER(this) || this.classname == "body")) + if(GetResourceAmount(this, RESOURCE_HEALTH) >= 1 || !(IS_PLAYER(this) || this.classname == "body")) return; if (!this.respawn_time) // can be set in the mutator hook PlayerDies @@ -588,8 +592,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // when we get here, player actually dies - Unfreeze(this); // remove any icy remains - this.health = 0; // Unfreeze resets health, so we need to set it back + Unfreeze(this, false); // remove any icy remains // clear waypoints WaypointSprite_PlayerDead(this); @@ -629,6 +632,7 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, // set damage function to corpse damage this.event_damage = PlayerCorpseDamage; + this.event_heal = func_null; // call the corpse damage function just in case it wants to gib this.event_damage(this, inflictor, attacker, excess, deathtype, weaponentity, hitloc, force); @@ -664,371 +668,11 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, } } -bool MoveToTeam(entity client, int team_colour, int type) +bool PlayerHeal(entity targ, entity inflictor, float amount, float limit) { - int lockteams_backup = lockteams; // backup any team lock - lockteams = 0; // disable locked teams - TeamchangeFrags(client); // move the players frags - if (!SetPlayerTeamSimple(client, team_colour)) - { + if(GetResourceAmount(targ, RESOURCE_HEALTH) <= 0 || GetResourceAmount(targ, RESOURCE_HEALTH) >= limit) return false; - } - Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, DMG_NOWEP, 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 dedicated_print(string input) -{ - if (server_is_dedicated) print(input); -} - -void PrintToChat(entity player, string text) -{ - text = strcat("\{1}^7", text, "\n"); - sprint(player, text); -} - -void DebugPrintToChat(entity player, string text) -{ - if (autocvar_developer) - { - PrintToChat(player, text); - } -} - -void PrintToChatAll(string text) -{ - text = strcat("\{1}^7", text, "\n"); - bprint(text); -} - -void DebugPrintToChatAll(string text) -{ - if (autocvar_developer) - { - PrintToChatAll(text); - } -} - -void PrintToChatTeam(int teamnum, string text) -{ - text = strcat("\{1}^7", text, "\n"); - FOREACH_CLIENT(IS_REAL_CLIENT(it), - { - if (it.team == teamnum) - { - sprint(it, text); - } - }); -} -void DebugPrintToChatTeam(int teamnum, string text) -{ - if (autocvar_developer) - { - PrintToChatTeam(teamnum, text); - } -} - -/** - * message "": do not say, just test flood control - * return value: - * 1 = accept - * 0 = reject - * -1 = fake accept - */ -int Say(entity source, int teamsay, entity privatesay, string msgin, bool floodcontrol) -{ - if (!teamsay && !privatesay && substring(msgin, 0, 1) == " ") - msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) - - if(source) - msgin = formatmessage(source, msgin); - - string colorstr; - if (!IS_PLAYER(source)) - colorstr = "^0"; // black for spectators - else if(teamplay) - colorstr = Team_ColorCode(source.team); - else - { - colorstr = ""; - teamsay = false; - } - - if(game_stopped) - teamsay = false; - - if (!source) { - colorstr = ""; - teamsay = false; - } - - if(msgin != "") - msgin = trigger_magicear_processmessage_forallears(source, teamsay, privatesay, msgin); - - /* - * using bprint solves this... me stupid - // how can we prevent the message from appearing in a listen server? - // for now, just give "say" back and only handle say_team - if(!teamsay) - { - clientcommand(source, strcat("say ", msgin)); - return; - } - */ - - string namestr = ""; - if (source) - namestr = playername(source, autocvar_g_chat_teamcolors); - - string colorprefix = (strdecolorize(namestr) == namestr) ? "^3" : "^7"; - - string msgstr, cmsgstr; - string privatemsgprefix = string_null; - int privatemsgprefixlen = 0; - if (msgin == "") { - msgstr = cmsgstr = ""; - } else { - if(privatesay) - { - msgstr = strcat("\{1}\{13}* ", colorprefix, namestr, "^3 tells you: ^7"); - privatemsgprefixlen = strlen(msgstr); - msgstr = strcat(msgstr, msgin); - cmsgstr = strcat(colorstr, colorprefix, namestr, "^3 tells you:\n^7", msgin); - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay, autocvar_g_chat_teamcolors), ": ^7"); - } - else if(teamsay) - { - if(strstrofs(msgin, "/me", 0) >= 0) - { - //msgin = strreplace("/me", "", msgin); - //msgin = substring(msgin, 3, strlen(msgin)); - msgin = strreplace("/me", strcat(colorstr, "(", colorprefix, namestr, colorstr, ")^7"), msgin); - msgstr = strcat("\{1}\{13}^4* ", "^7", msgin); - } - else - msgstr = strcat("\{1}\{13}", colorstr, "(", colorprefix, namestr, colorstr, ") ^7", msgin); - cmsgstr = strcat(colorstr, "(", colorprefix, namestr, colorstr, ")\n^7", msgin); - } - else - { - if(strstrofs(msgin, "/me", 0) >= 0) - { - //msgin = strreplace("/me", "", msgin); - //msgin = substring(msgin, 3, strlen(msgin)); - msgin = strreplace("/me", strcat(colorprefix, namestr), msgin); - msgstr = strcat("\{1}^4* ", "^7", msgin); - } - else { - msgstr = "\{1}"; - msgstr = strcat(msgstr, (namestr != "") ? strcat(colorprefix, namestr, "^7: ") : "^7"); - msgstr = strcat(msgstr, msgin); - } - cmsgstr = ""; - } - msgstr = strcat(strreplace("\n", " ", msgstr), "\n"); // newlines only are good for centerprint - } - - string fullmsgstr = msgstr; - string fullcmsgstr = cmsgstr; - - // FLOOD CONTROL - int flood = 0; - var .float flood_field = floodcontrol_chat; - if(floodcontrol && source) - { - float flood_spl; - float flood_burst; - float flood_lmax; - float lines; - if(privatesay) - { - flood_spl = autocvar_g_chat_flood_spl_tell; - flood_burst = autocvar_g_chat_flood_burst_tell; - flood_lmax = autocvar_g_chat_flood_lmax_tell; - flood_field = floodcontrol_chattell; - } - else if(teamsay) - { - flood_spl = autocvar_g_chat_flood_spl_team; - flood_burst = autocvar_g_chat_flood_burst_team; - flood_lmax = autocvar_g_chat_flood_lmax_team; - flood_field = floodcontrol_chatteam; - } - else - { - flood_spl = autocvar_g_chat_flood_spl; - flood_burst = autocvar_g_chat_flood_burst; - flood_lmax = autocvar_g_chat_flood_lmax; - flood_field = floodcontrol_chat; - } - flood_burst = max(0, flood_burst - 1); - // to match explanation in default.cfg, a value of 3 must allow three-line bursts and not four! - - // do flood control for the default line size - if(msgstr != "") - { - getWrappedLine_remaining = msgstr; - msgstr = ""; - lines = 0; - while(getWrappedLine_remaining && (!flood_lmax || lines <= flood_lmax)) - { - msgstr = strcat(msgstr, " ", getWrappedLineLen(82.4289758859709, strlennocol)); // perl averagewidth.pl < gfx/vera-sans.width - ++lines; - } - msgstr = substring(msgstr, 1, strlen(msgstr) - 1); - - if(getWrappedLine_remaining != "") - { - msgstr = strcat(msgstr, "\n"); - flood = 2; - } - - if (time >= source.(flood_field)) - { - source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + lines * flood_spl; - } - else - { - flood = 1; - msgstr = fullmsgstr; - } - } - else - { - if (time >= source.(flood_field)) - source.(flood_field) = max(time - flood_burst * flood_spl, source.(flood_field)) + flood_spl; - else - flood = 1; - } - - if (timeout_status == TIMEOUT_ACTIVE) // when game is paused, no flood protection - source.(flood_field) = flood = 0; - } - - string sourcemsgstr, sourcecmsgstr; - if(flood == 2) // cannot happen for empty msgstr - { - if(autocvar_g_chat_flood_notify_flooder) - { - sourcemsgstr = strcat(msgstr, "\n^3FLOOD CONTROL: ^7message too long, trimmed\n"); - sourcecmsgstr = ""; - } - else - { - sourcemsgstr = fullmsgstr; - sourcecmsgstr = fullcmsgstr; - } - cmsgstr = ""; - } - else - { - sourcemsgstr = msgstr; - sourcecmsgstr = cmsgstr; - } - - if (!privatesay && source && !IS_PLAYER(source)) - { - if (!game_stopped) - if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage)) - teamsay = -1; // spectators - } - - if(flood) - LOG_INFO("NOTE: ", playername(source, true), "^7 is flooding."); - - // build sourcemsgstr by cutting off a prefix and replacing it by the other one - if(privatesay) - sourcemsgstr = strcat(privatemsgprefix, substring(sourcemsgstr, privatemsgprefixlen, -1)); - - int ret; - if(source && CS(source).muted) - { - // always fake the message - ret = -1; - } - else if(flood == 1) - { - if (autocvar_g_chat_flood_notify_flooder) - { - sprint(source, strcat("^3FLOOD CONTROL: ^7wait ^1", ftos(source.(flood_field) - time), "^3 seconds\n")); - ret = 0; - } - else - ret = -1; - } - else - { - ret = 1; - } - - if (privatesay && source && !IS_PLAYER(source)) - { - if (!game_stopped) - if ((privatesay && !IS_PLAYER(privatesay)) || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage)) - ret = -1; // just hide the message completely - } - - MUTATOR_CALLHOOK(ChatMessage, source, ret); - ret = M_ARGV(1, int); - - if(sourcemsgstr != "" && ret != 0) - { - if(ret < 0) // faked message, because the player is muted - { - sprint(source, sourcemsgstr); - if(sourcecmsgstr != "" && !privatesay) - centerprint(source, sourcecmsgstr); - } - else if(privatesay) // private message, between 2 people only - { - sprint(source, sourcemsgstr); - if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled - if(!MUTATOR_CALLHOOK(ChatMessageTo, privatesay, source)) - { - sprint(privatesay, msgstr); - if(cmsgstr != "") - centerprint(privatesay, cmsgstr); - } - } - else if ( teamsay && CS(source).active_minigame ) - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && CS(it).active_minigame == CS(source).active_minigame && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr)); - } - else if(teamsay > 0) // team message, only sent to team mates - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - if(sourcecmsgstr != "") - centerprint(source, sourcecmsgstr); - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && it.team == source.team && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), { - sprint(it, msgstr); - if(cmsgstr != "") - centerprint(it, cmsgstr); - }); - } - else if(teamsay < 0) // spectator message, only sent to spectators - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(!IS_PLAYER(it) && IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr)); - } - else - { - if (source) { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - MX_Say(strcat(playername(source, true), "^7: ", msgin)); - } - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr)); - } - } - - return ret; + GiveResourceWithLimit(targ, RESOURCE_HEALTH, amount, limit); + return true; } diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh index ee073ccb44..514b34726c 100644 --- a/qcsrc/server/player.qh +++ b/qcsrc/server/player.qh @@ -2,52 +2,13 @@ .entity pusher; .float pushltime; -.float istypefrag; +.bool istypefrag; .float CopyBody_nextthink; .void(entity this) CopyBody_think; void CopyBody_Think(entity this); void CopyBody(entity this, float keepvelocity); -void dedicated_print(string input); - -/// \brief Print the string to player's chat. -/// \param[in] player Player to print to. -/// \param[in] text Text to print. -/// \return No return. -void PrintToChat(entity player, string text); - -/// \brief Print the string to player's chat if the server cvar "developer" is -/// not 0. -/// \param[in] player Player to print to. -/// \param[in] text Text to print. -/// \return No return. -void DebugPrintToChat(entity player, string text); - -/// \brief Prints the string to all players' chat. -/// \param[in] text Text to print. -/// \return No return. -void PrintToChatAll(string text); - -/// \brief Prints the string to all players' chat if the server cvar "developer" -/// is not 0. -/// \param[in] text Text to print. -/// \return No return. -void DebugPrintToChatAll(string text); - -/// \brief Print the string to chat of all players of the specified team. -/// \param[in] teamnum Team to print to. See NUM_TEAM constants. -/// \param[in] text Text to print. -/// \return No return. -void PrintToChatTeam(int teamnum, string text); - -/// \brief Print the string to chat of all players of the specified team if the -/// server cvar "developer" is not 0. -/// \param[in] teamnum Team to print to. See NUM_TEAM constants. -/// \param[in] text Text to print. -/// \return No return. -void DebugPrintToChatTeam(int teamnum, string text); - void player_setupanimsformodel(entity this); void player_anim(entity this); @@ -67,15 +28,6 @@ void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float da void calculate_player_respawn_time(entity this); -void ClientKill_Now_TeamChange(entity this); - -/// \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, .entity weaponentity, vector hitloc, vector force); -int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); +bool PlayerHeal(entity targ, entity inflictor, float amount, float limit); diff --git a/qcsrc/server/playerdemo.qc b/qcsrc/server/playerdemo.qc deleted file mode 100644 index 411d826a87..0000000000 --- a/qcsrc/server/playerdemo.qc +++ /dev/null @@ -1,170 +0,0 @@ -#include "playerdemo.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include "defs.qh" - #include "playerdemo.qh" - #include <common/state.qh> -#endif - -.float playerdemo_fh; -.float playerdemo_mode; -.float playerdemo_starttime; -.float playerdemo_time; -const float PLAYERDEMO_MODE_OFF = 0; -const float PLAYERDEMO_MODE_READING = 1; -const float PLAYERDEMO_MODE_WRITING = 2; -void playerdemo_init(entity this) -{ - this.playerdemo_mode = PLAYERDEMO_MODE_OFF; -} -void playerdemo_shutdown(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_OFF) - { - LOG_INFO("playerdemo: ", this.netname, " closed"); - fclose(this.playerdemo_fh); - } - this.playerdemo_mode = 0; -} -void playerdemo_open_read(entity this, string f) -{ - playerdemo_shutdown(this); - this.playerdemo_mode = PLAYERDEMO_MODE_READING; - this.playerdemo_fh = fopen(f, FILE_READ); - this.playerdemo_starttime = time - 1; - this.playerdemo_time = stof(fgets(this.playerdemo_fh)); - this.playerdemo_time += this.playerdemo_starttime; - set_movetype(this, MOVETYPE_NONE); - LOG_INFO("playerdemo: ", this.netname, " reading from ", f); -} -void playerdemo_open_write(entity this, string f) -{ - playerdemo_shutdown(this); - this.playerdemo_mode = PLAYERDEMO_MODE_WRITING; - this.playerdemo_fh = fopen(f, FILE_WRITE); - this.playerdemo_starttime = time - 1; - LOG_INFO("playerdemo: ", this.netname, " writing to ", f); - LOG_INFO("WARNING: playerdemo file format is incomplete and not stable yet. DO NOT RELY ON IT!"); -} -#define PLAYERDEMO_FIELD(ent,func,t,f) func##t(ent,f,#f); -#define PLAYERDEMO_FIELDS(ent,func) \ - PLAYERDEMO_FIELD(ent,func,originvector,origin) \ - PLAYERDEMO_FIELD(ent,func,vector,angles) \ - PLAYERDEMO_FIELD(ent,func,sizevector,mins) \ - PLAYERDEMO_FIELD(ent,func,sizevector,maxs) \ - PLAYERDEMO_FIELD(ent,func,vector,v_angle) \ - PLAYERDEMO_FIELD(ent,func,modelstring,model) \ - PLAYERDEMO_FIELD(ent,func,string,playermodel) \ - PLAYERDEMO_FIELD(ent,func,float,skin) \ - PLAYERDEMO_FIELD(ent,func,string,playerskin) \ - PLAYERDEMO_FIELD(ent,func,float,frame) \ - PLAYERDEMO_FIELD(ent,func,float,effects) \ - /* PLAYERDEMO_FIELD(ent,func,float,switchweapon) */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button0) /* TODO: PHYS_INPUT_BUTTON_ATCK */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button3) /* TODO: PHYS_INPUT_BUTTON_ATCK2 */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button5) /* TODO: PHYS_INPUT_BUTTON_CROUCH */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,button6) /* TODO: PHYS_INPUT_BUTTON_HOOK */ \ - PLAYERDEMO_FIELD(CS(ent),func,float,buttonuse) /* TODO: PHYS_INPUT_BUTTON_USE */ \ - PLAYERDEMO_FIELD(ent,func,float,flags) \ - // end of list - -void playerdemo_write_originvector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_sizevector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_vector(entity this, .vector f, string name) -{ - fputs(this.playerdemo_fh, strcat(vtos(this.(f)), "\n")); -} -void playerdemo_write_string(entity this, .string f, string name) -{ - fputs(this.playerdemo_fh, strcat(this.(f), "\n")); -} -void playerdemo_write_modelstring(entity this, .string f, string name) -{ - fputs(this.playerdemo_fh, strcat(this.(f), "\n")); -} -void playerdemo_write_float(entity this, .float f, string name) -{ - fputs(this.playerdemo_fh, strcat(ftos(this.(f)), "\n")); -} -void playerdemo_write(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_WRITING) - return; - fputs(this.playerdemo_fh, strcat(ftos(time - this.playerdemo_starttime), "\n")); - PLAYERDEMO_FIELDS(this, playerdemo_write_) -} -void playerdemo_read_originvector(entity this, .vector f, string name) -{ - setorigin(this, stov(fgets(this.playerdemo_fh))); -} -void playerdemo_read_sizevector(entity this, .vector f, string name) -{ - this.(f) = stov(fgets(this.playerdemo_fh)); - setsize(this, this.mins, this.maxs); -} -void playerdemo_read_vector(entity this, .vector f, string name) -{ - this.(f) = stov(fgets(this.playerdemo_fh)); -} -void playerdemo_read_string(entity this, .string f, string name) -{ - string s = fgets(this.playerdemo_fh); - if (s != this.(f)) - { - /* - if(this.f) - strunzone(this.f); - */ - this.(f) = strzone(s); - } -} -void playerdemo_read_modelstring(entity this, .string f, string name) -{ - string s = fgets(this.playerdemo_fh); - if (s != this.(f)) - _setmodel(this, s); -} -void playerdemo_read_float(entity this, .float f, string name) -{ - this.(f) = stof(fgets(this.playerdemo_fh)); -} -float playerdemo_read(entity this) -{ - if(this.playerdemo_mode != PLAYERDEMO_MODE_READING) - return 0; - if(this.playerdemo_time < 0) - return 1; - float t; - t = time; - while(time >= this.playerdemo_time) - { - PLAYERDEMO_FIELDS(this, playerdemo_read_) - { - time = this.playerdemo_time; - PlayerPreThink(this); - // not running physics though... this is just so we can run weapon stuff - PlayerPostThink(this); - } - this.playerdemo_time = stof(fgets(this.playerdemo_fh)); - if(this.playerdemo_time == 0) - { - this.playerdemo_time = -1; - return 1; - } - this.playerdemo_time += this.playerdemo_starttime; - } - this.velocity = '0 0 0'; - CS(this).movement = '0 0 0'; - this.dmg_take = 0; // so screen doesn't stay blurry - this.dmg_save = 0; - this.dmg_inflictor = NULL; - time = t; - return 1; -} diff --git a/qcsrc/server/playerdemo.qh b/qcsrc/server/playerdemo.qh deleted file mode 100644 index c2da2bc6f0..0000000000 --- a/qcsrc/server/playerdemo.qh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -void playerdemo_init(entity this); -void playerdemo_shutdown(entity this); -void playerdemo_write(entity this); -float playerdemo_read(entity this); - -void playerdemo_open_read(entity this, string f); -void playerdemo_open_write(entity this, string f); diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index ca0dc20fd7..99f0c0d82e 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -6,8 +6,8 @@ #include "../common/constants.qh" #include "../common/deathtypes/all.qh" #include "../common/notifications/all.qh" -#include "../common/triggers/teleporters.qh" -#include "../common/triggers/subs.qh" +#include "../common/mapobjects/teleporters.qh" +#include "../common/mapobjects/subs.qh" #include "../common/util.qh" #include <common/weapons/_all.qh> #include "../lib/csqcmodel/sv_model.qh" @@ -198,9 +198,9 @@ float Portal_TeleportPlayer(entity teleporter, entity player) // reset fade counter teleporter.portal_wants_to_vanish = 0; - teleporter.fade_time = time + autocvar_g_balance_portal_lifetime; - teleporter.health = autocvar_g_balance_portal_health; - teleporter.enemy.health = autocvar_g_balance_portal_health; + teleporter.fade_time = ((autocvar_g_balance_portal_lifetime >= 0) ? time + autocvar_g_balance_portal_lifetime : 0); + SetResourceAmountExplicit(teleporter, RESOURCE_HEALTH, autocvar_g_balance_portal_health); + SetResourceAmountExplicit(teleporter.enemy, RESOURCE_HEALTH, autocvar_g_balance_portal_health); return 1; } @@ -323,7 +323,6 @@ void Portal_Touch(entity this, entity toucher) toucher.effects += EF_BLUE - EF_RED; } -void Portal_Think(entity this); void Portal_MakeBrokenPortal(entity portal) { portal.skin = 2; @@ -384,7 +383,7 @@ void Portal_Connect(entity teleporter, entity destination) destination.enemy = teleporter; Portal_MakeInPortal(teleporter); Portal_MakeOutPortal(destination); - teleporter.fade_time = time + autocvar_g_balance_portal_lifetime; + teleporter.fade_time = ((autocvar_g_balance_portal_lifetime >= 0) ? time + autocvar_g_balance_portal_lifetime : 0); destination.fade_time = teleporter.fade_time; teleporter.portal_wants_to_vanish = 0; destination.portal_wants_to_vanish = 0; @@ -436,8 +435,8 @@ void Portal_Damage(entity this, entity inflictor, entity attacker, float damage, if(attacker != this.aiment) if(IS_INDEPENDENT_PLAYER(attacker) || IS_INDEPENDENT_PLAYER(this.aiment)) return; - this.health -= damage; - if(this.health < 0) + TakeResource(this, RESOURCE_HEALTH, damage); + if(GetResourceAmount(this, RESOURCE_HEALTH) < 0) Portal_Remove(this, 1); } @@ -495,7 +494,7 @@ void Portal_Think(entity this) this.nextthink = time; - if(time > this.fade_time) + if(this.fade_time && time > this.fade_time) Portal_Remove(this, 0); } @@ -639,8 +638,8 @@ entity Portal_Spawn(entity own, vector org, vector ang) portal.portal_activatetime = time + 0.1; portal.takedamage = DAMAGE_AIM; portal.event_damage = Portal_Damage; - portal.fade_time = time + autocvar_g_balance_portal_lifetime; - portal.health = autocvar_g_balance_portal_health; + portal.fade_time = ((autocvar_g_balance_portal_lifetime >= 0) ? time + autocvar_g_balance_portal_lifetime : 0); + SetResourceAmountExplicit(portal, RESOURCE_HEALTH, autocvar_g_balance_portal_health); setmodel(portal, MDL_PORTAL); portal.savemodelindex = portal.modelindex; setcefc(portal, Portal_Customize); diff --git a/qcsrc/server/portals.qh b/qcsrc/server/portals.qh index f3528d0810..20a2bd930c 100644 --- a/qcsrc/server/portals.qh +++ b/qcsrc/server/portals.qh @@ -11,3 +11,5 @@ void Portal_ClearWithID(entity own, float id); vector Portal_ApplyTransformToPlayerAngle(vector transform, vector vangle); void Portal_ClearAll_PortalsOnly(entity own); + +void Portal_Think(entity this); diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index 7f7f19b518..5286032fb5 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -14,7 +14,9 @@ #include <common/gamemodes/rules.qh> #include <common/net_linked.qh> #include <common/state.qh> -#include "../common/triggers/subs.qh" +#include <common/weapons/weapon/porto.qh> +#include "../common/mapobjects/subs.qh" +#include <common/mapobjects/triggers.qh> #include "../lib/warpzone/util_server.qh" #include "../lib/warpzone/common.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" @@ -29,8 +31,6 @@ void race_InitSpectator() race_SendNextCheckpoint(msg_entity.enemy, 1); } -void W_Porto_Fail(entity this, float failhard); - float race_readTime(string map, float pos) { string rr = ((g_cts) ? CTS_RECORD : ((g_ctf) ? CTF_RECORD : RACE_RECORD)); @@ -104,8 +104,6 @@ string race_readName(string map, float pos) const float MAX_CHECKPOINTS = 255; -spawnfunc(target_checkpoint); - .float race_penalty; .float race_penalty_accumulator; .string race_penalty_reason; @@ -224,6 +222,14 @@ void race_send_speedaward_alltimebest(float msg) WriteString(msg, speedaward_alltimebest_holder); } +void race_send_rankings_cnt(float msg) +{ + WriteHeader(msg, TE_CSQC_RACE); + WriteByte(msg, RACE_NET_RANKINGS_CNT); + int m = min(RANKINGS_CNT, autocvar_g_cts_send_rankings_cnt); + WriteByte(msg, m); +} + void race_SendRankings(float pos, float prevpos, float del, float msg) { WriteHeader(msg, TE_CSQC_RACE); @@ -316,9 +322,7 @@ void race_setTime(string map, float t, string myuid, string mynetname, entity e, } race_SendRankings(newpos, player_prevpos, 0, MSG_ALL); - if(rankings_reply) - strunzone(rankings_reply); - rankings_reply = strzone(getrankings()); + strcpy(rankings_reply, getrankings()); if(newpos == player_prevpos) { @@ -366,9 +370,7 @@ void race_deleteTime(string map, float pos) if(pos == 1) race_send_recordtime(MSG_ALL); - if(rankings_reply) - strunzone(rankings_reply); - rankings_reply = strzone(getrankings()); + strcpy(rankings_reply, getrankings()); } void race_SendTime(entity e, float cp, float t, float tvalid) @@ -442,9 +444,7 @@ void race_SendTime(entity e, float cp, float t, float tvalid) if(t < recordtime || recordtime == 0) { race_checkpoint_records[cp] = t; - if(race_checkpoint_recordholders[cp]) - strunzone(race_checkpoint_recordholders[cp]); - race_checkpoint_recordholders[cp] = strzone(e.netname); + strcpy(race_checkpoint_recordholders[cp], e.netname); if(g_race_qualifying) FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && it.race_checkpoint == cp, { race_SendNextCheckpoint(it, 0); }); } @@ -995,7 +995,7 @@ spawnfunc(target_checkpoint) // defrag entity defrag_ents = 1; // if this is targeted, then it probably isn't a trigger - bool is_trigger = !boolean(!this.nottargeted && this.targetname != ""); + bool is_trigger = this.targetname == ""; if(is_trigger) EXACTTRIGGER_INIT; @@ -1089,9 +1089,7 @@ void race_ClearRecords() for(int j = 0; j < MAX_CHECKPOINTS; ++j) { race_checkpoint_records[j] = 0; - if(race_checkpoint_recordholders[j]) - strunzone(race_checkpoint_recordholders[j]); - race_checkpoint_recordholders[j] = string_null; + strfree(race_checkpoint_recordholders[j]); } FOREACH_CLIENT(true, { diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh index 472827efa4..4402e22568 100644 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@ -5,6 +5,8 @@ float race_teams; // scores const float ST_RACE_LAPS = 1; +int autocvar_g_cts_send_rankings_cnt = 15; + bool g_race_qualifying; float speedaward_lastsent; @@ -61,8 +63,12 @@ void race_send_speedaward(float msg); void race_send_speedaward_alltimebest(float msg); +void race_send_rankings_cnt(float msg); + void race_SendRankings(float pos, float prevpos, float del, float msg); void race_RetractPlayer(entity this); void race_InitSpectator(); + +spawnfunc(target_checkpoint); diff --git a/qcsrc/server/resources.qc b/qcsrc/server/resources.qc index a2a1358b97..74a0c55788 100644 --- a/qcsrc/server/resources.qc +++ b/qcsrc/server/resources.qc @@ -10,6 +10,9 @@ float GetResourceLimit(entity e, int resource_type) { + if(!IS_PLAYER(e)) + return RESOURCE_LIMIT_NONE; // no limits on non-players + float limit; switch (resource_type) { @@ -74,6 +77,17 @@ float GetResourceAmount(entity e, int resource_type) return e.(resource_field); } +bool SetResourceAmountExplicit(entity e, int resource_type, float amount) +{ + .float resource_field = GetResourceField(resource_type); + if (e.(resource_field) != amount) + { + e.(resource_field) = amount; + return true; + } + return false; +} + void SetResourceAmount(entity e, int resource_type, float amount) { bool forbid = MUTATOR_CALLHOOK(SetResourceAmount, e, resource_type, amount); @@ -83,22 +97,28 @@ void SetResourceAmount(entity e, int resource_type, float amount) } resource_type = M_ARGV(1, int); amount = M_ARGV(2, float); - .float resource_field = GetResourceField(resource_type); - if (e.(resource_field) == amount) + float max_amount = GetResourceLimit(e, resource_type); // TODO: should allow overriding these limits if cheats are enabled! + float amount_wasted = 0; + if (amount > max_amount && max_amount != RESOURCE_LIMIT_NONE) { - return; + amount_wasted = amount - max_amount; + amount = max_amount; } - float max_amount = GetResourceLimit(e, resource_type); - if (amount > max_amount) + bool changed = SetResourceAmountExplicit(e, resource_type, amount); + if (changed) { - amount = max_amount; + MUTATOR_CALLHOOK(ResourceAmountChanged, e, resource_type, amount); } - e.(resource_field) = amount; + if (amount_wasted == 0) + { + return; + } + MUTATOR_CALLHOOK(ResourceWasted, e, resource_type, amount_wasted); } void GiveResource(entity receiver, int resource_type, float amount) { - if (amount == 0) + if (amount <= 0) { return; } @@ -144,18 +164,106 @@ void GiveResource(entity receiver, int resource_type, float amount) void GiveResourceWithLimit(entity receiver, int resource_type, float amount, float limit) { - if (amount == 0) + if (amount <= 0) + { + return; + } + bool forbid = MUTATOR_CALLHOOK(GiveResourceWithLimit, receiver, + resource_type, amount, limit); + if (forbid) + { + return; + } + resource_type = M_ARGV(1, int); + amount = M_ARGV(2, float); + limit = M_ARGV(3, float); + if (amount <= 0) { return; } float current_amount = GetResourceAmount(receiver, resource_type); - if (current_amount + amount > limit) + if (current_amount + amount > limit && limit != RESOURCE_LIMIT_NONE) { amount = limit - current_amount; } GiveResource(receiver, resource_type, amount); } +void TakeResource(entity receiver, int resource_type, float amount) +{ + if (amount <= 0) + { + return; + } + bool forbid = MUTATOR_CALLHOOK(TakeResource, receiver, resource_type, + amount); + if (forbid) + { + return; + } + resource_type = M_ARGV(1, int); + amount = M_ARGV(2, float); + if (amount <= 0) + { + return; + } + SetResourceAmount(receiver, resource_type, + GetResourceAmount(receiver, resource_type) - amount); +} + +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit) +{ + if (amount <= 0) + { + return; + } + bool forbid = MUTATOR_CALLHOOK(TakeResourceWithLimit, receiver, + resource_type, amount, limit); + if (forbid) + { + return; + } + resource_type = M_ARGV(1, int); + amount = M_ARGV(2, float); + limit = M_ARGV(3, float); + if (amount <= 0) + { + return; + } + float current_amount = GetResourceAmount(receiver, resource_type); + if (current_amount - amount < -limit) + { + amount = -limit + current_amount; + } + TakeResource(receiver, resource_type, amount); +} + +void GiveOrTakeResource(entity receiver, int resource_type, float amount) +{ + if(amount < 0) + { + TakeResource(receiver, resource_type, amount * -1); + } + else + { + GiveResource(receiver, resource_type, amount); + } +} + +void GiveOrTakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit) +{ + if(amount < 0) + { + TakeResourceWithLimit(receiver, resource_type, amount * -1, limit); + } + else + { + GiveResourceWithLimit(receiver, resource_type, amount, limit); + } +} + int GetResourceType(.float resource_field) { switch (resource_field) diff --git a/qcsrc/server/resources.qh b/qcsrc/server/resources.qh index 6ff3cea679..15433b264f 100644 --- a/qcsrc/server/resources.qh +++ b/qcsrc/server/resources.qh @@ -7,9 +7,6 @@ #include <common/resources.qh> -/// \brief Unconditional maximum amount of resources the entity can have. -const int RESOURCE_AMOUNT_HARD_LIMIT = 999; - // ============================ Public API ==================================== /// \brief Returns the maximum amount of the given resource. @@ -24,6 +21,13 @@ float GetResourceLimit(entity e, int resource_type); /// \return Current amount of resource the given entity has. float GetResourceAmount(entity e, int resource_type); +/// \brief Sets the resource amount of an entity without calling any hooks. +/// \param[in,out] e Entity to adjust. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to set. +/// \return Boolean for whether the ammo amount was changed +bool SetResourceAmountExplicit(entity e, int resource_type, float amount); + /// \brief Sets the current amount of resource the given entity will have. /// \param[in,out] e Entity to adjust. /// \param[in] resource_type Type of the resource (a RESOURCE_* constant). @@ -47,6 +51,38 @@ void GiveResource(entity receiver, int resource_type, float amount); void GiveResourceWithLimit(entity receiver, int resource_type, float amount, float limit); +/// \brief Takes an entity some resource. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \return No return. +void TakeResource(entity receiver, int resource_type, float amount); + +/// \brief Takes an entity some resource but not less than a limit. +/// \param[in,out] receiver Entity to take resource from. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to take. +/// \param[in] limit Limit of resources to take. +/// \return No return. +void TakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit); + +/// \brief Gives to or takes from an entity resource. +/// \param[in,out] receiver Entity to give or take resource. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to give or take. +/// \return No return. +void GiveOrTakeResource(entity receiver, int resource_type, float amount); + +/// \brief Gives to or takes from an entity resource but not more/less than a limit. +/// \param[in,out] receiver Entity to give or take resource. +/// \param[in] resource_type Type of the resource (a RESOURCE_* constant). +/// \param[in] amount Amount of resource to give or take. +/// \param[in] limit Limit of resources to give or take. +/// \return No return. +void GiveOrTakeResourceWithLimit(entity receiver, int resource_type, float amount, + float limit); + // ===================== Legacy and/or internal API =========================== /// \brief Converts an entity field to resource type. diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc index ae64e74e49..d347fcbaf9 100644 --- a/qcsrc/server/round_handler.qc +++ b/qcsrc/server/round_handler.qc @@ -86,7 +86,7 @@ void round_handler_FirstThink(entity this) this.nextthink = max(time, game_starttime); } -void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func) +void round_handler_Spawn(bool() canRoundStart_func, bool() canRoundEnd_func, void() roundStart_func) { if (round_handler) { diff --git a/qcsrc/server/round_handler.qh b/qcsrc/server/round_handler.qh index e6b17b0e77..5979eb5c33 100644 --- a/qcsrc/server/round_handler.qh +++ b/qcsrc/server/round_handler.qh @@ -3,17 +3,17 @@ entity round_handler; .float delay; // stores delay from round end to countdown start .float count; // stores initial number of the countdown -.float wait; // it's set to true when round ends, to false when countdown starts +.bool wait; // it's set to true when round ends, to false when countdown starts .float cnt; // its initial value is .count + 1, then decreased while counting down // reaches 0 when the round starts .float round_timelimit; .float round_endtime; -.float() canRoundStart; -.float() canRoundEnd; +.bool() canRoundStart; +.bool() canRoundEnd; .void() roundStart; void round_handler_Init(float the_delay, float the_count, float the_round_timelimit); -void round_handler_Spawn(float() canRoundStart_func, float() canRoundEnd_func, void() roundStart_func); +void round_handler_Spawn(bool() canRoundStart_func, bool() canRoundEnd_func, void() roundStart_func); void round_handler_Reset(float next_think); void round_handler_Remove(); diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index c9948660ef..af2f4b574f 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -1,11 +1,18 @@ #include "scores.qh" #include "command/common.qh" -#include "mutators/_mod.qh" +#include "defs.qh" +#include <server/g_world.qh> +#include <server/miscfunctions.qh> +#include <server/mutators/_mod.qh> #include <common/net_linked.qh> #include "../common/playerstats.qh" #include "../common/teams.qh" +#include <common/mapinfo.qh> +#include <common/mutators/base.qh> #include <common/scores.qh> +#include <common/state.qh> +#include <common/stats.qh> .entity scorekeeper; entity teamscorekeepers[16]; @@ -353,6 +360,28 @@ float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score) return s.(scores(scorefield)); } +float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score) +{ + if(!scores_initialized) return 0; // FIXME remove this when everything uses this system + entity s = CS(player).scorekeeper; + if(!s) + { + if(game_stopped) + return 0; + LOG_WARN("Setting score of unknown player!"); + return 0; + } + + float oldscore = s.(scores(scorefield)); + if(oldscore == score) + return oldscore; + + if(scores_label(scorefield) != "") + s.SendFlags |= (2 ** (scorefield.m_id % 16)); + s.(scores(scorefield)) = score; + return s.(scores(scorefield)); +} + float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score) { float r; @@ -373,7 +402,7 @@ float PlayerScore_Compare(entity t1, entity t2, float strict) }); if (result.x == 0 && strict) - result.x = etof(t1.owner) - etof(t2.owner); + result.x = t1.owner.playerid - t2.owner.playerid; return result.x; } @@ -513,9 +542,7 @@ void WinningConditionHelper(entity this) } } - if(worldstatus) - strunzone(worldstatus); - worldstatus = strzone(s); + strcpy(worldstatus, s); FOREACH_CLIENT(true, { string s = ""; @@ -534,9 +561,7 @@ void WinningConditionHelper(entity this) s = "-666"; } - if(it.clientstatus) - strunzone(it.clientstatus); - it.clientstatus = strzone(s); + strcpy(it.clientstatus, s); }); } diff --git a/qcsrc/server/scores.qh b/qcsrc/server/scores.qh index e2a57f43fb..ad40619663 100644 --- a/qcsrc/server/scores.qh +++ b/qcsrc/server/scores.qh @@ -24,6 +24,14 @@ void PlayerScore_Detach(entity player); */ float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score); +/** + * Sets the player's score to the score parameter. + * NEVER call this if PlayerScore_Attach has not been called yet! + * Means: FIXME make players unable to join the game when not called ClientConnect yet. + * Returns the new (or old if unchanged) score. + */ +float PlayerScore_Set(entity player, PlayerScoreField scorefield, float score); + /** * \brief Returns the player's score. * \param[in] player Player to inspect. diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 8d87407e64..39dbd49a35 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -9,8 +9,6 @@ int ScoreRules_teams; -void CheckAllowedTeams (entity for_whom); - int NumTeams(int teams) { return boolean(teams & BIT(0)) + boolean(teams & BIT(1)) + boolean(teams & BIT(2)) + boolean(teams & BIT(3)); @@ -19,8 +17,6 @@ int NumTeams(int teams) int AvailableTeams() { return NumTeams(ScoreRules_teams); - // NOTE: this method is unreliable, as forced teams set the c* globals to weird values - //return boolean(c1 >= 0) + boolean(c2 >= 0) + boolean(c3 >= 0) + boolean(c4 >= 0); } // NOTE: ST_constants may not be >= MAX_TEAMSCORE @@ -55,6 +51,9 @@ void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled ScoreInfo_SetLabel_PlayerScore(SP_DMG, "dmg", 0); ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "dmgtaken", SFL_LOWER_IS_BETTER); ScoreInfo_SetLabel_PlayerScore(SP_ELO, "elo", 0); + + if(STAT(SHOWFPS)) + ScoreInfo_SetLabel_PlayerScore(SP_FPS, "fps", 0); } void ScoreRules_basics_end() { @@ -63,12 +62,11 @@ void ScoreRules_basics_end() void ScoreRules_generic() { int teams = 0; - if (teamplay) { - CheckAllowedTeams(NULL); - if (c1 >= 0) teams |= BIT(0); - if (c2 >= 0) teams |= BIT(1); - if (c3 >= 0) teams |= BIT(2); - if (c4 >= 0) teams |= BIT(3); + if (teamplay) + { + entity balance = TeamBalance_CheckAllowedTeams(NULL); + teams = TeamBalance_GetAllowedTeams(balance); + TeamBalance_Destroy(balance); } GameRules_scoring(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, {}); } diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index e48840883a..03b40c5e12 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -1,16 +1,18 @@ #include "spawnpoints.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "g_world.qh" #include "race.qh" +#include "defs.qh" #include "../common/constants.qh" #include <common/net_linked.qh> #include "../common/teams.qh" -#include "../common/triggers/subs.qh" -#include "../common/triggers/target/spawnpoint.qh" +#include "../common/mapobjects/subs.qh" +#include "../common/mapobjects/target/spawnpoint.qh" #include "../common/util.qh" #include "../lib/warpzone/common.qh" #include "../lib/warpzone/util_server.qh" +#include <server/utils.qh> bool SpawnPoint_Send(entity this, entity to, int sf) { @@ -246,7 +248,7 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) vector spawn_score = prio * '1 0 0' + shortest * '0 1 0'; // filter out spots for assault - if(spot.target != "") + if(spot.target && spot.target != "") { int found = 0; for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain) @@ -325,14 +327,21 @@ SelectSpawnPoint Finds a point to respawn ============= */ +bool testspawn_checked; +entity testspawn_point; entity SelectSpawnPoint(entity this, bool anypoint) { float teamcheck; - entity spot, firstspot; + entity spot = NULL; - spot = find(NULL, classname, "testplayerstart"); - if (spot) - return spot; + if(!testspawn_checked) + { + testspawn_point = find(NULL, classname, "testplayerstart"); + testspawn_checked = true; + } + + if(testspawn_point) + return testspawn_point; if(this.spawnpoint_targ) return this.spawnpoint_targ; @@ -363,7 +372,16 @@ entity SelectSpawnPoint(entity this, bool anypoint) // get the entire list of spots - firstspot = findchain(classname, "info_player_deathmatch"); + //entity firstspot = findchain(classname, "info_player_deathmatch"); + entity firstspot = IL_FIRST(g_spawnpoints); + entity prev = NULL; + IL_EACH(g_spawnpoints, true, + { + if(prev) + prev.chain = it; + it.chain = NULL; + prev = it; + }); // filter out the bad ones // (note this returns the original list if none survived) if(anypoint) diff --git a/qcsrc/server/steerlib.qc b/qcsrc/server/steerlib.qc index 3418a2c4e3..92c918f00c 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -29,11 +29,8 @@ vector steerlib_push(entity this, vector point) **/ vector steerlib_arrive(entity this, vector point, float maximal_distance) { - float distance; - vector direction; - - distance = bound(0.001,vlen(this.origin - point),maximal_distance); - direction = normalize(point - this.origin); + float distance = bound(0.001,vlen(this.origin - point),maximal_distance); + vector direction = normalize(point - this.origin); return direction * (distance / maximal_distance); } @@ -42,25 +39,18 @@ vector steerlib_arrive(entity this, vector point, float maximal_distance) **/ vector steerlib_attract(entity this, vector point, float maximal_distance) { - float distance; - vector direction; - - distance = bound(0.001,vlen(this.origin - point),maximal_distance); - direction = normalize(point - this.origin); + float distance = bound(0.001,vlen(this.origin - point),maximal_distance); + vector direction = normalize(point - this.origin); return direction * (1-(distance / maximal_distance)); } vector steerlib_attract2(entity this, vector point, float min_influense,float max_distance,float max_influense) { - float distance; - vector direction; - float influense; + float distance = bound(0.00001,vlen(this.origin - point),max_distance); + vector direction = normalize(point - this.origin); - distance = bound(0.00001,vlen(this.origin - point),max_distance); - direction = normalize(point - this.origin); - - influense = 1 - (distance / max_distance); + float influense = 1 - (distance / max_distance); influense = min_influense + (influense * (max_influense - min_influense)); return direction * influense; @@ -447,176 +437,3 @@ vector steerlib_beamsteer(entity this, vector dir, float length, float step, flo return normalize(vr + vl); } - - -////////////////////////////////////////////// -// Testting // -// Everything below this point is a mess :D // -////////////////////////////////////////////// -//#define TLIBS_TETSLIBS -#ifdef TLIBS_TETSLIBS -void flocker_die(entity this) -{ - Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); - - this.owner.cnt += 1; - this.owner = NULL; - - this.nextthink = time; - setthink(this, SUB_Remove); -} - - -void flocker_think(entity this) -{ - vector dodgemove,swarmmove; - vector reprellmove,wandermove,newmove; - - this.angles_x = this.angles.x * -1; - makevectors(this.angles); - this.angles_x = this.angles.x * -1; - - dodgemove = steerlib_traceavoid(this, 0.35,1000); - swarmmove = steerlib_flock(this, 500,75,700,500); - reprellmove = steerlib_repell(this, this.owner.enemy.origin+this.enemy.velocity,2000) * 700; - - if(dodgemove == '0 0 0') - { - this.pos1 = steerlib_wander(this, 0.5,0.1,this.pos1); - wandermove = this.pos1 * 50; - } - else - this.pos1 = normalize(this.velocity); - - dodgemove = dodgemove * vlen(this.velocity) * 5; - - newmove = swarmmove + reprellmove + wandermove + dodgemove; - this.velocity = movelib_inertmove_byspeed(this, newmove,300,0.2,0.9); - //this.velocity = movelib_inertmove(this, dodgemove,0.65); - - this.velocity = movelib_dragvec(this, 0.01,0.6); - - this.angles = vectoangles(this.velocity); - - if(this.health <= 0) - flocker_die(this); - else - this.nextthink = time + 0.1; -} - -MODEL(FLOCKER, "models/turrets/rocket.md3"); - -void spawn_flocker(entity this) -{ - entity flocker = new(flocker); - - setorigin(flocker, this.origin + '0 0 32'); - setmodel (flocker, MDL_FLOCKER); - setsize (flocker, '-3 -3 -3', '3 3 3'); - - flocker.flock_id = this.flock_id; - flocker.owner = this; - setthink(flocker, flocker_think); - flocker.nextthink = time + random() * 5; - PROJECTILE_MAKETRIGGER(flocker); - set_movetype(flocker, MOVETYPE_BOUNCEMISSILE); - flocker.effects = EF_LOWPRECISION; - flocker.velocity = randomvec() * 300; - flocker.angles = vectoangles(flocker.velocity); - flocker.health = 10; - flocker.pos1 = normalize(flocker.velocity + randomvec() * 0.1); - - IL_PUSH(g_flockers, flocker); - - this.cnt = this.cnt -1; - -} - -void flockerspawn_think(entity this) -{ - if(this.cnt > 0) - spawn_flocker(this); - - this.nextthink = time + this.delay; - -} - -void flocker_hunter_think(entity this) -{ - vector dodgemove,attractmove,newmove; - entity ee; - - this.angles_x = this.angles.x * -1; - makevectors(this.angles); - this.angles_x = this.angles.x * -1; - - if(this.enemy) - if(vdist(this.enemy.origin - this.origin, <, 64)) - { - ee = this.enemy; - ee.health = -1; - this.enemy = NULL; - - } - - if(!this.enemy) - { - IL_EACH(g_flockers, it.flock_id == this.flock_id, - { - if(it == this.owner || it == ee) - continue; - - if(!this.enemy || vlen2(this.origin - it.origin) > vlen2(this.origin - this.enemy.origin)) - this.enemy = it; - }); - } - - if(this.enemy) - attractmove = steerlib_attract(this, this.enemy.origin+this.enemy.velocity * 0.1,5000) * 1250; - else - attractmove = normalize(this.velocity) * 200; - - dodgemove = steerlib_traceavoid(this, 0.35,1500) * vlen(this.velocity); - - newmove = dodgemove + attractmove; - this.velocity = movelib_inertmove_byspeed(this, newmove,1250,0.3,0.7); - this.velocity = movelib_dragvec(this, 0.01,0.5); - - this.angles = vectoangles(this.velocity); - this.nextthink = time + 0.1; -} - - -float globflockcnt; -spawnfunc(flockerspawn) -{ - ++globflockcnt; - - if(!this.cnt) this.cnt = 20; - if(!this.delay) this.delay = 0.25; - if(!this.flock_id) this.flock_id = globflockcnt; - - setthink(this, flockerspawn_think); - this.nextthink = time + 0.25; - - this.enemy = new(FLock Hunter); - - setmodel(this.enemy, MDL_FLOCKER); - setorigin(this.enemy, this.origin + '0 0 768' + (randomvec() * 128)); - - this.enemy.scale = 3; - this.enemy.effects = EF_LOWPRECISION; - set_movetype(this.enemy, MOVETYPE_BOUNCEMISSILE); - PROJECTILE_MAKETRIGGER(this.enemy); - setthink(this.enemy, flocker_hunter_think); - this.enemy.nextthink = time + 10; - this.enemy.flock_id = this.flock_id; - this.enemy.owner = this; - - IL_PUSH(g_flockers, this); - IL_PUSH(g_flockers, this.enemy); -} -#endif - - - diff --git a/qcsrc/server/steerlib.qh b/qcsrc/server/steerlib.qh index 89ffb698ec..4beb69f632 100644 --- a/qcsrc/server/steerlib.qh +++ b/qcsrc/server/steerlib.qh @@ -5,6 +5,3 @@ vector steerlib_arrive(entity this, vector point, float maximal_distance); vector steerlib_attract2(entity this, vector point, float min_influense, float max_distance, float max_influense); //vector steerlib_pull(entity this, vector point); - -IntrusiveList g_flockers; -STATIC_INIT(g_flockers) { g_flockers = IL_NEW(); } diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 1130b6a4e5..a92a4ea12f 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -2,14 +2,16 @@ #include "anticheat.qh" #include "g_hook.qh" +#include "g_damage.qh" #include "g_world.qh" #include "bot/api.qh" #include "command/common.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "weapons/csqcprojectile.qh" +#include <server/compat/quake3.qh> #include "../common/constants.qh" #include "../common/deathtypes/all.qh" @@ -18,6 +20,7 @@ #include "../common/util.qh" #include "../common/vehicles/all.qh" +#include <common/monsters/sv_monsters.qh> #include <common/weapons/_all.qh> #include "../lib/csqcmodel/sv_model.qh" @@ -165,7 +168,6 @@ Called before each frame by the server bool game_delay_last; bool autocvar_sv_autopause = false; -float RedirectionThink(); void systems_update(); void sys_phys_update(entity this, float dt); void StartFrame() @@ -246,7 +248,6 @@ void StartFrame() .float anglejitter; .string gametypefilter; .string cvarfilter; -bool DoesQ3ARemoveThisEntity(entity this); /** * Evaluate an expression of the form: [+ | -]? [var[op]val | [op]var | val | var] ... @@ -360,7 +361,7 @@ void SV_OnEntityPreSpawnFunction(entity this) } return; LABEL(cleanup) - builtin_remove(this); + delete(this); } void WarpZone_PostInitialize_Callback() @@ -371,15 +372,18 @@ void WarpZone_PostInitialize_Callback() tracetest_ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; //for(entity e = warpzone_first; e; e = e.warpzone_next) for(entity e = NULL; (e = find(e, classname, "trigger_warpzone")); ) - { - vector src, dst; - src = (e.absmin + e.absmax) * 0.5; - makevectors(e.warpzone_angles); - src = src + ((e.warpzone_origin - src) * v_forward) * v_forward + 16 * v_right; - dst = (e.enemy.absmin + e.enemy.absmax) * 0.5; - makevectors(e.enemy.warpzone_angles); - dst = dst + ((e.enemy.warpzone_origin - dst) * v_forward) * v_forward - 16 * v_right; - waypoint_spawnforteleporter_wz(e, src, dst, 0, -v_up, tracetest_ent); - } + waypoint_spawnforteleporter_wz(e, tracetest_ent); delete(tracetest_ent); } + +/* +================== +main + +unused but required by the engine +================== +*/ +void main () +{ + +} diff --git a/qcsrc/server/sv_main.qh b/qcsrc/server/sv_main.qh index 7f86d19c01..93480cf282 100644 --- a/qcsrc/server/sv_main.qh +++ b/qcsrc/server/sv_main.qh @@ -1,3 +1,12 @@ #pragma once bool expr_evaluate(string s); + +/* +================== +main + +unused but required by the engine +================== +*/ +void main (); diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index cf21de4bfd..239fb69f0c 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -9,151 +9,123 @@ #include "command/vote.qh" -#include "mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "../common/deathtypes/all.qh" -#include "../common/gamemodes/_mod.qh" +#include <common/gamemodes/_mod.qh> #include "../common/teams.qh" -void TeamchangeFrags(entity e) +/// \brief Describes a state of team balance entity. +enum { - PlayerScore_Clear(e); -} + TEAM_BALANCE_UNINITIALIZED, ///< The team balance has not been initialized. + /// \brief TeamBalance_CheckAllowedTeams has been called. + TEAM_BALANCE_TEAMS_CHECKED, + /// \brief TeamBalance_GetTeamCounts has been called. + TEAM_BALANCE_TEAM_COUNTS_FILLED +}; -void LogTeamchange(float player_id, float team_number, float type) -{ - if(!autocvar_sv_eventlog) - return; +/// \brief Indicates that the player is not allowed to join a team. +const int TEAM_NOT_ALLOWED = -1; - if(player_id < 1) - return; +.float team_forced; // can be a team number to force a team, or 0 for default action, or -1 for forced spectator - GameLogEcho(strcat(":team:", ftos(player_id), ":", ftos(team_number), ":", ftos(type))); -} +.int m_team_balance_state; ///< Holds the state of the team balance entity. +.entity m_team_balance_team[NUM_TEAMS]; ///< ??? -void default_delayedinit(entity this) -{ - if(!scores_initialized) - ScoreRules_generic(); -} +.float m_team_score; ///< The score of the team. +.int m_num_players; ///< Number of players (both humans and bots) in a team. +.int m_num_bots; ///< Number of bots in a team. +.int m_num_players_alive; ///< Number of alive players in a team. +.int m_num_control_points; ///< Number of control points owned by a team. -void InitGameplayMode() -{ - VoteReset(); - - // find out good world mins/maxs bounds, either the static bounds found by looking for solid, or the mapinfo specified bounds - get_mi_min_max(1); - // assign reflectively to avoid "assignment to world" warning - 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("%v", v)); - if (++done == 2) break; - } - } - // currently, NetRadiant's limit is 131072 qu for each side - // distance from one corner of a 131072qu cube to the opposite corner is approx. 227023 qu - // set the distance according to map size but don't go over the limit to avoid issues with float precision - // in case somebody makes extremely large maps - max_shot_distance = min(230000, vlen(world.maxs - world.mins)); - - MapInfo_LoadMapSettings(mapname); - GameRules_teams(false); - - if (!cvar_value_issafe(world.fog)) - { - LOG_INFO("The current map contains a potentially harmful fog setting, ignored"); - world.fog = string_null; - } - if(MapInfo_Map_fog != "") - if(MapInfo_Map_fog == "none") - world.fog = string_null; - else - world.fog = strzone(MapInfo_Map_fog); - clientstuff = strzone(MapInfo_Map_clientstuff); +string autocvar_g_forced_team_red; +string autocvar_g_forced_team_blue; +string autocvar_g_forced_team_yellow; +string autocvar_g_forced_team_pink; - MapInfo_ClearTemps(); +entity g_team_entities[NUM_TEAMS]; ///< Holds global team entities. - gamemode_name = MapInfo_Type_ToText(MapInfo_LoadedGametype); - - cache_mutatormsg = strzone(""); - cache_lastmutatormsg = strzone(""); - - InitializeEntity(NULL, default_delayedinit, INITPRIO_GAMETYPE_FALLBACK); +STATIC_INIT(g_team_entities) +{ + for (int i = 0; i < NUM_TEAMS; ++i) + { + g_team_entities[i] = spawn(); + } } -string GetClientVersionMessage(entity this) +entity Team_GetTeamFromIndex(int index) { - if (CS(this).version_mismatch) { - if(CS(this).version < autocvar_gameversion) { - return strcat("This is Xonotic ", autocvar_g_xonoticversion, - "\n^3Your client version is outdated.\n\n\n### YOU WON'T BE ABLE TO PLAY ON THIS SERVER ###\n\n\nPlease update!!!^8"); - } else { - return strcat("This is Xonotic ", autocvar_g_xonoticversion, - "\n^3This server is using an outdated Xonotic version.\n\n\n ### THIS SERVER IS INCOMPATIBLE AND THUS YOU CANNOT JOIN ###.^8"); - } - } else { - return strcat("Welcome to Xonotic ", autocvar_g_xonoticversion); + if (!Team_IsValidIndex(index)) + { + LOG_FATALF("Team_GetTeamFromIndex: Index is invalid: %f", index); } + return g_team_entities[index - 1]; } -string getwelcomemessage(entity this) +entity Team_GetTeam(int team_num) { - MUTATOR_CALLHOOK(BuildMutatorsPrettyString, ""); - string modifications = M_ARGV(0, string); - - if(g_weaponarena) + if (!Team_IsValidTeam(team_num)) { - if(g_weaponarena_random) - modifications = strcat(modifications, ", ", ftos(g_weaponarena_random), " of ", g_weaponarena_list, " Arena"); - else - modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena"); + LOG_FATALF("Team_GetTeam: Value is invalid: %f", team_num); } - else if(cvar("g_balance_blaster_weaponstartoverride") == 0) - modifications = strcat(modifications, ", No start weapons"); - if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) - modifications = strcat(modifications, ", Low gravity"); - if(g_weapon_stay && !g_cts) - modifications = strcat(modifications, ", Weapons stay"); - if(g_jetpack) - modifications = strcat(modifications, ", Jet pack"); - if(autocvar_g_powerups == 0) - modifications = strcat(modifications, ", No powerups"); - if(autocvar_g_powerups > 0) - modifications = strcat(modifications, ", Powerups"); - modifications = substring(modifications, 2, strlen(modifications) - 2); + return g_team_entities[Team_TeamToIndex(team_num) - 1]; +} - string versionmessage = GetClientVersionMessage(this); - string s = strcat(versionmessage, "^8\n^8\nmatch type is ^1", gamemode_name, "^8\n"); +float Team_GetTeamScore(entity team_ent) +{ + return team_ent.m_team_score; +} - if(modifications != "") - s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n"); +void Team_SetTeamScore(entity team_ent, float score) +{ + team_ent.m_team_score = score; +} - if(cache_lastmutatormsg != autocvar_g_mutatormsg) - { - if(cache_lastmutatormsg) - strunzone(cache_lastmutatormsg); - if(cache_mutatormsg) - strunzone(cache_mutatormsg); - cache_lastmutatormsg = strzone(autocvar_g_mutatormsg); - cache_mutatormsg = strzone(cache_lastmutatormsg); - } +int Team_GetNumberOfAlivePlayers(entity team_ent) +{ + return team_ent.m_num_players_alive; +} + +void Team_SetNumberOfAlivePlayers(entity team_ent, int number) +{ + team_ent.m_num_players_alive = number; +} - if (cache_mutatormsg != "") { - s = strcat(s, "\n\n^8special gameplay tips: ^7", cache_mutatormsg); +int Team_GetNumberOfAliveTeams() +{ + int result = 0; + for (int i = 0; i < NUM_TEAMS; ++i) + { + if (g_team_entities[i].m_num_players_alive > 0) + { + ++result; + } } + return result; +} - string mutator_msg = ""; - MUTATOR_CALLHOOK(BuildGameplayTipsString, mutator_msg); - mutator_msg = M_ARGV(0, string); +int Team_GetNumberOfControlPoints(entity team_ent) +{ + return team_ent.m_num_control_points; +} - s = strcat(s, mutator_msg); // trust that the mutator will do proper formatting +void Team_SetNumberOfControlPoints(entity team_ent, int number) +{ + team_ent.m_num_control_points = number; +} - string motd = autocvar_sv_motd; - if (motd != "") { - s = strcat(s, "\n\n^8MOTD: ^7", strreplace("\\n", "\n", motd)); +int Team_GetNumberOfTeamsWithControlPoints() +{ + int result = 0; + for (int i = 0; i < NUM_TEAMS; ++i) + { + if (g_team_entities[i].m_num_control_points > 0) + { + ++result; + } } - return s; + return result; } void setcolor(entity this, int clr) @@ -166,6 +138,26 @@ void setcolor(entity this, int clr) #endif } +bool Entity_HasValidTeam(entity this) +{ + return Team_IsValidTeam(this.team); +} + +int Entity_GetTeamIndex(entity this) +{ + return Team_TeamToIndex(this.team); +} + +entity Entity_GetTeam(entity this) +{ + int index = Entity_GetTeamIndex(this); + if (!Team_IsValidIndex(index)) + { + return NULL; + } + return Team_GetTeamFromIndex(index); +} + void SetPlayerColors(entity player, float _color) { float pants = _color & 0x0F; @@ -180,901 +172,970 @@ void SetPlayerColors(entity player, float _color) } } -void KillPlayerForTeamChange(entity player) +bool Player_SetTeamIndex(entity player, int index) { - if (IS_DEAD(player)) + int new_team = Team_IndexToTeam(index); + if (player.team == new_team) { - return; + if (new_team != -1) + { + // 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, new_team - 1); + } + return true; } - if (MUTATOR_CALLHOOK(Player_ChangeTeamKill, player) == true) + int old_index = Team_TeamToIndex(player.team); + if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, old_index, index) == true) { - return; + // Mutator has blocked team change. + return false; + } + if (new_team == -1) + { + player.team = -1; + } + else + { + SetPlayerColors(player, new_team - 1); } - Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP, player.origin, - '0 0 0'); + MUTATOR_CALLHOOK(Player_ChangedTeam, player, old_index, index); + return true; } -bool SetPlayerTeamSimple(entity player, int team_num) +bool SetPlayerTeam(entity player, int team_index, int type) { - if (player.team == team_num) + int old_team_index = Entity_GetTeamIndex(player); + if (!Player_SetTeamIndex(player, team_index)) { - // 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; + return false; } - if (MUTATOR_CALLHOOK(Player_ChangeTeam, player, Team_TeamToNumber( - player.team), Team_TeamToNumber(team_num)) == true) + LogTeamChange(player.playerid, player.team, type); + if (team_index != old_team_index) { - // Mutator has blocked team change. - return false; + PlayerScore_Clear(player); + if (team_index != -1) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM( + player.team, INFO_JOIN_PLAY_TEAM), player.netname); + } + else + { + if (!CS(player).just_joined) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, + player.netname); + } + else + { + CS(player).just_joined = false; + } + } + KillPlayerForTeamChange(player); + if (!IS_BOT_CLIENT(player)) + { + TeamBalance_AutoBalanceBots(); + } } - 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) +void Player_SetTeamIndexChecked(entity player, int team_index) { - int team_num = Team_NumberToTeam(destination_team); - if (!SetPlayerTeamSimple(player, team_num)) + if (!teamplay) { - return false; + return; } - LogTeamchange(player.playerid, player.team, 3); // log manual team join - if (no_print) + if (!Team_IsValidIndex(team_index)) { - return true; + return; } - bprint(playername(player, false), "^7 has changed from ", Team_NumberToColoredFullName(source_team), "^7 to ", Team_NumberToColoredFullName(destination_team), "\n"); + if ((autocvar_g_campaign) || (autocvar_g_changeteam_banned && + CS(player).wasplayer)) + { + Send_Notification(NOTIF_ONE, player, MSG_INFO, + INFO_TEAMCHANGE_NOTALLOWED); + return; + } + entity balance = TeamBalance_CheckAllowedTeams(player); + if (team_index == 1 && !TeamBalance_IsTeamAllowedInternal(balance, 1)) + { + team_index = 4; + } + if (team_index == 4 && !TeamBalance_IsTeamAllowedInternal(balance, 4)) + { + team_index = 3; + } + if (team_index == 3 && !TeamBalance_IsTeamAllowedInternal(balance, 3)) + { + team_index = 2; + } + if (team_index == 2 && !TeamBalance_IsTeamAllowedInternal(balance, 2)) + { + team_index = 1; + } + // 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) + { + TeamBalance_GetTeamCounts(balance, player); + if ((Team_IndexToBit(team_index) & TeamBalance_FindBestTeams(balance, + player, false)) == 0) + { + Send_Notification(NOTIF_ONE, player, MSG_INFO, + INFO_TEAMCHANGE_LARGERTEAM); + TeamBalance_Destroy(balance); + return; + } + } + TeamBalance_Destroy(balance); + SetPlayerTeam(player, team_index, TEAM_CHANGE_MANUAL); +} + +bool MoveToTeam(entity client, int team_index, int type) +{ + //PrintToChatAll(sprintf("MoveToTeam: %s, %f", client.netname, team_index)); + int lockteams_backup = lockteams; // backup any team lock + lockteams = 0; // disable locked teams + if (!SetPlayerTeam(client, team_index, type)) + { + lockteams = lockteams_backup; // restore the team lock + return false; + } + lockteams = lockteams_backup; // restore the team lock return true; } -// set c1...c4 to show what teams are allowed -void CheckAllowedTeams(entity for_whom) +bool Player_HasRealForcedTeam(entity player) { - int teams_mask = 0; + return player.team_forced > TEAM_FORCE_DEFAULT; +} - c1 = c2 = c3 = c4 = -1; - num_bots_team1 = num_bots_team2 = num_bots_team3 = num_bots_team4 = 0; +int Player_GetForcedTeamIndex(entity player) +{ + return player.team_forced; +} - string teament_name = string_null; +void Player_SetForcedTeamIndex(entity player, int team_index) +{ + switch (team_index) + { + case TEAM_FORCE_SPECTATOR: + case TEAM_FORCE_DEFAULT: + { + player.team_forced = team_index; + break; + } + default: + { + if (!Team_IsValidIndex(team_index)) + { + LOG_FATAL("Player_SetForcedTeamIndex: Invalid team index."); + } + else + { + player.team_forced = team_index; + break; + } + } + } +} - bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, teament_name, for_whom); +void Player_DetermineForcedTeam(entity player) +{ + if (autocvar_g_campaign) + { + if (IS_REAL_CLIENT(player)) // only players, not bots + { + if (Team_IsValidIndex(autocvar_g_campaign_forceteam)) + { + player.team_forced = autocvar_g_campaign_forceteam; + } + else + { + player.team_forced = TEAM_FORCE_DEFAULT; + } + } + } + else if (PlayerInList(player, autocvar_g_forced_team_red)) + { + player.team_forced = 1; + } + else if (PlayerInList(player, autocvar_g_forced_team_blue)) + { + player.team_forced = 2; + } + else if (PlayerInList(player, autocvar_g_forced_team_yellow)) + { + player.team_forced = 3; + } + else if (PlayerInList(player, autocvar_g_forced_team_pink)) + { + player.team_forced = 4; + } + else + { + switch (autocvar_g_forced_team_otherwise) + { + case "red": + { + player.team_forced = 1; + break; + } + case "blue": + { + player.team_forced = 2; + break; + } + case "yellow": + { + player.team_forced = 3; + break; + } + case "pink": + { + player.team_forced = 4; + break; + } + case "spectate": + case "spectator": + { + player.team_forced = TEAM_FORCE_SPECTATOR; + break; + } + default: + { + player.team_forced = TEAM_FORCE_DEFAULT; + break; + } + } + } + if (!teamplay && Player_HasRealForcedTeam(player)) + { + player.team_forced = TEAM_FORCE_DEFAULT; + } +} + +void TeamBalance_JoinBestTeam(entity player) +{ + //PrintToChatAll(sprintf("TeamBalance_JoinBestTeam: %s", player.netname)); + if (!teamplay) + { + return; + } + if (player.bot_forced_team) + { + return; + } + entity balance = TeamBalance_CheckAllowedTeams(player); + if (Player_HasRealForcedTeam(player)) + { + int forced_team_index = player.team_forced; + bool is_team_allowed = TeamBalance_IsTeamAllowedInternal(balance, + forced_team_index); + TeamBalance_Destroy(balance); + if (!is_team_allowed) + { + return; + } + if (!SetPlayerTeam(player, forced_team_index, TEAM_CHANGE_AUTO)) + { + return; + } + return; + } + int best_team_index = TeamBalance_FindBestTeam(balance, player, true); + TeamBalance_Destroy(balance); + if (!SetPlayerTeam(player, best_team_index, TEAM_CHANGE_AUTO)) + { + return; + } +} + +entity TeamBalance_CheckAllowedTeams(entity for_whom) +{ + entity balance = spawn(); + for (int i = 0; i < NUM_TEAMS; ++i) + { + entity team_ent = balance.m_team_balance_team[i] = spawn(); + team_ent.m_team_score = g_team_entities[i].m_team_score; + team_ent.m_num_players = TEAM_NOT_ALLOWED; + team_ent.m_num_bots = 0; + } + setthink(balance, TeamBalance_Destroy); + + int teams_mask = 0; + string teament_name = string_null; + bool mutator_returnvalue = MUTATOR_CALLHOOK(TeamBalance_CheckAllowedTeams, + teams_mask, teament_name, for_whom); teams_mask = M_ARGV(0, float); teament_name = M_ARGV(1, string); - - if(!mutator_returnvalue) + if (mutator_returnvalue) { - if(teams_mask & BIT(0)) c1 = 0; - if(teams_mask & BIT(1)) c2 = 0; - if(teams_mask & BIT(2)) c3 = 0; - if(teams_mask & BIT(3)) c4 = 0; + for (int i = 0; i < NUM_TEAMS; ++i) + { + if (teams_mask & BIT(i)) + { + balance.m_team_balance_team[i].m_num_players = 0; + } + } } - // find out what teams are allowed if necessary - if(teament_name) + if (teament_name) { entity head = find(NULL, classname, teament_name); - while(head) + while (head) { - switch(head.team) + if (Team_IsValidTeam(head.team)) { - case NUM_TEAM_1: c1 = 0; break; - case NUM_TEAM_2: c2 = 0; break; - case NUM_TEAM_3: c3 = 0; break; - case NUM_TEAM_4: c4 = 0; break; + TeamBalance_GetTeam(balance, head.team).m_num_players = 0; } - head = find(head, classname, teament_name); } } // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line) - if(AvailableTeams() == 2) - if(autocvar_bot_vs_human && for_whom) + if (AvailableTeams() == 2) + if (autocvar_bot_vs_human && for_whom) { - if(autocvar_bot_vs_human > 0) + if (autocvar_bot_vs_human > 0) { // find last team available - - if(IS_BOT_CLIENT(for_whom)) + if (IS_BOT_CLIENT(for_whom)) { - if(c4 >= 0) { c3 = c2 = c1 = -1; } - else if(c3 >= 0) { c4 = c2 = c1 = -1; } - else { c4 = c3 = c1 = -1; } + if (TeamBalance_IsTeamAllowedInternal(balance, 4)) + { + TeamBalance_BanTeamsExcept(balance, 4); + } + else if (TeamBalance_IsTeamAllowedInternal(balance, 3)) + { + TeamBalance_BanTeamsExcept(balance, 3); + } + else + { + TeamBalance_BanTeamsExcept(balance, 2); + } // no further cases, we know at least 2 teams exist } else { - if(c1 >= 0) { c2 = c3 = c4 = -1; } - else if(c2 >= 0) { c1 = c3 = c4 = -1; } - else { c1 = c2 = c4 = -1; } + if (TeamBalance_IsTeamAllowedInternal(balance, 1)) + { + TeamBalance_BanTeamsExcept(balance, 1); + } + else if (TeamBalance_IsTeamAllowedInternal(balance, 2)) + { + TeamBalance_BanTeamsExcept(balance, 2); + } + else + { + TeamBalance_BanTeamsExcept(balance, 3); + } // no further cases, bots have one of the teams } } else { // find first team available - - if(IS_BOT_CLIENT(for_whom)) + if (IS_BOT_CLIENT(for_whom)) { - if(c1 >= 0) { c2 = c3 = c4 = -1; } - else if(c2 >= 0) { c1 = c3 = c4 = -1; } - else { c1 = c2 = c4 = -1; } + if (TeamBalance_IsTeamAllowedInternal(balance, 1)) + { + TeamBalance_BanTeamsExcept(balance, 1); + } + else if (TeamBalance_IsTeamAllowedInternal(balance, 2)) + { + TeamBalance_BanTeamsExcept(balance, 2); + } + else + { + TeamBalance_BanTeamsExcept(balance, 3); + } // no further cases, we know at least 2 teams exist } else { - if(c4 >= 0) { c3 = c2 = c1 = -1; } - else if(c3 >= 0) { c4 = c2 = c1 = -1; } - else { c4 = c3 = c1 = -1; } + if (TeamBalance_IsTeamAllowedInternal(balance, 4)) + { + TeamBalance_BanTeamsExcept(balance, 4); + } + else if (TeamBalance_IsTeamAllowedInternal(balance, 3)) + { + TeamBalance_BanTeamsExcept(balance, 3); + } + else + { + TeamBalance_BanTeamsExcept(balance, 2); + } // no further cases, bots have one of the teams } } } - if(!for_whom) - return; + if (!for_whom) + { + balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED; + return balance; + } // if player has a forced team, ONLY allow that one - if(for_whom.team_forced == NUM_TEAM_1 && c1 >= 0) - c2 = c3 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_2 && c2 >= 0) - c1 = c3 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_3 && c3 >= 0) - c1 = c2 = c4 = -1; - else if(for_whom.team_forced == NUM_TEAM_4 && c4 >= 0) - c1 = c2 = c3 = -1; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (for_whom.team_forced == i && + TeamBalance_IsTeamAllowedInternal(balance, i)) + { + TeamBalance_BanTeamsExcept(balance, i); + break; + } + } + balance.m_team_balance_state = TEAM_BALANCE_TEAMS_CHECKED; + return balance; } -float PlayerValue(entity p) +void TeamBalance_Destroy(entity balance) { - return 1; - // FIXME: it always returns 1... + if (balance == NULL) + { + return; + } + for (int i = 0; i < NUM_TEAMS; ++i) + { + delete(balance.(m_team_balance_team[i])); + } + delete(balance); } -// c1...c4 should be set to -1 (not allowed) or 0 (allowed). -// teams that are allowed will now have their player counts stored in c1...c4 -void GetTeamCounts(entity ignore) +int TeamBalance_GetAllowedTeams(entity balance) { - if (MUTATOR_CALLHOOK(GetTeamCounts) == true) + if (balance == NULL) + { + LOG_FATAL("TeamBalance_GetAllowedTeams: Team balance entity is NULL."); + } + if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED) + { + LOG_FATAL("TeamBalance_GetAllowedTeams: " + "Team balance entity is not initialized."); + } + int result = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (TeamBalance_IsTeamAllowedInternal(balance, i)) + { + result |= Team_IndexToBit(i); + } + } + return result; +} + +bool TeamBalance_IsTeamAllowed(entity balance, int index) +{ + if (balance == NULL) + { + LOG_FATAL("TeamBalance_IsTeamAllowed: Team balance entity is NULL."); + } + if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED) + { + LOG_FATAL("TeamBalance_IsTeamAllowed: " + "Team balance entity is not initialized."); + } + if (!Team_IsValidIndex(index)) + { + LOG_FATALF("TeamBalance_IsTeamAllowed: Team index is invalid: %f", + index); + } + return TeamBalance_IsTeamAllowedInternal(balance, index); +} + +void TeamBalance_GetTeamCounts(entity balance, entity ignore) +{ + if (balance == NULL) + { + LOG_FATAL("TeamBalance_GetTeamCounts: Team balance entity is NULL."); + } + if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED) + { + LOG_FATAL("TeamBalance_GetTeamCounts: " + "Team balance entity is not initialized."); + } + if (MUTATOR_CALLHOOK(TeamBalance_GetTeamCounts) == true) { - if (c1 >= 0) + // Mutator has overriden the configuration. + for (int i = 1; i <= NUM_TEAMS; ++i) { - 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); + entity team_ent = TeamBalance_GetTeamFromIndex(balance, i); + if (TeamBalanceTeam_IsAllowed(team_ent)) + { + MUTATOR_CALLHOOK(TeamBalance_GetTeamCount, i, ignore); + team_ent.m_num_players = M_ARGV(2, float); + team_ent.m_num_bots = M_ARGV(3, float); + } } } 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; + // Manually count all players. FOREACH_CLIENT(true, { - float t; - if (IS_PLAYER(it) || it.caplayer) + if (it == ignore) { - t = it.team; + continue; } - else if (it.team_forced > 0) + int team_num; + // TODO: Reconsider when the player is truly on the team. + if (IS_CLIENT(it) || (it.caplayer)) { - t = it.team_forced; // reserve the spot + team_num = it.team; } - else + else if (Player_HasRealForcedTeam(it)) { - continue; + // Do we really need this? Probably not. + team_num = Team_IndexToTeam(it.team_forced); // reserve the spot } - if (it == ignore) + else { continue; } - value = PlayerValue(it); - if (IS_BOT_CLIENT(it)) - { - bvalue = value; - } - else + if (!Team_IsValidTeam(team_num)) { - bvalue = 0; + continue; } - if (value == 0) + entity team_ent = TeamBalance_GetTeam(balance, team_num); + if (!TeamBalanceTeam_IsAllowed(team_ent)) { continue; } - switch (t) + ++team_ent.m_num_players; + if (IS_BOT_CLIENT(it)) { - case NUM_TEAM_1: - { - 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; - } - case NUM_TEAM_2: - { - 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; - } - 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: - { - 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; - } + ++team_ent.m_num_bots; } }); } // if the player who has a forced team has not joined yet, reserve the spot - if(autocvar_g_campaign) + if (autocvar_g_campaign) { - switch(autocvar_g_campaign_forceteam) + if (Team_IsValidIndex(autocvar_g_campaign_forceteam)) { - 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; + entity team_ent = TeamBalance_GetTeamFromIndex(balance, + autocvar_g_campaign_forceteam); + if (team_ent.m_num_players == team_ent.m_num_bots) + { + ++team_ent.m_num_players; + } } } + balance.m_team_balance_state = TEAM_BALANCE_TEAM_COUNTS_FILLED; } -bool IsTeamSmallerThanTeam(int team_a, int team_b, entity player, - bool use_score) +int TeamBalance_GetNumberOfPlayers(entity balance, int index) { - if (!Team_IsValidNumber(team_a)) - { - LOG_FATALF("IsTeamSmallerThanTeam: team_a is invalid: %f", team_a); - } - if (!Team_IsValidNumber(team_b)) + if (balance == NULL) { - LOG_FATALF("IsTeamSmallerThanTeam: team_b is invalid: %f", team_b); + LOG_FATAL("TeamBalance_GetNumberOfPlayers: " + "Team balance entity is NULL."); } - if (team_a == team_b) + if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED) { - return false; + LOG_FATAL("TeamBalance_GetNumberOfPlayers: " + "TeamBalance_GetTeamCounts has not been called."); } - // 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 (!Team_IsValidIndex(index)) { - 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; - } + LOG_FATALF("TeamBalance_GetNumberOfPlayers: Team index is invalid: %f", + index); } - switch (team_b) + return balance.m_team_balance_team[index - 1].m_num_players; +} + +int TeamBalance_FindBestTeam(entity balance, entity player, bool ignore_player) +{ + if (balance == NULL) { - 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; - } + LOG_FATAL("TeamBalance_FindBestTeam: Team balance entity is NULL."); } - // invalid - if (num_players_team_a < 0 || num_players_team_b < 0) + if (balance.m_team_balance_state == TEAM_BALANCE_UNINITIALIZED) { - return false; + LOG_FATAL("TeamBalance_FindBestTeam: " + "Team balance entity is not initialized."); } - if (IS_REAL_CLIENT(player) && bots_would_leave) + // count how many players are in each team + if (ignore_player) { - num_players_team_a -= num_bots_team_a; - num_players_team_b -= num_bots_team_b; + TeamBalance_GetTeamCounts(balance, player); } - if (!use_score) + else { - return num_players_team_a < num_players_team_b; + TeamBalance_GetTeamCounts(balance, NULL); } - if (num_players_team_a < num_players_team_b) + int team_bits = TeamBalance_FindBestTeams(balance, player, true); + if (team_bits == 0) { - return true; + LOG_FATALF("TeamBalance_FindBestTeam: No teams available for %s\n", + MapInfo_Type_ToString(MapInfo_CurrentGametype())); } - if (num_players_team_a > num_players_team_b) + RandomSelection_Init(); + for (int i = 1; i <= NUM_TEAMS; ++i) { - return false; + if (team_bits & Team_IndexToBit(i)) + { + RandomSelection_AddFloat(i, 1, 1); + } } - return score_team_a < score_team_b; + return RandomSelection_chosen_float; } -bool IsTeamEqualToTeam(int team_a, int team_b, entity player, bool use_score) +int TeamBalance_FindBestTeams(entity balance, entity player, bool use_score) { - if (!Team_IsValidNumber(team_a)) + if (balance == NULL) { - LOG_FATALF("IsTeamEqualToTeam: team_a is invalid: %f", team_a); + LOG_FATAL("TeamBalance_FindBestTeams: Team balance entity is NULL."); } - if (!Team_IsValidNumber(team_b)) + if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED) { - LOG_FATALF("IsTeamEqualToTeam: team_b is invalid: %f", team_b); + LOG_FATAL("TeamBalance_FindBestTeams: " + "TeamBalance_GetTeamCounts has not been called."); } - if (team_a == team_b) + if (MUTATOR_CALLHOOK(TeamBalance_FindBestTeams, player) == true) { - return true; + return M_ARGV(1, float); } - // 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) + int team_bits = 0; + int previous_team = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) { - case 1: + if (!TeamBalance_IsTeamAllowedInternal(balance, i)) { - num_players_team_a = c1; - num_bots_team_a = num_bots_team1; - score_team_a = team1_score; - break; + continue; } - case 2: + if (previous_team == 0) { - num_players_team_a = c2; - num_bots_team_a = num_bots_team2; - score_team_a = team2_score; - break; + team_bits = Team_IndexToBit(i); + previous_team = i; + continue; } - case 3: + int compare = TeamBalance_CompareTeams(balance, i, previous_team, + player, use_score); + if (compare == TEAMS_COMPARE_LESS) { - num_players_team_a = c3; - num_bots_team_a = num_bots_team3; - score_team_a = team3_score; - break; + team_bits = Team_IndexToBit(i); + previous_team = i; + continue; } - case 4: + if (compare == TEAMS_COMPARE_EQUAL) { - num_players_team_a = c4; - num_bots_team_a = num_bots_team4; - score_team_a = team4_score; - break; + team_bits |= Team_IndexToBit(i); + previous_team = i; } } - switch (team_b) + return team_bits; +} + +int TeamBalance_CompareTeams(entity balance, int team_index_a, int team_index_b, + entity player, bool use_score) +{ + if (balance == NULL) { - 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; - } + LOG_FATAL("TeamBalance_CompareTeams: Team balance entity is NULL."); } - // invalid - if (num_players_team_a < 0 || num_players_team_b < 0) - return false; - - if (IS_REAL_CLIENT(player) && bots_would_leave) + if (balance.m_team_balance_state != TEAM_BALANCE_TEAM_COUNTS_FILLED) { - num_players_team_a -= num_bots_team_a; - num_players_team_b -= num_bots_team_b; + LOG_FATAL("TeamBalance_CompareTeams: " + "TeamBalance_GetTeamCounts has not been called."); } - if (!use_score) + if (!Team_IsValidIndex(team_index_a)) { - return num_players_team_a == num_players_team_b; + LOG_FATALF("TeamBalance_CompareTeams: team_index_a is invalid: %f", + team_index_a); } - if (num_players_team_a != num_players_team_b) + if (!Team_IsValidIndex(team_index_b)) { - return false; + LOG_FATALF("TeamBalance_CompareTeams: team_index_b is invalid: %f", + team_index_b); + } + if (team_index_a == team_index_b) + { + return TEAMS_COMPARE_EQUAL; } - return score_team_a == score_team_b; + entity team_a = TeamBalance_GetTeamFromIndex(balance, team_index_a); + entity team_b = TeamBalance_GetTeamFromIndex(balance, team_index_b); + return TeamBalance_CompareTeamsInternal(team_a, team_b, player, use_score); } -int FindBestTeams(entity player, bool use_score) +void TeamBalance_AutoBalanceBots() { - if (MUTATOR_CALLHOOK(FindBestTeams, player) == true) - { - return M_ARGV(1, float); - } - int team_bits = 0; - int previous_team = 0; - if (c1 >= 0) + if (!autocvar_g_balance_teams || + !autocvar_g_balance_teams_prevent_imbalance) { - team_bits = BIT(0); - previous_team = 1; + return; } - if (c2 >= 0) + //PrintToChatAll("TeamBalance_AutoBalanceBots"); + entity balance = TeamBalance_CheckAllowedTeams(NULL); + TeamBalance_GetTeamCounts(balance, NULL); + int smallest_team_index = 0; + int smallest_team_player_count = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) { - if (previous_team == 0) + entity team_ = TeamBalance_GetTeamFromIndex(balance, i); + if (!TeamBalanceTeam_IsAllowed(team_)) { - team_bits = BIT(1); - previous_team = 2; + continue; } - else if (IsTeamSmallerThanTeam(2, previous_team, player, use_score)) + int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_); + if (smallest_team_index == 0) { - team_bits = BIT(1); - previous_team = 2; + smallest_team_index = i; + smallest_team_player_count = playercount; } - else if (IsTeamEqualToTeam(2, previous_team, player, use_score)) + else if (playercount < smallest_team_player_count) { - team_bits |= BIT(1); - previous_team = 2; + smallest_team_index = i; + smallest_team_player_count = playercount; } } - if (c3 >= 0) + //PrintToChatAll(sprintf("Smallest team: %f", smallest_team_index)); + //PrintToChatAll(sprintf("Smallest team players: %f", smallest_team_player_count)); + entity switchable_bot = NULL; + int teams = BITS(NUM_TEAMS); + while (teams != 0) { - if (previous_team == 0) + int largest_team_index = TeamBalance_GetLargestTeamIndex(balance, + teams); + if (smallest_team_index == largest_team_index) { - team_bits = BIT(2); - previous_team = 3; + TeamBalance_Destroy(balance); + return; } - else if (IsTeamSmallerThanTeam(3, previous_team, player, use_score)) + entity largest_team = TeamBalance_GetTeamFromIndex(balance, + largest_team_index); + int largest_team_player_count = TeamBalanceTeam_GetNumberOfPlayers( + largest_team); + if (largest_team_player_count - smallest_team_player_count < 2) { - team_bits = BIT(2); - previous_team = 3; + TeamBalance_Destroy(balance); + return; } - else if (IsTeamEqualToTeam(3, previous_team, player, use_score)) + //PrintToChatAll(sprintf("Largest team: %f", largest_team_index)); + //PrintToChatAll(sprintf("Largest team players: %f", largest_team_player_count)); + switchable_bot = TeamBalance_GetPlayerForTeamSwitch(largest_team_index, + smallest_team_index, true); + if (switchable_bot != NULL) { - team_bits |= BIT(2); - previous_team = 3; + break; } + teams &= ~Team_IndexToBit(largest_team_index); } - if (c4 >= 0) + TeamBalance_Destroy(balance); + if (switchable_bot == NULL) { - if (previous_team == 0) + //PrintToChatAll("No bot found after searching through all the teams"); + return; + } + SetPlayerTeam(switchable_bot, smallest_team_index, TEAM_CHANGE_AUTO); +} + +int TeamBalance_GetLargestTeamIndex(entity balance, int teams) +{ + int largest_team_index = 0; + int largest_team_player_count = 0; + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (!(Team_IndexToBit(i) & teams)) { - team_bits = BIT(3); + continue; } - else if (IsTeamSmallerThanTeam(4, previous_team, player, use_score)) + entity team_ = TeamBalance_GetTeamFromIndex(balance, i); + if (!TeamBalanceTeam_IsAllowed(team_)) { - team_bits = BIT(3); + continue; } - else if (IsTeamEqualToTeam(4, previous_team, player, use_score)) + int playercount = TeamBalanceTeam_GetNumberOfPlayers(team_); + if (largest_team_index == 0) { - team_bits |= BIT(3); + largest_team_index = i; + largest_team_player_count = playercount; + } + else if (playercount > largest_team_player_count) + { + largest_team_index = i; + largest_team_player_count = playercount; } } - return team_bits; -} - -// returns # of smallest team (1, 2, 3, 4) -// NOTE: Assumes CheckAllowedTeams has already been called! -int FindSmallestTeam(entity player, float ignore_player) -{ - // count how many players are in each team - if (ignore_player) - { - GetTeamCounts(player); - } - 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 ((team_bits & BIT(0)) != 0) - { - RandomSelection_AddFloat(1, 1, 1); - } - if ((team_bits & BIT(1)) != 0) - { - RandomSelection_AddFloat(2, 1, 1); - } - if ((team_bits & BIT(2)) != 0) - { - RandomSelection_AddFloat(3, 1, 1); - } - if ((team_bits & BIT(3)) != 0) - { - RandomSelection_AddFloat(4, 1, 1); - } - return RandomSelection_chosen_float; + return largest_team_index; } -void JoinBestTeam(entity this, bool force_best_team) +entity TeamBalance_GetPlayerForTeamSwitch(int source_team_index, + int destination_team_index, bool is_bot) { - // don't join a team if we're not playing a team game - if (!teamplay) + if (MUTATOR_CALLHOOK(TeamBalance_GetPlayerForTeamSwitch, source_team_index, + destination_team_index, is_bot)) { - return; + return M_ARGV(3, entity); } - - // find out what teams are available - CheckAllowedTeams(this); - - // if we don't care what team they end up on, put them on whatever team they entered as. - // if they're not on a valid team, then let other code put them on the smallest team - if (!force_best_team) + entity lowest_player = NULL; + float lowest_score = FLOAT_MAX; + FOREACH_CLIENT(Entity_GetTeamIndex(it) == source_team_index, { - int selected_team; - if ((c1 >= 0) && (this.team == NUM_TEAM_1)) - { - selected_team = this.team; - } - else if ((c2 >= 0) && (this.team == NUM_TEAM_2)) + if (IS_BOT_CLIENT(it) != is_bot) { - selected_team = this.team; + continue; } - else if ((c3 >= 0) && (this.team == NUM_TEAM_3)) + float temp_score = PlayerScore_Get(it, SP_SCORE); + if (temp_score >= lowest_score) { - selected_team = this.team; + continue; } - else if ((c4 >= 0) && (this.team == NUM_TEAM_4)) + //PrintToChatAll(sprintf( + // "Found %s with lowest score, checking allowed teams", it.netname)); + entity balance = TeamBalance_CheckAllowedTeams(it); + if (TeamBalance_IsTeamAllowed(balance, source_team_index)) { - selected_team = this.team; + //PrintToChatAll("Allowed"); + lowest_player = it; + lowest_score = temp_score; } else { - selected_team = -1; + //PrintToChatAll("Not allowed"); } + TeamBalance_Destroy(balance); + }); + return lowest_player; +} - if (selected_team > 0) - { - SetPlayerTeamSimple(this, selected_team); - LogTeamchange(this.playerid, this.team, 99); - return; - } - } - // otherwise end up on the smallest team (handled below) - if (this.bot_forced_team) +void LogTeamChange(float player_id, float team_number, int type) +{ + if (!autocvar_sv_eventlog) { return; } - int best_team = FindSmallestTeam(this, true); - 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 ((old_team != -1) && !IS_BOT_CLIENT(this)) + if (player_id < 1) { - AutoBalanceBots(old_team, Team_TeamToNumber(best_team)); + return; } - KillPlayerForTeamChange(this); + GameLogEcho(sprintf(":team:%f:%f:%f", player_id, team_number, type)); } -void SV_ChangeTeam(entity this, float _color) +void KillPlayerForTeamChange(entity player) { - float source_color, destination_color, source_team, destination_team; - - // in normal deathmatch we can just apply the color and we're done - if(!teamplay) - SetPlayerColors(this, _color); - - if(!IS_CLIENT(this)) + if (IS_DEAD(player)) { - // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CONNECTING, this.netname); return; } - - if(!teamplay) + if (MUTATOR_CALLHOOK(Player_ChangeTeamKill, player) == true) + { return; + } + Damage(player, player, player, 100000, DEATH_TEAMCHANGE.m_id, DMG_NOWEP, + player.origin, '0 0 0'); +} - source_color = this.clientcolors & 0x0F; - destination_color = _color & 0x0F; +bool TeamBalance_IsTeamAllowedInternal(entity balance, int index) +{ + return balance.m_team_balance_team[index - 1].m_num_players != + TEAM_NOT_ALLOWED; +} - source_team = Team_TeamToNumber(source_color + 1); - destination_team = Team_TeamToNumber(destination_color + 1); +void TeamBalance_BanTeamsExcept(entity balance, int index) +{ + for (int i = 1; i <= NUM_TEAMS; ++i) + { + if (i != index) + { + balance.m_team_balance_team[i - 1].m_num_players = TEAM_NOT_ALLOWED; + } + } +} - if (destination_team == -1) +entity TeamBalance_GetTeamFromIndex(entity balance, int index) +{ + if (!Team_IsValidIndex(index)) { - return; + LOG_FATALF("TeamBalance_GetTeamFromIndex: Index is invalid: %f", index); } + return balance.m_team_balance_team[index - 1]; +} - CheckAllowedTeams(this); +entity TeamBalance_GetTeam(entity balance, int team_num) +{ + return TeamBalance_GetTeamFromIndex(balance, Team_TeamToIndex(team_num)); +} - 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; +bool TeamBalanceTeam_IsAllowed(entity team_ent) +{ + return team_ent.m_num_players != TEAM_NOT_ALLOWED; +} - // not changing teams - if (source_color == destination_color) - { - SetPlayerTeam(this, destination_team, source_team, true); - return; - } +int TeamBalanceTeam_GetNumberOfPlayers(entity team_ent) +{ + return team_ent.m_num_players; +} - if((autocvar_g_campaign) || (autocvar_g_changeteam_banned && CS(this).wasplayer)) { - Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_NOTALLOWED); - return; // changing teams is not allowed - } +int TeamBalanceTeam_GetNumberOfBots(entity team_ent) +{ + return team_ent.m_num_bots; +} - // 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) +int TeamBalance_CompareTeamsInternal(entity team_a, entity team_b, + entity player, bool use_score) +{ + if (team_a == team_b) { - GetTeamCounts(this); - if ((BIT(destination_team - 1) & FindBestTeams(this, false)) == 0) - { - Send_Notification(NOTIF_ONE, this, MSG_INFO, INFO_TEAMCHANGE_LARGERTEAM); - return; - } + return TEAMS_COMPARE_EQUAL; } - if(IS_PLAYER(this) && source_team != destination_team) + if (!TeamBalanceTeam_IsAllowed(team_a) || + !TeamBalanceTeam_IsAllowed(team_b)) { - // reduce frags during a team change - TeamchangeFrags(this); + return TEAMS_COMPARE_INVALID; } - if (!SetPlayerTeam(this, destination_team, source_team, !IS_CLIENT(this))) + int num_players_team_a = team_a.m_num_players; + int num_players_team_b = team_b.m_num_players; + if (IS_REAL_CLIENT(player) && bots_would_leave) { - return; + num_players_team_a -= team_a.m_num_bots; + num_players_team_b -= team_b.m_num_bots; } - AutoBalanceBots(source_team, destination_team); - if (!IS_PLAYER(this) || (source_team == destination_team)) + if (num_players_team_a < num_players_team_b) { - return; + return TEAMS_COMPARE_LESS; } - KillPlayerForTeamChange(this); -} - -void AutoBalanceBots(int source_team, int destination_team) -{ - if (!Team_IsValidNumber(source_team)) + if (num_players_team_a > num_players_team_b) { - LOG_WARNF("AutoBalanceBots: Source team is invalid: %f", source_team); - return; + return TEAMS_COMPARE_GREATER; } - if (!Team_IsValidNumber(destination_team)) + if (!use_score) { - LOG_WARNF("AutoBalanceBots: Destination team is invalid: %f", - destination_team); - return; + return TEAMS_COMPARE_EQUAL; } - if (!autocvar_g_balance_teams || - !autocvar_g_balance_teams_prevent_imbalance) + if (team_a.m_team_score < team_b.m_team_score) { - return; + return TEAMS_COMPARE_LESS; } - int num_players_source_team = 0; - int num_players_destination_team = 0; - entity lowest_bot_destination_team = NULL; - switch (source_team) + if (team_a.m_team_score > team_b.m_team_score) { - case 1: - { - num_players_source_team = c1; - break; - } - case 2: - { - num_players_source_team = c2; - break; - } - case 3: - { - num_players_source_team = c3; - break; - } - case 4: - { - num_players_source_team = c4; - break; - } + return TEAMS_COMPARE_GREATER; } - if (num_players_source_team < 0) + return TEAMS_COMPARE_EQUAL; +} + +void SV_ChangeTeam(entity player, int new_color) +{ + if (!teamplay) { - return; + SetPlayerColors(player, new_color); } - switch (destination_team) + // TODO: Should we really bother with this? + if(!IS_CLIENT(player)) { - 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; - } + // since this is an engine function, and gamecode doesn't have any calls earlier than this, do the connecting message here + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_CONNECTING, + player.netname); + return; } - if ((num_players_destination_team <= num_players_source_team) || - (lowest_bot_destination_team == NULL)) + if (!teamplay) { return; } - SetPlayerTeamSimple(lowest_bot_destination_team, - Team_NumberToTeam(source_team)); - KillPlayerForTeamChange(lowest_bot_destination_team); + Player_SetTeamIndexChecked(player, Team_TeamToIndex((new_color & 0x0F) + + 1)); } diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index 7c4ebe77b6..33f9d02d7e 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -1,120 +1,326 @@ #pragma once -string cache_mutatormsg; -string cache_lastmutatormsg; +int autocvar_teamplay_mode; -// The following variables are used for balancing. They are not updated -// automatically. You need to call CheckAllowedTeams and GetTeamCounts to get -// proper values. +bool autocvar_g_changeteam_banned; +bool autocvar_teamplay_lockonrestart; -// 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. +bool autocvar_g_balance_teams; +bool autocvar_g_balance_teams_prevent_imbalance; -int redowned, blueowned, yellowowned, pinkowned; +bool lockteams; -//float audit_teams_time; +// ========================== Global teams API ================================ -void TeamchangeFrags(entity e); +/// \brief Returns the global team entity at the given index. +/// \param[in] index Index of the team. +/// \return Global team entity at the given index. +entity Team_GetTeamFromIndex(int index); -void LogTeamchange(float player_id, float team_number, float type); +/// \brief Returns the global team entity that corresponds to the given TEAM_NUM +/// value. +/// \param[in] team_num Team value. See TEAM_NUM constants. +/// \return Global team entity that corresponds to the given TEAM_NUM value. +entity Team_GetTeam(int team_num); -void default_delayedinit(entity this); +// ========================= Team specific API ================================ -void InitGameplayMode(); +/// \brief Returns the score of the team. +/// \param[in] team_ent Team entity. +/// \return Score of the team. +float Team_GetTeamScore(entity team_ent); -string GetClientVersionMessage(entity this); +/// \brief Sets the score of the team. +/// \param[in,out] team_ent Team entity. +/// \param[in] score Score to set. +void Team_SetTeamScore(entity team_ent, float score); -string getwelcomemessage(entity this); +/// \brief Returns the number of alive players in a team. +/// \param[in] team_ent Team entity. +/// \return Number of alive players in a team. +int Team_GetNumberOfAlivePlayers(entity team_ent); -void SetPlayerColors(entity player, float _color); +/// \brief Sets the number of alive players in a team. +/// \param[in,out] team_ent Team entity. +/// \param[in] number Number of players to set. +void Team_SetNumberOfAlivePlayers(entity team_ent, int number); -/// \brief Kills player as a result of team change. -/// \param[in,out] player Player to kill. -/// \return No return. -void KillPlayerForTeamChange(entity player); +/// \brief Returns the number of alive teams. +/// \return Number of alive teams. +int Team_GetNumberOfAliveTeams(); -/// \brief Sets the team of the player. +/// \brief Returns the number of control points owned by a team. +/// \param[in] team_ent Team entity. +/// \return Number of control points owned by a team. +int Team_GetNumberOfControlPoints(entity team_ent); + +/// \brief Sets the number of control points owned by a team. +/// \param[in,out] team_ent Team entity. +/// \param[in] number Number of control points to set. +void Team_SetNumberOfControlPoints(entity team_ent, int number); + +/// \brief Returns the number of teams that own control points. +/// \return Number of teams that own control points. +int Team_GetNumberOfTeamsWithControlPoints(); + +// ======================= Entity specific API ================================ + +void setcolor(entity this, int clr); + +/// \brief Returns whether the given entity belongs to a valid team. +/// \param[in] this Entity to check. +/// \return True if entity belongs to a valid team, false otherwise. +bool Entity_HasValidTeam(entity this); + +/// \brief Returns the team index of the given entity. +/// \param[in] this Entity to check. +/// \return Team index of the entity. +int Entity_GetTeamIndex(entity this); + +/// \brief Returns the team entity of the given entity. +/// \param[in] this Entity to check. +/// \return Team entity of the given entity or NULL if the entity doesn't belong +/// to any team. +entity Entity_GetTeam(entity this); + +void SetPlayerColors(entity player, float _color); + +/// \brief Sets the team of the player using its index. /// \param[in,out] player Player to adjust. -/// \param[in] team_num Team number to set. See TEAM_NUM constants. +/// \param[in] index Index of the team to set. /// \return True if team switch was successful, false otherwise. -bool SetPlayerTeamSimple(entity player, int team_num); +bool Player_SetTeamIndex(entity player, int index); + +enum +{ + TEAM_CHANGE_AUTO = 2, ///< The team was selected by autobalance. + TEAM_CHANGE_MANUAL = 3, ///< Player has manually selected their team. + TEAM_CHANGE_SPECTATOR = 4 ///< Player is joining spectators. //TODO: Remove? +}; /// \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. +/// \param[in] team_index Index of the team to set. +/// \param[in] type Type of the team change. See TEAM_CHANGE constants. /// \return True if team switch was successful, false otherwise. -bool SetPlayerTeam(entity player, int destination_team, int source_team, - bool no_print); +bool SetPlayerTeam(entity player, int team_index, int type); -// set c1...c4 to show what teams are allowed -void CheckAllowedTeams(entity for_whom); +/// \brief Sets the team of the player with all sanity checks. +/// \param[in,out] player Player to adjust. +/// \param[in] team_index Index of the team to set. +void Player_SetTeamIndexChecked(entity player, int team_index); -float PlayerValue(entity p); +/// \brief Moves player to the specified team. +/// \param[in,out] client Client to move. +/// \param[in] team_index Index of the team. +/// \param[in] type ??? +/// \return True on success, false otherwise. +bool MoveToTeam(entity client, int team_index, int type); -// c1...c4 should be set to -1 (not allowed) or 0 (allowed). -// teams that are allowed will now have their player counts stored in c1...c4 -void GetTeamCounts(entity ignore); +enum +{ + TEAM_FORCE_SPECTATOR = -1, ///< Force the player to spectator team. + TEAM_FORCE_DEFAULT = 0 ///< Don't force any team. +}; -/// \brief Returns whether one team is smaller than the other. -/// \param[in] team_a First team. -/// \param[in] team_b Second team. +/// \brief Returns whether player has real forced team. Spectator team is +/// ignored. /// \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); +/// \return True if player has real forced team, false otherwise. +bool Player_HasRealForcedTeam(entity player); -/// \brief Returns whether one team is equal to the other. -/// \param[in] team_a First team. -/// \param[in] team_b Second team. +/// \brief Returns the index of the forced team of the given player. +/// \param[in] player Player to check. +/// \return Index of the forced team. +int Player_GetForcedTeamIndex(entity player); + +/// \brief Sets the index of the forced team of the given player. +/// \param[in,out] player Player to adjust. +/// \param[in] team_index Index of the team to set. +void Player_SetForcedTeamIndex(entity player, int team_index); + +/// \brief Determines the forced team of the player using current global config. +/// \param[in,out] player Player to adjust. +void Player_DetermineForcedTeam(entity player); + +// ========================= Team balance API ================================= + +/// \brief Assigns the given player to a team that will make the game most +/// balanced. +/// \param[in,out] player Player to assign. +void TeamBalance_JoinBestTeam(entity player); + +/// \brief Checks whether the player can join teams according to global +/// configuration and mutator settings. +/// \param[in] for_whom Player to check for. Pass NULL for global rules. +/// \return Team balance entity that holds information about teams. This entity +/// will be automatically destroyed on the next frame but you are encouraged to +/// manually destroy it by calling TeamBalance_Destroy for performance reasons. +entity TeamBalance_CheckAllowedTeams(entity for_whom); + +/// \brief Destroy the team balance entity. +/// \param[in,out] balance Team balance entity to destroy. +/// \note Team balance entity is allowed to be NULL. +void TeamBalance_Destroy(entity balance); + +/// \brief Returns the bitmask of allowed teams. +/// \param[in] balance Team balance entity. +/// \return Bitmask of allowed teams. +int TeamBalance_GetAllowedTeams(entity balance); + +/// \brief Returns whether the team change to the specified team is allowed. +/// \param[in] balance Team balance entity. +/// \param[in] index Index of the team. +/// \return True if team change to the specified team is allowed, false +/// otherwise. +bool TeamBalance_IsTeamAllowed(entity balance, int index); + +/// \brief Counts the number of players and various other information about +/// each team. +/// \param[in,out] balance Team balance entity. +/// \param[in] ignore Player to ignore. This is useful if you plan to switch the +/// player's team. Pass NULL for global information. +/// \note This function updates the internal state of the team balance entity. +void TeamBalance_GetTeamCounts(entity balance, entity ignore); + +/// \brief Returns the number of players (both humans and bots) in a team. +/// \param[in] balance Team balance entity. +/// \param[in] index Index of the team. +/// \return Number of player (both humans and bots) in a team. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalance_GetNumberOfPlayers(entity balance, int index); + +/// \brief Finds the team that will make the game most balanced if the player +/// joins it. +/// \param[in] balance Team balance entity. +/// \param[in] player Player to check. +/// \param[in] ignore_player ??? +/// \return Index of the team that will make the game most balanced if the +/// player joins it. If there are several equally good teams available, the +/// function will pick a random one. +int TeamBalance_FindBestTeam(entity balance, entity player, bool ignore_player); + +/// \brief Returns the bitmask of the teams that will make the game most +/// balanced if the player joins any of them. +/// \param[in] balance Team balance entity. /// \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); +/// \return Bitmask of the teams that will make the game most balanced if the +/// player joins any of them. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalance_FindBestTeams(entity balance, entity player, bool use_score); + +/// \brief Describes the result of comparing teams. +enum +{ + TEAMS_COMPARE_INVALID, ///< One or both teams are invalid. + TEAMS_COMPARE_LESS, ///< First team is less than the second one. + TEAMS_COMPARE_EQUAL, ///< Both teams are equal. + TEAMS_COMPARE_GREATER ///< First team the greater than the second one. +}; -/// \brief Returns the bitmask of the best teams for the player to join. +/// \brief Compares two teams for the purposes of game balance. +/// \param[in] balance Team balance entity. +/// \param[in] team_index_a Index of the first team. +/// \param[in] team_index_b Index of the second team. /// \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); +/// \return TEAMS_COMPARE value. See above. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalance_CompareTeams(entity balance, int team_index_a, int team_index_b, + entity player, bool use_score); -// returns # of smallest team (1, 2, 3, 4) -// NOTE: Assumes CheckAllowedTeams has already been called! -int FindSmallestTeam(entity player, float ignore_player); +/// \brief Switches a bot from one team to another if teams are not balanced. +void TeamBalance_AutoBalanceBots(); -void JoinBestTeam(entity this, bool force_best_team); +/// \brief Returns the index of the team with most players that is contained in +/// the given bitmask of teams. +/// \param[in] balance Team balance entity. +/// \param[in] teams Bitmask of teams to search in. +/// \return Index of the team with most players. +int TeamBalance_GetLargestTeamIndex(entity balance, int teams); -/// \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); +/// \brief Returns the player who is the most suitable for switching between +/// the given teams. +/// \param[in] source_team_index Index of the team to search in. +/// \param[in] destination_team_index Index of the team to switch to. +/// \param[in] is_bot True to search for bot, false for human. +/// \return Player who is the most suitable for switching between the given +/// teams or NULL if not found. +entity TeamBalance_GetPlayerForTeamSwitch(int source_team_index, + int destination_team_index, bool is_bot); -void setcolor(entity this, int clr); +// ============================ Internal API ================================== + +void LogTeamChange(float player_id, float team_number, int type); + +/// \brief Kills player as a result of team change. +/// \param[in,out] player Player to kill. +void KillPlayerForTeamChange(entity player); + +/// \brief Returns whether the team change to the specified team is allowed. +/// \param[in] balance Team balance entity. +/// \param[in] index Index of the team. +/// \return True if team change to the specified team is allowed, false +/// otherwise. +/// \note This function bypasses all the sanity checks. +bool TeamBalance_IsTeamAllowedInternal(entity balance, int index); + +/// \brief Bans team change to all teams except the given one. +/// \param[in,out] balance Team balance entity. +/// \param[in] index Index of the team. +void TeamBalance_BanTeamsExcept(entity balance, int index); + +/// \brief Returns the team entity of the team balance entity at the given +/// index. +/// \param[in] balance Team balance entity. +/// \param[in] index Index of the team. +/// \return Team entity of the team balance entity at the given index. +entity TeamBalance_GetTeamFromIndex(entity balance, int index); + +/// \brief Returns the team entity of the team balance entity that corresponds +/// to the given TEAM_NUM value. +/// \param[in] balance Team balance entity. +/// \param[in] team_num Team value. See TEAM_NUM constants. +/// \return Team entity of the team balance entity that corresponds to the given +/// TEAM_NUM value. +entity TeamBalance_GetTeam(entity balance, int team_num); + +/// \brief Returns whether the team is allowed. +/// \param[in] team_ent Team entity. +/// \return True if team is allowed, false otherwise. +bool TeamBalanceTeam_IsAllowed(entity team_ent); + +/// \brief Returns the number of players (both humans and bots) in a team. +/// \param[in] team_ent Team entity. +/// \return Number of player (both humans and bots) in a team. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalanceTeam_GetNumberOfPlayers(entity team_ent); + +/// \brief Returns the number of bots in a team. +/// \param[in] team_ent Team entity. +/// \return Number of bots in a team. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalanceTeam_GetNumberOfBots(entity team_ent); + +/// \brief Compares two teams for the purposes of game balance. +/// \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 TEAMS_COMPARE value. See above. +/// \note You need to call TeamBalance_GetTeamCounts before calling this +/// function. +int TeamBalance_CompareTeamsInternal(entity team_a, entity team_index_b, + entity player, bool use_score); + +/// \brief Called when the player connects or when they change their color with +/// the "color" command. +/// \param[in,out] player Player that requested a new color. +/// \param[in] new_color Requested color. +void SV_ChangeTeam(entity player, int new_color); diff --git a/qcsrc/server/tests.qc b/qcsrc/server/tests.qc index 50dc5a35bc..e52d4fcf11 100644 --- a/qcsrc/server/tests.qc +++ b/qcsrc/server/tests.qc @@ -2,7 +2,7 @@ void test_weapons_hurt(entity this) { - EXPECT_NE(100, this.health); + EXPECT_NE(100, GetResourceAmount(this, RESOURCE_HEALTH)); delete(this.enemy); delete(this); } diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index 1eda4e25b1..0dc71ddcc0 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -1,6 +1,6 @@ #include "accuracy.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include <common/constants.qh> #include <common/net_linked.qh> #include <common/teams.qh> @@ -61,30 +61,31 @@ void accuracy_resend(entity e) //.float hit_time; .float fired_time; -void accuracy_add(entity this, int w, int fired, int hit) +void accuracy_add(entity this, Weapon w, int fired, int hit) { if (IS_INDEPENDENT_PLAYER(this)) return; entity a = CS(this).accuracy; if (!a) return; if (!hit && !fired) return; - if (w == WEP_Null.m_id) return; - w -= WEP_FIRST; - int b = accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w]); - if (hit) a.accuracy_hit [w] += hit; - if (fired) a.accuracy_fired[w] += fired; + if (w == WEP_Null) return; + int wepid = w.m_id; + wepid -= WEP_FIRST; + int b = accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid]); + if (hit) a.accuracy_hit [wepid] += hit; + if (fired) a.accuracy_fired[wepid] += fired; if (hit && STAT(HIT_TIME, a) != time) { // only run this once per frame - a.accuracy_cnt_hit[w] += 1; + a.accuracy_cnt_hit[wepid] += 1; STAT(HIT_TIME, a) = time; } if (fired && a.fired_time != time) { // only run this once per frame - a.accuracy_cnt_fired[w] += 1; + a.accuracy_cnt_fired[wepid] += 1; a.fired_time = time; } - if (b == accuracy_byte(a.accuracy_hit[w], a.accuracy_fired[w])) return; // no change - int sf = 1 << (w % 24); + if (b == accuracy_byte(a.accuracy_hit[wepid], a.accuracy_fired[wepid])) return; // no change + int sf = 1 << (wepid % 24); a.SendFlags |= sf; FOREACH_CLIENT(IS_SPEC(it) && it.enemy == this, { CS(it).accuracy.SendFlags |= sf; }); } @@ -94,6 +95,7 @@ bool accuracy_isgooddamage(entity attacker, entity targ) int mutator_check = MUTATOR_CALLHOOK(AccuracyTargetValid, attacker, targ); if (warmup_stage) return false; + if (game_stopped) return false; // damage to dead/frozen players is good only if it happens in the frame they get killed / frozen // so that stats for weapons that shoot multiple projectiles per shot are properly counted @@ -104,12 +106,12 @@ bool accuracy_isgooddamage(entity attacker, entity targ) if (mutator_check == MUT_ACCADD_INVALID) return true; if (mutator_check != MUT_ACCADD_VALID) return false; - if (!IS_CLIENT(targ)) return false; + if (!IS_CLIENT(targ) || !IS_CLIENT(attacker)) return false; return true; } bool accuracy_canbegooddamage(entity attacker) { - return !warmup_stage; + return !warmup_stage && IS_CLIENT(attacker); } diff --git a/qcsrc/server/weapons/accuracy.qh b/qcsrc/server/weapons/accuracy.qh index d24ee1cf50..627698aa21 100644 --- a/qcsrc/server/weapons/accuracy.qh +++ b/qcsrc/server/weapons/accuracy.qh @@ -25,7 +25,7 @@ void accuracy_free(entity e); void accuracy_resend(entity e); // update accuracy stats -void accuracy_add(entity e, float w, float fired, float hit); +void accuracy_add(entity e, Weapon w, float fired, float hit); // helper bool accuracy_isgooddamage(entity attacker, entity targ); diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 90aca172cc..06615c4eac 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -30,7 +30,7 @@ void W_GiveWeapon(entity e, int wep) { if (!wep) return; - e.weapons |= WepSet_FromWeapon(Weapons_from(wep)); + STAT(WEAPONS, e) |= WepSet_FromWeapon(Weapons_from(wep)); if (IS_PLAYER(e)) { Send_Notification(NOTIF_ONE, e, MSG_MULTI, ITEM_WEAPON_GOT, wep); diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc index e6bdd00d2b..e79e9ddb64 100644 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@ -3,7 +3,6 @@ #include <server/defs.qh> #include <server/miscfunctions.qh> #include "../antilag.qh" -#include "../g_subs.qh" #include <common/weapons/_all.qh> #include <common/state.qh> #include <common/wepent.qh> diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index f059dfba9b..ea5bd22551 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -52,7 +52,7 @@ bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andam // ignore hook button when using other offhand equipment if (this.offhand != OFFHAND_HOOK) - if (wpn == WEP_HOOK && !((this.weapons | weaponsInMap) & WepSet_FromWeapon(wpn))) + if (wpn == WEP_HOOK && !((STAT(WEAPONS, this) | weaponsInMap) & WepSet_FromWeapon(wpn))) complain = 0; if (complain) @@ -66,7 +66,7 @@ bool client_hasweapon(entity this, Weapon wpn, .entity weaponentity, float andam } if (autocvar_g_weaponswitch_debug == 2 && weaponslot(weaponentity) > 0 && !(wpn.spawnflags & WEP_FLAG_DUALWIELD) && !(PS(this).dual_weapons & wpn.m_wepset)) return false; // no complaints needed - if (this.weapons & WepSet_FromWeapon(wpn)) + if (STAT(WEAPONS, this) & WepSet_FromWeapon(wpn)) { if (andammo) { @@ -161,12 +161,12 @@ float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, fl FOREACH(Weapons, it != WEP_Null, { if(i != weaponwant) if(it.impulse == imp || imp < 0) - if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) + if((STAT(WEAPONS, this) & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) have_other = true; }); // skip weapons we don't own that aren't normal and aren't in the map - if(!(this.weapons & wepset)) + if(!(STAT(WEAPONS, this) & wepset)) if(!(weaponsInMap & wepset)) if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) continue; @@ -217,12 +217,12 @@ float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, fl FOREACH(Weapons, it != WEP_Null, { if(i != weaponwant) if(it.impulse == imp || imp < 0) - if((this.weapons & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) + if((STAT(WEAPONS, this) & (it.m_wepset)) || (weaponsInMap & (it.m_wepset))) have_other = true; }); // skip weapons we don't own that aren't normal and aren't in the map - if(!(this.weapons & wepset)) + if(!(STAT(WEAPONS, this) & wepset)) if(!(weaponsInMap & wepset)) if((wep.spawnflags & WEP_FLAG_MUTATORBLOCKED) || have_other) continue; @@ -240,7 +240,7 @@ float W_GetCycleWeapon(entity this, string weaponorder, float dir, float imp, fl void W_SwitchWeapon_Force(Player this, Weapon wep, .entity weaponentity) { - TC(Weapon, wep); + TC(Weapon, wep); this.(weaponentity).cnt = this.(weaponentity).m_switchweapon.m_id; this.(weaponentity).m_switchweapon = wep; this.(weaponentity).selectweapon = wep.m_id; @@ -252,11 +252,11 @@ void W_SwitchToOtherWeapon(entity this, .entity weaponentity) // hack to ensure it switches to an OTHER weapon (in case the other fire mode still has ammo, we want that anyway) Weapon ww; WepSet set = WepSet_FromWeapon(this.(weaponentity).m_weapon); - if (this.weapons & set) + if (STAT(WEAPONS, this) & set) { - this.weapons &= ~set; + STAT(WEAPONS, this) &= ~set; ww = w_getbestweapon(this, weaponentity); - this.weapons |= set; + STAT(WEAPONS, this) |= set; } else { @@ -266,20 +266,34 @@ void W_SwitchToOtherWeapon(entity this, .entity weaponentity) W_SwitchWeapon_Force(this, ww, weaponentity); } -void W_SwitchWeapon(entity this, Weapon w, .entity weaponentity) +bool W_SwitchWeapon(entity this, Weapon w, .entity weaponentity) { if(this.(weaponentity).m_switchweapon != w) { if(client_hasweapon(this, w, weaponentity, true, true)) + { W_SwitchWeapon_Force(this, w, weaponentity); + return true; + } else + { this.(weaponentity).selectweapon = w.m_id; // update selectweapon anyway + return false; + } } - else if(!forbidWeaponUse(this)) + else if(!forbidWeaponUse(this) && CS(this).cvar_cl_weapon_switch_reload) { entity actor = this; w.wr_reload(w, actor, weaponentity); } + + return true; // player already has the weapon out or needs to reload +} + +void W_SwitchWeapon_TryOthers(entity this, Weapon w, .entity weaponentity) +{ + if(!W_SwitchWeapon(this, w, weaponentity) && CS(this).cvar_cl_weapon_switch_fallback_to_impulse) + W_NextWeaponOnImpulse(this, w.impulse, weaponentity); } void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity) diff --git a/qcsrc/server/weapons/selection.qh b/qcsrc/server/weapons/selection.qh index eea33ddb7b..dd21e6419d 100644 --- a/qcsrc/server/weapons/selection.qh +++ b/qcsrc/server/weapons/selection.qh @@ -18,7 +18,8 @@ void W_SwitchWeapon_Force(Player this, Weapon w, .entity weaponentity); // perform weapon to attack (weaponstate and attack_finished check is here) void W_SwitchToOtherWeapon(entity this, .entity weaponentity); -void W_SwitchWeapon(entity this, Weapon imp, .entity weaponentity); +bool W_SwitchWeapon(entity this, Weapon imp, .entity weaponentity); // returns false if the player does not have the weapon +void W_SwitchWeapon_TryOthers(entity this, Weapon imp, .entity weaponentity); void W_CycleWeapon(entity this, string weaponorder, float dir, .entity weaponentity); diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc index d47351cb37..4ab5a43717 100644 --- a/qcsrc/server/weapons/spawning.qc +++ b/qcsrc/server/weapons/spawning.qc @@ -2,7 +2,7 @@ #include "weaponsystem.qh" #include "../resources.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include <common/t_items.qh> #include <server/items.qh> #include <common/weapons/_all.qh> @@ -56,29 +56,20 @@ void weapon_defaultspawnfunc(entity this, Weapon e) for (int i = 1; i < t; ++i) { s = argv(i); - FOREACH(Weapons, it != WEP_Null, { - if(it.netname == s) - { - entity replacement = spawn(); - copyentity(this, replacement); - replacement.m_isreplaced = true; - weapon_defaultspawnfunc(replacement, it); - break; - } - }); + Weapon wep = Weapons_fromstr(s); + if(wep != WEP_Null) + { + entity replacement = spawn(); + copyentity(this, replacement); + replacement.m_isreplaced = true; + weapon_defaultspawnfunc(replacement, wep); + } } } if (t >= 1) // always the case! { s = argv(0); - wpn = WEP_Null; - FOREACH(Weapons, it != WEP_Null, { - if(it.netname == s) - { - wpn = it; - break; - } - }); + wpn = Weapons_fromstr(s); } if (wpn == WEP_Null) { @@ -88,6 +79,13 @@ void weapon_defaultspawnfunc(entity this, Weapon e) } } + if (!Item_IsDefinitionAllowed(wpn.m_pickup)) + { + delete(this); + startitem_failed = true; + return; + } + if (!this.respawntime) { if (wpn.spawnflags & WEP_FLAG_SUPERWEAPON) diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc index ae745efd6f..9aaabb05bf 100644 --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@ -3,13 +3,13 @@ #include "weaponsystem.qh" #include "../resources.qh" #include "../items.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include <common/t_items.qh> #include "../g_damage.qh" #include <common/items/item.qh> #include <common/mapinfo.qh> #include <common/notifications/all.qh> -#include <common/triggers/subs.qh> +#include <common/mapobjects/subs.qh> #include <common/util.qh> #include <common/weapons/_all.qh> #include <common/state.qh> @@ -33,11 +33,9 @@ void thrown_wep_think(entity this) SUB_VanishOrRemove(this); } -// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count -string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity) +// returns amount of ammo used, or -1 for failure, or 0 for no ammo count +float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity) { - float thisammo; - string s; Weapon info = Weapons_from(wpn); int ammotype = info.ammo_type; @@ -65,7 +63,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto int superweapons = 1; FOREACH(Weapons, it != WEP_Null, { WepSet set = it.m_wepset; - if((set & WEPSET_SUPERWEAPONS) && (own.weapons & set)) ++superweapons; + if((set & WEPSET_SUPERWEAPONS) && (STAT(WEAPONS, own) & set)) ++superweapons; }); if(superweapons <= 1) { @@ -84,7 +82,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto weapon_defaultspawnfunc(wep, info); if(startitem_failed) - return string_null; + return -1; setthink(wep, thrown_wep_think); wep.savenextthink = wep.nextthink; wep.nextthink = min(wep.nextthink, time + 0.5); @@ -93,12 +91,10 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto //wa = W_AmmoItemCode(wpn); if(ammotype == RESOURCE_NONE) { - return ""; + return 0; } else { - s = ""; - if(doreduce && g_weapon_stay == 2) { // if our weapon is loaded, give its load back to the player @@ -121,23 +117,13 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto } float ownderammo = GetResourceAmount(own, ammotype); - thisammo = min(ownderammo, GetResourceAmount(wep, ammotype)); + float thisammo = min(ownderammo, GetResourceAmount(wep, ammotype)); SetResourceAmount(wep, ammotype, thisammo); SetResourceAmount(own, ammotype, ownderammo - thisammo); - switch (ammotype) - { - 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); + return thisammo; } - return s; + return 0; } } @@ -152,19 +138,7 @@ bool W_IsWeaponThrowable(entity this, int w) if(w == WEP_Null.m_id) return false; - #if 0 - if(start_weapons & WepSet_FromWeapon(Weapons_from(w))) - { - // 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_type == RESOURCE_NONE) - return false; - } - return true; - #else return (Weapons_from(w)).weaponthrowable; - #endif } // toss current weapon @@ -183,21 +157,21 @@ void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, return; WepSet set = WepSet_FromWeapon(w); - if(!(this.weapons & set)) return; - this.weapons &= ~set; + if(!(STAT(WEAPONS, this) & set)) return; + STAT(WEAPONS, this) &= ~set; W_SwitchWeapon_Force(this, w_getbestweapon(this, weaponentity), weaponentity); - string a = W_ThrowNewWeapon(this, w.m_id, doreduce, this.origin + delta, velo, weaponentity); + float a = W_ThrowNewWeapon(this, w.m_id, doreduce, this.origin + delta, velo, weaponentity); - if(!a) return; - Send_Notification(NOTIF_ONE, this, MSG_MULTI, ITEM_WEAPON_DROP, a, w.m_id); + if(a < 0) return; + Send_Notification(NOTIF_ONE, this, MSG_MULTI, ITEM_WEAPON_DROP, w.m_id, a); } void SpawnThrownWeapon(entity this, vector org, Weapon wep, .entity weaponentity) { //entity wep = this.(weaponentity).m_weapon; - if(this.weapons & WepSet_FromWeapon(wep)) + if(STAT(WEAPONS, this) & WepSet_FromWeapon(wep)) if(W_IsWeaponThrowable(this, wep.m_id)) W_ThrowNewWeapon(this, wep.m_id, false, org, randomvec() * 125 + '0 0 200', weaponentity); } diff --git a/qcsrc/server/weapons/throwing.qh b/qcsrc/server/weapons/throwing.qh index 9ea5e5cb8e..20732753e4 100644 --- a/qcsrc/server/weapons/throwing.qh +++ b/qcsrc/server/weapons/throwing.qh @@ -6,8 +6,8 @@ .float savenextthink; void thrown_wep_think(entity this); -// returns amount of ammo used as string, or -1 for failure, or 0 for no ammo count -string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity); +// returns amount of ammo used, or -1 for failure, or 0 for no ammo count +float W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo, .entity weaponentity); bool W_IsWeaponThrowable(entity this, int w); diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index a3898c6277..9e78aa3c36 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -8,7 +8,6 @@ #include "weaponsystem.qh" #include "../g_damage.qh" -#include "../g_subs.qh" #include "../antilag.qh" #include <common/constants.qh> @@ -27,13 +26,21 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range, int deathtype) { TC(Sound, snd); - float nudge = 1; // added to traceline target and subtracted from result TOOD(divVerent): do we still need this? Doesn't the engine do this now for us? float oldsolid = ent.dphitcontentsmask; Weapon wep = DEATH_WEAPONOF(deathtype); if(!IS_CLIENT(ent)) antilag = false; // no antilag for non-clients! if (IS_PLAYER(ent) && (wep.spawnflags & WEP_FLAG_PENETRATEWALLS)) + { + // This is the reason rifle, MG, OKMG and other fireBullet weapons don't hit the crosshair when shooting at walls. + // This is intentional, otherwise if you stand too close to a (glass) wall and attempt to shoot an enemy through it, + // trueaim will cause the shot to hit the wall exactly but then miss the enemy (unless shooting from eye/center). + // TODO for fireBullet, find how far the shot will penetrate and aim at that + // for fireRailgunbullet, find the farthest target and aim at that + // this will avoid issues when another player is passing in front of you when you shoot + // (currently such a shot will hit him but then miss the original target) ent.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_CORPSE; + } else ent.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; if(antilag) @@ -58,7 +65,7 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vect // track max damage if (IS_PLAYER(ent) && accuracy_canbegooddamage(ent)) - accuracy_add(ent, wep.m_id, maxdamage, 0); + accuracy_add(ent, wep, maxdamage, 0); if(IS_PLAYER(ent)) W_HitPlotAnalysis(ent, wep, v_forward, v_right, v_up); @@ -66,22 +73,25 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vect vector md = ent.(weaponentity).movedir; vector vecs = ((md.x > 0) ? md : '0 0 0'); - vector dv = v_right * -vecs.y + v_up * vecs.z; - w_shotorg = ent.origin + ent.view_ofs + dv; + vector dv = v_forward * vecs.x + v_right * -vecs.y + v_up * vecs.z; + w_shotorg = ent.origin + ent.view_ofs; // now move the shotorg forward as much as requested if possible if(antilag) { if(CS(ent).antilag_debug) - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent, CS(ent).antilag_debug); + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent, CS(ent).antilag_debug); else - tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); + tracebox_antilag(ent, w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent, ANTILAG_LATENCY(ent)); } else - tracebox(w_shotorg, mi, ma, w_shotorg + v_forward * (vecs.x + nudge), MOVE_NORMAL, ent); - w_shotorg = trace_endpos - v_forward * nudge; + tracebox(w_shotorg, mi, ma, w_shotorg + dv, MOVE_NORMAL, ent); + w_shotorg = trace_endpos; // calculate the shotdir from the chosen shotorg - w_shotdir = normalize(w_shotend - w_shotorg); + if(W_DualWielding(ent)) + w_shotdir = s_forward; + else + w_shotdir = normalize(w_shotend - w_shotorg); //vector prevdir = w_shotdir; //vector prevorg = w_shotorg; @@ -136,12 +146,12 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, .entity weaponentity, vect ent.punchangle_x = recoil * -1; if (snd != SND_Null) { - sound (ent, chan, snd, (W_DualWielding(ent) ? VOL_BASE * 0.7 : VOL_BASE), ATTN_NORM); + sound(ent, chan, snd, (W_DualWielding(ent) ? VOL_BASE * 0.7 : VOL_BASE), ATTN_NORM); W_PlayStrengthSound(ent); } // nudge w_shotend so a trace to w_shotend hits - w_shotend = w_shotend + normalize(w_shotend - w_shotorg) * nudge; + w_shotend = w_shotend + normalize(w_shotend - w_shotorg); //if(w_shotend != prevend) { printf("SERVER: shotEND differs: %s - %s\n", vtos(w_shotend), vtos(prevend)); } //if(w_shotorg != prevorg) { printf("SERVER: shotORG differs: %s - %s\n", vtos(w_shotorg), vtos(prevorg)); } //if(w_shotdir != prevdir) { printf("SERVER: shotDIR differs: %s - %s\n", vtos(w_shotdir), vtos(prevdir)); } @@ -264,7 +274,7 @@ void FireRailgunBullet (entity this, .entity weaponentity, vector start, vector //explosion = spawn(); // Find all non-hit players the beam passed close by - if(deathtype == WEP_VAPORIZER.m_id || deathtype == WEP_VORTEX.m_id) + if(deathtype == WEP_VAPORIZER.m_id || deathtype == WEP_VORTEX.m_id) // WEAPONTODO { FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != this, { if(!it.railgunhit) @@ -319,7 +329,8 @@ void FireRailgunBullet (entity this, .entity weaponentity, vector start, vector IL_CLEAR(g_railgunhit); // calculate hits and fired shots for hitscan - accuracy_add(this, this.(weaponentity).m_weapon.m_id, 0, min(bdamage, totaldmg)); + if(this.(weaponentity)) + accuracy_add(this, this.(weaponentity).m_weapon, 0, min(bdamage, totaldmg)); trace_endpos = endpoint; trace_ent = endent; @@ -334,7 +345,7 @@ void fireBullet_trace_callback(vector start, vector hit, vector end) fireBullet_last_hit = NULL; } -void fireBullet(entity this, .entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects) +void fireBullet(entity this, .entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, entity tracer_effect) { vector end; @@ -342,16 +353,11 @@ void fireBullet(entity this, .entity weaponentity, vector start, vector dir, flo end = start + dir * max_shot_distance; fireBullet_last_hit = NULL; + fireBullet_trace_callback_eff = tracer_effect; + float solid_penetration_left = 1; float total_damage = 0; - if(tracereffects & EF_RED) - fireBullet_trace_callback_eff = EFFECT_RIFLE; - else if(tracereffects & EF_BLUE) - fireBullet_trace_callback_eff = EFFECT_RIFLE_WEAK; - else - fireBullet_trace_callback_eff = EFFECT_BULLET; - float lag = ((IS_REAL_CLIENT(this)) ? ANTILAG_LATENCY(this) : 0); if(lag < 0.001) lag = 0; @@ -424,7 +430,7 @@ void fireBullet(entity this, .entity weaponentity, vector start, vector dir, flo // do not exceed 100% float added_damage = min(damage - total_damage, damage * solid_penetration_left); total_damage += damage * solid_penetration_left; - accuracy_add(this, this.(weaponentity).m_weapon.m_id, 0, added_damage); + accuracy_add(this, this.(weaponentity).m_weapon, 0, added_damage); } } diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh index 9224a970cf..9e39ecc350 100644 --- a/qcsrc/server/weapons/tracing.qh +++ b/qcsrc/server/weapons/tracing.qh @@ -57,4 +57,4 @@ void FireRailgunBullet (entity this, .entity weaponentity, vector start, vector entity fireBullet_trace_callback_eff; entity fireBullet_last_hit; void fireBullet_trace_callback(vector start, vector hit, vector end); -void fireBullet(entity this, .entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, int tracereffects); +void fireBullet(entity this, .entity weaponentity, vector start, vector dir, float spread, float max_solid_penetration, float damage, float force, float dtype, entity tracer_effect); diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 809db42710..d791fc798e 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -3,9 +3,10 @@ #include "selection.qh" #include "../command/common.qh" -#include "../mutators/_mod.qh" +#include <server/mutators/_mod.qh> #include "../round_handler.qh" -#include "../resources.qh" +#include <server/cheats.qh> +#include <server/resources.qh> #include <common/t_items.qh> #include <common/animdecide.qh> #include <common/constants.qh> @@ -49,7 +50,7 @@ bool CL_Weaponentity_CustomizeEntityForClient(entity this, entity client) { this.viewmodelforclient = this.owner; if (IS_SPEC(client) && client.enemy == this.owner) this.viewmodelforclient = client; - return true; + return false; } vector CL_Weapon_GetShotOrg(int wpn) @@ -178,6 +179,7 @@ void CL_SpawnWeaponentity(entity actor, .entity weaponentity) setthink(view, CL_Weaponentity_Think); view.nextthink = time; view.viewmodelforclient = actor; + view.draggable = drag_undraggable; setcefc(view, CL_Weaponentity_CustomizeEntityForClient); wepent_link(view); @@ -187,6 +189,7 @@ void CL_SpawnWeaponentity(entity actor, .entity weaponentity) entity exterior = actor.exteriorweaponentity = new(exteriorweaponentity); exterior.solid = SOLID_NOT; exterior.owner = actor; + exterior.draggable = drag_undraggable; exterior.weaponentity_fld = weaponentity; setorigin(exterior, '0 0 0'); setthink(exterior, CL_ExteriorWeaponentity_Think); @@ -289,9 +292,8 @@ bool weapon_prepareattack_check(Weapon thiswep, entity actor, .entity weaponenti if (attacktime >= 0) { - int slot = weaponslot(weaponentity); // don't fire if previous attack is not finished - if (ATTACK_FINISHED(actor, slot) > time + actor.(weaponentity).weapon_frametime * 0.5) return false; + if (ATTACK_FINISHED(actor, weaponentity) > time + actor.(weaponentity).weapon_frametime * 0.5) return false; entity this = actor.(weaponentity); // don't fire while changing weapon if (this.state != WS_READY) return false; @@ -310,16 +312,35 @@ void weapon_prepareattack_do(entity actor, .entity weaponentity, bool secondary, // if the weapon hasn't been firing continuously, reset the timer if (attacktime >= 0) { - int slot = weaponslot(weaponentity); - if (ATTACK_FINISHED(actor, slot) < time - this.weapon_frametime * 1.5) + if (ATTACK_FINISHED(actor, weaponentity) < time - this.weapon_frametime * 1.5) { - ATTACK_FINISHED(actor, slot) = time; + ATTACK_FINISHED(actor, weaponentity) = time; // dprint("resetting attack finished to ", ftos(time), "\n"); } - ATTACK_FINISHED(actor, slot) = ATTACK_FINISHED(actor, slot) + attacktime * W_WeaponRateFactor(actor); + float arate = W_WeaponRateFactor(actor); + ATTACK_FINISHED(actor, weaponentity) = ATTACK_FINISHED(actor, weaponentity) + attacktime * arate; + + if(autocvar_g_weaponswitch_debug_alternate && W_DualWielding(actor)) + { + int slot = weaponslot(weaponentity); + for(int wepslot = 0; wepslot < MAX_WEAPONSLOTS; ++wepslot) + { + if(slot == wepslot) + continue; + .entity wepent = weaponentities[wepslot]; + if(actor.(wepent) && actor.(wepent).m_weapon != WEP_Null) + { + if(ATTACK_FINISHED(actor, wepent) > time + actor.(wepent).weapon_frametime * 0.5) + continue; // still cooling down! + if (ATTACK_FINISHED(actor, wepent) < time - actor.(wepent).weapon_frametime * 1.5) + ATTACK_FINISHED(actor, wepent) = time; + ATTACK_FINISHED(actor, wepent) = ATTACK_FINISHED(actor, wepent) + (attacktime * arate) / MAX_WEAPONSLOTS; + } + } + } } this.bulletcounter += 1; - // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, slot)), "\n"); + // dprint("attack finished ", ftos(ATTACK_FINISHED(actor, weaponentity)), "\n"); } bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bool secondary, float attacktime) @@ -332,8 +353,6 @@ bool weapon_prepareattack(Weapon thiswep, entity actor, .entity weaponentity, bo return false; } -void wframe_send(entity actor, entity weaponentity, vector a, bool restartanim); - /** * @param t defer thinking until time + t * @param func next think function @@ -430,12 +449,12 @@ bool forbidWeaponUse(entity player) void W_WeaponFrame(Player actor, .entity weaponentity) { - TC(Player, actor); - TC(PlayerState, PS(actor)); + TC(Player, actor); + TC(PlayerState, PS(actor)); entity this = actor.(weaponentity); if (frametime) this.weapon_frametime = frametime; - if (!this || actor.health < 1) return; // Dead player can't use weapons and injure impulse commands + if (!this || GetResourceAmount(actor, RESOURCE_HEALTH) < 1) return; // Dead player can't use weapons and injure impulse commands int button_atck = PHYS_INPUT_BUTTON_ATCK(actor); int button_atck2 = PHYS_INPUT_BUTTON_ATCK2(actor); @@ -537,7 +556,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) entity oldwep = this.m_weapon; // set up weapon switch think in the future, and start drop anim - if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponslot(weaponentity)) <= time + this.weapon_frametime * 0.5) + if (INDEPENDENT_ATTACK_FINISHED || ATTACK_FINISHED(actor, weaponentity) <= time + this.weapon_frametime * 0.5) { sound(actor, CH_WEAPON_SINGLE, SND_WEAPON_SWITCH, VOL_BASE, ATTN_NORM); this.state = WS_DROP; @@ -550,7 +569,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) // LordHavoc: network timing test code // if (actor.button0) - // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, slot)), " >= ", ftos(this.weapon_nextthink), "\n"); + // print(ftos(frametime), " ", ftos(time), " >= ", ftos(ATTACK_FINISHED(actor, weaponentity)), " >= ", ftos(this.weapon_nextthink), "\n"); Weapon w = this.m_weapon; @@ -559,7 +578,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) // server framerate is very low and the weapon fire rate very high for (int c = 0; c < W_TICSPERFRAME; ++c) { - if (w != WEP_Null && !(actor.weapons & WepSet_FromWeapon(w))) + if (w != WEP_Null && !(STAT(WEAPONS, actor) & WepSet_FromWeapon(w))) { if (this.m_weapon == this.m_switchweapon) W_SwitchWeapon_Force(actor, w_getbestweapon(actor, weaponentity), weaponentity); w = WEP_Null; @@ -576,7 +595,7 @@ void W_WeaponFrame(Player actor, .entity weaponentity) key_pressed = false; Weapon off = actor.offhand; - if (off && !(actor.weapons & WEPSET(HOOK))) + if (off && (!(STAT(WEAPONS, actor) & WEPSET(HOOK)) || off != OFFHAND_HOOK)) { if (off.offhand_think) off.offhand_think(off, actor, key_pressed); } @@ -597,8 +616,8 @@ void W_WeaponFrame(Player actor, .entity weaponentity) if (!block_weapon) { - Weapon e = this.m_weapon; - TC(Weapon, e); + Weapon e = this.m_weapon; + TC(Weapon, e); if (w != WEP_Null) { e.wr_think(e, actor, weaponentity, button_atck | (button_atck2 << 1)); @@ -660,9 +679,11 @@ void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use, .entity weaponentity) { - if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor, actor.(weaponentity))) return; + if (MUTATOR_CALLHOOK(W_DecreaseAmmo, actor, actor.(weaponentity), ammo_use)) return; if ((actor.items & IT_UNLIMITED_WEAPON_AMMO) && !wep.reloading_ammo) return; + ammo_use = M_ARGV(2, float); + entity w_ent = actor.(weaponentity); // if this weapon is reloadable, decrease its load. Else decrease the player's ammo @@ -678,7 +699,7 @@ void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use, .entity weaponenti { 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", + "Please notify the developers immediately with a copy of this backtrace!\n", ammo_use, wep.netname, GetAmmoPicture(wep.ammo_type), @@ -724,14 +745,14 @@ void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - // ATTACK_FINISHED(actor, slot) -= w_ent.reload_time - 1; + // ATTACK_FINISHED(actor, weaponentity) -= w_ent.reload_time - 1; w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1)); } void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sent_sound) { - TC(Sound, sent_sound); + TC(Sound, sent_sound); // set global values to work with entity this = actor.(weaponentity); Weapon e = this.m_weapon; @@ -741,8 +762,7 @@ void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sen this.reload_ammo_min = sent_ammo_min; this.reload_ammo_amount = e.reloading_ammo; this.reload_time = e.reloading_time; - if (actor.reload_sound) strunzone(actor.reload_sound); - actor.reload_sound = strzone(Sound_fixpath(sent_sound)); + strcpy(actor.reload_sound, Sound_fixpath(sent_sound)); // don't reload weapons that don't have the RELOADABLE flag if (!(e.spawnflags & WEP_FLAG_RELOADABLE)) @@ -798,7 +818,7 @@ void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sen // then quickly switch to another weapon and back. Reloading is canceled, but the reload delay is still there, // so your weapon is disabled for a few seconds without reason - // ATTACK_FINISHED(actor, slot) = max(time, ATTACK_FINISHED(actor, slot)) + this.reload_time + 1; + // ATTACK_FINISHED(actor, weaponentity) = max(time, ATTACK_FINISHED(actor, weaponentity)) + this.reload_time + 1; weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, this.reload_time, W_ReloadedAndReady); diff --git a/qcsrc/tools/qcc.sh b/qcsrc/tools/qcc.sh index 15cbfc4aab..865e6e592c 100755 --- a/qcsrc/tools/qcc.sh +++ b/qcsrc/tools/qcc.sh @@ -32,7 +32,7 @@ function qpp() { err=$? set -e if [ ${err} -ne 0 ]; then return ${err}; fi - sed 's/^#\(line\)\? \([[:digit:]]\+\) "\(.*\)".*/\n#pragma file(\3)\n#pragma line(\2)/g' "${WORKDIR}/${MODE}.txt" + sed -E 's/^#(line)? ([[:digit:]]+) "(.*)".*/'$'\\\n''#pragma file(\3)'$'\\\n''#pragma line(\2)/g' "${WORKDIR}/${MODE}.txt" } function qcc() { diff --git a/randomitems-overkill.cfg b/randomitems-overkill.cfg new file mode 100644 index 0000000000..e2a2617e57 --- /dev/null +++ b/randomitems-overkill.cfg @@ -0,0 +1,21 @@ +// Random items mutator config for Overkill ruleset + +// Map items + +set g_random_items_item_health_mega_probability 1 "Probability of random mega health spawning in the map during overkill." +set g_random_items_item_armor_small_probability 10 "Probability of random small armor spawning in the map during overkill." +set g_random_items_item_armor_medium_probability 4 "Probability of random medium armor spawning in the map during overkill." +set g_random_items_item_armor_big_probability 2 "Probability of random big armor spawning in the map during overkill." +set g_random_items_item_armor_mega_probability 1 "Probability of random mega armor spawning in the map during overkill." +set g_random_items_weapon_okhmg_probability 0.5 "Probability of random overkill HMG spawning in the map during overkill." +set g_random_items_weapon_okrpc_probability 0.5 "Probability of random overkill RPC spawning in the map during overkill." + +// Loot + +set g_random_loot_item_health_mega_probability 1 "Probability of random mega health spawning as loot during overkill." +set g_random_loot_item_armor_small_probability 10 "Probability of random small armor spawning as loot during overkill." +set g_random_loot_item_armor_medium_probability 4 "Probability of random medium armor spawning as loot during overkill." +set g_random_loot_item_armor_big_probability 2 "Probability of random big armor spawning as loot during overkill." +set g_random_loot_item_armor_mega_probability 1 "Probability of random mega armor spawning as loot during overkill." +set g_random_loot_weapon_okhmg_probability 1 "Probability of random overkill HMG spawning as loot during overkill." +set g_random_loot_weapon_okrpc_probability 1 "Probability of random overkill RPC spawning as loot during overkill." diff --git a/randomitems-xonotic.cfg b/randomitems-xonotic.cfg index 803e6c3ae8..47611a9e23 100644 --- a/randomitems-xonotic.cfg +++ b/randomitems-xonotic.cfg @@ -29,6 +29,9 @@ set g_random_items_replace_weapon_shockwave "random" "Classnames to replace shoc 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_okshotgun "random" "Classnames to replace overkill shotgun with." +set g_random_items_replace_weapon_okmachinegun "random" "Classnames to replace overkill machinegun with." +set g_random_items_replace_weapon_oknex "random" "Classnames to replace overkill nex 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." @@ -36,8 +39,8 @@ 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_weapon_okhmg "random" "Classnames to replace overkill HMG with." +set g_random_items_replace_weapon_okrpc "random" "Classnames to replace overkill 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." @@ -78,6 +81,9 @@ set g_random_items_weapon_shockwave_probability 0 "Probability of random shockwa 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_okshotgun_probability 0 "Probability of random overkill shotgun spawning in the map." +set g_random_items_weapon_okmachinegun_probability 0 "Probability of random overkill machinegun spawning in the map." +set g_random_items_weapon_oknex_probability 0 "Probability of random overkill nex 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." @@ -85,6 +91,11 @@ set g_random_items_weapon_hlac_probability 0 "Probability of random HLAC spawnin 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_weapon_okshotgun_probability 0 "Probability of random overkill shotgun spawning in the map." +set g_random_items_weapon_okmachinegun_probability 0 "Probability of random overkill machinegun spawning in the map." +set g_random_items_weapon_oknex_probability 0 "Probability of random overkill nex spawning in the map." +set g_random_items_weapon_okhmg_probability 0 "Probability of random overkill HMG spawning in the map." +set g_random_items_weapon_okrpc_probability 0 "Probability of random overkill RPC 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." @@ -93,13 +104,6 @@ set g_random_items_item_vaporizer_cells_probability 20 "Probability of random va 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 @@ -139,6 +143,9 @@ set g_random_loot_weapon_shockwave_probability 0 "Probability of random shockwav 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_okshotgun_probability 0 "Probability of random overkill shotgun spawning as loot." +set g_random_loot_weapon_okmachinegun_probability 0 "Probability of random overkill machinegun spawning as loot." +set g_random_loot_weapon_oknex_probability 0 "Probability of random overkill nex 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." @@ -146,6 +153,11 @@ set g_random_loot_weapon_hlac_probability 0 "Probability of random HLAC spawning 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_weapon_okshotgun_probability 0 "Probability of random overkill shotgun spawning as loot." +set g_random_loot_weapon_okmachinegun_probability 0 "Probability of random overkill machinegun spawning as loot." +set g_random_loot_weapon_oknex_probability 0 "Probability of random overkill nex spawning as loot." +set g_random_loot_weapon_okhmg_probability 0 "Probability of random overkill HMG spawning as loot." +set g_random_loot_weapon_okrpc_probability 0 "Probability of random overkill RPC 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." @@ -154,10 +166,3 @@ set g_random_loot_item_vaporizer_cells_probability 20 "Probability of random vap 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." diff --git a/ruleset-XDF.cfg b/ruleset-XDF.cfg index b37f01dcff..20740d9a86 100644 --- a/ruleset-XDF.cfg +++ b/ruleset-XDF.cfg @@ -7,7 +7,7 @@ exec balance-xdf.cfg exec physicsXDF.cfg // general gameplay -set g_jump_grunt 1 // make enemies even easier to hear when they're jumping around +// set g_jump_grunt 1 // just no set g_shootfromcenter 1 // hit where you point at with the crosshair (almost so, no shooteye because it's really ugly) set g_balance_kill_antispam 0 set g_forced_respawn 1 diff --git a/ruleset-XPM.cfg b/ruleset-XPM.cfg index f3607bb2f8..67589147b6 100644 --- a/ruleset-XPM.cfg +++ b/ruleset-XPM.cfg @@ -14,6 +14,7 @@ set teamplay_mode 2 // friendly fire and self damage set sv_vote_nospectators 1 set g_chat_nospectators 2 set g_warmup 1 +set g_warmup_limit 0 set g_balance_teams 0 set g_spawnshieldtime 0 set g_spawn_furthest 1 @@ -24,3 +25,4 @@ set g_monsters 0 set g_turrets 0 set g_vehicles 0 set sv_showspectators 0 +set sv_taunt 0 diff --git a/ruleset-nexuiz.cfg b/ruleset-nexuiz.cfg new file mode 100644 index 0000000000..7441942200 --- /dev/null +++ b/ruleset-nexuiz.cfg @@ -0,0 +1,11 @@ +exec xonotic-server.cfg + +exec balance-nexuiz25.cfg +exec physicsNexuiz26.cfg + +sv_gameplayfix_delayprojectiles 1 + +// new toys to restore the weapons +g_new_toys 1 +g_new_toys_autoreplace 0 +g_new_toys_use_pickupsound 0 diff --git a/ruleset-overkill.cfg b/ruleset-overkill.cfg index 49eb730c93..15086f22a6 100644 --- a/ruleset-overkill.cfg +++ b/ruleset-overkill.cfg @@ -5,6 +5,8 @@ exec xonotic-server.cfg exec balance-overkill.cfg exec physicsOverkill.cfg +exec randomitems-overkill.cfg +if_dedicated exec help-overkill.cfg // general gameplay set g_overkill 1 diff --git a/scripts/cellammo.Shader b/scripts/cellammo.Shader new file mode 100644 index 0000000000..94b1c8878b --- /dev/null +++ b/scripts/cellammo.Shader @@ -0,0 +1,9 @@ +cellammo +{ + dpreflectcube cubemaps/default/sky + dpoffsetmapping - 0.5 match8 128 + { + map textures/items/cellammo + rgbgen lightingDiffuse + } +} \ No newline at end of file diff --git a/scripts/crylink.shader b/scripts/crylink.shader new file mode 100644 index 0000000000..ab59f84aa1 --- /dev/null +++ b/scripts/crylink.shader @@ -0,0 +1,9 @@ +crylink_new +{ + dpreflectcube cubemaps/default/sky + { + map textures/crylink_new.tga + rgbgen lightingDiffuse + } +} + diff --git a/scripts/electro.shader b/scripts/electro.shader index 84fa240505..f87b86c8d9 100644 --- a/scripts/electro.shader +++ b/scripts/electro.shader @@ -14,3 +14,12 @@ electro_plasma_hull rgbGen Vertex } } + +electro +{ + dpreflectcube cubemaps/default/sky + { + map textures/electronew.tga + rgbgen lightingDiffuse + } +} \ No newline at end of file diff --git a/scripts/explosiveammo.shader b/scripts/explosiveammo.shader new file mode 100644 index 0000000000..d6c3a90d32 --- /dev/null +++ b/scripts/explosiveammo.shader @@ -0,0 +1,20 @@ +explosive_ammo +{ + dpreflectcube cubemaps/default/sky + { + map textures/items/explosiveammo.tga + rgbgen lightingDiffuse + } +} +explosive_ammo_icons +{ + { + + animMap 2 textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_01.tga textures/items/explosiveammo_icon_blank.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_02.tga textures/items/explosiveammo_icon_blank.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_03.tga textures/items/explosiveammo_icon_blank.tga + blendFunc GL_ONE GL_ONE + rgbGen wave sawtooth 0 1 0 10 + } + +} + + diff --git a/scripts/weapons.shader b/scripts/weapons.shader index 491fd052ea..85f27d5c0b 100644 --- a/scripts/weapons.shader +++ b/scripts/weapons.shader @@ -1,11 +1,3 @@ -electro -{ - dpreflectcube cubemaps/default/sky - { - map textures/electro.tga - rgbgen lightingDiffuse - } -} nexgun { dpreflectcube cubemaps/default/sky diff --git a/sound/misc/kill.ogg b/sound/misc/kill.ogg deleted file mode 100644 index c1d3b8c237..0000000000 Binary files a/sound/misc/kill.ogg and /dev/null differ diff --git a/sound/misc/kill.wav b/sound/misc/kill.wav new file mode 100644 index 0000000000..139ff57178 Binary files /dev/null and b/sound/misc/kill.wav differ diff --git a/textures/cellammoskin.jpg b/textures/cellammoskin.jpg deleted file mode 100644 index 116ba9d6ad..0000000000 Binary files a/textures/cellammoskin.jpg and /dev/null differ diff --git a/textures/cellammoskin_glow.jpg b/textures/cellammoskin_glow.jpg deleted file mode 100644 index 274913ccc2..0000000000 Binary files a/textures/cellammoskin_glow.jpg and /dev/null differ diff --git a/textures/crylink.tga b/textures/crylink.tga deleted file mode 100644 index c07f02a704..0000000000 Binary files a/textures/crylink.tga and /dev/null differ diff --git a/textures/crylink_bump.tga b/textures/crylink_bump.tga deleted file mode 100644 index bae1984cfe..0000000000 Binary files a/textures/crylink_bump.tga and /dev/null differ diff --git a/textures/crylink_gloss.tga b/textures/crylink_gloss.tga deleted file mode 100644 index 84e2c17c7c..0000000000 Binary files a/textures/crylink_gloss.tga and /dev/null differ diff --git a/textures/crylink_glow.jpg b/textures/crylink_glow.jpg deleted file mode 100644 index de4be8ef2e..0000000000 Binary files a/textures/crylink_glow.jpg and /dev/null differ diff --git a/textures/crylink_new.tga b/textures/crylink_new.tga new file mode 100644 index 0000000000..877f08e273 Binary files /dev/null and b/textures/crylink_new.tga differ diff --git a/textures/crylink_new_gloss.tga b/textures/crylink_new_gloss.tga new file mode 100644 index 0000000000..871a1b3533 Binary files /dev/null and b/textures/crylink_new_gloss.tga differ diff --git a/textures/crylink_new_glow.tga b/textures/crylink_new_glow.tga new file mode 100644 index 0000000000..933ddcf452 Binary files /dev/null and b/textures/crylink_new_glow.tga differ diff --git a/textures/crylink_new_norm.tga b/textures/crylink_new_norm.tga new file mode 100644 index 0000000000..026bf1da63 Binary files /dev/null and b/textures/crylink_new_norm.tga differ diff --git a/textures/crylink_new_reflect.tga b/textures/crylink_new_reflect.tga new file mode 100644 index 0000000000..70d35c0cdf Binary files /dev/null and b/textures/crylink_new_reflect.tga differ diff --git a/textures/crylink_new_shirt.tga b/textures/crylink_new_shirt.tga new file mode 100644 index 0000000000..630dadaf3f Binary files /dev/null and b/textures/crylink_new_shirt.tga differ diff --git a/textures/crylink_reflect.tga b/textures/crylink_reflect.tga deleted file mode 100644 index ec7857b197..0000000000 Binary files a/textures/crylink_reflect.tga and /dev/null differ diff --git a/textures/crylink_shirt.tga b/textures/crylink_shirt.tga deleted file mode 100644 index ae10eccf56..0000000000 Binary files a/textures/crylink_shirt.tga and /dev/null differ diff --git a/textures/electro.tga b/textures/electro.tga deleted file mode 100644 index adf55c2ed5..0000000000 Binary files a/textures/electro.tga and /dev/null differ diff --git a/textures/electro_gloss.tga b/textures/electro_gloss.tga deleted file mode 100644 index d506dcbb0a..0000000000 Binary files a/textures/electro_gloss.tga and /dev/null differ diff --git a/textures/electro_glow.tga b/textures/electro_glow.tga deleted file mode 100644 index 9c6a5bfffe..0000000000 Binary files a/textures/electro_glow.tga and /dev/null differ diff --git a/textures/electro_norm.tga b/textures/electro_norm.tga deleted file mode 100644 index 019abc9ce0..0000000000 Binary files a/textures/electro_norm.tga and /dev/null differ diff --git a/textures/electro_reflect.tga b/textures/electro_reflect.tga deleted file mode 100644 index 95cc2badd8..0000000000 Binary files a/textures/electro_reflect.tga and /dev/null differ diff --git a/textures/electronew.tga b/textures/electronew.tga new file mode 100644 index 0000000000..54e3706196 Binary files /dev/null and b/textures/electronew.tga differ diff --git a/textures/electronew_gloss.tga b/textures/electronew_gloss.tga new file mode 100644 index 0000000000..a59c0805b5 Binary files /dev/null and b/textures/electronew_gloss.tga differ diff --git a/textures/electronew_glow.tga b/textures/electronew_glow.tga new file mode 100644 index 0000000000..f5399689e6 Binary files /dev/null and b/textures/electronew_glow.tga differ diff --git a/textures/electronew_norm.tga b/textures/electronew_norm.tga new file mode 100644 index 0000000000..8341b474af Binary files /dev/null and b/textures/electronew_norm.tga differ diff --git a/textures/electronew_reflect.tga b/textures/electronew_reflect.tga new file mode 100644 index 0000000000..073600b78a Binary files /dev/null and b/textures/electronew_reflect.tga differ diff --git a/textures/electronew_shirt.tga b/textures/electronew_shirt.tga new file mode 100644 index 0000000000..38d99d6aa4 Binary files /dev/null and b/textures/electronew_shirt.tga differ diff --git a/textures/items/a_rocket_bottom.jpg b/textures/items/a_rocket_bottom.jpg deleted file mode 100644 index b2db59520c..0000000000 Binary files a/textures/items/a_rocket_bottom.jpg and /dev/null differ diff --git a/textures/items/a_rocket_box.jpg b/textures/items/a_rocket_box.jpg deleted file mode 100644 index a29505bfe3..0000000000 Binary files a/textures/items/a_rocket_box.jpg and /dev/null differ diff --git a/textures/items/a_rocket_gre.jpg b/textures/items/a_rocket_gre.jpg deleted file mode 100644 index 66fbc3c5d2..0000000000 Binary files a/textures/items/a_rocket_gre.jpg and /dev/null differ diff --git a/textures/items/a_rocket_gre_glow.jpg b/textures/items/a_rocket_gre_glow.jpg deleted file mode 100644 index b26e87da73..0000000000 Binary files a/textures/items/a_rocket_gre_glow.jpg and /dev/null differ diff --git a/textures/items/a_rocket_roc.jpg b/textures/items/a_rocket_roc.jpg deleted file mode 100644 index 300b2b998d..0000000000 Binary files a/textures/items/a_rocket_roc.jpg and /dev/null differ diff --git a/textures/items/a_rocket_roc_gloss.jpg b/textures/items/a_rocket_roc_gloss.jpg deleted file mode 100644 index 0e6f937f3c..0000000000 Binary files a/textures/items/a_rocket_roc_gloss.jpg and /dev/null differ diff --git a/textures/items/a_rocket_roc_glow.jpg b/textures/items/a_rocket_roc_glow.jpg deleted file mode 100644 index 2bb921a722..0000000000 Binary files a/textures/items/a_rocket_roc_glow.jpg and /dev/null differ diff --git a/textures/items/a_rocket_roc_norm.jpg b/textures/items/a_rocket_roc_norm.jpg deleted file mode 100644 index 8073764e47..0000000000 Binary files a/textures/items/a_rocket_roc_norm.jpg and /dev/null differ diff --git a/textures/items/a_rocket_tag.jpg b/textures/items/a_rocket_tag.jpg deleted file mode 100644 index 13146052d3..0000000000 Binary files a/textures/items/a_rocket_tag.jpg and /dev/null differ diff --git a/textures/items/cellammo.tga b/textures/items/cellammo.tga new file mode 100644 index 0000000000..50b648343a Binary files /dev/null and b/textures/items/cellammo.tga differ diff --git a/textures/items/cellammo_gloss.tga b/textures/items/cellammo_gloss.tga new file mode 100644 index 0000000000..64b22bc5f5 Binary files /dev/null and b/textures/items/cellammo_gloss.tga differ diff --git a/textures/items/cellammo_glow.tga b/textures/items/cellammo_glow.tga new file mode 100644 index 0000000000..b44ff6fc5c Binary files /dev/null and b/textures/items/cellammo_glow.tga differ diff --git a/textures/items/cellammo_norm.tga b/textures/items/cellammo_norm.tga new file mode 100644 index 0000000000..f7cd35288b Binary files /dev/null and b/textures/items/cellammo_norm.tga differ diff --git a/textures/items/cellammo_reflect.tga b/textures/items/cellammo_reflect.tga new file mode 100644 index 0000000000..96c0759d4f Binary files /dev/null and b/textures/items/cellammo_reflect.tga differ diff --git a/textures/items/explosiveammo.tga b/textures/items/explosiveammo.tga new file mode 100644 index 0000000000..45aa786d93 Binary files /dev/null and b/textures/items/explosiveammo.tga differ diff --git a/textures/items/explosiveammo_gloss.tga b/textures/items/explosiveammo_gloss.tga new file mode 100644 index 0000000000..22d3f1e377 Binary files /dev/null and b/textures/items/explosiveammo_gloss.tga differ diff --git a/textures/items/explosiveammo_glow.tga b/textures/items/explosiveammo_glow.tga new file mode 100644 index 0000000000..0f1bdb161d Binary files /dev/null and b/textures/items/explosiveammo_glow.tga differ diff --git a/textures/items/explosiveammo_icon_01.tga b/textures/items/explosiveammo_icon_01.tga new file mode 100644 index 0000000000..e368ea4967 Binary files /dev/null and b/textures/items/explosiveammo_icon_01.tga differ diff --git a/textures/items/explosiveammo_icon_01_glow.tga b/textures/items/explosiveammo_icon_01_glow.tga new file mode 100644 index 0000000000..22f16247ef Binary files /dev/null and b/textures/items/explosiveammo_icon_01_glow.tga differ diff --git a/textures/items/explosiveammo_icon_02.tga b/textures/items/explosiveammo_icon_02.tga new file mode 100644 index 0000000000..e368ea4967 Binary files /dev/null and b/textures/items/explosiveammo_icon_02.tga differ diff --git a/textures/items/explosiveammo_icon_02_glow.tga b/textures/items/explosiveammo_icon_02_glow.tga new file mode 100644 index 0000000000..d1dcd8bd9e Binary files /dev/null and b/textures/items/explosiveammo_icon_02_glow.tga differ diff --git a/textures/items/explosiveammo_icon_03.tga b/textures/items/explosiveammo_icon_03.tga new file mode 100644 index 0000000000..e368ea4967 Binary files /dev/null and b/textures/items/explosiveammo_icon_03.tga differ diff --git a/textures/items/explosiveammo_icon_03_glow.tga b/textures/items/explosiveammo_icon_03_glow.tga new file mode 100644 index 0000000000..44a05dcf4e Binary files /dev/null and b/textures/items/explosiveammo_icon_03_glow.tga differ diff --git a/textures/items/explosiveammo_icon_blank.tga b/textures/items/explosiveammo_icon_blank.tga new file mode 100644 index 0000000000..e368ea4967 Binary files /dev/null and b/textures/items/explosiveammo_icon_blank.tga differ diff --git a/textures/items/explosiveammo_norm.tga b/textures/items/explosiveammo_norm.tga new file mode 100644 index 0000000000..67c93ed773 Binary files /dev/null and b/textures/items/explosiveammo_norm.tga differ diff --git a/textures/items/explosiveammo_reflect.tga b/textures/items/explosiveammo_reflect.tga new file mode 100644 index 0000000000..6e2731b907 Binary files /dev/null and b/textures/items/explosiveammo_reflect.tga differ diff --git a/vehicles.cfg b/vehicles.cfg new file mode 100644 index 0000000000..489d82b154 --- /dev/null +++ b/vehicles.cfg @@ -0,0 +1,305 @@ +set g_vehicles 1 + +set g_vehicles_enter 0 "require pressing use key to enter a vehicle" +set g_vehicles_enter_radius 250 +set g_vehicles_steal 1 "allow stealing enemy vehicles in teamplay modes" +set g_vehicles_steal_show_waypoint 1 "show a waypoint above the thief" +set g_vehicles_delayspawn 1 +set g_vehicles_delayspawn_jitter 10 +set g_vehicles_teams 1 "allow team specific vehicles" + +set g_vehicles_teleportable 0 +set g_vehicles_crush_dmg 70 +set g_vehicles_crush_force 50 +set g_vehicles_allow_bots 0 +set g_vehicles_exit_attempts 25 +set g_vehicles_thinkrate 0.1 + +set g_vehicles_vortex_damagerate 0.75 +set g_vehicles_machinegun_damagerate 0.75 +set g_vehicles_rifle_damagerate 0.75 +set g_vehicles_vaporizer_damagerate 0.5 +set g_vehicles_tag_damagerate 5 +set g_vehicles_weapon_damagerate 2 + +// {{{ #1: Bumblebee +set g_vehicle_bumblebee 1 +set g_vehicle_bumblebee_respawntime 60 + +set g_vehicle_bumblebee_speed_forward 350 +set g_vehicle_bumblebee_speed_strafe 350 +set g_vehicle_bumblebee_speed_up 350 +set g_vehicle_bumblebee_speed_down 350 +set g_vehicle_bumblebee_turnspeed 120 +set g_vehicle_bumblebee_pitchspeed 60 +set g_vehicle_bumblebee_pitchlimit 60 +set g_vehicle_bumblebee_friction 0.5 +set g_vehicle_bumblebee_swim 1 + +set g_vehicle_bumblebee_energy 500 +set g_vehicle_bumblebee_energy_regen 50 +set g_vehicle_bumblebee_energy_regen_pause 1 + +set g_vehicle_bumblebee_health 1000 +set g_vehicle_bumblebee_health_regen 65 +set g_vehicle_bumblebee_health_regen_pause 10 + +set g_vehicle_bumblebee_shield 400 +set g_vehicle_bumblebee_shield_regen 150 +set g_vehicle_bumblebee_shield_regen_pause 0.75 + +set g_vehicle_bumblebee_cannon_ammo 100 +set g_vehicle_bumblebee_cannon_ammo_regen 100 +set g_vehicle_bumblebee_cannon_ammo_regen_pause 1 + +set g_vehicle_bumblebee_cannon_lock 1 + +set g_vehicle_bumblebee_cannon_turnspeed 260 +set g_vehicle_bumblebee_cannon_pitchlimit_down 60 +set g_vehicle_bumblebee_cannon_pitchlimit_up 60 +set g_vehicle_bumblebee_cannon_turnlimit_in 20 +set g_vehicle_bumblebee_cannon_turnlimit_out 80 + + +set g_vehicle_bumblebee_raygun_turnspeed 180 +set g_vehicle_bumblebee_raygun_pitchlimit_down 20 +set g_vehicle_bumblebee_raygun_pitchlimit_up 5 +set g_vehicle_bumblebee_raygun_turnlimit_sides 35 + +set g_vehicle_bumblebee_raygun 0 +set g_vehicle_bumblebee_raygun_range 2048 +set g_vehicle_bumblebee_raygun_dps 250 +set g_vehicle_bumblebee_raygun_aps 100 +set g_vehicle_bumblebee_raygun_fps 100 + +set g_vehicle_bumblebee_healgun_hps 150 +set g_vehicle_bumblebee_healgun_hmax 100 +set g_vehicle_bumblebee_healgun_aps 75 +set g_vehicle_bumblebee_healgun_amax 100 +set g_vehicle_bumblebee_healgun_sps 100 +set g_vehicle_bumblebee_healgun_locktime 2.5 + +set g_vehicle_bumblebee_blowup_radius 500 +set g_vehicle_bumblebee_blowup_coredamage 500 +set g_vehicle_bumblebee_blowup_edgedamage 100 +set g_vehicle_bumblebee_blowup_forceintensity 600 +set g_vehicle_bumblebee_bouncepain "1 100 200" + +set g_vehicle_bumblebee_cannon_cost 2 +set g_vehicle_bumblebee_cannon_damage 60 +set g_vehicle_bumblebee_cannon_radius 225 +set g_vehicle_bumblebee_cannon_refire 0.2 +set g_vehicle_bumblebee_cannon_speed 20000 +set g_vehicle_bumblebee_cannon_spread 0 +set g_vehicle_bumblebee_cannon_force -35 +// }}} +// {{{ #2: Racer +set g_vehicle_racer 1 +set g_vehicle_racer_respawntime 35 + +set g_vehicle_racer_thinkrate 0.05 // TODO: any higher causes it to sink in liquids + +set g_vehicle_racer_speed_afterburn 3000 +set g_vehicle_racer_afterburn_cost 130 "energy consumed per second" + +set g_vehicle_racer_waterburn_cost 5 +set g_vehicle_racer_waterburn_speed 750 + +set g_vehicle_racer_water_speed_forward 600 +set g_vehicle_racer_water_speed_strafe 600 + +set g_vehicle_racer_pitchlimit 30 + +set g_vehicle_racer_water_downforce 0.03 +set g_vehicle_racer_water_upforcedamper 15 + +set g_vehicle_racer_anglestabilizer 1.75 +set g_vehicle_racer_downforce 0.01 + +set g_vehicle_racer_speed_forward 650 +set g_vehicle_racer_speed_strafe 650 +set g_vehicle_racer_springlength 90 +set g_vehicle_racer_upforcedamper 2 +set g_vehicle_racer_friction 0.45 + +set g_vehicle_racer_water_time 5 + +set g_vehicle_racer_hovertype 0 "0 = hover, otherwise = maglev" +set g_vehicle_racer_hoverpower 8000 "this is multiplied by 4 for the 4 engines" + +set g_vehicle_racer_turnroll 30 +set g_vehicle_racer_turnspeed 220 +set g_vehicle_racer_pitchspeed 125 + +set g_vehicle_racer_energy 100 +set g_vehicle_racer_energy_regen 90 +set g_vehicle_racer_energy_regen_pause 0.35 + +set g_vehicle_racer_health 200 +set g_vehicle_racer_health_regen 0 +set g_vehicle_racer_health_regen_pause 0 + +set g_vehicle_racer_shield 100 +set g_vehicle_racer_shield_regen 30 +set g_vehicle_racer_shield_regen_pause 1 + +set g_vehicle_racer_rocket_locktarget 1 +set g_vehicle_racer_rocket_locking_time 0.35 +set g_vehicle_racer_rocket_locking_releasetime 0.5 +set g_vehicle_racer_rocket_locked_time 4 + +set g_vehicle_racer_blowup_radius 250 +set g_vehicle_racer_blowup_coredamage 250 +set g_vehicle_racer_blowup_edgedamage 15 +set g_vehicle_racer_blowup_forceintensity 250 + +set g_vehicle_racer_bouncefactor 0.25 "factor of old velocity to keep after collision" +set g_vehicle_racer_bouncestop 0 "if not 0, new velocity after bounce is 0 if new velocity is smaller than this" +set g_vehicle_racer_bouncepain "200 0.15 150" "minspeed_for_pain speedchange_to_pain_factor max_damage" + +set g_vehicle_racer_cannon_cost 1.5 +set g_vehicle_racer_cannon_damage 15 +set g_vehicle_racer_cannon_radius 100 +set g_vehicle_racer_cannon_refire 0.05 +set g_vehicle_racer_cannon_speed 15000 +set g_vehicle_racer_cannon_spread 0.0125 +set g_vehicle_racer_cannon_force 50 + +set g_vehicle_racer_rocket_accel 1600 +set g_vehicle_racer_rocket_damage 100 +set g_vehicle_racer_rocket_radius 125 +set g_vehicle_racer_rocket_force 350 +set g_vehicle_racer_rocket_speed 900 +set g_vehicle_racer_rocket_turnrate 0.2 +set g_vehicle_racer_rocket_refire 3 + +set g_vehicle_racer_rocket_climbspeed 1600 +set g_vehicle_racer_rocket_locked_maxangle 1.8 +// }}} +// {{{ #3: Raptor +set g_vehicle_raptor 1 +set g_vehicle_raptor_respawntime 40 + +set g_vehicle_raptor_takeofftime 1.5 + +set g_vehicle_raptor_movestyle 1 "0: move relative to player angles, 1: ignore aiming for up/down movement" +set g_vehicle_raptor_turnspeed 200 +set g_vehicle_raptor_pitchspeed 50 +set g_vehicle_raptor_pitchlimit 45 + +set g_vehicle_raptor_speed_forward 1700 +set g_vehicle_raptor_speed_strafe 2200 +set g_vehicle_raptor_speed_up 2300 +set g_vehicle_raptor_speed_down 2000 +set g_vehicle_raptor_friction 2 + +set g_vehicle_raptor_swim 0 + +set g_vehicle_raptor_cannon_turnspeed 120 +set g_vehicle_raptor_cannon_turnlimit 20 +set g_vehicle_raptor_cannon_pitchlimit_up 12 +set g_vehicle_raptor_cannon_pitchlimit_down 32 + +set g_vehicle_raptor_cannon_locktarget 1 +set g_vehicle_raptor_cannon_locking_time 0.2 +set g_vehicle_raptor_cannon_locking_releasetime 0.45 +set g_vehicle_raptor_cannon_locked_time 1 +set g_vehicle_raptor_cannon_predicttarget 1 + +set g_vehicle_raptor_energy 100 +set g_vehicle_raptor_energy_regen 25 +set g_vehicle_raptor_energy_regen_pause 0.25 + +set g_vehicle_raptor_health 250 +set g_vehicle_raptor_health_regen 0 +set g_vehicle_raptor_health_regen_pause 0 + +set g_vehicle_raptor_shield 200 +set g_vehicle_raptor_shield_regen 25 +set g_vehicle_raptor_shield_regen_pause 1.5 + +set g_vehicle_raptor_bouncefactor 0.2 +set g_vehicle_raptor_bouncestop 0 +set g_vehicle_raptor_bouncepain "1 4 1000" + +set g_vehicle_raptor_cannon_cost 1 +set g_vehicle_raptor_cannon_damage 10 +set g_vehicle_raptor_cannon_radius 60 +set g_vehicle_raptor_cannon_refire 0.03 +set g_vehicle_raptor_cannon_speed 24000 +set g_vehicle_raptor_cannon_spread 0.01 +set g_vehicle_raptor_cannon_force 25 + +set g_vehicle_raptor_bomblets 8 +set g_vehicle_raptor_bomblet_alt 750 +set g_vehicle_raptor_bomblet_time 0.5 +set g_vehicle_raptor_bomblet_damage 55 +set g_vehicle_raptor_bomblet_spread 0.4 +set g_vehicle_raptor_bomblet_edgedamage 25 +set g_vehicle_raptor_bomblet_radius 350 +set g_vehicle_raptor_bomblet_force 150 +set g_vehicle_raptor_bomblet_explode_delay 0.4 + +set g_vehicle_raptor_bombs_refire 5 + +set g_vehicle_raptor_flare_refire 5 +set g_vehicle_raptor_flare_lifetime 10 +set g_vehicle_raptor_flare_chase 0.9 +set g_vehicle_raptor_flare_range 2000 +// }}} +// {{{ #4: Spiderbot +set g_vehicle_spiderbot 1 +set g_vehicle_spiderbot_respawntime 45 + +set g_vehicle_spiderbot_speed_stop 50 +set g_vehicle_spiderbot_speed_strafe 400 +set g_vehicle_spiderbot_speed_walk 500 +set g_vehicle_spiderbot_speed_run 700 +set g_vehicle_spiderbot_turnspeed 90 +set g_vehicle_spiderbot_turnspeed_strafe 300 +set g_vehicle_spiderbot_movement_inertia 0.15 + +set g_vehicle_spiderbot_springlength 150 +set g_vehicle_spiderbot_springup 20 +set g_vehicle_spiderbot_springblend 0.1 +set g_vehicle_spiderbot_tiltlimit 90 + +set g_vehicle_spiderbot_head_pitchlimit_down -20 +set g_vehicle_spiderbot_head_pitchlimit_up 30 +set g_vehicle_spiderbot_head_turnlimit 90 +set g_vehicle_spiderbot_head_turnspeed 110 + +set g_vehicle_spiderbot_health 800 +set g_vehicle_spiderbot_health_regen 10 +set g_vehicle_spiderbot_health_regen_pause 5 + +set g_vehicle_spiderbot_shield 200 +set g_vehicle_spiderbot_shield_regen 25 +set g_vehicle_spiderbot_shield_regen_pause 0.35 + +set g_vehicle_spiderbot_bouncepain "0 0 0" "minspeed_for_pain speedchange_to_pain_factor max_damage" + +set g_vehicle_spiderbot_minigun_damage 16 +set g_vehicle_spiderbot_minigun_refire 0.06 +set g_vehicle_spiderbot_minigun_spread 0.012 +set g_vehicle_spiderbot_minigun_ammo_cost 1 +set g_vehicle_spiderbot_minigun_ammo_max 100 +set g_vehicle_spiderbot_minigun_ammo_regen 40 +set g_vehicle_spiderbot_minigun_ammo_regen_pause 1 +set g_vehicle_spiderbot_minigun_force 9 +set g_vehicle_spiderbot_minigun_solidpenetration 32 + +set g_vehicle_spiderbot_rocket_damage 50 +set g_vehicle_spiderbot_rocket_force 150 +set g_vehicle_spiderbot_rocket_radius 250 +set g_vehicle_spiderbot_rocket_speed 3500 +set g_vehicle_spiderbot_rocket_spread 0.05 +set g_vehicle_spiderbot_rocket_refire 0.1 +// volley +set g_vehicle_spiderbot_rocket_refire2 0.025 +set g_vehicle_spiderbot_rocket_reload 4 +set g_vehicle_spiderbot_rocket_health 100 +set g_vehicle_spiderbot_rocket_noise 0.2 +set g_vehicle_spiderbot_rocket_turnrate 0.25 +set g_vehicle_spiderbot_rocket_lifetime 20 +// }}} diff --git a/xonotic-client.cfg b/xonotic-client.cfg index c9128c0bb6..c229ea3c81 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -60,6 +60,9 @@ seta cl_unpress_zoom_on_death 1 "automatically unpress zoom when you die (and do seta cl_unpress_zoom_on_weapon_switch 1 "automatically unpress zoom when you switch a weapon" seta cl_unpress_attack_on_weapon_switch 0 "automatically unpress fire and fire1 attack buttons when you switch a weapon" +seta cl_weapon_switch_reload 1 "When trying to switch to the currently held weapon, reload it" +seta cl_weapon_switch_fallback_to_impulse 1 "When trying to switch to a weapon that is not available, switch to an alternative from the same impulse" + seta cl_spawn_event_particles 1 "pointparticles effect whenever a player spawns" seta cl_spawn_event_sound 1 "sound effect whenever a player spawns" //seta cl_spawn_point_model 0 "place a model at all spawn points" // still needs a model @@ -197,6 +200,8 @@ seta cl_hitsound_min_pitch 0.75 "minimum pitch of hit sound" seta cl_hitsound_max_pitch 1.5 "maximum pitch of hit sound" seta cl_hitsound_nom_damage 25 "damage amount at which hitsound bases pitch off" +seta cl_eventchase_spectated_change 1 "camera goes into 3rd person mode for a moment when changing spectated player" +seta cl_eventchase_spectated_change_time 1 "how much time the effect lasts when changing spectated player" seta cl_eventchase_death 1 "camera goes into 3rd person mode when the player is dead; set to 2 to active the effect only when the corpse doesn't move anymore" seta cl_eventchase_frozen 0 "camera goes into 3rd person mode when the player is frozen" seta cl_eventchase_nexball 1 "camera goes into 3rd person mode when in nexball game-mode" @@ -229,11 +234,17 @@ cl_movement 1 cl_movement_track_canjump 0 cl_stairsmoothspeed 200 -alias g_waypointeditor_spawn "impulse 103" -alias g_waypointeditor_remove "impulse 104" -alias g_waypointeditor_relinkall "impulse 105" -alias g_waypointeditor_saveall "impulse 106" -alias g_waypointeditor_unreachable "impulse 107" +alias g_waypointeditor_spawn "wpeditor spawn" +alias g_waypointeditor_remove "wpeditor remove" +alias g_waypointeditor_relinkall "wpeditor relinkall" +alias g_waypointeditor_saveall "wpeditor saveall" +alias g_waypointeditor_unreachable "wpeditor unreachable" + +alias navwaypoint_relink g_waypointeditor_spawn +alias navwaypoint_remove g_waypointeditor_remove +alias navwaypoint_save g_waypointeditor_relinkall +alias navwaypoint_spawn g_waypointeditor_saveall +alias navwaypoint_unreachable g_waypointeditor_unreachable seta menu_sandbox_spawn_model "" seta menu_sandbox_attach_bone "" @@ -384,6 +395,7 @@ set g_waypointsprite_spam 0 "Debugging feature. Set to 10 and load courtfun in r set g_waypointsprite_timealphaexponent 1 seta g_waypointsprite_turrets 1 "disable turret waypoints" seta g_waypointsprite_turrets_maxdist 5000 "max distance for turret waypoints" +seta g_waypointsprite_turrets_text 0 "show the turret's name in the waypoint" seta g_waypointsprite_uppercase 1 seta g_waypointsprite_text 0 "Always show text instead of icons, setting this to 0 will still use text if the icon is unavailable" seta g_waypointsprite_iconsize 32 @@ -599,11 +611,12 @@ makesaved music_playlist_random0 cl_netfps 60 // should match or be a multiple of sys_ticrate -seta gl_texturecompression 0 +gl_texture_anisotropy 8 +seta gl_texturecompression 0 // FIXME the description is wrong - when this is 0, e.g. gl_texturecompression_sky still takes effect gl_texturecompression_color 1 gl_texturecompression_gloss 1 gl_texturecompression_glow 1 -gl_texturecompression_lightcubemaps 1 +gl_texturecompression_lightcubemaps 0 gl_texturecompression_q3bsplightmaps 0 gl_texturecompression_sky 1 @@ -614,13 +627,13 @@ seta menu_mouse_speed 1 "speed multiplier for the mouse in the menu (does not af set menu_use_default_hostname 1 alias sethostname "set menu_use_default_hostname 0; hostname $*" -seta cl_weaponpriority "vaporizer hmg rpc vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list" +seta cl_weaponpriority "vaporizer okhmg okrpc oknex vortex fireball mortar okmachinegun machinegun hagar rifle arc electro devastator crylink minelayer okshotgun shotgun shockwave hlac tuba blaster porto seeker hook" "weapon priority list" seta cl_weaponpriority_useforcycling 0 "when set, weapon cycling by the mouse wheel makes use of the weapon priority list (the special value 2 uses the weapon ID list for cycling)" -seta cl_weaponpriority0 "rpc devastator mortar hagar seeker fireball" "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun. Default value: explosives" -seta cl_weaponpriority1 "vaporizer vortex crylink hlac arc electro blaster shockwave" "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun. Default value: energy" -seta cl_weaponpriority2 "vaporizer vortex rifle" "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun. Default value: hitscan exact" -seta cl_weaponpriority3 "vaporizer hmg vortex rifle machinegun shotgun shockwave" "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun. Default value: hitscan all" -seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker shotgun shockwave" "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun. Default value: spam weapons" +seta cl_weaponpriority0 "okrpc devastator mortar hagar seeker fireball" "use weapon_priority_0_prev for prev gun from this list, weapon_priority_0_best for best gun, weapon_priority_0_next for next gun. Default value: explosives" +seta cl_weaponpriority1 "vaporizer oknex vortex crylink hlac arc electro blaster shockwave" "use weapon_priority_1_prev for prev gun from this list, weapon_priority_1_best for best gun, weapon_priority_1_next for next gun. Default value: energy" +seta cl_weaponpriority2 "vaporizer oknex vortex rifle" "use weapon_priority_2_prev for prev gun from this list, weapon_priority_2_best for best gun, weapon_priority_2_next for next gun. Default value: hitscan exact" +seta cl_weaponpriority3 "vaporizer okhmg oknex vortex rifle okmachinegun machinegun okshotgun shotgun shockwave" "use weapon_priority_3_prev for prev gun from this list, weapon_priority_3_best for best gun, weapon_priority_3_next for next gun. Default value: hitscan all" +seta cl_weaponpriority4 "mortar minelayer hlac hagar crylink seeker okshotgun shotgun shockwave" "use weapon_priority_4_prev for prev gun from this list, weapon_priority_4_best for best gun, weapon_priority_4_next for next gun. Default value: spam weapons" seta cl_weaponpriority5 "blaster shockwave hook porto" "use weapon_priority_5_prev for prev gun from this list, weapon_priority_5_best for best gun, weapon_priority_5_next for next gun. Default value: weapons for moving" seta cl_weaponpriority6 "" "use weapon_priority_6_prev for prev gun from this list, weapon_priority_6_best for best gun, weapon_priority_6_next for next gun" seta cl_weaponpriority7 "" "use weapon_priority_7_prev for prev gun from this list, weapon_priority_7_best for best gun, weapon_priority_7_next for next gun" @@ -645,6 +658,8 @@ seta cl_jetpack_jump 1 "Activate jetpack by pressing jump in the air. 0 = Disabl seta cl_race_cptimes_showself 1 "Always show your own times as well as the current best on checkpoints in Race/CTS" seta cl_race_cptimes_onlyself 0 "Only show your own times on checkpoints in Race/CTS" +seta cl_cts_noautoswitch 0 "Prevent forced switching to new weapons in CTS" + set cl_stripcolorcodes 0 "experimental feature (notes: strips ALL color codes from messages!)" // Demo camera @@ -678,11 +693,10 @@ set cl_effects_lightningarc_drift_end 0.1 set cl_effects_lightningarc_branchfactor_start 0.25 set cl_effects_lightningarc_branchfactor_add 0.1 -set menu_updatecheck 1 "check for updates" set menu_updatecheck_getpacks 1 "get update packs from update server" -set cl_loddistance1 1024 -set cl_loddistance2 3072 +seta cl_loddistance1 1024 +seta cl_loddistance2 3072 seta cl_playerdetailreduction 4 "the higher, the less detailed player models are displayed (LOD)" seta cl_modeldetailreduction 1 "the higher, the less detailed certain map models are displayed (LOD)" @@ -719,6 +733,8 @@ seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_c seta cl_movement_errorcompensation 1 "try to compensate for prediction errors and reduce perceived lag" seta cl_movement_intermissionrunning 0 "keep velocity after the match ends, players may appear to continue running while stationary" +seta cl_viewmodel_alpha 0 "Maximum transparency of the view model, set to 0 to disable" + set debugdraw 0 set debugdraw_filter "" set debugdraw_filterout "" @@ -775,17 +791,14 @@ r_cullentities_trace 0 r_shadow_glossexact 1 r_shadow_glossintensity 1 -// use fake light if map has no lightmaps -r_fakelight 1 +// use slightly better lighting than r_fullbright if map has no lightmaps, and for fullbrightplayers +r_fullbright_directed 1 r_water_hideplayer 1 // hide your own feet/player model in refraction views, this way you don't see half of your body under water r_water_refractdistort 0.019 set cl_rainsnow_maxdrawdist 2048 -// equalize looks better than fullbright -r_equalize_entities_fullbright 1 - // safe font defaults r_font_hinting 1 r_font_disable_freetype 0 @@ -837,11 +850,11 @@ alias menu_sync "menu_cmd sync" seta cl_items_nofade 0 seta cl_animate_items 1 seta cl_ghost_items 0.45 "enable ghosted items (when between 0 and 1, overrides the alpha value)" -seta cl_ghost_items_color "-1 -1 -1" "color of ghosted items, 0 0 0 leaves the color unchanged" +seta cl_ghost_items_color "-1 -1 -1" "color of ghosted items (colormod format: 0 0 0 leaves the color unchanged, negative values allowed)" seta cl_simple_items 0 "enable simple items (if server allows)" set cl_simpleitems_postfix "_luma" "posfix to add fo model name when simple items are enabled" set cl_fullbright_items 0 "enable fullbright items (if server allows, controlled by g_fullbrightitems)" -set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0" +set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0 (colormod format: 0 0 0 leaves the color unchanged, negative values allowed)" set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0" seta cl_showspectators 0 "Show who's spectating you if server has sv_showspectators enabled" diff --git a/xonotic-common.cfg b/xonotic-common.cfg index 078fbed74c..3a01784c7c 100644 --- a/xonotic-common.cfg +++ b/xonotic-common.cfg @@ -145,3 +145,6 @@ exec commands.cfg // Change g_start_delay based upon if the server is local or not. if_client set g_start_delay 0 "delay before the game starts, so everyone can join; recommended to set this to like 15 on a public server" if_dedicated set g_start_delay 15 "delay before the game starts, so everyone can join; recommended to set this to like 15 on a public server" + +// this should be execed only once even on ruleset-votable servers, otherwise the tips would always start from 0 +if_dedicated exec help.cfg diff --git a/xonotic-server.cfg b/xonotic-server.cfg index 7b6504d516..e25ae7faca 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -10,7 +10,7 @@ set sv_autotaunt 1 "allow autotaunts on the server" // server settings hostname "Xonotic $g_xonoticversion Server" set sv_mapchange_delay 5 -set minplayers 0 "number of players playing at the same time (if not enough real players are there the remaining slots are filled with bots)" +set minplayers 0 "fill server with bots to reach this number of players (if bot_number is not enough)" // restart server if all players hit "ready"-button set sv_ready_restart 0 "allow a map to be restarted once all players pressed the \"ready\" button" @@ -25,7 +25,7 @@ set g_maxplayers_spectator_blocktime 5 "if the players voted for the \"nospectat // tournament mod set g_warmup 0 "split the game into a warmup- and match-stage" -set g_warmup_limit 0 "limit warmup-stage to this time (in seconds); if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage" +set g_warmup_limit 180 "limit warmup-stage to this time (in seconds); if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage" set g_warmup_allow_timeout 0 "allow calling timeouts in the warmup-stage (if sv_timeout is set to 1)" set g_warmup_allguns 1 "provide more weapons on start while in warmup: 0 = normal start weapons, 1 = all guns available on the map, 2 = all normal weapons" set g_warmup_majority_factor 0.8 "minimum percentage of players ready needed for warmup to end" @@ -43,7 +43,7 @@ set sv_timeout_number 2 "how many timeouts one player is allowed to call (gets r set sv_timeout_leadtime 4 "how long the players will be informed that a timeout was called before it starts, in seconds" set sv_timeout_resumetime 3 "how long the remaining timeout-time will be after a player called the timein command" -set g_allow_oldvortexbeam 0 "If enabled, clients are allowed to use old v2.3 Vortex beam" +set g_allow_oldvortexbeam 1 "If enabled, clients are allowed to use old v2.3 Vortex beam" set g_telefrags 1 "telefragging, i.e. killing someone who stands in the way of someone who is teleporting" set g_telefrags_teamplay 1 "never telefrag team mates" @@ -80,8 +80,6 @@ set sv_track_canjump 0 "track if the player released the jump key between 2 jump 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_precacheitems 0 set sv_spectator_speed_multiplier 1.5 set sv_spectator_speed_multiplier_min 1 set sv_spectator_speed_multiplier_max 5 @@ -116,6 +114,7 @@ set skill_auto 0 "when 1, \"skill\" gets adjusted to match the best player on th set bot_debug_tracewalk 0 "Enable visual indicators for short-term navigation. Green: Goal Reached / Yellow: Obstacle found / Red: Unsolvable obstacle found" set bot_debug_goalstack 0 "Visualize the current path that each bot is following. Use with as few bots as possible." set bot_wander_enable 1 "Have bots wander around if they are unable to reach any useful goal. Disable only for debugging purposes." +set bot_typefrag 0 "Allow bots to shoot players while they're typing" // general bot AI cvars set bot_ai_thinkinterval 0.05 set bot_ai_strategyinterval 7 "How often a new objective is chosen" @@ -137,9 +136,9 @@ set bot_ai_keyboard_threshold 0.57 set bot_ai_aimskill_offset 0.3 "Amount of error induced to the bots aim" set bot_ai_aimskill_think 1 "Aiming velocity. Use values below 1 for slower aiming" set bot_ai_custom_weapon_priority_distances "300 850" "Define close and far distances in any order. Based on the distance to the enemy bots will choose different weapons" -set bot_ai_custom_weapon_priority_far "vaporizer vortex rifle electro devastator mortar hagar hlac crylink blaster machinegun fireball seeker shotgun shockwave tuba minelayer" "Desired weapons for far distances ordered by priority" -set bot_ai_custom_weapon_priority_mid "vaporizer devastator vortex fireball seeker mortar electro machinegun arc crylink hlac hagar shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority" -set bot_ai_custom_weapon_priority_close "vaporizer vortex shotgun shockwave machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority" +set bot_ai_custom_weapon_priority_far "vaporizer oknex vortex rifle electro devastator mortar hagar hlac crylink blaster okmachinegun machinegun fireball seeker okshotgun shotgun shockwave tuba minelayer" "Desired weapons for far distances ordered by priority" +set bot_ai_custom_weapon_priority_mid "vaporizer devastator oknex vortex fireball seeker mortar electro okmachinegun machinegun arc crylink hlac hagar okshotgun shotgun shockwave blaster rifle tuba minelayer" "Desired weapons for middle distances ordered by priority" +set bot_ai_custom_weapon_priority_close "vaporizer oknex vortex okshotgun shotgun shockwave okmachinegun machinegun arc hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer" "Desired weapons for close distances ordered by priority" set bot_ai_weapon_combo 1 "Enable bots to do weapon combos" set bot_ai_weapon_combo_threshold 0.4 "Try to make a combo N seconds after the last attack" set bot_ai_friends_aware_pickup_radius "500" "Bots will not pickup items if a team mate is this distance near the item" @@ -169,6 +168,7 @@ set bot_ai_timeitems_minrespawndelay 25 "bots run to items with this minimum res set g_waypointeditor 0 set g_waypointeditor_auto 0 "Automatically create waypoints for bots while playing; BEWARE, this currently creates too many of them" set g_waypointeditor_symmetrical 0 "Enable symmetrical editing of waypoints on symmetrical CTF maps (NOTE: it assumes that the map is perfectly symmetrical). 1: automatically determine origin of symmetry; -1: use custom origin (g_waypointeditor_symmetrical_origin); 2: automatically determine axis of symmetry; -2: use custom axis (g_waypointeditor_symmetrical_axis)" +set g_waypointeditor_symmetrical_allowload 1 "Allow loading symmetry settings from waypoint files into g_waypointeditor_symmetrical* cvars on map start" set g_waypointeditor_symmetrical_origin "0 0" "Custom origin of symmetry (x y)" set g_waypointeditor_symmetrical_order 0 "if >= 2 apply rotational symmetry (around origin of symmetry) of this order, otherwise apply autodetected order of symmetry" set g_waypointeditor_symmetrical_axis "0 0" "Custom axis of symmetry (m q parameters of y = mx + q)" @@ -178,7 +178,7 @@ set bot_vs_human 0 "Bots and humans play in different teams when set. positive v set g_spawnshieldtime 1 "number of seconds you are invincible after you spawned, this shield is lost after you fire" set g_spawnshield_blockdamage 1 "how much spawn shield protects you from damage (1 = full protection)" -set g_antilag 2 "AntiLag (0 = no AntiLag, 1 = verified client side hit scan, 2 = server side hit scan in the past, 3 = unverified client side hit scan)" +set g_antilag 2 "AntiLag (0 = no AntiLag, 1 = verified client side hit scan, 2 = server side hit scan in the past)" set g_antilag_nudge 0 "don't touch" set g_shootfromeye 0 "shots are fired from your eye/crosshair; visual gun position can still be influenced by cl_gunalign 1 and 2" set g_shootfromcenter 0 "weapon gets moved to the center, shots still come from the barrel of your weapon; visual gun position can still be influenced by cl_gunalign 1 and 2" @@ -241,8 +241,6 @@ set timelimit_overtimes 0 "how many overtimes to add at max" set timelimit_suddendeath 5 "number of minutes suddendeath mode lasts after all overtimes were added and still no winner was found" // common team values -set g_tdm 0 "Team Deathmatch: the team who kills their opponents most often wins" -set g_tdm_on_dm_maps 0 "when this is set, all DM maps automatically support TDM" set teamplay_mode 4 "default teamplay setting in team games. 1 = no friendly fire, self damage. 2 = friendly fire and self damage enabled. 3 = no friendly fire, but self damage enabled. 4 = obey the cvars g_mirrordamage*, g_friendlyfire* and g_teamdamage*" set g_mirrordamage 0.7 "for teamplay_mode 4: mirror damage factor" @@ -256,9 +254,7 @@ set g_teamdamage_resetspeed 20 "for teamplay_mode 4: how fast player's team set g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team" set g_balance_teams_prevent_imbalance 1 "prevent players from changing to larger teams" -set g_balance_teams_scorefactor 0.25 "at the end of the game, take score into account instead of team size by this amount (beware: values over 0.5 mean that a x:0 score imbalance will cause ALL new players to prefer the losing team at the end, despite numbers)" set g_changeteam_banned 0 "not allowed to change team" -set g_changeteam_fragtransfer 0 "% of frags you get to keep when you change teams (rounded down)" set sv_teamnagger 1 "enable a nag message when the teams are unbalanced" @@ -337,13 +333,13 @@ set g_maplist_votable_keeptwotime 15 "show only 2 options after this amount of t set g_maplist_votable_timeout 30 "timeout for the map voting; must be below 50 seconds!" set g_maplist_votable_suggestions 2 set g_maplist_votable_suggestions_override_mostrecent 0 -set g_maplist_votable_nodetail 1 "nodetail only shows total count instead of all vote counts per map, so votes don't influence others that much" +set g_maplist_votable_nodetail 0 "nodetail only shows total count instead of all vote counts per map, so votes don't influence others that much" set g_maplist_votable_abstain 0 "when 1, you can abstain from your vote" set g_maplist_votable_screenshot_dir "maps levelshots" "where to look for map screenshots" set sv_vote_gametype 0 "show a vote screen for gametypes before map vote screen" set sv_vote_gametype_keeptwotime 10 "show only 2 options after this amount of time during gametype vote screen" -set sv_vote_gametype_options "dm ctf ca lms tdm ft" +set sv_vote_gametype_options "dm tdm ctf" "Keep the identifiers short, otherwise you'll run into issues with too long alias names (max is 31 characters) when using sv_vote_gametype_hook_*" set sv_vote_gametype_timeout 20 set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes" @@ -372,6 +368,7 @@ set sv_itemstime 1 "enable networking of time left until respawn for items such set g_ban_default_bantime 5400 "90 minutes" set g_ban_default_masksize 3 "masksize 0 means banning by UID only, 1 means banning by /8 (IPv6: /32) network, 2 means banning by /16 (IPv6: /48) network, 3 means banning by /24 (IPv6: /56) network, 4 means banning by single IP (IPv6: /64 network)" +set g_ban_telluser 1 "notify the banned player about it when they try to join" set g_banned_list "" "format: IP remainingtime IP remainingtime ..." set g_banned_list_idmode "1" "when set, the IP banning system always uses the ID over the IP address (so a user in a banned IP range can connect if they have a valid signed ID)" @@ -384,6 +381,7 @@ set timelimit_max 60 sv_gameplayfix_delayprojectiles 0 sv_gameplayfix_q2airaccelerate 1 sv_gameplayfix_stepmultipletimes 1 +sv_gameplayfix_stepdown 2 // delay for "kill" to prevent abuse set g_balance_kill_delay 2 @@ -438,6 +436,7 @@ set g_jetpack 0 "Jetpack mutator" set g_hitplots 0 "when set to 1, hitplots are stored by the server to provide a means of proving that a triggerbot was used" set g_hitplots_individuals "" "the individuals, by IP, that should have their hitplots recorded" +// set it to 1 to "fix bot moveto command and routing... now all bots can get to their seats" (Nexuiz repo, commit 2c9873e6) set bot_navigation_ignoreplayers 0 // FIXME remove this once the issue is solved set bot_sound_monopoly 0 "when enabled, only bots can make any noise" @@ -481,6 +480,7 @@ sv_gameplayfix_gravityunaffectedbyticrate 1 sv_gameplayfix_nogravityonground 1 set sv_q3acompat_machineshotgunswap 0 "shorthand for swapping machinegun and shotgun (for Q3A map compatibility in mapinfo files)" +set sv_vq3compat 0 "toggle for some compatibility hacks (for VQ3 and CPM map compatibility in mapinfo files)" set g_movement_highspeed 1 "movement speed modification factor (only changes movement when above maxspeed)" @@ -541,11 +541,13 @@ set g_mod_config "" "Current config mod name" exec balance-xonotic.cfg exec physicsX.cfg exec turrets.cfg +exec vehicles.cfg exec gamemodes-server.cfg exec mutators.cfg exec monsters.cfg exec minigames.cfg exec physics.cfg +if_dedicated exec help-xonotic.cfg set sv_join_notices "" set sv_join_notices_time 15 @@ -555,3 +557,5 @@ 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_showfps 5 "Show player's FPS counters in the scoreboard. This setting acts as a delay in seconds between updates"