From: Mario Date: Sat, 31 Dec 2016 00:54:39 +0000 (+1000) Subject: Merge branch 'master' into Mirio/balance X-Git-Tag: xonotic-v0.8.2~339^2~2 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=34e7f534e2015466228eb3a78c9857741b736dca;hp=8ba1f6c672361186033b8bebc3be677ac94bd4da Merge branch 'master' into Mirio/balance --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3cdd43f305..39414d108a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -5,6 +5,12 @@ before_script: - cd gmqcc && make -j $(nproc) && export QCC="$PWD/gmqcc" - cd .. +report_cloc: + stage: test + script: + - cloc --force-lang-def=qcsrc/tools/cloc.txt --sql 1 --sql-project xonotic qcsrc | sqlite3 code.db + - sqlite3 code.db 'select file,nCode from t where nCode > 1000 order by nBlank+nComment+nCode desc' + test_compilation_units: stage: test script: @@ -24,7 +30,7 @@ test_sv_game: - wget -O data/maps/g-23.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.cache - wget -O data/maps/g-23.waypoints.hardwired https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/g-23.waypoints.hardwired - make - - EXPECT=4bd5b0276cdd100c831c73f0400eca71 + - EXPECT=37be9dc5489925451eb497390fdf58b9 - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg | tee /dev/stderr | grep '^:' diff --git a/CMakeLists.txt b/CMakeLists.txt index c2b8564351..2f38e43ba2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -4,6 +4,7 @@ project(xonotic-data LANGUAGES ASM) include_directories(qcsrc) +add_definitions(-DXONOTIC=1) add_definitions(-DNDEBUG=1) find_package(Git REQUIRED) @@ -25,11 +26,11 @@ set_source_files_properties( ) add_executable(csprogs qcsrc/client/progs.inc) -target_compile_definitions(csprogs PRIVATE -DCSQC) +target_compile_definitions(csprogs PRIVATE -DGAMEQC -DCSQC) add_dependencies(csprogs gmqcc) add_executable(progs qcsrc/server/progs.inc) -target_compile_definitions(progs PRIVATE -DSVQC) +target_compile_definitions(progs PRIVATE -DGAMEQC -DSVQC) add_dependencies(progs gmqcc) add_executable(menu qcsrc/menu/progs.inc) diff --git a/_hud_common.cfg b/_hud_common.cfg index d8c7713f19..6e88873e96 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -37,13 +37,15 @@ seta hud_panel_engineinfo 0 "enable this panel" seta hud_panel_infomessages 1 "enable this panel" seta hud_panel_physics 3 "enable this panel, 1 = show if not observing, 2 = show always, 3 = show only in race/cts if not observing" seta hud_panel_centerprint 1 "enable this panel" -seta hud_panel_minigameboard 1 "enable this panel" -seta hud_panel_minigamestatus 1 "enable this panel" -seta hud_panel_minigamehelp 1 "enable this panel" -seta hud_panel_minigamemenu 0 "enable this panel" -seta hud_panel_mapvote 1 "enable this panel" +//seta hud_panel_minigameboard 1 "enable this panel" +//seta hud_panel_minigamestatus 1 "enable this panel" +//seta hud_panel_minigamehelp 1 "enable this panel" +//seta hud_panel_minigamemenu 0 "enable this panel" +//seta hud_panel_mapvote 1 "enable this panel" seta hud_panel_itemstime 2 "enable this panel, 1 = show when spectating, 2 = even playing in warmup stage" -seta hud_panel_quickmenu 1 "enable this panel" +//seta hud_panel_quickmenu 1 "enable this panel" +//seta hud_panel_scoreboard 1 "enable this panel" +seta hud_panel_scoreboard_accuracy 1 "show weapon accuracy stats panel on scoreboard; colors can be configured with accuracy_color* cvars" seta hud_panel_weapons_dynamichud 1 "apply the dynamic hud effects to this panel" seta hud_panel_ammo_dynamichud 1 "apply the dynamic hud effects to this panel" @@ -62,6 +64,7 @@ seta hud_panel_infomessages_dynamichud 0 "apply the dynamic hud effects to t seta hud_panel_physics_dynamichud 1 "apply the dynamic hud effects to this panel" seta hud_panel_centerprint_dynamichud 1 "apply the dynamic hud effects to this panel" seta hud_panel_itemstime_dynamichud 1 "apply the dynamic hud effects to this panel" +seta hud_panel_scoreboard_dynamichud 0 "apply the dynamic hud effects to this panel" seta hud_panel_weapons_ammo_full_shells 60 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_nails 320 "show 100% of the status bar at this ammo count" @@ -69,13 +72,18 @@ seta hud_panel_weapons_ammo_full_cells 180 "show 100% of the status bar at this seta hud_panel_weapons_ammo_full_plasma 180 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_rockets 160 "show 100% of the status bar at this ammo count" seta hud_panel_weapons_ammo_full_fuel 100 "show 100% of the status bar at this ammo count" +seta hud_panel_weapons_hide_ondeath 0 "hide this panel when dead" seta hud_panel_ammo_maxammo "40" "when you have this much ammo, the ammo status bar is full" +seta hud_panel_ammo_hide_ondeath 0 "hide this panel when dead" + +seta hud_panel_powerups_hide_ondeath 0 "hide this panel when dead" seta hud_panel_healtharmor_maxhealth "200" "when you have this much health, the health status bar is full" seta hud_panel_healtharmor_maxarmor "200" "when you have this much armor, the armor status bar is full" seta hud_panel_healtharmor_progressbar_gfx_damage 5 "show damage effect when damaged at least by this amount; 0 disables the effect" seta hud_panel_healtharmor_progressbar_gfx_lowhealth 40 "health progressbar blinks when health is lower than this amount" +seta hud_panel_healtharmor_hide_ondeath 0 "hide this panel when dead" seta hud_panel_timer_increment "0" "show elapsed time instead of remaining time" @@ -84,17 +92,28 @@ 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_phisics_update_interval 0.0666 "how often (in seconds) numeric values get updated on screen" +seta hud_panel_physics_update_interval 0.0666 "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" seta hud_panel_itemstime_hidespawned "1" "if 1 hide an item from the panel when all the occurrences of it are available again; if 2 hide it when at least one occurrence is available again" -seta hud_panel_itemstime_hidelarge "0" "if 1 hide large armor and health from the panel" +seta hud_panel_itemstime_hidebig "0" "if 1 hide big armor and health from the panel" seta hud_panel_quickmenu_file "" "load the quick menu from this file (empty or 0 to disable)" seta hud_panel_quickmenu_translatecommands 0 "when the game is translated, translate strings inside commands too (useful for chat commands)" seta hud_panel_quickmenu_time 5 "quickmenu expires after this number of seconds in the same page" +seta hud_panel_infomessages_group0 1 "show group 0 messages (showing keys for non-crucial actions you can do while spectating/observing)" +seta hud_panel_infomessages_group_time 6 "number of seconds a message of a group lasts before it gets changed" +seta hud_panel_infomessages_group_fadetime 0.4 "group message fade in/out time" + +seta hud_panel_scoreboard_namesize 15 "size limit of player names and relative column (multiplied by fontsize)" +seta hud_panel_scoreboard_maxheight 0.6 "max height of the scoreboard; a few players that wouldn't fit into the scoreboard are listed in the last row" +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" + // hud panel aliases alias quickmenu "cl_cmd hud quickmenu ${* ?}" diff --git a/_hud_descriptions.cfg b/_hud_descriptions.cfg index ee46898904..9a1654f83a 100644 --- a/_hud_descriptions.cfg +++ b/_hud_descriptions.cfg @@ -329,6 +329,7 @@ seta hud_panel_mapvote_bg_color_team "" "override panel color with team color in seta hud_panel_mapvote_bg_alpha "" "if set to something else than \"\" = override default panel background alpha" seta hud_panel_mapvote_bg_border "" "if set to something else than \"\" = override default size of border around the background" seta hud_panel_mapvote_bg_padding "" "if set to something else than \"\" = override default padding of contents from border" +seta hud_panel_mapvote_highlight_border "" "highlight border size of the selected map" seta hud_panel_itemstime_pos "" "position of this base of the panel" seta hud_panel_itemstime_size "" "size of this panel" @@ -355,3 +356,25 @@ seta hud_panel_quickmenu_bg_alpha "" "if set to something else than \"\" = overr seta hud_panel_quickmenu_bg_border "" "if set to something else than \"\" = override default size of border around the background" seta hud_panel_quickmenu_bg_padding "" "if set to something else than \"\" = override default padding of contents from border" seta hud_panel_quickmenu_align "" "text alignment: 0 left, 0.5 center, 1 right" + +seta hud_panel_scoreboard_pos "" "position of this base of the panel" +seta hud_panel_scoreboard_size "" "size of this panel" +seta hud_panel_scoreboard_bg "" "if set to something else than \"\" = override default background" +seta hud_panel_scoreboard_bg_color "" "if set to something else than \"\" = override default panel background color" +seta hud_panel_scoreboard_bg_color_team "" "override panel color with team color in team based games" +seta hud_panel_scoreboard_bg_alpha "" "if set to something else than \"\" = override default panel background alpha" +seta hud_panel_scoreboard_bg_border "" "if set to something else than \"\" = override default size of border around the background" +seta hud_panel_scoreboard_bg_padding "" "if set to something else than \"\" = override default padding of contents from border" +seta hud_panel_scoreboard_fadeinspeed "" "speed at which scoreboard fades in, higher is faster (0 = instant)" +seta hud_panel_scoreboard_fadeoutspeed "" "speed at which scoreboard fades out, higher is faster (0 = instant)" +seta hud_panel_scoreboard_respawntime_decimals "" "decimal places to show for the respawntime countdown display on the scoreboard" +seta hud_panel_scoreboard_table_bg_alpha "" "background alpha of a scoreboard table" +seta hud_panel_scoreboard_table_bg_scale "" "scale for the tiled scoreboard background" +seta hud_panel_scoreboard_table_fg_alpha "" "foreground alpha of a scoreboard table" +seta hud_panel_scoreboard_table_fg_alpha_self "" "self foreground alpha of a scoreboard table" +seta hud_panel_scoreboard_table_highlight "" "enable highlighting for rows and columns in scoreboard tables" +seta hud_panel_scoreboard_table_highlight_alpha "" "highlight alpha of a scoreboard table" +seta hud_panel_scoreboard_table_highlight_alpha_self "" "self highlight alpha of a scoreboard table" +seta hud_panel_scoreboard_bg_teams_color_team "" "override panel team color in team tables" +seta hud_panel_scoreboard_accuracy_doublerows "" "use two rows instead of one" +seta hud_panel_scoreboard_accuracy_nocolors "" "don't use colors displaying accuracy stats" diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg new file mode 100644 index 0000000000..d3b106640d --- /dev/null +++ b/bal-wep-mario.cfg @@ -0,0 +1,820 @@ +// {{{ #1: Blaster +set g_balance_blaster_primary_animtime 0.2 +set g_balance_blaster_primary_damage 25 +set g_balance_blaster_primary_delay 0 +set g_balance_blaster_primary_edgedamage 12.5 +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 1 +set g_balance_shotgun_primary_animtime 0.2 +set g_balance_shotgun_primary_bullets 12 +set g_balance_shotgun_primary_damage 4 +set g_balance_shotgun_primary_force 15 +set g_balance_shotgun_primary_refire 0.75 +set g_balance_shotgun_primary_solidpenetration 3.8 +set g_balance_shotgun_primary_spread 0.12 +set g_balance_shotgun_reload_ammo 0 +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 60 +set g_balance_machinegun_reload_time 2 +set g_balance_machinegun_solidpenetration 13.1 +set g_balance_machinegun_spread_add 0.012 +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_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_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 1 +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 12 +set g_balance_crylink_primary_edgedamage 6 +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 10 +set g_balance_crylink_secondary_edgedamage 5 +set g_balance_crylink_secondary_force -250 +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 6 +set g_balance_vortex_primary_animtime 0.4 +set g_balance_vortex_primary_damage 70 +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 400 +set g_balance_vortex_primary_refire 1.5 +set g_balance_vortex_reload_ammo 0 +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_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 100 +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 400 +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 0 +// }}} +// {{{ #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 1 +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 10 +set g_balance_hmg_force 5 +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.01 +set g_balance_hmg_spread_max 0.05 +set g_balance_hmg_spread_min 0.02 +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 1250 +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-nexuiz25.cfg b/bal-wep-nexuiz25.cfg index 97707a7255..41c9081b92 100644 --- a/bal-wep-nexuiz25.cfg +++ b/bal-wep-nexuiz25.cfg @@ -219,6 +219,7 @@ set g_balance_electro_secondary_speed 900 set g_balance_electro_secondary_speed_up 200 set g_balance_electro_secondary_speed_z 0 set g_balance_electro_secondary_spread 0.04 +set g_balance_electro_secondary_stick 0 set g_balance_electro_secondary_touchexplode 0 set g_balance_electro_switchdelay_drop 0.15 set g_balance_electro_switchdelay_raise 0.15 @@ -398,6 +399,7 @@ set g_balance_devastator_remote_damage 105 set g_balance_devastator_remote_edgedamage 40 set g_balance_devastator_remote_force 600 set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_force 0 set g_balance_devastator_remote_jump_radius 0 set g_balance_devastator_remote_jump_velocity_z_add 400 set g_balance_devastator_remote_jump_velocity_z_max 1500 @@ -720,7 +722,7 @@ set g_balance_shockwave_melee_traces 10 set g_balance_shockwave_switchdelay_drop 0.15 set g_balance_shockwave_switchdelay_raise 0.15 set g_balance_shockwave_weaponreplace "" -set g_balance_shockwave_weaponstart 1 +set g_balance_shockwave_weaponstart 0 set g_balance_shockwave_weaponstartoverride -1 set g_balance_shockwave_weaponthrowable 0 // }}} diff --git a/bal-wep-overkill.cfg b/bal-wep-overkill.cfg index d0cfa361d2..82609a76ca 100644 --- a/bal-wep-overkill.cfg +++ b/bal-wep-overkill.cfg @@ -84,7 +84,7 @@ set g_balance_machinegun_reload_time 1.5 set g_balance_machinegun_solidpenetration 13.1 set g_balance_machinegun_spread_add 0.012 set g_balance_machinegun_spread_max 0.05 -set g_balance_machinegun_spread_min 0.02 +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 @@ -219,6 +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.04 +set g_balance_electro_secondary_stick 0 set g_balance_electro_secondary_touchexplode 0 set g_balance_electro_switchdelay_drop 0.2 set g_balance_electro_switchdelay_raise 0.2 @@ -301,7 +302,7 @@ 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.95 +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 @@ -324,8 +325,8 @@ 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.25 -set g_balance_vortex_switchdelay_raise 0.25 +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 @@ -398,6 +399,7 @@ 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 0 set g_balance_devastator_remote_jump_radius 0 set g_balance_devastator_remote_jump_velocity_z_add 400 set g_balance_devastator_remote_jump_velocity_z_max 1500 @@ -776,15 +778,15 @@ set g_balance_arc_weaponthrowable 1 // }}} // {{{ #21: Heavy Machine Gun set g_balance_hmg_ammo 1 -set g_balance_hmg_damage 10 -set g_balance_hmg_force 5 +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.01 -set g_balance_hmg_spread_max 0.05 -set g_balance_hmg_spread_min 0.02 +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 "" diff --git a/bal-wep-samual.cfg b/bal-wep-samual.cfg index af2234f0d6..99edec78b8 100644 --- a/bal-wep-samual.cfg +++ b/bal-wep-samual.cfg @@ -234,6 +234,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.04 +set g_balance_electro_secondary_stick 0 set g_balance_electro_secondary_touchexplode 0 set g_balance_electro_switchdelay_drop 0.2 set g_balance_electro_switchdelay_raise 0.2 @@ -464,6 +465,7 @@ set g_balance_devastator_remote_damage 70 set g_balance_devastator_remote_edgedamage 35 set g_balance_devastator_remote_force 400 set g_balance_devastator_remote_jump_damage 40 +set g_balance_devastator_remote_jump_force 0 set g_balance_devastator_remote_jump_radius 200 set g_balance_devastator_remote_jump_velocity_z_add 500 set g_balance_devastator_remote_jump_velocity_z_max 1500 diff --git a/bal-wep-xdf.cfg b/bal-wep-xdf.cfg index 931a63c6a2..95ac919b6a 100644 --- a/bal-wep-xdf.cfg +++ b/bal-wep-xdf.cfg @@ -219,6 +219,7 @@ set g_balance_electro_secondary_speed 900 set g_balance_electro_secondary_speed_up 200 set g_balance_electro_secondary_speed_z 0 set g_balance_electro_secondary_spread 0.05 +set g_balance_electro_secondary_stick 0 set g_balance_electro_secondary_touchexplode 0 set g_balance_electro_switchdelay_drop 0 set g_balance_electro_switchdelay_raise 0 @@ -398,6 +399,7 @@ set g_balance_devastator_remote_damage 70 set g_balance_devastator_remote_edgedamage 35 set g_balance_devastator_remote_force 350 set g_balance_devastator_remote_jump_damage 70 +set g_balance_devastator_remote_jump_force 0 set g_balance_devastator_remote_jump_radius 0 set g_balance_devastator_remote_jump_velocity_z_add 400 set g_balance_devastator_remote_jump_velocity_z_max 1500 diff --git a/bal-wep-xonotic.cfg b/bal-wep-xonotic.cfg index 8d70876901..898c3b3924 100644 --- a/bal-wep-xonotic.cfg +++ b/bal-wep-xonotic.cfg @@ -193,7 +193,7 @@ 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 150 +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 @@ -218,7 +218,8 @@ 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.04 +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 @@ -324,8 +325,8 @@ 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.25 -set g_balance_vortex_switchdelay_raise 0.25 +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 @@ -367,7 +368,7 @@ 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.05 +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 "" @@ -398,8 +399,9 @@ 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 400 +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 @@ -454,7 +456,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 @@ -675,23 +677,23 @@ set g_balance_seeker_weaponthrowable 1 // }}} // {{{ #19: Shockwave (MUTATOR WEAPON) set g_balance_shockwave_blast_animtime 0.3 -set g_balance_shockwave_blast_damage 20 +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 200 +set g_balance_shockwave_blast_force 15 set g_balance_shockwave_blast_force_forwardbias 50 -set g_balance_shockwave_blast_force_zscale 2 +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 300 -set g_balance_shockwave_blast_jump_force_velocitybias 0 -set g_balance_shockwave_blast_jump_force_zscale 1.25 +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.5 -set g_balance_shockwave_blast_multiplier_distance 0.5 +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 @@ -726,16 +728,16 @@ set g_balance_shockwave_weaponthrowable 0 // }}} // {{{ #20: Arc set g_balance_arc_beam_ammo 6 -set g_balance_arc_beam_animtime 0.2 +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 115 +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 900 +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 @@ -744,8 +746,8 @@ 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.75 -set g_balance_arc_burst_heat 4 +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 @@ -762,7 +764,7 @@ 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 2200 +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 diff --git a/bal-wep-xpm.cfg b/bal-wep-xpm.cfg index 8d70876901..898c3b3924 100644 --- a/bal-wep-xpm.cfg +++ b/bal-wep-xpm.cfg @@ -193,7 +193,7 @@ 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 150 +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 @@ -218,7 +218,8 @@ 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.04 +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 @@ -324,8 +325,8 @@ 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.25 -set g_balance_vortex_switchdelay_raise 0.25 +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 @@ -367,7 +368,7 @@ 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.05 +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 "" @@ -398,8 +399,9 @@ 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 400 +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 @@ -454,7 +456,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 @@ -675,23 +677,23 @@ set g_balance_seeker_weaponthrowable 1 // }}} // {{{ #19: Shockwave (MUTATOR WEAPON) set g_balance_shockwave_blast_animtime 0.3 -set g_balance_shockwave_blast_damage 20 +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 200 +set g_balance_shockwave_blast_force 15 set g_balance_shockwave_blast_force_forwardbias 50 -set g_balance_shockwave_blast_force_zscale 2 +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 300 -set g_balance_shockwave_blast_jump_force_velocitybias 0 -set g_balance_shockwave_blast_jump_force_zscale 1.25 +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.5 -set g_balance_shockwave_blast_multiplier_distance 0.5 +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 @@ -726,16 +728,16 @@ set g_balance_shockwave_weaponthrowable 0 // }}} // {{{ #20: Arc set g_balance_arc_beam_ammo 6 -set g_balance_arc_beam_animtime 0.2 +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 115 +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 900 +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 @@ -744,8 +746,8 @@ 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.75 -set g_balance_arc_burst_heat 4 +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 @@ -762,7 +764,7 @@ 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 2200 +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 diff --git a/balance-mario.cfg b/balance-mario.cfg new file mode 100644 index 0000000000..d290be0126 --- /dev/null +++ b/balance-mario.cfg @@ -0,0 +1,235 @@ +g_mod_balance Mario + +// {{{ starting gear +set g_balance_health_start 100 +set g_balance_armor_start 0 +set g_start_ammo_shells 15 +set g_start_ammo_nails 0 +set g_start_ammo_rockets 0 +set g_start_ammo_cells 0 +set g_start_ammo_plasma 0 +set g_start_ammo_fuel 0 +set g_warmup_start_health 100 "starting values when being in warmup-stage" +set g_warmup_start_armor 100 "starting values when being in warmup-stage" +set g_warmup_start_ammo_shells 30 "starting values when being in warmup-stage" +set g_warmup_start_ammo_nails 160 "starting values when being in warmup-stage" +set g_warmup_start_ammo_rockets 80 "starting values when being in warmup-stage" +set g_warmup_start_ammo_cells 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_plasma 90 "starting values when being in warmup-stage" +set g_warmup_start_ammo_fuel 0 "starting values when being in warmup-stage" +set g_lms_start_health 200 +set g_lms_start_armor 200 +set g_lms_start_ammo_shells 60 +set g_lms_start_ammo_nails 320 +set g_lms_start_ammo_rockets 160 +set g_lms_start_ammo_cells 180 +set g_lms_start_ammo_plasma 180 +set g_lms_start_ammo_fuel 0 +set g_balance_nix_roundtime 25 +set g_balance_nix_incrtime 1.6 +set g_balance_nix_ammo_shells 60 +set g_balance_nix_ammo_nails 320 +set g_balance_nix_ammo_rockets 160 +set g_balance_nix_ammo_cells 180 +set g_balance_nix_ammo_plasma 180 +set g_balance_nix_ammo_fuel 100 +set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume +set g_balance_nix_ammoincr_nails 6 +set g_balance_nix_ammoincr_rockets 2 +set g_balance_nix_ammoincr_cells 2 +set g_balance_nix_ammoincr_plasma 2 +set g_balance_nix_ammoincr_fuel 2 +// }}} + +// {{{ pickup items +set g_pickup_ammo_anyway 1 +set g_pickup_weapons_anyway 1 +set g_pickup_shells 15 +set g_pickup_shells_weapon 15 +set g_pickup_shells_max 60 +set g_pickup_nails 80 +set g_pickup_nails_weapon 80 +set g_pickup_nails_max 320 +set g_pickup_rockets 40 +set g_pickup_rockets_weapon 40 +set g_pickup_rockets_max 160 +set g_pickup_cells 30 +set g_pickup_cells_weapon 30 +set g_pickup_cells_max 180 +set g_pickup_plasma 30 +set g_pickup_plasma_weapon 30 +set g_pickup_plasma_max 180 +set g_pickup_fuel 50 +set g_pickup_fuel_weapon 50 +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_armormedium 25 +set g_pickup_armormedium_max 200 +set g_pickup_armormedium_anyway 1 +set g_pickup_armorbig 50 +set g_pickup_armorbig_max 200 +set g_pickup_armorbig_anyway 1 +set g_pickup_armormega 100 +set g_pickup_armormega_max 200 +set g_pickup_armormega_anyway 1 +set g_pickup_healthsmall 5 +set g_pickup_healthsmall_max 200 +set g_pickup_healthsmall_anyway 1 +set g_pickup_healthmedium 25 +set g_pickup_healthmedium_max 200 +set g_pickup_healthmedium_anyway 1 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 200 +set g_pickup_healthbig_anyway 1 +set g_pickup_healthmega 100 +set g_pickup_healthmega_max 200 +set g_pickup_healthmega_anyway 1 +set g_pickup_respawntime_short 15 +set g_pickup_respawntime_medium 20 +set g_pickup_respawntime_long 30 +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_respawntimejitter_short 0 +set g_pickup_respawntimejitter_medium 0 +set g_pickup_respawntimejitter_long 0 +set g_pickup_respawntimejitter_powerup 0 +set g_pickup_respawntimejitter_weapon 0 +set g_pickup_respawntimejitter_superweapon 10 +set g_pickup_respawntimejitter_ammo 0 +// }}} + +// {{{ regen/rot +set g_balance_health_regen 0.08 +set g_balance_health_regenlinear 0.5 +set g_balance_pause_health_regen 5 +set g_balance_pause_health_regen_spawn 0 +set g_balance_health_rot 0.02 +set g_balance_health_rotlinear 1 +set g_balance_pause_health_rot 1 +set g_balance_pause_health_rot_spawn 5 +set g_balance_health_regenstable 100 +set g_balance_health_rotstable 100 +set g_balance_health_limit 999 +set g_balance_armor_regen 0 +set g_balance_armor_regenlinear 0 +set g_balance_armor_rot 0.02 +set g_balance_armor_rotlinear 1 +set g_balance_pause_armor_rot 1 +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 999 +set g_balance_armor_blockpercent 0.7 +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 +set g_balance_fuel_rot 0.05 +set g_balance_fuel_rotlinear 0 +set g_balance_pause_fuel_rot 5 +set g_balance_pause_fuel_rot_spawn 10 +set g_balance_fuel_regenstable 50 +set g_balance_fuel_rotstable 100 +set g_balance_fuel_limit 999 +// }}} + +// {{{ misc +set g_balance_selfdamagepercent 0.65 +set g_weaponspeedfactor 1 "weapon projectile speed multiplier" +set g_weaponratefactor 1 "weapon fire rate multiplier" +set g_weapondamagefactor 1 "weapon damage multiplier" +set g_weaponforcefactor 1 "weapon force multiplier" +set g_weaponspreadfactor 1 "weapon spread multiplier" +set g_balance_firetransfer_time 0.9 +set g_balance_firetransfer_damage 0.8 +set g_throughfloor_damage 0.75 +set g_throughfloor_force 0.75 +set g_projectiles_damage -2 +// possible values: +// -2: absolutely no damage to projectiles (no exceptions) +// -1: no damage other than the exceptions (electro combo, hagar join explode, ML mines) +// 0: only damage from contents (lava/slime) or exceptions +// 1: only self damage or damage from contents or exceptions +// 2: allow all damage to projectiles normally +set g_projectiles_keep_owner 1 +set g_projectiles_newton_style 0 +// possible values: +// 0: absolute velocity projectiles (like Quake) +// 1: relative velocity projectiles, "Newtonian" (like Tribes 2) +// 2: relative velocity projectiles, but aim is precorrected so projectiles hit the crosshair (note: strafe rockets then are SLOWER than ones shot while standing, happens in 1 too when aiming correctly which is hard) +set g_projectiles_newton_style_2_minfactor 0.8 +set g_projectiles_newton_style_2_maxfactor 1.5 +set g_projectiles_spread_style 7 +// possible values: +// 0: forward + solid sphere (like Quake) - varies velocity +// 1: forward + flattened solid sphere +// 2: forward + solid circle +// 3: forward + normal distribution 3D - varies velocity +// 4: forward + normal distribution on a plane +// 5: forward + circle with 1-r falloff +// 6: forward + circle with 1-r^2 falloff +// 7: forward + circle with (1-r)(2-r) falloff +set g_balance_falldamage_deadminspeed 250 +set g_balance_falldamage_minspeed 900 +set g_balance_falldamage_factor 0.20 +set g_balance_falldamage_maxdamage 40 +set g_balance_damagepush_speedfactor 2.5 +set g_balance_contents_damagerate 0.2 // ticrate interval for applying damage with playerdamage/projectiledamage +set g_balance_contents_drowndelay 10 // time under water before a player begins drowning +set g_balance_contents_playerdamage_drowning 20 // damage per second for while player is drowning +set g_balance_contents_playerdamage_lava 50 // damage per second for while player is inside lava +set g_balance_contents_playerdamage_lava_burn 0 // extra burning damage after leaving lava +set g_balance_contents_playerdamage_lava_burn_time 2.5 // time across which the damage is applied (note: not dps!) +set g_balance_contents_playerdamage_slime 30 // damage per second for while player is inside slime +set g_balance_contents_projectiledamage 10000 // instantly kill projectiles upon touching lava/slime +set g_maxpushtime 8.0 "timeout for kill credit when your damage knocks someone into a death trap" +// }}} + +// {{{ powerups +set g_balance_powerup_invincible_takedamage 0.33 // only 1/3rd damage is taken +set g_balance_powerup_invincible_time 30 +set g_balance_powerup_strength_damage 3 +set g_balance_powerup_strength_force 3 +set g_balance_powerup_strength_time 30 +set g_balance_powerup_strength_selfdamage 1.5 +set g_balance_powerup_strength_selfforce 1.5 +set g_balance_superweapons_time 30 +// }}} + +// {{{ jetpack/hook +set g_jetpack_antigravity 0.8 "factor of gravity compensation of the jetpack" +set g_jetpack_acceleration_side 1200 "acceleration of the jetpack in xy direction" +set g_jetpack_acceleration_up 600 "acceleration of the jetpack in z direction (note: you have to factor in gravity here, if antigravity is not 1)" +set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" +set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" +set g_jetpack_fuel 8 "fuel per second for jetpack" +set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" + +set g_grappling_hook_tarzan 2 // 2: can also pull players +set g_balance_grapplehook_speed_fly 1800 +set g_balance_grapplehook_speed_pull 2000 +set g_balance_grapplehook_force_rubber 2000 +set g_balance_grapplehook_force_rubber_overstretch 1000 +set g_balance_grapplehook_length_min 50 +set g_balance_grapplehook_stretch 50 +set g_balance_grapplehook_airfriction 0.2 +set g_balance_grapplehook_health 50 +set g_balance_grapplehook_damagedbycontents 1 +set g_balance_grapplehook_refire 0.2 +set g_balance_grapplehook_nade_time 0.7 +set g_balance_grapplehook_crouchslide 0 +set g_balance_grapplehook_gravity 0 +set g_balance_grapplehook_pull_frozen 0 +// }}} + +// {{{ port-o-launch +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-mario.cfg diff --git a/balance-nexuiz25.cfg b/balance-nexuiz25.cfg index 3ac47371ee..39405f3998 100644 --- a/balance-nexuiz25.cfg +++ b/balance-nexuiz25.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 45 set g_balance_nix_ammo_rockets 15 set g_balance_nix_ammo_cells 15 set g_balance_nix_ammo_plasma 15 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 25 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0 set g_pickup_armorbig 50 set g_pickup_armorbig_max 999 set g_pickup_armorbig_anyway 0 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 999 -set g_pickup_armorlarge_anyway 0 +set g_pickup_armormega 100 +set g_pickup_armormega_max 999 +set g_pickup_armormega_anyway 0 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 999 set g_pickup_healthsmall_anyway 0 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 999 set g_pickup_healthmedium_anyway 0 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 999 -set g_pickup_healthlarge_anyway 0 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 999 +set g_pickup_healthbig_anyway 0 set g_pickup_healthmega 100 set g_pickup_healthmega_max 999 set g_pickup_healthmega_anyway 0 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1500 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/balance-overkill.cfg b/balance-overkill.cfg index a691a19183..da17e0afeb 100644 --- a/balance-overkill.cfg +++ b/balance-overkill.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320 set g_balance_nix_ammo_rockets 160 set g_balance_nix_ammo_cells 180 set g_balance_nix_ammo_plasma 180 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 100 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -65,25 +65,25 @@ set g_pickup_fuel_jetpack 100 set g_pickup_fuel_max 100 set g_pickup_armorsmall 5 set g_pickup_armorsmall_max 20 -set g_pickup_armorsmall_anyway 0 +set g_pickup_armorsmall_anyway 1 set g_pickup_armormedium 25 -set g_pickup_armormedium_max 200 +set g_pickup_armormedium_max 100 set g_pickup_armormedium_anyway 1 set g_pickup_armorbig 50 -set g_pickup_armorbig_max 200 +set g_pickup_armorbig_max 100 set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 +set g_pickup_armormega 100 +set g_pickup_armormega_max 100 +set g_pickup_armormega_anyway 1 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 set g_pickup_healthsmall_anyway 1 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 200 set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 200 +set g_pickup_healthbig_anyway 1 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 set g_pickup_healthmega_anyway 0 @@ -119,12 +119,12 @@ set g_balance_armor_regen 0 set g_balance_armor_regenlinear 0 set g_balance_armor_rot 0 set g_balance_armor_rotlinear 1 -set g_balance_pause_armor_rot 1 +set g_balance_pause_armor_rot 5 set g_balance_pause_armor_rot_spawn 5 set g_balance_armor_regenstable 100 set g_balance_armor_rotstable 0 set g_balance_armor_limit 999 -set g_balance_armor_blockpercent 0.7 +set g_balance_armor_blockpercent 0.6 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 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/balance-samual.cfg b/balance-samual.cfg index 6d7831adac..ad7192227e 100644 --- a/balance-samual.cfg +++ b/balance-samual.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320 set g_balance_nix_ammo_rockets 160 set g_balance_nix_ammo_cells 180 set g_balance_nix_ammo_plasma 180 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 100 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1 set g_pickup_armorbig 50 set g_pickup_armorbig_max 200 set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 +set g_pickup_armormega 100 +set g_pickup_armormega_max 200 +set g_pickup_armormega_anyway 1 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 set g_pickup_healthsmall_anyway 1 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 200 set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 200 +set g_pickup_healthbig_anyway 1 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 set g_pickup_healthmega_anyway 1 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/balance-xdf.cfg b/balance-xdf.cfg index 07b38e830a..79344fda96 100644 --- a/balance-xdf.cfg +++ b/balance-xdf.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320 set g_balance_nix_ammo_rockets 160 set g_balance_nix_ammo_cells 180 set g_balance_nix_ammo_plasma 180 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 100 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1 set g_pickup_armorbig 50 set g_pickup_armorbig_max 200 set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 +set g_pickup_armormega 100 +set g_pickup_armormega_max 200 +set g_pickup_armormega_anyway 1 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 set g_pickup_healthsmall_anyway 1 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 200 set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 200 +set g_pickup_healthbig_anyway 1 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 set g_pickup_healthmega_anyway 1 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/balance-xonotic.cfg b/balance-xonotic.cfg index ddc40e288e..f317d2e6b4 100644 --- a/balance-xonotic.cfg +++ b/balance-xonotic.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320 set g_balance_nix_ammo_rockets 160 set g_balance_nix_ammo_cells 180 set g_balance_nix_ammo_plasma 180 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 100 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 1 set g_pickup_armorbig 50 set g_pickup_armorbig_max 200 set g_pickup_armorbig_anyway 1 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 1 +set g_pickup_armormega 100 +set g_pickup_armormega_max 200 +set g_pickup_armormega_anyway 1 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 set g_pickup_healthsmall_anyway 1 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 200 set g_pickup_healthmedium_anyway 1 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 200 -set g_pickup_healthlarge_anyway 1 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 200 +set g_pickup_healthbig_anyway 1 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 set g_pickup_healthmega_anyway 1 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/balance-xpm.cfg b/balance-xpm.cfg index e0aa8b0827..f5ec29240e 100644 --- a/balance-xpm.cfg +++ b/balance-xpm.cfg @@ -32,7 +32,7 @@ set g_balance_nix_ammo_nails 320 set g_balance_nix_ammo_rockets 160 set g_balance_nix_ammo_cells 180 set g_balance_nix_ammo_plasma 180 -set g_balance_nix_ammo_fuel 0 +set g_balance_nix_ammo_fuel 100 set g_balance_nix_ammoincr_shells 2 // eh this will need figured out later I assume set g_balance_nix_ammoincr_nails 6 set g_balance_nix_ammoincr_rockets 2 @@ -72,18 +72,18 @@ set g_pickup_armormedium_anyway 0 set g_pickup_armorbig 50 set g_pickup_armorbig_max 100 set g_pickup_armorbig_anyway 0 -set g_pickup_armorlarge 100 -set g_pickup_armorlarge_max 200 -set g_pickup_armorlarge_anyway 0 +set g_pickup_armormega 100 +set g_pickup_armormega_max 200 +set g_pickup_armormega_anyway 0 set g_pickup_healthsmall 5 set g_pickup_healthsmall_max 200 set g_pickup_healthsmall_anyway 0 set g_pickup_healthmedium 25 set g_pickup_healthmedium_max 100 set g_pickup_healthmedium_anyway 0 -set g_pickup_healthlarge 50 -set g_pickup_healthlarge_max 100 -set g_pickup_healthlarge_anyway 0 +set g_pickup_healthbig 50 +set g_pickup_healthbig_max 100 +set g_pickup_healthbig_anyway 0 set g_pickup_healthmega 100 set g_pickup_healthmega_max 200 set g_pickup_healthmega_anyway 0 @@ -208,6 +208,7 @@ set g_jetpack_maxspeed_side 1200 "max speed of the jetpack in xy direction" set g_jetpack_maxspeed_up 600 "max speed of the jetpack in z direction" set g_jetpack_fuel 8 "fuel per second for jetpack" set g_jetpack_attenuation 2 "jetpack sound attenuation" +set g_jetpack_reverse_thrust 0 "if not 0, downward acceleration when crouching with the jetpack" set g_grappling_hook_tarzan 2 // 2: can also pull players set g_balance_grapplehook_speed_fly 1800 diff --git a/binds-xonotic.cfg b/binds-xonotic.cfg index 361621c6a8..82ea7815e5 100644 --- a/binds-xonotic.cfg +++ b/binds-xonotic.cfg @@ -17,7 +17,6 @@ bind LEFTARROW +moveleft bind DOWNARROW +back bind RIGHTARROW +moveright bind SHIFT +crouch -bind ENTER +jump bind SPACE +jump // weapons @@ -51,6 +50,7 @@ bind ` toggleconsole bind ~ toggleconsole bind TAB +showscores bind ESCAPE togglemenu +bind ENTER messagemode bind t messagemode bind y messagemode2 bind z messagemode2 diff --git a/bots.txt b/bots.txt index fc8082f49d..18df7f835b 100644 --- a/bots.txt +++ b/bots.txt @@ -2,7 +2,7 @@ //default team values (team-override): 1 = red, 2 = blue, 3 = yellow, 4 = pink //use -1 for shirt-color or pants-color to get random colors Hellfire ignis 0 4 0 0 0 0 -0.5 -1 1 1 -0.5 -1 -1 2 0.5 -1 -Toxic gakarmored 0 14 7 0 -1 -1.5 -0.5 0 1 0 0 0 2 -0.5 -0.5 1 +Toxic gakmasked 0 14 7 0 -1 -1.5 -0.5 0 1 0 0 0 2 -0.5 -0.5 1 Discovery erebus 0 2 6 0 0 -1 -0.5 -0.5 1 -0.5 0.5 1.5 -0.5 -1 1 0.5 Pegasus umbra 0 13 11 0 1 1 1 1 -1 0 0.5 0 -2 0 -1 0 Eureka umbra 0 12 7 0 0 0 -1.5 -0.5 -0.5 0 0 0 0 -0.5 1.5 1.5 @@ -10,7 +10,7 @@ Airhead ignis 0 11 1 0 -1 -1.5 -1 -0.5 1 1 -1 1 -0.5 1 0.5 0 Gator gak 0 3 10 0 0 1 0 0.5 -0.5 0.5 -0.5 -1 0 0 -0.5 0 Delirium gakmasked 0 8 12 0 0 -1 -1 -1 0 2 0 1 0 2 -1 -1 Death gakmasked 0 4 11 0 -0.5 0 0 1 -0.5 0 1 0 0 0 0 0 -Scorcher ignishalfmasked 0 13 13 0 0 -1 0 -0.5 0.5 1 0 1 -2 1 0 0 +Scorcher ignismasked 0 13 13 0 0 -1 0 -0.5 0.5 1 0 1 -2 1 0 0 Necrotic nyx 0 12 14 0 0 0 0 1 0 -1 -0.5 -1 1 0 0 0 Dominator nyx 0 3 9 0 0 0 0 2 -1 0 0 0 -1 0 0 0 Thunderstorm erebus 0 13 6 0 0 0 0 -0.5 -1 1 0.5 0.5 -0.5 0 0.5 0 diff --git a/defaultXonotic.cfg b/defaultXonotic.cfg index dea35012a7..bbdbdcb74a 100644 --- a/defaultXonotic.cfg +++ b/defaultXonotic.cfg @@ -36,6 +36,8 @@ alias +hook +button6 alias -hook -button6 alias +jetpack +button10 alias -jetpack -button10 +alias +dodge +button11 +alias -dodge -button11 alias use "impulse 21" // for backwards compatibility @@ -79,12 +81,14 @@ seta cl_zoomsensitivity 0 "how zoom changes sensitivity (0 = weakest, 1 = strong seta cl_unpress_zoom_on_spawn 1 "automatically unpress zoom when you spawn" seta cl_unpress_zoom_on_death 1 "automatically unpress zoom when you die (and don't allow zoom again while dead)" seta cl_unpress_zoom_on_weapon_switch 1 "automatically unpress zoom when you switch a weapon" -seta cl_unpress_attack_on_weapon_switch 1 "automatically unpress fire and fire1 attack buttons 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_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 seta cl_spawn_point_particles 1 "pointparticles effect at all spawn points" // managed by effects-.cfg files +seta cl_spawn_point_dist_min 1200 +seta cl_spawn_point_dist_max 1600 freelook 1 sensitivity 6 @@ -214,8 +218,8 @@ 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)" // restart server if all players hit "ready"-button -set sv_ready_restart 0 "if set to 1 allow a map to be restarted once all players pressed the \"ready\" button'" -set sv_ready_restart_after_countdown 0 "if set to 1 the players and map items are reset after the countdown ended, otherwise they're reset already at the beginning of the countdown" +set sv_ready_restart 0 "allow a map to be restarted once all players pressed the \"ready\" button" +set sv_ready_restart_after_countdown 0 "reset players and map items after the countdown ended, instead of at the beginning of the countdown" set sv_ready_restart_repeatable 0 "allows the players to restart the game as often as needed" seta cl_hitsound 1 "play a hit notifier sound when you have hit an enemy, 1: same pitch 2: increase pitch with more damage 3: decrease pitch with more damage" @@ -233,28 +237,33 @@ seta cl_eventchase_maxs "12 12 8" "max size of eventchase camera bbox" seta cl_eventchase_mins "-12 -12 -8" "min size of eventchase camera bbox" seta cl_eventchase_viewoffset "0 0 20" "viewoffset of eventchase camera" seta cl_eventchase_generator_viewoffset "0 0 80" "viewoffset of eventchase camera while viewing generator explosion" +seta cl_eventchase_vehicle 1 "camera goes into 3rd person mode when inside a vehicle" +seta cl_eventchase_vehicle_viewoffset "0 0 80" +seta cl_eventchase_vehicle_distance 250 + +set _vehicles_shownchasemessage 0 //nifreks lockonrestart feature, used in team-based game modes, if set to 1 and all players readied up no other player can then join the game anymore, useful to block spectators from joining -set teamplay_lockonrestart 0 "it set to 1 in a team-based game, the teams are locked once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)" +set teamplay_lockonrestart 0 "lock teams once all players readied up and the game restarted (no new players can join after restart unless using the server-command unlockteams)" set g_maxplayers 0 "maximum number of players allowed to play at the same time, set to 0 to allow all players to join the game" set g_maxplayers_spectator_blocktime 5 "if the players voted for the \"nospectators\" command, this setting defines the number of seconds a observer/spectator has time to join the game before he gets kicked" // tournament mod -set g_warmup 0 "split the game into a warmup- and match-stage when set to 1" -set g_warmup_limit 0 "if set to -1 the warmup-stage is not affected by any timelimit, if set to 0 the usual timelimit also affects warmup-stage, otherwise warmup will be limited to this time in SECONDS (useful for public matches)" -set g_warmup_allow_timeout 0 "if set to 1 timeouts can also be called in the warmup-stage, when sv_timeout is set to 1" +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_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" set g_chat_nospectators 0 "if 0 spec/observer chat is always visible to the player, if 1 it is never visible to players, if 2 it is only visible to players during warmup stage" -set sv_vote_nospectators 0 "Only players can call a vote (thus spectators and observers can't call a vote): 0 = all people can vote, 1 = spectators can vote in warmup stage, 2 = only players can vote (no exceptions)." +set sv_vote_nospectators 0 "only players can call a vote (thus spectators and observers can't call a vote): 0 = all people can vote, 1 = spectators can vote in warmup stage, 2 = only players can vote (no exceptions)." alias g_tourney "g_tourney_$1" alias g_tourney_1 "g_warmup 1; g_chat_nospectators 2; sv_vote_nospectators 1" alias g_tourney_0 "g_warmup 0; g_chat_nospectators 0; sv_vote_nospectators 0" -set sv_timeout 0 "allows a player to call a timeout, this will pause the game for some time" +set sv_timeout 0 "allow a player to call a timeout, this will pause the game for some time" set sv_timeout_length 120 "how long the game will be paused at max, in seconds" set sv_timeout_number 2 "how many timeouts one player is allowed to call (gets reset after a restart)" set sv_timeout_leadtime 4 "how long the players will be informed that a timeout was called before it starts, in seconds" @@ -268,7 +277,7 @@ set g_telefrags_teamplay 1 "never telefrag team mates" set g_telefrags_avoid 1 "when teleporters have a random destination, avoid teleporting to locations where a telefrag would happen" set g_teleport_maxspeed 0 "maximum speed that a player can keep when going through a teleporter (if a misc_teleporter_dest also has a cap the smallest one of these will be used), 0 = don't limit, -1 = keep no speed" -seta cl_damageeffect 1 "enable weapon damage effects. 1 enables the feature on skeletal models, 2 on any model" +seta cl_damageeffect 1 "enable weapon damage effects: 1 enables the feature on skeletal models, 2 on any model" seta cl_damageeffect_ticrate 0.1 "particle spawn rate" seta cl_damageeffect_bones 5 "how many damages to allow on a rigged mesh at once (non-skeletal objects are limited to one)" seta cl_damageeffect_distribute 1 "divide particle intensity if multiple damages are present" @@ -287,6 +296,10 @@ set sv_gibhealth 100 "Minus health a dead body must have in order to get gibbed" set sv_friction_on_land 0 set sv_friction_slick 0.5 +set sv_slick_applygravity 0 + +set sv_aircontrol_backwards 0 "apply forward aircontrol options to backward movement" + set sv_player_viewoffset "0 0 35" "view offset of the player model" set sv_player_mins "-16 -16 -24" "playermodel mins" set sv_player_maxs "16 16 45" "playermodel maxs" @@ -360,26 +373,26 @@ 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 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 crylink hlac hagar shotgun blaster rifle tuba minelayer arc shockwave" "Desired weapons for middle distances ordered by priority" -set bot_ai_custom_weapon_priority_close "vaporizer shotgun vortex machinegun hlac tuba seeker hagar crylink mortar electro devastator blaster fireball rifle minelayer arc shockwave" "Desired weapons for close distances ordered by priority" +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 shotgun shockwave vortex 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" set bot_ai_ignoregoal_timeout 3 "Ignore goals making bots to get stuck in front of a wall for N seconds" set bot_ai_bunnyhop_skilloffset 7 "Bots with skill equal or greater than this value will perform the \"bunnyhop\" technique" set bot_ai_bunnyhop_startdistance 200 "Run to goals located further than this distance" -set bot_ai_bunnyhop_stopdistance 200 "Stop jumping after reaching this distance to the goal" +set bot_ai_bunnyhop_stopdistance 300 "Stop jumping after reaching this distance to the goal" set bot_ai_bunnyhop_firstjumpdelay 0.2 "Start running to the goal only if it was seen for more than N seconds" set bot_god 0 "god mode for bots" set bot_ai_navigation_jetpack 0 "Enable bots to navigate maps using the jetpack" set bot_ai_navigation_jetpack_mindistance 3500 "Bots will try fly to objects located farther than this distance" // Better don't touch these, there are hard to tweak! set bot_ai_aimskill_order_mix_1st 0.01 "Amount of the 1st filter output to apply to the aiming angle" -set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 1st filter output to apply to the aiming angle" -set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 1st filter output to apply to the aiming angle" -set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 1st filter output to apply to the aiming angle" -set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 1st filter output to apply to the aiming angle" +set bot_ai_aimskill_order_mix_2nd 0.1 "Amount of the 2nd filter output to apply to the aiming angle" +set bot_ai_aimskill_order_mix_3th 0.01 "Amount of the 3th filter output to apply to the aiming angle" +set bot_ai_aimskill_order_mix_4th 0.05 "Amount of the 4th filter output to apply to the aiming angle" +set bot_ai_aimskill_order_mix_5th 0.01 "Amount of the 5th filter output to apply to the aiming angle" set bot_ai_aimskill_order_filter_1st 0.4 "Position filter" set bot_ai_aimskill_order_filter_2nd 0.4 "Movement filter" set bot_ai_aimskill_order_filter_3th 0.2 "Acceleration filter" @@ -487,15 +500,15 @@ seta timelimit_suddendeath 5 "number of minutes suddendeath mode lasts after all 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" -seta 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_threshold*" -seta g_mirrordamage 0.700000 "for teamplay 4: mirror damage factor" -seta g_mirrordamage_virtual 1 "for teamplay 4: do not actually apply mirror damage, just show graphics effect for it" -seta g_mirrordamage_onlyweapons 0 "for teamplay 4: only apply mirror damage if the attack was from a weapon" -seta g_friendlyfire 0.500000 "for teamplay 4: fiendly fire factor" -seta g_friendlyfire_virtual 1 "for teamplay 4: do not actually apply friendly fire, just show graphics effect for it" -seta g_friendlyfire_virtual_force 1 "for teamplay 4: apply force even though damage was made virtual only" -seta g_teamdamage_threshold 40 "for teamplay 4: threshold over which to apply mirror damage" -seta g_teamdamage_resetspeed 20 "for teamplay 4: how fast player's teamdamage count decreases" +seta 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*" +seta g_mirrordamage 0.7 "for teamplay_mode 4: mirror damage factor" +seta g_mirrordamage_virtual 1 "for teamplay_mode 4: do not actually apply mirror damage, just show graphics effect for it" +seta g_mirrordamage_onlyweapons 0 "for teamplay_mode 4: only apply mirror damage if the attack was from a weapon" +seta g_friendlyfire 0.5 "for teamplay_mode 4: friendly fire factor" +seta g_friendlyfire_virtual 1 "for teamplay_mode 4: do not actually apply friendly fire, just show graphics effect for it" +seta g_friendlyfire_virtual_force 1 "for teamplay_mode 4: apply force even though damage was made virtual only" +seta g_teamdamage_threshold 40 "for teamplay_mode 4: threshold over which to apply mirror damage" +seta g_teamdamage_resetspeed 20 "for teamplay_mode 4: how fast player's teamdamage count decreases" seta g_balance_teams 1 "automatically balance out players entering instead of asking them for their preferred team" seta g_balance_teams_prevent_imbalance 1 "prevent players from changing to larger teams" @@ -682,7 +695,8 @@ alias sv_fbskin_off "sv_defaultcharacter 0; sv_defaultplayerskin 0; sv_defaultpl seta sv_servermodelsonly 1 cl_curl_enabled 1 -cl_curl_maxspeed 500 +cl_curl_maxdownloads 3 +cl_curl_maxspeed 0 cl_curl_useragent 1 cl_curl_useragent_append "$g_xonoticversion" @@ -694,7 +708,7 @@ set sv_motd "" set g_waypoints_for_items 0 "make waypoints out of items, values: 0 = never, 1 = unless the mapper prevents it by worldspawn.spawnflags & 1, 2 = always" seta g_maplist_votable 6 "number of maps that are shown in the map voting at the end of a match" -seta g_maplist_votable_keeptwotime 15 +seta g_maplist_votable_keeptwotime 15 "show only 2 options after this amount of time during map vote screen" seta g_maplist_votable_timeout 30 "timeout for the map voting; must be below 50 seconds!" seta g_maplist_votable_suggestions 2 seta g_maplist_votable_suggestions_override_mostrecent 0 @@ -703,7 +717,7 @@ seta g_maplist_votable_abstain 0 "when 1, you can abstain from your vote" seta 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 for this amount of time during gametype 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_timeout 20 set sv_vote_gametype_default_current 1 "Keep the current gametype if no one votes" @@ -774,8 +788,9 @@ seta g_waypointsprite_turrets 1 "disable turret waypoints" seta g_waypointsprite_turrets_maxdist 5000 "max distace for turret sprites" seta g_waypointsprite_tactical 1 "tactical overlay on turrets when in a vehicle" -seta cl_damagetext "0" "Draw damage dealt where you hit the enemy" -seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}" +seta cl_damagetext "1" "Draw damage dealt where you hit the enemy" +seta cl_damagetext_format "-{total}" "How to format the damage text. {health}, {armor}, {total}, {potential}: full damage not capped to target's health, {potential_health}: health damage not capped to target's health" +seta cl_damagetext_format_verbose 0 "{health} shows {potential_health} too when they differ; {total} shows {potential} too when they differ" seta cl_damagetext_color "1 1 0" "Damage text color" seta cl_damagetext_color_per_weapon "0" "Damage text uses weapon color" seta cl_damagetext_size "8" "Damage text font size" @@ -784,8 +799,18 @@ seta cl_damagetext_alpha_lifetime "3" "Damage text lifetime in seconds" seta cl_damagetext_velocity "0 0 20" "Damage text move direction" seta cl_damagetext_offset "0 -40 0" "Damage text offset" seta cl_damagetext_accumulate_range "30" "Damage text spawned within this range is accumulated" +seta cl_damagetext_accumulate_alpha_rel "0.65" "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha" +seta cl_damagetext_friendlyfire "1" "Show damage text for friendlyfire too" +seta cl_damagetext_friendlyfire_color "1 0 0" "Damage text color for friendlyfire" -set sv_itemstime 1 "enable networking of left time until respawn for items such as mega health and large armor" +seta cl_vehicles_alarm 1 "Play an alarm sound when the vehicle you are driving is heavily damaged" +seta cl_vehicles_hud_tactical 1 +seta cl_vehicles_hudscale 0.5 +seta cl_vehicles_notify_time 15 +seta cl_vehicles_crosshair_size 0.5 +seta cl_vehicles_crosshair_colorize 1 + +set sv_itemstime 1 "enable networking of time left until respawn for items such as mega health/armor and powerups" // so it can be stuffcmd-ed still set cl_gravity 800 "but ignored anyway" @@ -830,6 +855,10 @@ seta sbar_info_pos 0 "Y-axis distance from lower right corner for engine info pr // scoreboard seta scoreboard_columns default + +// keep old scoreboard cvars for compatibility's sake +// they've been replaced by hud_panel_scoreboard_* cvars +// TODO remove them after a future release (0.8.2+) seta scoreboard_border_thickness 1 "scoreboard border thickness" seta scoreboard_accuracy_border_thickness 1 "accuracy stats border thickness" seta scoreboard_accuracy_doublerows 0 "use two rows instead of one" @@ -881,7 +910,7 @@ seta menu_slist_categories_CAT_FAVORITED_override "" seta menu_slist_categories_CAT_RECOMMENDED_override "" seta menu_slist_categories_CAT_NORMAL_override "" seta menu_slist_categories_CAT_SERVERS_override "CAT_NORMAL" -seta menu_slist_categories_CAT_XPM_override "CAT_NORMAL" +seta menu_slist_categories_CAT_XPM_override "" seta menu_slist_categories_CAT_MODIFIED_override "" seta menu_slist_categories_CAT_OVERKILL_override "" seta menu_slist_categories_CAT_INSTAGIB_override "" @@ -936,7 +965,7 @@ set con_completion_playermodel "models/player/*.iqm" // helper // these non-saved engine cvars shall be saved -alias makesaved "seta $1 \"${$1 ?}\" +alias makesaved "seta $1 \"${$1 ?}\"" makesaved cl_maxfps_alwayssleep makesaved cl_port makesaved gl_finish @@ -961,8 +990,6 @@ sv_gameplayfix_delayprojectiles 0 sv_gameplayfix_q2airaccelerate 1 sv_gameplayfix_stepmultipletimes 1 -cl_gameplayfix_fixedcheckwatertransition 1 - // delay for "kill" to prevent abuse set g_balance_kill_delay 2 set g_balance_kill_antispam 5 @@ -978,6 +1005,8 @@ gl_texturecompression_lightcubemaps 1 gl_texturecompression_q3bsplightmaps 0 gl_texturecompression_sky 1 +cl_maxfps 200 + seta menu_mouse_absolute 1 "use the OS mouse pointer motion for menu" seta menu_mouse_speed 1 "speed multiplier for the mouse in the menu (does not affect in-game aiming)" set menu_use_default_hostname 1 @@ -1005,6 +1034,8 @@ seta menu_cdtrack "rising-of-the-phoenix" set sv_maxidle 0 "kick players idle for more than this amount of time in seconds" set sv_maxidle_spectatorsareidle 0 "when sv_maxidle is not 0, assume spectators are idle too" +set sv_maxidle_slots 0 "when not 0, only kick idlers when this many or less player slots are available" +set sv_maxidle_slots_countbots 1 "count bots as player slots" // these entities are not referenced by anything directly, they just represent // teams and are found by find() when needed @@ -1016,13 +1047,13 @@ sv_allowdownloads 0 // download protocol is evil set g_jump_grunt 0 "Do you make a grunting noise every time you jump? Is it the same grunting noise every time?" -seta cl_weaponpriority "vaporizer vortex fireball mortar machinegun hagar rifle arc electro devastator crylink minelayer shotgun hlac tuba blaster porto seeker hook" "weapon priority list" +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_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 "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_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 vortex rifle machinegun shotgun" "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" "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_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_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" @@ -1073,7 +1104,7 @@ set g_ban_sync_trusted_servers "" "request ban lists from these xonotic servers set g_ban_sync_timeout 45 "time out in seconds for the ban sync requests" set g_ban_sync_trusted_servers_verify 0 "when set to 1, additional bans sent by the servers are ignored, and only bans for the requested IP are used" -set g_showweaponspawns 1 "display sprites for weapon spawns found on the map when a weapon key is pressed and the weapon is not available" +set g_showweaponspawns 1 "1: display waypoints for weapon spawns found on the map when a weapon key is pressed and the weapon is not owned; 2: for dropped weapons too; 3: for all the weapons sharing the same impulse" // ballistics use physical units, but qu based // Quake-Newton: 1 qN = 1 qu * 1 g / 1 s^2 @@ -1083,6 +1114,7 @@ set g_showweaponspawns 1 "display sprites for weapon spawns found on the map whe set g_ballistics_mindistance 2 // enable ballistics starting from 2 qu set g_ballistics_density_player 0.50 // players are 2x as easy to pass as walls set g_ballistics_density_corpse 0.10 // corpses are 10x as easy to pass as walls +set g_ballistics_penetrate_clips 0 "allow ballistics to pass through weapon clips" set cl_stripcolorcodes 0 "experimental feature (notes: strips ALL color codes from messages!)" @@ -1193,6 +1225,7 @@ seta cl_forcemyplayermodel "" "set to the model file name you want to show yours seta cl_forcemyplayerskin 0 "set to the skin number you want to show yourself as (does not affect how enemies look with cl_forceplayermodels)" seta cl_forcemyplayercolors 0 "set to the color value (encoding is same as _cl_color) for your own player model (ignored in teamplay; does not affect how enemies look with cl_forceplayermodels)" seta cl_movement_errorcompensation 1 "try to compensate for prediction errors and reduce preceived lag" +seta cl_movement_intermissionrunning 0 "keep velocity after the match ends, players may appear to continue running while stationary" // campaign internal, set when loading a campaign map1G set _campaign_index "" @@ -1202,6 +1235,10 @@ set _campaign_testrun 0 "To verify the campaign file, set this to 1, then start // debug set _independent_players 0 "DO NOT TOUCH" set _notarget 0 "NO, REALLY, DON'T" +set debugdraw 0 +set debugdraw_filter "" +set debugdraw_filterout "" +set debugtrace 0 // define some engine cvars that we need even on dedicated server set r_showbboxes 0 @@ -1290,6 +1327,8 @@ r_fakelight 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 + // strength sound settings set sv_strengthsound_antispam_time 0.1 "minimum distance of strength sounds" set sv_strengthsound_antispam_refire_threshold 0.04 "apply minimum distance only if refire of the gun is smaller than this" @@ -1355,8 +1394,12 @@ set g_frozen_revive_falldamage_health 40 "Amount of health player has if they re set g_frozen_damage_trigger 1 "if 1, frozen players falling into the void will die instead of teleporting to spawn" set g_frozen_force 0.6 "How much to multiply the force on a frozen player with" -// player statistics server URI +// player statistics set g_playerstats_gamereport_uri "http://stats.xonotic.org/stats/submit" "Output player statistics information to either: URL (with ://), console (with a dash like this: -), or supply a filename to output to data directory." +set g_playerstats_gamereport_ladder "" +set g_playerstats_playerbasic_uri "http://stats.xonotic.org" +set g_playerstats_playerdetail_uri "http://stats.xonotic.org/player/me" +set g_playerstats_playerdetail_autoupdatetime 1800 // automatically update every 30 minutes anyway // autoscreenshots set g_max_info_autoscreenshot 3 "how many info_autoscreenshot entities are allowed" @@ -1436,6 +1479,8 @@ alias menu_sync "menu_cmd sync" set sv_join_notices "" set sv_join_notices_time 15 +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" set sv_simple_items 1 "allow or forbid client use of simple items" @@ -1445,6 +1490,9 @@ set cl_fullbright_items 0 "enable fullbright items (if server allows, controled set cl_weapon_stay_color "2 0.5 0.5" "Color of picked up weapons when g_weapon_stay > 0" set cl_weapon_stay_alpha 0.75 "Alpha of picked up weapons when g_weapon_stay > 0" +set sv_showspectators 0 "Show who's spectating who in the player info panel. Shouldn't be used on competitive servers, also disable when watching a suspected cheater" +seta cl_showspectators 1 + // Facility for config.cfg use ONLY. // Interpreted in post-config.cfg. seta menu_forced_saved_cvars "" "These cvars will always be saved, despite engine/Xonotic cvar saving status" diff --git a/gamemodes.cfg b/gamemodes.cfg index 4cf77d52e3..71f0dc1792 100644 --- a/gamemodes.cfg +++ b/gamemodes.cfg @@ -259,9 +259,10 @@ set g_ctf_oneflag_reverse 0 "apply reverse mode to oneflag CTF (take flag to ene set g_ctf_flag_return 1 "auto return the flag to base when touched by a teammate" set g_ctf_flag_return_carrying 0 "(manual return mode) auto return the flag to base if touched by a flag carrier" set g_ctf_flag_return_carried_radius 100 "allow flags to be returned by carrier if base is within this radius" -set g_ctf_flag_return_time 15 -set g_ctf_flag_return_dropped 100 -set g_ctf_flag_return_damage 0 +set g_ctf_flag_return_time 15 "automatically return the flag to base after this amount of time" +set g_ctf_flag_return_dropped 100 "automatically return the flag to base if dropped within this distance from base (in qu)" +set g_ctf_flag_return_damage 0 "allow the flag to be damaged, reducing time needed to automatically return to base" +set g_ctf_flag_return_damage_delay 0 "how much time the flag takes to automatically return to base if it falls into lava/slime/trigger hurt" set g_ctf_flag_return_when_unreachable 1 "automatically return the flag if it falls into lava/slime/trigger hurt" set g_ctf_flagcarrier_auto_helpme_damage 100 "automatically place a helpme notification on flag carrier waypointsprite if they get hit and their health dips below this value" set g_ctf_flagcarrier_auto_helpme_time 2 "antispam time for the helpme notification" @@ -391,7 +392,7 @@ set g_freezetag_teams 0 // ========== // keepaway // ========== -set g_keepaway 0 "game mode which focuses around a ball, look at g_keepaway_win_mode for further details" +set g_keepaway 0 "game mode which focuses around a ball" set g_keepaway_score_bckill 1 "enable scoring points (y/n) for ball carrier kills (value is how many points to award)" set g_keepaway_score_killac 1 "amount of points to give when you kill someone while you have the ball" set g_keepaway_score_timeinterval 1 "amount of time it takes between intervals for timepoints to be added to the score" @@ -488,6 +489,7 @@ set g_nexball_sound_bounce 1 "bouncing sound (0: off)" set g_nexball_basketball_trail 1 "1 to leave a trail" set g_nexball_football_trail 0 "1 to leave a trail" set g_nexball_trail_color 254 "1-256 for different colors (Quake palette, 254 is white)" +set g_nexball_playerclip_collisions 1 "make the ball bounce off clips" set g_nexball_radar_showallplayers 1 "1: show every player and the ball on the radar 0: only show teammates and the ball on the radar" seta g_nexball_safepass_maxdist 5000 "Max distance to allow save fassping (0 to turn off safe passing)" seta g_nexball_safepass_turnrate 0.1 "How fast the safe-pass ball can habge direction" diff --git a/gfx/hud/default/flag_stalemate.tga b/gfx/hud/default/flag_stalemate.tga new file mode 100644 index 0000000000..c52ebb8d11 Binary files /dev/null and b/gfx/hud/default/flag_stalemate.tga differ diff --git a/gfx/hud/luma/flag_blue_lost.tga b/gfx/hud/luma/flag_blue_lost.tga index 2c5229aa93..1609b782c9 100644 Binary files a/gfx/hud/luma/flag_blue_lost.tga and b/gfx/hud/luma/flag_blue_lost.tga differ diff --git a/gfx/hud/luma/flag_neutral_lost.tga b/gfx/hud/luma/flag_neutral_lost.tga index e6fd5c5bc0..85ef904c58 100644 Binary files a/gfx/hud/luma/flag_neutral_lost.tga and b/gfx/hud/luma/flag_neutral_lost.tga differ diff --git a/gfx/hud/luma/flag_pink_lost.tga b/gfx/hud/luma/flag_pink_lost.tga index 0cd5467199..f636620b59 100644 Binary files a/gfx/hud/luma/flag_pink_lost.tga and b/gfx/hud/luma/flag_pink_lost.tga differ diff --git a/gfx/hud/luma/flag_red_lost.tga b/gfx/hud/luma/flag_red_lost.tga index 014a4163c1..7260275eaf 100644 Binary files a/gfx/hud/luma/flag_red_lost.tga and b/gfx/hud/luma/flag_red_lost.tga differ diff --git a/gfx/hud/luma/flag_stalemate.tga b/gfx/hud/luma/flag_stalemate.tga new file mode 100644 index 0000000000..c52ebb8d11 Binary files /dev/null and b/gfx/hud/luma/flag_stalemate.tga differ diff --git a/gfx/hud/luma/flag_yellow_lost.tga b/gfx/hud/luma/flag_yellow_lost.tga index bd5f3def21..ec45f11740 100644 Binary files a/gfx/hud/luma/flag_yellow_lost.tga and b/gfx/hud/luma/flag_yellow_lost.tga differ diff --git a/gfx/hud/luma/notify_blue_lost.tga b/gfx/hud/luma/notify_blue_lost.tga index 2c5229aa93..1609b782c9 100644 Binary files a/gfx/hud/luma/notify_blue_lost.tga and b/gfx/hud/luma/notify_blue_lost.tga differ diff --git a/gfx/hud/luma/notify_neutral_lost.tga b/gfx/hud/luma/notify_neutral_lost.tga index e6fd5c5bc0..85ef904c58 100644 Binary files a/gfx/hud/luma/notify_neutral_lost.tga and b/gfx/hud/luma/notify_neutral_lost.tga differ diff --git a/gfx/hud/luma/notify_pink_lost.tga b/gfx/hud/luma/notify_pink_lost.tga index 0cd5467199..f636620b59 100644 Binary files a/gfx/hud/luma/notify_pink_lost.tga and b/gfx/hud/luma/notify_pink_lost.tga differ diff --git a/gfx/hud/luma/notify_red_lost.tga b/gfx/hud/luma/notify_red_lost.tga index 014a4163c1..7260275eaf 100644 Binary files a/gfx/hud/luma/notify_red_lost.tga and b/gfx/hud/luma/notify_red_lost.tga differ diff --git a/gfx/hud/luma/notify_yellow_lost.tga b/gfx/hud/luma/notify_yellow_lost.tga index bd5f3def21..ec45f11740 100644 Binary files a/gfx/hud/luma/notify_yellow_lost.tga and b/gfx/hud/luma/notify_yellow_lost.tga differ diff --git a/gfx/menu/luma/icon_mod_jeff.tga b/gfx/menu/luma/icon_mod_jeff.tga new file mode 100644 index 0000000000..ba3080a7df Binary files /dev/null and b/gfx/menu/luma/icon_mod_jeff.tga differ diff --git a/gfx/menu/luminos/icon_mod_jeff.tga b/gfx/menu/luminos/icon_mod_jeff.tga new file mode 100644 index 0000000000..4bf904b81d Binary files /dev/null and b/gfx/menu/luminos/icon_mod_jeff.tga differ diff --git a/gfx/menu/wickedx/icon_mod_jeff.tga b/gfx/menu/wickedx/icon_mod_jeff.tga new file mode 100644 index 0000000000..4bf904b81d Binary files /dev/null and b/gfx/menu/wickedx/icon_mod_jeff.tga differ diff --git a/gfx/menu/xaw/icon_mod_jeff.tga b/gfx/menu/xaw/icon_mod_jeff.tga new file mode 100644 index 0000000000..4bf904b81d Binary files /dev/null and b/gfx/menu/xaw/icon_mod_jeff.tga differ diff --git a/hud_luma.cfg b/hud_luma.cfg index 2f4f31e92b..5ebdeaeec9 100644 --- a/hud_luma.cfg +++ b/hud_luma.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.86 0.35 0" seta hud_progressbar_vehicles_ammo1_color "0.77 0.67 0" seta hud_progressbar_vehicles_ammo2_color "0.86 0.35 0" -seta _hud_panelorder "17 15 12 9 5 10 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 15 12 9 5 10 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.005000" @@ -230,7 +230,7 @@ seta hud_panel_engineinfo_bg_padding "" seta hud_panel_engineinfo_framecounter_time "0.1" seta hud_panel_engineinfo_framecounter_decimals "0" -seta hud_panel_infomessages_pos "0.720000 0.100000" +seta hud_panel_infomessages_pos "0.680000 0.100000" seta hud_panel_infomessages_size "0.280000 0.080000" seta hud_panel_infomessages_bg "0" seta hud_panel_infomessages_bg_color "" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.030000 0.260000" seta hud_panel_itemstime_size "0.070000 0.230000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "0" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "border_default" +seta hud_panel_scoreboard_bg_color "0 0.3 0.5" +seta hud_panel_scoreboard_bg_color_team "" +seta hud_panel_scoreboard_bg_alpha "0.7" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.2" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.4" +seta hud_panel_scoreboard_bg_teams_color_team "0" +seta hud_panel_scoreboard_accuracy_doublerows "0" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/hud_luminos.cfg b/hud_luminos.cfg index 3d259ebf39..8f64ddebb6 100644 --- a/hud_luminos.cfg +++ b/hud_luminos.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5" seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0" seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0" -seta _hud_panelorder "17 15 12 9 10 5 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 15 12 9 10 5 6 14 0 7 4 11 2 1 3 8 13 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.010000" @@ -230,7 +230,7 @@ seta hud_panel_engineinfo_bg_padding "" seta hud_panel_engineinfo_framecounter_time "0.1" seta hud_panel_engineinfo_framecounter_decimals "0" -seta hud_panel_infomessages_pos "0.720000 0.100000" +seta hud_panel_infomessages_pos "0.680000 0.100000" seta hud_panel_infomessages_size "0.280000 0.080000" seta hud_panel_infomessages_bg "0" seta hud_panel_infomessages_bg_color "" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.030000 0.260000" seta hud_panel_itemstime_size "0.070000 0.230000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "0" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "border_default" +seta hud_panel_scoreboard_bg_color "" +seta hud_panel_scoreboard_bg_color_team "0.7" +seta hud_panel_scoreboard_bg_alpha "0.7" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0.8" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.08" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.3" +seta hud_panel_scoreboard_bg_teams_color_team "0" +seta hud_panel_scoreboard_accuracy_doublerows "1" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/hud_luminos_minimal.cfg b/hud_luminos_minimal.cfg index c1ac51b7ab..31926d2f5a 100644 --- a/hud_luminos_minimal.cfg +++ b/hud_luminos_minimal.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5" seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0" seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0" -seta _hud_panelorder "17 10 3 0 14 6 9 13 4 1 2 11 12 7 5 8 15 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 10 3 0 14 6 9 13 4 1 2 11 12 7 5 8 15 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.010000" @@ -231,7 +231,7 @@ seta hud_panel_engineinfo_framecounter_time "0.1" seta hud_panel_engineinfo_framecounter_decimals "0" seta hud_panel_infomessages_pos "0.710000 0" -seta hud_panel_infomessages_size "0.290000 0.100000" +seta hud_panel_infomessages_size "0.280000 0.090000" seta hud_panel_infomessages_bg "0" seta hud_panel_infomessages_bg_color "" seta hud_panel_infomessages_bg_color_team "" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.000000 0.310000" seta hud_panel_itemstime_size "0.070000 0.180000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "0" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "0" +seta hud_panel_scoreboard_bg_color "" +seta hud_panel_scoreboard_bg_color_team "" +seta hud_panel_scoreboard_bg_alpha "" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0.8" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.08" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.3" +seta hud_panel_scoreboard_bg_teams_color_team "0.7" +seta hud_panel_scoreboard_accuracy_doublerows "1" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/hud_luminos_minimal_xhair.cfg b/hud_luminos_minimal_xhair.cfg index d6125985b8..380b658148 100644 --- a/hud_luminos_minimal_xhair.cfg +++ b/hud_luminos_minimal_xhair.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5" seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0" seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0" -seta _hud_panelorder "17 15 3 1 2 11 10 0 14 6 9 13 4 12 7 5 8 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 15 3 1 2 11 10 0 14 6 9 13 4 12 7 5 8 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.010000" @@ -231,7 +231,7 @@ seta hud_panel_engineinfo_framecounter_time "0.1" seta hud_panel_engineinfo_framecounter_decimals "0" seta hud_panel_infomessages_pos "0.710000 0" -seta hud_panel_infomessages_size "0.290000 0.100000" +seta hud_panel_infomessages_size "0.280000 0.090000" seta hud_panel_infomessages_bg "0" seta hud_panel_infomessages_bg_color "" seta hud_panel_infomessages_bg_color_team "" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.000000 0.310000" seta hud_panel_itemstime_size "0.070000 0.180000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "0" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "0" +seta hud_panel_scoreboard_bg_color "" +seta hud_panel_scoreboard_bg_color_team "" +seta hud_panel_scoreboard_bg_alpha "" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0.8" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.08" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.3" +seta hud_panel_scoreboard_bg_teams_color_team "0.7" +seta hud_panel_scoreboard_accuracy_doublerows "1" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/hud_luminos_old.cfg b/hud_luminos_old.cfg index 4ee8005213..96b4f508f5 100644 --- a/hud_luminos_old.cfg +++ b/hud_luminos_old.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5" seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0" seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0" -seta _hud_panelorder "17 15 10 9 6 8 14 5 0 4 13 2 7 1 3 11 12 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 15 10 9 6 8 14 5 0 4 13 2 7 1 3 11 12 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.010000" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.020000 0.490000" seta hud_panel_itemstime_size "0.090000 0.140000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "1" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "" +seta hud_panel_scoreboard_bg_color "" +seta hud_panel_scoreboard_bg_color_team "" +seta hud_panel_scoreboard_bg_alpha "" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0.8" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.08" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.3" +seta hud_panel_scoreboard_bg_teams_color_team "0.7" +seta hud_panel_scoreboard_accuracy_doublerows "1" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/hud_nexuiz.cfg b/hud_nexuiz.cfg index df565ef686..ae786574f3 100644 --- a/hud_nexuiz.cfg +++ b/hud_nexuiz.cfg @@ -29,7 +29,7 @@ seta hud_progressbar_acceleration_neg_color "0.125 0.25 0.5" seta hud_progressbar_vehicles_ammo1_color "0.8 0.7 0" seta hud_progressbar_vehicles_ammo2_color "0.7 0.4 0" -seta _hud_panelorder "17 15 0 11 8 5 6 14 9 13 7 2 3 1 10 12 4 16 18 23 19 20 21 22 " +seta _hud_panelorder "17 15 0 11 8 5 6 14 9 13 7 2 3 1 10 12 4 16 18 23 19 20 21 22 23 24 " seta hud_configure_grid "1" seta hud_configure_grid_xsize "0.010000" @@ -330,6 +330,7 @@ seta hud_panel_mapvote_bg_color_team "" seta hud_panel_mapvote_bg_alpha "" seta hud_panel_mapvote_bg_border "" seta hud_panel_mapvote_bg_padding "" +seta hud_panel_mapvote_highlight_border "1" seta hud_panel_itemstime_pos "0.000000 0.290000" seta hud_panel_itemstime_size "0.150000 0.060000" @@ -357,4 +358,26 @@ seta hud_panel_quickmenu_bg_border "" seta hud_panel_quickmenu_bg_padding "" seta hud_panel_quickmenu_align "0" +seta hud_panel_scoreboard_pos "0.150000 0.150000" +seta hud_panel_scoreboard_size "0.700000 0.700000" +seta hud_panel_scoreboard_bg "" +seta hud_panel_scoreboard_bg_color "" +seta hud_panel_scoreboard_bg_color_team "" +seta hud_panel_scoreboard_bg_alpha "0.7" +seta hud_panel_scoreboard_bg_border "" +seta hud_panel_scoreboard_bg_padding "" +seta hud_panel_scoreboard_fadeinspeed "10" +seta hud_panel_scoreboard_fadeoutspeed "5" +seta hud_panel_scoreboard_respawntime_decimals "1" +seta hud_panel_scoreboard_table_bg_alpha "0.8" +seta hud_panel_scoreboard_table_bg_scale "0.25" +seta hud_panel_scoreboard_table_fg_alpha "0.9" +seta hud_panel_scoreboard_table_fg_alpha_self "1" +seta hud_panel_scoreboard_table_highlight "1" +seta hud_panel_scoreboard_table_highlight_alpha "0.1" +seta hud_panel_scoreboard_table_highlight_alpha_self "0.25" +seta hud_panel_scoreboard_bg_teams_color_team "0.7" +seta hud_panel_scoreboard_accuracy_doublerows "1" +seta hud_panel_scoreboard_accuracy_nocolors "0" + menu_sync diff --git a/minigames.cfg b/minigames.cfg index 6dfec5088f..8e70653bb2 100644 --- a/minigames.cfg +++ b/minigames.cfg @@ -19,3 +19,8 @@ set sv_minigames_snake_wrap 0 "Wrap around the edges of the screen instead of dy set sv_minigames_snake_delay_initial 0.7 "Initial delay between snake movement" set sv_minigames_snake_delay_multiplier 50 "Multiplier of incremental of movement speed (player_score / cvar)" set sv_minigames_snake_delay_min 0.1 "Minimum delay between snake movement (at fastest rate)" +set sv_minigames_snake_lives 3 + + +// Bulldozer +set sv_minigames_bulldozer_startlevel "level1" diff --git a/minigames/bulldozer/storage_level1.txt b/minigames/bulldozer/storage_level1.txt index d82be653c5..6d12e37cf1 100644 --- a/minigames/bulldozer/storage_level1.txt +++ b/minigames/bulldozer/storage_level1.txt @@ -1,399 +1,399 @@ // bulldozer storage "level1" last updated 16-11-2015 03:11:33 nextlevel = "level2" -"k11" 1 "0 -1 0" -"a20" 6 "0 0 0" -"a19" 6 "0 0 0" -"a18" 6 "0 0 0" -"a17" 6 "0 0 0" -"a15" 6 "0 0 0" -"a14" 6 "0 0 0" -"a13" 6 "0 0 0" -"a12" 6 "0 0 0" -"a11" 6 "0 0 0" -"a10" 6 "0 0 0" -"a9" 6 "0 0 0" -"a8" 6 "0 0 0" -"a7" 6 "0 0 0" -"a6" 6 "0 0 0" -"a5" 6 "0 0 0" -"a4" 6 "0 0 0" -"a3" 6 "0 0 0" -"a2" 6 "0 0 0" -"a1" 6 "0 0 0" -"b1" 6 "0 0 0" -"c1" 6 "0 0 0" -"d1" 6 "0 0 0" -"e1" 6 "0 0 0" -"f1" 6 "0 0 0" -"g1" 6 "0 0 0" -"h1" 6 "0 0 0" -"i1" 6 "0 0 0" -"j1" 6 "0 0 0" -"k1" 6 "0 0 0" -"l1" 6 "0 0 0" -"m1" 6 "0 0 0" -"n1" 6 "0 0 0" -"o1" 6 "0 0 0" -"p1" 6 "0 0 0" -"q1" 6 "0 0 0" -"r1" 6 "0 0 0" -"s1" 6 "0 0 0" -"t1" 6 "0 0 0" -"t2" 6 "0 0 0" -"t3" 6 "0 0 0" -"t4" 6 "0 0 0" -"t5" 6 "0 0 0" -"t6" 6 "0 0 0" -"t7" 6 "0 0 0" -"t8" 6 "0 0 0" -"t9" 6 "0 0 0" -"t10" 6 "0 0 0" -"t11" 6 "0 0 0" -"t12" 6 "0 0 0" -"t13" 6 "0 0 0" -"t14" 6 "0 0 0" -"t15" 6 "0 0 0" -"t16" 6 "0 0 0" -"t17" 6 "0 0 0" -"t18" 6 "0 0 0" -"t19" 6 "0 0 0" -"t20" 6 "0 0 0" -"s20" 6 "0 0 0" -"r20" 6 "0 0 0" -"q20" 6 "0 0 0" -"p20" 6 "0 0 0" -"o20" 6 "0 0 0" -"n20" 6 "0 0 0" -"m20" 6 "0 0 0" -"l20" 6 "0 0 0" -"k20" 6 "0 0 0" -"j20" 6 "0 0 0" -"i20" 6 "0 0 0" -"h20" 6 "0 0 0" -"g20" 6 "0 0 0" -"f20" 6 "0 0 0" -"e20" 6 "0 0 0" -"d20" 6 "0 0 0" -"c20" 6 "0 0 0" -"b20" 6 "0 0 0" -"a16" 6 "0 0 0" -"b2" 4 "0 0 0" -"c18" 6 "0 0 0" -"e18" 6 "0 0 0" -"e17" 6 "0 0 0" -"e16" 6 "0 0 0" -"e15" 6 "0 0 0" -"e14" 6 "0 0 0" -"e13" 6 "0 0 0" -"e12" 6 "0 0 0" -"e11" 6 "0 0 0" -"f10" 4 "0 0 0" -"e9" 6 "0 0 0" -"e8" 6 "0 0 0" -"e7" 6 "0 0 0" -"e6" 6 "0 0 0" -"e5" 6 "0 0 0" -"e4" 6 "0 0 0" -"e3" 6 "0 0 0" -"e2" 6 "0 0 0" -"e19" 6 "0 0 0" -"c17" 6 "0 0 0" -"c16" 6 "0 0 0" -"c15" 6 "0 0 0" -"c14" 6 "0 0 0" -"c13" 6 "0 0 0" -"c12" 6 "0 0 0" -"c11" 6 "0 0 0" -"c10" 6 "0 0 0" -"c9" 6 "0 0 0" -"c8" 6 "0 0 0" -"c7" 6 "0 0 0" -"c6" 6 "0 0 0" -"c5" 6 "0 0 0" -"c4" 6 "0 0 0" -"c3" 6 "0 0 0" -"c2" 6 "0 0 0" -"c19" 6 "0 0 0" -"i14" 5 "0 0 0" -"f12" 4 "0 0 0" -"f11" 4 "0 0 0" -"e10" 6 "0 0 0" -"i10" 5 "0 0 0" -"h10" 5 "0 0 0" -"j10" 5 "0 0 0" -"j9" 5 "0 0 0" -"g11" 5 "0 0 0" -"j8" 5 "0 0 0" -"k8" 5 "0 0 0" -"l8" 5 "0 0 0" -"l9" 5 "0 0 0" -"i8" 4 "0 0 0" -"i9" 4 "0 0 0" -"m11" 5 "0 0 0" -"l10" 5 "0 0 0" -"n11" 5 "0 0 0" -"n13" 5 "0 0 0" -"n12" 5 "0 0 0" -"m13" 5 "0 0 0" -"k13" 5 "0 0 0" -"k14" 5 "0 0 0" -"k15" 5 "0 0 0" -"j15" 5 "0 0 0" -"i13" 5 "0 0 0" -"h15" 4 "0 0 0" -"h14" 4 "0 0 0" -"h13" 4 "0 0 0" -"g15" 4 "0 0 0" -"p17" 6 "0 0 0" -"p16" 6 "0 0 0" -"l13" 5 "0 0 0" -"h12" 5 "0 0 0" -"i15" 5 "0 0 0" -"l15" 4 "0 0 0" -"m15" 4 "0 0 0" -"l14" 4 "0 0 0" -"m14" 4 "0 0 0" -"n14" 4 "0 0 0" -"p15" 6 "0 0 0" -"p14" 6 "0 0 0" -"l11" 5 "0 0 0" -"h11" 2 "0 0 0" -"k9" 2 "0 0 0" -"j14" 2 "0 0 0" -"m12" 2 "0 0 0" -"l12" 3 "0 0 0" -"j11" 3 "0 0 0" -"j12" 3 "0 0 0" -"k10" 3 "0 0 0" -"i12" 5 "0 0 0" -"g12" 5 "0 0 0" -"g10" 5 "0 0 0" -"p13" 6 "0 0 0" -"p12" 6 "0 0 0" -"p11" 6 "0 0 0" -"p10" 6 "0 0 0" -"p9" 6 "0 0 0" -"p8" 6 "0 0 0" -"p7" 6 "0 0 0" -"p6" 6 "0 0 0" -"p5" 6 "0 0 0" -"p4" 6 "0 0 0" -"p3" 6 "0 0 0" -"p2" 6 "0 0 0" -"p18" 6 "0 0 0" -"p19" 6 "0 0 0" -"r16" 6 "0 0 0" -"r15" 6 "0 0 0" -"r14" 6 "0 0 0" -"r13" 6 "0 0 0" -"r12" 6 "0 0 0" -"r11" 6 "0 0 0" -"r10" 6 "0 0 0" -"r9" 6 "0 0 0" -"r8" 6 "0 0 0" -"r7" 6 "0 0 0" -"r6" 6 "0 0 0" -"r5" 6 "0 0 0" -"r4" 6 "0 0 0" -"r3" 6 "0 0 0" -"r2" 6 "0 0 0" -"r17" 6 "0 0 0" -"r18" 6 "0 0 0" -"r19" 6 "0 0 0" -"d12" 4 "0 0 0" -"d6" 4 "0 0 0" -"f6" 4 "0 0 0" -"f7" 4 "0 0 0" -"f8" 4 "0 0 0" -"f9" 4 "0 0 0" -"h9" 4 "0 0 0" -"g9" 4 "0 0 0" -"g8" 4 "0 0 0" -"g7" 4 "0 0 0" -"g6" 4 "0 0 0" -"g5" 4 "0 0 0" -"g4" 4 "0 0 0" -"h6" 4 "0 0 0" -"h7" 4 "0 0 0" -"h8" 4 "0 0 0" -"i7" 4 "0 0 0" -"i6" 4 "0 0 0" -"j6" 4 "0 0 0" -"k6" 4 "0 0 0" -"j7" 4 "0 0 0" -"k7" 4 "0 0 0" -"l6" 4 "0 0 0" -"l7" 4 "0 0 0" -"m10" 4 "0 0 0" -"m8" 4 "0 0 0" -"m7" 4 "0 0 0" -"m6" 4 "0 0 0" -"m5" 4 "0 0 0" -"m4" 4 "0 0 0" -"m3" 4 "0 0 0" -"m2" 4 "0 0 0" -"f19" 4 "0 0 0" -"f18" 4 "0 0 0" -"f17" 4 "0 0 0" -"f16" 4 "0 0 0" -"f15" 4 "0 0 0" -"f13" 4 "0 0 0" -"l3" 4 "0 0 0" -"l4" 4 "0 0 0" -"l5" 4 "0 0 0" -"j3" 4 "0 0 0" -"j2" 4 "0 0 0" -"k3" 4 "0 0 0" -"i3" 4 "0 0 0" -"g3" 4 "0 0 0" -"h3" 4 "0 0 0" -"f3" 4 "0 0 0" -"d3" 4 "0 0 0" -"d19" 4 "0 0 0" -"d17" 4 "0 0 0" -"d5" 4 "0 0 0" -"d4" 4 "0 0 0" -"d7" 4 "0 0 0" -"h5" 4 "0 0 0" -"h4" 4 "0 0 0" -"i4" 4 "0 0 0" -"j4" 4 "0 0 0" -"k5" 4 "0 0 0" -"j5" 4 "0 0 0" -"i5" 4 "0 0 0" -"d18" 4 "0 0 0" -"d10" 4 "0 0 0" -"g13" 4 "0 0 0" -"g14" 4 "0 0 0" -"k4" 4 "0 0 0" -"d8" 4 "0 0 0" -"d9" 4 "0 0 0" -"g16" 4 "0 0 0" -"g17" 4 "0 0 0" -"g18" 4 "0 0 0" -"d11" 4 "0 0 0" -"g19" 4 "0 0 0" -"h19" 4 "0 0 0" -"h18" 4 "0 0 0" -"h16" 4 "0 0 0" -"h17" 4 "0 0 0" -"i16" 4 "0 0 0" -"i17" 4 "0 0 0" -"i18" 4 "0 0 0" -"j19" 4 "0 0 0" -"j18" 4 "0 0 0" -"j17" 4 "0 0 0" -"i19" 4 "0 0 0" -"j16" 4 "0 0 0" -"k16" 4 "0 0 0" -"k17" 4 "0 0 0" -"k18" 4 "0 0 0" -"k19" 4 "0 0 0" -"l19" 4 "0 0 0" -"f5" 4 "0 0 0" -"f4" 4 "0 0 0" -"l18" 4 "0 0 0" -"l17" 4 "0 0 0" -"l16" 4 "0 0 0" -"m16" 4 "0 0 0" -"m17" 4 "0 0 0" -"m18" 4 "0 0 0" -"n19" 4 "0 0 0" -"m19" 4 "0 0 0" -"n18" 4 "0 0 0" -"n17" 4 "0 0 0" -"n16" 4 "0 0 0" -"n15" 4 "0 0 0" -"n10" 4 "0 0 0" -"n9" 4 "0 0 0" -"n8" 4 "0 0 0" -"n7" 4 "0 0 0" -"n6" 4 "0 0 0" -"n5" 4 "0 0 0" -"n4" 4 "0 0 0" -"n3" 4 "0 0 0" -"m9" 4 "0 0 0" -"s2" 4 "0 0 0" -"s3" 4 "0 0 0" -"s4" 4 "0 0 0" -"s5" 4 "0 0 0" -"s6" 4 "0 0 0" -"s7" 4 "0 0 0" -"s8" 4 "0 0 0" -"s9" 4 "0 0 0" -"s10" 4 "0 0 0" -"s11" 4 "0 0 0" -"s12" 4 "0 0 0" -"s13" 4 "0 0 0" -"s14" 4 "0 0 0" -"s15" 4 "0 0 0" -"s16" 4 "0 0 0" -"s17" 4 "0 0 0" -"s18" 4 "0 0 0" -"s19" 4 "0 0 0" -"q19" 4 "0 0 0" -"o19" 4 "0 0 0" -"o18" 4 "0 0 0" -"o17" 4 "0 0 0" -"o16" 4 "0 0 0" -"o15" 4 "0 0 0" -"o14" 4 "0 0 0" -"o13" 4 "0 0 0" -"o12" 4 "0 0 0" -"o11" 4 "0 0 0" -"o10" 4 "0 0 0" -"o9" 4 "0 0 0" -"o8" 4 "0 0 0" -"o7" 4 "0 0 0" -"o6" 4 "0 0 0" -"o5" 4 "0 0 0" -"o4" 4 "0 0 0" -"o3" 4 "0 0 0" -"q8" 4 "0 0 0" -"q15" 4 "0 0 0" -"q16" 4 "0 0 0" -"q17" 4 "0 0 0" -"q18" 4 "0 0 0" -"q3" 4 "0 0 0" -"q4" 4 "0 0 0" -"q6" 4 "0 0 0" -"q7" 4 "0 0 0" -"q5" 4 "0 0 0" -"q9" 4 "0 0 0" -"q10" 4 "0 0 0" -"q11" 4 "0 0 0" -"q12" 4 "0 0 0" -"q13" 4 "0 0 0" -"q14" 4 "0 0 0" -"b14" 4 "0 0 0" -"b13" 4 "0 0 0" -"d13" 4 "0 0 0" -"d14" 4 "0 0 0" -"d15" 4 "0 0 0" -"b15" 4 "0 0 0" -"b16" 4 "0 0 0" -"d16" 4 "0 0 0" -"b17" 4 "0 0 0" -"b18" 4 "0 0 0" -"b19" 4 "0 0 0" -"f2" 4 "0 0 0" -"g2" 4 "0 0 0" -"h2" 4 "0 0 0" -"i2" 4 "0 0 0" -"k2" 4 "0 0 0" -"l2" 4 "0 0 0" -"n2" 4 "0 0 0" -"o2" 4 "0 0 0" -"q2" 4 "0 0 0" -"f14" 4 "0 0 0" -"b12" 4 "0 0 0" -"b11" 4 "0 0 0" -"b10" 4 "0 0 0" -"b9" 4 "0 0 0" -"b8" 4 "0 0 0" -"b7" 4 "0 0 0" -"b6" 4 "0 0 0" -"b5" 4 "0 0 0" -"b4" 4 "0 0 0" -"b3" 4 "0 0 0" -"d2" 4 "0 0 0" +"k11" 1 1 +"a20" 6 0 +"a19" 6 0 +"a18" 6 0 +"a17" 6 0 +"a15" 6 0 +"a14" 6 0 +"a13" 6 0 +"a12" 6 0 +"a11" 6 0 +"a10" 6 0 +"a9" 6 0 +"a8" 6 0 +"a7" 6 0 +"a6" 6 0 +"a5" 6 0 +"a4" 6 0 +"a3" 6 0 +"a2" 6 0 +"a1" 6 0 +"b1" 6 0 +"c1" 6 0 +"d1" 6 0 +"e1" 6 0 +"f1" 6 0 +"g1" 6 0 +"h1" 6 0 +"i1" 6 0 +"j1" 6 0 +"k1" 6 0 +"l1" 6 0 +"m1" 6 0 +"n1" 6 0 +"o1" 6 0 +"p1" 6 0 +"q1" 6 0 +"r1" 6 0 +"s1" 6 0 +"t1" 6 0 +"t2" 6 0 +"t3" 6 0 +"t4" 6 0 +"t5" 6 0 +"t6" 6 0 +"t7" 6 0 +"t8" 6 0 +"t9" 6 0 +"t10" 6 0 +"t11" 6 0 +"t12" 6 0 +"t13" 6 0 +"t14" 6 0 +"t15" 6 0 +"t16" 6 0 +"t17" 6 0 +"t18" 6 0 +"t19" 6 0 +"t20" 6 0 +"s20" 6 0 +"r20" 6 0 +"q20" 6 0 +"p20" 6 0 +"o20" 6 0 +"n20" 6 0 +"m20" 6 0 +"l20" 6 0 +"k20" 6 0 +"j20" 6 0 +"i20" 6 0 +"h20" 6 0 +"g20" 6 0 +"f20" 6 0 +"e20" 6 0 +"d20" 6 0 +"c20" 6 0 +"b20" 6 0 +"a16" 6 0 +"b2" 4 0 +"c18" 6 0 +"e18" 6 0 +"e17" 6 0 +"e16" 6 0 +"e15" 6 0 +"e14" 6 0 +"e13" 6 0 +"e12" 6 0 +"e11" 6 0 +"f10" 4 0 +"e9" 6 0 +"e8" 6 0 +"e7" 6 0 +"e6" 6 0 +"e5" 6 0 +"e4" 6 0 +"e3" 6 0 +"e2" 6 0 +"e19" 6 0 +"c17" 6 0 +"c16" 6 0 +"c15" 6 0 +"c14" 6 0 +"c13" 6 0 +"c12" 6 0 +"c11" 6 0 +"c10" 6 0 +"c9" 6 0 +"c8" 6 0 +"c7" 6 0 +"c6" 6 0 +"c5" 6 0 +"c4" 6 0 +"c3" 6 0 +"c2" 6 0 +"c19" 6 0 +"i14" 5 0 +"f12" 4 0 +"f11" 4 0 +"e10" 6 0 +"i10" 5 0 +"h10" 5 0 +"j10" 5 0 +"j9" 5 0 +"g11" 5 0 +"j8" 5 0 +"k8" 5 0 +"l8" 5 0 +"l9" 5 0 +"i8" 4 0 +"i9" 4 0 +"m11" 5 0 +"l10" 5 0 +"n11" 5 0 +"n13" 5 0 +"n12" 5 0 +"m13" 5 0 +"k13" 5 0 +"k14" 5 0 +"k15" 5 0 +"j15" 5 0 +"i13" 5 0 +"h15" 4 0 +"h14" 4 0 +"h13" 4 0 +"g15" 4 0 +"p17" 6 0 +"p16" 6 0 +"l13" 5 0 +"h12" 5 0 +"i15" 5 0 +"l15" 4 0 +"m15" 4 0 +"l14" 4 0 +"m14" 4 0 +"n14" 4 0 +"p15" 6 0 +"p14" 6 0 +"l11" 5 0 +"h11" 2 0 +"k9" 2 0 +"j14" 2 0 +"m12" 2 0 +"l12" 3 0 +"j11" 3 0 +"j12" 3 0 +"k10" 3 0 +"i12" 5 0 +"g12" 5 0 +"g10" 5 0 +"p13" 6 0 +"p12" 6 0 +"p11" 6 0 +"p10" 6 0 +"p9" 6 0 +"p8" 6 0 +"p7" 6 0 +"p6" 6 0 +"p5" 6 0 +"p4" 6 0 +"p3" 6 0 +"p2" 6 0 +"p18" 6 0 +"p19" 6 0 +"r16" 6 0 +"r15" 6 0 +"r14" 6 0 +"r13" 6 0 +"r12" 6 0 +"r11" 6 0 +"r10" 6 0 +"r9" 6 0 +"r8" 6 0 +"r7" 6 0 +"r6" 6 0 +"r5" 6 0 +"r4" 6 0 +"r3" 6 0 +"r2" 6 0 +"r17" 6 0 +"r18" 6 0 +"r19" 6 0 +"d12" 4 0 +"d6" 4 0 +"f6" 4 0 +"f7" 4 0 +"f8" 4 0 +"f9" 4 0 +"h9" 4 0 +"g9" 4 0 +"g8" 4 0 +"g7" 4 0 +"g6" 4 0 +"g5" 4 0 +"g4" 4 0 +"h6" 4 0 +"h7" 4 0 +"h8" 4 0 +"i7" 4 0 +"i6" 4 0 +"j6" 4 0 +"k6" 4 0 +"j7" 4 0 +"k7" 4 0 +"l6" 4 0 +"l7" 4 0 +"m10" 4 0 +"m8" 4 0 +"m7" 4 0 +"m6" 4 0 +"m5" 4 0 +"m4" 4 0 +"m3" 4 0 +"m2" 4 0 +"f19" 4 0 +"f18" 4 0 +"f17" 4 0 +"f16" 4 0 +"f15" 4 0 +"f13" 4 0 +"l3" 4 0 +"l4" 4 0 +"l5" 4 0 +"j3" 4 0 +"j2" 4 0 +"k3" 4 0 +"i3" 4 0 +"g3" 4 0 +"h3" 4 0 +"f3" 4 0 +"d3" 4 0 +"d19" 4 0 +"d17" 4 0 +"d5" 4 0 +"d4" 4 0 +"d7" 4 0 +"h5" 4 0 +"h4" 4 0 +"i4" 4 0 +"j4" 4 0 +"k5" 4 0 +"j5" 4 0 +"i5" 4 0 +"d18" 4 0 +"d10" 4 0 +"g13" 4 0 +"g14" 4 0 +"k4" 4 0 +"d8" 4 0 +"d9" 4 0 +"g16" 4 0 +"g17" 4 0 +"g18" 4 0 +"d11" 4 0 +"g19" 4 0 +"h19" 4 0 +"h18" 4 0 +"h16" 4 0 +"h17" 4 0 +"i16" 4 0 +"i17" 4 0 +"i18" 4 0 +"j19" 4 0 +"j18" 4 0 +"j17" 4 0 +"i19" 4 0 +"j16" 4 0 +"k16" 4 0 +"k17" 4 0 +"k18" 4 0 +"k19" 4 0 +"l19" 4 0 +"f5" 4 0 +"f4" 4 0 +"l18" 4 0 +"l17" 4 0 +"l16" 4 0 +"m16" 4 0 +"m17" 4 0 +"m18" 4 0 +"n19" 4 0 +"m19" 4 0 +"n18" 4 0 +"n17" 4 0 +"n16" 4 0 +"n15" 4 0 +"n10" 4 0 +"n9" 4 0 +"n8" 4 0 +"n7" 4 0 +"n6" 4 0 +"n5" 4 0 +"n4" 4 0 +"n3" 4 0 +"m9" 4 0 +"s2" 4 0 +"s3" 4 0 +"s4" 4 0 +"s5" 4 0 +"s6" 4 0 +"s7" 4 0 +"s8" 4 0 +"s9" 4 0 +"s10" 4 0 +"s11" 4 0 +"s12" 4 0 +"s13" 4 0 +"s14" 4 0 +"s15" 4 0 +"s16" 4 0 +"s17" 4 0 +"s18" 4 0 +"s19" 4 0 +"q19" 4 0 +"o19" 4 0 +"o18" 4 0 +"o17" 4 0 +"o16" 4 0 +"o15" 4 0 +"o14" 4 0 +"o13" 4 0 +"o12" 4 0 +"o11" 4 0 +"o10" 4 0 +"o9" 4 0 +"o8" 4 0 +"o7" 4 0 +"o6" 4 0 +"o5" 4 0 +"o4" 4 0 +"o3" 4 0 +"q8" 4 0 +"q15" 4 0 +"q16" 4 0 +"q17" 4 0 +"q18" 4 0 +"q3" 4 0 +"q4" 4 0 +"q6" 4 0 +"q7" 4 0 +"q5" 4 0 +"q9" 4 0 +"q10" 4 0 +"q11" 4 0 +"q12" 4 0 +"q13" 4 0 +"q14" 4 0 +"b14" 4 0 +"b13" 4 0 +"d13" 4 0 +"d14" 4 0 +"d15" 4 0 +"b15" 4 0 +"b16" 4 0 +"d16" 4 0 +"b17" 4 0 +"b18" 4 0 +"b19" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"k2" 4 0 +"l2" 4 0 +"n2" 4 0 +"o2" 4 0 +"q2" 4 0 +"f14" 4 0 +"b12" 4 0 +"b11" 4 0 +"b10" 4 0 +"b9" 4 0 +"b8" 4 0 +"b7" 4 0 +"b6" 4 0 +"b5" 4 0 +"b4" 4 0 +"b3" 4 0 +"d2" 4 0 diff --git a/minigames/bulldozer/storage_level10.txt b/minigames/bulldozer/storage_level10.txt index eb47650b3f..38f0560db0 100644 --- a/minigames/bulldozer/storage_level10.txt +++ b/minigames/bulldozer/storage_level10.txt @@ -1,387 +1,387 @@ // bulldozer storage "level10" last updated 11-06-2016 14:57:48 nextlevel = "level11" -"e13" 4 "0 -1 0" -"d13" 4 "0 -1 0" -"c13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"c10" 10 "0 -1 0" -"c12" 4 "0 -1 0" -"e12" 4 "0 -1 0" -"e14" 10 "0 -1 0" -"g12" 4 "0 -1 0" -"j14" 5 "0 -1 0" -"k13" 2 "0 -1 0" -"k12" 2 "0 -1 0" -"l12" 2 "0 -1 0" -"l13" 2 "0 -1 0" -"k11" 5 "0 -1 0" -"k14" 5 "0 -1 0" -"o12" 4 "0 -1 0" -"r8" 10 "0 -1 0" -"q12" 4 "0 -1 0" -"r12" 10 "0 -1 0" -"s12" 4 "0 -1 0" -"s10" 10 "0 -1 0" -"t11" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"q11" 4 "0 -1 0" -"p11" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"k15" 5 "0 -1 0" -"m11" 1 "0 -1 0" -"j12" 3 "0 -1 0" -"j11" 3 "0 -1 0" -"j10" 3 "0 -1 0" -"l14" 3 "0 -1 0" -"k16" 5 "0 -1 0" -"g11" 4 "0 -1 0" -"f11" 4 "0 -1 0" -"e11" 4 "0 -1 0" -"d11" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"a11" 4 "0 -1 0" -"a10" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"e10" 10 "0 -1 0" -"d10" 4 "0 -1 0" -"d12" 10 "0 -1 0" -"f10" 4 "0 -1 0" -"g10" 4 "0 -1 0" -"l16" 5 "0 -1 0" -"t12" 10 "0 -1 0" -"s14" 10 "0 -1 0" -"r16" 10 "0 -1 0" -"q14" 10 "0 -1 0" -"p12" 10 "0 -1 0" -"m16" 5 "0 -1 0" -"o10" 4 "0 -1 0" -"q10" 10 "0 -1 0" -"p10" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"f12" 10 "0 -1 0" -"t10" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"q9" 4 "0 -1 0" -"p9" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"n16" 5 "0 -1 0" -"n15" 5 "0 -1 0" -"n14" 5 "0 -1 0" -"d16" 10 "0 -1 0" -"c14" 10 "0 -1 0" -"b12" 10 "0 -1 0" -"n13" 5 "0 -1 0" -"g9" 4 "0 -1 0" -"f9" 4 "0 -1 0" -"e9" 4 "0 -1 0" -"d9" 4 "0 -1 0" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"a9" 4 "0 -1 0" -"a8" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"c8" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"g8" 4 "0 -1 0" -"n12" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"m8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"d8" 10 "0 -1 0" -"s8" 4 "0 -1 0" -"t8" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"a7" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"q13" 4 "0 -1 0" -"p13" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"l9" 5 "0 -1 0" -"l8" 5 "0 -1 0" -"k8" 5 "0 -1 0" -"j8" 5 "0 -1 0" -"g13" 4 "0 -1 0" -"f13" 4 "0 -1 0" -"f14" 4 "0 -1 0" -"d14" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"d15" 4 "0 -1 0" -"e15" 4 "0 -1 0" -"f15" 4 "0 -1 0" -"g15" 4 "0 -1 0" -"h15" 4 "0 -1 0" -"i15" 4 "0 -1 0" -"j15" 4 "0 -1 0" -"i8" 5 "0 -1 0" -"h8" 5 "0 -1 0" -"o15" 4 "0 -1 0" -"p15" 4 "0 -1 0" -"q15" 4 "0 -1 0" -"r15" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"o14" 4 "0 -1 0" -"h9" 5 "0 -1 0" -"h10" 5 "0 -1 0" -"h11" 5 "0 -1 0" -"i14" 4 "0 -1 0" -"h14" 4 "0 -1 0" -"g14" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"q16" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"h12" 5 "0 -1 0" -"h13" 5 "0 -1 0" -"i13" 5 "0 -1 0" -"j13" 5 "0 -1 0" -"j16" 4 "0 -1 0" -"i16" 4 "0 -1 0" -"h16" 4 "0 -1 0" -"g16" 4 "0 -1 0" -"f16" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"a20" 4 "0 -1 0" +"e13" 4 0 +"d13" 4 0 +"c13" 4 0 +"b13" 4 0 +"a13" 4 0 +"a12" 4 0 +"c10" 10 0 +"c12" 4 0 +"e12" 4 0 +"e14" 10 0 +"g12" 4 0 +"j14" 5 0 +"k13" 2 0 +"k12" 2 0 +"l12" 2 0 +"l13" 2 0 +"k11" 5 0 +"k14" 5 0 +"o12" 4 0 +"r8" 10 0 +"q12" 4 0 +"r12" 10 0 +"s12" 4 0 +"s10" 10 0 +"t11" 4 0 +"s11" 4 0 +"r11" 4 0 +"q11" 4 0 +"p11" 4 0 +"o11" 4 0 +"k15" 5 0 +"m11" 1 1 +"j12" 3 0 +"j11" 3 0 +"j10" 3 0 +"l14" 3 0 +"k16" 5 0 +"g11" 4 0 +"f11" 4 0 +"e11" 4 0 +"d11" 4 0 +"c11" 4 0 +"b11" 4 0 +"a11" 4 0 +"a10" 4 0 +"b10" 4 0 +"e10" 10 0 +"d10" 4 0 +"d12" 10 0 +"f10" 4 0 +"g10" 4 0 +"l16" 5 0 +"t12" 10 0 +"s14" 10 0 +"r16" 10 0 +"q14" 10 0 +"p12" 10 0 +"m16" 5 0 +"o10" 4 0 +"q10" 10 0 +"p10" 4 0 +"r10" 4 0 +"f12" 10 0 +"t10" 4 0 +"t9" 4 0 +"s9" 4 0 +"r9" 4 0 +"q9" 4 0 +"p9" 4 0 +"o9" 4 0 +"n16" 5 0 +"n15" 5 0 +"n14" 5 0 +"d16" 10 0 +"c14" 10 0 +"b12" 10 0 +"n13" 5 0 +"g9" 4 0 +"f9" 4 0 +"e9" 4 0 +"d9" 4 0 +"c9" 4 0 +"b9" 4 0 +"a9" 4 0 +"a8" 4 0 +"b8" 4 0 +"c8" 4 0 +"e8" 4 0 +"f8" 4 0 +"g8" 4 0 +"n12" 5 0 +"n11" 5 0 +"n10" 5 0 +"n9" 5 0 +"m9" 5 0 +"m8" 4 0 +"n8" 4 0 +"o8" 4 0 +"p8" 4 0 +"q8" 4 0 +"d8" 10 0 +"s8" 4 0 +"t8" 4 0 +"t7" 4 0 +"s7" 4 0 +"r7" 4 0 +"q7" 4 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"m7" 4 0 +"l7" 4 0 +"k7" 4 0 +"j7" 4 0 +"i7" 4 0 +"h7" 4 0 +"g7" 4 0 +"f7" 4 0 +"e7" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"a7" 4 0 +"a6" 4 0 +"b6" 4 0 +"c6" 4 0 +"d6" 4 0 +"e6" 4 0 +"f6" 4 0 +"g6" 4 0 +"h6" 4 0 +"i6" 4 0 +"j6" 4 0 +"k6" 4 0 +"l6" 4 0 +"m6" 4 0 +"n6" 4 0 +"o6" 4 0 +"p6" 4 0 +"q6" 4 0 +"r6" 4 0 +"s6" 4 0 +"t6" 4 0 +"t5" 4 0 +"s5" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"l5" 4 0 +"k5" 4 0 +"j5" 4 0 +"i5" 4 0 +"h5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"c5" 4 0 +"b5" 4 0 +"a5" 4 0 +"a4" 4 0 +"b4" 4 0 +"c4" 4 0 +"d4" 4 0 +"e4" 4 0 +"f4" 4 0 +"g4" 4 0 +"h4" 4 0 +"i4" 4 0 +"j4" 4 0 +"k4" 4 0 +"l4" 4 0 +"m4" 4 0 +"n4" 4 0 +"o4" 4 0 +"p4" 4 0 +"q4" 4 0 +"r4" 4 0 +"s4" 4 0 +"t4" 4 0 +"t3" 4 0 +"s3" 4 0 +"r3" 4 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"k3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"g3" 4 0 +"f3" 4 0 +"e3" 4 0 +"d3" 4 0 +"c3" 4 0 +"b3" 4 0 +"a3" 4 0 +"a2" 4 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"t2" 4 0 +"t1" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"a1" 4 0 +"t13" 4 0 +"s13" 4 0 +"r13" 4 0 +"q13" 4 0 +"p13" 4 0 +"o13" 4 0 +"l9" 5 0 +"l8" 5 0 +"k8" 5 0 +"j8" 5 0 +"g13" 4 0 +"f13" 4 0 +"f14" 4 0 +"d14" 4 0 +"b14" 4 0 +"a14" 4 0 +"a15" 4 0 +"b15" 4 0 +"c15" 4 0 +"d15" 4 0 +"e15" 4 0 +"f15" 4 0 +"g15" 4 0 +"h15" 4 0 +"i15" 4 0 +"j15" 4 0 +"i8" 5 0 +"h8" 5 0 +"o15" 4 0 +"p15" 4 0 +"q15" 4 0 +"r15" 4 0 +"s15" 4 0 +"t15" 4 0 +"t14" 4 0 +"r14" 4 0 +"p14" 4 0 +"o14" 4 0 +"h9" 5 0 +"h10" 5 0 +"h11" 5 0 +"i14" 4 0 +"h14" 4 0 +"g14" 4 0 +"t16" 4 0 +"s16" 4 0 +"q16" 4 0 +"p16" 4 0 +"o16" 4 0 +"h12" 5 0 +"h13" 5 0 +"i13" 5 0 +"j13" 5 0 +"j16" 4 0 +"i16" 4 0 +"h16" 4 0 +"g16" 4 0 +"f16" 4 0 +"e16" 4 0 +"c16" 4 0 +"b16" 4 0 +"a16" 4 0 +"a17" 4 0 +"b17" 4 0 +"c17" 4 0 +"d17" 4 0 +"e17" 4 0 +"f17" 4 0 +"g17" 4 0 +"h17" 4 0 +"i17" 4 0 +"j17" 4 0 +"k17" 4 0 +"l17" 4 0 +"m17" 4 0 +"n17" 4 0 +"o17" 4 0 +"p17" 4 0 +"q17" 4 0 +"r17" 4 0 +"s17" 4 0 +"t17" 4 0 +"t18" 4 0 +"s18" 4 0 +"r18" 4 0 +"q18" 4 0 +"p18" 4 0 +"o18" 4 0 +"n18" 4 0 +"m18" 4 0 +"l18" 4 0 +"k18" 4 0 +"j18" 4 0 +"i18" 4 0 +"h18" 4 0 +"g18" 4 0 +"f18" 4 0 +"e18" 4 0 +"d18" 4 0 +"c18" 4 0 +"b18" 4 0 +"a18" 4 0 +"a19" 4 0 +"b19" 4 0 +"c19" 4 0 +"d19" 4 0 +"e19" 4 0 +"f19" 4 0 +"g19" 4 0 +"h19" 4 0 +"i19" 4 0 +"j19" 4 0 +"k19" 4 0 +"l19" 4 0 +"m19" 4 0 +"n19" 4 0 +"o19" 4 0 +"p19" 4 0 +"q19" 4 0 +"r19" 4 0 +"s19" 4 0 +"t19" 4 0 +"t20" 4 0 +"s20" 4 0 +"r20" 4 0 +"q20" 4 0 +"p20" 4 0 +"o20" 4 0 +"n20" 4 0 +"m20" 4 0 +"l20" 4 0 +"k20" 4 0 +"j20" 4 0 +"i20" 4 0 +"h20" 4 0 +"g20" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"a20" 4 0 diff --git a/minigames/bulldozer/storage_level11.txt b/minigames/bulldozer/storage_level11.txt index 5cb5462355..e8ecfcaf50 100644 --- a/minigames/bulldozer/storage_level11.txt +++ b/minigames/bulldozer/storage_level11.txt @@ -1,355 +1,355 @@ // bulldozer storage "level11" last updated 11-06-2016 15:03:24 nextlevel = "level12" -"e6" 5 "0 -1 0" -"f6" 5 "0 -1 0" -"g6" 5 "0 -1 0" -"h6" 5 "0 -1 0" -"i6" 5 "0 -1 0" -"j6" 5 "0 -1 0" -"k6" 5 "0 -1 0" -"l6" 5 "0 -1 0" -"m6" 5 "0 -1 0" -"n6" 5 "0 -1 0" -"o6" 5 "0 -1 0" -"p6" 5 "0 -1 0" -"e7" 5 "0 -1 0" -"e8" 5 "0 -1 0" -"e9" 5 "0 -1 0" -"d9" 5 "0 -1 0" -"d10" 5 "0 -1 0" -"d11" 5 "0 -1 0" -"d12" 5 "0 -1 0" -"d13" 5 "0 -1 0" -"d14" 5 "0 -1 0" -"d15" 5 "0 -1 0" -"e15" 5 "0 -1 0" -"f15" 5 "0 -1 0" -"g15" 5 "0 -1 0" -"h16" 5 "0 -1 0" -"g16" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"j16" 5 "0 -1 0" -"k16" 5 "0 -1 0" -"l16" 5 "0 -1 0" -"m16" 5 "0 -1 0" -"m15" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"m13" 5 "0 -1 0" -"n13" 5 "0 -1 0" -"n12" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"o9" 5 "0 -1 0" -"p9" 5 "0 -1 0" -"p8" 5 "0 -1 0" -"p7" 5 "0 -1 0" -"a20" 10 "0 -1 0" -"a19" 10 "0 -1 0" -"a18" 10 "0 -1 0" -"a17" 10 "0 -1 0" -"a16" 10 "0 -1 0" -"a15" 10 "0 -1 0" -"a14" 10 "0 -1 0" -"a13" 10 "0 -1 0" -"a12" 10 "0 -1 0" -"a11" 10 "0 -1 0" -"a10" 10 "0 -1 0" -"a9" 10 "0 -1 0" -"a8" 10 "0 -1 0" -"a7" 10 "0 -1 0" -"a6" 10 "0 -1 0" -"a5" 10 "0 -1 0" -"a4" 10 "0 -1 0" -"a3" 10 "0 -1 0" -"a2" 10 "0 -1 0" -"a1" 10 "0 -1 0" -"t20" 10 "0 -1 0" -"t19" 10 "0 -1 0" -"t18" 10 "0 -1 0" -"t17" 10 "0 -1 0" -"t16" 10 "0 -1 0" -"t15" 10 "0 -1 0" -"t14" 10 "0 -1 0" -"t12" 10 "0 -1 0" -"t11" 10 "0 -1 0" -"t10" 10 "0 -1 0" -"t9" 10 "0 -1 0" -"t8" 10 "0 -1 0" -"t7" 10 "0 -1 0" -"t6" 10 "0 -1 0" -"t5" 10 "0 -1 0" -"t4" 10 "0 -1 0" -"t3" 10 "0 -1 0" -"t2" 10 "0 -1 0" -"t1" 10 "0 -1 0" -"t13" 10 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"q9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"q10" 4 "0 -1 0" -"p10" 4 "0 -1 0" -"o10" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"p11" 4 "0 -1 0" -"q11" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"q12" 4 "0 -1 0" -"p12" 4 "0 -1 0" -"o12" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"p13" 4 "0 -1 0" -"q13" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"s14" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"q14" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"o14" 4 "0 -1 0" -"n14" 4 "0 -1 0" -"n15" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"p15" 4 "0 -1 0" -"q15" 4 "0 -1 0" -"r15" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"q16" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"d16" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"f16" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"c14" 4 "0 -1 0" -"c13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"b12" 4 "0 -1 0" -"c12" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"c10" 4 "0 -1 0" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"c8" 4 "0 -1 0" -"d8" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"f13" 5 "0 -1 0" -"f12" 5 "0 -1 0" -"f11" 5 "0 -1 0" -"g10" 5 "0 -1 0" -"h13" 5 "0 -1 0" -"i14" 5 "0 -1 0" -"j14" 5 "0 -1 0" -"k14" 5 "0 -1 0" -"k12" 5 "0 -1 0" -"l11" 5 "0 -1 0" -"l10" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"l7" 5 "0 -1 0" -"k7" 5 "0 -1 0" -"g8" 5 "0 -1 0" -"i8" 5 "0 -1 0" -"h8" 5 "0 -1 0" -"j9" 5 "0 -1 0" -"l8" 1 "0 -1 0" -"j8" 2 "0 -1 0" -"i11" 2 "0 -1 0" -"f10" 2 "0 -1 0" -"h14" 2 "0 -1 0" -"l12" 2 "0 -1 0" -"h10" 3 "0 -1 0" -"i11" 3 "0 -1 0" -"h12" 3 "0 -1 0" -"j12" 3 "0 -1 0" -"j10" 3 "0 -1 0" +"e6" 5 0 +"f6" 5 0 +"g6" 5 0 +"h6" 5 0 +"i6" 5 0 +"j6" 5 0 +"k6" 5 0 +"l6" 5 0 +"m6" 5 0 +"n6" 5 0 +"o6" 5 0 +"p6" 5 0 +"e7" 5 0 +"e8" 5 0 +"e9" 5 0 +"d9" 5 0 +"d10" 5 0 +"d11" 5 0 +"d12" 5 0 +"d13" 5 0 +"d14" 5 0 +"d15" 5 0 +"e15" 5 0 +"f15" 5 0 +"g15" 5 0 +"h16" 5 0 +"g16" 5 0 +"i16" 5 0 +"j16" 5 0 +"k16" 5 0 +"l16" 5 0 +"m16" 5 0 +"m15" 5 0 +"m14" 5 0 +"m13" 5 0 +"n13" 5 0 +"n12" 5 0 +"n11" 5 0 +"n10" 5 0 +"n9" 5 0 +"o9" 5 0 +"p9" 5 0 +"p8" 5 0 +"p7" 5 0 +"a20" 10 0 +"a19" 10 0 +"a18" 10 0 +"a17" 10 0 +"a16" 10 0 +"a15" 10 0 +"a14" 10 0 +"a13" 10 0 +"a12" 10 0 +"a11" 10 0 +"a10" 10 0 +"a9" 10 0 +"a8" 10 0 +"a7" 10 0 +"a6" 10 0 +"a5" 10 0 +"a4" 10 0 +"a3" 10 0 +"a2" 10 0 +"a1" 10 0 +"t20" 10 0 +"t19" 10 0 +"t18" 10 0 +"t17" 10 0 +"t16" 10 0 +"t15" 10 0 +"t14" 10 0 +"t12" 10 0 +"t11" 10 0 +"t10" 10 0 +"t9" 10 0 +"t8" 10 0 +"t7" 10 0 +"t6" 10 0 +"t5" 10 0 +"t4" 10 0 +"t3" 10 0 +"t2" 10 0 +"t1" 10 0 +"t13" 10 0 +"e3" 4 0 +"d3" 4 0 +"c3" 4 0 +"b3" 4 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"s3" 4 0 +"r3" 4 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"k3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"g3" 4 0 +"f3" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"b4" 4 0 +"b5" 4 0 +"c5" 4 0 +"d5" 4 0 +"e5" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"p5" 4 0 +"q5" 4 0 +"r5" 4 0 +"s5" 4 0 +"s4" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"s6" 4 0 +"r6" 4 0 +"q6" 4 0 +"q7" 4 0 +"r7" 4 0 +"s7" 4 0 +"s8" 4 0 +"r8" 4 0 +"q8" 4 0 +"q9" 4 0 +"r9" 4 0 +"s9" 4 0 +"s10" 4 0 +"r10" 4 0 +"q10" 4 0 +"p10" 4 0 +"o10" 4 0 +"o11" 4 0 +"p11" 4 0 +"q11" 4 0 +"r11" 4 0 +"s11" 4 0 +"s12" 4 0 +"r12" 4 0 +"q12" 4 0 +"p12" 4 0 +"o12" 4 0 +"o13" 4 0 +"p13" 4 0 +"q13" 4 0 +"r13" 4 0 +"s13" 4 0 +"s14" 4 0 +"r14" 4 0 +"q14" 4 0 +"p14" 4 0 +"o14" 4 0 +"n14" 4 0 +"n15" 4 0 +"o15" 4 0 +"p15" 4 0 +"q15" 4 0 +"r15" 4 0 +"s15" 4 0 +"s16" 4 0 +"r16" 4 0 +"q16" 4 0 +"p16" 4 0 +"o16" 4 0 +"n16" 4 0 +"n17" 4 0 +"m17" 4 0 +"l17" 4 0 +"k17" 4 0 +"j17" 4 0 +"i17" 4 0 +"h17" 4 0 +"g17" 4 0 +"f17" 4 0 +"e17" 4 0 +"d17" 4 0 +"c17" 4 0 +"b17" 4 0 +"b16" 4 0 +"c16" 4 0 +"d16" 4 0 +"e16" 4 0 +"f16" 4 0 +"c15" 4 0 +"b15" 4 0 +"b14" 4 0 +"c14" 4 0 +"c13" 4 0 +"b13" 4 0 +"b12" 4 0 +"c12" 4 0 +"c11" 4 0 +"b11" 4 0 +"b10" 4 0 +"c10" 4 0 +"c9" 4 0 +"b9" 4 0 +"b8" 4 0 +"c8" 4 0 +"d8" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"b6" 4 0 +"c6" 4 0 +"d6" 4 0 +"b18" 4 0 +"c18" 4 0 +"d18" 4 0 +"e18" 4 0 +"f18" 4 0 +"g18" 4 0 +"h18" 4 0 +"i18" 4 0 +"j18" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"s18" 4 0 +"s17" 4 0 +"r17" 4 0 +"q17" 4 0 +"p17" 4 0 +"o17" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"b19" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"f20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"f13" 5 0 +"f12" 5 0 +"f11" 5 0 +"g10" 5 0 +"h13" 5 0 +"i14" 5 0 +"j14" 5 0 +"k14" 5 0 +"k12" 5 0 +"l11" 5 0 +"l10" 5 0 +"l9" 5 0 +"l7" 5 0 +"k7" 5 0 +"g8" 5 0 +"i8" 5 0 +"h8" 5 0 +"j9" 5 0 +"l8" 1 1 +"j8" 2 0 +"i11" 2 0 +"f10" 2 0 +"h14" 2 0 +"l12" 2 0 +"h10" 3 0 +"i11" 3 0 +"h12" 3 0 +"j12" 3 0 +"j10" 3 0 diff --git a/minigames/bulldozer/storage_level12.txt b/minigames/bulldozer/storage_level12.txt index f7162fb046..ccd163583b 100644 --- a/minigames/bulldozer/storage_level12.txt +++ b/minigames/bulldozer/storage_level12.txt @@ -1,323 +1,323 @@ // bulldozer storage "level12" last updated 11-06-2016 15:11:43 nextlevel = "level13" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"t7" 11 "0 -1 0" -"t17" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"t12" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"t10" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"a18" 11 "0 -1 0" -"a17" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"a11" 4 "0 -1 0" -"a10" 4 "0 -1 0" -"a9" 4 "0 -1 0" -"a8" 4 "0 -1 0" -"d17" 5 "0 -1 0" -"t8" 4 "0 -1 0" -"a7" 11 "0 -1 0" -"s7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"a20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"t18" 11 "0 -1 0" -"d16" 5 "0 -1 0" -"e13" 5 "0 -1 0" -"d15" 5 "0 -1 0" -"d14" 5 "0 -1 0" -"d13" 5 "0 -1 0" -"e12" 5 "0 -1 0" -"g11" 5 "0 -1 0" -"e11" 5 "0 -1 0" -"g12" 5 "0 -1 0" -"g13" 5 "0 -1 0" -"g14" 5 "0 -1 0" -"h14" 5 "0 -1 0" -"i14" 5 "0 -1 0" -"i13" 5 "0 -1 0" -"i12" 5 "0 -1 0" -"i11" 5 "0 -1 0" -"g10" 5 "0 -1 0" -"i10" 5 "0 -1 0" -"h10" 5 "0 -1 0" -"h11" 10 "0 -1 0" -"h12" 10 "0 -1 0" -"h13" 10 "0 -1 0" -"d8" 5 "0 -1 0" -"d9" 5 "0 -1 0" -"e17" 5 "0 -1 0" -"f17" 5 "0 -1 0" -"g17" 5 "0 -1 0" -"h17" 5 "0 -1 0" -"i17" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"m17" 5 "0 -1 0" -"n17" 5 "0 -1 0" -"o17" 5 "0 -1 0" -"l15" 5 "0 -1 0" -"m15" 5 "0 -1 0" -"o15" 5 "0 -1 0" -"n15" 5 "0 -1 0" -"p15" 5 "0 -1 0" -"p14" 5 "0 -1 0" -"q14" 5 "0 -1 0" -"r14" 5 "0 -1 0" -"s14" 8 "0 -1 0" -"r13" 8 "0 -1 0" -"q12" 8 "0 -1 0" -"p11" 8 "0 -1 0" -"o10" 8 "0 -1 0" -"n9" 8 "0 -1 0" -"m8" 8 "0 -1 0" -"m9" 5 "0 -1 0" -"m11" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"m13" 5 "0 -1 0" -"q9" 4 "0 -1 0" -"p9" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"q10" 4 "0 -1 0" -"p10" 4 "0 -1 0" -"q11" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"f15" 1 "0 -1 0" -"q16" 2 "0 -1 0" -"q15" 2 "0 -1 0" -"r15" 2 "0 -1 0" -"r16" 2 "0 -1 0" -"s16" 2 "0 -1 0" -"s15" 2 "0 -1 0" -"s17" 2 "0 -1 0" -"b9" 2 "0 -1 0" -"c8" 2 "0 -1 0" -"n10" 2 "0 -1 0" -"o11" 2 "0 -1 0" -"p12" 2 "0 -1 0" -"q13" 2 "0 -1 0" -"c15" 3 "0 -1 0" -"b11" 3 "0 -1 0" -"c13" 3 "0 -1 0" -"d11" 3 "0 -1 0" -"c9" 3 "0 -1 0" -"j16" 3 "0 -1 0" -"j14" 3 "0 -1 0" -"k13" 3 "0 -1 0" -"j12" 3 "0 -1 0" -"k11" 3 "0 -1 0" -"j10" 3 "0 -1 0" -"n16" 3 "0 -1 0" -"o13" 3 "0 -1 0" +"b18" 4 0 +"c18" 4 0 +"d18" 4 0 +"e18" 4 0 +"f18" 4 0 +"g18" 4 0 +"h18" 4 0 +"i18" 4 0 +"j18" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"s18" 4 0 +"t7" 11 0 +"t17" 4 0 +"t16" 4 0 +"t15" 4 0 +"t14" 4 0 +"t13" 4 0 +"t12" 4 0 +"t11" 4 0 +"t10" 4 0 +"t9" 4 0 +"b7" 4 0 +"c7" 4 0 +"a18" 11 0 +"a17" 4 0 +"a16" 4 0 +"a15" 4 0 +"a14" 4 0 +"a13" 4 0 +"a12" 4 0 +"a11" 4 0 +"a10" 4 0 +"a9" 4 0 +"a8" 4 0 +"d17" 5 0 +"t8" 4 0 +"a7" 11 0 +"s7" 4 0 +"r7" 4 0 +"q7" 4 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"l7" 4 0 +"k7" 4 0 +"j7" 4 0 +"i7" 4 0 +"h7" 4 0 +"g7" 4 0 +"f7" 4 0 +"e7" 4 0 +"d7" 4 0 +"m7" 4 0 +"c5" 4 0 +"b5" 4 0 +"a5" 4 0 +"a4" 4 0 +"b4" 4 0 +"c4" 4 0 +"d4" 4 0 +"e4" 4 0 +"f4" 4 0 +"g4" 4 0 +"h4" 4 0 +"i4" 4 0 +"j4" 4 0 +"k4" 4 0 +"l4" 4 0 +"m4" 4 0 +"n4" 4 0 +"o4" 4 0 +"p4" 4 0 +"q4" 4 0 +"r4" 4 0 +"s4" 4 0 +"t4" 4 0 +"t3" 4 0 +"s3" 4 0 +"r3" 4 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"k3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"g3" 4 0 +"f3" 4 0 +"e3" 4 0 +"d3" 4 0 +"c3" 4 0 +"b3" 4 0 +"a3" 4 0 +"a2" 4 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"t2" 4 0 +"t1" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"a1" 4 0 +"t5" 4 0 +"s5" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"l5" 4 0 +"k5" 4 0 +"j5" 4 0 +"i5" 4 0 +"h5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"d6" 4 0 +"c6" 4 0 +"b6" 4 0 +"a6" 4 0 +"e6" 4 0 +"f6" 4 0 +"g6" 4 0 +"h6" 4 0 +"i6" 4 0 +"j6" 4 0 +"k6" 4 0 +"l6" 4 0 +"m6" 4 0 +"n6" 4 0 +"o6" 4 0 +"p6" 4 0 +"q6" 4 0 +"r6" 4 0 +"s6" 4 0 +"t6" 4 0 +"b19" 4 0 +"a19" 4 0 +"a20" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"f20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"t20" 4 0 +"t19" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"t18" 11 0 +"d16" 5 0 +"e13" 5 0 +"d15" 5 0 +"d14" 5 0 +"d13" 5 0 +"e12" 5 0 +"g11" 5 0 +"e11" 5 0 +"g12" 5 0 +"g13" 5 0 +"g14" 5 0 +"h14" 5 0 +"i14" 5 0 +"i13" 5 0 +"i12" 5 0 +"i11" 5 0 +"g10" 5 0 +"i10" 5 0 +"h10" 5 0 +"h11" 10 0 +"h12" 10 0 +"h13" 10 0 +"d8" 5 0 +"d9" 5 0 +"e17" 5 0 +"f17" 5 0 +"g17" 5 0 +"h17" 5 0 +"i17" 5 0 +"i16" 5 0 +"m17" 5 0 +"n17" 5 0 +"o17" 5 0 +"l15" 5 0 +"m15" 5 0 +"o15" 5 0 +"n15" 5 0 +"p15" 5 0 +"p14" 5 0 +"q14" 5 0 +"r14" 5 0 +"s14" 8 0 +"r13" 8 0 +"q12" 8 0 +"p11" 8 0 +"o10" 8 0 +"n9" 8 0 +"m8" 8 0 +"m9" 5 0 +"m11" 5 0 +"m14" 5 0 +"m13" 5 0 +"q9" 4 0 +"p9" 4 0 +"o9" 4 0 +"o8" 4 0 +"n8" 4 0 +"p8" 4 0 +"q8" 4 0 +"r8" 4 0 +"s8" 4 0 +"s9" 4 0 +"r9" 4 0 +"r10" 4 0 +"q10" 4 0 +"p10" 4 0 +"q11" 4 0 +"r11" 4 0 +"s11" 4 0 +"s10" 4 0 +"s12" 4 0 +"r12" 4 0 +"s13" 4 0 +"f15" 1 1 +"q16" 2 0 +"q15" 2 0 +"r15" 2 0 +"r16" 2 0 +"s16" 2 0 +"s15" 2 0 +"s17" 2 0 +"b9" 2 0 +"c8" 2 0 +"n10" 2 0 +"o11" 2 0 +"p12" 2 0 +"q13" 2 0 +"c15" 3 0 +"b11" 3 0 +"c13" 3 0 +"d11" 3 0 +"c9" 3 0 +"j16" 3 0 +"j14" 3 0 +"k13" 3 0 +"j12" 3 0 +"k11" 3 0 +"j10" 3 0 +"n16" 3 0 +"o13" 3 0 diff --git a/minigames/bulldozer/storage_level13.txt b/minigames/bulldozer/storage_level13.txt index 54f7499496..c5e7f38c47 100644 --- a/minigames/bulldozer/storage_level13.txt +++ b/minigames/bulldozer/storage_level13.txt @@ -1,359 +1,359 @@ // bulldozer storage "level13" last updated 11-06-2016 15:20:23 nextlevel = "level14" -"a10" 5 "0 -1 0" -"a8" 5 "0 -1 0" -"a9" 5 "0 -1 0" -"a11" 5 "0 -1 0" -"b11" 5 "0 -1 0" -"d13" 5 "0 -1 0" -"c11" 5 "0 -1 0" -"c12" 5 "0 -1 0" -"c13" 5 "0 -1 0" -"e13" 5 "0 -1 0" -"e14" 5 "0 -1 0" -"e15" 5 "0 -1 0" -"e16" 5 "0 -1 0" -"f16" 5 "0 -1 0" -"g16" 5 "0 -1 0" -"h16" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"i15" 5 "0 -1 0" -"i14" 5 "0 -1 0" -"i13" 5 "0 -1 0" -"j13" 5 "0 -1 0" -"j12" 5 "0 -1 0" -"j11" 5 "0 -1 0" -"j10" 5 "0 -1 0" -"k10" 5 "0 -1 0" -"l10" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"m10" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"o11" 5 "0 -1 0" -"p11" 5 "0 -1 0" -"q11" 5 "0 -1 0" -"r11" 5 "0 -1 0" -"s11" 5 "0 -1 0" -"s10" 5 "0 -1 0" -"s8" 5 "0 -1 0" -"s9" 5 "0 -1 0" -"s7" 5 "0 -1 0" -"r7" 5 "0 -1 0" -"q7" 5 "0 -1 0" -"p7" 5 "0 -1 0" -"o7" 5 "0 -1 0" -"n7" 5 "0 -1 0" -"m7" 5 "0 -1 0" -"l7" 5 "0 -1 0" -"k7" 5 "0 -1 0" -"k8" 5 "0 -1 0" -"m8" 5 "0 -1 0" -"n8" 5 "0 -1 0" -"k6" 5 "0 -1 0" -"j6" 5 "0 -1 0" -"i6" 5 "0 -1 0" -"b8" 5 "0 -1 0" -"c8" 5 "0 -1 0" -"d8" 5 "0 -1 0" -"e8" 5 "0 -1 0" -"e7" 5 "0 -1 0" -"e6" 5 "0 -1 0" -"f6" 5 "0 -1 0" -"g6" 5 "0 -1 0" -"h6" 5 "0 -1 0" -"e11" 8 "0 -1 0" -"e10" 8 "0 -1 0" -"g11" 8 "0 -1 0" -"g10" 8 "0 -1 0" -"h10" 8 "0 -1 0" -"h11" 8 "0 -1 0" -"g8" 8 "0 -1 0" -"h8" 8 "0 -1 0" -"i8" 8 "0 -1 0" -"b15" 8 "0 -1 0" -"o12" 11 "0 -1 0" -"n13" 11 "0 -1 0" -"m14" 11 "0 -1 0" -"l15" 11 "0 -1 0" -"k16" 11 "0 -1 0" -"m16" 11 "0 -1 0" -"n15" 11 "0 -1 0" -"o16" 11 "0 -1 0" -"p15" 11 "0 -1 0" -"q16" 11 "0 -1 0" -"r15" 11 "0 -1 0" -"s16" 11 "0 -1 0" -"q14" 11 "0 -1 0" -"p13" 11 "0 -1 0" -"o14" 11 "0 -1 0" -"j19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"d16" 4 "0 -1 0" -"d15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"c14" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"b12" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"d14" 4 "0 -1 0" -"j16" 4 "0 -1 0" -"j15" 4 "0 -1 0" -"k15" 4 "0 -1 0" -"k14" 4 "0 -1 0" -"j14" 4 "0 -1 0" -"l14" 4 "0 -1 0" -"l13" 4 "0 -1 0" -"k13" 4 "0 -1 0" -"k12" 4 "0 -1 0" -"l12" 4 "0 -1 0" -"m12" 4 "0 -1 0" -"n12" 4 "0 -1 0" -"m11" 4 "0 -1 0" -"l11" 4 "0 -1 0" -"k11" 4 "0 -1 0" -"m13" 4 "0 -1 0" -"l16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"s14" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"q13" 4 "0 -1 0" -"q12" 4 "0 -1 0" -"p12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"t12" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"t10" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"t8" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"a7" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"a20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"m15" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"q15" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"n14" 4 "0 -1 0" -"l8" 1 "0 -1 0" -"r10" 2 "0 -1 0" -"r9" 2 "0 -1 0" -"r8" 2 "0 -1 0" -"q8" 2 "0 -1 0" -"q9" 2 "0 -1 0" -"q10" 2 "0 -1 0" -"c9" 3 "0 -1 0" -"f9" 3 "0 -1 0" -"f12" 3 "0 -1 0" -"f14" 3 "0 -1 0" -"h13" 3 "0 -1 0" -"h12" 3 "0 -1 0" +"a10" 5 0 +"a8" 5 0 +"a9" 5 0 +"a11" 5 0 +"b11" 5 0 +"d13" 5 0 +"c11" 5 0 +"c12" 5 0 +"c13" 5 0 +"e13" 5 0 +"e14" 5 0 +"e15" 5 0 +"e16" 5 0 +"f16" 5 0 +"g16" 5 0 +"h16" 5 0 +"i16" 5 0 +"i15" 5 0 +"i14" 5 0 +"i13" 5 0 +"j13" 5 0 +"j12" 5 0 +"j11" 5 0 +"j10" 5 0 +"k10" 5 0 +"l10" 5 0 +"n10" 5 0 +"m10" 5 0 +"n11" 5 0 +"o11" 5 0 +"p11" 5 0 +"q11" 5 0 +"r11" 5 0 +"s11" 5 0 +"s10" 5 0 +"s8" 5 0 +"s9" 5 0 +"s7" 5 0 +"r7" 5 0 +"q7" 5 0 +"p7" 5 0 +"o7" 5 0 +"n7" 5 0 +"m7" 5 0 +"l7" 5 0 +"k7" 5 0 +"k8" 5 0 +"m8" 5 0 +"n8" 5 0 +"k6" 5 0 +"j6" 5 0 +"i6" 5 0 +"b8" 5 0 +"c8" 5 0 +"d8" 5 0 +"e8" 5 0 +"e7" 5 0 +"e6" 5 0 +"f6" 5 0 +"g6" 5 0 +"h6" 5 0 +"e11" 8 0 +"e10" 8 0 +"g11" 8 0 +"g10" 8 0 +"h10" 8 0 +"h11" 8 0 +"g8" 8 0 +"h8" 8 0 +"i8" 8 0 +"b15" 8 0 +"o12" 11 0 +"n13" 11 0 +"m14" 11 0 +"l15" 11 0 +"k16" 11 0 +"m16" 11 0 +"n15" 11 0 +"o16" 11 0 +"p15" 11 0 +"q16" 11 0 +"r15" 11 0 +"s16" 11 0 +"q14" 11 0 +"p13" 11 0 +"o14" 11 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"b19" 4 0 +"a19" 4 0 +"a18" 4 0 +"b18" 4 0 +"c18" 4 0 +"d18" 4 0 +"e18" 4 0 +"f18" 4 0 +"g18" 4 0 +"h18" 4 0 +"i18" 4 0 +"j18" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"s18" 4 0 +"t18" 4 0 +"t17" 4 0 +"s17" 4 0 +"r17" 4 0 +"q17" 4 0 +"p17" 4 0 +"o17" 4 0 +"n17" 4 0 +"m17" 4 0 +"l17" 4 0 +"k17" 4 0 +"j17" 4 0 +"i17" 4 0 +"h17" 4 0 +"g17" 4 0 +"f17" 4 0 +"e17" 4 0 +"d17" 4 0 +"c17" 4 0 +"b17" 4 0 +"a17" 4 0 +"a16" 4 0 +"b16" 4 0 +"c16" 4 0 +"d16" 4 0 +"d15" 4 0 +"c15" 4 0 +"c14" 4 0 +"b14" 4 0 +"a14" 4 0 +"a13" 4 0 +"b13" 4 0 +"b12" 4 0 +"a12" 4 0 +"a15" 4 0 +"d14" 4 0 +"j16" 4 0 +"j15" 4 0 +"k15" 4 0 +"k14" 4 0 +"j14" 4 0 +"l14" 4 0 +"l13" 4 0 +"k13" 4 0 +"k12" 4 0 +"l12" 4 0 +"m12" 4 0 +"n12" 4 0 +"m11" 4 0 +"l11" 4 0 +"k11" 4 0 +"m13" 4 0 +"l16" 4 0 +"n16" 4 0 +"p16" 4 0 +"r16" 4 0 +"t16" 4 0 +"t15" 4 0 +"s15" 4 0 +"s14" 4 0 +"r14" 4 0 +"r13" 4 0 +"q13" 4 0 +"q12" 4 0 +"p12" 4 0 +"r12" 4 0 +"s12" 4 0 +"t12" 4 0 +"t11" 4 0 +"t10" 4 0 +"t9" 4 0 +"t8" 4 0 +"t7" 4 0 +"t6" 4 0 +"s6" 4 0 +"r6" 4 0 +"q6" 4 0 +"p6" 4 0 +"o6" 4 0 +"n6" 4 0 +"m6" 4 0 +"l6" 4 0 +"l5" 4 0 +"k5" 4 0 +"j5" 4 0 +"i5" 4 0 +"h5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"c5" 4 0 +"b5" 4 0 +"a5" 4 0 +"a4" 4 0 +"b4" 4 0 +"c4" 4 0 +"d4" 4 0 +"e4" 4 0 +"f4" 4 0 +"g4" 4 0 +"h4" 4 0 +"i4" 4 0 +"j4" 4 0 +"k4" 4 0 +"l4" 4 0 +"m4" 4 0 +"n4" 4 0 +"o4" 4 0 +"p4" 4 0 +"q4" 4 0 +"r4" 4 0 +"s4" 4 0 +"t4" 4 0 +"t3" 4 0 +"s3" 4 0 +"r3" 4 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"k3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"g3" 4 0 +"f3" 4 0 +"e3" 4 0 +"d3" 4 0 +"c3" 4 0 +"b3" 4 0 +"a3" 4 0 +"a2" 4 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"t2" 4 0 +"t1" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"a1" 4 0 +"t5" 4 0 +"s5" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"a6" 4 0 +"b6" 4 0 +"c6" 4 0 +"d6" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"a7" 4 0 +"t13" 4 0 +"s13" 4 0 +"t14" 4 0 +"t19" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"k20" 4 0 +"j20" 4 0 +"i20" 4 0 +"h20" 4 0 +"g20" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"a20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"t20" 4 0 +"m15" 4 0 +"o15" 4 0 +"q15" 4 0 +"p14" 4 0 +"o13" 4 0 +"n14" 4 0 +"l8" 1 1 +"r10" 2 0 +"r9" 2 0 +"r8" 2 0 +"q8" 2 0 +"q9" 2 0 +"q10" 2 0 +"c9" 3 0 +"f9" 3 0 +"f12" 3 0 +"f14" 3 0 +"h13" 3 0 +"h12" 3 0 diff --git a/minigames/bulldozer/storage_level14.txt b/minigames/bulldozer/storage_level14.txt index 540c31fe3d..c7ab1e0c2d 100644 --- a/minigames/bulldozer/storage_level14.txt +++ b/minigames/bulldozer/storage_level14.txt @@ -1,383 +1,383 @@ // bulldozer storage "level14" last updated 11-06-2016 15:24:43 nextlevel = "level15" -"a20" 10 "0 -1 0" -"a19" 10 "0 -1 0" -"a18" 10 "0 -1 0" -"a17" 10 "0 -1 0" -"a16" 10 "0 -1 0" -"a15" 10 "0 -1 0" -"a14" 10 "0 -1 0" -"a13" 10 "0 -1 0" -"a12" 10 "0 -1 0" -"a11" 10 "0 -1 0" -"a10" 10 "0 -1 0" -"a9" 10 "0 -1 0" -"a8" 10 "0 -1 0" -"a7" 10 "0 -1 0" -"a6" 10 "0 -1 0" -"a5" 10 "0 -1 0" -"a4" 10 "0 -1 0" -"a3" 10 "0 -1 0" -"a2" 10 "0 -1 0" -"a1" 10 "0 -1 0" -"t20" 10 "0 -1 0" -"t19" 10 "0 -1 0" -"t18" 10 "0 -1 0" -"t17" 10 "0 -1 0" -"t16" 10 "0 -1 0" -"t15" 10 "0 -1 0" -"t13" 10 "0 -1 0" -"t12" 10 "0 -1 0" -"t11" 10 "0 -1 0" -"t10" 10 "0 -1 0" -"t9" 10 "0 -1 0" -"t8" 10 "0 -1 0" -"t7" 10 "0 -1 0" -"t6" 10 "0 -1 0" -"t5" 10 "0 -1 0" -"t4" 10 "0 -1 0" -"t2" 10 "0 -1 0" -"t1" 10 "0 -1 0" -"t3" 10 "0 -1 0" -"t14" 10 "0 -1 0" -"c17" 5 "0 -1 0" -"d17" 5 "0 -1 0" -"d16" 5 "0 -1 0" -"c16" 5 "0 -1 0" -"c15" 5 "0 -1 0" -"c14" 5 "0 -1 0" -"c13" 5 "0 -1 0" -"c12" 5 "0 -1 0" -"c11" 5 "0 -1 0" -"c10" 5 "0 -1 0" -"c9" 5 "0 -1 0" -"c8" 5 "0 -1 0" -"d15" 5 "0 -1 0" -"d14" 5 "0 -1 0" -"d13" 5 "0 -1 0" -"d12" 5 "0 -1 0" -"d11" 5 "0 -1 0" -"d10" 5 "0 -1 0" -"d8" 5 "0 -1 0" -"f9" 5 "0 -1 0" -"d9" 5 "0 -1 0" -"f10" 5 "0 -1 0" -"f11" 5 "0 -1 0" -"g11" 5 "0 -1 0" -"h11" 5 "0 -1 0" -"h12" 5 "0 -1 0" -"h13" 5 "0 -1 0" -"h14" 5 "0 -1 0" -"h15" 5 "0 -1 0" -"i15" 5 "0 -1 0" -"j16" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"k16" 5 "0 -1 0" -"l16" 5 "0 -1 0" -"m16" 5 "0 -1 0" -"n16" 5 "0 -1 0" -"o16" 5 "0 -1 0" -"o15" 5 "0 -1 0" -"o14" 5 "0 -1 0" -"o13" 5 "0 -1 0" -"o12" 5 "0 -1 0" -"o11" 5 "0 -1 0" -"q17" 5 "0 -1 0" -"q16" 5 "0 -1 0" -"q15" 5 "0 -1 0" -"q14" 5 "0 -1 0" -"q13" 5 "0 -1 0" -"q11" 5 "0 -1 0" -"q10" 5 "0 -1 0" -"q9" 5 "0 -1 0" -"q8" 5 "0 -1 0" -"r8" 5 "0 -1 0" -"q12" 5 "0 -1 0" -"r9" 5 "0 -1 0" -"r10" 5 "0 -1 0" -"r11" 5 "0 -1 0" -"r13" 5 "0 -1 0" -"r14" 5 "0 -1 0" -"r15" 5 "0 -1 0" -"r16" 5 "0 -1 0" -"r17" 5 "0 -1 0" -"r12" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"k9" 5 "0 -1 0" -"i9" 5 "0 -1 0" -"h9" 5 "0 -1 0" -"g9" 5 "0 -1 0" -"j9" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"l11" 5 "0 -1 0" -"l12" 5 "0 -1 0" -"k12" 5 "0 -1 0" -"l15" 5 "0 -1 0" -"l14" 5 "0 -1 0" -"n15" 1 "0 -1 0" -"g10" 2 "0 -1 0" -"h10" 2 "0 -1 0" -"i10" 2 "0 -1 0" -"j10" 2 "0 -1 0" -"k10" 2 "0 -1 0" -"j11" 3 "0 -1 0" -"j12" 3 "0 -1 0" -"k13" 3 "0 -1 0" -"i13" 3 "0 -1 0" -"m13" 3 "0 -1 0" -"k6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"b12" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"s14" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"m8" 4 "0 -1 0" -"l8" 4 "0 -1 0" -"k8" 4 "0 -1 0" -"j8" 4 "0 -1 0" -"i8" 4 "0 -1 0" -"h8" 4 "0 -1 0" -"g8" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"e9" 4 "0 -1 0" -"e10" 4 "0 -1 0" -"e11" 4 "0 -1 0" -"e12" 4 "0 -1 0" -"f12" 4 "0 -1 0" -"g12" 4 "0 -1 0" -"g13" 4 "0 -1 0" -"f13" 4 "0 -1 0" -"e13" 4 "0 -1 0" -"e14" 4 "0 -1 0" -"f14" 4 "0 -1 0" -"g14" 4 "0 -1 0" -"g15" 4 "0 -1 0" -"f15" 4 "0 -1 0" -"e15" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"f16" 4 "0 -1 0" -"g16" 4 "0 -1 0" -"h16" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"p15" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"p13" 4 "0 -1 0" -"p12" 4 "0 -1 0" -"p11" 4 "0 -1 0" -"p10" 4 "0 -1 0" -"o10" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"p9" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" +"a20" 10 0 +"a19" 10 0 +"a18" 10 0 +"a17" 10 0 +"a16" 10 0 +"a15" 10 0 +"a14" 10 0 +"a13" 10 0 +"a12" 10 0 +"a11" 10 0 +"a10" 10 0 +"a9" 10 0 +"a8" 10 0 +"a7" 10 0 +"a6" 10 0 +"a5" 10 0 +"a4" 10 0 +"a3" 10 0 +"a2" 10 0 +"a1" 10 0 +"t20" 10 0 +"t19" 10 0 +"t18" 10 0 +"t17" 10 0 +"t16" 10 0 +"t15" 10 0 +"t13" 10 0 +"t12" 10 0 +"t11" 10 0 +"t10" 10 0 +"t9" 10 0 +"t8" 10 0 +"t7" 10 0 +"t6" 10 0 +"t5" 10 0 +"t4" 10 0 +"t2" 10 0 +"t1" 10 0 +"t3" 10 0 +"t14" 10 0 +"c17" 5 0 +"d17" 5 0 +"d16" 5 0 +"c16" 5 0 +"c15" 5 0 +"c14" 5 0 +"c13" 5 0 +"c12" 5 0 +"c11" 5 0 +"c10" 5 0 +"c9" 5 0 +"c8" 5 0 +"d15" 5 0 +"d14" 5 0 +"d13" 5 0 +"d12" 5 0 +"d11" 5 0 +"d10" 5 0 +"d8" 5 0 +"f9" 5 0 +"d9" 5 0 +"f10" 5 0 +"f11" 5 0 +"g11" 5 0 +"h11" 5 0 +"h12" 5 0 +"h13" 5 0 +"h14" 5 0 +"h15" 5 0 +"i15" 5 0 +"j16" 5 0 +"i16" 5 0 +"k16" 5 0 +"l16" 5 0 +"m16" 5 0 +"n16" 5 0 +"o16" 5 0 +"o15" 5 0 +"o14" 5 0 +"o13" 5 0 +"o12" 5 0 +"o11" 5 0 +"q17" 5 0 +"q16" 5 0 +"q15" 5 0 +"q14" 5 0 +"q13" 5 0 +"q11" 5 0 +"q10" 5 0 +"q9" 5 0 +"q8" 5 0 +"r8" 5 0 +"q12" 5 0 +"r9" 5 0 +"r10" 5 0 +"r11" 5 0 +"r13" 5 0 +"r14" 5 0 +"r15" 5 0 +"r16" 5 0 +"r17" 5 0 +"r12" 5 0 +"n11" 5 0 +"n10" 5 0 +"n9" 5 0 +"m9" 5 0 +"k9" 5 0 +"i9" 5 0 +"h9" 5 0 +"g9" 5 0 +"j9" 5 0 +"l9" 5 0 +"l11" 5 0 +"l12" 5 0 +"k12" 5 0 +"l15" 5 0 +"l14" 5 0 +"n15" 1 1 +"g10" 2 0 +"h10" 2 0 +"i10" 2 0 +"j10" 2 0 +"k10" 2 0 +"j11" 3 0 +"j12" 3 0 +"k13" 3 0 +"i13" 3 0 +"m13" 3 0 +"k6" 4 0 +"j6" 4 0 +"i6" 4 0 +"h6" 4 0 +"g6" 4 0 +"f6" 4 0 +"e6" 4 0 +"d6" 4 0 +"c6" 4 0 +"b6" 4 0 +"b5" 4 0 +"c5" 4 0 +"d5" 4 0 +"e5" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"p5" 4 0 +"q5" 4 0 +"r5" 4 0 +"s5" 4 0 +"s4" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"b4" 4 0 +"b3" 4 0 +"c3" 4 0 +"d3" 4 0 +"e3" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"p3" 4 0 +"q3" 4 0 +"r3" 4 0 +"s3" 4 0 +"s2" 4 0 +"r2" 4 0 +"q2" 4 0 +"p2" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"e2" 4 0 +"d2" 4 0 +"c2" 4 0 +"b2" 4 0 +"b1" 4 0 +"c1" 4 0 +"d1" 4 0 +"e1" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"p1" 4 0 +"q1" 4 0 +"r1" 4 0 +"s1" 4 0 +"s6" 4 0 +"r6" 4 0 +"q6" 4 0 +"p6" 4 0 +"o6" 4 0 +"n6" 4 0 +"m6" 4 0 +"l6" 4 0 +"l7" 4 0 +"k7" 4 0 +"j7" 4 0 +"i7" 4 0 +"h7" 4 0 +"g7" 4 0 +"f7" 4 0 +"e7" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"b8" 4 0 +"b9" 4 0 +"b10" 4 0 +"b11" 4 0 +"b12" 4 0 +"b13" 4 0 +"b14" 4 0 +"b15" 4 0 +"b16" 4 0 +"b17" 4 0 +"b18" 4 0 +"c18" 4 0 +"d18" 4 0 +"e18" 4 0 +"f18" 4 0 +"g18" 4 0 +"h18" 4 0 +"i18" 4 0 +"j18" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"s18" 4 0 +"s17" 4 0 +"s16" 4 0 +"s15" 4 0 +"s14" 4 0 +"s13" 4 0 +"s12" 4 0 +"s11" 4 0 +"s10" 4 0 +"s9" 4 0 +"s8" 4 0 +"s7" 4 0 +"r7" 4 0 +"q7" 4 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"m7" 4 0 +"m8" 4 0 +"l8" 4 0 +"k8" 4 0 +"j8" 4 0 +"i8" 4 0 +"h8" 4 0 +"g8" 4 0 +"f8" 4 0 +"e8" 4 0 +"e9" 4 0 +"e10" 4 0 +"e11" 4 0 +"e12" 4 0 +"f12" 4 0 +"g12" 4 0 +"g13" 4 0 +"f13" 4 0 +"e13" 4 0 +"e14" 4 0 +"f14" 4 0 +"g14" 4 0 +"g15" 4 0 +"f15" 4 0 +"e15" 4 0 +"e16" 4 0 +"f16" 4 0 +"g16" 4 0 +"h16" 4 0 +"h17" 4 0 +"g17" 4 0 +"f17" 4 0 +"e17" 4 0 +"i17" 4 0 +"j17" 4 0 +"k17" 4 0 +"l17" 4 0 +"m17" 4 0 +"n17" 4 0 +"o17" 4 0 +"p17" 4 0 +"p16" 4 0 +"p15" 4 0 +"p14" 4 0 +"p13" 4 0 +"p12" 4 0 +"p11" 4 0 +"p10" 4 0 +"o10" 4 0 +"o9" 4 0 +"p9" 4 0 +"p8" 4 0 +"o8" 4 0 +"n8" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"b19" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"f20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 diff --git a/minigames/bulldozer/storage_level15.txt b/minigames/bulldozer/storage_level15.txt index 8a2d85668d..fa22ecc606 100644 --- a/minigames/bulldozer/storage_level15.txt +++ b/minigames/bulldozer/storage_level15.txt @@ -1,307 +1,307 @@ // bulldozer storage "ending" last updated 16-11-2015 04:16:07 nextlevel = "level1" -"e15" 3 "0 0 0" -"f14" 3 "0 0 0" -"g13" 3 "0 0 0" -"k13" 3 "0 0 0" -"b14" 4 "0 0 0" -"a14" 4 "0 0 0" -"a13" 4 "0 0 0" -"b13" 4 "0 0 0" -"i13" 3 "0 0 0" -"m13" 3 "0 0 0" -"n14" 3 "0 0 0" -"o15" 3 "0 0 0" -"h17" 2 "0 0 0" -"l17" 2 "0 0 0" -"c12" 2 "0 0 0" -"c13" 2 "0 0 0" -"c14" 2 "0 0 0" -"r14" 2 "0 0 0" -"r13" 2 "0 0 0" -"r12" 2 "0 0 0" -"j15" 1 "0 -1 0" -"s13" 4 "0 0 0" -"t13" 4 "0 0 0" -"t12" 4 "0 0 0" -"s12" 4 "0 0 0" -"b12" 4 "0 0 0" -"a12" 4 "0 0 0" -"a11" 4 "0 0 0" -"b11" 4 "0 0 0" -"c11" 4 "0 0 0" -"d11" 4 "0 0 0" -"e11" 4 "0 0 0" -"f11" 4 "0 0 0" -"g11" 4 "0 0 0" -"h11" 4 "0 0 0" -"i11" 4 "0 0 0" -"j11" 4 "0 0 0" -"k11" 4 "0 0 0" -"l11" 4 "0 0 0" -"m11" 4 "0 0 0" -"n11" 4 "0 0 0" -"o11" 4 "0 0 0" -"p11" 4 "0 0 0" -"q11" 4 "0 0 0" -"r11" 4 "0 0 0" -"s11" 4 "0 0 0" -"t11" 4 "0 0 0" -"t6" 6 "0 0 0" -"s10" 4 "0 0 0" -"r10" 4 "0 0 0" -"q10" 4 "0 0 0" -"b10" 6 "0 0 0" -"o10" 4 "0 0 0" -"c10" 6 "0 0 0" -"d10" 6 "0 0 0" -"e6" 4 "0 0 0" -"b9" 6 "0 0 0" -"b8" 6 "0 0 0" -"c7" 4 "0 0 0" -"b7" 6 "0 0 0" -"b6" 6 "0 0 0" -"c6" 6 "0 0 0" -"d6" 6 "0 0 0" -"f10" 6 "0 0 0" -"g10" 6 "0 0 0" -"h10" 6 "0 0 0" -"a10" 4 "0 0 0" -"a9" 4 "0 0 0" -"h9" 4 "0 0 0" -"c9" 4 "0 0 0" -"d9" 4 "0 0 0" -"h8" 4 "0 0 0" -"h7" 4 "0 0 0" -"i10" 6 "0 0 0" -"i9" 6 "0 0 0" -"j7" 4 "0 0 0" -"j6" 4 "0 0 0" -"j10" 4 "0 0 0" -"i8" 6 "0 0 0" -"m9" 4 "0 0 0" -"i7" 6 "0 0 0" -"o9" 4 "0 0 0" -"i6" 6 "0 0 0" -"q9" 4 "0 0 0" -"r9" 4 "0 0 0" -"s9" 4 "0 0 0" -"h6" 6 "0 0 0" -"g6" 6 "0 0 0" -"s8" 4 "0 0 0" -"r8" 4 "0 0 0" -"q8" 4 "0 0 0" -"f6" 6 "0 0 0" -"o8" 4 "0 0 0" -"f7" 6 "0 0 0" -"m8" 4 "0 0 0" -"f8" 6 "0 0 0" -"f9" 6 "0 0 0" -"k10" 6 "0 0 0" -"e10" 4 "0 0 0" -"d7" 4 "0 0 0" -"e9" 4 "0 0 0" -"e8" 4 "0 0 0" -"e7" 4 "0 0 0" -"l10" 6 "0 0 0" -"c8" 4 "0 0 0" -"m10" 6 "0 0 0" -"a8" 4 "0 0 0" -"a7" 4 "0 0 0" -"g7" 4 "0 0 0" -"g8" 4 "0 0 0" -"g9" 4 "0 0 0" -"n10" 6 "0 0 0" -"n9" 6 "0 0 0" -"n8" 6 "0 0 0" -"d8" 4 "0 0 0" -"n7" 6 "0 0 0" -"n6" 6 "0 0 0" -"m6" 6 "0 0 0" -"l6" 6 "0 0 0" -"m7" 4 "0 0 0" -"k6" 6 "0 0 0" -"o7" 4 "0 0 0" -"k7" 6 "0 0 0" -"q7" 4 "0 0 0" -"r7" 4 "0 0 0" -"s7" 4 "0 0 0" -"k8" 6 "0 0 0" -"k9" 6 "0 0 0" -"s6" 4 "0 0 0" -"p9" 6 "0 0 0" -"p8" 6 "0 0 0" -"p7" 6 "0 0 0" -"o6" 4 "0 0 0" -"p6" 6 "0 0 0" -"q6" 6 "0 0 0" -"r6" 6 "0 0 0" -"p10" 6 "0 0 0" -"j9" 4 "0 0 0" -"j8" 4 "0 0 0" -"t10" 6 "0 0 0" -"t9" 6 "0 0 0" -"t8" 6 "0 0 0" -"l7" 4 "0 0 0" -"l8" 4 "0 0 0" -"l9" 4 "0 0 0" -"t7" 4 "0 0 0" -"a6" 4 "0 0 0" -"a5" 4 "0 0 0" -"b5" 4 "0 0 0" -"c5" 4 "0 0 0" -"d5" 4 "0 0 0" -"e5" 4 "0 0 0" -"f5" 4 "0 0 0" -"g5" 4 "0 0 0" -"h5" 4 "0 0 0" -"i5" 4 "0 0 0" -"j5" 4 "0 0 0" -"k5" 4 "0 0 0" -"l5" 4 "0 0 0" -"m5" 4 "0 0 0" -"n5" 4 "0 0 0" -"o5" 4 "0 0 0" -"p5" 4 "0 0 0" -"q5" 4 "0 0 0" -"r5" 4 "0 0 0" -"s5" 4 "0 0 0" -"t5" 4 "0 0 0" -"t4" 4 "0 0 0" -"s4" 4 "0 0 0" -"r4" 4 "0 0 0" -"q4" 4 "0 0 0" -"p4" 4 "0 0 0" -"o4" 4 "0 0 0" -"n4" 4 "0 0 0" -"m4" 4 "0 0 0" -"l4" 4 "0 0 0" -"k4" 4 "0 0 0" -"j4" 4 "0 0 0" -"i4" 4 "0 0 0" -"h4" 4 "0 0 0" -"g4" 4 "0 0 0" -"f4" 4 "0 0 0" -"e4" 4 "0 0 0" -"d4" 4 "0 0 0" -"c4" 4 "0 0 0" -"b4" 4 "0 0 0" -"a4" 4 "0 0 0" -"a3" 4 "0 0 0" -"b3" 4 "0 0 0" -"c3" 4 "0 0 0" -"d3" 4 "0 0 0" -"e3" 4 "0 0 0" -"f3" 4 "0 0 0" -"g3" 4 "0 0 0" -"h3" 4 "0 0 0" -"i3" 4 "0 0 0" -"j3" 4 "0 0 0" -"k3" 4 "0 0 0" -"l3" 4 "0 0 0" -"m3" 4 "0 0 0" -"n3" 4 "0 0 0" -"o3" 4 "0 0 0" -"p3" 4 "0 0 0" -"q3" 4 "0 0 0" -"r3" 4 "0 0 0" -"s3" 4 "0 0 0" -"t3" 4 "0 0 0" -"t2" 4 "0 0 0" -"s2" 4 "0 0 0" -"r2" 4 "0 0 0" -"q2" 4 "0 0 0" -"p2" 4 "0 0 0" -"o2" 4 "0 0 0" -"n2" 4 "0 0 0" -"m2" 4 "0 0 0" -"l2" 4 "0 0 0" -"k2" 4 "0 0 0" -"j2" 4 "0 0 0" -"i2" 4 "0 0 0" -"h2" 4 "0 0 0" -"g2" 4 "0 0 0" -"f2" 4 "0 0 0" -"e2" 4 "0 0 0" -"d2" 4 "0 0 0" -"c2" 4 "0 0 0" -"b2" 4 "0 0 0" -"a2" 4 "0 0 0" -"a1" 4 "0 0 0" -"b1" 4 "0 0 0" -"c1" 4 "0 0 0" -"d1" 4 "0 0 0" -"e1" 4 "0 0 0" -"f1" 4 "0 0 0" -"g1" 4 "0 0 0" -"h1" 4 "0 0 0" -"i1" 4 "0 0 0" -"j1" 4 "0 0 0" -"k1" 4 "0 0 0" -"l1" 4 "0 0 0" -"m1" 4 "0 0 0" -"n1" 4 "0 0 0" -"o1" 4 "0 0 0" -"p1" 4 "0 0 0" -"q1" 4 "0 0 0" -"r1" 4 "0 0 0" -"s1" 4 "0 0 0" -"t1" 4 "0 0 0" -"t14" 4 "0 0 0" -"s14" 4 "0 0 0" -"b15" 4 "0 0 0" -"a15" 4 "0 0 0" -"a16" 4 "0 0 0" -"b16" 4 "0 0 0" -"s16" 4 "0 0 0" -"t16" 4 "0 0 0" -"t15" 4 "0 0 0" -"s15" 4 "0 0 0" -"t17" 4 "0 0 0" -"s17" 4 "0 0 0" -"b17" 4 "0 0 0" -"a17" 4 "0 0 0" -"a18" 4 "0 0 0" -"b18" 4 "0 0 0" -"s18" 4 "0 0 0" -"t18" 4 "0 0 0" -"t19" 4 "0 0 0" -"s19" 4 "0 0 0" -"r19" 4 "0 0 0" -"q19" 4 "0 0 0" -"p19" 4 "0 0 0" -"o19" 4 "0 0 0" -"n19" 4 "0 0 0" -"m19" 4 "0 0 0" -"l19" 4 "0 0 0" -"k19" 4 "0 0 0" -"j19" 4 "0 0 0" -"i19" 4 "0 0 0" -"h19" 4 "0 0 0" -"g19" 4 "0 0 0" -"f19" 4 "0 0 0" -"e19" 4 "0 0 0" -"d19" 4 "0 0 0" -"c19" 4 "0 0 0" -"b19" 4 "0 0 0" -"a19" 4 "0 0 0" -"a20" 4 "0 0 0" -"b20" 4 "0 0 0" -"c20" 4 "0 0 0" -"d20" 4 "0 0 0" -"e20" 4 "0 0 0" -"f20" 4 "0 0 0" -"g20" 4 "0 0 0" -"h20" 4 "0 0 0" -"i20" 4 "0 0 0" -"j20" 4 "0 0 0" -"k20" 4 "0 0 0" -"l20" 4 "0 0 0" -"m20" 4 "0 0 0" -"n20" 4 "0 0 0" -"o20" 4 "0 0 0" -"p20" 4 "0 0 0" -"q20" 4 "0 0 0" -"r20" 4 "0 0 0" -"s20" 4 "0 0 0" -"t20" 4 "0 0 0" +"e15" 3 0 +"f14" 3 0 +"g13" 3 0 +"k13" 3 0 +"b14" 4 0 +"a14" 4 0 +"a13" 4 0 +"b13" 4 0 +"i13" 3 0 +"m13" 3 0 +"n14" 3 0 +"o15" 3 0 +"h17" 2 0 +"l17" 2 0 +"c12" 2 0 +"c13" 2 0 +"c14" 2 0 +"r14" 2 0 +"r13" 2 0 +"r12" 2 0 +"j15" 1 1 +"s13" 4 0 +"t13" 4 0 +"t12" 4 0 +"s12" 4 0 +"b12" 4 0 +"a12" 4 0 +"a11" 4 0 +"b11" 4 0 +"c11" 4 0 +"d11" 4 0 +"e11" 4 0 +"f11" 4 0 +"g11" 4 0 +"h11" 4 0 +"i11" 4 0 +"j11" 4 0 +"k11" 4 0 +"l11" 4 0 +"m11" 4 0 +"n11" 4 0 +"o11" 4 0 +"p11" 4 0 +"q11" 4 0 +"r11" 4 0 +"s11" 4 0 +"t11" 4 0 +"t6" 6 0 +"s10" 4 0 +"r10" 4 0 +"q10" 4 0 +"b10" 6 0 +"o10" 4 0 +"c10" 6 0 +"d10" 6 0 +"e6" 4 0 +"b9" 6 0 +"b8" 6 0 +"c7" 4 0 +"b7" 6 0 +"b6" 6 0 +"c6" 6 0 +"d6" 6 0 +"f10" 6 0 +"g10" 6 0 +"h10" 6 0 +"a10" 4 0 +"a9" 4 0 +"h9" 4 0 +"c9" 4 0 +"d9" 4 0 +"h8" 4 0 +"h7" 4 0 +"i10" 6 0 +"i9" 6 0 +"j7" 4 0 +"j6" 4 0 +"j10" 4 0 +"i8" 6 0 +"m9" 4 0 +"i7" 6 0 +"o9" 4 0 +"i6" 6 0 +"q9" 4 0 +"r9" 4 0 +"s9" 4 0 +"h6" 6 0 +"g6" 6 0 +"s8" 4 0 +"r8" 4 0 +"q8" 4 0 +"f6" 6 0 +"o8" 4 0 +"f7" 6 0 +"m8" 4 0 +"f8" 6 0 +"f9" 6 0 +"k10" 6 0 +"e10" 4 0 +"d7" 4 0 +"e9" 4 0 +"e8" 4 0 +"e7" 4 0 +"l10" 6 0 +"c8" 4 0 +"m10" 6 0 +"a8" 4 0 +"a7" 4 0 +"g7" 4 0 +"g8" 4 0 +"g9" 4 0 +"n10" 6 0 +"n9" 6 0 +"n8" 6 0 +"d8" 4 0 +"n7" 6 0 +"n6" 6 0 +"m6" 6 0 +"l6" 6 0 +"m7" 4 0 +"k6" 6 0 +"o7" 4 0 +"k7" 6 0 +"q7" 4 0 +"r7" 4 0 +"s7" 4 0 +"k8" 6 0 +"k9" 6 0 +"s6" 4 0 +"p9" 6 0 +"p8" 6 0 +"p7" 6 0 +"o6" 4 0 +"p6" 6 0 +"q6" 6 0 +"r6" 6 0 +"p10" 6 0 +"j9" 4 0 +"j8" 4 0 +"t10" 6 0 +"t9" 6 0 +"t8" 6 0 +"l7" 4 0 +"l8" 4 0 +"l9" 4 0 +"t7" 4 0 +"a6" 4 0 +"a5" 4 0 +"b5" 4 0 +"c5" 4 0 +"d5" 4 0 +"e5" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"p5" 4 0 +"q5" 4 0 +"r5" 4 0 +"s5" 4 0 +"t5" 4 0 +"t4" 4 0 +"s4" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"b4" 4 0 +"a4" 4 0 +"a3" 4 0 +"b3" 4 0 +"c3" 4 0 +"d3" 4 0 +"e3" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"p3" 4 0 +"q3" 4 0 +"r3" 4 0 +"s3" 4 0 +"t3" 4 0 +"t2" 4 0 +"s2" 4 0 +"r2" 4 0 +"q2" 4 0 +"p2" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"e2" 4 0 +"d2" 4 0 +"c2" 4 0 +"b2" 4 0 +"a2" 4 0 +"a1" 4 0 +"b1" 4 0 +"c1" 4 0 +"d1" 4 0 +"e1" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"p1" 4 0 +"q1" 4 0 +"r1" 4 0 +"s1" 4 0 +"t1" 4 0 +"t14" 4 0 +"s14" 4 0 +"b15" 4 0 +"a15" 4 0 +"a16" 4 0 +"b16" 4 0 +"s16" 4 0 +"t16" 4 0 +"t15" 4 0 +"s15" 4 0 +"t17" 4 0 +"s17" 4 0 +"b17" 4 0 +"a17" 4 0 +"a18" 4 0 +"b18" 4 0 +"s18" 4 0 +"t18" 4 0 +"t19" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"b19" 4 0 +"a19" 4 0 +"a20" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"f20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"t20" 4 0 diff --git a/minigames/bulldozer/storage_level2.txt b/minigames/bulldozer/storage_level2.txt index 1be5fb6098..3da61ac523 100644 --- a/minigames/bulldozer/storage_level2.txt +++ b/minigames/bulldozer/storage_level2.txt @@ -1,247 +1,247 @@ // bulldozer storage "level2" last updated 16-11-2015 03:34:13 nextlevel = "level3" -"a20" 4 "0 0 0" -"b20" 4 "0 0 0" -"c20" 4 "0 0 0" -"d20" 4 "0 0 0" -"e20" 4 "0 0 0" -"r13" 5 "0 0 0" -"f20" 4 "0 0 0" -"g20" 4 "0 0 0" -"c12" 5 "0 0 0" -"h20" 4 "0 0 0" -"c13" 5 "0 0 0" -"i20" 4 "0 0 0" -"c14" 5 "0 0 0" -"j20" 4 "0 0 0" -"c15" 5 "0 0 0" -"k20" 4 "0 0 0" -"c16" 5 "0 0 0" -"l20" 4 "0 0 0" -"c17" 5 "0 0 0" -"m20" 4 "0 0 0" -"c18" 5 "0 0 0" -"n20" 4 "0 0 0" -"c19" 5 "0 0 0" -"o20" 4 "0 0 0" -"r18" 5 "0 0 0" -"p20" 4 "0 0 0" -"r17" 5 "0 0 0" -"q20" 4 "0 0 0" -"r12" 5 "0 0 0" -"r20" 4 "0 0 0" -"r15" 5 "0 0 0" -"r14" 5 "0 0 0" -"s20" 4 "0 0 0" -"t20" 4 "0 0 0" -"t19" 4 "0 0 0" -"t18" 4 "0 0 0" -"t17" 4 "0 0 0" -"t16" 4 "0 0 0" -"t15" 4 "0 0 0" -"t14" 4 "0 0 0" -"t13" 4 "0 0 0" -"t9" 4 "0 0 0" -"s9" 4 "0 0 0" -"r9" 4 "0 0 0" -"q9" 4 "0 0 0" -"p9" 4 "0 0 0" -"o9" 4 "0 0 0" -"n9" 4 "0 0 0" -"m9" 4 "0 0 0" -"l9" 4 "0 0 0" -"k9" 4 "0 0 0" -"j9" 4 "0 0 0" -"i9" 4 "0 0 0" -"h9" 4 "0 0 0" -"g9" 4 "0 0 0" -"f9" 4 "0 0 0" -"e9" 4 "0 0 0" -"d9" 4 "0 0 0" -"c9" 4 "0 0 0" -"b9" 4 "0 0 0" -"a9" 4 "0 0 0" -"a10" 4 "0 0 0" -"a11" 4 "0 0 0" -"a12" 4 "0 0 0" -"a13" 4 "0 0 0" -"a14" 4 "0 0 0" -"a15" 4 "0 0 0" -"a16" 4 "0 0 0" -"a17" 4 "0 0 0" -"a18" 4 "0 0 0" -"a19" 4 "0 0 0" -"t12" 4 "0 0 0" -"t11" 4 "0 0 0" -"t10" 4 "0 0 0" -"k4" 4 "0 0 0" -"t3" 4 "0 0 0" -"s4" 4 "0 0 0" -"t2" 4 "0 0 0" -"l5" 4 "0 0 0" -"j6" 4 "0 0 0" -"r11" 5 "0 0 0" -"r6" 4 "0 0 0" -"r10" 5 "0 0 0" -"b7" 5 "0 0 0" -"i6" 4 "0 0 0" -"a7" 5 "0 0 0" -"a2" 4 "0 0 0" -"c4" 5 "0 0 0" -"b8" 4 "0 0 0" -"a6" 5 "0 0 0" -"o6" 4 "0 0 0" -"p6" 4 "0 0 0" -"a5" 5 "0 0 0" -"l7" 4 "0 0 0" -"l6" 4 "0 0 0" -"t6" 4 "0 0 0" -"t5" 4 "0 0 0" -"b19" 2 "0 0 0" -"k5" 4 "0 0 0" -"b18" 2 "0 0 0" -"p5" 4 "0 0 0" -"j4" 4 "0 0 0" -"k15" 1 "0 -1 0" -"g16" 3 "0 0 0" -"g12" 3 "0 0 0" -"l3" 4 "0 0 0" -"b3" 5 "0 0 0" -"i5" 4 "0 0 0" -"c8" 4 "0 0 0" -"c5" 5 "0 0 0" -"c6" 5 "0 0 0" -"a4" 5 "0 0 0" -"a3" 5 "0 0 0" -"a8" 4 "0 0 0" -"c3" 4 "0 0 0" -"c7" 4 "0 0 0" -"k6" 5 "0 0 0" -"d6" 4 "0 0 0" -"e7" 5 "0 0 0" -"e6" 5 "0 0 0" -"s10" 2 "0 0 0" -"d5" 4 "0 0 0" -"s11" 2 "0 0 0" -"d4" 4 "0 0 0" -"o17" 3 "0 0 0" -"o13" 3 "0 0 0" -"l4" 4 "0 0 0" -"h7" 4 "0 0 0" -"o4" 4 "0 0 0" -"p4" 4 "0 0 0" -"t4" 4 "0 0 0" -"h6" 4 "0 0 0" -"h5" 4 "0 0 0" -"h4" 4 "0 0 0" -"h3" 4 "0 0 0" -"f4" 4 "0 0 0" -"f5" 4 "0 0 0" -"f6" 4 "0 0 0" -"n6" 4 "0 0 0" -"d3" 4 "0 0 0" -"o5" 4 "0 0 0" -"n4" 4 "0 0 0" -"e5" 5 "0 0 0" -"b2" 4 "0 0 0" -"c2" 4 "0 0 0" -"d2" 4 "0 0 0" -"e2" 4 "0 0 0" -"f2" 4 "0 0 0" -"g2" 4 "0 0 0" -"h2" 4 "0 0 0" -"i2" 4 "0 0 0" -"j2" 4 "0 0 0" -"k2" 4 "0 0 0" -"l2" 4 "0 0 0" -"m2" 4 "0 0 0" -"n2" 4 "0 0 0" -"o2" 4 "0 0 0" -"p2" 4 "0 0 0" -"q2" 4 "0 0 0" -"r2" 4 "0 0 0" -"s2" 4 "0 0 0" -"t1" 4 "0 0 0" -"s1" 4 "0 0 0" -"r1" 4 "0 0 0" -"q1" 4 "0 0 0" -"p1" 4 "0 0 0" -"o1" 4 "0 0 0" -"n1" 4 "0 0 0" -"m1" 4 "0 0 0" -"l1" 4 "0 0 0" -"k1" 4 "0 0 0" -"j1" 4 "0 0 0" -"i1" 4 "0 0 0" -"h1" 4 "0 0 0" -"g1" 4 "0 0 0" -"f1" 4 "0 0 0" -"e1" 4 "0 0 0" -"d1" 4 "0 0 0" -"c1" 4 "0 0 0" -"b1" 4 "0 0 0" -"a1" 4 "0 0 0" -"t7" 4 "0 0 0" -"p3" 4 "0 0 0" -"p7" 4 "0 0 0" -"d7" 4 "0 0 0" -"r3" 4 "0 0 0" -"e4" 5 "0 0 0" -"e3" 5 "0 0 0" -"f3" 5 "0 0 0" -"d8" 4 "0 0 0" -"e8" 4 "0 0 0" -"f8" 4 "0 0 0" -"g8" 4 "0 0 0" -"h8" 4 "0 0 0" -"i8" 4 "0 0 0" -"j8" 4 "0 0 0" -"k8" 4 "0 0 0" -"l8" 4 "0 0 0" -"m8" 4 "0 0 0" -"n8" 4 "0 0 0" -"o8" 4 "0 0 0" -"p8" 4 "0 0 0" -"q8" 4 "0 0 0" -"r8" 4 "0 0 0" -"s8" 4 "0 0 0" -"t8" 4 "0 0 0" -"g3" 5 "0 0 0" -"g7" 5 "0 0 0" -"g4" 5 "0 0 0" -"g6" 5 "0 0 0" -"g5" 5 "0 0 0" -"f7" 5 "0 0 0" -"j5" 5 "0 0 0" -"i7" 5 "0 0 0" -"j7" 5 "0 0 0" -"k7" 5 "0 0 0" -"i4" 5 "0 0 0" -"i3" 5 "0 0 0" -"j3" 5 "0 0 0" -"k3" 5 "0 0 0" -"m7" 5 "0 0 0" -"m6" 5 "0 0 0" -"m4" 5 "0 0 0" -"m5" 5 "0 0 0" -"m3" 5 "0 0 0" -"n3" 5 "0 0 0" -"o3" 5 "0 0 0" -"n5" 5 "0 0 0" -"n7" 5 "0 0 0" -"o7" 5 "0 0 0" -"q7" 5 "0 0 0" -"q6" 5 "0 0 0" -"q4" 5 "0 0 0" -"q5" 5 "0 0 0" -"q3" 5 "0 0 0" -"r5" 5 "0 0 0" -"r4" 5 "0 0 0" -"s3" 5 "0 0 0" -"s5" 5 "0 0 0" -"s6" 5 "0 0 0" -"s7" 5 "0 0 0" -"r7" 5 "0 0 0" -"b6" 4 "0 0 0" -"b5" 4 "0 0 0" -"b4" 4 "0 0 0" +"a20" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"r13" 5 0 +"f20" 4 0 +"g20" 4 0 +"c12" 5 0 +"h20" 4 0 +"c13" 5 0 +"i20" 4 0 +"c14" 5 0 +"j20" 4 0 +"c15" 5 0 +"k20" 4 0 +"c16" 5 0 +"l20" 4 0 +"c17" 5 0 +"m20" 4 0 +"c18" 5 0 +"n20" 4 0 +"c19" 5 0 +"o20" 4 0 +"r18" 5 0 +"p20" 4 0 +"r17" 5 0 +"q20" 4 0 +"r12" 5 0 +"r20" 4 0 +"r15" 5 0 +"r14" 5 0 +"s20" 4 0 +"t20" 4 0 +"t19" 4 0 +"t18" 4 0 +"t17" 4 0 +"t16" 4 0 +"t15" 4 0 +"t14" 4 0 +"t13" 4 0 +"t9" 4 0 +"s9" 4 0 +"r9" 4 0 +"q9" 4 0 +"p9" 4 0 +"o9" 4 0 +"n9" 4 0 +"m9" 4 0 +"l9" 4 0 +"k9" 4 0 +"j9" 4 0 +"i9" 4 0 +"h9" 4 0 +"g9" 4 0 +"f9" 4 0 +"e9" 4 0 +"d9" 4 0 +"c9" 4 0 +"b9" 4 0 +"a9" 4 0 +"a10" 4 0 +"a11" 4 0 +"a12" 4 0 +"a13" 4 0 +"a14" 4 0 +"a15" 4 0 +"a16" 4 0 +"a17" 4 0 +"a18" 4 0 +"a19" 4 0 +"t12" 4 0 +"t11" 4 0 +"t10" 4 0 +"k4" 4 0 +"t3" 4 0 +"s4" 4 0 +"t2" 4 0 +"l5" 4 0 +"j6" 4 0 +"r11" 5 0 +"r6" 4 0 +"r10" 5 0 +"b7" 5 0 +"i6" 4 0 +"a7" 5 0 +"a2" 4 0 +"c4" 5 0 +"b8" 4 0 +"a6" 5 0 +"o6" 4 0 +"p6" 4 0 +"a5" 5 0 +"l7" 4 0 +"l6" 4 0 +"t6" 4 0 +"t5" 4 0 +"b19" 2 0 +"k5" 4 0 +"b18" 2 0 +"p5" 4 0 +"j4" 4 0 +"k15" 1 1 +"g16" 3 0 +"g12" 3 0 +"l3" 4 0 +"b3" 5 0 +"i5" 4 0 +"c8" 4 0 +"c5" 5 0 +"c6" 5 0 +"a4" 5 0 +"a3" 5 0 +"a8" 4 0 +"c3" 4 0 +"c7" 4 0 +"k6" 5 0 +"d6" 4 0 +"e7" 5 0 +"e6" 5 0 +"s10" 2 0 +"d5" 4 0 +"s11" 2 0 +"d4" 4 0 +"o17" 3 0 +"o13" 3 0 +"l4" 4 0 +"h7" 4 0 +"o4" 4 0 +"p4" 4 0 +"t4" 4 0 +"h6" 4 0 +"h5" 4 0 +"h4" 4 0 +"h3" 4 0 +"f4" 4 0 +"f5" 4 0 +"f6" 4 0 +"n6" 4 0 +"d3" 4 0 +"o5" 4 0 +"n4" 4 0 +"e5" 5 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"t1" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"a1" 4 0 +"t7" 4 0 +"p3" 4 0 +"p7" 4 0 +"d7" 4 0 +"r3" 4 0 +"e4" 5 0 +"e3" 5 0 +"f3" 5 0 +"d8" 4 0 +"e8" 4 0 +"f8" 4 0 +"g8" 4 0 +"h8" 4 0 +"i8" 4 0 +"j8" 4 0 +"k8" 4 0 +"l8" 4 0 +"m8" 4 0 +"n8" 4 0 +"o8" 4 0 +"p8" 4 0 +"q8" 4 0 +"r8" 4 0 +"s8" 4 0 +"t8" 4 0 +"g3" 5 0 +"g7" 5 0 +"g4" 5 0 +"g6" 5 0 +"g5" 5 0 +"f7" 5 0 +"j5" 5 0 +"i7" 5 0 +"j7" 5 0 +"k7" 5 0 +"i4" 5 0 +"i3" 5 0 +"j3" 5 0 +"k3" 5 0 +"m7" 5 0 +"m6" 5 0 +"m4" 5 0 +"m5" 5 0 +"m3" 5 0 +"n3" 5 0 +"o3" 5 0 +"n5" 5 0 +"n7" 5 0 +"o7" 5 0 +"q7" 5 0 +"q6" 5 0 +"q4" 5 0 +"q5" 5 0 +"q3" 5 0 +"r5" 5 0 +"r4" 5 0 +"s3" 5 0 +"s5" 5 0 +"s6" 5 0 +"s7" 5 0 +"r7" 5 0 +"b6" 4 0 +"b5" 4 0 +"b4" 4 0 diff --git a/minigames/bulldozer/storage_level3.txt b/minigames/bulldozer/storage_level3.txt index 7433ae2095..2b12c6757a 100644 --- a/minigames/bulldozer/storage_level3.txt +++ b/minigames/bulldozer/storage_level3.txt @@ -1,279 +1,279 @@ // bulldozer storage "level3" last updated 16-11-2015 04:06:54 nextlevel = "level4" -"a20" 4 "0 0 0" -"a19" 4 "0 0 0" -"f14" 2 "0 0 0" -"a18" 4 "0 0 0" -"a17" 4 "0 0 0" -"a16" 4 "0 0 0" -"a15" 4 "0 0 0" -"f14" 3 "0 0 0" -"b7" 4 "0 0 0" -"b5" 4 "0 0 0" -"b3" 4 "0 0 0" -"s7" 4 "0 0 0" -"s5" 4 "0 0 0" -"s3" 4 "0 0 0" -"b19" 4 "0 0 0" -"s19" 4 "0 0 0" -"a14" 4 "0 0 0" -"a13" 4 "0 0 0" -"a12" 4 "0 0 0" -"a11" 4 "0 0 0" -"a10" 4 "0 0 0" -"a9" 4 "0 0 0" -"a8" 4 "0 0 0" -"a7" 4 "0 0 0" -"a6" 4 "0 0 0" -"a5" 4 "0 0 0" -"a4" 4 "0 0 0" -"a3" 4 "0 0 0" -"a2" 4 "0 0 0" -"a1" 4 "0 0 0" -"b1" 4 "0 0 0" -"c1" 4 "0 0 0" -"d1" 4 "0 0 0" -"e1" 4 "0 0 0" -"f1" 4 "0 0 0" -"g1" 4 "0 0 0" -"h1" 4 "0 0 0" -"i1" 4 "0 0 0" -"j1" 4 "0 0 0" -"k1" 4 "0 0 0" -"l1" 4 "0 0 0" -"m1" 4 "0 0 0" -"n1" 4 "0 0 0" -"o1" 4 "0 0 0" -"p1" 4 "0 0 0" -"q1" 4 "0 0 0" -"r1" 4 "0 0 0" -"s1" 4 "0 0 0" -"t1" 4 "0 0 0" -"t2" 4 "0 0 0" -"t3" 4 "0 0 0" -"t4" 4 "0 0 0" -"t5" 4 "0 0 0" -"t6" 4 "0 0 0" -"t12" 4 "0 0 0" -"t9" 4 "0 0 0" -"t10" 4 "0 0 0" -"t11" 4 "0 0 0" -"t7" 4 "0 0 0" -"t8" 4 "0 0 0" -"t13" 4 "0 0 0" -"t14" 4 "0 0 0" -"t15" 4 "0 0 0" -"t16" 4 "0 0 0" -"t17" 4 "0 0 0" -"t18" 4 "0 0 0" -"t19" 4 "0 0 0" -"t20" 4 "0 0 0" -"r20" 4 "0 0 0" -"q20" 4 "0 0 0" -"p20" 4 "0 0 0" -"s20" 4 "0 0 0" -"o20" 4 "0 0 0" -"n20" 4 "0 0 0" -"m20" 4 "0 0 0" -"l20" 4 "0 0 0" -"k20" 4 "0 0 0" -"j20" 4 "0 0 0" -"i20" 4 "0 0 0" -"h20" 4 "0 0 0" -"g20" 4 "0 0 0" -"f20" 4 "0 0 0" -"e20" 4 "0 0 0" -"d20" 4 "0 0 0" -"c20" 4 "0 0 0" -"b20" 4 "0 0 0" -"c19" 4 "0 0 0" -"b18" 4 "0 0 0" -"b16" 4 "0 0 0" -"c18" 4 "0 0 0" -"b14" 4 "0 0 0" -"b17" 7 "0 0 0" -"b12" 4 "0 0 0" -"b15" 7 "0 0 0" -"b9" 7 "0 0 0" -"b8" 4 "0 0 0" -"b11" 7 "0 0 0" -"b10" 4 "0 0 0" -"b6" 4 "0 0 0" -"b13" 7 "0 0 0" -"b2" 4 "0 0 0" -"b4" 4 "0 0 0" -"s18" 4 "0 0 0" -"s13" 7 "0 0 0" -"s16" 4 "0 0 0" -"s11" 7 "0 0 0" -"s14" 4 "0 0 0" -"s9" 7 "0 0 0" -"s12" 4 "0 0 0" -"s8" 4 "0 0 0" -"s6" 4 "0 0 0" -"s4" 4 "0 0 0" -"s2" 4 "0 0 0" -"s10" 4 "0 0 0" -"s17" 7 "0 0 0" -"s15" 7 "0 0 0" -"c7" 4 "0 0 0" -"c6" 4 "0 0 0" -"d7" 4 "0 0 0" -"f7" 4 "0 0 0" -"e7" 4 "0 0 0" -"g7" 4 "0 0 0" -"h7" 4 "0 0 0" -"i7" 4 "0 0 0" -"j7" 4 "0 0 0" -"k7" 4 "0 0 0" -"l7" 4 "0 0 0" -"m7" 4 "0 0 0" -"n7" 4 "0 0 0" -"o7" 4 "0 0 0" -"p7" 4 "0 0 0" -"q7" 4 "0 0 0" -"r7" 4 "0 0 0" -"h4" 4 "0 0 0" -"g4" 4 "0 0 0" -"f4" 4 "0 0 0" -"e4" 4 "0 0 0" -"d4" 4 "0 0 0" -"c4" 4 "0 0 0" -"c3" 4 "0 0 0" -"d3" 4 "0 0 0" -"e3" 4 "0 0 0" -"f3" 4 "0 0 0" -"g3" 4 "0 0 0" -"h3" 4 "0 0 0" -"i3" 4 "0 0 0" -"j3" 4 "0 0 0" -"k3" 4 "0 0 0" -"l3" 4 "0 0 0" -"m3" 4 "0 0 0" -"n3" 4 "0 0 0" -"o3" 4 "0 0 0" -"p3" 4 "0 0 0" -"q3" 4 "0 0 0" -"r3" 4 "0 0 0" -"r2" 4 "0 0 0" -"q2" 4 "0 0 0" -"p2" 4 "0 0 0" -"o2" 4 "0 0 0" -"n2" 4 "0 0 0" -"m2" 4 "0 0 0" -"l2" 4 "0 0 0" -"k2" 4 "0 0 0" -"j2" 4 "0 0 0" -"i2" 4 "0 0 0" -"h2" 4 "0 0 0" -"g2" 4 "0 0 0" -"f2" 4 "0 0 0" -"e2" 4 "0 0 0" -"d2" 4 "0 0 0" -"c2" 4 "0 0 0" -"r4" 4 "0 0 0" -"q4" 4 "0 0 0" -"p4" 4 "0 0 0" -"o4" 4 "0 0 0" -"n4" 4 "0 0 0" -"m4" 4 "0 0 0" -"l4" 4 "0 0 0" -"k4" 4 "0 0 0" -"j4" 4 "0 0 0" -"i4" 4 "0 0 0" -"i5" 4 "0 0 0" -"h5" 4 "0 0 0" -"g5" 4 "0 0 0" -"f5" 4 "0 0 0" -"e5" 4 "0 0 0" -"d5" 4 "0 0 0" -"c5" 4 "0 0 0" -"d6" 4 "0 0 0" -"e6" 4 "0 0 0" -"f6" 4 "0 0 0" -"g6" 4 "0 0 0" -"h6" 4 "0 0 0" -"i6" 4 "0 0 0" -"j6" 4 "0 0 0" -"k6" 4 "0 0 0" -"l6" 4 "0 0 0" -"m6" 4 "0 0 0" -"n6" 4 "0 0 0" -"o6" 4 "0 0 0" -"p6" 4 "0 0 0" -"q6" 4 "0 0 0" -"r6" 4 "0 0 0" -"r5" 4 "0 0 0" -"q5" 4 "0 0 0" -"p5" 4 "0 0 0" -"o5" 4 "0 0 0" -"n5" 4 "0 0 0" -"m5" 4 "0 0 0" -"l5" 4 "0 0 0" -"k5" 4 "0 0 0" -"j5" 4 "0 0 0" -"d18" 4 "0 0 0" -"e18" 4 "0 0 0" -"g18" 4 "0 0 0" -"h18" 4 "0 0 0" -"f18" 4 "0 0 0" -"j18" 4 "0 0 0" -"i18" 4 "0 0 0" -"k18" 4 "0 0 0" -"m18" 4 "0 0 0" -"l18" 4 "0 0 0" -"n18" 4 "0 0 0" -"o18" 4 "0 0 0" -"p18" 4 "0 0 0" -"q18" 4 "0 0 0" -"r18" 4 "0 0 0" -"q19" 4 "0 0 0" -"p19" 4 "0 0 0" -"o19" 4 "0 0 0" -"n19" 4 "0 0 0" -"m19" 4 "0 0 0" -"l19" 4 "0 0 0" -"k19" 4 "0 0 0" -"j19" 4 "0 0 0" -"i19" 4 "0 0 0" -"h19" 4 "0 0 0" -"g19" 4 "0 0 0" -"f19" 4 "0 0 0" -"e19" 4 "0 0 0" -"d19" 4 "0 0 0" -"r19" 4 "0 0 0" -"f15" 5 "0 0 0" -"f13" 5 "0 0 0" -"f12" 5 "0 0 0" -"g12" 5 "0 0 0" -"g9" 5 "0 0 0" -"g10" 5 "0 0 0" -"g11" 5 "0 0 0" -"h9" 5 "0 0 0" -"i9" 5 "0 0 0" -"k9" 5 "0 0 0" -"l9" 5 "0 0 0" -"l13" 5 "0 0 0" -"l12" 5 "0 0 0" -"l10" 5 "0 0 0" -"l11" 5 "0 0 0" -"l15" 5 "0 0 0" -"l14" 5 "0 0 0" -"l16" 5 "0 0 0" -"g15" 5 "0 0 0" -"h15" 5 "0 0 0" -"i15" 5 "0 0 0" -"j15" 5 "0 0 0" -"k15" 5 "0 0 0" -"p9" 5 "0 0 0" -"p10" 5 "0 0 0" -"q10" 5 "0 0 0" -"q8" 2 "0 0 0" -"r8" 2 "0 0 0" -"r9" 2 "0 0 0" -"j9" 3 "0 0 0" -"i13" 2 "0 0 0" -"k13" 1 "0 -1 0" -"n13" 3 "0 0 0" -"n14" 3 "0 0 0" -"n12" 3 "0 0 0" +"a20" 4 0 +"a19" 4 0 +"f14" 2 0 +"a18" 4 0 +"a17" 4 0 +"a16" 4 0 +"a15" 4 0 +"f14" 3 0 +"b7" 4 0 +"b5" 4 0 +"b3" 4 0 +"s7" 4 0 +"s5" 4 0 +"s3" 4 0 +"b19" 4 0 +"s19" 4 0 +"a14" 4 0 +"a13" 4 0 +"a12" 4 0 +"a11" 4 0 +"a10" 4 0 +"a9" 4 0 +"a8" 4 0 +"a7" 4 0 +"a6" 4 0 +"a5" 4 0 +"a4" 4 0 +"a3" 4 0 +"a2" 4 0 +"a1" 4 0 +"b1" 4 0 +"c1" 4 0 +"d1" 4 0 +"e1" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"p1" 4 0 +"q1" 4 0 +"r1" 4 0 +"s1" 4 0 +"t1" 4 0 +"t2" 4 0 +"t3" 4 0 +"t4" 4 0 +"t5" 4 0 +"t6" 4 0 +"t12" 4 0 +"t9" 4 0 +"t10" 4 0 +"t11" 4 0 +"t7" 4 0 +"t8" 4 0 +"t13" 4 0 +"t14" 4 0 +"t15" 4 0 +"t16" 4 0 +"t17" 4 0 +"t18" 4 0 +"t19" 4 0 +"t20" 4 0 +"r20" 4 0 +"q20" 4 0 +"p20" 4 0 +"s20" 4 0 +"o20" 4 0 +"n20" 4 0 +"m20" 4 0 +"l20" 4 0 +"k20" 4 0 +"j20" 4 0 +"i20" 4 0 +"h20" 4 0 +"g20" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"c19" 4 0 +"b18" 4 0 +"b16" 4 0 +"c18" 4 0 +"b14" 4 0 +"b17" 7 0 +"b12" 4 0 +"b15" 7 0 +"b9" 7 0 +"b8" 4 0 +"b11" 7 0 +"b10" 4 0 +"b6" 4 0 +"b13" 7 0 +"b2" 4 0 +"b4" 4 0 +"s18" 4 0 +"s13" 7 0 +"s16" 4 0 +"s11" 7 0 +"s14" 4 0 +"s9" 7 0 +"s12" 4 0 +"s8" 4 0 +"s6" 4 0 +"s4" 4 0 +"s2" 4 0 +"s10" 4 0 +"s17" 7 0 +"s15" 7 0 +"c7" 4 0 +"c6" 4 0 +"d7" 4 0 +"f7" 4 0 +"e7" 4 0 +"g7" 4 0 +"h7" 4 0 +"i7" 4 0 +"j7" 4 0 +"k7" 4 0 +"l7" 4 0 +"m7" 4 0 +"n7" 4 0 +"o7" 4 0 +"p7" 4 0 +"q7" 4 0 +"r7" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"c3" 4 0 +"d3" 4 0 +"e3" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"p3" 4 0 +"q3" 4 0 +"r3" 4 0 +"r2" 4 0 +"q2" 4 0 +"p2" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"e2" 4 0 +"d2" 4 0 +"c2" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"i5" 4 0 +"h5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"c5" 4 0 +"d6" 4 0 +"e6" 4 0 +"f6" 4 0 +"g6" 4 0 +"h6" 4 0 +"i6" 4 0 +"j6" 4 0 +"k6" 4 0 +"l6" 4 0 +"m6" 4 0 +"n6" 4 0 +"o6" 4 0 +"p6" 4 0 +"q6" 4 0 +"r6" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"l5" 4 0 +"k5" 4 0 +"j5" 4 0 +"d18" 4 0 +"e18" 4 0 +"g18" 4 0 +"h18" 4 0 +"f18" 4 0 +"j18" 4 0 +"i18" 4 0 +"k18" 4 0 +"m18" 4 0 +"l18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"e19" 4 0 +"d19" 4 0 +"r19" 4 0 +"f15" 5 0 +"f13" 5 0 +"f12" 5 0 +"g12" 5 0 +"g9" 5 0 +"g10" 5 0 +"g11" 5 0 +"h9" 5 0 +"i9" 5 0 +"k9" 5 0 +"l9" 5 0 +"l13" 5 0 +"l12" 5 0 +"l10" 5 0 +"l11" 5 0 +"l15" 5 0 +"l14" 5 0 +"l16" 5 0 +"g15" 5 0 +"h15" 5 0 +"i15" 5 0 +"j15" 5 0 +"k15" 5 0 +"p9" 5 0 +"p10" 5 0 +"q10" 5 0 +"q8" 2 0 +"r8" 2 0 +"r9" 2 0 +"j9" 3 0 +"i13" 2 0 +"k13" 1 1 +"n13" 3 0 +"n14" 3 0 +"n12" 3 0 diff --git a/minigames/bulldozer/storage_level4.txt b/minigames/bulldozer/storage_level4.txt index 596ae6ee71..19fea571a8 100644 --- a/minigames/bulldozer/storage_level4.txt +++ b/minigames/bulldozer/storage_level4.txt @@ -1,370 +1,370 @@ // bulldozer storage "level4" last updated 16-11-2015 05:09:50 nextlevel = "level5" -"k10" 9 "0 -1 0" -"k11" 9 "0 -1 0" -"k13" 9 "0 -1 0" -"j14" 9 "0 -1 0" -"k14" 9 "0 -1 0" -"k8" 5 "0 -1 0" -"k9" 5 "0 -1 0" -"k7" 5 "0 -1 0" -"m8" 4 "0 -1 0" -"l14" 9 "0 -1 0" -"i14" 9 "0 -1 0" -"m12" 1 "0 -1 0" -"l11" 2 "0 -1 0" -"m11" 2 "0 -1 0" -"m10" 2 "0 -1 0" -"l10" 2 "0 -1 0" -"k12" 3 "0 -1 0" -"m16" 5 "0 -1 0" -"l16" 5 "0 -1 0" -"k16" 5 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"h11" 3 "0 -1 0" -"f20" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"h16" 5 "0 -1 0" -"f16" 4 "0 -1 0" -"f15" 4 "0 -1 0" -"f14" 5 "0 -1 0" -"g14" 5 "0 -1 0" -"e13" 4 "0 -1 0" -"e12" 4 "0 -1 0" -"e11" 4 "0 -1 0" -"e10" 4 "0 -1 0" -"e9" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"f9" 5 "0 -1 0" -"f7" 5 "0 -1 0" -"f8" 5 "0 -1 0" -"f10" 5 "0 -1 0" -"g7" 5 "0 -1 0" -"h12" 3 "0 -1 0" -"m14" 3 "0 -1 0" -"a4" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"s8" 8 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"j7" 5 "0 -1 0" -"i7" 5 "0 -1 0" -"l8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"s12" 8 "0 -1 0" -"t8" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"q7" 8 "0 -1 0" -"p9" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"m9" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"o10" 4 "0 -1 0" -"p10" 4 "0 -1 0" -"q10" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"s14" 8 "0 -1 0" -"t10" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"q9" 8 "0 -1 0" -"p11" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"n10" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"o12" 4 "0 -1 0" -"p12" 4 "0 -1 0" -"q12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"t12" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"s13" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"q11" 8 "0 -1 0" -"p13" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"l9" 5 "0 -1 0" -"n12" 5 "0 -1 0" -"o14" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"q14" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"f12" 5 "0 -1 0" -"t14" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"r15" 4 "0 -1 0" -"q15" 8 "0 -1 0" -"p15" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"n13" 5 "0 -1 0" -"n16" 5 "0 -1 0" -"o16" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"q16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"q13" 8 "0 -1 0" -"t16" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"a9" 8 "0 -1 0" -"a8" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"a7" 8 "0 -1 0" -"d8" 4 "0 -1 0" -"d9" 4 "0 -1 0" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"a13" 8 "0 -1 0" -"a10" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"a11" 8 "0 -1 0" -"d10" 4 "0 -1 0" -"d11" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"a15" 8 "0 -1 0" -"a12" 4 "0 -1 0" -"b12" 4 "0 -1 0" -"c10" 8 "0 -1 0" -"d12" 4 "0 -1 0" -"d13" 4 "0 -1 0" -"c13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"s16" 8 "0 -1 0" -"a14" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"c12" 8 "0 -1 0" -"d14" 4 "0 -1 0" -"e14" 4 "0 -1 0" -"e15" 4 "0 -1 0" -"d15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"s10" 8 "0 -1 0" -"a16" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"c16" 8 "0 -1 0" -"d16" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"a20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"f11" 5 "0 -1 0" -"c14" 8 "0 -1 0" -"c8" 8 "0 -1 0" -"f13" 5 "0 -1 0" -"g15" 5 "0 -1 0" -"g16" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"j16" 5 "0 -1 0" -"n15" 5 "0 -1 0" -"n14" 5 "0 -1 0" -"h7" 5 "0 -1 0" +"k10" 9 0 +"k11" 9 0 +"k13" 9 0 +"j14" 9 0 +"k14" 9 0 +"k8" 5 0 +"k9" 5 0 +"k7" 5 0 +"m8" 4 0 +"l14" 9 0 +"i14" 9 0 +"m12" 1 1 +"l11" 2 0 +"m11" 2 0 +"m10" 2 0 +"l10" 2 0 +"k12" 3 0 +"m16" 5 0 +"l16" 5 0 +"k16" 5 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"b4" 4 0 +"h11" 3 0 +"f20" 4 0 +"f19" 4 0 +"f17" 4 0 +"f18" 4 0 +"h16" 5 0 +"f16" 4 0 +"f15" 4 0 +"f14" 5 0 +"g14" 5 0 +"e13" 4 0 +"e12" 4 0 +"e11" 4 0 +"e10" 4 0 +"e9" 4 0 +"e8" 4 0 +"e7" 4 0 +"f9" 5 0 +"f7" 5 0 +"f8" 5 0 +"f10" 5 0 +"g7" 5 0 +"h12" 3 0 +"m14" 3 0 +"a4" 4 0 +"a3" 4 0 +"b3" 4 0 +"c3" 4 0 +"d3" 4 0 +"e3" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"p3" 4 0 +"q3" 4 0 +"r3" 4 0 +"s3" 4 0 +"t3" 4 0 +"t2" 4 0 +"s2" 4 0 +"r2" 4 0 +"q2" 4 0 +"p2" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"e2" 4 0 +"d2" 4 0 +"c2" 4 0 +"b2" 4 0 +"a2" 4 0 +"a1" 4 0 +"b1" 4 0 +"c1" 4 0 +"d1" 4 0 +"e1" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"p1" 4 0 +"q1" 4 0 +"r1" 4 0 +"s1" 4 0 +"t1" 4 0 +"t4" 4 0 +"s4" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"k5" 4 0 +"j5" 4 0 +"i5" 4 0 +"h5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"c5" 4 0 +"b5" 4 0 +"a5" 4 0 +"a6" 4 0 +"b6" 4 0 +"c6" 4 0 +"d6" 4 0 +"e6" 4 0 +"f6" 4 0 +"g6" 4 0 +"h6" 4 0 +"i6" 4 0 +"j6" 4 0 +"k6" 4 0 +"l6" 4 0 +"m6" 4 0 +"n6" 4 0 +"o6" 4 0 +"p6" 4 0 +"q6" 4 0 +"r6" 4 0 +"s6" 4 0 +"t6" 4 0 +"t5" 4 0 +"s5" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"l5" 4 0 +"t7" 4 0 +"s7" 4 0 +"r7" 4 0 +"s8" 8 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"m7" 4 0 +"l7" 4 0 +"j7" 5 0 +"i7" 5 0 +"l8" 4 0 +"n8" 4 0 +"o8" 4 0 +"p8" 4 0 +"q8" 4 0 +"r8" 4 0 +"s12" 8 0 +"t8" 4 0 +"t9" 4 0 +"s9" 4 0 +"r9" 4 0 +"q7" 8 0 +"p9" 4 0 +"o9" 4 0 +"m9" 5 0 +"n9" 5 0 +"o10" 4 0 +"p10" 4 0 +"q10" 4 0 +"r10" 4 0 +"s14" 8 0 +"t10" 4 0 +"t11" 4 0 +"s11" 4 0 +"r11" 4 0 +"q9" 8 0 +"p11" 4 0 +"o11" 4 0 +"n10" 5 0 +"n11" 5 0 +"o12" 4 0 +"p12" 4 0 +"q12" 4 0 +"r12" 4 0 +"t12" 4 0 +"t13" 4 0 +"s13" 4 0 +"r13" 4 0 +"q11" 8 0 +"p13" 4 0 +"o13" 4 0 +"l9" 5 0 +"n12" 5 0 +"o14" 4 0 +"p14" 4 0 +"q14" 4 0 +"r14" 4 0 +"f12" 5 0 +"t14" 4 0 +"t15" 4 0 +"s15" 4 0 +"r15" 4 0 +"q15" 8 0 +"p15" 4 0 +"o15" 4 0 +"n13" 5 0 +"n16" 5 0 +"o16" 4 0 +"p16" 4 0 +"q16" 4 0 +"r16" 4 0 +"q13" 8 0 +"t16" 4 0 +"t17" 4 0 +"s17" 4 0 +"r17" 4 0 +"q17" 4 0 +"p17" 4 0 +"o17" 4 0 +"n17" 4 0 +"m17" 4 0 +"l17" 4 0 +"k17" 4 0 +"j17" 4 0 +"i17" 4 0 +"h17" 4 0 +"g17" 4 0 +"g18" 4 0 +"h18" 4 0 +"i18" 4 0 +"j18" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"p18" 4 0 +"q18" 4 0 +"r18" 4 0 +"s18" 4 0 +"t18" 4 0 +"t19" 4 0 +"s19" 4 0 +"r19" 4 0 +"q19" 4 0 +"p19" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"t20" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"a9" 8 0 +"a8" 4 0 +"b8" 4 0 +"a7" 8 0 +"d8" 4 0 +"d9" 4 0 +"c9" 4 0 +"b9" 4 0 +"a13" 8 0 +"a10" 4 0 +"b10" 4 0 +"a11" 8 0 +"d10" 4 0 +"d11" 4 0 +"c11" 4 0 +"b11" 4 0 +"a15" 8 0 +"a12" 4 0 +"b12" 4 0 +"c10" 8 0 +"d12" 4 0 +"d13" 4 0 +"c13" 4 0 +"b13" 4 0 +"s16" 8 0 +"a14" 4 0 +"b14" 4 0 +"c12" 8 0 +"d14" 4 0 +"e14" 4 0 +"e15" 4 0 +"d15" 4 0 +"c15" 4 0 +"b15" 4 0 +"s10" 8 0 +"a16" 4 0 +"b16" 4 0 +"c16" 8 0 +"d16" 4 0 +"e16" 4 0 +"e17" 4 0 +"d17" 4 0 +"c17" 4 0 +"b17" 4 0 +"a17" 4 0 +"a18" 4 0 +"b18" 4 0 +"c18" 4 0 +"d18" 4 0 +"e18" 4 0 +"e19" 4 0 +"d19" 4 0 +"c19" 4 0 +"b19" 4 0 +"a19" 4 0 +"a20" 4 0 +"b20" 4 0 +"c20" 4 0 +"d20" 4 0 +"e20" 4 0 +"f11" 5 0 +"c14" 8 0 +"c8" 8 0 +"f13" 5 0 +"g15" 5 0 +"g16" 5 0 +"i16" 5 0 +"j16" 5 0 +"n15" 5 0 +"n14" 5 0 +"h7" 5 0 diff --git a/minigames/bulldozer/storage_level5.txt b/minigames/bulldozer/storage_level5.txt index e5d7c2177d..535811d542 100644 --- a/minigames/bulldozer/storage_level5.txt +++ b/minigames/bulldozer/storage_level5.txt @@ -1,384 +1,384 @@ // bulldozer storage "level5" last updated 17-11-2015 21:42:14 nextlevel = "level6" -"a7" 4 "0 -1 0" -"c18" 6 "0 -1 0" -"a19" 4 "0 -1 0" -"d17" 6 "0 -1 0" -"c19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"q17" 6 "0 -1 0" -"t19" 4 "0 -1 0" -"e10" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"e9" 4 "0 -1 0" -"e15" 4 "0 -1 0" -"e13" 4 "0 -1 0" -"e12" 4 "0 -1 0" -"e11" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"e14" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"p9" 4 "0 -1 0" -"p10" 4 "0 -1 0" -"p11" 4 "0 -1 0" -"p12" 4 "0 -1 0" -"p13" 4 "0 -1 0" -"p14" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"c13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"b12" 4 "0 -1 0" -"c12" 4 "0 -1 0" -"d12" 4 "0 -1 0" -"d11" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"a11" 4 "0 -1 0" -"a10" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"c10" 4 "0 -1 0" -"d10" 4 "0 -1 0" -"d9" 4 "0 -1 0" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"a9" 4 "0 -1 0" -"a8" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"c8" 4 "0 -1 0" -"d8" 4 "0 -1 0" -"d13" 4 "0 -1 0" -"d14" 4 "0 -1 0" -"c14" 4 "0 -1 0" -"b14" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"d15" 4 "0 -1 0" -"d16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"a1" 6 "0 -1 0" -"d18" 4 "0 -1 0" -"b2" 6 "0 -1 0" -"b18" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"r18" 6 "0 -1 0" -"s13" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"q13" 4 "0 -1 0" -"q12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"t12" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"s11" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"q11" 4 "0 -1 0" -"q10" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"t10" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"q9" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"t8" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"s14" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"q14" 4 "0 -1 0" -"q15" 4 "0 -1 0" -"r15" 4 "0 -1 0" -"s15" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"q16" 4 "0 -1 0" -"g15" 1 "0 -1 0" -"r17" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"m13" 2 "0 -1 0" -"q18" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"t1" 6 "0 -1 0" -"b3" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"d4" 6 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"q4" 6 "0 -1 0" -"t2" 4 "0 -1 0" -"r3" 6 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"c3" 6 "0 -1 0" -"t3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"t20" 6 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"s2" 6 "0 -1 0" -"c4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"s19" 6 "0 -1 0" -"p4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"p15" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"f16" 5 "0 -1 0" -"f13" 5 "0 -1 0" -"f15" 5 "0 -1 0" -"f14" 5 "0 -1 0" -"f12" 5 "0 -1 0" -"g12" 5 "0 -1 0" -"h12" 5 "0 -1 0" -"h11" 5 "0 -1 0" -"g9" 5 "0 -1 0" -"g8" 5 "0 -1 0" -"g11" 5 "0 -1 0" -"g10" 5 "0 -1 0" -"h8" 5 "0 -1 0" -"i8" 5 "0 -1 0" -"j8" 5 "0 -1 0" -"k8" 5 "0 -1 0" -"k9" 5 "0 -1 0" -"k10" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"n12" 5 "0 -1 0" -"n13" 5 "0 -1 0" -"n14" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"l14" 5 "0 -1 0" -"j17" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"l13" 5 "0 -1 0" -"l12" 5 "0 -1 0" -"k12" 5 "0 -1 0" -"j12" 5 "0 -1 0" -"j13" 5 "0 -1 0" -"j15" 5 "0 -1 0" -"j14" 5 "0 -1 0" -"j16" 5 "0 -1 0" -"i16" 5 "0 -1 0" -"h16" 5 "0 -1 0" -"g16" 5 "0 -1 0" -"f10" 4 "0 -1 0" -"f9" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"f11" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"m8" 4 "0 -1 0" -"l8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"m15" 4 "0 -1 0" -"l15" 4 "0 -1 0" -"k15" 4 "0 -1 0" -"o10" 4 "0 -1 0" -"k14" 4 "0 -1 0" -"k13" 4 "0 -1 0" -"k16" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"o12" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"o14" 4 "0 -1 0" -"l16" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"m16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"n15" 4 "0 -1 0" -"a20" 6 "0 -1 0" -"b19" 6 "0 -1 0" -"m12" 2 "0 -1 0" -"m11" 2 "0 -1 0" -"i14" 3 "0 -1 0" -"h14" 3 "0 -1 0" -"h13" 3 "0 -1 0" -"i17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" +"a7" 4 0 +"c18" 6 0 +"a19" 4 0 +"d17" 6 0 +"c19" 4 0 +"d19" 4 0 +"e19" 4 0 +"f19" 4 0 +"g19" 4 0 +"h19" 4 0 +"i19" 4 0 +"j19" 4 0 +"k19" 4 0 +"l19" 4 0 +"m19" 4 0 +"n19" 4 0 +"o19" 4 0 +"p19" 4 0 +"q19" 4 0 +"r19" 4 0 +"q17" 6 0 +"t19" 4 0 +"e10" 4 0 +"e18" 4 0 +"p8" 4 0 +"e17" 4 0 +"e16" 4 0 +"e8" 4 0 +"e9" 4 0 +"e15" 4 0 +"e13" 4 0 +"e12" 4 0 +"e11" 4 0 +"e7" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"e14" 4 0 +"f17" 4 0 +"o7" 4 0 +"f6" 4 0 +"i6" 4 0 +"g6" 4 0 +"h6" 4 0 +"k6" 4 0 +"j6" 4 0 +"l6" 4 0 +"m6" 4 0 +"p7" 4 0 +"p9" 4 0 +"p10" 4 0 +"p11" 4 0 +"p12" 4 0 +"p13" 4 0 +"p14" 4 0 +"o15" 4 0 +"p16" 4 0 +"p17" 4 0 +"p18" 4 0 +"q7" 4 0 +"r7" 4 0 +"s7" 4 0 +"t7" 4 0 +"c13" 4 0 +"b13" 4 0 +"a13" 4 0 +"a12" 4 0 +"b12" 4 0 +"c12" 4 0 +"d12" 4 0 +"d11" 4 0 +"c11" 4 0 +"b11" 4 0 +"a11" 4 0 +"a10" 4 0 +"b10" 4 0 +"c10" 4 0 +"d10" 4 0 +"d9" 4 0 +"c9" 4 0 +"b9" 4 0 +"a9" 4 0 +"a8" 4 0 +"b8" 4 0 +"c8" 4 0 +"d8" 4 0 +"d13" 4 0 +"d14" 4 0 +"c14" 4 0 +"b14" 4 0 +"a14" 4 0 +"a15" 4 0 +"b15" 4 0 +"c15" 4 0 +"d15" 4 0 +"d16" 4 0 +"c16" 4 0 +"b16" 4 0 +"a16" 4 0 +"a17" 4 0 +"b17" 4 0 +"c17" 4 0 +"a1" 6 0 +"d18" 4 0 +"b2" 6 0 +"b18" 4 0 +"a18" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"p20" 4 0 +"q20" 4 0 +"r20" 4 0 +"s20" 4 0 +"r18" 6 0 +"s13" 4 0 +"r13" 4 0 +"q13" 4 0 +"q12" 4 0 +"r12" 4 0 +"s12" 4 0 +"t12" 4 0 +"t11" 4 0 +"s11" 4 0 +"r11" 4 0 +"q11" 4 0 +"q10" 4 0 +"r10" 4 0 +"s10" 4 0 +"t10" 4 0 +"t9" 4 0 +"s9" 4 0 +"r9" 4 0 +"q9" 4 0 +"q8" 4 0 +"r8" 4 0 +"s8" 4 0 +"t8" 4 0 +"t13" 4 0 +"t14" 4 0 +"s14" 4 0 +"r14" 4 0 +"q14" 4 0 +"q15" 4 0 +"r15" 4 0 +"s15" 4 0 +"t15" 4 0 +"t16" 4 0 +"s16" 4 0 +"r16" 4 0 +"q16" 4 0 +"g15" 1 1 +"r17" 4 0 +"s17" 4 0 +"t17" 4 0 +"t18" 4 0 +"s18" 4 0 +"m13" 2 0 +"q18" 4 0 +"g3" 4 0 +"f3" 4 0 +"e3" 4 0 +"d3" 4 0 +"t1" 6 0 +"b3" 4 0 +"a3" 4 0 +"a2" 4 0 +"d4" 6 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"k2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"q4" 6 0 +"t2" 4 0 +"r3" 6 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"c3" 6 0 +"t3" 4 0 +"s3" 4 0 +"t20" 6 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"k3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"s2" 6 0 +"c4" 4 0 +"b4" 4 0 +"a4" 4 0 +"a5" 4 0 +"b5" 4 0 +"c5" 4 0 +"d5" 4 0 +"e5" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"p5" 4 0 +"q5" 4 0 +"r5" 4 0 +"s5" 4 0 +"t5" 4 0 +"t4" 4 0 +"s4" 4 0 +"r4" 4 0 +"s19" 6 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"t6" 4 0 +"s6" 4 0 +"r6" 4 0 +"q6" 4 0 +"p6" 4 0 +"o6" 4 0 +"n6" 4 0 +"n7" 4 0 +"l7" 4 0 +"j7" 4 0 +"h7" 4 0 +"f7" 4 0 +"g7" 4 0 +"i7" 4 0 +"k7" 4 0 +"e6" 4 0 +"d6" 4 0 +"c6" 4 0 +"b6" 4 0 +"a6" 4 0 +"p15" 4 0 +"m7" 4 0 +"f16" 5 0 +"f13" 5 0 +"f15" 5 0 +"f14" 5 0 +"f12" 5 0 +"g12" 5 0 +"h12" 5 0 +"h11" 5 0 +"g9" 5 0 +"g8" 5 0 +"g11" 5 0 +"g10" 5 0 +"h8" 5 0 +"i8" 5 0 +"j8" 5 0 +"k8" 5 0 +"k9" 5 0 +"k10" 5 0 +"l9" 5 0 +"m9" 5 0 +"n9" 5 0 +"n10" 5 0 +"n11" 5 0 +"n12" 5 0 +"n13" 5 0 +"n14" 5 0 +"m14" 5 0 +"l14" 5 0 +"j17" 4 0 +"j18" 4 0 +"i18" 4 0 +"h18" 4 0 +"g18" 4 0 +"f18" 4 0 +"l13" 5 0 +"l12" 5 0 +"k12" 5 0 +"j12" 5 0 +"j13" 5 0 +"j15" 5 0 +"j14" 5 0 +"j16" 5 0 +"i16" 5 0 +"h16" 5 0 +"g16" 5 0 +"f10" 4 0 +"f9" 4 0 +"f8" 4 0 +"f11" 4 0 +"o9" 4 0 +"o8" 4 0 +"m8" 4 0 +"l8" 4 0 +"n8" 4 0 +"m15" 4 0 +"l15" 4 0 +"k15" 4 0 +"o10" 4 0 +"k14" 4 0 +"k13" 4 0 +"k16" 4 0 +"o11" 4 0 +"o12" 4 0 +"o13" 4 0 +"o14" 4 0 +"l16" 4 0 +"o16" 4 0 +"o17" 4 0 +"n17" 4 0 +"m17" 4 0 +"l17" 4 0 +"k17" 4 0 +"m16" 4 0 +"n16" 4 0 +"k18" 4 0 +"l18" 4 0 +"m18" 4 0 +"n18" 4 0 +"o18" 4 0 +"n15" 4 0 +"a20" 6 0 +"b19" 6 0 +"m12" 2 0 +"m11" 2 0 +"i14" 3 0 +"h14" 3 0 +"h13" 3 0 +"i17" 4 0 +"h17" 4 0 +"g17" 4 0 diff --git a/minigames/bulldozer/storage_level6.txt b/minigames/bulldozer/storage_level6.txt index 44a8137f4b..a849a97ad5 100644 --- a/minigames/bulldozer/storage_level6.txt +++ b/minigames/bulldozer/storage_level6.txt @@ -1,383 +1,383 @@ // bulldozer storage "level6" last updated 06-12-2015 16:11:35 nextlevel = "level7" -"g13" 5 "0 -1 0" -"f13" 5 "0 -1 0" -"f12" 5 "0 -1 0" -"f11" 5 "0 -1 0" -"f10" 5 "0 -1 0" -"g10" 5 "0 -1 0" -"i9" 5 "0 -1 0" -"g9" 5 "0 -1 0" -"h9" 5 "0 -1 0" -"j9" 5 "0 -1 0" -"k9" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"o12" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"o11" 5 "0 -1 0" -"n14" 5 "0 -1 0" -"o13" 5 "0 -1 0" -"o14" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"l15" 5 "0 -1 0" -"m15" 5 "0 -1 0" -"g14" 5 "0 -1 0" -"g15" 5 "0 -1 0" -"h15" 5 "0 -1 0" -"i15" 5 "0 -1 0" -"j15" 5 "0 -1 0" -"k15" 5 "0 -1 0" -"i13" 5 "0 -1 0" -"j13" 5 "0 -1 0" -"k13" 5 "0 -1 0" -"j10" 5 "0 -1 0" -"j11" 5 "0 -1 0" -"h10" 2 "0 -1 0" -"i10" 2 "0 -1 0" -"h11" 2 "0 -1 0" -"i11" 2 "0 -1 0" -"g12" 1 "0 -1 0" -"h13" 3 "0 -1 0" -"j12" 3 "0 -1 0" -"l11" 3 "0 -1 0" -"m12" 3 "0 -1 0" -"b2" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"f5" 6 "0 -1 0" -"e5" 6 "0 -1 0" -"d5" 6 "0 -1 0" -"c5" 6 "0 -1 0" -"b5" 6 "0 -1 0" -"a5" 6 "0 -1 0" -"a4" 6 "0 -1 0" -"b4" 6 "0 -1 0" -"c4" 6 "0 -1 0" -"d4" 6 "0 -1 0" -"e4" 6 "0 -1 0" -"f4" 6 "0 -1 0" -"g4" 6 "0 -1 0" -"h4" 6 "0 -1 0" -"i4" 6 "0 -1 0" -"j4" 6 "0 -1 0" -"k4" 6 "0 -1 0" -"l4" 6 "0 -1 0" -"m4" 6 "0 -1 0" -"n4" 6 "0 -1 0" -"o4" 6 "0 -1 0" -"p4" 6 "0 -1 0" -"q4" 6 "0 -1 0" -"r4" 6 "0 -1 0" -"s4" 6 "0 -1 0" -"t4" 6 "0 -1 0" -"t3" 6 "0 -1 0" -"t2" 6 "0 -1 0" -"t1" 6 "0 -1 0" -"s1" 6 "0 -1 0" -"r1" 6 "0 -1 0" -"q1" 6 "0 -1 0" -"p1" 6 "0 -1 0" -"o1" 6 "0 -1 0" -"n1" 6 "0 -1 0" -"m1" 6 "0 -1 0" -"l1" 6 "0 -1 0" -"k1" 6 "0 -1 0" -"j1" 6 "0 -1 0" -"i1" 6 "0 -1 0" -"h1" 6 "0 -1 0" -"g1" 6 "0 -1 0" -"f1" 6 "0 -1 0" -"e1" 6 "0 -1 0" -"d1" 6 "0 -1 0" -"c1" 6 "0 -1 0" -"b1" 6 "0 -1 0" -"a1" 6 "0 -1 0" -"a2" 6 "0 -1 0" -"a3" 6 "0 -1 0" -"d2" 6 "0 -1 0" -"e2" 6 "0 -1 0" -"f2" 6 "0 -1 0" -"g2" 6 "0 -1 0" -"h2" 6 "0 -1 0" -"i2" 6 "0 -1 0" -"j2" 6 "0 -1 0" -"k2" 6 "0 -1 0" -"l2" 6 "0 -1 0" -"m2" 6 "0 -1 0" -"n2" 6 "0 -1 0" -"o2" 6 "0 -1 0" -"p2" 6 "0 -1 0" -"q2" 6 "0 -1 0" -"q3" 6 "0 -1 0" -"p3" 6 "0 -1 0" -"o3" 6 "0 -1 0" -"n3" 6 "0 -1 0" -"m3" 6 "0 -1 0" -"l3" 6 "0 -1 0" -"k3" 6 "0 -1 0" -"j3" 6 "0 -1 0" -"i3" 6 "0 -1 0" -"h3" 6 "0 -1 0" -"g3" 6 "0 -1 0" -"f3" 6 "0 -1 0" -"e3" 6 "0 -1 0" -"d3" 6 "0 -1 0" -"t5" 6 "0 -1 0" -"s5" 6 "0 -1 0" -"r5" 6 "0 -1 0" -"q5" 6 "0 -1 0" -"p5" 6 "0 -1 0" -"o5" 6 "0 -1 0" -"n5" 6 "0 -1 0" -"m5" 6 "0 -1 0" -"l5" 6 "0 -1 0" -"k5" 6 "0 -1 0" -"j5" 6 "0 -1 0" -"i5" 6 "0 -1 0" -"h5" 6 "0 -1 0" -"g5" 6 "0 -1 0" -"g6" 6 "0 -1 0" -"f6" 6 "0 -1 0" -"e6" 6 "0 -1 0" -"d6" 6 "0 -1 0" -"c6" 6 "0 -1 0" -"b6" 6 "0 -1 0" -"a6" 6 "0 -1 0" -"a7" 6 "0 -1 0" -"b7" 6 "0 -1 0" -"c7" 6 "0 -1 0" -"d7" 6 "0 -1 0" -"e7" 6 "0 -1 0" -"f7" 6 "0 -1 0" -"g7" 6 "0 -1 0" -"h7" 6 "0 -1 0" -"i7" 6 "0 -1 0" -"j7" 6 "0 -1 0" -"k7" 6 "0 -1 0" -"l7" 6 "0 -1 0" -"m7" 6 "0 -1 0" -"n7" 6 "0 -1 0" -"o7" 6 "0 -1 0" -"p7" 6 "0 -1 0" -"q7" 6 "0 -1 0" -"r7" 6 "0 -1 0" -"s7" 6 "0 -1 0" -"t7" 6 "0 -1 0" -"t6" 6 "0 -1 0" -"s6" 6 "0 -1 0" -"r6" 6 "0 -1 0" -"q6" 6 "0 -1 0" -"p6" 6 "0 -1 0" -"o6" 6 "0 -1 0" -"n6" 6 "0 -1 0" -"m6" 6 "0 -1 0" -"l6" 6 "0 -1 0" -"k6" 6 "0 -1 0" -"j6" 6 "0 -1 0" -"i6" 6 "0 -1 0" -"h6" 6 "0 -1 0" -"t8" 6 "0 -1 0" -"s8" 6 "0 -1 0" -"r8" 6 "0 -1 0" -"q8" 6 "0 -1 0" -"p8" 6 "0 -1 0" -"o8" 6 "0 -1 0" -"n8" 6 "0 -1 0" -"m8" 6 "0 -1 0" -"l8" 6 "0 -1 0" -"k8" 6 "0 -1 0" -"j8" 6 "0 -1 0" -"i8" 6 "0 -1 0" -"h8" 6 "0 -1 0" -"g8" 6 "0 -1 0" -"f8" 6 "0 -1 0" -"e8" 6 "0 -1 0" -"d8" 6 "0 -1 0" -"c8" 6 "0 -1 0" -"b8" 6 "0 -1 0" -"a8" 6 "0 -1 0" -"a9" 6 "0 -1 0" -"b9" 6 "0 -1 0" -"c9" 6 "0 -1 0" -"d9" 6 "0 -1 0" -"e9" 6 "0 -1 0" -"f9" 6 "0 -1 0" -"e10" 6 "0 -1 0" -"d10" 6 "0 -1 0" -"c10" 6 "0 -1 0" -"b10" 6 "0 -1 0" -"a10" 6 "0 -1 0" -"a11" 6 "0 -1 0" -"b11" 6 "0 -1 0" -"c11" 6 "0 -1 0" -"d11" 6 "0 -1 0" -"e11" 6 "0 -1 0" -"e12" 6 "0 -1 0" -"d12" 6 "0 -1 0" -"c12" 6 "0 -1 0" -"b12" 6 "0 -1 0" -"a12" 6 "0 -1 0" -"a13" 6 "0 -1 0" -"b13" 6 "0 -1 0" -"c13" 6 "0 -1 0" -"d13" 6 "0 -1 0" -"e13" 6 "0 -1 0" -"e14" 6 "0 -1 0" -"d14" 6 "0 -1 0" -"c14" 6 "0 -1 0" -"b14" 6 "0 -1 0" -"a14" 6 "0 -1 0" -"a15" 6 "0 -1 0" -"b15" 6 "0 -1 0" -"c15" 6 "0 -1 0" -"d15" 6 "0 -1 0" -"e15" 6 "0 -1 0" -"f15" 6 "0 -1 0" -"f14" 6 "0 -1 0" -"f16" 6 "0 -1 0" -"e16" 6 "0 -1 0" -"d16" 6 "0 -1 0" -"c16" 6 "0 -1 0" -"b16" 6 "0 -1 0" -"a16" 6 "0 -1 0" -"a17" 6 "0 -1 0" -"b17" 6 "0 -1 0" -"c17" 6 "0 -1 0" -"d17" 6 "0 -1 0" -"e17" 6 "0 -1 0" -"f17" 6 "0 -1 0" -"g17" 6 "0 -1 0" -"h17" 6 "0 -1 0" -"i17" 6 "0 -1 0" -"j17" 6 "0 -1 0" -"k17" 6 "0 -1 0" -"l17" 6 "0 -1 0" -"m17" 6 "0 -1 0" -"n17" 6 "0 -1 0" -"o17" 6 "0 -1 0" -"p17" 6 "0 -1 0" -"q17" 6 "0 -1 0" -"r17" 6 "0 -1 0" -"s17" 6 "0 -1 0" -"t17" 6 "0 -1 0" -"t16" 6 "0 -1 0" -"s16" 6 "0 -1 0" -"r16" 6 "0 -1 0" -"q16" 6 "0 -1 0" -"p16" 6 "0 -1 0" -"o16" 6 "0 -1 0" -"n16" 6 "0 -1 0" -"m16" 6 "0 -1 0" -"l16" 6 "0 -1 0" -"k16" 6 "0 -1 0" -"j16" 6 "0 -1 0" -"i16" 6 "0 -1 0" -"h16" 6 "0 -1 0" -"g16" 6 "0 -1 0" -"n15" 6 "0 -1 0" -"o15" 6 "0 -1 0" -"p15" 6 "0 -1 0" -"q15" 6 "0 -1 0" -"r15" 6 "0 -1 0" -"s15" 6 "0 -1 0" -"t15" 6 "0 -1 0" -"t14" 6 "0 -1 0" -"s14" 6 "0 -1 0" -"r14" 6 "0 -1 0" -"q14" 6 "0 -1 0" -"p14" 6 "0 -1 0" -"p13" 6 "0 -1 0" -"q13" 6 "0 -1 0" -"r13" 6 "0 -1 0" -"s13" 6 "0 -1 0" -"t13" 6 "0 -1 0" -"t12" 6 "0 -1 0" -"s12" 6 "0 -1 0" -"r12" 6 "0 -1 0" -"q12" 6 "0 -1 0" -"p12" 6 "0 -1 0" -"p11" 6 "0 -1 0" -"q11" 6 "0 -1 0" -"r11" 6 "0 -1 0" -"s11" 6 "0 -1 0" -"t11" 6 "0 -1 0" -"t10" 6 "0 -1 0" -"s10" 6 "0 -1 0" -"r10" 6 "0 -1 0" -"q10" 6 "0 -1 0" -"p10" 6 "0 -1 0" -"o10" 6 "0 -1 0" -"o9" 6 "0 -1 0" -"p9" 6 "0 -1 0" -"q9" 6 "0 -1 0" -"r9" 6 "0 -1 0" -"s9" 6 "0 -1 0" -"t9" 6 "0 -1 0" -"t18" 6 "0 -1 0" -"t19" 6 "0 -1 0" -"t20" 6 "0 -1 0" -"s20" 6 "0 -1 0" -"r20" 6 "0 -1 0" -"q20" 6 "0 -1 0" -"p20" 6 "0 -1 0" -"o20" 6 "0 -1 0" -"n20" 6 "0 -1 0" -"m20" 6 "0 -1 0" -"l20" 6 "0 -1 0" -"k20" 6 "0 -1 0" -"j20" 6 "0 -1 0" -"i20" 6 "0 -1 0" -"h20" 6 "0 -1 0" -"g20" 6 "0 -1 0" -"f20" 6 "0 -1 0" -"e20" 6 "0 -1 0" -"d20" 6 "0 -1 0" -"c20" 6 "0 -1 0" -"b20" 6 "0 -1 0" -"a20" 6 "0 -1 0" -"a19" 6 "0 -1 0" -"a18" 6 "0 -1 0" -"d19" 6 "0 -1 0" -"e19" 6 "0 -1 0" -"f19" 6 "0 -1 0" -"g19" 6 "0 -1 0" -"h19" 6 "0 -1 0" -"i19" 6 "0 -1 0" -"j19" 6 "0 -1 0" -"k19" 6 "0 -1 0" -"l19" 6 "0 -1 0" -"m19" 6 "0 -1 0" -"n19" 6 "0 -1 0" -"o19" 6 "0 -1 0" -"p19" 6 "0 -1 0" -"q19" 6 "0 -1 0" -"q18" 6 "0 -1 0" -"p18" 6 "0 -1 0" -"o18" 6 "0 -1 0" -"n18" 6 "0 -1 0" -"m18" 6 "0 -1 0" -"l18" 6 "0 -1 0" -"k18" 6 "0 -1 0" -"j18" 6 "0 -1 0" -"i18" 6 "0 -1 0" -"h18" 6 "0 -1 0" -"g18" 6 "0 -1 0" -"f18" 6 "0 -1 0" -"e18" 6 "0 -1 0" -"d18" 6 "0 -1 0" +"g13" 5 0 +"f13" 5 0 +"f12" 5 0 +"f11" 5 0 +"f10" 5 0 +"g10" 5 0 +"i9" 5 0 +"g9" 5 0 +"h9" 5 0 +"j9" 5 0 +"k9" 5 0 +"l9" 5 0 +"m9" 5 0 +"n9" 5 0 +"n10" 5 0 +"o12" 5 0 +"n11" 5 0 +"o11" 5 0 +"n14" 5 0 +"o13" 5 0 +"o14" 5 0 +"m14" 5 0 +"l15" 5 0 +"m15" 5 0 +"g14" 5 0 +"g15" 5 0 +"h15" 5 0 +"i15" 5 0 +"j15" 5 0 +"k15" 5 0 +"i13" 5 0 +"j13" 5 0 +"k13" 5 0 +"j10" 5 0 +"j11" 5 0 +"h10" 2 0 +"i10" 2 0 +"h11" 2 0 +"i11" 2 0 +"g12" 1 1 +"h13" 3 0 +"j12" 3 0 +"l11" 3 0 +"m12" 3 0 +"b2" 4 0 +"b3" 4 0 +"c3" 4 0 +"c2" 4 0 +"b19" 4 0 +"b18" 4 0 +"c18" 4 0 +"c19" 4 0 +"r19" 4 0 +"r18" 4 0 +"s18" 4 0 +"s19" 4 0 +"r2" 4 0 +"r3" 4 0 +"s3" 4 0 +"s2" 4 0 +"f5" 6 0 +"e5" 6 0 +"d5" 6 0 +"c5" 6 0 +"b5" 6 0 +"a5" 6 0 +"a4" 6 0 +"b4" 6 0 +"c4" 6 0 +"d4" 6 0 +"e4" 6 0 +"f4" 6 0 +"g4" 6 0 +"h4" 6 0 +"i4" 6 0 +"j4" 6 0 +"k4" 6 0 +"l4" 6 0 +"m4" 6 0 +"n4" 6 0 +"o4" 6 0 +"p4" 6 0 +"q4" 6 0 +"r4" 6 0 +"s4" 6 0 +"t4" 6 0 +"t3" 6 0 +"t2" 6 0 +"t1" 6 0 +"s1" 6 0 +"r1" 6 0 +"q1" 6 0 +"p1" 6 0 +"o1" 6 0 +"n1" 6 0 +"m1" 6 0 +"l1" 6 0 +"k1" 6 0 +"j1" 6 0 +"i1" 6 0 +"h1" 6 0 +"g1" 6 0 +"f1" 6 0 +"e1" 6 0 +"d1" 6 0 +"c1" 6 0 +"b1" 6 0 +"a1" 6 0 +"a2" 6 0 +"a3" 6 0 +"d2" 6 0 +"e2" 6 0 +"f2" 6 0 +"g2" 6 0 +"h2" 6 0 +"i2" 6 0 +"j2" 6 0 +"k2" 6 0 +"l2" 6 0 +"m2" 6 0 +"n2" 6 0 +"o2" 6 0 +"p2" 6 0 +"q2" 6 0 +"q3" 6 0 +"p3" 6 0 +"o3" 6 0 +"n3" 6 0 +"m3" 6 0 +"l3" 6 0 +"k3" 6 0 +"j3" 6 0 +"i3" 6 0 +"h3" 6 0 +"g3" 6 0 +"f3" 6 0 +"e3" 6 0 +"d3" 6 0 +"t5" 6 0 +"s5" 6 0 +"r5" 6 0 +"q5" 6 0 +"p5" 6 0 +"o5" 6 0 +"n5" 6 0 +"m5" 6 0 +"l5" 6 0 +"k5" 6 0 +"j5" 6 0 +"i5" 6 0 +"h5" 6 0 +"g5" 6 0 +"g6" 6 0 +"f6" 6 0 +"e6" 6 0 +"d6" 6 0 +"c6" 6 0 +"b6" 6 0 +"a6" 6 0 +"a7" 6 0 +"b7" 6 0 +"c7" 6 0 +"d7" 6 0 +"e7" 6 0 +"f7" 6 0 +"g7" 6 0 +"h7" 6 0 +"i7" 6 0 +"j7" 6 0 +"k7" 6 0 +"l7" 6 0 +"m7" 6 0 +"n7" 6 0 +"o7" 6 0 +"p7" 6 0 +"q7" 6 0 +"r7" 6 0 +"s7" 6 0 +"t7" 6 0 +"t6" 6 0 +"s6" 6 0 +"r6" 6 0 +"q6" 6 0 +"p6" 6 0 +"o6" 6 0 +"n6" 6 0 +"m6" 6 0 +"l6" 6 0 +"k6" 6 0 +"j6" 6 0 +"i6" 6 0 +"h6" 6 0 +"t8" 6 0 +"s8" 6 0 +"r8" 6 0 +"q8" 6 0 +"p8" 6 0 +"o8" 6 0 +"n8" 6 0 +"m8" 6 0 +"l8" 6 0 +"k8" 6 0 +"j8" 6 0 +"i8" 6 0 +"h8" 6 0 +"g8" 6 0 +"f8" 6 0 +"e8" 6 0 +"d8" 6 0 +"c8" 6 0 +"b8" 6 0 +"a8" 6 0 +"a9" 6 0 +"b9" 6 0 +"c9" 6 0 +"d9" 6 0 +"e9" 6 0 +"f9" 6 0 +"e10" 6 0 +"d10" 6 0 +"c10" 6 0 +"b10" 6 0 +"a10" 6 0 +"a11" 6 0 +"b11" 6 0 +"c11" 6 0 +"d11" 6 0 +"e11" 6 0 +"e12" 6 0 +"d12" 6 0 +"c12" 6 0 +"b12" 6 0 +"a12" 6 0 +"a13" 6 0 +"b13" 6 0 +"c13" 6 0 +"d13" 6 0 +"e13" 6 0 +"e14" 6 0 +"d14" 6 0 +"c14" 6 0 +"b14" 6 0 +"a14" 6 0 +"a15" 6 0 +"b15" 6 0 +"c15" 6 0 +"d15" 6 0 +"e15" 6 0 +"f15" 6 0 +"f14" 6 0 +"f16" 6 0 +"e16" 6 0 +"d16" 6 0 +"c16" 6 0 +"b16" 6 0 +"a16" 6 0 +"a17" 6 0 +"b17" 6 0 +"c17" 6 0 +"d17" 6 0 +"e17" 6 0 +"f17" 6 0 +"g17" 6 0 +"h17" 6 0 +"i17" 6 0 +"j17" 6 0 +"k17" 6 0 +"l17" 6 0 +"m17" 6 0 +"n17" 6 0 +"o17" 6 0 +"p17" 6 0 +"q17" 6 0 +"r17" 6 0 +"s17" 6 0 +"t17" 6 0 +"t16" 6 0 +"s16" 6 0 +"r16" 6 0 +"q16" 6 0 +"p16" 6 0 +"o16" 6 0 +"n16" 6 0 +"m16" 6 0 +"l16" 6 0 +"k16" 6 0 +"j16" 6 0 +"i16" 6 0 +"h16" 6 0 +"g16" 6 0 +"n15" 6 0 +"o15" 6 0 +"p15" 6 0 +"q15" 6 0 +"r15" 6 0 +"s15" 6 0 +"t15" 6 0 +"t14" 6 0 +"s14" 6 0 +"r14" 6 0 +"q14" 6 0 +"p14" 6 0 +"p13" 6 0 +"q13" 6 0 +"r13" 6 0 +"s13" 6 0 +"t13" 6 0 +"t12" 6 0 +"s12" 6 0 +"r12" 6 0 +"q12" 6 0 +"p12" 6 0 +"p11" 6 0 +"q11" 6 0 +"r11" 6 0 +"s11" 6 0 +"t11" 6 0 +"t10" 6 0 +"s10" 6 0 +"r10" 6 0 +"q10" 6 0 +"p10" 6 0 +"o10" 6 0 +"o9" 6 0 +"p9" 6 0 +"q9" 6 0 +"r9" 6 0 +"s9" 6 0 +"t9" 6 0 +"t18" 6 0 +"t19" 6 0 +"t20" 6 0 +"s20" 6 0 +"r20" 6 0 +"q20" 6 0 +"p20" 6 0 +"o20" 6 0 +"n20" 6 0 +"m20" 6 0 +"l20" 6 0 +"k20" 6 0 +"j20" 6 0 +"i20" 6 0 +"h20" 6 0 +"g20" 6 0 +"f20" 6 0 +"e20" 6 0 +"d20" 6 0 +"c20" 6 0 +"b20" 6 0 +"a20" 6 0 +"a19" 6 0 +"a18" 6 0 +"d19" 6 0 +"e19" 6 0 +"f19" 6 0 +"g19" 6 0 +"h19" 6 0 +"i19" 6 0 +"j19" 6 0 +"k19" 6 0 +"l19" 6 0 +"m19" 6 0 +"n19" 6 0 +"o19" 6 0 +"p19" 6 0 +"q19" 6 0 +"q18" 6 0 +"p18" 6 0 +"o18" 6 0 +"n18" 6 0 +"m18" 6 0 +"l18" 6 0 +"k18" 6 0 +"j18" 6 0 +"i18" 6 0 +"h18" 6 0 +"g18" 6 0 +"f18" 6 0 +"e18" 6 0 +"d18" 6 0 diff --git a/minigames/bulldozer/storage_level7.txt b/minigames/bulldozer/storage_level7.txt index 5f351c629c..a6473ae0ea 100644 --- a/minigames/bulldozer/storage_level7.txt +++ b/minigames/bulldozer/storage_level7.txt @@ -1,386 +1,386 @@ // bulldozer storage "level7" last updated 06-12-2015 16:19:03 nextlevel = "level8" -"g10" 5 "0 -1 0" -"g9" 5 "0 -1 0" -"g8" 5 "0 -1 0" -"g11" 5 "0 -1 0" -"g12" 5 "0 -1 0" -"h8" 5 "0 -1 0" -"i8" 5 "0 -1 0" -"j8" 5 "0 -1 0" -"l8" 5 "0 -1 0" -"k8" 5 "0 -1 0" -"m8" 5 "0 -1 0" -"n8" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"n10" 5 "0 -1 0" -"n11" 5 "0 -1 0" -"n12" 5 "0 -1 0" -"m12" 5 "0 -1 0" -"m13" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"h12" 5 "0 -1 0" -"h13" 5 "0 -1 0" -"h14" 5 "0 -1 0" -"h15" 5 "0 -1 0" -"i15" 5 "0 -1 0" -"j15" 5 "0 -1 0" -"k15" 5 "0 -1 0" -"k14" 5 "0 -1 0" -"l14" 5 "0 -1 0" -"k12" 5 "0 -1 0" -"k11" 5 "0 -1 0" -"i12" 5 "0 -1 0" -"i11" 5 "0 -1 0" -"l10" 5 "0 -1 0" -"j14" 1 "0 -1 0" -"j13" 3 "0 -1 0" -"i10" 3 "0 -1 0" -"l9" 3 "0 -1 0" -"h11" 2 "0 -1 0" -"h10" 2 "0 -1 0" -"h9" 2 "0 -1 0" -"a19" 6 "0 -1 0" -"b20" 6 "0 -1 0" -"b18" 6 "0 -1 0" -"a17" 6 "0 -1 0" -"b16" 6 "0 -1 0" -"a15" 6 "0 -1 0" -"b14" 6 "0 -1 0" -"a13" 6 "0 -1 0" -"b12" 6 "0 -1 0" -"a10" 11 "0 -1 0" -"b10" 6 "0 -1 0" -"a7" 6 "0 -1 0" -"a9" 6 "0 -1 0" -"b8" 6 "0 -1 0" -"b6" 6 "0 -1 0" -"a5" 6 "0 -1 0" -"b4" 6 "0 -1 0" -"a3" 6 "0 -1 0" -"b2" 6 "0 -1 0" -"a1" 6 "0 -1 0" -"c1" 6 "0 -1 0" -"d2" 6 "0 -1 0" -"e1" 6 "0 -1 0" -"c3" 6 "0 -1 0" -"c5" 6 "0 -1 0" -"c7" 6 "0 -1 0" -"c9" 6 "0 -1 0" -"c11" 6 "0 -1 0" -"c13" 6 "0 -1 0" -"c15" 6 "0 -1 0" -"c17" 6 "0 -1 0" -"c19" 6 "0 -1 0" -"d20" 6 "0 -1 0" -"d18" 6 "0 -1 0" -"d16" 6 "0 -1 0" -"d14" 6 "0 -1 0" -"d12" 6 "0 -1 0" -"d10" 6 "0 -1 0" -"d8" 6 "0 -1 0" -"d6" 6 "0 -1 0" -"d4" 6 "0 -1 0" -"e3" 6 "0 -1 0" -"e9" 6 "0 -1 0" -"e5" 6 "0 -1 0" -"e7" 6 "0 -1 0" -"e11" 6 "0 -1 0" -"e13" 6 "0 -1 0" -"e15" 6 "0 -1 0" -"e17" 6 "0 -1 0" -"e19" 6 "0 -1 0" -"a20" 11 "0 -1 0" -"b19" 11 "0 -1 0" -"a18" 11 "0 -1 0" -"a16" 11 "0 -1 0" -"a14" 11 "0 -1 0" -"a12" 11 "0 -1 0" -"a8" 11 "0 -1 0" -"a6" 11 "0 -1 0" -"a4" 11 "0 -1 0" -"a2" 11 "0 -1 0" -"b1" 11 "0 -1 0" -"b3" 11 "0 -1 0" -"b5" 11 "0 -1 0" -"b7" 11 "0 -1 0" -"b9" 11 "0 -1 0" -"b11" 11 "0 -1 0" -"b13" 11 "0 -1 0" -"b15" 11 "0 -1 0" -"b17" 11 "0 -1 0" -"c20" 11 "0 -1 0" -"c18" 11 "0 -1 0" -"c16" 11 "0 -1 0" -"c14" 11 "0 -1 0" -"c12" 11 "0 -1 0" -"c10" 11 "0 -1 0" -"c8" 11 "0 -1 0" -"c6" 11 "0 -1 0" -"c4" 11 "0 -1 0" -"c2" 11 "0 -1 0" -"d1" 11 "0 -1 0" -"d3" 11 "0 -1 0" -"d5" 11 "0 -1 0" -"d7" 11 "0 -1 0" -"d9" 11 "0 -1 0" -"d11" 11 "0 -1 0" -"d13" 11 "0 -1 0" -"d15" 11 "0 -1 0" -"d17" 11 "0 -1 0" -"d19" 11 "0 -1 0" -"e20" 11 "0 -1 0" -"e18" 11 "0 -1 0" -"e16" 11 "0 -1 0" -"e14" 11 "0 -1 0" -"e12" 11 "0 -1 0" -"e10" 11 "0 -1 0" -"e8" 11 "0 -1 0" -"e6" 11 "0 -1 0" -"e4" 11 "0 -1 0" -"e2" 11 "0 -1 0" -"a11" 6 "0 -1 0" -"p1" 6 "0 -1 0" -"p3" 6 "0 -1 0" -"p5" 6 "0 -1 0" -"p7" 6 "0 -1 0" -"p9" 6 "0 -1 0" -"p15" 6 "0 -1 0" -"p11" 6 "0 -1 0" -"p13" 6 "0 -1 0" -"q20" 6 "0 -1 0" -"p17" 6 "0 -1 0" -"p19" 6 "0 -1 0" -"q18" 6 "0 -1 0" -"q16" 6 "0 -1 0" -"q14" 6 "0 -1 0" -"q12" 6 "0 -1 0" -"q10" 6 "0 -1 0" -"q8" 6 "0 -1 0" -"q6" 6 "0 -1 0" -"q4" 6 "0 -1 0" -"q2" 6 "0 -1 0" -"r1" 6 "0 -1 0" -"r3" 6 "0 -1 0" -"r5" 6 "0 -1 0" -"r7" 6 "0 -1 0" -"r9" 6 "0 -1 0" -"r11" 6 "0 -1 0" -"r13" 6 "0 -1 0" -"r15" 6 "0 -1 0" -"r17" 6 "0 -1 0" -"r19" 6 "0 -1 0" -"s20" 6 "0 -1 0" -"s18" 6 "0 -1 0" -"s16" 6 "0 -1 0" -"s14" 6 "0 -1 0" -"s12" 6 "0 -1 0" -"s10" 6 "0 -1 0" -"s8" 6 "0 -1 0" -"s6" 6 "0 -1 0" -"s4" 6 "0 -1 0" -"s2" 6 "0 -1 0" -"t1" 6 "0 -1 0" -"t3" 6 "0 -1 0" -"t5" 6 "0 -1 0" -"t7" 6 "0 -1 0" -"t9" 6 "0 -1 0" -"t11" 6 "0 -1 0" -"t13" 6 "0 -1 0" -"t14" 11 "0 -1 0" -"t17" 6 "0 -1 0" -"t19" 6 "0 -1 0" -"p20" 11 "0 -1 0" -"p18" 11 "0 -1 0" -"p16" 11 "0 -1 0" -"p14" 11 "0 -1 0" -"p12" 11 "0 -1 0" -"p10" 11 "0 -1 0" -"p8" 11 "0 -1 0" -"p6" 11 "0 -1 0" -"p4" 11 "0 -1 0" -"p2" 11 "0 -1 0" -"q1" 11 "0 -1 0" -"q3" 11 "0 -1 0" -"q5" 11 "0 -1 0" -"q7" 11 "0 -1 0" -"q9" 11 "0 -1 0" -"q11" 11 "0 -1 0" -"q13" 11 "0 -1 0" -"q15" 11 "0 -1 0" -"q17" 11 "0 -1 0" -"q19" 11 "0 -1 0" -"r20" 11 "0 -1 0" -"r18" 11 "0 -1 0" -"r16" 11 "0 -1 0" -"r14" 11 "0 -1 0" -"r12" 11 "0 -1 0" -"r10" 11 "0 -1 0" -"r8" 11 "0 -1 0" -"r6" 11 "0 -1 0" -"r4" 11 "0 -1 0" -"r2" 11 "0 -1 0" -"s1" 11 "0 -1 0" -"s3" 11 "0 -1 0" -"s5" 11 "0 -1 0" -"s7" 11 "0 -1 0" -"s9" 11 "0 -1 0" -"s11" 11 "0 -1 0" -"s13" 11 "0 -1 0" -"s15" 11 "0 -1 0" -"s17" 11 "0 -1 0" -"s19" 11 "0 -1 0" -"t20" 11 "0 -1 0" -"t18" 11 "0 -1 0" -"t16" 11 "0 -1 0" -"t10" 11 "0 -1 0" -"t12" 11 "0 -1 0" -"t8" 11 "0 -1 0" -"t6" 11 "0 -1 0" -"t4" 11 "0 -1 0" -"t2" 11 "0 -1 0" -"t15" 6 "0 -1 0" -"h18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"m16" 4 "0 -1 0" -"l16" 4 "0 -1 0" -"k16" 4 "0 -1 0" -"j16" 4 "0 -1 0" -"i16" 4 "0 -1 0" -"h16" 4 "0 -1 0" -"g16" 4 "0 -1 0" -"f16" 4 "0 -1 0" -"f15" 4 "0 -1 0" -"g15" 4 "0 -1 0" -"g14" 4 "0 -1 0" -"f14" 4 "0 -1 0" -"f13" 4 "0 -1 0" -"g13" 4 "0 -1 0" -"f12" 4 "0 -1 0" -"f11" 4 "0 -1 0" -"f10" 4 "0 -1 0" -"f9" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"o9" 4 "0 -1 0" -"o10" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"o12" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"n13" 4 "0 -1 0" -"n14" 4 "0 -1 0" -"o14" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"n15" 4 "0 -1 0" -"m15" 4 "0 -1 0" -"l15" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"j19" 4 "0 -1 0" +"g10" 5 0 +"g9" 5 0 +"g8" 5 0 +"g11" 5 0 +"g12" 5 0 +"h8" 5 0 +"i8" 5 0 +"j8" 5 0 +"l8" 5 0 +"k8" 5 0 +"m8" 5 0 +"n8" 5 0 +"n9" 5 0 +"n10" 5 0 +"n11" 5 0 +"n12" 5 0 +"m12" 5 0 +"m13" 5 0 +"m14" 5 0 +"h12" 5 0 +"h13" 5 0 +"h14" 5 0 +"h15" 5 0 +"i15" 5 0 +"j15" 5 0 +"k15" 5 0 +"k14" 5 0 +"l14" 5 0 +"k12" 5 0 +"k11" 5 0 +"i12" 5 0 +"i11" 5 0 +"l10" 5 0 +"j14" 1 1 +"j13" 3 0 +"i10" 3 0 +"l9" 3 0 +"h11" 2 0 +"h10" 2 0 +"h9" 2 0 +"a19" 6 0 +"b20" 6 0 +"b18" 6 0 +"a17" 6 0 +"b16" 6 0 +"a15" 6 0 +"b14" 6 0 +"a13" 6 0 +"b12" 6 0 +"a10" 11 0 +"b10" 6 0 +"a7" 6 0 +"a9" 6 0 +"b8" 6 0 +"b6" 6 0 +"a5" 6 0 +"b4" 6 0 +"a3" 6 0 +"b2" 6 0 +"a1" 6 0 +"c1" 6 0 +"d2" 6 0 +"e1" 6 0 +"c3" 6 0 +"c5" 6 0 +"c7" 6 0 +"c9" 6 0 +"c11" 6 0 +"c13" 6 0 +"c15" 6 0 +"c17" 6 0 +"c19" 6 0 +"d20" 6 0 +"d18" 6 0 +"d16" 6 0 +"d14" 6 0 +"d12" 6 0 +"d10" 6 0 +"d8" 6 0 +"d6" 6 0 +"d4" 6 0 +"e3" 6 0 +"e9" 6 0 +"e5" 6 0 +"e7" 6 0 +"e11" 6 0 +"e13" 6 0 +"e15" 6 0 +"e17" 6 0 +"e19" 6 0 +"a20" 11 0 +"b19" 11 0 +"a18" 11 0 +"a16" 11 0 +"a14" 11 0 +"a12" 11 0 +"a8" 11 0 +"a6" 11 0 +"a4" 11 0 +"a2" 11 0 +"b1" 11 0 +"b3" 11 0 +"b5" 11 0 +"b7" 11 0 +"b9" 11 0 +"b11" 11 0 +"b13" 11 0 +"b15" 11 0 +"b17" 11 0 +"c20" 11 0 +"c18" 11 0 +"c16" 11 0 +"c14" 11 0 +"c12" 11 0 +"c10" 11 0 +"c8" 11 0 +"c6" 11 0 +"c4" 11 0 +"c2" 11 0 +"d1" 11 0 +"d3" 11 0 +"d5" 11 0 +"d7" 11 0 +"d9" 11 0 +"d11" 11 0 +"d13" 11 0 +"d15" 11 0 +"d17" 11 0 +"d19" 11 0 +"e20" 11 0 +"e18" 11 0 +"e16" 11 0 +"e14" 11 0 +"e12" 11 0 +"e10" 11 0 +"e8" 11 0 +"e6" 11 0 +"e4" 11 0 +"e2" 11 0 +"a11" 6 0 +"p1" 6 0 +"p3" 6 0 +"p5" 6 0 +"p7" 6 0 +"p9" 6 0 +"p15" 6 0 +"p11" 6 0 +"p13" 6 0 +"q20" 6 0 +"p17" 6 0 +"p19" 6 0 +"q18" 6 0 +"q16" 6 0 +"q14" 6 0 +"q12" 6 0 +"q10" 6 0 +"q8" 6 0 +"q6" 6 0 +"q4" 6 0 +"q2" 6 0 +"r1" 6 0 +"r3" 6 0 +"r5" 6 0 +"r7" 6 0 +"r9" 6 0 +"r11" 6 0 +"r13" 6 0 +"r15" 6 0 +"r17" 6 0 +"r19" 6 0 +"s20" 6 0 +"s18" 6 0 +"s16" 6 0 +"s14" 6 0 +"s12" 6 0 +"s10" 6 0 +"s8" 6 0 +"s6" 6 0 +"s4" 6 0 +"s2" 6 0 +"t1" 6 0 +"t3" 6 0 +"t5" 6 0 +"t7" 6 0 +"t9" 6 0 +"t11" 6 0 +"t13" 6 0 +"t14" 11 0 +"t17" 6 0 +"t19" 6 0 +"p20" 11 0 +"p18" 11 0 +"p16" 11 0 +"p14" 11 0 +"p12" 11 0 +"p10" 11 0 +"p8" 11 0 +"p6" 11 0 +"p4" 11 0 +"p2" 11 0 +"q1" 11 0 +"q3" 11 0 +"q5" 11 0 +"q7" 11 0 +"q9" 11 0 +"q11" 11 0 +"q13" 11 0 +"q15" 11 0 +"q17" 11 0 +"q19" 11 0 +"r20" 11 0 +"r18" 11 0 +"r16" 11 0 +"r14" 11 0 +"r12" 11 0 +"r10" 11 0 +"r8" 11 0 +"r6" 11 0 +"r4" 11 0 +"r2" 11 0 +"s1" 11 0 +"s3" 11 0 +"s5" 11 0 +"s7" 11 0 +"s9" 11 0 +"s11" 11 0 +"s13" 11 0 +"s15" 11 0 +"s17" 11 0 +"s19" 11 0 +"t20" 11 0 +"t18" 11 0 +"t16" 11 0 +"t10" 11 0 +"t12" 11 0 +"t8" 11 0 +"t6" 11 0 +"t4" 11 0 +"t2" 11 0 +"t15" 6 0 +"h18" 4 0 +"g18" 4 0 +"f18" 4 0 +"f17" 4 0 +"g17" 4 0 +"h17" 4 0 +"i17" 4 0 +"j17" 4 0 +"k17" 4 0 +"l17" 4 0 +"m17" 4 0 +"n17" 4 0 +"o17" 4 0 +"o16" 4 0 +"n16" 4 0 +"m16" 4 0 +"l16" 4 0 +"k16" 4 0 +"j16" 4 0 +"i16" 4 0 +"h16" 4 0 +"g16" 4 0 +"f16" 4 0 +"f15" 4 0 +"g15" 4 0 +"g14" 4 0 +"f14" 4 0 +"f13" 4 0 +"g13" 4 0 +"f12" 4 0 +"f11" 4 0 +"f10" 4 0 +"f9" 4 0 +"f8" 4 0 +"f7" 4 0 +"g7" 4 0 +"h7" 4 0 +"i7" 4 0 +"j7" 4 0 +"k7" 4 0 +"l7" 4 0 +"m7" 4 0 +"n7" 4 0 +"o7" 4 0 +"o6" 4 0 +"n6" 4 0 +"m6" 4 0 +"l6" 4 0 +"k6" 4 0 +"j6" 4 0 +"i6" 4 0 +"h6" 4 0 +"g6" 4 0 +"f6" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"o8" 4 0 +"o9" 4 0 +"o10" 4 0 +"o11" 4 0 +"o12" 4 0 +"o13" 4 0 +"n13" 4 0 +"n14" 4 0 +"o14" 4 0 +"o15" 4 0 +"n15" 4 0 +"m15" 4 0 +"l15" 4 0 +"o18" 4 0 +"n18" 4 0 +"m18" 4 0 +"l18" 4 0 +"k18" 4 0 +"j18" 4 0 +"i18" 4 0 +"i19" 4 0 +"h19" 4 0 +"g19" 4 0 +"f19" 4 0 +"f20" 4 0 +"g20" 4 0 +"h20" 4 0 +"i20" 4 0 +"j20" 4 0 +"k20" 4 0 +"l20" 4 0 +"m20" 4 0 +"n20" 4 0 +"o20" 4 0 +"o19" 4 0 +"n19" 4 0 +"m19" 4 0 +"l19" 4 0 +"k19" 4 0 +"j19" 4 0 diff --git a/minigames/bulldozer/storage_level8.txt b/minigames/bulldozer/storage_level8.txt index 2d461a0b2c..6d616e1e31 100644 --- a/minigames/bulldozer/storage_level8.txt +++ b/minigames/bulldozer/storage_level8.txt @@ -1,383 +1,383 @@ // bulldozer storage "level8" last updated 06-12-2015 16:23:34 nextlevel = "level9" -"e13" 5 "0 -1 0" -"e12" 5 "0 -1 0" -"e11" 5 "0 -1 0" -"e10" 5 "0 -1 0" -"e9" 5 "0 -1 0" -"f9" 5 "0 -1 0" -"g9" 5 "0 -1 0" -"h9" 5 "0 -1 0" -"i10" 5 "0 -1 0" -"i9" 5 "0 -1 0" -"i11" 5 "0 -1 0" -"j11" 5 "0 -1 0" -"k11" 5 "0 -1 0" -"l11" 5 "0 -1 0" -"l10" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"n9" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"o9" 5 "0 -1 0" -"o10" 5 "0 -1 0" -"p10" 5 "0 -1 0" -"p11" 5 "0 -1 0" -"p12" 5 "0 -1 0" -"o13" 5 "0 -1 0" -"p13" 5 "0 -1 0" -"o14" 5 "0 -1 0" -"n14" 5 "0 -1 0" -"l14" 5 "0 -1 0" -"f14" 5 "0 -1 0" -"g14" 5 "0 -1 0" -"h14" 5 "0 -1 0" -"i14" 5 "0 -1 0" -"j14" 5 "0 -1 0" -"m14" 5 "0 -1 0" -"k14" 5 "0 -1 0" -"f13" 5 "0 -1 0" -"j13" 5 "0 -1 0" -"i12" 2 "0 -1 0" -"l12" 2 "0 -1 0" -"j12" 2 "0 -1 0" -"k12" 2 "0 -1 0" -"h13" 1 "0 -1 0" -"g12" 3 "0 -1 0" -"h11" 3 "0 -1 0" -"m12" 3 "0 -1 0" -"n11" 3 "0 -1 0" -"d13" 8 "0 -1 0" -"d15" 8 "0 -1 0" -"b16" 8 "0 -1 0" -"b14" 8 "0 -1 0" -"b12" 8 "0 -1 0" -"b10" 8 "0 -1 0" -"b8" 8 "0 -1 0" -"d11" 8 "0 -1 0" -"d9" 8 "0 -1 0" -"d7" 8 "0 -1 0" -"q10" 8 "0 -1 0" -"q8" 8 "0 -1 0" -"s15" 8 "0 -1 0" -"s7" 8 "0 -1 0" -"s9" 8 "0 -1 0" -"s11" 8 "0 -1 0" -"s13" 8 "0 -1 0" -"q12" 8 "0 -1 0" -"q14" 8 "0 -1 0" -"q16" 8 "0 -1 0" -"h17" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"f17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"c14" 4 "0 -1 0" -"d14" 4 "0 -1 0" -"e14" 4 "0 -1 0" -"e15" 4 "0 -1 0" -"f15" 4 "0 -1 0" -"g15" 4 "0 -1 0" -"h15" 4 "0 -1 0" -"i15" 4 "0 -1 0" -"j15" 4 "0 -1 0" -"k15" 4 "0 -1 0" -"l15" 4 "0 -1 0" -"m15" 4 "0 -1 0" -"n15" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"p15" 4 "0 -1 0" -"q15" 4 "0 -1 0" -"r15" 4 "0 -1 0" -"r14" 4 "0 -1 0" -"s14" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"t13" 4 "0 -1 0" -"t12" 4 "0 -1 0" -"s12" 4 "0 -1 0" -"r12" 4 "0 -1 0" -"r11" 4 "0 -1 0" -"q11" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"t10" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"t8" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"h7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"k5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"k2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"k6" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"h6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"a7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"c8" 4 "0 -1 0" -"d8" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"g8" 4 "0 -1 0" -"h8" 4 "0 -1 0" -"i8" 4 "0 -1 0" -"j8" 4 "0 -1 0" -"k8" 4 "0 -1 0" -"l8" 4 "0 -1 0" -"m8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"p9" 4 "0 -1 0" -"q9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"k9" 4 "0 -1 0" -"j9" 4 "0 -1 0" -"j10" 4 "0 -1 0" -"k10" 4 "0 -1 0" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"a9" 4 "0 -1 0" -"a8" 4 "0 -1 0" -"a10" 4 "0 -1 0" -"a11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"c10" 4 "0 -1 0" -"d10" 4 "0 -1 0" -"c12" 4 "0 -1 0" -"d12" 4 "0 -1 0" -"c13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"r13" 4 "0 -1 0" -"q13" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"p17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"i16" 4 "0 -1 0" -"h16" 4 "0 -1 0" -"g16" 4 "0 -1 0" -"f16" 4 "0 -1 0" -"e16" 4 "0 -1 0" -"d16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"j16" 4 "0 -1 0" -"k16" 4 "0 -1 0" -"l16" 4 "0 -1 0" -"m16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"p16" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"a20" 4 "0 -1 0" -"p14" 4 "0 -1 0" +"e13" 5 0 +"e12" 5 0 +"e11" 5 0 +"e10" 5 0 +"e9" 5 0 +"f9" 5 0 +"g9" 5 0 +"h9" 5 0 +"i10" 5 0 +"i9" 5 0 +"i11" 5 0 +"j11" 5 0 +"k11" 5 0 +"l11" 5 0 +"l10" 5 0 +"l9" 5 0 +"n9" 5 0 +"m9" 5 0 +"o9" 5 0 +"o10" 5 0 +"p10" 5 0 +"p11" 5 0 +"p12" 5 0 +"o13" 5 0 +"p13" 5 0 +"o14" 5 0 +"n14" 5 0 +"l14" 5 0 +"f14" 5 0 +"g14" 5 0 +"h14" 5 0 +"i14" 5 0 +"j14" 5 0 +"m14" 5 0 +"k14" 5 0 +"f13" 5 0 +"j13" 5 0 +"i12" 2 0 +"l12" 2 0 +"j12" 2 0 +"k12" 2 0 +"h13" 1 1 +"g12" 3 0 +"h11" 3 0 +"m12" 3 0 +"n11" 3 0 +"d13" 8 0 +"d15" 8 0 +"b16" 8 0 +"b14" 8 0 +"b12" 8 0 +"b10" 8 0 +"b8" 8 0 +"d11" 8 0 +"d9" 8 0 +"d7" 8 0 +"q10" 8 0 +"q8" 8 0 +"s15" 8 0 +"s7" 8 0 +"s9" 8 0 +"s11" 8 0 +"s13" 8 0 +"q12" 8 0 +"q14" 8 0 +"q16" 8 0 +"h17" 4 0 +"g17" 4 0 +"f17" 4 0 +"e17" 4 0 +"d17" 4 0 +"c17" 4 0 +"b17" 4 0 +"a17" 4 0 +"a16" 4 0 +"a15" 4 0 +"b15" 4 0 +"c15" 4 0 +"c14" 4 0 +"d14" 4 0 +"e14" 4 0 +"e15" 4 0 +"f15" 4 0 +"g15" 4 0 +"h15" 4 0 +"i15" 4 0 +"j15" 4 0 +"k15" 4 0 +"l15" 4 0 +"m15" 4 0 +"n15" 4 0 +"o15" 4 0 +"p15" 4 0 +"q15" 4 0 +"r15" 4 0 +"r14" 4 0 +"s14" 4 0 +"t14" 4 0 +"t13" 4 0 +"t12" 4 0 +"s12" 4 0 +"r12" 4 0 +"r11" 4 0 +"q11" 4 0 +"r10" 4 0 +"s10" 4 0 +"t10" 4 0 +"t9" 4 0 +"t8" 4 0 +"s8" 4 0 +"r8" 4 0 +"r7" 4 0 +"q7" 4 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"m7" 4 0 +"l7" 4 0 +"k7" 4 0 +"j7" 4 0 +"i7" 4 0 +"h7" 4 0 +"g7" 4 0 +"f7" 4 0 +"e7" 4 0 +"e6" 4 0 +"d6" 4 0 +"c6" 4 0 +"b6" 4 0 +"a6" 4 0 +"a5" 4 0 +"b5" 4 0 +"c5" 4 0 +"d5" 4 0 +"e5" 4 0 +"f5" 4 0 +"g5" 4 0 +"h5" 4 0 +"i5" 4 0 +"j5" 4 0 +"k5" 4 0 +"l5" 4 0 +"m5" 4 0 +"n5" 4 0 +"o5" 4 0 +"p5" 4 0 +"q5" 4 0 +"r5" 4 0 +"s5" 4 0 +"t5" 4 0 +"t4" 4 0 +"s4" 4 0 +"r4" 4 0 +"q4" 4 0 +"p4" 4 0 +"o4" 4 0 +"n4" 4 0 +"m4" 4 0 +"l4" 4 0 +"k4" 4 0 +"j4" 4 0 +"i4" 4 0 +"h4" 4 0 +"g4" 4 0 +"f4" 4 0 +"e4" 4 0 +"d4" 4 0 +"c4" 4 0 +"b4" 4 0 +"a4" 4 0 +"a3" 4 0 +"b3" 4 0 +"c3" 4 0 +"d3" 4 0 +"e3" 4 0 +"f3" 4 0 +"g3" 4 0 +"h3" 4 0 +"i3" 4 0 +"j3" 4 0 +"k3" 4 0 +"l3" 4 0 +"m3" 4 0 +"n3" 4 0 +"o3" 4 0 +"p3" 4 0 +"q3" 4 0 +"r3" 4 0 +"s3" 4 0 +"t3" 4 0 +"t2" 4 0 +"s2" 4 0 +"r2" 4 0 +"q2" 4 0 +"p2" 4 0 +"o2" 4 0 +"n2" 4 0 +"m2" 4 0 +"l2" 4 0 +"k2" 4 0 +"j2" 4 0 +"i2" 4 0 +"h2" 4 0 +"g2" 4 0 +"f2" 4 0 +"e2" 4 0 +"d2" 4 0 +"c2" 4 0 +"b2" 4 0 +"a2" 4 0 +"a1" 4 0 +"b1" 4 0 +"c1" 4 0 +"d1" 4 0 +"e1" 4 0 +"f1" 4 0 +"g1" 4 0 +"h1" 4 0 +"i1" 4 0 +"j1" 4 0 +"k1" 4 0 +"l1" 4 0 +"m1" 4 0 +"n1" 4 0 +"o1" 4 0 +"p1" 4 0 +"q1" 4 0 +"r1" 4 0 +"s1" 4 0 +"t1" 4 0 +"t6" 4 0 +"s6" 4 0 +"r6" 4 0 +"q6" 4 0 +"p6" 4 0 +"o6" 4 0 +"n6" 4 0 +"m6" 4 0 +"l6" 4 0 +"k6" 4 0 +"j6" 4 0 +"i6" 4 0 +"h6" 4 0 +"g6" 4 0 +"f6" 4 0 +"t7" 4 0 +"a7" 4 0 +"b7" 4 0 +"c7" 4 0 +"c8" 4 0 +"d8" 4 0 +"e8" 4 0 +"f8" 4 0 +"g8" 4 0 +"h8" 4 0 +"i8" 4 0 +"j8" 4 0 +"k8" 4 0 +"l8" 4 0 +"m8" 4 0 +"n8" 4 0 +"o8" 4 0 +"p8" 4 0 +"p9" 4 0 +"q9" 4 0 +"r9" 4 0 +"k9" 4 0 +"j9" 4 0 +"j10" 4 0 +"k10" 4 0 +"c9" 4 0 +"b9" 4 0 +"a9" 4 0 +"a8" 4 0 +"a10" 4 0 +"a11" 4 0 +"b11" 4 0 +"c11" 4 0 +"c10" 4 0 +"d10" 4 0 +"c12" 4 0 +"d12" 4 0 +"c13" 4 0 +"b13" 4 0 +"a13" 4 0 +"a12" 4 0 +"a14" 4 0 +"t11" 4 0 +"r13" 4 0 +"q13" 4 0 +"t15" 4 0 +"t16" 4 0 +"s16" 4 0 +"r16" 4 0 +"r17" 4 0 +"q17" 4 0 +"p17" 4 0 +"o17" 4 0 +"n17" 4 0 +"m17" 4 0 +"l17" 4 0 +"k17" 4 0 +"j17" 4 0 +"i17" 4 0 +"i16" 4 0 +"h16" 4 0 +"g16" 4 0 +"f16" 4 0 +"e16" 4 0 +"d16" 4 0 +"c16" 4 0 +"j16" 4 0 +"k16" 4 0 +"l16" 4 0 +"m16" 4 0 +"n16" 4 0 +"o16" 4 0 +"p16" 4 0 +"i18" 4 0 +"h18" 4 0 +"g18" 4 0 +"f18" 4 0 +"e18" 4 0 +"d18" 4 0 +"c18" 4 0 +"b18" 4 0 +"a18" 4 0 +"a19" 4 0 +"b19" 4 0 +"c19" 4 0 +"d19" 4 0 +"e19" 4 0 +"f19" 4 0 +"g19" 4 0 +"h19" 4 0 +"i19" 4 0 +"j19" 4 0 +"k19" 4 0 +"l19" 4 0 +"m19" 4 0 +"n19" 4 0 +"o19" 4 0 +"p19" 4 0 +"q19" 4 0 +"r19" 4 0 +"s19" 4 0 +"t19" 4 0 +"t18" 4 0 +"s18" 4 0 +"r18" 4 0 +"q18" 4 0 +"p18" 4 0 +"o18" 4 0 +"n18" 4 0 +"m18" 4 0 +"l18" 4 0 +"k18" 4 0 +"j18" 4 0 +"s17" 4 0 +"t17" 4 0 +"t20" 4 0 +"s20" 4 0 +"r20" 4 0 +"q20" 4 0 +"p20" 4 0 +"o20" 4 0 +"n20" 4 0 +"m20" 4 0 +"l20" 4 0 +"k20" 4 0 +"j20" 4 0 +"i20" 4 0 +"h20" 4 0 +"g20" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"a20" 4 0 +"p14" 4 0 diff --git a/minigames/bulldozer/storage_level9.txt b/minigames/bulldozer/storage_level9.txt index 676e4baa15..a0da90da16 100644 --- a/minigames/bulldozer/storage_level9.txt +++ b/minigames/bulldozer/storage_level9.txt @@ -1,395 +1,395 @@ // bulldozer storage "level9" last updated 11-06-2016 14:53:20 nextlevel = "level10" -"c9" 4 "0 -1 0" -"b9" 4 "0 -1 0" -"a9" 4 "0 -1 0" -"a8" 4 "0 -1 0" -"b8" 4 "0 -1 0" -"c8" 4 "0 -1 0" -"d8" 4 "0 -1 0" -"e8" 4 "0 -1 0" -"f8" 4 "0 -1 0" -"g8" 4 "0 -1 0" -"h8" 4 "0 -1 0" -"i8" 4 "0 -1 0" -"j8" 4 "0 -1 0" -"k8" 4 "0 -1 0" -"l8" 4 "0 -1 0" -"m8" 4 "0 -1 0" -"n8" 4 "0 -1 0" -"o8" 4 "0 -1 0" -"p8" 4 "0 -1 0" -"q8" 4 "0 -1 0" -"r8" 4 "0 -1 0" -"s8" 4 "0 -1 0" -"t8" 4 "0 -1 0" -"t7" 4 "0 -1 0" -"s7" 4 "0 -1 0" -"r7" 4 "0 -1 0" -"q7" 4 "0 -1 0" -"p7" 4 "0 -1 0" -"o7" 4 "0 -1 0" -"n7" 4 "0 -1 0" -"m7" 4 "0 -1 0" -"l7" 4 "0 -1 0" -"i7" 4 "0 -1 0" -"h5" 4 "0 -1 0" -"j7" 4 "0 -1 0" -"g7" 4 "0 -1 0" -"f7" 4 "0 -1 0" -"e7" 4 "0 -1 0" -"d7" 4 "0 -1 0" -"c7" 4 "0 -1 0" -"b7" 4 "0 -1 0" -"a7" 4 "0 -1 0" -"a6" 4 "0 -1 0" -"b6" 4 "0 -1 0" -"c6" 4 "0 -1 0" -"d6" 4 "0 -1 0" -"e6" 4 "0 -1 0" -"f6" 4 "0 -1 0" -"g6" 4 "0 -1 0" -"k7" 4 "0 -1 0" -"j6" 4 "0 -1 0" -"l6" 4 "0 -1 0" -"m6" 4 "0 -1 0" -"n6" 4 "0 -1 0" -"o6" 4 "0 -1 0" -"p6" 4 "0 -1 0" -"q6" 4 "0 -1 0" -"r6" 4 "0 -1 0" -"s6" 4 "0 -1 0" -"t6" 4 "0 -1 0" -"t5" 4 "0 -1 0" -"s5" 4 "0 -1 0" -"r5" 4 "0 -1 0" -"q5" 4 "0 -1 0" -"p5" 4 "0 -1 0" -"o5" 4 "0 -1 0" -"n5" 4 "0 -1 0" -"m5" 4 "0 -1 0" -"l5" 4 "0 -1 0" -"g5" 4 "0 -1 0" -"f5" 4 "0 -1 0" -"e5" 4 "0 -1 0" -"d5" 4 "0 -1 0" -"c5" 4 "0 -1 0" -"b5" 4 "0 -1 0" -"a5" 4 "0 -1 0" -"a4" 4 "0 -1 0" -"b4" 4 "0 -1 0" -"c4" 4 "0 -1 0" -"d4" 4 "0 -1 0" -"e4" 4 "0 -1 0" -"f4" 4 "0 -1 0" -"g4" 4 "0 -1 0" -"h4" 4 "0 -1 0" -"i4" 4 "0 -1 0" -"j4" 4 "0 -1 0" -"l4" 4 "0 -1 0" -"m4" 4 "0 -1 0" -"n4" 4 "0 -1 0" -"o4" 4 "0 -1 0" -"p4" 4 "0 -1 0" -"q4" 4 "0 -1 0" -"r4" 4 "0 -1 0" -"s4" 4 "0 -1 0" -"t4" 4 "0 -1 0" -"t3" 4 "0 -1 0" -"s3" 4 "0 -1 0" -"r3" 4 "0 -1 0" -"q3" 4 "0 -1 0" -"p3" 4 "0 -1 0" -"o3" 4 "0 -1 0" -"n3" 4 "0 -1 0" -"m3" 4 "0 -1 0" -"l3" 4 "0 -1 0" -"j3" 4 "0 -1 0" -"i3" 4 "0 -1 0" -"h3" 4 "0 -1 0" -"g3" 4 "0 -1 0" -"f3" 4 "0 -1 0" -"e3" 4 "0 -1 0" -"d3" 4 "0 -1 0" -"c3" 4 "0 -1 0" -"b3" 4 "0 -1 0" -"a3" 4 "0 -1 0" -"a2" 4 "0 -1 0" -"b2" 4 "0 -1 0" -"c2" 4 "0 -1 0" -"d2" 4 "0 -1 0" -"e2" 4 "0 -1 0" -"f2" 4 "0 -1 0" -"g2" 4 "0 -1 0" -"h2" 4 "0 -1 0" -"i2" 4 "0 -1 0" -"j2" 4 "0 -1 0" -"l2" 4 "0 -1 0" -"m2" 4 "0 -1 0" -"n2" 4 "0 -1 0" -"o2" 4 "0 -1 0" -"p2" 4 "0 -1 0" -"q2" 4 "0 -1 0" -"r2" 4 "0 -1 0" -"s2" 4 "0 -1 0" -"t2" 4 "0 -1 0" -"t1" 4 "0 -1 0" -"s1" 4 "0 -1 0" -"r1" 4 "0 -1 0" -"q1" 4 "0 -1 0" -"p1" 4 "0 -1 0" -"o1" 4 "0 -1 0" -"n1" 4 "0 -1 0" -"m1" 4 "0 -1 0" -"l1" 4 "0 -1 0" -"k1" 4 "0 -1 0" -"j1" 4 "0 -1 0" -"i1" 4 "0 -1 0" -"h1" 4 "0 -1 0" -"g1" 4 "0 -1 0" -"f1" 4 "0 -1 0" -"e1" 4 "0 -1 0" -"d1" 4 "0 -1 0" -"c1" 4 "0 -1 0" -"b1" 4 "0 -1 0" -"a1" 4 "0 -1 0" -"t9" 4 "0 -1 0" -"s9" 4 "0 -1 0" -"r9" 4 "0 -1 0" -"q9" 4 "0 -1 0" -"p11" 6 "0 -1 0" -"o9" 4 "0 -1 0" -"n9" 4 "0 -1 0" -"m10" 5 "0 -1 0" -"m9" 5 "0 -1 0" -"l9" 5 "0 -1 0" -"k9" 5 "0 -1 0" -"j9" 5 "0 -1 0" -"i9" 5 "0 -1 0" -"g9" 4 "0 -1 0" -"f11" 6 "0 -1 0" -"e9" 4 "0 -1 0" -"d9" 4 "0 -1 0" -"d10" 4 "0 -1 0" -"c10" 4 "0 -1 0" -"b10" 4 "0 -1 0" -"a10" 4 "0 -1 0" -"a11" 4 "0 -1 0" -"b11" 4 "0 -1 0" -"c11" 4 "0 -1 0" -"f9" 6 "0 -1 0" -"f12" 11 "0 -1 0" -"f15" 6 "0 -1 0" -"g11" 4 "0 -1 0" -"h9" 5 "0 -1 0" -"i14" 1 "0 -1 0" -"i11" 2 "0 -1 0" -"i10" 2 "0 -1 0" -"j10" 2 "0 -1 0" -"h10" 5 "0 -1 0" -"n11" 4 "0 -1 0" -"o11" 4 "0 -1 0" -"p13" 6 "0 -1 0" -"h7" 4 "0 -1 0" -"p9" 6 "0 -1 0" -"s11" 4 "0 -1 0" -"t11" 4 "0 -1 0" -"t10" 4 "0 -1 0" -"s10" 4 "0 -1 0" -"r10" 4 "0 -1 0" -"p15" 6 "0 -1 0" -"q11" 11 "0 -1 0" -"o10" 4 "0 -1 0" -"n10" 4 "0 -1 0" -"h11" 5 "0 -1 0" -"k10" 2 "0 -1 0" -"l10" 2 "0 -1 0" -"j14" 3 "0 -1 0" -"j13" 3 "0 -1 0" -"h12" 5 "0 -1 0" -"g10" 4 "0 -1 0" -"f14" 11 "0 -1 0" -"f13" 6 "0 -1 0" -"t12" 4 "0 -1 0" -"r11" 6 "0 -1 0" -"h6" 4 "0 -1 0" -"p14" 11 "0 -1 0" -"p10" 11 "0 -1 0" -"o12" 4 "0 -1 0" -"n12" 4 "0 -1 0" -"i12" 5 "0 -1 0" -"k12" 3 "0 -1 0" -"j11" 3 "0 -1 0" -"k10" 3 "0 -1 0" -"i13" 5 "0 -1 0" -"h13" 5 "0 -1 0" -"g12" 4 "0 -1 0" -"f16" 11 "0 -1 0" -"c13" 11 "0 -1 0" -"e13" 11 "0 -1 0" -"e10" 6 "0 -1 0" -"b12" 4 "0 -1 0" -"a12" 4 "0 -1 0" -"a13" 4 "0 -1 0" -"b13" 4 "0 -1 0" -"d14" 11 "0 -1 0" -"e12" 6 "0 -1 0" -"f10" 11 "0 -1 0" -"d13" 6 "0 -1 0" -"g13" 4 "0 -1 0" -"h14" 5 "0 -1 0" -"h15" 5 "0 -1 0" -"f17" 6 "0 -1 0" -"e16" 6 "0 -1 0" -"i15" 5 "0 -1 0" -"d15" 6 "0 -1 0" -"n13" 4 "0 -1 0" -"o13" 4 "0 -1 0" -"p17" 6 "0 -1 0" -"r12" 11 "0 -1 0" -"q13" 11 "0 -1 0" -"s14" 6 "0 -1 0" -"t13" 4 "0 -1 0" -"t14" 4 "0 -1 0" -"s12" 6 "0 -1 0" -"s13" 11 "0 -1 0" -"q12" 6 "0 -1 0" -"p12" 11 "0 -1 0" -"o14" 4 "0 -1 0" -"n14" 4 "0 -1 0" -"m14" 4 "0 -1 0" -"i16" 5 "0 -1 0" -"c14" 6 "0 -1 0" -"c12" 6 "0 -1 0" -"d11" 6 "0 -1 0" -"j16" 5 "0 -1 0" -"g14" 4 "0 -1 0" -"p16" 11 "0 -1 0" -"e14" 6 "0 -1 0" -"e15" 11 "0 -1 0" -"d12" 11 "0 -1 0" -"b14" 4 "0 -1 0" -"a14" 4 "0 -1 0" -"a15" 4 "0 -1 0" -"b15" 4 "0 -1 0" -"c15" 4 "0 -1 0" -"e11" 11 "0 -1 0" -"q15" 11 "0 -1 0" -"r14" 11 "0 -1 0" -"k16" 5 "0 -1 0" -"l16" 5 "0 -1 0" -"l15" 5 "0 -1 0" -"l14" 5 "0 -1 0" -"q10" 6 "0 -1 0" -"l13" 5 "0 -1 0" -"m15" 4 "0 -1 0" -"n15" 4 "0 -1 0" -"o15" 4 "0 -1 0" -"q16" 6 "0 -1 0" -"r15" 6 "0 -1 0" -"q14" 6 "0 -1 0" -"s15" 4 "0 -1 0" -"t15" 4 "0 -1 0" -"t16" 4 "0 -1 0" -"s16" 4 "0 -1 0" -"r16" 4 "0 -1 0" -"r13" 6 "0 -1 0" -"k6" 4 "0 -1 0" -"o16" 4 "0 -1 0" -"n16" 4 "0 -1 0" -"m16" 4 "0 -1 0" -"m13" 5 "0 -1 0" -"m12" 5 "0 -1 0" -"h16" 4 "0 -1 0" -"g16" 4 "0 -1 0" -"g15" 4 "0 -1 0" -"m11" 5 "0 -1 0" -"k5" 4 "0 -1 0" -"j5" 4 "0 -1 0" -"d16" 4 "0 -1 0" -"c16" 4 "0 -1 0" -"b16" 4 "0 -1 0" -"a16" 4 "0 -1 0" -"a17" 4 "0 -1 0" -"b17" 4 "0 -1 0" -"c17" 4 "0 -1 0" -"d17" 4 "0 -1 0" -"e17" 4 "0 -1 0" -"i5" 4 "0 -1 0" -"g17" 4 "0 -1 0" -"h17" 4 "0 -1 0" -"i17" 4 "0 -1 0" -"j17" 4 "0 -1 0" -"k17" 4 "0 -1 0" -"l17" 4 "0 -1 0" -"m17" 4 "0 -1 0" -"n17" 4 "0 -1 0" -"o17" 4 "0 -1 0" -"i6" 4 "0 -1 0" -"q17" 4 "0 -1 0" -"r17" 4 "0 -1 0" -"s17" 4 "0 -1 0" -"t17" 4 "0 -1 0" -"t18" 4 "0 -1 0" -"s18" 4 "0 -1 0" -"r18" 4 "0 -1 0" -"q18" 4 "0 -1 0" -"p18" 4 "0 -1 0" -"o18" 4 "0 -1 0" -"n18" 4 "0 -1 0" -"m18" 4 "0 -1 0" -"l18" 4 "0 -1 0" -"k18" 4 "0 -1 0" -"j18" 4 "0 -1 0" -"i18" 4 "0 -1 0" -"h18" 4 "0 -1 0" -"g18" 4 "0 -1 0" -"f18" 4 "0 -1 0" -"e18" 4 "0 -1 0" -"d18" 4 "0 -1 0" -"c18" 4 "0 -1 0" -"b18" 4 "0 -1 0" -"a18" 4 "0 -1 0" -"a19" 4 "0 -1 0" -"b19" 4 "0 -1 0" -"c19" 4 "0 -1 0" -"d19" 4 "0 -1 0" -"e19" 4 "0 -1 0" -"f19" 4 "0 -1 0" -"g19" 4 "0 -1 0" -"h19" 4 "0 -1 0" -"i19" 4 "0 -1 0" -"j19" 4 "0 -1 0" -"k19" 4 "0 -1 0" -"l19" 4 "0 -1 0" -"m19" 4 "0 -1 0" -"n19" 4 "0 -1 0" -"o19" 4 "0 -1 0" -"p19" 4 "0 -1 0" -"q19" 4 "0 -1 0" -"r19" 4 "0 -1 0" -"s19" 4 "0 -1 0" -"t19" 4 "0 -1 0" -"t20" 4 "0 -1 0" -"s20" 4 "0 -1 0" -"r20" 4 "0 -1 0" -"q20" 4 "0 -1 0" -"p20" 4 "0 -1 0" -"o20" 4 "0 -1 0" -"n20" 4 "0 -1 0" -"m20" 4 "0 -1 0" -"l20" 4 "0 -1 0" -"k20" 4 "0 -1 0" -"j20" 4 "0 -1 0" -"i20" 4 "0 -1 0" -"h20" 4 "0 -1 0" -"g20" 4 "0 -1 0" -"f20" 4 "0 -1 0" -"e20" 4 "0 -1 0" -"d20" 4 "0 -1 0" -"c20" 4 "0 -1 0" -"b20" 4 "0 -1 0" -"a20" 4 "0 -1 0" -"k4" 4 "0 -1 0" -"k3" 4 "0 -1 0" -"k2" 4 "0 -1 0" +"c9" 4 0 +"b9" 4 0 +"a9" 4 0 +"a8" 4 0 +"b8" 4 0 +"c8" 4 0 +"d8" 4 0 +"e8" 4 0 +"f8" 4 0 +"g8" 4 0 +"h8" 4 0 +"i8" 4 0 +"j8" 4 0 +"k8" 4 0 +"l8" 4 0 +"m8" 4 0 +"n8" 4 0 +"o8" 4 0 +"p8" 4 0 +"q8" 4 0 +"r8" 4 0 +"s8" 4 0 +"t8" 4 0 +"t7" 4 0 +"s7" 4 0 +"r7" 4 0 +"q7" 4 0 +"p7" 4 0 +"o7" 4 0 +"n7" 4 0 +"m7" 4 0 +"l7" 4 0 +"i7" 4 0 +"h5" 4 0 +"j7" 4 0 +"g7" 4 0 +"f7" 4 0 +"e7" 4 0 +"d7" 4 0 +"c7" 4 0 +"b7" 4 0 +"a7" 4 0 +"a6" 4 0 +"b6" 4 0 +"c6" 4 0 +"d6" 4 0 +"e6" 4 0 +"f6" 4 0 +"g6" 4 0 +"k7" 4 0 +"j6" 4 0 +"l6" 4 0 +"m6" 4 0 +"n6" 4 0 +"o6" 4 0 +"p6" 4 0 +"q6" 4 0 +"r6" 4 0 +"s6" 4 0 +"t6" 4 0 +"t5" 4 0 +"s5" 4 0 +"r5" 4 0 +"q5" 4 0 +"p5" 4 0 +"o5" 4 0 +"n5" 4 0 +"m5" 4 0 +"l5" 4 0 +"g5" 4 0 +"f5" 4 0 +"e5" 4 0 +"d5" 4 0 +"c5" 4 0 +"b5" 4 0 +"a5" 4 0 +"a4" 4 0 +"b4" 4 0 +"c4" 4 0 +"d4" 4 0 +"e4" 4 0 +"f4" 4 0 +"g4" 4 0 +"h4" 4 0 +"i4" 4 0 +"j4" 4 0 +"l4" 4 0 +"m4" 4 0 +"n4" 4 0 +"o4" 4 0 +"p4" 4 0 +"q4" 4 0 +"r4" 4 0 +"s4" 4 0 +"t4" 4 0 +"t3" 4 0 +"s3" 4 0 +"r3" 4 0 +"q3" 4 0 +"p3" 4 0 +"o3" 4 0 +"n3" 4 0 +"m3" 4 0 +"l3" 4 0 +"j3" 4 0 +"i3" 4 0 +"h3" 4 0 +"g3" 4 0 +"f3" 4 0 +"e3" 4 0 +"d3" 4 0 +"c3" 4 0 +"b3" 4 0 +"a3" 4 0 +"a2" 4 0 +"b2" 4 0 +"c2" 4 0 +"d2" 4 0 +"e2" 4 0 +"f2" 4 0 +"g2" 4 0 +"h2" 4 0 +"i2" 4 0 +"j2" 4 0 +"l2" 4 0 +"m2" 4 0 +"n2" 4 0 +"o2" 4 0 +"p2" 4 0 +"q2" 4 0 +"r2" 4 0 +"s2" 4 0 +"t2" 4 0 +"t1" 4 0 +"s1" 4 0 +"r1" 4 0 +"q1" 4 0 +"p1" 4 0 +"o1" 4 0 +"n1" 4 0 +"m1" 4 0 +"l1" 4 0 +"k1" 4 0 +"j1" 4 0 +"i1" 4 0 +"h1" 4 0 +"g1" 4 0 +"f1" 4 0 +"e1" 4 0 +"d1" 4 0 +"c1" 4 0 +"b1" 4 0 +"a1" 4 0 +"t9" 4 0 +"s9" 4 0 +"r9" 4 0 +"q9" 4 0 +"p11" 6 0 +"o9" 4 0 +"n9" 4 0 +"m10" 5 0 +"m9" 5 0 +"l9" 5 0 +"k9" 5 0 +"j9" 5 0 +"i9" 5 0 +"g9" 4 0 +"f11" 6 0 +"e9" 4 0 +"d9" 4 0 +"d10" 4 0 +"c10" 4 0 +"b10" 4 0 +"a10" 4 0 +"a11" 4 0 +"b11" 4 0 +"c11" 4 0 +"f9" 6 0 +"f12" 11 0 +"f15" 6 0 +"g11" 4 0 +"h9" 5 0 +"i14" 1 1 +"i11" 2 0 +"i10" 2 0 +"j10" 2 0 +"h10" 5 0 +"n11" 4 0 +"o11" 4 0 +"p13" 6 0 +"h7" 4 0 +"p9" 6 0 +"s11" 4 0 +"t11" 4 0 +"t10" 4 0 +"s10" 4 0 +"r10" 4 0 +"p15" 6 0 +"q11" 11 0 +"o10" 4 0 +"n10" 4 0 +"h11" 5 0 +"k10" 2 0 +"l10" 2 0 +"j14" 3 0 +"j13" 3 0 +"h12" 5 0 +"g10" 4 0 +"f14" 11 0 +"f13" 6 0 +"t12" 4 0 +"r11" 6 0 +"h6" 4 0 +"p14" 11 0 +"p10" 11 0 +"o12" 4 0 +"n12" 4 0 +"i12" 5 0 +"k12" 3 0 +"j11" 3 0 +"k10" 3 0 +"i13" 5 0 +"h13" 5 0 +"g12" 4 0 +"f16" 11 0 +"c13" 11 0 +"e13" 11 0 +"e10" 6 0 +"b12" 4 0 +"a12" 4 0 +"a13" 4 0 +"b13" 4 0 +"d14" 11 0 +"e12" 6 0 +"f10" 11 0 +"d13" 6 0 +"g13" 4 0 +"h14" 5 0 +"h15" 5 0 +"f17" 6 0 +"e16" 6 0 +"i15" 5 0 +"d15" 6 0 +"n13" 4 0 +"o13" 4 0 +"p17" 6 0 +"r12" 11 0 +"q13" 11 0 +"s14" 6 0 +"t13" 4 0 +"t14" 4 0 +"s12" 6 0 +"s13" 11 0 +"q12" 6 0 +"p12" 11 0 +"o14" 4 0 +"n14" 4 0 +"m14" 4 0 +"i16" 5 0 +"c14" 6 0 +"c12" 6 0 +"d11" 6 0 +"j16" 5 0 +"g14" 4 0 +"p16" 11 0 +"e14" 6 0 +"e15" 11 0 +"d12" 11 0 +"b14" 4 0 +"a14" 4 0 +"a15" 4 0 +"b15" 4 0 +"c15" 4 0 +"e11" 11 0 +"q15" 11 0 +"r14" 11 0 +"k16" 5 0 +"l16" 5 0 +"l15" 5 0 +"l14" 5 0 +"q10" 6 0 +"l13" 5 0 +"m15" 4 0 +"n15" 4 0 +"o15" 4 0 +"q16" 6 0 +"r15" 6 0 +"q14" 6 0 +"s15" 4 0 +"t15" 4 0 +"t16" 4 0 +"s16" 4 0 +"r16" 4 0 +"r13" 6 0 +"k6" 4 0 +"o16" 4 0 +"n16" 4 0 +"m16" 4 0 +"m13" 5 0 +"m12" 5 0 +"h16" 4 0 +"g16" 4 0 +"g15" 4 0 +"m11" 5 0 +"k5" 4 0 +"j5" 4 0 +"d16" 4 0 +"c16" 4 0 +"b16" 4 0 +"a16" 4 0 +"a17" 4 0 +"b17" 4 0 +"c17" 4 0 +"d17" 4 0 +"e17" 4 0 +"i5" 4 0 +"g17" 4 0 +"h17" 4 0 +"i17" 4 0 +"j17" 4 0 +"k17" 4 0 +"l17" 4 0 +"m17" 4 0 +"n17" 4 0 +"o17" 4 0 +"i6" 4 0 +"q17" 4 0 +"r17" 4 0 +"s17" 4 0 +"t17" 4 0 +"t18" 4 0 +"s18" 4 0 +"r18" 4 0 +"q18" 4 0 +"p18" 4 0 +"o18" 4 0 +"n18" 4 0 +"m18" 4 0 +"l18" 4 0 +"k18" 4 0 +"j18" 4 0 +"i18" 4 0 +"h18" 4 0 +"g18" 4 0 +"f18" 4 0 +"e18" 4 0 +"d18" 4 0 +"c18" 4 0 +"b18" 4 0 +"a18" 4 0 +"a19" 4 0 +"b19" 4 0 +"c19" 4 0 +"d19" 4 0 +"e19" 4 0 +"f19" 4 0 +"g19" 4 0 +"h19" 4 0 +"i19" 4 0 +"j19" 4 0 +"k19" 4 0 +"l19" 4 0 +"m19" 4 0 +"n19" 4 0 +"o19" 4 0 +"p19" 4 0 +"q19" 4 0 +"r19" 4 0 +"s19" 4 0 +"t19" 4 0 +"t20" 4 0 +"s20" 4 0 +"r20" 4 0 +"q20" 4 0 +"p20" 4 0 +"o20" 4 0 +"n20" 4 0 +"m20" 4 0 +"l20" 4 0 +"k20" 4 0 +"j20" 4 0 +"i20" 4 0 +"h20" 4 0 +"g20" 4 0 +"f20" 4 0 +"e20" 4 0 +"d20" 4 0 +"c20" 4 0 +"b20" 4 0 +"a20" 4 0 +"k4" 4 0 +"k3" 4 0 +"k2" 4 0 diff --git a/models/items/a_rockets.md3 b/models/items/a_rockets.md3 index 61384b5fe7..3f1a594d06 100644 Binary files a/models/items/a_rockets.md3 and b/models/items/a_rockets.md3 differ diff --git a/models/player/gakarmored.iqm b/models/player/gakarmored.iqm deleted file mode 100644 index 824e52ae6c..0000000000 Binary files a/models/player/gakarmored.iqm and /dev/null differ diff --git a/models/player/gakarmored.iqm.framegroups b/models/player/gakarmored.iqm.framegroups deleted file mode 100644 index 98eabd70b5..0000000000 --- a/models/player/gakarmored.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 15.000000 0 // dieone -36 20 15.000000 0 // dietwo -56 15 15.000000 1 // draw -71 20 15.000000 1 // duck -91 21 30.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 5.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 15.000000 0 // painone -359 17 15.000000 0 // paintwo -376 3 15.000000 0 // shoot -379 21 15.000000 1 // taunt -400 21 35.000000 1 // run -421 21 35.000000 1 // runbackwards -442 21 35.000000 1 // strafeleft -463 21 35.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 21 35.000000 1 // forwardright -509 21 35.000000 1 // forwardleft -530 21 35.000000 1 // backright -551 21 30.000000 1 // backleft -572 21 20.000000 0 // melee -593 21 30.000000 1 // duckwalkbackwards -91 21 30.000000 1 // duckwalkstrafeleft -91 21 30.000000 1 // duckwalkstraferight -91 21 30.000000 1 // duckwalkforwardright -91 21 30.000000 1 // duckwalkforwardleft -593 21 30.000000 1 // duckwalkbackright -593 21 30.000000 1 // duckwalkbackleft \ No newline at end of file diff --git a/models/player/gakarmored.iqm_0.skin b/models/player/gakarmored.iqm_0.skin deleted file mode 100644 index 1e8d3e9cfa..0000000000 --- a/models/player/gakarmored.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gak -gak1,gakarmor diff --git a/models/player/gakarmored.iqm_0.sounds b/models/player/gakarmored.iqm_0.sounds deleted file mode 100644 index fb5b767df0..0000000000 --- a/models/player/gakarmored.iqm_0.sounds +++ /dev/null @@ -1,32 +0,0 @@ -//TAG: reptilian -//affirmative sound/player/carni-lycan/player/affirmative 0 -attack sound/player/reptilian/coms/attack 0 -//attacking sound/player/carni-lycan/player/attacking 0 -attackinfive sound/player/reptilian/coms/attackinfive 0 -coverme sound/player/reptilian/coms/coverme 0 -defend sound/player/reptilian/coms/defend 0 -//defending sound/player/carni-lycan/player/defending 0 -//droppedflag sound/player/carni-lycan/player/droppedflag 0 -//flagcarriertakingdamage sound/player/soldier/player/flagcarriertakingdamage 0 -freelance sound/player/reptilian/coms/freelance 2 -//getflag sound/player/soldier/player/getflag 0 -incoming sound/player/reptilian/coms/incoming 0 -meet sound/player/reptilian/coms/meet 0 -needhelp sound/player/reptilian/coms/needhelp 2 -//negative sound/player/carni-lycan/player/negative 0 -//onmyway sound/player/carni-lycan/player/onmyway 0 -//roaming sound/player/carni-lycan/player/roaming 0 -//seenenemy sound/player/carni-lycan/player/seenenemy 0 -seenflag sound/player/reptilian/coms/seenflag 0 -taunt sound/player/reptilian/coms/taunt 3 -teamshoot sound/player/reptilian/coms/teamshoot 3 -death sound/player/reptilian/player/death 3 -drown sound/player/reptilian/player/drown 0 -fall sound/player/reptilian/player/fall 0 -falling sound/player/reptilian/player/falling 0 -gasp sound/player/reptilian/player/gasp 0 -jump sound/player/reptilian/player/jump 0 -pain25 sound/player/reptilian/player/pain25 0 -pain50 sound/player/reptilian/player/pain50 0 -pain75 sound/player/reptilian/player/pain75 0 -pain100 sound/player/reptilian/player/pain100 0 diff --git a/models/player/gakarmored.iqm_0.tga b/models/player/gakarmored.iqm_0.tga deleted file mode 100644 index 0fbb375cdd..0000000000 Binary files a/models/player/gakarmored.iqm_0.tga and /dev/null differ diff --git a/models/player/gakarmored.iqm_0.txt b/models/player/gakarmored.iqm_0.txt deleted file mode 100644 index 46977b8854..0000000000 --- a/models/player/gakarmored.iqm_0.txt +++ /dev/null @@ -1,12 +0,0 @@ -name Gak Armored -species alien -sex Male -weight 90 -age 20 -description Heavyweight Xonotic Soldier -bone_upperbody spine2 -bone_aim0 0.25 spine2 -bone_aim1 0.4 spine4 -bone_aim2 0.35 bip01 r hand -bone_weapon bip01 r hand -fixbone 1 diff --git a/models/player/gakarmored.iqm_1.skin b/models/player/gakarmored.iqm_1.skin deleted file mode 100644 index 3377bcb48e..0000000000 --- a/models/player/gakarmored.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gakfullbright -gak1,gakarmorfb diff --git a/models/player/gakarmored_lod1.iqm b/models/player/gakarmored_lod1.iqm deleted file mode 100644 index bbf00f7018..0000000000 Binary files a/models/player/gakarmored_lod1.iqm and /dev/null differ diff --git a/models/player/gakarmored_lod1.iqm.framegroups b/models/player/gakarmored_lod1.iqm.framegroups deleted file mode 100644 index 98eabd70b5..0000000000 --- a/models/player/gakarmored_lod1.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 15.000000 0 // dieone -36 20 15.000000 0 // dietwo -56 15 15.000000 1 // draw -71 20 15.000000 1 // duck -91 21 30.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 5.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 15.000000 0 // painone -359 17 15.000000 0 // paintwo -376 3 15.000000 0 // shoot -379 21 15.000000 1 // taunt -400 21 35.000000 1 // run -421 21 35.000000 1 // runbackwards -442 21 35.000000 1 // strafeleft -463 21 35.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 21 35.000000 1 // forwardright -509 21 35.000000 1 // forwardleft -530 21 35.000000 1 // backright -551 21 30.000000 1 // backleft -572 21 20.000000 0 // melee -593 21 30.000000 1 // duckwalkbackwards -91 21 30.000000 1 // duckwalkstrafeleft -91 21 30.000000 1 // duckwalkstraferight -91 21 30.000000 1 // duckwalkforwardright -91 21 30.000000 1 // duckwalkforwardleft -593 21 30.000000 1 // duckwalkbackright -593 21 30.000000 1 // duckwalkbackleft \ No newline at end of file diff --git a/models/player/gakarmored_lod1.iqm_0.skin b/models/player/gakarmored_lod1.iqm_0.skin deleted file mode 100644 index 1e8d3e9cfa..0000000000 --- a/models/player/gakarmored_lod1.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gak -gak1,gakarmor diff --git a/models/player/gakarmored_lod1.iqm_1.skin b/models/player/gakarmored_lod1.iqm_1.skin deleted file mode 100644 index 3377bcb48e..0000000000 --- a/models/player/gakarmored_lod1.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gakfullbright -gak1,gakarmorfb diff --git a/models/player/gakarmored_lod2.iqm b/models/player/gakarmored_lod2.iqm deleted file mode 100644 index 8fdbceffad..0000000000 Binary files a/models/player/gakarmored_lod2.iqm and /dev/null differ diff --git a/models/player/gakarmored_lod2.iqm.framegroups b/models/player/gakarmored_lod2.iqm.framegroups deleted file mode 100644 index 98eabd70b5..0000000000 --- a/models/player/gakarmored_lod2.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 15.000000 0 // dieone -36 20 15.000000 0 // dietwo -56 15 15.000000 1 // draw -71 20 15.000000 1 // duck -91 21 30.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 5.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 15.000000 0 // painone -359 17 15.000000 0 // paintwo -376 3 15.000000 0 // shoot -379 21 15.000000 1 // taunt -400 21 35.000000 1 // run -421 21 35.000000 1 // runbackwards -442 21 35.000000 1 // strafeleft -463 21 35.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 21 35.000000 1 // forwardright -509 21 35.000000 1 // forwardleft -530 21 35.000000 1 // backright -551 21 30.000000 1 // backleft -572 21 20.000000 0 // melee -593 21 30.000000 1 // duckwalkbackwards -91 21 30.000000 1 // duckwalkstrafeleft -91 21 30.000000 1 // duckwalkstraferight -91 21 30.000000 1 // duckwalkforwardright -91 21 30.000000 1 // duckwalkforwardleft -593 21 30.000000 1 // duckwalkbackright -593 21 30.000000 1 // duckwalkbackleft \ No newline at end of file diff --git a/models/player/gakarmored_lod2.iqm_0.skin b/models/player/gakarmored_lod2.iqm_0.skin deleted file mode 100644 index 1e8d3e9cfa..0000000000 --- a/models/player/gakarmored_lod2.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gak -gak1,gakarmor diff --git a/models/player/gakarmored_lod2.iqm_1.skin b/models/player/gakarmored_lod2.iqm_1.skin deleted file mode 100644 index 3377bcb48e..0000000000 --- a/models/player/gakarmored_lod2.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -gak1.001,gakfullbright -gak1,gakarmorfb diff --git a/models/player/ignishalfmasked.iqm b/models/player/ignishalfmasked.iqm deleted file mode 100644 index 7b11d80edb..0000000000 Binary files a/models/player/ignishalfmasked.iqm and /dev/null differ diff --git a/models/player/ignishalfmasked.iqm.framegroups b/models/player/ignishalfmasked.iqm.framegroups deleted file mode 100644 index 6432971bbe..0000000000 --- a/models/player/ignishalfmasked.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 30.000000 0 // dieone -36 20 25.000000 0 // dietwo -56 15 30.000000 1 // draw -71 20 15.000000 1 // duck -91 20 32.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 10.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 24.000000 0 // painone -359 17 34.000000 0 // paintwo -376 3 3.000000 0 // shoot -379 21 15.000000 1 // taunt -400 20 29.000000 1 // run -421 20 29.000000 1 // runbackwards -442 20 29.000000 1 // strafeleft -463 20 29.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 20 29.000000 1 // forwardright -509 20 29.000000 1 // forwardleft -530 20 29.000000 1 // backright -551 20 29.000000 1 // backleft -572 21 20.000000 0 // melee -593 20 32.000000 1 // duckwalkbackwards -614 20 32.000000 1 // duckwalkstrafeleft -635 20 32.000000 1 // duckwalkstraferight -656 20 32.000000 1 // duckwalkforwardright -677 20 32.000000 1 // duckwalkforwardleft -698 20 32.000000 1 // duckwalkbackright -719 20 32.000000 1 // duckwalkbackleft diff --git a/models/player/ignishalfmasked.iqm_0.skin b/models/player/ignishalfmasked.iqm_0.skin deleted file mode 100644 index 9523f9edba..0000000000 --- a/models/player/ignishalfmasked.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignis -ignis2.001,ignishead diff --git a/models/player/ignishalfmasked.iqm_0.sounds b/models/player/ignishalfmasked.iqm_0.sounds deleted file mode 100644 index 952ba1c797..0000000000 --- a/models/player/ignishalfmasked.iqm_0.sounds +++ /dev/null @@ -1,32 +0,0 @@ -//TAG: insurrectionist -//affirmative sound/player/carni-lycan/player/affirmative 0 -attack sound/player/insurrectionist/coms/attack 0 -//attacking sound/player/carni-lycan/player/attacking 0 -attackinfive sound/player/insurrectionist/coms/attackinfive 0 -coverme sound/player/insurrectionist/coms/coverme 0 -defend sound/player/insurrectionist/coms/defend 0 -//defending sound/player/carni-lycan/player/defending 0 -//droppedflag sound/player/carni-lycan/player/droppedflag 0 -//flagcarriertakingdamage sound/player/soldier/player/flagcarriertakingdamage 0 -freelance sound/player/insurrectionist/coms/freelance 2 -//getflag sound/player/soldier/player/getflag 0 -incoming sound/player/insurrectionist/coms/incoming 0 -meet sound/player/insurrectionist/coms/meet 0 -needhelp sound/player/insurrectionist/coms/needhelp 2 -//negative sound/player/carni-lycan/player/negative 0 -//onmyway sound/player/carni-lycan/player/onmyway 0 -//roaming sound/player/carni-lycan/player/roaming 0 -//seenenemy sound/player/carni-lycan/player/seenenemy 0 -seenflag sound/player/insurrectionist/coms/seenflag 0 -taunt sound/player/insurrectionist/coms/taunt 4 -teamshoot sound/player/insurrectionist/coms/teamshoot 3 -death sound/player/insurrectionist/player/death 3 -drown sound/player/insurrectionist/player/drown 0 -fall sound/player/insurrectionist/player/fall 0 -falling sound/player/insurrectionist/player/falling 0 -gasp sound/player/insurrectionist/player/gasp 0 -jump sound/player/insurrectionist/player/jump 0 -pain25 sound/player/insurrectionist/player/pain25 0 -pain50 sound/player/insurrectionist/player/pain50 0 -pain75 sound/player/insurrectionist/player/pain75 0 -pain100 sound/player/insurrectionist/player/pain100 0 diff --git a/models/player/ignishalfmasked.iqm_0.tga b/models/player/ignishalfmasked.iqm_0.tga deleted file mode 100644 index 487a131163..0000000000 Binary files a/models/player/ignishalfmasked.iqm_0.tga and /dev/null differ diff --git a/models/player/ignishalfmasked.iqm_0.txt b/models/player/ignishalfmasked.iqm_0.txt deleted file mode 100644 index 01c99adf0b..0000000000 --- a/models/player/ignishalfmasked.iqm_0.txt +++ /dev/null @@ -1,13 +0,0 @@ -name Ignis - Half Masked -species human -sex Male -weight 90 -age 31 -description Heavyweight Xonotic Soldier -bone_upperbody spine2 -bone_aim0 0.25 spine2 -bone_aim1 0.4 spine4 -bone_aim2 0.2 upperarm_L -bone_aim3 0.35 bip01 r hand -bone_weapon bip01 r hand -fixbone 1 diff --git a/models/player/ignishalfmasked.iqm_1.skin b/models/player/ignishalfmasked.iqm_1.skin deleted file mode 100644 index 89a0020284..0000000000 --- a/models/player/ignishalfmasked.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignisfullbright -ignis2.001,ignishead diff --git a/models/player/ignishalfmasked_lod1.iqm b/models/player/ignishalfmasked_lod1.iqm deleted file mode 100644 index e3877a753c..0000000000 Binary files a/models/player/ignishalfmasked_lod1.iqm and /dev/null differ diff --git a/models/player/ignishalfmasked_lod1.iqm.framegroups b/models/player/ignishalfmasked_lod1.iqm.framegroups deleted file mode 100644 index 6432971bbe..0000000000 --- a/models/player/ignishalfmasked_lod1.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 30.000000 0 // dieone -36 20 25.000000 0 // dietwo -56 15 30.000000 1 // draw -71 20 15.000000 1 // duck -91 20 32.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 10.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 24.000000 0 // painone -359 17 34.000000 0 // paintwo -376 3 3.000000 0 // shoot -379 21 15.000000 1 // taunt -400 20 29.000000 1 // run -421 20 29.000000 1 // runbackwards -442 20 29.000000 1 // strafeleft -463 20 29.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 20 29.000000 1 // forwardright -509 20 29.000000 1 // forwardleft -530 20 29.000000 1 // backright -551 20 29.000000 1 // backleft -572 21 20.000000 0 // melee -593 20 32.000000 1 // duckwalkbackwards -614 20 32.000000 1 // duckwalkstrafeleft -635 20 32.000000 1 // duckwalkstraferight -656 20 32.000000 1 // duckwalkforwardright -677 20 32.000000 1 // duckwalkforwardleft -698 20 32.000000 1 // duckwalkbackright -719 20 32.000000 1 // duckwalkbackleft diff --git a/models/player/ignishalfmasked_lod1.iqm_0.skin b/models/player/ignishalfmasked_lod1.iqm_0.skin deleted file mode 100644 index 9523f9edba..0000000000 --- a/models/player/ignishalfmasked_lod1.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignis -ignis2.001,ignishead diff --git a/models/player/ignishalfmasked_lod1.iqm_1.skin b/models/player/ignishalfmasked_lod1.iqm_1.skin deleted file mode 100644 index 89a0020284..0000000000 --- a/models/player/ignishalfmasked_lod1.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignisfullbright -ignis2.001,ignishead diff --git a/models/player/ignishalfmasked_lod2.iqm b/models/player/ignishalfmasked_lod2.iqm deleted file mode 100644 index 48b0696de9..0000000000 Binary files a/models/player/ignishalfmasked_lod2.iqm and /dev/null differ diff --git a/models/player/ignishalfmasked_lod2.iqm.framegroups b/models/player/ignishalfmasked_lod2.iqm.framegroups deleted file mode 100644 index 6432971bbe..0000000000 --- a/models/player/ignishalfmasked_lod2.iqm.framegroups +++ /dev/null @@ -1,31 +0,0 @@ -0 36 30.000000 0 // dieone -36 20 25.000000 0 // dietwo -56 15 30.000000 1 // draw -71 20 15.000000 1 // duck -91 20 32.000000 1 // duckwalk -112 16 15.000000 0 // duckjump -128 15 10.000000 1 // duckidle -143 41 5.000000 1 // idle -184 160 25.000000 0 // jump -344 15 24.000000 0 // painone -359 17 34.000000 0 // paintwo -376 3 3.000000 0 // shoot -379 21 15.000000 1 // taunt -400 20 29.000000 1 // run -421 20 29.000000 1 // runbackwards -442 20 29.000000 1 // strafeleft -463 20 29.000000 1 // straferight -484 2 15.000000 0 // deadone -486 2 15.000000 0 // deadtwo -488 20 29.000000 1 // forwardright -509 20 29.000000 1 // forwardleft -530 20 29.000000 1 // backright -551 20 29.000000 1 // backleft -572 21 20.000000 0 // melee -593 20 32.000000 1 // duckwalkbackwards -614 20 32.000000 1 // duckwalkstrafeleft -635 20 32.000000 1 // duckwalkstraferight -656 20 32.000000 1 // duckwalkforwardright -677 20 32.000000 1 // duckwalkforwardleft -698 20 32.000000 1 // duckwalkbackright -719 20 32.000000 1 // duckwalkbackleft diff --git a/models/player/ignishalfmasked_lod2.iqm_0.skin b/models/player/ignishalfmasked_lod2.iqm_0.skin deleted file mode 100644 index 9523f9edba..0000000000 --- a/models/player/ignishalfmasked_lod2.iqm_0.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignis -ignis2.001,ignishead diff --git a/models/player/ignishalfmasked_lod2.iqm_1.skin b/models/player/ignishalfmasked_lod2.iqm_1.skin deleted file mode 100644 index 89a0020284..0000000000 --- a/models/player/ignishalfmasked_lod2.iqm_1.skin +++ /dev/null @@ -1,2 +0,0 @@ -ignis1,ignisfullbright -ignis2.001,ignishead diff --git a/models/player/megaerebus.iqm_0.txt b/models/player/megaerebus.iqm_0.txt index f75a15fb8e..f4b62dea70 100644 --- a/models/player/megaerebus.iqm_0.txt +++ b/models/player/megaerebus.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/pyria.iqm_0.skin b/models/player/pyria.iqm_0.skin index 1932354e80..7533a4c53a 100644 --- a/models/player/pyria.iqm_0.skin +++ b/models/player/pyria.iqm_0.skin @@ -1,2 +1,2 @@ pyria_obj.001,pyriahair -pyria_obj,pyriafullbright +pyria_obj,pyria diff --git a/models/player/pyria_lod1.iqm_0.skin b/models/player/pyria_lod1.iqm_0.skin index 1932354e80..7533a4c53a 100644 --- a/models/player/pyria_lod1.iqm_0.skin +++ b/models/player/pyria_lod1.iqm_0.skin @@ -1,2 +1,2 @@ pyria_obj.001,pyriahair -pyria_obj,pyriafullbright +pyria_obj,pyria diff --git a/models/player/pyria_lod2.iqm_0.skin b/models/player/pyria_lod2.iqm_0.skin index 1932354e80..7533a4c53a 100644 --- a/models/player/pyria_lod2.iqm_0.skin +++ b/models/player/pyria_lod2.iqm_0.skin @@ -1,2 +1,2 @@ pyria_obj.001,pyriahair -pyria_obj,pyriafullbright +pyria_obj,pyria diff --git a/models/player/seraphinamasked.iqm_0.skin b/models/player/seraphinamasked.iqm_0.skin index 7b94ebe4a4..7138ad587e 100644 --- a/models/player/seraphinamasked.iqm_0.skin +++ b/models/player/seraphinamasked.iqm_0.skin @@ -1,2 +1,2 @@ -ignis42,ignisfullbright +ignis42,ignis ignis42.002,ignishead diff --git a/models/player/seraphinamasked_lod1.iqm_0.skin b/models/player/seraphinamasked_lod1.iqm_0.skin index 7b94ebe4a4..7138ad587e 100644 --- a/models/player/seraphinamasked_lod1.iqm_0.skin +++ b/models/player/seraphinamasked_lod1.iqm_0.skin @@ -1,2 +1,2 @@ -ignis42,ignisfullbright +ignis42,ignis ignis42.002,ignishead diff --git a/models/player/seraphinamasked_lod2.iqm_0.skin b/models/player/seraphinamasked_lod2.iqm_0.skin index 7b94ebe4a4..7138ad587e 100644 --- a/models/player/seraphinamasked_lod2.iqm_0.skin +++ b/models/player/seraphinamasked_lod2.iqm_0.skin @@ -1,2 +1,2 @@ -ignis42,ignisfullbright +ignis42,ignis ignis42.002,ignishead diff --git a/models/weapons/g_fireball.md3 b/models/weapons/g_fireball.md3 index c953426724..b1463090f7 100644 Binary files a/models/weapons/g_fireball.md3 and b/models/weapons/g_fireball.md3 differ diff --git a/models/weapons/g_ok_hmg_luma.iqm b/models/weapons/g_ok_hmg_luma.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_ok_hmg_luma.iqm differ diff --git a/models/weapons/g_ok_hmg_luma.iqm_0.skin b/models/weapons/g_ok_hmg_luma.iqm_0.skin new file mode 100644 index 0000000000..2c77f8ec7e --- /dev/null +++ b/models/weapons/g_ok_hmg_luma.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_ok_hmg_luma diff --git a/models/weapons/g_ok_hmg_luma.tga b/models/weapons/g_ok_hmg_luma.tga new file mode 100644 index 0000000000..5f8e135488 Binary files /dev/null and b/models/weapons/g_ok_hmg_luma.tga differ diff --git a/models/weapons/g_ok_hmg_simple.iqm b/models/weapons/g_ok_hmg_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_ok_hmg_simple.iqm differ diff --git a/models/weapons/g_ok_hmg_simple.iqm_0.skin b/models/weapons/g_ok_hmg_simple.iqm_0.skin new file mode 100644 index 0000000000..f4316a52cd --- /dev/null +++ b/models/weapons/g_ok_hmg_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_ok_hmg_simple \ No newline at end of file diff --git a/models/weapons/g_ok_hmg_simple.tga b/models/weapons/g_ok_hmg_simple.tga new file mode 100644 index 0000000000..c811a597e2 Binary files /dev/null and b/models/weapons/g_ok_hmg_simple.tga differ diff --git a/models/weapons/g_ok_rl_luma.iqm b/models/weapons/g_ok_rl_luma.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_ok_rl_luma.iqm differ diff --git a/models/weapons/g_ok_rl_luma.iqm_0.skin b/models/weapons/g_ok_rl_luma.iqm_0.skin new file mode 100644 index 0000000000..502ff5b256 --- /dev/null +++ b/models/weapons/g_ok_rl_luma.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_ok_rl_luma diff --git a/models/weapons/g_ok_rl_luma.tga b/models/weapons/g_ok_rl_luma.tga new file mode 100644 index 0000000000..b2471c8ddd Binary files /dev/null and b/models/weapons/g_ok_rl_luma.tga differ diff --git a/models/weapons/g_ok_rl_simple.iqm b/models/weapons/g_ok_rl_simple.iqm new file mode 100644 index 0000000000..a5cc3e602c Binary files /dev/null and b/models/weapons/g_ok_rl_simple.iqm differ diff --git a/models/weapons/g_ok_rl_simple.iqm_0.skin b/models/weapons/g_ok_rl_simple.iqm_0.skin new file mode 100644 index 0000000000..804fe521f0 --- /dev/null +++ b/models/weapons/g_ok_rl_simple.iqm_0.skin @@ -0,0 +1 @@ +Plane,g_ok_rl_simple \ No newline at end of file diff --git a/models/weapons/g_ok_rl_simple.tga b/models/weapons/g_ok_rl_simple.tga new file mode 100644 index 0000000000..53f1be5677 Binary files /dev/null and b/models/weapons/g_ok_rl_simple.tga differ diff --git a/models/weapons/h_fireball.iqm b/models/weapons/h_fireball.iqm index 40a6b06a83..3a9db00ced 100644 Binary files a/models/weapons/h_fireball.iqm and b/models/weapons/h_fireball.iqm differ diff --git a/models/weapons/v_fireball.md3 b/models/weapons/v_fireball.md3 index a5e847c37b..18fbf74890 100644 Binary files a/models/weapons/v_fireball.md3 and b/models/weapons/v_fireball.md3 differ diff --git a/monsters.cfg b/monsters.cfg index f6eca00d00..ac6039041f 100644 --- a/monsters.cfg +++ b/monsters.cfg @@ -88,11 +88,13 @@ set g_monster_shambler_speed_walk 150 // {{{ Misc set g_monsters 1 set g_monsters_edit 0 -set g_monsters_think_delay 0.1 +set g_monsters_think_delay 0.03333 set g_monsters_skill 1 "Monster skill (affecting some of their attributes). 1 - easy, 2 - medium, 3 - hard, 4 - insane, 5 - nightmare" set g_monsters_miniboss_chance 5 set g_monsters_miniboss_healthboost 100 set g_monsters_drop_time 10 +set g_monsters_ignoretraces 1 +set g_monsters_lineofsight 1 set g_monsters_owners 1 set g_monsters_teams 1 set g_monsters_score_kill 0 @@ -109,4 +111,7 @@ set g_monsters_respawn_delay 20 set g_monsters_max 20 set g_monsters_max_perplayer 0 set g_monsters_armor_blockpercent 0.5 +set g_monsters_damageforcescale 0.8 +set g_monsters_quake_resize 1 +set g_monsters_healthbars 0 // }}} diff --git a/mutators.cfg b/mutators.cfg index 7910786107..cd6a9e231a 100644 --- a/mutators.cfg +++ b/mutators.cfg @@ -10,7 +10,8 @@ set g_dodging 0 "set to 1 to enable dodging in games" seta cl_dodging_timeout 0.2 "determines how long apart (in seconds) two taps on the same direction key are considered a dodge. use 0 to disable" -set sv_dodging_wall_dodging 0 "set to 1 to allow dodging off walls. 0 to disable" +set sv_dodging_air_dodging 0 +set sv_dodging_wall_dodging 0 "allow dodging off walls" set sv_dodging_delay 0.7 "determines how long a player has to wait to be able to dodge again after dodging" set sv_dodging_up_speed 200 "the jump velocity of the dodge" set sv_dodging_horiz_speed 400 "the horizontal velocity of the dodge" @@ -20,6 +21,7 @@ set sv_dodging_height_threshold 10 "the maximum height above ground where to all set sv_dodging_wall_distance_threshold 10 "the maximum distance from a wall that still allows dodging" set sv_dodging_sound 1 "if 1 dodging makes a sound. if 0 dodging is silent" set sv_dodging_frozen 0 "allow dodging while frozen" +set sv_dodging_frozen_doubletap 0 // =========== @@ -29,6 +31,10 @@ set g_instagib 0 "enable instagib" set g_instagib_extralives 1 "how many extra lives you will get per powerup" set g_instagib_ammo_start 10 "starting ammo" set g_instagib_ammo_drop 5 "how much ammo you'll get for weapons or cells" +set g_instagib_ammo_convert_bullets 0 "convert bullet ammo packs to insta cell ammo packs" +set g_instagib_ammo_convert_cells 0 "convert normal cell ammo packs to insta cell ammo packs" +set g_instagib_ammo_convert_rockets 0 "convert rocket ammo packs to insta cell ammo packs" +set g_instagib_ammo_convert_shells 0 "convert shell ammo packs to insta cell ammo packs" set g_instagib_invis_alpha 0.15 set g_instagib_speed_highspeed 1.5 "speed-multiplier that applies while you carry the invincibility powerup" set g_instagib_damagedbycontents 1 "allow damage from lava pits in instagib" @@ -43,10 +49,11 @@ set g_instagib_friendlypush 1 "allow pushing teammates with the vaporizer primar // ========== set g_overkill 0 "enable overkill" -set g_overkill_100a_anyway 1 -set g_overkill_100h_anyway 1 set g_overkill_powerups_replace 1 -set g_overkill_superguns_respawn_time 120 +set g_overkill_filter_healthmega 0 +set g_overkill_filter_armormedium 0 +set g_overkill_filter_armorbig 0 +set g_overkill_filter_armormega 0 set g_overkill_ammo_charge 0 set g_overkill_ammo_charge_notice 1 @@ -118,8 +125,9 @@ seta cl_spawn_near_teammate 1 "toggle for spawning near teammates (only effectiv set g_spawn_near_teammate 0 "if set, players prefer spawns near a team mate" set g_spawn_near_teammate_distance 640 "max distance to consider a spawn to be near a team mate" set g_spawn_near_teammate_ignore_spawnpoint 0 "ignore spawnpoints and spawn right at team mates, if 2, clients can ignore this option" +set g_spawn_near_teammate_ignore_spawnpoint_max 10 "if set, test at most this many of the available teammates" set g_spawn_near_teammate_ignore_spawnpoint_delay 2.5 "how long to wait before its OK to spawn at a player after someone just spawned at this player" -set g_spawn_near_teammate_ignore_spawnpoint_delay_death 0 "how long to wait before its OK to spawn at a player after death" +set g_spawn_near_teammate_ignore_spawnpoint_delay_death 3 "how long to wait before its OK to spawn at a player after death" set g_spawn_near_teammate_ignore_spawnpoint_check_health 1 "only allow spawn at this player if their health is full" set g_spawn_near_teammate_ignore_spawnpoint_closetodeath 1 "spawn as close to death location as possible" @@ -187,6 +195,7 @@ set g_nades_spawn 1 "give nades right away when player spawns rather than delayi set g_nades_client_select 0 "allow client side selection of nade type" set g_nades_pickup 0 "allow picking up thrown nades (not your own)" set g_nades_pickup_time 2 "time until picked up nade explodes" +set g_nades_override_dropweapon 1 set g_nades_nade_lifetime 3.5 set g_nades_nade_minforce 400 set g_nades_nade_maxforce 2000 @@ -295,15 +304,15 @@ set g_new_toys_use_pickupsound 1 "play the 'new toys, new toys!' roflsound when // buffs // ======= set cl_buffs_autoreplace 1 "automatically drop current buff when picking up another" -set g_buffs 0 "enable buffs (requires buff items or powerups)" +set g_buffs -1 "enable buffs (requires buff items or powerups)" set g_buffs_effects 1 "show particle effects from carried buffs" set g_buffs_waypoint_distance 1024 "maximum distance at which buff waypoint can be seen from item" set g_buffs_randomize 1 "randomize buff type when player drops buff" set g_buffs_random_lifetime 30 "re-spawn the buff again if it hasn't been touched after this time in seconds" set g_buffs_random_location 0 "randomize buff location on start and when reset" set g_buffs_random_location_attempts 10 "number of random locations a single buff will attempt to respawn at before giving up" -set g_buffs_spawn_count 5 "how many buffs to spawn on the map if none exist already" -set g_buffs_replace_powerups 1 "replace powerups on the map with random buffs" +set g_buffs_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_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" @@ -368,6 +377,8 @@ set g_buffs_luck 1 "luck buff: randomly increased damage" set g_buffs_luck_time 60 "luck buff carry time" set g_buffs_luck_chance 0.15 "chance for 'critical' hit (multiplied damage) with luck buff" set g_buffs_luck_damagemultiplier 3 "luck damage multiplier" +set g_buffs_flight 0 "flight buff: crouch jump to reverse your gravity!" +set g_buffs_flight_time 60 "flight buff carry time" // ============== @@ -419,3 +430,24 @@ seta cl_multijump 1 "allow multijump mutator" set g_multijump 0 "Number of multiple jumps to allow (jumping again in the air), -1 allows for infinite jumps" set g_multijump_add 0 "0 = make the current z velocity equal to jumpvelocity, 1 = add jumpvelocity to the current z velocity" set g_multijump_speed -999999 "Minimum vertical speed a player must have in order to jump again" +set g_multijump_maxspeed 0 +set g_multijump_dodging 1 + + +// =========== +// wall jump +// =========== +set g_walljump 0 "Enable wall jumping mutator" +set g_walljump_delay 1 "Minimum delay between wall jumps" +set g_walljump_force 300 "How far to bounce/jump off the wall" +set g_walljump_velocity_xy_factor 1.15 "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping" +set g_walljump_velocity_z_factor 0.5 "Upwards velocity factor, multiplied by normal jump velocity" + + +// =============== +// global forces +// =============== +set g_globalforces 0 "Global forces: knockback affects everyone" +set g_globalforces_noself 1 "Global forces: ignore self damage" +set g_globalforces_self 1 "Global forces: knockback self scale" +set g_globalforces_range 1000 "Global forces: max range of effect" diff --git a/notifications.cfg b/notifications.cfg index 2ee0a4084e..8ca401bb42 100644 --- a/notifications.cfg +++ b/notifications.cfg @@ -102,7 +102,7 @@ seta notification_ANNCE_VOTE_ACCEPT "2" "0 = disabled, 1 = enabled if gentle mod seta notification_ANNCE_VOTE_CALL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled" seta notification_ANNCE_VOTE_FAIL "2" "0 = disabled, 1 = enabled if gentle mode is off, 2 = always enabled" -// MSG_INFO notifications (count = 316): +// MSG_INFO notifications (count = 320): seta notification_INFO_CA_JOIN_LATE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CA_LEAVE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_CHAT_NOSPECTATORS "2" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -311,6 +311,10 @@ seta notification_INFO_LMS_FORFEIT "1" "0 = off, 1 = print to console, 2 = print seta notification_INFO_LMS_NOLIVES "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_MINIGAME_INVITE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_MONSTERS_DISABLED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_NEXBALL_RETURN_HELD_BLUE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_NEXBALL_RETURN_HELD_PINK "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_NEXBALL_RETURN_HELD_RED "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" +seta notification_INFO_NEXBALL_RETURN_HELD_YELLOW "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_ONSLAUGHT_CAPTURE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_ONSLAUGHT_CPDESTROYED_BLUE "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_ONSLAUGHT_CPDESTROYED_PINK "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" @@ -420,10 +424,11 @@ seta notification_INFO_WEAPON_TUBA_SUICIDE "1" "0 = off, 1 = print to console, 2 seta notification_INFO_WEAPON_VAPORIZER_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" seta notification_INFO_WEAPON_VORTEX_MURDER "1" "0 = off, 1 = print to console, 2 = print to console and chatbox (if notification_allow_chatboxprint is enabled)" -// MSG_CENTER notifications (count = 224): +// MSG_CENTER notifications (count = 231): seta notification_CENTER_ALONE "1" "0 = off, 1 = centerprint" seta notification_CENTER_ASSAULT_ATTACKING "1" "0 = off, 1 = centerprint" seta notification_CENTER_ASSAULT_DEFENDING "1" "0 = off, 1 = centerprint" +seta notification_CENTER_ASSAULT_OBJ_DESTROYED "1" "0 = off, 1 = centerprint" seta notification_CENTER_CAMPCHECK "1" "0 = off, 1 = centerprint" seta notification_CENTER_COINTOSS "1" "0 = off, 1 = centerprint" seta notification_CENTER_COUNTDOWN_BEGIN "1" "0 = off, 1 = centerprint" @@ -478,6 +483,7 @@ seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_PINK "1" "0 = off, 1 = centerpr seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_RED "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_PICKUP_TEAM_VERBOSE_YELLOW "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_PICKUP_TEAM_YELLOW "1" "0 = off, 1 = centerprint" +seta notification_CENTER_CTF_PICKUP_VISIBLE "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_PICKUP_YELLOW "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_RETURN_BLUE "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_RETURN_PINK "1" "0 = off, 1 = centerprint" @@ -487,7 +493,15 @@ seta notification_CENTER_CTF_STALEMATE_CARRIER "1" "0 = off, 1 = centerprint" seta notification_CENTER_CTF_STALEMATE_OTHER "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_FRAG "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_FRAGGED "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAGGED_FIRE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAGGED_FIRE_VERBOSE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAGGED_FREEZE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAGGED_FREEZE_VERBOSE "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_FRAGGED_VERBOSE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAG_FIRE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAG_FIRE_VERBOSE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAG_FREEZE "1" "0 = off, 1 = centerprint" +seta notification_CENTER_DEATH_MURDER_FRAG_FREEZE_VERBOSE "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_FRAG_VERBOSE "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_TYPEFRAG "1" "0 = off, 1 = centerprint" seta notification_CENTER_DEATH_MURDER_TYPEFRAGGED "1" "0 = off, 1 = centerprint" @@ -536,8 +550,6 @@ seta notification_CENTER_DOOR_LOCKED_NEED "1" "0 = off, 1 = centerprint" seta notification_CENTER_DOOR_UNLOCKED "1" "0 = off, 1 = centerprint" seta notification_CENTER_EXTRALIVES "1" "0 = off, 1 = centerprint" seta notification_CENTER_FREEZETAG_AUTO_REVIVED "1" "0 = off, 1 = centerprint" -seta notification_CENTER_FREEZETAG_FREEZE "1" "0 = off, 1 = centerprint" -seta notification_CENTER_FREEZETAG_FROZEN "1" "0 = off, 1 = centerprint" seta notification_CENTER_FREEZETAG_REVIVE "1" "0 = off, 1 = centerprint" seta notification_CENTER_FREEZETAG_REVIVED "1" "0 = off, 1 = centerprint" seta notification_CENTER_FREEZETAG_REVIVE_SELF "1" "0 = off, 1 = centerprint" @@ -582,7 +594,6 @@ seta notification_CENTER_LMS_NOLIVES "1" "0 = off, 1 = centerprint" seta notification_CENTER_MISSING_PLAYERS "1" "0 = off, 1 = centerprint" seta notification_CENTER_MISSING_TEAMS "1" "0 = off, 1 = centerprint" seta notification_CENTER_MOTD "1" "0 = off, 1 = centerprint" -seta notification_CENTER_NADE "1" "0 = off, 1 = centerprint" seta notification_CENTER_NADE_BONUS "1" "0 = off, 1 = centerprint" seta notification_CENTER_NADE_THROW "1" "0 = off, 1 = centerprint" seta notification_CENTER_NIX_COUNTDOWN "1" "0 = off, 1 = centerprint" @@ -801,7 +812,7 @@ seta notification_WEAPON_TUBA_SUICIDE "1" "Enable this multiple notification" seta notification_WEAPON_VAPORIZER_MURDER "1" "Enable this multiple notification" seta notification_WEAPON_VORTEX_MURDER "1" "Enable this multiple notification" -// MSG_CHOICE notifications (count = 24): +// MSG_CHOICE notifications (count = 28): seta notification_CHOICE_CTF_CAPTURE_BROKEN_BLUE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_CTF_CAPTURE_BROKEN_BLUE_ALLOWED "2" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" seta notification_CHOICE_CTF_CAPTURE_BROKEN_PINK "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" @@ -846,6 +857,14 @@ seta notification_CHOICE_FRAG "1" "Choice for this notification 0 = off, 1 = def seta notification_CHOICE_FRAG_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" seta notification_CHOICE_FRAGGED "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_FRAGGED_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" +seta notification_CHOICE_FRAGGED_FIRE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_FRAGGED_FIRE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" +seta notification_CHOICE_FRAGGED_FREEZE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_FRAGGED_FREEZE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" +seta notification_CHOICE_FRAG_FIRE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_FRAG_FIRE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" +seta notification_CHOICE_FRAG_FREEZE "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" +seta notification_CHOICE_FRAG_FREEZE_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" seta notification_CHOICE_TYPEFRAG "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" seta notification_CHOICE_TYPEFRAG_ALLOWED "1" "Allow choice for this notification 0 = off, 1 = only in warmup mode, 2 = always" seta notification_CHOICE_TYPEFRAGGED "1" "Choice for this notification 0 = off, 1 = default message, 2 = verbose message" @@ -868,4 +887,4 @@ seta notification_show_sprees_info "3" "Show spree information in MSG_INFO messa seta notification_show_sprees_info_newline "1" "Show attacker spree information for MSG_INFO messages on a separate line than the death notification itself" seta notification_show_sprees_info_specialonly "1" "Don't show attacker spree information in MSG_INFO messages if it isn't an achievement" -// Notification counts (total = 806): MSG_ANNCE = 89, MSG_INFO = 316, MSG_CENTER = 224, MSG_MULTI = 153, MSG_CHOICE = 24 +// Notification counts (total = 821): MSG_ANNCE = 89, MSG_INFO = 320, MSG_CENTER = 231, MSG_MULTI = 153, MSG_CHOICE = 28 diff --git a/physics.cfg b/physics.cfg index e9fe562894..1a38a7f617 100644 --- a/physics.cfg +++ b/physics.cfg @@ -28,6 +28,7 @@ set g_physics_xonotic_airaccel_qw_stretchfactor 2 set g_physics_xonotic_airaccel_sideways_friction 0 set g_physics_xonotic_aircontrol 100 set g_physics_xonotic_aircontrol_power 2 +set g_physics_xonotic_aircontrol_backwards 0 set g_physics_xonotic_aircontrol_penalty 0 set g_physics_xonotic_warsowbunny_airforwardaccel 1.00001 set g_physics_xonotic_warsowbunny_topspeed 925 @@ -56,6 +57,7 @@ set g_physics_nexuiz_airaccel_qw_stretchfactor 0 set g_physics_nexuiz_airaccel_sideways_friction 0.35 set g_physics_nexuiz_aircontrol 0 set g_physics_nexuiz_aircontrol_power 2 +set g_physics_nexuiz_aircontrol_backwards 0 set g_physics_nexuiz_aircontrol_penalty 0 set g_physics_nexuiz_warsowbunny_airforwardaccel 1.00001 set g_physics_nexuiz_warsowbunny_topspeed 925 @@ -84,6 +86,7 @@ set g_physics_quake_airaccel_qw_stretchfactor 0 set g_physics_quake_airaccel_sideways_friction 0 set g_physics_quake_aircontrol 0 set g_physics_quake_aircontrol_power 2 +set g_physics_quake_aircontrol_backwards 0 set g_physics_quake_aircontrol_penalty 0 set g_physics_quake_warsowbunny_airforwardaccel 1.00001 set g_physics_quake_warsowbunny_topspeed 925 @@ -112,6 +115,7 @@ set g_physics_warsow_airaccel_qw_stretchfactor 0 set g_physics_warsow_airaccel_sideways_friction 0 set g_physics_warsow_aircontrol 0 set g_physics_warsow_aircontrol_power 2 +set g_physics_warsow_aircontrol_backwards 0 set g_physics_warsow_aircontrol_penalty 0 set g_physics_warsow_warsowbunny_airforwardaccel 1.00001 set g_physics_warsow_warsowbunny_topspeed 925 @@ -140,6 +144,7 @@ set g_physics_defrag_airaccel_qw_stretchfactor 0 set g_physics_defrag_airaccel_sideways_friction 0 set g_physics_defrag_aircontrol 150 set g_physics_defrag_aircontrol_power 2 +set g_physics_defrag_aircontrol_backwards 0 set g_physics_defrag_aircontrol_penalty 0 set g_physics_defrag_warsowbunny_airforwardaccel 1.00001 set g_physics_defrag_warsowbunny_topspeed 925 @@ -168,6 +173,7 @@ set g_physics_quake3_airaccel_qw_stretchfactor 0 set g_physics_quake3_airaccel_sideways_friction 0 set g_physics_quake3_aircontrol 0 set g_physics_quake3_aircontrol_power 2 +set g_physics_quake3_aircontrol_backwards 0 set g_physics_quake3_aircontrol_penalty 0 set g_physics_quake3_warsowbunny_airforwardaccel 1.00001 set g_physics_quake3_warsowbunny_topspeed 925 @@ -196,6 +202,7 @@ set g_physics_vecxis_airaccel_qw_stretchfactor 0 set g_physics_vecxis_airaccel_sideways_friction 0.3 set g_physics_vecxis_aircontrol 0 set g_physics_vecxis_aircontrol_power 2 +set g_physics_vecxis_aircontrol_backwards 0 set g_physics_vecxis_aircontrol_penalty 0 set g_physics_vecxis_warsowbunny_airforwardaccel 1.00001 set g_physics_vecxis_warsowbunny_topspeed 925 @@ -224,6 +231,7 @@ set g_physics_quake2_airaccel_qw_stretchfactor 0 set g_physics_quake2_airaccel_sideways_friction 0 set g_physics_quake2_aircontrol 0 set g_physics_quake2_aircontrol_power 2 +set g_physics_quake2_aircontrol_backwards 0 set g_physics_quake2_aircontrol_penalty 0 set g_physics_quake2_warsowbunny_airforwardaccel 1.00001 set g_physics_quake2_warsowbunny_topspeed 925 @@ -252,6 +260,7 @@ set g_physics_bones_airaccel_qw_stretchfactor 0 set g_physics_bones_airaccel_sideways_friction 0 set g_physics_bones_aircontrol 150 set g_physics_bones_aircontrol_power 2 +set g_physics_bones_aircontrol_backwards 0 set g_physics_bones_aircontrol_penalty 0 set g_physics_bones_warsowbunny_airforwardaccel 1.00001 set g_physics_bones_warsowbunny_topspeed 925 @@ -263,3 +272,32 @@ set g_physics_bones_stopspeed 100 set g_physics_bones_airaccelerate 1 set g_physics_bones_airstopaccelerate 2.5 set g_physics_bones_track_canjump 0 + +// ========== +// Overkill +// ========== +set g_physics_overkill_airaccel_qw -0.8 +set g_physics_overkill_airstrafeaccel_qw -0.95 +set g_physics_overkill_airspeedlimit_nonqw 900 +set g_physics_overkill_maxspeed 400 +set g_physics_overkill_jumpvelocity 260 +set g_physics_overkill_maxairstrafespeed 100 +set g_physics_overkill_maxairspeed 360 +set g_physics_overkill_airstrafeaccelerate 24 +set g_physics_overkill_warsowbunny_turnaccel 0 +set g_physics_overkill_airaccel_qw_stretchfactor 2 +set g_physics_overkill_airaccel_sideways_friction 0 +set g_physics_overkill_aircontrol 125 +set g_physics_overkill_aircontrol_power 2 +set g_physics_overkill_aircontrol_backwards 0 +set g_physics_overkill_aircontrol_penalty 180 +set g_physics_overkill_warsowbunny_airforwardaccel 1.00001 +set g_physics_overkill_warsowbunny_topspeed 925 +set g_physics_overkill_warsowbunny_accel 0.1593 +set g_physics_overkill_warsowbunny_backtosideratio 0.8 +set g_physics_overkill_friction 8 +set g_physics_overkill_accelerate 15 +set g_physics_overkill_stopspeed 100 +set g_physics_overkill_airaccelerate 2 +set g_physics_overkill_airstopaccelerate 3 +set g_physics_overkill_track_canjump 0 diff --git a/physicsCPMA.cfg b/physicsCPMA.cfg index e4bbad3d48..f2a4209c25 100644 --- a/physicsCPMA.cfg +++ b/physicsCPMA.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 150 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsFruit.cfg b/physicsFruit.cfg index ee44b5547a..efe5b46ac3 100644 --- a/physicsFruit.cfg +++ b/physicsFruit.cfg @@ -22,6 +22,7 @@ sv_maxairstrafespeed 100 sv_airstrafeaccel_qw -0.97 sv_aircontrol 125 sv_aircontrol_power 2.5 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsHavoc.cfg b/physicsHavoc.cfg index 6aac341780..8d02ff4627 100644 --- a/physicsHavoc.cfg +++ b/physicsHavoc.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsLeeStricklin-ModdedFruit.cfg b/physicsLeeStricklin-ModdedFruit.cfg index 90eaa49c12..61ce6557df 100644 --- a/physicsLeeStricklin-ModdedFruit.cfg +++ b/physicsLeeStricklin-ModdedFruit.cfg @@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.97 sv_aircontrol 125 sv_aircontrol_penalty 150 sv_aircontrol_power 2.5 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 800 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsLeeStricklin.cfg b/physicsLeeStricklin.cfg index 03081fdab7..53ec90148e 100644 --- a/physicsLeeStricklin.cfg +++ b/physicsLeeStricklin.cfg @@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.95 sv_aircontrol 125 sv_aircontrol_penalty 150 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 800 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsLeeStricklinOld.cfg b/physicsLeeStricklinOld.cfg index ef213a70f9..6a24567a93 100644 --- a/physicsLeeStricklinOld.cfg +++ b/physicsLeeStricklinOld.cfg @@ -26,6 +26,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsLzd.cfg b/physicsLzd.cfg index 51c49d7832..f6f74cd21a 100644 --- a/physicsLzd.cfg +++ b/physicsLzd.cfg @@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz10.cfg b/physicsNexuiz10.cfg index 0ce33aa951..9292016afe 100644 --- a/physicsNexuiz10.cfg +++ b/physicsNexuiz10.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz11.cfg b/physicsNexuiz11.cfg index 08711fd5b0..660e7b8183 100644 --- a/physicsNexuiz11.cfg +++ b/physicsNexuiz11.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz151.cfg b/physicsNexuiz151.cfg index 092985cae5..2c2e94c5f4 100644 --- a/physicsNexuiz151.cfg +++ b/physicsNexuiz151.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz151b.cfg b/physicsNexuiz151b.cfg index c6d9bef4b9..45a46cb46e 100644 --- a/physicsNexuiz151b.cfg +++ b/physicsNexuiz151b.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz16rc1.cfg b/physicsNexuiz16rc1.cfg index 5e0ee6ae0c..86f1c31c74 100644 --- a/physicsNexuiz16rc1.cfg +++ b/physicsNexuiz16rc1.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz20.cfg b/physicsNexuiz20.cfg index d129a2db05..fe46176885 100644 --- a/physicsNexuiz20.cfg +++ b/physicsNexuiz20.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz25.cfg b/physicsNexuiz25.cfg index 8a52299763..ed45598a9b 100644 --- a/physicsNexuiz25.cfg +++ b/physicsNexuiz25.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNexuiz26.cfg b/physicsNexuiz26.cfg index 4df3f211eb..3f7cd58f18 100644 --- a/physicsNexuiz26.cfg +++ b/physicsNexuiz26.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsNoQWBunny-nexbased.cfg b/physicsNoQWBunny-nexbased.cfg index 3e905428f7..77660a426a 100644 --- a/physicsNoQWBunny-nexbased.cfg +++ b/physicsNoQWBunny-nexbased.cfg @@ -32,6 +32,7 @@ sv_airstrafeaccel_qw -0.9825 sv_aircontrol 125 sv_aircontrol_penalty 100 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQ.cfg b/physicsQ.cfg index 448e087f46..609a8f3d30 100644 --- a/physicsQ.cfg +++ b/physicsQ.cfg @@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQ2.cfg b/physicsQ2.cfg index 124c50660a..a9fbe166da 100644 --- a/physicsQ2.cfg +++ b/physicsQ2.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQ2a.cfg b/physicsQ2a.cfg index 580f6257fa..87817dfcfd 100644 --- a/physicsQ2a.cfg +++ b/physicsQ2a.cfg @@ -24,6 +24,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQ3.cfg b/physicsQ3.cfg index 6f00896b67..7363b216de 100644 --- a/physicsQ3.cfg +++ b/physicsQ3.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQBF.cfg b/physicsQBF.cfg index 31963e9fee..adc9df69c9 100644 --- a/physicsQBF.cfg +++ b/physicsQBF.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsQBFplus.cfg b/physicsQBFplus.cfg index 8246385983..596d6ca3b8 100644 --- a/physicsQBFplus.cfg +++ b/physicsQBFplus.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsSamual.cfg b/physicsSamual.cfg index 6858892e3b..9041601e2f 100644 --- a/physicsSamual.cfg +++ b/physicsSamual.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsWarsow.cfg b/physicsWarsow.cfg index 50101d73d4..5fbe22bab9 100644 --- a/physicsWarsow.cfg +++ b/physicsWarsow.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 9 // activates warsow movement mode sv_warsowbunny_accel 0.1593 diff --git a/physicsWarsowClassicBunny.cfg b/physicsWarsowClassicBunny.cfg index c4c3535a7f..3b28d90999 100644 --- a/physicsWarsowClassicBunny.cfg +++ b/physicsWarsowClassicBunny.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 150 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsWarsowDev.cfg b/physicsWarsowDev.cfg index 331723ec93..cf31a3f932 100644 --- a/physicsWarsowDev.cfg +++ b/physicsWarsowDev.cfg @@ -23,6 +23,7 @@ sv_airstrafeaccel_qw 0 sv_aircontrol 0 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 6 // activates warsow movement mode sv_warsowbunny_accel 0.1585 diff --git a/physicsX.cfg b/physicsX.cfg index c109e11576..2ef0689755 100644 --- a/physicsX.cfg +++ b/physicsX.cfg @@ -17,7 +17,7 @@ sv_stepheight 31 // Samual: 31 (just below 32, keeping things smooth without allowing 32qu steps) // jump duration == 2*sv_jumpvelocity / sv_gravity -// in this case: 0.6888888888 (thus either 20 or 21 frames) +// in this case: 0.65 (thus either 19 or 20 frames) // jump height == sv_jumpvelocity^2 / (2*sv_gravity) // in this case: 42.25 // player: 24+45 qu @@ -38,6 +38,7 @@ sv_airstrafeaccel_qw -0.95 sv_aircontrol 100 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 900 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsX010.cfg b/physicsX010.cfg index 6dadcf161b..0dfe0b46c9 100644 --- a/physicsX010.cfg +++ b/physicsX010.cfg @@ -31,6 +31,7 @@ sv_airstrafeaccel_qw -0.95 sv_aircontrol 125 sv_aircontrol_penalty 150 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 800 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsX07.cfg b/physicsX07.cfg index a3e42a977e..132aa9eff6 100644 --- a/physicsX07.cfg +++ b/physicsX07.cfg @@ -37,6 +37,7 @@ sv_airstrafeaccel_qw -0.95 sv_aircontrol 125 sv_aircontrol_penalty 180 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 800 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsXDF.cfg b/physicsXDF.cfg index 8597e9e005..5a3aaa393b 100644 --- a/physicsXDF.cfg +++ b/physicsXDF.cfg @@ -29,6 +29,7 @@ sv_airstrafeaccel_qw 1 sv_aircontrol 150 sv_aircontrol_penalty 0 sv_aircontrol_power 2 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/physicsXDFLight.cfg b/physicsXDFLight.cfg index 6cb0780dd9..4d1f652bb8 100644 --- a/physicsXDFLight.cfg +++ b/physicsXDFLight.cfg @@ -29,6 +29,7 @@ sv_airstrafeaccel_qw -0.987 sv_aircontrol 100 sv_aircontrol_penalty 100 sv_aircontrol_power 2.5 +sv_aircontrol_backwards 0 sv_airspeedlimit_nonqw 0 sv_warsowbunny_turnaccel 0 sv_warsowbunny_accel 0.1593 diff --git a/qcsrc/.editorconfig b/qcsrc/.editorconfig new file mode 100644 index 0000000000..013c331e8a --- /dev/null +++ b/qcsrc/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*.{qc,qh,inc}] +end_of_line = lf +insert_final_newline = true +indent_style = tab +charset = utf-8 diff --git a/qcsrc/Doxyfile b/qcsrc/Doxyfile index cb9ca2a883..06ee7468dd 100644 --- a/qcsrc/Doxyfile +++ b/qcsrc/Doxyfile @@ -1997,15 +1997,17 @@ INCLUDE_FILE_PATTERNS = # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = \ + "XONOTIC" \ "USING(name, T)=using name = T" \ "CLASS(name, base)=class name : public base { public:" \ "INIT(class)=class::class()" \ "CONSTRUCTOR(class)=class::class(" \ "DESTRUCTOR(class)=class::~class()" \ - "ATTRIB(class, name, T, val)=T name = val;" \ - "ATTRIB_STRZONE(class, name, T, val)=T name = val;" \ - "STATIC_ATTRIB(class, name, T, val)=static T name = val;" \ - "STATIC_ATTRIB_STRZONE(class, name, T, val)=static T name = val;" \ + "ATTRIB(class, name, T, val)=T name = val" \ + "ATTRIB_STRZONE(class, name, T, val)=T name = val" \ + "ATTRIBARRAY(class, name, T, val)=T name[val]" \ + "STATIC_ATTRIB(class, name, T, val)=static T name = val" \ + "STATIC_ATTRIB_STRZONE(class, name, T, val)=static T name = val" \ "METHOD(class, name, prototype)=virtual void class::name()" \ "ENDCLASS(class)=};" \ __STDC__ diff --git a/qcsrc/Makefile b/qcsrc/Makefile index ee335d9b00..903d8537e6 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -7,7 +7,8 @@ WORKDIR ?= ../.tmp QCCFLAGS_WATERMARK ?= $(shell git describe --tags --dirty='~') VER = $(subst *,\*,$(QCCFLAGS_WATERMARK)) NDEBUG ?= 1 -BUILD_MOD ?= 0 +XONOTIC ?= 1 +BUILD_MOD ?= ifndef ZIP ifneq ($(shell which zip),) @@ -30,9 +31,10 @@ QCCFLAGS_WTFS ?= \ -Wno-field-redeclared QCCDEFS ?= \ + -DXONOTIC=$(XONOTIC) \ -DWATERMARK="$(QCCFLAGS_WATERMARK)" \ -DNDEBUG=$(NDEBUG) \ - -DBUILD_MOD=$(BUILD_MOD) \ + $(if $(BUILD_MOD), -DBUILD_MOD="$(BUILD_MOD)" -I$(BUILD_MOD), ) \ $(QCCDEFS_EXTRA) # -Ooverlap-locals is required diff --git a/qcsrc/client/_all.inc b/qcsrc/client/_all.inc new file mode 100644 index 0000000000..f592f8adb1 --- /dev/null +++ b/qcsrc/client/_all.inc @@ -0,0 +1,20 @@ +#include +#include "_mod.inc" + +#include "commands/_mod.inc" +#include "hud/_mod.inc" +#include "mutators/_mod.inc" +#include "weapons/_mod.inc" + +#include +#include + +#include +#include +#include + +#include +#include +#include +#include +#include diff --git a/qcsrc/client/_all.qh b/qcsrc/client/_all.qh index 746734dee2..077b5f450a 100644 --- a/qcsrc/client/_all.qh +++ b/qcsrc/client/_all.qh @@ -1,4 +1,5 @@ #pragma once +//#include "_mod.qh" #include @@ -10,3 +11,5 @@ #include "defs.qh" #include "main.qh" #include "miscfunctions.qh" + +#include diff --git a/qcsrc/client/_mod.inc b/qcsrc/client/_mod.inc index 186be97400..0920265d63 100644 --- a/qcsrc/client/_mod.inc +++ b/qcsrc/client/_mod.inc @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/qcsrc/client/_mod.qh b/qcsrc/client/_mod.qh index 1c66ff781e..75266dfa2d 100644 --- a/qcsrc/client/_mod.qh +++ b/qcsrc/client/_mod.qh @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/qcsrc/client/announcer.qc b/qcsrc/client/announcer.qc index 0a0959b87a..62b732bec2 100644 --- a/qcsrc/client/announcer.qc +++ b/qcsrc/client/announcer.qc @@ -24,7 +24,7 @@ void Announcer_Countdown(entity this) if(roundstarttime == -1) { Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_ROUNDSTOP); - remove(this); + delete(this); announcer_countdown = NULL; return; } @@ -40,7 +40,7 @@ void Announcer_Countdown(entity this) { Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_BEGIN); Local_Notification(MSG_MULTI, MULTI_COUNTDOWN_BEGIN); - remove(this); + delete(this); announcer_countdown = NULL; return; } @@ -58,7 +58,7 @@ void Announcer_Countdown(entity this) { Local_Notification(MSG_CENTER, CENTER_COUNTDOWN_GAMESTART, countdown_rounded); Notification annce_num = Announcer_PickNumber(CNT_GAMESTART, countdown_rounded); - if(annce_num != NULL) + if(annce_num != NULL) Local_Notification(MSG_ANNCE, annce_num); } @@ -87,7 +87,7 @@ void Announcer_Gamestart() centerprint_kill(ORDINAL(CPID_ROUND)); if(announcer_countdown) { - remove(announcer_countdown); + delete(announcer_countdown); announcer_countdown = NULL; } } diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index d159cd3170..193b7ef7ca 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -93,14 +93,13 @@ string autocvar_cl_weaponpriority; float autocvar_cl_zoomfactor; float autocvar_cl_zoomsensitivity; float autocvar_cl_zoomspeed; -bool autocvar_cl_unpress_zoom_on_spawn = 1; -bool autocvar_cl_unpress_zoom_on_death = 1; -bool autocvar_cl_unpress_zoom_on_weapon_switch = 1; -bool autocvar_cl_unpress_attack_on_weapon_switch = 1; +bool autocvar_cl_unpress_zoom_on_spawn = true; +bool autocvar_cl_unpress_zoom_on_death = true; +bool autocvar_cl_unpress_zoom_on_weapon_switch = true; +bool autocvar_cl_unpress_attack_on_weapon_switch = false; bool autocvar_con_chat; bool autocvar_con_chatrect; float autocvar_con_chatsize; -float autocvar_con_chattime; float autocvar_con_notify; float autocvar_con_notifysize; string autocvar_crosshair; @@ -123,7 +122,7 @@ string autocvar_crosshair_hitindication_per_weapon_color; float autocvar_crosshair_hitindication_speed; bool autocvar_crosshair_hittest; bool autocvar_crosshair_hittest_blur; -float autocvar_crosshair_hittest_scale = 1.25; +//float autocvar_crosshair_hittest_scale = 1.25; bool autocvar_crosshair_hittest_showimpact; bool autocvar_crosshair_per_weapon; float autocvar_crosshair_pickup; @@ -209,7 +208,11 @@ bool autocvar_hud_panel_engineinfo_dynamichud = true; bool autocvar_hud_panel_infomessages_dynamichud = false; bool autocvar_hud_panel_physics_dynamichud = true; bool autocvar_hud_panel_centerprint_dynamichud = true; -bool autocvar_hud_panel_itemstime_dynamichud = true; +//bool autocvar_hud_panel_itemstime_dynamichud = true; +bool autocvar_hud_panel_healtharmor_hide_ondeath = false; +bool autocvar_hud_panel_ammo_hide_ondeath = false; +bool autocvar_hud_panel_powerups_hide_ondeath = false; +bool autocvar_hud_panel_weapons_hide_ondeath = false; bool autocvar_hud_panel_ammo; bool autocvar_hud_panel_ammo_iconalign; int autocvar_hud_panel_ammo_maxammo; @@ -262,6 +265,7 @@ float autocvar_hud_panel_healtharmor_progressbar_gfx_smooth; int autocvar_hud_panel_healtharmor_text; bool autocvar_hud_panel_infomessages; bool autocvar_hud_panel_infomessages_flip; +float autocvar_hud_panel_mapvote_highlight_border = 1; bool autocvar_hud_panel_modicons; int autocvar_hud_panel_modicons_ca_layout; int autocvar_hud_panel_modicons_dom_layout; @@ -319,7 +323,6 @@ bool autocvar_hud_panel_timer_increment; float autocvar_hud_panel_update_interval; bool autocvar_hud_panel_vote; float autocvar_hud_panel_vote_alreadyvoted_alpha; -string autocvar_hud_panel_vote_bg_alpha; bool autocvar_hud_panel_weapons; bool autocvar_hud_panel_weapons_accuracy; bool autocvar_hud_panel_weapons_ammo; @@ -391,34 +394,10 @@ float autocvar_hud_shownames_offset; string autocvar_hud_skin; float autocvar_menu_mouse_speed; string autocvar_menu_skin; -float autocvar_r_drawviewmodel; int autocvar_r_fakelight; int autocvar_r_fullbright; float autocvar_r_letterbox; -bool autocvar_scoreboard_accuracy; -bool autocvar_scoreboard_accuracy_doublerows; -bool autocvar_scoreboard_accuracy_nocolors; -float autocvar_scoreboard_alpha_bg; -float autocvar_scoreboard_alpha_fg = 1.0; -float autocvar_scoreboard_alpha_name = 0.9; -float autocvar_scoreboard_alpha_name_self = 1; -float autocvar_scoreboard_bg_scale; -float autocvar_scoreboard_border_thickness; -float autocvar_scoreboard_color_bg_b; -float autocvar_scoreboard_color_bg_g; -float autocvar_scoreboard_color_bg_r; -float autocvar_scoreboard_color_bg_team; string autocvar_scoreboard_columns; -float autocvar_scoreboard_fadeinspeed = 10; -float autocvar_scoreboard_fadeoutspeed = 5; -bool autocvar_scoreboard_highlight; -float autocvar_scoreboard_highlight_alpha = 0.10; -float autocvar_scoreboard_highlight_alpha_self = 0.25; -float autocvar_scoreboard_offset_left; -float autocvar_scoreboard_offset_right; -float autocvar_scoreboard_offset_vertical; -float autocvar_scoreboard_respawntime_decimals; -bool autocvar_scoreboard_dynamichud = false; bool autocvar_v_flipped; float autocvar_vid_conheight; float autocvar_vid_conwidth; @@ -433,8 +412,6 @@ 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_death = 1; -vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80'; -float autocvar_cl_eventchase_generator_distance = 400; float autocvar_cl_eventchase_distance = 140; float autocvar_cl_eventchase_speed = 1.3; vector autocvar_cl_eventchase_maxs = '12 12 8'; @@ -464,14 +441,6 @@ string autocvar__cl_playermodel; float autocvar_cl_deathglow; bool autocvar_developer_csqcentities; float autocvar_g_jetpack_attenuation; -string autocvar_crosshair_hmg = ""; -vector autocvar_crosshair_hmg_color = '0.2 1.0 0.2'; -float autocvar_crosshair_hmg_alpha = 1; -float autocvar_crosshair_hmg_size = 1; -string autocvar_crosshair_rpc = ""; -vector autocvar_crosshair_rpc_color = '0.2 1.0 0.2'; -float autocvar_crosshair_rpc_alpha = 1; -float autocvar_crosshair_rpc_size = 1; +bool autocvar_cl_showspectators; int autocvar_cl_nade_timer; -bool autocvar_cl_items_nofade; -float autocvar_slowmo; +bool autocvar_r_drawviewmodel; diff --git a/qcsrc/client/commands/_mod.inc b/qcsrc/client/commands/_mod.inc index 235f1297fd..dcfc6ecea5 100644 --- a/qcsrc/client/commands/_mod.inc +++ b/qcsrc/client/commands/_mod.inc @@ -1,3 +1,4 @@ // generated file; do not modify -#include -#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/client/commands/_mod.qh b/qcsrc/client/commands/_mod.qh index 03df56398c..7829965a23 100644 --- a/qcsrc/client/commands/_mod.qh +++ b/qcsrc/client/commands/_mod.qh @@ -1,3 +1,4 @@ // generated file; do not modify -#include -#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/client/commands/all.qc b/qcsrc/client/commands/all.qc deleted file mode 100644 index bc15eeb689..0000000000 --- a/qcsrc/client/commands/all.qc +++ /dev/null @@ -1,2 +0,0 @@ -#include "all.qh" -#include diff --git a/qcsrc/client/commands/all.qh b/qcsrc/client/commands/all.qh deleted file mode 100644 index 2df61f004b..0000000000 --- a/qcsrc/client/commands/all.qh +++ /dev/null @@ -1,5 +0,0 @@ -#pragma once - -#include - -#include "cl_cmd.qh" diff --git a/qcsrc/client/commands/cl_cmd.qc b/qcsrc/client/commands/cl_cmd.qc index dce058c816..631090cc1f 100644 --- a/qcsrc/client/commands/cl_cmd.qc +++ b/qcsrc/client/commands/cl_cmd.qc @@ -4,12 +4,12 @@ // Last updated: December 28th, 2011 // ============================================== -#include +#include #include "cl_cmd.qh" #include "../autocvars.qh" #include "../defs.qh" -#include +#include #include "../main.qh" #include "../mapvoting.qh" #include "../miscfunctions.qh" @@ -18,8 +18,6 @@ #include -#include - void DrawDebugModel(entity this) { if (time - floor(time) > 0.5) @@ -183,6 +181,7 @@ void LocalCommand_debugmodel(int request, int argc) setorigin(debugmodel_entity, view_origin); debugmodel_entity.angles = view_angles; debugmodel_entity.draw = DrawDebugModel; + IL_PUSH(g_drawables, debugmodel_entity); return; } @@ -306,13 +305,13 @@ void LocalCommand_hud(int request, int argc) case "scoreboard_columns_set": { - Cmd_HUD_SetFields(argc); + Cmd_Scoreboard_SetFields(argc); return; } case "scoreboard_columns_help": { - Cmd_HUD_Help(); + Cmd_Scoreboard_Help(); return; } diff --git a/qcsrc/client/commands/cl_cmd.qh b/qcsrc/client/commands/cl_cmd.qh index 65a723389e..f1be4315fe 100644 --- a/qcsrc/client/commands/cl_cmd.qh +++ b/qcsrc/client/commands/cl_cmd.qh @@ -1,7 +1,7 @@ #pragma once -void Cmd_HUD_SetFields(int); -void Cmd_HUD_Help(); +void Cmd_Scoreboard_SetFields(int); +void Cmd_Scoreboard_Help(); // 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/csqc_constants.qh b/qcsrc/client/csqc_constants.qh index 2ea1e746f7..d8906b12ba 100644 --- a/qcsrc/client/csqc_constants.qh +++ b/qcsrc/client/csqc_constants.qh @@ -132,7 +132,7 @@ const int SOLID_CORPSE = 5; // same as SOLID_BBOX, except it behaves as SOLID_N const int MOVE_NORMAL = 0; // same as false const int MOVE_NOMONSTERS = 1; // same as true -const int MOVE_MISSILE = 2; // save as movement with .movetype == MOVETYPE_FLYMISSILE +const int MOVE_MISSILE = 2; // save as movement with .move_movetype == MOVETYPE_FLYMISSILE const int MOVE_HITMODEL = 4; const int MOVE_WORLDONLY = 3; diff --git a/qcsrc/client/csqcmodel_hooks.qc b/qcsrc/client/csqcmodel_hooks.qc index 30148bdd44..1044653607 100644 --- a/qcsrc/client/csqcmodel_hooks.qc +++ b/qcsrc/client/csqcmodel_hooks.qc @@ -3,6 +3,7 @@ #include "player_skeleton.qh" #include "weapons/projectile.qh" #include +#include #include #include #include @@ -142,7 +143,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) _setmodel(e, cvar_defstring("_cl_playermodel")); forceplayermodels_goodmodel = e.model; forceplayermodels_goodmodelindex = e.modelindex; - remove(e); + delete(e); } // first, try finding it from the server @@ -175,7 +176,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) forceplayermodels_model = e.model; forceplayermodels_modelindex = e.modelindex; forceplayermodels_skin = autocvar__cl_playerskin; - remove(e); + delete(e); } if(autocvar_cl_forcemyplayermodel != "" && autocvar_cl_forcemyplayermodel != forceplayermodels_mymodel) @@ -185,14 +186,14 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) forceplayermodels_myisgoodmodel = fexists(e.model); forceplayermodels_mymodel = e.model; forceplayermodels_mymodelindex = e.modelindex; - remove(e); + delete(e); } // apply it bool isfriend; int cm; cm = this.forceplayermodels_savecolormap; - cm = (cm >= 1024) ? cm : (stof(getplayerkeyvalue(cm - 1, "colors")) + 1024); + cm = (cm >= 1024) ? cm : (entcs_GetClientColors(cm - 1) + 1024); if(teamplay) isfriend = (cm == 1024 + 17 * myteam); @@ -279,7 +280,7 @@ void CSQCPlayer_ModelAppearance_Apply(entity this, bool islocalplayer) // GLOWMOD AND DEATH FADING if(this.colormap > 0) - this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : stof(getplayerkeyvalue(this.colormap - 1, "colors"))) & 0x0F, true) * 2; + this.glowmod = colormapPaletteColor(((this.colormap >= 1024) ? this.colormap : entcs_GetClientColors(this.colormap - 1)) & 0x0F, true) * 2; else this.glowmod = '1 1 1'; @@ -369,7 +370,7 @@ int CSQCPlayer_FallbackFrame(entity this, int f) case 29: return 4; // anim_duckwalkbackright -> anim_duckwalk case 30: return 4; // anim_duckwalkbackleft -> anim_duckwalk } - LOG_DEBUGF("Frame %d missing in model %s, and we have no fallback - FAIL!\n", f, this.model); + LOG_DEBUGF("Frame %d missing in model %s, and we have no fallback - FAIL!", f, this.model); return f; } void CSQCPlayer_FallbackFrame_Apply(entity this) @@ -391,8 +392,6 @@ void CSQCModel_AutoTagIndex_Apply(entity this) if(this.tag_entity && wasfreed(this.tag_entity)) this.tag_entity = NULL; - viewloc_SetTags(this); - MUTATOR_CALLHOOK(TagIndex_Update, this); if(this.tag_networkentity) @@ -433,7 +432,7 @@ void CSQCModel_AutoTagIndex_Apply(entity this) // we need to prevent this from 'appening this.tag_entity = NULL; this.drawmask = 0; - LOG_TRACE("h_ model lacks weapon attachment, but v_ model is attached to it\n"); + LOG_TRACE("h_ model lacks weapon attachment, but v_ model is attached to it"); } } else if(this.tag_entity.isplayermodel) diff --git a/qcsrc/client/defs.qh b/qcsrc/client/defs.qh index 84bcb2b3d1..309ed1d815 100644 --- a/qcsrc/client/defs.qh +++ b/qcsrc/client/defs.qh @@ -27,7 +27,7 @@ float dmg_take; .int team; .int team_size; -float vid_conwidth, vid_conheight; +float vid_conheight; int binddb; // QUALIFYING @@ -47,8 +47,10 @@ string race_penaltyreason; // reason for penalty float race_server_record; // server record float race_speedaward; string race_speedaward_holder; +string race_speedaward_unit; float race_speedaward_alltimebest; string race_speedaward_alltimebest_holder; +string race_speedaward_alltimebest_unit; // RACE float race_mycheckpoint; @@ -86,7 +88,7 @@ vector lightning_shotorigin[4]; float blurtest_time0, blurtest_time1, blurtest_radius, blurtest_power; #endif -float servertime, serverprevtime, serverdeltatime; +float serverprevtime, serverdeltatime; float ticrate; @@ -118,6 +120,8 @@ int serverflags; float uid2name_dialog; +float intermission_time; + .bool csqcmodel_isdead; // used by shownames and miscfunctions (entcs_IsDead) to know when a player is dead #define player_currententnum (spectatee_status > 0 ? spectatee_status : player_localnum + 1) diff --git a/qcsrc/client/hud/_mod.inc b/qcsrc/client/hud/_mod.inc index b90f61b8cc..aa785a9e94 100644 --- a/qcsrc/client/hud/_mod.inc +++ b/qcsrc/client/hud/_mod.inc @@ -1,3 +1,6 @@ // generated file; do not modify #include #include +#include + +#include diff --git a/qcsrc/client/hud/_mod.qh b/qcsrc/client/hud/_mod.qh index ee9ac8f76f..2d4850d521 100644 --- a/qcsrc/client/hud/_mod.qh +++ b/qcsrc/client/hud/_mod.qh @@ -1,3 +1,6 @@ // generated file; do not modify #include #include +#include + +#include diff --git a/qcsrc/client/hud/all.inc b/qcsrc/client/hud/all.inc deleted file mode 100644 index aa36eec409..0000000000 --- a/qcsrc/client/hud/all.inc +++ /dev/null @@ -1,21 +0,0 @@ -#include "panel/weapons.qc" -#include "panel/ammo.qc" -#include "panel/powerups.qc" -#include "panel/healtharmor.qc" -#include "panel/notify.qc" -#include "panel/timer.qc" -#include "panel/radar.qc" -#include "panel/score.qc" -#include "panel/racetimer.qc" -#include "panel/vote.qc" -#include "panel/modicons.qc" -#include "panel/pressedkeys.qc" -#include "panel/chat.qc" -#include "panel/engineinfo.qc" -#include "panel/infomessages.qc" -#include "panel/physics.qc" -#include "panel/centerprint.qc" -#include "panel/minigame.qc" -// #include "panel/mapvote.qc" -// #include "panel/itemstime.qc" -#include "panel/quickmenu.qc" diff --git a/qcsrc/client/hud/all.qh b/qcsrc/client/hud/all.qh deleted file mode 100644 index 2e458e6dd5..0000000000 --- a/qcsrc/client/hud/all.qh +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -#include "hud.qh" -#include "hud_config.qh" diff --git a/qcsrc/client/hud/hud.qc b/qcsrc/client/hud/hud.qc index c6cb5257b1..ee94af7270 100644 --- a/qcsrc/client/hud/hud.qc +++ b/qcsrc/client/hud/hud.qc @@ -1,12 +1,13 @@ #include "hud.qh" +#include "panel/scoreboard.qh" #include "hud_config.qh" #include "../mapvoting.qh" -#include "../scoreboard.qh" #include "../teamradar.qh" +#include #include #include -#include +#include #include #include #include @@ -21,34 +22,34 @@ Misc HUD functions ================== */ -vector HUD_Get_Num_Color (float x, float maxvalue) +vector HUD_Get_Num_Color (float hp, float maxvalue) { float blinkingamt; vector color; - if(x >= maxvalue) { + if(hp >= maxvalue) { color.x = sin(2*M_PI*time); color.y = 1; color.z = sin(2*M_PI*time); } - else if(x > maxvalue * 0.75) { - color.x = 0.4 - (x-150)*0.02 * 0.4; //red value between 0.4 -> 0 - color.y = 0.9 + (x-150)*0.02 * 0.1; // green value between 0.9 -> 1 + else if(hp > maxvalue * 0.75) { + color.x = 0.4 - (hp-150)*0.02 * 0.4; //red value between 0.4 -> 0 + color.y = 0.9 + (hp-150)*0.02 * 0.1; // green value between 0.9 -> 1 color.z = 0; } - else if(x > maxvalue * 0.5) { - color.x = 1 - (x-100)*0.02 * 0.6; //red value between 1 -> 0.4 - color.y = 1 - (x-100)*0.02 * 0.1; // green value between 1 -> 0.9 - color.z = 1 - (x-100)*0.02; // blue value between 1 -> 0 + else if(hp > maxvalue * 0.5) { + color.x = 1 - (hp-100)*0.02 * 0.6; //red value between 1 -> 0.4 + color.y = 1 - (hp-100)*0.02 * 0.1; // green value between 1 -> 0.9 + color.z = 1 - (hp-100)*0.02; // blue value between 1 -> 0 } - else if(x > maxvalue * 0.25) { + else if(hp > maxvalue * 0.25) { color.x = 1; color.y = 1; - color.z = 0.2 + (x-50)*0.02 * 0.8; // blue value between 0.2 -> 1 + color.z = 0.2 + (hp-50)*0.02 * 0.8; // blue value between 0.2 -> 1 } - else if(x > maxvalue * 0.1) { + else if(hp > maxvalue * 0.1) { color.x = 1; - color.y = (x-20)*90/27/100; // green value between 0 -> 1 - color.z = (x-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2 + color.y = (hp-20)*90/27/100; // green value between 0 -> 1 + color.z = (hp-20)*90/27/100 * 0.2; // blue value between 0 -> 0.2 } else { color.x = 1; @@ -56,7 +57,7 @@ vector HUD_Get_Num_Color (float x, float maxvalue) color.z = 0; } - blinkingamt = (1 - x/maxvalue/0.25); + blinkingamt = (1 - hp/maxvalue/0.25); if(blinkingamt > 0) { color.x = color.x - color.x * blinkingamt * sin(2*M_PI*time); @@ -120,6 +121,59 @@ HUD panels ================== */ +void HUD_Panel_LoadCvars() +{ + // NOTE: in hud_configure mode cvars must be reloaded every frame + if (panel.update_time <= time) + { + panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); + panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); + HUD_Panel_ScalePosSize(); + panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); + panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); + panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); + panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); + panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); + panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); + HUD_Panel_GetBg(); + if (panel.current_panel_bg != "0") + { + HUD_Panel_GetBgAlpha(); + HUD_Panel_GetBorder(); + } + HUD_Panel_GetColorTeam(); + HUD_Panel_GetColor(); + HUD_Panel_GetFgAlpha(); + HUD_Panel_GetPadding(); + panel.current_panel_bg_alpha = panel_bg_alpha; + panel.current_panel_fg_alpha = panel_fg_alpha; + if (hud_configure_menu_open == 2 && panel == highlightedPanel) + HUD_Panel_UpdatePosSize_ForMenu(); + else + { + panel_bg_alpha *= hud_fade_alpha * panel_fade_alpha; + panel_fg_alpha *= hud_fade_alpha * panel_fade_alpha; + } + panel.current_panel_pos = panel_pos; + panel.current_panel_size = panel_size; + panel.current_panel_bg_border = panel_bg_border; + panel.current_panel_bg_color = panel_bg_color; + panel.current_panel_bg_color_team = panel_bg_color_team; + panel.current_panel_bg_padding = panel_bg_padding; + panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; + return; + } + + panel_pos = panel.current_panel_pos; + panel_size = panel.current_panel_size; + panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha * panel_fade_alpha; + panel_bg_border = panel.current_panel_bg_border; + panel_bg_color = panel.current_panel_bg_color; + panel_bg_color_team = panel.current_panel_bg_color_team; + panel_bg_padding = panel.current_panel_bg_padding; + panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha * panel_fade_alpha; +} + //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) { @@ -249,7 +303,7 @@ void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theA drawsubpic(pos + eX * mySize.x - eX * min(mySize.x * 0.5, mySize.y), eX * min(mySize.x * 0.5, mySize.y) + eY * mySize.y, pic, '0.75 0 0', '0.25 1 0', color, theAlpha, drawflag); } -void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp) +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); vector newPos = '0 0 0', newSize = '0 0 0'; @@ -291,7 +345,7 @@ void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bo // reduce only y to draw numbers with different number of digits with the same y size numpos.y += newSize.y * ((1 - 0.7) / 2); newSize.y *= 0.7; - drawstring_aspect(numpos, ftos(x), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); + drawstring_aspect(numpos, ftos(theTime), newSize, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL); return; } @@ -325,18 +379,16 @@ void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bo // NOTE: newSize_x is always equal to 3 * mySize_y so we can use // '2 1 0' * newSize_y instead of eX * (2/3) * newSize_x + eY * newSize_y - drawstring_aspect_expanding(numpos, ftos(x), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); + drawstring_aspect_expanding(numpos, ftos(theTime), '2 1 0' * newSize.y, color, panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); drawpic_aspect_skin_expanding(picpos, icon, '1 1 0' * newSize.y, '1 1 1', panel_fg_alpha * theAlpha, DRAWFLAG_NORMAL, fadelerp); } -void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha) +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); - DrawNumIcon_expanding(myPos, mySize, x, icon, vertical, icon_right_align, color, theAlpha, 0); + DrawNumIcon_expanding(myPos, mySize, theTime, icon, vertical, icon_right_align, color, theAlpha, 0); } -#include "all.inc" - /* ================== Main HUD system @@ -358,33 +410,52 @@ void HUD_Vehicle() } } -bool HUD_Panel_CheckFlags(int showflags) -{ - TC(int, showflags); - if ( HUD_Minigame_Showpanels() ) - return showflags & PANEL_SHOW_MINIGAME; - if(intermission == 2) - return showflags & PANEL_SHOW_MAPVOTE; - return showflags & PANEL_SHOW_MAINGAME; -} - void HUD_Panel_Draw(entity panent) { panel = panent; - if(autocvar__hud_configure) + if (autocvar__hud_configure) + { + if (!(panel.panel_configflags & PANEL_CONFIG_MAIN)) + return; + panel_fade_alpha = 1; + Hud_Panel_GetPanelEnabled(); + panel.panel_draw(); + return; + } + + bool draw_allowed = false; + if (active_minigame && HUD_MinigameMenu_IsOpened()) { - if(panel.panel_configflags & PANEL_CONFIG_MAIN) - panel.panel_draw(); + if (panel.panel_showflags & PANEL_SHOW_MINIGAME) + draw_allowed = true; } - else if(HUD_Panel_CheckFlags(panel.panel_showflags)) + else if(intermission == 2) + { + if(panel.panel_showflags & PANEL_SHOW_MAPVOTE) + draw_allowed = true; + } + else if (panel.panel_showflags & PANEL_SHOW_MAINGAME) + draw_allowed = true; + + if (draw_allowed) + { + if (panel.panel_showflags & PANEL_SHOW_WITH_SB) + panel_fade_alpha = 1; + else + { + panel_fade_alpha = 1 - scoreboard_fade_alpha; + if(!panel_fade_alpha) + return; + } panel.panel_draw(); + } } void HUD_Reset() { // reset gametype specific icons - if(gametype == MAPINFO_TYPE_CTF) - HUD_Mod_CTF_Reset(); + if(gametype.m_modicons_reset) + gametype.m_modicons_reset(); } float autocvar_hud_dynamic_shake = 1; @@ -414,6 +485,7 @@ bool Hud_Shake_Update() return true; } +entity CSQCModel_server2csqc(int i); void calc_followmodel_ofs(entity view); void Hud_Dynamic_Frame() { @@ -493,34 +565,18 @@ void Hud_Dynamic_Frame() void HUD_Main() { int i; - // global hud theAlpha fade - if(menu_enabled == 1) + if(hud_configure_menu_open == 1) hud_fade_alpha = 1; else - hud_fade_alpha = (1 - autocvar__menu_alpha); - - if(scoreboard_fade_alpha) - hud_fade_alpha = (1 - scoreboard_fade_alpha); + hud_fade_alpha = 1 - autocvar__menu_alpha; HUD_Configure_Frame(); Hud_Dynamic_Frame(); - // panels that we want to be active together with the scoreboard - // they must fade only when the menu does if(scoreboard_fade_alpha == 1) - { - HUD_Panel_Draw(HUD_PANEL(CENTERPRINT)); - return; - } - - if(!autocvar__hud_configure && !hud_fade_alpha) - { - hud_fade_alpha = 1; - HUD_Panel_Draw(HUD_PANEL(VOTE)); - hud_fade_alpha = 0; - return; - } + if(autocvar__menu_alpha == 1) + return; // Drawing stuff if (hud_skin_prev != autocvar_hud_skin) @@ -552,11 +608,11 @@ void HUD_Main() { string hud_dock_color = autocvar_hud_dock_color; if(hud_dock_color == "shirt") { - f = stof(getplayerkeyvalue(current_player, "colors")); + f = entcs_GetClientColors(current_player); color = colormapPaletteColor(floor(f / 16), 0); } else if(hud_dock_color == "pants") { - f = stof(getplayerkeyvalue(current_player, "colors")); + f = entcs_GetClientColors(current_player); color = colormapPaletteColor(f % 16, 1); } else @@ -602,7 +658,7 @@ void HUD_Main() } } if (warning) - LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder\n"); + LOG_TRACE("Automatically fixed wrong/missing panel numbers in _hud_panelorder"); cvar_set("_hud_panelorder", s); if(hud_panelorder_prev) @@ -631,8 +687,9 @@ void HUD_Main() HUD_Panel_Draw(HUD_PANEL(CHAT)); if(hud_panel_quickmenu) HUD_Panel_Draw(HUD_PANEL(QUICKMENU)); + HUD_Panel_Draw(HUD_PANEL(SCOREBOARD)); - if (scoreboard_active || intermission == 2) + if (intermission == 2) HUD_Reset(); HUD_Configure_PostDraw(); diff --git a/qcsrc/client/hud/hud.qh b/qcsrc/client/hud/hud.qh index 2a53444669..06a3491145 100644 --- a/qcsrc/client/hud/hud.qh +++ b/qcsrc/client/hud/hud.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include bool HUD_Radar_Clickable(); void HUD_Radar_Mouse(); @@ -22,14 +22,13 @@ REGISTER_REGISTRY(hud_panels) #define HUD_PANEL(NAME) HUD_PANEL_##NAME // draw the background/borders -#define HUD_Panel_DrawBg(theAlpha) MACRO_BEGIN { \ +#define HUD_Panel_DrawBg() MACRO_BEGIN { \ if(panel.current_panel_bg != "0" && panel.current_panel_bg != "") \ draw_BorderPicture( \ HUD_Shift(panel_pos - '1 1 0' * panel_bg_border), \ panel.current_panel_bg, \ HUD_Scale(panel_size + '1 1 0' * 2 * panel_bg_border), \ - panel_bg_color, \ - panel_bg_alpha * theAlpha, \ + panel_bg_color, panel_bg_alpha, \ HUD_Scale('1 1 0' * (panel_bg_border/BORDER_MULTIPLIER)) \ ); \ } MACRO_END @@ -49,9 +48,9 @@ float HUD_Radar_InputEvent(float bInputType, float nPrimary, float nSecondary); void HUD_Radar_Hide_Maximized(); float HUD_GetRowCount(int item_count, vector size, float item_aspect); -vector HUD_Get_Num_Color (float x, float maxvalue); -void DrawNumIcon(vector myPos, vector mySize, float x, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha); -void DrawNumIcon_expanding(vector myPos, vector mySize, float x, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp); +vector HUD_Get_Num_Color (float hp, float maxvalue); +void DrawNumIcon(vector myPos, vector mySize, float theTime, string icon, bool vertical, bool icon_right_align, vector color, float theAlpha); +void DrawNumIcon_expanding(vector myPos, vector mySize, float theTime, string icon, bool vertical, int icon_right_align, vector color, float theAlpha, float fadelerp); void HUD_Panel_DrawHighlight(vector pos, vector mySize, vector color, float theAlpha, int drawflag); vector HUD_GetTableSize_BestItemAR(int item_count, vector psize, float item_aspect); @@ -88,7 +87,7 @@ int complain_weapon; float complain_weapon_type; float complain_weapon_time; -int ps_primary, ps_secondary; +PlayerScoreField ps_primary, ps_secondary; int ts_primary, ts_secondary; Weapon last_switchweapon; @@ -98,18 +97,11 @@ float weaponprevtime; float teamnagger; -float hud_configure_checkcollisions; -float hud_configure_prev; -vector hud_configure_gridSize; -vector hud_configure_realGridSize; - int hudShiftState; const int S_SHIFT = 1; const int S_CTRL = 2; const int S_ALT = 4; -float menu_enabled; // 1 showing the entire HUD, 2 showing only the clicked panel - float hud_fade_alpha; string hud_skin_path; @@ -189,12 +181,13 @@ const int PANEL_SHOW_NEVER = 0x00; const int PANEL_SHOW_MAINGAME = 0x01; const int PANEL_SHOW_MINIGAME = 0x02; const int PANEL_SHOW_MAPVOTE = 0x04; +const int PANEL_SHOW_WITH_SB = 0x08; const int PANEL_SHOW_ALWAYS = 0xff; -bool HUD_Panel_CheckFlags(int showflags); .int panel_configflags; -const int PANEL_CONFIG_NO = 0x00; -const int PANEL_CONFIG_MAIN = 0x01; +const int PANEL_CONFIG_NO = 0x00; +const int PANEL_CONFIG_MAIN = 0x01; +const int PANEL_CONFIG_CANBEOFF = 0x02; // panel can be disabled (if disabled it's displayed with a low alpha) // prev_* vars contain the health/armor at the previous FRAME @@ -211,30 +204,31 @@ int prev_p_health, prev_p_armor; void HUD_ItemsTime(); -REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS & ~PANEL_SHOW_MAPVOTE) -REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN, PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME) -REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_ALWAYS ) -REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE ) -REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) -REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN, PANEL_SHOW_MAINGAME) +REGISTER_HUD_PANEL(WEAPONS, HUD_Weapons, weapons, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // WEAPONS +REGISTER_HUD_PANEL(AMMO, HUD_Ammo, ammo, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // AMMO +REGISTER_HUD_PANEL(POWERUPS, HUD_Powerups, powerups, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // POWERUPS +REGISTER_HUD_PANEL(HEALTHARMOR, HUD_HealthArmor, healtharmor, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // HEALTHARMOR +REGISTER_HUD_PANEL(NOTIFY, HUD_Notify, notify, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME ) // NOTIFY +REGISTER_HUD_PANEL(TIMER, HUD_Timer, timer, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_WITH_SB) // TIMER +REGISTER_HUD_PANEL(RADAR, HUD_Radar, radar, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // RADAR +REGISTER_HUD_PANEL(SCORE, HUD_Score, score, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME ) // SCORE +REGISTER_HUD_PANEL(RACETIMER, HUD_RaceTimer, racetimer, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // RACETIMER +REGISTER_HUD_PANEL(VOTE, HUD_Vote, vote, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // VOTE +REGISTER_HUD_PANEL(MODICONS, HUD_ModIcons, modicons, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_WITH_SB) // MODICONS +REGISTER_HUD_PANEL(PRESSEDKEYS, HUD_PressedKeys, pressedkeys, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // PRESSEDKEYS +REGISTER_HUD_PANEL(CHAT, HUD_Chat, chat, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // CHAT +REGISTER_HUD_PANEL(ENGINEINFO, HUD_EngineInfo, engineinfo, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // ENGINEINFO +REGISTER_HUD_PANEL(INFOMESSAGES, HUD_InfoMessages, infomessages, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // INFOMESSAGES +REGISTER_HUD_PANEL(PHYSICS, HUD_Physics, physics, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // PHYSICS +REGISTER_HUD_PANEL(CENTERPRINT, HUD_CenterPrint, centerprint, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME | PANEL_SHOW_WITH_SB) // CENTERPRINT +REGISTER_HUD_PANEL(MINIGAME_BOARD, HUD_MinigameBoard, minigameboard, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME | PANEL_SHOW_WITH_SB) // MINIGAME_BOARD +REGISTER_HUD_PANEL(MINIGAME_STATUS, HUD_MinigameStatus, minigamestatus, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME | PANEL_SHOW_WITH_SB) // MINIGAME_STATUS +REGISTER_HUD_PANEL(MINIGAME_HELP, HUD_MinigameHelp, minigamehelp, PANEL_CONFIG_NO , PANEL_SHOW_MINIGAME | PANEL_SHOW_WITH_SB) // MINIGAME_HELP +REGISTER_HUD_PANEL(MINIGAME_MENU, HUD_MinigameMenu, minigamemenu, PANEL_CONFIG_NO , PANEL_SHOW_MAINGAME | PANEL_SHOW_MINIGAME | PANEL_SHOW_MAPVOTE | PANEL_SHOW_WITH_SB) // MINIGAME_MENU +REGISTER_HUD_PANEL(MAPVOTE, MapVote_Draw, mapvote, PANEL_CONFIG_NO , PANEL_SHOW_MAPVOTE ) // MAPVOTE +REGISTER_HUD_PANEL(ITEMSTIME, HUD_ItemsTime, itemstime, PANEL_CONFIG_MAIN | PANEL_CONFIG_CANBEOFF, PANEL_SHOW_MAINGAME ) // ITEMSTIME +REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CONFIG_MAIN , PANEL_SHOW_MAINGAME ) // QUICKMENU +REGISTER_HUD_PANEL(SCOREBOARD, Scoreboard_Draw, scoreboard, 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 // Because calling lots of functions in QC apparently cuts fps in half on many machines: @@ -274,21 +268,21 @@ REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CO // Get value for panel_bg_color: if "" fetch default, else use panel_bg_color. Convert pants, shirt or teamcolor into a vector. #define HUD_Panel_GetColor() MACRO_BEGIN { \ - if ((teamplay) && panel_bg_color_team) { \ + if ((teamplay) && panel_bg_color_team > 0) { \ if (autocvar__hud_configure && myteam == NUM_SPECTATOR) \ panel_bg_color = '1 0 0' * panel_bg_color_team; \ else \ panel_bg_color = myteamcolors * panel_bg_color_team; \ - } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team) { \ + } else if (autocvar_hud_configure_teamcolorforced && autocvar__hud_configure && panel_bg_color_team > 0) { \ panel_bg_color = '1 0 0' * panel_bg_color_team; \ } else { \ if (panel_bg_color_str == "") { \ panel_bg_color = autocvar_hud_panel_bg_color; \ } else { \ if (panel_bg_color_str == "shirt") { \ - panel_bg_color = colormapPaletteColor(floor(stof(getplayerkeyvalue(current_player, "colors")) / 16), 0); \ + panel_bg_color = colormapPaletteColor(floor(entcs_GetClientColors(current_player) / 16), 0); \ } else if (panel_bg_color_str == "pants") { \ - panel_bg_color = colormapPaletteColor(stof(getplayerkeyvalue(current_player, "colors")) % 16, 1); \ + panel_bg_color = colormapPaletteColor(entcs_GetClientColors(current_player) % 16, 1); \ } else { \ panel_bg_color = stov(panel_bg_color_str); \ } \ @@ -315,7 +309,7 @@ REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CO if (autocvar__hud_configure) { \ if (!panel_enabled) \ panel_bg_alpha = 0.25; \ - else if (menu_enabled == 2 && panel == highlightedPanel) \ + else if (hud_configure_menu_open == 2 && panel == highlightedPanel) \ panel_bg_alpha = (1 - autocvar__menu_alpha) * max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha) + autocvar__menu_alpha * panel_bg_alpha;\ else \ panel_bg_alpha = max(cvar("hud_configure_bg_minalpha"), panel_bg_alpha); \ @@ -353,16 +347,16 @@ REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CO // return smoothly faded pos and size of given panel when a dialog is active // don't center too wide panels, it doesn't work with different resolutions #define HUD_Panel_UpdatePosSize_ForMenu() MACRO_BEGIN { \ - vector menu_enable_size = panel_size; \ + vector new_size = panel_size; \ float max_panel_width = 0.52 * vid_conwidth; \ if(panel_size.x > max_panel_width) \ { \ - menu_enable_size.x = max_panel_width; \ - menu_enable_size.y = panel_size.y * (menu_enable_size.x / panel_size.x); \ + new_size.x = max_panel_width; \ + new_size.y = panel_size.y * (new_size.x / panel_size.x); \ } \ - vector menu_enable_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * menu_enable_size; \ - panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * menu_enable_pos; \ - panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * menu_enable_size; \ + vector new_pos = eX * (panel_bg_border + 0.5 * max_panel_width) + eY * 0.5 * vid_conheight - 0.5 * new_size; \ + panel_pos = (1 - autocvar__menu_alpha) * panel_pos + (autocvar__menu_alpha) * new_pos; \ + panel_size = (1 - autocvar__menu_alpha) * panel_size + (autocvar__menu_alpha) * new_size; \ } MACRO_END // Scale the pos and size vectors to absolute coordinates @@ -371,61 +365,19 @@ REGISTER_HUD_PANEL(QUICKMENU, HUD_QuickMenu, quickmenu, PANEL_CO panel_size.x *= vid_conwidth; panel_size.y *= vid_conheight; \ } MACRO_END -// NOTE: in hud_configure mode cvars must be reloaded every frame -#define HUD_Panel_UpdateCvars() MACRO_BEGIN { \ - if (panel.update_time <= time) { \ - if (autocvar__hud_configure) panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ - panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ - panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ - HUD_Panel_ScalePosSize(); \ - panel_bg_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg")); \ - panel_bg_color_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color")); \ - panel_bg_color_team_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_color_team")); \ - panel_bg_alpha_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_alpha")); \ - panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ - panel_bg_padding_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_padding")); \ - HUD_Panel_GetBg(); \ - if (panel.current_panel_bg != "0") { \ - HUD_Panel_GetColorTeam(); \ - HUD_Panel_GetColor(); \ - HUD_Panel_GetBgAlpha(); \ - HUD_Panel_GetBorder(); \ - } \ - HUD_Panel_GetFgAlpha(); \ - HUD_Panel_GetPadding(); \ - panel.current_panel_bg_alpha = panel_bg_alpha; \ - panel.current_panel_fg_alpha = panel_fg_alpha; \ - if (menu_enabled == 2 && panel == highlightedPanel) { \ - HUD_Panel_UpdatePosSize_ForMenu(); \ - } else { \ - panel_bg_alpha *= hud_fade_alpha; \ - panel_fg_alpha *= hud_fade_alpha; \ - } \ - panel.current_panel_pos = panel_pos; \ - panel.current_panel_size = panel_size; \ - panel.current_panel_bg_border = panel_bg_border; \ - panel.current_panel_bg_color = panel_bg_color; \ - panel.current_panel_bg_color_team = panel_bg_color_team; \ - panel.current_panel_bg_padding = panel_bg_padding; \ - panel.update_time = (autocvar__hud_configure) ? time : time + autocvar_hud_panel_update_interval; \ - } else { \ - panel_pos = panel.current_panel_pos; \ - panel_size = panel.current_panel_size; \ - panel_bg_alpha = panel.current_panel_bg_alpha * hud_fade_alpha; \ - panel_bg_border = panel.current_panel_bg_border; \ - panel_bg_color = panel.current_panel_bg_color; \ - panel_bg_color_team = panel.current_panel_bg_color_team; \ - panel_bg_padding = panel.current_panel_bg_padding; \ - panel_fg_alpha = panel.current_panel_fg_alpha * hud_fade_alpha; \ - } \ -} MACRO_END +float panel_fade_alpha; +void HUD_Panel_LoadCvars(); + +#define Hud_Panel_GetPanelEnabled() \ + panel_enabled = ((panel.panel_configflags & PANEL_CONFIG_CANBEOFF) \ + ? cvar(strcat("hud_panel_", panel.panel_name)) : true) #define HUD_Panel_UpdatePosSize() MACRO_BEGIN { \ - panel_enabled = cvar(strcat("hud_panel_", panel.panel_name)); \ + Hud_Panel_GetPanelEnabled(); \ panel_pos = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_pos"))); \ panel_size = stov(cvar_string(strcat("hud_panel_", panel.panel_name, "_size"))); \ HUD_Panel_ScalePosSize(); \ - if (menu_enabled == 2 && panel == highlightedPanel) { \ + if (hud_configure_menu_open == 2 && panel == highlightedPanel) { \ HUD_Panel_UpdatePosSize_ForMenu(); \ } \ panel_bg_border_str = cvar_string(strcat("hud_panel_", panel.panel_name, "_bg_border")); \ diff --git a/qcsrc/client/hud/hud_config.qc b/qcsrc/client/hud/hud_config.qc index f45a052aa1..f2b23e3925 100644 --- a/qcsrc/client/hud/hud_config.qc +++ b/qcsrc/client/hud/hud_config.qc @@ -1,6 +1,7 @@ #include "hud_config.qh" #include "hud.qh" +#include "panel/scoreboard.qh" #define HUD_Write(s) fputs(fh, s) #define HUD_Write_Cvar(cvar) HUD_Write(strcat("seta ", cvar, " \"", cvar_string(cvar), "\"\n")) @@ -207,6 +208,21 @@ void HUD_Panel_ExportCfg(string cfgname) case HUD_PANEL_QUICKMENU: HUD_Write_PanelCvar("_align"); break; + case HUD_PANEL_SCOREBOARD: + HUD_Write_PanelCvar("_fadeinspeed"); + HUD_Write_PanelCvar("_fadeoutspeed"); + HUD_Write_PanelCvar("_respawntime_decimals"); + HUD_Write_PanelCvar("_table_bg_alpha"); + HUD_Write_PanelCvar("_table_bg_scale"); + HUD_Write_PanelCvar("_table_fg_alpha"); + HUD_Write_PanelCvar("_table_fg_alpha_self"); + HUD_Write_PanelCvar("_table_highlight"); + HUD_Write_PanelCvar("_table_highlight_alpha"); + HUD_Write_PanelCvar("_table_highlight_alpha_self"); + HUD_Write_PanelCvar("_bg_teams_color_team"); + HUD_Write_PanelCvar("_accuracy_doublerows"); + HUD_Write_PanelCvar("_accuracy_nocolors"); + break; } HUD_Write("\n"); } @@ -221,9 +237,9 @@ void HUD_Panel_ExportCfg(string cfgname) void HUD_Configure_Exit_Force() { - if (menu_enabled) + if (hud_configure_menu_open) { - menu_enabled = 0; + hud_configure_menu_open = 0; localcmd("togglemenu\n"); } cvar_set("_hud_configure", "0"); @@ -671,10 +687,8 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary) } // allow console bind to work - string con_keys; - float keys; - con_keys = findkeysforcommand("toggleconsole", 0); - keys = tokenize(con_keys); // findkeysforcommand returns data for this + string con_keys = findkeysforcommand("toggleconsole", 0); + int keys = tokenize(con_keys); // findkeysforcommand returns data for this bool hit_con_bind = false; int i; @@ -729,14 +743,14 @@ float HUD_Panel_InputEvent(float bInputType, float nPrimary, float nSecondary) { if (bInputType == 1) return true; - menu_enabled = 1; + hud_configure_menu_open = 1; localcmd("menu_showhudexit\n"); } else if(nPrimary == K_BACKSPACE && hudShiftState & S_CTRL) { if (bInputType == 1) return true; - if (!menu_enabled) + if (!hud_configure_menu_open) cvar_set("_hud_configure", "0"); } else if(nPrimary == K_TAB && hudShiftState & S_CTRL) // switch panel @@ -834,7 +848,10 @@ LABEL(find_tab_panel) return true; if (highlightedPanel) - cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name)))); + { + if(panel.panel_configflags & PANEL_CONFIG_CANBEOFF) + cvar_set(strcat("hud_panel_", highlightedPanel.panel_name), ftos(!cvar(strcat("hud_panel_", highlightedPanel.panel_name)))); + } else cvar_set(strcat("hud_dock"), (autocvar_hud_dock == "") ? "dock" : ""); } @@ -1084,7 +1101,7 @@ void HUD_Panel_Highlight(float allow_move) void HUD_Panel_EnableMenu() { - menu_enabled = 2; + hud_configure_menu_open = 2; localcmd("menu_showhudoptions ", highlightedPanel.panel_name, "\n"); } float mouse_over_panel; @@ -1094,12 +1111,7 @@ void HUD_Panel_Mouse() return; if (!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos.x = bound(0, mousepos.x, vid_conwidth); - mousepos.y = bound(0, mousepos.y, vid_conheight); - } + update_mousepos(); if(mouseClicked) { @@ -1183,7 +1195,7 @@ void HUD_Panel_Mouse() { if(prevMouseClicked) highlightedAction = 0; - if(menu_enabled == 2) + if(hud_configure_menu_open == 2) mouse_over_panel = 0; else mouse_over_panel = HUD_Panel_Check_Mouse_Pos(true); @@ -1231,7 +1243,7 @@ void HUD_Configure_Frame() int i; if(autocvar__hud_configure) { - if(isdemo() || intermission == 2) + if(isdemo() || intermission == 2 || scoreboard_active) { HUD_Configure_Exit_Force(); return; @@ -1250,7 +1262,7 @@ void HUD_Configure_Frame() if(autocvar__menu_alpha != _menu_alpha_prev) { if(autocvar__menu_alpha == 0) - menu_enabled = 0; + hud_configure_menu_open = 0; _menu_alpha_prev = autocvar__menu_alpha; } @@ -1258,8 +1270,8 @@ void HUD_Configure_Frame() } else if(hud_configure_prev) { - if(menu_enabled) - menu_enabled = 0; + 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 32c8460761..6ab64f6aed 100644 --- a/qcsrc/client/hud/hud_config.qh +++ b/qcsrc/client/hud/hud_config.qh @@ -8,6 +8,12 @@ int prevMouseClicked; // previous state float prevMouseClickedTime; // time during previous left mouse click, to check for doubleclicks vector prevMouseClickedPos; // pos during previous left mouse click, to check for doubleclicks +float hud_configure_prev; +float hud_configure_checkcollisions; +vector hud_configure_gridSize; +vector hud_configure_realGridSize; +float hud_configure_menu_open; // 1 showing the entire HUD, 2 showing only the clicked panel + void HUD_Panel_ExportCfg(string cfgname); void HUD_Panel_Mouse(); diff --git a/qcsrc/client/hud/panel.qc b/qcsrc/client/hud/panel.qc new file mode 100644 index 0000000000..558f320338 --- /dev/null +++ b/qcsrc/client/hud/panel.qc @@ -0,0 +1 @@ +#include "panel.qh" diff --git a/qcsrc/client/hud/panel/_mod.inc b/qcsrc/client/hud/panel/_mod.inc index 5681474b84..7a95752132 100644 --- a/qcsrc/client/hud/panel/_mod.inc +++ b/qcsrc/client/hud/panel/_mod.inc @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/hud/panel/_mod.qh b/qcsrc/client/hud/panel/_mod.qh index 930a3fe02b..c24b5c0002 100644 --- a/qcsrc/client/hud/panel/_mod.qh +++ b/qcsrc/client/hud/panel/_mod.qh @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/client/hud/panel/ammo.qc b/qcsrc/client/hud/panel/ammo.qc index 5c45ff0f33..e299d1416e 100644 --- a/qcsrc/client/hud/panel/ammo.qc +++ b/qcsrc/client/hud/panel/ammo.qc @@ -98,11 +98,13 @@ void HUD_Ammo() if(hud != HUD_NORMAL) return; if(!autocvar__hud_configure) { - if(!autocvar_hud_panel_ammo) return; - if(spectatee_status == -1) return; + if((!autocvar_hud_panel_ammo) || (spectatee_status == -1)) + return; + if(STAT(HEALTH) < 1 && autocvar_hud_panel_ammo_hide_ondeath) + return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); draw_beginBoldFont(); @@ -114,7 +116,7 @@ void HUD_Ammo() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; diff --git a/qcsrc/client/hud/panel/ammo.qh b/qcsrc/client/hud/panel/ammo.qh index 6db88c68b3..71919d2fb0 100644 --- a/qcsrc/client/hud/panel/ammo.qh +++ b/qcsrc/client/hud/panel/ammo.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void DrawNadeProgressBar(vector myPos, vector mySize, float progress, vector color); diff --git a/qcsrc/client/hud/panel/centerprint.qc b/qcsrc/client/hud/panel/centerprint.qc index afda329838..f67cbffbd0 100644 --- a/qcsrc/client/hud/panel/centerprint.qc +++ b/qcsrc/client/hud/panel/centerprint.qc @@ -1,6 +1,7 @@ #include "centerprint.qh" -#include +#include "scoreboard.qh" +#include // CenterPrint (#16) @@ -57,8 +58,7 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun { // fade out the current msg (duration and countdown_num are ignored) centerprint_time[j] = min(5, autocvar_hud_panel_centerprint_fade_out); - if (centerprint_expire_time[j] > time + min(5, autocvar_hud_panel_centerprint_fade_out) || centerprint_expire_time[j] < time) - centerprint_expire_time[j] = time + min(5, autocvar_hud_panel_centerprint_fade_out); + centerprint_expire_time[j] = -1; // don't use the variable time here! return; } break; // found a msg with the same id, at position j @@ -80,14 +80,14 @@ void centerprint_generic(int new_id, string strMessage, float duration, int coun if (duration < 0) { centerprint_time[j] = -1; - centerprint_expire_time[j] = time; + centerprint_expire_time[j] = -1; // don't use the variable time here! } else { if(duration == 0) duration = max(1, autocvar_hud_panel_centerprint_time); centerprint_time[j] = duration; - centerprint_expire_time[j] = time + duration; + centerprint_expire_time[j] = -1; // don't use the variable time here! } centerprint_countdown_num[j] = countdown_num; } @@ -128,7 +128,10 @@ void HUD_CenterPrint () else { if(!hud_configure_prev) + { reset_centerprint_messages(); + hud_configure_cp_generation_time = time; // show a message immediately + } if (time > hud_configure_cp_generation_time) { if(highlightedPanel == HUD_PANEL(CENTERPRINT)) @@ -151,14 +154,7 @@ void HUD_CenterPrint () } } - // this panel fades only when the menu does - float hud_fade_alpha_save = 0; - if(scoreboard_fade_alpha) - { - hud_fade_alpha_save = hud_fade_alpha; - hud_fade_alpha = 1 - autocvar__menu_alpha; - } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); if ( HUD_Radar_Clickable() ) { @@ -168,17 +164,13 @@ void HUD_CenterPrint () panel_pos = eY * hud_panel_radar_bottom + eX * 0.5 * (vid_conwidth - panel_size_x); panel_size_y = min(panel_size_y, vid_conheight - hud_panel_radar_bottom); } - else if(scoreboard_fade_alpha) + else if(!autocvar__hud_configure && scoreboard_fade_alpha) { - hud_fade_alpha = hud_fade_alpha_save; - // move the panel below the scoreboard if (scoreboard_bottom >= 0.96 * vid_conheight) return; vector target_pos; - target_pos = eY * scoreboard_bottom + eX * 0.5 * (vid_conwidth - panel_size.x); - if(target_pos.y > panel_pos.y) { panel_pos = panel_pos + (target_pos - panel_pos) * sqrt(scoreboard_fade_alpha); @@ -190,7 +182,7 @@ void HUD_CenterPrint () HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if (!centerprint_showing) return; @@ -225,6 +217,13 @@ void HUD_CenterPrint () { if (j == CENTERPRINT_MAX_MSGS) j = 0; + if (centerprint_expire_time[j] == -1) + { + // here we are sure the time variable is not altered by CSQC_Ent_Update + centerprint_expire_time[j] = time; + if (centerprint_time[j] > 0) + centerprint_expire_time[j] += centerprint_time[j]; + } if (centerprint_expire_time[j] <= time) { if (centerprint_countdown_num[j] && centerprint_time[j] > 0) @@ -248,6 +247,9 @@ void HUD_CenterPrint () else // Expiring soon, so fade it out. a = (centerprint_expire_time[j] - time) / max(0.0001, autocvar_hud_panel_centerprint_fade_out); + if(centerprint_msgID[j] == CPID_TIMEIN) + a = 1; + // while counting down show it anyway in order to hold the current message position if (a <= 0.5/255.0 && centerprint_countdown_num[j] == 0) // Guaranteed invisible - don't show. continue; @@ -260,12 +262,14 @@ void HUD_CenterPrint () // also fade it based on positioning if(autocvar_hud_panel_centerprint_fade_subsequent) { - a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); // pass one: all messages after the first have half theAlpha - a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); // pass two: after that, gradually lower theAlpha even more for each message + // pass one: all messages after the first have half alpha + a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passone_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passone))), 1); + // pass two: after that, gradually lower alpha even more for each message + a = a * bound(autocvar_hud_panel_centerprint_fade_subsequent_passtwo_minalpha, (1 - (g / max(1, autocvar_hud_panel_centerprint_fade_subsequent_passtwo))), 1); } a *= panel_fg_alpha; - // finally set the size based on the new theAlpha from subsequent fading + // finally set the size based on the new alpha from subsequent fading sz = sz * (autocvar_hud_panel_centerprint_fade_subsequent_minfontsize + a * (1 - autocvar_hud_panel_centerprint_fade_subsequent_minfontsize)); drawfontscale = hud_scale * sz; diff --git a/qcsrc/client/hud/panel/centerprint.qh b/qcsrc/client/hud/panel/centerprint.qh index 6db88c68b3..1bec93efa6 100644 --- a/qcsrc/client/hud/panel/centerprint.qh +++ b/qcsrc/client/hud/panel/centerprint.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void reset_centerprint_messages(); diff --git a/qcsrc/client/hud/panel/chat.qc b/qcsrc/client/hud/panel/chat.qc index a27e7b9070..554f44e17e 100644 --- a/qcsrc/client/hud/panel/chat.qc +++ b/qcsrc/client/hud/panel/chat.qc @@ -1,5 +1,7 @@ #include "chat.qh" -/** Handle chat as a panel (#12) */ + +// Chat (#12) + void HUD_Chat() { if(!autocvar__hud_configure) @@ -21,7 +23,7 @@ void HUD_Chat() } } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); if(intermission == 2) { @@ -47,7 +49,7 @@ void HUD_Chat() panel.current_panel_bg = strzone(panel_bg); chat_panel_modified = true; } - panel_bg_alpha = max(0.75, panel_bg_alpha); // force an theAlpha of at least 0.75 + panel_bg_alpha = max(0.75, panel_bg_alpha); } vector pos, mySize; @@ -56,7 +58,7 @@ void HUD_Chat() // chat messages don't scale properly since they are displayed directly by the engine HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { @@ -75,17 +77,13 @@ void HUD_Chat() if(autocvar__hud_configure) { - vector chatsize; - chatsize = '1 1 0' * autocvar_con_chatsize; - cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over theAlpha and such - float i, a; - for(i = 0; i < autocvar_con_chat; ++i) + vector chatsize = '1 1 0' * autocvar_con_chatsize; + cvar_set("con_chatrect_x", "9001"); // over 9000, we'll fake it instead for more control over alpha and such + string str = textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors); + for(int i = 0; i < autocvar_con_chat; ++i) { - if(i == autocvar_con_chat - 1) - a = panel_fg_alpha; - else - a = panel_fg_alpha * floor(((i + 1) * 7 + autocvar_con_chattime)/45); - drawcolorcodedstring(pos, textShortenToWidth(_("^3Player^7: This is the chat area."), mySize.x, chatsize, stringwidth_colors), chatsize, a, DRAWFLAG_NORMAL); + // engine displays chat text at full alpha + drawcolorcodedstring(pos, str, chatsize, 1, DRAWFLAG_NORMAL); pos.y += chatsize.y; } } diff --git a/qcsrc/client/hud/panel/engineinfo.qc b/qcsrc/client/hud/panel/engineinfo.qc index 773c751d82..c8b7203eee 100644 --- a/qcsrc/client/hud/panel/engineinfo.qc +++ b/qcsrc/client/hud/panel/engineinfo.qc @@ -1,5 +1,6 @@ #include "engineinfo.qh" -// Engine info panel (#13) + +// Engine info (#13) float prevfps; float prevfps_time; @@ -18,7 +19,7 @@ void HUD_EngineInfo() if(!autocvar_hud_panel_engineinfo) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -27,7 +28,7 @@ void HUD_EngineInfo() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; diff --git a/qcsrc/client/hud/panel/healtharmor.qc b/qcsrc/client/hud/panel/healtharmor.qc index bc4291a948..4fad2c7c43 100644 --- a/qcsrc/client/hud/panel/healtharmor.qc +++ b/qcsrc/client/hud/panel/healtharmor.qc @@ -2,25 +2,28 @@ #include -/** Health/armor (#3) */ +// Health/armor (#3) + void HUD_HealthArmor() { int armor, health, fuel; if(!autocvar__hud_configure) { - if(!autocvar_hud_panel_healtharmor) return; + if((!autocvar_hud_panel_healtharmor) || (spectatee_status == -1)) + return; if(hud != HUD_NORMAL) return; - if(spectatee_status == -1) return; health = STAT(HEALTH); if(health <= 0) { + health = 0; prev_health = -1; - return; + if(autocvar_hud_panel_healtharmor_hide_ondeath) + return; } armor = STAT(ARMOR); - // code to check for spectatee_status changes is in Ent_ClientData() + // code to check for spectatee_status changes is in ENT_CLIENT_CLIENTDATA // prev_p_health and prev_health can be set to -1 there if (prev_p_health == -1) @@ -54,7 +57,7 @@ void HUD_HealthArmor() fuel = 20; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); draw_beginBoldFont(); @@ -66,7 +69,7 @@ void HUD_HealthArmor() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; @@ -83,8 +86,7 @@ void HUD_HealthArmor() vector v; v = healtharmor_maxdamage(health, armor, armorblockpercent, DEATH_WEAPON.m_id); - float x; - x = floor(v.x + 1); + float hp = floor(v.x + 1); float maxtotal = maxhealth + maxarmor; string biggercount; @@ -92,7 +94,7 @@ void HUD_HealthArmor() { biggercount = "health"; if(autocvar_hud_panel_healtharmor_progressbar) - HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_health, hp/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_health_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); if(armor) if(autocvar_hud_panel_healtharmor_text) drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "armor", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha * armor / health, DRAWFLAG_NORMAL); @@ -101,13 +103,13 @@ void HUD_HealthArmor() { biggercount = "armor"; if(autocvar_hud_panel_healtharmor_progressbar) - HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, x/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + HUD_Panel_DrawProgressBar(pos, mySize, autocvar_hud_panel_healtharmor_progressbar_armor, hp/maxtotal, 0, (baralign == 1 || baralign == 2), autocvar_hud_progressbar_armor_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); if(health) if(autocvar_hud_panel_healtharmor_text) drawpic_aspect_skin(pos + eX * mySize.x - eX * 0.5 * mySize.y, "health", '0.5 0.5 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); } if(autocvar_hud_panel_healtharmor_text) - DrawNumIcon(pos, mySize, x, biggercount, 0, iconalign, HUD_Get_Num_Color(x, maxtotal), 1); + DrawNumIcon(pos, mySize, hp, biggercount, 0, iconalign, HUD_Get_Num_Color(hp, maxtotal), 1); if(fuel) HUD_Panel_DrawProgressBar(pos, eX * mySize.x + eY * 0.2 * mySize.y, "progressbar", fuel/100, 0, (baralign == 1 || baralign == 3), autocvar_hud_progressbar_fuel_color, panel_fg_alpha * 0.8, DRAWFLAG_NORMAL); diff --git a/qcsrc/client/hud/panel/infomessages.qc b/qcsrc/client/hud/panel/infomessages.qc index a197963e75..2d6c952fc0 100644 --- a/qcsrc/client/hud/panel/infomessages.qc +++ b/qcsrc/client/hud/panel/infomessages.qc @@ -3,14 +3,56 @@ #include #include -// Info messages panel (#14) +// Info messages (#14) -#define drawInfoMessage(s) MACRO_BEGIN { \ - if(autocvar_hud_panel_infomessages_flip) \ - o.x = pos.x + mySize.x - stringwidth(s, true, fontsize); \ - drawcolorcodedstring(o, s, fontsize, a, DRAWFLAG_NORMAL); \ - o.y += fontsize.y; \ +float autocvar_hud_panel_infomessages_group0 = 1; +float autocvar_hud_panel_infomessages_group_fadetime = 0.4; +float autocvar_hud_panel_infomessages_group_time = 6; +const int IMG_COUNT = 1; // number of InfoMessage Groups +float img_fade[IMG_COUNT]; +int img_cur_msg[IMG_COUNT]; +float img_time[IMG_COUNT]; + +int img_select(int group_id) +{ + float fadetime = max(0.001, autocvar_hud_panel_infomessages_group_fadetime); + if(time > img_time[group_id]) + { + img_fade[group_id] = max(0, img_fade[group_id] - frametime / fadetime); + if(!img_fade[group_id]) + { + ++img_cur_msg[group_id]; + img_time[group_id] = floor(time) + autocvar_hud_panel_infomessages_group_time; + } + } + else + img_fade[group_id] = min(1, img_fade[group_id] + frametime / fadetime); + 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; + float offset = 0; + while(getWrappedLine_remaining) + { + s = getWrappedLine(sz.x - offset, fontsize, stringwidth_colors); + if(autocvar_hud_panel_infomessages_flip) + offset = sz.x - stringwidth_colors(s, fontsize) - offset; + drawcolorcodedstring(pos + eX * offset, s, fontsize, a, DRAWFLAG_NORMAL); + pos.y += fontsize.y; + offset = fontsize.x; + } + pos.y += fontsize.y * 0.25; + return pos; +} + +#define InfoMessage(s) MACRO_BEGIN { \ + pos = InfoMessages_drawstring(s, pos, mySize, ((img_curr_group >= 0) ? panel_fg_alpha * img_fade[img_curr_group] : panel_fg_alpha), fontsize); \ + img_curr_group = -1; \ } MACRO_END + void HUD_InfoMessages() { if(!autocvar__hud_configure) @@ -18,7 +60,7 @@ void HUD_InfoMessages() if(!autocvar_hud_panel_infomessages) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -27,82 +69,67 @@ void HUD_InfoMessages() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; mySize -= '2 2 0' * panel_bg_padding; } - // always force 5:1 aspect - vector newSize = '0 0 0'; - if(mySize.x/mySize.y > 5) - { - newSize.x = 5 * mySize.y; - newSize.y = mySize.y; - - pos.x = pos.x + (mySize.x - newSize.x) / 2; - } - else - { - newSize.y = 1/5 * mySize.x; - newSize.x = mySize.x; - - pos.y = pos.y + (mySize.y - newSize.y) / 2; - } - - mySize = newSize; - entity tm; - vector o; - o = pos; - - vector fontsize; - fontsize = '0.20 0.20 0' * mySize.y; - - float a; - a = panel_fg_alpha; - + vector fontsize = '0.2 0.2 0' * mySize.y; string s; + int img_curr_group = -1; if(!autocvar__hud_configure) { if(spectatee_status) { - a = 1; if(spectatee_status == -1) s = _("^1Observing"); else s = sprintf(_("^1Spectating: ^7%s"), entcs_GetName(current_player)); - drawInfoMessage(s); + InfoMessage(s); - if(spectatee_status == -1) - s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey("primary fire", "+fire")); - else - s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); - drawInfoMessage(s); - - if(spectatee_status == -1) - s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey("next weapon", "weapnext"), getcommandkey("previous weapon", "weapprev")); - else - s = sprintf(_("^1Press ^3%s^1 to observe"), getcommandkey("secondary fire", "+fire2")); - drawInfoMessage(s); + if(autocvar_hud_panel_infomessages_group0) + { + img_curr_group = 0; + switch(img_select(img_curr_group) % 3) + { + default: + case 0: + if(spectatee_status == -1) + s = sprintf(_("^1Press ^3%s^1 to spectate"), getcommandkey(_("primary fire"), "+fire")); + else + s = sprintf(_("^1Press ^3%s^1 or ^3%s^1 for next or previous player"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev")); + break; + case 1: + if(spectatee_status == -1) + s = sprintf(_("^1Use ^3%s^1 or ^3%s^1 to change the speed"), getcommandkey(_("next weapon"), "weapnext"), getcommandkey(_("previous weapon"), "weapprev")); + else + s = sprintf(_("^1Press ^3%s^1 to observe, ^3%s^1 to change camera mode"), getcommandkey(_("secondary fire"), "+fire2"), getcommandkey(_("drop weapon"), "dropweapon")); + break; + case 2: + s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey(_("server info"), "+show_info")); + break; + } + InfoMessage(s); + } - s = sprintf(_("^1Press ^3%s^1 for gamemode info"), getcommandkey("server info", "+show_info")); - drawInfoMessage(s); + MUTATOR_CALLHOOK(DrawInfoMessages, pos, mySize); - if(gametype == MAPINFO_TYPE_LMS) + if(!warmup_stage && gametype == MAPINFO_TYPE_LMS) { entity sk; sk = playerslots[player_localnum]; - if(sk.(scores[ps_primary]) >= 666) + if(sk.(scores(ps_primary)) >= 666) s = _("^1Match has already begun"); - else if(sk.(scores[ps_primary]) > 0) + else if(sk.(scores(ps_primary)) > 0) s = _("^1You have no more lives left"); else - s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); + s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump")); } else - s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey("jump", "+jump")); - drawInfoMessage(s); + s = sprintf(_("^1Press ^3%s^1 to join"), getcommandkey(_("jump"), "+jump")); + InfoMessage(s); } if (time < STAT(GAMESTARTTIME)) @@ -110,13 +137,13 @@ void HUD_InfoMessages() //we need to ceil, otherwise the countdown would be off by .5 when using round() float countdown = ceil(STAT(GAMESTARTTIME) - time); s = sprintf(_("^1Game starts in ^3%d^1 seconds"), countdown); - drawInfoMessage(s); + InfoMessage(s); } if(warmup_stage) { s = _("^2Currently in ^1warmup^2 stage!"); - drawInfoMessage(s); + InfoMessage(s); } string blinkcolor; @@ -130,9 +157,9 @@ void HUD_InfoMessages() if(ready_waiting_for_me) { if(warmup_stage) - s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); + s = sprintf(_("%sPress ^3%s%s to end warmup"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor); else - s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey("ready", "ready"), blinkcolor); + s = sprintf(_("%sPress ^3%s%s once you are ready"), blinkcolor, getcommandkey(_("ready"), "ready"), blinkcolor); } else { @@ -141,18 +168,18 @@ void HUD_InfoMessages() else s = _("^2Waiting for others to ready up..."); } - drawInfoMessage(s); + InfoMessage(s); } else if(warmup_stage && !spectatee_status) { - s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey("ready", "ready")); - drawInfoMessage(s); + s = sprintf(_("^2Press ^3%s^2 to end warmup"), getcommandkey(_("ready"), "ready")); + InfoMessage(s); } if(teamplay && !spectatee_status && gametype != MAPINFO_TYPE_CA && teamnagger) { float ts_min = 0, ts_max = 0; - tm = teams.sort_next; + entity tm = teams.sort_next; if (tm) { for (; tm.sort_next; tm = tm.sort_next) @@ -168,24 +195,36 @@ void HUD_InfoMessages() { s = strcat(blinkcolor, _("Teamnumbers are unbalanced!")); tm = GetTeam(myteam, false); - if (tm) - if (tm.team != NUM_SPECTATOR) - if (tm.team_size == ts_max) - s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey("team menu", "menu_showteamselect"), blinkcolor)); - drawInfoMessage(s); + if (tm && tm.team != NUM_SPECTATOR && tm.team_size == ts_max) + s = strcat(s, sprintf(_(" Press ^3%s%s to adjust"), getcommandkey(_("team menu"), "menu_showteamselect"), blinkcolor)); + InfoMessage(s); } } } + + if(autocvar_cl_showspectators) + if(num_spectators) + //if(spectatee_status != -1) + { + s = ((spectatee_status) ? _("^1Spectating this player:") : _("^1Spectating you:")); + // InfoMessage(s) + int limit = min(num_spectators, MAX_SPECTATORS); + for(int i = 0; i < limit; ++i) + { + float slot = spectatorlist[i]; + if(i == 0) + s = strcat(s, " ^7", entcs_GetName(slot)); + else + s = strcat("^7", entcs_GetName(slot)); + InfoMessage(s); + } + } } else { - s = _("^7Press ^3ESC ^7to show HUD options."); - drawInfoMessage(s); - s = _("^3Doubleclick ^7a panel for panel-specific options."); - drawInfoMessage(s); - s = _("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and"); - drawInfoMessage(s); - s = _("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments."); - drawInfoMessage(s); + InfoMessage(_("^7Press ^3ESC ^7to show HUD options.")); + InfoMessage(_("^3Doubleclick ^7a panel for panel-specific options.")); + InfoMessage(_("^3CTRL ^7to disable collision testing, ^3SHIFT ^7and")); + InfoMessage(_("^3ALT ^7+ ^3ARROW KEYS ^7for fine adjustments.")); } } diff --git a/qcsrc/client/hud/panel/minigame.qc b/qcsrc/client/hud/panel/minigame.qc index 933528a495..618f30b108 100644 --- a/qcsrc/client/hud/panel/minigame.qc +++ b/qcsrc/client/hud/panel/minigame.qc @@ -1,4 +1,5 @@ #include "minigame.qh" -// Minigame + +// Minigame (#17, #18, #19, #20) #include diff --git a/qcsrc/client/hud/panel/modicons.qc b/qcsrc/client/hud/panel/modicons.qc index 54c835831d..bf302fdfa3 100644 --- a/qcsrc/client/hud/panel/modicons.qc +++ b/qcsrc/client/hud/panel/modicons.qc @@ -4,7 +4,7 @@ #include #include // TODO: remove -// Mod icons panel (#10) +// Mod icons (#10) bool mod_active; // is there any active mod icon? @@ -16,27 +16,11 @@ void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int vector color = '0 0 0'; switch(i) { - case 0: - stat = STAT(REDALIVE); - pic = "player_red.tga"; - color = '1 0 0'; - break; - case 1: - stat = STAT(BLUEALIVE); - pic = "player_blue.tga"; - color = '0 0 1'; - break; - case 2: - stat = STAT(YELLOWALIVE); - pic = "player_yellow.tga"; - color = '1 1 0'; - break; + case 0: stat = STAT(REDALIVE); pic = "player_red"; color = '1 0 0'; break; + case 1: stat = STAT(BLUEALIVE); pic = "player_blue"; color = '0 0 1'; break; + case 2: stat = STAT(YELLOWALIVE); pic = "player_yellow"; color = '1 1 0'; break; default: - case 3: - stat = STAT(PINKALIVE); - pic = "player_pink.tga"; - color = '1 0 1'; - break; + case 3: stat = STAT(PINKALIVE); pic = "player_pink"; color = '1 0 1'; break; } if(mySize.x/mySize.y > aspect_ratio) @@ -108,6 +92,7 @@ void HUD_Mod_CTF_Reset() redflag_statuschange_time = blueflag_statuschange_time = yellowflag_statuschange_time = pinkflag_statuschange_time = neutralflag_statuschange_time = 0; } +int autocvar__teams_available; void HUD_Mod_CTF(vector pos, vector mySize) { vector redflag_pos, blueflag_pos, yellowflag_pos, pinkflag_pos, neutralflag_pos; @@ -117,10 +102,13 @@ void HUD_Mod_CTF(vector pos, vector mySize) int redflag, blueflag, yellowflag, pinkflag, neutralflag; // current status float redflag_statuschange_elapsedtime = 0, blueflag_statuschange_elapsedtime = 0, yellowflag_statuschange_elapsedtime = 0, pinkflag_statuschange_elapsedtime = 0, neutralflag_statuschange_elapsedtime = 0; // time since the status changed bool ctf_oneflag; // one-flag CTF mode enabled/disabled + bool ctf_stalemate; // currently in stalemate int stat_items = STAT(CTF_FLAGSTATUS); float fs, fs2, fs3, size1, size2; vector e1, e2; + int nteams = autocvar__teams_available; + redflag = (stat_items/CTF_RED_FLAG_TAKEN) & 3; blueflag = (stat_items/CTF_BLUE_FLAG_TAKEN) & 3; yellowflag = (stat_items/CTF_YELLOW_FLAG_TAKEN) & 3; @@ -129,14 +117,16 @@ void HUD_Mod_CTF(vector pos, vector mySize) ctf_oneflag = (stat_items & CTF_FLAG_NEUTRAL); + ctf_stalemate = (stat_items & CTF_STALEMATE); + mod_active = (redflag || blueflag || yellowflag || pinkflag || neutralflag || (stat_items & CTF_SHIELDED)); if (autocvar__hud_configure) { redflag = 1; blueflag = 2; - if (team_count >= 3) + if (nteams & BIT(2)) yellowflag = 2; - if (team_count >= 4) + if (nteams & BIT(3)) pinkflag = 3; ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor? } @@ -198,18 +188,24 @@ void HUD_Mod_CTF(vector pos, vector mySize) break; \ } \ } MACRO_END - X(red, myteam != NUM_TEAM_1); - X(blue, myteam != NUM_TEAM_2); - X(yellow, myteam != NUM_TEAM_3 && team_count >= 3); - X(pink, myteam != NUM_TEAM_4 && team_count >= 4); + X(red, myteam != NUM_TEAM_1 && (nteams & BIT(0))); + X(blue, myteam != NUM_TEAM_2 && (nteams & BIT(1))); + X(yellow, myteam != NUM_TEAM_3 && (nteams & BIT(2))); + X(pink, myteam != NUM_TEAM_4 && (nteams & BIT(3))); X(neutral, ctf_oneflag); #undef X + int tcount = 2; + if(nteams & BIT(2)) + tcount = 3; + if(nteams & BIT(3)) + tcount = 4; + if (ctf_oneflag) { // hacky, but these aren't needed red_icon = red_icon_prevstatus = blue_icon = blue_icon_prevstatus = yellow_icon = yellow_icon_prevstatus = pink_icon = pink_icon_prevstatus = string_null; fs = fs2 = fs3 = 1; - } else switch (team_count) { + } else switch (tcount) { default: case 2: fs = 0.5; fs2 = 0.5; fs3 = 0.5; break; case 3: fs = 1; fs2 = 0.35; fs3 = 0.35; break; @@ -264,6 +260,8 @@ void HUD_Mod_CTF(vector pos, vector mySize) #define X(team) MACRO_BEGIN { \ f = bound(0, team##flag_statuschange_elapsedtime * 2, 1); \ + if (team##_icon && ctf_stalemate) \ + drawpic_aspect_skin(team##flag_pos, "flag_stalemate", flag_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); \ if (team##_icon_prevstatus && f < 1) \ drawpic_aspect_skin_expanding(team##flag_pos, team##_icon_prevstatus, flag_size, '1 1 1', panel_fg_alpha * team##_alpha_prevstatus, DRAWFLAG_NORMAL, f); \ if (team##_icon) \ @@ -285,8 +283,9 @@ void HUD_Mod_KH(vector pos, vector mySize) mod_active = 1; // keyhunt should never hide the mod icons panel // Read current state - int state = STAT(KH_KEYS); + if(!state) return; + int i, key_state; int all_keys, team1_keys, team2_keys, team3_keys, team4_keys, dropped_keys, carrying_keys; all_keys = team1_keys = team2_keys = team3_keys = team4_keys = dropped_keys = carrying_keys = 0; @@ -317,9 +316,7 @@ void HUD_Mod_KH(vector pos, vector mySize) } // Calculate slot measurements - vector slot_size; - if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x) { // Quadratic arrangement @@ -349,11 +346,10 @@ void HUD_Mod_KH(vector pos, vector mySize) // Make icons blink in case of RUN HERE - float blink = 0.6 + sin(2*M_PI*time) / 2.5; // Oscillate between 0.2 and 1 - float alpha; - alpha = 1; - + float alpha = 1; if(carrying_keys) + { + float blink = 0.6 + sin(2 * M_PI * time) * 0.4; // Oscillate between 0.2 and 1 switch(myteam) { case NUM_TEAM_1: if(team1_keys == all_keys) alpha = blink; break; @@ -361,6 +357,7 @@ void HUD_Mod_KH(vector pos, vector mySize) case NUM_TEAM_3: if(team3_keys == all_keys) alpha = blink; break; case NUM_TEAM_4: if(team4_keys == all_keys) alpha = blink; break; } + } // Draw icons @@ -518,9 +515,9 @@ void HUD_Mod_Race(vector pos, vector mySize) entity me; me = playerslots[player_localnum]; float score; - score = me.(scores[ps_primary]); + score = me.(scores(ps_primary)); - if(!(scores_flags[ps_primary] & SFL_TIME) || teamplay) // race/cts record display on HUD + if(!(scores_flags(ps_primary) & SFL_TIME) || teamplay) // race/cts record display on HUD return; // no records in the actual race // clientside personal record @@ -640,29 +637,15 @@ void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, in vector color = '0 0 0'; switch(i) { - case 0: - stat = STAT(DOM_PPS_RED); - pic = "dom_icon_red"; - color = '1 0 0'; - break; - case 1: - stat = STAT(DOM_PPS_BLUE); - pic = "dom_icon_blue"; - color = '0 0 1'; - break; - case 2: - stat = STAT(DOM_PPS_YELLOW); - pic = "dom_icon_yellow"; - color = '1 1 0'; - break; + case 0: stat = STAT(DOM_PPS_RED); pic = "dom_icon_red"; color = '1 0 0'; break; + case 1: stat = STAT(DOM_PPS_BLUE); pic = "dom_icon_blue"; color = '0 0 1'; break; + case 2: stat = STAT(DOM_PPS_YELLOW); pic = "dom_icon_yellow"; color = '1 1 0'; break; default: - case 3: - stat = STAT(DOM_PPS_PINK); - pic = "dom_icon_pink"; - color = '1 0 1'; - break; + case 3: stat = STAT(DOM_PPS_PINK); pic = "dom_icon_pink"; color = '1 0 1'; break; } - float pps_ratio = stat / STAT(DOM_TOTAL_PPS); + float pps_ratio = 0; + if(STAT(DOM_TOTAL_PPS)) + pps_ratio = stat / STAT(DOM_TOTAL_PPS); if(mySize.x/mySize.y > aspect_ratio) { @@ -729,18 +712,7 @@ void HUD_Mod_Dom(vector myPos, vector mySize) void HUD_ModIcons_SetFunc() { - switch(gametype) - { - case MAPINFO_TYPE_KEYHUNT: HUD_ModIcons_GameType = HUD_Mod_KH; break; - case MAPINFO_TYPE_CTF: HUD_ModIcons_GameType = HUD_Mod_CTF; break; - case MAPINFO_TYPE_NEXBALL: HUD_ModIcons_GameType = HUD_Mod_NexBall; break; - case MAPINFO_TYPE_CTS: - case MAPINFO_TYPE_RACE: HUD_ModIcons_GameType = HUD_Mod_Race; break; - case MAPINFO_TYPE_CA: - case MAPINFO_TYPE_FREEZETAG: HUD_ModIcons_GameType = HUD_Mod_CA; break; - case MAPINFO_TYPE_DOMINATION: HUD_ModIcons_GameType = HUD_Mod_Dom; break; - case MAPINFO_TYPE_KEEPAWAY: HUD_ModIcons_GameType = HUD_Mod_Keepaway; break; - } + HUD_ModIcons_GameType = gametype.m_modicons; } int mod_prev; // previous state of mod_active to check for a change @@ -755,9 +727,6 @@ void HUD_ModIcons() if(!HUD_ModIcons_GameType) return; } - HUD_Panel_UpdateCvars(); - - draw_beginBoldFont(); if(mod_active != mod_prev) { mod_change = time; @@ -769,12 +738,19 @@ void HUD_ModIcons() else mod_alpha = bound(0, 1 - (time - mod_change) * 2, 1); + //if(mod_alpha <= 0) + // return; + panel_fade_alpha *= mod_alpha; + HUD_Panel_LoadCvars(); + + draw_beginBoldFont(); + if (autocvar_hud_panel_modicons_dynamichud) HUD_Scale_Enable(); else HUD_Scale_Disable(); - if(mod_alpha) - HUD_Panel_DrawBg(mod_alpha); + + HUD_Panel_DrawBg(); if(panel_bg_padding) { diff --git a/qcsrc/client/hud/panel/notify.qc b/qcsrc/client/hud/panel/notify.qc index a5e923825a..a49d262a4f 100644 --- a/qcsrc/client/hud/panel/notify.qc +++ b/qcsrc/client/hud/panel/notify.qc @@ -1,5 +1,7 @@ #include "notify.qh" -// Notification area (#4) + + +// Notifications (#4) void HUD_Notify_Push(string icon, string attacker, string victim) { @@ -47,12 +49,13 @@ void HUD_Notify() if (!autocvar_hud_panel_notify) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); + if (autocvar_hud_panel_notify_dynamichud) HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if (!autocvar__hud_configure) if (notify_count == 0) diff --git a/qcsrc/client/hud/panel/physics.qc b/qcsrc/client/hud/panel/physics.qc index 371a9f344b..6befd1a30a 100644 --- a/qcsrc/client/hud/panel/physics.qc +++ b/qcsrc/client/hud/panel/physics.qc @@ -1,9 +1,10 @@ #include "physics.qh" +#include #include #include -// Physics panel (#15) +// Physics (#15) vector acc_prevspeed; float acc_prevtime, acc_avg, top_speed, top_speed_time; @@ -17,7 +18,7 @@ void HUD_Physics() if(autocvar_hud_panel_physics == 3 && !(gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); draw_beginBoldFont(); @@ -25,7 +26,7 @@ void HUD_Physics() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { panel_pos += '1 1 0' * panel_bg_padding; @@ -43,34 +44,8 @@ void HUD_Physics() text_scale = min(autocvar_hud_panel_physics_text_scale, 1); //compute speed - float speed, conversion_factor; - string unit; - - switch(autocvar_hud_panel_physics_speed_unit) - { - default: - case 1: - unit = _(" qu/s"); - conversion_factor = 1.0; - break; - case 2: - unit = _(" m/s"); - conversion_factor = 0.0254; - break; - case 3: - unit = _(" km/h"); - conversion_factor = 0.0254 * 3.6; - break; - case 4: - unit = _(" mph"); - conversion_factor = 0.0254 * 3.6 * 0.6213711922; - break; - case 5: - unit = _(" knots"); - conversion_factor = 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h - break; - } - + float speed, conversion_factor = GetSpeedUnitFactor(autocvar_hud_panel_physics_speed_unit); + string unit = GetSpeedUnit(autocvar_hud_panel_physics_speed_unit); vector vel = (csqcplayer ? csqcplayer.velocity : pmove_vel); float max_speed = floor( autocvar_hud_panel_physics_speed_max * conversion_factor + 0.5 ); diff --git a/qcsrc/client/hud/panel/powerups.qc b/qcsrc/client/hud/panel/powerups.qc index 223bf72ce8..076ce918f1 100644 --- a/qcsrc/client/hud/panel/powerups.qc +++ b/qcsrc/client/hud/panel/powerups.qc @@ -1,6 +1,6 @@ #include "powerups.qh" -#include +#include // Powerups (#2) @@ -63,15 +63,16 @@ void HUD_Powerups() { int allItems = STAT(ITEMS); int allBuffs = STAT(BUFFS); - int strengthTime, shieldTime, superTime; + float strengthTime, shieldTime, superTime; // Initialize items if(!autocvar__hud_configure) { - if(!autocvar_hud_panel_powerups) return; - if(spectatee_status == -1) return; - if(STAT(HEALTH) <= 0) return; - if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return; + if((!autocvar_hud_panel_powerups) || (spectatee_status == -1)) + return; + if(STAT(HEALTH) <= 0 && autocvar_hud_panel_powerups_hide_ondeath) + return; + //if(!(allItems & (ITEM_Strength.m_itemid | ITEM_Shield.m_itemid | IT_SUPERWEAPON)) && !allBuffs) return; strengthTime = bound(0, STAT(STRENGTH_FINISHED) - time, 99); shieldTime = bound(0, STAT(INVINCIBLE_FINISHED) - time, 99); @@ -108,12 +109,13 @@ void HUD_Powerups() return; // Draw panel background - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); + if (autocvar_hud_panel_powerups_dynamichud) HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); // Set drawing area vector pos = panel_pos; diff --git a/qcsrc/client/hud/panel/powerups.qh b/qcsrc/client/hud/panel/powerups.qh index 6db88c68b3..3235f8da67 100644 --- a/qcsrc/client/hud/panel/powerups.qh +++ b/qcsrc/client/hud/panel/powerups.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +void addPowerupItem(string name, string icon, vector color, float currentTime, float lifeTime); diff --git a/qcsrc/client/hud/panel/pressedkeys.qc b/qcsrc/client/hud/panel/pressedkeys.qc index d0a4f39fdf..abb9ccef9a 100644 --- a/qcsrc/client/hud/panel/pressedkeys.qc +++ b/qcsrc/client/hud/panel/pressedkeys.qc @@ -1,5 +1,8 @@ #include "pressedkeys.qh" -/** Draw pressed keys (#11) */ + + +// Pressed keys (#11) + void HUD_PressedKeys() { if(!autocvar__hud_configure) @@ -8,7 +11,7 @@ void HUD_PressedKeys() if(spectatee_status <= 0 && autocvar_hud_panel_pressedkeys < 2) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -17,7 +20,7 @@ void HUD_PressedKeys() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; diff --git a/qcsrc/client/hud/panel/quickmenu.qc b/qcsrc/client/hud/panel/quickmenu.qc index 08d97ffd2e..e242ae8958 100644 --- a/qcsrc/client/hud/panel/quickmenu.qc +++ b/qcsrc/client/hud/panel/quickmenu.qc @@ -1,10 +1,11 @@ #include "quickmenu.qh" -// QuickMenu (#23) #include -#include +#include #include +// QuickMenu (#23) + // QUICKMENU_MAXLINES must be <= 10 const int QUICKMENU_MAXLINES = 10; // visible entries are loaded from QuickMenu_Buffer into QuickMenu_Page_* arrays @@ -58,6 +59,7 @@ void QuickMenu_Page_ClearEntry(int i) if (QuickMenu_Page_Command[i]) strunzone(QuickMenu_Page_Command[i]); QuickMenu_Page_Command[i] = string_null; + QuickMenu_Page_Command_Type[i] = 0; } float QuickMenu_Page_Load(string target_submenu, float new_page); @@ -155,7 +157,7 @@ bool QuickMenu_Open(string mode, string submenu) } else { - LOG_WARNINGF("Unrecognized mode %s\n", mode); + LOG_WARNF("Unrecognized mode %s", mode); return false; } @@ -291,7 +293,7 @@ bool QuickMenu_Page_Load(string target_submenu, bool new_page) // printf("^1 skipping %s\n", s); } if(QuickMenu_Buffer_Index == QuickMenu_Buffer_Size) - LOG_WARNINGF("Couldn't find submenu \"%s\"\n", z_submenu); + LOG_WARNF("Couldn't find submenu \"%s\"", z_submenu); } // only the last page can contain up to QUICKMENU_MAXLINES entries @@ -430,11 +432,8 @@ bool QuickMenu_InputEvent(int bInputType, float nPrimary, float nSecondary) } // allow console bind to work - string con_keys; - float keys; - con_keys = findkeysforcommand("toggleconsole", 0); - keys = tokenize(con_keys); // findkeysforcommand returns data for this - + string con_keys = findkeysforcommand("toggleconsole", 0); + int keys = tokenize(con_keys); // findkeysforcommand returns data for this bool hit_con_bind = false; int i; for (i = 0; i < keys; ++i) @@ -497,15 +496,11 @@ void QuickMenu_Mouse() return; } - if(!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos.x = bound(0, mousepos.x, vid_conwidth); - mousepos.y = bound(0, mousepos.y, vid_conheight); - } + if (!autocvar_hud_cursormode) + update_mousepos(); - HUD_Panel_UpdateCvars(); + panel = HUD_PANEL(QUICKMENU); + HUD_Panel_LoadCvars(); if(panel_bg_padding) { @@ -619,10 +614,10 @@ void HUD_QuickMenu() } } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { @@ -660,7 +655,7 @@ void HUD_QuickMenu() else tokenize_console(substring(QuickMenu_Page_Command[i], 0, end)); - //if(argv(1) && argv(0) == "toggle") // already checked + if(argv(1) && argv(0) == "toggle") { // "enable feature xxx" "toggle xxx" (or "toggle xxx 1 0") // "disable feature xxx" "toggle xxx 0 1" @@ -868,9 +863,9 @@ void QuickMenu_Default(string target_submenu) if(target_submenu != "" && !target_submenu_found) { - LOG_WARNINGF("Couldn't find submenu \"%s\"\n", target_submenu); + LOG_WARNF("Couldn't find submenu \"%s\"", target_submenu); if(prvm_language != "en") - LOG_WARNINGF("^3Warning: submenu must be in English\n", target_submenu); + LOG_WARNF("^3Warning: submenu must be in English", target_submenu); QuickMenu_Buffer_Size = 0; } } diff --git a/qcsrc/client/hud/panel/racetimer.qc b/qcsrc/client/hud/panel/racetimer.qc index dd8bbdfec9..02b631b300 100644 --- a/qcsrc/client/hud/panel/racetimer.qc +++ b/qcsrc/client/hud/panel/racetimer.qc @@ -2,7 +2,7 @@ #include -/** Race timer (#8) */ +// Race timer (#6) // return the string of the onscreen race timer string MakeRaceString(int cp, float mytime, float theirtime, float lapdelta, string theirname) @@ -83,7 +83,7 @@ void HUD_RaceTimer () if(spectatee_status == -1) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; pos = panel_pos; @@ -93,7 +93,7 @@ void HUD_RaceTimer () HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; diff --git a/qcsrc/client/hud/panel/radar.qc b/qcsrc/client/hud/panel/radar.qc index 1642e41891..3bc537c8f8 100644 --- a/qcsrc/client/hud/panel/radar.qc +++ b/qcsrc/client/hud/panel/radar.qc @@ -26,6 +26,13 @@ void HUD_Radar_Show_Maximized(bool doshow,float 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 + localcmd("-fire\n"); + localcmd("-fire2\n"); + localcmd("-use\n"); + localcmd("-hook\n"); + localcmd("-jump\n"); } } else if ( hud_panel_radar_mouse ) @@ -78,11 +85,9 @@ float HUD_Radar_InputEvent(int bInputType, float nPrimary, float nSecondary) else { // allow console/use binds to work without hiding the map - string con_keys; - float keys; - float i; - con_keys = strcat(findkeysforcommand("toggleconsole", 0)," ",findkeysforcommand("+use", 0)) ; - keys = tokenize(con_keys); // findkeysforcommand returns data for this + string con_keys = strcat(findkeysforcommand("toggleconsole", 0), " ", findkeysforcommand("+use", 0)) ; + int keys = tokenize(con_keys); // findkeysforcommand returns data for this + int i; for (i = 0; i < keys; ++i) { if(nPrimary == stof(argv(i))) @@ -132,15 +137,11 @@ void HUD_Radar_Mouse() return; } - if(!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos_x = bound(0, mousepos_x, vid_conwidth); - mousepos_y = bound(0, mousepos_y, vid_conheight); - } + if (!autocvar_hud_cursormode) + update_mousepos(); - HUD_Panel_UpdateCvars(); + panel = HUD_PANEL(RADAR); + HUD_Panel_LoadCvars(); panel_size = autocvar_hud_panel_radar_maximized_size; @@ -192,7 +193,7 @@ void HUD_Radar() if ( hud_panel_radar_temp_hidden ) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); float f = 0; @@ -279,7 +280,7 @@ void HUD_Radar() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; @@ -311,10 +312,10 @@ void HUD_Radar() else { vector c0, c1, c2, c3, span; - c0 = rotate(mi_min, teamradar_angle * DEG2RAD); - c1 = rotate(mi_max, teamradar_angle * DEG2RAD); - c2 = rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD); - c3 = rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD); + c0 = Rotate(mi_min, teamradar_angle * DEG2RAD); + c1 = Rotate(mi_max, teamradar_angle * DEG2RAD); + c2 = Rotate('1 0 0' * mi_min.x + '0 1 0' * mi_max.y, teamradar_angle * DEG2RAD); + c3 = Rotate('1 0 0' * mi_max.x + '0 1 0' * mi_min.y, teamradar_angle * DEG2RAD); span = '0 0 0'; span.x = max(c0_x, c1_x, c2_x, c3_x) - min(c0_x, c1_x, c2_x, c3_x); span.y = max(c0_y, c1_y, c2_y, c3_y) - min(c0_y, c1_y, c2_y, c3_y); @@ -346,12 +347,12 @@ void HUD_Radar() draw_teamradar_background(hud_panel_radar_foreground_alpha); - FOREACH_ENTITY_CLASS("radarlink", true, draw_teamradar_link(it.origin, it.velocity, it.team)); + IL_EACH(g_radarlinks, true, draw_teamradar_link(it.origin, it.velocity, it.team)); - FOREACH_ENTITY_FLAGS(teamradar_icon, 0xFFFFFF, { + 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 ) + if ( it.health >= 0 ) + if ( it.team == myteam + 1 || gametype == MAPINFO_TYPE_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/score.qc b/qcsrc/client/hud/panel/score.qc index b07b84ed33..f3d55a433e 100644 --- a/qcsrc/client/hud/panel/score.qc +++ b/qcsrc/client/hud/panel/score.qc @@ -1,12 +1,11 @@ #include "score.qh" -#include +#include "scoreboard.qh" #include #include // Score (#7) -void HUD_UpdatePlayerTeams(); void HUD_Score_Rankings(vector pos, vector mySize, entity me) { float score; @@ -71,8 +70,8 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me) return; } - if (!scoreboard_fade_alpha) // the scoreboard too calls HUD_UpdatePlayerTeams - HUD_UpdatePlayerTeams(); + if (!scoreboard_fade_alpha) // the scoreboard too calls Scoreboard_UpdatePlayerTeams + Scoreboard_UpdatePlayerTeams(); if (team_count) { // show team scores in the first line @@ -80,11 +79,12 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me) for(tm = teams.sort_next; tm; tm = tm.sort_next) { if(tm.team == NUM_SPECTATOR) continue; - if(!tm.team && teamplay) + if(!tm.team) continue; + if (tm.team == myteam) drawfill(pos + eX * score_size * i, eX * score_size + eY * fontsize.y, '1 1 1', highlight_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores[ts_primary])), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(pos + eX * score_size * i, ftos(tm.(teamscores(ts_primary))), eX * score_size + eY * fontsize.y, Team_ColorRGB(tm.team) * 0.8, panel_fg_alpha, DRAWFLAG_NORMAL); ++i; } first_pl = 1; @@ -124,7 +124,7 @@ void HUD_Score_Rankings(vector pos, vector mySize, entity me) score_color = Team_ColorRGB(pl.team) * 0.8; s = textShortenToWidth(entcs_GetName(pl.sv_entnum), name_size, fontsize, stringwidth_colors); drawcolorcodedstring(pos + eX * (name_size - stringwidth(s, true, fontsize)), s, fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores[ps_primary])), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring(pos + eX * (name_size + spacing_size), ftos(pl.(scores(ps_primary))), fontsize, score_color, panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += fontsize.y; ++i; } @@ -139,7 +139,7 @@ void HUD_Score() if(spectatee_status == -1 && (gametype == MAPINFO_TYPE_RACE || gametype == MAPINFO_TYPE_CTS)) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; pos = panel_pos; mySize = panel_size; @@ -148,7 +148,7 @@ void HUD_Score() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; @@ -162,23 +162,23 @@ void HUD_Score() me = playerslots[current_player]; - if((scores_flags[ps_primary] & SFL_TIME) && !teamplay) { // race/cts record display on HUD + if((scores_flags(ps_primary) & SFL_TIME) && !teamplay) { // race/cts record display on HUD string timer, distrtimer; pl = players.sort_next; if(pl == me) pl = pl.sort_next; - if(scores_flags[ps_primary] & SFL_ZERO_IS_WORST) - if(pl.scores[ps_primary] == 0) + if(scores_flags(ps_primary) & SFL_ZERO_IS_WORST) + if(pl.scores(ps_primary) == 0) pl = NULL; - score = me.(scores[ps_primary]); + score = me.(scores(ps_primary)); timer = TIME_ENCODED_TOSTRING(score); draw_beginBoldFont(); - if (pl && ((!(scores_flags[ps_primary] & SFL_ZERO_IS_WORST)) || score)) { + if (pl && ((!(scores_flags(ps_primary) & SFL_ZERO_IS_WORST)) || score)) { // distribution display - distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]); + distribution = me.(scores(ps_primary)) - pl.(scores(ps_primary)); distrtimer = ftos_decimals(fabs(distribution/pow(10, TIME_DECIMALS)), TIME_DECIMALS); @@ -211,11 +211,11 @@ void HUD_Score() if(autocvar__hud_configure) distribution = 42; else if(pl) - distribution = me.(scores[ps_primary]) - pl.(scores[ps_primary]); + distribution = me.(scores(ps_primary)) - pl.(scores(ps_primary)); else distribution = 0; - score = me.(scores[ps_primary]); + score = me.(scores(ps_primary)); if(autocvar__hud_configure) score = 123; @@ -281,9 +281,10 @@ void HUD_Score() for(tm = teams.sort_next; tm; tm = tm.sort_next) { if(tm.team == NUM_SPECTATOR) continue; - if(!tm.team && teamplay) + if(!tm.team) continue; - score = tm.(teamscores[ts_primary]); + + score = tm.(teamscores(ts_primary)); if(autocvar__hud_configure) score = 123; diff --git a/qcsrc/client/hud/panel/scoreboard.qc b/qcsrc/client/hud/panel/scoreboard.qc new file mode 100644 index 0000000000..35de2196b9 --- /dev/null +++ b/qcsrc/client/hud/panel/scoreboard.qc @@ -0,0 +1,1702 @@ +#include "scoreboard.qh" + +#include "quickmenu.qh" +#include +#include +#include +#include +#include +#include +#include + +// Scoreboard (#24) + +const int MAX_SBT_FIELDS = MAX_SCORE; + +PlayerScoreField sbt_field[MAX_SBT_FIELDS + 1]; +float sbt_field_size[MAX_SBT_FIELDS + 1]; +string sbt_field_title[MAX_SBT_FIELDS + 1]; +int sbt_num_fields; + +string autocvar_hud_fontsize; +string hud_fontsize_str; +float max_namesize; + +float sbt_bg_alpha; +float sbt_fg_alpha; +float sbt_fg_alpha_self; +bool sbt_highlight; +float sbt_highlight_alpha; +float sbt_highlight_alpha_self; + +// provide basic panel cvars to old clients +// TODO remove them after a future release (0.8.2+) +noref string autocvar_hud_panel_scoreboard_pos = "0.150000 0.150000"; +noref string autocvar_hud_panel_scoreboard_size = "0.700000 0.700000"; +noref string autocvar_hud_panel_scoreboard_bg = "border_default"; +noref string autocvar_hud_panel_scoreboard_bg_color = "0 0.3 0.5"; +noref string autocvar_hud_panel_scoreboard_bg_color_team = ""; +noref string autocvar_hud_panel_scoreboard_bg_alpha = "0.7"; +noref string autocvar_hud_panel_scoreboard_bg_border = ""; +noref string autocvar_hud_panel_scoreboard_bg_padding = ""; + +float autocvar_hud_panel_scoreboard_fadeinspeed = 10; +float autocvar_hud_panel_scoreboard_fadeoutspeed = 5; +float autocvar_hud_panel_scoreboard_respawntime_decimals = 1; +float autocvar_hud_panel_scoreboard_table_bg_alpha = 0; +float autocvar_hud_panel_scoreboard_table_bg_scale = 0.25; +float autocvar_hud_panel_scoreboard_table_fg_alpha = 0.9; +float autocvar_hud_panel_scoreboard_table_fg_alpha_self = 1; +bool autocvar_hud_panel_scoreboard_table_highlight = true; +float autocvar_hud_panel_scoreboard_table_highlight_alpha = 0.2; +float autocvar_hud_panel_scoreboard_table_highlight_alpha_self = 0.4; +float autocvar_hud_panel_scoreboard_bg_teams_color_team = 0; +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; + +bool autocvar_hud_panel_scoreboard_dynamichud = false; + +float autocvar_hud_panel_scoreboard_maxheight = 0.6; +bool autocvar_hud_panel_scoreboard_others_showscore = true; +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) +{ + switch(l) + { + case "bckills": return CTX(_("SCO^bckills")); + case "bctime": return CTX(_("SCO^bctime")); + case "caps": return CTX(_("SCO^caps")); + case "captime": return CTX(_("SCO^captime")); + case "deaths": return CTX(_("SCO^deaths")); + case "destroyed": return CTX(_("SCO^destroyed")); + case "dmg": return CTX(_("SCO^damage")); + case "dmgtaken": return CTX(_("SCO^dmgtaken")); + case "drops": return CTX(_("SCO^drops")); + case "faults": return CTX(_("SCO^faults")); + case "fckills": return CTX(_("SCO^fckills")); + case "goals": return CTX(_("SCO^goals")); + case "kckills": return CTX(_("SCO^kckills")); + case "kdratio": return CTX(_("SCO^kdratio")); + case "kd": return CTX(_("SCO^k/d")); + case "kdr": return CTX(_("SCO^kdr")); + case "kills": return CTX(_("SCO^kills")); + case "laps": return CTX(_("SCO^laps")); + case "lives": return CTX(_("SCO^lives")); + case "losses": return CTX(_("SCO^losses")); + case "name": return CTX(_("SCO^name")); + case "sum": return CTX(_("SCO^sum")); + case "nick": return CTX(_("SCO^nick")); + case "objectives": return CTX(_("SCO^objectives")); + case "pickups": return CTX(_("SCO^pickups")); + case "ping": return CTX(_("SCO^ping")); + case "pl": return CTX(_("SCO^pl")); + case "pushes": return CTX(_("SCO^pushes")); + case "rank": return CTX(_("SCO^rank")); + case "returns": return CTX(_("SCO^returns")); + case "revivals": return CTX(_("SCO^revivals")); + case "rounds": return CTX(_("SCO^rounds won")); + case "score": return CTX(_("SCO^score")); + case "suicides": return CTX(_("SCO^suicides")); + case "takes": return CTX(_("SCO^takes")); + case "ticks": return CTX(_("SCO^ticks")); + default: return l; + } +} + +void Scoreboard_InitScores() +{ + int i, f; + + ps_primary = ps_secondary = NULL; + ts_primary = ts_secondary = -1; + FOREACH(Scores, true, { + f = (scores_flags(it) & SFL_SORT_PRIO_MASK); + if(f == SFL_SORT_PRIO_PRIMARY) + ps_primary = it; + if(f == SFL_SORT_PRIO_SECONDARY) + ps_secondary = it; + }); + if(ps_secondary == NULL) + ps_secondary = ps_primary; + + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + f = (teamscores_flags(i) & SFL_SORT_PRIO_MASK); + if(f == SFL_SORT_PRIO_PRIMARY) + ts_primary = i; + if(f == SFL_SORT_PRIO_SECONDARY) + ts_secondary = i; + } + if(ts_secondary == -1) + ts_secondary = ts_primary; + + Cmd_Scoreboard_SetFields(0); +} + +float SetTeam(entity pl, float Team); +//float lastpnum; +void Scoreboard_UpdatePlayerTeams() +{ + float Team; + entity pl, tmp; + //int num = 0; + for(pl = players.sort_next; pl; pl = pl.sort_next) + { + //num += 1; + Team = entcs_GetScoreTeam(pl.sv_entnum); + if(SetTeam(pl, Team)) + { + tmp = pl.sort_prev; + Scoreboard_UpdatePlayerPos(pl); + if(tmp) + pl = tmp; + else + pl = players.sort_next; + } + } + /* + if(num != lastpnum) + print(strcat("PNUM: ", ftos(num), "\n")); + lastpnum = num; + */ +} + +int Scoreboard_CompareScore(int vl, int vr, int f) +{ + TC(int, vl); TC(int, vr); TC(int, f); + if(f & SFL_ZERO_IS_WORST) + { + if(vl == 0 && vr != 0) + return 1; + if(vl != 0 && vr == 0) + return 0; + } + if(vl > vr) + return IS_INCREASING(f); + if(vl < vr) + return IS_DECREASING(f); + return -1; +} + +float Scoreboard_ComparePlayerScores(entity left, entity right) +{ + float vl, vr, r; + vl = entcs_GetTeam(left.sv_entnum); + vr = entcs_GetTeam(right.sv_entnum); + + if(!left.gotscores) + vl = NUM_SPECTATOR; + if(!right.gotscores) + vr = NUM_SPECTATOR; + + if(vl > vr) + return true; + if(vl < vr) + return false; + + if(vl == NUM_SPECTATOR) + { + // FIRST the one with scores (spectators), THEN the ones without (downloaders) + // no other sorting + if(!left.gotscores && right.gotscores) + return true; + return false; + } + + r = Scoreboard_CompareScore(left.scores(ps_primary), right.scores(ps_primary), scores_flags(ps_primary)); + if (r >= 0) + return r; + + r = Scoreboard_CompareScore(left.scores(ps_secondary), right.scores(ps_secondary), scores_flags(ps_secondary)); + if (r >= 0) + return r; + + FOREACH(Scores, true, { + r = Scoreboard_CompareScore(left.scores(it), right.scores(it), scores_flags(it)); + if (r >= 0) return r; + }); + + if (left.sv_entnum < right.sv_entnum) + return true; + + return false; +} + +void Scoreboard_UpdatePlayerPos(entity player) +{ + entity ent; + for(ent = player.sort_next; ent && Scoreboard_ComparePlayerScores(player, ent); ent = player.sort_next) + { + SORT_SWAP(player, ent); + } + for(ent = player.sort_prev; ent != players && Scoreboard_ComparePlayerScores(ent, player); ent = player.sort_prev) + { + SORT_SWAP(ent, player); + } +} + +float Scoreboard_CompareTeamScores(entity left, entity right) +{ + int i, r; + + if(left.team == NUM_SPECTATOR) + return 1; + if(right.team == NUM_SPECTATOR) + return 0; + + r = Scoreboard_CompareScore(left.teamscores(ts_primary), right.teamscores(ts_primary), teamscores_flags(ts_primary)); + if (r >= 0) + return r; + + r = Scoreboard_CompareScore(left.teamscores(ts_secondary), right.teamscores(ts_secondary), teamscores_flags(ts_secondary)); + if (r >= 0) + return r; + + for(i = 0; i < MAX_TEAMSCORE; ++i) + { + r = Scoreboard_CompareScore(left.teamscores(i), right.teamscores(i), teamscores_flags(i)); + if (r >= 0) + return r; + } + + if (left.team < right.team) + return true; + + return false; +} + +void Scoreboard_UpdateTeamPos(entity Team) +{ + entity ent; + for(ent = Team.sort_next; ent && Scoreboard_CompareTeamScores(Team, ent); ent = Team.sort_next) + { + SORT_SWAP(Team, ent); + } + for(ent = Team.sort_prev; ent != teams && Scoreboard_CompareTeamScores(ent, Team); ent = Team.sort_prev) + { + SORT_SWAP(ent, Team); + } +} + +void Cmd_Scoreboard_Help() +{ + LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n")); + LOG_INFO(_("^3|---------------------------------------------------------------|\n")); + LOG_INFO(_("Usage:\n")); + LOG_INFO(_("^2scoreboard_columns_set default\n")); + LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n")); + LOG_INFO(_("The following field names are recognized (case insensitive):\n")); + LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n")); + LOG_INFO("\n"); + + LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n")); + LOG_INFO(_("^3ping^7 Ping time\n")); + LOG_INFO(_("^3pl^7 Packet loss\n")); + LOG_INFO(_("^3elo^7 Player ELO\n")); + LOG_INFO(_("^3kills^7 Number of kills\n")); + LOG_INFO(_("^3deaths^7 Number of deaths\n")); + LOG_INFO(_("^3suicides^7 Number of suicides\n")); + LOG_INFO(_("^3frags^7 kills - suicides\n")); + LOG_INFO(_("^3kd^7 The kill-death ratio\n")); + LOG_INFO(_("^3dmg^7 The total damage done\n")); + LOG_INFO(_("^3dmgtaken^7 The total damage taken\n")); + LOG_INFO(_("^3sum^7 frags - deaths\n")); + LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n")); + LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n")); + LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n")); + LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n")); + LOG_INFO(_("^3returns^7 Number of flag returns\n")); + LOG_INFO(_("^3drops^7 Number of flag drops\n")); + LOG_INFO(_("^3lives^7 Number of lives (LMS)\n")); + LOG_INFO(_("^3rank^7 Player rank\n")); + LOG_INFO(_("^3pushes^7 Number of players pushed into void\n")); + LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n")); + LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n")); + LOG_INFO(_("^3losses^7 Number of times a key was lost\n")); + LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n")); + LOG_INFO(_("^3time^7 Total time raced (race/cts)\n")); + LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n")); + LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n")); + LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n")); + LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n")); + LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n")); + LOG_INFO(_("^3score^7 Total score\n")); + LOG_INFO("\n"); + + 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")); + + LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n" + "include/exclude ALL teams/noteams game modes.\n\n")); + + LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\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.\n")); + LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n" + "other gamemodes except DM.\n")); +} + +// NOTE: adding a gametype with ? to not warn for an optional field +// make sure it's excluded in a previous exclusive rule, if any +// otherwise the previous exclusive rule warns anyway +// e.g. -teams,rc,cts,lms/kills ?+rc/kills +#define SCOREBOARD_DEFAULT_COLUMNS \ +"ping pl name |" \ +" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \ +" -teams,lms/deaths +ft,tdm/deaths" \ +" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \ +" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \ +" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \ +" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \ +" +lms/lives +lms/rank" \ +" +kh/caps +kh/pushes +kh/destroyed" \ +" ?+rc/laps ?+rc/time +rc,cts/fastest" \ +" +as/objectives +nb/faults +nb/goals" \ +" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \ +" -lms,rc,cts,inv,nb/score" + +void Cmd_Scoreboard_SetFields(int argc) +{ + TC(int, argc); + int i, slash; + string str, pattern; + bool have_name = false, have_primary = false, have_secondary = false, have_separator = false; + int missing; + + if(!gametype) + return; // do nothing, we don't know gametype and scores yet + + // sbt_fields uses strunzone on the titles! + if(!sbt_field_title[0]) + for(i = 0; i < MAX_SBT_FIELDS; ++i) + sbt_field_title[i] = strzone("(null)"); + + // TODO: re enable with gametype dependant cvars? + if(argc < 3) // no arguments provided + argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " "); + + if(argc < 3) + argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " "); + + if(argc == 3) + { + if(argv(2) == "default") + argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " "); + else if(argv(2) == "all") + { + string s; + s = "ping pl name |"; + FOREACH(Scores, true, { + if(it != ps_primary) + if(it != ps_secondary) + if(scores_label(it) != "") + s = strcat(s, " ", scores_label(it)); + }); + if(ps_secondary != ps_primary) + s = strcat(s, " ", scores_label(ps_secondary)); + s = strcat(s, " ", scores_label(ps_primary)); + argc = tokenizebyseparator(strcat("0 1 ", s), " "); + } + } + + + sbt_num_fields = 0; + + hud_fontsize = HUD_GetFontsize("hud_fontsize"); + + for(i = 1; i < argc - 1; ++i) + { + float nocomplain; + str = argv(i+1); + + nocomplain = false; + if(substring(str, 0, 1) == "?") + { + nocomplain = true; + str = substring(str, 1, strlen(str) - 1); + } + + slash = strstrofs(str, "/", 0); + if(slash >= 0) + { + pattern = substring(str, 0, slash); + str = substring(str, slash + 1, strlen(str) - (slash + 1)); + + if (!isGametypeInFilter(gametype, teamplay, false, pattern)) + continue; + } + + strunzone(sbt_field_title[sbt_num_fields]); + sbt_field_title[sbt_num_fields] = strzone(TranslateScoresLabel(str)); + sbt_field_size[sbt_num_fields] = stringwidth(sbt_field_title[sbt_num_fields], false, hud_fontsize); + str = strtolower(str); + + PlayerScoreField j; + switch(str) + { + case "ping": sbt_field[sbt_num_fields] = SP_PING; break; + case "pl": sbt_field[sbt_num_fields] = SP_PL; break; + case "kd": case "kdr": case "kdratio": sbt_field[sbt_num_fields] = SP_KDRATIO; break; + case "sum": case "diff": case "k-d": sbt_field[sbt_num_fields] = SP_SUM; break; + case "name": case "nick": sbt_field[sbt_num_fields] = SP_NAME; have_name = true; break; + case "|": sbt_field[sbt_num_fields] = SP_SEPARATOR; have_separator = true; break; + 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; + default: + { + FOREACH(Scores, true, { + if (str == strtolower(scores_label(it))) { + j = it; + goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code" + } + }); + +LABEL(notfound) + if(str == "frags") + j = SP_FRAGS; + else + { + if(!nocomplain) + LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str); + continue; + } +LABEL(found) + sbt_field[sbt_num_fields] = j; + if(j == ps_primary) + have_primary = true; + if(j == ps_secondary) + have_secondary = true; + + } + } + ++sbt_num_fields; + if(sbt_num_fields >= MAX_SBT_FIELDS) + break; + } + + if(scores_flags(ps_primary) & SFL_ALLOW_HIDE) + have_primary = true; + if(scores_flags(ps_secondary) & SFL_ALLOW_HIDE) + have_secondary = true; + if(ps_primary == ps_secondary) + have_secondary = true; + missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name); + + if(sbt_num_fields + missing < MAX_SBT_FIELDS) + { + if(!have_name) + { + strunzone(sbt_field_title[sbt_num_fields]); + for(i = sbt_num_fields; i > 0; --i) + { + sbt_field_title[i] = sbt_field_title[i-1]; + sbt_field_size[i] = sbt_field_size[i-1]; + sbt_field[i] = sbt_field[i-1]; + } + sbt_field_title[0] = strzone(TranslateScoresLabel("name")); + sbt_field[0] = SP_NAME; + ++sbt_num_fields; + LOG_INFO("fixed missing field 'name'\n"); + + if(!have_separator) + { + strunzone(sbt_field_title[sbt_num_fields]); + for(i = sbt_num_fields; i > 1; --i) + { + sbt_field_title[i] = sbt_field_title[i-1]; + sbt_field_size[i] = sbt_field_size[i-1]; + sbt_field[i] = sbt_field[i-1]; + } + sbt_field_title[1] = strzone("|"); + sbt_field[1] = SP_SEPARATOR; + sbt_field_size[1] = stringwidth("|", false, hud_fontsize); + ++sbt_num_fields; + LOG_INFO("fixed missing field '|'\n"); + } + } + else if(!have_separator) + { + strunzone(sbt_field_title[sbt_num_fields]); + sbt_field_title[sbt_num_fields] = strzone("|"); + sbt_field_size[sbt_num_fields] = stringwidth("|", false, hud_fontsize); + sbt_field[sbt_num_fields] = SP_SEPARATOR; + ++sbt_num_fields; + LOG_INFO("fixed missing field '|'\n"); + } + if(!have_secondary) + { + strunzone(sbt_field_title[sbt_num_fields]); + sbt_field_title[sbt_num_fields] = strzone(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; + LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_secondary)); + } + if(!have_primary) + { + strunzone(sbt_field_title[sbt_num_fields]); + sbt_field_title[sbt_num_fields] = strzone(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; + LOG_INFOF("fixed missing field '%s'\n", scores_label(ps_primary)); + } + } + + sbt_field[sbt_num_fields] = SP_END; +} + +// MOVEUP:: +vector sbt_field_rgb; +string sbt_field_icon0; +string sbt_field_icon1; +string sbt_field_icon2; +vector sbt_field_icon0_rgb; +vector sbt_field_icon1_rgb; +vector sbt_field_icon2_rgb; +string Scoreboard_GetField(entity pl, PlayerScoreField field) +{ + float tmp, num, denom; + int f; + string str; + sbt_field_rgb = '1 1 1'; + sbt_field_icon0 = ""; + sbt_field_icon1 = ""; + sbt_field_icon2 = ""; + sbt_field_icon0_rgb = '1 1 1'; + sbt_field_icon1_rgb = '1 1 1'; + sbt_field_icon2_rgb = '1 1 1'; + switch(field) + { + case SP_PING: + if (!pl.gotscores) + return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 + //str = getplayerkeyvalue(pl.sv_entnum, "ping"); + f = pl.ping; + if(f == 0) + return _("N/A"); + tmp = max(0, min(220, f-80)) / 220; + sbt_field_rgb = '1 1 1' - '0 1 1' * tmp; + return ftos(f); + + case SP_PL: + if (!pl.gotscores) + return _("N/A"); + f = pl.ping_packetloss; + tmp = pl.ping_movementloss; + if(f == 0 && tmp == 0) + return ""; + str = ftos(ceil(f * 100)); + if(tmp != 0) + str = strcat(str, "~", ftos(ceil(tmp * 100))); + tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl + sbt_field_rgb = '1 0.5 0.5' - '0 0.5 0.5' * tmp; + return str; + + case SP_NAME: + if(ready_waiting && pl.ready) + { + sbt_field_icon0 = "gfx/scoreboard/player_ready"; + } + else if(!teamplay) + { + f = entcs_GetClientColors(pl.sv_entnum); + { + sbt_field_icon0 = "gfx/scoreboard/playercolor_base"; + sbt_field_icon1 = "gfx/scoreboard/playercolor_shirt"; + sbt_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0); + sbt_field_icon2 = "gfx/scoreboard/playercolor_pants"; + sbt_field_icon2_rgb = colormapPaletteColor(f % 16, 1); + } + } + return entcs_GetName(pl.sv_entnum); + + case SP_FRAGS: + f = pl.(scores(SP_KILLS)); + f -= pl.(scores(SP_SUICIDES)); + return ftos(f); + + case SP_KDRATIO: + num = pl.(scores(SP_KILLS)); + denom = pl.(scores(SP_DEATHS)); + + if(denom == 0) { + sbt_field_rgb = '0 1 0'; + str = sprintf("%d", num); + } else if(num <= 0) { + sbt_field_rgb = '1 0 0'; + str = sprintf("%.1f", num/denom); + } else + str = sprintf("%.1f", num/denom); + return str; + + case SP_SUM: + f = pl.(scores(SP_KILLS)); + f -= pl.(scores(SP_DEATHS)); + + if(f > 0) { + sbt_field_rgb = '0 1 0'; + } else if(f == 0) { + sbt_field_rgb = '1 1 1'; + } else { + sbt_field_rgb = '1 0 0'; + } + return ftos(f); + + case SP_ELO: + { + float elo = pl.(scores(SP_ELO)); + switch (elo) { + case -1: return "..."; + case -2: return _("N/A"); + default: return ftos(elo); + } + } + + case SP_DMG: case SP_DMGTAKEN: + return sprintf("%.1f k", pl.(scores(field)) / 1000); + + default: case SP_SCORE: + tmp = pl.(scores(field)); + f = scores_flags(field); + if(field == ps_primary) + sbt_field_rgb = '1 1 0'; + else if(field == ps_secondary) + sbt_field_rgb = '0 1 1'; + else + sbt_field_rgb = '1 1 1'; + return ScoreString(f, tmp); + } + //return "error"; +} + +float sbt_fixcolumnwidth_len; +float sbt_fixcolumnwidth_iconlen; +float sbt_fixcolumnwidth_marginlen; + +string Scoreboard_FixColumnWidth(int i, string str) +{ + TC(int, i); + float f; + vector sz; + + sbt_fixcolumnwidth_iconlen = 0; + + if(sbt_field_icon0 != "") + { + sz = draw_getimagesize(sbt_field_icon0); + f = sz.x / sz.y; + if(sbt_fixcolumnwidth_iconlen < f) + sbt_fixcolumnwidth_iconlen = f; + } + + if(sbt_field_icon1 != "") + { + sz = draw_getimagesize(sbt_field_icon1); + f = sz.x / sz.y; + if(sbt_fixcolumnwidth_iconlen < f) + sbt_fixcolumnwidth_iconlen = f; + } + + if(sbt_field_icon2 != "") + { + sz = draw_getimagesize(sbt_field_icon2); + f = sz.x / sz.y; + if(sbt_fixcolumnwidth_iconlen < f) + sbt_fixcolumnwidth_iconlen = f; + } + + sbt_fixcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect + + if(sbt_fixcolumnwidth_iconlen != 0) + sbt_fixcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize); + else + sbt_fixcolumnwidth_marginlen = 0; + + if(sbt_field[i] == SP_NAME) // name gets all remaining space + { + int j; + float remaining_space = 0; + for(j = 0; j < sbt_num_fields; ++j) + if(j != i) + if (sbt_field[i] != SP_SEPARATOR) + remaining_space += sbt_field_size[j] + hud_fontsize.x; + sbt_field_size[i] = panel_size.x - remaining_space; + + if (sbt_fixcolumnwidth_iconlen != 0) + remaining_space += sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x; + float namesize = panel_size.x - remaining_space; + str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors); + sbt_fixcolumnwidth_len = stringwidth(str, true, hud_fontsize); + + max_namesize = vid_conwidth - remaining_space; + } + else + sbt_fixcolumnwidth_len = stringwidth(str, false, hud_fontsize); + + f = sbt_fixcolumnwidth_len + sbt_fixcolumnwidth_marginlen + sbt_fixcolumnwidth_iconlen * hud_fontsize.x; + if(sbt_field_size[i] < f) + sbt_field_size[i] = f; + + return str; +} + +void Scoreboard_initFieldSizes() +{ + for(int i = 0; i < sbt_num_fields; ++i) + { + sbt_field_size[i] = stringwidth(sbt_field_title[i], false, hud_fontsize); + Scoreboard_FixColumnWidth(i, ""); + } +} + +vector Scoreboard_DrawHeader(vector pos, vector rgb, bool other_players) +{ + int i; + vector column_dim = eY * panel_size.y; + if(other_players) + column_dim.y -= 1.25 * hud_fontsize.y; + vector text_offset = eY * (1.25 - 1) / 2 * hud_fontsize.y; + pos.x += hud_fontsize.x * 0.5; + for(i = 0; i < sbt_num_fields; ++i) + { + if(sbt_field[i] == SP_SEPARATOR) + break; + column_dim.x = sbt_field_size[i] + hud_fontsize.x; + if (sbt_highlight) + if (i % 2) + drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL); + pos.x += column_dim.x; + } + if(sbt_field[i] == SP_SEPARATOR) + { + pos.x = panel_pos.x + panel_size.x - hud_fontsize.x * 0.5; + for(i = sbt_num_fields - 1; i > 0; --i) + { + if(sbt_field[i] == SP_SEPARATOR) + break; + + pos.x -= sbt_field_size[i]; + + if (sbt_highlight) + if (!(i % 2)) + { + column_dim.x = sbt_field_size[i] + hud_fontsize.x; + drawfill(pos - eX * hud_fontsize.x * 0.5, column_dim, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + } + + text_offset.x = sbt_field_size[i] - stringwidth(sbt_field_title[i], false, hud_fontsize); + drawstring(pos + text_offset, sbt_field_title[i], hud_fontsize, rgb * 1.5, sbt_fg_alpha, DRAWFLAG_NORMAL); + pos.x -= hud_fontsize.x; + } + } + + pos.x = panel_pos.x; + pos.y += 1.25 * hud_fontsize.y; + return pos; +} + +void Scoreboard_DrawItem(vector item_pos, vector rgb, entity pl, bool is_self, int pl_number) +{ + TC(bool, is_self); TC(int, pl_number); + string str; + bool is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR); + + vector h_pos = item_pos; + vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25; + // alternated rows highlighting + if(is_self) + drawfill(h_pos, h_size, rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL); + else if((sbt_highlight) && (!(pl_number % 2))) + drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); + + float fg_alpha = (is_self ? sbt_fg_alpha_self : sbt_fg_alpha); + + vector pos = item_pos; + pos.x += hud_fontsize.x * 0.5; + pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically + vector tmp = '0 0 0'; + int i; + PlayerScoreField field; + for(i = 0; i < sbt_num_fields; ++i) + { + field = sbt_field[i]; + if(field == SP_SEPARATOR) + break; + + if(is_spec && field != SP_NAME && field != SP_PING) { + pos.x += sbt_field_size[i] + hud_fontsize.x; + continue; + } + str = Scoreboard_GetField(pl, field); + str = Scoreboard_FixColumnWidth(i, str); + + pos.x += sbt_field_size[i] + hud_fontsize.x; + + if(field == SP_NAME) { + tmp.x = sbt_field_size[i] - hud_fontsize.x * sbt_fixcolumnwidth_iconlen - sbt_fixcolumnwidth_marginlen + hud_fontsize.x; + drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL); + } else { + tmp.x = sbt_fixcolumnwidth_len + hud_fontsize.x; + drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL); + } + + tmp.x = sbt_field_size[i] + hud_fontsize.x; + if(sbt_field_icon0 != "") + drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL); + if(sbt_field_icon1 != "") + drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL); + if(sbt_field_icon2 != "") + drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL); + } + + if(sbt_field[i] == SP_SEPARATOR) + { + pos.x = item_pos.x + panel_size.x - hud_fontsize.x * 0.5; + for(i = sbt_num_fields-1; i > 0; --i) + { + field = sbt_field[i]; + if(field == SP_SEPARATOR) + break; + + if(is_spec && field != SP_NAME && field != SP_PING) { + pos.x -= sbt_field_size[i] + hud_fontsize.x; + continue; + } + + str = Scoreboard_GetField(pl, field); + str = Scoreboard_FixColumnWidth(i, str); + + if(field == SP_NAME) { + tmp.x = sbt_fixcolumnwidth_len; // left or right aligned? let's put it right... + drawcolorcodedstring(pos - tmp, str, hud_fontsize, fg_alpha, DRAWFLAG_NORMAL); + } else { + tmp.x = sbt_fixcolumnwidth_len; + drawstring(pos - tmp, str, hud_fontsize, sbt_field_rgb, fg_alpha, DRAWFLAG_NORMAL); + } + + tmp.x = sbt_field_size[i]; + if(sbt_field_icon0 != "") + drawpic(pos - tmp, sbt_field_icon0, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL); + if(sbt_field_icon1 != "") + drawpic(pos - tmp, sbt_field_icon1, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon1_rgb, fg_alpha, DRAWFLAG_NORMAL); + if(sbt_field_icon2 != "") + drawpic(pos - tmp, sbt_field_icon2, eY * hud_fontsize.y + eX * hud_fontsize.x * sbt_fixcolumnwidth_iconlen, sbt_field_icon2_rgb, fg_alpha, DRAWFLAG_NORMAL); + pos.x -= sbt_field_size[i] + hud_fontsize.x; + } + } + + if(pl.eliminated) + drawfill(h_pos, h_size, '0 0 0', 0.5 * panel_fg_alpha, DRAWFLAG_NORMAL); +} + +vector Scoreboard_DrawOthers(vector item_pos, vector rgb, int this_team, entity ignored_pl, entity pl, int pl_number) +{ + int i = 0; + vector h_pos = item_pos; + vector h_size = eX * panel_size.x + eY * hud_fontsize.y * 1.25; + + bool complete = (this_team == NUM_SPECTATOR); + + if(!complete) + if((sbt_highlight) && (!(pl_number % 2))) + drawfill(h_pos, h_size, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); + + vector pos = item_pos; + pos.x += hud_fontsize.x * 0.5; + pos.y += (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically + + float width_limit = item_pos.x + panel_size.x - hud_fontsize.x; + if(!complete) + width_limit -= stringwidth("...", false, hud_fontsize); + float namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x; + static float max_name_width = 0; + string field = ""; + float fieldsize = 0; + float min_fieldsize = 0; + float fieldpadding = hud_fontsize.x * 0.25; + if(this_team == NUM_SPECTATOR) + { + if(autocvar_hud_panel_scoreboard_spectators_showping) + min_fieldsize = stringwidth("999", false, hud_fontsize); + } + else if(autocvar_hud_panel_scoreboard_others_showscore) + min_fieldsize = stringwidth("99", false, hud_fontsize); + for(i = 0; pl; pl = pl.sort_next) + { + if(pl.team != this_team) + continue; + if(pl == ignored_pl) + continue; + + field = ""; + if(this_team == NUM_SPECTATOR) + { + if(autocvar_hud_panel_scoreboard_spectators_showping) + field = Scoreboard_GetField(pl, SP_PING); + } + else if(autocvar_hud_panel_scoreboard_others_showscore) + field = Scoreboard_GetField(pl, SP_SCORE); + + string str = textShortenToWidth(entcs_GetName(pl.sv_entnum), namesize, hud_fontsize, stringwidth_colors); + float column_width = stringwidth(str, true, hud_fontsize); + if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned) + { + if(column_width > max_name_width) + max_name_width = column_width; + column_width = max_name_width; + } + if(field != "") + { + fieldsize = stringwidth(field, false, hud_fontsize); + column_width += hud_fontsize.x * 0.25 + max(fieldsize, min_fieldsize) + 2 * fieldpadding; + } + + if(pos.x + column_width > width_limit) + { + ++i; + if(!complete) + { + drawstring(pos, "...", hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL); + break; + } + else + { + pos.x = item_pos.x + hud_fontsize.x * 0.5; + pos.y += hud_fontsize.y * 1.25; + } + } + + vector name_pos = pos; + if((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned) + name_pos.x += max(fieldsize, min_fieldsize) + 2 * fieldpadding + hud_fontsize.x * 0.25; + drawcolorcodedstring(name_pos, str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL); + if(field != "") + { + h_size.x = max(fieldsize, min_fieldsize) + 2 * fieldpadding; + h_size.y = hud_fontsize.y; + vector field_pos = pos; + if(!((this_team == NUM_SPECTATOR) && autocvar_hud_panel_scoreboard_spectators_aligned)) + field_pos.x += column_width - h_size.x; + if(sbt_highlight) + drawfill(field_pos, h_size, '1 1 1', sbt_highlight_alpha, DRAWFLAG_NORMAL); + 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); + } + pos.x += column_width; + pos.x += hud_fontsize.x; + } + return eX * item_pos.x + eY * (item_pos.y + i * hud_fontsize.y * 1.25); +} + +vector Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size) +{ + int max_players = 999; + if(autocvar_hud_panel_scoreboard_maxheight > 0) + { + float height = autocvar_hud_panel_scoreboard_maxheight * vid_conheight; + if(teamplay) + { + height -= (panel_bg_padding * 2 + hud_fontsize.y * 1.25) * team_count; // - padding and header + height -= hud_fontsize.y * (team_count - 1); // - spacing between tables + height /= team_count; + } + else + height -= panel_bg_padding * 2; // - padding + max_players = floor(height / (hud_fontsize.y * 1.25)); + if(max_players <= 1) + max_players = 1; + if(max_players == tm.team_size) + max_players = 999; + } + + entity pl; + entity me = playerslots[current_player]; + panel_pos = pos; + panel_size.y = 1.25 * hud_fontsize.y * (1 + bound(1, tm.team_size, max_players)); + panel_size.y += panel_bg_padding * 2; + HUD_Panel_DrawBg(); + + vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y); + if(panel.current_panel_bg != "0") + end_pos.y += panel_bg_border * 2; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + pos = panel_pos; + vector tmp = eX * panel_size.x + eY * 1.25 * hud_fontsize.y; + + // rounded header + if (sbt_bg_alpha) + drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', sbt_bg_alpha, DRAWFLAG_NORMAL); + + pos.y += 1.25 * hud_fontsize.y; + + // table background + tmp.y = panel_size.y - 1.25 * hud_fontsize.y; + if (sbt_bg_alpha) + drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL); + + + // print header row and highlight columns + pos = Scoreboard_DrawHeader(panel_pos, rgb, (max_players < tm.team_size)); + + // fill the table and draw the rows + bool is_self = false; + bool self_shown = false; + int i = 0; + for(pl = players.sort_next; pl; pl = pl.sort_next) + { + if(pl.team != tm.team) + continue; + if(i == max_players - 2 && pl != me) + { + if(!self_shown && me.team == tm.team) + { + Scoreboard_DrawItem(pos, rgb, me, true, i); + self_shown = true; + pos.y += 1.25 * hud_fontsize.y; + ++i; + } + } + if(i >= max_players - 1) + { + pos = Scoreboard_DrawOthers(pos, rgb, tm.team, (self_shown ? me : NULL), pl, i); + break; + } + is_self = (pl.sv_entnum == current_player); + Scoreboard_DrawItem(pos, rgb, pl, is_self, i); + if(is_self) + self_shown = true; + pos.y += 1.25 * hud_fontsize.y; + ++i; + } + + panel_size.x += panel_bg_padding * 2; // restore initial width + return end_pos; +} + +bool Scoreboard_WouldDraw() +{ + if (MUTATOR_CALLHOOK(DrawScoreboard)) + return false; + else if (QuickMenu_IsOpened()) + return false; + else if (HUD_Radar_Clickable()) + return false; + else if (scoreboard_showscores) + return true; + else if (intermission == 1) + 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) + return true; + else if (scoreboard_showscores_force) + return true; + return false; +} + +float average_accuracy; +vector Scoreboard_AccuracyStats_Draw(vector pos, vector rgb, vector bg_size) +{ + WepSet weapons_stat = WepSet_GetFromStat(); + WepSet weapons_inmap = WepSet_GetFromStat_InMap(); + int disownedcnt = 0; + int nHidden = 0; + FOREACH(Weapons, it != WEP_Null, { + int weapon_stats = weapon_accuracy[i - WEP_FIRST]; + + WepSet set = it.m_wepset; + if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set))) + { + if (((it.spawnflags & WEP_FLAG_HIDDEN) || (it.spawnflags & WEP_FLAG_MUTATORBLOCKED))) + ++nHidden; + else + ++disownedcnt; + } + }); + + int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt - nHidden; + if (weapon_cnt <= 0) return pos; + + int rows = 1; + if (autocvar_hud_panel_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - nHidden - 1) * 0.5)) + rows = 2; + int columnns = ceil(weapon_cnt / rows); + + 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); + pos.y += 1.25 * hud_fontsize.y; + if(panel.current_panel_bg != "0") + pos.y += panel_bg_border; + + panel_pos = pos; + panel_size.y = height * rows; + panel_size.y += panel_bg_padding * 2; + HUD_Panel_DrawBg(); + + vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y); + if(panel.current_panel_bg != "0") + end_pos.y += panel_bg_border * 2; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + pos = panel_pos; + vector tmp = panel_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); + + if(sbt_highlight) + { + // column highlighting + for (int i = 0; i < columnns; ++i) + if ((i % 2) == 0) + drawfill(pos + eX * weapon_width * rows * i, eY * height * rows + eX * weapon_width * rows, '0 0 0', sbt_highlight_alpha, DRAWFLAG_NORMAL); + + // row highlighting + for (int i = 0; i < rows; ++i) + drawfill(pos + eY * weapon_height + eY * height * i, eX * tmp.x + eY * hud_fontsize.y, rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); + } + + average_accuracy = 0; + int weapons_with_stats = 0; + if (rows == 2) + pos.x += weapon_width / 2; + + if (autocvar_hud_panel_scoreboard_accuracy_nocolors) + rgb = '1 1 1'; + else + Accuracy_LoadColors(); + + float oldposx = pos.x; + vector tmpos = pos; + + int column = 0; + FOREACH(Weapons, it != WEP_Null, { + int weapon_stats = weapon_accuracy[i - WEP_FIRST]; + + WepSet set = it.m_wepset; + if (weapon_stats < 0 && !((weapons_stat & set) || (weapons_inmap & set))) + continue; + + float weapon_alpha; + if (weapon_stats >= 0) + weapon_alpha = sbt_fg_alpha; + else + weapon_alpha = 0.2 * sbt_fg_alpha; + + // weapon icon + drawpic_aspect_skin(tmpos, it.model2, eX * weapon_width + eY * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); + // the accuracy + if (weapon_stats >= 0) { + weapons_with_stats += 1; + average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy + + string s; + s = sprintf("%d%%", weapon_stats * 100); + + float padding; + padding = (weapon_width - stringwidth(s, false, hud_fontsize)) / 2; // center the accuracy value + + if(!autocvar_hud_panel_scoreboard_accuracy_nocolors) + rgb = Accuracy_GetColor(weapon_stats); + + drawstring(tmpos + eX * padding + eY * weapon_height, s, hud_fontsize, rgb, sbt_fg_alpha, DRAWFLAG_NORMAL); + } + tmpos.x += weapon_width * rows; + pos.x += weapon_width * rows; + if (rows == 2 && column == columnns - 1) { + tmpos.x = oldposx; + tmpos.y += height; + pos.y += height; + } + ++column; + }); + + if (weapons_with_stats) + average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5); + + panel_size.x += panel_bg_padding * 2; // restore initial width + return end_pos; +} + +vector MapStats_DrawKeyValue(vector pos, string key, string value) { + float px = pos.x; + pos.x += hud_fontsize.x * 0.25; + drawstring(pos, key, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL); + pos.x = panel_pos.x + panel_size.x - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25; + drawstring(pos, value, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL); + pos.x = px; + pos.y += hud_fontsize.y; + + return pos; +} + +vector Scoreboard_MapStats_Draw(vector pos, vector rgb, vector bg_size) { + float stat_secrets_found, stat_secrets_total; + float stat_monsters_killed, stat_monsters_total; + float rows = 0; + string val; + + // get monster stats + stat_monsters_killed = STAT(MONSTERS_KILLED); + stat_monsters_total = STAT(MONSTERS_TOTAL); + + // get secrets stats + stat_secrets_found = STAT(SECRETS_FOUND); + stat_secrets_total = STAT(SECRETS_TOTAL); + + // get number of rows + if(stat_secrets_total) + rows += 1; + if(stat_monsters_total) + rows += 1; + + // if no rows, return + if (!rows) + return pos; + + // draw table header + drawstring(pos + eX * panel_bg_padding, _("Map stats:"), 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; + + panel_pos = pos; + panel_size.y = hud_fontsize.y * rows; + panel_size.y += panel_bg_padding * 2; + HUD_Panel_DrawBg(); + + vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y); + if(panel.current_panel_bg != "0") + end_pos.y += panel_bg_border * 2; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + pos = panel_pos; + vector tmp = panel_size; + + if (sbt_bg_alpha) + drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL); + + // draw monsters + if(stat_monsters_total) + { + val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total); + pos = MapStats_DrawKeyValue(pos, _("Monsters killed:"), val); + } + + // draw secrets + if(stat_secrets_total) + { + val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total); + pos = MapStats_DrawKeyValue(pos, _("Secrets found:"), val); + } + + panel_size.x += panel_bg_padding * 2; // restore initial width + return end_pos; +} + + +vector Scoreboard_Rankings_Draw(vector pos, entity pl, vector rgb, vector bg_size) +{ + int i; + RANKINGS_RECEIVED_CNT = 0; + for (i=RANKINGS_CNT-1; i>=0; --i) + if (grecordtime[i]) + ++RANKINGS_RECEIVED_CNT; + + if (RANKINGS_RECEIVED_CNT == 0) + return pos; + + vector hl_rgb = rgb + '0.5 0.5 0.5'; + + pos.y += hud_fontsize.y; + drawstring(pos + eX * panel_bg_padding, _("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; + + panel_pos = pos; + + float namesize = 0; + for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i) + { + float f = stringwidth(grecordholder[i], true, hud_fontsize); + if(f > namesize) + namesize = f; + } + bool cut = false; + if(namesize > autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x) + { + namesize = autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x; + cut = true; + } + + float ranksize = 3 * hud_fontsize.x; + float timesize = 5 * hud_fontsize.x; + vector columnsize = eX * (ranksize + timesize + namesize + hud_fontsize.x) + eY * 1.25 * hud_fontsize.y; + int columns = max(1, floor((panel_size.x - 2 * panel_bg_padding) / columnsize.x)); + columns = min(columns, RANKINGS_RECEIVED_CNT); + + // expand name column to fill the entire row + float available_space = (panel_size.x - 2 * panel_bg_padding - columnsize.x * columns) / columns; + namesize += available_space; + columnsize.x += available_space; + + panel_size.y = ceil(RANKINGS_RECEIVED_CNT / columns) * 1.25 * hud_fontsize.y; + panel_size.y += panel_bg_padding * 2; + + HUD_Panel_DrawBg(); + + vector end_pos = panel_pos + eY * (panel_size.y + hud_fontsize.y); + if(panel.current_panel_bg != "0") + end_pos.y += panel_bg_border * 2; + + if(panel_bg_padding) + { + panel_pos += '1 1 0' * panel_bg_padding; + panel_size -= '2 2 0' * panel_bg_padding; + } + + pos = panel_pos; + + if (sbt_bg_alpha) + drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, panel_size, rgb, sbt_bg_alpha, DRAWFLAG_NORMAL); + + vector text_ofs = eX * 0.5 * hud_fontsize.x + eY * (1.25 - 1) / 2 * hud_fontsize.y; // center text vertically + string str = ""; + int column = 0, j = 0; + for(i = 0; i < RANKINGS_RECEIVED_CNT; ++i) + { + float t; + t = grecordtime[i]; + if (t == 0) + continue; + + if(grecordholder[i] == entcs_GetName(player_localnum)) + drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha_self, DRAWFLAG_NORMAL); + else if(!((j + column) & 1) && sbt_highlight) + drawfill(pos, columnsize, hl_rgb, sbt_highlight_alpha, DRAWFLAG_NORMAL); + + str = count_ordinal(i+1); + drawstring(pos + text_ofs, str, hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL); + drawstring(pos + text_ofs + eX * ranksize, TIME_ENCODED_TOSTRING(t), hud_fontsize, '1 1 1', sbt_fg_alpha, DRAWFLAG_NORMAL); + str = grecordholder[i]; + if(cut) + str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors); + drawcolorcodedstring(pos + text_ofs + eX * (ranksize + timesize), str, hud_fontsize, sbt_fg_alpha, DRAWFLAG_NORMAL); + + pos.y += 1.25 * hud_fontsize.y; + j++; + if(j >= ceil(RANKINGS_RECEIVED_CNT / columns)) + { + column++; + j = 0; + pos.x += panel_size.x / columns; + pos.y = panel_pos.y; + } + } + + panel_size.x += panel_bg_padding * 2; // restore initial width + return end_pos; +} + +void Scoreboard_Draw() +{ + if(!autocvar__hud_configure) + { + if(!hud_draw_maximized) return; + + // frametime checks allow to toggle the scoreboard even when the game is paused + if(scoreboard_active) { + if(hud_configure_menu_open == 1) + scoreboard_fade_alpha = 1; + float scoreboard_fadeinspeed = autocvar_hud_panel_scoreboard_fadeinspeed; + if (scoreboard_fadeinspeed && frametime) + scoreboard_fade_alpha = min(1, scoreboard_fade_alpha + frametime * scoreboard_fadeinspeed); + else + scoreboard_fade_alpha = 1; + if(hud_fontsize_str != autocvar_hud_fontsize) + { + hud_fontsize = HUD_GetFontsize("hud_fontsize"); + Scoreboard_initFieldSizes(); + if(hud_fontsize_str) + strunzone(hud_fontsize_str); + hud_fontsize_str = strzone(autocvar_hud_fontsize); + } + } + else { + float scoreboard_fadeoutspeed = autocvar_hud_panel_scoreboard_fadeoutspeed; + if (scoreboard_fadeoutspeed && frametime) + scoreboard_fade_alpha = max(0, scoreboard_fade_alpha - frametime * scoreboard_fadeoutspeed); + else + scoreboard_fade_alpha = 0; + } + + if (!scoreboard_fade_alpha) + return; + } + else + scoreboard_fade_alpha = 0; + + if (autocvar_hud_panel_scoreboard_dynamichud) + HUD_Scale_Enable(); + else + HUD_Scale_Disable(); + + if(scoreboard_fade_alpha <= 0) + return; + panel_fade_alpha *= scoreboard_fade_alpha; + HUD_Panel_LoadCvars(); + + sbt_bg_alpha = autocvar_hud_panel_scoreboard_table_bg_alpha * panel_fg_alpha; + sbt_highlight = autocvar_hud_panel_scoreboard_table_highlight; + sbt_highlight_alpha = autocvar_hud_panel_scoreboard_table_highlight_alpha * panel_fg_alpha; + sbt_highlight_alpha_self = autocvar_hud_panel_scoreboard_table_highlight_alpha_self * panel_fg_alpha; + sbt_fg_alpha = autocvar_hud_panel_scoreboard_table_fg_alpha * panel_fg_alpha; + sbt_fg_alpha_self = autocvar_hud_panel_scoreboard_table_fg_alpha_self * panel_fg_alpha; + + // don't overlap with con_notify + if(!autocvar__hud_configure) + panel_pos.y = max((autocvar_con_notify * autocvar_con_notifysize), panel_pos.y); + + float excess = max(0, max_namesize - autocvar_hud_panel_scoreboard_namesize * hud_fontsize.x); + float fixed_scoreboard_width = bound(vid_conwidth * autocvar_hud_panel_scoreboard_minwidth, vid_conwidth - excess, vid_conwidth * 0.93); + panel_pos.x = 0.5 * (vid_conwidth - fixed_scoreboard_width); + panel_size.x = fixed_scoreboard_width; + + Scoreboard_UpdatePlayerTeams(); + + vector pos = panel_pos; + entity pl, tm; + string str; + + // Heading + vector sb_heading_fontsize; + sb_heading_fontsize = hud_fontsize * 2; + draw_beginBoldFont(); + drawstring(pos + eX * panel_bg_padding, _("Scoreboard"), sb_heading_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); + + pos.y += sb_heading_fontsize.y; + if(panel.current_panel_bg != "0") + pos.y += panel_bg_border; + + // Draw the scoreboard + float scale = autocvar_hud_panel_scoreboard_table_bg_scale; + if(scale <= 0) + scale = 0.25; + vector bg_size = draw_getimagesize("gfx/scoreboard/scoreboard_bg") * scale; + + if(teamplay) + { + vector panel_bg_color_save = panel_bg_color; + vector team_score_baseoffset = eY * hud_fontsize.y - eX * hud_fontsize.x * 0.5; + if(panel.current_panel_bg != "0") + team_score_baseoffset.x -= panel_bg_border; + for(tm = teams.sort_next; tm; tm = tm.sort_next) + { + if(tm.team == NUM_SPECTATOR) + continue; + if(!tm.team) + continue; + + draw_beginBoldFont(); + vector rgb = Team_ColorRGB(tm.team); + str = ftos(tm.(teamscores(ts_primary))); + drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, panel_fg_alpha, DRAWFLAG_NORMAL); + + if(ts_primary != ts_secondary) + { + str = ftos(tm.(teamscores(ts_secondary))); + drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL); + } + draw_endBoldFont(); + if(autocvar_hud_panel_scoreboard_bg_teams_color_team > 0) + panel_bg_color = rgb * autocvar_hud_panel_scoreboard_bg_teams_color_team; + else if(panel_bg_color_team > 0) + panel_bg_color = rgb * panel_bg_color_team; + else + panel_bg_color = rgb; + pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size); + } + panel_bg_color = panel_bg_color_save; + } + else + { + for(tm = teams.sort_next; tm; tm = tm.sort_next) + if(tm.team != NUM_SPECTATOR) + break; + // display it anyway + pos = Scoreboard_MakeTable(pos, tm, panel_bg_color, bg_size); + } + + if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) { + if(race_speedaward) { + drawcolorcodedstring(pos, sprintf(_("Speed award: %d%s ^7(%s^7)"), race_speedaward, race_speedaward_unit, race_speedaward_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + pos.y += 1.25 * hud_fontsize.y; + } + if(race_speedaward_alltimebest) { + drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d%s ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_unit, race_speedaward_alltimebest_holder), hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + pos.y += 1.25 * hud_fontsize.y; + } + pos = Scoreboard_Rankings_Draw(pos, playerslots[player_localnum], panel_bg_color, bg_size); + } + else if (autocvar_hud_panel_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) + pos = Scoreboard_AccuracyStats_Draw(pos, panel_bg_color, bg_size); + + pos = Scoreboard_MapStats_Draw(pos, panel_bg_color, bg_size); + + // List spectators + for(pl = players.sort_next; pl; pl = pl.sort_next) + { + if(pl.team == NUM_SPECTATOR) + { + for(tm = teams.sort_next; tm; tm = tm.sort_next) + if(tm.team == NUM_SPECTATOR) + break; + str = sprintf("%s (%d)", _("Spectators"), tm.team_size); + draw_beginBoldFont(); + drawstring(pos, str, hud_fontsize, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + draw_endBoldFont(); + pos.y += 1.25 * hud_fontsize.y; + + pos = Scoreboard_DrawOthers(pos, '0 0 0', pl.team, NULL, pl, 0); + pos.y += 1.25 * hud_fontsize.y; + + break; + } + } + + // Print info string + float tl, fl, ll; + str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname); + tl = STAT(TIMELIMIT); + fl = STAT(FRAGLIMIT); + ll = STAT(LEADLIMIT); + if(gametype == MAPINFO_TYPE_LMS) + { + if(tl > 0) + str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); + } + else + { + if(tl > 0) + str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); + if(fl > 0) + { + if(tl > 0) + str = strcat(str, _(" or")); + if(teamplay) + { + str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), fl), + (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) : + (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) : + TranslateScoresLabel(teamscores_label(ts_primary)))); + } + else + { + str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags(ps_primary), fl), + (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) : + (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) : + TranslateScoresLabel(scores_label(ps_primary)))); + } + } + if(ll > 0) + { + if(tl > 0 || fl > 0) + str = strcat(str, _(" or")); + if(teamplay) + { + str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags(ts_primary), ll), + (teamscores_label(ts_primary) == "score") ? CTX(_("SCO^points")) : + (teamscores_label(ts_primary) == "fastest") ? CTX(_("SCO^is beaten")) : + TranslateScoresLabel(teamscores_label(ts_primary)))); + } + else + { + str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags(ps_primary), ll), + (scores_label(ps_primary) == "score") ? CTX(_("SCO^points")) : + (scores_label(ps_primary) == "fastest") ? CTX(_("SCO^is beaten")) : + TranslateScoresLabel(scores_label(ps_primary)))); + } + } + } + + pos.y += 1.2 * hud_fontsize.y; + drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + + // print information about respawn status + float respawn_time = STAT(RESPAWN_TIME); + if(!intermission) + if(respawn_time) + { + if(respawn_time < 0) + { + // a negative number means we are awaiting respawn, time value is still the same + respawn_time *= -1; // remove mark now that we checked it + + if(respawn_time < time) // it happens for a few frames when server is respawning the player + str = ""; // draw an empty string to not change suddenly scoreboard_bottom + else + str = sprintf(_("^1Respawning in ^3%s^1..."), + (autocvar_hud_panel_scoreboard_respawntime_decimals ? + count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals) + : + count_seconds(ceil(respawn_time - time)) + ) + ); + } + else if(time < respawn_time) + { + str = sprintf(_("You are dead, wait ^3%s^7 before respawning"), + (autocvar_hud_panel_scoreboard_respawntime_decimals ? + count_seconds_decs(respawn_time - time, autocvar_hud_panel_scoreboard_respawntime_decimals) + : + count_seconds(ceil(respawn_time - time)) + ) + ); + } + else if(time >= respawn_time) + str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump")); + + pos.y += 1.2 * hud_fontsize.y; + drawcolorcodedstring(pos + '0.5 0 0' * (panel_size.x - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); + } + + scoreboard_bottom = pos.y + 2 * hud_fontsize.y; +} diff --git a/qcsrc/client/hud/panel/scoreboard.qh b/qcsrc/client/hud/panel/scoreboard.qh new file mode 100644 index 0000000000..a560b74c74 --- /dev/null +++ b/qcsrc/client/hud/panel/scoreboard.qh @@ -0,0 +1,13 @@ +#pragma once +#include "../panel.qh" + +bool scoreboard_active; +float scoreboard_fade_alpha; + +void Cmd_Scoreboard_SetFields(float argc); +void Scoreboard_Draw(); +void Scoreboard_InitScores(); +void Scoreboard_UpdatePlayerTeams(); +void Scoreboard_UpdatePlayerPos(entity pl); +void Scoreboard_UpdateTeamPos(entity Team); +bool Scoreboard_WouldDraw(); diff --git a/qcsrc/client/hud/panel/timer.qc b/qcsrc/client/hud/panel/timer.qc index 5a7194a458..45f46b4087 100644 --- a/qcsrc/client/hud/panel/timer.qc +++ b/qcsrc/client/hud/panel/timer.qc @@ -1,4 +1,7 @@ #include "timer.qh" + +// Timer (#5) + void HUD_Timer() { if(!autocvar__hud_configure) @@ -6,7 +9,7 @@ void HUD_Timer() if(!autocvar_hud_panel_timer) return; } - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); draw_beginBoldFont(); @@ -18,7 +21,7 @@ void HUD_Timer() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { pos += '1 1 0' * panel_bg_padding; @@ -26,7 +29,7 @@ void HUD_Timer() } string timer; - float timelimit, elapsedTime, timeleft, minutesLeft; + float timelimit, timeleft, minutesLeft; timelimit = STAT(TIMELIMIT); @@ -47,21 +50,20 @@ void HUD_Timer() } vector timer_color; - if(minutesLeft >= 5 || warmup_stage || timelimit == 0) //don't use red or yellow in warmup or when there is no timelimit + if(intermission_time || minutesLeft >= 5 || warmup_stage || timelimit == 0) timer_color = '1 1 1'; //white else if(minutesLeft >= 1) timer_color = '1 1 0'; //yellow else timer_color = '1 0 0'; //red - if (autocvar_hud_panel_timer_increment || (!warmup_stage && timelimit == 0) || (warmup_stage && warmup_timeleft <= 0)) { - if (time < STAT(GAMESTARTTIME)) { - //while restart is still active, show 00:00 - timer = seconds_tostring(0); - } else { - elapsedTime = floor(time - STAT(GAMESTARTTIME)); - timer = seconds_tostring(elapsedTime); - } + if (intermission_time) { + timer = seconds_tostring(max(0, floor(intermission_time - STAT(GAMESTARTTIME)))); + } 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 + else + timer = seconds_tostring(floor(time - STAT(GAMESTARTTIME))); } else { if(warmup_stage) timer = seconds_tostring(warmup_timeleft); diff --git a/qcsrc/client/hud/panel/vote.qc b/qcsrc/client/hud/panel/vote.qc index 89c784a108..1c2ea03bc8 100644 --- a/qcsrc/client/hud/panel/vote.qc +++ b/qcsrc/client/hud/panel/vote.qc @@ -2,11 +2,20 @@ #include -/** Vote window (#9) */ +// Vote (#9) + void HUD_Vote() { if(autocvar_cl_allow_uid2name == -1 && (gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_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 + if(!autocvar__menu_alpha) + uid2name_dialog = 0; + + if (!uid2name_dialog) + localcmd("menu_cmd directmenu Uid2Name\n"); + vote_active = 1; if (autocvar__hud_configure) { @@ -24,14 +33,12 @@ void HUD_Vote() if(!autocvar__hud_configure) { if(!autocvar_hud_panel_vote) return; - - panel_fg_alpha = autocvar_hud_panel_fg_alpha; - panel_bg_alpha_str = autocvar_hud_panel_vote_bg_alpha; - - if(panel_bg_alpha_str == "") { - panel_bg_alpha_str = ftos(autocvar_hud_panel_bg_alpha); - } - panel_bg_alpha = stof(panel_bg_alpha_str); + /* + if(cvar("hud_panel_vote_test")) { + if(vote_called_vote) strunzone(vote_called_vote); vote_called_vote = strzone("^1test the vote panel"); + vote_active = true; vote_yescount = 3; vote_nocount = 2; vote_needed = 4; + } else vote_active = false; + */ } else { @@ -52,10 +59,18 @@ void HUD_Vote() else vote_alpha = bound(0, 1 - (time - vote_change) * 2, 1); - if(!vote_alpha) + a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1); + if(a <= 0) return; - - HUD_Panel_UpdateCvars(); + //panel_fade_alpha *= a; + // nothing can hide this panel, not even the menu + float hud_fade_alpha_save = hud_fade_alpha; + if(uid2name_dialog && autocvar__menu_alpha) + hud_fade_alpha = 0; + else + hud_fade_alpha = a; + HUD_Panel_LoadCvars(); + hud_fade_alpha = hud_fade_alpha_save; if(uid2name_dialog) { @@ -63,18 +78,15 @@ void HUD_Vote() panel_size = eX * 0.4 * vid_conwidth + eY * 0.3 * vid_conheight; } - // these must be below above block vector pos, mySize; pos = panel_pos; mySize = panel_size; - a = vote_alpha * (vote_highlighted ? autocvar_hud_panel_vote_alreadyvoted_alpha : 1); if (autocvar_hud_panel_vote_dynamichud) HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(a); - a = panel_fg_alpha * a; + HUD_Panel_DrawBg(); if(panel_bg_padding) { @@ -103,42 +115,44 @@ void HUD_Vote() s = _("A vote has been called for:"); if(uid2name_dialog) s = _("Allow servers to store and display your name?"); - drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawstring_aspect(pos, s, eX * mySize.x + eY * (2/8) * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); s = textShortenToWidth(vote_called_vote, mySize.x, '1 1 0' * mySize.y * (1/8), stringwidth_colors); if(autocvar__hud_configure) s = _("^1Configure the HUD"); - drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, a, DRAWFLAG_NORMAL); + drawcolorcodedstring_aspect(pos + eY * (2/8) * mySize.y, s, eX * mySize.x + eY * (1.75/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); // print the yes/no counts - s = sprintf(_("Yes (%s): %d"), getcommandkey("vyes", "vyes"), vote_yescount); - drawstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '0 1 0', a, DRAWFLAG_NORMAL); - s = sprintf(_("No (%s): %d"), getcommandkey("vno", "vno"), vote_nocount); - drawstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, '1 0 0', a, DRAWFLAG_NORMAL); + s = sprintf("^2%s ^7(%d)", getcommandkey_forcename(_("Yes"), "vyes"), vote_yescount); + drawcolorcodedstring_aspect(pos + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); + s = sprintf("^1%s ^7(%d)", getcommandkey_forcename(_("No"), "vno"), vote_nocount); + drawcolorcodedstring_aspect(pos + eX * 0.5 * mySize.x + eY * (4/8) * mySize.y, s, eX * 0.5 * mySize.x + eY * (1.5/8) * mySize.y, panel_fg_alpha, DRAWFLAG_NORMAL); + pos.y += (5/8) * mySize.y; + vector tmp_size = eX * mySize.x + eY * (3/8) * mySize.y; // draw the progress bar backgrounds - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_back", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawpic_skin(pos, "voteprogress_back", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); // draw the highlights if(vote_highlighted == 1) { drawsetcliparea(pos.x, pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawpic_skin(pos, "voteprogress_voted", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); } else if(vote_highlighted == -1) { drawsetcliparea(pos.x + 0.5 * mySize.x, pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_voted", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawpic_skin(pos, "voteprogress_voted", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); } // draw the progress bars if(vote_yescount && vote_needed) { drawsetcliparea(pos.x, pos.y, mySize.x * 0.5 * (vote_yescount/vote_needed), mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawpic_skin(pos, "voteprogress_prog", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); } if(vote_nocount && vote_needed) { drawsetcliparea(pos.x + mySize.x - mySize.x * 0.5 * (vote_nocount/vote_needed), pos.y, mySize.x * 0.5, mySize.y); - drawpic_skin(pos + eY * (5/8) * mySize.y, "voteprogress_prog", eX * mySize.x + eY * (3/8) * mySize.y, '1 1 1', a, DRAWFLAG_NORMAL); + drawpic_skin(pos, "voteprogress_prog", tmp_size, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); } drawresetcliparea(); diff --git a/qcsrc/client/hud/panel/weapons.qc b/qcsrc/client/hud/panel/weapons.qc index 984cb4f505..db25d532a0 100644 --- a/qcsrc/client/hud/panel/weapons.qc +++ b/qcsrc/client/hud/panel/weapons.qc @@ -1,5 +1,7 @@ #include "weapons.qh" -// Weapon icons (#0) + + +// Weapons (#0) entity weaponorder[Weapons_MAX]; void weaponorder_swap(int i, int j, entity pass) @@ -23,7 +25,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_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; \ @@ -56,6 +58,8 @@ void HUD_Weapons() float when = max(1, autocvar_hud_panel_weapons_complainbubble_time); float fadetime = max(0, autocvar_hud_panel_weapons_complainbubble_fadetime); + bool infinite_ammo = (STAT(ITEMS) & IT_UNLIMITED_WEAPON_AMMO); + vector weapon_pos, weapon_size = '0 0 0'; vector color; @@ -68,6 +72,8 @@ void HUD_Weapons() { if((!autocvar_hud_panel_weapons) || (spectatee_status == -1)) return; + if(STAT(HEALTH) <= 0 && autocvar_hud_panel_weapons_hide_ondeath) + return; if(timeout && time >= weapontime + timeout + timeout_effect_length) if(autocvar_hud_panel_weapons_timeout_effect == 3 || (autocvar_hud_panel_weapons_timeout_effect == 1 && !(autocvar_hud_panel_weapons_timeout_fadebgmin + autocvar_hud_panel_weapons_timeout_fadefgmin))) { @@ -77,7 +83,7 @@ void HUD_Weapons() } // update generic hud functions - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); // figure out weapon order (how the weapons are sorted) // TODO make this configurable if(weaponorder_bypriority != autocvar_cl_weaponpriority || !weaponorder[0]) @@ -147,8 +153,8 @@ void HUD_Weapons() { if(autocvar__hud_configure) { - if(menu_enabled != 2) - HUD_Panel_DrawBg(1); // also draw the bg of the entire panel + if(hud_configure_menu_open != 2) + HUD_Panel_DrawBg(); // also draw the bg of the entire panel } // do we own this weapon? @@ -310,7 +316,7 @@ void HUD_Weapons() HUD_Scale_Enable(); else HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(center.x == -1) return; // panel has gone off screen @@ -330,7 +336,7 @@ void HUD_Weapons() } // calculate position/size for visual bar displaying ammount of ammo status - if (autocvar_hud_panel_weapons_ammo) + if (!infinite_ammo && autocvar_hud_panel_weapons_ammo) { ammo_color = stov(autocvar_hud_panel_weapons_ammo_color); ammo_alpha = panel_fg_alpha * autocvar_hud_panel_weapons_ammo_alpha; @@ -356,7 +362,6 @@ void HUD_Weapons() vector label_size = '1 1 0' * min(weapon_size.x, weapon_size.y) * bound(0, autocvar_hud_panel_weapons_label_scale, 1); vector noncurrent_size = weapon_size * bound(0.01, autocvar_hud_panel_weapons_noncurrent_scale, 1); float noncurrent_alpha = panel_fg_alpha * bound(0, autocvar_hud_panel_weapons_noncurrent_alpha, 1); - bool isCurrent; static vector weapon_pos_current = '-1 0 0'; if(weapon_pos_current.x == -1) weapon_pos_current = panel_pos; @@ -368,7 +373,9 @@ void HUD_Weapons() switch_speed = frametime * autocvar_hud_panel_weapons_selection_speed; vector radius_size = weapon_size * (autocvar_hud_panel_weapons_selection_radius + 1); - if(!panel_switchweapon) + if(switchweapon == WEP_Null) + panel_switchweapon = NULL; + else if(!panel_switchweapon) panel_switchweapon = switchweapon; // draw background behind currently selected weapon @@ -401,8 +408,7 @@ void HUD_Weapons() weapon_pos = (panel_pos + eX * column * weapon_size.x + eY * row * weapon_size.y); // update position of the currently selected weapon - isCurrent = (it == panel_switchweapon); - if(isCurrent) + if(it == panel_switchweapon) { if(weapon_pos_current.y > weapon_pos.y) weapon_pos_current.y = max(weapon_pos.y, weapon_pos_current.y - switch_speed * (weapon_pos_current.y - weapon_pos.y)); @@ -466,7 +472,7 @@ void HUD_Weapons() } // draw ammo status bar - if(autocvar_hud_panel_weapons_ammo && (it.ammo_field != ammo_none)) + if(!infinite_ammo && autocvar_hud_panel_weapons_ammo && (it.ammo_field != ammo_none)) { float ammo_full; a = getstati(GetAmmoStat(it.ammo_field)); // how much ammo do we have? diff --git a/qcsrc/client/main.qc b/qcsrc/client/main.qc index c62a72e6e9..36ca217240 100644 --- a/qcsrc/client/main.qc +++ b/qcsrc/client/main.qc @@ -1,20 +1,21 @@ #include "main.qh" #include -#include "hud/all.qh" +#include "hud/_mod.qh" #include "mapvoting.qh" #include "mutators/events.qh" +#include "hud/panel/scoreboard.qh" #include "hud/panel/quickmenu.qh" -#include "scoreboard.qh" #include "shownames.qh" #include #include "wall.qh" #include "weapons/projectile.qh" #include -#include +#include #include #include #include +#include #include #include #include @@ -108,7 +109,6 @@ void CSQC_Init() binddb = db_create(); tempdb = db_create(); ClientProgsDB = db_load("client.db"); - compressShortVector_init(); draw_endBoldFont(); @@ -131,13 +131,7 @@ void CSQC_Init() registercvar("cl_spawn_near_teammate", "1"); - gametype = 0; - - // hud_fields uses strunzone on the titles! - for(int i = 0; i < MAX_HUD_FIELDS; ++i) - hud_title[i] = strzone("(null)"); - - Cmd_HUD_SetFields(0); + gametype = NULL; postinit = false; @@ -185,8 +179,8 @@ void Shutdown() { WarpZone_Shutdown(); - remove(teams); - remove(players); + delete(teams); + delete(players); db_close(binddb); db_close(tempdb); if(autocvar_cl_db_saveasdump) @@ -235,7 +229,7 @@ float SetTeam(entity o, int Team) default: if(GetTeam(Team, false) == NULL) { - LOG_TRACEF("trying to switch to unsupported team %d\n", Team); + LOG_TRACEF("trying to switch to unsupported team %d", Team); Team = NUM_SPECTATOR; } break; @@ -251,7 +245,7 @@ float SetTeam(entity o, int Team) default: if(GetTeam(Team, false) == NULL) { - LOG_TRACEF("trying to switch to unsupported team %d\n", Team); + LOG_TRACEF("trying to switch to unsupported team %d", Team); Team = NUM_SPECTATOR; } break; @@ -323,9 +317,9 @@ void Playerchecker_Think(entity this) e.ping_movementloss = 0; //e.gotscores = 0; // we might already have the scores... int t = entcs_GetScoreTeam(i); - if (t) SetTeam(e, t); // will not hurt; later updates come with HUD_UpdatePlayerTeams + if (t) SetTeam(e, t); // will not hurt; later updates come with Scoreboard_UpdatePlayerTeams RegisterPlayer(e); - HUD_UpdatePlayerPos(e); + Scoreboard_UpdatePlayerPos(e); } } } @@ -385,23 +379,21 @@ void Ent_RemovePlayerScore(entity this) if(this.owner) { SetTeam(this.owner, -1); this.owner.gotscores = 0; - for(int i = 0; i < MAX_SCORE; ++i) { - this.owner.(scores[i]) = 0; // clear all scores - } + FOREACH(Scores, true, { + this.owner.(scores(it)) = 0; // clear all scores + }); } } NET_HANDLE(ENT_CLIENT_SCORES, bool isnew) { make_pure(this); - int i, n; - bool isNew; entity o; // damnit -.- don't want to go change every single .sv_entnum in hud.qc AGAIN // (no I've never heard of M-x replace-string, sed, or anything like that) - isNew = !this.owner; // workaround for DP bug - n = ReadByte()-1; + bool isNew = !this.owner; // workaround for DP bug + int n = ReadByte()-1; #ifdef DP_CSQC_ENTITY_REMOVE_IS_B0RKED if(!isNew && n != this.sv_entnum) @@ -429,27 +421,23 @@ NET_HANDLE(ENT_CLIENT_SCORES, bool isnew) //playerchecker will do this for us later, if it has not already done so int sf, lf; -#if MAX_SCORE <= 8 - sf = ReadByte(); - lf = ReadByte(); -#else sf = ReadShort(); lf = ReadShort(); -#endif - int p; - for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) - if(sf & p) + FOREACH(Scores, true, { + int p = 1 << (i % 16); + if (sf & p) { - if(lf & p) - o.(scores[i]) = ReadInt24_t(); + if (lf & p) + o.(scores(it)) = ReadInt24_t(); else - o.(scores[i]) = ReadChar(); + o.(scores(it)) = ReadChar(); } + }); return = true; if(o.sort_prev) - HUD_UpdatePlayerPos(o); // if not registered, we cannot do this yet! + Scoreboard_UpdatePlayerPos(o); // if not registered, we cannot do this yet! this.entremove = Ent_RemovePlayerScore; } @@ -476,14 +464,14 @@ NET_HANDLE(ENT_CLIENT_TEAMSCORES, bool isnew) if(sf & p) { if(lf & p) - o.(teamscores[i]) = ReadInt24_t(); + o.(teamscores(i)) = ReadInt24_t(); else - o.(teamscores[i]) = ReadChar(); + o.(teamscores(i)) = ReadChar(); } return = true; - HUD_UpdateTeamPos(o); + Scoreboard_UpdateTeamPos(o); } NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew) @@ -516,6 +504,22 @@ NET_HANDLE(ENT_CLIENT_CLIENTDATA, bool isnew) else angles_held_status = 0; + if(f & 16) + { + num_spectators = ReadByte(); + + float i, slot; + + for(i = 0; i < MAX_SPECTATORS; ++i) + spectatorlist[i] = 0; // reset list first + + for(i = 0; i < num_spectators; ++i) + { + slot = ReadByte(); + spectatorlist[i] = slot - 1; + } + } + return = true; if(newspectatee_status != spectatee_status) @@ -684,6 +688,8 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new) spn_origin.y = ReadCoord(); spn_origin.z = ReadCoord(); + this.team = (teamnum + 1); + //if(is_new) //{ this.origin = spn_origin; @@ -697,12 +703,13 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new) precache_model(this.mdl); setmodel(this, this.mdl); this.drawmask = MASK_NORMAL; - //this.movetype = MOVETYPE_NOCLIP; + //this.move_movetype = MOVETYPE_NOCLIP; //this.draw = Spawn_Draw; + IL_PUSH(g_drawables, this); }*/ if(autocvar_cl_spawn_point_particles) { - if((serverflags & SERVERFLAG_TEAMPLAY)) + if(teamplay) { switch(teamnum) { @@ -716,6 +723,7 @@ NET_HANDLE(ENT_CLIENT_SPAWNPOINT, bool is_new) else { this.cnt = particleeffectnum(EFFECT_SPAWNPOINT_NEUTRAL); } this.draw = Spawn_Draw; + if (is_new) IL_PUSH(g_drawables, this); setpredraw(this, Spawn_PreDraw); this.fade_start = autocvar_cl_spawn_point_dist_min; this.fade_end = autocvar_cl_spawn_point_dist_max; @@ -780,8 +788,8 @@ NET_HANDLE(ENT_CLIENT_SPAWNEVENT, bool is_new) // CSQC_Ent_Update : Called every frame that the server has indicated an update to the SSQC / CSQC entity has occured. // The only parameter reflects if the entity is "new" to the client, meaning it just came into the client's PVS. -void CSQC_Ent_Update(bool isnew) -{ENGINE_EVENT(); +void CSQC_Ent_Update(entity this, bool isnew) +{ this.sourceLoc = __FILE__ ":" STR(__LINE__); int t = ReadByte(); @@ -825,12 +833,13 @@ void CSQC_Ent_Update(bool isnew) if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t); done = it.m_read(this, NULL, isnew); + MUTATOR_CALLHOOK(Ent_Update, this, isnew); break; }); time = savetime; if (!done) { - LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)\n", isnew, savetime, this, this.entnum, this.enttype, this.classname, t); + LOG_FATALF("CSQC_Ent_Update(%d) at %f with this=%i {.entnum=%d, .enttype=%d} t=%s (%d)", isnew, savetime, this, this.entnum, this.enttype, this.classname, t); } } @@ -860,16 +869,16 @@ void Ent_Remove(entity this) // TODO possibly set more stuff to defaults } // CSQC_Ent_Remove : Called when the server requests a SSQC / CSQC entity to be removed. Essentially call remove(this) as well. -void CSQC_Ent_Remove() -{ENGINE_EVENT(); +void CSQC_Ent_Remove(entity this) +{ if (autocvar_developer_csqcentities) LOG_INFOF("CSQC_Ent_Remove() with this=%i {.entnum=%d, .enttype=%d}\n", this, this.entnum, this.enttype); if (wasfreed(this)) { - LOG_WARNING("CSQC_Ent_Remove called for already removed entity. Packet loss?\n"); + LOG_WARN("CSQC_Ent_Remove called for already removed entity. Packet loss?"); return; } if (this.enttype) Ent_Remove(this); - remove(this); + delete(this); } void Gamemode_Init() @@ -935,22 +944,22 @@ void Gamemode_Init(); NET_HANDLE(ENT_CLIENT_SCORES_INFO, bool isnew) { make_pure(this); - gametype = ReadInt24_t(); + gametype = ReadRegistered(Gametypes); + teamplay = _MapInfo_GetTeamPlayBool(gametype); HUD_ModIcons_SetFunc(); - for (int i = 0; i < MAX_SCORE; ++i) - { - if (scores_label[i]) strunzone(scores_label[i]); - scores_label[i] = strzone(ReadString()); - scores_flags[i] = ReadByte(); - } + FOREACH(Scores, true, { + if (scores_label(it)) strunzone(scores_label(it)); + scores_label(it) = strzone(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()); - teamscores_flags[i] = ReadByte(); + if (teamscores_label(i)) strunzone(teamscores_label(i)); + teamscores_label(i) = strzone(ReadString()); + teamscores_flags(i) = ReadByte(); } return = true; - HUD_InitScores(); + Scoreboard_InitScores(); Gamemode_Init(); } @@ -983,6 +992,42 @@ NET_HANDLE(ENT_CLIENT_INIT, bool isnew) if (!postinit) PostInit(); } +float GetSpeedUnitFactor(int speed_unit) +{ + switch(speed_unit) + { + default: + case 1: + return 1.0; + case 2: + return 0.0254; + case 3: + return 0.0254 * 3.6; + case 4: + return 0.0254 * 3.6 * 0.6213711922; + case 5: + return 0.0254 * 1.943844492; // 1 m/s = 1.943844492 knots, because 1 knot = 1.852 km/h + } +} + +string GetSpeedUnit(int speed_unit) +{ + switch(speed_unit) + { + default: + case 1: + return _(" qu/s"); + case 2: + return _(" m/s"); + case 3: + return _(" km/h"); + case 4: + return _(" mph"); + case 5: + return _(" knots"); + } +} + NET_HANDLE(TE_CSQC_RACE, bool isNew) { int b = ReadByte(); @@ -1071,16 +1116,22 @@ NET_HANDLE(TE_CSQC_RACE, bool isNew) race_server_record = ReadInt24_t(); break; case RACE_NET_SPEED_AWARD: - race_speedaward = ReadInt24_t(); + 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)); break; case RACE_NET_SPEED_AWARD_BEST: - race_speedaward_alltimebest = ReadInt24_t(); + 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)); break; case RACE_NET_SERVER_RANKINGS: float prevpos, del; @@ -1175,13 +1226,13 @@ NET_HANDLE(TE_CSQC_WEAPONCOMPLAIN, bool isNew) } } -string getcommandkey(string text, string command) +string _getcommandkey(string cmd_name, string command, bool forcename) { string keys; float n, j, k, l = 0; if (!autocvar_hud_showbinds) - return text; + return cmd_name; keys = db_get(binddb, command); if (keys == "") @@ -1215,12 +1266,12 @@ string getcommandkey(string text, string command) if (keys == "NO_KEY") { if (autocvar_hud_showbinds > 1) - return sprintf(_("%s (not bound)"), text); + return sprintf(_("%s (not bound)"), cmd_name); else - return text; + return cmd_name; } - else if (autocvar_hud_showbinds > 1) - return sprintf("%s (%s)", text, keys); + else if (autocvar_hud_showbinds > 1 || forcename) + return sprintf("%s (%s)", cmd_name, keys); else return keys; } diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index 8601d26b58..54ed9e2c39 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -1,13 +1,7 @@ #pragma once #include -#include - -// -------------------------------------------------------------------------- -// MENU Functionality - -// -------------------------------------------------------------------------- -// Onslaught +#include // Map coordinate base calculations need these vector mi_center; @@ -15,18 +9,8 @@ vector mi_scale; // Minimap string minimapname; -// -------------------------------------------------------------------------- -// General stuff - float postinit; -float gametype; - -//float sorted_players; -//float sorted_teams; - -// Defs -//.float ctf_state; -//.float health; +entity gametype; float FONT_USER = 8; @@ -37,45 +21,11 @@ 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(); -// -------------------------------------------------------------------------- -// Scoreboard stuff - -const int MAX_HUD_FIELDS = 16; - -const int SP_END = -1; - -const int SP_PING = -2; -const int SP_NAME = -3; -const int SP_KDRATIO = -4; -const int SP_CLRATIO = -5; -const int SP_PL = -6; -const int SP_FRAGS = -7; -const int SP_SUM = -8; - -const int SP_SEPARATOR = -100; - -float hud_field[MAX_HUD_FIELDS + 1]; -float hud_size[MAX_HUD_FIELDS + 1]; -string hud_title[MAX_HUD_FIELDS + 1]; -int hud_num_fields; - -string scores_label[MAX_SCORE]; -int scores_flags[MAX_SCORE]; -string teamscores_label[MAX_SCORE]; -int teamscores_flags[MAX_SCORE]; -.int scores[MAX_SCORE]; -.float teamscores[MAX_TEAMSCORE]; - -#define IS_INCREASING(x) ( (x)&SFL_LOWER_IS_BETTER ) -#define IS_DECREASING(x) ( !((x)&SFL_LOWER_IS_BETTER) ) - - vector hud_fontsize; float RANKINGS_RECEIVED_CNT; string grecordholder[RANKINGS_CNT]; float grecordtime[RANKINGS_CNT]; -//float csqc_flags; entity playerslots[255]; // 255 is engine limit on maxclients entity teamslots[17]; // 17 teams (including "spectator team") @@ -85,11 +35,20 @@ entity teamslots[17]; // 17 teams (including "spectator team") .float eliminated; .void(entity) draw; +IntrusiveList g_drawables; +STATIC_INIT(g_drawables) { g_drawables = IL_NEW(); } .void(entity) draw2d; +IntrusiveList g_drawables_2d; +STATIC_INIT(g_drawables_2d) { g_drawables_2d = IL_NEW(); } .void(entity) entremove; float drawframetime; vector view_origin, view_forward, view_right, view_up; +IntrusiveList g_radarlinks; +STATIC_INIT(g_radarlinks) { g_radarlinks = IL_NEW(); } +IntrusiveList g_radaricons; +STATIC_INIT(g_radaricons) { g_radaricons = IL_NEW(); } + bool button_zoom; bool spectatorbutton_zoom; bool button_attack2; @@ -103,7 +62,9 @@ float warmup_stage; void Fog_Force(); -string getcommandkey(string text, string command); +string _getcommandkey(string text, string command, bool forcename); +#define getcommandkey(cmd_name, command) _getcommandkey(cmd_name, command, false) +#define getcommandkey_forcename(cmd_name, command) _getcommandkey(cmd_name, command, true) string vote_called_vote; float ready_waiting; @@ -138,7 +99,15 @@ const int HOOK_END = 2; float g_trueaim_minrange; -float hud; +int hud; float view_quality; + +int num_spectators; +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 bfc25e99c1..7b07b68005 100644 --- a/qcsrc/client/mapvoting.qc +++ b/qcsrc/client/mapvoting.qc @@ -1,7 +1,7 @@ #include "mapvoting.qh" -#include "hud/all.qh" -#include "scoreboard.qh" +#include "hud/_mod.qh" +#include "hud/panel/scoreboard.qh" #include @@ -24,7 +24,6 @@ float mv_timeout; float mv_top2_time; float mv_top2_alpha; -vector mv_mousepos; int mv_selection; int mv_columns; int mv_mouse_selection; @@ -84,14 +83,15 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g alpha = mv_top2_alpha; // Fade away if not one of the top 2 choice else alpha = 1; // Normal, full alpha + alpha *= panel_fg_alpha; // Bounding box details float rect_margin = hud_fontsize.y / 2; - pos.x += rect_margin + autocvar_scoreboard_border_thickness; - pos.y += rect_margin + autocvar_scoreboard_border_thickness; - maxh -= 2 * (rect_margin + autocvar_scoreboard_border_thickness); - tsize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness); + pos.x += rect_margin + autocvar_hud_panel_mapvote_highlight_border; + pos.y += rect_margin + autocvar_hud_panel_mapvote_highlight_border; + maxh -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border); + tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border); vector rect_pos = pos - '0.5 0.5 0' * rect_margin; vector rect_size = '1 1 0'; @@ -101,15 +101,15 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g // Highlight selected item if(id == mv_selection && (mv_flags[id] & GTV_AVAILABLE)) { - drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL); + drawfill(rect_pos, rect_size, '1 1 1', 0.1 * panel_fg_alpha, DRAWFLAG_NORMAL); } // Highlight current vote vector rgb = MapVote_RGB(id); if(id == mv_ownvote) { - drawfill(rect_pos, rect_size, rgb, 0.1*alpha, DRAWFLAG_NORMAL); - drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL); + drawfill(rect_pos, rect_size, rgb, 0.1 * alpha, DRAWFLAG_NORMAL); + drawborderlines(autocvar_hud_panel_mapvote_highlight_border, rect_pos, rect_size, rgb, alpha, DRAWFLAG_NORMAL); } vector offset = pos; @@ -177,11 +177,11 @@ void GameTypeVote_DrawGameTypeItem(vector pos, float maxh, float tsize, string g drawstring(last.origin+offset, last.message, gtv_text_size_small, '1 1 1', alpha, DRAWFLAG_NORMAL); next = last; last = last.chain; - remove(next); + delete(next); } // Cleanup - remove(title); + delete(title); } void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, string pic, float _count, int id) @@ -193,10 +193,10 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin float rect_margin = hud_fontsize.y / 2; - pos.x += rect_margin + autocvar_scoreboard_border_thickness; - pos.y += rect_margin + autocvar_scoreboard_border_thickness; - isize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness); - tsize -= 2 * (rect_margin + autocvar_scoreboard_border_thickness); + pos.x += rect_margin + autocvar_hud_panel_mapvote_highlight_border; + pos.y += rect_margin + autocvar_hud_panel_mapvote_highlight_border; + isize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border); + tsize -= 2 * (rect_margin + autocvar_hud_panel_mapvote_highlight_border); vector rect_pos = pos - '0.5 0.5 0' * rect_margin; vector rect_size = '1 1 0'; @@ -230,17 +230,18 @@ void MapVote_DrawMapItem(vector pos, float isize, float tsize, string map, strin theAlpha = mv_top2_alpha; else theAlpha = 1; + theAlpha *= panel_fg_alpha; // Highlight selected item if(id == mv_selection && (mv_flags[id] & GTV_AVAILABLE)) - drawfill(rect_pos, rect_size, '1 1 1', 0.1, DRAWFLAG_NORMAL); + drawfill(rect_pos, rect_size, '1 1 1', 0.1 * panel_fg_alpha, DRAWFLAG_NORMAL); // Highlight current vote vector rgb = MapVote_RGB(id); if(id == mv_ownvote) { - drawfill(rect_pos, rect_size, rgb, 0.1*theAlpha, DRAWFLAG_NORMAL); - drawborderlines(autocvar_scoreboard_border_thickness, rect_pos, rect_size, rgb, theAlpha, DRAWFLAG_NORMAL); + drawfill(rect_pos, rect_size, rgb, 0.1 * theAlpha, DRAWFLAG_NORMAL); + drawborderlines(autocvar_hud_panel_mapvote_highlight_border, rect_pos, rect_size, rgb, theAlpha, DRAWFLAG_NORMAL); } drawstring(text_pos, label, hud_fontsize, rgb, theAlpha, DRAWFLAG_NORMAL); @@ -272,7 +273,7 @@ void MapVote_DrawAbstain(vector pos, float isize, float tsize, float _count, int text_size = stringwidth(label, false, hud_fontsize); pos.x -= text_size*0.5; - drawstring(pos, label, hud_fontsize, rgb, 1, DRAWFLAG_NORMAL); + drawstring(pos, label, hud_fontsize, rgb, panel_fg_alpha, DRAWFLAG_NORMAL); } vector MapVote_GridVec(vector gridspec, int i, int m) @@ -295,10 +296,10 @@ float MapVote_Selection(vector topleft, vector cellsize, float rows, float colum for (r = 0; r < rows; ++r) for (c = 0; c < columns; ++c) { - if (mv_mousepos.x >= topleft.x + cellsize.x * c && - mv_mousepos.x <= topleft.x + cellsize.x * (c + 1) && - mv_mousepos.y >= topleft.y + cellsize.y * r && - mv_mousepos.y <= topleft.y + cellsize.y * (r + 1)) + if (mousepos.x >= topleft.x + cellsize.x * c && + mousepos.x <= topleft.x + cellsize.x * (c + 1) && + mousepos.y >= topleft.y + cellsize.y * r && + mousepos.y <= topleft.y + cellsize.y * (r + 1)) { mv_mouse_selection = r * columns + c; break; @@ -332,15 +333,14 @@ void MapVote_Draw() if(!mv_active) return; + HUD_Panel_LoadCvars(); + if (!autocvar_hud_cursormode) { - vector mpos = mv_mousepos + getmousepos(); - mpos.x = bound(0, mpos.x, vid_conwidth); - mpos.y = bound(0, mpos.y, vid_conheight); - - if ( mpos.x != mv_mousepos.x || mpos.y != mv_mousepos.y ) + vector mpos = mousepos; + update_mousepos(); + if (mpos.x != mousepos.x || mpos.y != mousepos.y) mv_selection_keyboard = 0; - mv_mousepos = mpos; } center = (vid_conwidth - 1)/2; @@ -364,14 +364,14 @@ void MapVote_Draw() map = ((gametypevote) ? _("Decide the gametype") : _("Vote for a map")); pos.x = center - stringwidth(map, false, hud_fontsize * 2) * 0.5; - drawstring(pos, map, hud_fontsize * 2, '1 1 1', 1, DRAWFLAG_NORMAL); + drawstring(pos, map, hud_fontsize * 2, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += hud_fontsize.y * 2; if( mapvote_chosenmap != "" ) { pos.y += hud_fontsize.y * 0.25; pos.x = center - stringwidth(mapvote_chosenmap, false, hud_fontsize * 1.5) * 0.5; - drawstring(pos, mapvote_chosenmap, hud_fontsize * 1.5, '1 1 1', 1, DRAWFLAG_NORMAL); + drawstring(pos, mapvote_chosenmap, hud_fontsize * 1.5, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += hud_fontsize.y * 1.5; } pos.y += hud_fontsize.y * 0.5; @@ -381,12 +381,10 @@ void MapVote_Draw() i = ceil(max(0, mv_timeout - time)); map = sprintf(_("%d seconds left"), i); pos.x = center - stringwidth(map, false, hud_fontsize * 1.5) * 0.5; - drawstring(pos, map, hud_fontsize * 1.5, '0 1 0', 1, DRAWFLAG_NORMAL); + drawstring(pos, map, hud_fontsize * 1.5, '0 1 0', panel_fg_alpha, DRAWFLAG_NORMAL); pos.y += hud_fontsize.y * 1.5; pos.y += hud_fontsize.y * 0.5; - HUD_Panel_UpdateCvars(); - // base for multi-column stuff... pos.y += hud_fontsize.y; pos.x = xmin; @@ -440,7 +438,7 @@ void MapVote_Draw() panel_pos.y = pos.y; panel_size.x = xmax - xmin; panel_size.y = ymax - ymin; - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { @@ -486,7 +484,7 @@ void MapVote_Draw() MapVote_DrawAbstain(pos, dist.x, xmax - xmin, tmp, i); } - draw_cursor_normal(mv_mousepos, '1 1 1', 1 - autocvar__menu_alpha); + draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha); } void Cmd_MapVote_MapDownload(int argc) @@ -610,12 +608,7 @@ void GameTypeVote_ReadOption(int i) mv_maps[i] = gt; mv_flags[i] = ReadByte(); - string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, gt); - if(precache_pic(mv_picpath) == "") - mv_picpath = strcat("gfx/menu/default/gametype_", gt); - string pic = strzone(mv_picpath); - mv_pics[i] = pic; - mv_preview[i] = PreviewExists(pic); + string basetype = ""; if ( mv_flags[i] & GTV_CUSTOM ) { @@ -624,20 +617,38 @@ void GameTypeVote_ReadOption(int i) name = gt; mv_pk3[i] = strzone(name); mv_desc[i] = strzone(ReadString()); + basetype = strzone(ReadString()); } else { - int type = MapInfo_Type_FromString(gt); + Gametype type = MapInfo_Type_FromString(gt); mv_pk3[i] = strzone(MapInfo_Type_ToText(type)); mv_desc[i] = MapInfo_Type_Description(type); } + + string mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, gt); + if(precache_pic(mv_picpath) == "") + { + mv_picpath = strcat("gfx/menu/default/gametype_", gt); + if(precache_pic(mv_picpath) == "") + { + mv_picpath = sprintf("gfx/menu/%s/gametype_%s", autocvar_menu_skin, basetype); + if(precache_pic(mv_picpath) == "") + { + mv_picpath = strcat("gfx/menu/default/gametype_", basetype); + } + } + } + string pic = strzone(mv_picpath); + mv_pics[i] = pic; + mv_preview[i] = PreviewExists(pic); } void MapVote_Init() { mv_active = 1; - if(autocvar_hud_cursormode) { setcursormode(1); } - else { mv_mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; } + if(autocvar_hud_cursormode) setcursormode(1); + else mousepos = '0.5 0 0' * vid_conwidth + '0 0.5 0' * vid_conheight; mv_selection = -1; mv_selection_keyboard = 0; @@ -773,8 +784,8 @@ float MapVote_InputEvent(int bInputType, float nPrimary, float nSecondary) if(bInputType == 3) { - mv_mousepos.x = nPrimary; - mv_mousepos.y = nSecondary; + mousepos.x = nPrimary; + mousepos.y = nSecondary; mv_selection_keyboard = 0; return true; } diff --git a/qcsrc/client/mapvoting.qh b/qcsrc/client/mapvoting.qh index ebc107a0b2..2f95102a99 100644 --- a/qcsrc/client/mapvoting.qh +++ b/qcsrc/client/mapvoting.qh @@ -10,3 +10,4 @@ float MapVote_InputEvent(float bInputType, float nPrimary, float nSecondary); void Net_MapVote_Picture(); float mv_active; +float xmin, xmax, ymin, ymax; diff --git a/qcsrc/client/miscfunctions.qc b/qcsrc/client/miscfunctions.qc index d3b3e46d73..a0d7b8789c 100644 --- a/qcsrc/client/miscfunctions.qc +++ b/qcsrc/client/miscfunctions.qc @@ -1,8 +1,8 @@ #include "miscfunctions.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" -#include +#include #include @@ -68,11 +68,11 @@ void RemovePlayer(entity player) void MoveToLast(entity e) { AuditLists(); - other = e.sort_next; - while(other) + entity ent = e.sort_next; + while(ent) { - SORT_SWAP(other, e); - other = e.sort_next; + SORT_SWAP(ent, e); + ent = e.sort_next; } AuditLists(); } @@ -350,6 +350,13 @@ void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, f drawcolorcodedstring_expanding(pos, text, '1 1 0' * sz.y, theAlpha, drawflag, fadelerp); } +void update_mousepos() +{ + mousepos += getmousepos() * autocvar_menu_mouse_speed; + mousepos.x = bound(0, mousepos.x, vid_conwidth); + mousepos.y = bound(0, mousepos.y, vid_conheight); +} + // this draws the triangles of a model DIRECTLY. Don't expect high performance, really... float PolyDrawModelSurface(entity e, float i_s) { @@ -382,15 +389,15 @@ void PolyDrawModel(entity e) void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector rgb, float a, float drawflag) { - float x, y, q, d; + float d; vector ringsize, v, t; ringsize = radi * '1 1 0'; - x = cos(f * 2 * M_PI); - y = sin(f * 2 * M_PI); - q = fabs(x) + fabs(y); - x /= q; - y /= q; + float co = cos(f * 2 * M_PI); + float si = sin(f * 2 * M_PI); + float q = fabs(co) + fabs(si); + co /= q; + si /= q; if(f >= 1) { @@ -523,8 +530,8 @@ void DrawCircleClippedPic(vector centre, float radi, string pic, float f, vector if(d > 0) { v = centre; t = '0.5 0.5 0'; - v.x += x * 0.5 * ringsize.x; t += x * '0.5 0.5 0'; - v.y += y * 0.5 * ringsize.y; t += y * '0.5 -0.5 0'; + v.x += co * 0.5 * ringsize.x; t += co * '0.5 0.5 0'; + v.y += si * 0.5 * ringsize.y; t += si * '0.5 -0.5 0'; R_PolygonVertex(v, t, rgb, a); R_EndPolygon(); } diff --git a/qcsrc/client/miscfunctions.qh b/qcsrc/client/miscfunctions.qh index 1f8790481f..62de4565e5 100644 --- a/qcsrc/client/miscfunctions.qh +++ b/qcsrc/client/miscfunctions.qh @@ -31,7 +31,7 @@ vector HUD_GetFontsize(string cvarname); float PreviewExists(string name); -vector rotate(vector v, float a); +vector Rotate(vector v, float a); #define IS_DEAD(s) (((s).classname == "csqcmodel") ? (s).csqcmodel_isdead : ((s).health <= 0)) @@ -187,6 +187,8 @@ void drawcolorcodedstring_expanding(vector position, string text, vector theScal void drawcolorcodedstring_aspect_expanding(vector pos, string text, vector sz, float theAlpha, float drawflag, float fadelerp); +void update_mousepos(); + // this draws the triangles of a model DIRECTLY. Don't expect high performance, really... float PolyDrawModelSurface(entity e, float i_s); void PolyDrawModel(entity e); diff --git a/qcsrc/client/mutators/_mod.inc b/qcsrc/client/mutators/_mod.inc index 98fb4815c1..3dfd4f7897 100644 --- a/qcsrc/client/mutators/_mod.inc +++ b/qcsrc/client/mutators/_mod.inc @@ -1 +1,2 @@ // generated file; do not modify +#include diff --git a/qcsrc/client/mutators/_mod.qh b/qcsrc/client/mutators/_mod.qh index 98fb4815c1..b54ee489cf 100644 --- a/qcsrc/client/mutators/_mod.qh +++ b/qcsrc/client/mutators/_mod.qh @@ -1 +1,2 @@ // generated file; do not modify +#include diff --git a/qcsrc/client/mutators/events.qc b/qcsrc/client/mutators/events.qc new file mode 100644 index 0000000000..c2dbb70215 --- /dev/null +++ b/qcsrc/client/mutators/events.qc @@ -0,0 +1 @@ +#include "events.qh" diff --git a/qcsrc/client/mutators/events.qh b/qcsrc/client/mutators/events.qh index 9a2e8b1373..edd178199d 100644 --- a/qcsrc/client/mutators/events.qh +++ b/qcsrc/client/mutators/events.qh @@ -88,6 +88,15 @@ MUTATOR_HOOKABLE(GetModelParams, EV_GetModelParams); /**/ MUTATOR_HOOKABLE(WantEventchase, EV_WantEventchase); +/** allow customizing 3rd person mode effect */ +#define EV_CustomizeEventchase(i, o) \ + /** entity id */ i(entity, MUTATOR_ARGV_0_entity) \ + /* current_view_origin_override */ o(vector, MUTATOR_ARGV_0_vector) \ + /* view_offset_override */ o(vector, MUTATOR_ARGV_1_vector) \ + /* chase_distance_override */ o(float, MUTATOR_ARGV_0_float) \ + /**/ +MUTATOR_HOOKABLE(CustomizeEventchase, EV_CustomizeEventchase); + #define EV_AnnouncerOption(i, o) \ /** announcer string */ i(string, MUTATOR_ARGV_0_string) \ /** announcer string */ o(string, MUTATOR_ARGV_0_string) \ @@ -124,3 +133,34 @@ MUTATOR_HOOKABLE(Weapon_ImpactEffect, EV_Weapon_ImpactEffect); /** argc (also, argv() can be used) */ i(int, MUTATOR_ARGV_0_int) \ /**/ MUTATOR_HOOKABLE(HUD_Command, EV_HUD_Command); + +/** Draw the grapple hook, allows changing hook texture and colour */ +#define EV_DrawGrapplingHook(i, o) \ + /** hook */ i(entity, MUTATOR_ARGV_0_entity) \ + /** texture */ i(string, MUTATOR_ARGV_1_string) \ + /***/ o(string, MUTATOR_ARGV_1_string) \ + /** colour */ i(vector, MUTATOR_ARGV_2_vector) \ + /***/ o(vector, MUTATOR_ARGV_2_vector) \ + /** team */ i(float, MUTATOR_ARGV_3_float) \ + /**/ +MUTATOR_HOOKABLE(DrawGrapplingHook, EV_DrawGrapplingHook); + +/** Called when an entity is updated (either by SVQC networking or PVS) */ +#define EV_Ent_Update(i, o) \ + /** entity id */ i(entity, MUTATOR_ARGV_0_entity) \ + /** is new to client */ i(bool, MUTATOR_ARGV_1_bool) \ + /**/ +MUTATOR_HOOKABLE(Ent_Update, EV_Ent_Update); + +/** Return true to not draw crosshair */ +MUTATOR_HOOKABLE(DrawCrosshair, EV_NO_ARGS); + +/** Return true to not draw scoreboard */ +MUTATOR_HOOKABLE(DrawScoreboard, EV_NO_ARGS); + +/** Called when drawing info messages, allows adding new info messages */ +#define EV_DrawInfoMessages(i, o) \ + /** pos */ i(vector, MUTATOR_ARGV_0_vector) \ + /** mySize */ i(vector, MUTATOR_ARGV_1_vector) \ + /**/ +MUTATOR_HOOKABLE(DrawInfoMessages, EV_DrawInfoMessages); diff --git a/qcsrc/client/player_skeleton.qc b/qcsrc/client/player_skeleton.qc index c8abdc6f57..66827c8f30 100644 --- a/qcsrc/client/player_skeleton.qc +++ b/qcsrc/client/player_skeleton.qc @@ -1,10 +1,12 @@ #include "player_skeleton.qh" +#include #include "mutators/events.qh" #include "../lib/csqcmodel/cl_player.qh" #include "../lib/warpzone/anglestransform.qh" .vector v_angle; +.float v_angle_save_x; class(Skeleton) .float skeleton_info_modelindex; class(Skeleton) .float skeleton_info_skin; @@ -50,7 +52,7 @@ void skeleton_loadinfo(entity e) } } else - LOG_TRACE("No model parameters for ", e.model, "\n"); + LOG_TRACE("No model parameters for ", e.model); //dprint(e.model, " uses ", ftos(e.bone_upperbody), " ", ftos(e.fixbone), "\n"); get_model_parameters(string_null, 0); e.skeleton_info_modelindex = e.modelindex; @@ -179,13 +181,22 @@ void skeleton_from_frames(entity e, bool is_dead) if(!is_dead) { if(e == csqcplayer) - e.v_angle_x = input_angles_x; + { + if(e.move_movetype == MOVETYPE_NONE) + { + if(!e.v_angle_save_x) + e.v_angle_save_x = input_angles.x; + e.v_angle_x = e.v_angle_save_x; + } + else + e.v_angle_x = input_angles.x; + } int i; for(i = 0; i < MAX_AIM_BONES; ++i) { if(e.(bone_aim[i])) { - vector aim = '1 0 0' * bound(-90, e.v_angle.x, 90) * e.(bone_aimweight[i]); + vector aim = '1 0 0' * bound(-90, e.v_angle_x, 90) * e.(bone_aimweight[i]); vector org = skel_get_boneabs(s, e.(bone_aim[i])); vector ang_cur = fixedvectoangles2(v_forward, v_up); vector ang = AnglesTransform_Multiply(aim, ang_cur); diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index a736d32622..738831a5d0 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -1,25 +1,11 @@ #include -#include "_all.qh" -#include "../client/_mod.inc" -#include "commands/_mod.inc" -#include "hud/_mod.inc" -#include "mutators/_mod.inc" -#include "weapons/_mod.inc" +#if XONOTIC +#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include -#include +#include +#endif -#if BUILD_MOD -#include "../../mod/client/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/client/scoreboard.qc b/qcsrc/client/scoreboard.qc deleted file mode 100644 index 2fb239ba10..0000000000 --- a/qcsrc/client/scoreboard.qc +++ /dev/null @@ -1,1515 +0,0 @@ -#include "scoreboard.qh" - -#include "hud/panel/quickmenu.qh" -#include "hud/all.qh" - -#include -#include -#include -#include -#include -#include - -float scoreboard_alpha_bg; -float scoreboard_alpha_fg; -float scoreboard_highlight; -float scoreboard_highlight_alpha; -float scoreboard_highlight_alpha_self; -float scoreboard_alpha_name; -float scoreboard_alpha_name_self; - -void drawstringright(vector, string, vector, vector, float, float); -void drawstringcenter(vector, string, vector, vector, float, float); - -const float SCOREBOARD_OFFSET = 50; - -// wrapper to put all possible scores titles through gettext -string TranslateScoresLabel(string l) -{ - switch(l) - { - case "bckills": return CTX(_("SCO^bckills")); - case "bctime": return CTX(_("SCO^bctime")); - case "caps": return CTX(_("SCO^caps")); - case "captime": return CTX(_("SCO^captime")); - case "deaths": return CTX(_("SCO^deaths")); - case "destroyed": return CTX(_("SCO^destroyed")); - case "dmg": return CTX(_("SCO^dmg")); - case "dmgtaken": return CTX(_("SCO^dmgtaken")); - case "drops": return CTX(_("SCO^drops")); - case "faults": return CTX(_("SCO^faults")); - case "fckills": return CTX(_("SCO^fckills")); - case "goals": return CTX(_("SCO^goals")); - case "kckills": return CTX(_("SCO^kckills")); - case "kdratio": return CTX(_("SCO^kdratio")); - case "k/d": return CTX(_("SCO^k/d")); - case "kd": return CTX(_("SCO^kd")); - case "kdr": return CTX(_("SCO^kdr")); - case "kills": return CTX(_("SCO^kills")); - case "laps": return CTX(_("SCO^laps")); - case "lives": return CTX(_("SCO^lives")); - case "losses": return CTX(_("SCO^losses")); - case "name": return CTX(_("SCO^name")); - case "sum": return CTX(_("SCO^sum")); - case "nick": return CTX(_("SCO^nick")); - case "objectives": return CTX(_("SCO^objectives")); - case "pickups": return CTX(_("SCO^pickups")); - case "ping": return CTX(_("SCO^ping")); - case "pl": return CTX(_("SCO^pl")); - case "pushes": return CTX(_("SCO^pushes")); - case "rank": return CTX(_("SCO^rank")); - case "returns": return CTX(_("SCO^returns")); - case "revivals": return CTX(_("SCO^revivals")); - case "score": return CTX(_("SCO^score")); - case "suicides": return CTX(_("SCO^suicides")); - case "takes": return CTX(_("SCO^takes")); - case "ticks": return CTX(_("SCO^ticks")); - default: return l; - } -} - -void HUD_InitScores() -{ - int i, f; - - ps_primary = ps_secondary = ts_primary = ts_secondary = -1; - for(i = 0; i < MAX_SCORE; ++i) - { - f = (scores_flags[i] & SFL_SORT_PRIO_MASK); - if(f == SFL_SORT_PRIO_PRIMARY) - ps_primary = i; - if(f == SFL_SORT_PRIO_SECONDARY) - ps_secondary = i; - } - if(ps_secondary == -1) - ps_secondary = ps_primary; - - for(i = 0; i < MAX_TEAMSCORE; ++i) - { - f = (teamscores_flags[i] & SFL_SORT_PRIO_MASK); - if(f == SFL_SORT_PRIO_PRIMARY) - ts_primary = i; - if(f == SFL_SORT_PRIO_SECONDARY) - ts_secondary = i; - } - if(ts_secondary == -1) - ts_secondary = ts_primary; - - Cmd_HUD_SetFields(0); -} - -float SetTeam(entity pl, float Team); -//float lastpnum; -void HUD_UpdatePlayerTeams() -{ - float Team; - entity pl, tmp; - float num; - - num = 0; - for(pl = players.sort_next; pl; pl = pl.sort_next) - { - num += 1; - Team = entcs_GetScoreTeam(pl.sv_entnum); - if(SetTeam(pl, Team)) - { - tmp = pl.sort_prev; - HUD_UpdatePlayerPos(pl); - if(tmp) - pl = tmp; - else - pl = players.sort_next; - } - } - /* - if(num != lastpnum) - print(strcat("PNUM: ", ftos(num), "\n")); - lastpnum = num; - */ -} - -int HUD_CompareScore(int vl, int vr, int f) -{ - TC(int, vl); TC(int, vr); TC(int, f); - if(f & SFL_ZERO_IS_WORST) - { - if(vl == 0 && vr != 0) - return 1; - if(vl != 0 && vr == 0) - return 0; - } - if(vl > vr) - return IS_INCREASING(f); - if(vl < vr) - return IS_DECREASING(f); - return -1; -} - -float HUD_ComparePlayerScores(entity left, entity right) -{ - float vl, vr, r; - vl = entcs_GetTeam(left.sv_entnum); - vr = entcs_GetTeam(right.sv_entnum); - - if(!left.gotscores) - vl = NUM_SPECTATOR; - if(!right.gotscores) - vr = NUM_SPECTATOR; - - if(vl > vr) - return true; - if(vl < vr) - return false; - - if(vl == NUM_SPECTATOR) - { - // FIRST the one with scores (spectators), THEN the ones without (downloaders) - // no other sorting - if(!left.gotscores && right.gotscores) - return true; - return false; - } - - r = HUD_CompareScore(left.scores[ps_primary], right.scores[ps_primary], scores_flags[ps_primary]); - if (r >= 0) - return r; - - r = HUD_CompareScore(left.scores[ps_secondary], right.scores[ps_secondary], scores_flags[ps_secondary]); - if (r >= 0) - return r; - - int i; - for(i = 0; i < MAX_SCORE; ++i) - { - r = HUD_CompareScore(left.scores[i], right.scores[i], scores_flags[i]); - if (r >= 0) - return r; - } - - if (left.sv_entnum < right.sv_entnum) - return true; - - return false; -} - -void HUD_UpdatePlayerPos(entity player) -{ - for(other = player.sort_next; other && HUD_ComparePlayerScores(player, other); other = player.sort_next) - { - SORT_SWAP(player, other); - } - for(other = player.sort_prev; other != players && HUD_ComparePlayerScores(other, player); other = player.sort_prev) - { - SORT_SWAP(other, player); - } -} - -float HUD_CompareTeamScores(entity left, entity right) -{ - int i, r; - - if(left.team == NUM_SPECTATOR) - return 1; - if(right.team == NUM_SPECTATOR) - return 0; - - r = HUD_CompareScore(left.teamscores[ts_primary], right.teamscores[ts_primary], teamscores_flags[ts_primary]); - if (r >= 0) - return r; - - r = HUD_CompareScore(left.teamscores[ts_secondary], right.teamscores[ts_secondary], teamscores_flags[ts_secondary]); - if (r >= 0) - return r; - - for(i = 0; i < MAX_SCORE; ++i) - { - r = HUD_CompareScore(left.teamscores[i], right.teamscores[i], teamscores_flags[i]); - if (r >= 0) - return r; - } - - if (left.team < right.team) - return true; - - return false; -} - -void HUD_UpdateTeamPos(entity Team) -{ - for(other = Team.sort_next; other && HUD_CompareTeamScores(Team, other); other = Team.sort_next) - { - SORT_SWAP(Team, other); - } - for(other = Team.sort_prev; other != teams && HUD_CompareTeamScores(other, Team); other = Team.sort_prev) - { - SORT_SWAP(other, Team); - } -} - -void Cmd_HUD_Help() -{ - LOG_INFO(_("You can modify the scoreboard using the ^2scoreboard_columns_set command.\n")); - LOG_INFO(_("^3|---------------------------------------------------------------|\n")); - LOG_INFO(_("Usage:\n")); - LOG_INFO(_("^2scoreboard_columns_set default\n")); - LOG_INFO(_("^2scoreboard_columns_set ^7field1 field2 ...\n")); - LOG_INFO(_("The following field names are recognized (case insensitive):\n")); - LOG_INFO(_("You can use a ^3|^7 to start the right-aligned fields.\n\n")); - - LOG_INFO(_("^3name^7 or ^3nick^7 Name of a player\n")); - LOG_INFO(_("^3ping^7 Ping time\n")); - LOG_INFO(_("^3pl^7 Packet loss\n")); - LOG_INFO(_("^3kills^7 Number of kills\n")); - LOG_INFO(_("^3deaths^7 Number of deaths\n")); - LOG_INFO(_("^3suicides^7 Number of suicides\n")); - LOG_INFO(_("^3frags^7 kills - suicides\n")); - LOG_INFO(_("^3kd^7 The kill-death ratio\n")); - LOG_INFO(_("^3dmg^7 The total damage done\n")); - LOG_INFO(_("^3dmgtaken^7 The total damage taken\n")); - LOG_INFO(_("^3sum^7 frags - deaths\n")); - LOG_INFO(_("^3caps^7 How often a flag (CTF) or a key (KeyHunt) was captured\n")); - LOG_INFO(_("^3pickups^7 How often a flag (CTF) or a key (KeyHunt) or a ball (Keepaway) was picked up\n")); - LOG_INFO(_("^3captime^7 Time of fastest cap (CTF)\n")); - LOG_INFO(_("^3fckills^7 Number of flag carrier kills\n")); - LOG_INFO(_("^3returns^7 Number of flag returns\n")); - LOG_INFO(_("^3drops^7 Number of flag drops\n")); - LOG_INFO(_("^3lives^7 Number of lives (LMS)\n")); - LOG_INFO(_("^3rank^7 Player rank\n")); - LOG_INFO(_("^3pushes^7 Number of players pushed into void\n")); - LOG_INFO(_("^3destroyed^7 Number of keys destroyed by pushing them into void\n")); - LOG_INFO(_("^3kckills^7 Number of keys carrier kills\n")); - LOG_INFO(_("^3losses^7 Number of times a key was lost\n")); - LOG_INFO(_("^3laps^7 Number of laps finished (race/cts)\n")); - LOG_INFO(_("^3time^7 Total time raced (race/cts)\n")); - LOG_INFO(_("^3fastest^7 Time of fastest lap (race/cts)\n")); - LOG_INFO(_("^3ticks^7 Number of ticks (DOM)\n")); - LOG_INFO(_("^3takes^7 Number of domination points taken (DOM)\n")); - LOG_INFO(_("^3bckills^7 Number of ball carrier kills\n")); - LOG_INFO(_("^3bctime^7 Total amount of time holding the ball in Keepaway\n")); - LOG_INFO(_("^3score^7 Total score\n\n")); - - 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")); - - LOG_INFO(_("The special game type names 'teams' and 'noteams' can be used to\n" - "include/exclude ALL teams/noteams game modes.\n\n")); - - LOG_INFO(_("Example: scoreboard_columns_set name ping pl | +ctf/field3 -dm/field4\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.\n")); - LOG_INFO(_("'field3' will only be shown in CTF, and 'field4' will be shown in all\n" - "other gamemodes except DM.\n")); -} - -// NOTE: adding a gametype with ? to not warn for an optional field -// make sure it's excluded in a previous exclusive rule, if any -// otherwise the previous exclusive rule warns anyway -// e.g. -teams,rc,cts,lms/kills ?+rc/kills -#define SCOREBOARD_DEFAULT_COLUMNS \ -"ping pl name |" \ -" -teams,rc,cts,inv,lms/kills +ft,tdm/kills ?+rc,inv/kills" \ -" -teams,lms/deaths +ft,tdm/deaths" \ -" -teams,lms,rc,cts,inv,ka/suicides +ft,tdm/suicides ?+rc,inv/suicides" \ -" -cts,dm,tdm,ka,ft/frags" /* tdm already has this in "score" */ \ -" -rc,cts,nb/dmg -rc,cts,nb/dmgtaken" \ -" +ctf/caps +ctf/pickups +ctf/fckills +ctf/returns +ons/caps +ons/takes" \ -" +lms/lives +lms/rank" \ -" +kh/caps +kh/pushes +kh/destroyed" \ -" ?+rc/laps ?+rc/time +rc,cts/fastest" \ -" +as/objectives +nb/faults +nb/goals" \ -" +ka/pickups +ka/bckills +ka/bctime +ft/revivals" \ -" -lms,rc,cts,inv,nb/score" - -void Cmd_HUD_SetFields(int argc) -{ - TC(int, argc); - int i, j, slash; - string str, pattern; - float have_name = 0, have_primary = 0, have_secondary = 0, have_separator = 0; - float missing; - - if(!gametype) - { - // set up a temporary scoreboard layout - // no layout can be properly set up until score_info data haven't been received - argc = tokenizebyseparator("0 1 ping pl name | score", " "); - ps_primary = 0; - scores_label[ps_primary] = strzone("score"); - scores_flags[ps_primary] = SFL_ALLOW_HIDE; - } - - // TODO: re enable with gametype dependant cvars? - if(argc < 3) // no arguments provided - argc = tokenizebyseparator(strcat("0 1 ", autocvar_scoreboard_columns), " "); - - if(argc < 3) - argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " "); - - if(argc == 3) - { - if(argv(2) == "default") - argc = tokenizebyseparator(strcat("0 1 ", SCOREBOARD_DEFAULT_COLUMNS), " "); - else if(argv(2) == "all") - { - string s; - s = "ping pl name |"; - for(i = 0; i < MAX_SCORE; ++i) - { - if(i != ps_primary) - if(i != ps_secondary) - if(scores_label[i] != "") - s = strcat(s, " ", scores_label[i]); - } - if(ps_secondary != ps_primary) - s = strcat(s, " ", scores_label[ps_secondary]); - s = strcat(s, " ", scores_label[ps_primary]); - argc = tokenizebyseparator(strcat("0 1 ", s), " "); - } - } - - - hud_num_fields = 0; - - hud_fontsize = HUD_GetFontsize("hud_fontsize"); - - for(i = 1; i < argc - 1; ++i) - { - float nocomplain; - str = argv(i+1); - - nocomplain = false; - if(substring(str, 0, 1) == "?") - { - nocomplain = true; - str = substring(str, 1, strlen(str) - 1); - } - - slash = strstrofs(str, "/", 0); - if(slash >= 0) - { - pattern = substring(str, 0, slash); - str = substring(str, slash + 1, strlen(str) - (slash + 1)); - - if (!isGametypeInFilter(gametype, teamplay, false, pattern)) - continue; - } - - strunzone(hud_title[hud_num_fields]); - hud_title[hud_num_fields] = strzone(TranslateScoresLabel(str)); - hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize); - str = strtolower(str); - - switch(str) - { - case "ping": hud_field[hud_num_fields] = SP_PING; break; - case "pl": hud_field[hud_num_fields] = SP_PL; break; - case "kd": case "kdr": case "kdratio": case "k/d": hud_field[hud_num_fields] = SP_KDRATIO; break; - case "sum": case "diff": case "k-d": hud_field[hud_num_fields] = SP_SUM; break; - case "name": case "nick": hud_field[hud_num_fields] = SP_NAME; have_name = true; break; - case "|": hud_field[hud_num_fields] = SP_SEPARATOR; have_separator = true; break; - case "dmg": hud_field[hud_num_fields] = SP_DMG; break; - case "dmgtaken": hud_field[hud_num_fields] = SP_DMGTAKEN; break; - default: - { - for(j = 0; j < MAX_SCORE; ++j) - if(str == strtolower(scores_label[j])) - goto found; // sorry, but otherwise fteqcc -O3 miscompiles this and warns about "unreachable code" - -LABEL(notfound) - if(str == "frags") - j = SP_FRAGS; - else - { - if(!nocomplain) - LOG_INFOF("^1Error:^7 Unknown score field: '%s'\n", str); - continue; - } -LABEL(found) - hud_field[hud_num_fields] = j; - if(j == ps_primary) - have_primary = 1; - if(j == ps_secondary) - have_secondary = 1; - - } - } - ++hud_num_fields; - if(hud_num_fields >= MAX_HUD_FIELDS) - break; - } - - if(scores_flags[ps_primary] & SFL_ALLOW_HIDE) - have_primary = 1; - if(scores_flags[ps_secondary] & SFL_ALLOW_HIDE) - have_secondary = 1; - if(ps_primary == ps_secondary) - have_secondary = 1; - missing = (!have_primary) + (!have_secondary) + (!have_separator) + (!have_name); - - if(hud_num_fields+missing < MAX_HUD_FIELDS) - { - if(!have_name) - { - strunzone(hud_title[hud_num_fields]); - for(i = hud_num_fields; i > 0; --i) - { - hud_title[i] = hud_title[i-1]; - hud_size[i] = hud_size[i-1]; - hud_field[i] = hud_field[i-1]; - } - hud_title[0] = strzone(TranslateScoresLabel("name")); - hud_field[0] = SP_NAME; - ++hud_num_fields; - LOG_INFO("fixed missing field 'name'\n"); - - if(!have_separator) - { - strunzone(hud_title[hud_num_fields]); - for(i = hud_num_fields; i > 1; --i) - { - hud_title[i] = hud_title[i-1]; - hud_size[i] = hud_size[i-1]; - hud_field[i] = hud_field[i-1]; - } - hud_title[1] = strzone("|"); - hud_field[1] = SP_SEPARATOR; - hud_size[1] = stringwidth("|", false, hud_fontsize); - ++hud_num_fields; - LOG_INFO("fixed missing field '|'\n"); - } - } - else if(!have_separator) - { - strunzone(hud_title[hud_num_fields]); - hud_title[hud_num_fields] = strzone("|"); - hud_size[hud_num_fields] = stringwidth("|", false, hud_fontsize); - hud_field[hud_num_fields] = SP_SEPARATOR; - ++hud_num_fields; - LOG_INFO("fixed missing field '|'\n"); - } - if(!have_secondary) - { - strunzone(hud_title[hud_num_fields]); - hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_secondary])); - hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize); - hud_field[hud_num_fields] = ps_secondary; - ++hud_num_fields; - LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_secondary]); - } - if(!have_primary) - { - strunzone(hud_title[hud_num_fields]); - hud_title[hud_num_fields] = strzone(TranslateScoresLabel(scores_label[ps_primary])); - hud_size[hud_num_fields] = stringwidth(hud_title[hud_num_fields], false, hud_fontsize); - hud_field[hud_num_fields] = ps_primary; - ++hud_num_fields; - LOG_INFOF("fixed missing field '%s'\n", scores_label[ps_primary]); - } - } - - hud_field[hud_num_fields] = SP_END; -} - -// MOVEUP:: -vector hud_field_rgb; -string hud_field_icon0; -string hud_field_icon1; -string hud_field_icon2; -vector hud_field_icon0_rgb; -vector hud_field_icon1_rgb; -vector hud_field_icon2_rgb; -float hud_field_icon0_alpha; -float hud_field_icon1_alpha; -float hud_field_icon2_alpha; -string HUD_GetField(entity pl, int field) -{ - TC(int, field); - float tmp, num, denom; - int f; - string str; - hud_field_rgb = '1 1 1'; - hud_field_icon0 = ""; - hud_field_icon1 = ""; - hud_field_icon2 = ""; - hud_field_icon0_rgb = '1 1 1'; - hud_field_icon1_rgb = '1 1 1'; - hud_field_icon2_rgb = '1 1 1'; - hud_field_icon0_alpha = 1; - hud_field_icon1_alpha = 1; - hud_field_icon2_alpha = 1; - switch(field) - { - case SP_PING: - if (!pl.gotscores) - return "\xE2\x96\xB6\xE2\x96\xB6\xE2\x96\xB6"; // >>> sign using U+25B6 - //str = getplayerkeyvalue(pl.sv_entnum, "ping"); - f = pl.ping; - if(f == 0) - return _("N/A"); - tmp = max(0, min(220, f-80)) / 220; - hud_field_rgb = '1 1 1' - '0 1 1'*tmp; - return ftos(f); - - case SP_PL: - if (!pl.gotscores) - return _("N/A"); - f = pl.ping_packetloss; - tmp = pl.ping_movementloss; - if(f == 0 && tmp == 0) - return ""; - str = ftos(ceil(f * 100)); - if(tmp != 0) - str = strcat(str, "~", ftos(ceil(tmp * 100))); - tmp = bound(0, f / 0.2 + tmp / 0.04, 1); // 20% is REALLY BAD pl - hud_field_rgb = '1 0.5 0.5' - '0 0.5 0.5'*tmp; - return str; - - case SP_NAME: - if(ready_waiting && pl.ready) - { - hud_field_icon0 = "gfx/scoreboard/player_ready"; - } - else if(!teamplay) - { - f = stof(getplayerkeyvalue(pl.sv_entnum, "colors")); - { - hud_field_icon0 = "gfx/scoreboard/playercolor_base"; - hud_field_icon1 = "gfx/scoreboard/playercolor_shirt"; - hud_field_icon1_rgb = colormapPaletteColor(floor(f / 16), 0); - hud_field_icon2 = "gfx/scoreboard/playercolor_pants"; - hud_field_icon2_rgb = colormapPaletteColor(f % 16, 1); - } - } - return entcs_GetName(pl.sv_entnum); - - case SP_FRAGS: - f = pl.(scores[SP_KILLS]); - f -= pl.(scores[SP_SUICIDES]); - return ftos(f); - - case SP_KDRATIO: - num = pl.(scores[SP_KILLS]); - denom = pl.(scores[SP_DEATHS]); - - if(denom == 0) { - hud_field_rgb = '0 1 0'; - str = sprintf("%d", num); - } else if(num <= 0) { - hud_field_rgb = '1 0 0'; - str = sprintf("%.1f", num/denom); - } else - str = sprintf("%.1f", num/denom); - return str; - - case SP_SUM: - f = pl.(scores[SP_KILLS]); - f -= pl.(scores[SP_DEATHS]); - - if(f > 0) { - hud_field_rgb = '0 1 0'; - } else if(f == 0) { - hud_field_rgb = '1 1 1'; - } else { - hud_field_rgb = '1 0 0'; - } - return ftos(f); - - case SP_DMG: - num = pl.(scores[SP_DMG]); - denom = 1000; - - str = sprintf("%.1f k", num/denom); - return str; - - case SP_DMGTAKEN: - num = pl.(scores[SP_DMGTAKEN]); - denom = 1000; - - str = sprintf("%.1f k", num/denom); - return str; - - default: - tmp = pl.(scores[field]); - f = scores_flags[field]; - if(field == ps_primary) - hud_field_rgb = '1 1 0'; - else if(field == ps_secondary) - hud_field_rgb = '0 1 1'; - else - hud_field_rgb = '1 1 1'; - return ScoreString(f, tmp); - } - //return "error"; -} - -float hud_fixscoreboardcolumnwidth_len; -float hud_fixscoreboardcolumnwidth_iconlen; -float hud_fixscoreboardcolumnwidth_marginlen; - -string HUD_FixScoreboardColumnWidth(int i, string str) -{ - TC(int, i); - float field, f; - vector sz; - field = hud_field[i]; - - hud_fixscoreboardcolumnwidth_iconlen = 0; - - if(hud_field_icon0 != "") - { - sz = draw_getimagesize(hud_field_icon0); - f = sz.x / sz.y; - if(hud_fixscoreboardcolumnwidth_iconlen < f) - hud_fixscoreboardcolumnwidth_iconlen = f; - } - - if(hud_field_icon1 != "") - { - sz = draw_getimagesize(hud_field_icon1); - f = sz.x / sz.y; - if(hud_fixscoreboardcolumnwidth_iconlen < f) - hud_fixscoreboardcolumnwidth_iconlen = f; - } - - if(hud_field_icon2 != "") - { - sz = draw_getimagesize(hud_field_icon2); - f = sz.x / sz.y; - if(hud_fixscoreboardcolumnwidth_iconlen < f) - hud_fixscoreboardcolumnwidth_iconlen = f; - } - - hud_fixscoreboardcolumnwidth_iconlen *= hud_fontsize.y / hud_fontsize.x; // fix icon aspect - - if(hud_fixscoreboardcolumnwidth_iconlen != 0) - hud_fixscoreboardcolumnwidth_marginlen = stringwidth(" ", false, hud_fontsize); - else - hud_fixscoreboardcolumnwidth_marginlen = 0; - - if(field == SP_NAME) // name gets all remaining space - { - int j; - float namesize; - namesize = sbwidth;// / hud_fontsize_x; - for(j = 0; j < hud_num_fields; ++j) - if(j != i) - if (hud_field[i] != SP_SEPARATOR) - namesize -= hud_size[j] + hud_fontsize.x; - namesize += hud_fontsize.x; - hud_size[i] = namesize; - - if (hud_fixscoreboardcolumnwidth_iconlen != 0) - namesize -= hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen; - str = textShortenToWidth(str, namesize, hud_fontsize, stringwidth_colors); - hud_fixscoreboardcolumnwidth_len = stringwidth(str, true, hud_fontsize); - } - else - hud_fixscoreboardcolumnwidth_len = stringwidth(str, false, hud_fontsize); - - f = hud_fixscoreboardcolumnwidth_len + hud_fixscoreboardcolumnwidth_marginlen + hud_fixscoreboardcolumnwidth_iconlen; - if(hud_size[i] < f) - hud_size[i] = f; - - return str; -} - -void HUD_PrintScoreboardItem(vector pos, vector item_size, entity pl, bool is_self, int pl_number) -{ - TC(bool, is_self); TC(int, pl_number); - vector tmp, rgb; - rgb = Team_ColorRGB(pl.team); - string str; - int field; - float is_spec; - is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR); - - if((rgb == '1 1 1') && (!is_spec)) { - rgb.x = autocvar_scoreboard_color_bg_r + 0.5; - rgb.y = autocvar_scoreboard_color_bg_g + 0.5; - rgb.z = autocvar_scoreboard_color_bg_b + 0.5; } - - vector h_pos = pos - '1 1 0'; - vector h_size = item_size + '2 0 0'; - // alternated rows highlighting - if(is_self) - drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha_self, DRAWFLAG_NORMAL); - else if((scoreboard_highlight) && (!(pl_number % 2))) - drawfill(h_pos, h_size, rgb, scoreboard_highlight_alpha, DRAWFLAG_NORMAL); - - tmp.x = item_size.x; - tmp.y = 0; - tmp.z = 0; - int i; - for(i = 0; i < hud_num_fields; ++i) - { - field = hud_field[i]; - if(field == SP_SEPARATOR) - break; - - if(is_spec && field != SP_NAME && field != SP_PING) { - pos.x += hud_size[i] + hud_fontsize.x; - continue; - } - str = HUD_GetField(pl, field); - str = HUD_FixScoreboardColumnWidth(i, str); - - pos.x += hud_size[i] + hud_fontsize.x; - - if(field == SP_NAME) { - tmp.x = hud_size[i] - hud_fontsize.x*hud_fixscoreboardcolumnwidth_iconlen - hud_fixscoreboardcolumnwidth_marginlen + hud_fontsize.x; - if (is_self) - drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL); - } else { - tmp.x = hud_fixscoreboardcolumnwidth_len + hud_fontsize.x; - if (is_self) - drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL); - } - - tmp.x = hud_size[i] + hud_fontsize.x; - if(hud_field_icon0 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - if(hud_field_icon1 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - if(hud_field_icon2 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - } - - if(hud_field[i] == SP_SEPARATOR) - { - pos.x = xmax; - for(i = hud_num_fields-1; i > 0; --i) - { - field = hud_field[i]; - if(field == SP_SEPARATOR) - break; - - if(is_spec && field != SP_NAME && field != SP_PING) { - pos.x -= hud_size[i] + hud_fontsize.x; - continue; - } - - str = HUD_GetField(pl, field); - str = HUD_FixScoreboardColumnWidth(i, str); - - if(field == SP_NAME) { - tmp.x = hud_fixscoreboardcolumnwidth_len; // left or right aligned? let's put it right... - if(is_self) - drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawcolorcodedstring(pos - tmp, str, hud_fontsize, scoreboard_alpha_name, DRAWFLAG_NORMAL); - } else { - tmp.x = hud_fixscoreboardcolumnwidth_len; - if(is_self) - drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawstring(pos - tmp, str, hud_fontsize, hud_field_rgb, scoreboard_alpha_name, DRAWFLAG_NORMAL); - } - - tmp.x = hud_size[i]; - if(hud_field_icon0 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon0, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon0_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - if(hud_field_icon1 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon1, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon1_rgb, hud_field_icon1_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - if(hud_field_icon2 != "") - if (is_self) - drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name_self, DRAWFLAG_NORMAL); - else - drawpic(pos - tmp, hud_field_icon2, '0 1 0' * hud_fontsize.y + '1 0 0' * hud_fontsize.x * hud_fixscoreboardcolumnwidth_iconlen, hud_field_icon2_rgb, hud_field_icon2_alpha * scoreboard_alpha_name, DRAWFLAG_NORMAL); - pos.x -= hud_size[i] + hud_fontsize.x; - } - } - - if(pl.eliminated) - drawfill(h_pos, h_size, '0 0 0', 0.5, DRAWFLAG_NORMAL); -} - -/* - * HUD_Scoreboard_MakeTable - * - * Makes a table for a team (for all playing players in DM) and fills it - */ - -vector HUD_Scoreboard_MakeTable(vector pos, entity tm, vector rgb, vector bg_size) -{ - float body_table_height; - vector tmp = '0 0 0', column_dim = '0 0 0'; - entity pl; - - body_table_height = 1.25 * hud_fontsize.y * max(1, tm.team_size); // no player? show 1 empty line - - pos.y += autocvar_scoreboard_border_thickness; - pos -= '1 1 0'; - - tmp.x = sbwidth + 2; - tmp.y = 1.25 * hud_fontsize.y; - - // rounded header - if (teamplay) - drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, (rgb * autocvar_scoreboard_color_bg_team) + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL); - else - drawpic(pos, "gfx/scoreboard/scoreboard_tableheader", tmp, rgb + '0.5 0.5 0.5', scoreboard_alpha_bg, DRAWFLAG_NORMAL); - - // table border - tmp.y += autocvar_scoreboard_border_thickness; - tmp.y += body_table_height; - drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); // more transparency for the scoreboard - - // separator header/table - pos.y += 1.25 * hud_fontsize.y; - tmp.y = autocvar_scoreboard_border_thickness; - drawfill(pos, tmp, '0 0 0', scoreboard_alpha_bg, DRAWFLAG_NORMAL); - - pos.y += autocvar_scoreboard_border_thickness; - - // table background - tmp.y = body_table_height; - if (teamplay) - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - else - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - - // anyway, apply some color - //drawfill(pos, tmp + '2 0 0', rgb, 0.1, DRAWFLAG_NORMAL); - - // go back to the top to make alternated columns highlighting and to print the strings - pos.y -= 1.25 * hud_fontsize.y; - pos.y -= autocvar_scoreboard_border_thickness; - - pos += '1 1 0'; - - if (scoreboard_highlight) - { - column_dim.y = 1.25 * hud_fontsize.y; // header - column_dim.y += autocvar_scoreboard_border_thickness; - column_dim.y += body_table_height; - } - - // print the strings of the columns headers and draw the columns - int i; - for(i = 0; i < hud_num_fields; ++i) - { - if(hud_field[i] == SP_SEPARATOR) - break; - column_dim.x = hud_size[i] + hud_fontsize.x; - if (scoreboard_highlight) - { - if (i % 2) - drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL); - } - drawstring(pos, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.x += column_dim.x; - } - if(hud_field[i] == SP_SEPARATOR) - { - pos.x = xmax; - tmp.y = 0; - for(i = hud_num_fields-1; i > 0; --i) - { - if(hud_field[i] == SP_SEPARATOR) - break; - - pos.x -= hud_size[i]; - - if (scoreboard_highlight) - { - if (!(i % 2)) - { - if (i == hud_num_fields-1) - column_dim.x = hud_size[i] + hud_fontsize.x / 2 + 1; - else - column_dim.x = hud_size[i] + hud_fontsize.x; - drawfill(pos - '0 1 0' - hud_fontsize.x / 2 * '1 0 0', column_dim, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL); - } - } - - tmp.x = stringwidth(hud_title[i], false, hud_fontsize); - tmp.x = (hud_size[i] - tmp.x); - drawstring(pos + tmp, hud_title[i], hud_fontsize, rgb * 1.5, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.x -= hud_fontsize.x; - } - } - - pos.x = xmin; - pos.y += 1.25 * hud_fontsize.y; // skip the header - pos.y += autocvar_scoreboard_border_thickness; - - // item size - tmp.x = sbwidth; - tmp.y = hud_fontsize.y * 1.25; - - // fill the table and draw the rows - i = 0; - if (teamplay) - for(pl = players.sort_next; pl; pl = pl.sort_next) - { - if(pl.team != tm.team) - continue; - HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i); - pos.y += 1.25 * hud_fontsize.y; - ++i; - } - else - for(pl = players.sort_next; pl; pl = pl.sort_next) - { - if(pl.team == NUM_SPECTATOR) - continue; - HUD_PrintScoreboardItem(pos, tmp, pl, (pl.sv_entnum == player_localnum), i); - pos.y += 1.25 * hud_fontsize.y; - ++i; - } - - if (i == 0) - pos.y += 1.25 * hud_fontsize.y; // move to the end of the table - pos.y += 1.25 * hud_fontsize.y; // move empty row (out of the table) - - return pos; -} - -float HUD_WouldDrawScoreboard() { - if (autocvar__hud_configure) - return 0; - else if (QuickMenu_IsOpened()) - return 0; - else if (HUD_Radar_Clickable()) - return 0; - else if (scoreboard_showscores) - return 1; - else if (intermission == 1) - return 1; - else if (intermission == 2) - return 0; - else if (spectatee_status != -1 && STAT(HEALTH) <= 0 && autocvar_cl_deathscoreboard && gametype != MAPINFO_TYPE_CTS && !active_minigame) - return 1; - else if (scoreboard_showscores_force) - return 1; - return 0; -} - -float average_accuracy; -vector HUD_DrawScoreboardAccuracyStats(vector pos, vector rgb, vector bg_size) -{ - WepSet weapons_stat = WepSet_GetFromStat(); - WepSet weapons_inmap = WepSet_GetFromStat_InMap(); - float initial_posx = pos.x; - int disownedcnt = 0; - FOREACH(Weapons, it != WEP_Null, { - int weapon_stats = weapon_accuracy[i - WEP_FIRST]; - - WepSet set = it.m_wepset; - if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set)) - ++disownedcnt; - }); - - int weapon_cnt = (Weapons_COUNT - 1) - disownedcnt; - if (weapon_cnt <= 0) return pos; - - int rows = 1; - if (autocvar_scoreboard_accuracy_doublerows && weapon_cnt >= floor((Weapons_COUNT - 1) * 0.5)) - rows = 2; - int columnns = ceil(weapon_cnt / rows); - - float height = 40; - float fontsize = height * 1/3; - float weapon_height = height * 2/3; - float weapon_width = sbwidth / columnns / rows; - - drawstring(pos, sprintf(_("Accuracy stats (average %d%%)"), average_accuracy), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness; - vector tmp = '0 0 0'; - tmp.x = sbwidth; - tmp.y = height * rows; - - if (teamplay) - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - else - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL); - - // column highlighting - for (int i = 0; i < columnns; ++i) - { - if ((i % 2) == 0) - drawfill(pos + '1 0 0' * weapon_width * rows * i, '0 1 0' * height * rows + '1 0 0' * weapon_width * rows, '0 0 0', scoreboard_alpha_bg * 0.2, DRAWFLAG_NORMAL); - } - - // row highlighting - for (int i = 0; i < rows; ++i) - { - drawfill(pos + '0 1 0' * weapon_height + '0 1 0' * height * i, '1 0 0' * sbwidth + '0 1 0' * fontsize, '1 1 1', scoreboard_highlight_alpha, DRAWFLAG_NORMAL); - } - - average_accuracy = 0; - int weapons_with_stats = 0; - if (rows == 2) - pos.x += weapon_width / 2; - - if (autocvar_scoreboard_accuracy_nocolors) - rgb = '1 1 1'; - else - Accuracy_LoadColors(); - - float oldposx = pos.x; - vector tmpos = pos; - - int column = 0; - FOREACH(Weapons, it != WEP_Null, { - int weapon_stats = weapon_accuracy[i - WEP_FIRST]; - - WepSet set = it.m_wepset; - if (weapon_stats < 0 && !(weapons_stat & set || weapons_inmap & set)) - continue; - - float weapon_alpha; - if (weapon_stats >= 0) - weapon_alpha = scoreboard_alpha_fg; - else - weapon_alpha = 0.2 * scoreboard_alpha_fg; - - // weapon icon - drawpic_aspect_skin(tmpos, it.model2, '1 0 0' * weapon_width + '0 1 0' * weapon_height, '1 1 1', weapon_alpha, DRAWFLAG_NORMAL); - // the accuracy - if (weapon_stats >= 0) { - weapons_with_stats += 1; - average_accuracy += weapon_stats; // store sum of all accuracies in average_accuracy - - string s; - s = sprintf("%d%%", weapon_stats*100); - - float padding; - padding = (weapon_width - stringwidth(s, false, '1 0 0' * fontsize)) / 2; // center the accuracy value - - if(!autocvar_scoreboard_accuracy_nocolors) - rgb = Accuracy_GetColor(weapon_stats); - - drawstring(tmpos + '1 0 0' * padding + '0 1 0' * weapon_height, s, '1 1 0' * fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - } - tmpos.x += weapon_width * rows; - pos.x += weapon_width * rows; - if (rows == 2 && column == columnns - 1) { - tmpos.x = oldposx; - tmpos.y += height; - pos.y += height; - } - ++column; - }); - - if (weapons_with_stats) - average_accuracy = floor((average_accuracy * 100 / weapons_with_stats) + 0.5); - - pos.y += height; - pos.y += 1.25 * hud_fontsize.y; - pos.x = initial_posx; - return pos; -} - -vector HUD_DrawKeyValue(vector pos, string key, string value) { - float px = pos.x; - pos.x += hud_fontsize.x * 0.25; - drawstring(pos, key, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.x = xmax - stringwidth(value, false, hud_fontsize) - hud_fontsize.x * 0.25; - drawstring(pos, value, hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.x = px; - pos.y+= hud_fontsize.y; - - return pos; -} - -vector HUD_DrawMapStats(vector pos, vector rgb, vector bg_size) { - float stat_secrets_found, stat_secrets_total; - float stat_monsters_killed, stat_monsters_total; - float rows = 0; - string val; - - // get monster stats - stat_monsters_killed = STAT(MONSTERS_KILLED); - stat_monsters_total = STAT(MONSTERS_TOTAL); - - // get secrets stats - stat_secrets_found = STAT(SECRETS_FOUND); - stat_secrets_total = STAT(SECRETS_TOTAL); - - // get number of rows - if(stat_secrets_total) - rows += 1; - if(stat_monsters_total) - rows += 1; - - // if no rows, return - if (!rows) - return pos; - - // draw table header - drawstring(pos, _("Map stats:"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.y += 1.25 * hud_fontsize.y + autocvar_scoreboard_border_thickness; - - // draw table - vector tmp = '0 0 0'; - tmp.x = sbwidth; - tmp.y = hud_fontsize.y * rows; - - if (teamplay) - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - else - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL); - - // draw monsters - if(stat_monsters_total) - { - val = sprintf("%d/%d", stat_monsters_killed, stat_monsters_total); - pos = HUD_DrawKeyValue(pos, _("Monsters killed:"), val); - } - - // draw secrets - if(stat_secrets_total) - { - val = sprintf("%d/%d", stat_secrets_found, stat_secrets_total); - pos = HUD_DrawKeyValue(pos, _("Secrets found:"), val); - } - - // update position - pos.y += 1.25 * hud_fontsize.y; - return pos; -} - - -vector HUD_DrawScoreboardRankings(vector pos, entity pl, vector rgb, vector bg_size) -{ - int i; - RANKINGS_RECEIVED_CNT = 0; - for (i=RANKINGS_CNT-1; i>=0; --i) - if (grecordtime[i]) - ++RANKINGS_RECEIVED_CNT; - - if (RANKINGS_RECEIVED_CNT == 0) - return pos; - - float is_spec; - is_spec = (entcs_GetTeam(pl.sv_entnum) == NUM_SPECTATOR); - vector hl_rgb; - hl_rgb.x = autocvar_scoreboard_color_bg_r + 0.5; - hl_rgb.y = autocvar_scoreboard_color_bg_g + 0.5; - hl_rgb.z = autocvar_scoreboard_color_bg_b + 0.5; - - pos.y += hud_fontsize.y; - drawstring(pos, _("Rankings"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.y += hud_fontsize.y + autocvar_scoreboard_border_thickness; - vector tmp = '0 0 0'; - tmp.x = sbwidth; - tmp.y = 1.25 * hud_fontsize.y * RANKINGS_RECEIVED_CNT; - - if (teamplay) - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb * autocvar_scoreboard_color_bg_team, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - else - drawpic_tiled(pos, "gfx/scoreboard/scoreboard_bg", bg_size, tmp, rgb, scoreboard_alpha_bg, DRAWFLAG_NORMAL); - drawborderlines(autocvar_scoreboard_border_thickness, pos, tmp, '0 0 0', scoreboard_alpha_bg * 0.75, DRAWFLAG_NORMAL); - - // row highlighting - for(i = 0; i 0) ? autocvar_scoreboard_bg_scale : 0.25); - - if(teamplay) - { - vector team_score_baseoffset; - team_score_baseoffset = eY * (2 * autocvar_scoreboard_border_thickness + hud_fontsize.y) - eX * (autocvar_scoreboard_border_thickness + hud_fontsize.x * 0.25); - for(tm = teams.sort_next; tm; tm = tm.sort_next) - { - if(tm.team == NUM_SPECTATOR) - continue; - if(!tm.team && teamplay) - continue; - - draw_beginBoldFont(); - rgb = Team_ColorRGB(tm.team); - str = ftos(tm.(teamscores[ts_primary])); - drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize * 1.5), str, hud_fontsize * 1.5, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - - if(ts_primary != ts_secondary) - { - str = ftos(tm.(teamscores[ts_secondary])); - drawstring(pos + team_score_baseoffset - eX * stringwidth(str, false, hud_fontsize) + eY * hud_fontsize.y * 1.5, str, hud_fontsize, rgb, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - } - draw_endBoldFont(); - - pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size); - } - rgb.x = autocvar_scoreboard_color_bg_r; - rgb.y = autocvar_scoreboard_color_bg_g; - rgb.z = autocvar_scoreboard_color_bg_b; - } - else - { - rgb.x = autocvar_scoreboard_color_bg_r; - rgb.y = autocvar_scoreboard_color_bg_g; - rgb.z = autocvar_scoreboard_color_bg_b; - - for(tm = teams.sort_next; tm; tm = tm.sort_next) - { - if(tm.team == NUM_SPECTATOR) - continue; - if(!tm.team && teamplay) - continue; - - pos = HUD_Scoreboard_MakeTable(pos, tm, rgb, bg_size); - } - } - - if(gametype == MAPINFO_TYPE_CTS || gametype == MAPINFO_TYPE_RACE) { - if(race_speedaward) { - drawcolorcodedstring(pos, sprintf(_("Speed award: %d ^7(%s^7)"), race_speedaward, race_speedaward_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.y += 1.25 * hud_fontsize.y; - } - if(race_speedaward_alltimebest) { - drawcolorcodedstring(pos, sprintf(_("All-time fastest: %d ^7(%s^7)"), race_speedaward_alltimebest, race_speedaward_alltimebest_holder), hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - pos.y += 1.25 * hud_fontsize.y; - } - pos = HUD_DrawScoreboardRankings(pos, playerslots[player_localnum], rgb, bg_size); - } - else if (autocvar_scoreboard_accuracy && !warmup_stage && gametype != MAPINFO_TYPE_NEXBALL) { - if(teamplay) - pos = HUD_DrawScoreboardAccuracyStats(pos, Team_ColorRGB(myteam), bg_size); - else - pos = HUD_DrawScoreboardAccuracyStats(pos, rgb, bg_size); - } - - - if(teamplay) - pos = HUD_DrawMapStats(pos, Team_ColorRGB(myteam), bg_size); - else - pos = HUD_DrawMapStats(pos, rgb, bg_size); - - // List spectators - float specs; - specs = 0; - tmp = pos; - vector item_size; - item_size.x = sbwidth; - item_size.y = hud_fontsize.y * 1.25; - item_size.z = 0; - for(pl = players.sort_next; pl; pl = pl.sort_next) - { - if(pl.team != NUM_SPECTATOR) - continue; - pos.y += 1.25 * hud_fontsize.y; - HUD_PrintScoreboardItem(pos, item_size, pl, (pl.sv_entnum == player_localnum), specs); - ++specs; - } - - if(specs) - { - draw_beginBoldFont(); - drawstring(tmp, _("Spectators"), hud_fontsize, '1 1 1', scoreboard_alpha_fg, DRAWFLAG_NORMAL); - draw_endBoldFont(); - pos.y += 1.25 * hud_fontsize.y; - } - - // Print info string - float tl, fl, ll; - str = sprintf(_("playing ^3%s^7 on ^2%s^7"), MapInfo_Type_ToText(gametype), shortmapname); - tl = STAT(TIMELIMIT); - fl = STAT(FRAGLIMIT); - ll = STAT(LEADLIMIT); - if(gametype == MAPINFO_TYPE_LMS) - { - if(tl > 0) - str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); - } - else - { - if(tl > 0) - str = strcat(str, sprintf(_(" for up to ^1%1.0f minutes^7"), tl)); - if(fl > 0) - { - if(tl > 0) - str = strcat(str, _(" or")); - if(teamplay) - { - str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], fl), - (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) : - (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) : - TranslateScoresLabel(teamscores_label[ts_primary]))); - } - else - { - str = strcat(str, sprintf(_(" until ^3%s %s^7"), ScoreString(scores_flags[ps_primary], fl), - (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) : - (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) : - TranslateScoresLabel(scores_label[ps_primary]))); - } - } - if(ll > 0) - { - if(tl > 0 || fl > 0) - str = strcat(str, _(" or")); - if(teamplay) - { - str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(teamscores_flags[ts_primary], ll), - (teamscores_label[ts_primary] == "score") ? CTX(_("SCO^points")) : - (teamscores_label[ts_primary] == "fastest") ? CTX(_("SCO^is beaten")) : - TranslateScoresLabel(teamscores_label[ts_primary]))); - } - else - { - str = strcat(str, sprintf(_(" until a lead of ^3%s %s^7"), ScoreString(scores_flags[ps_primary], ll), - (scores_label[ps_primary] == "score") ? CTX(_("SCO^points")) : - (scores_label[ps_primary] == "fastest") ? CTX(_("SCO^is beaten")) : - TranslateScoresLabel(scores_label[ps_primary]))); - } - } - } - - pos.y += 1.2 * hud_fontsize.y; - drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - - // print information about respawn status - float respawn_time = STAT(RESPAWN_TIME); - if(!intermission) - if(respawn_time) - { - if(respawn_time < 0) - { - // a negative number means we are awaiting respawn, time value is still the same - respawn_time *= -1; // remove mark now that we checked it - respawn_time = max(time, respawn_time); // don't show a negative value while the server is respawning the player (lag) - - str = sprintf(_("^1Respawning in ^3%s^1..."), - (autocvar_scoreboard_respawntime_decimals ? - count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals) - : - count_seconds(respawn_time - time) - ) - ); - } - else if(time < respawn_time) - { - str = sprintf(_("You are dead, wait ^3%s^7 before respawning"), - (autocvar_scoreboard_respawntime_decimals ? - count_seconds_decs(respawn_time - time, autocvar_scoreboard_respawntime_decimals) - : - count_seconds(respawn_time - time) - ) - ); - } - else if(time >= respawn_time) - str = sprintf(_("You are dead, press ^2%s^7 to respawn"), getcommandkey("jump", "+jump")); - - pos.y += 1.2 * hud_fontsize.y; - drawcolorcodedstring(pos + '0.5 0 0' * (sbwidth - stringwidth(str, true, hud_fontsize)), str, hud_fontsize, scoreboard_alpha_fg, DRAWFLAG_NORMAL); - } - - scoreboard_bottom = pos.y + 2 * hud_fontsize.y; -} diff --git a/qcsrc/client/scoreboard.qh b/qcsrc/client/scoreboard.qh deleted file mode 100644 index 8fccae9484..0000000000 --- a/qcsrc/client/scoreboard.qh +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -float xmin, xmax, ymin, ymax, sbwidth; - -float scoreboard_active; -float scoreboard_fade_alpha; - -void Cmd_HUD_SetFields(float argc); -void HUD_DrawScoreboard(); -void HUD_InitScores(); -void HUD_UpdatePlayerPos(entity pl); -void HUD_UpdateTeamPos(entity Team); -float HUD_WouldDrawScoreboard(); diff --git a/qcsrc/client/shownames.qc b/qcsrc/client/shownames.qc index 6700ba61c7..e46a97cb1d 100644 --- a/qcsrc/client/shownames.qc +++ b/qcsrc/client/shownames.qc @@ -1,9 +1,10 @@ #include "shownames.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include #include +#include #include #include @@ -34,7 +35,7 @@ const float SHOWNAMES_FADESPEED = 4; const float SHOWNAMES_FADEDELAY = 0.4; void Draw_ShowNames(entity this) { - if (this.sv_entnum == player_localentnum) // self or spectatee + if (this.sv_entnum == (current_player + 1)) // self or spectatee if (!(autocvar_hud_shownames_self && autocvar_chase_active)) return; if (!this.sameteam && !autocvar_hud_shownames_enemies) return; bool hit; @@ -48,34 +49,41 @@ void Draw_ShowNames(entity this) hit = !(trace_fraction < 1 && (trace_networkentity != this.sv_entnum && trace_ent.entnum != this.sv_entnum)); } // handle tag fading - bool overlap = false; + int overlap = -1; vector o = project_3d_to_2d(this.origin + eZ * autocvar_hud_shownames_offset); + if (autocvar_hud_shownames_crosshairdistance) + { + float d = autocvar_hud_shownames_crosshairdistance; + float w = o.x - vid_conwidth / 2; + float h = o.y - vid_conheight / 2; + if (d * d > w * w + h * h) this.pointtime = time; + if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) + overlap = 1; + else if(!autocvar_hud_shownames_crosshairdistance_antioverlap) + overlap = 0; + } + float dist = vlen(this.origin - view_origin); - if (autocvar_hud_shownames_antioverlap) + if (overlap == -1 && autocvar_hud_shownames_antioverlap) { // fade tag out if another tag that is closer to you overlaps - LL_EACH(shownames_ent, it != this && entcs_receiver(i), { + entity entcs = NULL; + LL_EACH(shownames_ent, it != this, { + entcs = entcs_receiver(i); + if (!(entcs && entcs.has_sv_origin)) + continue; vector eo = project_3d_to_2d(it.origin); if (eo.z < 0 || eo.x < 0 || eo.y < 0 || eo.x > vid_conwidth || eo.y > vid_conheight) continue; eo.z = 0; if (vdist(((eX * o.x + eY * o.y) - eo), <, autocvar_hud_shownames_antioverlap_distance) && vdist((it.origin - view_origin), <, dist)) { - overlap = true; + overlap = 1; break; } }); } bool onscreen = (o.z >= 0 && o.x >= 0 && o.y >= 0 && o.x <= vid_conwidth && o.y <= vid_conheight); - if (autocvar_hud_shownames_crosshairdistance) - { - float d = autocvar_hud_shownames_crosshairdistance; - float w = o.x - vid_conwidth / 2; - float h = o.y - vid_conheight / 2; - if (d * d > w * w + h * h) this.pointtime = time; - if (this.pointtime + autocvar_hud_shownames_crosshairdistance_time <= time) overlap = true; - else overlap = (autocvar_hud_shownames_crosshairdistance_antioverlap ? overlap : false); // override what antioverlap says unless allowed by cvar. - } if (!this.fadedelay) this.fadedelay = time + SHOWNAMES_FADEDELAY; if (this.csqcmodel_isdead) // dead player, fade out slowly { @@ -86,7 +94,7 @@ void Draw_ShowNames(entity this) this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime); this.fadedelay = 0; // reset fade in delay, enemy has left the view } - else if (overlap) // tag overlap detected, fade out + else if (overlap > 0) // tag overlap detected, fade out { this.alpha = max(0, this.alpha - SHOWNAMES_FADESPEED * frametime); } diff --git a/qcsrc/client/teamradar.qc b/qcsrc/client/teamradar.qc index a8708f2460..782776eb78 100644 --- a/qcsrc/client/teamradar.qc +++ b/qcsrc/client/teamradar.qc @@ -1,6 +1,6 @@ #include "teamradar.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include @@ -20,7 +20,7 @@ vector teamradar_texcoord_to_2dcoord(vector in) vector out; in -= teamradar_origin3d_in_texcoord; - out = rotate(in, teamradar_angle * DEG2RAD); + out = Rotate(in, teamradar_angle * DEG2RAD); out.y = - out.y; // screen space is reversed out = out * teamradar_size; @@ -42,19 +42,19 @@ vector teamradar_2dcoord_to_texcoord(vector in) out = out / teamradar_size; out_y = - out_y; // screen space is reversed - out = rotate(out, -teamradar_angle * DEG2RAD); + out = Rotate(out, -teamradar_angle * DEG2RAD); out += teamradar_origin3d_in_texcoord; return out; } -vector teamradar_texcoord_to_3dcoord(vector in,float z) +vector teamradar_texcoord_to_3dcoord(vector in,float oz) { vector out; out_x = in_x * (mi_picmax_x - mi_picmin_x) + mi_picmin_x; out_y = in_y * (mi_picmax_y - mi_picmin_y) + mi_picmin_y; - out_z = z; + out_z = oz; return out; } @@ -203,6 +203,7 @@ NET_HANDLE(ENT_CLIENT_RADARLINK, bool isnew) this.iflags = IFLAG_VELOCITY | IFLAG_ORIGIN; this.classname = "radarlink"; + if (isnew) IL_PUSH(g_radarlinks, this); if(sendflags & 1) { diff --git a/qcsrc/client/teamradar.qh b/qcsrc/client/teamradar.qh index 0b1884ccf3..251c1a53f1 100644 --- a/qcsrc/client/teamradar.qh +++ b/qcsrc/client/teamradar.qh @@ -32,7 +32,7 @@ vector teamradar_3dcoord_to_texcoord(vector in); vector teamradar_texcoord_to_2dcoord(vector in); -vector teamradar_texcoord_to_3dcoord(vector in,float z); +vector teamradar_texcoord_to_3dcoord(vector in,float oz); void draw_teamradar_background(float fg); diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 5cb3c18c1f..5f88e78edf 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -1,10 +1,10 @@ #include "view.qh" #include "announcer.qh" -#include "hud/all.qh" +#include "hud/_mod.qh" #include "mapvoting.qh" -#include "scoreboard.qh" #include "shownames.qh" +#include "hud/panel/scoreboard.qh" #include "hud/panel/quickmenu.qh" #include "mutators/events.qh" @@ -13,16 +13,19 @@ #include #include #include +#include #include #include -#include +#include #include #include #include #include +#include + #include -#include +#include #include #include #include @@ -122,7 +125,6 @@ void calc_followmodel_ofs(entity view) float frac; vector gunorg = '0 0 0'; static vector vel_average; - static vector gunorg_prev = '0 0 0'; static vector gunorg_adjustment_highpass; static vector gunorg_adjustment_lowpass; @@ -293,8 +295,8 @@ void viewmodel_draw(entity this) else if (wasinvehicle) a = 1; wasinvehicle = invehicle; Weapon wep = activeweapon; - int c = stof(getplayerkeyvalue(current_player, "colors")); - vector g = weaponentity_glowmod(wep, c); + int c = entcs_GetClientColors(current_player); + vector g = weaponentity_glowmod(wep, NULL, c); entity me = CSQCModel_server2csqc(player_localentnum - 1); int fx = ((me.csqcmodel_effects & EFMASK_CHEAP) | EF_NODEPTHTEST) @@ -311,6 +313,9 @@ void viewmodel_draw(entity this) { static string name_last; string name = wep.mdl; + string newname = wep.wr_viewmodel(wep, this); + if(newname) + name = newname; bool swap = name != name_last; // if (swap) { @@ -363,6 +368,7 @@ STATIC_INIT(Porto) { entity e = new_pure(porto); e.draw = Porto_Draw; + IL_PUSH(g_drawables, e); e.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP; } @@ -447,7 +453,7 @@ vector GetCurrentFov(float fov) zoomspeed = 3.5; zoomdir = button_zoom; - if(hud == HUD_NORMAL) + if(hud == HUD_NORMAL && !spectatee_status) if(switchweapon == activeweapon) if((activeweapon == WEP_VORTEX && !WEP_CVAR(vortex, secondary)) || (activeweapon == WEP_RIFLE && !WEP_CVAR(rifle, secondary))) // do NOT use switchweapon here zoomdir += button_attack2; @@ -709,7 +715,6 @@ float TrueAimCheck() void PostInit(); void CSQC_Demo_Camera(); -float HUD_WouldDrawScoreboard(); float camera_mode; const float CAMERA_FREE = 1; const float CAMERA_CHASE = 2; @@ -742,7 +747,7 @@ bool WantEventchase(entity this) { if(autocvar_cl_orthoview) return false; - if(intermission) + if(STAT(GAMEOVER) || intermission) return true; if(this.viewloc) return true; @@ -791,7 +796,7 @@ void UpdateDamage() if (damage_dealt_time != damage_dealt_time_prev) { unaccounted_damage += unaccounted_damage_new; - LOG_TRACE("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")", "\n"); + LOG_TRACE("dmg total: ", ftos(unaccounted_damage), " (+", ftos(unaccounted_damage_new), ")"); } damage_dealt_time_prev = damage_dealt_time; @@ -817,8 +822,8 @@ void HitSound() float a = autocvar_cl_hitsound_max_pitch; float b = autocvar_cl_hitsound_min_pitch; float c = autocvar_cl_hitsound_nom_damage; - float x = unaccounted_damage; - float pitch_shift = (b*x*(a-1) + a*c*(1-b)) / (x*(a-1) + c*(1-b)); + float d = unaccounted_damage; + float pitch_shift = (b*d*(a-1) + a*c*(1-b)) / (d*(a-1) + c*(1-b)); // if sound variation is disabled, set pitch_shift to 1 if (autocvar_cl_hitsound == 1) @@ -831,7 +836,7 @@ void HitSound() pitch_shift = mirror_value + (mirror_value - pitch_shift); } - LOG_TRACE("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift), "\n"); + LOG_TRACE("dmg total (dmg): ", ftos(unaccounted_damage), " , pitch shift: ", ftos(pitch_shift)); // todo: avoid very long and very short sounds from wave stretching using different sound files? seems unnecessary // todo: normalize sound pressure levels? seems unnecessary @@ -860,7 +865,7 @@ vector crosshair_getcolor(entity this, float health_stat) { case 1: // crosshair_color_per_weapon { - if(this != WEP_Null) + if(this != WEP_Null && hud == HUD_NORMAL) { wcross_color = this.wpcolor; break; @@ -870,7 +875,7 @@ vector crosshair_getcolor(entity this, float health_stat) case 2: // crosshair_color_by_health { - float x = health_stat; + float hp = health_stat; //x = red //y = green @@ -878,33 +883,33 @@ vector crosshair_getcolor(entity this, float health_stat) wcross_color.z = 0; - if(x > 200) + if(hp > 200) { wcross_color.x = 0; wcross_color.y = 1; } - else if(x > 150) + else if(hp > 150) { - wcross_color.x = 0.4 - (x-150)*0.02 * 0.4; - wcross_color.y = 0.9 + (x-150)*0.02 * 0.1; + wcross_color.x = 0.4 - (hp-150)*0.02 * 0.4; + wcross_color.y = 0.9 + (hp-150)*0.02 * 0.1; } - else if(x > 100) + else if(hp > 100) { - wcross_color.x = 1 - (x-100)*0.02 * 0.6; - wcross_color.y = 1 - (x-100)*0.02 * 0.1; - wcross_color.z = 1 - (x-100)*0.02; + wcross_color.x = 1 - (hp-100)*0.02 * 0.6; + wcross_color.y = 1 - (hp-100)*0.02 * 0.1; + wcross_color.z = 1 - (hp-100)*0.02; } - else if(x > 50) + else if(hp > 50) { wcross_color.x = 1; wcross_color.y = 1; - wcross_color.z = 0.2 + (x-50)*0.02 * 0.8; + wcross_color.z = 0.2 + (hp-50)*0.02 * 0.8; } - else if(x > 20) + else if(hp > 20) { wcross_color.x = 1; - wcross_color.y = (x-20)*90/27/100; - wcross_color.z = (x-20)*90/27/100 * 0.2; + wcross_color.y = (hp-20)*90/27/100; + wcross_color.z = (hp-20)*90/27/100 * 0.2; } else { @@ -935,13 +940,16 @@ void HUD_Crosshair(entity this) { float f, i, j; vector v; - if(!scoreboard_active && !camera_active && intermission != 2 && - spectatee_status != -1 && !csqcplayer.viewloc && + if(!scoreboard_active && !camera_active && intermission != 2 && !STAT(GAMEOVER) && + spectatee_status != -1 && !csqcplayer.viewloc && !MUTATOR_CALLHOOK(DrawCrosshair) && !HUD_MinigameMenu_IsOpened() ) { if (!autocvar_crosshair_enabled) // main toggle for crosshair rendering return; + if (spectatee_status > 0 && STAT(CAMERA_SPECTATOR) == 2) + return; + if (hud != HUD_NORMAL) { HUD_Crosshair_Vehicle(this); @@ -976,7 +984,7 @@ void HUD_Crosshair(entity this) v = wcross_origin - wcross_oldorigin; v.x /= vid_conwidth; v.y /= vid_conheight; - if(vlen(v) > 0.01) + if(vdist(v, >, 0.01)) shottype = SHOTTYPE_HITOBSTRUCTION; } if(!autocvar_crosshair_hittest_showimpact) @@ -1318,6 +1326,13 @@ void HUD_Crosshair(entity this) void HUD_Draw(entity this) { + // if we don't know gametype and scores yet avoid drawing the scoreboard + // also in the very first frames, player state may be inconsistent so avoid drawing the hud at all + // e.g. since initial player's health is 0 hud would display the hud_damage effect, + // cl_deathscoreboard would show the scoreboard and so on + if(!gametype) + return; + if(!intermission) if (MUTATOR_CALLHOOK(HUD_Draw_overlay)) { @@ -1333,6 +1348,11 @@ void HUD_Draw(entity this) DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(NADE_TIMER), '0.25 0.90 1' + ('1 0 0' * STAT(NADE_TIMER)) - ('0 1 1' * STAT(NADE_TIMER)), autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); drawstring_aspect(eY * 0.64 * vid_conheight, ((autocvar_cl_nade_timer == 2) ? _("Nade timer") : ""), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); } + else if(STAT(CAPTURE_PROGRESS)) + { + DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(CAPTURE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); + drawstring_aspect(eY * 0.64 * vid_conheight, _("Capture progress"), eX * vid_conwidth + eY * 0.025 * vid_conheight, '1 1 1', 1, DRAWFLAG_NORMAL); + } else if(STAT(REVIVE_PROGRESS)) { DrawCircleClippedPic(eX * 0.5 * vid_conwidth + eY * 0.6 * vid_conheight, 0.1 * vid_conheight, "gfx/crosshair_ring.tga", STAT(REVIVE_PROGRESS), '0.25 0.90 1', autocvar_hud_colorflash_alpha, DRAWFLAG_ADDITIVE); @@ -1346,7 +1366,6 @@ void HUD_Draw(entity this) Accuracy_LoadLevels(); HUD_Main(); - HUD_DrawScoreboard(); HUD_Scale_Disable(); } @@ -1365,12 +1384,13 @@ 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(float w, float h) -{ENGINE_EVENT(); +void CSQC_UpdateView(entity this, float w, float h) +{ TC(int, w); TC(int, h); entity e; float fov; @@ -1404,10 +1424,6 @@ void CSQC_UpdateView(float w, float h) else view_quality = 1; - // this needs to be updated manually now due to the destruction of engine physics stats - if(!isdemo() && autocvar_slowmo != STAT(MOVEVARS_TIMESCALE)) - cvar_set("slowmo", ftos(STAT(MOVEVARS_TIMESCALE))); - button_attack2 = PHYS_INPUT_BUTTON_ATCK2(this); button_zoom = PHYS_INPUT_BUTTON_ZOOM(this); @@ -1459,9 +1475,26 @@ void CSQC_UpdateView(float w, float h) // event chase camera if(autocvar_chase_active <= 0) // greater than 0 means it's enabled manually, and this code is skipped { + if(STAT(CAMERA_SPECTATOR)) + { + if(spectatee_status > 0) + { + if(!autocvar_chase_active) + { + cvar_set("chase_active", "-2"); + goto skip_eventchase_death; + } + } + else if(autocvar_chase_active == -2) + cvar_set("chase_active", "0"); + + if(autocvar_chase_active == -2) + goto skip_eventchase_death; + } + else if(autocvar_chase_active == -2) + cvar_set("chase_active", "0"); + float vehicle_chase = (hud != HUD_NORMAL && (autocvar_cl_eventchase_vehicle || spectatee_status > 0)); - float ons_roundlost = (gametype == MAPINFO_TYPE_ONSLAUGHT && STAT(ROUNDLOST)); - entity gen = NULL; float vehicle_viewdist = 0; vector vehicle_viewofs = '0 0 0'; @@ -1476,17 +1509,18 @@ void CSQC_UpdateView(float w, float h) } } - if(ons_roundlost) - { - FOREACH_ENTITY_CLASS("onslaught_generator", it.health <= 0, { - gen = it; - break; - }); - if(!gen) - ons_roundlost = false; // don't enforce the 3rd person camera if there is no dead generator to show - } - if(WantEventchase(this) || (!autocvar_cl_orthoview && ons_roundlost)) + if(WantEventchase(this)) { + vector current_view_origin_override = '0 0 0'; + vector view_offset_override = '0 0 0'; + float chase_distance_override = 0; + bool custom_eventchase = MUTATOR_CALLHOOK(CustomizeEventchase, this); + if(custom_eventchase) + { + current_view_origin_override = M_ARGV(0, vector); + view_offset_override = M_ARGV(1, vector); + chase_distance_override = M_ARGV(0, float); + } eventchase_running = true; entity local_player = ((csqcplayer) ? csqcplayer : CSQCModel_server2csqc(player_localentnum - 1)); @@ -1495,7 +1529,8 @@ void CSQC_UpdateView(float w, float h) // make special vector since we can't use view_origin (It is one frame old as of this code, it gets set later with the results this code makes.) vector current_view_origin = (csqcplayer ? csqcplayer.origin : pmove_org); - if(ons_roundlost) { current_view_origin = gen.origin; } + if (custom_eventchase) + current_view_origin = current_view_origin_override; // detect maximum viewoffset and use it vector view_offset = autocvar_cl_eventchase_viewoffset; @@ -1506,7 +1541,8 @@ void CSQC_UpdateView(float w, float h) else view_offset = autocvar_cl_eventchase_vehicle_viewoffset; } - if(ons_roundlost) { view_offset = autocvar_cl_eventchase_generator_viewoffset; } + if (custom_eventchase) + view_offset = view_offset_override; if(view_offset) { @@ -1529,7 +1565,8 @@ void CSQC_UpdateView(float w, float h) else chase_distance = autocvar_cl_eventchase_vehicle_distance; } - if(ons_roundlost) { chase_distance = autocvar_cl_eventchase_generator_distance; } + if (custom_eventchase) + chase_distance = chase_distance_override; if(autocvar_cl_eventchase_speed && eventchase_current_distance < chase_distance) eventchase_current_distance += autocvar_cl_eventchase_speed * (chase_distance - eventchase_current_distance) * frametime; // slow down the further we get @@ -1569,6 +1606,8 @@ void CSQC_UpdateView(float w, float h) eventchase_current_distance = 0; } + LABEL(skip_eventchase_death); + // do lockview after event chase camera so that it still applies whenever necessary. if(autocvar_cl_lockview || (!autocvar_hud_cursormode && (autocvar__hud_configure && spectatee_status <= 0 || intermission > 1 || QuickMenu_IsOpened()))) { @@ -1709,6 +1748,9 @@ void CSQC_UpdateView(float w, float h) if(!postinit) PostInit(); + if(intermission && !intermission_time) + intermission_time = time; + if(intermission && !isdemo() && !(calledhooks & HOOK_END)) { if(calledhooks & HOOK_START) @@ -1748,13 +1790,6 @@ void CSQC_UpdateView(float w, float h) switchweapon = Weapons_from(STAT(SWITCHWEAPON)); - f = (serverflags & SERVERFLAG_TEAMPLAY); - if(f != teamplay) - { - teamplay = f; - HUD_InitScores(); - } - if(last_switchweapon != switchweapon) { weapontime = time; @@ -1805,8 +1840,7 @@ void CSQC_UpdateView(float w, float h) else if(csqcplayer.viewloc) { setproperty(VF_FOV, GetViewLocationFOV(110)); } // enforce 110 fov, so things dont look odd else { setproperty(VF_FOV, GetCurrentFov(fov)); } - // Camera for demo playback - if(camera_active) + if(camera_active) // Camera for demo playback { if(autocvar_camera_enable) CSQC_Demo_Camera(); @@ -1847,7 +1881,7 @@ void CSQC_UpdateView(float w, float h) mousepos = mousepos*0.5 + getmousepos(); */ - FOREACH_ENTITY(it.draw, it.draw(it)); + IL_EACH(g_drawables, it.draw, it.draw(it)); addentities(MASK_NORMAL | MASK_ENGINE | MASK_ENGINEVIEWMODELS); renderscene(); @@ -2177,11 +2211,11 @@ void CSQC_UpdateView(float w, float h) } else */ // draw 2D entities - FOREACH_ENTITY(it.draw2d, it.draw2d(it)); + IL_EACH(g_drawables_2d, it.draw2d, it.draw2d(it)); Draw_ShowNames_All(); Debug_Draw(); - scoreboard_active = HUD_WouldDrawScoreboard(); + scoreboard_active = Scoreboard_WouldDraw(); HUD_Draw(this); // this parameter for deep vehicle function @@ -2220,7 +2254,7 @@ void CSQC_UpdateView(float w, float h) if(autocvar__hud_configure) HUD_Panel_Mouse(); - else if ( HUD_MinigameMenu_IsOpened() || minigame_isactive() ) + else if (HUD_MinigameMenu_IsOpened() || active_minigame) HUD_Minigame_Mouse(); else if(QuickMenu_IsOpened()) QuickMenu_Mouse(); @@ -2234,6 +2268,8 @@ void CSQC_UpdateView(float w, float h) // let's reset the view back to normal for the end setproperty(VF_MIN, '0 0 0'); setproperty(VF_SIZE, '1 0 0' * w + '0 1 0' * h); + + IL_ENDFRAME(); } diff --git a/qcsrc/client/wall.qc b/qcsrc/client/wall.qc index b94fddd8d8..64916ad8c1 100644 --- a/qcsrc/client/wall.qc +++ b/qcsrc/client/wall.qc @@ -213,11 +213,11 @@ NET_HANDLE(ENT_CLIENT_WALL, bool isnew) this.movedir_z = ReadCoord(); this.lip = ReadByte() / 255.0; } - this.fade_start = ReadShort(); - this.fade_end = ReadShort(); - this.alpha_max = ReadShort(); - this.alpha_min = ReadShort(); - this.inactive = ReadShort(); + 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); } @@ -230,5 +230,6 @@ NET_HANDLE(ENT_CLIENT_WALL, bool isnew) 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/weapons/projectile.qc b/qcsrc/client/weapons/projectile.qc index 6261fa842f..1f88bc8d60 100644 --- a/qcsrc/client/weapons/projectile.qc +++ b/qcsrc/client/weapons/projectile.qc @@ -6,6 +6,7 @@ #include "../mutators/events.qh" #include +#include #include #include @@ -16,10 +17,10 @@ .float scale; .vector colormod; -void SUB_Stop(entity this) +void SUB_Stop(entity this, entity toucher) { - this.move_velocity = this.move_avelocity = '0 0 0'; - this.move_movetype = MOVETYPE_NONE; + this.velocity = this.avelocity = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); } void Projectile_ResetTrail(entity this, vector to) @@ -58,11 +59,11 @@ void Projectile_Draw(entity this) float t; float a; - f = this.move_flags; + f = this.flags; if (this.count & 0x80) { - // this.move_flags &= ~FL_ONGROUND; + // UNSET_ONGROUND(this); if (this.move_movetype == MOVETYPE_NONE || this.move_movetype == MOVETYPE_FLY) Movetype_Physics_NoMatchServer(this); // the trivial movetypes do not have to match the @@ -72,9 +73,9 @@ void Projectile_Draw(entity this) // moving, we might still be ticrate dependent. else Movetype_Physics_MatchServer(this, autocvar_cl_projectiles_sloppy); - if (!(this.move_flags & FL_ONGROUND)) + if (!IS_ONGROUND(this)) if (this.velocity != '0 0 0') - this.move_angles = this.angles = vectoangles(this.velocity); + this.angles = vectoangles(this.velocity); } else { @@ -209,9 +210,9 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) // projectiles no longer being able to lie on a bmodel this.move_nomonsters = MOVE_WORLDONLY; if (f & 0x40) - this.move_flags |= FL_ONGROUND; + SET_ONGROUND(this); else - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); if (!this.move_time) { @@ -243,8 +244,6 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) this.gravity = ReadCoord(); else this.gravity = 0; // none - this.move_origin = this.origin; - this.move_velocity = this.velocity; } if (time == this.spawntime || (this.count & 0x80) || (f & 0x08)) @@ -332,7 +331,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) this.maxs = '0 0 0'; this.colormod = '0 0 0'; settouch(this, SUB_Stop); - this.move_movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); this.alphamod = 1; switch (this.cnt) @@ -340,12 +339,12 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) case PROJECTILE_ELECTRO: // only new engines support sound moving with object loopsound(this, CH_SHOTS_SINGLE, SND(ELECTRO_FLY), VOL_BASE, ATTEN_NORM); - this.mins = '0 0 -4'; - this.maxs = '0 0 -4'; - this.move_movetype = MOVETYPE_BOUNCE; + this.mins = '-4 -4 -4'; + this.maxs = '4 4 4'; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); - this.move_bounce_factor = WEP_CVAR_SEC(electro, bouncefactor); - this.move_bounce_stopspeed = WEP_CVAR_SEC(electro, bouncestop); + this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor); + this.bouncestop = WEP_CVAR_SEC(electro, bouncestop); break; case PROJECTILE_RPC: case PROJECTILE_ROCKET: @@ -360,10 +359,10 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) case PROJECTILE_GRENADE_BOUNCING: this.mins = '-3 -3 -3'; this.maxs = '3 3 3'; - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); - this.move_bounce_factor = WEP_CVAR(mortar, bouncefactor); - this.move_bounce_stopspeed = WEP_CVAR(mortar, bouncestop); + this.bouncefactor = WEP_CVAR(mortar, bouncefactor); + this.bouncestop = WEP_CVAR(mortar, bouncestop); break; case PROJECTILE_SHAMBLER_LIGHTNING: this.mins = '-8 -8 -8'; @@ -378,21 +377,21 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) case PROJECTILE_PORTO_RED: this.colormod = '2 1 1'; this.alphamod = 0.5; - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); break; case PROJECTILE_PORTO_BLUE: this.colormod = '1 1 2'; this.alphamod = 0.5; - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); break; case PROJECTILE_HAGAR_BOUNCING: - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); break; case PROJECTILE_CRYLINK_BOUNCING: - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); break; case PROJECTILE_FIREBALL: @@ -402,7 +401,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) break; case PROJECTILE_FIREMINE: loopsound(this, CH_SHOTS_SINGLE, SND(FIREBALL_FLY), VOL_BASE, ATTEN_NORM); - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); settouch(this, func_null); this.mins = '-4 -4 -4'; this.maxs = '4 4 4'; @@ -443,9 +442,9 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) this.mins = '0 0 -4'; this.maxs = '0 0 -4'; this.move_movetype = MOVETYPE_BOUNCE; - this.move_touch = func_null; - this.move_bounce_factor = WEP_CVAR_SEC(electro, bouncefactor); - this.move_bounce_stopspeed = WEP_CVAR_SEC(electro, bouncestop); + settouch(this, func_null); + this.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor); + this.bouncestop = WEP_CVAR_SEC(electro, bouncestop); break; */ default: @@ -462,16 +461,16 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) if (this.gravity) { if (this.move_movetype == MOVETYPE_FLY) - this.move_movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) - this.move_movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); } else { if (this.move_movetype == MOVETYPE_TOSS) - this.move_movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); if (this.move_movetype == MOVETYPE_BOUNCE) - this.move_movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(this, MOVETYPE_BOUNCEMISSILE); } if (!(this.count & 0x80)) @@ -479,6 +478,7 @@ NET_HANDLE(ENT_CLIENT_PROJECTILE, bool isnew) this.classname = "csqcprojectile"; this.draw = Projectile_Draw; + if (isnew) IL_PUSH(g_drawables, this); this.entremove = Ent_RemoveProjectile; } diff --git a/qcsrc/client/weapons/projectile.qh b/qcsrc/client/weapons/projectile.qh index 66e1be4707..27bca00c80 100644 --- a/qcsrc/client/weapons/projectile.qh +++ b/qcsrc/client/weapons/projectile.qh @@ -16,7 +16,7 @@ class(Projectile).float gravity; class(Projectile).int snd_looping; class(Projectile).bool silent; -void SUB_Stop(entity this); +void SUB_Stop(entity this, entity toucher); void Projectile_ResetTrail(entity this, vector to); diff --git a/qcsrc/common/_all.inc b/qcsrc/common/_all.inc index b16c24fb47..988413f7a9 100644 --- a/qcsrc/common/_all.inc +++ b/qcsrc/common/_all.inc @@ -1,6 +1,6 @@ -float autocvar_net_connecttimeout = 30; +noref float autocvar_net_connecttimeout = 30; -#ifndef MENUQC +#ifdef GAMEQC #include "anim.qc" #include "animdecide.qc" #include "ent_cs.qc" @@ -19,19 +19,21 @@ float autocvar_net_connecttimeout = 30; #include "campaign_setup.qc" #endif -#ifndef MENUQC +#ifdef GAMEQC #include "physics/all.inc" #include "triggers/include.qc" #include "viewloc.qc" #endif -#ifndef MENUQC +#ifdef GAMEQC #include "minigames/minigames.qc" #endif #include "debug.qh" -#ifndef MENUQC +#include "command/_mod.inc" + +#ifdef GAMEQC #include "deathtypes/all.qc" #include "effects/all.qc" #include "impulses/all.qc" @@ -40,8 +42,8 @@ float autocvar_net_connecttimeout = 30; #endif #include "items/_mod.inc" - #include "weapons/all.qc" - #include "monsters/all.qc" + #include "weapons/_all.inc" + #include "monsters/_mod.inc" #include "turrets/all.qc" #include "vehicles/all.qc" diff --git a/qcsrc/common/anim.qh b/qcsrc/common/anim.qh index acf6735f37..84d59720d4 100644 --- a/qcsrc/common/anim.qh +++ b/qcsrc/common/anim.qh @@ -1,5 +1,4 @@ -#ifndef ANIM_H -#define ANIM_H +#pragma once // begin engine fields @@ -46,5 +45,3 @@ void anim_set(entity e, vector anim, bool looping, bool override, bool restart); #define setanim(...) anim_set(__VA_ARGS__) void anim_update(entity e); #define updateanim(...) anim_update(__VA_ARGS__) - -#endif diff --git a/qcsrc/common/animdecide.qc b/qcsrc/common/animdecide.qc index ab389278d1..b53a9ba0e9 100644 --- a/qcsrc/common/animdecide.qc +++ b/qcsrc/common/animdecide.qc @@ -1,6 +1,6 @@ #include "animdecide.qh" -#include +#include #if defined(SVQC) #include "util.qh" diff --git a/qcsrc/common/animdecide.qh b/qcsrc/common/animdecide.qh index 39ca54ff2f..16feb948c3 100644 --- a/qcsrc/common/animdecide.qh +++ b/qcsrc/common/animdecide.qh @@ -1,5 +1,4 @@ -#ifndef ANIMDECIDE_H -#define ANIMDECIDE_H +#pragma once // must be called at least once to initialize, or when modelindex is changed void animdecide_load_if_needed(entity e); @@ -9,7 +8,7 @@ void animdecide_setimplicitstate(entity e, float onground); void animdecide_setframes(entity e, bool support_blending, .int fld_frame, .int fld_frame1time, .int fld_frame2, .int fld_frame2time); CLASS(Animation, Object) - ATTRIB(Animation, m_framenames, string, string_null) + ATTRIB(Animation, m_framenames, string); STATIC_METHOD(Animation, getframe, int(Animation this, int mdlidx)) { FOREACH_WORD(this.m_framenames, true, { @@ -17,7 +16,7 @@ CLASS(Animation, Object) if (f != -1) return f; }); #ifdef CSQC - LOG_DEBUGF("Missing animation for %s: %s\n", modelnameforindex(mdlidx), this.registered_id); + LOG_DEBUGF("Missing animation for %s: %s", modelnameforindex(mdlidx), this.registered_id); #endif return -1; } @@ -145,4 +144,3 @@ const int ANIMACTION_PAIN2 = 3; // pain const int ANIMACTION_SHOOT = 4; // shoot const int ANIMACTION_TAUNT = 5; // taunt const int ANIMACTION_MELEE = 6; // melee -#endif diff --git a/qcsrc/common/campaign_common.qh b/qcsrc/common/campaign_common.qh index 3f494a4639..3bdc8725c2 100644 --- a/qcsrc/common/campaign_common.qh +++ b/qcsrc/common/campaign_common.qh @@ -1,5 +1,4 @@ -#ifndef CAMPAIGN_COMMON_H -#define CAMPAIGN_COMMON_H +#pragma once #ifndef CAMPAIGN_MAX_ENTRIES #define CAMPAIGN_MAX_ENTRIES 64 @@ -31,4 +30,3 @@ void CampaignFile_Unload(); // Sets up the campaign for the n-th array item (meaning: campaign_offset+nth // level) using localcmd() void CampaignSetup(float n); -#endif diff --git a/qcsrc/common/campaign_file.qc b/qcsrc/common/campaign_file.qc index a8bbe8e856..af81942a20 100644 --- a/qcsrc/common/campaign_file.qc +++ b/qcsrc/common/campaign_file.qc @@ -1,3 +1,4 @@ +#include "campaign_file.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/campaign_file.qh b/qcsrc/common/campaign_file.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/campaign_file.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/campaign_setup.qc b/qcsrc/common/campaign_setup.qc index 8c20c755c3..258d47f452 100644 --- a/qcsrc/common/campaign_setup.qc +++ b/qcsrc/common/campaign_setup.qc @@ -1,3 +1,4 @@ +#include "campaign_setup.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) diff --git a/qcsrc/common/campaign_setup.qh b/qcsrc/common/campaign_setup.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/campaign_setup.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/command/_mod.inc b/qcsrc/common/command/_mod.inc index d2d24f03d9..e3099980ed 100644 --- a/qcsrc/common/command/_mod.inc +++ b/qcsrc/common/command/_mod.inc @@ -1,5 +1,5 @@ // generated file; do not modify -#include #include #include +#include #include diff --git a/qcsrc/common/command/_mod.qh b/qcsrc/common/command/_mod.qh index 440bdcb134..6ca293773c 100644 --- a/qcsrc/common/command/_mod.qh +++ b/qcsrc/common/command/_mod.qh @@ -1,5 +1,5 @@ // generated file; do not modify -#include #include #include +#include #include diff --git a/qcsrc/common/command/all.qc b/qcsrc/common/command/all.qc deleted file mode 100644 index dc1c0441fa..0000000000 --- a/qcsrc/common/command/all.qc +++ /dev/null @@ -1,3 +0,0 @@ -#include "generic.qc" -#include "markup.qc" -#include "rpn.qc" diff --git a/qcsrc/common/command/all.qh b/qcsrc/common/command/all.qh deleted file mode 100644 index 129090d3ab..0000000000 --- a/qcsrc/common/command/all.qh +++ /dev/null @@ -1,26 +0,0 @@ -#ifndef COMMON_COMMANDS_ALL_H -#define COMMON_COMMANDS_ALL_H - -#include "command.qh" -REGISTRY(GENERIC_COMMANDS, BITS(7)) -#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL) -REGISTER_REGISTRY(GENERIC_COMMANDS) -REGISTRY_SORT(GENERIC_COMMANDS) - -#define GENERIC_COMMAND(id, description) \ - CLASS(genericcommand_##id, Command) \ - ATTRIB(genericcommand_##id, m_name, string, #id); \ - ATTRIB(genericcommand_##id, m_description, string, description); \ - ENDCLASS(genericcommand_##id) \ - REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \ - METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command)) - -STATIC_INIT(GENERIC_COMMANDS_aliases) { - FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu"))); -} - -#include "generic.qh" -#include "markup.qh" -#include "rpn.qh" - -#endif diff --git a/qcsrc/common/command/command.qh b/qcsrc/common/command/command.qh index 72eaa18dab..349d492da8 100644 --- a/qcsrc/common/command/command.qh +++ b/qcsrc/common/command/command.qh @@ -1,16 +1,13 @@ -#ifndef COMMAND_H -#define COMMAND_H +#pragma once const int CMD_REQUEST_COMMAND = 1; const int CMD_REQUEST_USAGE = 2; CLASS(Command, Object) - ATTRIB(Command, m_name, string, string_null); - ATTRIB(Command, m_description, string, string_null); + ATTRIB(Command, m_name, string); + ATTRIB(Command, m_description, string); METHOD(Command, m_invokecmd, void(Command this, int request, entity caller, int arguments, string command)) { TC(Command, this); } ENDCLASS(Command) - -#endif diff --git a/qcsrc/common/command/generic.qc b/qcsrc/common/command/generic.qc index 90d6cefe7b..49a9d13098 100644 --- a/qcsrc/common/command/generic.qc +++ b/qcsrc/common/command/generic.qc @@ -1,11 +1,13 @@ -#include "all.qh" +#include "generic.qh" +#include "_mod.qh" +#include "reg.qh" #include "markup.qh" #include "rpn.qh" #include "../mapinfo.qh" -#ifndef MENUQC +#ifdef GAMEQC #include "../notifications/all.qh" #endif @@ -14,10 +16,7 @@ #endif #ifdef SVQC - #include - #include - #include - #include + #include #include #include #endif @@ -36,7 +35,7 @@ void Curl_URI_Get_Callback(int id, float status, string data) string do_cvar = curl_uri_get_cvar[i]; if(status != 0) { - LOG_TRACEF("error: status is %d\n", status); + LOG_TRACEF("error: status is %d", status); if(do_cvar) strunzone(do_cvar); return; @@ -374,7 +373,7 @@ void GenericCommand_restartnotifs(float request) { case CMD_REQUEST_COMMAND: { - #ifndef MENUQC + #ifdef GAMEQC int NOTIF_ANNCE_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_ANNCE, { ++NOTIF_ANNCE_COUNT; }); int NOTIF_INFO_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_INFO, { ++NOTIF_INFO_COUNT; }); int NOTIF_CENTER_COUNT = 0; FOREACH(Notifications, it.nent_type == MSG_CENTER, { ++NOTIF_CENTER_COUNT; }); @@ -426,9 +425,9 @@ void GenericCommand_settemp(float request, float argc) { float f = cvar_settemp(argv(1), argv(2)); if(f == 1) - LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily.\n"); + LOG_TRACE("Creating new settemp tracker for ", argv(1), " and setting it to \"", argv(2), "\" temporarily."); else if(f == -1) - LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\".\n"); + LOG_TRACE("Already had a tracker for ", argv(1), ", updating it to \"", argv(2), "\"."); // else cvar_settemp itself errors out return; @@ -456,9 +455,9 @@ void GenericCommand_settemp_restore(float request, float argc) float i = cvar_settemp_restore(); if(i) - LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values.\n"); + LOG_TRACE("Restored ", ftos(i), " temporary cvar settings to their original values."); else - LOG_TRACE("Nothing to restore.\n"); + LOG_TRACE("Nothing to restore."); return; } diff --git a/qcsrc/common/command/generic.qh b/qcsrc/common/command/generic.qh index f8139aaf47..b39c799014 100644 --- a/qcsrc/common/command/generic.qh +++ b/qcsrc/common/command/generic.qh @@ -1,5 +1,4 @@ -#ifndef COMMAND_GENERIC_H -#define COMMAND_GENERIC_H +#pragma once #include @@ -39,4 +38,3 @@ void Curl_URI_Get_Callback(int id, float status, string data); int curl_uri_get_pos; float curl_uri_get_exec[URI_GET_CURL_END - URI_GET_CURL + 1]; string curl_uri_get_cvar[URI_GET_CURL_END - URI_GET_CURL + 1]; -#endif diff --git a/qcsrc/common/command/markup.qc b/qcsrc/common/command/markup.qc index 9f0883bd61..95a3b53c54 100644 --- a/qcsrc/common/command/markup.qc +++ b/qcsrc/common/command/markup.qc @@ -1,5 +1,5 @@ -#include "command.qh" #include "markup.qh" +#include "command.qh" // ========================================================= // Markup chat characters command code, reworked by Samual @@ -53,7 +53,6 @@ void GenericCommand_markup_init() markup_from[i] = "&.."; markup_to[i] = "\x9e"; ++i; markup_from[i] = "&.)"; markup_to[i] = "\x9f"; ++i; markup_from[i] = "&<|"; markup_to[i] = "\xff"; ++i; - unused_float = i; } string GenericCommand_markup(string s2) diff --git a/qcsrc/common/command/markup.qh b/qcsrc/common/command/markup.qh index bccc78d0bc..7a1b3876af 100644 --- a/qcsrc/common/command/markup.qh +++ b/qcsrc/common/command/markup.qh @@ -1,5 +1,4 @@ -#ifndef COMMAND_MARKUP_H -#define COMMAND_MARKUP_H +#pragma once // ========================================================== // Declarations for markup command code, reworked by Samual @@ -12,4 +11,3 @@ string markup_from[NUM_MARKUPS]; string markup_to[NUM_MARKUPS]; string GenericCommand_markup(string s2); -#endif diff --git a/qcsrc/common/command/reg.qc b/qcsrc/common/command/reg.qc new file mode 100644 index 0000000000..c0af5b5e09 --- /dev/null +++ b/qcsrc/common/command/reg.qc @@ -0,0 +1 @@ +#include "reg.qh" diff --git a/qcsrc/common/command/reg.qh b/qcsrc/common/command/reg.qh new file mode 100644 index 0000000000..9868e2490a --- /dev/null +++ b/qcsrc/common/command/reg.qh @@ -0,0 +1,19 @@ +#pragma once + +#include "command.qh" +REGISTRY(GENERIC_COMMANDS, BITS(7)) +#define GENERIC_COMMANDS_from(i) _GENERIC_COMMANDS_from(i, NULL) +REGISTER_REGISTRY(GENERIC_COMMANDS) +REGISTRY_SORT(GENERIC_COMMANDS) + +#define GENERIC_COMMAND(id, description) \ + CLASS(genericcommand_##id, Command) \ + ATTRIB(genericcommand_##id, m_name, string, #id); \ + ATTRIB(genericcommand_##id, m_description, string, description); \ + ENDCLASS(genericcommand_##id) \ + REGISTER(GENERIC_COMMANDS, CMD_G, id, m_id, NEW(genericcommand_##id)); \ + METHOD(genericcommand_##id, m_invokecmd, void(genericcommand_##id this, int request, entity caller, int arguments, string command)) + +STATIC_INIT(GENERIC_COMMANDS_aliases) { + FOREACH(GENERIC_COMMANDS, true, localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_svmenu"))); +} diff --git a/qcsrc/common/command/rpn.qc b/qcsrc/common/command/rpn.qc index 12bb99d1d3..f88bf635ee 100644 --- a/qcsrc/common/command/rpn.qc +++ b/qcsrc/common/command/rpn.qc @@ -1,5 +1,5 @@ -#include "command.qh" #include "rpn.qh" +#include "command.qh" // ======================================== diff --git a/qcsrc/common/command/rpn.qh b/qcsrc/common/command/rpn.qh index 3c5a8019b2..ba028e2484 100644 --- a/qcsrc/common/command/rpn.qh +++ b/qcsrc/common/command/rpn.qh @@ -1,5 +1,4 @@ -#ifndef COMMAND_RPN_H -#define COMMAND_RPN_H +#pragma once // ========================================================= // Declarations for RPN command code, written by divVerent @@ -13,5 +12,3 @@ int rpn_sp; string rpn_stack[MAX_RPN_STACK]; void GenericCommand_rpn(float request, float argc, string command); - -#endif diff --git a/qcsrc/common/constants.qh b/qcsrc/common/constants.qh index 28db23dc11..910f34e326 100644 --- a/qcsrc/common/constants.qh +++ b/qcsrc/common/constants.qh @@ -1,60 +1,6 @@ -#ifndef CONSTANTS_H -#define CONSTANTS_H - -REGISTER_NET_TEMP(TE_CSQC_PICTURE) -REGISTER_NET_TEMP(TE_CSQC_RACE) -REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER) -REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT) -REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN) -REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP) - -const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder -const int RACE_NET_CHECKPOINT_CLEAR = 1; -const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder -const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent -const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent -const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder -const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason -const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason -const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client -const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client -const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client -const int RACE_NET_SERVER_RANKINGS = 11; -const int RACE_NET_SERVER_STATUS = 12; -const int RANKINGS_CNT = 15; +#pragma once -REGISTER_NET_LINKED(_ENT_CLIENT_INIT) -#ifdef CSQC -NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { make_pure(this); return true; } -#endif -/** Sent as a temp entity from a persistent linked entity */ -REGISTER_NET_TEMP(ENT_CLIENT_INIT) - -REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO) -REGISTER_NET_LINKED(ENT_CLIENT_SCORES) -REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES) -REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote] -REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor] -REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE) -REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE) -REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA) -REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED) -REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY) -REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS) - -REGISTER_NET_LINKED(ENT_CLIENT_MODEL) - -REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE) -REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA) -REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED) - -REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM) -REGISTER_NET_LINKED(ENT_CLIENT_HOOK) -REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE) - -REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT) -REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT) -REGISTER_NET_LINKED(ENT_CLIENT_WALL) +const int RANKINGS_CNT = 15; const int SPRITERULE_DEFAULT = 0; const int SPRITERULE_TEAMPLAY = 1; @@ -123,19 +69,114 @@ const int SFL_SORT_PRIO_SECONDARY = 4; const int SFL_SORT_PRIO_PRIMARY = 8; const int SFL_SORT_PRIO_MASK = 12; -/** +/* * Score indices */ -#define MAX_SCORE 12 + +#ifdef GAMEQC + +#define IS_INCREASING(x) ( (x) & SFL_LOWER_IS_BETTER ) +#define IS_DECREASING(x) ( !((x) & SFL_LOWER_IS_BETTER) ) + + +#define MAX_SCORE 64 + +#define REGISTER_SP(id) REGISTER(Scores, SP, id, m_id, new_pure(PlayerScoreField)) +REGISTRY(Scores, MAX_SCORE); +#define Scores_from(i) _Scores_from(i, NULL) +REGISTER_REGISTRY(Scores) +REGISTRY_SORT(Scores); +REGISTRY_CHECK(Scores); +STATIC_INIT(Scores_renumber) { FOREACH(Scores, true, it.m_id = i); } + +USING(PlayerScoreField, entity); +.int _scores[MAX_SCORE]; +.string m_name; +.int m_flags; + +#define scores(this) _scores[(this).m_id] +#define scores_label(this) ((this).m_name) +#define scores_flags(this) ((this).m_flags) + +REGISTER_SP(END); + +REGISTER_SP(PING); +REGISTER_SP(PL); +REGISTER_SP(NAME); +REGISTER_SP(KDRATIO); +REGISTER_SP(SUM); + +REGISTER_SP(SEPARATOR); + +REGISTER_SP(SCORE); + +REGISTER_SP(DMG); +REGISTER_SP(DMGTAKEN); + +REGISTER_SP(KILLS); +REGISTER_SP(DEATHS); +REGISTER_SP(SUICIDES); +REGISTER_SP(FRAGS); + +REGISTER_SP(ELO); + +// TODO: move to common mutators + +REGISTER_SP(RACE_TIME); +REGISTER_SP(RACE_LAPS); +REGISTER_SP(RACE_FASTEST); + +//REGISTER_SP(CTS_TIME); +//REGISTER_SP(CTS_LAPS); +//REGISTER_SP(CTS_FASTEST); + +REGISTER_SP(ASSAULT_OBJECTIVES); + +REGISTER_SP(CTF_PICKUPS); +REGISTER_SP(CTF_FCKILLS); +REGISTER_SP(CTF_RETURNS); +REGISTER_SP(CTF_CAPS); +REGISTER_SP(CTF_CAPTIME); +REGISTER_SP(CTF_DROPS); + +REGISTER_SP(DOM_TAKES); +REGISTER_SP(DOM_TICKS); + +REGISTER_SP(FREEZETAG_REVIVALS); + +REGISTER_SP(KEEPAWAY_PICKUPS); +REGISTER_SP(KEEPAWAY_BCTIME); +REGISTER_SP(KEEPAWAY_CARRIERKILLS); + +REGISTER_SP(KH_PICKUPS); +REGISTER_SP(KH_CAPS); +REGISTER_SP(KH_KCKILLS); +REGISTER_SP(KH_PUSHES); +REGISTER_SP(KH_DESTROYS); +REGISTER_SP(KH_LOSSES); + +REGISTER_SP(LMS_RANK); +REGISTER_SP(LMS_LIVES); + +REGISTER_SP(NEXBALL_GOALS); +REGISTER_SP(NEXBALL_FAULTS); + +REGISTER_SP(ONS_TAKES); +REGISTER_SP(ONS_CAPS); + #define MAX_TEAMSCORE 2 +USING(ScoreTeam, string); +.int _teamscores[MAX_TEAMSCORE]; +#define teamscores(i) _teamscores[i] +string _teamscores_label[MAX_TEAMSCORE]; +#define teamscores_label(i) _teamscores_label[i] +int _teamscores_flags[MAX_TEAMSCORE]; +#define teamscores_flags(i) _teamscores_flags[i] + +#endif const int ST_SCORE = 0; -const int SP_KILLS = 0; -const int SP_DEATHS = 1; -const int SP_SUICIDES = 2; -const int SP_SCORE = 3; -const int SP_DMG = 10; -const int SP_DMGTAKEN = 11; + // game mode specific indices are not in common/, but in server/scores_rules.qc! // WEAPONTODO: move this into separate/new projectile handling code // this sets sounds and other properties of the projectiles in csqc @@ -192,7 +233,7 @@ const int SPECIES_RESERVED = 15; const int FRAGS_PLAYER = 0; const int FRAGS_SPECTATOR = -666; const int FRAGS_LMS_LOSER = -616; -const int FRAGS_PLAYER_NONSOLID = -616; +const int FRAGS_PLAYER_NONSOLID = FRAGS_LMS_LOSER; // we can use this frags value for both // water levels @@ -206,6 +247,7 @@ const int SERVERFLAG_ALLOW_FULLBRIGHT = 1; const int SERVERFLAG_TEAMPLAY = 2; const int SERVERFLAG_PLAYERSTATS = 4; +#ifdef SVQC // FIXME/EXPLAINME: why? Mario: because vector autocvar_sv_player_maxs = '16 16 45'; vector autocvar_sv_player_mins = '-16 -16 -24'; @@ -213,7 +255,8 @@ vector autocvar_sv_player_viewoffset = '0 0 20'; vector autocvar_sv_player_crouch_maxs = '16 16 25'; vector autocvar_sv_player_crouch_mins = '-16 -16 -24'; vector autocvar_sv_player_crouch_viewoffset = '0 0 20'; -vector autocvar_sv_player_headsize = '24 24 12'; +//vector autocvar_sv_player_headsize = '24 24 12'; +#endif // a bit more constant @@ -230,4 +273,3 @@ const int SPAWN_PRIO_GOOD_DISTANCE = 10; const int GTV_FORBIDDEN = 0; // Cannot be voted const int GTV_AVAILABLE = 1; // Can be voted const int GTV_CUSTOM = 2; // Custom entry -#endif diff --git a/qcsrc/common/csqcmodel_settings.qh b/qcsrc/common/csqcmodel_settings.qh index ff890afec6..9c4909671e 100644 --- a/qcsrc/common/csqcmodel_settings.qh +++ b/qcsrc/common/csqcmodel_settings.qh @@ -1,5 +1,4 @@ -#ifndef CSQCMODEL_SETTINGS_H -#define CSQCMODEL_SETTINGS_H +#pragma once // define this if svqc code wants to use .frame2 and .lerpfrac //#define CSQCMODEL_HAVE_TWO_FRAMES @@ -19,16 +18,12 @@ # define TAG_VIEWLOC_NAME tag_networkviewloc # define TAG_VIEWLOC_TYPE int .float tag_networkviewloc; - -# define MOVETYPE_NAME move_movetype #else # define TAG_ENTITY_NAME tag_entity # define TAG_ENTITY_TYPE entity # define TAG_VIEWLOC_NAME viewloc # define TAG_VIEWLOC_TYPE entity - -# define MOVETYPE_NAME movetype #endif // new fields @@ -68,7 +63,7 @@ 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(15), int, ReadByte, WriteByte, multijump_count) \ - CSQCMODEL_PROPERTY(BIT(16), int, ReadByte, WriteByte, MOVETYPE_NAME) + CSQCMODEL_PROPERTY(BIT(16), int, ReadByte, WriteByte, move_movetype) // TODO get rid of colormod/glowmod here; also get rid of some useless properties on non-players that only exist for CopyBody // add hook function calls here @@ -94,4 +89,3 @@ #endif #define CSQCMODEL_EF_RESPAWNGHOST EF_SELECTABLE -#endif diff --git a/qcsrc/common/deathtypes/all.qh b/qcsrc/common/deathtypes/all.qh index 9030879474..0466c230ab 100644 --- a/qcsrc/common/deathtypes/all.qh +++ b/qcsrc/common/deathtypes/all.qh @@ -1,5 +1,4 @@ -#ifndef DEATHTYPES_ALL_H -#define DEATHTYPES_ALL_H +#pragma once #include @@ -47,5 +46,3 @@ const int DT_FIRST = BIT(13); string Deathtype_Name(int deathtype); #include "all.inc" - -#endif diff --git a/qcsrc/common/debug.qh b/qcsrc/common/debug.qh index 9aca8e774c..6d580bd2d6 100644 --- a/qcsrc/common/debug.qh +++ b/qcsrc/common/debug.qh @@ -4,7 +4,7 @@ .entity tag_entity; #endif -#ifndef MENUQC +#ifdef GAMEQC .bool debug; .int sv_entnum; REGISTER_NET_TEMP(net_debug) @@ -47,7 +47,7 @@ REGISTER_NET_TEMP(net_debug) } #endif -#ifndef MENUQC +#ifdef GAMEQC /** * 0: off * 1: on @@ -100,7 +100,7 @@ bool autocvar_debugdraw; // if (it.entnum) break; // if (it.drawmask) break; // if (it.predraw) break; -// if (it.movetype) break; +// if (it.move_movetype) break; if (it.solid) break; // if (it.origin) break; // if (it.oldorigin) break; @@ -223,6 +223,40 @@ GENERIC_COMMAND(version, "Print the current version") } } +#ifdef CSQC +void(float bufhandle, string pattern, string antipattern) buf_cvarlist = #517; +#endif +GENERIC_COMMAND(cvar_localchanges, "Print locally changed cvars") +{ + switch (request) + { + case CMD_REQUEST_COMMAND: + { + string s = ""; + int h = buf_create(); + buf_cvarlist(h, "", "_"); // exclude all _ cvars as they are temporary + int n = buf_getsize(h); + for (int i = 0; i < n; ++i) { + string k = bufstr_get(h, i); + string v = cvar_string(k); + string d = cvar_defstring(k); + if (v == d) + continue; + s = strcat(s, k, " \"", v, "\" // \"", d, "\"\n"); + } + buf_del(h); + LOG_INFO(s); + return; + } + default: + case CMD_REQUEST_USAGE: + { + LOG_INFO(strcat("\nUsage:^3 ", GetProgramCommandPrefix(), " cvar_localchanges")); + return; + } + } +} + REGISTER_STAT(TRACE_ENT, int) #ifdef SVQC bool autocvar_debugtrace; @@ -275,6 +309,7 @@ STATIC_INIT(TRACE_ENT) { entity e = TRACE_ENT = new_pure(TRACE_ENT); e.draw2d = Trace_draw2d; + IL_PUSH(g_drawables_2d, e); } #endif diff --git a/qcsrc/common/effects/_mod.inc b/qcsrc/common/effects/_mod.inc index 6975259c17..d5dab8cad2 100644 --- a/qcsrc/common/effects/_mod.inc +++ b/qcsrc/common/effects/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/effects/_mod.qh b/qcsrc/common/effects/_mod.qh index 3f5ed82198..8d6e8ed18c 100644 --- a/qcsrc/common/effects/_mod.qh +++ b/qcsrc/common/effects/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/effects/all.qc b/qcsrc/common/effects/all.qc index e69a03f729..d229b05100 100644 --- a/qcsrc/common/effects/all.qc +++ b/qcsrc/common/effects/all.qc @@ -76,7 +76,7 @@ void Send_Effect(entity eff, vector eff_loc, vector eff_vel, int eff_cnt) net_eff.eent_eff_trail = eff.eent_eff_trail; FOREACH_CLIENT(IS_REAL_CLIENT(it), Net_Write_Effect(net_eff, it, 0)); - remove(net_eff); + delete(net_eff); } void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt) @@ -91,4 +91,6 @@ void Send_Effect_(string eff_name, vector eff_loc, vector eff_vel, int eff_cnt) } #endif -#include "effectinfo.qc" +#ifdef EFFECTINFO + #include "effectinfo.qc" +#endif diff --git a/qcsrc/common/effects/all.qh b/qcsrc/common/effects/all.qh index e0e9a3ca86..7581618375 100644 --- a/qcsrc/common/effects/all.qh +++ b/qcsrc/common/effects/all.qh @@ -1,5 +1,4 @@ -#ifndef EFFECTS_ALL_H -#define EFFECTS_ALL_H +#pragma once #include "effect.qh" @@ -17,5 +16,3 @@ REGISTRY_CHECK(Effects) EFFECT(0, Null, string_null) #include "all.inc" - -#endif diff --git a/qcsrc/common/effects/effect.qh b/qcsrc/common/effects/effect.qh index e58d42ca7b..7802f0a91b 100644 --- a/qcsrc/common/effects/effect.qh +++ b/qcsrc/common/effects/effect.qh @@ -1,5 +1,4 @@ -#ifndef EFFECT_H -#define EFFECT_H +#pragma once #define particleeffectnum(e) \ _particleeffectnum(e.eent_eff_name) @@ -32,5 +31,3 @@ entity Create_Effect_Entity(string eff_name, bool eff_trail) this.eent_eff_trail = eff_trail; return this; } - -#endif diff --git a/qcsrc/common/effects/effectinfo.qc b/qcsrc/common/effects/effectinfo.qc index 07d76c480a..f662aee7b2 100644 --- a/qcsrc/common/effects/effectinfo.qc +++ b/qcsrc/common/effects/effectinfo.qc @@ -1,3 +1,4 @@ +#include "effectinfo.qh" #define EFFECTINFO_PARSER(on, MY) \ on(type, MY(type) \ ,{ demand(n == 1 && "type"); MY(type) = strzone(argv(1)); \ @@ -138,7 +139,7 @@ /**/ CLASS(EffectInfo, Object) - ATTRIB(EffectInfo, effectinfo_name, string, string_null) + ATTRIB(EffectInfo, effectinfo_name, string); CONSTRUCTOR(EffectInfo, string s) { CONSTRUCT(EffectInfo); this.effectinfo_name = s; @@ -193,7 +194,7 @@ CLASS(EffectInfo, Object) MY(velocityoffset, vector, '0 0 0') \ /**/ - #define MY(f, type, val) ATTRIB(EffectInfo, effectinfo_##f, type, val) + #define MY(f, type, val) ATTRIB(EffectInfo, effectinfo_##f, type, val); FIELDS(MY) #undef MY @@ -227,8 +228,8 @@ CLASS(EffectInfo, Object) ENDCLASS(EffectInfo) CLASS(EffectInfoGroup, Object) - ATTRIBARRAY(EffectInfoGroup, children, EffectInfo, 16) - ATTRIB(EffectInfoGroup, children_count, int, 0) + ATTRIBARRAY(EffectInfoGroup, children, EffectInfo, 16); + ATTRIB(EffectInfoGroup, children_count, int, 0); ENDCLASS(EffectInfoGroup) void effectinfo_read() @@ -253,7 +254,7 @@ void effectinfo_read() #undef p #undef MY default: - LOG_WARNINGF("Unknown property '%s'\n", k); + LOG_WARNF("Unknown property '%s'", k); break; } } @@ -306,7 +307,7 @@ GENERIC_COMMAND(dumpeffectinfo, "Dump all effectinfo to effectinfo_dump.txt") LOG_INFOF("Reload with ^2cl_particles_reloadeffects data/%s^7.\n", filename); fclose(fh); } else { - LOG_WARNINGF("Could not open file '%s'!\n", filename); + LOG_WARNF("Could not open file '%s'!", filename); } return; } diff --git a/qcsrc/common/effects/effectinfo.qh b/qcsrc/common/effects/effectinfo.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/effects/effectinfo.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/effects/qc/all.qh b/qcsrc/common/effects/qc/all.qh index 5b9b364313..54cf6b6ff0 100644 --- a/qcsrc/common/effects/qc/all.qh +++ b/qcsrc/common/effects/qc/all.qh @@ -1,4 +1,3 @@ -#ifndef EFFECTS_QC -#define EFFECTS_QC +#pragma once + #include "all.inc" -#endif diff --git a/qcsrc/common/effects/qc/casings.qc b/qcsrc/common/effects/qc/casings.qc index 605d42e671..d0befbb7ed 100644 --- a/qcsrc/common/effects/qc/casings.qc +++ b/qcsrc/common/effects/qc/casings.qc @@ -1,5 +1,6 @@ +#include "casings.qh" #ifdef SVQC -void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner); +void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity); #endif #ifdef IMPLEMENTATION @@ -14,9 +15,8 @@ void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float ran REGISTER_NET_TEMP(casings) #ifdef SVQC -void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner) +void SpawnCasing(vector vel, float randomvel, vector ang, vector avel, float randomavel, int casingtype, entity casingowner, .entity weaponentity) { - .entity weaponentity = weaponentities[0]; // TODO: parameter entity wep = casingowner.(weaponentity); vector org = casingowner.origin + casingowner.view_ofs + wep.spawnorigin.x * v_forward - wep.spawnorigin.y * v_right + wep.spawnorigin.z * v_up; @@ -44,16 +44,16 @@ class(Casing) .float cnt; void Casing_Delete(entity this) { - remove(this); + delete(this); } void Casing_Draw(entity this) { - if (this.move_flags & FL_ONGROUND) + if (IS_ONGROUND(this)) { - this.move_angles_x = 0; - this.move_angles_z = 0; - UNSET_ONGROUND(this); + this.angles_x = 0; + this.angles_z = 0; + //UNSET_ONGROUND(this); } Movetype_Physics_MatchTicrate(this, autocvar_cl_casings_ticrate, autocvar_cl_casings_sloppy); @@ -83,7 +83,7 @@ Sound SND_CASINGS_RANDOM() { return Sounds_from(SND_CASINGS1.m_id + floor(prandom() * 3)); } -void Casing_Touch(entity this) +void Casing_Touch(entity this, entity toucher) { if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) { @@ -121,8 +121,8 @@ void Casing_Damage(entity this, float thisdmg, int hittype, vector org, vector t { if (thisforce.z < 0) thisforce.z = 0; - this.move_velocity = this.move_velocity + thisforce + '0 0 100'; - this.move_flags &= ~FL_ONGROUND; + this.velocity = this.velocity + thisforce + '0 0 100'; + UNSET_ONGROUND(this); } NET_HANDLE(casings, bool isNew) @@ -151,11 +151,10 @@ NET_HANDLE(casings, bool isNew) casing.drawmask = MASK_NORMAL; casing.draw = Casing_Draw; - casing.move_origin = casing.origin; - casing.move_velocity = casing.velocity + 2 * prandomvec(); - casing.move_angles = casing.angles; - casing.move_avelocity = '0 250 0' + 100 * prandomvec(); - casing.move_movetype = MOVETYPE_BOUNCE; + if (isNew) IL_PUSH(g_drawables, casing); + casing.velocity = casing.velocity + 2 * prandomvec(); + casing.avelocity = '0 250 0' + 100 * prandomvec(); + set_movetype(casing, MOVETYPE_BOUNCE); settouch(casing, Casing_Touch); casing.move_time = time; casing.event_damage = Casing_Damage; diff --git a/qcsrc/common/effects/qc/casings.qh b/qcsrc/common/effects/qc/casings.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/effects/qc/casings.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index c1144d2616..71e1e2a7bc 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -1,15 +1,4 @@ -#ifndef DAMAGEEFFECTS_H -#define DAMAGEEFFECTS_H - -#ifdef CSQC -#include -#include -#include -#include -#include -#endif - -#endif +#include "damageeffects.qh" #ifdef IMPLEMENTATION @@ -81,7 +70,7 @@ void DamageEffect_Think(entity this) { // time is up or the player got gibbed / disconnected this.owner.total_damages = max(0, this.owner.total_damages - 1); - remove(this); + delete(this); return; } if(this.state && !this.owner.csqcmodel_isdead) @@ -89,7 +78,7 @@ void DamageEffect_Think(entity this) // if the player was dead but is now alive, it means he respawned // if so, clear his damage effects, or damages from his dead body will be copied back this.owner.total_damages = max(0, this.owner.total_damages - 1); - remove(this); + delete(this); return; } this.state = this.owner.csqcmodel_isdead; @@ -255,8 +244,8 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) if(it.damageforcescale) if(vdist(thisforce, !=, 0)) { - it.move_velocity = it.move_velocity + damage_explosion_calcpush(it.damageforcescale * thisforce, it.move_velocity, autocvar_g_balance_damagepush_speedfactor); - it.move_flags &= ~FL_ONGROUND; + it.velocity = it.velocity + damage_explosion_calcpush(it.damageforcescale * thisforce, it.velocity, autocvar_g_balance_damagepush_speedfactor); + UNSET_ONGROUND(it); } if(w_issilent) diff --git a/qcsrc/common/effects/qc/damageeffects.qh b/qcsrc/common/effects/qc/damageeffects.qh new file mode 100644 index 0000000000..2a1d587ca4 --- /dev/null +++ b/qcsrc/common/effects/qc/damageeffects.qh @@ -0,0 +1,9 @@ +#pragma once + +#ifdef CSQC +#include +#include +#include +#include +#include +#endif diff --git a/qcsrc/common/effects/qc/gibs.qc b/qcsrc/common/effects/qc/gibs.qc index bc82b50d0c..d1e3d9880d 100644 --- a/qcsrc/common/effects/qc/gibs.qc +++ b/qcsrc/common/effects/qc/gibs.qc @@ -46,7 +46,7 @@ void Violence_GibSplash_At(vector org, vector dir, float type, float amount, ent e.oldorigin_x = compressShortVector(e.velocity); FOREACH_CLIENT(IS_REAL_CLIENT(it), Violence_GibSplash_SendEntity(e, it, 0)); - remove(e); + delete(e); } void Violence_GibSplash(entity source, float type, float amount, entity attacker) @@ -70,7 +70,7 @@ void Violence_GibSplash(entity source, float type, float amount, entity attacker void Gib_Delete(entity this) { - remove(this); + delete(this); } string species_prefix(int specnum); @@ -111,13 +111,13 @@ void new_te_bloodshower (int ef, vector org, float explosionspeed, int howmany) __pointparticles(ef, org, randomvec() * explosionspeed, howmany / 50); } -void SUB_RemoveOnNoImpact(entity this) +void SUB_RemoveOnNoImpact(entity this, entity toucher) { if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) Gib_Delete(this); } -void Gib_Touch(entity this) +void Gib_Touch(entity this, entity toucher) { // TODO maybe bounce of walls, make more gibs, etc. @@ -170,7 +170,7 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector // TODO remove some gibs according to cl_nogibs gib = RubbleNew("gib"); - gib.move_movetype = MOVETYPE_BOUNCE; + set_movetype(gib, MOVETYPE_BOUNCE); gib.gravity = 1; gib.solid = SOLID_CORPSE; gib.cnt = specnum; @@ -180,6 +180,7 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector setsize (gib, '-8 -8 -8', '8 8 8'); gib.draw = Gib_Draw; + IL_PUSH(g_drawables, gib); if(destroyontouch) settouch(gib, Gib_Touch); else @@ -192,10 +193,9 @@ void TossGib (string mdlname, vector safeorg, vector org, vector vconst, vector org = trace_endpos; } - gib.move_origin = org; setorigin(gib, org); - gib.move_velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up; - gib.move_avelocity = prandomvec() * vlen(gib.move_velocity) * autocvar_cl_gibs_avelocity_scale; + gib.velocity = vconst * autocvar_cl_gibs_velocity_scale + vrand * autocvar_cl_gibs_velocity_random + '0 0 1' * autocvar_cl_gibs_velocity_up; + gib.avelocity = prandomvec() * vlen(gib.velocity) * autocvar_cl_gibs_avelocity_scale; gib.move_time = time; gib.damageforcescale = autocvar_cl_gibs_damageforcescale; @@ -311,7 +311,7 @@ NET_HANDLE(net_gibsplash, bool isNew) // no gibs in gentle mode, sorry break; } - remove(this); + delete(this); } #endif diff --git a/qcsrc/common/effects/qc/globalsound.qc b/qcsrc/common/effects/qc/globalsound.qc index a2653238e7..d60b2e8a65 100644 --- a/qcsrc/common/effects/qc/globalsound.qc +++ b/qcsrc/common/effects/qc/globalsound.qc @@ -6,7 +6,7 @@ #include #ifdef SVQC - #include + #include #endif REGISTER_NET_TEMP(globalsound) @@ -114,7 +114,7 @@ else { // Can this happen? - LOG_WARNINGF("Missing entcs data for player %d\n", who); + LOG_WARNF("Missing entcs data for player %d", who); sound8(e, o, chan, sample, vol, atten, 0, 0); } return true; @@ -146,7 +146,7 @@ else { // Can this happen? - LOG_WARNINGF("Missing entcs data for player %d\n", who); + LOG_WARNF("Missing entcs data for player %d", who); sound8(e, o, chan, sample, vol, atten, 0, 0); } return true; @@ -219,7 +219,7 @@ int fh = fopen(f, FILE_READ); if (fh < 0) { - LOG_WARNINGF("Player sound file not found: %s\n", f); + LOG_WARNF("Player sound file not found: %s", f); return; } for (string s; (s = fgets(fh)); ) @@ -227,7 +227,7 @@ int n = tokenize_console(s); if (n != 3) { - if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); + if (n != 0) LOG_WARNF("Invalid sound info line: %s", s); continue; } string file = argv(1); @@ -261,7 +261,7 @@ int fh = fopen(f, FILE_READ); if (fh < 0) { - if (strict) LOG_WARNINGF("Player sound file not found: %s\n", f); + if (strict) LOG_WARNF("Player sound file not found: %s", f); return false; } for (string s; (s = fgets(fh)); ) @@ -269,7 +269,7 @@ int n = tokenize_console(s); if (n != 3) { - if (n != 0) LOG_WARNINGF("Invalid sound info line: %s\n", s); + if (n != 0) LOG_WARNF("Invalid sound info line: %s", s); continue; } string key = argv(0); @@ -277,7 +277,7 @@ if (GetPlayerSoundSampleField_notFound) field = GetVoiceMessageSampleField(key); if (GetPlayerSoundSampleField_notFound) { - LOG_TRACEF("Invalid sound info field: %s\n", key); + LOG_TRACEF("Invalid sound info field: %s", key); continue; } string file = argv(1); @@ -316,7 +316,7 @@ #ifdef SVQC - void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, int voicetype, bool fake) + void _GlobalSound(entity this, entity gs, entity ps, string sample, int chan, float vol, int voicetype, bool fake) { if (gs == NULL && ps == NULL && sample == "") return; if(this.classname == "body") return; @@ -334,9 +334,9 @@ if (IS_REAL_CLIENT(msg_entity)) { float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; - if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); - else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); - else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); + if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); + else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); + else soundto(MSG_ONE, this, chan, sample, vol, atten); } } if (voicetype == VOICETYPE_LASTATTACKER_ONLY) break; @@ -355,15 +355,15 @@ MACRO_BEGIN \ { \ float atten = (msg_entity.cvar_cl_voice_directional == 1) ? ATTEN_MIN : ATTEN_NONE; \ - if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \ - else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \ - else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ + if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \ + else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \ + else soundto(MSG_ONE, this, chan, sample, vol, atten); \ } MACRO_END if (fake) { msg_entity = this; X(); } else { - FOREACH_CLIENT(IS_REAL_CLIENT(it) && (!teamplay || msg_entity.team == this.team), { + FOREACH_CLIENT(IS_REAL_CLIENT(it) && SAME_TEAM(it, this), { msg_entity = it; X(); }); @@ -390,9 +390,9 @@ ? bound(ATTEN_MIN, msg_entity.cvar_cl_voice_directional_taunt_attenuation, \ ATTEN_MAX) \ : ATTEN_NONE; \ - if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASEVOICE, atten); \ - else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASEVOICE, atten); \ - else soundto(MSG_ONE, this, chan, sample, VOL_BASEVOICE, atten); \ + if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, atten); \ + else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, atten); \ + else soundto(MSG_ONE, this, chan, sample, vol, atten); \ } \ } MACRO_END if (fake) @@ -415,15 +415,15 @@ msg_entity = this; if (fake) { - if (gs) globalsound(MSG_ONE, this, gs, r, chan, VOL_BASE, ATTEN_NORM); - else if (ps) playersound(MSG_ONE, this, ps, r, chan, VOL_BASE, ATTEN_NORM); - else soundto(MSG_ONE, this, chan, sample, VOL_BASE, ATTEN_NORM); + if (gs) globalsound(MSG_ONE, this, gs, r, chan, vol, ATTEN_NORM); + else if (ps) playersound(MSG_ONE, this, ps, r, chan, vol, ATTEN_NORM); + else soundto(MSG_ONE, this, chan, sample, vol, ATTEN_NORM); } else { - if (gs) globalsound(MSG_ALL, this, gs, r, chan, VOL_BASE, ATTEN_NORM); - else if (ps) playersound(MSG_ALL, this, ps, r, chan, VOL_BASE, ATTEN_NORM); - else _sound(this, chan, sample, VOL_BASE, ATTEN_NORM); + if (gs) globalsound(MSG_ALL, this, gs, r, chan, vol, ATTEN_NORM); + else if (ps) playersound(MSG_ALL, this, ps, r, chan, vol, ATTEN_NORM); + else _sound(this, chan, sample, vol, ATTEN_NORM); } break; } diff --git a/qcsrc/common/effects/qc/globalsound.qh b/qcsrc/common/effects/qc/globalsound.qh index 44925cd510..1df0b1a42b 100644 --- a/qcsrc/common/effects/qc/globalsound.qh +++ b/qcsrc/common/effects/qc/globalsound.qh @@ -1,5 +1,4 @@ -#ifndef GLOBALSOUND_H -#define GLOBALSOUND_H +#pragma once #ifdef SVQC /** Use new sound handling. TODO: use when sounds play correctly on clients */ @@ -122,10 +121,10 @@ entity GetVoiceMessage(string type); #ifdef SVQC - void _GlobalSound(entity this, entity gs, entity ps, string sample, float chan, float voicetype, bool fake); - #define GlobalSound(this, def, chan, voicetype) _GlobalSound(this, def, NULL, string_null, chan, voicetype, false) - #define GlobalSound_string(this, def, chan, voicetype) _GlobalSound(this, NULL, NULL, def, chan, voicetype, false) - #define PlayerSound(this, def, chan, voicetype) _GlobalSound(this, NULL, def, string_null, chan, voicetype, false) + void _GlobalSound(entity this, entity gs, entity ps, string sample, float chan, float vol, float voicetype, bool fake); + #define GlobalSound(this, def, chan, vol, voicetype) _GlobalSound(this, def, NULL, string_null, chan, vol, voicetype, false) + #define GlobalSound_string(this, def, chan, vol, voicetype) _GlobalSound(this, NULL, NULL, def, chan, vol, voicetype, false) + #define PlayerSound(this, def, chan, vol, voicetype) _GlobalSound(this, NULL, def, string_null, chan, vol, voicetype, false) #define VoiceMessage(this, def, msg) \ MACRO_BEGIN \ { \ @@ -137,7 +136,7 @@ entity GetVoiceMessage(string type); if (IS_SPEC(this) || IS_OBSERVER(this) || flood < 0) fake = true; \ else if (flood > 0) fake = false; \ else break; \ - _GlobalSound(this, NULL, VM, string_null, CH_VOICE, voicetype, fake); \ + _GlobalSound(this, NULL, VM, string_null, CH_VOICE, VOL_BASEVOICE, voicetype, fake); \ } MACRO_END #endif @@ -148,5 +147,3 @@ STATIC_INIT(allvoicesamples) FOREACH(PlayerSounds, it.instanceOfVoiceMessage, allvoicesamples = strcat(allvoicesamples, " ", it.m_playersoundstr)); allvoicesamples = strzone(substring(allvoicesamples, 1, -1)); } - -#endif diff --git a/qcsrc/common/effects/qc/modeleffects.qc b/qcsrc/common/effects/qc/modeleffects.qc index f577762906..8fbef5b587 100644 --- a/qcsrc/common/effects/qc/modeleffects.qc +++ b/qcsrc/common/effects/qc/modeleffects.qc @@ -103,7 +103,7 @@ void ModelEffect_Draw(entity this) this.alpha = this.cnt * bound(0, 1 - (time - this.lifetime) / this.fadetime, 1); if(this.alpha < ALPHA_MIN_VISIBLE) { - remove(this); + delete(this); return; } this.drawmask = MASK_NORMAL; @@ -156,8 +156,9 @@ NET_HANDLE(ENT_CLIENT_MODELEFFECT, bool isnew) e.cnt = ReadByte() / 255.0; // actually alpha e.draw = ModelEffect_Draw; + if (isnew) IL_PUSH(g_drawables, e); - if (!isnew) remove(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then + if (!isnew) delete(e); // yes, this IS stupid, but I don't need to duplicate all the read* stuff then return true; } #endif diff --git a/qcsrc/common/effects/qc/rubble.qh b/qcsrc/common/effects/qc/rubble.qh index 7848b7b1f3..dd3785b68e 100644 --- a/qcsrc/common/effects/qc/rubble.qh +++ b/qcsrc/common/effects/qc/rubble.qh @@ -1,41 +1,34 @@ -#ifndef RUBBLE_H -#define RUBBLE_H +#pragma once #ifdef CSQC entityclass(Rubble); class(Rubble).float creationtime; -void RubbleLimit(string cname, float limit, void(entity) deleteproc) -{ - entity e; - entity oldest; - float c; - float oldesttime; +IntrusiveList g_rubble; +STATIC_INIT(g_rubble) { g_rubble = IL_NEW(); } +void RubbleLimit(string cname, int limit, void(entity) deleteproc) +{ // remove rubble of the same type if it's at the limit // remove multiple rubble if the limit has been decreased while (1) { - e = findchain(classname, cname); - if (e == NULL) break; // walk the list and count the entities, find the oldest // initialize our search with the first entity - c = 1; - oldest = e; - oldesttime = e.creationtime; - e = e.chain; + int c = 0; + entity oldest = NULL; + float oldesttime = 0; // compare to all other matching entities - while (e) + IL_EACH(g_rubble, it.classname == cname, { - c = c + 1; - if (oldesttime > e.creationtime) + ++c; + if(!oldest || oldesttime > it.creationtime) { - oldesttime = e.creationtime; - oldest = e; + oldest = it; + oldesttime = it.creationtime; } - e = e.chain; - } + }); // stop if there are less than the limit already if (c <= limit) break; @@ -51,9 +44,8 @@ entity RubbleNew(string cname) entity e = spawn(); e.classname = cname; e.creationtime = time; + IL_PUSH(g_rubble, e); return e; } #endif - -#endif diff --git a/qcsrc/common/ent_cs.qc b/qcsrc/common/ent_cs.qc index cafef4868b..5a5c6acbeb 100644 --- a/qcsrc/common/ent_cs.qc +++ b/qcsrc/common/ent_cs.qc @@ -1,39 +1,53 @@ #include "ent_cs.qh" -// #define PROP(public, fld, sv, cl) -#define ENTCS_NETPROPS(PROP) \ - PROP(true, sv_entnum, \ - { WriteByte(chan, etof(player) - 1); }, \ - { this.sv_entnum = ReadByte(); }) \ +#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); \ +MACRO_END + +// #define PROP(public, fld, set, sv, cl) +#define ENTCS_NETPROPS(ent, PROP) PROP(false, sv_entnum, ENTCS_SET_NORMAL, {}, {}) /* sentinel */ \ + PROP(false, origin, ENTCS_SET_NORMAL, \ + { WriteCoord(chan, ent.origin.x); WriteCoord(chan, ent.origin.y); \ + WriteCoord(chan, ent.origin.z); }, \ + { ent.has_sv_origin = true; vector v; v.x = ReadCoord(); v.y = ReadCoord(); v.z = ReadCoord(); setorigin(ent, v); }) \ \ - PROP(false, origin, \ - { WriteShort(chan, this.origin.x); WriteShort(chan, this.origin.y); \ - WriteShort(chan, this.origin.z); }, \ - { this.has_sv_origin = true; vector v; v.x = ReadShort(); v.y = ReadShort(); v.z = ReadShort(); setorigin(this, v); }) \ + PROP(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; }) \ \ - PROP(false, angles_y, \ - { WriteByte(chan, this.angles.y / 360 * 256); }, \ - { vector v = '0 0 0'; v.y = ReadByte() / 256 * 360; this.angles = v; }) \ + PROP(false, health, ENTCS_SET_NORMAL, \ + { WriteByte(chan, bound(0, ent.health / 10, 255)); /* FIXME: use a better scale? */ }, \ + { ent.healthvalue = ReadByte() * 10; }) \ \ - PROP(false, health, \ - { WriteByte(chan, bound(0, this.health / 10, 255)); /* FIXME: use a better scale? */ }, \ - { this.healthvalue = ReadByte() * 10; }) \ + PROP(false, armorvalue, ENTCS_SET_NORMAL, \ + { WriteByte(chan, bound(0, ent.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, \ + { ent.armorvalue = ReadByte() * 10; }) \ \ - PROP(false, armorvalue, \ - { WriteByte(chan, bound(0, this.armorvalue / 10, 255)); /* FIXME: use a better scale? */ }, \ - { this.armorvalue = ReadByte() * 10; }) \ + PROP(true, netname, ENTCS_SET_MUTABLE_STRING, \ + { WriteString(chan, ent.netname); }, \ + { if (ent.netname) strunzone(ent.netname); ent.netname = strzone(ReadString()); }) \ \ - PROP(true, netname, \ - { WriteString(chan, this.netname); }, \ - { if (this.netname) strunzone(this.netname); this.netname = strzone(ReadString()); }) \ + PROP(true, model, ENTCS_SET_NORMAL, \ + { WriteString(chan, ent.model); }, \ + { if (ent.model) strunzone(ent.model); ent.model = strzone(ReadString()); }) \ \ - PROP(true, model, \ - { WriteString(chan, this.model); }, \ - { if (this.model) strunzone(this.model); this.model = strzone(ReadString()); }) \ + PROP(true, skin, ENTCS_SET_NORMAL, \ + { WriteByte(chan, ent.skin); }, \ + { ent.skin = ReadByte(); }) \ \ - PROP(true, skin, \ - { WriteByte(chan, this.skin); }, \ - { this.skin = ReadByte(); }) \ + PROP(true, clientcolors, ENTCS_SET_NORMAL, \ + { WriteByte(chan, ent.clientcolors); }, \ + { ent.colormap = ReadByte(); }) \ + \ + PROP(true, frags, ENTCS_SET_NORMAL, \ + { WriteShort(chan, ent.frags); }, \ + { ent.frags = ReadShort(); }) \ \ /**/ @@ -42,9 +56,14 @@ int ENTCS_PUBLICMASK = 0; STATIC_INIT(ENTCS_PUBLICMASK) { - int i = 1; - #define X(public, fld, sv, cl) { if (public) ENTCS_PUBLICMASK |= BIT(i); } i += 1; - ENTCS_NETPROPS(X); + int i = 0; + #define X(public, fld, set, sv, cl) { \ + if (public) { \ + ENTCS_PUBLICMASK |= BIT(i); \ + } \ + i += 1; \ + } + ENTCS_NETPROPS(this, X); #undef X if (i >= BITS(16 - 1)) LOG_FATAL("Exceeded ENTCS_NETPROPS limit"); } @@ -52,28 +71,34 @@ bool _entcs_send(entity this, entity to, int sf, int chan) { entity player = this.owner; - sf |= BIT(0) | BIT(1); - if (IS_PLAYER(to) || to.caplayer) // unless spectating, - { - bool same_team = (to == player) || (teamplay && player.team == to.team); - if (!same_team && !radar_showennemies) sf &= ENTCS_PUBLICMASK; // no private updates - } + sf |= BIT(0); // assume private + do { + if (!(IS_PLAYER(player))) + { + sf &= ENTCS_PUBLICMASK; // no private updates + break; + } + if (radar_showennemies) break; + if (SAME_TEAM(to, player)) break; + if (!(IS_PLAYER(to) || to.caplayer) && time > game_starttime) break; + sf &= ENTCS_PUBLICMASK; // no private updates + } while (0); sf |= this.m_forceupdate; this.m_forceupdate = 0; - bool valid = - IS_PLAYER(player) // player must be active - || player == to // player is self - ; - if (!valid) sf = 0; if (chan == MSG_ENTITY) WriteHeader(chan, ENT_CLIENT_ENTCS); else WriteHeader(chan, CLIENT_ENTCS); WriteByte(chan, etof(player) - 1); WriteShort(chan, sf); - int i = 1; - #define X(public, fld, sv, cl) { if (sf & BIT(i)) sv; } i += 1; - ENTCS_NETPROPS(X); + int i = 0; + #define X(public, fld, set, sv, cl) { \ + if (sf & BIT(i)) { \ + sv; \ + } \ + i += 1; \ + } + ENTCS_NETPROPS(this, X); #undef X return true; } @@ -87,15 +112,15 @@ { this.nextthink = time + 0.033333333333; // TODO: increase this to like 0.15 once the client can do smoothing entity o = this.owner; - int i = 1; - #define X(public, fld, sv, cl) \ - if (o.fld != this.fld) \ - { \ - this.fld = o.fld; \ + int i = 0; + #define X(public, fld, set, sv, cl) { \ + if (o.fld != this.fld) { \ + set(this.fld, o.fld); \ this.SendFlags |= BIT(i); \ } \ - i += 1; - ENTCS_NETPROPS(X); + i += 1; \ + } + ENTCS_NETPROPS(this, X); #undef X setorigin(this, this.origin); // relink } @@ -117,7 +142,7 @@ void entcs_detach(entity player) { if (!player.entcs) return; - remove(player.entcs); + delete(player.entcs); player.entcs = NULL; } @@ -130,7 +155,7 @@ int n = this.sv_entnum; entity e = entcs_receiver(n); entcs_receiver(n, NULL); - if (e != this) remove(e); + if (e != this) delete(e); } void entcs_think(entity this) @@ -157,37 +182,41 @@ entity e = entcs_receiver(n); if (e == NULL) { - if (this) - { - e = this; - } + if (!this) + // initial = temp + e = new_pure(entcs_receiver); else - { - e = new(entcs_receiver); - make_pure(e); - } - e.sv_entnum = n; + // initial = linked + e = this; setthink(e, entcs_think); entcs_receiver(n, e); } - else if (this && e != this) + else if (e != this && this) { - this.classname = "entcs_gc"; - this.sv_entnum = n; + // upgrade to linked + delete(e); + e = this; + setthink(e, entcs_think); + entcs_receiver(n, e); } - this = e; - InterpolateOrigin_Undo(this); - this.sv_entnum = n; + + InterpolateOrigin_Undo(e); + e.sv_entnum = n; int sf = ReadShort(); - this.has_sv_origin = false; - this.m_entcs_private = boolean(sf & BIT(0)); - int i = 1; - #define X(public, fld, sv, cl) { if (sf & BIT(i)) cl; } i += 1; - ENTCS_NETPROPS(X); + e.has_sv_origin = false; + e.m_entcs_private = boolean(sf & BIT(0)); + int i = 0; + #define X(public, fld, set, sv, cl) { \ + if (sf & BIT(i)) { \ + cl; \ + } \ + i += 1; \ + } + ENTCS_NETPROPS(e, X); #undef X - this.iflags |= IFLAG_ORIGIN; - InterpolateOrigin_Note(this); - getthink(this)(this); + e.iflags |= IFLAG_ORIGIN; + InterpolateOrigin_Note(e); + getthink(e)(e); return true; } diff --git a/qcsrc/common/ent_cs.qh b/qcsrc/common/ent_cs.qh index 1c72b351f7..65cdd83d3a 100644 --- a/qcsrc/common/ent_cs.qh +++ b/qcsrc/common/ent_cs.qh @@ -1,5 +1,4 @@ -#ifndef ENT_CS_H -#define ENT_CS_H +#pragma once REGISTER_NET_LINKED(ENT_CLIENT_ENTCS) REGISTER_NET_TEMP(CLIENT_ENTCS) @@ -59,10 +58,21 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) /** * @param i zero indexed player */ + .int frags; bool entcs_IsSpectating(int i) { bool unconnected = !playerslots[i].gotscores; - return unconnected || stof(getplayerkeyvalue(i, "frags")) == FRAGS_SPECTATOR; + entity e = entcs_receiver(i); + return unconnected || ((e) ? e.frags : stof(getplayerkeyvalue(i, "frags"))) == FRAGS_SPECTATOR; + } + + /** + * @param i zero indexed player + */ + int entcs_GetClientColors(int i) + { + entity e = entcs_receiver(i); + return e ? e.colormap : stof(getplayerkeyvalue(i, "colors")); } /** @@ -71,7 +81,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) */ int entcs_GetTeamColor(int i) { - return (!teamplay) ? 0 : stof(getplayerkeyvalue(i, "colors")) & 15; + return (!teamplay) ? 0 : entcs_GetClientColors(i) & 15; } /** @@ -98,7 +108,8 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) */ string entcs_GetName(int i) { - return ColorTranslateRGB(getplayerkeyvalue(i, "name")); + entity e = entcs_receiver(i); + return ColorTranslateRGB(e ? e.netname : getplayerkeyvalue(i, "name")); } /** @@ -127,7 +138,7 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) ? '1 1 1' : colormapPaletteColor(((e.colormap >= 1024) ? e.colormap - : stof(getplayerkeyvalue(e.colormap - 1, "colors"))) & 15, true) + : entcs_GetClientColors(e.colormap - 1)) & 15, true) ; } @@ -141,5 +152,3 @@ REGISTER_NET_TEMP(CLIENT_ENTCS) } #endif - -#endif diff --git a/qcsrc/common/gamemodes/_mod.inc b/qcsrc/common/gamemodes/_mod.inc index 0b779498b1..c3cec69dc5 100644 --- a/qcsrc/common/gamemodes/_mod.inc +++ b/qcsrc/common/gamemodes/_mod.inc @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/gamemodes/_mod.qh b/qcsrc/common/gamemodes/_mod.qh index a7b7a54af4..685c277b4e 100644 --- a/qcsrc/common/gamemodes/_mod.qh +++ b/qcsrc/common/gamemodes/_mod.qh @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/gamemodes/all.inc b/qcsrc/common/gamemodes/all.inc deleted file mode 100644 index bcdcd7c4c0..0000000000 --- a/qcsrc/common/gamemodes/all.inc +++ /dev/null @@ -1,2 +0,0 @@ -#include "gamemode/nexball/module.inc" -#include "gamemode/onslaught/module.inc" diff --git a/qcsrc/common/gamemodes/all.qc b/qcsrc/common/gamemodes/all.qc deleted file mode 100644 index f0fc7195a8..0000000000 --- a/qcsrc/common/gamemodes/all.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "all.qh" - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/common/gamemodes/all.qh b/qcsrc/common/gamemodes/all.qh deleted file mode 100644 index 62ba61696e..0000000000 --- a/qcsrc/common/gamemodes/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef GAMEMODES_ALL_H -#define GAMEMODES_ALL_H - -#include "all.inc" - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/_mod.inc b/qcsrc/common/gamemodes/gamemode/_mod.inc index 98fb4815c1..2fc2c40467 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/_mod.inc @@ -1 +1,4 @@ // generated file; do not modify + +#include +#include diff --git a/qcsrc/common/gamemodes/gamemode/_mod.qh b/qcsrc/common/gamemodes/gamemode/_mod.qh index 98fb4815c1..d799570126 100644 --- a/qcsrc/common/gamemodes/gamemode/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/_mod.qh @@ -1 +1,4 @@ // generated file; do not modify + +#include +#include diff --git a/qcsrc/common/gamemodes/gamemode/nexball/module.inc b/qcsrc/common/gamemodes/gamemode/nexball/module.inc deleted file mode 100644 index 0d809629de..0000000000 --- a/qcsrc/common/gamemodes/gamemode/nexball/module.inc +++ /dev/null @@ -1,2 +0,0 @@ -#include "nexball.qc" -#include "weapon.qc" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc index 46b4e03455..d67ce5ff07 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc @@ -1,6 +1,5 @@ #include "nexball.qh" -#ifdef IMPLEMENTATION #ifdef CSQC int autocvar_cl_eventchase_nexball = 1; @@ -35,6 +34,7 @@ float autocvar_g_nexball_football_bouncestop; bool autocvar_g_nexball_radar_showallplayers; bool autocvar_g_nexball_sound_bounce; int autocvar_g_nexball_trail_color; +bool autocvar_g_nexball_playerclip_collisions = true; float autocvar_g_nexball_safepass_turnrate; float autocvar_g_nexball_safepass_maxdist; @@ -52,8 +52,8 @@ float autocvar_g_balance_nexball_secondary_lifetime; float autocvar_g_balance_nexball_secondary_refire; float autocvar_g_balance_nexball_secondary_speed; -void basketball_touch(entity this); -void football_touch(entity this); +void basketball_touch(entity this, entity toucher); +void football_touch(entity this, entity toucher); void ResetBall(entity this); const int NBM_NONE = 0; const int NBM_FOOTBALL = 2; @@ -70,9 +70,7 @@ float OtherTeam(float t) //works only if there are two teams on the map! } const float ST_NEXBALL_GOALS = 1; -const float SP_NEXBALL_GOALS = 4; -const float SP_NEXBALL_FAULTS = 5; -void nb_ScoreRules(float teams) +void nb_ScoreRules(int teams) { ScoreRules_basics(teams, 0, 0, true); ScoreInfo_SetLabel_TeamScore( ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); @@ -106,9 +104,9 @@ void nexball_setstatus(entity this) { if(this.ballcarried.teamtime && (this.ballcarried.teamtime < time)) { - bprint("The ", Team_ColoredFullName(this.team), " held the ball for too long.\n"); - DropBall(this.ballcarried, this.ballcarried.owner.origin, '0 0 0'); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD)); entity e = this.ballcarried; + DropBall(this.ballcarried, this.ballcarried.owner.origin, '0 0 0'); ResetBall(e); } else @@ -178,7 +176,7 @@ void GiveBall(entity plyr, entity ball) ball.effects &= ~autocvar_g_nexball_basketball_effects_default; ball.velocity = '0 0 0'; - ball.movetype = MOVETYPE_NONE; + set_movetype(ball, MOVETYPE_NONE); settouch(ball, func_null); ball.effects |= EF_NOSHADOW; ball.scale = 1; // scale down. @@ -209,7 +207,7 @@ void DropBall(entity ball, vector org, vector vel) setattachment(ball, NULL, ""); setorigin(ball, org); - ball.movetype = MOVETYPE_BOUNCE; + set_movetype(ball, MOVETYPE_BOUNCE); UNSET_ONGROUND(ball); ball.scale = ball_scale; ball.velocity = vel; @@ -237,7 +235,7 @@ void InitBall(entity this) { if(gameover) return; UNSET_ONGROUND(this); - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); if(this.classname == "nexball_basketball") settouch(this, basketball_touch); else if(this.classname == "nexball_football") @@ -258,10 +256,10 @@ void ResetBall(entity this) if(this.cnt < 2) // step 1 { if(time == this.teamtime) - bprint("The ", Team_ColoredFullName(this.team), " held the ball for too long.\n"); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD)); settouch(this, func_null); - this.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); this.velocity = '0 0 0'; // just in case? if(!this.cnt) LogNB("resetidle", NULL); @@ -283,15 +281,15 @@ void ResetBall(entity this) vtos(this.origin - this.spawnorigin), " Velocity: ", vtos(this.velocity), "\n"); this.velocity = '0 0 0'; setorigin(this, this.spawnorigin); // make sure it's positioned correctly anyway - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); setthink(this, InitBall); this.nextthink = max(time, game_starttime) + autocvar_g_nexball_delay_start; } } -void football_touch(entity this) +void football_touch(entity this, entity toucher) { - if(other.solid == SOLID_BSP) + if(toucher.solid == SOLID_BSP) { if(time > this.lastground + 0.1) { @@ -302,54 +300,54 @@ void football_touch(entity this) this.nextthink = time + autocvar_g_nexball_delay_idle; return; } - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; - if(other.health < 1) + if(toucher.health < 1) return; if(!this.cnt) this.nextthink = time + autocvar_g_nexball_delay_idle; - this.pusher = other; - this.team = other.team; + this.pusher = toucher; + this.team = toucher.team; if(autocvar_g_nexball_football_physics == -1) // MrBougo try 1, before decompiling Rev's original { - if(other.velocity) - this.velocity = other.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up; + if(toucher.velocity) + this.velocity = toucher.velocity * 1.5 + '0 0 1' * autocvar_g_nexball_football_boost_up; } else if(autocvar_g_nexball_football_physics == 1) // MrBougo's modded Rev style: partially independant of the height of the aiming point { - makevectors(other.v_angle); - this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up; + makevectors(toucher.v_angle); + this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + '0 0 1' * autocvar_g_nexball_football_boost_up; } else if(autocvar_g_nexball_football_physics == 2) // 2nd mod try: totally independant. Really playable! { - makevectors(other.v_angle.y * '0 1 0'); - this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; + makevectors(toucher.v_angle.y * '0 1 0'); + this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; } else // Revenant's original style (from the original mod's disassembly, acknowledged by Revenant) { - makevectors(other.v_angle); - this.velocity = other.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; + makevectors(toucher.v_angle); + this.velocity = toucher.velocity + v_forward * autocvar_g_nexball_football_boost_forward + v_up * autocvar_g_nexball_football_boost_up; } this.avelocity = -250 * v_forward; // maybe there is a way to make it look better? } -void basketball_touch(entity this) +void basketball_touch(entity this, entity toucher) { - if(other.ballcarried) + if(toucher.ballcarried) { - football_touch(this); + football_touch(this, toucher); return; } - if(!this.cnt && IS_PLAYER(other) && !STAT(FROZEN, other) && !IS_DEAD(other) && (other != this.nb_dropper || time > this.nb_droptime + autocvar_g_nexball_delay_collect)) + 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(other.health <= 0) + if(toucher.health <= 0) return; - LogNB("caught", other); - GiveBall(other, this); + LogNB("caught", toucher); + GiveBall(toucher, this); } - else if(other.solid == SOLID_BSP) + else if(toucher.solid == SOLID_BSP) { _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); if(this.velocity && !this.cnt) @@ -357,26 +355,26 @@ void basketball_touch(entity this) } } -void GoalTouch(entity this) +void GoalTouch(entity this, entity toucher) { entity ball; float isclient, pscore, otherteam; string pname; if(gameover) return; - if((this.spawnflags & GOAL_TOUCHPLAYER) && other.ballcarried) - ball = other.ballcarried; + if((this.spawnflags & GOAL_TOUCHPLAYER) && toucher.ballcarried) + ball = toucher.ballcarried; else - ball = other; + ball = toucher; if(ball.classname != "nexball_basketball") if(ball.classname != "nexball_football") return; if((!ball.pusher && this.team != GOAL_OUT) || ball.cnt) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); - if(nb_teams == 2) + if(NumTeams(nb_teams) == 2) otherteam = OtherTeam(ball.team); else otherteam = 0; @@ -395,7 +393,7 @@ void GoalTouch(entity this) else if(this.team == GOAL_FAULT) { LogNB("fault", ball.pusher); - if(nb_teams == 2) + if(NumTeams(nb_teams) == 2) bprint(Team_ColoredFullName(otherteam), " gets a point due to ", pname, "^7's silliness.\n"); else bprint(Team_ColoredFullName(ball.team), " loses a point due to ", pname, "^7's silliness.\n"); @@ -421,7 +419,7 @@ void GoalTouch(entity this) if(ball.team && pscore) { - if(nb_teams == 2 && pscore < 0) + if(NumTeams(nb_teams) == 2 && pscore < 0) TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore); else TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore); @@ -453,7 +451,7 @@ spawnfunc(nexball_team) { if(!g_nexball) { - remove(this); + delete(this); return; } this.team = this.cnt + 1; @@ -461,12 +459,12 @@ spawnfunc(nexball_team) void nb_spawnteam(string teamname, float teamcolor) { - LOG_TRACE("^2spawned team ", teamname, "\n"); + LOG_TRACE("^2spawned team ", teamname); entity e = new(nexball_team); e.netname = teamname; e.cnt = teamcolor; e.team = e.cnt + 1; - nb_teams += 1; + //nb_teams += 1; } void nb_spawnteams() @@ -481,6 +479,7 @@ void nb_spawnteams() if(!t_red) { nb_spawnteam("Red", e.team-1) ; + nb_teams |= BIT(0); t_red = true; } break; @@ -489,6 +488,7 @@ void nb_spawnteams() { nb_spawnteam("Blue", e.team-1) ; t_blue = true; + nb_teams |= BIT(1); } break; case NUM_TEAM_3: @@ -496,6 +496,7 @@ void nb_spawnteams() { nb_spawnteam("Yellow", e.team-1); t_yellow = true; + nb_teams |= BIT(2); } break; case NUM_TEAM_4: @@ -503,6 +504,7 @@ void nb_spawnteams() { nb_spawnteam("Pink", e.team-1) ; t_pink = true; + nb_teams |= BIT(3); } break; } @@ -523,7 +525,7 @@ void nb_delayedinit(entity this) void SpawnBall(entity this) { - if(!g_nexball) { remove(this); return; } + if(!g_nexball) { delete(this); return; } // balls += 4; // using the remaining bits to count balls will leave more than the max edict count, so it's fine @@ -549,7 +551,10 @@ void SpawnBall(entity this) this.glow_trail = true; } - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); + + if(autocvar_g_nexball_playerclip_collisions) + this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID | DPCONTENTS_PLAYERCLIP; if(!autocvar_g_nexball_sound_bounce) this.noise = ""; @@ -609,11 +614,10 @@ spawnfunc(nexball_football) SpawnBall(this); } -float nb_Goal_Customize(entity this) +bool nb_Goal_Customize(entity this, entity client) { - entity e, wp_owner; - e = WaypointSprite_getviewentity(other); - wp_owner = this.owner; + entity e = WaypointSprite_getviewentity(client); + entity wp_owner = this.owner; if(SAME_TEAM(e, wp_owner)) { return false; } return true; @@ -621,7 +625,7 @@ float nb_Goal_Customize(entity this) void SpawnGoal(entity this) { - if(!g_nexball) { remove(this); return; } + if(!g_nexball) { delete(this); return; } EXACTTRIGGER_INIT; @@ -730,45 +734,45 @@ void W_Nexball_Think(entity this) this.nextthink = time; } -void W_Nexball_Touch(entity this) +void W_Nexball_Touch(entity this, entity toucher) { entity ball, attacker; attacker = this.owner; //this.think = func_null; //this.enemy = NULL; - PROJECTILE_TOUCH(this); - if(attacker.team != other.team || autocvar_g_nexball_basketball_teamsteal) - if((ball = other.ballcarried) && !STAT(FROZEN, other) && !IS_DEAD(other) && (IS_PLAYER(attacker))) + PROJECTILE_TOUCH(this, toucher); + if(attacker.team != toucher.team || autocvar_g_nexball_basketball_teamsteal) + if((ball = toucher.ballcarried) && !STAT(FROZEN, toucher) && !IS_DEAD(toucher) && (IS_PLAYER(attacker))) { - other.velocity = other.velocity + normalize(this.velocity) * other.damageforcescale * autocvar_g_balance_nexball_secondary_force; - UNSET_ONGROUND(other); + toucher.velocity = toucher.velocity + normalize(this.velocity) * toucher.damageforcescale * autocvar_g_balance_nexball_secondary_force; + UNSET_ONGROUND(toucher); if(!attacker.ballcarried) { LogNB("stole", attacker); - _sound(other, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM); + _sound(toucher, CH_TRIGGER, ball.noise2, VOL_BASE, ATTEN_NORM); - if(SAME_TEAM(attacker, other) && time > attacker.teamkill_complain) + if(SAME_TEAM(attacker, toucher) && time > attacker.teamkill_complain) { attacker.teamkill_complain = time + 5; attacker.teamkill_soundtime = time + 0.4; - attacker.teamkill_soundsource = other; + attacker.teamkill_soundsource = toucher; } - GiveBall(attacker, other.ballcarried); + GiveBall(attacker, toucher.ballcarried); } } - remove(this); + delete(this); } -void W_Nexball_Attack(entity actor, float t) +void W_Nexball_Attack(entity actor, .entity weaponentity, float t) { entity ball; float mul, mi, ma; if(!(ball = actor.ballcarried)) return; - W_SetupShot(actor, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0); + W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0); tracebox(w_shotorg, BALL_MINS, BALL_MAXS, w_shotorg, MOVE_WORLDONLY, NULL); if(trace_startsolid) { @@ -799,12 +803,12 @@ void W_Nexball_Attack(entity actor, float t) vector trigger_push_calculatevelocity(vector org, entity tgt, float ht); -void W_Nexball_Attack2(entity actor) +void W_Nexball_Attack2(entity actor, .entity weaponentity) { if(actor.ballcarried.enemy) { entity _ball = actor.ballcarried; - W_SetupShot(actor, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0); + W_SetupShot(actor, weaponentity, false, 4, SND_NB_SHOOT1, CH_WEAPON_A, 0); DropBall(_ball, w_shotorg, trigger_push_calculatevelocity(_ball.origin, _ball.enemy, 32)); setthink(_ball, W_Nexball_Think); _ball.nextthink = time; @@ -814,12 +818,12 @@ void W_Nexball_Attack2(entity actor) if(!autocvar_g_nexball_tackling) return; - W_SetupShot(actor, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0); + W_SetupShot(actor, weaponentity, false, 2, SND_NB_SHOOT2, CH_WEAPON_A, 0); entity missile = new(ballstealer); missile.owner = actor; - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); //setmodel(missile, "models/elaser.mdl"); // precision set below @@ -834,11 +838,13 @@ void W_Nexball_Attack2(entity actor) missile.effects = EF_BRIGHTFIELD | EF_LOWPRECISION; missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); CSQCProjectile(missile, true, PROJECTILE_ELECTRO, true); } -float ball_customize(entity this) +bool ball_customize(entity this, entity client) { if(!this.owner) { @@ -848,7 +854,7 @@ float ball_customize(entity this) return true; } - if(other == this.owner) + if(client == this.owner) { this.scale = autocvar_g_nexball_viewmodel_scale; if(this.enemy) @@ -879,19 +885,19 @@ METHOD(BallStealer, wr_think, void(BallStealer thiswep, entity actor, .entity we } else { - W_Nexball_Attack(actor, -1); + W_Nexball_Attack(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); + W_Nexball_Attack2(actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, autocvar_g_balance_nexball_secondary_animtime, w_ready); } if(!(fire & 1) && actor.metertime && actor.ballcarried) { - W_Nexball_Attack(actor, time - actor.metertime); + W_Nexball_Attack(actor, weaponentity, time - actor.metertime); // DropBall or stealing will set metertime back to 0 weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, autocvar_g_balance_nexball_primary_animtime, w_ready); } @@ -1073,7 +1079,18 @@ MUTATOR_HOOKFUNCTION(nb, FilterItem) return false; } -MUTATOR_HOOKFUNCTION(nb, GetTeamCount) +MUTATOR_HOOKFUNCTION(nb, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + if(item.weapon && toucher.ballcarried) + return MUT_ITEMTOUCH_RETURN; // no new weapons for you, mister! + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(nb, CheckAllowedTeams) { M_ARGV(1, string) = "nexball_team"; return true; @@ -1144,4 +1161,3 @@ REGISTER_MUTATOR(nb, g_nexball) } #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh index 9dd8042be3..53797d2bc6 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh +++ b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh @@ -1,5 +1,4 @@ -#ifndef GAMEMODE_NEXBALL_H -#define GAMEMODE_NEXBALL_H +#pragma once #ifdef SVQC //EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME @@ -35,4 +34,3 @@ float nb_teams; .float teamtime; #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc index c6aa4be215..f207263535 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qc @@ -1,12 +1 @@ -#ifndef GAMEMODE_NEXBALL_WEAPON_H -#define GAMEMODE_NEXBALL_WEAPON_H - -CLASS(BallStealer, PortoLaunch) -/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(BallStealer, impulse, int, 0); -/* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); -/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); -ENDCLASS(BallStealer) -REGISTER_WEAPON(NEXBALL, NEW(BallStealer)); - -#endif +#include "weapon.qh" diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh new file mode 100644 index 0000000000..73b887260c --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh @@ -0,0 +1,9 @@ +#pragma once + +CLASS(BallStealer, PortoLaunch) +/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(BallStealer, impulse, int, 0); +/* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); +/* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); +ENDCLASS(BallStealer) +REGISTER_WEAPON(NEXBALL, NEW(BallStealer)); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc index 334eb561ee..ca8c83c19a 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.inc @@ -1,6 +1,19 @@ // generated file; do not modify -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include -#include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh index e895495581..bb95416b31 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/_mod.qh @@ -1,6 +1,19 @@ // generated file; do not modify -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include -#include -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc index 8389b22dae..ee348fdbc6 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qc @@ -74,9 +74,9 @@ void cpicon_draw(entity this) } this.angles_x = this.punchangle_x; - this.angles_y = this.punchangle_y + this.move_angles_y; + this.angles_y = this.punchangle_y + this.angles_y; this.angles_z = this.punchangle_z; - this.move_angles_y = this.move_angles_y + 45 * frametime; + this.angles_y = this.angles_y + 45 * frametime; } setorigin(this, this.cp_origin + this.cp_bob_origin + this.cp_bob_dmg); @@ -104,7 +104,7 @@ void cpicon_damage(entity this, float hp) setsize(this, CPICON_MIN, CPICON_MAX); } -void cpicon_construct(entity this) +void cpicon_construct(entity this, bool isnew) { this.netname = "Control Point Icon"; @@ -117,17 +117,15 @@ void cpicon_construct(entity this) setmodel(this.icon_realmodel, MDL_Null); setorigin(this.icon_realmodel, this.origin); setsize(this.icon_realmodel, CPICON_MIN, CPICON_MAX); - this.icon_realmodel.movetype = MOVETYPE_NOCLIP; + set_movetype(this.icon_realmodel, MOVETYPE_NOCLIP); this.icon_realmodel.solid = SOLID_NOT; - this.icon_realmodel.move_origin = this.icon_realmodel.origin; } if(this.iscaptured) { this.icon_realmodel.solid = SOLID_BBOX; } - this.move_movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); this.solid = SOLID_NOT; - this.movetype = MOVETYPE_NOCLIP; - this.move_origin = this.origin; + set_movetype(this, MOVETYPE_NOCLIP); this.move_time = time; this.drawmask = MASK_NORMAL; this.alpha = 1; @@ -135,6 +133,9 @@ void cpicon_construct(entity this) this.cp_origin = this.origin; this.cp_bob_origin = '0 0 0.1'; this.cp_bob_spd = 0; + + if(isnew) + IL_PUSH(g_drawables, this); } .vector glowmod; @@ -176,7 +177,7 @@ NET_HANDLE(ENT_CLIENT_CONTROLPOINT_ICON, bool isnew) this.count = (this.health - this.max_health) * frametime; cpicon_changeteam(this); - cpicon_construct(this); + cpicon_construct(this, isnew); } if(sf & CPSF_STATUS) diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh index 15586ea5dc..d5437338e3 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_controlpoint.qh @@ -1,10 +1,7 @@ -#ifndef CLIENT_CONTROLPOINT_H -#define CLIENT_CONTROLPOINT_H +#pragma once const vector CPICON_MIN = '-32 -32 -9'; const vector CPICON_MAX = '32 32 25'; const int CPSF_STATUS = 4; const int CPSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc index 03e81f4bde..ac7a066ab1 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qc @@ -14,7 +14,8 @@ void ons_generator_ray_draw(entity this) if(this.count > 10) { - remove(this); + IL_REMOVE(g_drawables, this); + delete(this); return; } @@ -33,13 +34,13 @@ void ons_generator_ray_spawn(vector org) setmodel(e, MDL_ONS_RAY); setorigin(e, org); e.angles = randomvec() * 360; - e.move_origin = org; - e.movetype = MOVETYPE_NONE; + set_movetype(e, MOVETYPE_NONE); e.alpha = 0; e.scale = random() * 5 + 8; e.move_time = time + 0.05; e.drawmask = MASK_NORMAL; e.draw = ons_generator_ray_draw; + IL_PUSH(g_drawables, e); } void generator_draw(entity this) @@ -143,19 +144,24 @@ void generator_damage(entity this, float hp) setsize(this, GENERATOR_MIN, GENERATOR_MAX); } -void generator_construct(entity this) +void generator_construct(entity this, bool isnew) { this.netname = "Generator"; this.classname = "onslaught_generator"; + if(isnew) + { + IL_PUSH(g_onsgenerators, this); + IL_PUSH(g_drawables, this); + } + setorigin(this, this.origin); setmodel(this, MDL_ONS_GEN); setsize(this, GENERATOR_MIN, GENERATOR_MAX); - this.move_movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); this.solid = SOLID_BBOX; - this.movetype = MOVETYPE_NOCLIP; - this.move_origin = this.origin; + set_movetype(this, MOVETYPE_NOCLIP); this.move_time = time; this.drawmask = MASK_NORMAL; this.alpha = 1; @@ -200,7 +206,7 @@ NET_HANDLE(ENT_CLIENT_GENERATOR, bool isnew) this.count = 40; generator_changeteam(this); - generator_construct(this); + generator_construct(this, isnew); } if(sf & GSF_STATUS) diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh index 3c0cf28697..49524687f4 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/cl_generator.qh @@ -1,9 +1,7 @@ -#ifndef CLIENT_GENERATOR_H -#define CLIENT_GENERATOR_H +#pragma once + const vector GENERATOR_MIN = '-52 -52 -14'; const vector GENERATOR_MAX = '52 52 75'; const int GSF_STATUS = 4; const int GSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc new file mode 100644 index 0000000000..b21f5fd228 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qc @@ -0,0 +1 @@ +#include "controlpoint.qh" diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/controlpoint.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc new file mode 100644 index 0000000000..f9415f619f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qc @@ -0,0 +1 @@ +#include "generator.qh" diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/generator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc b/qcsrc/common/gamemodes/gamemode/onslaught/module.inc deleted file mode 100644 index fee33b14e2..0000000000 --- a/qcsrc/common/gamemodes/gamemode/onslaught/module.inc +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef ONS_CONSTANTS - #define ONS_CONSTANTS - REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR) - REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON) -#endif - -#if defined(SVQC) - #include "onslaught.qc" - #ifndef IMPLEMENTATION - #include "sv_controlpoint.qh" - #include "sv_generator.qh" - #else - #include "sv_controlpoint.qc" - #include "sv_generator.qc" - #endif -#elif defined(CSQC) - #ifndef IMPLEMENTATION - #include "cl_controlpoint.qh" - #include "cl_generator.qh" - #else - #include "cl_controlpoint.qc" - #include "cl_generator.qc" - #endif -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc index 2c52982709..afbd318429 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qc @@ -1,2277 +1,70 @@ -#ifndef GAMEMODE_ONSLAUGHT_H -#define GAMEMODE_ONSLAUGHT_H +#include "onslaught.qh" -float autocvar_g_onslaught_point_limit; -void ons_Initialize(); - -REGISTER_MUTATOR(ons, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ons_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ons_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return false; -} - -#ifdef SVQC - -.entity ons_toucher; // player who touched the control point - -// control point / generator constants -const float ONS_CP_THINKRATE = 0.2; -const float GEN_THINKRATE = 1; -#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) -const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); -const vector CPICON_OFFSET = ('0 0 96'); - -// list of generators on the map -entity ons_worldgeneratorlist; -.entity ons_worldgeneratornext; -.entity ons_stalegeneratornext; - -// list of control points on the map -entity ons_worldcplist; -.entity ons_worldcpnext; -.entity ons_stalecpnext; - -// list of links on the map -entity ons_worldlinklist; -.entity ons_worldlinknext; -.entity ons_stalelinknext; - -// definitions -.entity sprite; -.string target2; -.int iscaptured; -.int islinked; -.int isshielded; -.float lasthealth; -.int lastteam; -.int lastshielded; -.int lastcaptured; - -.bool waslinked; - -bool ons_stalemate; - -.float teleport_antispam; - -.bool ons_roundlost = _STAT(ROUNDLOST); - -// waypoint sprites -.entity bot_basewaypoint; // generator waypointsprite - -.bool isgenneighbor[17]; -.bool iscpneighbor[17]; -float ons_notification_time[17]; - -.float ons_overtime_damagedelay; - -.vector ons_deathloc; - -.entity ons_spawn_by; - -// declarations for functions used outside gamemode_onslaught.qc -void ons_Generator_UpdateSprite(entity e); -void ons_ControlPoint_UpdateSprite(entity e); -bool ons_ControlPoint_Attackable(entity cp, int teamnumber); - -// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet -float ons_captureshield_force; // push force of the shield - -// bot player logic -const int HAVOCBOT_ONS_ROLE_NONE = 0; -const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; -const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; -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); -void havocbot_role_ons_offense(entity this); -void havocbot_role_ons_assistant(entity this); - -void havocbot_ons_reset_role(entity this); -void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius); -void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius); - -// score rule declarations -const int ST_ONS_CAPS = 1; -const int SP_ONS_CAPS = 4; -const int SP_ONS_TAKES = 6; - -#endif -#endif - -#ifdef IMPLEMENTATION - -#include "sv_controlpoint.qh" -#include "sv_generator.qh" - -bool g_onslaught; - -float autocvar_g_onslaught_teleport_wait; -bool autocvar_g_onslaught_spawn_at_controlpoints; -bool autocvar_g_onslaught_spawn_at_generator; -float autocvar_g_onslaught_cp_proxydecap; -float autocvar_g_onslaught_cp_proxydecap_distance = 512; -float autocvar_g_onslaught_cp_proxydecap_dps = 100; -float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; -float autocvar_g_onslaught_spawn_at_controlpoints_random; -float autocvar_g_onslaught_spawn_at_generator_chance; -float autocvar_g_onslaught_spawn_at_generator_random; -float autocvar_g_onslaught_cp_buildhealth; -float autocvar_g_onslaught_cp_buildtime; -float autocvar_g_onslaught_cp_health; -float autocvar_g_onslaught_cp_regen; -float autocvar_g_onslaught_gen_health; -float autocvar_g_onslaught_shield_force = 100; -float autocvar_g_onslaught_allow_vehicle_touch; -float autocvar_g_onslaught_round_timelimit; -float autocvar_g_onslaught_warmup; -float autocvar_g_onslaught_teleport_radius; -float autocvar_g_onslaught_spawn_choose; -float autocvar_g_onslaught_click_radius; - -void FixSize(entity e); - -// ======================= -// CaptureShield Functions -// ======================= - -bool ons_CaptureShield_Customize(entity this) -{ - entity e = WaypointSprite_getviewentity(other); - - if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; } - if(SAME_TEAM(this, e)) { return false; } - - return true; -} - -void ons_CaptureShield_Touch(entity this) -{ - if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, other.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; } - if(!IS_PLAYER(other)) { return; } - if(SAME_TEAM(other, this)) { return; } - - vector mymid = (this.absmin + this.absmax) * 0.5; - vector othermid = (other.absmin + other.absmax) * 0.5; - - Damage(other, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ons_captureshield_force); - - if(IS_REAL_CLIENT(other)) - { - play2(other, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - - if(this.enemy.classname == "onslaught_generator") - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); - else - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); - } -} - -void ons_CaptureShield_Reset(entity this) -{ - this.colormap = this.enemy.colormap; - this.team = this.enemy.team; -} - -void ons_CaptureShield_Spawn(entity generator, bool is_generator) -{ - entity shield = new(ons_captureshield); - - shield.enemy = generator; - shield.team = generator.team; - shield.colormap = generator.colormap; - shield.reset = ons_CaptureShield_Reset; - settouch(shield, ons_CaptureShield_Touch); - setcefc(shield, ons_CaptureShield_Customize); - shield.effects = EF_ADDITIVE; - shield.movetype = MOVETYPE_NOCLIP; - shield.solid = SOLID_TRIGGER; - shield.avelocity = '7 0 11'; - shield.scale = 1; - shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); - - precache_model(shield.model); - setorigin(shield, generator.origin); - _setmodel(shield, shield.model); - setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); -} - - -// ========== -// Junk Pile -// ========== - -void setmodel_fixsize(entity e, Model m) -{ - setmodel(e, m); - FixSize(e); -} - -void onslaught_updatelinks() -{ - entity l; - // first check if the game has ended - LOG_DEBUG("--- updatelinks ---\n"); - // mark generators as being shielded and networked - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.iscaptured) - LOG_DEBUG(strcat(etos(l), " (generator) belongs to team ", ftos(l.team), "\n")); - else - LOG_DEBUG(strcat(etos(l), " (generator) is destroyed\n")); - l.islinked = l.iscaptured; - l.isshielded = l.iscaptured; - l.sprite.SendFlags |= 16; - } - // mark points as shielded and not networked - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.islinked = false; - l.isshielded = true; - int i; - for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } - LOG_DEBUG(strcat(etos(l), " (point) belongs to team ", ftos(l.team), "\n")); - l.sprite.SendFlags |= 16; - } - // flow power outward from the generators through the network - bool stop = false; - while (!stop) - { - stop = true; - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - // if both points are captured by the same team, and only one of - // them is powered, mark the other one as powered as well - if (l.enemy.iscaptured && l.goalentity.iscaptured) - if (l.enemy.islinked != l.goalentity.islinked) - if(SAME_TEAM(l.enemy, l.goalentity)) - { - if (!l.goalentity.islinked) - { - stop = false; - l.goalentity.islinked = true; - LOG_DEBUG(strcat(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)\n")); - } - else if (!l.enemy.islinked) - { - stop = false; - l.enemy.islinked = true; - LOG_DEBUG(strcat(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)\n")); - } - } - } - } - // now that we know which points are powered we can mark their neighbors - // as unshielded if team differs - for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) - { - if (l.goalentity.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - LOG_DEBUG(strcat(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)\n")); - l.enemy.isshielded = false; - } - if(l.goalentity.classname == "onslaught_generator") - l.enemy.isgenneighbor[l.goalentity.team] = true; - else - l.enemy.iscpneighbor[l.goalentity.team] = true; - } - if (l.enemy.islinked) - { - if(DIFF_TEAM(l.goalentity, l.enemy)) - { - LOG_DEBUG(strcat(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)\n")); - l.goalentity.isshielded = false; - } - if(l.enemy.classname == "onslaught_generator") - l.goalentity.isgenneighbor[l.enemy.team] = true; - else - l.goalentity.iscpneighbor[l.enemy.team] = true; - } - } - // now update the generators - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - if (l.isshielded) - { - LOG_DEBUG(strcat(etos(l), " (generator) is shielded\n")); - l.takedamage = DAMAGE_NO; - l.bot_attack = false; - } - else - { - LOG_DEBUG(strcat(etos(l), " (generator) is not shielded\n")); - l.takedamage = DAMAGE_AIM; - l.bot_attack = true; - } - - ons_Generator_UpdateSprite(l); - } - // now update the takedamage and alpha variables on control point icons - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - if (l.isshielded) - { - LOG_DEBUG(strcat(etos(l), " (point) is shielded\n")); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_NO; - l.goalentity.bot_attack = false; - } - } - else - { - LOG_DEBUG(strcat(etos(l), " (point) is not shielded\n")); - if (l.goalentity) - { - l.goalentity.takedamage = DAMAGE_AIM; - l.goalentity.bot_attack = true; - } - } - ons_ControlPoint_UpdateSprite(l); - } - l = findchain(classname, "ons_captureshield"); - while(l) - { - l.team = l.enemy.team; - l.colormap = l.enemy.colormap; - l = l.chain; - } -} - - -// =================== -// Main Link Functions -// =================== - -bool ons_Link_Send(entity this, entity to, int sendflags) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK); - WriteByte(MSG_ENTITY, sendflags); - if(sendflags & 1) - { - WriteCoord(MSG_ENTITY, this.goalentity.origin_x); - WriteCoord(MSG_ENTITY, this.goalentity.origin_y); - WriteCoord(MSG_ENTITY, this.goalentity.origin_z); - } - if(sendflags & 2) - { - WriteCoord(MSG_ENTITY, this.enemy.origin_x); - WriteCoord(MSG_ENTITY, this.enemy.origin_y); - WriteCoord(MSG_ENTITY, this.enemy.origin_z); - } - if(sendflags & 4) - { - WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16 - } - return true; -} - -void ons_Link_CheckUpdate(entity this) -{ - // TODO check if the two sides have moved (currently they won't move anyway) - float cc = 0, cc1 = 0, cc2 = 0; - - if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; } - if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; } - - cc = cc1 + cc2; - - if(cc != this.clientcolors) - { - this.clientcolors = cc; - this.SendFlags |= 4; - } - - this.nextthink = time; -} - -void ons_DelayedLinkSetup(entity this) -{ - this.goalentity = find(NULL, targetname, this.target); - this.enemy = find(NULL, targetname, this.target2); - if(!this.goalentity) { objerror(this, "can not find target\n"); } - if(!this.enemy) { objerror(this, "can not find target2\n"); } - - LOG_DEBUG(strcat(etos(this.goalentity), " linked with ", etos(this.enemy), "\n")); - this.SendFlags |= 3; - setthink(this, ons_Link_CheckUpdate); - this.nextthink = time; -} - - -// ============================= -// Main Control Point Functions -// ============================= - -int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) -{ - if(cp.isgenneighbor[teamnumber]) { return 2; } - if(cp.iscpneighbor[teamnumber]) { return 1; } - - return 0; -} - -int ons_ControlPoint_Attackable(entity cp, int teamnumber) - // -2: SAME TEAM, attackable by enemy! - // -1: SAME TEAM! - // 0: off limits - // 1: attack it - // 2: touch it - // 3: attack it (HIGH PRIO) - // 4: touch it (HIGH PRIO) -{ - int a; - - if(cp.isshielded) - { - return 0; - } - else if(cp.goalentity) - { - // if there's already an icon built, nothing happens - if(cp.team == teamnumber) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a) // attackable by enemy? - return -2; // EMERGENCY! - return -1; - } - // we know it can be linked, so no need to check - // but... - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); - if(a == 2) // near our generator? - return 3; // EMERGENCY! - return 1; - } - else - { - // free point - if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) - { - a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t - if(a == 2) - return 4; // GET THIS ONE NOW! - else - return 2; // TOUCH ME - } - } - return 0; -} - -void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(damage <= 0) { return; } - - if (this.owner.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > this.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - this.pain_finished = time + 1; - attacker.typehitsound += 1; // play both sounds (shield is way too quiet) - } - - return; - } - - if(IS_PLAYER(attacker)) - if(time - ons_notification_time[this.team] > 10) - { - play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); - ons_notification_time[this.team] = time; - } - - this.health = this.health - damage; - if(this.owner.iscaptured) - WaypointSprite_UpdateHealth(this.owner.sprite, this.health); - else - WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); - this.pain_finished = time + 1; - // particles on every hit - pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); - //sound on every hit - if (random() < 0.5) - sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); - else - sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); - - if (this.health < 0) - { - sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname); - - PlayerScore_Add(attacker, SP_ONS_TAKES, 1); - PlayerScore_Add(attacker, SP_SCORE, 10); - - this.owner.goalentity = NULL; - this.owner.islinked = false; - this.owner.iscaptured = false; - this.owner.team = 0; - this.owner.colormap = 1024; - - WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0); - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - SUB_UseTargets(this.owner, this, NULL); - - this.owner.waslinked = this.owner.islinked; - if(this.owner.model != "models/onslaught/controlpoint_pad.md3") - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1); - //setsize(this, '-32 -32 0', '32 32 8'); - - remove(this); - } - - this.SendFlags |= CPSF_STATUS; -} - -void ons_ControlPoint_Icon_Think(entity this) -{ - this.nextthink = time + ONS_CP_THINKRATE; - - if(autocvar_g_onslaught_cp_proxydecap) - { - int _enemy_count = 0; - int _friendly_count = 0; - - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance)) - { - if(SAME_TEAM(it, this)) - ++_friendly_count; - else - ++_enemy_count; - } - }); - - _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); - this.SendFlags |= CPSF_STATUS; - if(this.health <= 0) - { - ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0'); - return; - } - } - - if (time > this.pain_finished + 5) - { - if(this.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); - } - } - - if(this.owner.islinked != this.owner.waslinked) - { - // unteam the spawnpoint if needed - int t = this.owner.team; - if(!this.owner.islinked) - this.owner.team = 0; - - SUB_UseTargets(this.owner, this, NULL); - - this.owner.team = t; - - this.owner.waslinked = this.owner.islinked; - } - - // damaged fx - if(random() < 0.6 - this.health / this.max_health) - { - Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); - - if(random() > 0.8) - sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); - else if (random() > 0.5) - sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); - } -} - -void ons_ControlPoint_Icon_BuildThink(entity this) -{ - int a; - - this.nextthink = time + ONS_CP_THINKRATE; - - // only do this if there is power - a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team); - if(!a) - return; - - this.health = this.health + this.count; - - this.SendFlags |= CPSF_STATUS; - - if (this.health >= this.max_health) - { - this.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); - this.owner.iscaptured = true; - this.solid = SOLID_BBOX; - - 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); - - if(IS_PLAYER(this.owner.ons_toucher)) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message); - Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message); - Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message); - PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1); - PlayerTeamScore_AddScore(this.owner.ons_toucher, 10); - } - - this.owner.ons_toucher = NULL; - - onslaught_updatelinks(); - - // Use targets now (somebody make sure this is in the right place..) - SUB_UseTargets(this.owner, this, NULL); - - this.SendFlags |= CPSF_SETUP; - } - if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) - setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); - - if(random() < 0.9 - this.health / this.max_health) - Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); -} - -void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc); - -void ons_ControlPoint_Icon_Spawn(entity cp, entity player) -{ - entity e = new(onslaught_controlpoint_icon); - - setsize(e, CPICON_MIN, CPICON_MAX); - setorigin(e, cp.origin + CPICON_OFFSET); - - e.owner = cp; - e.max_health = autocvar_g_onslaught_cp_health; - e.health = autocvar_g_onslaught_cp_buildhealth; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_AIM; - e.bot_attack = true; - e.event_damage = ons_ControlPoint_Icon_Damage; - 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 - - sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); - - cp.goalentity = e; - cp.team = e.team; - cp.colormap = e.colormap; - - 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_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); - cp.sprite.SendFlags |= 16; - - onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); -} - -entity ons_ControlPoint_Waypoint(entity e) -{ - if(e.team) - { - int a = ons_ControlPoint_Attackable(e, e.team); - - if(a == -2) { return WP_OnsCPDefend; } // defend now - if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch - if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack - } - else - return WP_OnsCP; - - return WP_Null; -} - -void ons_ControlPoint_UpdateSprite(entity e) -{ - entity s1 = ons_ControlPoint_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - bool sh; - sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); - - if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) - { - if(e.iscaptured) // don't mess up build bars! - { - if(sh) - { - WaypointSprite_UpdateMaxHealth(e.sprite, 0); - } - else - { - WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); - WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); - } - } - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - - e.lastteam = e.team + 2; - e.lastshielded = sh; - e.lastcaptured = e.iscaptured; - } -} - -void ons_ControlPoint_Touch(entity this) -{ - entity toucher = other; - int attackable; - - if(IS_VEHICLE(toucher) && toucher.owner) - if(autocvar_g_onslaught_allow_vehicle_touch) - toucher = toucher.owner; - else - return; - - if(!IS_PLAYER(toucher)) { return; } - if(STAT(FROZEN, toucher)) { return; } - if(IS_DEAD(toucher)) { return; } - - if ( SAME_TEAM(this,toucher) ) - if ( this.iscaptured ) - { - if(time <= toucher.teleport_antispam) - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); - else - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); - } - - attackable = ons_ControlPoint_Attackable(this, toucher.team); - if(attackable != 2 && attackable != 4) - return; - // we've verified that this player has a legitimate claim to this point, - // so start building the captured point icon (which only captures this - // point if it successfully builds without being destroyed first) - ons_ControlPoint_Icon_Spawn(this, toucher); - - this.ons_toucher = toucher; - - onslaught_updatelinks(); -} - -void ons_ControlPoint_Think(entity this) -{ - this.nextthink = time + ONS_CP_THINKRATE; - CSQCMODEL_AUTOUPDATE(this); -} - -void ons_ControlPoint_Reset(entity this) -{ - if(this.goalentity) - remove(this.goalentity); - - this.goalentity = NULL; - this.team = 0; - this.colormap = 1024; - this.iscaptured = false; - this.islinked = false; - this.isshielded = true; - setthink(this, ons_ControlPoint_Think); - this.ons_toucher = NULL; - this.nextthink = time + ONS_CP_THINKRATE; - setmodel_fixsize(this, MDL_ONS_CP_PAD1); - - WaypointSprite_UpdateMaxHealth(this.sprite, 0); - WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); - - SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc. - - CSQCMODEL_AUTOUPDATE(this); -} - -void ons_DelayedControlPoint_Setup(entity this) -{ - onslaught_updatelinks(); - - // captureshield setup - ons_CaptureShield_Spawn(this, false); - - CSQCMODEL_AUTOINIT(this); -} - -void ons_ControlPoint_Setup(entity cp) -{ - // main setup - cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist - ons_worldcplist = cp; - - cp.netname = "Control point"; - cp.team = 0; - cp.solid = SOLID_BBOX; - cp.movetype = MOVETYPE_NONE; - settouch(cp, ons_ControlPoint_Touch); - setthink(cp, ons_ControlPoint_Think); - cp.nextthink = time + ONS_CP_THINKRATE; - cp.reset = ons_ControlPoint_Reset; - cp.colormap = 1024; - cp.iscaptured = false; - cp.islinked = false; - cp.isshielded = true; - - if(cp.message == "") { cp.message = "a"; } - - // appearence - setmodel_fixsize(cp, MDL_ONS_CP_PAD1); - - // control point placement - if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location - { - cp.noalign = true; - cp.movetype = MOVETYPE_NONE; - } - else // drop to floor, automatically find a platform and set that as spawn origin - { - setorigin(cp, cp.origin + '0 0 20'); - cp.noalign = false; - droptofloor(cp); - cp.movetype = MOVETYPE_TOSS; - } - - // waypointsprites - WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE); - WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY); - - InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); -} - - -// ========================= -// Main Generator Functions -// ========================= - -entity ons_Generator_Waypoint(entity e) -{ - if (e.isshielded) - return WP_OnsGenShielded; - return WP_OnsGen; -} - -void ons_Generator_UpdateSprite(entity e) -{ - entity s1 = ons_Generator_Waypoint(e); - WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); - - if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) - { - e.lastteam = e.team + 2; - e.lastshielded = e.isshielded; - if(e.lastshielded) - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); - } - else - { - if(e.team) - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); - else - WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); - } - WaypointSprite_Ping(e.sprite); - } -} - -void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(damage <= 0) { return; } - if(warmup_stage || gameover) { return; } - if(!round_handler_IsRoundStarted()) { return; } - - if (attacker != this) - { - if (this.isshielded) - { - // this is protected by a shield, so ignore the damage - if (time > this.pain_finished) - if (IS_PLAYER(attacker)) - { - play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); - attacker.typehitsound += 1; - this.pain_finished = time + 1; - } - return; - } - if (time > this.pain_finished) - { - this.pain_finished = time + 10; - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK)); - play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); - } - } - this.health = this.health - damage; - WaypointSprite_UpdateHealth(this.sprite, this.health); - // choose an animation frame based on health - this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); - // see if the generator is still functional, or dying - if (this.health > 0) - { - this.lasthealth = this.health; - } - else - { - if (attacker == this) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME)); - else - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED)); - PlayerScore_Add(attacker, SP_SCORE, 100); - } - this.iscaptured = false; - this.islinked = false; - this.isshielded = false; - this.takedamage = DAMAGE_NO; // can't be hurt anymore - this.event_damage = func_null; // won't do anything if hurt - this.count = 0; // reset counter - setthink(this, func_null); - this.nextthink = 0; - //this.think(); // do the first explosion now - - WaypointSprite_UpdateMaxHealth(this.sprite, 0); - WaypointSprite_Ping(this.sprite); - //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor - - onslaught_updatelinks(); - } - - // Throw some flaming gibs on damage, more damage = more chance for gib - if(random() < damage/220) - { - sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); - } - else - { - // particles on every hit - Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); - - //sound on every hit - if (random() < 0.5) - sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); - else - sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); - } - - this.SendFlags |= GSF_STATUS; -} - -void ons_GeneratorThink(entity this) -{ - this.nextthink = time + GEN_THINKRATE; - if (!gameover) - { - if(!this.isshielded && this.wait < time) - { - 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)); - }); - } - } -} - -void ons_GeneratorReset(entity this) -{ - this.team = this.team_saved; - this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; - this.takedamage = DAMAGE_AIM; - this.bot_attack = true; - this.iscaptured = true; - this.islinked = true; - this.isshielded = true; - this.event_damage = ons_GeneratorDamage; - setthink(this, ons_GeneratorThink); - this.nextthink = time + GEN_THINKRATE; - - Net_LinkEntity(this, false, 0, generator_send); - - this.SendFlags = GSF_SETUP; // just incase - this.SendFlags |= GSF_STATUS; - - WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); - WaypointSprite_UpdateHealth(this.sprite, this.health); - WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); - - onslaught_updatelinks(); -} - -void ons_DelayedGeneratorSetup(entity this) -{ - // bot waypoints - waypoint_spawnforitem_force(this, this.origin); - this.nearestwaypointtimeout = 0; // activate waypointing again - this.bot_basewaypoint = this.nearestwaypoint; - - // captureshield setup - ons_CaptureShield_Spawn(this, true); - - onslaught_updatelinks(); - - Net_LinkEntity(this, false, 0, generator_send); -} - - -void onslaught_generator_touch(entity this) -{ - if ( IS_PLAYER(other) ) - if ( SAME_TEAM(this,other) ) - if ( this.iscaptured ) - { - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_ONS_TELEPORT); - } -} - -void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc -{ - // declarations - int teamnumber = gen.team; - - // main setup - gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist - ons_worldgeneratorlist = gen; - - gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); - gen.classname = "onslaught_generator"; - gen.solid = SOLID_BBOX; - gen.team_saved = teamnumber; - gen.movetype = MOVETYPE_NONE; - gen.lasthealth = gen.max_health = gen.health = autocvar_g_onslaught_gen_health; - gen.takedamage = DAMAGE_AIM; - gen.bot_attack = true; - gen.event_damage = ons_GeneratorDamage; - gen.reset = ons_GeneratorReset; - setthink(gen, ons_GeneratorThink); - gen.nextthink = time + GEN_THINKRATE; - gen.iscaptured = true; - gen.islinked = true; - gen.isshielded = true; - settouch(gen, onslaught_generator_touch); - - // appearence - // model handled by CSQC - setsize(gen, GENERATOR_MIN, GENERATOR_MAX); - setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); - gen.colormap = 1024 + (teamnumber - 1) * 17; - - // generator placement - droptofloor(gen); - - // waypointsprites - 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); - - InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); -} - - -// =============== -// Round Handler -// =============== - -int total_generators; -void Onslaught_count_generators() -{ - entity e; - total_generators = redowned = blueowned = yellowowned = pinkowned = 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); - } -} - -int Onslaught_GetWinnerTeam() -{ - int winner_team = 0; - if(redowned > 0) - winner_team = NUM_TEAM_1; - if(blueowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_2; - } - if(yellowowned > 0) - { - if(winner_team) return 0; - winner_team = NUM_TEAM_3; - } - if(pinkowned > 0) - { - 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)) - { - ons_stalemate = true; - - if (!wpforenemy_announced) - { - Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); - sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); - - wpforenemy_announced = true; - } - - entity tmp_entity; // temporary entity - float d; - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) - { - // tmp_entity.max_health / 300 gives 5 minutes of overtime. - // control points reduce the overtime duration. - d = 1; - entity e; - for(e = ons_worldcplist; e; e = e.ons_worldcpnext) - { - if(DIFF_TEAM(e, tmp_entity)) - if(e.islinked) - d = d + 1; - } - - if(autocvar_g_campaign && autocvar__campaign_testrun) - d = d * tmp_entity.max_health; - else - d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); - - Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); - - tmp_entity.sprite.SendFlags |= 16; - - tmp_entity.ons_overtime_damagedelay = time + 1; - } - } - else { wpforenemy_announced = false; ons_stalemate = false; } - - Onslaught_count_generators(); - - if(ONS_OWNED_GENERATORS_OK()) - return 0; - - int winner_team = Onslaught_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_ONS_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); - } - - ons_stalemate = false; - - play2all(SND(CTF_CAPTURE(winner_team))); - - round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); - - FOREACH_CLIENT(IS_PLAYER(it), { - it.ons_roundlost = true; - it.player_blocked = true; - - nades_Clear(it); - }); - - return 1; -} - -bool Onslaught_CheckPlayers() -{ - return 1; -} - -void Onslaught_RoundStart() -{ - entity tmp_entity; - FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false); - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - tmp_entity.sprite.SendFlags |= 16; - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - tmp_entity.sprite.SendFlags |= 16; -} - - -// ================ -// Bot player logic -// ================ - -// NOTE: LEGACY CODE, needs to be re-written! - -void havocbot_goalrating_ons_offenseitems(entity this, float ratingscale, vector org, float sradius) -{ - entity head; - float t, c; - bool needarmor = false, needweapons = false; - - // Needs armor/health? - if(this.health<100) - needarmor = true; - - // Needs weapons? - 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(strcat(this.netname, " needs weapons ", ftos(needweapons) , "\n")); - LOG_DEBUG(strcat(this.netname, " needs armor ", ftos(needarmor) , "\n")); - - // See what is around - head = findchainfloat(bot_pickup, true); - while (head) - { - // gather health and armor only - if (head.solid) - if ( ((head.health || head.armorvalue) && needarmor) || (head.weapons && needweapons ) ) - if (vdist(head.origin - org, <, sradius)) - { - t = head.bot_pickupevalfunc(this, head); - if (t > 0) - navigation_routerating(this, head, t * ratingscale, 500); - } - head = head.chain; - } -} - -void havocbot_role_ons_setrole(entity this, int role) -{ - LOG_DEBUG(strcat(this.netname," switched to ")); - switch(role) - { - case HAVOCBOT_ONS_ROLE_DEFENSE: - LOG_DEBUG("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"); - 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"); - this.havocbot_role = havocbot_role_ons_offense; - this.havocbot_role_flags = HAVOCBOT_ONS_ROLE_OFFENSE; - this.havocbot_role_timeout = 0; - break; - } - LOG_DEBUG("\n"); -} - -void havocbot_goalrating_ons_controlpoints_attack(entity this, float ratingscale) -{ - entity cp, cp1, cp2, best, wp; - float radius, bestvalue; - int c; - bool found; - - // Filter control points - for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) - { - cp2.wpcost = c = 0; - cp2.wpconsidered = false; - - if(cp2.isshielded) - continue; - - // Ignore owned controlpoints - if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team])) - continue; - - // Count team mates interested in this control point - // (easier and cleaner than keeping counters per cp and teams) - FOREACH_CLIENT(IS_PLAYER(it), { - if(SAME_TEAM(it, this)) - if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) - if(it.havocbot_ons_target == cp2) - ++c; - }); - - // NOTE: probably decrease the cost of attackable control points - cp2.wpcost = c; - cp2.wpconsidered = true; - } - - // We'll consider only the best case - bestvalue = 99999999999; - cp = NULL; - for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) - { - if (!cp1.wpconsidered) - continue; - - if(cp1.wpcost this.havocbot_role_timeout) - { - havocbot_ons_reset_role(this); - return; - } - - if(this.havocbot_attack_time>time) - return; - - if (this.bot_strategytime < time) - { - 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); - navigation_goalrating_end(this); - - this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - } -} - -void havocbot_role_ons_assistant(entity this) -{ - havocbot_ons_reset_role(this); -} - -void havocbot_role_ons_defense(entity this) -{ - havocbot_ons_reset_role(this); -} - -void havocbot_ons_reset_role(entity this) -{ - if(IS_DEAD(this)) - return; - - this.havocbot_ons_target = NULL; - - // TODO: Defend control points or generator if necessary - - havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE); -} - - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - */ -entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist) -{ - entity tmp_entity, closest_target = NULL; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) - if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <=, max_dist)) - if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, this)) - if(max_dist <= 0 || vdist(tmp_entity.origin - pos, <, max_dist)) - if(vlen2(tmp_entity.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) - closest_target = tmp_entity; - tmp_entity = tmp_entity.chain; - } - - return closest_target; -} - -/* - * Find control point or generator owned by the same team self which is nearest to pos - * if max_dist is positive, only control points within this range will be considered - * This function only check distances on the XY plane, disregarding Z - */ -entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist) -{ - entity tmp_entity, closest_target = NULL; - vector delta; - float smallest_distance = 0, distance; - - tmp_entity = findchain(classname, "onslaught_controlpoint"); - while(tmp_entity) - { - delta = tmp_entity.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == NULL || distance <= smallest_distance ) - { - closest_target = tmp_entity; - smallest_distance = distance; - } - - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - delta = tmp_entity.origin - pos; - delta_z = 0; - distance = vlen(delta); - - if(SAME_TEAM(tmp_entity, this)) - if(max_dist <= 0 || distance <= max_dist) - if(closest_target == NULL || distance <= smallest_distance ) - { - closest_target = tmp_entity; - smallest_distance = distance; - } - - tmp_entity = tmp_entity.chain; - } - - return closest_target; -} -/** - * find the number of control points and generators in the same team as this - */ -int ons_Count_SelfControlPoints(entity this) -{ - entity tmp_entity; - tmp_entity = findchain(classname, "onslaught_controlpoint"); - int n = 0; - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, this)) - if(tmp_entity.iscaptured) - n++; - tmp_entity = tmp_entity.chain; - } - tmp_entity = findchain(classname, "onslaught_generator"); - while(tmp_entity) - { - if(SAME_TEAM(tmp_entity, this)) - n++; - tmp_entity = tmp_entity.chain; - } - return n; -} - -/** - * Teleport player to a random position near tele_target - * if tele_effects is true, teleport sound+particles are created - * return false on failure - */ -bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) -{ - if ( !tele_target ) - return false; - - int i; - vector loc; - float theta; - // narrow the range for each iteration to increase chances that a spawnpoint - // can be found even if there's little room around the control point - float iteration_scale = 1; - for(i = 0; i < 16; ++i) - { - iteration_scale -= i / 16; - theta = random() * 2 * M_PI; - loc_y = sin(theta); - loc_x = cos(theta); - loc_z = 0; - loc *= random() * range * iteration_scale; - - loc += tele_target.origin + '0 0 128' * iteration_scale; - - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - if ( tele_effects ) - { - Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); - sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); - } - setorigin(player, loc); - player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); - makevectors(player.angles); - player.fixangle = true; - player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; - - if ( tele_effects ) - Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); - return true; - } - } - } - - return false; -} - -// ============== -// Hook Functions -// ============== - -MUTATOR_HOOKFUNCTION(ons, reset_map_global) -{ - FOREACH_CLIENT(IS_PLAYER(it), { - it.ons_roundlost = false; - it.ons_deathloc = '0 0 0'; - WITHSELF(it, PutClientInServer()); - }); - return false; -} - -MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - player.ons_deathloc = '0 0 0'; -} - -MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - player.ons_deathloc = '0 0 0'; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(!round_handler_IsRoundStarted()) - { - player.player_blocked = true; - return false; - } - - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( player.ons_spawn_by ) - if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) - { - player.ons_spawn_by = NULL; - return false; - } - - if(autocvar_g_onslaught_spawn_at_controlpoints) - if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; - entity tmp_entity, closest_target = NULL; - vector spawn_loc = player.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) - { - if(SAME_TEAM(tmp_entity, player)) - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) - closest_target = tmp_entity; - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 96' * iteration_scale; - loc += ('0 1 0' * random()) * 128 * iteration_scale; - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(player, loc); - player.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - if(autocvar_g_onslaught_spawn_at_generator) - if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) - { - float random_target = autocvar_g_onslaught_spawn_at_generator_random; - entity tmp_entity, closest_target = NULL; - vector spawn_loc = player.ons_deathloc; - - // new joining player or round reset, don't bother checking - if(spawn_loc == '0 0 0') { return false; } - - if(random_target) { RandomSelection_Init(); } - - for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) - { - if(random_target) - RandomSelection_Add(tmp_entity, 0, string_null, 1, 1); - else - { - if(SAME_TEAM(tmp_entity, player)) - if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) - closest_target = tmp_entity; - } - } - - if(random_target) { closest_target = RandomSelection_chosen_ent; } - - if(closest_target) - { - float i; - vector loc; - float iteration_scale = 1; - for(i = 0; i < 10; ++i) - { - iteration_scale -= i / 10; - loc = closest_target.origin + '0 0 128' * iteration_scale; - loc += ('0 1 0' * random()) * 256 * iteration_scale; - tracebox(loc, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), loc, MOVE_NORMAL, player); - if(trace_fraction == 1.0 && !trace_startsolid) - { - traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL - if(trace_fraction == 1.0 && !trace_startsolid) - { - setorigin(player, loc); - player.angles = normalize(loc - closest_target.origin) * RAD2DEG; - return false; - } - } - } - } - } - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - frag_target.ons_deathloc = frag_target.origin; - entity l; - for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) - { - l.sprite.SendFlags |= 16; - } - for(l = ons_worldcplist; l; l = l.ons_worldcpnext) - { - l.sprite.SendFlags |= 16; - } - - if ( autocvar_g_onslaught_spawn_choose ) - if ( ons_Count_SelfControlPoints(frag_target) > 1 ) - stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n"); - - return false; -} - -MUTATOR_HOOKFUNCTION(ons, MonsterMove) -{ - entity mon = M_ARGV(0, entity); - - entity e = find(NULL, targetname, mon.target); - if (e != NULL) - mon.team = e.team; -} - -void ons_MonsterSpawn_Delayed(entity this) -{ - entity own = this.owner; - - if(!own) { remove(this); return; } - - if(own.targetname) - { - entity e = find(NULL, target, own.targetname); - if(e != NULL) - { - own.team = e.team; - - own.use(own, e, NULL); - } - } - - remove(this); -} - -MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) -{ - entity mon = M_ARGV(0, entity); - - entity e = spawn(); - e.owner = mon; - InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); -} - -void ons_TurretSpawn_Delayed(entity this) -{ - entity own = this.owner; - - if(!own) { remove(this); return; } - - if(own.targetname) - { - entity e = find(NULL, target, own.targetname); - if(e != NULL) - { - own.team = e.team; - own.active = ACTIVE_NOT; - - own.use(own, e, NULL); - } - } +#ifndef MENUQC +REGISTER_NET_LINKED(ENT_ONSCAMERA) +#endif - remove(this); -} +#ifdef CSQC -MUTATOR_HOOKFUNCTION(ons, TurretSpawn) +entity generator_camera; +NET_HANDLE(ENT_ONSCAMERA, bool isnew) { - entity turret = M_ARGV(0, entity); + this.origin_x = ReadCoord(); + this.origin_y = ReadCoord(); + this.origin_z = ReadCoord(); + setorigin(this, this.origin); - entity e = spawn(); - e.owner = turret; - InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); + this.angles_x = ReadAngle(); + this.angles_y = ReadAngle(); + this.angles_z = ReadAngle(); - return false; -} - -MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) -{ - entity bot = M_ARGV(0, entity); + this.drawmask = MASK_NORMAL; + setmodel(this, MDL_Null); // give it a size for clientcamera + setsize(this, '-1 -1 -1', '1 1 1'); - havocbot_ons_reset_role(bot); + generator_camera = this; return true; } -MUTATOR_HOOKFUNCTION(ons, GetTeamCount) +REGISTER_MUTATOR(cl_ons, true); + +float ons_roundlost; +vector generator_origin; +vector autocvar_cl_eventchase_generator_viewoffset = '0 0 80'; +float autocvar_cl_eventchase_generator_distance = 400; +MUTATOR_HOOKFUNCTION(cl_ons, WantEventchase) { - // onslaught is special - for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + ons_roundlost = STAT(ROUNDLOST); + entity gen = NULL; + if(ons_roundlost) { - switch(tmp_entity.team) + IL_EACH(g_onsgenerators, it.health <= 0, { - 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; - } + gen = it; + break; + }); + if(!gen) + ons_roundlost = false; // don't enforce the 3rd person camera if there is no dead generator to show } - return true; -} - -MUTATOR_HOOKFUNCTION(ons, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.ons_roundlost = spectatee.ons_roundlost; // make spectators see it too -} - -MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) -{ - if(MUTATOR_RETURNVALUE) // command was already handled? - return false; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if ( cmd_name == "ons_spawn" ) + if(ons_roundlost) { - vector pos = player.origin; - if(cmd_argc > 1) - pos_x = stof(argv(1)); - if(cmd_argc > 2) - pos_y = stof(argv(2)); - if(cmd_argc > 3) - pos_z = stof(argv(3)); - - if ( IS_PLAYER(player) ) - { - if ( !STAT(FROZEN, player) ) - { - entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - - if ( !source_point && player.health > 0 ) - { - sprint(player, "\nYou need to be next to a control point\n"); - return true; - } - - - entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius); - - if ( closest_target == NULL ) - { - sprint(player, "\nNo control point found\n"); - return true; - } - - if ( player.health <= 0 ) - { - player.ons_spawn_by = closest_target; - player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; - } - else - { - if ( source_point == closest_target ) - { - sprint(player, "\nTeleporting to the same point\n"); - return true; - } - - if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) ) - sprint(player, "\nUnable to teleport there\n"); - } - - return true; - } - - sprint(player, "\nNo teleportation for you\n"); - } - + generator_origin = gen.origin; return true; } return false; } -MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) +MUTATOR_HOOKFUNCTION(cl_ons, CustomizeEventchase) { - if(MUTATOR_RETURNVALUE || gameover) { return false; } - - entity player = M_ARGV(0, entity); - - if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle) - { - entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); - if ( source_point ) - { - stuffcmd(player, "qc_cmd_cl hud clickradar\n"); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(ons, PlayHitsound) -{ - entity frag_victim = M_ARGV(0, entity); - - return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) - || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); -} - -MUTATOR_HOOKFUNCTION(ons, SendWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity to = M_ARGV(1, entity); - int sf = M_ARGV(2, int); - int wp_flag = M_ARGV(3, int); - - if(sf & 16) - { - if(wp.owner.classname == "onslaught_controlpoint") - { - 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(!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; } - } - } - - M_ARGV(3, int) = wp_flag; -} - -MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) -{ - entity turret_target = M_ARGV(1, entity); - - if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + if(ons_roundlost) { - M_ARGV(3, float) = -3; + M_ARGV(0, vector) = generator_camera.origin; + M_ARGV(1, vector) = autocvar_cl_eventchase_generator_viewoffset; + M_ARGV(0, float) = autocvar_cl_eventchase_generator_distance; return true; } - return false; } -MUTATOR_HOOKFUNCTION(ons, TurretThink) -{ - entity turret = M_ARGV(0, entity); - - // ONS uses somewhat backwards linking. - if(turret.target) - { - entity e = find(NULL, targetname, turret.target); - if (e != NULL) - turret.team = e.team; - } - - if(turret.team != turret.tur_head.team) - turret_respawn(turret); -} - - -// ========== -// Spawnfuncs -// ========== - -/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) - Link between control points. - - This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. - -keys: -"target" - first control point. -"target2" - second control point. - */ -spawnfunc(onslaught_link) -{ - if(!g_onslaught) { remove(this); return; } - - if (this.target == "" || this.target2 == "") - objerror(this, "target and target2 must be set\n"); - - this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist - ons_worldlinklist = this; - - InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); - Net_LinkEntity(this, false, 0, ons_Link_Send); -} - -/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) - Control point. Be sure to give this enough clearance so that the shootable part has room to exist - - This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. - -keys: -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. -"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. -"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) - */ - -spawnfunc(onslaught_controlpoint) -{ - if(!g_onslaught) { remove(this); return; } - - ons_ControlPoint_Setup(this); -} - -/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) - Base generator. - - spawnfunc_onslaught_link entities can target this. - -keys: -"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. -"targetname" - name that spawnfunc_onslaught_link entities will use to target this. - */ -spawnfunc(onslaught_generator) -{ - if(!g_onslaught) { remove(this); return; } - if(!this.team) { objerror(this, "team must be set"); } - - ons_GeneratorSetup(this); -} - -// scoreboard setup -void ons_ScoreRules() -{ - CheckAllowedTeams(NULL); - ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, 0, true); - ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); - ScoreRules_basics_end(); -} - -void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up -{ - ons_ScoreRules(); - - round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); - round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); -} - -void ons_Initialize() -{ - g_onslaught = true; - ons_captureshield_force = autocvar_g_onslaught_shield_force; - - InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE); -} - #endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh new file mode 100644 index 0000000000..c863e2f71f --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/onslaught.qh @@ -0,0 +1,14 @@ +#pragma once + +REGISTER_NET_LINKED(ENT_CLIENT_GENERATOR) +REGISTER_NET_LINKED(ENT_CLIENT_CONTROLPOINT_ICON) + +#ifdef SVQC +IntrusiveList g_onsshields; +STATIC_INIT(g_onsshields) { g_onsshields = IL_NEW(); } +#endif + +#ifdef CSQC +IntrusiveList g_onsgenerators; +STATIC_INIT(g_onsgenerators) { g_onsgenerators = IL_NEW(); } +#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc index 491acdd007..0941833de8 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qc @@ -5,6 +5,8 @@ bool cpicon_send(entity this, entity to, int sf) { WriteHeader(MSG_ENTITY, ENT_CLIENT_CONTROLPOINT_ICON); + if(sf & CPSF_SETUP) + sf &= ~CPSF_STATUS; WriteByte(MSG_ENTITY, sf); if(sf & CPSF_SETUP) { diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh index d76f0ea069..d5437338e3 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_controlpoint.qh @@ -1,10 +1,7 @@ -#ifndef CONTROLPOINT_H -#define CONTROLPOINT_H +#pragma once const vector CPICON_MIN = '-32 -32 -9'; const vector CPICON_MAX = '32 32 25'; const int CPSF_STATUS = 4; const int CPSF_SETUP = 8; - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh index 003c2b1d68..8356689e20 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_generator.qh @@ -1,5 +1,5 @@ -#ifndef GENERATOR_H -#define GENERATOR_H +#pragma once + const vector GENERATOR_MIN = '-52 -52 -14'; const vector GENERATOR_MAX = '52 52 75'; @@ -7,4 +7,3 @@ const int GSF_STATUS = 4; const int GSF_SETUP = 8; bool generator_send(entity this, entity to, int sf); -#endif diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc new file mode 100644 index 0000000000..195a3bd982 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -0,0 +1,2198 @@ +#include "sv_onslaught.qh" +#include "sv_controlpoint.qh" +#include "sv_generator.qh" + +bool g_onslaught; + +float autocvar_g_onslaught_teleport_wait; +bool autocvar_g_onslaught_spawn_at_controlpoints; +bool autocvar_g_onslaught_spawn_at_generator; +float autocvar_g_onslaught_cp_proxydecap; +float autocvar_g_onslaught_cp_proxydecap_distance = 512; +float autocvar_g_onslaught_cp_proxydecap_dps = 100; +float autocvar_g_onslaught_spawn_at_controlpoints_chance = 0.5; +float autocvar_g_onslaught_spawn_at_controlpoints_random; +float autocvar_g_onslaught_spawn_at_generator_chance; +float autocvar_g_onslaught_spawn_at_generator_random; +float autocvar_g_onslaught_cp_buildhealth; +float autocvar_g_onslaught_cp_buildtime; +float autocvar_g_onslaught_cp_health; +float autocvar_g_onslaught_cp_regen; +float autocvar_g_onslaught_gen_health; +float autocvar_g_onslaught_shield_force = 100; +float autocvar_g_onslaught_allow_vehicle_touch; +float autocvar_g_onslaught_round_timelimit; +float autocvar_g_onslaught_warmup; +float autocvar_g_onslaught_teleport_radius; +float autocvar_g_onslaught_spawn_choose; +float autocvar_g_onslaught_click_radius; + +void FixSize(entity e); +entity cam; + +// ======================= +// CaptureShield Functions +// ======================= + +bool clientcamera_send(entity this, entity to, int sf) +{ + WriteHeader(MSG_ENTITY, ENT_ONSCAMERA); + + WriteCoord(MSG_ENTITY, this.origin_x); + WriteCoord(MSG_ENTITY, this.origin_y); + WriteCoord(MSG_ENTITY, this.origin_z); + + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); + + return true; +} + +bool ons_CaptureShield_Customize(entity this, entity client) +{ + entity e = WaypointSprite_getviewentity(client); + + if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, e.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return false; } + if(SAME_TEAM(this, e)) { return false; } + + return true; +} + +void ons_CaptureShield_Touch(entity this, entity toucher) +{ + if(!this.enemy.isshielded && (ons_ControlPoint_Attackable(this.enemy, toucher.team) > 0 || this.enemy.classname != "onslaught_controlpoint")) { return; } + if(!IS_PLAYER(toucher)) { return; } + if(SAME_TEAM(toucher, this)) { 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, mymid, normalize(theirmid - mymid) * ons_captureshield_force); + + if(IS_REAL_CLIENT(toucher)) + { + play2(toucher, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + + if(this.enemy.classname == "onslaught_generator") + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_GENERATOR_SHIELDED); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_CONTROLPOINT_SHIELDED); + } +} + +void ons_CaptureShield_Reset(entity this) +{ + this.colormap = this.enemy.colormap; + this.team = this.enemy.team; +} + +void ons_CaptureShield_Spawn(entity generator, bool is_generator) +{ + entity shield = new(ons_captureshield); + IL_PUSH(g_onsshields, shield); + + shield.enemy = generator; + shield.team = generator.team; + shield.colormap = generator.colormap; + shield.reset = ons_CaptureShield_Reset; + settouch(shield, ons_CaptureShield_Touch); + setcefc(shield, ons_CaptureShield_Customize); + shield.effects = EF_ADDITIVE; + set_movetype(shield, MOVETYPE_NOCLIP); + shield.solid = SOLID_TRIGGER; + shield.avelocity = '7 0 11'; + shield.scale = 1; + shield.model = ((is_generator) ? "models/onslaught/generator_shield.md3" : "models/onslaught/controlpoint_shield.md3"); + + precache_model(shield.model); + setorigin(shield, generator.origin); + _setmodel(shield, shield.model); + setsize(shield, shield.scale * shield.mins, shield.scale * shield.maxs); +} + + +// ========== +// Junk Pile +// ========== + +void setmodel_fixsize(entity e, Model m) +{ + setmodel(e, m); + FixSize(e); +} + +void onslaught_updatelinks() +{ + entity l; + // first check if the game has ended + LOG_DEBUG("--- updatelinks ---"); + // mark generators as being shielded and networked + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.iscaptured) + LOG_DEBUG(etos(l), " (generator) belongs to team ", ftos(l.team)); + else + LOG_DEBUG(etos(l), " (generator) is destroyed"); + l.islinked = l.iscaptured; + l.isshielded = l.iscaptured; + l.sprite.SendFlags |= 16; + } + // mark points as shielded and not networked + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.islinked = false; + l.isshielded = true; + int i; + for(i = 0; i < 17; ++i) { l.isgenneighbor[i] = false; l.iscpneighbor[i] = false; } + LOG_DEBUG(etos(l), " (point) belongs to team ", ftos(l.team)); + l.sprite.SendFlags |= 16; + } + // flow power outward from the generators through the network + bool stop = false; + while (!stop) + { + stop = true; + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + // if both points are captured by the same team, and only one of + // them is powered, mark the other one as powered as well + if (l.enemy.iscaptured && l.goalentity.iscaptured) + if (l.enemy.islinked != l.goalentity.islinked) + if(SAME_TEAM(l.enemy, l.goalentity)) + { + if (!l.goalentity.islinked) + { + stop = false; + l.goalentity.islinked = true; + LOG_DEBUG(etos(l), " (link) is marking ", etos(l.goalentity), " (point) because its team matches ", etos(l.enemy), " (point)"); + } + else if (!l.enemy.islinked) + { + stop = false; + l.enemy.islinked = true; + LOG_DEBUG(etos(l), " (link) is marking ", etos(l.enemy), " (point) because its team matches ", etos(l.goalentity), " (point)"); + } + } + } + } + // now that we know which points are powered we can mark their neighbors + // as unshielded if team differs + for(l = ons_worldlinklist; l; l = l.ons_worldlinknext) + { + if (l.goalentity.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.enemy), " (point) because its team does not match ", etos(l.goalentity), " (point)"); + l.enemy.isshielded = false; + } + if(l.goalentity.classname == "onslaught_generator") + l.enemy.isgenneighbor[l.goalentity.team] = true; + else + l.enemy.iscpneighbor[l.goalentity.team] = true; + } + if (l.enemy.islinked) + { + if(DIFF_TEAM(l.goalentity, l.enemy)) + { + LOG_DEBUG(etos(l), " (link) is unshielding ", etos(l.goalentity), " (point) because its team does not match ", etos(l.enemy), " (point)"); + l.goalentity.isshielded = false; + } + if(l.enemy.classname == "onslaught_generator") + l.goalentity.isgenneighbor[l.enemy.team] = true; + else + l.goalentity.iscpneighbor[l.enemy.team] = true; + } + } + // now update the generators + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + if (l.isshielded) + { + LOG_DEBUG(etos(l), " (generator) is shielded"); + l.takedamage = DAMAGE_NO; + if(l.bot_attack) + IL_REMOVE(g_bot_targets, l); + l.bot_attack = false; + } + else + { + LOG_DEBUG(etos(l), " (generator) is not shielded"); + l.takedamage = DAMAGE_AIM; + if(!l.bot_attack) + IL_PUSH(g_bot_targets, l); + l.bot_attack = true; + } + + ons_Generator_UpdateSprite(l); + } + // now update the takedamage and alpha variables on control point icons + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + if (l.isshielded) + { + LOG_DEBUG(etos(l), " (point) is shielded"); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_NO; + if(l.goalentity.bot_attack) + IL_REMOVE(g_bot_targets, l.goalentity); + l.goalentity.bot_attack = false; + } + } + else + { + LOG_DEBUG(etos(l), " (point) is not shielded"); + if (l.goalentity) + { + l.goalentity.takedamage = DAMAGE_AIM; + if(!l.goalentity.bot_attack) + IL_PUSH(g_bot_targets, l.goalentity); + l.goalentity.bot_attack = true; + } + } + ons_ControlPoint_UpdateSprite(l); + } + IL_EACH(g_onsshields, true, + { + it.team = it.enemy.team; + it.colormap = it.enemy.colormap; + }); +} + + +// =================== +// Main Link Functions +// =================== + +bool ons_Link_Send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_CLIENT_RADARLINK); + WriteByte(MSG_ENTITY, sendflags); + if(sendflags & 1) + { + WriteCoord(MSG_ENTITY, this.goalentity.origin_x); + WriteCoord(MSG_ENTITY, this.goalentity.origin_y); + WriteCoord(MSG_ENTITY, this.goalentity.origin_z); + } + if(sendflags & 2) + { + WriteCoord(MSG_ENTITY, this.enemy.origin_x); + WriteCoord(MSG_ENTITY, this.enemy.origin_y); + WriteCoord(MSG_ENTITY, this.enemy.origin_z); + } + if(sendflags & 4) + { + WriteByte(MSG_ENTITY, this.clientcolors); // which is goalentity's color + enemy's color * 16 + } + return true; +} + +void ons_Link_CheckUpdate(entity this) +{ + // TODO check if the two sides have moved (currently they won't move anyway) + float cc = 0, cc1 = 0, cc2 = 0; + + if(this.goalentity.islinked || this.goalentity.iscaptured) { cc1 = (this.goalentity.team - 1) * 0x01; } + if(this.enemy.islinked || this.enemy.iscaptured) { cc2 = (this.enemy.team - 1) * 0x10; } + + cc = cc1 + cc2; + + if(cc != this.clientcolors) + { + this.clientcolors = cc; + this.SendFlags |= 4; + } + + this.nextthink = time; +} + +void ons_DelayedLinkSetup(entity this) +{ + this.goalentity = find(NULL, targetname, this.target); + this.enemy = find(NULL, targetname, this.target2); + if(!this.goalentity) { objerror(this, "can not find target\n"); } + if(!this.enemy) { objerror(this, "can not find target2\n"); } + + LOG_DEBUG(etos(this.goalentity), " linked with ", etos(this.enemy)); + this.SendFlags |= 3; + setthink(this, ons_Link_CheckUpdate); + this.nextthink = time; +} + + +// ============================= +// Main Control Point Functions +// ============================= + +int ons_ControlPoint_CanBeLinked(entity cp, int teamnumber) +{ + if(cp.isgenneighbor[teamnumber]) { return 2; } + if(cp.iscpneighbor[teamnumber]) { return 1; } + + return 0; +} + +int ons_ControlPoint_Attackable(entity cp, int teamnumber) + // -2: SAME TEAM, attackable by enemy! + // -1: SAME TEAM! + // 0: off limits + // 1: attack it + // 2: touch it + // 3: attack it (HIGH PRIO) + // 4: touch it (HIGH PRIO) +{ + int a; + + if(cp.isshielded) + { + return 0; + } + else if(cp.goalentity) + { + // if there's already an icon built, nothing happens + if(cp.team == teamnumber) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a) // attackable by enemy? + return -2; // EMERGENCY! + return -1; + } + // we know it can be linked, so no need to check + // but... + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); + if(a == 2) // near our generator? + return 3; // EMERGENCY! + return 1; + } + else + { + // free point + if(ons_ControlPoint_CanBeLinked(cp, teamnumber)) + { + a = ons_ControlPoint_CanBeLinked(cp, teamnumber); // why was this here NUM_TEAM_1 + NUM_TEAM_2 - t + if(a == 2) + return 4; // GET THIS ONE NOW! + else + return 2; // TOUCH ME + } + } + return 0; +} + +void ons_ControlPoint_Icon_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + + if (this.owner.isshielded) + { + // this is protected by a shield, so ignore the damage + if (time > this.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + this.pain_finished = time + 1; + attacker.typehitsound += 1; // play both sounds (shield is way too quiet) + } + + return; + } + + if(IS_PLAYER(attacker)) + if(time - ons_notification_time[this.team] > 10) + { + play2team(this.team, SND(ONS_CONTROLPOINT_UNDERATTACK)); + ons_notification_time[this.team] = time; + } + + this.health = this.health - damage; + if(this.owner.iscaptured) + WaypointSprite_UpdateHealth(this.owner.sprite, this.health); + else + WaypointSprite_UpdateBuildFinished(this.owner.sprite, time + (this.max_health - this.health) / (this.count / ONS_CP_THINKRATE)); + this.pain_finished = time + 1; + // particles on every hit + pointparticles(EFFECT_SPARKS, hitloc, force*-1, 1); + //sound on every hit + if (random() < 0.5) + sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE+0.3, ATTEN_NORM); + else + sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE+0.3, ATTEN_NORM); + + if (this.health < 0) + { + sound(this, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + pointparticles(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_CPDESTROYED), this.owner.message, attacker.netname); + + PlayerScore_Add(attacker, SP_ONS_TAKES, 1); + PlayerScore_Add(attacker, SP_SCORE, 10); + + this.owner.goalentity = NULL; + this.owner.islinked = false; + this.owner.iscaptured = false; + this.owner.team = 0; + this.owner.colormap = 1024; + + WaypointSprite_UpdateMaxHealth(this.owner.sprite, 0); + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + SUB_UseTargets(this.owner, this, NULL); + + this.owner.waslinked = this.owner.islinked; + if(this.owner.model != "models/onslaught/controlpoint_pad.md3") + setmodel_fixsize(this.owner, MDL_ONS_CP_PAD1); + //setsize(this, '-32 -32 0', '32 32 8'); + + delete(this); + } + + this.SendFlags |= CPSF_STATUS; +} + +void ons_ControlPoint_Icon_Think(entity this) +{ + this.nextthink = time + ONS_CP_THINKRATE; + + if(autocvar_g_onslaught_cp_proxydecap) + { + int _enemy_count = 0; + int _friendly_count = 0; + + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { + if(vdist(it.origin - this.origin, <, autocvar_g_onslaught_cp_proxydecap_distance)) + { + if(SAME_TEAM(it, this)) + ++_friendly_count; + else + ++_enemy_count; + } + }); + + _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); + this.SendFlags |= CPSF_STATUS; + if(this.health <= 0) + { + ons_ControlPoint_Icon_Damage(this, this, this, 1, 0, this.origin, '0 0 0'); + return; + } + } + + if (time > this.pain_finished + 5) + { + if(this.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); + } + } + + if(this.owner.islinked != this.owner.waslinked) + { + // unteam the spawnpoint if needed + int t = this.owner.team; + if(!this.owner.islinked) + this.owner.team = 0; + + SUB_UseTargets(this.owner, this, NULL); + + this.owner.team = t; + + this.owner.waslinked = this.owner.islinked; + } + + // damaged fx + if(random() < 0.6 - this.health / this.max_health) + { + Send_Effect(EFFECT_ELECTRIC_SPARKS, this.origin + randompos('-10 -10 -20', '10 10 20'), '0 0 0', 1); + + if(random() > 0.8) + sound(this, CH_PAIN, SND_ONS_SPARK1, VOL_BASE, ATTEN_NORM); + else if (random() > 0.5) + sound(this, CH_PAIN, SND_ONS_SPARK2, VOL_BASE, ATTEN_NORM); + } +} + +void ons_ControlPoint_Icon_BuildThink(entity this) +{ + int a; + + this.nextthink = time + ONS_CP_THINKRATE; + + // only do this if there is power + a = ons_ControlPoint_CanBeLinked(this.owner, this.owner.team); + if(!a) + return; + + this.health = this.health + this.count; + + this.SendFlags |= CPSF_STATUS; + + if (this.health >= this.max_health) + { + this.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); + this.owner.iscaptured = true; + this.solid = SOLID_BBOX; + + 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); + + if(IS_PLAYER(this.owner.ons_toucher)) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ONSLAUGHT_CAPTURE, this.owner.ons_toucher.netname, this.owner.message); + Send_Notification(NOTIF_ALL_EXCEPT, this.owner.ons_toucher, MSG_CENTER, APP_TEAM_NUM(this.owner.ons_toucher.team, CENTER_ONS_CAPTURE), this.owner.message); + Send_Notification(NOTIF_ONE, this.owner.ons_toucher, MSG_CENTER, CENTER_ONS_CAPTURE, this.owner.message); + PlayerScore_Add(this.owner.ons_toucher, SP_ONS_CAPS, 1); + PlayerTeamScore_AddScore(this.owner.ons_toucher, 10); + } + + this.owner.ons_toucher = NULL; + + onslaught_updatelinks(); + + // Use targets now (somebody make sure this is in the right place..) + SUB_UseTargets(this.owner, this, NULL); + + this.SendFlags |= CPSF_SETUP; + } + if(this.owner.model != MDL_ONS_CP_PAD2.model_str()) + setmodel_fixsize(this.owner, MDL_ONS_CP_PAD2); + + if(random() < 0.9 - this.health / this.max_health) + Send_Effect(EFFECT_RAGE, this.origin + 10 * randomvec(), '0 0 -1', 1); +} + +void onslaught_controlpoint_icon_link(entity e, void(entity this) spawnproc); + +void ons_ControlPoint_Icon_Spawn(entity cp, entity player) +{ + entity e = new(onslaught_controlpoint_icon); + + setsize(e, CPICON_MIN, CPICON_MAX); + setorigin(e, cp.origin + CPICON_OFFSET); + + e.owner = cp; + e.max_health = autocvar_g_onslaught_cp_health; + e.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.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 + + sound(e, CH_TRIGGER, SND_ONS_CONTROLPOINT_BUILD, VOL_BASE, ATTEN_NORM); + + cp.goalentity = e; + cp.team = e.team; + cp.colormap = e.colormap; + + 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_UpdateRule(cp.sprite,cp.team,SPRITERULE_TEAMPLAY); + cp.sprite.SendFlags |= 16; + + onslaught_controlpoint_icon_link(e, ons_ControlPoint_Icon_BuildThink); +} + +entity ons_ControlPoint_Waypoint(entity e) +{ + if(e.team) + { + int a = ons_ControlPoint_Attackable(e, e.team); + + if(a == -2) { return WP_OnsCPDefend; } // defend now + if(a == -1 || a == 1 || a == 2) { return WP_OnsCP; } // touch + if(a == 3 || a == 4) { return WP_OnsCPAttack; } // attack + } + else + return WP_OnsCP; + + return WP_Null; +} + +void ons_ControlPoint_UpdateSprite(entity e) +{ + entity s1 = ons_ControlPoint_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + bool sh; + sh = !(ons_ControlPoint_CanBeLinked(e, NUM_TEAM_1) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_2) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_3) || ons_ControlPoint_CanBeLinked(e, NUM_TEAM_4)); + + if(e.lastteam != e.team + 2 || e.lastshielded != sh || e.iscaptured != e.lastcaptured) + { + if(e.iscaptured) // don't mess up build bars! + { + if(sh) + { + WaypointSprite_UpdateMaxHealth(e.sprite, 0); + } + else + { + WaypointSprite_UpdateMaxHealth(e.sprite, e.goalentity.max_health); + WaypointSprite_UpdateHealth(e.sprite, e.goalentity.health); + } + } + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_CONTROLPOINT, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + + e.lastteam = e.team + 2; + e.lastshielded = sh; + e.lastcaptured = e.iscaptured; + } +} + +void ons_ControlPoint_Touch(entity this, entity toucher) +{ + int attackable; + + if(IS_VEHICLE(toucher) && toucher.owner) + if(autocvar_g_onslaught_allow_vehicle_touch) + toucher = toucher.owner; + else + return; + + if(!IS_PLAYER(toucher)) { return; } + if(STAT(FROZEN, toucher)) { return; } + if(IS_DEAD(toucher)) { return; } + + if ( SAME_TEAM(this,toucher) ) + if ( this.iscaptured ) + { + if(time <= toucher.teleport_antispam) + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT_ANTISPAM, rint(toucher.teleport_antispam - time)); + else + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); + } + + attackable = ons_ControlPoint_Attackable(this, toucher.team); + if(attackable != 2 && attackable != 4) + return; + // we've verified that this player has a legitimate claim to this point, + // so start building the captured point icon (which only captures this + // point if it successfully builds without being destroyed first) + ons_ControlPoint_Icon_Spawn(this, toucher); + + this.ons_toucher = toucher; + + onslaught_updatelinks(); +} + +void ons_ControlPoint_Think(entity this) +{ + this.nextthink = time + ONS_CP_THINKRATE; + CSQCMODEL_AUTOUPDATE(this); +} + +void ons_ControlPoint_Reset(entity this) +{ + if(this.goalentity) + delete(this.goalentity); + + this.goalentity = NULL; + this.team = 0; + this.colormap = 1024; + this.iscaptured = false; + this.islinked = false; + this.isshielded = true; + setthink(this, ons_ControlPoint_Think); + this.ons_toucher = NULL; + this.nextthink = time + ONS_CP_THINKRATE; + setmodel_fixsize(this, MDL_ONS_CP_PAD1); + + WaypointSprite_UpdateMaxHealth(this.sprite, 0); + WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); + + SUB_UseTargets(this, this, NULL); // to reset the structures, playerspawns etc. + + CSQCMODEL_AUTOUPDATE(this); +} + +void ons_DelayedControlPoint_Setup(entity this) +{ + onslaught_updatelinks(); + + // captureshield setup + ons_CaptureShield_Spawn(this, false); + + CSQCMODEL_AUTOINIT(this); +} + +void ons_ControlPoint_Setup(entity cp) +{ + // main setup + cp.ons_worldcpnext = ons_worldcplist; // link control point into ons_worldcplist + ons_worldcplist = cp; + + cp.netname = "Control point"; + cp.team = 0; + cp.solid = SOLID_BBOX; + set_movetype(cp, MOVETYPE_NONE); + settouch(cp, ons_ControlPoint_Touch); + setthink(cp, ons_ControlPoint_Think); + cp.nextthink = time + ONS_CP_THINKRATE; + cp.reset = ons_ControlPoint_Reset; + cp.colormap = 1024; + cp.iscaptured = false; + cp.islinked = false; + cp.isshielded = true; + + if(cp.message == "") { cp.message = "a"; } + + // appearence + setmodel_fixsize(cp, MDL_ONS_CP_PAD1); + + // control point placement + if((cp.spawnflags & 1) || cp.noalign) // don't drop to floor, just stay at fixed location + { + cp.noalign = true; + set_movetype(cp, MOVETYPE_NONE); + } + else // drop to floor, automatically find a platform and set that as spawn origin + { + setorigin(cp, cp.origin + '0 0 20'); + cp.noalign = false; + droptofloor(cp); + set_movetype(cp, MOVETYPE_TOSS); + } + + // waypointsprites + WaypointSprite_SpawnFixed(WP_Null, cp.origin + CPGEN_WAYPOINT_OFFSET, cp, sprite, RADARICON_NONE); + WaypointSprite_UpdateRule(cp.sprite, cp.team, SPRITERULE_TEAMPLAY); + + InitializeEntity(cp, ons_DelayedControlPoint_Setup, INITPRIO_SETLOCATION); +} + + +// ========================= +// Main Generator Functions +// ========================= + +entity ons_Generator_Waypoint(entity e) +{ + if (e.isshielded) + return WP_OnsGenShielded; + return WP_OnsGen; +} + +void ons_Generator_UpdateSprite(entity e) +{ + entity s1 = ons_Generator_Waypoint(e); + WaypointSprite_UpdateSprites(e.sprite, s1, s1, s1); + + if(e.lastteam != e.team + 2 || e.lastshielded != e.isshielded) + { + e.lastteam = e.team + 2; + e.lastshielded = e.isshielded; + if(e.lastshielded) + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, 0.5 * colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.5 0.5 0.5'); + } + else + { + if(e.team) + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, colormapPaletteColor(e.team - 1, false)); + else + WaypointSprite_UpdateTeamRadar(e.sprite, RADARICON_GENERATOR, '0.75 0.75 0.75'); + } + WaypointSprite_Ping(e.sprite); + } +} + +void ons_camSetup(entity this) +{ + vector dir; + vector ang = '0 0 0'; + vector best_ang = '0 0 0'; + float best_trace_fraction = 0; + while(ang.y < 360) + { + dir = eX * cos(ang.y * DEG2RAD) + eY * sin(ang.y * DEG2RAD); + dir *= 500; + traceline(this.origin, this.origin - dir, MOVE_WORLDONLY, this); + if(trace_fraction > best_trace_fraction) + { + best_trace_fraction = trace_fraction; + best_ang = ang; + if(trace_fraction == 1) + break; + } + ang.y += 90; + if(ang.y == 360) + ang.y = 45; + } + cam.origin = this.origin; + setorigin(cam, cam.origin); + cam.angles = best_ang; + Net_LinkEntity(cam, false, 0, clientcamera_send); + + FOREACH_CLIENT(true, it.clientcamera = cam;); + + WriteByte(MSG_ALL, SVC_SETVIEWANGLES); + WriteAngle(MSG_ALL, cam.angles_x); + WriteAngle(MSG_ALL, cam.angles_y); + WriteAngle(MSG_ALL, cam.angles_z); +} + +void ons_GeneratorDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(damage <= 0) { return; } + if(warmup_stage || gameover) { return; } + if(!round_handler_IsRoundStarted()) { return; } + + if (attacker != this) + { + if (this.isshielded) + { + // generator is protected by a shield, so ignore the damage + if (time > this.pain_finished) + if (IS_PLAYER(attacker)) + { + play2(attacker, SND(ONS_DAMAGEBLOCKEDBYSHIELD)); + attacker.typehitsound += 1; + this.pain_finished = time + 1; + } + return; + } + if (time > this.pain_finished) + { + this.pain_finished = time + 10; + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && SAME_TEAM(it, this), Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_GENERATOR_UNDERATTACK)); + play2team(this.team, SND(ONS_GENERATOR_UNDERATTACK)); + } + } + this.health = this.health - damage; + WaypointSprite_UpdateHealth(this.sprite, this.health); + // choose an animation frame based on health + this.frame = 10 * bound(0, (1 - this.health / this.max_health), 1); + // see if the generator is still functional, or dying + if (this.health > 0) + { + this.lasthealth = this.health; + } + else + { + if (attacker == this) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED_OVERTIME)); + else + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_ONSLAUGHT_GENDESTROYED)); + PlayerScore_Add(attacker, SP_SCORE, 100); + } + this.iscaptured = false; + this.islinked = false; + this.isshielded = false; + this.takedamage = DAMAGE_NO; // can't be hurt anymore + this.event_damage = func_null; // won't do anything if hurt + this.count = 0; // reset counter + setthink(this, func_null); + this.nextthink = 0; + //this.think(); // do the first explosion now + + WaypointSprite_UpdateMaxHealth(this.sprite, 0); + WaypointSprite_Ping(this.sprite); + //WaypointSprite_Kill(this.sprite); // can't do this yet, code too poor + + onslaught_updatelinks(); + + ons_camSetup(this); + } + + // Throw some flaming gibs on damage, more damage = more chance for gib + if(random() < damage/220) + { + sound(this, CH_TRIGGER, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); + } + else + { + // particles on every hit + Send_Effect(EFFECT_SPARKS, hitloc, force * -1, 1); + + //sound on every hit + if (random() < 0.5) + sound(this, CH_TRIGGER, SND_ONS_HIT1, VOL_BASE, ATTEN_NORM); + else + sound(this, CH_TRIGGER, SND_ONS_HIT2, VOL_BASE, ATTEN_NORM); + } + + this.SendFlags |= GSF_STATUS; +} + +void ons_GeneratorThink(entity this) +{ + this.nextthink = time + GEN_THINKRATE; + if (!gameover) + { + if(!this.isshielded && this.wait < time) + { + 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)); + }); + } + } +} + +void ons_GeneratorReset(entity this) +{ + this.team = this.team_saved; + this.lasthealth = this.max_health = this.health = autocvar_g_onslaught_gen_health; + this.takedamage = DAMAGE_AIM; + this.bot_attack = true; + if(!IL_CONTAINS(g_bot_targets, this)) + IL_PUSH(g_bot_targets, this); + this.iscaptured = true; + this.islinked = true; + this.isshielded = true; + this.event_damage = ons_GeneratorDamage; + setthink(this, ons_GeneratorThink); + this.nextthink = time + GEN_THINKRATE; + + Net_LinkEntity(this, false, 0, generator_send); + + this.SendFlags = GSF_SETUP; // just incase + this.SendFlags |= GSF_STATUS; + + WaypointSprite_UpdateMaxHealth(this.sprite, this.max_health); + WaypointSprite_UpdateHealth(this.sprite, this.health); + WaypointSprite_UpdateRule(this.sprite,this.team,SPRITERULE_TEAMPLAY); + + onslaught_updatelinks(); +} + +void ons_DelayedGeneratorSetup(entity this) +{ + // bot waypoints + waypoint_spawnforitem_force(this, this.origin); + this.nearestwaypointtimeout = 0; // activate waypointing again + this.bot_basewaypoint = this.nearestwaypoint; + + // captureshield setup + ons_CaptureShield_Spawn(this, true); + + onslaught_updatelinks(); + + Net_LinkEntity(this, false, 0, generator_send); +} + + +void onslaught_generator_touch(entity this, entity toucher) +{ + if ( IS_PLAYER(toucher) ) + if ( SAME_TEAM(this,toucher) ) + if ( this.iscaptured ) + { + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_ONS_TELEPORT); + } +} + +void ons_GeneratorSetup(entity gen) // called when spawning a generator entity on the map as a spawnfunc +{ + // declarations + int teamnumber = gen.team; + + // main setup + gen.ons_worldgeneratornext = ons_worldgeneratorlist; // link generator into ons_worldgeneratorlist + ons_worldgeneratorlist = gen; + + gen.netname = sprintf("%s generator", Team_ColoredFullName(teamnumber)); + gen.classname = "onslaught_generator"; + gen.solid = SOLID_BBOX; + gen.team_saved = teamnumber; + set_movetype(gen, MOVETYPE_NONE); + gen.lasthealth = gen.max_health = gen.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.reset = ons_GeneratorReset; + setthink(gen, ons_GeneratorThink); + gen.nextthink = time + GEN_THINKRATE; + gen.iscaptured = true; + gen.islinked = true; + gen.isshielded = true; + settouch(gen, onslaught_generator_touch); + + // appearence + // model handled by CSQC + setsize(gen, GENERATOR_MIN, GENERATOR_MAX); + setorigin(gen, (gen.origin + CPGEN_SPAWN_OFFSET)); + gen.colormap = 1024 + (teamnumber - 1) * 17; + + // generator placement + droptofloor(gen); + + // waypointsprites + 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); + + InitializeEntity(gen, ons_DelayedGeneratorSetup, INITPRIO_SETLOCATION); +} + + +// =============== +// Round Handler +// =============== + +int total_generators; +void Onslaught_count_generators() +{ + entity e; + total_generators = redowned = blueowned = yellowowned = pinkowned = 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); + } +} + +int Onslaught_GetWinnerTeam() +{ + int winner_team = 0; + if(redowned > 0) + winner_team = NUM_TEAM_1; + if(blueowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_2; + } + if(yellowowned > 0) + { + if(winner_team) return 0; + winner_team = NUM_TEAM_3; + } + if(pinkowned > 0) + { + 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)) + { + ons_stalemate = true; + + if (!wpforenemy_announced) + { + Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); + sound(NULL, CH_INFO, SND_ONS_GENERATOR_DECAY, VOL_BASE, ATTEN_NONE); + + wpforenemy_announced = true; + } + + entity tmp_entity; // temporary entity + float d; + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) if(time >= tmp_entity.ons_overtime_damagedelay) + { + // tmp_entity.max_health / 300 gives 5 minutes of overtime. + // control points reduce the overtime duration. + d = 1; + entity e; + for(e = ons_worldcplist; e; e = e.ons_worldcpnext) + { + if(DIFF_TEAM(e, tmp_entity)) + if(e.islinked) + d = d + 1; + } + + if(autocvar_g_campaign && autocvar__campaign_testrun) + d = d * tmp_entity.max_health; + else + d = d * tmp_entity.max_health / max(30, 60 * autocvar_timelimit_suddendeath); + + Damage(tmp_entity, tmp_entity, tmp_entity, d, DEATH_HURTTRIGGER.m_id, tmp_entity.origin, '0 0 0'); + + tmp_entity.sprite.SendFlags |= 16; + + tmp_entity.ons_overtime_damagedelay = time + 1; + } + } + else { wpforenemy_announced = false; ons_stalemate = false; } + + Onslaught_count_generators(); + + if(ONS_OWNED_GENERATORS_OK()) + return 0; + + int winner_team = Onslaught_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_ONS_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); + } + + ons_stalemate = false; + + play2all(SND(CTF_CAPTURE(winner_team))); + + round_handler_Init(7, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); + + FOREACH_CLIENT(IS_PLAYER(it), { + STAT(ROUNDLOST, it) = true; + it.player_blocked = true; + + nades_Clear(it); + }); + + return 1; +} + +bool Onslaught_CheckPlayers() +{ + return 1; +} + +void Onslaught_RoundStart() +{ + entity tmp_entity; + FOREACH_CLIENT(IS_PLAYER(it), it.player_blocked = false); + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + tmp_entity.sprite.SendFlags |= 16; + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + tmp_entity.sprite.SendFlags |= 16; +} + + +// ================ +// Bot player logic +// ================ + +// 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"); + 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"); + 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"); + 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) +{ + entity cp, cp1, cp2, best, wp; + float radius, bestvalue; + int c; + bool found; + + // Filter control points + for(cp2 = ons_worldcplist; cp2; cp2 = cp2.ons_worldcpnext) + { + cp2.wpcost = c = 0; + cp2.wpconsidered = false; + + if(cp2.isshielded) + continue; + + // Ignore owned controlpoints + if(!(cp2.isgenneighbor[this.team] || cp2.iscpneighbor[this.team])) + continue; + + // Count team mates interested in this control point + // (easier and cleaner than keeping counters per cp and teams) + FOREACH_CLIENT(IS_PLAYER(it), { + if(SAME_TEAM(it, this)) + if(it.havocbot_role_flags & HAVOCBOT_ONS_ROLE_OFFENSE) + if(it.havocbot_ons_target == cp2) + ++c; + }); + + // NOTE: probably decrease the cost of attackable control points + cp2.wpcost = c; + cp2.wpconsidered = true; + } + + // We'll consider only the best case + bestvalue = 99999999999; + cp = NULL; + for(cp1 = ons_worldcplist; cp1; cp1 = cp1.ons_worldcpnext) + { + if (!cp1.wpconsidered) + continue; + + if(cp1.wpcost this.havocbot_role_timeout) + { + havocbot_ons_reset_role(this); + return; + } + + if(this.havocbot_attack_time>time) + return; + + if (this.bot_strategytime < time) + { + 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); + navigation_goalrating_end(this); + + this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + } +} + +void havocbot_role_ons_assistant(entity this) +{ + havocbot_ons_reset_role(this); +} + +void havocbot_role_ons_defense(entity this) +{ + havocbot_ons_reset_role(this); +} + +void havocbot_ons_reset_role(entity this) +{ + if(IS_DEAD(this)) + return; + + this.havocbot_ons_target = NULL; + + // TODO: Defend control points or generator if necessary + + havocbot_role_ons_setrole(this, HAVOCBOT_ONS_ROLE_OFFENSE); +} + + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + */ +entity ons_Nearest_ControlPoint(entity this, vector pos, float max_dist) +{ + entity closest_target = NULL; + for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext) + { + if(SAME_TEAM(cp, this)) + if(cp.iscaptured) + if(max_dist <= 0 || vdist(cp.origin - pos, <=, max_dist)) + if(vlen2(cp.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = cp; + } + for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext) + { + if(SAME_TEAM(gen, this)) + if(max_dist <= 0 || vdist(gen.origin - pos, <, max_dist)) + if(vlen2(gen.origin - pos) <= vlen2(closest_target.origin - pos) || closest_target == NULL) + closest_target = gen; + } + + return closest_target; +} + +/* + * Find control point or generator owned by the same team self which is nearest to pos + * if max_dist is positive, only control points within this range will be considered + * This function only check distances on the XY plane, disregarding Z + */ +entity ons_Nearest_ControlPoint_2D(entity this, vector pos, float max_dist) +{ + entity closest_target = NULL; + vector delta; + float smallest_distance = 0, distance; + + for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext) + { + delta = cp.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(cp, this)) + if(cp.iscaptured) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == NULL || distance <= smallest_distance ) + { + closest_target = cp; + smallest_distance = distance; + } + } + for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext) + { + delta = gen.origin - pos; + delta_z = 0; + distance = vlen(delta); + + if(SAME_TEAM(gen, this)) + if(max_dist <= 0 || distance <= max_dist) + if(closest_target == NULL || distance <= smallest_distance ) + { + closest_target = gen; + smallest_distance = distance; + } + } + + return closest_target; +} +/** + * find the number of control points and generators in the same team as this + */ +int ons_Count_SelfControlPoints(entity this) +{ + int n = 0; + for(entity cp = ons_worldcplist; cp; cp = cp.ons_worldcpnext) + { + if(SAME_TEAM(cp, this)) + if(cp.iscaptured) + n++; + } + for(entity gen = ons_worldgeneratorlist; gen; gen = gen.ons_worldgeneratornext) + { + if(SAME_TEAM(gen, this)) + n++; + } + return n; +} + +/** + * Teleport player to a random position near tele_target + * if tele_effects is true, teleport sound+particles are created + * return false on failure + */ +bool ons_Teleport(entity player, entity tele_target, float range, bool tele_effects) +{ + if ( !tele_target ) + return false; + + int i; + vector loc; + float theta; + // narrow the range for each iteration to increase chances that a spawnpoint + // can be found even if there's little room around the control point + float iteration_scale = 1; + for(i = 0; i < 16; ++i) + { + iteration_scale -= i / 16; + theta = random() * 2 * M_PI; + loc_y = sin(theta); + loc_x = cos(theta); + loc_z = 0; + loc *= random() * range * iteration_scale; + + loc += tele_target.origin + '0 0 128' * iteration_scale; + + tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(tele_target.origin, loc, MOVE_NOMONSTERS, tele_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + if ( tele_effects ) + { + Send_Effect(EFFECT_TELEPORT, player.origin, '0 0 0', 1); + sound (player, CH_TRIGGER, SND_TELEPORT, VOL_BASE, ATTEN_NORM); + } + setorigin(player, loc); + player.angles = '0 1 0' * ( theta * RAD2DEG + 180 ); + makevectors(player.angles); + player.fixangle = true; + player.teleport_antispam = time + autocvar_g_onslaught_teleport_wait; + + if ( tele_effects ) + Send_Effect(EFFECT_TELEPORT, player.origin + v_forward * 32, '0 0 0', 1); + return true; + } + } + } + + return false; +} + +// ============== +// Hook Functions +// ============== + +MUTATOR_HOOKFUNCTION(ons, reset_map_global) +{ + FOREACH_CLIENT(IS_PLAYER(it), { + STAT(ROUNDLOST, it) = false; + it.ons_deathloc = '0 0 0'; + PutClientInServer(it); + it.clientcamera = it; + }); + return false; +} + +MUTATOR_HOOKFUNCTION(ons, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + player.ons_deathloc = '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(ons, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + player.ons_deathloc = '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(!round_handler_IsRoundStarted()) + { + player.player_blocked = true; + return false; + } + + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if(ons_stalemate) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERTIME_CONTROLPOINT); } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( player.ons_spawn_by ) + if ( ons_Teleport(player,player.ons_spawn_by,autocvar_g_onslaught_teleport_radius,false) ) + { + player.ons_spawn_by = NULL; + return false; + } + + if(autocvar_g_onslaught_spawn_at_controlpoints) + if(random() <= autocvar_g_onslaught_spawn_at_controlpoints_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_controlpoints_random; + entity tmp_entity, closest_target = NULL; + vector spawn_loc = player.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldcplist; tmp_entity; tmp_entity = tmp_entity.ons_worldcpnext) + { + if(SAME_TEAM(tmp_entity, player)) + if(random_target) + RandomSelection_AddEnt(tmp_entity, 1, 1); + else if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) + closest_target = tmp_entity; + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 96' * iteration_scale; + loc += ('0 1 0' * random()) * 128 * iteration_scale; + tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(player, loc); + player.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + if(autocvar_g_onslaught_spawn_at_generator) + if(random() <= autocvar_g_onslaught_spawn_at_generator_chance) + { + float random_target = autocvar_g_onslaught_spawn_at_generator_random; + entity tmp_entity, closest_target = NULL; + vector spawn_loc = player.ons_deathloc; + + // new joining player or round reset, don't bother checking + if(spawn_loc == '0 0 0') { return false; } + + if(random_target) { RandomSelection_Init(); } + + for(tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + if(random_target) + RandomSelection_AddEnt(tmp_entity, 1, 1); + else + { + if(SAME_TEAM(tmp_entity, player)) + if(vlen2(tmp_entity.origin - spawn_loc) <= vlen2(closest_target.origin - spawn_loc) || closest_target == NULL) + closest_target = tmp_entity; + } + } + + if(random_target) { closest_target = RandomSelection_chosen_ent; } + + if(closest_target) + { + float i; + vector loc; + float iteration_scale = 1; + for(i = 0; i < 10; ++i) + { + iteration_scale -= i / 10; + loc = closest_target.origin + '0 0 128' * iteration_scale; + loc += ('0 1 0' * random()) * 256 * iteration_scale; + tracebox(loc, STAT(PL_MIN, player), STAT(PL_MAX, player), loc, MOVE_NORMAL, player); + if(trace_fraction == 1.0 && !trace_startsolid) + { + traceline(closest_target.origin, loc, MOVE_NOMONSTERS, closest_target); // double check to make sure we're not spawning outside the NULL + if(trace_fraction == 1.0 && !trace_startsolid) + { + setorigin(player, loc); + player.angles = normalize(loc - closest_target.origin) * RAD2DEG; + return false; + } + } + } + } + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + frag_target.ons_deathloc = frag_target.origin; + entity l; + for(l = ons_worldgeneratorlist; l; l = l.ons_worldgeneratornext) + { + l.sprite.SendFlags |= 16; + } + for(l = ons_worldcplist; l; l = l.ons_worldcpnext) + { + l.sprite.SendFlags |= 16; + } + + if ( autocvar_g_onslaught_spawn_choose ) + if ( ons_Count_SelfControlPoints(frag_target) > 1 ) + stuffcmd(frag_target, "qc_cmd_cl hud clickradar\n"); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, MonsterMove) +{ + entity mon = M_ARGV(0, entity); + + entity e = find(NULL, targetname, mon.target); + if (e != NULL) + mon.team = e.team; +} + +void ons_MonsterSpawn_Delayed(entity this) +{ + entity own = this.owner; + + if(!own) { delete(this); return; } + + if(own.targetname) + { + entity e = find(NULL, target, own.targetname); + if(e != NULL) + { + own.team = e.team; + + own.use(own, e, NULL); + } + } + + delete(this); +} + +MUTATOR_HOOKFUNCTION(ons, MonsterSpawn) +{ + entity mon = M_ARGV(0, entity); + + entity e = spawn(); + e.owner = mon; + InitializeEntity(e, ons_MonsterSpawn_Delayed, INITPRIO_FINDTARGET); +} + +void ons_TurretSpawn_Delayed(entity this) +{ + entity own = this.owner; + + if(!own) { delete(this); return; } + + if(own.targetname) + { + entity e = find(NULL, target, own.targetname); + if(e != NULL) + { + own.team = e.team; + own.active = ACTIVE_NOT; + + own.use(own, e, NULL); + } + } + + delete(this); +} + +MUTATOR_HOOKFUNCTION(ons, TurretSpawn) +{ + entity turret = M_ARGV(0, entity); + + entity e = spawn(); + e.owner = turret; + InitializeEntity(e, ons_TurretSpawn_Delayed, INITPRIO_FINDTARGET); + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, HavocBot_ChooseRole) +{ + entity bot = M_ARGV(0, entity); + + havocbot_ons_reset_role(bot); + return true; +} + +MUTATOR_HOOKFUNCTION(ons, CheckAllowedTeams) +{ + // onslaught is special + for(entity tmp_entity = ons_worldgeneratorlist; tmp_entity; tmp_entity = tmp_entity.ons_worldgeneratornext) + { + switch(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; + } + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ons, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + STAT(ROUNDLOST, client) = STAT(ROUNDLOST, spectatee); // make spectators see it too +} + +MUTATOR_HOOKFUNCTION(ons, SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? + return false; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if ( cmd_name == "ons_spawn" ) + { + vector pos = player.origin; + if(cmd_argc > 1) + pos_x = stof(argv(1)); + if(cmd_argc > 2) + pos_y = stof(argv(2)); + if(cmd_argc > 3) + pos_z = stof(argv(3)); + + if ( IS_PLAYER(player) ) + { + if ( !STAT(FROZEN, player) ) + { + entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); + + if ( !source_point && player.health > 0 ) + { + sprint(player, "\nYou need to be next to a control point\n"); + return true; + } + + + entity closest_target = ons_Nearest_ControlPoint_2D(player, pos, autocvar_g_onslaught_click_radius); + + if ( closest_target == NULL ) + { + sprint(player, "\nNo control point found\n"); + return true; + } + + if ( player.health <= 0 ) + { + player.ons_spawn_by = closest_target; + player.respawn_flags = player.respawn_flags | RESPAWN_FORCE; + } + else + { + if ( source_point == closest_target ) + { + sprint(player, "\nTeleporting to the same point\n"); + return true; + } + + if ( !ons_Teleport(player,closest_target,autocvar_g_onslaught_teleport_radius,true) ) + sprint(player, "\nUnable to teleport there\n"); + } + + return true; + } + + sprint(player, "\nNo teleportation for you\n"); + } + + return true; + } + return false; +} + +MUTATOR_HOOKFUNCTION(ons, PlayerUseKey) +{ + if(MUTATOR_RETURNVALUE || gameover) { return false; } + + entity player = M_ARGV(0, entity); + + if((time > player.teleport_antispam) && (!IS_DEAD(player)) && !player.vehicle) + { + entity source_point = ons_Nearest_ControlPoint(player, player.origin, autocvar_g_onslaught_teleport_radius); + if ( source_point ) + { + stuffcmd(player, "qc_cmd_cl hud clickradar\n"); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(ons, PlayHitsound) +{ + entity frag_victim = M_ARGV(0, entity); + + return (frag_victim.classname == "onslaught_generator" && !frag_victim.isshielded) + || (frag_victim.classname == "onslaught_controlpoint_icon" && !frag_victim.owner.isshielded); +} + +MUTATOR_HOOKFUNCTION(ons, SendWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity to = M_ARGV(1, entity); + int sf = M_ARGV(2, int); + int wp_flag = M_ARGV(3, int); + + if(sf & 16) + { + if(wp.owner.classname == "onslaught_controlpoint") + { + 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(!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; } + } + } + + M_ARGV(3, int) = wp_flag; +} + +MUTATOR_HOOKFUNCTION(ons, TurretValidateTarget) +{ + entity turret_target = M_ARGV(1, entity); + + if(substring(turret_target.classname, 0, 10) == "onslaught_") // don't attack onslaught targets, that's the player's job! + { + M_ARGV(3, float) = -3; + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(ons, TurretThink) +{ + entity turret = M_ARGV(0, entity); + + // ONS uses somewhat backwards linking. + if(turret.target) + { + entity e = find(NULL, targetname, turret.target); + if (e != NULL) + turret.team = e.team; + } + + if(turret.team != turret.tur_head.team) + turret_respawn(turret); +} + + +// ========== +// Spawnfuncs +// ========== + +/*QUAKED spawnfunc_onslaught_link (0 .5 .8) (-16 -16 -16) (16 16 16) + Link between control points. + + This entity targets two different spawnfunc_onslaught_controlpoint or spawnfunc_onslaught_generator entities, and suppresses shielding on both if they are owned by different teams. + +keys: +"target" - first control point. +"target2" - second control point. + */ +spawnfunc(onslaught_link) +{ + if(!g_onslaught) { delete(this); return; } + + if (this.target == "" || this.target2 == "") + objerror(this, "target and target2 must be set\n"); + + this.ons_worldlinknext = ons_worldlinklist; // link into ons_worldlinklist + ons_worldlinklist = this; + + InitializeEntity(this, ons_DelayedLinkSetup, INITPRIO_FINDTARGET); + Net_LinkEntity(this, false, 0, ons_Link_Send); +} + +/*QUAKED spawnfunc_onslaught_controlpoint (0 .5 .8) (-32 -32 0) (32 32 128) + Control point. Be sure to give this enough clearance so that the shootable part has room to exist + + This should link to an spawnfunc_onslaught_controlpoint entity or spawnfunc_onslaught_generator entity. + +keys: +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. +"target" - target any entities that are tied to this control point, such as vehicles and buildable structure entities. +"message" - name of this control point (should reflect the location in the map, such as "center bridge", "north tower", etc) + */ + +spawnfunc(onslaught_controlpoint) +{ + if(!g_onslaught) { delete(this); return; } + + ons_ControlPoint_Setup(this); +} + +/*QUAKED spawnfunc_onslaught_generator (0 .5 .8) (-32 -32 -24) (32 32 64) + Base generator. + + spawnfunc_onslaught_link entities can target this. + +keys: +"team" - team that owns this generator (5 = red, 14 = blue, etc), MUST BE SET. +"targetname" - name that spawnfunc_onslaught_link entities will use to target this. + */ +spawnfunc(onslaught_generator) +{ + if(!g_onslaught) { delete(this); return; } + if(!this.team) { objerror(this, "team must be set"); } + + ons_GeneratorSetup(this); +} + +// 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); + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore (ST_ONS_CAPS, "destroyed", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_ONS_TAKES, "takes", 0); + ScoreRules_basics_end(); +} + +void ons_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up +{ + ons_ScoreRules(); + + round_handler_Spawn(Onslaught_CheckPlayers, Onslaught_CheckWinner, Onslaught_RoundStart); + round_handler_Init(5, autocvar_g_onslaught_warmup, autocvar_g_onslaught_round_timelimit); +} + +void ons_Initialize() +{ + g_onslaught = true; + ons_captureshield_force = autocvar_g_onslaught_shield_force; + + cam = new(objective_camera); + + InitializeEntity(NULL, ons_DelayedInit, INITPRIO_GAMETYPE); +} diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh new file mode 100644 index 0000000000..d8b7fd044b --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qh @@ -0,0 +1,114 @@ +#pragma once + +float autocvar_g_onslaught_point_limit; +void ons_Initialize(); + +REGISTER_MUTATOR(ons, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ons_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_onslaught_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ons_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return false; +} + +.entity ons_toucher; // player who touched the control point + +// control point / generator constants +const float ONS_CP_THINKRATE = 0.2; +const float GEN_THINKRATE = 1; +#define CPGEN_SPAWN_OFFSET ('0 0 1' * (PL_MAX_CONST.z - 13)) +const vector CPGEN_WAYPOINT_OFFSET = ('0 0 128'); +const vector CPICON_OFFSET = ('0 0 96'); + +// list of generators on the map +entity ons_worldgeneratorlist; +.entity ons_worldgeneratornext; + +// list of control points on the map +entity ons_worldcplist; +.entity ons_worldcpnext; + +// list of links on the map +entity ons_worldlinklist; +.entity ons_worldlinknext; + +// definitions +.entity sprite; +.string target2; +.int iscaptured; +.int islinked; +.int isshielded; +.float lasthealth; +.int lastteam; +.int lastshielded; +.int lastcaptured; + +.bool waslinked; + +bool ons_stalemate; + +.float teleport_antispam; + +// waypoint sprites +.entity bot_basewaypoint; // generator waypointsprite + +.bool isgenneighbor[17]; +.bool iscpneighbor[17]; +float ons_notification_time[17]; + +.float ons_overtime_damagedelay; + +.vector ons_deathloc; + +.entity ons_spawn_by; + +// declarations for functions used outside gamemode_onslaught.qc +void ons_Generator_UpdateSprite(entity e); +void ons_ControlPoint_UpdateSprite(entity e); +bool ons_ControlPoint_Attackable(entity cp, int teamnumber); + +// CaptureShield: Prevent capturing or destroying control point/generator if it is not available yet +float ons_captureshield_force; // push force of the shield + +// bot player logic +const int HAVOCBOT_ONS_ROLE_NONE = 0; +const int HAVOCBOT_ONS_ROLE_DEFENSE = 2; +const int HAVOCBOT_ONS_ROLE_ASSISTANT = 4; +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); +void havocbot_role_ons_offense(entity this); +void havocbot_role_ons_assistant(entity this); + +void havocbot_ons_reset_role(entity this); +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius); +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius); + +// score rule declarations +const int ST_ONS_CAPS = 1; diff --git a/qcsrc/common/impulses/all.qh b/qcsrc/common/impulses/all.qh index 6780ac16d0..45a8f1323b 100644 --- a/qcsrc/common/impulses/all.qh +++ b/qcsrc/common/impulses/all.qh @@ -1,5 +1,4 @@ -#ifndef IMPULSES_ALL_H -#define IMPULSES_ALL_H +#pragma once REGISTRY(IMPULSES, 255) REGISTER_REGISTRY(IMPULSES) @@ -216,5 +215,3 @@ CHIMPULSE(SPEEDRUN, 141) CHIMPULSE(CLONE_STANDING, 142) CHIMPULSE(TELEPORT, 143) CHIMPULSE(R00T, 148) - -#endif diff --git a/qcsrc/common/items/_mod.inc b/qcsrc/common/items/_mod.inc index 3b5dd9550f..6214fac042 100644 --- a/qcsrc/common/items/_mod.inc +++ b/qcsrc/common/items/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify #include + +#include diff --git a/qcsrc/common/items/_mod.qh b/qcsrc/common/items/_mod.qh index 158814c557..a04e90b1dc 100644 --- a/qcsrc/common/items/_mod.qh +++ b/qcsrc/common/items/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify #include + +#include diff --git a/qcsrc/common/items/all.qc b/qcsrc/common/items/all.qc index 7a3e500469..0a26a75b5a 100644 --- a/qcsrc/common/items/all.qc +++ b/qcsrc/common/items/all.qc @@ -1,9 +1,5 @@ -#ifndef ITEMS_ALL_C -#define ITEMS_ALL_C #include "all.qh" -#include "item/_mod.inc" - void Dump_Items() { FOREACH(Items, true, ITEM_HANDLE(Show, it)); @@ -19,4 +15,13 @@ string Item_Model(string item_mdl) return output; } +string Item_Sound(string it_snd) +{ + string output = strcat("misc/", it_snd); +#ifdef SVQC + MUTATOR_CALLHOOK(ItemSound, it_snd, output); + return M_ARGV(1, string); +#else + return output; #endif +} diff --git a/qcsrc/common/items/all.qh b/qcsrc/common/items/all.qh index 18cc5ae4f3..dc8cf21c02 100644 --- a/qcsrc/common/items/all.qh +++ b/qcsrc/common/items/all.qh @@ -1,11 +1,10 @@ -#ifndef ITEMS_ALL_H -#define ITEMS_ALL_H +#pragma once -#include +#include #include "item.qh" -REGISTRY(Items, BITS(5)) +REGISTRY(Items, BITS(7)) #define Items_from(i) _Items_from(i, NULL) REGISTER_REGISTRY(Items) #define REGISTER_ITEM(id, class) REGISTER(Items, ITEM, id, m_id, NEW(class)) @@ -30,8 +29,6 @@ GENERIC_COMMAND(dumpitems, "Dump all items to the console") { } } -#ifndef MENUQC +#ifdef GAMEQC string Item_Model(string item_mdl); #endif - -#endif diff --git a/qcsrc/common/items/inventory.qh b/qcsrc/common/items/inventory.qh index 934271059b..a022979a70 100644 --- a/qcsrc/common/items/inventory.qh +++ b/qcsrc/common/items/inventory.qh @@ -1,14 +1,13 @@ -#ifndef INVENTORY_H -#define INVENTORY_H +#pragma once #include "all.qh" #include "item/pickup.qh" CLASS(Inventory, Object) /** Stores counts of items, the id being the index */ - ATTRIBARRAY(Inventory, inv_items, int, Items_MAX) + ATTRIBARRAY(Inventory, inv_items, int, Items_MAX); /** Previous state */ - ATTRIB(Inventory, inventory, Inventory, NULL) + ATTRIB(Inventory, inventory, Inventory); ENDCLASS(Inventory) /** Player inventory */ @@ -16,17 +15,33 @@ ENDCLASS(Inventory) REGISTER_NET_LINKED(ENT_CLIENT_INVENTORY) +const int Inventory_groups_major = 16; +const int Inventory_groups_minor = 8; // ceil(Items_MAX / Inventory_groups_major) + +#define G_MAJOR(id) (floor((id) / Inventory_groups_minor)) +#define G_MINOR(id) ((id) % Inventory_groups_minor) + #ifdef CSQC NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew) { make_pure(this); - const int bits = ReadInt24_t(); - FOREACH(Items, bits & BIT(it.m_id), { - .int fld = inv_items[it.m_id]; - int prev = this.(fld); - int next = this.(fld) = ReadByte(); - LOG_TRACEF("%s: %.0f -> %.0f\n", it.m_name, prev, next); - }); + const int majorBits = ReadShort(); + for (int i = 0; i < Inventory_groups_major; ++i) { + if (!(majorBits & BIT(i))) { + continue; + } + const int minorBits = ReadByte(); + for (int j = 0; j < Inventory_groups_minor; ++j) { + if (!(minorBits & BIT(j))) { + continue; + } + const GameItem it = Items_from(Inventory_groups_minor * i + j); + .int fld = inv_items[it.m_id]; + int prev = this.(fld); + int next = this.(fld) = ReadByte(); + LOG_TRACEF("%s: %.0f -> %.0f", it.m_name, prev, next); + } + } return true; } #endif @@ -35,22 +50,56 @@ NET_HANDLE(ENT_CLIENT_INVENTORY, bool isnew) void Inventory_Write(Inventory data) { if (!data) { - WriteInt24_t(MSG_ENTITY, 0); + WriteShort(MSG_ENTITY, 0); return; } TC(Inventory, data); - int bits = 0; + + int majorBits = 0; FOREACH(Items, true, { .int fld = inv_items[it.m_id]; - bits = BITSET(bits, BIT(it.m_id), data.inventory.(fld) != (data.inventory.(fld) = data.(fld))); + const bool changed = data.inventory.(fld) != data.(fld); + if (changed) { + majorBits = BITSET(majorBits, BIT(G_MAJOR(it.m_id)), true); + } }); - WriteInt24_t(MSG_ENTITY, bits); - FOREACH(Items, bits & BIT(it.m_id), { - WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); + WriteShort(MSG_ENTITY, majorBits); + + int minorBits = 0; + int lastMaj = 0; + int maj = 0; + FOREACH(Items, majorBits & BIT(maj = G_MAJOR(it.m_id)), { + .int fld = inv_items[it.m_id]; + const bool changed = data.inventory.(fld) != (data.inventory.(fld) = data.(fld)); + if (changed) { + if (maj != lastMaj) { + lastMaj = maj; +#define X() MACRO_BEGIN \ + if (minorBits) { \ + WriteByte(MSG_ENTITY, minorBits); \ + for (int j = 0; j < Inventory_groups_minor; ++j) { \ + if (!(minorBits & BIT(j))) { \ + continue; \ + } \ + const GameItem it = Items_from(Inventory_groups_minor * maj + j); \ + WriteByte(MSG_ENTITY, data.inv_items[it.m_id]); \ + } \ + } \ +MACRO_END + X(); + minorBits = 0; + } + minorBits = BITSET(minorBits, BIT(G_MINOR(it.m_id)), true); + } }); + X(); +#undef X } #endif +#undef G_MAJOR +#undef G_MINOR + #ifdef SVQC bool Inventory_Send(Inventory this, Client to, int sf) { @@ -71,8 +120,6 @@ void Inventory_new(entity e) inv.drawonlytoclient = e; Net_LinkEntity((inv.owner = e).inventory = inv, false, 0, Inventory_Send); } -void Inventory_delete(entity e) { remove(e.inventory.inventory); remove(e.inventory); } +void Inventory_delete(entity e) { delete(e.inventory.inventory); delete(e.inventory); } void Inventory_update(entity e) { e.inventory.SendFlags = 0xFFFFFF; } #endif - -#endif diff --git a/qcsrc/common/items/item.qh b/qcsrc/common/items/item.qh index 4ab7af4f23..f3aa0cec26 100644 --- a/qcsrc/common/items/item.qh +++ b/qcsrc/common/items/item.qh @@ -1,5 +1,5 @@ -#ifndef GAMEITEM_H -#define GAMEITEM_H +#pragma once +#include 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. @@ -34,18 +34,21 @@ const int IT_SUPERWEAPON = BIT(21); // suit const int IT_STRENGTH = BIT(22); // item masks -const int IT_AMMO = IT_FUEL | IT_SHELLS | IT_NAILS | IT_ROCKETS | IT_CELLS | IT_PLASMA; const int IT_UNLIMITED_AMMO = IT_UNLIMITED_WEAPON_AMMO | IT_UNLIMITED_SUPERWEAPONS; const int IT_PICKUPMASK = IT_UNLIMITED_AMMO | IT_JETPACK | IT_FUEL_REGEN; // strength and invincible are handled separately #define ITEM_HANDLE(signal, ...) __Item_Send_##signal(__VA_ARGS__) CLASS(GameItem, Object) - ATTRIB(GameItem, m_id, int, 0) - ATTRIB(GameItem, m_name, string, string_null) - ATTRIB(GameItem, m_icon, string, string_null) - ATTRIB(GameItem, m_color, vector, '1 1 1') - ATTRIB(GameItem, m_waypoint, string, string_null) - ATTRIB(GameItem, m_waypointblink, int, 1) + ATTRIB(GameItem, m_id, int, 0); + ATTRIB(GameItem, m_name, string); + ATTRIB(GameItem, m_icon, string); + ATTRIB(GameItem, m_color, vector, '1 1 1'); + ATTRIB(GameItem, m_waypoint, string); + ATTRIB(GameItem, m_waypointblink, int, 1); +#ifdef GAMEQC + ATTRIB(GameItem, m_glow, bool, false); + ATTRIB(GameItem, m_respawnsound, Sound, SND_ITEMRESPAWN); +#endif METHOD(GameItem, display, void(GameItem this, void(string name, string icon) returns)) { TC(GameItem, this); @@ -58,5 +61,3 @@ CLASS(GameItem, Object) } void ITEM_HANDLE(Show, GameItem this) { this.show(this); } ENDCLASS(GameItem) - -#endif diff --git a/qcsrc/common/items/item/ammo.qc b/qcsrc/common/items/item/ammo.qc index 525a5b3d93..d7e0dcc687 100644 --- a/qcsrc/common/items/item/ammo.qc +++ b/qcsrc/common/items/item/ammo.qc @@ -1,84 +1 @@ #include "ammo.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); -#endif - -REGISTER_ITEM(Bullets, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Bullets_ITEM; -#endif - this.m_name = "bullets"; - this.m_icon = "ammo_bullets"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_NAILS; -#endif -} - -#ifndef MENUQC -MODEL(Cells_ITEM, Item_Model("a_cells.md3")); -#endif - -REGISTER_ITEM(Cells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Cells_ITEM; -#endif - this.m_name = "cells"; - this.m_icon = "ammo_cells"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_CELLS; -#endif -} - -#ifndef MENUQC -MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); -#endif - -REGISTER_ITEM(Plasma, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Plasma_ITEM; -#endif - this.m_name = "plasma"; - this.m_icon = "ammo_plasma"; -#ifdef SVQC - this.m_botvalue = 2000; - this.m_itemid = IT_PLASMA; -#endif -} - -#ifndef MENUQC -MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); -#endif - -REGISTER_ITEM(Rockets, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Rockets_ITEM; -#endif - this.m_name = "rockets"; - this.m_icon = "ammo_rockets"; -#ifdef SVQC - this.m_botvalue = 3000; - this.m_itemid = IT_ROCKETS; -#endif -} - -#ifndef MENUQC -MODEL(Shells_ITEM, Item_Model("a_shells.md3")); -#endif - -REGISTER_ITEM(Shells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_Shells_ITEM; -#endif - this.m_name = "shells"; - this.m_icon = "ammo_shells"; -#ifdef SVQC - this.m_botvalue = 500; - this.m_itemid = IT_SHELLS; -#endif -} diff --git a/qcsrc/common/items/item/ammo.qh b/qcsrc/common/items/item/ammo.qh index 84f20483e4..e1d493fe97 100644 --- a/qcsrc/common/items/item/ammo.qh +++ b/qcsrc/common/items/item/ammo.qh @@ -1,11 +1,94 @@ -#ifndef AMMO_H -#define AMMO_H +#pragma once + #include "pickup.qh" CLASS(Ammo, Pickup) #ifdef SVQC - ATTRIB(Ammo, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc) - ATTRIB(Ammo, m_respawntime, float(), GET(g_pickup_respawntime_ammo)) - ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo)) + ATTRIB(Ammo, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc); + ATTRIB(Ammo, m_respawntime, float(), GET(g_pickup_respawntime_ammo)); + ATTRIB(Ammo, m_respawntimejitter, float(), GET(g_pickup_respawntimejitter_ammo)); #endif ENDCLASS(Ammo) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(Bullets_ITEM, Item_Model("a_bullets.mdl")); +#endif + +REGISTER_ITEM(Bullets, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Bullets_ITEM; +#endif + this.m_name = "bullets"; + this.m_icon = "ammo_bullets"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_NAILS; +#endif +} + +#ifdef GAMEQC +MODEL(Cells_ITEM, Item_Model("a_cells.md3")); +#endif + +REGISTER_ITEM(Cells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Cells_ITEM; +#endif + this.m_name = "cells"; + this.m_icon = "ammo_cells"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_CELLS; +#endif +} + +#ifdef GAMEQC +MODEL(Plasma_ITEM, Item_Model("a_cells.md3")); +#endif + +REGISTER_ITEM(Plasma, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Plasma_ITEM; +#endif + this.m_name = "plasma"; + this.m_icon = "ammo_plasma"; +#ifdef SVQC + this.m_botvalue = 2000; + this.m_itemid = IT_PLASMA; +#endif +} + +#ifdef GAMEQC +MODEL(Rockets_ITEM, Item_Model("a_rockets.md3")); +#endif + +REGISTER_ITEM(Rockets, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Rockets_ITEM; +#endif + this.m_name = "rockets"; + this.m_icon = "ammo_rockets"; +#ifdef SVQC + this.m_botvalue = 3000; + this.m_itemid = IT_ROCKETS; +#endif +} + +#ifdef GAMEQC +MODEL(Shells_ITEM, Item_Model("a_shells.md3")); +#endif + +REGISTER_ITEM(Shells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_Shells_ITEM; +#endif + this.m_name = "shells"; + this.m_icon = "ammo_shells"; +#ifdef SVQC + this.m_botvalue = 500; + this.m_itemid = IT_SHELLS; #endif +} diff --git a/qcsrc/common/items/item/armor.qc b/qcsrc/common/items/item/armor.qc index f8669e0e08..cca0b54184 100644 --- a/qcsrc/common/items/item/armor.qc +++ b/qcsrc/common/items/item/armor.qc @@ -1,89 +1 @@ #include "armor.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3")); -SOUND(ArmorSmall, "misc/armor1"); -#endif - -REGISTER_ITEM(ArmorSmall, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorSmall_ITEM; - this.m_sound = SND_ArmorSmall; -#endif - this.m_name = "5 Armor"; - this.m_icon = "armor"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_ARMOR_SHARD; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3")); -SOUND(ArmorMedium, "misc/armor10"); -#endif - -REGISTER_ITEM(ArmorMedium, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorMedium_ITEM; - this.m_sound = SND_ArmorMedium; -#endif - this.m_name = "25 Armor"; - this.m_icon = "armor"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_medium); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); -#endif -} - -#ifndef MENUQC -MODEL(ArmorLarge_ITEM, Item_Model("item_armor_big.md3")); -SOUND(ArmorLarge, "misc/armor17_5"); -#endif - -REGISTER_ITEM(ArmorLarge, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorLarge_ITEM; - this.m_sound = SND_ArmorLarge; -#endif - this.m_name = "50 Armor"; - this.m_icon = "armor"; - this.m_color = '0 1 0'; - this.m_waypoint = _("Large armor"); -#ifdef SVQC - this.m_botvalue = 20000; // FIXME: higher than BOT_PICKUP_RATING_HIGH? - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} - -#ifndef MENUQC -MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3")); -SOUND(ArmorMega, "misc/armor25"); -#endif - -REGISTER_ITEM(ArmorMega, Armor) { -#ifndef MENUQC - this.m_model = MDL_ArmorMega_ITEM; - this.m_sound = SND_ArmorMega; -#endif - this.m_name = "100 Armor"; - this.m_icon = "item_large_armor"; - this.m_color = '0 1 0'; - this.m_waypoint = _("Mega armor"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_HIGH; - this.m_itemid = IT_ARMOR; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} diff --git a/qcsrc/common/items/item/armor.qh b/qcsrc/common/items/item/armor.qh index adb93e7c94..4b6b9fdf6c 100644 --- a/qcsrc/common/items/item/armor.qh +++ b/qcsrc/common/items/item/armor.qh @@ -1,11 +1,100 @@ -#ifndef ARMOR_H -#define ARMOR_H +#pragma once + #include "pickup.qh" CLASS(Armor, Pickup) #ifdef SVQC - ATTRIB(Armor, m_mins, vector, '-16 -16 0') - ATTRIB(Armor, m_maxs, vector, '16 16 48') - ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc) + ATTRIB(Armor, m_mins, vector, '-16 -16 0'); + ATTRIB(Armor, m_maxs, vector, '16 16 48'); + ATTRIB(Armor, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc); #endif ENDCLASS(Armor) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(ArmorSmall_ITEM, Item_Model("item_armor_small.md3")); +SOUND(ArmorSmall, Item_Sound("armor1")); +#endif + +REGISTER_ITEM(ArmorSmall, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorSmall_ITEM; + this.m_sound = SND_ArmorSmall; +#endif + this.m_name = "5 Armor"; + this.m_icon = "armor"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_ARMOR_SHARD; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorMedium_ITEM, Item_Model("item_armor_medium.md3")); +SOUND(ArmorMedium, Item_Sound("armor10")); +#endif + +REGISTER_ITEM(ArmorMedium, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorMedium_ITEM; + this.m_sound = SND_ArmorMedium; +#endif + this.m_name = "25 Armor"; + this.m_icon = "armor"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_medium); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorBig_ITEM, Item_Model("item_armor_big.md3")); +SOUND(ArmorBig, Item_Sound("armor17_5")); +#endif + +REGISTER_ITEM(ArmorBig, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorBig_ITEM; + this.m_sound = SND_ArmorBig; +#endif + this.m_name = "50 Armor"; + this.m_icon = "armor"; + this.m_color = '0 1 0'; + this.m_waypoint = _("Big armor"); +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_HIGH; + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); +#endif +} + +#ifdef GAMEQC +MODEL(ArmorMega_ITEM, Item_Model("item_armor_large.md3")); +SOUND(ArmorMega, Item_Sound("armor25")); +#endif + +REGISTER_ITEM(ArmorMega, Armor) { +#ifdef GAMEQC + this.m_model = MDL_ArmorMega_ITEM; + this.m_sound = SND_ArmorMega; +#endif + this.m_name = "100 Armor"; + this.m_icon = "item_large_armor"; + this.m_color = '0 1 0'; + this.m_waypoint = _("Mega armor"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_maxs = '16 16 70'; + this.m_botvalue = BOT_PICKUP_RATING_HIGH; + this.m_itemid = IT_ARMOR; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); #endif +} diff --git a/qcsrc/common/items/item/health.qc b/qcsrc/common/items/item/health.qc index 93ba2f36e2..49a34c1c8e 100644 --- a/qcsrc/common/items/item/health.qc +++ b/qcsrc/common/items/item/health.qc @@ -1,89 +1 @@ #include "health.qh" -#ifdef SVQC - #include -#endif - -#ifndef MENUQC -MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3")); -SOUND(HealthSmall, "misc/minihealth"); -#endif - -REGISTER_ITEM(HealthSmall, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthSmall_ITEM; - this.m_sound = SND_HealthSmall; -#endif - this.m_name = "5 Health"; - this.m_icon = "health"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_5HP; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3")); -SOUND(HealthMedium, "misc/mediumhealth"); -#endif - -REGISTER_ITEM(HealthMedium, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthMedium_ITEM; - this.m_sound = SND_HealthMedium; -#endif - this.m_name = "25 Health"; - this.m_icon = "health"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_25HP; - this.m_respawntime = GET(g_pickup_respawntime_short); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); -#endif -} - -#ifndef MENUQC -MODEL(HealthLarge_ITEM, Item_Model("g_h50.md3")); -SOUND(HealthLarge, "misc/mediumhealth"); -#endif - -REGISTER_ITEM(HealthLarge, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthLarge_ITEM; - this.m_sound = SND_HealthLarge; -#endif - this.m_name = "50 Health"; - this.m_icon = "health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Large health"); -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_MID; - this.m_itemid = IT_25HP; - this.m_respawntime = GET(g_pickup_respawntime_medium); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); -#endif -} - -#ifndef MENUQC -MODEL(HealthMega_ITEM, Item_Model("g_h100.md3")); -SOUND(HealthMega, "misc/megahealth"); -#endif - -REGISTER_ITEM(HealthMega, Health) { -#ifndef MENUQC - this.m_model = MDL_HealthMega_ITEM; - this.m_sound = SND_HealthMega; -#endif - this.m_name = "100 Health"; - this.m_icon = "item_mega_health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Mega health"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_HIGH; - this.m_itemid = IT_HEALTH; - this.m_respawntime = GET(g_pickup_respawntime_long); - this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); -#endif -} diff --git a/qcsrc/common/items/item/health.qh b/qcsrc/common/items/item/health.qh index f7bfb5c938..f7915987e3 100644 --- a/qcsrc/common/items/item/health.qh +++ b/qcsrc/common/items/item/health.qh @@ -1,11 +1,100 @@ -#ifndef HEALTH_H -#define HEALTH_H +#pragma once + #include "pickup.qh" CLASS(Health, Pickup) #ifdef SVQC - ATTRIB(Health, m_mins, vector, '-16 -16 0') - ATTRIB(Health, m_maxs, vector, '16 16 48') - ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc) + ATTRIB(Health, m_mins, vector, '-16 -16 0'); + ATTRIB(Health, m_maxs, vector, '16 16 48'); + ATTRIB(Health, m_pickupevalfunc, float(entity player, entity item), commodity_pickupevalfunc); #endif ENDCLASS(Health) + +#ifdef SVQC + #include +#endif + +#ifdef GAMEQC +MODEL(HealthSmall_ITEM, Item_Model("g_h1.md3")); +SOUND(HealthSmall, Item_Sound("minihealth")); +#endif + +REGISTER_ITEM(HealthSmall, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthSmall_ITEM; + this.m_sound = SND_HealthSmall; +#endif + this.m_name = "5 Health"; + this.m_icon = "health"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_5HP; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(HealthMedium_ITEM, Item_Model("g_h25.md3")); +SOUND(HealthMedium, Item_Sound("mediumhealth")); +#endif + +REGISTER_ITEM(HealthMedium, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthMedium_ITEM; + this.m_sound = SND_HealthMedium; +#endif + this.m_name = "25 Health"; + this.m_icon = "health"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_25HP; + this.m_respawntime = GET(g_pickup_respawntime_short); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_short); +#endif +} + +#ifdef GAMEQC +MODEL(HealthBig_ITEM, Item_Model("g_h50.md3")); +SOUND(HealthBig, Item_Sound("mediumhealth")); +#endif + +REGISTER_ITEM(HealthBig, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthBig_ITEM; + this.m_sound = SND_HealthBig; +#endif + this.m_name = "50 Health"; + this.m_icon = "health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Big health"); +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_MID; + this.m_itemid = IT_25HP; + this.m_respawntime = GET(g_pickup_respawntime_medium); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_medium); +#endif +} + +#ifdef GAMEQC +MODEL(HealthMega_ITEM, Item_Model("g_h100.md3")); +SOUND(HealthMega, Item_Sound("megahealth")); +#endif + +REGISTER_ITEM(HealthMega, Health) { +#ifdef GAMEQC + this.m_model = MDL_HealthMega_ITEM; + this.m_sound = SND_HealthMega; +#endif + this.m_name = "100 Health"; + this.m_icon = "item_mega_health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Mega health"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_maxs = '16 16 70'; + this.m_botvalue = BOT_PICKUP_RATING_HIGH; + this.m_itemid = IT_HEALTH; + this.m_respawntime = GET(g_pickup_respawntime_long); + this.m_respawntimejitter = GET(g_pickup_respawntimejitter_long); #endif +} diff --git a/qcsrc/common/items/item/jetpack.qc b/qcsrc/common/items/item/jetpack.qc index 7fd29e299f..ec09d5c45d 100644 --- a/qcsrc/common/items/item/jetpack.qc +++ b/qcsrc/common/items/item/jetpack.qc @@ -1,66 +1 @@ -#ifdef SVQC - #include -#endif - -#include "ammo.qh" -#include "powerup.qh" - -#ifndef SVQC -.int m_itemid; -#endif - -#ifndef MENUQC -MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); -#endif - -REGISTER_ITEM(Jetpack, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Jetpack_ITEM; - this.m_itemid = IT_JETPACK; -#endif - this.m_name = "Jet pack"; - this.m_icon = "jetpack"; - this.m_color = '0.5 0.5 0.5'; - this.m_waypoint = _("Jet Pack"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_pickupevalfunc = commodity_pickupevalfunc; -#endif -} - -#ifndef MENUQC -MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); -#endif - -REGISTER_ITEM(JetpackFuel, Ammo) { -#ifndef MENUQC - this.m_model = MDL_JetpackFuel_ITEM; -#endif - this.m_name = "Fuel"; - this.m_icon = "ammo_fuel"; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_FUEL; -#endif -} - -#ifndef MENUQC -MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3")); -#endif - -REGISTER_ITEM(JetpackRegen, Powerup) { -#ifndef MENUQC - this.m_model = MDL_JetpackRegen_ITEM; -#endif - this.m_name = "Fuel regenerator"; - this.m_icon = "fuelregen"; - this.m_color = '1 0.5 0'; - this.m_waypoint = _("Fuel regen"); - this.m_waypointblink = 2; -#ifdef SVQC - this.m_botvalue = BOT_PICKUP_RATING_LOW; - this.m_itemid = IT_FUEL_REGEN; - this.m_pickupevalfunc = commodity_pickupevalfunc; -#endif -} +#include "jetpack.qh" diff --git a/qcsrc/common/items/item/jetpack.qh b/qcsrc/common/items/item/jetpack.qh new file mode 100644 index 0000000000..8334c791ab --- /dev/null +++ b/qcsrc/common/items/item/jetpack.qh @@ -0,0 +1,68 @@ +#pragma once + +#ifdef SVQC + #include +#endif + +#include "ammo.qh" +#include "powerup.qh" + +#ifndef SVQC +.int m_itemid; +#endif + +#ifdef GAMEQC +MODEL(Jetpack_ITEM, Item_Model("g_jetpack.md3")); +#endif + +REGISTER_ITEM(Jetpack, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Jetpack_ITEM; + this.m_itemid = IT_JETPACK; +#endif + this.m_name = "Jet pack"; + this.m_icon = "jetpack"; + this.m_color = '0.5 0.5 0.5'; + this.m_waypoint = _("Jet Pack"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_pickupevalfunc = commodity_pickupevalfunc; +#endif +} + +#ifdef GAMEQC +MODEL(JetpackFuel_ITEM, Item_Model("g_fuel.md3")); +#endif + +REGISTER_ITEM(JetpackFuel, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_JetpackFuel_ITEM; +#endif + this.m_name = "Fuel"; + this.m_icon = "ammo_fuel"; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_FUEL; +#endif +} + +#ifdef GAMEQC +MODEL(JetpackRegen_ITEM, Item_Model("g_fuelregen.md3")); +#endif + +REGISTER_ITEM(JetpackRegen, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_JetpackRegen_ITEM; +#endif + this.m_name = "Fuel regenerator"; + this.m_icon = "fuelregen"; + this.m_color = '1 0.5 0'; + this.m_waypoint = _("Fuel regen"); + this.m_waypointblink = 2; +#ifdef SVQC + this.m_botvalue = BOT_PICKUP_RATING_LOW; + this.m_itemid = IT_FUEL_REGEN; + this.m_pickupevalfunc = commodity_pickupevalfunc; +#endif +} diff --git a/qcsrc/common/items/item/pickup.qh b/qcsrc/common/items/item/pickup.qh index 08f7ff9c3d..204a49921d 100644 --- a/qcsrc/common/items/item/pickup.qh +++ b/qcsrc/common/items/item/pickup.qh @@ -1,35 +1,59 @@ -#ifndef PICKUP_H -#define PICKUP_H +#pragma once + +#ifdef SVQC +PROPERTY(float, g_pickup_respawntime_weapon) +PROPERTY(float, g_pickup_respawntime_superweapon) +PROPERTY(float, g_pickup_respawntime_ammo) +PROPERTY(float, g_pickup_respawntime_short) +PROPERTY(float, g_pickup_respawntime_medium) +PROPERTY(float, g_pickup_respawntime_long) +PROPERTY(float, g_pickup_respawntime_powerup) +PROPERTY(float, g_pickup_respawntimejitter_weapon) +PROPERTY(float, g_pickup_respawntimejitter_superweapon) +PROPERTY(float, g_pickup_respawntimejitter_ammo) +PROPERTY(float, g_pickup_respawntimejitter_short) +PROPERTY(float, g_pickup_respawntimejitter_medium) +PROPERTY(float, g_pickup_respawntimejitter_long) +PROPERTY(float, g_pickup_respawntimejitter_powerup) +#endif + +// pickup ratings for bot logic +const int BOT_PICKUP_RATING_LOW = 2500; +const int BOT_PICKUP_RATING_MID = 5000; +const int BOT_PICKUP_RATING_HIGH = 10000; + #include #include +#include + CLASS(Pickup, GameItem) -#ifndef MENUQC - ATTRIB(Pickup, m_model, Model, NULL) - ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP) +#ifdef GAMEQC + ATTRIB(Pickup, m_model, Model); + ATTRIB(Pickup, m_sound, Sound, SND_ITEMPICKUP); #endif - ATTRIB(Pickup, m_name, string, string_null) + ATTRIB(Pickup, m_name, string); METHOD(Pickup, show, void(Pickup this)) { TC(Pickup, this); LOG_INFOF("%s: %s\n", etos(this), this.m_name); } + ATTRIB(Pickup, m_itemid, int, 0); #ifdef SVQC - ATTRIB(Pickup, m_mins, vector, '-16 -16 0') - ATTRIB(Pickup, m_maxs, vector, '16 16 32') - ATTRIB(Pickup, m_botvalue, int, 0) - ATTRIB(Pickup, m_itemflags, int, 0) - ATTRIB(Pickup, m_itemid, int, 0) + ATTRIB(Pickup, m_mins, vector, '-16 -16 0'); + ATTRIB(Pickup, m_maxs, vector, '16 16 32'); + ATTRIB(Pickup, m_botvalue, int, 0); + ATTRIB(Pickup, m_itemflags, int, 0); float generic_pickupevalfunc(entity player, entity item); - ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc) - ATTRIB(Pickup, m_respawntime, float(), func_null) - ATTRIB(Pickup, m_respawntimejitter, float(), func_null) + ATTRIB(Pickup, m_pickupevalfunc, float(entity player, entity item), generic_pickupevalfunc); + ATTRIB(Pickup, m_respawntime, float()); + ATTRIB(Pickup, m_respawntimejitter, float()); float Item_GiveTo(entity item, entity player); METHOD(Pickup, giveTo, bool(Pickup this, entity item, entity player)) { TC(Pickup, this); bool b = Item_GiveTo(item, player); if (b) { - LOG_TRACEF("entity %i picked up %s\n", player, this.m_name); + LOG_DEBUGF("entity %i picked up %s", player, this.m_name); player.inventory.inv_items[this.m_id]++; Inventory_update(player); } @@ -38,5 +62,3 @@ CLASS(Pickup, GameItem) bool ITEM_HANDLE(Pickup, Pickup this, entity item, entity player); #endif ENDCLASS(Pickup) - -#endif diff --git a/qcsrc/common/items/item/powerup.qc b/qcsrc/common/items/item/powerup.qc index 375f958a16..7c7405b75b 100644 --- a/qcsrc/common/items/item/powerup.qc +++ b/qcsrc/common/items/item/powerup.qc @@ -1,37 +1 @@ #include "powerup.qh" - -#ifndef MENUQC -MODEL(Strength_ITEM, Item_Model("g_strength.md3")); -SOUND(Strength, "misc/powerup"); -#endif - -REGISTER_ITEM(Strength, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Strength_ITEM; - this.m_sound = SND_Strength; -#endif - this.m_name = "Strength Powerup"; - this.m_icon = "strength"; - this.m_color = '0 0 1'; - this.m_waypoint = _("Strength"); - this.m_waypointblink = 2; - this.m_itemid = IT_STRENGTH; -} - -#ifndef MENUQC -MODEL(Shield_ITEM, Item_Model("g_invincible.md3")); -SOUND(Shield, "misc/powerup_shield"); -#endif - -REGISTER_ITEM(Shield, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Shield_ITEM; - this.m_sound = SND_Shield; -#endif - this.m_name = "Shield"; - this.m_icon = "shield"; - this.m_color = '1 0 1'; - this.m_waypoint = _("Shield"); - this.m_waypointblink = 2; - this.m_itemid = IT_INVINCIBLE; -} diff --git a/qcsrc/common/items/item/powerup.qh b/qcsrc/common/items/item/powerup.qh index 0a3d907288..80b1e6affc 100644 --- a/qcsrc/common/items/item/powerup.qh +++ b/qcsrc/common/items/item/powerup.qh @@ -8,11 +8,51 @@ #include "pickup.qh" CLASS(Powerup, Pickup) #ifdef SVQC - ATTRIB(Powerup, m_mins, vector, '-16 -16 0') - ATTRIB(Powerup, m_maxs, vector, '16 16 48') - ATTRIB(Powerup, m_botvalue, int, 100000) - 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)) + ATTRIB(Powerup, m_mins, vector, '-16 -16 0'); + ATTRIB(Powerup, m_maxs, vector, '16 16 80'); + ATTRIB(Powerup, m_botvalue, int, 100000); + 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)); #endif ENDCLASS(Powerup) + +#ifdef GAMEQC +MODEL(Strength_ITEM, Item_Model("g_strength.md3")); +SOUND(Strength, Item_Sound("powerup")); +#endif + +REGISTER_ITEM(Strength, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Strength_ITEM; + this.m_sound = SND_Strength; + this.m_glow = true; + this.m_respawnsound = SND_STRENGTH_RESPAWN; +#endif + this.m_name = "Strength Powerup"; + this.m_icon = "strength"; + this.m_color = '0 0 1'; + this.m_waypoint = _("Strength"); + this.m_waypointblink = 2; + this.m_itemid = IT_STRENGTH; +} + +#ifdef GAMEQC +MODEL(Shield_ITEM, Item_Model("g_invincible.md3")); +SOUND(Shield, Item_Sound("powerup_shield")); +#endif + +REGISTER_ITEM(Shield, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Shield_ITEM; + this.m_sound = SND_Shield; + this.m_glow = true; + this.m_respawnsound = SND_SHIELD_RESPAWN; +#endif + this.m_name = "Shield"; + this.m_icon = "shield"; + this.m_color = '1 0 1'; + this.m_waypoint = _("Shield"); + this.m_waypointblink = 2; + this.m_itemid = IT_INVINCIBLE; +} diff --git a/qcsrc/common/mapinfo.qc b/qcsrc/common/mapinfo.qc index 2ec902f685..2af3d90823 100644 --- a/qcsrc/common/mapinfo.qc +++ b/qcsrc/common/mapinfo.qc @@ -1,13 +1,12 @@ +#include "mapinfo.qh" #if defined(CSQC) #include "../client/defs.qh" #include "util.qh" - #include - #include "mapinfo.qh" + #include #elif defined(MENUQC) #elif defined(SVQC) #include "util.qh" - #include - #include "mapinfo.qh" + #include #endif // generic string stuff @@ -150,7 +149,11 @@ float _MapInfo_FilterList_cmp(float i, float j, entity pass) return strcasecmp(a, b); } -float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate) +float MapInfo_FilterGametype(Gametype pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate) +{ + return _MapInfo_FilterGametype(pGametype.m_flags, pFeatures, pFlagsRequired, pFlagsForbidden, pAbortOnGenerate); +} +float _MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, int pFlagsForbidden, bool pAbortOnGenerate) { float i, j; if (!_MapInfo_filtered_allocated) @@ -161,10 +164,10 @@ float MapInfo_FilterGametype(int pGametype, int pFeatures, int pFlagsRequired, i MapInfo_count = 0; for(i = 0, j = -1; i < _MapInfo_globcount; ++i) { - if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, 0) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame. + if(MapInfo_Get_ByName(_MapInfo_GlobItem(i), 1, NULL) == 2) // if we generated one... BAIL OUT and let the caller continue in the next frame. if(pAbortOnGenerate) { - LOG_TRACE("Autogenerated a .mapinfo, doing the rest later.\n"); + LOG_TRACE("Autogenerated a .mapinfo, doing the rest later."); MapInfo_progress = i / _MapInfo_globcount; return 0; } @@ -249,7 +252,7 @@ string unquote(string s) float MapInfo_Get_ByID(float i) { - if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, 0)) + if(MapInfo_Get_ByName(MapInfo_BSPName_ByID(i), 0, NULL)) return 1; return 0; } @@ -265,7 +268,6 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp float i; float inWorldspawn; float r; - float twoBaseModes; float diameter, spawnpoints; float spawnplaces; @@ -345,21 +347,7 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp } else if(k == "classname") { - if(v == "dom_controlpoint") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DOMINATION; - else if(v == "item_flag_team2") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF; - else if(v == "team_CTF_blueflag") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTF; - else if(v == "invasion_spawnpoint") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_INVASION; - else if(v == "target_assault_roundend") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ASSAULT; - else if(v == "onslaught_generator") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_ONSLAUGHT; - else if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_NEXBALL; - else if(v == "info_player_team1") + if(v == "info_player_team1") ++spawnpoints; else if(v == "info_player_team2") ++spawnpoints; @@ -367,10 +355,6 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp ++spawnpoints; else if(v == "info_player_deathmatch") ++spawnpoints; - else if(v == "trigger_race_checkpoint") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_RACE; - else if(v == "target_startTimer") - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS; else if(v == "weapon_nex") { } else if(v == "weapon_railgun") @@ -385,47 +369,39 @@ float _MapInfo_Generate(string pFilename) // 0: failure, 1: ok ent, 2: ok bsp MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS; else if(v == "target_music" || v == "trigger_music") _MapInfo_Map_worldspawn_music = string_null; // don't use regular BGM + else + FOREACH(Gametypes, true, it.m_generate_mapinfo(it, v)); } } } if(inWorldspawn) { - LOG_MAPWARN(fn, " ended still in worldspawn, BUG\n"); + LOG_WARN(fn, " ended still in worldspawn, BUG"); return 0; } diameter = vlen(mapMaxs - mapMins); - twoBaseModes = MapInfo_Map_supportedGametypes & (MAPINFO_TYPE_CTF | MAPINFO_TYPE_ASSAULT | MAPINFO_TYPE_RACE | MAPINFO_TYPE_NEXBALL); - if(twoBaseModes && (MapInfo_Map_supportedGametypes == twoBaseModes)) + int twoBaseModes = 0; + FOREACH(Gametypes, it.m_isTwoBaseMode(), twoBaseModes |= it.m_flags); + if(twoBaseModes && (twoBaseModes &= MapInfo_Map_supportedGametypes)) { - // we have a CTF-only or Assault-only map. Don't add other modes then, - // as the map is too symmetric for them. + // we have a symmetrical map, don't add the modes without bases } else { - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_DEATHMATCH; // DM always works - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_LMS; // LMS always works - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEEPAWAY; // Keepaway always works - - if(spawnpoints >= 8 && diameter > 4096) { - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_TEAM_DEATHMATCH; - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_FREEZETAG; - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CA; - } - if(spawnpoints >= 12 && diameter > 5120) - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_KEYHUNT; + FOREACH(Gametypes, it.m_isAlwaysSupported(it, spawnpoints, diameter), MapInfo_Map_supportedGametypes |= it.m_flags); } - if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE) + if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_RACE.m_flags) if(!spawnplaces) { - MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE; - MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS; + MapInfo_Map_supportedGametypes &= ~MAPINFO_TYPE_RACE.m_flags; + MapInfo_Map_supportedGametypes |= MAPINFO_TYPE_CTS.m_flags; } LOG_TRACE("-> diameter ", ftos(diameter)); LOG_TRACE("; spawnpoints ", ftos(spawnpoints)); - LOG_TRACE("; modes ", ftos(MapInfo_Map_supportedGametypes), "\n"); + LOG_TRACE("; modes ", ftos(MapInfo_Map_supportedGametypes)); fclose(fh); @@ -447,7 +423,7 @@ void _MapInfo_Map_Reset() MapInfo_Map_maxs = '0 0 0'; } -string _MapInfo_GetDefault(float t) +string _MapInfo_GetDefault(Gametype t) { switch(t) { @@ -472,11 +448,11 @@ string _MapInfo_GetDefault(float t) } } -void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int load_default) +void _MapInfo_Map_ApplyGametype(string s, Gametype pWantedType, Gametype pThisType, int load_default) { string sa; - MapInfo_Map_supportedGametypes |= pThisType; - if(!(pThisType & pWantedType)) + MapInfo_Map_supportedGametypes |= pThisType.m_flags; + if(!(pThisType.m_flags & pWantedType.m_flags)) return; if(load_default) @@ -499,55 +475,13 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo cvar_set("timelimit", sa); s = cdr(s); - if(pWantedType == MAPINFO_TYPE_TEAM_DEATHMATCH) - { - sa = car(s); - if(sa != "") - cvar_set("g_tdm_teams", sa); - s = cdr(s); - } - - if(pWantedType == MAPINFO_TYPE_KEYHUNT) - { - sa = car(s); - if(sa != "") - cvar_set("g_keyhunt_teams", sa); - s = cdr(s); - } - - if(pWantedType == MAPINFO_TYPE_CA) - { - sa = car(s); - if(sa != "") - cvar_set("g_ca_teams", sa); - s = cdr(s); - } - - if(pWantedType == MAPINFO_TYPE_FREEZETAG) - { - sa = car(s); - if(sa != "") - cvar_set("g_freezetag_teams", sa); - s = cdr(s); - } - - if(pWantedType == MAPINFO_TYPE_CTF) + if(pWantedType.m_setTeams) { sa = car(s); if(sa != "") - cvar_set("fraglimit", sa); - s = cdr(s); - } - - /* keepaway wuz here - if(pWantedType == MAPINFO_TYPE_KEEPAWAY) - { - sa = car(s); - if(sa != "") - cvar_set("fraglimit", sa); + pWantedType.m_setTeams(sa); s = cdr(s); } - */ // rc = timelimit timelimit_qualification laps laps_teamplay if(pWantedType == MAPINFO_TYPE_RACE) @@ -569,19 +503,6 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo s = cdr(s); } - if(pWantedType == MAPINFO_TYPE_CTS) - { - sa = car(s); - - // this is the skill of the map - // not parsed by anything yet - // for map databases - //if(sa != "") - // cvar_set("fraglimit", sa); - - s = cdr(s); - } - if(pWantedType == MAPINFO_TYPE_ASSAULT || pWantedType == MAPINFO_TYPE_ONSLAUGHT || pWantedType == MAPINFO_TYPE_CTS) // these modes don't use fraglimit { cvar_set("leadlimit", "0"); @@ -595,22 +516,20 @@ void _MapInfo_Map_ApplyGametype(string s, int pWantedType, int pThisType, int lo } } -string _MapInfo_GetDefaultEx(float t) +string _MapInfo_GetDefaultEx(Gametype t) { - FOREACH(Gametypes, it.items == t, return it.model2); - return ""; + return t ? t.model2 : ""; } -float _MapInfo_GetTeamPlayBool(float t) +float _MapInfo_GetTeamPlayBool(Gametype t) { - FOREACH(Gametypes, it.items == t, return it.team); - return false; + return t ? t.team : false; } -void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType) +void _MapInfo_Map_ApplyGametypeEx(string s, Gametype pWantedType, Gametype pThisType) { - MapInfo_Map_supportedGametypes |= pThisType; - if (!(pThisType & pWantedType)) + MapInfo_Map_supportedGametypes |= pThisType.m_flags; + if (!(pThisType.m_flags & pWantedType.m_flags)) return; // reset all the cvars to their defaults @@ -628,7 +547,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType) if (sa == "") continue; int p = strstrofs(sa, "=", 0); if (p < 0) { - LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa); + LOG_WARNF("Invalid gametype setting in mapinfo for gametype %s: %s", MapInfo_Type_ToString(pWantedType), sa); continue; } string k = substring(sa, 0, p); @@ -668,7 +587,7 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType) } FOREACH(Gametypes, true, handled |= it.m_parse_mapinfo(k, v)); if (!handled) - LOG_MAPWARNF("Invalid gametype setting in mapinfo for gametype %s: %s\n", MapInfo_Type_ToString(pWantedType), sa); + LOG_WARNF("Invalid gametype setting in mapinfo for gametype %s: %s", MapInfo_Type_ToString(pWantedType), sa); } if (pWantedType == MAPINFO_TYPE_RACE && cvar("g_race_teams") >= 2) @@ -683,18 +602,12 @@ void _MapInfo_Map_ApplyGametypeEx(string s, int pWantedType, int pThisType) } } -Gametype MapInfo_Type(int t) -{ - FOREACH(Gametypes, it.items == t, return it); - return NULL; -} - -int MapInfo_Type_FromString(string t) +Gametype MapInfo_Type_FromString(string t) { #define deprecate(from, to) MACRO_BEGIN { \ if (t == #from) { \ string replacement = #to; \ - LOG_MAPWARNF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.\n", MapInfo_Map_bspname, t, replacement); \ + LOG_WARNF("MapInfo_Type_FromString (probably %s): using deprecated name '%s'. Should use '%s'.", MapInfo_Map_bspname, t, replacement); \ t = replacement; \ } \ } MACRO_END @@ -704,31 +617,25 @@ int MapInfo_Type_FromString(string t) deprecate(invasion, inv); deprecate(assault, as); deprecate(race, rc); - if (t == "all") return MAPINFO_TYPE_ALL; - FOREACH(Gametypes, it.mdl == t, return it.items); - return 0; + FOREACH(Gametypes, it.mdl == t, return it); + return NULL; #undef deprecate } -string MapInfo_Type_Description(float t) +string MapInfo_Type_Description(Gametype t) { - FOREACH(Gametypes, it.items == t, return it.gametype_description); - return ""; + return t ? t.gametype_description : ""; } -string MapInfo_Type_ToString(float t) +string MapInfo_Type_ToString(Gametype t) { - if(t == MAPINFO_TYPE_ALL) - return "all"; - FOREACH(Gametypes, it.items == t, return it.mdl); - return ""; + return t ? t.mdl : ""; } -string MapInfo_Type_ToText(float t) +string MapInfo_Type_ToText(Gametype t) { - FOREACH(Gametypes, it.items == t, return it.message); /* xgettext:no-c-format */ - return _("@!#%'n Tuba Throwing"); + return t ? t.message : _("@!#%'n Tuba Throwing"); } void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, float recurse) @@ -770,7 +677,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, { fh = fopen(s, FILE_READ); if(fh < 0) - LOG_MAPWARN("Map ", pFilename, " references not existing config file ", s, "\n"); + LOG_WARN("Map ", pFilename, " references not existing config file ", s); else { for (;;) @@ -799,23 +706,23 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, } } else - LOG_MAPWARN("Map ", pFilename, " uses too many levels of inclusion\n"); + LOG_WARN("Map ", pFilename, " uses too many levels of inclusion"); } else if(t == "") - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored"); else if (!cvar_value_issafe(t)) - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored"); else if (!cvar_value_issafe(s)) - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored"); else if(matchacl(MAPINFO_SETTEMP_ACL_SYSTEM, t) <= 0) - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful setting, ignored"); else if(matchacl(acl, t) <= 0) - LOG_MAPWARN("Map ", pFilename, " contains a denied setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a denied setting, ignored"); else { if(type == 0) // server set { - LOG_TRACE("Applying temporary setting ", t, " := ", s, "\n"); + LOG_TRACE("Applying temporary setting ", t, " := ", s); if(cvar("g_campaign")) cvar_set(t, s); // this is a wrapper and is always temporary anyway; no need to backup old values then else @@ -823,7 +730,7 @@ void _MapInfo_Parse_Settemp(string pFilename, string acl, float type, string s, } else { - LOG_TRACE("Applying temporary client setting ", t, " := ", s, "\n"); + LOG_TRACE("Applying temporary client setting ", t, " := ", s); MapInfo_Map_clientstuff = strcat( MapInfo_Map_clientstuff, "cl_cmd settemp \"", t, "\" \"", s, "\"\n" ); @@ -857,7 +764,7 @@ float MapInfo_isRedundant(string fn, string t) } // load info about a map by name into the MapInfo_Map_* globals -float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int pGametypeToSet) +float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, Gametype pGametypeToSet) { string fn; string s, t; @@ -870,11 +777,11 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p if(strstrofs(pFilename, "/", 0) >= 0) { - LOG_MAPWARN("Invalid character in map name, ignored\n"); + LOG_WARN("Invalid character in map name, ignored"); return 0; } - if(pGametypeToSet == 0) + if(pGametypeToSet == NULL) if(MapInfo_Cache_Retrieve(pFilename)) return 1; @@ -939,9 +846,9 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p if(MapInfo_Map_flags & MAPINFO_FLAG_FRUSTRATING) fputs(fh, "frustrating\n"); - for(i = 1; i <= MapInfo_Map_supportedGametypes; i *= 2) - if(MapInfo_Map_supportedGametypes & i) - fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(i), _MapInfo_GetDefaultEx(i))); + FOREACH(Gametypes, MapInfo_Map_supportedGametypes & it.m_flags, { + fputs(fh, sprintf("gametype %s // defaults: %s\n", MapInfo_Type_ToString(it), _MapInfo_GetDefaultEx(it))); + }); if(fexists(strcat("scripts/", pFilename, ".arena"))) fputs(fh, "settemp_for_type all sv_q3acompat_machineshotgunswap 1\n"); @@ -960,7 +867,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p error("... but I just wrote it!"); } - LOG_MAPWARN("autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo\n"); + LOG_WARN("autogenerated mapinfo file ", fn, " has been loaded; please edit that file and move it to maps/", pFilename, ".mapinfo"); } _MapInfo_Map_Reset(); @@ -999,7 +906,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p else if(t == "monsters") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_MONSTERS; else if(t == "new_toys") MapInfo_Map_supportedFeatures |= MAPINFO_FEATURE_WEAPONS; else - LOG_MAPWARN("Map ", pFilename, " supports unknown feature ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " supports unknown feature ", t, ", ignored"); } else if(t == "hidden") { @@ -1025,21 +932,21 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p else if(t == "type") { t = car(s); s = cdr(s); - f = MapInfo_Type_FromString(t); - LOG_MAPWARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'.\n"); + Gametype f = MapInfo_Type_FromString(t); + LOG_WARN("Map ", pFilename, " contains the legacy 'type' keyword which is deprecated and will be removed in the future. Please migrate the mapinfo file to 'gametype'."); if(f) _MapInfo_Map_ApplyGametype (s, pGametypeToSet, f, true); else - LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " supports unknown game type ", t, ", ignored"); } else if(t == "gametype") { t = car(s); s = cdr(s); - f = MapInfo_Type_FromString(t); + Gametype f = MapInfo_Type_FromString(t); if(f) _MapInfo_Map_ApplyGametypeEx (s, pGametypeToSet, f); else - LOG_MAPWARN("Map ", pFilename, " supports unknown game type ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " supports unknown game type ", t, ", ignored"); } else if(t == "size") { @@ -1050,16 +957,16 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p t = car(s); s = cdr(s); d = stof(t); t = car(s); s = cdr(s); e = stof(t); if(s == "") - LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n"); + LOG_WARN("Map ", pFilename, " contains an incorrect size line (not enough params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z"); else { t = car(s); s = cdr(s); f = stof(t); if(s != "") - LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z\n"); + LOG_WARN("Map ", pFilename, " contains an incorrect size line (too many params), syntax: size mins_x mins_y mins_z maxs_x maxs_y maxs_z"); else { if(a >= d || b >= e || c >= f) - LOG_MAPWARN("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs\n"); + LOG_WARN("Map ", pFilename, " contains an incorrect size line, mins have to be < maxs"); else { MapInfo_Map_mins.x = a; @@ -1075,37 +982,41 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p else if(t == "settemp_for_type") { t = car(s); s = cdr(s); - if((f = MapInfo_Type_FromString(t))) + bool all = t == "all"; + Gametype f = NULL; + if(all || (f = MapInfo_Type_FromString(t))) { - if(f & pGametypeToSet) + if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags) { _MapInfo_Parse_Settemp(pFilename, acl, 0, s, 1); } } else { - LOG_MAPWARN("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " has a setting for unknown game type ", t, ", ignored"); } } else if(t == "clientsettemp_for_type") { t = car(s); s = cdr(s); - if((f = MapInfo_Type_FromString(t))) + bool all = t == "all"; + Gametype f = NULL; + if(all || (f = MapInfo_Type_FromString(t))) { - if(f & pGametypeToSet) + if((all ? MAPINFO_TYPE_ALL : f.m_flags) & pGametypeToSet.m_flags) { _MapInfo_Parse_Settemp(pFilename, acl, 1, s, 1); } } else { - LOG_MAPWARN("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " has a client setting for unknown game type ", t, ", ignored"); } } else if(t == "fog") { if (!cvar_value_issafe(s)) - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful fog setting, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful fog setting, ignored"); else MapInfo_Map_fog = s; } @@ -1121,7 +1032,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p if(pGametypeToSet) { if (!cvar_value_issafe(t)) - LOG_MAPWARN("Map ", pFilename, " contains a potentially harmful cdtrack, ignored\n"); + LOG_WARN("Map ", pFilename, " contains a potentially harmful cdtrack, ignored"); else MapInfo_Map_clientstuff = strcat( MapInfo_Map_clientstuff, "cd loop \"", t, "\"\n" @@ -1129,7 +1040,7 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p } } else - LOG_MAPWARN("Map ", pFilename, " provides unknown info item ", t, ", ignored\n"); + LOG_WARN("Map ", pFilename, " provides unknown info item ", t, ", ignored"); } fclose(fh); @@ -1143,24 +1054,24 @@ float MapInfo_Get_ByName_NoFallbacks(string pFilename, int pAllowGenerate, int p MapInfo_Cache_Store(); if(MapInfo_Map_supportedGametypes != 0) return r; - LOG_MAPWARN("Map ", pFilename, " supports no game types, ignored\n"); + LOG_WARN("Map ", pFilename, " supports no game types, ignored"); return 0; } -float MapInfo_Get_ByName(string pFilename, float pAllowGenerate, int pGametypeToSet) +int MapInfo_Get_ByName(string pFilename, float pAllowGenerate, Gametype pGametypeToSet) { - float r = MapInfo_Get_ByName_NoFallbacks(pFilename, pAllowGenerate, pGametypeToSet); + 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)) - if(MapInfo_Map_supportedGametypes & MAPINFO_TYPE_DEATHMATCH) + 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); } if(pGametypeToSet) { - if(!(MapInfo_Map_supportedGametypes & pGametypeToSet)) + if(!(MapInfo_Map_supportedGametypes & pGametypeToSet.m_flags)) { error("Can't select the requested game type. This should never happen as the caller should prevent it!\n"); //_MapInfo_Map_ApplyGametypeEx("", pGametypeToSet, MAPINFO_TYPE_DEATHMATCH); @@ -1233,20 +1144,21 @@ int MapInfo_CurrentFeatures() return req; } -int MapInfo_CurrentGametype() +Gametype MapInfo_CurrentGametype() { - int prev = cvar("gamecfg"); - FOREACH(Gametypes, cvar(it.netname) && it.items != prev, return it.items); - if (prev) return prev; - return MAPINFO_TYPE_DEATHMATCH; + Gametype prev = Gametypes_from(cvar("gamecfg")); + FOREACH(Gametypes, cvar(it.netname) && it != prev, return it); + return prev ? prev : MAPINFO_TYPE_DEATHMATCH; } -float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise +float _MapInfo_CheckMap(string s, bool gametype_only) // returns 0 if the map can't be played with the current settings, 1 otherwise { - if(!MapInfo_Get_ByName(s, 1, 0)) + if(!MapInfo_Get_ByName(s, 1, NULL)) return 0; - if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype()) == 0) + if((MapInfo_Map_supportedGametypes & MapInfo_CurrentGametype().m_flags) == 0) return 0; + if (gametype_only) + return 1; if((MapInfo_Map_supportedFeatures & MapInfo_CurrentFeatures()) != MapInfo_CurrentFeatures()) return 0; return 1; @@ -1255,14 +1167,14 @@ float _MapInfo_CheckMap(string s) // returns 0 if the map can't be played with t float MapInfo_CheckMap(string s) // returns 0 if the map can't be played with the current settings, 1 otherwise { float r; - r = _MapInfo_CheckMap(s); + r = _MapInfo_CheckMap(s, false); MapInfo_ClearTemps(); return r; } -void MapInfo_SwitchGameType(int t) +void MapInfo_SwitchGameType(Gametype t) { - FOREACH(Gametypes, true, cvar_set(it.netname, (it.items == t) ? "1" : "0")); + FOREACH(Gametypes, true, cvar_set(it.netname, (it == t) ? "1" : "0")); } void MapInfo_LoadMap(string s, float reinit) @@ -1272,7 +1184,7 @@ void MapInfo_LoadMap(string s, float reinit) //if(!MapInfo_CheckMap(s)) //{ // print("EMERGENCY: can't play the selected map in the given game mode. Falling back to DM.\n"); - // MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH); + // MapInfo_SwitchGameType(MAPINFO_TYPE_DEATHMATCH.m_flags); //} cvar_settemp_restore(); @@ -1282,7 +1194,7 @@ void MapInfo_LoadMap(string s, float reinit) localcmd(strcat("\nchangelevel ", s, "\n")); } -string MapInfo_ListAllowedMaps(float type, float pRequiredFlags, float pForbiddenFlags) +string MapInfo_ListAllowedMaps(Gametype type, float pRequiredFlags, float pForbiddenFlags) { string out; float i; @@ -1304,7 +1216,7 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags) // to make absolutely sure: MapInfo_Enumerate(); - MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0); + _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, pRequiredFlags, pForbiddenFlags, 0); out = ""; for(i = 0; i < MapInfo_count; ++i) @@ -1315,49 +1227,52 @@ string MapInfo_ListAllAllowedMaps(float pRequiredFlags, float pForbiddenFlags) return substring(out, 1, strlen(out) - 1); } -void MapInfo_LoadMapSettings_SaveGameType(float t) +void MapInfo_LoadMapSettings_SaveGameType(Gametype t) { MapInfo_SwitchGameType(t); - cvar_set("gamecfg", ftos(t)); + cvar_set("gamecfg", ftos(t.m_id)); MapInfo_LoadedGametype = t; } void MapInfo_LoadMapSettings(string s) // to be called from worldspawn { - float t; - - t = MapInfo_CurrentGametype(); + Gametype t = MapInfo_CurrentGametype(); MapInfo_LoadMapSettings_SaveGameType(t); - if(!_MapInfo_CheckMap(s)) // with underscore, it keeps temps + if(!_MapInfo_CheckMap(s, true)) // with underscore, it keeps temps { if(cvar("g_mapinfo_allow_unsupported_modes_and_let_stuff_break")) { - LOG_SEVERE("can't play the selected map in the given game mode. Working with only the override settings.\n"); + LOG_SEVERE("can't play the selected map in the given game mode. Working with only the override settings."); _MapInfo_Map_ApplyGametypeEx("", t, t); return; // do not call Get_ByName! } if(MapInfo_Map_supportedGametypes == 0) { - LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch.\n"); - MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH; + LOG_SEVERE("Mapinfo system is not functional at all. Assuming deathmatch."); + MapInfo_Map_supportedGametypes = MAPINFO_TYPE_DEATHMATCH.m_flags; MapInfo_LoadMapSettings_SaveGameType(MAPINFO_TYPE_DEATHMATCH); _MapInfo_Map_ApplyGametypeEx("", MAPINFO_TYPE_DEATHMATCH, MAPINFO_TYPE_DEATHMATCH); return; // do not call Get_ByName! } - t = 1; + int _t = 1; while(!(MapInfo_Map_supportedGametypes & 1)) { - t *= 2; - MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes / 2); + _t <<= 1; + MapInfo_Map_supportedGametypes = floor(MapInfo_Map_supportedGametypes >> 1); } + Gametype t_prev = t; + FOREACH(Gametypes, it.m_flags == _t, { t = it; break; }); // t is now a supported mode! - LOG_WARNING("can't play the selected map in the given game mode. Falling back to a supported mode.\n"); + LOG_WARNF("can't play the selected map in the given game mode (%s). Falling back to a supported mode (%s).", t_prev.mdl, t.mdl); MapInfo_LoadMapSettings_SaveGameType(t); } + if(!_MapInfo_CheckMap(s, false)) { // with underscore, it keeps temps + LOG_WARNF("the selected map lacks features required by current settings; playing anyway."); + } MapInfo_Get_ByName(s, 1, t); } @@ -1390,7 +1305,7 @@ int MapInfo_ForbiddenFlags() { int f = MAPINFO_FLAG_FORBIDDEN; -#ifndef MENUQC +#ifdef GAMEQC if (!cvar("g_maplist_allow_hidden")) #endif f |= MAPINFO_FLAG_HIDDEN; diff --git a/qcsrc/common/mapinfo.qh b/qcsrc/common/mapinfo.qh index b7115ef552..a3e000d333 100644 --- a/qcsrc/common/mapinfo.qh +++ b/qcsrc/common/mapinfo.qh @@ -1,32 +1,62 @@ -#ifndef MAPINFO_H -#define MAPINFO_H +#pragma once -bool autocvar_developer_mapper; +#include "util.qh" -#define LOG_MAPWARN(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNING(__VA_ARGS__); } MACRO_END -#define LOG_MAPWARNF(...) MACRO_BEGIN { if (autocvar_developer_mapper) LOG_WARNINGF(__VA_ARGS__); } MACRO_END +// info about a map that MapInfo loads +string MapInfo_Map_bspname; +string MapInfo_Map_title; +string MapInfo_Map_titlestring; // either bspname: title or just title, depending on whether bspname is redundant +string MapInfo_Map_description; +string MapInfo_Map_author; +string MapInfo_Map_clientstuff; // not in cache, only for map load +string MapInfo_Map_fog; // not in cache, only for map load +int MapInfo_Map_supportedGametypes; +int MapInfo_Map_supportedFeatures; +int MapInfo_Map_flags; +vector MapInfo_Map_mins; // these are '0 0 0' if not supported! +vector MapInfo_Map_maxs; // these are '0 0 0' if not specified! -#include "util.qh" +int MAPINFO_TYPE_ALL; +.int m_flags; CLASS(Gametype, Object) - ATTRIB(Gametype, m_id, int, 0) + ATTRIB(Gametype, m_id, int, 0); /** game type ID */ - ATTRIB(Gametype, items, int, 0) + ATTRIB(Gametype, items, int, 0); /** game type name as in cvar (with g_ prefix) */ - ATTRIB(Gametype, netname, string, string_null) + ATTRIB(Gametype, netname, string); /** game type short name */ - ATTRIB(Gametype, mdl, string, string_null) + ATTRIB(Gametype, mdl, string); /** human readable name */ - ATTRIB(Gametype, message, string, string_null) + ATTRIB(Gametype, message, string); /** does this gametype support teamplay? */ - ATTRIB(Gametype, team, bool, false) + ATTRIB(Gametype, team, bool, false); /** game type defaults */ - ATTRIB(Gametype, model2, string, string_null) + ATTRIB(Gametype, model2, string); /** game type description */ - ATTRIB(Gametype, gametype_description, string, string_null) + ATTRIB(Gametype, gametype_description, string); +#ifdef CSQC + ATTRIB(Gametype, m_modicons, void(vector pos, vector mySize)); + ATTRIB(Gametype, m_modicons_reset, void()); +#endif - ATTRIB(Gametype, m_mutators, string, string_null) - ATTRIB(Gametype, m_parse_mapinfo, bool(string k, string v), func_null) + ATTRIB(Gametype, m_mutators, string); + METHOD(Gametype, m_parse_mapinfo, bool(string k, string v)) + { + return false; + } + METHOD(Gametype, m_generate_mapinfo, void(Gametype this, string v)) + { + TC(Gametype, this); + } + METHOD(Gametype, m_isTwoBaseMode, bool()) + { + return false; + } + METHOD(Gametype, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return false; + } METHOD(Gametype, describe, string(Gametype this)) { @@ -40,150 +70,412 @@ CLASS(Gametype, Object) returns(this.message, strcat("gametype_", this.mdl)); } - CONSTRUCTOR(Gametype, 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, string mutators, string defaults, string gdescription)) { - CONSTRUCT(Gametype); this.netname = g_name; this.mdl = sname; this.message = hname; this.team = gteamplay; - this.m_mutators = mutators; + this.m_mutators = cons(sname, mutators); this.model2 = defaults; this.gametype_description = gdescription; + + // same as `1 << m_id` + MAPINFO_TYPE_ALL |= this.items = this.m_flags = (MAPINFO_TYPE_ALL + 1); } ENDCLASS(Gametype) -REGISTRY(Gametypes, BITS(4)) +REGISTRY(Gametypes, 24) #define Gametypes_from(i) _Gametypes_from(i, NULL) REGISTER_REGISTRY(Gametypes) REGISTRY_CHECK(Gametypes) -int MAPINFO_TYPE_ALL; -#define REGISTER_GAMETYPE(hname, sname, g_name, NAME, gteamplay, mutators, defaults, gdescription) \ - int MAPINFO_TYPE_##NAME; \ - bool NAME##_mapinfo(string k, string v) { return = false; } \ - REGISTER(Gametypes, MAPINFO_TYPE, g_name, m_id, \ - NEW(Gametype, hname, #sname, #g_name, gteamplay, #sname " " mutators, defaults, gdescription) \ - ) { \ - /* same as `1 << m_id` */ \ - MAPINFO_TYPE_##NAME = MAPINFO_TYPE_ALL + 1; MAPINFO_TYPE_ALL |= MAPINFO_TYPE_##NAME; \ - this.items = MAPINFO_TYPE_##NAME; \ - this.m_parse_mapinfo = NAME##_mapinfo; \ - } \ - [[accumulate]] bool NAME##_mapinfo(string k, string v) - -#define IS_GAMETYPE(NAME) \ - (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME) - -REGISTER_GAMETYPE(_("Deathmatch"),dm,g_dm,DEATHMATCH,false,"","timelimit=20 pointlimit=30 leadlimit=0",_("Score as many frags as you can")); - -REGISTER_GAMETYPE(_("Last Man Standing"),lms,g_lms,LMS,false,"","timelimit=20 lives=9 leadlimit=0",_("Survive and kill until the enemies have no lives left")); - -REGISTER_GAMETYPE(_("Race"),rc,g_race,RACE,false,"","timelimit=20 qualifying_timelimit=5 laplimit=7 teamlaplimit=15 leadlimit=0",_("Race against other players to the finish line")) -{ - if (!k) { - cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit")); +#define REGISTER_GAMETYPE(NAME, inst) REGISTER(Gametypes, MAPINFO_TYPE, NAME, m_id, inst) + +#define IS_GAMETYPE(NAME) (MapInfo_LoadedGametype == MAPINFO_TYPE_##NAME) + +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")); + } + METHOD(Deathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return true; + } +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")); + } + METHOD(LastManStanding, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { return true; } - switch (k) { - case "qualifying_timelimit": - cvar_set("g_race_qualifying_timelimit", v); +ENDCLASS(LastManStanding) +REGISTER_GAMETYPE(LMS, NEW(LastManStanding)); + +#ifdef CSQC +void HUD_Mod_Race(vector pos, vector mySize); +#endif +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")); + } + METHOD(Race, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_race_qualifying_timelimit", cvar_defstring("g_race_qualifying_timelimit")); return true; + } + switch (k) { + case "qualifying_timelimit": + cvar_set("g_race_qualifying_timelimit", v); + return true; + } + return false; + } + METHOD(Race, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "trigger_race_checkpoint") + MapInfo_Map_supportedGametypes |= this.m_flags; } -} + METHOD(Race, m_isTwoBaseMode, bool()) + { + return true; + } +#ifdef CSQC + ATTRIB(Race, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); +#endif +ENDCLASS(Race) +REGISTER_GAMETYPE(RACE, NEW(Race)); #define g_race IS_GAMETYPE(RACE) -REGISTER_GAMETYPE(_("Race CTS"),cts,g_cts,CTS,false,"cloaked","timelimit=20",_("Race for fastest time.")); +CLASS(RaceCTS, Gametype) + INIT(RaceCTS) + { + this.gametype_init(this, _("Race CTS"),"cts","g_cts",false,"cloaked","timelimit=20",_("Race for fastest time.")); + } + METHOD(RaceCTS, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "target_startTimer") + MapInfo_Map_supportedGametypes |= this.m_flags; + } + METHOD(RaceCTS, m_setTeams, void(string sa)) + { + // this is the skill of the map + // not parsed by anything yet + // for map databases + // cvar_set("fraglimit", sa); + } +#ifdef CSQC + ATTRIB(RaceCTS, m_modicons, void(vector pos, vector mySize), HUD_Mod_Race); +#endif +ENDCLASS(RaceCTS) +REGISTER_GAMETYPE(CTS, NEW(RaceCTS)); #define g_cts IS_GAMETYPE(CTS) -REGISTER_GAMETYPE(_("Team Deathmatch"),tdm,g_tdm,TEAM_DEATHMATCH,true,"","timelimit=20 pointlimit=50 teams=2 leadlimit=0",_("Help your team score the most frags against the enemy team")) -{ - if (!k) { - cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams")); - return true; +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")); + } + METHOD(TeamDeathmatch, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_tdm_teams", cvar_defstring("g_tdm_teams")); + return true; + } + switch (k) { + case "teams": + cvar_set("g_tdm_teams", v); + return true; + } + return false; } - switch (k) { - case "teams": - cvar_set("g_tdm_teams", v); + METHOD(TeamDeathmatch, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + if(spawnpoints >= 8 && diameter > 4096) return true; + return false; + } + METHOD(TeamDeathmatch, m_setTeams, void(string sa)) + { + cvar_set("g_tdm_teams", sa); } -} +ENDCLASS(TeamDeathmatch) +REGISTER_GAMETYPE(TEAM_DEATHMATCH, NEW(TeamDeathmatch)); #define g_tdm IS_GAMETYPE(TEAM_DEATHMATCH) -REGISTER_GAMETYPE(_("Capture the Flag"),ctf,g_ctf,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")); +#ifdef CSQC +void HUD_Mod_CTF(vector pos, vector mySize); +void HUD_Mod_CTF_Reset(); +#endif +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")); + } + METHOD(CaptureTheFlag, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "item_flag_team2" || v == "team_CTF_blueflag") + MapInfo_Map_supportedGametypes |= this.m_flags; + } + METHOD(CaptureTheFlag, m_isTwoBaseMode, bool()) + { + return true; + } + METHOD(CaptureTheFlag, m_setTeams, void(string sa)) + { + cvar_set("fraglimit", sa); + } +#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 +ENDCLASS(CaptureTheFlag) +REGISTER_GAMETYPE(CTF, NEW(CaptureTheFlag)); #define g_ctf IS_GAMETYPE(CTF) -REGISTER_GAMETYPE(_("Clan Arena"),ca,g_ca,CA,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill all enemy teammates to win the round")) -{ - if (!k) { - cvar_set("g_ca_teams", cvar_defstring("g_ca_teams")); - return true; +#ifdef CSQC +void HUD_Mod_CA(vector pos, vector mySize); +#endif +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")); } - switch (k) { - case "teams": - cvar_set("g_ca_teams", v); + METHOD(ClanArena, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_ca_teams", cvar_defstring("g_ca_teams")); return true; + } + switch (k) { + case "teams": + cvar_set("g_ca_teams", v); + return true; + } + return false; } -} + METHOD(ClanArena, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + if(spawnpoints >= 8 && diameter > 4096) + return true; + return false; + } + METHOD(ClanArena, m_setTeams, void(string sa)) + { + cvar_set("g_ca_teams", sa); + } +#ifdef CSQC + ATTRIB(ClanArena, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); +#endif +ENDCLASS(ClanArena) +REGISTER_GAMETYPE(CA, NEW(ClanArena)); #define g_ca IS_GAMETYPE(CA) -REGISTER_GAMETYPE(_("Domination"),dom,g_domination,DOMINATION,true,"","timelimit=20 pointlimit=200 teams=2 leadlimit=0",_("Capture and defend all the control points to win")) -{ - if (!k) { - cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams")); - return true; +#ifdef CSQC +void HUD_Mod_Dom(vector pos, vector mySize); +#endif +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")); } - switch (k) { - case "teams": - cvar_set("g_domination_default_teams", v); + METHOD(Domination, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_domination_default_teams", cvar_defstring("g_domination_default_teams")); return true; + } + switch (k) { + case "teams": + cvar_set("g_domination_default_teams", v); + return true; + } + return false; } -} + METHOD(Domination, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "dom_controlpoint") + MapInfo_Map_supportedGametypes |= this.m_flags; + } +#ifdef CSQC + ATTRIB(Domination, m_modicons, void(vector pos, vector mySize), HUD_Mod_Dom); +#endif +ENDCLASS(Domination) +REGISTER_GAMETYPE(DOMINATION, NEW(Domination)); -REGISTER_GAMETYPE(_("Key Hunt"),kh,g_keyhunt,KEYHUNT,true,"","timelimit=20 pointlimit=1000 teams=3 leadlimit=0",_("Gather all the keys to win the round")) -{ - if (!k) { - cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams")); - return true; +#ifdef CSQC +void HUD_Mod_KH(vector pos, vector mySize); +#endif +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")); } - switch (k) { - case "teams": - cvar_set("g_keyhunt_teams", v); + METHOD(KeyHunt, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_keyhunt_teams", cvar_defstring("g_keyhunt_teams")); return true; + } + switch (k) { + case "teams": + cvar_set("g_keyhunt_teams", v); + return true; + } + return false; + } + METHOD(KeyHunt, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + if(spawnpoints >= 12 && diameter > 5120) + return true; + return false; + } + METHOD(KeyHunt, m_setTeams, void(string sa)) + { + cvar_set("g_keyhunt_teams", sa); } -} +#ifdef CSQC + ATTRIB(KeyHunt, m_modicons, void(vector pos, vector mySize), HUD_Mod_KH); +#endif +ENDCLASS(KeyHunt) +REGISTER_GAMETYPE(KEYHUNT, NEW(KeyHunt)); -REGISTER_GAMETYPE(_("Assault"),as,g_assault,ASSAULT,true,"","timelimit=20",_("Destroy obstacles to find and destroy the enemy power core before time runs out")); +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")); + } + METHOD(Assault, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "target_assault_roundend") + MapInfo_Map_supportedGametypes |= this.m_flags; + } + METHOD(Assault, m_isTwoBaseMode, bool()) + { + return true; + } +ENDCLASS(Assault) +REGISTER_GAMETYPE(ASSAULT, NEW(Assault)); #define g_assault IS_GAMETYPE(ASSAULT) -REGISTER_GAMETYPE(_("Onslaught"),ons,g_onslaught,ONSLAUGHT,true,"","pointlimit=1 timelimit=20",_("Capture control points to reach and destroy the enemy generator")); +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")); + } + METHOD(Onslaught, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "onslaught_generator") + MapInfo_Map_supportedGametypes |= this.m_flags; + } +ENDCLASS(Onslaught) +REGISTER_GAMETYPE(ONSLAUGHT, NEW(Onslaught)); -REGISTER_GAMETYPE(_("Nexball"),nb,g_nexball,NEXBALL,true,"","timelimit=20 pointlimit=5 leadlimit=0",_("Shoot and kick the ball into the enemies goal, keep your goal clean")); +#ifdef CSQC +void HUD_Mod_NexBall(vector pos, vector mySize); +#endif +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")); + } + METHOD(NexBall, m_generate_mapinfo, void(Gametype this, string v)) + { + if(substring(v, 0, 8) == "nexball_" || substring(v, 0, 4) == "ball") + MapInfo_Map_supportedGametypes |= this.m_flags; + } + METHOD(NexBall, m_isTwoBaseMode, bool()) + { + return true; + } +#ifdef CSQC + ATTRIB(NexBall, m_modicons, void(vector pos, vector mySize), HUD_Mod_NexBall); +#endif +ENDCLASS(NexBall) +REGISTER_GAMETYPE(NEXBALL, NEW(NexBall)); #define g_nexball IS_GAMETYPE(NEXBALL) -REGISTER_GAMETYPE(_("Freeze Tag"),ft,g_freezetag,FREEZETAG,true,"","timelimit=20 pointlimit=10 teams=2 leadlimit=0",_("Kill enemies to freeze them, stand next to teammates to revive them, freeze the most enemies to win")) -{ - if (!k) { - cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams")); - return true; +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 teammates to revive them, freeze the most enemies to win")); + } + METHOD(FreezeTag, m_parse_mapinfo, bool(string k, string v)) + { + if (!k) { + cvar_set("g_freezetag_teams", cvar_defstring("g_freezetag_teams")); + return true; + } + switch (k) { + case "teams": + cvar_set("g_freezetag_teams", v); + return true; + } + return false; } - switch (k) { - case "teams": - cvar_set("g_freezetag_teams", v); + METHOD(FreezeTag, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + if(spawnpoints >= 8 && diameter > 4096) return true; + return false; + } + METHOD(FreezeTag, m_setTeams, void(string sa)) + { + cvar_set("g_freezetag_teams", sa); } -} +#ifdef CSQC + ATTRIB(FreezeTag, m_modicons, void(vector pos, vector mySize), HUD_Mod_CA); +#endif +ENDCLASS(FreezeTag) +REGISTER_GAMETYPE(FREEZETAG, NEW(FreezeTag)); #define g_freezetag IS_GAMETYPE(FREEZETAG) -REGISTER_GAMETYPE(_("Keepaway"),ka,g_keepaway,KEEPAWAY,true,"","timelimit=20 pointlimit=30",_("Hold the ball to get points for kills")); +#ifdef CSQC +void HUD_Mod_Keepaway(vector pos, vector mySize); +#endif +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")); + } + METHOD(Keepaway, m_isAlwaysSupported, bool(Gametype this, int spawnpoints, float diameter)) + { + return true; + } +#ifdef CSQC + ATTRIB(Keepaway, m_modicons, void(vector pos, vector mySize), HUD_Mod_Keepaway); +#endif +ENDCLASS(Keepaway) +REGISTER_GAMETYPE(KEEPAWAY, NEW(Keepaway)); -REGISTER_GAMETYPE(_("Invasion"),inv,g_invasion,INVASION,false,"","pointlimit=50 teams=0",_("Survive against waves of monsters")) -{ - switch (k) { - case "teams": - cvar_set("g_invasion_teams", v); - return true; +CLASS(Invasion, Gametype) + INIT(Invasion) + { + this.gametype_init(this, _("Invasion"),"inv","g_invasion",false,"","pointlimit=50 teams=0",_("Survive against waves of monsters")); } -} + METHOD(Invasion, m_parse_mapinfo, bool(string k, string v)) + { + switch (k) { + case "teams": + cvar_set("g_invasion_teams", v); + return true; + } + return false; + } + METHOD(Invasion, m_generate_mapinfo, void(Gametype this, string v)) + { + if(v == "invasion_spawnpoint") + MapInfo_Map_supportedGametypes |= this.m_flags; + } +ENDCLASS(Invasion) +REGISTER_GAMETYPE(INVASION, NEW(Invasion)); const int MAPINFO_FEATURE_WEAPONS = 1; // not defined for instagib-only maps const int MAPINFO_FEATURE_VEHICLES = 2; @@ -197,30 +489,17 @@ const int MAPINFO_FLAG_NOAUTOMAPLIST = 8; // do not include when automaticall float MapInfo_count; -// info about a map that MapInfo loads -string MapInfo_Map_bspname; -string MapInfo_Map_title; -string MapInfo_Map_titlestring; // either bspname: title or just title, depending on whether bspname is redundant -string MapInfo_Map_description; -string MapInfo_Map_author; -string MapInfo_Map_clientstuff; // not in cache, only for map load -string MapInfo_Map_fog; // not in cache, only for map load -int MapInfo_Map_supportedGametypes; -int MapInfo_Map_supportedFeatures; -int MapInfo_Map_flags; -vector MapInfo_Map_mins; // these are '0 0 0' if not supported! -vector MapInfo_Map_maxs; // these are '0 0 0' if not specified! - // load MapInfo_count; generate mapinfo for maps that miss them, and clear the // cache; you need to call MapInfo_FilterGametype afterwards! void MapInfo_Enumerate(); // filter the info by game type mask (updates MapInfo_count) float MapInfo_progress; -float MapInfo_FilterGametype(float gametype, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator) +float MapInfo_FilterGametype(Gametype gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator) +float _MapInfo_FilterGametype(int gametypeFlags, float features, float pFlagsRequired, float pFlagsForbidden, float pAbortOnGenerate); // 1 on success, 0 on temporary failure (call it again next frame then; use MapInfo_progress as progress indicator) void MapInfo_FilterString(string sf); // filter _MapInfo_filtered (created by MapInfo_FilterGametype) with keyword int MapInfo_CurrentFeatures(); // retrieves currently required features from cvars -int MapInfo_CurrentGametype(); // retrieves current gametype from cvars +Gametype MapInfo_CurrentGametype(); // retrieves current gametype from cvars int MapInfo_ForbiddenFlags(); // retrieves current flags from cvars int MapInfo_RequiredFlags(); // retrieves current flags from cvars @@ -229,7 +508,7 @@ float MapInfo_Get_ByID(float i); // 1 on success, 0 on failure string MapInfo_BSPName_ByID(float i); // load info about a map by name into the MapInfo_Map_* globals -float MapInfo_Get_ByName(string s, float allowGenerate, float gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file +int MapInfo_Get_ByName(string s, float allowGenerate, Gametype gametypeToSet); // 1 on success, 0 on failure, 2 if it autogenerated a mapinfo file // look for a map by a prefix, returns the actual map name on success, string_null on failure or ambigous match string MapInfo_FindName_match; // the name of the map that was found @@ -242,23 +521,22 @@ float MapInfo_CheckMap(string s); // returns 0 if the map can't be played with t void MapInfo_LoadMap(string s, float reinit); // list all maps for the current game type -string MapInfo_ListAllowedMaps(float type, float pFlagsRequired, float pFlagsForbidden); +string MapInfo_ListAllowedMaps(Gametype type, float pFlagsRequired, float pFlagsForbidden); // list all allowed maps (for any game type) string MapInfo_ListAllAllowedMaps(float pFlagsRequired, float pFlagsForbidden); // gets a gametype from a string -string _MapInfo_GetDefaultEx(float t); -float _MapInfo_GetTeamPlayBool(float t); -Gametype MapInfo_Type(int t); -float MapInfo_Type_FromString(string t); -string MapInfo_Type_Description(float t); -string MapInfo_Type_ToString(float t); -string MapInfo_Type_ToText(float t); -void MapInfo_SwitchGameType(int t); +string _MapInfo_GetDefaultEx(Gametype t); +float _MapInfo_GetTeamPlayBool(Gametype t); +Gametype MapInfo_Type_FromString(string t); +string MapInfo_Type_Description(Gametype t); +string MapInfo_Type_ToString(Gametype t); +string MapInfo_Type_ToText(Gametype t); +void MapInfo_SwitchGameType(Gametype t); // to be called from worldspawn to set up cvars void MapInfo_LoadMapSettings(string s); -float MapInfo_LoadedGametype; // game type that was active during map load +Gametype MapInfo_LoadedGametype; // game type that was active during map load void MapInfo_Cache_Destroy(); // disable caching void MapInfo_Cache_Create(); // enable caching @@ -270,4 +548,3 @@ 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* +*" -#endif diff --git a/qcsrc/common/minigames/_mod.inc b/qcsrc/common/minigames/_mod.inc index 43ad69de59..4171f475cd 100644 --- a/qcsrc/common/minigames/_mod.inc +++ b/qcsrc/common/minigames/_mod.inc @@ -1,5 +1,7 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif #include #ifdef CSQC #include @@ -7,3 +9,5 @@ #ifdef SVQC #include #endif + +#include diff --git a/qcsrc/common/minigames/_mod.qh b/qcsrc/common/minigames/_mod.qh index 36f5de2f15..e0daf8f677 100644 --- a/qcsrc/common/minigames/_mod.qh +++ b/qcsrc/common/minigames/_mod.qh @@ -1,3 +1,13 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif + +#include diff --git a/qcsrc/common/minigames/cl_minigames.qc b/qcsrc/common/minigames/cl_minigames.qc index 3d3ef42dff..42f8c514e9 100644 --- a/qcsrc/common/minigames/cl_minigames.qc +++ b/qcsrc/common/minigames/cl_minigames.qc @@ -74,8 +74,8 @@ MINIGAME_SIMPLELINKED_ENTITIES void minigame_autoclean_entity(entity e) { - LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")\n"); - remove(e); + LOG_DEBUG("CL Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")"); + delete(e); } void HUD_MinigameMenu_CurrentButton(); @@ -121,7 +121,7 @@ void activate_minigame(entity minigame) if ( !minigame.descriptor || minigame.classname != "minigame" ) { - LOG_TRACE("Trying to activate unregistered minigame ",minigame.netname," in client\n"); + LOG_TRACE("Trying to activate unregistered minigame ",minigame.netname," in client"); return; } @@ -174,7 +174,7 @@ void minigame_read_owner(entity this) this.owner = find(this.owner,netname,owner_name); while ( this.owner && this.owner.classname != "minigame" ); if ( !this.owner ) - LOG_TRACE("Got a minigame entity without a minigame!\n"); + LOG_TRACE("Got a minigame entity without a minigame!"); } NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) { @@ -196,7 +196,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) this.entremove = minigame_entremove; this.descriptor = minigame_get_descriptor(ReadString_Raw()); if ( !this.descriptor ) - LOG_TRACE("Got a minigame without a client-side descriptor!\n"); + LOG_TRACE("Got a minigame without a client-side descriptor!"); else this.minigame_event = this.descriptor.minigame_event; } @@ -212,7 +212,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) minigame_read_owner(this); float ent = ReadLong(); this.minigame_playerslot = ent; - LOG_DEBUG("Player: ",entcs_GetName(ent-1),"\n"); + LOG_DEBUG("Player: ",entcs_GetName(ent-1)); activate = (ent == player_localnum+1 && this.owner && this.owner != active_minigame); @@ -226,6 +226,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) { minigame_self = this; activate_minigame(this.owner); + minigame_self = this; // set it again (needed before, but may also be reset) } } MINIGAME_SIMPLELINKED_ENTITIES @@ -237,7 +238,7 @@ NET_HANDLE(ENT_CLIENT_MINIGAME, bool isnew) { LOG_DEBUG("CL Reading entity: ",ftos(etof(this)), " classname:",this.classname," enttype:",ftos(this.enttype) ); - LOG_DEBUG(" sf:",ftos(sf)," netname:",this.netname,"\n\n"); + LOG_DEBUG(" sf:",ftos(sf)," netname:",this.netname); } return true; } diff --git a/qcsrc/common/minigames/cl_minigames.qh b/qcsrc/common/minigames/cl_minigames.qh index cc24d4bf0c..a0f6195d12 100644 --- a/qcsrc/common/minigames/cl_minigames.qh +++ b/qcsrc/common/minigames/cl_minigames.qh @@ -1,5 +1,4 @@ -#ifndef CL_MINIGAMES_H -#define CL_MINIGAMES_H +#pragma once // Get a square in the center of the avaliable area // \note macro to pass by reference pos and mySize @@ -85,12 +84,6 @@ entity active_minigame; // minigame_player representing this client entity minigame_self; -// Whethere there's an active minigame -float minigame_isactive() -{ - return active_minigame != NULL; -} - // Execute a minigame command #define minigame_cmd(...) minigame_cmd_workaround(0,__VA_ARGS__) void minigame_cmd_workaround(float dummy, string...cmdargc); @@ -101,7 +94,7 @@ void minigame_prompt(); float HUD_MinigameMenu_IsOpened(); void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger); -float HUD_Minigame_Showpanels(); + // Adds a game-specific entry to the menu void HUD_MinigameMenu_CustomEntry(entity parent, string message, string event_arg); @@ -128,5 +121,3 @@ REGISTRY_CHECK(Minigames) this.minigame_event = name##_client_event; \ } \ REGISTER_INIT(MINIGAME_##name) - -#endif diff --git a/qcsrc/common/minigames/cl_minigames_hud.qc b/qcsrc/common/minigames/cl_minigames_hud.qc index f64ed44c84..1a6d4ff522 100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qc +++ b/qcsrc/common/minigames/cl_minigames_hud.qc @@ -35,7 +35,7 @@ void HUD_MinigameBoard () if ( !hud_minigame ) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; @@ -61,7 +61,7 @@ void HUD_MinigameStatus () if ( !hud_minigame ) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; @@ -143,7 +143,7 @@ void HUD_MinigameMenu_EraseEntry ( entity e ) if ( HUD_MinigameMenu_activeitem == e ) HUD_MinigameMenu_activeitem = NULL; - remove(e); + delete(e); } // Minigame menu options: create entry @@ -193,7 +193,7 @@ bool HUD_MinigameMenu_Click_ExpandCollapse(entity this) if ( e.flags & 2 ) HUD_MinigameMenu_Click(e); this.list_next = e.list_next; - remove(e); + delete(e); } if ( this.list_next ) this.list_next.list_prev = this; @@ -351,7 +351,7 @@ void HUD_MinigameMenu_Close(entity this, entity actor, entity trigger) for ( e = HUD_MinigameMenu_entries; e != NULL; e = p ) { p = e.list_next; - remove(e); + delete(e); } HUD_MinigameMenu_entries = NULL; HUD_MinigameMenu_last_entry = NULL; @@ -429,7 +429,7 @@ void HUD_MinigameMenu_MouseInput() { panel = HUD_PANEL(MINIGAME_MENU); - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); if(panel_bg_padding) { @@ -475,10 +475,10 @@ void HUD_MinigameMenu () if ( !HUD_MinigameMenu_IsOpened() ) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); HUD_Scale_Disable(); - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); if(panel_bg_padding) { @@ -545,7 +545,7 @@ void HUD_MinigameHelp() if ( !help_message ) return; - HUD_Panel_UpdateCvars(); + HUD_Panel_LoadCvars(); vector pos, mySize; @@ -575,7 +575,7 @@ float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary { mousepos_x = nPrimary; mousepos_y = nSecondary; - if ( minigame_isactive() && HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) ) + if ( active_minigame && HUD_mouse_over(HUD_PANEL(MINIGAME_BOARD)) ) active_minigame.minigame_event(active_minigame,"mouse_moved",mousepos); return true; @@ -598,16 +598,16 @@ float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary } // allow some binds - string con_keys; - con_keys = findkeysforcommand("toggleconsole", 0); + string con_keys = findkeysforcommand("toggleconsole", 0); int keys = tokenize(con_keys); // findkeysforcommand returns data for this - for (int i = 0; i < keys; ++i) + int i; + for (i = 0; i < keys; ++i) { if(nPrimary == stof(argv(i))) return false; } - if ( minigame_isactive() && ( bInputType == 0 || bInputType == 1 ) ) + if ( active_minigame && ( bInputType == 0 || bInputType == 1 ) ) { string device = ""; string action = bInputType == 0 ? "pressed" : "released"; @@ -682,21 +682,11 @@ void HUD_Minigame_Mouse() if( !HUD_MinigameMenu_IsOpened() || autocvar__hud_configure || mv_active ) return; - if(!autocvar_hud_cursormode) - { - mousepos = mousepos + getmousepos() * autocvar_menu_mouse_speed; - - mousepos_x = bound(0, mousepos_x, vid_conwidth); - mousepos_y = bound(0, mousepos_y, vid_conheight); - } + if (!autocvar_hud_cursormode) + update_mousepos(); if ( HUD_MinigameMenu_IsOpened() && HUD_mouse_over(HUD_PANEL(MINIGAME_MENU)) ) HUD_MinigameMenu_MouseInput(); draw_cursor_normal(mousepos, '1 1 1', panel_fg_alpha); } - -bool HUD_Minigame_Showpanels() -{ - return (HUD_MinigameMenu_IsOpened() && minigame_isactive()); -} diff --git a/qcsrc/common/minigames/cl_minigames_hud.qh b/qcsrc/common/minigames/cl_minigames_hud.qh index f170c590de..ef44ea025f 100644 --- a/qcsrc/common/minigames/cl_minigames_hud.qh +++ b/qcsrc/common/minigames/cl_minigames_hud.qh @@ -1,7 +1,4 @@ -#ifndef CL_MINIGAMES_HUD_H -#define CL_MINIGAMES_HUD_H +#pragma once float HUD_Minigame_InputEvent(float bInputType, float nPrimary, float nSecondary); void HUD_Minigame_Mouse(); - -#endif diff --git a/qcsrc/common/minigames/minigame/bd.qc b/qcsrc/common/minigames/minigame/bd.qc index db25c2959e..09fae55d80 100644 --- a/qcsrc/common/minigames/minigame/bd.qc +++ b/qcsrc/common/minigames/minigame/bd.qc @@ -1,3 +1,4 @@ +#include "bd.qh" REGISTER_MINIGAME(bd, "Bulldozer"); const int BD_TURN_MOVE = 0x0100; // player must move the bulldozer @@ -16,7 +17,7 @@ const int BD_TILE_SIZE = 20; const int BD_TEAMS = 1; -.vector bd_dir; +.int bd_dir; .int bd_moves; @@ -41,7 +42,14 @@ const int BD_TILE_BRICK7 = 10; const int BD_TILE_BRICK8 = 11; const int BD_TILE_LAST = 11; +const int BD_DIR_UP = 0; +const int BD_DIR_DN = 1; +const int BD_DIR_LF = 2; +const int BD_DIR_RT = 3; + +#ifdef SVQC string autocvar_sv_minigames_bulldozer_startlevel = "level1"; +#endif // find same game piece given its tile name entity bd_find_piece(entity minig, string tile, bool check_target) @@ -91,6 +99,44 @@ void bd_check_winner(entity minig) } } +vector bd_get_dir(int bdir) +{ + switch(bdir) + { + case BD_DIR_UP: return '0 1 0'; // up + default: + case BD_DIR_DN: return '0 -1 0'; // down + case BD_DIR_LF: return '-1 0 0'; // left + case BD_DIR_RT: return '1 0 0'; // right + } +} + +string bd_get_dir_name(int bdir) +{ + switch(bdir) + { + case BD_DIR_UP: return "u"; // up + default: + case BD_DIR_DN: return "d"; // down + case BD_DIR_LF: return "l"; // left + case BD_DIR_RT: return "r"; // right + } +} + +int bd_dir_fromname(string bdir) +{ + if(bdir == "up" || bdir == "u") + return BD_DIR_UP; // up + if(bdir == "down" || bdir == "dn" || bdir == "d") + return BD_DIR_DN; /// down + if(bdir == "left" || bdir == "lt" || bdir == "l") + return BD_DIR_LF; // left + if(bdir == "right" || bdir == "rt" || bdir == "r") + return BD_DIR_RT; // right + + return BD_DIR_DN; // down +} + bool bd_canfill(int ttype) { switch(ttype) @@ -110,14 +156,16 @@ bool bd_canfill(int ttype) bool bd_move_dozer(entity minigame, entity dozer) { - if(!dozer.bd_dir_x && !dozer.bd_dir_y) - return false; // nope! + //if(!dozer.bd_dir) + //return false; // nope! int myx = minigame_tile_letter(dozer.netname); int myy = minigame_tile_number(dozer.netname); - myx += dozer.bd_dir_x; - myy += dozer.bd_dir_y; + vector dir = bd_get_dir(dozer.bd_dir); + + myx += dir.x; + myy += dir.y; string newpos = minigame_tile_buildname(myx, myy); entity hit = bd_find_piece(minigame, newpos, false); @@ -143,8 +191,8 @@ bool bd_move_dozer(entity minigame, entity dozer) int tx = minigame_tile_letter(hit.netname); int ty = minigame_tile_number(hit.netname); - tx += dozer.bd_dir_x; - ty += dozer.bd_dir_y; + tx += dir.x; + ty += dir.y; testpos = minigame_tile_buildname(tx, ty); entity testhit = bd_find_piece(minigame, testpos, false); @@ -181,24 +229,15 @@ void bd_move(entity minigame, entity player, string dir) return; // should not happen... TODO: end match? } - int dxs = 0, dys = 0; string thedir = strtolower(dir); - if(thedir == "up" || thedir == "u") { dxs = 0; dys = 1; } - if(thedir == "down" || thedir == "dn" || thedir == "d") { dxs = 0; dys = -1; } - if(thedir == "left" || thedir == "lt" || thedir == "l") { dxs = -1; dys = 0; } - if(thedir == "right" || thedir == "rt" || thedir == "r") { dxs = 1; dys = 0; } - - int dx = bound(-1, dxs, 1); - int dy = bound(-1, dys, 1); + int bdir = bd_dir_fromname(thedir); int moved = 0; entity e = NULL; while ( ( e = findentity(e,owner,minigame) ) ) if ( e.classname == "minigame_board_piece" && e.bd_tiletype == BD_TILE_DOZER ) { - e.bd_dir_x = dx; - e.bd_dir_y = dy; - e.bd_dir_z = 0; + e.bd_dir = bdir; if(bd_move_dozer(minigame, e)) ++moved; @@ -230,19 +269,10 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st if(found_piece.bd_tiletype == BD_TILE_DOZER && thedir != "") { - int dxs = 0, dys = 0; string newdir = strtolower(thedir); - if(newdir == "up" || newdir == "u") { dxs = 0; dys = 1; } - if(newdir == "down" || newdir == "dn" || newdir == "d") { dxs = 0; dys = -1; } - if(newdir == "left" || newdir == "lt" || newdir == "l") { dxs = -1; dys = 0; } - if(newdir == "right" || newdir == "rt" || newdir == "r") { dxs = 1; dys = 0; } - - int dx = bound(-1, dxs, 1); - int dy = bound(-1, dys, 1); + int bdir = bd_dir_fromname(newdir); - found_piece.bd_dir_x = dx; - found_piece.bd_dir_y = dy; - found_piece.bd_dir_z = 0; + found_piece.bd_dir = bdir; minigame_server_sendflags(found_piece,MINIG_SF_UPDATE); // update anyway return; } @@ -259,7 +289,7 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st return; // how?! if(piece.netname) { strunzone(piece.netname); } - remove(piece); + delete(piece); minigame_server_sendflags(minigame,MINIG_SF_UPDATE); return; } @@ -268,7 +298,7 @@ void bd_editor_place(entity minigame, entity player, string pos, int thetile, st piece.team = 1; piece.netname = strzone(pos); piece.bd_tiletype = thetile; - piece.bd_dir = '0 -1 0'; + piece.bd_dir = 0; minigame_server_sendflags(piece,MINIG_SF_UPDATE); minigame_server_sendflags(minigame,MINIG_SF_UPDATE); @@ -313,12 +343,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); } - remove(targ); + delete(targ); } else if(piece && thetype == piece.bd_tiletype) { if(piece.netname) { strunzone(piece.netname); } - remove(piece); + delete(piece); } else return; @@ -393,7 +423,7 @@ void bd_setup_pieces(entity minigame) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } bd_load_level(minigame); @@ -464,7 +494,7 @@ string bd_save_piece(entity minigame, entity e) bd_string = strcat(bd_string, "\"", e.netname, "\" "); bd_string = strcat(bd_string, ftos(e.bd_tiletype), " "); - bd_string = strcat(bd_string, sprintf("\"%.9v\"", e.bd_dir)); + bd_string = strcat(bd_string, ftos(e.bd_dir)); return bd_string; } @@ -477,6 +507,16 @@ void bd_set_nextlevel(entity minigame, string s) minigame.bd_nextlevel = strzone(argv(2)); } +int bd_fix_dir(vector dir) +{ + if(dir.x == 0 && dir.y == 1) { return BD_DIR_UP; } // up + if(dir.x == 0 && dir.y == -1) { return BD_DIR_DN; } // down + if(dir.x == -1 && dir.y == 0) { return BD_DIR_LF; } // left + if(dir.x == 1 && dir.y == 0) { return BD_DIR_RT; } // right + + return BD_DIR_DN; // down if all else fails +} + entity bd_load_piece(entity minigame, string s) { // separate pieces between the ; symbols @@ -486,12 +526,12 @@ entity bd_load_piece(entity minigame, string s) entity e = msle_spawn(minigame,"minigame_board_piece"); e.team = 1; - e.bd_dir = '0 -1 0'; + e.bd_dir = 0; int argv_num = 0; e.netname = strzone(argv(argv_num)); ++argv_num; e.bd_tiletype = stof(argv(argv_num)); ++argv_num; - e.bd_dir = stov(argv(argv_num)); ++argv_num; + e.bd_dir = stoi(argv(argv_num)); ++argv_num; minigame_server_sendflags(e,MINIG_SF_ALL); @@ -631,7 +671,7 @@ int bd_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } if(minigame.bd_nextlevel) { strunzone(minigame.bd_nextlevel); } @@ -686,12 +726,7 @@ int bd_server_event(entity minigame, string event, ...) WriteByte(MSG_ENTITY,sent.bd_tiletype); - int dx = sent.bd_dir_x; - int dy = sent.bd_dir_y; - if(dx == -1) dx = 2; - if(dy == -1) dy = 2; - WriteByte(MSG_ENTITY,dx); - WriteByte(MSG_ENTITY,dy); + WriteByte(MSG_ENTITY,sent.bd_dir); } else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES)) WriteShort(MSG_ENTITY,sent.bd_moves); @@ -792,12 +827,17 @@ void bd_hud_board(vector pos, vector mySize) tile_pos = minigame_tile_pos(e.netname,BD_NUM_CNT,BD_LET_CNT); tile_pos = minigame_hud_denormalize(tile_pos,pos,mySize); - vector thedir = e.bd_dir; + int bdir = e.bd_dir; float theang = 0; - if(thedir_y == -1) { theang = M_PI; } - if(thedir_x == 1) { theang = M_PI/2; } - if(thedir_x == -1) { theang = M_PI*3/2; } + switch(bdir) + { + case BD_DIR_UP: theang = 0; break; + default: + case BD_DIR_DN: theang = M_PI; break; + case BD_DIR_LF: theang = M_PI * 3 / 2; break; + case BD_DIR_RT: theang = M_PI / 2; break; + } drawrotpic(tile_pos, theang, minigame_texture("bd/dozer"), tile_size, tile_size/2, '1 1 1', @@ -854,7 +894,7 @@ void bd_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void bd_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); @@ -985,14 +1025,15 @@ bool bd_change_dozer_angle(entity minigame) if(!dozer || dozer.bd_tiletype != BD_TILE_DOZER) return false; - string thedir = ""; - vector dir = dozer.bd_dir; - if(dir.x == 0 && dir.y == 0) { thedir = "r"; } - - if(dir.x == 0 && dir.y == 1) { thedir = "r"; } - if(dir.x == 0 && dir.y ==-1) { thedir = "l"; } - if(dir.x ==-1 && dir.y == 0) { thedir = "u"; } - if(dir.x == 1 && dir.y == 0) { thedir = "d"; } + switch(dozer.bd_dir) + { + case BD_DIR_UP: dozer.bd_dir = BD_DIR_LF; break; // up -> left + default: + case BD_DIR_DN: dozer.bd_dir = BD_DIR_RT; break; // down -> right + case BD_DIR_LF: dozer.bd_dir = BD_DIR_DN; break; // left -> down + case BD_DIR_RT: dozer.bd_dir = BD_DIR_UP; break; // right -> up + } + string thedir = bd_get_dir_name(dozer.bd_dir); bd_editor_make_move(minigame, thedir); return true; @@ -1128,15 +1169,7 @@ int bd_client_event(entity minigame, string event, ...) sent.bd_tiletype = ReadByte(); - int dx = ReadByte(); - int dy = ReadByte(); - - if(dx == 2) dx = -1; - if(dy == 2) dy = -1; - - sent.bd_dir_x = dx; - sent.bd_dir_y = dy; - sent.bd_dir_z = 0; + sent.bd_dir = ReadByte(); } } else if(sent.classname == "minigame_player" && (sf & BD_SF_PLAYERMOVES)) diff --git a/qcsrc/common/minigames/minigame/bd.qh b/qcsrc/common/minigames/minigame/bd.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/bd.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/c4.qc b/qcsrc/common/minigames/minigame/c4.qc index 3c2beacb6f..2a6cb3d475 100644 --- a/qcsrc/common/minigames/minigame/c4.qc +++ b/qcsrc/common/minigames/minigame/c4.qc @@ -1,3 +1,4 @@ +#include "c4.qh" REGISTER_MINIGAME(c4, "Connect Four"); const float C4_TURN_PLACE = 0x0100; // player has to place a piece on the board @@ -201,7 +202,7 @@ int c4_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } return false; } @@ -322,7 +323,7 @@ void c4_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void c4_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); diff --git a/qcsrc/common/minigames/minigame/c4.qh b/qcsrc/common/minigames/minigame/c4.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/c4.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/nmm.qc b/qcsrc/common/minigames/minigame/nmm.qc index 8f8e3da2ea..4184c26382 100644 --- a/qcsrc/common/minigames/minigame/nmm.qc +++ b/qcsrc/common/minigames/minigame/nmm.qc @@ -1,3 +1,4 @@ +#include "nmm.qh" REGISTER_MINIGAME(nmm, "Nine Men's Morris"); const int NMM_TURN_PLACE = 0x0100; // player has to place a piece on the board @@ -123,7 +124,7 @@ void nmm_kill_tiles(entity minig) strunzone(e.netname); strunzone(e.nmm_tile_hmill); strunzone(e.nmm_tile_vmill); - remove(e); + delete(e); } } @@ -369,7 +370,7 @@ int nmm_server_event(entity minigame, string event, ...) minigame.SendFlags |= MINIG_SF_UPDATE; } else - LOG_TRACE("Invalid move: ",...(2,string),"\n"); + LOG_TRACE("Invalid move: ", ...(2, string)); return 1; } } @@ -496,9 +497,8 @@ void nmm_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void nmm_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; - ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); pos_y += ts_y; diff --git a/qcsrc/common/minigames/minigame/nmm.qh b/qcsrc/common/minigames/minigame/nmm.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/nmm.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/pong.qc b/qcsrc/common/minigames/minigame/pong.qc index 602cad22d3..dd57fb5c66 100644 --- a/qcsrc/common/minigames/minigame/pong.qc +++ b/qcsrc/common/minigames/minigame/pong.qc @@ -1,3 +1,4 @@ +#include "pong.qh" REGISTER_MINIGAME(pong, "Pong"); // minigame flags @@ -442,8 +443,8 @@ int pong_server_event(entity minigame, string event, ...) paddle.realowner.classname == "pong_ai" ) { minigame.pong_paddles[i] = NULL; - remove(paddle.realowner); - remove(paddle); + delete(paddle.realowner); + delete(paddle); return true; } } @@ -542,7 +543,7 @@ void pong_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void pong_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); diff --git a/qcsrc/common/minigames/minigame/pong.qh b/qcsrc/common/minigames/minigame/pong.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/pong.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/pp.qc b/qcsrc/common/minigames/minigame/pp.qc index 7d4048d61b..4ae9fb06b2 100644 --- a/qcsrc/common/minigames/minigame/pp.qc +++ b/qcsrc/common/minigames/minigame/pp.qc @@ -1,3 +1,4 @@ +#include "pp.qh" REGISTER_MINIGAME(pp, "Push-Pull"); const int PP_TURN_PLACE = 0x0100; // player has to place a piece on the board @@ -121,7 +122,7 @@ void pp_move(entity minigame, entity player, string pos ) if(existing) { if(existing.netname) { strunzone(existing.netname); } - remove(existing); + delete(existing); } entity piece = msle_spawn(minigame,"minigame_board_piece"); @@ -186,7 +187,7 @@ void pp_next_match(entity minigame, entity player) entity e = NULL; while ( ( e = findentity(e,owner,minigame) ) ) if ( e.classname == "minigame_board_piece" ) - remove(e); + delete(e); minigame.pp_team1_score = 0; minigame.pp_team2_score = 0; @@ -215,7 +216,7 @@ int pp_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } return false; } @@ -270,8 +271,7 @@ int pp_server_event(entity minigame, string event, ...) string pp_curr_pos; // identifier of the tile under the mouse vector pp_boardpos; // HUD board position -vector pp_boardsize;// HUD board size -.int pp_checkwin; // Used to optimize checks to display a win +vector pp_boardsize; // HUD board size // Required function, draw the game board void pp_hud_board(vector pos, vector mySize) @@ -374,7 +374,7 @@ void pp_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void pp_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); diff --git a/qcsrc/common/minigames/minigame/pp.qh b/qcsrc/common/minigames/minigame/pp.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/pp.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/ps.qc b/qcsrc/common/minigames/minigame/ps.qc index 2a612c599a..cd5c001e7a 100644 --- a/qcsrc/common/minigames/minigame/ps.qc +++ b/qcsrc/common/minigames/minigame/ps.qc @@ -1,3 +1,4 @@ +#include "ps.qh" REGISTER_MINIGAME(ps, "Peg Solitaire"); const float PS_TURN_MOVE = 0x0100; // player has to click on a piece on the board @@ -139,7 +140,7 @@ bool ps_move_piece(entity minigame, entity piece, string pos, int leti, int numb return false; if(middle.netname) { strunzone(middle.netname); } - remove(middle); + delete(middle); if(piece.netname) { strunzone(piece.netname); } piece.netname = strzone(pos); @@ -232,7 +233,7 @@ int ps_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } return false; } @@ -429,7 +430,7 @@ void ps_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void ps_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); diff --git a/qcsrc/common/minigames/minigame/ps.qh b/qcsrc/common/minigames/minigame/ps.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/ps.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/snake.qc b/qcsrc/common/minigames/minigame/snake.qc index 617ed957ca..3aedeb0163 100644 --- a/qcsrc/common/minigames/minigame/snake.qc +++ b/qcsrc/common/minigames/minigame/snake.qc @@ -1,3 +1,4 @@ +#include "snake.qh" REGISTER_MINIGAME(snake, "Snake"); // SNAAAAKE const float SNAKE_TURN_MOVE = 0x0100; // the snake is moving, player must control it @@ -20,7 +21,9 @@ bool autocvar_sv_minigames_snake_wrap = false; float autocvar_sv_minigames_snake_delay_initial = 0.7; float autocvar_sv_minigames_snake_delay_multiplier = 50; float autocvar_sv_minigames_snake_delay_min = 0.1; +#ifdef SVQC int autocvar_sv_minigames_snake_lives = 3; +#endif .int snake_score; @@ -94,7 +97,7 @@ void snake_new_mouse(entity minigame) { string pos = minigame_tile_buildname(i, j); if(!snake_find_piece(minigame, pos)) - RandomSelection_Add(NULL, 0, pos, 1, 1); + RandomSelection_AddString(pos, 1, 1); } entity piece = msle_spawn(minigame,"minigame_board_piece"); @@ -174,7 +177,7 @@ void minigame_setup_snake(entity minigame, int pteam) { string pos = minigame_tile_buildname(i, j); if(!snake_find_piece(minigame, pos)) - RandomSelection_Add(NULL, 0, pos, 1, 1); + RandomSelection_AddString(pos, 1, 1); } entity piece = msle_spawn(minigame,"minigame_board_piece"); @@ -287,7 +290,7 @@ void snake_eat_team(entity minigame, int pteam) if ( e.classname == "minigame_board_piece" && e.cnt && e.team == pteam ) { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } if(minigame.snake_lives[pteam] <= 0) @@ -354,7 +357,7 @@ void snake_move_head(entity minigame, entity head) if(ate_mouse) { if(hit.netname) { strunzone(hit.netname); } - remove(hit); + delete(hit); snake_new_mouse(minigame); } @@ -425,7 +428,7 @@ int snake_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } return false; } @@ -673,17 +676,16 @@ void snake_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void snake_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); - vector ts; - ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, + HUD_Panel_DrawBg(); + vector ts = minigame_drawstring_wrapped(mySize.x, pos, active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); - ts_y += hud_fontsize_y; - pos_y += ts_y; - mySize_y -= ts_y; + ts.y += hud_fontsize.y; + pos.y += ts.y; + mySize.y -= ts.y; vector player_fontsize = hud_fontsize * 1.75; - ts_y = ( mySize_y - SNAKE_TEAMS*player_fontsize_y ) / SNAKE_TEAMS; - ts_x = mySize_x; + ts.y = player_fontsize.y + (mySize.y - SNAKE_TEAMS * player_fontsize.y) / SNAKE_TEAMS; + ts.x = mySize_x; vector mypos; entity e; @@ -691,22 +693,26 @@ void snake_hud_status(vector pos, vector mySize) { if ( e.classname == "minigame_player" ) { - mypos = pos; - mypos_y += (e.team-1) * (player_fontsize_y + ts_y); + mypos = pos + eY * (e.team - 1) * ts.y; - drawfill(mypos, ts, snake_teamcolor(e.team), 0.25, DRAWFLAG_ADDITIVE); + if (e == minigame_self) + { + const vector hl_size = '1 1 0'; + drawfill(mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE); + drawborderlines(hl_size.x, mypos + hl_size, ts - 2 * hl_size, snake_teamcolor(e.team), panel_fg_alpha, DRAWFLAG_NORMAL); + } + else + drawfill(mypos, ts, snake_teamcolor(e.team), 0.25 * panel_fg_alpha, DRAWFLAG_ADDITIVE); - minigame_drawcolorcodedstring_trunc(mySize_x,mypos, - entcs_GetName(e.minigame_playerslot-1), + minigame_drawcolorcodedstring_trunc(mySize.x - hud_fontsize.x * 0.5, mypos + eX * hud_fontsize.x * 0.25, + entcs_GetName(e.minigame_playerslot - 1), player_fontsize, panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring(mypos+eY*player_fontsize_y,ftos(e.snake_score),'48 48 0' * (SNAKE_TEAMS * 0.1), - '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawstring(mypos+(eY*player_fontsize_y) + (eX*player_fontsize_x),strcat("1UP: ", ftos(active_minigame.snake_lives[e.team])),'48 48 0' * (SNAKE_TEAMS * 0.1), - '1 0.44 0.54', panel_fg_alpha, DRAWFLAG_NORMAL); - - if ( e == minigame_self ) - drawborderlines(1, mypos, ts, snake_teamcolor(e.team), 1, DRAWFLAG_NORMAL); + mypos.y += player_fontsize.y; + drawstring_aspect(mypos, ftos(e.snake_score), ts - eY * player_fontsize.y - eX * ts.x * (3 / 4), + '0.7 0.84 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawstring_aspect(mypos + eX * ts.x * (1 / 4), strcat("1UP: ", ftos(active_minigame.snake_lives[e.team])), ts - eY * player_fontsize.y - eX * ts.x * (1 / 4), + '1 0.44 0.54', panel_fg_alpha, DRAWFLAG_NORMAL); } } } diff --git a/qcsrc/common/minigames/minigame/snake.qh b/qcsrc/common/minigames/minigame/snake.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/snake.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigame/ttt.qc b/qcsrc/common/minigames/minigame/ttt.qc index b5248467f5..c5a658054f 100644 --- a/qcsrc/common/minigames/minigame/ttt.qc +++ b/qcsrc/common/minigames/minigame/ttt.qc @@ -1,3 +1,4 @@ +#include "ttt.qh" REGISTER_MINIGAME(ttt, "Tic Tac Toe"); const int TTT_TURN_PLACE = 0x0100; // player has to place a piece on the board @@ -123,7 +124,7 @@ void ttt_next_match(entity minigame, entity player) entity e = NULL; while ( ( e = findentity(e,owner,minigame) ) ) if ( e.classname == "minigame_board_piece" ) - remove(e); + delete(e); } } @@ -147,7 +148,7 @@ int ttt_server_event(entity minigame, string event, ...) if(e.classname == "minigame_board_piece") { if(e.netname) { strunzone(e.netname); } - remove(e); + delete(e); } return false; } @@ -272,7 +273,7 @@ void ttt_hud_board(vector pos, vector mySize) // Required function, draw the game status panel void ttt_hud_status(vector pos, vector mySize) { - HUD_Panel_DrawBg(1); + HUD_Panel_DrawBg(); vector ts; ts = minigame_drawstring_wrapped(mySize_x,pos,active_minigame.descriptor.message, hud_fontsize * 2, '0.25 0.47 0.72', panel_fg_alpha, DRAWFLAG_NORMAL,0.5); @@ -417,11 +418,11 @@ int ttt_ai_random(int piecemask) for ( int i = 0; i < 9; i++ ) { if ( piecemask & f ) - RandomSelection_Add(NULL, f, string_null, 1, 1); + RandomSelection_AddFloat(f, 1, 1); f <<= 1; } - LOG_TRACE(sprintf("TTT AI: selected %x from %x\n", + LOG_TRACE(sprintf("TTT AI: selected %x from %x", RandomSelection_chosen_float, piecemask) ); return RandomSelection_chosen_float; } @@ -439,7 +440,7 @@ int ttt_ai_block3 ( int piecemask, int piecemask_free ) r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B3,TTT_AI_POSFLAG_C3); r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A1,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C3); r |= ttt_ai_1of3(piecemask,TTT_AI_POSFLAG_A3,TTT_AI_POSFLAG_B2,TTT_AI_POSFLAG_C1); - LOG_TRACE(sprintf("TTT AI: possible 3 in a rows in %x: %x (%x)\n",piecemask,r, r&piecemask_free)); + LOG_TRACE(sprintf("TTT AI: possible 3 in a rows in %x: %x (%x)",piecemask,r, r&piecemask_free)); r &= piecemask_free; return ttt_ai_random(r); } @@ -452,15 +453,15 @@ string ttt_ai_choose_simple(int piecemask_self, int piecemask_opponent, int piec { int move = 0; - LOG_TRACE("TTT AI: checking winning move\n"); + LOG_TRACE("TTT AI: checking winning move"); if (( move = ttt_ai_block3(piecemask_self,piecemask_free) )) return ttt_ai_piece_flag2pos(move); // place winning move - LOG_TRACE("TTT AI: checking opponent's winning move\n"); + LOG_TRACE("TTT AI: checking opponent's winning move"); if (( move = ttt_ai_block3(piecemask_opponent,piecemask_free) )) return ttt_ai_piece_flag2pos(move); // block opponent - LOG_TRACE("TTT AI: random move\n"); + LOG_TRACE("TTT AI: random move"); return ttt_ai_piece_flag2pos(ttt_ai_random(piecemask_free)); } @@ -510,12 +511,12 @@ void ttt_aimove(entity minigame) } // TODO multiple AI difficulties - LOG_TRACE(sprintf("TTT AI: self: %x opponent: %x free: %x\n", + LOG_TRACE(sprintf("TTT AI: self: %x opponent: %x free: %x", piecemask_self, piecemask_opponent, piecemask_free)); pos = ttt_ai_choose_simple(piecemask_self, piecemask_opponent, piecemask_free); - LOG_TRACE("TTT AI: chosen move: ",pos,"\n\n"); + LOG_TRACE("TTT AI: chosen move: ", pos); if ( !pos ) - LOG_TRACE("Tic Tac Toe AI has derped!\n"); + LOG_TRACE("Tic Tac Toe AI has derped!"); else ttt_move(minigame,aiplayer,pos); } diff --git a/qcsrc/common/minigames/minigame/ttt.qh b/qcsrc/common/minigames/minigame/ttt.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/minigames/minigame/ttt.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/minigames/minigames.qh b/qcsrc/common/minigames/minigames.qh index 7425149f38..284001a0a2 100644 --- a/qcsrc/common/minigames/minigames.qh +++ b/qcsrc/common/minigames/minigames.qh @@ -1,5 +1,4 @@ -#ifndef MINIGAMES_H -#define MINIGAMES_H +#pragma once // previous node in a doubly linked list .entity list_prev; @@ -121,5 +120,3 @@ entity msle_spawn(entity minigame_session, string class_name); int msle_id(string class_name); string msle_classname(int id); - -#endif diff --git a/qcsrc/common/minigames/sv_minigames.qc b/qcsrc/common/minigames/sv_minigames.qc index 689ce83d02..5f1d1280b9 100644 --- a/qcsrc/common/minigames/sv_minigames.qc +++ b/qcsrc/common/minigames/sv_minigames.qc @@ -1,3 +1,4 @@ +#include "sv_minigames.qh" #include "minigames.qh" void player_clear_minigame(entity player) @@ -5,9 +6,9 @@ void player_clear_minigame(entity player) player.active_minigame = NULL; player.minigame_players = NULL; if ( IS_PLAYER(player) ) - player.movetype = MOVETYPE_WALK; + set_movetype(player, MOVETYPE_WALK); else - player.movetype = MOVETYPE_FLY_WORLDONLY; + set_movetype(player, MOVETYPE_FLY_WORLDONLY); player.team_forced = 0; } @@ -27,7 +28,7 @@ void minigame_rmplayer(entity minigame_session, entity player) GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":", ftos(etof(player)),":",player.netname)); minigame_session.minigame_players = p.list_next; - remove ( p ); + delete ( p ); player_clear_minigame(player); } else @@ -40,7 +41,7 @@ void minigame_rmplayer(entity minigame_session, entity player) GameLogEcho(strcat(":minigame:part:",minigame_session.netname,":", ftos(etof(player)),":",player.netname)); p.list_next = e.list_next; - remove(e); + delete(e); player_clear_minigame(player); return; } @@ -112,11 +113,11 @@ void minigame_resend(entity minigame) } } -bool minigame_CheckSend(entity this) +bool minigame_CheckSend(entity this, entity client) { entity e; for ( e = this.owner.minigame_players; e != NULL; e = e.list_next ) - if ( e.minigame_players == other ) + if ( e.minigame_players == client ) return true; return false; } @@ -153,7 +154,7 @@ int minigame_addplayer(entity minigame_session, entity player) minigame_resend(minigame_session); } - else { remove(player_pointer); } + else { delete(player_pointer); } GameLogEcho(strcat(":minigame:join",(mgteam?"":"fail"),":",minigame_session.netname,":", ftos(etof(player)),":",player.netname)); @@ -176,7 +177,7 @@ entity start_minigame(entity player, string minigame ) GameLogEcho(strcat(":minigame:start:",minig.netname)); if ( ! minigame_addplayer(minig,player) ) { - LOG_TRACE("Minigame ",minig.netname," rejected the first player join!\n"); + LOG_TRACE("Minigame ",minig.netname," rejected the first player join!"); end_minigame(minig); return NULL; } @@ -235,8 +236,8 @@ void end_minigame(entity minigame_session) while( (e = findentity(e, owner, minigame_session)) ) if ( e.minigame_autoclean ) { - LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")\n"); - remove(e); + LOG_TRACE("SV Auto-cleaned: ",ftos(etof(e)), " (",e.classname,")"); + delete(e); } entity p; @@ -244,11 +245,11 @@ void end_minigame(entity minigame_session) { p = e.list_next; player_clear_minigame(e.minigame_players); - remove(e); + delete(e); } strunzone(minigame_session.netname); - remove(minigame_session); + delete(minigame_session); } void end_minigames() diff --git a/qcsrc/common/minigames/sv_minigames.qh b/qcsrc/common/minigames/sv_minigames.qh index 70c9bf8058..b5015a1f40 100644 --- a/qcsrc/common/minigames/sv_minigames.qh +++ b/qcsrc/common/minigames/sv_minigames.qh @@ -1,5 +1,4 @@ -#ifndef SV_MINIGAMES_H -#define SV_MINIGAMES_H +#pragma once /// Create a new minigame session /// \return minigame session entity @@ -24,7 +23,7 @@ void end_minigames(); // Only sends entities to players who joined the minigame // Use on customizeentityforclient for gameplay entities -bool minigame_CheckSend(entity this); +bool minigame_CheckSend(entity this, entity client); // Check for minigame impulses bool MinigameImpulse(entity this, int imp); @@ -59,5 +58,3 @@ REGISTRY_CHECK(Minigames) this.minigame_event = name##_server_event; \ } \ REGISTER_INIT(MINIGAME_##name) - -#endif diff --git a/qcsrc/common/models/all.qh b/qcsrc/common/models/all.qh index f3bfd64c75..ce98629411 100644 --- a/qcsrc/common/models/all.qh +++ b/qcsrc/common/models/all.qh @@ -1,5 +1,4 @@ -#ifndef MODELS_ALL_H -#define MODELS_ALL_H +#pragma once #include "model.qh" @@ -22,5 +21,3 @@ PRECACHE(Models) { MODEL(Null, "null"); #include "all.inc" - -#endif diff --git a/qcsrc/common/models/model.qh b/qcsrc/common/models/model.qh index 1c34a2547e..7a1e7d73c2 100644 --- a/qcsrc/common/models/model.qh +++ b/qcsrc/common/models/model.qh @@ -1,11 +1,10 @@ -#ifndef MODEL_H -#define MODEL_H +#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(), func_null) + ATTRIB(Model, m_id, int, 0); + ATTRIB(Model, model_str, string()); CONSTRUCTOR(Model, string() path) { CONSTRUCT(Model); @@ -16,12 +15,10 @@ CLASS(Model, Object) TC(Model, this); string s = this.model_str(); if (s != "" && s != "null" && !fexists(s)) { - LOG_WARNINGF("Missing model: \"%s\"\n", s); + LOG_WARNF("Missing model: \"%s\"", s); return; } - LOG_DEBUGF("precache_model(\"%s\")\n", s); + profile(sprintf("precache_model(\"%s\")", s)); precache_model(s); } ENDCLASS(Model) - -#endif diff --git a/qcsrc/common/monsters/_mod.inc b/qcsrc/common/monsters/_mod.inc index f9d80b3f4e..e7f1e9734d 100644 --- a/qcsrc/common/monsters/_mod.inc +++ b/qcsrc/common/monsters/_mod.inc @@ -1,4 +1,11 @@ // generated file; do not modify #include -#include -#include +#ifdef SVQC + #include +#endif +#ifdef SVQC + #include + #include +#endif + +#include diff --git a/qcsrc/common/monsters/_mod.qh b/qcsrc/common/monsters/_mod.qh index 48427f94f7..55204bd1a7 100644 --- a/qcsrc/common/monsters/_mod.qh +++ b/qcsrc/common/monsters/_mod.qh @@ -1,4 +1,10 @@ // generated file; do not modify #include -#include -#include +#ifdef SVQC + #include +#endif +#ifdef SVQC + #include +#endif + +#include diff --git a/qcsrc/common/monsters/all.qc b/qcsrc/common/monsters/all.qc index fa3f65193a..9dc09ca394 100644 --- a/qcsrc/common/monsters/all.qc +++ b/qcsrc/common/monsters/all.qc @@ -1,5 +1,4 @@ -#ifndef MONSTERS_ALL_C -#define MONSTERS_ALL_C +#include "all.qh" string M_Model(string m_mdl) { @@ -11,16 +10,3 @@ string M_Model(string m_mdl) return output; #endif } - -#include "all.qh" - -#define IMPLEMENTATION -#include "monster/_mod.inc" -#undef IMPLEMENTATION - -#ifdef SVQC -#include "spawn.qc" -#include "sv_monsters.qc" -#endif - -#endif diff --git a/qcsrc/common/monsters/all.qh b/qcsrc/common/monsters/all.qh index 1e23f32879..c9e5ad37ba 100644 --- a/qcsrc/common/monsters/all.qh +++ b/qcsrc/common/monsters/all.qh @@ -1,7 +1,4 @@ -#ifndef MONSTERS_ALL_H -#define MONSTERS_ALL_H - -#include "monster.qh" +#pragma once string M_Model(string m_mdl); @@ -14,9 +11,6 @@ const int MON_FIRST = 1; #define MON_LAST (Monsters_COUNT - 1) #define REGISTER_MONSTER(id, inst) REGISTER(Monsters, MON, id, monsterid, inst) -REGISTER_MONSTER(Null, NEW(Monster)); - - -#include "monster/_mod.inc" +#include "monster.qh" -#endif +REGISTER_MONSTER(Null, NEW(Monster)); diff --git a/qcsrc/common/monsters/monster.qh b/qcsrc/common/monsters/monster.qh index de24caa01a..ab644988aa 100644 --- a/qcsrc/common/monsters/monster.qh +++ b/qcsrc/common/monsters/monster.qh @@ -1,34 +1,18 @@ -#ifndef MONSTER_H -#define MONSTER_H - -#ifdef SVQC -#include "sv_monsters.qh" -#include -#include -#include -#include -#include -#include -#include -#endif - -#ifndef MENUQC -#include "../animdecide.qh" -#include "../anim.qh" -vector animfixfps(entity e, vector a, vector b); -#endif +#pragma once // special spawn flags -const int MONSTER_RESPAWN_DEATHPOINT = 16; // re-spawn where we died -const int MONSTER_TYPE_FLY = 32; -const int MONSTER_TYPE_SWIM = 64; -const int MONSTER_SIZE_BROKEN = 128; // TODO: remove when bad models are replaced -const int MON_FLAG_SUPERMONSTER = 256; // incredibly powerful monster -const int MON_FLAG_RANGED = 512; // monster shoots projectiles -const int MON_FLAG_MELEE = 1024; -const int MON_FLAG_CRUSH = 2048; // monster can be stomped in special modes -const int MON_FLAG_RIDE = 4096; // monster can be ridden in special modes -const int MONSTER_SIZE_QUAKE = 8192; +const int MONSTER_RESPAWN_DEATHPOINT = BIT(4); // re-spawn where we died +const int MONSTER_TYPE_FLY = BIT(5); +const int MONSTER_TYPE_SWIM = BIT(6); +const int MONSTER_SIZE_BROKEN = BIT(7); // TODO: remove when bad models are replaced +const int MON_FLAG_SUPERMONSTER = BIT(8); // incredibly powerful monster +const int MON_FLAG_RANGED = BIT(9); // monster shoots projectiles +const int MON_FLAG_MELEE = BIT(10); +const int MON_FLAG_CRUSH = BIT(11); // monster can be stomped in special modes +const int MON_FLAG_RIDE = BIT(12); // monster can be ridden in special modes +const int MONSTER_SIZE_QUAKE = BIT(13); +const int MONSTER_TYPE_PASSIVE = BIT(14); // doesn't target or chase enemies +const int MONSTER_TYPE_UNDEAD = BIT(15); // monster is by most definitions a zombie (doesn't fully die unless gibbed) // entity properties of monsterinfo: .bool(int, entity actor, entity targ) monster_attackfunc; @@ -39,26 +23,23 @@ const int MONSTER_SIZE_QUAKE = 8192; .vector anim_melee1; .vector anim_melee2; .vector anim_melee3; -.vector anim_pain3; -.vector anim_pain4; -.vector anim_pain5; .vector anim_walk; .vector anim_spawn; CLASS(Monster, Object) - ATTRIB(Monster, monsterid, int, 0) + ATTRIB(Monster, monsterid, int, 0); /** attributes */ - ATTRIB(Monster, spawnflags, int, 0) + ATTRIB(Monster, spawnflags, int, 0); /** human readable name */ - ATTRIB(Monster, monster_name, string, "Monster") + ATTRIB(Monster, monster_name, string, "Monster"); /** short name */ - ATTRIB(Monster, netname, string, "") + ATTRIB(Monster, netname, string, ""); /** model */ - ATTRIB(Monster, m_model, entity, NULL) + ATTRIB(Monster, m_model, entity); /** hitbox size */ - ATTRIB(Monster, mins, vector, '-0 -0 -0') + ATTRIB(Monster, mins, vector, '-0 -0 -0'); /** hitbox size */ - ATTRIB(Monster, maxs, vector, '0 0 0') + ATTRIB(Monster, maxs, vector, '0 0 0'); /** (SERVER) setup monster data */ METHOD(Monster, mr_setup, bool(Monster this, entity actor)) { TC(Monster, this); return false; } @@ -75,4 +56,20 @@ CLASS(Monster, Object) ENDCLASS(Monster) + +#ifdef SVQC +#include "sv_monsters.qh" +#include +#include +#include +#include +#include +#include +#include +#endif + +#ifdef GAMEQC +#include "../animdecide.qh" +#include "../anim.qh" +vector animfixfps(entity e, vector a, vector b); #endif diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 44d7121fcd..748f9a9bd6 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -1,41 +1,4 @@ -#ifndef MAGE_H -#define MAGE_H - -#ifndef MENUQC -MODEL(MON_MAGE, M_Model("mage.dpm")); -#endif - -CLASS(Mage, Monster) - ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED); - ATTRIB(Mage, mins, vector, '-36 -36 -24'); - ATTRIB(Mage, maxs, vector, '36 36 50'); -#ifndef MENUQC - ATTRIB(Mage, m_model, Model, MDL_MON_MAGE); -#endif - ATTRIB(Mage, netname, string, "mage"); - ATTRIB(Mage, monster_name, string, _("Mage")); -ENDCLASS(Mage) - -REGISTER_MONSTER(MAGE, NEW(Mage)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include -#include - -CLASS(MageSpike, PortoLaunch) -/* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(MageSpike, impulse, int, 9); -/* refname */ ATTRIB(MageSpike, netname, string, "magespike"); -/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike")); -ENDCLASS(MageSpike) -REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); - -#endif - -#ifdef IMPLEMENTATION +#include "mage.qh" #ifdef SVQC @@ -49,7 +12,7 @@ METHOD(MageSpike, wr_think, void(MageSpike thiswep, entity actor, .entity weapon if (!IS_PLAYER(actor) || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { if (!actor.target_range) actor.target_range = autocvar_g_monsters_target_range; actor.enemy = Monster_FindTarget(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_MageSpike_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MageSpike_FIRE, CH_WEAPON_B, 0); if (!IS_PLAYER(actor)) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin); M_Mage_Attack_Spike(actor, w_shotdir); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); @@ -146,7 +109,7 @@ bool M_Mage_Defend_Heal_Check(entity this, entity targ) return false; } -void M_Mage_Attack_Spike_Explode(entity this) +void M_Mage_Attack_Spike_Explode(entity this, entity directhitentity) { this.event_damage = func_null; @@ -155,16 +118,16 @@ void M_Mage_Attack_Spike_Explode(entity this) 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), NULL, NULL, 0, DEATH_MONSTER_MAGE.m_id, other); + 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, directhitentity); - remove (this); + delete (this); } -void M_Mage_Attack_Spike_Touch(entity this) +void M_Mage_Attack_Spike_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); - M_Mage_Attack_Spike_Explode(this); + M_Mage_Attack_Spike_Explode(this, toucher); } .float wait; @@ -174,7 +137,7 @@ void M_Mage_Attack_Spike_Think(entity this) { if (time > this.ltime || (this.enemy && this.enemy.health <= 0) || this.owner.health <= 0) { this.projectiledeathtype |= HITTYPE_SPLASH; - M_Mage_Attack_Spike_Explode(this); + M_Mage_Attack_Spike_Explode(this, NULL); } float spd = vlen(this.velocity); @@ -199,7 +162,7 @@ void M_Mage_Attack_Spike_Think(entity this) // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P ) if ((autocvar_g_monster_mage_attack_spike_smart) && vdist(eorg - this.origin, >, autocvar_g_monster_mage_attack_spike_smart_mindist)) { - // Is it a better idea (shorter distance) to trace to the target itthis? + // Is it a better idea (shorter distance) to trace to the target itself? if ( vlen2(this.origin + olddir * this.wait) < vlen2(eorg - this.origin)) traceline(this.origin, this.origin + olddir * this.wait, false, this); else @@ -233,8 +196,10 @@ void M_Mage_Attack_Spike(entity this, vector dir) missile.ltime = time + 7; missile.nextthink = time; missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLYMISSILE; + set_movetype(missile, MOVETYPE_FLYMISSILE); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); setorigin(missile, this.origin + v_forward * 14 + '0 0 30' + v_right * -14); setsize(missile, '0 0 0', '0 0 0'); missile.velocity = dir * 400; @@ -249,52 +214,51 @@ void M_Mage_Attack_Spike(entity this, vector dir) void M_Mage_Defend_Heal(entity this) { - entity head; float washealed = false; - for(head = findradius(this.origin, (autocvar_g_monster_mage_heal_range)); head; head = head.chain) if(M_Mage_Defend_Heal_Check(this, head)) + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_mage_heal_range, M_Mage_Defend_Heal_Check(this, it), { washealed = true; string fx = ""; - if(IS_PLAYER(head)) + if(IS_PLAYER(it)) { switch(this.skin) { case 0: - if(head.health < autocvar_g_balance_health_regenstable) head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_health_regenstable); + 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); fx = EFFECT_HEALING.eent_eff_name; break; case 1: - if(head.ammo_cells) head.ammo_cells = bound(head.ammo_cells, head.ammo_cells + 1, g_pickup_cells_max); - if(head.ammo_plasma) head.ammo_plasma = bound(head.ammo_plasma, head.ammo_plasma + 1, g_pickup_plasma_max); - if(head.ammo_rockets) head.ammo_rockets = bound(head.ammo_rockets, head.ammo_rockets + 1, g_pickup_rockets_max); - if(head.ammo_shells) head.ammo_shells = bound(head.ammo_shells, head.ammo_shells + 2, g_pickup_shells_max); - if(head.ammo_nails) head.ammo_nails = bound(head.ammo_nails, head.ammo_nails + 5, g_pickup_nails_max); + 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); fx = "ammoregen_fx"; break; case 2: - if(head.armorvalue < autocvar_g_balance_armor_regenstable) + if(it.armorvalue < autocvar_g_balance_armor_regenstable) { - head.armorvalue = bound(0, head.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable); + it.armorvalue = bound(0, it.armorvalue + (autocvar_g_monster_mage_heal_allies), autocvar_g_balance_armor_regenstable); fx = "armorrepair_fx"; } break; case 3: - head.health = bound(0, head.health - ((head == this) ? (autocvar_g_monster_mage_heal_self) : (autocvar_g_monster_mage_heal_allies)), autocvar_g_balance_health_regenstable); + 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); fx = EFFECT_RAGE.eent_eff_name; break; } - Send_Effect_(fx, head.origin, '0 0 0', 1); + Send_Effect_(fx, it.origin, '0 0 0', 1); } else { - Send_Effect(EFFECT_HEALING, head.origin, '0 0 0', 1); - head.health = bound(0, head.health + (autocvar_g_monster_mage_heal_allies), head.max_health); - if(!(head.spawnflags & MONSTERFLAG_INVINCIBLE) && head.sprite) - WaypointSprite_UpdateHealth(head.sprite, head.health); + 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); + if(!(it.spawnflags & MONSTERFLAG_INVINCIBLE) && it.sprite) + WaypointSprite_UpdateHealth(it.sprite, it.health); } - } + }); if(washealed) { @@ -407,7 +371,7 @@ bool M_Mage_Attack(int attack_type, entity actor, entity targ) return false; } -spawnfunc(monster_mage) { Monster_Spawn(this, MON_MAGE.monsterid); } +spawnfunc(monster_mage) { Monster_Spawn(this, true, MON_MAGE.monsterid); } #endif // SVQC @@ -417,10 +381,9 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor)) TC(Mage, thismon); bool need_help = false; - FOREACH_ENTITY_FLOAT(iscreature, true, + FOREACH_CLIENT(IS_PLAYER(it) && it != actor, { - if(it != actor) - if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range)) + if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range)) if(M_Mage_Defend_Heal_Check(actor, it)) { need_help = true; @@ -428,6 +391,19 @@ METHOD(Mage, mr_think, bool(Mage thismon, entity actor)) } }); + if(!need_help) + { + IL_EACH(g_monsters, it != actor, + { + if(vdist(it.origin - actor.origin, <=, autocvar_g_monster_mage_heal_range)) + if(M_Mage_Defend_Heal_Check(actor, it)) + { + need_help = true; + break; + } + }); + } + if(actor.health < (autocvar_g_monster_mage_heal_minhealth) || need_help) if(time >= actor.attack_finished_single[0]) if(random() < 0.5) @@ -459,7 +435,7 @@ METHOD(Mage, mr_death, bool(Mage this, entity actor)) } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Mage, mr_anim, bool(Mage this, entity actor)) { TC(Mage, this); @@ -475,7 +451,7 @@ METHOD(Mage, mr_anim, bool(Mage this, entity actor)) #endif #ifdef SVQC .float speed; -spawnfunc(item_health_large); +spawnfunc(item_health_big); METHOD(Mage, mr_setup, bool(Mage this, entity actor)) { TC(Mage, this); @@ -485,7 +461,7 @@ METHOD(Mage, mr_setup, bool(Mage this, entity actor)) if(!actor.stopspeed) { actor.stopspeed = (autocvar_g_monster_mage_speed_stop); } if(!actor.damageforcescale) { actor.damageforcescale = (autocvar_g_monster_mage_damageforcescale); } - actor.monster_loot = spawnfunc_item_health_large; + actor.monster_loot = spawnfunc_item_health_big; actor.monster_attackfunc = M_Mage_Attack; return true; @@ -497,5 +473,3 @@ METHOD(Mage, mr_precache, bool(Mage this)) return true; } #endif - -#endif diff --git a/qcsrc/common/monsters/monster/mage.qh b/qcsrc/common/monsters/monster/mage.qh new file mode 100644 index 0000000000..d78ee5ecf6 --- /dev/null +++ b/qcsrc/common/monsters/monster/mage.qh @@ -0,0 +1,35 @@ +#pragma once + +#include "../all.qh" + +#ifdef GAMEQC +MODEL(MON_MAGE, M_Model("mage.dpm")); +#endif + +CLASS(Mage, Monster) + ATTRIB(Mage, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED); + ATTRIB(Mage, mins, vector, '-36 -36 -24'); + ATTRIB(Mage, maxs, vector, '36 36 50'); +#ifdef GAMEQC + ATTRIB(Mage, m_model, Model, MDL_MON_MAGE); +#endif + ATTRIB(Mage, netname, string, "mage"); + ATTRIB(Mage, monster_name, string, _("Mage")); +ENDCLASS(Mage) + +REGISTER_MONSTER(MAGE, NEW(Mage)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include +#include + +CLASS(MageSpike, PortoLaunch) +/* flags */ ATTRIB(MageSpike, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(MageSpike, impulse, int, 9); +/* refname */ ATTRIB(MageSpike, netname, string, "magespike"); +/* wepname */ ATTRIB(MageSpike, m_name, string, _("Mage spike")); +ENDCLASS(MageSpike) +REGISTER_WEAPON(MAGE_SPIKE, NEW(MageSpike)); diff --git a/qcsrc/common/monsters/monster/shambler.qc b/qcsrc/common/monsters/monster/shambler.qc index 7a11b139e4..aae268666f 100644 --- a/qcsrc/common/monsters/monster/shambler.qc +++ b/qcsrc/common/monsters/monster/shambler.qc @@ -1,30 +1,4 @@ -#ifndef SHAMBLER_H -#define SHAMBLER_H - -#ifndef MENUQC -MODEL(MON_SHAMBLER, M_Model("shambler.mdl")); -#endif - -CLASS(Shambler, Monster) - ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED); - ATTRIB(Shambler, mins, vector, '-41 -41 -31'); - ATTRIB(Shambler, maxs, vector, '41 41 65'); -#ifndef MENUQC - ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER); -#endif - ATTRIB(Shambler, netname, string, "shambler"); - ATTRIB(Shambler, monster_name, string, _("Shambler")); -ENDCLASS(Shambler) - -REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#endif - -#ifdef IMPLEMENTATION +#include "shambler.qh" #ifdef SVQC float autocvar_g_monster_shambler_health; @@ -78,28 +52,27 @@ void M_Shambler_Attack_Swing(entity this) #include -void M_Shambler_Attack_Lightning_Explode(entity this) +void M_Shambler_Attack_Lightning_Explode(entity this, entity directhitentity) { - entity head; - sound(this, CH_SHOTS, SND_ELECTRO_IMPACT, VOL_BASE, ATTEN_NORM); Send_Effect(EFFECT_ELECTRO_IMPACT, this.origin, '0 0 0', 1); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.velocity = '0 0 0'; - if(this.movetype == MOVETYPE_NONE) + if(this.move_movetype == MOVETYPE_NONE) this.velocity = this.oldvelocity; - RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, other); + RadiusDamage (this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_damage), (autocvar_g_monster_shambler_attack_lightning_radius), + NULL, NULL, (autocvar_g_monster_shambler_attack_lightning_force), this.projectiledeathtype, directhitentity); - for(head = findradius(this.origin, (autocvar_g_monster_shambler_attack_lightning_radius_zap)); head; head = head.chain) if(head != this.realowner) if(head.takedamage) + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_shambler_attack_lightning_radius_zap, it != this.realowner && it.takedamage, { - te_csqc_lightningarc(this.origin, head.origin); - Damage(head, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, head.origin, '0 0 0'); - } + te_csqc_lightningarc(this.origin, it.origin); + Damage(it, this, this.realowner, (autocvar_g_monster_shambler_attack_lightning_damage_zap) * MONSTER_SKILLMOD(this), DEATH_MONSTER_SHAMBLER_ZAP.m_id, it.origin, '0 0 0'); + }); setthink(this, SUB_Remove); this.nextthink = time + 0.2; @@ -107,7 +80,7 @@ void M_Shambler_Attack_Lightning_Explode(entity this) void M_Shambler_Attack_Lightning_Explode_use(entity this, entity actor, entity trigger) { - M_Shambler_Attack_Lightning_Explode(this); + M_Shambler_Attack_Lightning_Explode(this, trigger); } void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -124,11 +97,11 @@ void M_Shambler_Attack_Lightning_Damage(entity this, entity inflictor, entity at W_PrepareExplosionByDamage(this, attacker, adaptor_think2use); } -void M_Shambler_Attack_Lightning_Touch(entity this) +void M_Shambler_Attack_Lightning_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); - this.use(this, NULL, NULL); + this.use(this, NULL, toucher); } void M_Shambler_Attack_Lightning_Think(entity this) @@ -136,8 +109,7 @@ void M_Shambler_Attack_Lightning_Think(entity this) this.nextthink = time; if (time > this.cnt) { - other = NULL; - M_Shambler_Attack_Lightning_Explode(this); + M_Shambler_Attack_Lightning_Explode(this, NULL); return; } } @@ -152,7 +124,7 @@ void M_Shambler_Attack_Lightning(entity this) gren.owner = gren.realowner = this; gren.bot_dodge = true; gren.bot_dodgerating = (autocvar_g_monster_shambler_attack_lightning_damage); - gren.movetype = MOVETYPE_BOUNCE; + set_movetype(gren, MOVETYPE_BOUNCE); PROJECTILE_MAKETRIGGER(gren); gren.projectiledeathtype = DEATH_MONSTER_SHAMBLER_ZAP.m_id; setorigin(gren, CENTER_OR_VIEWOFS(this)); @@ -170,11 +142,14 @@ void M_Shambler_Attack_Lightning(entity this) gren.damageforcescale = 0; gren.event_damage = M_Shambler_Attack_Lightning_Damage; gren.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, gren); gren.missile_flags = MIF_SPLASH | MIF_ARC; W_SetupProjVelocity_Explicit(gren, v_forward, v_up, (autocvar_g_monster_shambler_attack_lightning_speed), (autocvar_g_monster_shambler_attack_lightning_speed_up), 0, 0, false); gren.angles = vectoangles (gren.velocity); gren.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, gren); + IL_PUSH(g_bot_dodge, gren); CSQCProjectile(gren, true, PROJECTILE_SHAMBLER_LIGHTNING, true); } @@ -226,7 +201,7 @@ bool M_Shambler_Attack(int attack_type, entity actor, entity targ) return false; } -spawnfunc(monster_shambler) { Monster_Spawn(this, MON_SHAMBLER.monsterid); } +spawnfunc(monster_shambler) { Monster_Spawn(this, true, MON_SHAMBLER.monsterid); } #endif // SVQC #ifdef SVQC @@ -251,7 +226,7 @@ METHOD(Shambler, mr_death, bool(Shambler this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Shambler, mr_anim, bool(Shambler this, entity actor)) { TC(Shambler, this); @@ -298,5 +273,3 @@ METHOD(Shambler, mr_precache, bool(Shambler this)) return true; } #endif - -#endif diff --git a/qcsrc/common/monsters/monster/shambler.qh b/qcsrc/common/monsters/monster/shambler.qh new file mode 100644 index 0000000000..f7a3ce3a94 --- /dev/null +++ b/qcsrc/common/monsters/monster/shambler.qh @@ -0,0 +1,24 @@ +#pragma once + +#include "../all.qh" + +#ifdef GAMEQC +MODEL(MON_SHAMBLER, M_Model("shambler.mdl")); +#endif + +CLASS(Shambler, Monster) + ATTRIB(Shambler, spawnflags, int, MONSTER_SIZE_BROKEN | MON_FLAG_SUPERMONSTER | MON_FLAG_MELEE | MON_FLAG_RANGED); + ATTRIB(Shambler, mins, vector, '-41 -41 -31'); + ATTRIB(Shambler, maxs, vector, '41 41 65'); +#ifdef GAMEQC + ATTRIB(Shambler, m_model, Model, MDL_MON_SHAMBLER); +#endif + ATTRIB(Shambler, netname, string, "shambler"); + ATTRIB(Shambler, monster_name, string, _("Shambler")); +ENDCLASS(Shambler) + +REGISTER_MONSTER(SHAMBLER, NEW(Shambler)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} diff --git a/qcsrc/common/monsters/monster/spider.qc b/qcsrc/common/monsters/monster/spider.qc index 9338657082..a122865321 100644 --- a/qcsrc/common/monsters/monster/spider.qc +++ b/qcsrc/common/monsters/monster/spider.qc @@ -1,40 +1,4 @@ -#ifndef SPIDER_H -#define SPIDER_H - -#ifndef MENUQC -MODEL(MON_SPIDER, M_Model("spider.dpm")); -#endif - -CLASS(Spider, Monster) - ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE); - ATTRIB(Spider, mins, vector, '-18 -18 -25'); - ATTRIB(Spider, maxs, vector, '18 18 30'); -#ifndef MENUQC - ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER); -#endif - ATTRIB(Spider, netname, string, "spider"); - ATTRIB(Spider, monster_name, string, _("Spider")); -ENDCLASS(Spider) - -REGISTER_MONSTER(SPIDER, NEW(Spider)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include - -CLASS(SpiderAttack, PortoLaunch) -/* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(SpiderAttack, impulse, int, 9); -/* refname */ ATTRIB(SpiderAttack, netname, string, "spider"); -/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); -ENDCLASS(SpiderAttack) -REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); - -#endif - -#ifdef IMPLEMENTATION +#include "spider.qh" #ifdef SVQC @@ -106,7 +70,7 @@ METHOD(SpiderAttack, wr_think, void(SpiderAttack thiswep, entity actor, .entity actor.anim_finished = time + 1; } if (isPlayer) actor.enemy = Monster_FindTarget(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_SpiderAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_SpiderAttack_FIRE, CH_WEAPON_B, 0); if (!isPlayer) w_shotdir = normalize((actor.enemy.origin + '0 0 10') - actor.origin); M_Spider_Attack_Web(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); @@ -143,10 +107,12 @@ 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, NULL); - for(entity e = findradius(this.origin, 25); e; e = e.chain) if(e != this) if(e.takedamage && !IS_DEAD(e)) if(e.health > 0) if(e.monsterid != MON_SPIDER.monsterid) - e.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime); + FOREACH_ENTITY_RADIUS(this.origin, 25, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && it.monsterid != MON_SPIDER.monsterid, + { + it.spider_slowness = time + (autocvar_g_monster_spider_attack_web_damagetime); + }); - remove(this); + delete(this); } } @@ -155,9 +121,9 @@ void M_Spider_Attack_Web_Explode_use(entity this, entity actor, entity trigger) M_Spider_Attack_Web_Explode(this); } -void M_Spider_Attack_Web_Touch(entity this) +void M_Spider_Attack_Web_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); M_Spider_Attack_Web_Explode(this); } @@ -183,7 +149,7 @@ void M_Spider_Attack_Web(entity this) //proj.glow_size = 50; //proj.glow_color = 45; - proj.movetype = MOVETYPE_BOUNCE; + set_movetype(proj, MOVETYPE_BOUNCE); W_SetupProjVelocity_Explicit(proj, v_forward, v_up, (autocvar_g_monster_spider_attack_web_speed), (autocvar_g_monster_spider_attack_web_speed_up), 0, 0, false); settouch(proj, M_Spider_Attack_Web_Touch); setsize(proj, '-4 -4 -4', '4 4 4'); @@ -192,7 +158,10 @@ void M_Spider_Attack_Web(entity this) proj.health = 500; proj.event_damage = func_null; proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, proj); proj.bouncefactor = 0.3; proj.bouncestop = 0.05; @@ -222,7 +191,7 @@ bool M_Spider_Attack(int attack_type, entity actor, entity targ) return false; } -spawnfunc(monster_spider) { Monster_Spawn(this, MON_SPIDER.monsterid); } +spawnfunc(monster_spider) { Monster_Spawn(this, true, MON_SPIDER.monsterid); } #endif // SVQC #ifdef SVQC @@ -246,7 +215,7 @@ METHOD(Spider, mr_death, bool(Spider this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Spider, mr_anim, bool(Spider this, entity actor)) { TC(Spider, this); @@ -282,5 +251,3 @@ METHOD(Spider, mr_precache, bool(Spider this)) return true; } #endif - -#endif diff --git a/qcsrc/common/monsters/monster/spider.qh b/qcsrc/common/monsters/monster/spider.qh new file mode 100644 index 0000000000..c54eb3a7a8 --- /dev/null +++ b/qcsrc/common/monsters/monster/spider.qh @@ -0,0 +1,34 @@ +#pragma once + +#include "../all.qh" + +#ifdef GAMEQC +MODEL(MON_SPIDER, M_Model("spider.dpm")); +#endif + +CLASS(Spider, Monster) + ATTRIB(Spider, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RANGED | MON_FLAG_RIDE); + ATTRIB(Spider, mins, vector, '-18 -18 -25'); + ATTRIB(Spider, maxs, vector, '18 18 30'); +#ifdef GAMEQC + ATTRIB(Spider, m_model, Model, MDL_MON_SPIDER); +#endif + ATTRIB(Spider, netname, string, "spider"); + ATTRIB(Spider, monster_name, string, _("Spider")); +ENDCLASS(Spider) + +REGISTER_MONSTER(SPIDER, NEW(Spider)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include + +CLASS(SpiderAttack, PortoLaunch) +/* flags */ ATTRIB(SpiderAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(SpiderAttack, impulse, int, 9); +/* refname */ ATTRIB(SpiderAttack, netname, string, "spider"); +/* wepname */ ATTRIB(SpiderAttack, m_name, string, _("Spider attack")); +ENDCLASS(SpiderAttack) +REGISTER_WEAPON(SPIDER_ATTACK, NEW(SpiderAttack)); diff --git a/qcsrc/common/monsters/monster/wyvern.qc b/qcsrc/common/monsters/monster/wyvern.qc index 001b2e24d8..cd53ff26f7 100644 --- a/qcsrc/common/monsters/monster/wyvern.qc +++ b/qcsrc/common/monsters/monster/wyvern.qc @@ -1,40 +1,4 @@ -#ifndef WYVERN_H -#define WYVERN_H - -#ifndef MENUQC -MODEL(MON_WYVERN, M_Model("wizard.mdl")); -#endif - -CLASS(Wyvern, Monster) - ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE); - ATTRIB(Wyvern, mins, vector, '-20 -20 -58'); - ATTRIB(Wyvern, maxs, vector, '20 20 20'); -#ifndef MENUQC - ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN); -#endif - ATTRIB(Wyvern, netname, string, "wyvern"); - ATTRIB(Wyvern, monster_name, string, _("Wyvern")); -ENDCLASS(Wyvern) - -REGISTER_MONSTER(WYVERN, NEW(Wyvern)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#include - -CLASS(WyvernAttack, PortoLaunch) -/* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); -/* impulse */ ATTRIB(WyvernAttack, impulse, int, 9); -/* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern"); -/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack")); -ENDCLASS(WyvernAttack) -REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack)); - -#endif - -#ifdef IMPLEMENTATION +#include "wyvern.qh" #ifdef SVQC @@ -46,7 +10,7 @@ float autocvar_g_monster_wyvern_attack_fireball_radius; float autocvar_g_monster_wyvern_attack_fireball_speed; void M_Wyvern_Attack_Fireball_Explode(entity this); -void M_Wyvern_Attack_Fireball_Touch(entity this); +void M_Wyvern_Attack_Fireball_Touch(entity this, entity toucher); SOUND(WyvernAttack_FIRE, W_Sound("electro_fire")); METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity weaponentity, int fire)) @@ -54,7 +18,7 @@ METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity TC(WyvernAttack, thiswep); if (fire & 1) if (time > actor.attack_finished_single[0] || weapon_prepareattack(thiswep, actor, weaponentity, false, 1.2)) { - if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, v_forward, false, 0, SND_WyvernAttack_FIRE, CH_WEAPON_B, 0); + if (IS_PLAYER(actor)) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_WyvernAttack_FIRE, CH_WEAPON_B, 0); if (IS_MONSTER(actor)) { actor.attack_finished_single[0] = time + 1.2; actor.anim_finished = time + 1.2; @@ -64,11 +28,13 @@ METHOD(WyvernAttack, wr_think, void(WyvernAttack thiswep, entity actor, .entity entity missile = spawn(); missile.owner = missile.realowner = actor; missile.solid = SOLID_TRIGGER; - missile.movetype = MOVETYPE_FLYMISSILE; + set_movetype(missile, MOVETYPE_FLYMISSILE); missile.projectiledeathtype = DEATH_MONSTER_WYVERN.m_id; setsize(missile, '-6 -6 -6', '6 6 6'); setorigin(missile, actor.origin + actor.view_ofs + v_forward * 14); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.velocity = w_shotdir * (autocvar_g_monster_wyvern_attack_fireball_speed); missile.avelocity = '300 300 300'; missile.nextthink = time + 5; @@ -107,18 +73,17 @@ void M_Wyvern_Attack_Fireball_Explode(entity this) 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, NULL); - FOREACH_ENTITY_FLOAT(takedamage, DAMAGE_AIM, + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_monster_wyvern_attack_fireball_radius, it.takedamage == DAMAGE_AIM, { - if(vdist(it.origin - this.origin, <=, autocvar_g_monster_wyvern_attack_fireball_radius)) - Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype); + Fire_AddDamage(it, own, 5 * MONSTER_SKILLMOD(own), autocvar_g_monster_wyvern_attack_fireball_damagetime, this.projectiledeathtype); }); - remove(this); + delete(this); } -void M_Wyvern_Attack_Fireball_Touch(entity this) +void M_Wyvern_Attack_Fireball_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); M_Wyvern_Attack_Fireball_Explode(this); } @@ -141,7 +106,7 @@ bool M_Wyvern_Attack(int attack_type, entity actor, entity targ) return false; } -spawnfunc(monster_wyvern) { Monster_Spawn(this, MON_WYVERN.monsterid); } +spawnfunc(monster_wyvern) { Monster_Spawn(this, true, MON_WYVERN.monsterid); } #endif // SVQC #ifdef SVQC @@ -169,7 +134,7 @@ METHOD(Wyvern, mr_death, bool(Wyvern this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Wyvern, mr_anim, bool(Wyvern this, entity actor)) { TC(Wyvern, this); @@ -206,5 +171,3 @@ METHOD(Wyvern, mr_precache, bool(Wyvern this)) return true; } #endif - -#endif diff --git a/qcsrc/common/monsters/monster/wyvern.qh b/qcsrc/common/monsters/monster/wyvern.qh new file mode 100644 index 0000000000..0af84c1301 --- /dev/null +++ b/qcsrc/common/monsters/monster/wyvern.qh @@ -0,0 +1,34 @@ +#pragma once + +#include "../all.qh" + +#ifdef GAMEQC +MODEL(MON_WYVERN, M_Model("wizard.mdl")); +#endif + +CLASS(Wyvern, Monster) + ATTRIB(Wyvern, spawnflags, int, MONSTER_TYPE_FLY | MONSTER_SIZE_BROKEN | MON_FLAG_RANGED | MON_FLAG_RIDE); + ATTRIB(Wyvern, mins, vector, '-20 -20 -58'); + ATTRIB(Wyvern, maxs, vector, '20 20 20'); +#ifdef GAMEQC + ATTRIB(Wyvern, m_model, Model, MDL_MON_WYVERN); +#endif + ATTRIB(Wyvern, netname, string, "wyvern"); + ATTRIB(Wyvern, monster_name, string, _("Wyvern")); +ENDCLASS(Wyvern) + +REGISTER_MONSTER(WYVERN, NEW(Wyvern)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} + +#include + +CLASS(WyvernAttack, PortoLaunch) +/* flags */ ATTRIB(WyvernAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* impulse */ ATTRIB(WyvernAttack, impulse, int, 9); +/* refname */ ATTRIB(WyvernAttack, netname, string, "wyvern"); +/* wepname */ ATTRIB(WyvernAttack, m_name, string, _("Wyvern attack")); +ENDCLASS(WyvernAttack) +REGISTER_WEAPON(WYVERN_ATTACK, NEW(WyvernAttack)); diff --git a/qcsrc/common/monsters/monster/zombie.qc b/qcsrc/common/monsters/monster/zombie.qc index 1eafd4bfd1..8bbb300c76 100644 --- a/qcsrc/common/monsters/monster/zombie.qc +++ b/qcsrc/common/monsters/monster/zombie.qc @@ -1,30 +1,4 @@ -#ifndef ZOMBIE_H -#define ZOMBIE_H - -#ifndef MENUQC -MODEL(MON_ZOMBIE, M_Model("zombie.dpm")); -#endif - -CLASS(Zombie, Monster) - ATTRIB(Zombie, spawnflags, int, MON_FLAG_MELEE | MON_FLAG_RIDE); - ATTRIB(Zombie, mins, vector, '-18 -18 -25'); - ATTRIB(Zombie, maxs, vector, '18 18 47'); -#ifndef MENUQC - ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE); -#endif - ATTRIB(Zombie, netname, string, "zombie"); - ATTRIB(Zombie, monster_name, string, _("Zombie")); -ENDCLASS(Zombie) - -REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) { -#ifndef MENUQC - this.mr_precache(this); -#endif -} - -#endif - -#ifdef IMPLEMENTATION +#include "zombie.qh" #ifdef SVQC float autocvar_g_monster_zombie_health; @@ -75,18 +49,18 @@ const float zombie_anim_spawn = 30; .vector moveto; -void M_Zombie_Attack_Leap_Touch(entity this) +void M_Zombie_Attack_Leap_Touch(entity this, entity toucher) { if (this.health <= 0) return; vector angles_face; - if(other.takedamage) + if(toucher.takedamage) { angles_face = vectoangles(this.moveto - this.origin); angles_face = normalize(angles_face) * (autocvar_g_monster_zombie_attack_leap_force); - Damage(other, this, this, (autocvar_g_monster_zombie_attack_leap_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_ZOMBIE_JUMP.m_id, other.origin, angles_face); + Damage(toucher, this, this, (autocvar_g_monster_zombie_attack_leap_damage) * MONSTER_SKILLMOD(this), DEATH_MONSTER_ZOMBIE_JUMP.m_id, toucher.origin, angles_face); settouch(this, Monster_Touch); // instantly turn it off to stop damage spam this.state = 0; } @@ -151,7 +125,7 @@ bool M_Zombie_Attack(int attack_type, entity actor, entity targ) return false; } -spawnfunc(monster_zombie) { Monster_Spawn(this, MON_ZOMBIE.monsterid); } +spawnfunc(monster_zombie) { Monster_Spawn(this, true, MON_ZOMBIE.monsterid); } #endif // SVQC #ifdef SVQC @@ -180,7 +154,7 @@ METHOD(Zombie, mr_death, bool(Zombie this, entity actor)) return true; } #endif -#ifndef MENUQC +#ifdef GAMEQC METHOD(Zombie, mr_anim, bool(Zombie this, entity actor)) { TC(Zombie, this); @@ -214,6 +188,8 @@ METHOD(Zombie, mr_setup, bool(Zombie this, entity actor)) if(actor.spawnflags & MONSTERFLAG_NORESPAWN) actor.spawnflags &= ~MONSTERFLAG_NORESPAWN; // zombies always respawn + actor.spawnflags &= ~MONSTERFLAG_APPEAR; // once it's appeared, it will respawn quickly, we don't want it to appear + actor.spawnflags |= MONSTER_RESPAWN_DEATHPOINT; actor.monster_loot = spawnfunc_item_health_medium; @@ -234,5 +210,3 @@ METHOD(Zombie, mr_precache, bool(Zombie this)) return true; } #endif - -#endif diff --git a/qcsrc/common/monsters/monster/zombie.qh b/qcsrc/common/monsters/monster/zombie.qh new file mode 100644 index 0000000000..d3c94cd454 --- /dev/null +++ b/qcsrc/common/monsters/monster/zombie.qh @@ -0,0 +1,24 @@ +#pragma once + +#include "../all.qh" + +#ifdef GAMEQC +MODEL(MON_ZOMBIE, M_Model("zombie.dpm")); +#endif + +CLASS(Zombie, Monster) + ATTRIB(Zombie, spawnflags, int, MONSTER_TYPE_UNDEAD | MON_FLAG_MELEE | MON_FLAG_RIDE); + ATTRIB(Zombie, mins, vector, '-18 -18 -25'); + ATTRIB(Zombie, maxs, vector, '18 18 47'); +#ifdef GAMEQC + ATTRIB(Zombie, m_model, Model, MDL_MON_ZOMBIE); +#endif + ATTRIB(Zombie, netname, string, "zombie"); + ATTRIB(Zombie, monster_name, string, _("Zombie")); +ENDCLASS(Zombie) + +REGISTER_MONSTER(ZOMBIE, NEW(Zombie)) { +#ifdef GAMEQC + this.mr_precache(this); +#endif +} diff --git a/qcsrc/common/monsters/spawn.qc b/qcsrc/common/monsters/spawn.qc deleted file mode 100644 index 23fc845ac7..0000000000 --- a/qcsrc/common/monsters/spawn.qc +++ /dev/null @@ -1,69 +0,0 @@ -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include "../util.qh" - #include "all.qh" - #include "sv_monsters.qh" - #include "spawn.qh" - #include - #include -#endif -entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag) -{ - float i; - entity e = spawn(); - - e.spawnflags = MONSTERFLAG_SPAWNED; - - if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; } - if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; } - - setorigin(e, orig); - - if(monster == "random") - { - RandomSelection_Init(); - for(i = MON_FIRST; i <= MON_LAST; ++i) - RandomSelection_Add(NULL, i, string_null, 1, 1); - - monster_id = RandomSelection_chosen_float; - } - else if(monster != "") - { - float found = 0; - entity mon; - for(i = MON_FIRST; i <= MON_LAST; ++i) - { - mon = get_monsterinfo(i); - if(mon.netname == monster) - { - found = true; - monster_id = mon.monsterid; // we have the monster, old monster id is no longer required - break; - } - } - if(!found) - monster_id = ((monster_id > 0) ? monster_id : MON_FIRST); - } - - e.realowner = spawnedby; - - if(moveflag) - e.monster_moveflags = moveflag; - - if(IS_PLAYER(spawnedby)) - { - if(teamplay && autocvar_g_monsters_teams) - e.team = spawnedby.team; // colors handled in spawn code - - if(autocvar_g_monsters_owners) - e.monster_follow = own; // using .owner makes the monster non-solid for its master - - e.angles_y = spawnedby.angles_y; - } - - // Monster_Spawn checks if monster is valid - Monster_Spawn(e, monster_id); - - return e; -} diff --git a/qcsrc/common/monsters/spawn.qh b/qcsrc/common/monsters/spawn.qh deleted file mode 100644 index 2ebdcc54e8..0000000000 --- a/qcsrc/common/monsters/spawn.qh +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef SPAWN_H -#define SPAWN_H - -entity spawnmonster (string monster, float monster_id, entity spawnedby, entity own, vector orig, float respwn, float invincible, float moveflag); -#endif diff --git a/qcsrc/common/monsters/spawner.qc b/qcsrc/common/monsters/spawner.qc new file mode 100644 index 0000000000..0b34d13e65 --- /dev/null +++ b/qcsrc/common/monsters/spawner.qc @@ -0,0 +1,26 @@ +#include "sv_spawn.qh" + +void spawner_use(entity this, entity actor, entity trigger) +{ + int moncount = 0; + IL_EACH(g_monsters, it.realowner == this, + { + ++moncount; + }); + + if(moncount >= this.count) + return; + + entity e = spawn(); + e.noalign = this.noalign; + e.angles = this.angles; + e.monster_skill = this.monster_skill; + e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, true, this.monster_moveflags); +} + +spawnfunc(monster_spawner) +{ + if(!autocvar_g_monsters || !this.spawnmob || this.spawnmob == "") { delete(this); return; } + + this.use = spawner_use; +} diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index c0a15e97b1..f0f789af37 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1,29 +1,27 @@ -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include "../constants.qh" - #include "../teams.qh" - #include "../util.qh" - #include "all.qh" - #include "sv_monsters.qh" - #include "../physics/movelib.qh" - #include "../weapons/all.qh" - #include - #include - #include "../deathtypes/all.qh" - #include - #include - #include "../turrets/sv_turrets.qh" - #include "../turrets/util.qh" - #include "../vehicles/all.qh" - #include - #include - #include - #include "../triggers/triggers.qh" - #include - #include -#endif +#include "sv_monsters.qh" + +#include +#include +#include "../constants.qh" +#include "../teams.qh" +#include "../util.qh" +#include "all.qh" +#include "../physics/movelib.qh" +#include "../weapons/_mod.qh" +#include +#include +#include "../deathtypes/all.qh" +#include +#include +#include "../turrets/sv_turrets.qh" +#include "../turrets/util.qh" +#include "../vehicles/all.qh" +#include +#include +#include "../triggers/triggers.qh" +#include +#include +#include void monsters_setstatus(entity this) { @@ -36,7 +34,7 @@ void monster_dropitem(entity this, entity attacker) if(!this.candrop || !this.monster_loot) return; - vector org = this.origin + ((this.mins + this.maxs) * 0.5); + vector org = CENTER_OR_VIEWOFS(this); entity e = new(droppedweapon); // use weapon handling to remove it on touch e.spawnfunc_checked = true; @@ -50,7 +48,7 @@ void monster_dropitem(entity this, entity attacker) e.noalign = true; e.monster_loot(e); e.gravity = 1; - e.movetype = MOVETYPE_TOSS; + set_movetype(e, MOVETYPE_TOSS); e.reset = SUB_Remove; setorigin(e, org); e.velocity = randomvec() * 175 + '0 0 325'; @@ -82,7 +80,7 @@ bool Monster_ValidTarget(entity this, entity targ) if((targ == this) || (autocvar_g_monsters_lineofsight && !checkpvs(this.origin + this.view_ofs, targ)) // enemy cannot be seen - || (IS_VEHICLE(targ) && !((get_monsterinfo(this.monsterid)).spawnflags & MON_FLAG_RANGED)) // melee vs vehicle is useless + || (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) || (targ.items & IT_INVISIBILITY) @@ -94,24 +92,23 @@ bool Monster_ValidTarget(entity this, entity targ) || (SAME_TEAM(targ, this)) || (STAT(FROZEN, targ)) || (targ.alpha != 0 && targ.alpha < 0.5) + || (MUTATOR_CALLHOOK(MonsterValidTarget, this, targ)) ) { // if any of the above checks fail, target is not valid return false; } - traceline(this.origin + this.view_ofs, targ.origin, 0, this); + traceline(this.origin + this.view_ofs, targ.origin, MOVE_NOMONSTERS, this); - if((trace_fraction < 1) && (trace_ent != targ)) - return false; + if(trace_fraction < 1) + return false; // solid if(autocvar_g_monsters_target_infront || (this.spawnflags & MONSTERFLAG_INFRONT)) if(this.enemy != targ) { - float dot; - makevectors (this.angles); - dot = normalize (targ.origin - this.origin) * v_forward; + float dot = normalize (targ.origin - this.origin) * v_forward; if(dot <= autocvar_g_monsters_target_infront_range) { return false; } } @@ -119,74 +116,71 @@ bool Monster_ValidTarget(entity this, entity targ) return true; // this target is valid! } -entity Monster_FindTarget(entity mon) +entity Monster_FindTarget(entity this) { - if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return mon.enemy; } // Handled by a mutator + if(MUTATOR_CALLHOOK(MonsterFindTarget)) { return this.enemy; } // Handled by a mutator - entity head, closest_target = NULL; - head = findradius(mon.origin, mon.target_range); + entity closest_target = NULL; + vector my_center = CENTER_OR_VIEWOFS(this); - while(head) // find the closest acceptable target to pass to + // find the closest acceptable target to pass to + FOREACH_ENTITY_RADIUS(this.origin, this.target_range, it.monster_attack, { - if(head.monster_attack) - if(Monster_ValidTarget(mon, head)) + if(Monster_ValidTarget(this, it)) { // if it's a player, use the view origin as reference (stolen from RadiusDamage functions in g_damage.qc) - vector head_center = CENTER_OR_VIEWOFS(head); - vector ent_center = CENTER_OR_VIEWOFS(mon); + vector targ_center = CENTER_OR_VIEWOFS(it); if(closest_target) { vector closest_target_center = CENTER_OR_VIEWOFS(closest_target); - if(vlen2(ent_center - head_center) < vlen2(ent_center - closest_target_center)) - { closest_target = head; } + if(vlen2(my_center - targ_center) < vlen2(my_center - closest_target_center)) + { closest_target = it; } } - else { closest_target = head; } + else { closest_target = it; } } - - head = head.chain; - } + }); return closest_target; } -void monster_setupcolors(entity mon) +void monster_setupcolors(entity this) { - if(IS_PLAYER(mon.realowner)) - mon.colormap = mon.realowner.colormap; - else if(teamplay && mon.team) - mon.colormap = 1024 + (mon.team - 1) * 17; + if(IS_PLAYER(this.realowner)) + this.colormap = this.realowner.colormap; + else if(teamplay && this.team) + this.colormap = 1024 + (this.team - 1) * 17; else { - if(mon.monster_skill <= MONSTER_SKILL_EASY) - mon.colormap = 1029; - else if(mon.monster_skill <= MONSTER_SKILL_MEDIUM) - mon.colormap = 1027; - else if(mon.monster_skill <= MONSTER_SKILL_HARD) - mon.colormap = 1038; - else if(mon.monster_skill <= MONSTER_SKILL_INSANE) - mon.colormap = 1028; - else if(mon.monster_skill <= MONSTER_SKILL_NIGHTMARE) - mon.colormap = 1032; + if(this.monster_skill <= MONSTER_SKILL_EASY) + this.colormap = 1029; + else if(this.monster_skill <= MONSTER_SKILL_MEDIUM) + this.colormap = 1027; + else if(this.monster_skill <= MONSTER_SKILL_HARD) + this.colormap = 1038; + else if(this.monster_skill <= MONSTER_SKILL_INSANE) + this.colormap = 1028; + else if(this.monster_skill <= MONSTER_SKILL_NIGHTMARE) + this.colormap = 1032; else - mon.colormap = 1024; + this.colormap = 1024; } } -void monster_changeteam(entity ent, float newteam) +void monster_changeteam(entity this, int newteam) { if(!teamplay) { return; } - ent.team = newteam; - ent.monster_attack = true; // new team, activate attacking - monster_setupcolors(ent); + this.team = newteam; + this.monster_attack = true; // new team, activate attacking + monster_setupcolors(this); - if(ent.sprite) + if(this.sprite) { - WaypointSprite_UpdateTeamRadar(ent.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0')); + WaypointSprite_UpdateTeamRadar(this.sprite, RADARICON_DANGER, ((newteam) ? Team_ColorRGB(newteam) : '1 0 0')); - ent.sprite.team = newteam; - ent.sprite.SendFlags |= 1; + this.sprite.team = newteam; + this.sprite.SendFlags |= 1; } } @@ -250,7 +244,7 @@ void Monster_Sound_Precache(string f) { if(tokenize_console(s) != 3) { - LOG_TRACE("Invalid sound info line: ", s, "\n"); + LOG_TRACE("Invalid sound info line: ", s); continue; } PrecacheGlobalSound(strcat(argv(1), " ", argv(2))); @@ -299,13 +293,12 @@ void Monster_Sounds_Clear(entity this) bool Monster_Sounds_Load(entity this, string f, int first) { - float fh; string s; var .string field; - fh = fopen(f, FILE_READ); + float fh = fopen(f, FILE_READ); if(fh < 0) { - LOG_TRACE("Monster sound file not found: ", f, "\n"); + LOG_TRACE("Monster sound file not found: ", f); return false; } while((s = fgets(fh))) @@ -334,14 +327,14 @@ void Monster_Sounds_Update(entity this) Monster_Sounds_Load(this, get_monster_model_datafilename(this.model, 0, "sounds"), 0); } -void Monster_Sound(entity this, .string samplefield, float sound_delay, float delaytoo, float chan) +void Monster_Sound(entity this, .string samplefield, float sound_delay, bool delaytoo, float chan) { if(!autocvar_g_monsters_sounds) { return; } if(delaytoo) if(time < this.msound_delay) return; // too early - GlobalSound_string(this, this.(samplefield), chan, VOICETYPE_PLAYERSOUND); + GlobalSound_string(this, this.(samplefield), chan, VOL_BASE, VOICETYPE_PLAYERSOUND); this.msound_delay = time + sound_delay; } @@ -388,25 +381,25 @@ bool Monster_Attack_Leap_Check(entity this, vector vel) this.velocity = vel; tracetoss(this, this); this.velocity = old; - if (trace_ent != this.enemy) + if(trace_ent != this.enemy) return false; return true; } -bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, vector vel, float animtime) +bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity toucher) touchfunc, vector vel, float animtime) { if(!Monster_Attack_Leap_Check(this, vel)) return false; setanim(this, anm, false, true, false); - if(this.animstate_endtime > time && (this.flags & FL_MONSTER)) + if(this.animstate_endtime > time && IS_MONSTER(this)) this.attack_finished_single[0] = this.anim_finished = this.animstate_endtime; else this.attack_finished_single[0] = this.anim_finished = time + animtime; - if(this.flags & FL_MONSTER) + if(IS_MONSTER(this)) this.state = MONSTER_ATTACK_RANGED; settouch(this, touchfunc); this.origin_z += 1; @@ -418,14 +411,14 @@ bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, v void Monster_Attack_Check(entity this, entity targ) { - if((this == NULL || targ == NULL) + if((!this || !targ) || (!this.monster_attackfunc) || (time < this.attack_finished_single[0]) ) { return; } if(vdist(targ.origin - this.origin, <=, this.attack_range)) { - bool attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ); + int attack_success = this.monster_attackfunc(MONSTER_ATTACK_MELEE, this, targ); if(attack_success == 1) Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE); else if(attack_success > 0) @@ -434,7 +427,7 @@ void Monster_Attack_Check(entity this, entity targ) if(vdist(targ.origin - this.origin, >, this.attack_range)) { - float attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ); + int attack_success = this.monster_attackfunc(MONSTER_ATTACK_RANGED, this, targ); if(attack_success == 1) Monster_Sound(this, monstersound_melee, 0, false, CH_VOICE); else if(attack_success > 0) @@ -462,19 +455,19 @@ void Monster_UpdateModel(entity this) this.anim_die2 = animfixfps(this, '9 1 0.01', '0 0 0');*/ // then get the real values - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); mon.mr_anim(mon, this); } -void Monster_Touch(entity this) +void Monster_Touch(entity this, entity toucher) { - if(other == NULL) { return; } + if(toucher == NULL) { return; } - if(other.monster_attack) - if(this.enemy != other) - if(!IS_MONSTER(other)) - if(Monster_ValidTarget(this, other)) - this.enemy = other; + if(toucher.monster_attack) + if(this.enemy != toucher) + if(!IS_MONSTER(toucher)) + if(Monster_ValidTarget(this, toucher)) + this.enemy = toucher; } void Monster_Miniboss_Check(entity this) @@ -509,7 +502,9 @@ bool Monster_Respawn_Check(entity this) return true; } -void Monster_Respawn(entity this) { Monster_Spawn(this, this.monsterid); } +void Monster_Respawn(entity this) { Monster_Spawn(this, true, this.monsterid); } + +.vector pos1, pos2; void Monster_Dead_Fade(entity this) { @@ -546,6 +541,7 @@ void Monster_Use(entity this, entity actor, entity trigger) if(Monster_ValidTarget(this, actor)) { this.enemy = actor; } } +.float pass_distance; vector Monster_Move_Target(entity this, entity targ) { // enemy is always preferred target @@ -555,7 +551,8 @@ vector Monster_Move_Target(entity this, entity targ) targ_origin = WarpZone_RefSys_TransformOrigin(this.enemy, this, targ_origin); // origin of target as seen by the monster (us) WarpZone_TraceLine(this.origin, targ_origin, MOVE_NOMONSTERS, this); - if((this.enemy == NULL) + // 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) || (STAT(FROZEN, this.enemy)) || (this.enemy.flags & FL_NOTARGET) @@ -688,11 +685,13 @@ void Monster_CalculateVelocity(entity this, vector to, vector from, float turnra //this.angles = vectoangles(this.velocity); } +.entity draggedby; +.entity target2; + void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) { - if(this.target2) { this.goalentity = find(NULL, targetname, this.target2); } - - entity targ; + // update goal entity if lost + if(this.target2 && this.goalentity.targetname != this.target2) { this.goalentity = find(NULL, targetname, this.target2); } if(STAT(FROZEN, this) == 2) { @@ -766,19 +765,19 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) } - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); //this.velocity_z = -200; return; } - else if(this.movetype == MOVETYPE_BOUNCE) + else if(this.move_movetype == MOVETYPE_BOUNCE) { this.angles_x = 0; - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); } } - targ = this.goalentity; + entity targ = this.goalentity; if (MUTATOR_CALLHOOK(MonsterMove, this, runspeed, walkspeed, targ) || gameover @@ -799,8 +798,7 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) runspeed = bound(0, M_ARGV(1, float) * MONSTER_SKILLMOD(this), runspeed * 2.5); // limit maxspeed to prevent craziness walkspeed = bound(0, M_ARGV(2, float) * MONSTER_SKILLMOD(this), walkspeed * 2.5); // limit maxspeed to prevent craziness - if(teamplay) - if(autocvar_g_monsters_teams) + if(teamplay && autocvar_g_monsters_teams) if(DIFF_TEAM(this.monster_follow, this)) this.monster_follow = NULL; @@ -852,13 +850,12 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) if(vdist(this.origin - this.moveto, >, 100)) { - float do_run = (this.enemy || this.monster_moveto); + bool do_run = (this.enemy || this.monster_moveto); if(IS_ONGROUND(this) || ((this.flags & FL_FLY) || (this.flags & FL_SWIM))) Monster_CalculateVelocity(this, this.moveto, this.origin, true, ((do_run) ? runspeed : walkspeed)); - if(time > this.pain_finished) // TODO: use anim_finished instead! + if(time > this.pain_finished && time > this.anim_finished) // TODO: use anim_finished instead!? if(!this.state) - if(time > this.anim_finished) if(vdist(this.velocity, >, 10)) setanim(this, ((do_run) ? this.anim_run : this.anim_walk), true, false, false); else @@ -866,15 +863,14 @@ void Monster_Move(entity this, float runspeed, float walkspeed, float stpspeed) } else { - entity e = find(NULL, targetname, this.target2); + entity e = this.goalentity; //find(NULL, targetname, this.target2); if(e.target2) this.target2 = e.target2; - else if(e.target) + else if(e.target) // compatibility this.target2 = e.target; movelib_brake_simple(this, stpspeed); - if(time > this.anim_finished) - if(time > this.pain_finished) + if(time > this.anim_finished && time > this.pain_finished) if(!this.state) if(vdist(this.velocity, <=, 30)) setanim(this, this.anim_idle, true, false, false); @@ -900,16 +896,20 @@ void Monster_Remove(entity this) if(IS_CLIENT(this)) return; // don't remove it? - .entity weaponentity = weaponentities[0]; if(!this) { return; } if(!MUTATOR_CALLHOOK(MonsterRemove, this)) Send_Effect(EFFECT_ITEM_PICKUP, this.origin, '0 0 0', 1); - if(this.(weaponentity)) { remove(this.(weaponentity)); } - if(this.iceblock) { remove(this.iceblock); } + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(this.(weaponentity)) + delete(this.(weaponentity)); + } + if(this.iceblock) { delete(this.iceblock); } WaypointSprite_Kill(this.sprite); - remove(this); + delete(this); } void Monster_Dead_Think(entity this) @@ -927,8 +927,7 @@ void Monster_Dead_Think(entity this) void Monster_Appear(entity this, entity actor, entity trigger) { this.enemy = actor; - this.spawnflags &= ~MONSTERFLAG_APPEAR; // otherwise, we get an endless loop - Monster_Spawn(this, this.monsterid); + Monster_Spawn(this, false, this.monsterid); } bool Monster_Appear_Check(entity this, int monster_id) @@ -1013,7 +1012,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) this.takedamage = DAMAGE_AIM; this.deadflag = DEAD_DEAD; this.enemy = NULL; - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); this.moveto = this.origin; settouch(this, Monster_Touch); // reset incase monster was pouncing this.reset = func_null; @@ -1026,7 +1025,7 @@ void Monster_Dead(entity this, entity attacker, float gibbed) CSQCModel_UnlinkEntity(this); - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); mon.mr_death(mon, this); if(this.candrop && this.weapon) @@ -1050,14 +1049,11 @@ void Monster_Damage(entity this, entity inflictor, entity attacker, float damage if(deathtype == DEATH_FALL.m_id && this.draggedby != NULL) return; - vector v; - float take, save; + vector v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); + float take = v.x; + //float save = v.y; - v = healtharmor_applydamage(100, this.armorvalue / 100, deathtype, damage); - take = v_x; - save = v_y; - - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); take = mon.mr_pain(mon, this, take, attacker, deathtype); if(take) @@ -1122,23 +1118,19 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) return; } - float reverse = false; - vector a, b; - makevectors(this.angles); - a = this.origin + '0 0 16'; - b = this.origin + '0 0 16' + v_forward * 32; + vector a = CENTER_OR_VIEWOFS(this); + vector b = CENTER_OR_VIEWOFS(this) + v_forward * 32; traceline(a, b, MOVE_NORMAL, this); + bool reverse = false; if(trace_fraction != 1.0) - { reverse = true; - - if(trace_ent) - if(IS_PLAYER(trace_ent) && !(trace_ent.items & IT_STRENGTH)) - reverse = false; - } + if(trace_ent && IS_PLAYER(trace_ent) && !(trace_ent.items & ITEM_Strength.m_itemid)) + reverse = false; + if(trace_ent && IS_MONSTER(trace_ent)) + reverse = true; // TODO: fix this... tracing is broken if the floor is thin /* @@ -1158,8 +1150,7 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) movelib_move_simple_gravity(this, v_forward, mspeed, 1); - if(time > this.pain_finished) - if(time > this.attack_finished_single[0]) + if(time > this.pain_finished && time > this.attack_finished_single[0]) if(vdist(this.velocity, >, 10)) setanim(this, this.anim_walk, true, false, false); else @@ -1206,16 +1197,15 @@ void Monster_Anim(entity this) void Monster_Think(entity this) { setthink(this, Monster_Think); - this.nextthink = this.ticrate; + this.nextthink = time + this.ticrate; - if(this.monster_lifetime) - if(time >= this.monster_lifetime) + if(this.monster_lifetime && time >= this.monster_lifetime) { Damage(this, this, this, this.health + this.max_health, DEATH_KILL.m_id, this.origin, this.origin); return; } - Monster mon = get_monsterinfo(this.monsterid); + Monster mon = Monsters_from(this.monsterid); if(mon.mr_think(mon, this)) Monster_Move(this, this.speed2, this.speed, this.stopspeed); @@ -1285,7 +1275,7 @@ bool Monster_Spawn_Setup(entity this) return true; } -bool Monster_Spawn(entity this, int mon_id) +bool Monster_Spawn(entity this, bool check_appear, int mon_id) { // setup the basic required properties for a monster entity mon = Monsters_from(mon_id); @@ -1293,7 +1283,10 @@ bool Monster_Spawn(entity this, int mon_id) if(!autocvar_g_monsters) { Monster_Remove(this); return false; } - if(Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed + if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) + IL_PUSH(g_monsters, this); + + if(check_appear && Monster_Appear_Check(this, mon_id)) { return true; } // return true so the monster isn't removed if(!this.monster_skill) this.monster_skill = cvar("g_monsters_skill"); @@ -1314,16 +1307,20 @@ bool Monster_Spawn(entity this, int mon_id) this.flags = FL_MONSTER; this.classname = "monster"; this.takedamage = DAMAGE_AIM; + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); this.bot_attack = true; this.iscreature = true; this.teleportable = true; + if(!this.damagedbycontents) + IL_PUSH(g_damagedbycontents, this); this.damagedbycontents = true; this.monsterid = mon_id; this.event_damage = Monster_Damage; settouch(this, Monster_Touch); this.use = Monster_Use; this.solid = SOLID_BBOX; - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); this.spawnshieldtime = time + autocvar_g_monsters_spawnshieldtime; this.enemy = NULL; this.velocity = '0 0 0'; @@ -1339,13 +1336,13 @@ bool Monster_Spawn(entity this, int mon_id) this.oldtarget2 = this.target2; this.pass_distance = 0; this.deadflag = DEAD_NO; - this.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)); this.spawn_time = time; this.gravity = 1; this.monster_moveto = '0 0 0'; this.monster_face = '0 0 0'; this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_BOTCLIP | DPCONTENTS_MONSTERCLIP; + if(!this.noalign) { this.noalign = ((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)); } if(!this.scale) { this.scale = 1; } if(autocvar_g_monsters_edit) { this.grab = 1; } if(autocvar_g_fullbrightplayers) { this.effects |= EF_FULLBRIGHT; } @@ -1358,7 +1355,7 @@ bool Monster_Spawn(entity this, int mon_id) if(mon.spawnflags & MONSTER_TYPE_FLY) { this.flags |= FL_FLY; - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); } if(!(this.spawnflags & MONSTERFLAG_RESPAWNED)) diff --git a/qcsrc/common/monsters/sv_monsters.qh b/qcsrc/common/monsters/sv_monsters.qh index f8501fc758..b667373a0f 100644 --- a/qcsrc/common/monsters/sv_monsters.qh +++ b/qcsrc/common/monsters/sv_monsters.qh @@ -1,5 +1,4 @@ -#ifndef SV_MONSTERS_H -#define SV_MONSTERS_H +#pragma once // stats networking .int stat_monsters_killed; @@ -73,11 +72,11 @@ void Monster_Remove(entity this); void monsters_setstatus(entity this); -bool Monster_Spawn(entity this, int mon_id); +bool Monster_Spawn(entity this, bool check_appear, int mon_id); void monster_setupcolors(entity this); -void Monster_Touch(entity this); +void Monster_Touch(entity this, entity toucher); void Monster_Move_2D(entity this, float mspeed, float allow_jumpoff); @@ -85,7 +84,7 @@ void Monster_Delay(entity this, int repeat_count, float defer_amnt, void(entity) float Monster_Attack_Melee(entity this, entity targ, float damg, vector anim, float er, float animtime, int deathtype, float dostop); -bool Monster_Attack_Leap(entity this, vector anm, void(entity this) touchfunc, vector vel, float animtime); +bool Monster_Attack_Leap(entity this, vector anm, void(entity this, entity toucher) touchfunc, vector vel, float animtime); entity Monster_FindTarget(entity this); @@ -113,5 +112,3 @@ ALLMONSTERSOUNDS #undef _MSOUND float GetMonsterSoundSampleField_notFound; - -#endif diff --git a/qcsrc/common/monsters/sv_spawn.qc b/qcsrc/common/monsters/sv_spawn.qc new file mode 100644 index 0000000000..497dee8666 --- /dev/null +++ b/qcsrc/common/monsters/sv_spawn.qc @@ -0,0 +1,79 @@ +#include "sv_spawn.qh" +#if defined(CSQC) +#elif defined(MENUQC) +#elif defined(SVQC) + #include "../util.qh" + #include "all.qh" + #include "sv_monsters.qh" + #include + #include +#endif +entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag) +{ + e.spawnflags = MONSTERFLAG_SPAWNED; + + if(!respwn) { e.spawnflags |= MONSTERFLAG_NORESPAWN; } + //if(invincible) { e.spawnflags |= MONSTERFLAG_INVINCIBLE; } + + setorigin(e, orig); + + if(monster == "random") + { + RandomSelection_Init(); + FOREACH(Monsters, it != MON_Null && !(it.spawnflags & MONSTER_TYPE_PASSIVE), + { + RandomSelection_AddEnt(it, 1, 1); + }); + + monster_id = RandomSelection_chosen_ent.monsterid; + } + else if(monster != "") + { + bool found = false; + FOREACH(Monsters, it != MON_Null, + { + if(it.netname == monster) + { + found = true; + monster_id = it.monsterid; // we have the monster, old monster id is no longer required + break; + } + }); + + if(!found && !monster_id) + { + if(removeifinvalid) + { + delete(e); + return NULL; // no good + } + else + monster_id = MON_FIRST; + } + } + + e.realowner = spawnedby; + + if(moveflag) + e.monster_moveflags = moveflag; + + if(IS_PLAYER(spawnedby)) + { + if(teamplay && autocvar_g_monsters_teams) + e.team = spawnedby.team; // colors handled in spawn code + + if(autocvar_g_monsters_owners) + e.monster_follow = own; // using .owner makes the monster non-solid for its master + + e.angles_y = spawnedby.angles_y; + } + + // Monster_Spawn checks if monster is valid + if(!Monster_Spawn(e, false, monster_id)) + { + delete(e); + return NULL; // remove even if told not to, as we didn't spawn any kind of monster + } + + return e; +} diff --git a/qcsrc/common/monsters/sv_spawn.qh b/qcsrc/common/monsters/sv_spawn.qh new file mode 100644 index 0000000000..983676db87 --- /dev/null +++ b/qcsrc/common/monsters/sv_spawn.qh @@ -0,0 +1,3 @@ +#pragma once + +entity spawnmonster (entity e, string monster, int monster_id, entity spawnedby, entity own, vector orig, bool respwn, bool removeifinvalid, int moveflag); diff --git a/qcsrc/common/mutators/_mod.inc b/qcsrc/common/mutators/_mod.inc index 8220b2d195..29d6deabb9 100644 --- a/qcsrc/common/mutators/_mod.inc +++ b/qcsrc/common/mutators/_mod.inc @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/mutators/_mod.qh b/qcsrc/common/mutators/_mod.qh index 5d6ac5628b..b19e9c4d70 100644 --- a/qcsrc/common/mutators/_mod.qh +++ b/qcsrc/common/mutators/_mod.qh @@ -1,2 +1,3 @@ // generated file; do not modify -#include + +#include diff --git a/qcsrc/common/mutators/all.inc b/qcsrc/common/mutators/all.inc deleted file mode 100644 index e99092c660..0000000000 --- a/qcsrc/common/mutators/all.inc +++ /dev/null @@ -1,38 +0,0 @@ -#include "mutator/waypoints/module.inc" - -#include "mutator/itemstime.qc" -#include "mutator/multijump/module.inc" -#include "mutator/nades/module.inc" -#include "mutator/superspec/module.inc" - -// completely self contained - -#include "mutator/bloodloss/module.inc" -#include "mutator/breakablehook/module.inc" -#include "mutator/buffs/module.inc" -#include "mutator/bugrigs/module.inc" -#include "mutator/campcheck/module.inc" -#include "mutator/cloaked/module.inc" -#include "mutator/damagetext/module.inc" -#include "mutator/dodging/module.inc" -#include "mutator/doublejump/module.inc" -#include "mutator/hook/module.inc" -#include "mutator/instagib/module.inc" -#include "mutator/invincibleproj/module.inc" -#include "mutator/melee_only/module.inc" -#include "mutator/midair/module.inc" -#include "mutator/new_toys/module.inc" -#include "mutator/nix/module.inc" -#include "mutator/overkill/module.inc" -#include "mutator/physical_items/module.inc" -#include "mutator/pinata/module.inc" -#include "mutator/random_gravity/module.inc" -#include "mutator/rocketflying/module.inc" -#include "mutator/rocketminsta/module.inc" -#include "mutator/running_guns/module.inc" -#include "mutator/sandbox/module.inc" -#include "mutator/spawn_near_teammate/module.inc" -#include "mutator/touchexplode/module.inc" -#include "mutator/vampirehook/module.inc" -#include "mutator/vampire/module.inc" -#include "mutator/weaponarena_random/module.inc" diff --git a/qcsrc/common/mutators/all.qc b/qcsrc/common/mutators/all.qc deleted file mode 100644 index f0fc7195a8..0000000000 --- a/qcsrc/common/mutators/all.qc +++ /dev/null @@ -1,5 +0,0 @@ -#include "all.qh" - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/common/mutators/all.qh b/qcsrc/common/mutators/all.qh deleted file mode 100644 index ceb4b51dd5..0000000000 --- a/qcsrc/common/mutators/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef MUTATORS_ALL_H -#define MUTATORS_ALL_H - -#include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh index 4d92b5d405..ee5dc4ab12 100644 --- a/qcsrc/common/mutators/base.qh +++ b/qcsrc/common/mutators/base.qh @@ -1,5 +1,4 @@ -#ifndef MUTATORS_BASE_H -#define MUTATORS_BASE_H +#pragma once const int CBC_ORDER_FIRST = 1; const int CBC_ORDER_LAST = 2; @@ -20,7 +19,7 @@ CLASS(Callback, Object) * return false; * } */ - ATTRIB(Callback, cbc_func, bool(), func_null) + ATTRIB(Callback, cbc_func, bool()); CONSTRUCTOR(Callback, bool() func) { CONSTRUCT(Callback); this.cbc_func = func; @@ -32,9 +31,9 @@ ENDCLASS(Callback) */ CLASS(CallbackChain, Object) CLASS(CallbackNode, Object) - ATTRIB(CallbackNode, cbc, Callback, NULL) - ATTRIB(CallbackNode, cbc_next, CallbackNode, NULL) - ATTRIB(CallbackNode, cbc_order, int, 0) + ATTRIB(CallbackNode, cbc, Callback); + ATTRIB(CallbackNode, cbc_next, CallbackNode); + ATTRIB(CallbackNode, cbc_order, int, 0); CONSTRUCTOR(CallbackNode, Callback it, int order) { CONSTRUCT(CallbackNode); this.cbc = it; @@ -42,8 +41,8 @@ CLASS(CallbackChain, Object) } ENDCLASS(CallbackNode) - ATTRIB(CallbackChain, cbc_next, CallbackNode, NULL) - ATTRIB(CallbackChain, cbc_order, int, 0) + ATTRIB(CallbackChain, cbc_next, CallbackNode); + ATTRIB(CallbackChain, cbc_order, int, 0); CONSTRUCTOR(CallbackChain, string _name) { CONSTRUCT(CallbackChain); this.netname = _name; @@ -120,9 +119,10 @@ ENDCLASS(CallbackChain) void RegisterHooks() {}; void RegisterCallbacks() {}; -#define _MUTATOR_HOOKABLE(id, ...) CallbackChain HOOK_##id; bool __Mutator_Send_##id(__VA_ARGS__) -#define MUTATOR_HOOKABLE(id, params) \ - _MUTATOR_HOOKABLE(id, int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \ +#define MUTATOR_HOOKABLE(id, params) _MUTATOR_HOOKABLE(id, params) +#define _MUTATOR_HOOKABLE(id, params) \ + CallbackChain HOOK_##id; \ + bool __Mutator_Send_##id(int params(_MUTATOR_HANDLE_PARAMS, _MUTATOR_HANDLE_NOP)) { \ params(_MUTATOR_HANDLE_PUSHTMP, _MUTATOR_HANDLE_NOP) \ params(_MUTATOR_HANDLE_PREPARE, _MUTATOR_HANDLE_NOP) \ bool ret = CallbackChain_Call(HOOK_##id); \ @@ -132,10 +132,12 @@ void RegisterCallbacks() {}; return ret; \ } \ [[accumulate]] void RegisterHooks() { HOOK_##id = NEW(CallbackChain, #id); } + +#define MUTATOR_CALLHOOK(id, ...) _MUTATOR_CALLHOOK(id, __VA_ARGS__) #ifdef __STDC__ - #define MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__)) + #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0 P99_IF_EMPTY(__VA_ARGS__)()(, __VA_ARGS__)) #else - #define MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__) + #define _MUTATOR_CALLHOOK(id, ...) APPLY(__Mutator_Send_##id, 0, ##__VA_ARGS__) #endif enum { @@ -147,10 +149,10 @@ enum { USING(mutatorfunc_t, bool(int)); CLASS(Mutator, Object) - ATTRIB(Mutator, m_id, int, 0) - ATTRIB(Mutator, m_name, string, string_null) - ATTRIB(Mutator, mutatorfunc, mutatorfunc_t, func_null) - ATTRIB(Mutator, mutatorcheck, bool(), func_null) + ATTRIB(Mutator, m_id, int, 0); + ATTRIB(Mutator, m_name, string); + ATTRIB(Mutator, mutatorfunc, mutatorfunc_t); + ATTRIB(Mutator, mutatorcheck, bool()); CONSTRUCTOR(Mutator, string _name, mutatorfunc_t func) { CONSTRUCT(Mutator); this.m_name = _name; @@ -165,7 +167,7 @@ bool Mutator_Add(Mutator mut); void Mutator_Remove(Mutator mut); bool mutator_log = false; -#ifndef MENUQC +#ifdef GAMEQC /** server mutators activate corresponding client mutators for all clients */ REGISTER_NET_LINKED(Mutator) @@ -200,7 +202,7 @@ NET_HANDLE(Mutator, bool isNew) WITH(bool, mutator_log, true, LAMBDA( FOREACH(Mutators, it.registered_id == s, { Mutator_Add(it); ++added; }); )); - if (added > 1) LOG_WARNINGF("Added more than one mutator for %s\n", s); + if (added > 1) LOG_WARNF("Added more than one mutator for %s", s); } } #endif @@ -224,7 +226,7 @@ bool Mutator_Add(Mutator mut) mutatorfunc_t func = mut.mutatorfunc; if (!func(MUTATOR_ADDING)) { // good - if (mutator_log) LOG_TRACEF("Mutator: added %s\n", mut.m_name); + if (mutator_log) LOG_TRACEF("Mutator: added %s", mut.m_name); #ifdef SVQC Net_LinkEntity(mut, false, 0, Mutator_SendEntity); #endif @@ -254,7 +256,7 @@ void Mutator_Remove(Mutator mut) // baaaaad error("Mutator_Remove: removing mutator failed"); } - if (mutator_log) LOG_TRACEF("Mutator: removed %s\n", mut.m_name); + if (mutator_log) LOG_TRACEF("Mutator: removed %s", mut.m_name); #ifdef SVQC Net_UnlinkEntity(mut); #endif @@ -319,5 +321,3 @@ STATIC_INIT_LATE(Mutators) { } MACRO_END #include "events.qh" - -#endif diff --git a/qcsrc/common/mutators/events.qh b/qcsrc/common/mutators/events.qh index 75fc8d31e1..6b16371a2c 100644 --- a/qcsrc/common/mutators/events.qh +++ b/qcsrc/common/mutators/events.qh @@ -1,5 +1,4 @@ -#ifndef COMMON_MUTATORS_EVENTS_H -#define COMMON_MUTATORS_EVENTS_H +#pragma once #define EV_NO_ARGS(i, o) @@ -75,6 +74,7 @@ MUTATOR_HOOKABLE(WP_Format, EV_WP_Format); */ #define EV_PlayerPhysics(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** ticrate*/ i(float, MUTATOR_ARGV_1_float) \ /**/ MUTATOR_HOOKABLE(PlayerPhysics, EV_PlayerPhysics); @@ -92,6 +92,7 @@ MUTATOR_HOOKABLE(PlayerJump, EV_PlayerJump); #define EV_PM_Physics(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /** maxspeed_mod */ i(float, MUTATOR_ARGV_1_float) \ + /** tick rate */ i(float, MUTATOR_ARGV_2_float) \ /**/ MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics); @@ -102,5 +103,3 @@ MUTATOR_HOOKABLE(PM_Physics, EV_PM_Physics); /**/ o(string, MUTATOR_ARGV_1_string) \ /**/ MUTATOR_HOOKABLE(WeaponModel, EV_WeaponModel); - -#endif diff --git a/qcsrc/common/mutators/mutator/_mod.inc b/qcsrc/common/mutators/mutator/_mod.inc index 30d67e34bc..0d6326fef0 100644 --- a/qcsrc/common/mutators/mutator/_mod.inc +++ b/qcsrc/common/mutators/mutator/_mod.inc @@ -1,2 +1,38 @@ // generated file; do not modify -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/_mod.qh b/qcsrc/common/mutators/mutator/_mod.qh index ac056ac68b..917dc6557c 100644 --- a/qcsrc/common/mutators/mutator/_mod.qh +++ b/qcsrc/common/mutators/mutator/_mod.qh @@ -1,2 +1,38 @@ // generated file; do not modify -#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/qcsrc/common/mutators/mutator/bloodloss/_mod.inc b/qcsrc/common/mutators/mutator/bloodloss/_mod.inc index 16e6308acf..768808db71 100644 --- a/qcsrc/common/mutators/mutator/bloodloss/_mod.inc +++ b/qcsrc/common/mutators/mutator/bloodloss/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/_mod.qh b/qcsrc/common/mutators/mutator/bloodloss/_mod.qh index b1d45e2791..e6ad248c62 100644 --- a/qcsrc/common/mutators/mutator/bloodloss/_mod.qh +++ b/qcsrc/common/mutators/mutator/bloodloss/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc deleted file mode 100644 index a335bf1cc4..0000000000 --- a/qcsrc/common/mutators/mutator/bloodloss/bloodloss.qc +++ /dev/null @@ -1,43 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); - -.float bloodloss_timer; - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player)) - { - PHYS_INPUT_BUTTON_CROUCH(player) = true; - - if(time >= player.bloodloss_timer) - { - if(player.vehicle) - vehicles_exit(player.vehicle, VHEF_RELEASE); - if(player.event_damage) - player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0'); - player.bloodloss_timer = time + 0.5 + random() * 0.5; - } - } -} - -MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) -{ - entity player = M_ARGV(0, entity); - - if(player.health <= autocvar_g_bloodloss) - return true; -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss"); -} - -MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/module.inc b/qcsrc/common/mutators/mutator/bloodloss/module.inc deleted file mode 100644 index d3f665a18b..0000000000 --- a/qcsrc/common/mutators/mutator/bloodloss/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "bloodloss.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc new file mode 100644 index 0000000000..61b0c06016 --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qc @@ -0,0 +1,43 @@ +#include "sv_bloodloss.qh" + +REGISTER_MUTATOR(bloodloss, cvar("g_bloodloss")); + +.float bloodloss_timer; + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + if(player.health <= autocvar_g_bloodloss && !IS_DEAD(player)) + { + PHYS_INPUT_BUTTON_CROUCH(player) = true; + + if(time >= player.bloodloss_timer) + { + if(player.vehicle) + vehicles_exit(player.vehicle, VHEF_RELEASE); + if(player.event_damage) + player.event_damage(player, player, player, 1, DEATH_ROT.m_id, player.origin, '0 0 0'); + player.bloodloss_timer = time + 0.5 + random() * 0.5; + } + } +} + +MUTATOR_HOOKFUNCTION(bloodloss, PlayerJump) +{ + entity player = M_ARGV(0, entity); + + if(player.health <= autocvar_g_bloodloss) + return true; +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":bloodloss"); +} + +MUTATOR_HOOKFUNCTION(bloodloss, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Blood loss"); +} diff --git a/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/bloodloss/sv_bloodloss.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/breakablehook/_mod.inc b/qcsrc/common/mutators/mutator/breakablehook/_mod.inc index bdbbae46cd..11a080ef4a 100644 --- a/qcsrc/common/mutators/mutator/breakablehook/_mod.inc +++ b/qcsrc/common/mutators/mutator/breakablehook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/_mod.qh b/qcsrc/common/mutators/mutator/breakablehook/_mod.qh index 8a41908af3..f8b2d1bc65 100644 --- a/qcsrc/common/mutators/mutator/breakablehook/_mod.qh +++ b/qcsrc/common/mutators/mutator/breakablehook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc deleted file mode 100644 index ca266eb5af..0000000000 --- a/qcsrc/common/mutators/mutator/breakablehook/breakablehook.qc +++ /dev/null @@ -1,30 +0,0 @@ -#ifdef IMPLEMENTATION -#include -#include - -REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); - -bool autocvar_g_breakablehook; // allow toggling mid match? -bool autocvar_g_breakablehook_owner; - -MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(frag_target.classname == "grapplinghook") - { - if((!autocvar_g_breakablehook) - || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) - ) { M_ARGV(4, float) = 0; } - - // hurt the owner of the hook - if(DIFF_TEAM(frag_attacker, frag_target.realowner)) - { - Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); - RemoveGrapplingHook(frag_target.realowner); - return; // dead - } - } -} -#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/module.inc b/qcsrc/common/mutators/mutator/breakablehook/module.inc deleted file mode 100644 index 484eb4c563..0000000000 --- a/qcsrc/common/mutators/mutator/breakablehook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "breakablehook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc new file mode 100644 index 0000000000..fdb0dc38d1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qc @@ -0,0 +1,30 @@ +#include "sv_breakablehook.qh" + +#include +#include + +REGISTER_MUTATOR(breakablehook, cvar("g_breakablehook")); + +bool autocvar_g_breakablehook; // allow toggling mid match? +bool autocvar_g_breakablehook_owner; + +MUTATOR_HOOKFUNCTION(breakablehook, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(frag_target.classname == "grapplinghook") + { + if((!autocvar_g_breakablehook) + || (!autocvar_g_breakablehook_owner && frag_attacker == frag_target.realowner) + ) { M_ARGV(4, float) = 0; } + + // hurt the owner of the hook + if(DIFF_TEAM(frag_attacker, frag_target.realowner)) + { + Damage (frag_target.realowner, frag_attacker, frag_attacker, 5, WEP_HOOK.m_id | HITTYPE_SPLASH, frag_target.realowner.origin, '0 0 0'); + RemoveGrapplingHook(frag_target.realowner); + return; // dead + } + } +} diff --git a/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/breakablehook/sv_breakablehook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/buffs/_mod.inc b/qcsrc/common/mutators/mutator/buffs/_mod.inc index c06263a92f..715a3acfcc 100644 --- a/qcsrc/common/mutators/mutator/buffs/_mod.inc +++ b/qcsrc/common/mutators/mutator/buffs/_mod.inc @@ -1,3 +1,8 @@ // generated file; do not modify -#include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/_mod.qh b/qcsrc/common/mutators/mutator/buffs/_mod.qh index 2133c7250e..78217bb46e 100644 --- a/qcsrc/common/mutators/mutator/buffs/_mod.qh +++ b/qcsrc/common/mutators/mutator/buffs/_mod.qh @@ -1,3 +1,8 @@ // generated file; do not modify -#include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/buffs/all.inc b/qcsrc/common/mutators/mutator/buffs/all.inc index f6c25f7610..915f95329d 100644 --- a/qcsrc/common/mutators/mutator/buffs/all.inc +++ b/qcsrc/common/mutators/mutator/buffs/all.inc @@ -116,3 +116,11 @@ REGISTER_BUFF(LUCK) { 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_skin = 11; + this.m_color = '0.23 0.44 1'; +} +BUFF_SPAWNFUNCS(flight, BUFF_FLIGHT) diff --git a/qcsrc/common/mutators/mutator/buffs/all.qc b/qcsrc/common/mutators/mutator/buffs/all.qc deleted file mode 100644 index b056751624..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/all.qc +++ /dev/null @@ -1 +0,0 @@ -#include "all.qh" diff --git a/qcsrc/common/mutators/mutator/buffs/all.qh b/qcsrc/common/mutators/mutator/buffs/all.qh deleted file mode 100644 index 66f83d1c51..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/all.qh +++ /dev/null @@ -1,71 +0,0 @@ -#ifndef BUFFS_ALL_H -#define BUFFS_ALL_H - -#include -#include - -REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); -REGISTER_RADARICON(Buff, 1); - -REGISTRY(Buffs, BITS(5)) -#define Buffs_from(i) _Buffs_from(i, BUFF_Null) -REGISTER_REGISTRY(Buffs) -REGISTRY_CHECK(Buffs) - -#define REGISTER_BUFF(id) \ - REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff)) - -#include -CLASS(Buff, Pickup) - /** bit index */ - ATTRIB(Buff, m_itemid, int, 0) - ATTRIB(Buff, m_name, string, "buff") - ATTRIB(Buff, m_color, vector, '1 1 1') - ATTRIB(Buff, m_prettyName, 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)); - } -#ifdef SVQC - METHOD(Buff, m_time, float(Buff this)) - { return cvar(strcat("g_buffs_", this.netname, "_time")); } -#endif -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)); \ - }); -} - -#ifdef SVQC - // .int buffs = _STAT(BUFFS); - void buff_Init(entity ent); - void buff_Init_Compat(entity ent, entity replacement); - #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ - this.buffs = b.m_itemid; \ - this.team = t; \ - buff_Init(this); \ - } - #define BUFF_SPAWNFUNCS(e, b) \ - BUFF_SPAWNFUNC(e, b, 0) \ - BUFF_SPAWNFUNC(e##_team1, b, NUM_TEAM_1) \ - BUFF_SPAWNFUNC(e##_team2, b, NUM_TEAM_2) \ - BUFF_SPAWNFUNC(e##_team3, b, NUM_TEAM_3) \ - BUFF_SPAWNFUNC(e##_team4, b, NUM_TEAM_4) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); } -#else - #define BUFF_SPAWNFUNC(e, b, t) - #define BUFF_SPAWNFUNCS(e, b) - #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) -#endif - -REGISTER_BUFF(Null); -BUFF_SPAWNFUNCS(random, BUFF_Null) - -#include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qc b/qcsrc/common/mutators/mutator/buffs/buffs.qc index ec1fadabe5..f38d39d618 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qc +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qc @@ -1,1080 +1,16 @@ -#ifndef MUTATOR_BUFFS_H -#define MUTATOR_BUFFS_H +#include "buffs.qh" -#include "../instagib/module.inc" - -bool autocvar_g_buffs_effects; -float autocvar_g_buffs_waypoint_distance; -bool autocvar_g_buffs_randomize; -float autocvar_g_buffs_random_lifetime; -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; -float autocvar_g_buffs_cooldown_activate; -float autocvar_g_buffs_cooldown_respawn; -float autocvar_g_buffs_resistance_blockpercent; -float autocvar_g_buffs_medic_survive_chance; -float autocvar_g_buffs_medic_survive_health; -float autocvar_g_buffs_medic_rot; -float autocvar_g_buffs_medic_max; -float autocvar_g_buffs_medic_regen; -float autocvar_g_buffs_medic_heal_amount = 15; -float autocvar_g_buffs_medic_heal_delay = 1; -float autocvar_g_buffs_medic_heal_range = 400; -float autocvar_g_buffs_vengeance_damage_multiplier; -float autocvar_g_buffs_bash_force; -float autocvar_g_buffs_bash_force_self; -float autocvar_g_buffs_disability_slowtime; -float autocvar_g_buffs_disability_speed; -float autocvar_g_buffs_disability_rate; -float autocvar_g_buffs_disability_weaponspeed; -float autocvar_g_buffs_speed_speed; -float autocvar_g_buffs_speed_rate; -float autocvar_g_buffs_speed_weaponspeed; -float autocvar_g_buffs_speed_damage_take; -float autocvar_g_buffs_speed_regen; -float autocvar_g_buffs_vampire_damage_steal; -float autocvar_g_buffs_invisible_alpha; -float autocvar_g_buffs_jump_height; -float autocvar_g_buffs_inferno_burntime_factor; -float autocvar_g_buffs_inferno_burntime_min_time; -float autocvar_g_buffs_inferno_burntime_target_damage; -float autocvar_g_buffs_inferno_burntime_target_time; -float autocvar_g_buffs_inferno_damagemultiplier; -float autocvar_g_buffs_swapper_range; -float autocvar_g_buffs_magnet_range_item; -float autocvar_g_buffs_magnet_range_buff = 200; -float autocvar_g_buffs_luck_chance = 0.15; -float autocvar_g_buffs_luck_damagemultiplier = 3; - -// ammo -.float buff_ammo_prev_infitems; -.int buff_ammo_prev_clipload; -// invisible -.float buff_invisible_prev_alpha; -// medic -.float buff_medic_healtime; -// disability -.float buff_disability_time; -.float buff_disability_effect_time; -// common buff variables -.float buff_effect_delay; - -// buff definitions -.float buff_active; -.float buff_activetime; -.float buff_activetime_updated; -.entity buff_waypoint; -.int oldbuffs; // for updating effects -.entity buff_model; // controls effects (TODO: make csqc) - -const vector BUFF_MIN = ('-16 -16 -20'); -const vector BUFF_MAX = ('16 16 20'); - -// client side options -.float cvar_cl_buffs_autoreplace; -#endif - -#ifdef IMPLEMENTATION - -#include -#include - -.float buff_time = _STAT(BUFF_TIME); -void buffs_DelayedInit(entity this); - -REGISTER_MUTATOR(buffs, cvar("g_buffs")) -{ - MUTATOR_ONADD - { - InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET); - } -} - -bool buffs_BuffModel_Customize(entity this) -{ - entity player, myowner; - bool same_team; - - player = WaypointSprite_getviewentity(other); - myowner = this.owner; - same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); - - if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) - return false; - - if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player)) - return false; - - if(player == myowner || (IS_SPEC(other) && other.enemy == myowner)) - { - // somewhat hide the model, but keep the glow - this.effects = 0; - this.alpha = -1; - } - else - { - this.effects = EF_FULLBRIGHT | EF_LOWPRECISION; - this.alpha = 1; - } - return true; -} - -void buffs_BuffModel_Spawn(entity player) -{ - player.buff_model = spawn(); - setmodel(player.buff_model, MDL_BUFF); - setsize(player.buff_model, '0 0 -40', '0 0 40'); - setattachment(player.buff_model, player, ""); - setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); - player.buff_model.owner = player; - player.buff_model.scale = 0.7; - player.buff_model.pflags = PFLAGS_FULLDYNAMIC; - player.buff_model.light_lev = 200; - setcefc(player.buff_model, buffs_BuffModel_Customize); -} - -vector buff_GlowColor(entity buff) -{ - //if(buff.team) { return Team_ColorRGB(buff.team); } - return buff.m_color; -} - -void buff_Effect(entity player, string eff) -{ - if(!autocvar_g_buffs_effects) { return; } - - if(time >= player.buff_effect_delay) - { - Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - player.buff_effect_delay = time + 0.05; // prevent spam - } -} - -// buff item -bool buff_Waypoint_visible_for_player(entity this, entity player, entity view) -{ - if(!this.owner.buff_active && !this.owner.buff_activetime) - return false; - - if (view.buffs) - { - return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs; - } - - return WaypointSprite_visible_for_player(this, player, view); -} - -void buff_Waypoint_Spawn(entity e) -{ - entity buff = buff_FirstFromFlags(e.buffs); - entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff); - wp.wp_extra = buff.m_id; - WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); - e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; -} - -void buff_SetCooldown(entity this, float cd) -{ - cd = max(0, cd); - - if(!this.buff_waypoint) - buff_Waypoint_Spawn(this); - - WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd); - this.buff_activetime = cd; - this.buff_active = !cd; -} - -void buff_Respawn(entity this) -{ - if(gameover) { return; } - - vector oldbufforigin = this.origin; - this.velocity = '0 0 200'; - - if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, - ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) - { - entity spot = SelectSpawnPoint(this, true); - setorigin(this, spot.origin); - this.velocity = ((randomvec() * 100) + '0 0 200'); - this.angles = spot.angles; - } - - tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this); - - setorigin(this, trace_endpos); // attempt to unstick - - this.movetype = MOVETYPE_TOSS; - - makevectors(this.angles); - this.angles = '0 0 0'; - if(autocvar_g_buffs_random_lifetime > 0) - this.lifetime = time + autocvar_g_buffs_random_lifetime; - - Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - - WaypointSprite_Ping(this.buff_waypoint); - - sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) -} - -void buff_Touch(entity this) -{ - if(gameover) { return; } - - if(ITEM_TOUCH_NEEDKILL()) - { - buff_Respawn(this); - return; - } - - if((this.team && DIFF_TEAM(other, this)) - || (STAT(FROZEN, other)) - || (other.vehicle) - || (!this.buff_active) - ) - { - // can't touch this - return; - } - - if(MUTATOR_CALLHOOK(BuffTouch, this, other)) - return; - other = M_ARGV(1, entity); - - if(!IS_PLAYER(other)) - return; // incase mutator changed other - - if (other.buffs) - { - if (other.cvar_cl_buffs_autoreplace && other.buffs != this.buffs) - { - int buffid = buff_FirstFromFlags(other.buffs).m_id; - //Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_DROP, other.buffs); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, other.netname, buffid); - - other.buffs = 0; - //sound(other, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - else { return; } // do nothing - } - - this.owner = other; - this.buff_active = false; - this.lifetime = 0; - int buffid = buff_FirstFromFlags(this.buffs).m_id; - Send_Notification(NOTIF_ONE, other, MSG_MULTI, ITEM_BUFF_GOT, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_INFO, INFO_ITEM_BUFF, other.netname, buffid); - - Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - sound(other, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); - other.buffs |= (this.buffs); -} - -float buff_Available(entity buff) -{ - if (buff == BUFF_Null) - return false; - if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) - return false; - if (buff == BUFF_VAMPIRE && cvar("g_vampire")) - return false; - return cvar(strcat("g_buffs_", buff.m_name)); -} - -.int buff_seencount; - -void buff_NewType(entity ent, float cb) -{ - RandomSelection_Init(); - FOREACH(Buffs, buff_Available(it), LAMBDA( - it.buff_seencount += 1; - // if it's already been chosen, give it a lower priority - RandomSelection_Add(NULL, it.m_itemid, string_null, 1, max(0.2, 1 / it.buff_seencount)); - )); - ent.buffs = RandomSelection_chosen_float; -} - -void buff_Think(entity this) -{ - if(this.buffs != this.oldbuffs) - { - entity buff = buff_FirstFromFlags(this.buffs); - this.color = buff.m_color; - this.glowmod = buff_GlowColor(buff); - this.skin = buff.m_skin; - - setmodel(this, MDL_BUFF); - - if(this.buff_waypoint) - { - //WaypointSprite_Disown(this.buff_waypoint, 1); - WaypointSprite_Kill(this.buff_waypoint); - buff_Waypoint_Spawn(this); - if(this.buff_activetime) - WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime); - } - - this.oldbuffs = this.buffs; - } - - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - if(!this.buff_activetime_updated) - { - buff_SetCooldown(this, this.buff_activetime); - this.buff_activetime_updated = true; - } - - if(!this.buff_active && !this.buff_activetime) - if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs)) - { - buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime); - this.owner = NULL; - if(autocvar_g_buffs_randomize) - buff_NewType(this, this.buffs); - - if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) - buff_Respawn(this); - } - - if(this.buff_activetime) - if(!gameover) - if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) - { - this.buff_activetime = max(0, this.buff_activetime - frametime); - - if(!this.buff_activetime) - { - this.buff_active = true; - sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); - Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - } - } - - if(this.buff_active) - { - if(this.team && !this.buff_waypoint) - buff_Waypoint_Spawn(this); - - if(this.lifetime) - if(time >= this.lifetime) - buff_Respawn(this); - } - - this.nextthink = time; - //this.angles_y = time * 110.1; -} - -void buff_Waypoint_Reset(entity this) -{ - WaypointSprite_Kill(this.buff_waypoint); - - if(this.buff_activetime) { buff_Waypoint_Spawn(this); } -} - -void buff_Reset(entity this) -{ - if(autocvar_g_buffs_randomize) - buff_NewType(this, this.buffs); - this.owner = NULL; - buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate); - buff_Waypoint_Reset(this); - this.buff_activetime_updated = false; - - if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) - buff_Respawn(this); -} - -float buff_Customize(entity this) -{ - entity player = WaypointSprite_getviewentity(other); - if(!this.buff_active || (this.team && DIFF_TEAM(player, this))) - { - this.alpha = 0.3; - if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); } - this.pflags = 0; - } - else - { - this.alpha = 1; - if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; } - this.light_lev = 220 + 36 * sin(time); - this.pflags = PFLAGS_FULLDYNAMIC; - } - return true; -} - -void buff_Init(entity this) -{ - if(!cvar("g_buffs")) { remove(this); return; } - - if(!teamplay && this.team) { this.team = 0; } - - entity buff = buff_FirstFromFlags(this.buffs); - - if(!this.buffs || buff_Available(buff)) - buff_NewType(this, 0); - - this.classname = "item_buff"; - this.solid = SOLID_TRIGGER; - this.flags = FL_ITEM; - setthink(this, buff_Think); - settouch(this, buff_Touch); - this.reset = buff_Reset; - this.nextthink = time + 0.1; - this.gravity = 1; - this.movetype = MOVETYPE_TOSS; - this.scale = 1; - this.skin = buff.m_skin; - this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; - this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; - setcefc(this, buff_Customize); - //this.gravity = 100; - this.color = buff.m_color; - this.glowmod = buff_GlowColor(this); - buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime); - this.buff_active = !this.buff_activetime; - this.pflags = PFLAGS_FULLDYNAMIC; - - if(this.spawnflags & 1) - this.noalign = true; - - if(this.noalign) - this.movetype = MOVETYPE_NONE; // reset by random location - - setmodel(this, MDL_BUFF); - setsize(this, BUFF_MIN, BUFF_MAX); - - if(cvar("g_buffs_random_location") || (this.spawnflags & 64)) - buff_Respawn(this); -} - -void buff_Init_Compat(entity ent, entity replacement) -{ - if (ent.spawnflags & 2) - ent.team = NUM_TEAM_1; - else if (ent.spawnflags & 4) - ent.team = NUM_TEAM_2; - - ent.buffs = replacement.m_itemid; - - buff_Init(ent); -} - -void buff_SpawnReplacement(entity ent, entity old) -{ - setorigin(ent, old.origin); - ent.angles = old.angles; - ent.noalign = (old.noalign || (old.spawnflags & 1)); - - buff_Init(ent); -} - -void buff_Vengeance_DelayedDamage(entity this) -{ - if(this.enemy) - Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0'); - - remove(this); - return; -} - -// note: only really useful in teamplay -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(it.health < autocvar_g_balance_health_regenstable) - { - Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); - it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable); - } - }); -} - -float buff_Inferno_CalculateTime(float x, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) -{ - return offset_y + (intersect_y - offset_y) * logn(((x - offset_x) * ((base - 1) / intersect_x)) + 1, base); -} - -// mutator hooks -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) -{ - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(6, float); - float frag_damage = M_ARGV(7, float); - - if(frag_deathtype == DEATH_BUFF.m_id) { return; } - - if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) - { - vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); - M_ARGV(4, float) = v.x; // take - M_ARGV(5, float) = v.y; // save - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_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); - vector frag_force = M_ARGV(6, vector); - - if(frag_deathtype == DEATH_BUFF.m_id) { return; } - - if(frag_target.buffs & BUFF_SPEED.m_itemid) - if(frag_target != frag_attacker) - frag_damage *= autocvar_g_buffs_speed_damage_take; - - if(frag_target.buffs & BUFF_MEDIC.m_itemid) - if((frag_target.health - frag_damage) <= 0) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(frag_attacker) - if(random() <= autocvar_g_buffs_medic_survive_chance) - frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); - - if(frag_target.buffs & BUFF_JUMP.m_itemid) - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; - - if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) - if(frag_attacker) - if(frag_attacker != frag_target) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - { - entity dmgent = spawn(); - - dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; - dmgent.enemy = frag_attacker; - dmgent.owner = frag_target; - setthink(dmgent, buff_Vengeance_DelayedDamage); - dmgent.nextthink = time + 0.1; - } - - if(frag_target.buffs & BUFF_BASH.m_itemid) - if(frag_attacker != frag_target) - frag_force = '0 0 0'; - - if(frag_attacker.buffs & BUFF_BASH.m_itemid) - if(frag_force) - if(frag_attacker == frag_target) - frag_force *= autocvar_g_buffs_bash_force_self; - else - frag_force *= autocvar_g_buffs_bash_force; - - if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) - if(frag_target != frag_attacker) - frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; - - if(frag_target.buffs & BUFF_INFERNO.m_itemid) - { - if(frag_deathtype == DEATH_FIRE.m_id) - frag_damage = 0; - if(frag_deathtype == DEATH_LAVA.m_id) - frag_damage *= 0.5; // TODO: cvarize? - } - - if(frag_attacker.buffs & BUFF_LUCK.m_itemid) - if(frag_attacker != frag_target) - if(autocvar_g_buffs_luck_damagemultiplier > 0) - if(random() <= autocvar_g_buffs_luck_chance) - frag_damage *= autocvar_g_buffs_luck_damagemultiplier; - - if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) - if(frag_target != frag_attacker) { - float btime = buff_Inferno_CalculateTime( - frag_damage, - 0, - autocvar_g_buffs_inferno_burntime_min_time, - autocvar_g_buffs_inferno_burntime_target_damage, - autocvar_g_buffs_inferno_burntime_target_time, - autocvar_g_buffs_inferno_burntime_factor - ); - Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id); - } - - // this... is ridiculous (TODO: fix!) - if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) - if(!frag_target.vehicle) - if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) - if(!IS_DEAD(frag_target)) - if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) - if(frag_attacker != frag_target) - if(!STAT(FROZEN, frag_target)) - if(frag_target.takedamage) - if(DIFF_TEAM(frag_attacker, frag_target)) - { - frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); - if(frag_target.armorvalue) - frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); - } - - M_ARGV(4, float) = frag_damage; - M_ARGV(6, vector) = frag_force; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.buffs = 0; - // reset timers here to prevent them continuing after re-spawn - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; -} - -.float stat_sv_maxspeed; -.float stat_sv_airspeedlimit_nonqw; -.float stat_sv_jumpvelocity; - -MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - { - player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; - player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; - } - - if(time < player.buff_disability_time) - { - player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; - player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; - } - - if(player.buffs & BUFF_JUMP.m_itemid) - { - // automatically reset, no need to worry - player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerJump) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_JUMP.m_itemid) - M_ARGV(1, float) = autocvar_g_buffs_jump_height; -} - -MUTATOR_HOOKFUNCTION(buffs, MonsterMove) -{ - entity mon = M_ARGV(0, entity); - - if(time < mon.buff_disability_time) - { - M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed - M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - if(frag_target.buffs) - { - int buffid = buff_FirstFromFlags(frag_target.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); - frag_target.buffs = 0; - - if(frag_target.buff_model) - { - remove(frag_target.buff_model); - frag_target.buff_model = NULL; - } - } -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) -{ - if(MUTATOR_RETURNVALUE || gameover) { return; } - - entity player = M_ARGV(0, entity); - - if(player.buffs) - { - int buffid = buff_FirstFromFlags(player.buffs).m_id; - Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); - Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); - - player.buffs = 0; - sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - return true; - } -} - -MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) -{ - if(MUTATOR_RETURNVALUE || gameover) { return; } - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_SWAPPER.m_itemid) - { - float best_distance = autocvar_g_buffs_swapper_range; - entity closest = NULL; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle) - if(DIFF_TEAM(it, player)) - { - float test = vlen2(player.origin - it.origin); - if(test <= best_distance * best_distance) - { - best_distance = sqrt(test); - closest = it; - } - } - )); - - if(closest) - { - vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; - - my_org = player.origin; - my_vel = player.velocity; - my_ang = player.angles; - their_org = closest.origin; - their_vel = closest.velocity; - their_ang = closest.angles; - - Drop_Special_Items(closest); - - MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper - - setorigin(player, their_org); - setorigin(closest, my_org); - - closest.velocity = my_vel; - closest.angles = my_ang; - closest.fixangle = true; - closest.oldorigin = my_org; - closest.oldvelocity = my_vel; - player.velocity = their_vel; - player.angles = their_ang; - player.fixangle = true; - player.oldorigin = their_org; - player.oldvelocity = their_vel; - - // set pusher so player gets the kill if they fall into void - closest.pusher = player; - closest.pushltime = time + autocvar_g_maxpushtime; - closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest); - - Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); - Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); - - sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); - - // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam - player.buffs = 0; - return true; - } - } -} - -bool buffs_RemovePlayer(entity player) -{ - if(player.buff_model) - { - remove(player.buff_model); - player.buff_model = NULL; - } - - // also reset timers here to prevent them continuing after spectating - player.buff_disability_time = 0; - player.buff_disability_effect_time = 0; - - return false; -} -MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } -MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } - -MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity player = M_ARGV(1, entity); - - entity e = WaypointSprite_getviewentity(player); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player)) - if(DIFF_TEAM(wp.owner, e)) - return true; -} - -MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) -{ - entity ent = M_ARGV(0, entity); - - if(autocvar_g_buffs_replace_powerups) - switch(ent.classname) - { - case "item_strength": - case "item_invincible": - { - entity e = spawn(); - buff_SpawnReplacement(e, ent); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) -{ - entity player = M_ARGV(1, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(0, float) *= autocvar_g_buffs_speed_rate; - - if(time < player.buff_disability_time) - M_ARGV(0, float) *= autocvar_g_buffs_disability_rate; -} - -MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) -{ - entity player = M_ARGV(1, entity); - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed; - - if(time < player.buff_disability_time) - M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(gameover || IS_DEAD(player)) { return; } - - if(time < player.buff_disability_time) - if(time >= player.buff_disability_effect_time) - { - Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); - player.buff_disability_effect_time = time + 0.5; - } - - // handle buff lost status - // 1: notify everyone else - // 2: notify carrier as well - int buff_lost = 0; - - if(player.buff_time) - if(time >= player.buff_time) - buff_lost = 2; - - if(STAT(FROZEN, player)) { buff_lost = 1; } - - if(buff_lost) - { - if(player.buffs) - { - int buffid = buff_FirstFromFlags(player.buffs).m_id; - Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); - if(buff_lost >= 2) - { - Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? - sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); - } - player.buffs = 0; - } - } - - if(player.buffs & BUFF_MAGNET.m_itemid) - { - vector pickup_size; - FOREACH_ENTITY_FLAGS(flags, FL_ITEM, - { - if(it.buffs) - pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff; - else - pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; - - if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax)) - { - if(gettouch(it)) - { - entity oldother = other; - other = player; - gettouch(it)(it); - other = oldother; - } - } - }); - } - - if(player.buffs & BUFF_AMMO.m_itemid) - if(player.clip_size) - player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; - - if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid)) - if(player.alpha != autocvar_g_buffs_invisible_alpha) - player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) - - if(player.buffs & BUFF_MEDIC.m_itemid) - if(time >= player.buff_medic_healtime) - { - buff_Medic_Heal(player); - player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay; - } - -#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid)) -#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) && (player.oldbuffs & (b).m_itemid)) - - if(player.buffs != player.oldbuffs) - { - entity buff = buff_FirstFromFlags(player.buffs); - float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; - player.buff_time = (bufftime) ? time + bufftime : 0; - - BUFF_ONADD(BUFF_AMMO) - { - player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO); - player.items |= IT_UNLIMITED_WEAPON_AMMO; - - if(player.clip_load) - player.buff_ammo_prev_clipload = player.clip_load; - player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; - } - - BUFF_ONREM(BUFF_AMMO) - { - if(player.buff_ammo_prev_infitems) - player.items |= IT_UNLIMITED_WEAPON_AMMO; - else - player.items &= ~IT_UNLIMITED_WEAPON_AMMO; - - if(player.buff_ammo_prev_clipload) - player.clip_load = player.buff_ammo_prev_clipload; - } - - BUFF_ONADD(BUFF_INVISIBLE) - { - if(time < player.strength_finished && g_instagib) - player.alpha = autocvar_g_instagib_invis_alpha; - else - player.alpha = player.buff_invisible_prev_alpha; - player.alpha = autocvar_g_buffs_invisible_alpha; - } - - BUFF_ONREM(BUFF_INVISIBLE) - player.alpha = player.buff_invisible_prev_alpha; - - player.oldbuffs = player.buffs; - if(player.buffs) - { - if(!player.buff_model) - buffs_BuffModel_Spawn(player); - - player.buff_model.color = buff.m_color; - player.buff_model.glowmod = buff_GlowColor(player.buff_model); - player.buff_model.skin = buff.m_skin; - - player.effects |= EF_NOSHADOW; - } - else - { - remove(player.buff_model); - player.buff_model = NULL; - - player.effects &= ~(EF_NOSHADOW); - } - } - - if(player.buff_model) - { - player.buff_model.effects = player.effects; - player.buff_model.effects |= EF_LOWPRECISION; - player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance - - player.buff_model.alpha = player.alpha; - } - -#undef BUFF_ONADD -#undef BUFF_ONREM -} - -MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.buffs = spectatee.buffs; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) -{ - entity player = M_ARGV(0, entity); - entity veh = M_ARGV(1, entity); - - veh.buffs = player.buffs; - player.buffs = 0; - veh.buff_time = max(0, player.buff_time - time); - player.buff_time = 0; -} - -MUTATOR_HOOKFUNCTION(buffs, VehicleExit) -{ - entity player = M_ARGV(0, entity); - entity veh = M_ARGV(1, entity); - - player.buffs = player.oldbuffs = veh.buffs; - veh.buffs = 0; - player.buff_time = time + veh.buff_time; - veh.buff_time = 0; -} - -MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) -{ - entity player = M_ARGV(0, entity); - - if(player.buffs & BUFF_MEDIC.m_itemid) - { - M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod - M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod - M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod - } - - if(player.buffs & BUFF_SPEED.m_itemid) - M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod -} - -REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace"); - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs"); -} - -MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +string BUFF_NAME(int i) { - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs"); + Buff b = Buffs_from(i); + return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName); } -void buffs_DelayedInit(entity this) +entity buff_FirstFromFlags(int _buffs) { - if(autocvar_g_buffs_spawn_count > 0) - if(find(NULL, classname, "item_buff") == NULL) - { - float i; - for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) - { - entity e = spawn(); - e.spawnflags |= 64; // always randomize - e.velocity = randomvec() * 250; // this gets reset anyway if random location works - buff_Init(e); - } - } + if (flags) + { + FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); + } + return BUFF_Null; } -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh new file mode 100644 index 0000000000..ae76d9f59e --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qh @@ -0,0 +1,70 @@ +#pragma once + +#include +#include + +#ifdef GAMEQC +REGISTER_WAYPOINT(Buff, _("Buff"), '1 0.5 0', 1); +REGISTER_RADARICON(Buff, 1); +#endif + +REGISTRY(Buffs, BITS(5)) +#define Buffs_from(i) _Buffs_from(i, BUFF_Null) +REGISTER_REGISTRY(Buffs) +REGISTRY_CHECK(Buffs) + +#define REGISTER_BUFF(id) \ + REGISTER(Buffs, BUFF_##id, m_id, NEW(Buff)) + +#include +CLASS(Buff, Pickup) + /** bit index */ + ATTRIB(Buff, m_itemid, int, 0); + ATTRIB(Buff, m_name, string, "buff"); + ATTRIB(Buff, m_color, vector, '1 1 1'); + ATTRIB(Buff, m_prettyName, 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)); + } +#ifdef SVQC + METHOD(Buff, m_time, float(Buff this)) + { return cvar(strcat("g_buffs_", this.netname, "_time")); } +#endif +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)); \ + }); +} + +#ifdef SVQC + // .int buffs = _STAT(BUFFS); + void buff_Init(entity ent); + void buff_Init_Compat(entity ent, entity replacement); + #define BUFF_SPAWNFUNC(e, b, t) spawnfunc(item_buff_##e) { \ + this.buffs = b.m_itemid; \ + this.team = t; \ + buff_Init(this); \ + } + #define BUFF_SPAWNFUNCS(e, b) \ + BUFF_SPAWNFUNC(e, b, 0) \ + BUFF_SPAWNFUNC(e##_team1, b, NUM_TEAM_1) \ + BUFF_SPAWNFUNC(e##_team2, b, NUM_TEAM_2) \ + BUFF_SPAWNFUNC(e##_team3, b, NUM_TEAM_3) \ + BUFF_SPAWNFUNC(e##_team4, b, NUM_TEAM_4) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) spawnfunc(item_##o) { buff_Init_Compat(this, r); } +#else + #define BUFF_SPAWNFUNC(e, b, t) + #define BUFF_SPAWNFUNCS(e, b) + #define BUFF_SPAWNFUNC_Q3TA_COMPAT(o, r) +#endif + +REGISTER_BUFF(Null); +BUFF_SPAWNFUNCS(random, BUFF_Null) + +#include "all.inc" diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc new file mode 100644 index 0000000000..ca14753727 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qc @@ -0,0 +1,22 @@ +#include "cl_buffs.qh" + +REGISTER_MUTATOR(cl_buffs, true); +MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) +{ + int allBuffs = STAT(BUFFS); + FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( + addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); + )); +} +MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) +{ + entity this = M_ARGV(0, entity); + string s = M_ARGV(1, string); + if (s == WP_Buff.netname || s == RADARICON_Buff.netname) + { + Buff b = Buffs_from(this.wp_extra); + M_ARGV(2, vector) = b.m_color; + M_ARGV(3, string) = b.m_prettyName; + return true; + } +} diff --git a/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh new file mode 100644 index 0000000000..c93902291d --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/cl_buffs.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "buffs.qh" diff --git a/qcsrc/common/mutators/mutator/buffs/module.inc b/qcsrc/common/mutators/mutator/buffs/module.inc deleted file mode 100644 index c24892836a..0000000000 --- a/qcsrc/common/mutators/mutator/buffs/module.inc +++ /dev/null @@ -1,46 +0,0 @@ -#include "all.qc" -#ifdef SVQC -#include "buffs.qc" -#endif - -#ifdef IMPLEMENTATION - -string BUFF_NAME(int i) -{ - Buff b = Buffs_from(i); - return sprintf("%s%s", rgb_to_hexcolor(b.m_color), b.m_prettyName); -} - -entity buff_FirstFromFlags(int _buffs) -{ - if (flags) - { - FOREACH(Buffs, it.m_itemid & _buffs, LAMBDA(return it)); - } - return BUFF_Null; -} - -#ifdef CSQC -REGISTER_MUTATOR(cl_buffs, true); -MUTATOR_HOOKFUNCTION(cl_buffs, HUD_Powerups_add) -{ - int allBuffs = STAT(BUFFS); - FOREACH(Buffs, it.m_itemid & allBuffs, LAMBDA( - addPowerupItem(it.m_prettyName, strcat("buff_", it.m_name), it.m_color, bound(0, STAT(BUFF_TIME) - time, 99), 60); - )); -} -MUTATOR_HOOKFUNCTION(cl_buffs, WP_Format) -{ - entity this = M_ARGV(0, entity); - string s = M_ARGV(1, string); - if (s == WP_Buff.netname || s == RADARICON_Buff.netname) - { - Buff b = Buffs_from(this.wp_extra); - M_ARGV(2, vector) = b.m_color; - M_ARGV(3, string) = b.m_prettyName; - return true; - } -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc new file mode 100644 index 0000000000..d9eed031cd --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qc @@ -0,0 +1,1029 @@ +#include "sv_buffs.qh" + +#include +#include + +.float buff_time = _STAT(BUFF_TIME); +void buffs_DelayedInit(entity this); + +AUTOCVAR(g_buffs, int, -1, _("Enable buffs, -1: enabled but no auto location or replacing powerups, 1: enabled and can replace them")); + +REGISTER_MUTATOR(buffs, autocvar_g_buffs) +{ + MUTATOR_ONADD + { + if(autocvar_g_buffs > 0) + InitializeEntity(NULL, buffs_DelayedInit, INITPRIO_FINDTARGET); + } +} + +bool buffs_BuffModel_Customize(entity this, entity client) +{ + entity player, myowner; + bool same_team; + + player = WaypointSprite_getviewentity(client); + myowner = this.owner; + same_team = (SAME_TEAM(player, myowner) || SAME_TEAM(player, myowner)); + + if(myowner.alpha <= 0.5 && !same_team && myowner.alpha != 0) + return false; + + if(MUTATOR_CALLHOOK(BuffModel_Customize, this, player)) + return false; + + if(player == myowner || (IS_SPEC(client) && client.enemy == myowner)) + { + // somewhat hide the model, but keep the glow + this.effects = 0; + this.alpha = -1; + } + else + { + this.effects = EF_FULLBRIGHT | EF_LOWPRECISION; + this.alpha = 1; + } + return true; +} + +void buffs_BuffModel_Spawn(entity player) +{ + player.buff_model = spawn(); + setmodel(player.buff_model, MDL_BUFF); + setsize(player.buff_model, '0 0 -40', '0 0 40'); + setattachment(player.buff_model, player, ""); + setorigin(player.buff_model, '0 0 1' * (player.buff_model.maxs.z * 1)); + player.buff_model.owner = player; + player.buff_model.scale = 0.7; + player.buff_model.pflags = PFLAGS_FULLDYNAMIC; + player.buff_model.light_lev = 200; + setcefc(player.buff_model, buffs_BuffModel_Customize); +} + +vector buff_GlowColor(entity buff) +{ + //if(buff.team) { return Team_ColorRGB(buff.team); } + return buff.m_color; +} + +void buff_Effect(entity player, string eff) +{ + if(!autocvar_g_buffs_effects) { return; } + + if(time >= player.buff_effect_delay) + { + Send_Effect_(eff, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + player.buff_effect_delay = time + 0.05; // prevent spam + } +} + +// buff item +bool buff_Waypoint_visible_for_player(entity this, entity player, entity view) +{ + if(!this.owner.buff_active && !this.owner.buff_activetime) + return false; + + if (view.buffs) + { + return view.cvar_cl_buffs_autoreplace == false || view.buffs != this.owner.buffs; + } + + return WaypointSprite_visible_for_player(this, player, view); +} + +void buff_Waypoint_Spawn(entity e) +{ + entity buff = buff_FirstFromFlags(e.buffs); + entity wp = WaypointSprite_Spawn(WP_Buff, 0, autocvar_g_buffs_waypoint_distance, e, '0 0 1' * e.maxs.z, NULL, e.team, e, buff_waypoint, true, RADARICON_Buff); + wp.wp_extra = buff.m_id; + WaypointSprite_UpdateTeamRadar(e.buff_waypoint, RADARICON_Buff, e.glowmod); + e.buff_waypoint.waypointsprite_visible_for_player = buff_Waypoint_visible_for_player; +} + +void buff_SetCooldown(entity this, float cd) +{ + cd = max(0, cd); + + if(!this.buff_waypoint) + buff_Waypoint_Spawn(this); + + WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + cd); + this.buff_activetime = cd; + this.buff_active = !cd; +} + +void buff_Respawn(entity this) +{ + if(gameover) { return; } + + vector oldbufforigin = this.origin; + this.velocity = '0 0 200'; + + if(!MoveToRandomMapLocation(this, DPCONTENTS_SOLID | DPCONTENTS_CORPSE | DPCONTENTS_PLAYERCLIP, DPCONTENTS_SLIME | DPCONTENTS_LAVA | DPCONTENTS_SKY | DPCONTENTS_BODY | DPCONTENTS_DONOTENTER, Q3SURFACEFLAG_SKY, + ((autocvar_g_buffs_random_location_attempts > 0) ? autocvar_g_buffs_random_location_attempts : 10), 1024, 256)) + { + entity spot = SelectSpawnPoint(this, true); + setorigin(this, spot.origin); + this.velocity = ((randomvec() * 100) + '0 0 200'); + this.angles = spot.angles; + } + + tracebox(this.origin, this.mins * 1.5, this.maxs * 1.5, this.origin, MOVE_NOMONSTERS, this); + + setorigin(this, trace_endpos); // attempt to unstick + + set_movetype(this, MOVETYPE_TOSS); + + makevectors(this.angles); + this.angles = '0 0 0'; + if(autocvar_g_buffs_random_lifetime > 0) + this.lifetime = time + autocvar_g_buffs_random_lifetime; + + Send_Effect(EFFECT_ELECTRO_COMBO, oldbufforigin + ((this.mins + this.maxs) * 0.5), '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + + WaypointSprite_Ping(this.buff_waypoint); + + sound(this, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) +} + +void buff_Touch(entity this, entity toucher) +{ + if(gameover) { return; } + + if(ITEM_TOUCH_NEEDKILL()) + { + buff_Respawn(this); + return; + } + + if((this.team && DIFF_TEAM(toucher, this)) + || (STAT(FROZEN, toucher)) + || (toucher.vehicle) + || (!this.buff_active) + ) + { + // can't touch this + return; + } + + if(MUTATOR_CALLHOOK(BuffTouch, this, toucher)) + return; + toucher = M_ARGV(1, entity); + + if(!IS_PLAYER(toucher)) + return; // incase mutator changed toucher + + if (toucher.buffs) + { + if (toucher.cvar_cl_buffs_autoreplace && toucher.buffs != this.buffs) + { + int buffid = buff_FirstFromFlags(toucher.buffs).m_id; + //Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_DROP, toucher.buffs); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_ITEM_BUFF_LOST, toucher.netname, buffid); + + toucher.buffs = 0; + //sound(toucher, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else { return; } // do nothing + } + + this.owner = toucher; + this.buff_active = false; + this.lifetime = 0; + int buffid = buff_FirstFromFlags(this.buffs).m_id; + Send_Notification(NOTIF_ONE, toucher, MSG_MULTI, ITEM_BUFF_GOT, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, toucher, MSG_INFO, INFO_ITEM_BUFF, toucher.netname, buffid); + + Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + sound(toucher, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTN_NORM); + toucher.buffs |= (this.buffs); +} + +float buff_Available(entity buff) +{ + if (buff == BUFF_Null) + return false; + if (buff == BUFF_AMMO && ((start_items & IT_UNLIMITED_WEAPON_AMMO) || (start_items & IT_UNLIMITED_AMMO) || (cvar("g_melee_only")))) + return false; + if (buff == BUFF_VAMPIRE && cvar("g_vampire")) + return false; + return cvar(strcat("g_buffs_", buff.m_name)); +} + +.int buff_seencount; + +void buff_NewType(entity ent, float cb) +{ + RandomSelection_Init(); + FOREACH(Buffs, buff_Available(it), LAMBDA( + it.buff_seencount += 1; + // if it's already been chosen, give it a lower priority + RandomSelection_AddFloat(it.m_itemid, 1, max(0.2, 1 / it.buff_seencount)); + )); + ent.buffs = RandomSelection_chosen_float; +} + +void buff_Think(entity this) +{ + if(this.buffs != this.oldbuffs) + { + entity buff = buff_FirstFromFlags(this.buffs); + this.color = buff.m_color; + this.glowmod = buff_GlowColor(buff); + this.skin = buff.m_skin; + + setmodel(this, MDL_BUFF); + setsize(this, BUFF_MIN, BUFF_MAX); + + if(this.buff_waypoint) + { + //WaypointSprite_Disown(this.buff_waypoint, 1); + WaypointSprite_Kill(this.buff_waypoint); + buff_Waypoint_Spawn(this); + if(this.buff_activetime) + WaypointSprite_UpdateBuildFinished(this.buff_waypoint, time + this.buff_activetime - frametime); + } + + this.oldbuffs = this.buffs; + } + + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + if(!this.buff_activetime_updated) + { + buff_SetCooldown(this, this.buff_activetime); + this.buff_activetime_updated = true; + } + + if(!this.buff_active && !this.buff_activetime) + if(!this.owner || STAT(FROZEN, this.owner) || IS_DEAD(this.owner) || !this.owner.iscreature || !(this.owner.buffs & this.buffs)) + { + buff_SetCooldown(this, autocvar_g_buffs_cooldown_respawn + frametime); + this.owner = NULL; + if(autocvar_g_buffs_randomize) + buff_NewType(this, this.buffs); + + if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) + buff_Respawn(this); + } + + if(this.buff_activetime) + if(!gameover) + if((round_handler_IsActive() && !round_handler_IsRoundStarted()) || time >= game_starttime) + { + this.buff_activetime = max(0, this.buff_activetime - frametime); + + if(!this.buff_activetime) + { + this.buff_active = true; + sound(this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTN_NORM); + Send_Effect(EFFECT_ITEM_RESPAWN, CENTER_OR_VIEWOFS(this), '0 0 0', 1); + } + } + + if(this.buff_active) + { + if(this.team && !this.buff_waypoint) + buff_Waypoint_Spawn(this); + + if(this.lifetime) + if(time >= this.lifetime) + buff_Respawn(this); + } + + this.nextthink = time; + //this.angles_y = time * 110.1; +} + +void buff_Waypoint_Reset(entity this) +{ + WaypointSprite_Kill(this.buff_waypoint); + + if(this.buff_activetime) { buff_Waypoint_Spawn(this); } +} + +void buff_Reset(entity this) +{ + if(autocvar_g_buffs_randomize) + buff_NewType(this, this.buffs); + this.owner = NULL; + buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate); + buff_Waypoint_Reset(this); + this.buff_activetime_updated = false; + + if(autocvar_g_buffs_random_location || (this.spawnflags & 64)) + buff_Respawn(this); +} + +bool buff_Customize(entity this, entity client) +{ + entity player = WaypointSprite_getviewentity(client); + if(!this.buff_active || (this.team && DIFF_TEAM(player, this))) + { + this.alpha = 0.3; + if(this.effects & EF_FULLBRIGHT) { this.effects &= ~(EF_FULLBRIGHT); } + this.pflags = 0; + } + else + { + this.alpha = 1; + if(!(this.effects & EF_FULLBRIGHT)) { this.effects |= EF_FULLBRIGHT; } + this.light_lev = 220 + 36 * sin(time); + this.pflags = PFLAGS_FULLDYNAMIC; + } + return true; +} + +void buff_Init(entity this) +{ + if(!cvar("g_buffs")) { delete(this); return; } + + if(!teamplay && this.team) { this.team = 0; } + + entity buff = buff_FirstFromFlags(this.buffs); + + if(!this.buffs || buff_Available(buff)) + buff_NewType(this, 0); + + this.classname = "item_buff"; + this.solid = SOLID_TRIGGER; + this.flags = FL_ITEM; + this.bot_pickup = true; + this.bot_pickupevalfunc = commodity_pickupevalfunc; + this.bot_pickupbasevalue = 1000; + IL_PUSH(g_items, this); + setthink(this, buff_Think); + settouch(this, buff_Touch); + this.reset = buff_Reset; + this.nextthink = time + 0.1; + this.gravity = 1; + set_movetype(this, MOVETYPE_TOSS); + this.scale = 1; + this.skin = buff.m_skin; + this.effects = EF_FULLBRIGHT | EF_STARDUST | EF_NOSHADOW; + this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY; + setcefc(this, buff_Customize); + //this.gravity = 100; + this.color = buff.m_color; + this.glowmod = buff_GlowColor(this); + buff_SetCooldown(this, autocvar_g_buffs_cooldown_activate + game_starttime); + this.buff_active = !this.buff_activetime; + this.pflags = PFLAGS_FULLDYNAMIC; + + if(this.spawnflags & 1) + this.noalign = true; + + if(this.noalign) + set_movetype(this, MOVETYPE_NONE); // reset by random location + + setmodel(this, MDL_BUFF); + setsize(this, BUFF_MIN, BUFF_MAX); + + if(cvar("g_buffs_random_location") || (this.spawnflags & 64)) + buff_Respawn(this); +} + +void buff_Init_Compat(entity ent, entity replacement) +{ + if (ent.spawnflags & 2) + ent.team = NUM_TEAM_1; + else if (ent.spawnflags & 4) + ent.team = NUM_TEAM_2; + + ent.buffs = replacement.m_itemid; + + buff_Init(ent); +} + +void buff_SpawnReplacement(entity ent, entity old) +{ + setorigin(ent, old.origin); + ent.angles = old.angles; + ent.noalign = (old.noalign || (old.spawnflags & 1)); + + buff_Init(ent); +} + +void buff_Vengeance_DelayedDamage(entity this) +{ + if(this.enemy) + Damage(this.enemy, this.owner, this.owner, this.dmg, DEATH_BUFF.m_id, this.enemy.origin, '0 0 0'); + + delete(this); + return; +} + +// note: only really useful in teamplay +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(it.health < autocvar_g_balance_health_regenstable) + { + Send_Effect(EFFECT_HEALING, it.origin, '0 0 0', 1); + it.health = bound(0, it.health + autocvar_g_buffs_medic_heal_amount, autocvar_g_balance_health_regenstable); + } + }); +} + +float buff_Inferno_CalculateTime(float damg, float offset_x, float offset_y, float intersect_x, float intersect_y, float base) +{ + return offset_y + (intersect_y - offset_y) * logn(((damg - offset_x) * ((base - 1) / intersect_x)) + 1, base); +} + +// mutator hooks +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_SplitHealthArmor) +{ + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(6, float); + float frag_damage = M_ARGV(7, float); + + if(frag_deathtype == DEATH_BUFF.m_id) { return; } + + if(frag_target.buffs & BUFF_RESISTANCE.m_itemid) + { + vector v = healtharmor_applydamage(50, autocvar_g_buffs_resistance_blockpercent, frag_deathtype, frag_damage); + M_ARGV(4, float) = v.x; // take + M_ARGV(5, float) = v.y; // save + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDamage_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); + vector frag_force = M_ARGV(6, vector); + + if(frag_deathtype == DEATH_BUFF.m_id) { return; } + + if(frag_target.buffs & BUFF_SPEED.m_itemid) + if(frag_target != frag_attacker) + frag_damage *= autocvar_g_buffs_speed_damage_take; + + if(frag_target.buffs & BUFF_MEDIC.m_itemid) + if((frag_target.health - frag_damage) <= 0) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(frag_attacker) + if(random() <= autocvar_g_buffs_medic_survive_chance) + frag_damage = max(5, frag_target.health - autocvar_g_buffs_medic_survive_health); + + if(frag_target.buffs & BUFF_JUMP.m_itemid) + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; + + if(frag_target.buffs & BUFF_VENGEANCE.m_itemid) + if(frag_attacker) + if(frag_attacker != frag_target) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + { + entity dmgent = spawn(); + + dmgent.dmg = frag_damage * autocvar_g_buffs_vengeance_damage_multiplier; + dmgent.enemy = frag_attacker; + dmgent.owner = frag_target; + setthink(dmgent, buff_Vengeance_DelayedDamage); + dmgent.nextthink = time + 0.1; + } + + if(frag_target.buffs & BUFF_BASH.m_itemid) + if(frag_attacker != frag_target) + frag_force = '0 0 0'; + + if(frag_attacker.buffs & BUFF_BASH.m_itemid) + if(frag_force) + if(frag_attacker == frag_target) + frag_force *= autocvar_g_buffs_bash_force_self; + else + frag_force *= autocvar_g_buffs_bash_force; + + if(frag_attacker.buffs & BUFF_DISABILITY.m_itemid) + if(frag_target != frag_attacker) + frag_target.buff_disability_time = time + autocvar_g_buffs_disability_slowtime; + + if(frag_target.buffs & BUFF_INFERNO.m_itemid) + { + if(frag_deathtype == DEATH_FIRE.m_id) + frag_damage = 0; + if(frag_deathtype == DEATH_LAVA.m_id) + frag_damage *= 0.5; // TODO: cvarize? + } + + if(frag_attacker.buffs & BUFF_LUCK.m_itemid) + if(frag_attacker != frag_target) + if(autocvar_g_buffs_luck_damagemultiplier > 0) + if(random() <= autocvar_g_buffs_luck_chance) + frag_damage *= autocvar_g_buffs_luck_damagemultiplier; + + if(frag_attacker.buffs & BUFF_INFERNO.m_itemid) + if(frag_target != frag_attacker) { + float btime = buff_Inferno_CalculateTime( + frag_damage, + 0, + autocvar_g_buffs_inferno_burntime_min_time, + autocvar_g_buffs_inferno_burntime_target_damage, + autocvar_g_buffs_inferno_burntime_target_time, + autocvar_g_buffs_inferno_burntime_factor + ); + Fire_AddDamage(frag_target, frag_attacker, (frag_damage * autocvar_g_buffs_inferno_damagemultiplier), btime, DEATH_BUFF.m_id); + } + + // this... is ridiculous (TODO: fix!) + if(frag_attacker.buffs & BUFF_VAMPIRE.m_itemid) + if(!frag_target.vehicle) + if(!ITEM_DAMAGE_NEEDKILL(frag_deathtype)) + if(!IS_DEAD(frag_target)) + if(IS_PLAYER(frag_target) || IS_MONSTER(frag_target)) + if(frag_attacker != frag_target) + if(!STAT(FROZEN, frag_target)) + if(frag_target.takedamage) + if(DIFF_TEAM(frag_attacker, frag_target)) + { + frag_attacker.health = bound(0, frag_attacker.health + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.health), g_pickup_healthsmall_max); + if(frag_target.armorvalue) + frag_attacker.armorvalue = bound(0, frag_attacker.armorvalue + bound(0, frag_damage * autocvar_g_buffs_vampire_damage_steal, frag_target.armorvalue), g_pickup_armorsmall_max); + } + + M_ARGV(4, float) = frag_damage; + M_ARGV(6, vector) = frag_force; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.buffs = 0; + player.buff_time = 0; + // reset timers here to prevent them continuing after re-spawn + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; +} + +.float stat_sv_maxspeed; +.float stat_sv_airspeedlimit_nonqw; +.float stat_sv_jumpvelocity; + +MUTATOR_HOOKFUNCTION(buffs, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + { + player.stat_sv_maxspeed *= autocvar_g_buffs_speed_speed; + player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_speed_speed; + } + + if(time < player.buff_disability_time) + { + player.stat_sv_maxspeed *= autocvar_g_buffs_disability_speed; + player.stat_sv_airspeedlimit_nonqw *= autocvar_g_buffs_disability_speed; + } + + if(player.buffs & BUFF_JUMP.m_itemid) + { + // automatically reset, no need to worry + player.stat_sv_jumpvelocity = autocvar_g_buffs_jump_height; + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerJump) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_JUMP.m_itemid) + M_ARGV(1, float) = autocvar_g_buffs_jump_height; + + if(player.buffs & BUFF_FLIGHT.m_itemid) + if(!IS_JUMP_HELD(player) && PHYS_INPUT_BUTTON_CROUCH(player)) + player.gravity *= -1; +} + +MUTATOR_HOOKFUNCTION(buffs, MonsterMove) +{ + entity mon = M_ARGV(0, entity); + + if(time < mon.buff_disability_time) + { + M_ARGV(1, float) *= autocvar_g_buffs_disability_speed; // run speed + M_ARGV(2, float) *= autocvar_g_buffs_disability_speed; // walk speed + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + if(frag_target.buffs) + { + int buffid = buff_FirstFromFlags(frag_target.buffs).m_id; + Send_Notification(NOTIF_ALL_EXCEPT, frag_target, MSG_INFO, INFO_ITEM_BUFF_LOST, frag_target.netname, buffid); + frag_target.buffs = 0; + + if(frag_target.buff_model) + { + delete(frag_target.buff_model); + frag_target.buff_model = NULL; + } + } +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerUseKey, CBC_ORDER_FIRST) +{ + if(MUTATOR_RETURNVALUE || gameover) { return; } + + entity player = M_ARGV(0, entity); + + if(player.buffs) + { + int buffid = buff_FirstFromFlags(player.buffs).m_id; + Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); + + player.buffs = 0; + player.buff_time = 0; // already notified + sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + return true; + } +} + +MUTATOR_HOOKFUNCTION(buffs, ForbidThrowCurrentWeapon) +{ + if(MUTATOR_RETURNVALUE || gameover) { return; } + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_SWAPPER.m_itemid) + { + float best_distance = autocvar_g_buffs_swapper_range; + entity closest = NULL; + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(!IS_DEAD(it) && !STAT(FROZEN, it) && !it.vehicle) + if(DIFF_TEAM(it, player)) + { + float test = vlen2(player.origin - it.origin); + if(test <= best_distance * best_distance) + { + best_distance = sqrt(test); + closest = it; + } + } + )); + + if(closest) + { + vector my_org, my_vel, my_ang, their_org, their_vel, their_ang; + + my_org = player.origin; + my_vel = player.velocity; + my_ang = player.angles; + their_org = closest.origin; + their_vel = closest.velocity; + their_ang = closest.angles; + + Drop_Special_Items(closest); + + MUTATOR_CALLHOOK(PortalTeleport, player); // initiate flag dropper + + setorigin(player, their_org); + setorigin(closest, my_org); + + closest.velocity = my_vel; + closest.angles = my_ang; + closest.fixangle = true; + closest.oldorigin = my_org; + closest.oldvelocity = my_vel; + player.velocity = their_vel; + player.angles = their_ang; + player.fixangle = true; + player.oldorigin = their_org; + player.oldvelocity = their_vel; + + // set pusher so player gets the kill if they fall into void + closest.pusher = player; + closest.pushltime = time + autocvar_g_maxpushtime; + closest.istypefrag = PHYS_INPUT_BUTTON_CHAT(closest); + + Send_Effect(EFFECT_ELECTRO_COMBO, their_org, '0 0 0', 1); + Send_Effect(EFFECT_ELECTRO_COMBO, my_org, '0 0 0', 1); + + sound(player, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + sound(closest, CH_TRIGGER, SND_KA_RESPAWN, VOL_BASE, ATTEN_NORM); + + // TODO: add a counter to handle how many times one can teleport, and a delay to prevent spam + player.buffs = 0; + return true; + } + } +} + +bool buffs_RemovePlayer(entity player) +{ + if(player.buff_model) + { + delete(player.buff_model); + player.buff_model = NULL; + } + + // also reset timers here to prevent them continuing after spectating + player.buff_disability_time = 0; + player.buff_disability_effect_time = 0; + + return false; +} +MUTATOR_HOOKFUNCTION(buffs, MakePlayerObserver) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } +MUTATOR_HOOKFUNCTION(buffs, ClientDisconnect) { entity player = M_ARGV(0, entity); return buffs_RemovePlayer(player); } + +MUTATOR_HOOKFUNCTION(buffs, CustomizeWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity player = M_ARGV(1, entity); + + entity e = WaypointSprite_getviewentity(player); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((wp.owner.flags & FL_CLIENT) && (wp.owner.buffs & BUFF_INVISIBLE.m_itemid) && (e == player)) + if(DIFF_TEAM(wp.owner, e)) + return true; +} + +MUTATOR_HOOKFUNCTION(buffs, OnEntityPreSpawn, CBC_ORDER_LAST) +{ + if(autocvar_g_buffs < 0) + return; // no auto replacing of entities in this mode + + entity ent = M_ARGV(0, entity); + + if(autocvar_g_buffs_replace_powerups) + switch(ent.classname) + { + case "item_strength": + case "item_invincible": + { + entity e = spawn(); + buff_SpawnReplacement(e, ent); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponRateFactor) +{ + entity player = M_ARGV(1, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(0, float) *= autocvar_g_buffs_speed_rate; + + if(time < player.buff_disability_time) + M_ARGV(0, float) *= autocvar_g_buffs_disability_rate; +} + +MUTATOR_HOOKFUNCTION(buffs, WeaponSpeedFactor) +{ + entity player = M_ARGV(1, entity); + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(0, float) *= autocvar_g_buffs_speed_weaponspeed; + + if(time < player.buff_disability_time) + M_ARGV(0, float) *= autocvar_g_buffs_disability_weaponspeed; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(gameover || IS_DEAD(player)) { return; } + + if(time < player.buff_disability_time) + if(time >= player.buff_disability_effect_time) + { + Send_Effect(EFFECT_SMOKING, player.origin + ((player.mins + player.maxs) * 0.5), '0 0 0', 1); + player.buff_disability_effect_time = time + 0.5; + } + + // handle buff lost status + // 1: notify everyone else + // 2: notify carrier as well + int buff_lost = 0; + + if(player.buff_time && player.buffs) + if(time >= player.buff_time) + { + player.buff_time = 0; + buff_lost = 2; + } + + if(STAT(FROZEN, player)) { buff_lost = 1; } + + if(buff_lost) + { + if(player.buffs) + { + int buffid = buff_FirstFromFlags(player.buffs).m_id; + if(buff_lost == 2) + { + Send_Notification(NOTIF_ONE, player, MSG_MULTI, ITEM_BUFF_DROP, buffid); // TODO: special timeout message? + sound(player, CH_TRIGGER, SND_BUFF_LOST, VOL_BASE, ATTN_NORM); + } + else + Send_Notification(NOTIF_ALL_EXCEPT, player, MSG_INFO, INFO_ITEM_BUFF_LOST, player.netname, buffid); + player.buffs = 0; + } + } + + if(player.buffs & BUFF_MAGNET.m_itemid) + { + vector pickup_size; + IL_EACH(g_items, it.itemdef, + { + if(it.buffs) + pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_buff; + else + pickup_size = '1 1 1' * autocvar_g_buffs_magnet_range_item; + + if(boxesoverlap(player.absmin - pickup_size, player.absmax + pickup_size, it.absmin, it.absmax)) + { + if(gettouch(it)) + gettouch(it)(it, player); + } + }); + } + + if(player.buffs & BUFF_AMMO.m_itemid) + if(player.clip_size) + player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; + + if((player.buffs & BUFF_INVISIBLE.m_itemid) && (player.oldbuffs & BUFF_INVISIBLE.m_itemid)) + if(player.alpha != autocvar_g_buffs_invisible_alpha) + player.alpha = autocvar_g_buffs_invisible_alpha; // powerups reset alpha, so we must enforce this (TODO) + + if(player.buffs & BUFF_MEDIC.m_itemid) + if(time >= player.buff_medic_healtime) + { + buff_Medic_Heal(player); + player.buff_medic_healtime = time + autocvar_g_buffs_medic_heal_delay; + } + +#define BUFF_ONADD(b) if ( (player.buffs & (b).m_itemid) && !(player.oldbuffs & (b).m_itemid)) +#define BUFF_ONREM(b) if (!(player.buffs & (b).m_itemid) && (player.oldbuffs & (b).m_itemid)) + + if(player.buffs != player.oldbuffs) + { + entity buff = buff_FirstFromFlags(player.buffs); + float bufftime = buff != BUFF_Null ? buff.m_time(buff) : 0; + player.buff_time = (bufftime) ? time + bufftime : 0; + + BUFF_ONADD(BUFF_AMMO) + { + player.buff_ammo_prev_infitems = (player.items & IT_UNLIMITED_WEAPON_AMMO); + player.items |= IT_UNLIMITED_WEAPON_AMMO; + + if(player.clip_load) + player.buff_ammo_prev_clipload = player.clip_load; + player.clip_load = player.(weapon_load[PS(player).m_switchweapon.m_id]) = player.clip_size; + } + + BUFF_ONREM(BUFF_AMMO) + { + if(player.buff_ammo_prev_infitems) + player.items |= IT_UNLIMITED_WEAPON_AMMO; + else + player.items &= ~IT_UNLIMITED_WEAPON_AMMO; + + if(player.buff_ammo_prev_clipload) + player.clip_load = player.buff_ammo_prev_clipload; + } + + BUFF_ONADD(BUFF_INVISIBLE) + { + if(time < player.strength_finished && g_instagib) + player.alpha = autocvar_g_instagib_invis_alpha; + else + player.alpha = player.buff_invisible_prev_alpha; + player.alpha = autocvar_g_buffs_invisible_alpha; + } + + BUFF_ONREM(BUFF_INVISIBLE) + player.alpha = player.buff_invisible_prev_alpha; + + BUFF_ONADD(BUFF_FLIGHT) + { + player.buff_flight_oldgravity = player.gravity; + if(!player.gravity) + player.gravity = 1; + } + + BUFF_ONREM(BUFF_FLIGHT) + player.gravity = ((player.trigger_gravity_check) ? player.trigger_gravity_check.enemy.gravity : player.buff_flight_oldgravity); + + player.oldbuffs = player.buffs; + if(player.buffs) + { + if(!player.buff_model) + buffs_BuffModel_Spawn(player); + + player.buff_model.color = buff.m_color; + player.buff_model.glowmod = buff_GlowColor(player.buff_model); + player.buff_model.skin = buff.m_skin; + + player.effects |= EF_NOSHADOW; + } + else + { + delete(player.buff_model); + player.buff_model = NULL; + + player.effects &= ~(EF_NOSHADOW); + } + } + + if(player.buff_model) + { + player.buff_model.effects = player.effects; + player.buff_model.effects |= EF_LOWPRECISION; + player.buff_model.effects = player.buff_model.effects & EFMASK_CHEAP; // eat performance + + player.buff_model.alpha = player.alpha; + } + +#undef BUFF_ONADD +#undef BUFF_ONREM +} + +MUTATOR_HOOKFUNCTION(buffs, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + client.buffs = spectatee.buffs; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleEnter) +{ + entity player = M_ARGV(0, entity); + entity veh = M_ARGV(1, entity); + + veh.buffs = player.buffs; + player.buffs = 0; + veh.buff_time = max(0, player.buff_time - time); + player.buff_time = 0; +} + +MUTATOR_HOOKFUNCTION(buffs, VehicleExit) +{ + entity player = M_ARGV(0, entity); + entity veh = M_ARGV(1, entity); + + player.buffs = player.oldbuffs = veh.buffs; + veh.buffs = 0; + player.buff_time = time + veh.buff_time; + veh.buff_time = 0; +} + +MUTATOR_HOOKFUNCTION(buffs, PlayerRegen) +{ + entity player = M_ARGV(0, entity); + + if(player.buffs & BUFF_MEDIC.m_itemid) + { + M_ARGV(2, float) = autocvar_g_buffs_medic_rot; // rot_mod + M_ARGV(4, float) = M_ARGV(1, float) = autocvar_g_buffs_medic_max; // limit_mod = max_mod + M_ARGV(2, float) = autocvar_g_buffs_medic_regen; // regen_mod + } + + if(player.buffs & BUFF_SPEED.m_itemid) + M_ARGV(2, float) = autocvar_g_buffs_speed_regen; // regen_mod +} + +REPLICATE(cvar_cl_buffs_autoreplace, bool, "cl_buffs_autoreplace"); + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsString) +{ + if(autocvar_g_buffs > 0) // only report as a mutator if they're enabled + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Buffs"); +} + +MUTATOR_HOOKFUNCTION(buffs, BuildMutatorsPrettyString) +{ + if(autocvar_g_buffs > 0) + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Buffs"); +} + +void buffs_DelayedInit(entity this) +{ + if(autocvar_g_buffs_spawn_count > 0) + if(find(NULL, classname, "item_buff") == NULL) + { + float i; + for(i = 0; i < autocvar_g_buffs_spawn_count; ++i) + { + entity e = spawn(); + e.spawnflags |= 64; // always randomize + e.velocity = randomvec() * 250; // this gets reset anyway if random location works + buff_Init(e); + } + } +} diff --git a/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh new file mode 100644 index 0000000000..8383a72ef5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/buffs/sv_buffs.qh @@ -0,0 +1,79 @@ +#pragma once + +#include "buffs.qh" + +#include "../instagib/_mod.qh" + +bool autocvar_g_buffs_effects; +float autocvar_g_buffs_waypoint_distance; +bool autocvar_g_buffs_randomize; +float autocvar_g_buffs_random_lifetime; +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; +float autocvar_g_buffs_cooldown_activate; +float autocvar_g_buffs_cooldown_respawn; +float autocvar_g_buffs_resistance_blockpercent; +float autocvar_g_buffs_medic_survive_chance; +float autocvar_g_buffs_medic_survive_health; +float autocvar_g_buffs_medic_rot; +float autocvar_g_buffs_medic_max; +float autocvar_g_buffs_medic_regen; +float autocvar_g_buffs_medic_heal_amount = 15; +float autocvar_g_buffs_medic_heal_delay = 1; +float autocvar_g_buffs_medic_heal_range = 400; +float autocvar_g_buffs_vengeance_damage_multiplier; +float autocvar_g_buffs_bash_force; +float autocvar_g_buffs_bash_force_self; +float autocvar_g_buffs_disability_slowtime; +float autocvar_g_buffs_disability_speed; +float autocvar_g_buffs_disability_rate; +float autocvar_g_buffs_disability_weaponspeed; +float autocvar_g_buffs_speed_speed; +float autocvar_g_buffs_speed_rate; +float autocvar_g_buffs_speed_weaponspeed; +float autocvar_g_buffs_speed_damage_take; +float autocvar_g_buffs_speed_regen; +float autocvar_g_buffs_vampire_damage_steal; +float autocvar_g_buffs_invisible_alpha; +float autocvar_g_buffs_jump_height; +float autocvar_g_buffs_inferno_burntime_factor; +float autocvar_g_buffs_inferno_burntime_min_time; +float autocvar_g_buffs_inferno_burntime_target_damage; +float autocvar_g_buffs_inferno_burntime_target_time; +float autocvar_g_buffs_inferno_damagemultiplier; +float autocvar_g_buffs_swapper_range; +float autocvar_g_buffs_magnet_range_item; +float autocvar_g_buffs_magnet_range_buff = 200; +float autocvar_g_buffs_luck_chance = 0.15; +float autocvar_g_buffs_luck_damagemultiplier = 3; + +// ammo +.float buff_ammo_prev_infitems; +.int buff_ammo_prev_clipload; +// invisible +.float buff_invisible_prev_alpha; +// medic +.float buff_medic_healtime; +// disability +.float buff_disability_time; +.float buff_disability_effect_time; +// flight +.float buff_flight_oldgravity; +// common buff variables +.float buff_effect_delay; + +// buff definitions +.float buff_active; +.float buff_activetime; +.float buff_activetime_updated; +.entity buff_waypoint; +.int oldbuffs; // for updating effects +.entity buff_model; // controls effects (TODO: make csqc) + +const vector BUFF_MIN = ('-16 -16 0'); +const vector BUFF_MAX = ('16 16 60'); + +// client side options +.float cvar_cl_buffs_autoreplace; diff --git a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc index 96490700e5..a67e9455d9 100644 --- a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc +++ b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qc @@ -1,4 +1,7 @@ -#ifdef IMPLEMENTATION +#include "bugrigs.qh" + +#ifdef GAMEQC + #ifdef SVQC #include #endif @@ -60,7 +63,7 @@ void bugrigs_SetVars() #endif -void RaceCarPhysics(entity this) +void RaceCarPhysics(entity this, float dt) { // using this move type for "big rigs" // the engine does not push the entity! @@ -114,35 +117,35 @@ void RaceCarPhysics(entity this) { if (myspeed > 0) { - myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); + myspeed = max(0, myspeed - dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) - PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); } else { if (!PHYS_BUGRIGS_REVERSE_SPEEDING(this)) - myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this)); + myspeed = min(0, myspeed + dt * PHYS_BUGRIGS_FRICTION_FLOOR(this)); } } else { if (myspeed >= 0) { - myspeed = max(0, myspeed - PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_FRICTION_FLOOR(this)); + myspeed = max(0, myspeed - dt * PHYS_BUGRIGS_FRICTION_FLOOR(this)); } else { if (PHYS_BUGRIGS_REVERSE_STOPPING(this)) myspeed = 0; else - myspeed = min(0, myspeed + PHYS_INPUT_TIMELENGTH * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); + myspeed = min(0, myspeed + dt * (PHYS_BUGRIGS_FRICTION_FLOOR(this) + PHYS_BUGRIGS_FRICTION_BRAKE(this) * accel)); } } // terminal velocity = velocity at which 50 == accelfactor, that is, 1549 units/sec //MAXIMA: friction(v) := PHYS_BUGRIGS_FRICTION_FLOOR(this); - this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering + this.angles_y += steer * dt * steerfactor; // apply steering makevectors(this.angles); // new forward direction! - myspeed += accel * accelfactor * PHYS_INPUT_TIMELENGTH; + myspeed += accel * accelfactor * dt; rigvel = myspeed * v_forward + '0 0 1' * upspeed; } @@ -153,13 +156,13 @@ void RaceCarPhysics(entity this) // responsiveness factor for steering and acceleration float f = 1 / (1 + pow(max(0, myspeed / PHYS_BUGRIGS_SPEED_REF(this)), PHYS_BUGRIGS_SPEED_POW(this))); float steerfactor = -myspeed * f; - this.angles_y += steer * PHYS_INPUT_TIMELENGTH * steerfactor; // apply steering + this.angles_y += steer * dt * steerfactor; // apply steering rigvel = this.velocity; makevectors(this.angles); // new forward direction! } - rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * PHYS_INPUT_TIMELENGTH); + rigvel *= max(0, 1 - vlen(rigvel) * PHYS_BUGRIGS_FRICTION_AIR(this) * dt); //MAXIMA: airfriction(v) := v * v * PHYS_BUGRIGS_FRICTION_AIR(this); //MAXIMA: total_acceleration(v) := accel(v) - friction(v) - airfriction(v); //MAXIMA: solve(total_acceleration(v) = 0, v); @@ -169,7 +172,7 @@ void RaceCarPhysics(entity this) vector rigvel_xy, neworigin, up; float mt; - rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better + rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better rigvel_xy = vec2(rigvel); if (PHYS_BUGRIGS_CAR_JUMPING(this)) @@ -182,10 +185,10 @@ void RaceCarPhysics(entity this) // BUG RIGS: align the move to the surface instead of doing collision testing // can we move? - tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * PHYS_INPUT_TIMELENGTH, mt, this); + tracebox(trace_endpos, this.mins, this.maxs, trace_endpos + rigvel_xy * dt, mt, this); // align to surface - tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * PHYS_INPUT_TIMELENGTH, mt, this); + tracebox(trace_endpos, this.mins, this.maxs, trace_endpos - up + '0 0 1' * rigvel_z * dt, mt, this); if (trace_fraction < 0.5) { @@ -213,14 +216,14 @@ void RaceCarPhysics(entity this) UNSET_ONGROUND(this); } - this.velocity = (neworigin - this.origin) * (1.0 / PHYS_INPUT_TIMELENGTH); - this.movetype = MOVETYPE_NOCLIP; + this.velocity = (neworigin - this.origin) * (1.0 / dt); + set_movetype(this, MOVETYPE_NOCLIP); } else { - rigvel_z -= PHYS_INPUT_TIMELENGTH * PHYS_GRAVITY(this); // 4x gravity plays better + rigvel_z -= dt * PHYS_GRAVITY(this); // 4x gravity plays better this.velocity = rigvel; - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); } trace_fraction = 1; @@ -251,7 +254,7 @@ void RaceCarPhysics(entity this) // smooth the angles vector vf1, vu1, smoothangles; makevectors(this.angles); - float f = bound(0, PHYS_INPUT_TIMELENGTH * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1); + float f = bound(0, dt * PHYS_BUGRIGS_ANGLE_SMOOTHING(this), 1); if (f == 0) f = 1; vf1 = v_forward * f; @@ -262,8 +265,6 @@ void RaceCarPhysics(entity this) smoothangles = vectoangles2(vf1, vu1); this.angles_x = -smoothangles_x; this.angles_z = smoothangles_z; - - PM_ClientMovement_Move(this); } #ifdef SVQC @@ -272,6 +273,7 @@ void RaceCarPhysics(entity this) MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics) { entity player = M_ARGV(0, entity); + float dt = M_ARGV(2, float); if(!PHYS_BUGRIGS(player) || !IS_PLAYER(player)) { return; } @@ -279,7 +281,7 @@ MUTATOR_HOOKFUNCTION(bugrigs, PM_Physics) player.angles = player.bugrigs_prevangles; #endif - RaceCarPhysics(player); + RaceCarPhysics(player, dt); return true; } @@ -312,4 +314,5 @@ MUTATOR_HOOKFUNCTION(bugrigs, BuildMutatorsPrettyString) } #endif + #endif diff --git a/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/bugrigs/bugrigs.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/bugrigs/module.inc b/qcsrc/common/mutators/mutator/bugrigs/module.inc deleted file mode 100644 index cef744fdcb..0000000000 --- a/qcsrc/common/mutators/mutator/bugrigs/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "bugrigs.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/_mod.inc b/qcsrc/common/mutators/mutator/campcheck/_mod.inc index 3ddb376fc9..07c2ae2722 100644 --- a/qcsrc/common/mutators/mutator/campcheck/_mod.inc +++ b/qcsrc/common/mutators/mutator/campcheck/_mod.inc @@ -1,2 +1,5 @@ // generated file; do not modify #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/_mod.qh b/qcsrc/common/mutators/mutator/campcheck/_mod.qh index 81345f1cff..c52811129b 100644 --- a/qcsrc/common/mutators/mutator/campcheck/_mod.qh +++ b/qcsrc/common/mutators/mutator/campcheck/_mod.qh @@ -1,2 +1,5 @@ // generated file; do not modify #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc index 191f616815..822d4af780 100644 --- a/qcsrc/common/mutators/mutator/campcheck/campcheck.qc +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qc @@ -1,91 +1 @@ -#ifdef IMPLEMENTATION -float autocvar_g_campcheck_damage; -float autocvar_g_campcheck_distance; -float autocvar_g_campcheck_interval; - -REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); - -.float campcheck_nextcheck; -.float campcheck_traveled_distance; - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK); -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(IS_PLAYER(frag_target)) - if(IS_PLAYER(frag_attacker)) - if(frag_attacker != frag_target) - { - frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; - frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; - } -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(!gameover) - if(!warmup_stage) // don't consider it camping during warmup? - if(time >= game_starttime) - if(IS_PLAYER(player)) - if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them - if(!IS_DEAD(player)) - if(!STAT(FROZEN, player)) - if(!PHYS_INPUT_BUTTON_CHAT(player)) - if(autocvar_g_campcheck_interval) - { - vector dist; - - // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) - dist = player.prevorigin - player.origin; - dist.z = 0; - player.campcheck_traveled_distance += fabs(vlen(dist)); - - if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - { - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - player.campcheck_traveled_distance = 0; - } - - if(time > player.campcheck_nextcheck) - { - if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance) - { - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK); - if(player.vehicle) - Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0'); - else - Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0'); - } - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; - player.campcheck_traveled_distance = 0; - } - - return; - } - - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date -} - -MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; - player.campcheck_traveled_distance = 0; -} - -MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck");; -} -#endif +#include "campcheck.qh" diff --git a/qcsrc/common/mutators/mutator/campcheck/campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/campcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/campcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/campcheck/module.inc b/qcsrc/common/mutators/mutator/campcheck/module.inc deleted file mode 100644 index 9f4181f163..0000000000 --- a/qcsrc/common/mutators/mutator/campcheck/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "campcheck.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc new file mode 100644 index 0000000000..c4d2e03026 --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qc @@ -0,0 +1,92 @@ +#include "sv_campcheck.qh" + +float autocvar_g_campcheck_damage; +float autocvar_g_campcheck_distance; +float autocvar_g_campcheck_interval; + +REGISTER_MUTATOR(campcheck, cvar("g_campcheck")); + +.float campcheck_nextcheck; +.float campcheck_traveled_distance; + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + Kill_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CPID_CAMPCHECK); +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(IS_PLAYER(frag_target)) + if(IS_PLAYER(frag_attacker)) + if(frag_attacker != frag_target) + { + frag_target.campcheck_traveled_distance = autocvar_g_campcheck_distance; + frag_attacker.campcheck_traveled_distance = autocvar_g_campcheck_distance; + } +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(!gameover) + if(!warmup_stage) // don't consider it camping during warmup? + if(time >= game_starttime) + if(IS_PLAYER(player)) + if(IS_REAL_CLIENT(player)) // bots may camp, but that's no reason to constantly kill them + if(!IS_DEAD(player)) + if(!forbidWeaponUse(player)) + if(!STAT(FROZEN, player)) + if(!PHYS_INPUT_BUTTON_CHAT(player)) + if(autocvar_g_campcheck_interval) + { + vector dist; + + // calculate player movement (in 2 dimensions only, so jumping on one spot doesn't count as movement) + dist = player.prevorigin - player.origin; + dist.z = 0; + player.campcheck_traveled_distance += fabs(vlen(dist)); + + if((autocvar_g_campaign && !campaign_bots_may_start) || (time < game_starttime) || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + { + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + player.campcheck_traveled_distance = 0; + } + + if(time > player.campcheck_nextcheck) + { + if(player.campcheck_traveled_distance < autocvar_g_campcheck_distance) + { + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_CAMPCHECK); + if(player.vehicle) + Damage(player.vehicle, NULL, NULL, autocvar_g_campcheck_damage * 2, DEATH_CAMP.m_id, player.vehicle.origin, '0 0 0'); + else + Damage(player, NULL, NULL, bound(0, autocvar_g_campcheck_damage, player.health + player.armorvalue * autocvar_g_balance_armor_blockpercent + 5), DEATH_CAMP.m_id, player.origin, '0 0 0'); + } + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; + player.campcheck_traveled_distance = 0; + } + + return; + } + + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval; // one of the above checks failed, so keep the timer up to date +} + +MUTATOR_HOOKFUNCTION(campcheck, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.campcheck_nextcheck = time + autocvar_g_campcheck_interval * 2; + player.campcheck_traveled_distance = 0; +} + +MUTATOR_HOOKFUNCTION(campcheck, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":CampCheck"); +} diff --git a/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/campcheck/sv_campcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/cloaked/_mod.inc b/qcsrc/common/mutators/mutator/cloaked/_mod.inc index 7213695225..34fdea7479 100644 --- a/qcsrc/common/mutators/mutator/cloaked/_mod.inc +++ b/qcsrc/common/mutators/mutator/cloaked/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/_mod.qh b/qcsrc/common/mutators/mutator/cloaked/_mod.qh index 5606b9aafd..b225a2be5b 100644 --- a/qcsrc/common/mutators/mutator/cloaked/_mod.qh +++ b/qcsrc/common/mutators/mutator/cloaked/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/cloaked.qc deleted file mode 100644 index 4fca532768..0000000000 --- a/qcsrc/common/mutators/mutator/cloaked/cloaked.qc +++ /dev/null @@ -1,19 +0,0 @@ -#ifdef IMPLEMENTATION - -REGISTER_MUTATOR(cloaked, cvar("g_cloaked")); - -float autocvar_g_balance_cloaked_alpha; - -MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha) -{ - default_player_alpha = autocvar_g_balance_cloaked_alpha; - default_weapon_alpha = default_player_alpha; - return true; -} - -MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString) -{ - if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked"); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/module.inc b/qcsrc/common/mutators/mutator/cloaked/module.inc deleted file mode 100644 index 3d3a042936..0000000000 --- a/qcsrc/common/mutators/mutator/cloaked/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "cloaked.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc new file mode 100644 index 0000000000..eb45d4baf4 --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qc @@ -0,0 +1,17 @@ +#include "sv_cloaked.qh" + +REGISTER_MUTATOR(cloaked, cvar("g_cloaked")); + +float autocvar_g_balance_cloaked_alpha; + +MUTATOR_HOOKFUNCTION(cloaked, SetDefaultAlpha) +{ + default_player_alpha = autocvar_g_balance_cloaked_alpha; + default_weapon_alpha = default_player_alpha; + return true; +} + +MUTATOR_HOOKFUNCTION(cloaked, BuildMutatorsPrettyString) +{ + if (!g_cts) M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Cloaked"); +} diff --git a/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/cloaked/sv_cloaked.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc index 2a1795d63c..7f2b740ba5 100644 --- a/qcsrc/common/mutators/mutator/damagetext/damagetext.qc +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qc @@ -1,80 +1,115 @@ -#ifndef MUTATOR_DAMAGETEXT_H -#define MUTATOR_DAMAGETEXT_H +#include "damagetext.qh" -#ifdef MENUQC -#include -#endif +#define DAMAGETEXT_PRECISION_MULTIPLIER 128 +#define DAMAGETEXT_SHORT_LIMIT 256 // the smallest value that we can't send as short - 2^15 (signed short) / DAMAGETEXT_PRECISION_MULTIPLIER -#endif +const int DTFLAG_SAMETEAM = BIT(0); +const int DTFLAG_BIG_HEALTH = BIT(1); +const int DTFLAG_BIG_ARMOR = BIT(2); +const int DTFLAG_BIG_POTENTIAL = BIT(3); +const int DTFLAG_NO_ARMOR = BIT(4); +const int DTFLAG_NO_POTENTIAL = BIT(5); -#ifdef IMPLEMENTATION REGISTER_MUTATOR(damagetext, true); -#if defined(CSQC) || defined(MENUQC) +// || defined(MENUQC) +#if defined(CSQC) // no translatable cvar description please -AUTOCVAR_SAVE(cl_damagetext, bool, false, "Draw damage dealt where you hit the enemy"); -AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}"); +AUTOCVAR_SAVE(cl_damagetext, bool, true, "Draw damage dealt where you hit the enemy"); +AUTOCVAR_SAVE(cl_damagetext_format, string, "-{total}", "How to format the damage text. {health}, {armor}, {total}, {potential}: full damage not capped to target's health, {potential_health}: health damage not capped to target's health"); +AUTOCVAR_SAVE(cl_damagetext_format_verbose, bool, false, "{health} shows {potential_health} too when they differ; {total} shows {potential} too when they differ"); STATIC_INIT(DamageText_LegacyFormat) { if (strstrofs(autocvar_cl_damagetext_format, "{", 0) < 0) autocvar_cl_damagetext_format = "-{total}"; } -AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', "Damage text color"); -AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, "Damage text uses weapon color"); -AUTOCVAR_SAVE(cl_damagetext_size, float, 8, "Damage text font size"); -AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, "Damage text initial alpha"); -AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, "Damage text lifetime in seconds"); -AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', "Damage text move direction"); -AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', "Damage text offset"); -AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, "Damage text spawned within this range is accumulated"); +AUTOCVAR_SAVE(cl_damagetext_color, vector, '1 1 0', "Damage text color"); +AUTOCVAR_SAVE(cl_damagetext_color_per_weapon, bool, false, "Damage text uses weapon color"); +AUTOCVAR_SAVE(cl_damagetext_size, float, 8, "Damage text font size"); +AUTOCVAR_SAVE(cl_damagetext_alpha_start, float, 1, "Damage text initial alpha"); +AUTOCVAR_SAVE(cl_damagetext_alpha_lifetime, float, 3, "Damage text lifetime in seconds"); +AUTOCVAR_SAVE(cl_damagetext_velocity, vector, '0 0 20', "Damage text move direction"); +AUTOCVAR_SAVE(cl_damagetext_offset, vector, '0 -40 0', "Damage text offset"); +AUTOCVAR_SAVE(cl_damagetext_accumulate_range, float, 30, "Damage text spawned within this range is accumulated"); +AUTOCVAR_SAVE(cl_damagetext_accumulate_alpha_rel, float, 0.65, "Only update existing damage text when it's above this much percentage (0 to 1) of the starting alpha"); +AUTOCVAR_SAVE(cl_damagetext_friendlyfire, bool, true, "Show damage text for friendlyfire too"); +AUTOCVAR_SAVE(cl_damagetext_friendlyfire_color, vector, '1 0 0', "Damage text color for friendlyfire"); #endif #ifdef CSQC CLASS(DamageText, Object) - ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color) - ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size) - ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start) - ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime) - ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity) - ATTRIB(DamageText, m_group, int, 0) - ATTRIB(DamageText, m_damage, int, 0) - ATTRIB(DamageText, m_armordamage, int, 0) - ATTRIB(DamageText, m_deathtype, int, 0) - ATTRIB(DamageText, time_prev, float, time) + ATTRIB(DamageText, m_color, vector, autocvar_cl_damagetext_color); + ATTRIB(DamageText, m_color_friendlyfire, vector, autocvar_cl_damagetext_friendlyfire_color); + ATTRIB(DamageText, m_size, float, autocvar_cl_damagetext_size); + ATTRIB(DamageText, alpha, float, autocvar_cl_damagetext_alpha_start); + ATTRIB(DamageText, fade_rate, float, 1 / autocvar_cl_damagetext_alpha_lifetime); + ATTRIB(DamageText, velocity, vector, autocvar_cl_damagetext_velocity); + ATTRIB(DamageText, m_group, int, 0); + ATTRIB(DamageText, m_friendlyfire, bool, false); + ATTRIB(DamageText, m_damage, int, 0); + ATTRIB(DamageText, m_armordamage, int, 0); + ATTRIB(DamageText, m_potential_damage, int, 0); + ATTRIB(DamageText, m_deathtype, int, 0); + ATTRIB(DamageText, time_prev, float, time); void DamageText_draw2d(DamageText this) { float dt = time - this.time_prev; this.time_prev = time; setorigin(this, this.origin + dt * this.velocity); this.alpha -= dt * this.fade_rate; - if (this.alpha < 0) remove(this); + if (this.alpha < 0) delete(this); vector pos = project_3d_to_2d(this.origin) + autocvar_cl_damagetext_offset; if (pos.z >= 0 && this.m_size > 0) { pos.z = 0; - vector rgb = this.m_color; + vector rgb; + if (this.m_friendlyfire) { + rgb = this.m_color_friendlyfire; + } + else { + rgb = this.m_color; + } if (autocvar_cl_damagetext_color_per_weapon) { Weapon w = DEATH_WEAPONOF(this.m_deathtype); if (w != WEP_Null) rgb = w.wpcolor; } + int health = rint(this.m_damage / DAMAGETEXT_PRECISION_MULTIPLIER); + int total = rint((this.m_damage + this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER); + int potential = rint(this.m_potential_damage / DAMAGETEXT_PRECISION_MULTIPLIER); + int potential_health = rint((this.m_potential_damage - this.m_armordamage) / DAMAGETEXT_PRECISION_MULTIPLIER); + string s = autocvar_cl_damagetext_format; - s = strreplace("{health}", sprintf("%d", this.m_damage), s); - s = strreplace("{armor}", sprintf("%d", this.m_armordamage), s); - s = strreplace("{total}", sprintf("%d", this.m_damage + this.m_armordamage), s); + s = strreplace("{armor}", sprintf("%d", rint(this.m_armordamage / DAMAGETEXT_PRECISION_MULTIPLIER)), s); + s = strreplace("{potential}", sprintf("%d", potential), s); + s = strreplace("{potential_health}", sprintf("%d", potential_health), s); + + s = strreplace("{health}", ( + (health == potential_health || !autocvar_cl_damagetext_format_verbose) + ? sprintf("%d", health) + : sprintf("%d (%d)", health, potential_health) + ), s); + s = strreplace("{total}", ( + (total == potential || !autocvar_cl_damagetext_format_verbose) + ? sprintf("%d", total) + : sprintf("%d (%d)", total, potential) + ), s); drawcolorcodedstring2_builtin(pos, s, this.m_size * '1 1 0', rgb, this.alpha, DRAWFLAG_NORMAL); } } - ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d) + ATTRIB(DamageText, draw2d, void(DamageText), DamageText_draw2d); - void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _deathtype) { + void DamageText_update(DamageText this, vector _origin, int _health, int _armor, int _potential_damage, int _deathtype) { this.m_damage = _health; this.m_armordamage = _armor; + this.m_potential_damage = _potential_damage; this.m_deathtype = _deathtype; setorigin(this, _origin); - this.alpha = 1; + this.alpha = autocvar_cl_damagetext_alpha_start; } - CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _deathtype) { + CONSTRUCTOR(DamageText, int _group, vector _origin, int _health, int _armor, int _potential_damage, int _deathtype, bool _friendlyfire) { CONSTRUCT(DamageText); this.m_group = _group; - DamageText_update(this, _origin, _health, _armor, _deathtype); + this.m_friendlyfire = _friendlyfire; + DamageText_update(this, _origin, _health, _armor, _potential_damage, _deathtype); + IL_PUSH(g_drawables_2d, this); } ENDCLASS(DamageText) #endif @@ -91,9 +126,10 @@ 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 int health = M_ARGV(2, int); - const int armor = M_ARGV(3, int); + 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); const vector location = hit.origin; FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA( if ( @@ -102,15 +138,38 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) { (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_SPEC(it) && it.enemy == attacker) || (SV_DAMAGETEXT_SPECTATORS_ONLY() && IS_OBSERVER(it)) ) { + int flags = 0; + if (SAME_TEAM(hit, attacker)) flags |= DTFLAG_SAMETEAM; + if (health >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_HEALTH; + if (armor >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_ARMOR; + if (potential_damage >= DAMAGETEXT_SHORT_LIMIT) flags |= DTFLAG_BIG_POTENTIAL; + if (!armor) flags |= DTFLAG_NO_ARMOR; + if (fabs((armor + health) - potential_damage) < 0.0001) flags |= DTFLAG_NO_POTENTIAL; + msg_entity = it; WriteHeader(MSG_ONE, damagetext); - WriteShort(MSG_ONE, rint(health)); - WriteShort(MSG_ONE, rint(armor)); WriteEntity(MSG_ONE, hit); WriteCoord(MSG_ONE, location.x); WriteCoord(MSG_ONE, location.y); WriteCoord(MSG_ONE, location.z); WriteInt24_t(MSG_ONE, deathtype); + WriteByte(MSG_ONE, flags); + + // we need to send a few decimal places to minimize errors when accumulating damage + // sending them multiplied saves bandwidth compared to using WriteCoord, + // however if the multiplied damage would be too much for (signed) short, we send an int24 + if (flags & DTFLAG_BIG_HEALTH) WriteInt24_t(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER); + else WriteShort(MSG_ONE, health * DAMAGETEXT_PRECISION_MULTIPLIER); + if (!(flags & DTFLAG_NO_ARMOR)) + { + if (flags & DTFLAG_BIG_ARMOR) WriteInt24_t(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER); + else WriteShort(MSG_ONE, armor * DAMAGETEXT_PRECISION_MULTIPLIER); + } + if (!(flags & DTFLAG_NO_POTENTIAL)) + { + if (flags & DTFLAG_BIG_POTENTIAL) WriteInt24_t(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER); + else WriteShort(MSG_ONE, potential_damage * DAMAGETEXT_PRECISION_MULTIPLIER); + } } )); } @@ -119,34 +178,50 @@ MUTATOR_HOOKFUNCTION(damagetext, PlayerDamaged) { #ifdef CSQC NET_HANDLE(damagetext, bool isNew) { - int health = ReadShort(); - int armor = ReadShort(); int group = ReadShort(); vector location = vec3(ReadCoord(), ReadCoord(), ReadCoord()); int deathtype = ReadInt24_t(); + int flags = ReadByte(); + bool friendlyfire = flags & DTFLAG_SAMETEAM; + + int health, armor, potential_damage; + if (flags & DTFLAG_BIG_HEALTH) health = ReadInt24_t(); + else health = ReadShort(); + if (flags & DTFLAG_NO_ARMOR) armor = 0; + else if (flags & DTFLAG_BIG_ARMOR) armor = ReadInt24_t(); + else armor = ReadShort(); + if (flags & DTFLAG_NO_POTENTIAL) potential_damage = health + armor; + else if (flags & DTFLAG_BIG_POTENTIAL) potential_damage = ReadInt24_t(); + else potential_damage = ReadShort(); + return = true; if (autocvar_cl_damagetext) { + if (friendlyfire && !autocvar_cl_damagetext_friendlyfire) { + return; + } if (autocvar_cl_damagetext_accumulate_range) { for (entity e = findradius(location, autocvar_cl_damagetext_accumulate_range); e; e = e.chain) { - if (e.instanceOfDamageText && e.m_group == group) { - DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, deathtype); + if (e.instanceOfDamageText && e.m_group == group && e.alpha > autocvar_cl_damagetext_accumulate_alpha_rel * autocvar_cl_damagetext_alpha_start) { + DamageText_update(e, location, e.m_damage + health, e.m_armordamage + armor, e.m_potential_damage + potential_damage, deathtype); return; } } } - NEW(DamageText, group, location, health, armor, deathtype); + make_impure(NEW(DamageText, group, location, health, armor, potential_damage, deathtype, friendlyfire)); } } #endif #ifdef MENUQC + +#include + CLASS(XonoticDamageTextSettings, XonoticTab) - #include REGISTER_SETTINGS(damagetext, NEW(XonoticDamageTextSettings)); - ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text")) - ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9) - ATTRIB(XonoticDamageTextSettings, rows, float, 15.5) - ATTRIB(XonoticDamageTextSettings, columns, float, 5) + ATTRIB(XonoticDamageTextSettings, title, string, _("Damage text")); + ATTRIB(XonoticDamageTextSettings, intendedWidth, float, 0.9); + ATTRIB(XonoticDamageTextSettings, rows, float, 15.5); + ATTRIB(XonoticDamageTextSettings, columns, float, 5); INIT(XonoticDamageTextSettings) { this.configureDialog(this); } METHOD(XonoticDamageTextSettings, showNotify, void(entity this)) { loadAllCvars(this); } METHOD(XonoticDamageTextSettings, fill, void(entity this)) @@ -174,7 +249,17 @@ CLASS(XonoticDamageTextSettings, XonoticTab) setDependent(e, "cl_damagetext", 1, 1); this.TD(this, 2, 2, e = makeXonoticColorpickerString("cl_damagetext_color", "cl_damagetext_color")); setDependent(e, "cl_damagetext", 1, 1); + this.TR(this); + this.TR(this); + // friendly fire + this.TD(this, 1, 3, e = makeXonoticCheckBox(0, "cl_damagetext_friendlyfire", _("Draw damage numbers for friendly fire"))); + setDependent(e, "cl_damagetext", 1, 1); + this.TR(this); + this.TD(this, 1, 1, e = makeXonoticTextLabel(0, _("Color:"))); + setDependentAND(e, "cl_damagetext", 1, 1, "cl_damagetext_friendlyfire", 1, 1); + this.TD(this, 2, 2, e = makeXonoticColorpickerString("cl_damagetext_friendlyfire_color", "cl_damagetext_friendlyfire_color")); + setDependentAND(e, "cl_damagetext", 1, 1, "cl_damagetext_friendlyfire", 1, 1); + this.TR(this); } ENDCLASS(XonoticDamageTextSettings) #endif -#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/damagetext.qh b/qcsrc/common/mutators/mutator/damagetext/damagetext.qh new file mode 100644 index 0000000000..7228f37ea5 --- /dev/null +++ b/qcsrc/common/mutators/mutator/damagetext/damagetext.qh @@ -0,0 +1,5 @@ +#pragma once + +#ifdef MENUQC +#include +#endif diff --git a/qcsrc/common/mutators/mutator/damagetext/module.inc b/qcsrc/common/mutators/mutator/damagetext/module.inc deleted file mode 100644 index 8e70be7c6d..0000000000 --- a/qcsrc/common/mutators/mutator/damagetext/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "damagetext.qc" diff --git a/qcsrc/common/mutators/mutator/dodging/_mod.inc b/qcsrc/common/mutators/mutator/dodging/_mod.inc index 4902d5fc1b..80a7828a70 100644 --- a/qcsrc/common/mutators/mutator/dodging/_mod.inc +++ b/qcsrc/common/mutators/mutator/dodging/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/_mod.qh b/qcsrc/common/mutators/mutator/dodging/_mod.qh index b2b65f02f7..fef7b8e6e4 100644 --- a/qcsrc/common/mutators/mutator/dodging/_mod.qh +++ b/qcsrc/common/mutators/mutator/dodging/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/dodging.qc b/qcsrc/common/mutators/mutator/dodging/dodging.qc deleted file mode 100644 index 0d20ff7af8..0000000000 --- a/qcsrc/common/mutators/mutator/dodging/dodging.qc +++ /dev/null @@ -1,289 +0,0 @@ -#ifdef IMPLEMENTATION - -#define PHYS_DODGING STAT(DODGING, this) -#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this) -#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this) -#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this) -#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this) -#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this) -#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this) -#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this) -#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this) -#define PHYS_DODGING_WALL STAT(DODGING_WALL, this) -#define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys - -#ifdef CSQC - #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) - #define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT) -#elif defined(SVQC) - #define PHYS_DODGING_FRAMETIME sys_frametime - #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout - - -#endif - -#ifdef SVQC - -bool autocvar_sv_dodging_sound; - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - -#include -#include - -.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT); - -REGISTER_MUTATOR(dodging, cvar("g_dodging")) -{ - // this just turns on the cvar. - MUTATOR_ONADD - { - g_dodging = cvar("g_dodging"); - } - - // this just turns off the cvar. - MUTATOR_ONROLLBACK_OR_REMOVE - { - g_dodging = 0; - } - - return false; -} - -#elif defined(CSQC) -REGISTER_MUTATOR(dodging, true); -#endif - -// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. -.float dodging_action; - -// the jump part of the dodge cannot be ramped -.float dodging_single_action; - - -// these are used to store the last key press time for each of the keys.. -.float last_FORWARD_KEY_time; -.float last_BACKWARD_KEY_time; -.float last_LEFT_KEY_time; -.float last_RIGHT_KEY_time; - -// these store the movement direction at the time of the dodge action happening. -.vector dodging_direction; - -// this indicates the last time a dodge was executed. used to check if another one is allowed -// and to ramp up the dodge acceleration in the physics hook. -.float last_dodging_time; - -// This is the velocity gain to be added over the ramp time. -// It will decrease from frame to frame during dodging_action = 1 -// until it's 0. -.float dodging_velocity_gain; - -#ifdef CSQC -.int pressedkeys; -#endif - -// returns 1 if the player is close to a wall -bool check_close_to_wall(entity this, float threshold) -{ - if (PHYS_DODGING_WALL == 0) { return false; } - -#define X(OFFSET) \ - tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \ - if(trace_fraction < 1 && vdist(this.origin - trace_endpos, <, threshold)) \ - return true; - X(1000*v_right); - X(-1000*v_right); - X(1000*v_forward); - X(-1000*v_forward); -#undef X - - return false; -} - -bool check_close_to_ground(entity this, float threshold) -{ - return IS_ONGROUND(this) ? true : false; -} - -float PM_dodging_checkpressedkeys(entity this) -{ - if(!PHYS_DODGING) - return false; - - float frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this)); - float frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); - - // first check if the last dodge is far enough back in time so we can dodge again - if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY) - return false; - - makevectors(this.angles); - - if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1 - && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1) - return true; - - float tap_direction_x = 0; - float tap_direction_y = 0; - bool dodge_detected = false; - - #define X(COND,BTN,RESULT) \ - if (this.movement_##COND) \ - /* is this a state change? */ \ - if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \ - tap_direction_##RESULT; \ - if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \ - dodge_detected = true; \ - this.last_##BTN##_KEY_time = time; \ - } - X(x < 0, BACKWARD, x--); - X(x > 0, FORWARD, x++); - X(y < 0, LEFT, y--); - X(y > 0, RIGHT, y++); - #undef X - - if (dodge_detected) - { - this.last_dodging_time = time; - - this.dodging_action = 1; - this.dodging_single_action = 1; - - this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; - - this.dodging_direction_x = tap_direction_x; - this.dodging_direction_y = tap_direction_y; - - // normalize the dodging_direction vector.. (unlike UT99) XD - float length = this.dodging_direction_x * this.dodging_direction_x - + this.dodging_direction_y * this.dodging_direction_y; - length = sqrt(length); - - this.dodging_direction_x = this.dodging_direction_x * 1.0 / length; - this.dodging_direction_y = this.dodging_direction_y * 1.0 / length; - return true; - } - return false; -} - -void PM_dodging(entity this) -{ - if (!PHYS_DODGING) - return; - - if (IS_DEAD(this)) - return; - - // when swimming, no dodging allowed.. - if (this.waterlevel >= WATERLEVEL_SWIMMING) - { - this.dodging_action = 0; - this.dodging_direction_x = 0; - this.dodging_direction_y = 0; - return; - } - - // make sure v_up, v_right and v_forward are sane - makevectors(this.angles); - - // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code - // will be called ramp_time/frametime times = 2 times. so, we need to - // add 0.5 * the total speed each frame until the dodge action is done.. - float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; - - // if ramp time is smaller than frametime we get problems ;D - common_factor = min(common_factor, 1); - - float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; - float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed); - new_velocity_gain = max(0, new_velocity_gain); - - float velocity_difference = this.dodging_velocity_gain - new_velocity_gain; - - // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D - if (this.dodging_action == 1) - { - //disable jump key during dodge accel phase - if(this.movement_z > 0) { this.movement_z = 0; } - - this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right) - + ((this.dodging_direction_x * velocity_difference) * v_forward); - - this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference; - } - - // the up part of the dodge is a single shot action - if (this.dodging_single_action == 1) - { - UNSET_ONGROUND(this); - - this.velocity += PHYS_DODGING_UP_SPEED * v_up; - -#ifdef SVQC - if (autocvar_sv_dodging_sound) - PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); - - animdecide_setaction(this, ANIMACTION_JUMP, true); -#endif - - this.dodging_single_action = 0; - } - - // are we done with the dodging ramp yet? - if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) - { - // reset state so next dodge can be done correctly - this.dodging_action = 0; - this.dodging_direction_x = 0; - this.dodging_direction_y = 0; - } -} - -void PM_dodging_GetPressedKeys(entity this) -{ -#ifdef CSQC - if(!PHYS_DODGING) { return; } - - PM_dodging_checkpressedkeys(this); - - int keys = this.pressedkeys; - keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); - keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); - keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); - keys = BITSET(keys, KEY_LEFT, 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_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); - keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); - this.pressedkeys = keys; -#endif -} - -MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - // print("dodging_PlayerPhysics\n"); - PM_dodging_GetPressedKeys(player); - PM_dodging(player); -} - -#ifdef SVQC - -REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout"); - -MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) -{ - entity player = M_ARGV(0, entity); - - PM_dodging_checkpressedkeys(player); -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/dodging/module.inc b/qcsrc/common/mutators/mutator/dodging/module.inc deleted file mode 100644 index 60b193fdf9..0000000000 --- a/qcsrc/common/mutators/mutator/dodging/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC - #include "dodging.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc new file mode 100644 index 0000000000..fb1502dbc2 --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qc @@ -0,0 +1,293 @@ +#include "sv_dodging.qh" + +#define PHYS_DODGING STAT(DODGING, this) +#define PHYS_DODGING_DELAY STAT(DODGING_DELAY, this) +#define PHYS_DODGING_DISTANCE_THRESHOLD STAT(DODGING_DISTANCE_THRESHOLD, this) +#define PHYS_DODGING_FROZEN_NODOUBLETAP STAT(DODGING_FROZEN_NO_DOUBLETAP, this) +#define PHYS_DODGING_HEIGHT_THRESHOLD STAT(DODGING_HEIGHT_THRESHOLD, this) +#define PHYS_DODGING_HORIZ_SPEED STAT(DODGING_HORIZ_SPEED, this) +#define PHYS_DODGING_HORIZ_SPEED_FROZEN STAT(DODGING_HORIZ_SPEED_FROZEN, this) +#define PHYS_DODGING_RAMP_TIME STAT(DODGING_RAMP_TIME, this) +#define PHYS_DODGING_UP_SPEED STAT(DODGING_UP_SPEED, this) +#define PHYS_DODGING_WALL STAT(DODGING_WALL, this) +#define PHYS_DODGING_AIR STAT(DODGING_AIR, this) +#define PHYS_DODGING_PRESSED_KEYS(s) (s).pressedkeys + +#ifdef CSQC + #define PHYS_DODGING_FRAMETIME (1 / (frametime <= 0 ? 60 : frametime)) + #define PHYS_DODGING_TIMEOUT(s) STAT(DODGING_TIMEOUT) +#elif defined(SVQC) + #define PHYS_DODGING_FRAMETIME sys_frametime + #define PHYS_DODGING_TIMEOUT(s) s.cvar_cl_dodging_timeout +#endif + +#ifdef SVQC + +bool autocvar_sv_dodging_sound; + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + +#include +#include + +.float cvar_cl_dodging_timeout = _STAT(DODGING_TIMEOUT); + +REGISTER_MUTATOR(dodging, cvar("g_dodging")) +{ + // this just turns on the cvar. + MUTATOR_ONADD + { + g_dodging = cvar("g_dodging"); + } + + // this just turns off the cvar. + MUTATOR_ONROLLBACK_OR_REMOVE + { + g_dodging = 0; + } + + return false; +} + +#elif defined(CSQC) +REGISTER_MUTATOR(dodging, true); +#endif + +// set to 1 to indicate dodging has started.. reset by physics hook after dodge has been done.. +.float dodging_action; + +// the jump part of the dodge cannot be ramped +.float dodging_single_action; + + +// these are used to store the last key press time for each of the keys.. +.float last_FORWARD_KEY_time; +.float last_BACKWARD_KEY_time; +.float last_LEFT_KEY_time; +.float last_RIGHT_KEY_time; + +// these store the movement direction at the time of the dodge action happening. +.vector dodging_direction; + +// this indicates the last time a dodge was executed. used to check if another one is allowed +// and to ramp up the dodge acceleration in the physics hook. +.float last_dodging_time; + +// This is the velocity gain to be added over the ramp time. +// It will decrease from frame to frame during dodging_action = 1 +// until it's 0. +.float dodging_velocity_gain; + +#ifdef CSQC +.int pressedkeys; +#endif + +// returns 1 if the player is close to a wall +bool check_close_to_wall(entity this, float threshold) +{ + if (PHYS_DODGING_WALL == 0) { return false; } + +#define X(OFFSET) \ + tracebox(this.origin, this.mins, this.maxs, this.origin + OFFSET, true, this); \ + if(trace_fraction < 1 && !(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) && vdist(this.origin - trace_endpos, <, threshold)) \ + return true; + X(1000*v_right); + X(-1000*v_right); + X(1000*v_forward); + X(-1000*v_forward); +#undef X + + return false; +} + +bool check_close_to_ground(entity this, float threshold) +{ + return IS_ONGROUND(this) ? true : false; +} + +bool PM_dodging_checkpressedkeys(entity this) +{ + if(!PHYS_DODGING) + return false; + + bool frozen_dodging = (PHYS_FROZEN(this) && PHYS_DODGING_FROZEN(this)); + bool frozen_no_doubletap = (frozen_dodging && !PHYS_DODGING_FROZEN_NODOUBLETAP); + + // first check if the last dodge is far enough back in time so we can dodge again + if ((time - this.last_dodging_time) < PHYS_DODGING_DELAY) + return false; + + makevectors(this.angles); + + if(!PHYS_DODGING_AIR) + if (check_close_to_ground(this, PHYS_DODGING_HEIGHT_THRESHOLD) != 1 + && check_close_to_wall(this, PHYS_DODGING_DISTANCE_THRESHOLD) != 1) + return true; + + float tap_direction_x = 0; + float tap_direction_y = 0; + bool dodge_detected = false; + + #define X(COND,BTN,RESULT) \ + if (this.movement_##COND) \ + /* is this a state change? */ \ + if(!(PHYS_DODGING_PRESSED_KEYS(this) & KEY_##BTN) || frozen_no_doubletap) { \ + tap_direction_##RESULT; \ + if ((time - this.last_##BTN##_KEY_time) < PHYS_DODGING_TIMEOUT(this) || frozen_no_doubletap) \ + dodge_detected = true; \ + if(PHYS_INPUT_BUTTON_DODGE(this)) \ + dodge_detected = true; \ + this.last_##BTN##_KEY_time = time; \ + } + X(x < 0, BACKWARD, x--); + X(x > 0, FORWARD, x++); + X(y < 0, LEFT, y--); + X(y > 0, RIGHT, y++); + #undef X + + if (dodge_detected) + { + this.last_dodging_time = time; + + this.dodging_action = 1; + this.dodging_single_action = 1; + + this.dodging_velocity_gain = PHYS_DODGING_HORIZ_SPEED; + + this.dodging_direction_x = tap_direction_x; + this.dodging_direction_y = tap_direction_y; + + // normalize the dodging_direction vector.. (unlike UT99) XD + float length = this.dodging_direction_x * this.dodging_direction_x + + this.dodging_direction_y * this.dodging_direction_y; + length = sqrt(length); + + this.dodging_direction_x = this.dodging_direction_x * 1.0 / length; + this.dodging_direction_y = this.dodging_direction_y * 1.0 / length; + return true; + } + return false; +} + +void PM_dodging(entity this) +{ + if (!PHYS_DODGING) + return; + + if (IS_DEAD(this)) + return; + + // when swimming, no dodging allowed.. + if (this.waterlevel >= WATERLEVEL_SWIMMING) + { + this.dodging_action = 0; + this.dodging_direction_x = 0; + this.dodging_direction_y = 0; + return; + } + + // make sure v_up, v_right and v_forward are sane + if(PHYS_DODGING_AIR) + makevectors(this.v_angle); + else + makevectors(this.angles); + + // if we have e.g. 0.5 sec ramptime and a frametime of 0.25, then the ramp code + // will be called ramp_time/frametime times = 2 times. so, we need to + // add 0.5 * the total speed each frame until the dodge action is done.. + float common_factor = PHYS_DODGING_FRAMETIME / PHYS_DODGING_RAMP_TIME; + + // if ramp time is smaller than frametime we get problems ;D + common_factor = min(common_factor, 1); + + float horiz_speed = PHYS_FROZEN(this) ? PHYS_DODGING_HORIZ_SPEED_FROZEN : PHYS_DODGING_HORIZ_SPEED; + float new_velocity_gain = this.dodging_velocity_gain - (common_factor * horiz_speed); + new_velocity_gain = max(0, new_velocity_gain); + + float velocity_difference = this.dodging_velocity_gain - new_velocity_gain; + + // ramp up dodging speed by adding some velocity each frame.. TODO: do it! :D + if (this.dodging_action == 1) + { + //disable jump key during dodge accel phase + if(this.movement_z > 0) { this.movement_z = 0; } + + this.velocity += ((this.dodging_direction_y * velocity_difference) * v_right) + + ((this.dodging_direction_x * velocity_difference) * v_forward); + + this.dodging_velocity_gain = this.dodging_velocity_gain - velocity_difference; + } + + // the up part of the dodge is a single shot action + if (this.dodging_single_action == 1) + { + UNSET_ONGROUND(this); + + this.velocity += PHYS_DODGING_UP_SPEED * v_up; + +#ifdef SVQC + if (autocvar_sv_dodging_sound) + PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); + + animdecide_setaction(this, ANIMACTION_JUMP, true); +#endif + + this.dodging_single_action = 0; + } + + // are we done with the dodging ramp yet? + if((this.dodging_action == 1) && ((time - this.last_dodging_time) > PHYS_DODGING_RAMP_TIME)) + { + // reset state so next dodge can be done correctly + this.dodging_action = 0; + this.dodging_direction_x = 0; + this.dodging_direction_y = 0; + } +} + +void PM_dodging_GetPressedKeys(entity this) +{ +#ifdef CSQC + if(!PHYS_DODGING) { return; } + + PM_dodging_checkpressedkeys(this); + + int keys = this.pressedkeys; + keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); + keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); + keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); + keys = BITSET(keys, KEY_LEFT, 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_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); + keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); + this.pressedkeys = keys; +#endif +} + +MUTATOR_HOOKFUNCTION(dodging, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + // print("dodging_PlayerPhysics\n"); + PM_dodging_GetPressedKeys(player); + PM_dodging(player); +} + +#ifdef SVQC + +REPLICATE(cvar_cl_dodging_timeout, float, "cl_dodging_timeout"); + +MUTATOR_HOOKFUNCTION(dodging, GetPressedKeys) +{ + entity player = M_ARGV(0, entity); + + PM_dodging_checkpressedkeys(player); +} + +#endif diff --git a/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/dodging/sv_dodging.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/doublejump/doublejump.qc b/qcsrc/common/mutators/mutator/doublejump/doublejump.qc index d490ecaaff..cc60ccc1fd 100644 --- a/qcsrc/common/mutators/mutator/doublejump/doublejump.qc +++ b/qcsrc/common/mutators/mutator/doublejump/doublejump.qc @@ -1,4 +1,6 @@ -#ifdef IMPLEMENTATION +#include "doublejump.qh" + +#ifdef GAMEQC #ifdef SVQC #include #endif @@ -31,5 +33,4 @@ MUTATOR_HOOKFUNCTION(doublejump, PlayerJump) } } } - #endif diff --git a/qcsrc/common/mutators/mutator/doublejump/doublejump.qh b/qcsrc/common/mutators/mutator/doublejump/doublejump.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/doublejump/doublejump.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/doublejump/module.inc b/qcsrc/common/mutators/mutator/doublejump/module.inc deleted file mode 100644 index f4c695be0e..0000000000 --- a/qcsrc/common/mutators/mutator/doublejump/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "doublejump.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.inc b/qcsrc/common/mutators/mutator/globalforces/_mod.inc new file mode 100644 index 0000000000..2302d85fb1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/_mod.qh b/qcsrc/common/mutators/mutator/globalforces/_mod.qh new file mode 100644 index 0000000000..c31ee5cfcb --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc new file mode 100644 index 0000000000..3e9cd0d51c --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qc @@ -0,0 +1,31 @@ +#include "sv_globalforces.qh" + +AUTOCVAR(g_globalforces, float, false, "Global forces: knockback affects everyone"); +AUTOCVAR(g_globalforces_noself, bool, true, "Global forces: ignore self damage"); +AUTOCVAR(g_globalforces_self, float, 1, "Global forces: knockback self scale"); +AUTOCVAR(g_globalforces_range, float, 1000, "Global forces: max range of effect"); +REGISTER_MUTATOR(mutator_globalforces, autocvar_g_globalforces); + +MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsString) { + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":GlobalForces"); +} + +MUTATOR_HOOKFUNCTION(mutator_globalforces, BuildMutatorsPrettyString) { + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Global forces"); +} + +MUTATOR_HOOKFUNCTION(mutator_globalforces, PlayerDamage_SplitHealthArmor) { + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + if (autocvar_g_globalforces_noself && frag_target == frag_attacker) return; + vector damage_force = M_ARGV(3, vector) * autocvar_g_globalforces; + FOREACH_CLIENT(IS_PLAYER(it) && it != frag_target, { + if (autocvar_g_globalforces_range) { + if (vdist(it.origin - frag_target.origin, >, autocvar_g_globalforces_range)) { + continue; + } + } + float f = (it == frag_attacker) ? autocvar_g_globalforces_self : 1; + it.velocity += damage_explosion_calcpush(f * it.damageforcescale * damage_force, it.velocity, autocvar_g_balance_damagepush_speedfactor); + }); +} diff --git a/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/globalforces/sv_globalforces.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/hook/_mod.inc b/qcsrc/common/mutators/mutator/hook/_mod.inc index ec6da662e7..e5e68b6106 100644 --- a/qcsrc/common/mutators/mutator/hook/_mod.inc +++ b/qcsrc/common/mutators/mutator/hook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/hook/_mod.qh b/qcsrc/common/mutators/mutator/hook/_mod.qh index 50c0c13726..5a5d26e817 100644 --- a/qcsrc/common/mutators/mutator/hook/_mod.qh +++ b/qcsrc/common/mutators/mutator/hook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/hook/hook.qc b/qcsrc/common/mutators/mutator/hook/hook.qc deleted file mode 100644 index e72134e0e7..0000000000 --- a/qcsrc/common/mutators/mutator/hook/hook.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); -#ifdef SVQC -REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { - MUTATOR_ONADD { - g_grappling_hook = true; - WEP_HOOK.ammo_factor = 0; - } - MUTATOR_ONROLLBACK_OR_REMOVE { - g_grappling_hook = false; - WEP_HOOK.ammo_factor = 1; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook"); -} - -MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); -} - -MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.offhand = OFFHAND_HOOK; -} - -MUTATOR_HOOKFUNCTION(hook, FilterItem) -{ - entity item = M_ARGV(0, entity); - - return item.weapon == WEP_HOOK.m_id; -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/hook/module.inc b/qcsrc/common/mutators/mutator/hook/module.inc deleted file mode 100644 index 61600c47b1..0000000000 --- a/qcsrc/common/mutators/mutator/hook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "hook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qc b/qcsrc/common/mutators/mutator/hook/sv_hook.qc new file mode 100644 index 0000000000..b5a196c511 --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/sv_hook.qc @@ -0,0 +1,47 @@ +#include "sv_hook.qh" + +AUTOCVAR(g_grappling_hook, bool, false, _("let players spawn with the grappling hook which allows them to pull themselves up")); +#ifdef SVQC +REGISTER_MUTATOR(hook, autocvar_g_grappling_hook) { + MUTATOR_ONADD { + g_grappling_hook = true; + WEP_HOOK.ammo_factor = 0; + } + MUTATOR_ONROLLBACK_OR_REMOVE { + g_grappling_hook = false; + WEP_HOOK.ammo_factor = 1; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":grappling_hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Hook"); +} + +MUTATOR_HOOKFUNCTION(hook, BuildGameplayTipsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), "\n\n^3grappling hook^8 is enabled, press 'e' to use it\n"); +} + +MUTATOR_HOOKFUNCTION(hook, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.offhand = OFFHAND_HOOK; +} + +MUTATOR_HOOKFUNCTION(hook, FilterItem) +{ + entity item = M_ARGV(0, entity); + + return item.weapon == WEP_HOOK.m_id; +} + +#endif diff --git a/qcsrc/common/mutators/mutator/hook/sv_hook.qh b/qcsrc/common/mutators/mutator/hook/sv_hook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/hook/sv_hook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.inc b/qcsrc/common/mutators/mutator/instagib/_mod.inc index dad005fe49..2195111f0f 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.inc +++ b/qcsrc/common/mutators/mutator/instagib/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify -#include #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/_mod.qh b/qcsrc/common/mutators/mutator/instagib/_mod.qh index 2e88f427e9..7097eaf390 100644 --- a/qcsrc/common/mutators/mutator/instagib/_mod.qh +++ b/qcsrc/common/mutators/mutator/instagib/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify -#include #include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/instagib/instagib.qc b/qcsrc/common/mutators/mutator/instagib/instagib.qc deleted file mode 100644 index 34f3825680..0000000000 --- a/qcsrc/common/mutators/mutator/instagib/instagib.qc +++ /dev/null @@ -1,505 +0,0 @@ -#ifndef MUTATOR_INSTAGIB_H -#define MUTATOR_INSTAGIB_H - -#include "items.qc" - -#ifdef SVQC -float autocvar_g_instagib_invis_alpha; -#endif - -#endif - -#ifdef IMPLEMENTATION -#ifdef SVQC - -int autocvar_g_instagib_ammo_drop; -int autocvar_g_instagib_extralives; -float autocvar_g_instagib_speed_highspeed; - -#include - -#include - -REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball); - -spawnfunc(item_minst_cells) -{ - if (!g_instagib) { remove(this); return; } - if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop; - StartItem(this, ITEM_VaporizerCells); -} - -void instagib_invisibility(entity this) -{ - this.strength_finished = autocvar_g_balance_powerup_strength_time; - StartItem(this, ITEM_Invisibility); -} - -void instagib_extralife(entity this) -{ - StartItem(this, ITEM_ExtraLife); -} - -void instagib_speed(entity this) -{ - this.invincible_finished = autocvar_g_balance_powerup_invincible_time; - StartItem(this, ITEM_Speed); -} - -.float instagib_nextthink; -.float instagib_needammo; -void instagib_stop_countdown(entity e) -{ - if (!e.instagib_needammo) - return; - Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO); - e.instagib_needammo = false; -} -void instagib_ammocheck(entity this) -{ - if(time < this.instagib_nextthink) - return; - if(!IS_PLAYER(this)) - return; // not a player - - if(IS_DEAD(this) || gameover) - instagib_stop_countdown(this); - else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE)) - instagib_stop_countdown(this); - else if(autocvar_g_rm && autocvar_g_rm_laser) - { - if(!this.instagib_needammo) - { - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); - this.instagib_needammo = true; - } - } - else - { - this.instagib_needammo = true; - if (this.health <= 5) - { - Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); - } - else if (this.health <= 10) - { - Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1); - } - else if (this.health <= 20) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2); - } - else if (this.health <= 30) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3); - } - else if (this.health <= 40) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4); - } - else if (this.health <= 50) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5); - } - else if (this.health <= 60) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6); - } - else if (this.health <= 70) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7); - } - else if (this.health <= 80) - { - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8); - } - else if (this.health <= 90) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9); - } - else - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); - Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); - } - } - this.instagib_nextthink = time + 1; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) -{ - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it))); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) -{ - entity item = M_ARGV(1, entity); - - item.monster_loot = spawnfunc_item_minst_cells; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn) -{ - entity mon = M_ARGV(0, entity); - - // always refill ammo - if(mon.monsterid == MON_MAGE.monsterid) - mon.skin = 1; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack) -{ - entity targ = M_ARGV(1, entity); - - if (targ.items & ITEM_Invisibility.m_itemid) - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - instagib_stop_countdown(player); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.effects |= EF_FULLBRIGHT; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - instagib_ammocheck(player); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen) -{ - // no regeneration in instagib - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups) -{ - entity player = M_ARGV(0, entity); - - if (!(player.effects & EF_FULLBRIGHT)) - player.effects |= EF_FULLBRIGHT; - - if (player.items & ITEM_Invisibility.m_itemid) - { - play_countdown(player, player.strength_finished, SND_POWEROFF); - if (time > player.strength_finished) - { - player.alpha = default_player_alpha; - player.exteriorweaponentity.alpha = default_weapon_alpha; - player.items &= ~ITEM_Invisibility.m_itemid; - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); - } - } - else - { - if (time < player.strength_finished) - { - player.alpha = autocvar_g_instagib_invis_alpha; - player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; - player.items |= ITEM_Invisibility.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); - } - } - - if (player.items & ITEM_Speed.m_itemid) - { - play_countdown(player, player.invincible_finished, SND_POWEROFF); - if (time > player.invincible_finished) - { - player.items &= ~ITEM_Speed.m_itemid; - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED); - } - } - else - { - if (time < player.invincible_finished) - { - player.items |= ITEM_Speed.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname); - Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED); - } - } -} - -.float stat_sv_maxspeed; - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics) -{ - entity player = M_ARGV(0, entity); - - if(player.items & ITEM_Speed.m_itemid) - player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor) -{ - M_ARGV(4, float) = M_ARGV(7, float); // take = damage - M_ARGV(5, float) = 0; // save -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon) -{ - // weapon dropping on death handled by FilterItem - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_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); - vector frag_force = M_ARGV(6, vector); - - if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) - frag_damage = 0; - - if(IS_PLAYER(frag_target)) - { - if(frag_deathtype == DEATH_FALL.m_id) - frag_damage = 0; // never count fall damage - - if(!autocvar_g_instagib_damagedbycontents) - switch(DEATH_ENT(frag_deathtype)) - { - case DEATH_DROWN: - case DEATH_SLIME: - case DEATH_LAVA: - frag_damage = 0; - break; - } - - if(IS_PLAYER(frag_attacker)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) - { - if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker)) - frag_force = '0 0 0'; - - if(frag_target.armorvalue) - { - frag_target.armorvalue -= 1; - frag_damage = 0; - frag_target.damage_dealt += 1; - frag_attacker.damage_dealt += 1; - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); - } - } - - if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) - { - if(frag_deathtype & HITTYPE_SECONDARY) - { - if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target) - { - frag_damage = 0; - if(!autocvar_g_instagib_mirrordamage) - frag_mirrordamage = 0; // never do mirror damage on enemies - } - - if(frag_target != frag_attacker) - { - if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } - if(!autocvar_g_instagib_blaster_keepforce) - frag_force = '0 0 0'; - } - } - } - } - - if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring - if(IS_PLAYER(frag_attacker)) - if(frag_mirrordamage > 0) - { - // just lose extra LIVES, don't kill the player for mirror damage - if(frag_attacker.armorvalue > 0) - { - frag_attacker.armorvalue -= 1; - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue); - frag_attacker.damage_dealt += frag_mirrordamage; - } - frag_mirrordamage = 0; - } - - if(frag_target.alpha && frag_target.alpha < 1) - if(IS_PLAYER(frag_target)) - yoda = 1; - - M_ARGV(4, float) = frag_damage; - M_ARGV(5, float) = frag_mirrordamage; - M_ARGV(6, vector) = frag_force; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems) -{ - start_health = warmup_start_health = 100; - start_armorvalue = warmup_start_armorvalue = 0; - - start_ammo_shells = warmup_start_ammo_shells = 0; - start_ammo_nails = warmup_start_ammo_nails = 0; - start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); - start_ammo_plasma = warmup_start_ammo_plasma = 0; - start_ammo_rockets = warmup_start_ammo_rockets = 0; - start_ammo_fuel = warmup_start_ammo_fuel = 0; - - start_weapons = warmup_start_weapons = WEPSET(VAPORIZER); - start_items |= IT_UNLIMITED_SUPERWEAPONS; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(item.classname == "item_cells") - return true; // no normal cells? - - if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon") - { - item.ammo_cells = autocvar_g_instagib_ammo_drop; - return false; - } - - if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id) - { - entity e = spawn(); - setorigin(e, item.origin); - e.noalign = item.noalign; - e.cnt = item.cnt; - e.team = item.team; - e.spawnfunc_checked = true; - spawnfunc_item_minst_cells(e); - return true; - } - - if(item.flags & FL_POWERUP) - return false; - - if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells") - item.ammo_cells = autocvar_g_instagib_ammo_drop; - - if(item.ammo_cells && !item.weapon) - return false; - - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint) -{ - entity wp = M_ARGV(0, entity); - entity player = M_ARGV(1, entity); - - entity e = WaypointSprite_getviewentity(player); - - // if you have the invisibility powerup, sprites ALWAYS are restricted to your team - // but only apply this to real players, not to spectators - if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player)) - if(DIFF_TEAM(wp.owner, e)) - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies) -{ - float frag_deathtype = M_ARGV(3, float); - - if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) - M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch) -{ - entity item = M_ARGV(0, entity); - entity toucher = M_ARGV(1, entity); - - if(item.ammo_cells) - { - // play some cool sounds ;) - if (IS_CLIENT(toucher)) - { - if(toucher.health <= 5) - Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND); - else if(toucher.health < 50) - Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY); - } - - if(toucher.health < 100) - toucher.health = 100; - - return MUT_ITEMTOUCH_CONTINUE; - } - - if(item.itemdef == ITEM_ExtraLife) - { - toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives); - Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); - return MUT_ITEMTOUCH_PICKUP; - } - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn) -{ - if (!autocvar_g_powerups) { return; } - entity ent = M_ARGV(0, entity); - // Can't use .itemdef here - if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega")) - return; - - entity e = spawn(); - - float r = random(); - if (r < 0.3) - setthink(e, instagib_invisibility); - else if (r < 0.6) - setthink(e, instagib_extralife); - else - setthink(e, instagib_speed); - - e.nextthink = time + 0.1; - e.spawnflags = ent.spawnflags; - e.noalign = ent.noalign; - setorigin(e, ent.origin); - - return true; -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib"); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib"); -} - -MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname) -{ - M_ARGV(0, string) = "InstaGib"; - return true; -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/instagib/items.qc b/qcsrc/common/mutators/mutator/instagib/items.qc index 269ec2d245..b0205a5f7b 100644 --- a/qcsrc/common/mutators/mutator/instagib/items.qc +++ b/qcsrc/common/mutators/mutator/instagib/items.qc @@ -1,84 +1 @@ -#pragma once - -#include -#include -#include - -float instagib_respawntime_ammo = 45; -float instagib_respawntimejitter_ammo = 0; -GETTER(float, instagib_respawntime_ammo) -GETTER(float, instagib_respawntimejitter_ammo) - -#ifndef MENUQC -MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3")); -SOUND(VaporizerCells, "misc/itempickup"); -#endif - -REGISTER_ITEM(VaporizerCells, Ammo) { -#ifndef MENUQC - this.m_model = MDL_VaporizerCells_ITEM; - this.m_sound = SND_VaporizerCells; -#endif - this.m_name = "Vaporizer Ammo"; - this.m_icon = "ammo_supercells"; -#ifdef SVQC - this.m_botvalue = 100; - this.m_itemid = IT_CELLS; - this.m_respawntime = GET(instagib_respawntime_ammo); - this.m_respawntimejitter = GET(instagib_respawntimejitter_ammo); -#endif -} - -#ifndef MENUQC -MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3")); -SOUND(ExtraLife, "misc/megahealth"); -#endif - -REGISTER_ITEM(ExtraLife, Powerup) { -#ifndef MENUQC - this.m_model = MDL_ExtraLife_ITEM; - this.m_sound = SND_ExtraLife; -#endif - this.m_name = "Extra life"; - this.m_icon = "item_mega_health"; - this.m_color = '1 0 0'; - this.m_waypoint = _("Extra life"); - this.m_waypointblink = 2; - this.m_itemid = IT_NAILS; -} - -#ifndef MENUQC -MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); -SOUND(Invisibility, "misc/powerup"); -#endif - -REGISTER_ITEM(Invisibility, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Invisibility_ITEM; - this.m_sound = SND_Invisibility; -#endif - this.m_name = "Invisibility"; - this.m_icon = "strength"; - this.m_color = '0 0 1'; - this.m_waypoint = _("Invisibility"); - this.m_waypointblink = 2; - this.m_itemid = IT_STRENGTH; -} - -#ifndef MENUQC -MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); -SOUND(Speed, "misc/powerup_shield"); -#endif - -REGISTER_ITEM(Speed, Powerup) { -#ifndef MENUQC - this.m_model = MDL_Speed_ITEM; - this.m_sound = SND_Speed; -#endif - this.m_name = "Speed"; - this.m_icon = "shield"; - this.m_color = '1 0 1'; - this.m_waypoint = _("Speed"); - this.m_waypointblink = 2; - this.m_itemid = IT_INVINCIBLE; -} +#include "items.qh" diff --git a/qcsrc/common/mutators/mutator/instagib/items.qh b/qcsrc/common/mutators/mutator/instagib/items.qh new file mode 100644 index 0000000000..1efa4cbdc1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/items.qh @@ -0,0 +1,84 @@ +#pragma once + +#include +#include +#include + +float instagib_respawntime_ammo = 45; +float instagib_respawntimejitter_ammo = 0; +GETTER(float, instagib_respawntime_ammo) +GETTER(float, instagib_respawntimejitter_ammo) + +#ifdef GAMEQC +MODEL(VaporizerCells_ITEM, Item_Model("a_cells.md3")); +SOUND(VaporizerCells, Item_Sound("itempickup")); +#endif + +REGISTER_ITEM(VaporizerCells, Ammo) { +#ifdef GAMEQC + this.m_model = MDL_VaporizerCells_ITEM; + this.m_sound = SND_VaporizerCells; +#endif + this.m_name = "Vaporizer Ammo"; + this.m_icon = "ammo_supercells"; +#ifdef SVQC + this.m_botvalue = 100; + this.m_itemid = IT_CELLS; + this.m_respawntime = GET(instagib_respawntime_ammo); + this.m_respawntimejitter = GET(instagib_respawntimejitter_ammo); +#endif +} + +#ifdef GAMEQC +MODEL(ExtraLife_ITEM, Item_Model("g_h100.md3")); +SOUND(ExtraLife, Item_Sound("megahealth")); +#endif + +REGISTER_ITEM(ExtraLife, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_ExtraLife_ITEM; + this.m_sound = SND_ExtraLife; +#endif + this.m_name = "Extra life"; + this.m_icon = "item_mega_health"; + this.m_color = '1 0 0'; + this.m_waypoint = _("Extra life"); + this.m_waypointblink = 2; + this.m_itemid = IT_NAILS; +} + +#ifdef GAMEQC +MODEL(Invisibility_ITEM, Item_Model("g_strength.md3")); +SOUND(Invisibility, Item_Sound("powerup")); +#endif + +REGISTER_ITEM(Invisibility, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Invisibility_ITEM; + this.m_sound = SND_Invisibility; +#endif + this.m_name = "Invisibility"; + this.m_icon = "strength"; + this.m_color = '0 0 1'; + this.m_waypoint = _("Invisibility"); + this.m_waypointblink = 2; + this.m_itemid = IT_STRENGTH; +} + +#ifdef GAMEQC +MODEL(Speed_ITEM, Item_Model("g_invincible.md3")); +SOUND(Speed, Item_Sound("powerup_shield")); +#endif + +REGISTER_ITEM(Speed, Powerup) { +#ifdef GAMEQC + this.m_model = MDL_Speed_ITEM; + this.m_sound = SND_Speed; +#endif + this.m_name = "Speed"; + this.m_icon = "shield"; + this.m_color = '1 0 1'; + this.m_waypoint = _("Speed"); + this.m_waypointblink = 2; + this.m_itemid = IT_INVINCIBLE; +} diff --git a/qcsrc/common/mutators/mutator/instagib/module.inc b/qcsrc/common/mutators/mutator/instagib/module.inc deleted file mode 100644 index 7270067d35..0000000000 --- a/qcsrc/common/mutators/mutator/instagib/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "instagib.qc" diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc new file mode 100644 index 0000000000..4c6dc15a3c --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -0,0 +1,535 @@ +#include "sv_instagib.qh" + +int autocvar_g_instagib_ammo_drop; +bool autocvar_g_instagib_ammo_convert_cells; +bool autocvar_g_instagib_ammo_convert_rockets; +bool autocvar_g_instagib_ammo_convert_shells; +bool autocvar_g_instagib_ammo_convert_bullets; +int autocvar_g_instagib_extralives; +float autocvar_g_instagib_speed_highspeed; + +#include + +#include + +REGISTER_MUTATOR(mutator_instagib, cvar("g_instagib") && !g_nexball); + +spawnfunc(item_minst_cells) +{ + if (!g_instagib) { delete(this); return; } + if (!this.ammo_cells) this.ammo_cells = autocvar_g_instagib_ammo_drop; + StartItem(this, ITEM_VaporizerCells); +} + +void instagib_invisibility(entity this) +{ + this.strength_finished = autocvar_g_balance_powerup_strength_time; + StartItem(this, ITEM_Invisibility); +} + +void instagib_extralife(entity this) +{ + StartItem(this, ITEM_ExtraLife); +} + +void instagib_speed(entity this) +{ + this.invincible_finished = autocvar_g_balance_powerup_invincible_time; + StartItem(this, ITEM_Speed); +} + +.float instagib_nextthink; +.float instagib_needammo; +void instagib_stop_countdown(entity e) +{ + if (!e.instagib_needammo) + return; + Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_INSTAGIB_FINDAMMO); + e.instagib_needammo = false; +} +void instagib_ammocheck(entity this) +{ + if(time < this.instagib_nextthink) + return; + if(!IS_PLAYER(this)) + return; // not a player + + if(IS_DEAD(this) || gameover) + instagib_stop_countdown(this); + else if (this.ammo_cells > 0 || (this.items & IT_UNLIMITED_WEAPON_AMMO) || (this.flags & FL_GODMODE)) + instagib_stop_countdown(this); + else if(autocvar_g_rm && autocvar_g_rm_laser) + { + if(!this.instagib_needammo) + { + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_INSTAGIB_DOWNGRADE); + this.instagib_needammo = true; + } + } + else + { + this.instagib_needammo = true; + if (this.health <= 5) + { + Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_INSTAGIB_TERMINATED); + } + else if (this.health <= 10) + { + Damage(this, this, this, 5, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_1); + } + else if (this.health <= 20) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_2); + } + else if (this.health <= 30) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_3); + } + else if (this.health <= 40) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_4); + } + else if (this.health <= 50) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_5); + } + else if (this.health <= 60) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_6); + } + else if (this.health <= 70) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_7); + } + else if (this.health <= 80) + { + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_8); + } + else if (this.health <= 90) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_INSTAGIB_FINDAMMO); + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, ANNCE_NUM_9); + } + else + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_MULTI, MULTI_INSTAGIB_FINDAMMO); + Damage(this, this, this, 10, DEATH_NOAMMO.m_id, this.origin, '0 0 0'); + } + } + this.instagib_nextthink = time + 1; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MatchEnd) +{ + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(instagib_stop_countdown(it))); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterDropItem) +{ + entity item = M_ARGV(1, entity); + + item.monster_loot = spawnfunc_item_minst_cells; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MonsterSpawn) +{ + entity mon = M_ARGV(0, entity); + + // always refill ammo + if(mon.monsterid == MON_MAGE.monsterid) + mon.skin = 1; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BotShouldAttack) +{ + entity targ = M_ARGV(1, entity); + + if (targ.items & ITEM_Invisibility.m_itemid) + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + instagib_stop_countdown(player); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.effects |= EF_FULLBRIGHT; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + instagib_ammocheck(player); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerRegen) +{ + // no regeneration in instagib + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPowerups) +{ + entity player = M_ARGV(0, entity); + + if (!(player.effects & EF_FULLBRIGHT)) + player.effects |= EF_FULLBRIGHT; + + if (player.items & ITEM_Invisibility.m_itemid) + { + play_countdown(player, player.strength_finished, SND_POWEROFF); + if (time > player.strength_finished) + { + player.alpha = default_player_alpha; + player.exteriorweaponentity.alpha = default_weapon_alpha; + player.items &= ~ITEM_Invisibility.m_itemid; + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_INVISIBILITY); + } + } + else + { + if (time < player.strength_finished) + { + player.alpha = autocvar_g_instagib_invis_alpha; + player.exteriorweaponentity.alpha = autocvar_g_instagib_invis_alpha; + player.items |= ITEM_Invisibility.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_INVISIBILITY, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_INVISIBILITY); + } + } + + if (player.items & ITEM_Speed.m_itemid) + { + play_countdown(player, player.invincible_finished, SND_POWEROFF); + if (time > player.invincible_finished) + { + player.items &= ~ITEM_Speed.m_itemid; + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERDOWN_SPEED); + } + } + else + { + if (time < player.invincible_finished) + { + player.items |= ITEM_Speed.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SPEED, player.netname); + Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_POWERUP_SPEED); + } + } +} + +.float stat_sv_maxspeed; + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerPhysics) +{ + entity player = M_ARGV(0, entity); + + if(player.items & ITEM_Speed.m_itemid) + player.stat_sv_maxspeed = player.stat_sv_maxspeed * autocvar_g_instagib_speed_highspeed; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_SplitHealthArmor) +{ + M_ARGV(4, float) = M_ARGV(7, float); // take = damage + M_ARGV(5, float) = 0; // save +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, ForbidThrowCurrentWeapon) +{ + // weapon dropping on death handled by FilterItem + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDamage_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); + vector frag_force = M_ARGV(6, vector); + + if(autocvar_g_friendlyfire == 0 && SAME_TEAM(frag_target, frag_attacker) && IS_PLAYER(frag_target) && IS_PLAYER(frag_attacker)) + frag_damage = 0; + + if(IS_PLAYER(frag_target)) + { + if(frag_deathtype == DEATH_FALL.m_id) + frag_damage = 0; // never count fall damage + + if(!autocvar_g_instagib_damagedbycontents) + switch(DEATH_ENT(frag_deathtype)) + { + case DEATH_DROWN: + case DEATH_SLIME: + case DEATH_LAVA: + frag_damage = 0; + break; + } + + if(IS_PLAYER(frag_attacker)) + if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) + { + if(!autocvar_g_instagib_friendlypush && SAME_TEAM(frag_target, frag_attacker)) + frag_force = '0 0 0'; + + if(frag_target.armorvalue) + { + frag_target.armorvalue -= 1; + frag_damage = 0; + frag_target.damage_dealt += 1; + frag_attacker.damage_dealt += 1; + Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_target.armorvalue); + } + } + + if(IS_PLAYER(frag_attacker) && DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + if(frag_deathtype & HITTYPE_SECONDARY) + { + if(!autocvar_g_instagib_blaster_keepdamage || frag_attacker == frag_target) + { + frag_damage = 0; + if(!autocvar_g_instagib_mirrordamage) + frag_mirrordamage = 0; // never do mirror damage on enemies + } + + if(frag_target != frag_attacker) + { + if(frag_damage <= 0 && frag_target.health > 0) { Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); } + if(!autocvar_g_instagib_blaster_keepforce) + frag_force = '0 0 0'; + } + } + } + } + + if(!autocvar_g_instagib_mirrordamage) // only apply the taking lives hack if we don't want to support real damage mirroring + if(IS_PLAYER(frag_attacker)) + if(frag_mirrordamage > 0) + { + // just lose extra LIVES, don't kill the player for mirror damage + if(frag_attacker.armorvalue > 0) + { + frag_attacker.armorvalue -= 1; + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_INSTAGIB_LIVES_REMAINING, frag_attacker.armorvalue); + frag_attacker.damage_dealt += frag_mirrordamage; + } + frag_mirrordamage = 0; + } + + if(frag_target.alpha && frag_target.alpha < 1) + if(IS_PLAYER(frag_target)) + yoda = 1; + + M_ARGV(4, float) = frag_damage; + M_ARGV(5, float) = frag_mirrordamage; + M_ARGV(6, vector) = frag_force; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, SetStartItems) +{ + start_health = warmup_start_health = 100; + start_armorvalue = warmup_start_armorvalue = 0; + + start_ammo_shells = warmup_start_ammo_shells = 0; + start_ammo_nails = warmup_start_ammo_nails = 0; + start_ammo_cells = warmup_start_ammo_cells = cvar("g_instagib_ammo_start"); + start_ammo_plasma = warmup_start_ammo_plasma = 0; + start_ammo_rockets = warmup_start_ammo_rockets = 0; + start_ammo_fuel = warmup_start_ammo_fuel = 0; + + start_weapons = warmup_start_weapons = WEPSET(VAPORIZER); + start_items |= IT_UNLIMITED_SUPERWEAPONS; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, SetWeaponArena) +{ + // turn weapon arena off + M_ARGV(0, string) = "off"; +} + +void replace_with_insta_cells(entity item) +{ + entity e = spawn(); + setorigin(e, item.origin); + e.noalign = item.noalign; + e.cnt = item.cnt; + e.team = item.team; + e.spawnfunc_checked = true; + spawnfunc_item_minst_cells(e); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(item.classname == "item_cells") + { + if(autocvar_g_instagib_ammo_convert_cells) + { + replace_with_insta_cells(item); + } + return true; + } + else if(item.classname == "item_rockets") + { + if(autocvar_g_instagib_ammo_convert_rockets) + { + replace_with_insta_cells(item); + } + return true; + } + else if(item.classname == "item_shells") + { + if(autocvar_g_instagib_ammo_convert_shells) + { + replace_with_insta_cells(item); + } + return true; + } + else if(item.classname == "item_bullets") + { + if(autocvar_g_instagib_ammo_convert_bullets) + { + replace_with_insta_cells(item); + } + return true; + } + + if(item.weapon == WEP_VAPORIZER.m_id && item.classname == "droppedweapon") + { + item.ammo_cells = autocvar_g_instagib_ammo_drop; + return false; + } + + if(item.weapon == WEP_DEVASTATOR.m_id || item.weapon == WEP_VORTEX.m_id) + { + replace_with_insta_cells(item); + return true; + } + + if(item.flags & FL_POWERUP) + return false; + + if(item.ammo_cells > autocvar_g_instagib_ammo_drop && item.classname != "item_minst_cells") + item.ammo_cells = autocvar_g_instagib_ammo_drop; + + if(item.ammo_cells && !item.weapon) + return false; + + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, CustomizeWaypoint) +{ + entity wp = M_ARGV(0, entity); + entity player = M_ARGV(1, entity); + + entity e = WaypointSprite_getviewentity(player); + + // if you have the invisibility powerup, sprites ALWAYS are restricted to your team + // but only apply this to real players, not to spectators + if((wp.owner.flags & FL_CLIENT) && (wp.owner.items & ITEM_Invisibility.m_itemid) && (e == player)) + if(DIFF_TEAM(wp.owner, e)) + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, PlayerDies) +{ + float frag_deathtype = M_ARGV(3, float); + + if(DEATH_ISWEAPON(frag_deathtype, WEP_VAPORIZER)) + M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + if(item.ammo_cells) + { + // play some cool sounds ;) + if (IS_CLIENT(toucher)) + { + if(toucher.health <= 5) + Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_LASTSECOND); + else if(toucher.health < 50) + Send_Notification(NOTIF_ONE, toucher, MSG_ANNCE, ANNCE_INSTAGIB_NARROWLY); + } + + if(toucher.health < 100) + toucher.health = 100; + + return MUT_ITEMTOUCH_CONTINUE; + } + + if(item.itemdef == ITEM_ExtraLife) + { + toucher.armorvalue = bound(toucher.armorvalue, 999, toucher.armorvalue + autocvar_g_instagib_extralives); + Send_Notification(NOTIF_ONE, toucher, MSG_CENTER, CENTER_EXTRALIVES); + return MUT_ITEMTOUCH_PICKUP; + } + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, OnEntityPreSpawn) +{ + if (!autocvar_g_powerups) { return; } + entity ent = M_ARGV(0, entity); + // Can't use .itemdef here + if (!(ent.classname == "item_strength" || ent.classname == "item_invincible" || ent.classname == "item_health_mega")) + return; + + entity e = spawn(); + + float r = random(); + if (r < 0.3) + setthink(e, instagib_invisibility); + else if (r < 0.6) + setthink(e, instagib_extralife); + else + setthink(e, instagib_speed); + + e.nextthink = time + 0.1; + e.spawnflags = ent.spawnflags; + e.noalign = ent.noalign; + setorigin(e, ent.origin); + + return true; +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":instagib"); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", instagib"); +} + +MUTATOR_HOOKFUNCTION(mutator_instagib, SetModname) +{ + M_ARGV(0, string) = "InstaGib"; + return true; +} diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh new file mode 100644 index 0000000000..4c6d20b129 --- /dev/null +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qh @@ -0,0 +1,5 @@ +#pragma once + +#include "items.qh" + +float autocvar_g_instagib_invis_alpha; diff --git a/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc b/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc index 68d313e95c..eb8c95fcb8 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc +++ b/qcsrc/common/mutators/mutator/invincibleproj/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh b/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh index dc3f32f10e..2d59a0891a 100644 --- a/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh +++ b/qcsrc/common/mutators/mutator/invincibleproj/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc deleted file mode 100644 index 5bdafa1f12..0000000000 --- a/qcsrc/common/mutators/mutator/invincibleproj/invincibleproj.qc +++ /dev/null @@ -1,24 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) -{ - entity proj = M_ARGV(1, entity); - - if(proj.health) - { - // disable health which in effect disables damage calculations - proj.health = 0; - } -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles"); -} - -MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/module.inc b/qcsrc/common/mutators/mutator/invincibleproj/module.inc deleted file mode 100644 index 61d038350f..0000000000 --- a/qcsrc/common/mutators/mutator/invincibleproj/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "invincibleproj.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc new file mode 100644 index 0000000000..23e0d0d850 --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qc @@ -0,0 +1,24 @@ +#include "sv_invincibleproj.qh" + +REGISTER_MUTATOR(invincibleprojectiles, cvar("g_invincible_projectiles")); + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, EditProjectile) +{ + entity proj = M_ARGV(1, entity); + + if(proj.health) + { + // disable health which in effect disables damage calculations + proj.health = 0; + } +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":InvincibleProjectiles"); +} + +MUTATOR_HOOKFUNCTION(invincibleprojectiles, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Invincible Projectiles"); +} diff --git a/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/invincibleproj/sv_invincibleproj.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime.qc deleted file mode 100644 index 1c4de9a4e2..0000000000 --- a/qcsrc/common/mutators/mutator/itemstime.qc +++ /dev/null @@ -1,428 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(itemstime, true); - -REGISTER_NET_TEMP(itemstime) - -#ifdef SVQC -void IT_Write(entity e, int i, float f) { - if (!IS_REAL_CLIENT(e)) return; - msg_entity = e; - WriteHeader(MSG_ONE, itemstime); - WriteByte(MSG_ONE, i); - WriteFloat(MSG_ONE, f); -} -#endif - -#ifdef CSQC -// reserve one more spot for superweapons time -float ItemsTime_time[Items_MAX + 1]; -float ItemsTime_availableTime[Items_MAX + 1]; -NET_HANDLE(itemstime, bool isNew) -{ - int i = ReadByte(); - float f = ReadFloat(); - return = true; - ItemsTime_time[i] = f; -} -#endif - -#ifdef CSQC -void Item_ItemsTime_Init() -{ - FOREACH(Items, true, LAMBDA( - ItemsTime_time[it.m_id] = -1; - )); - ItemsTime_time[Items_MAX] = -1; -} - -STATIC_INIT(ItemsTime_Init) { - Item_ItemsTime_Init(); -} - -int autocvar_hud_panel_itemstime = 2; -float autocvar_hud_panel_itemstime_dynamicsize = 1; -float autocvar_hud_panel_itemstime_ratio = 2; -int autocvar_hud_panel_itemstime_iconalign; -bool autocvar_hud_panel_itemstime_progressbar = 0; -float autocvar_hud_panel_itemstime_progressbar_maxtime = 30; -string autocvar_hud_panel_itemstime_progressbar_name = "progressbar"; -float autocvar_hud_panel_itemstime_progressbar_reduced; -bool autocvar_hud_panel_itemstime_hidespawned = 1; -bool autocvar_hud_panel_itemstime_hidelarge = false; -int autocvar_hud_panel_itemstime_text = 1; -#define hud_panel_itemstime_hidelarge autocvar_hud_panel_itemstime_hidelarge -#else -#define hud_panel_itemstime_hidelarge false -#endif - -bool Item_ItemsTime_SpectatorOnly(GameItem it) -{ - return (false - || it == ITEM_ArmorMega || (it == ITEM_ArmorLarge && !hud_panel_itemstime_hidelarge) - || it == ITEM_HealthMega || (it == ITEM_HealthLarge && !hud_panel_itemstime_hidelarge) - ); -} - -bool Item_ItemsTime_Allow(GameItem it) -{ - return (false - || it.instanceOfPowerup - || Item_ItemsTime_SpectatorOnly(it) - ); -} - -#ifdef SVQC - -// reserve one more spot for superweapons time -float it_times[Items_MAX + 1]; - -void Item_ItemsTime_Init() -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - it_times[it.m_id] = -1; - )); - it_times[Items_MAX] = -1; -} - -STATIC_INIT(ItemsTime_Init) { - // items time - Item_ItemsTime_Init(); -} - -void Item_ItemsTime_ResetTimes() -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0; - )); - it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0; -} - -void Item_ItemsTime_ResetTimesForPlayer(entity e) -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0); - )); - IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0); -} - -void Item_ItemsTime_SetTimesForPlayer(entity e) -{ - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - IT_Write(e, it.m_id, it_times[it.m_id]); - )); - IT_Write(e, Items_MAX, it_times[Items_MAX]); -} - -void Item_ItemsTime_SetTime(entity e, float t) -{ - if (!autocvar_sv_itemstime) - return; - - GameItem item = e.itemdef; - if (item.instanceOfGameItem) - { - if (!item.instanceOfWeaponPickup) - it_times[item.m_id] = t; - else if (e.weapons & WEPSET_SUPERWEAPONS) - it_times[Items_MAX] = t; - } -} - -void Item_ItemsTime_SetTimesForAllPlayers() -{ - FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it)), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it))); -} - -float Item_ItemsTime_UpdateTime(entity e, float t) -{ - bool isavailable = (t == 0); - FOREACH_ENTITY(it.itemdef == e.itemdef || ((e.weapons & WEPSET_SUPERWEAPONS) && (it.weapons & WEPSET_SUPERWEAPONS) && clienttype(it) == CLIENTTYPE_NOTACLIENT), LAMBDA( - if (e == it) continue; - if (it.scheduledrespawntime <= time) - isavailable = true; - else if (t == 0 || it.scheduledrespawntime < t) - t = it.scheduledrespawntime; - )); - if (isavailable) - t = -t; // let know the client there's another available item - return t; -} - -MUTATOR_HOOKFUNCTION(itemstime, reset_map_global) -{ - Item_ItemsTime_ResetTimes(); - // ALL the times need to be reset before .reset()ing each item - // since Item_Reset schedules respawn of superweapons and powerups - FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA( - if (it.reset) Item_ItemsTime_SetTime(it, 0); - )); - Item_ItemsTime_SetTimesForAllPlayers(); -} - -MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - Item_ItemsTime_SetTimesForPlayer(player); -} - -MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST) -{ - entity player = M_ARGV(0, entity); - - if(IS_PLAYER(player)) - { - // client became player on connection skipping putObserverInServer step - if (IS_REAL_CLIENT(player)) - if (warmup_stage) - Item_ItemsTime_SetTimesForPlayer(player); - } -} - -MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn) -{ - if (warmup_stage) return; - entity player = M_ARGV(0, entity); - - Item_ItemsTime_ResetTimesForPlayer(player); -} - -#endif - -#ifdef CSQC - -void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime) -{ - float t = 0; - vector color = '0 0 0'; - float picalpha; - - if (autocvar_hud_panel_itemstime_hidespawned == 2) - picalpha = 1; - else if (item_available) - { - float BLINK_FACTOR = 0.15; - float BLINK_BASE = 0.85; - float BLINK_FREQ = 5; - picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); - } - else - picalpha = 0.5; - t = floor(item_time - time + 0.999); - if (t < 5) - color = '0.7 0 0'; - else if (t < 10) - color = '0.7 0.7 0'; - else - color = '1 1 1'; - - vector picpos, numpos; - if (autocvar_hud_panel_itemstime_iconalign) - { - numpos = myPos; - picpos = myPos + eX * (ar - 1) * mySize_y; - } - else - { - numpos = myPos + eX * mySize_y; - picpos = myPos; - } - - if (t > 0 && autocvar_hud_panel_itemstime_progressbar) - { - vector p_pos, p_size; - if (autocvar_hud_panel_itemstime_progressbar_reduced) - { - p_pos = numpos; - p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y; - } - else - { - p_pos = myPos; - p_size = mySize; - } - HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - if(autocvar_hud_panel_itemstime_text) - { - if(t > 0) - drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL); - else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it - drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); - else // legacy code, if the image is missing just center the icon - picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2; - } - if (item_availableTime) - drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime); - drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); -} - -void HUD_ItemsTime() -{ - if (!autocvar__hud_configure) - { - if (!( - (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0) - || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage)) - )) { return; } - } - else - { - ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0; - ItemsTime_time[ITEM_HealthMega.m_id] = time + 8; - ItemsTime_time[ITEM_Strength.m_id] = time + 0; - ItemsTime_time[ITEM_Shield.m_id] = time + 4; - } - - int count = 0; - if (autocvar_hud_panel_itemstime_hidespawned == 1) - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time); - )); - count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time); - } - else if (autocvar_hud_panel_itemstime_hidespawned == 2) - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] > time); - )); - count += (ItemsTime_time[Items_MAX] > time); - } - else - { - FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( - count += (ItemsTime_time[it.m_id] != -1); - )); - count += (ItemsTime_time[Items_MAX] != -1); - } - if (count == 0) - return; - - HUD_Panel_UpdateCvars(); - - vector pos, mySize; - pos = panel_pos; - mySize = panel_size; - - if (panel_bg_padding) - { - pos += '1 1 0' * panel_bg_padding; - mySize -= '2 2 0' * panel_bg_padding; - } - - float rows, columns; - float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1; - rows = HUD_GetRowCount(count, mySize, ar); - columns = ceil(count/rows); - - vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); - - vector offset = '0 0 0'; - float newSize; - if (autocvar_hud_panel_itemstime_dynamicsize) - { - if (autocvar__hud_configure) - if (menu_enabled != 2) - HUD_Panel_DrawBg(1); // also draw the bg of the entire panel - - // reduce panel to avoid spacing items - if (itemstime_size.x / itemstime_size.y < ar) - { - newSize = rows * itemstime_size.x / ar; - pos.y += (mySize.y - newSize) / 2; - mySize.y = newSize; - itemstime_size.y = mySize.y / rows; - } - else - { - newSize = columns * itemstime_size.y * ar; - pos.x += (mySize.x - newSize) / 2; - mySize.x = newSize; - itemstime_size.x = mySize.x / columns; - } - panel_pos = pos - '1 1 0' * panel_bg_padding; - panel_size = mySize + '2 2 0' * panel_bg_padding; - } - else - { - if (itemstime_size.x/itemstime_size.y > ar) - { - newSize = ar * itemstime_size.y; - offset.x = itemstime_size.x - newSize; - pos.x += offset.x/2; - itemstime_size.x = newSize; - } - else - { - newSize = 1/ar * itemstime_size.x; - offset.y = itemstime_size.y - newSize; - pos.y += offset.y/2; - itemstime_size.y = newSize; - } - } - - HUD_Scale_Enable(); - HUD_Panel_DrawBg(1); - - float row = 0, column = 0; - bool item_available; - int id = 0; - string icon = ""; - FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA( - id = it.m_id; - icon = it.m_icon; - -LABEL(iteration) - float item_time = ItemsTime_time[id]; - if (item_time < -1) - { - item_available = true; - item_time = -item_time; - } - else - item_available = (item_time <= time); - - if (ItemsTime_time[id] >= 0) - { - if (time <= ItemsTime_time[id]) - ItemsTime_availableTime[id] = 0; - else if (ItemsTime_availableTime[id] == 0) - ItemsTime_availableTime[id] = time; - } - else if (ItemsTime_availableTime[id] == 0) - ItemsTime_availableTime[id] = time; - - float f = (time - ItemsTime_availableTime[id]) * 2; - f = (f > 1) ? 0 : bound(0, f, 1); - - if (autocvar_hud_panel_itemstime_hidespawned == 1) - if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time)) - continue; - - if (autocvar_hud_panel_itemstime_hidespawned == 2) - if (!(ItemsTime_time[id] > time)) - continue; - - DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f); - ++row; - if (row >= rows) - { - row = 0; - column = column + 1; - } - if(id == Items_MAX) // can happen only in the last fake iteration - break; - )); - // add another fake iteration for superweapons time - if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1) - { - id = Items_MAX; - icon = "superweapons"; - goto iteration; - } -} - -#endif -#endif diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.inc b/qcsrc/common/mutators/mutator/itemstime/_mod.inc new file mode 100644 index 0000000000..5b34dd6210 --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/_mod.inc @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/common/mutators/mutator/itemstime/_mod.qh b/qcsrc/common/mutators/mutator/itemstime/_mod.qh new file mode 100644 index 0000000000..5c73eea2ec --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/_mod.qh @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc new file mode 100644 index 0000000000..3db867a5b9 --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc @@ -0,0 +1,431 @@ +#include "itemstime.qh" + +REGISTER_MUTATOR(itemstime, true); + +REGISTER_NET_TEMP(itemstime) + +#ifdef SVQC +void IT_Write(entity e, int i, float f) { + if (!IS_REAL_CLIENT(e)) return; + msg_entity = e; + WriteHeader(MSG_ONE, itemstime); + WriteByte(MSG_ONE, i); + WriteFloat(MSG_ONE, f); +} +#endif + +#ifdef CSQC +// reserve one more spot for superweapons time +float ItemsTime_time[Items_MAX + 1]; +float ItemsTime_availableTime[Items_MAX + 1]; +NET_HANDLE(itemstime, bool isNew) +{ + int i = ReadByte(); + float f = ReadFloat(); + return = true; + ItemsTime_time[i] = f; +} +#endif + +#ifdef CSQC +void Item_ItemsTime_Init() +{ + FOREACH(Items, true, LAMBDA( + ItemsTime_time[it.m_id] = -1; + )); + ItemsTime_time[Items_MAX] = -1; +} + +STATIC_INIT(ItemsTime_Init) { + Item_ItemsTime_Init(); +} + +int autocvar_hud_panel_itemstime = 2; +float autocvar_hud_panel_itemstime_dynamicsize = 1; +float autocvar_hud_panel_itemstime_ratio = 2; +int autocvar_hud_panel_itemstime_iconalign; +bool autocvar_hud_panel_itemstime_progressbar = 0; +float autocvar_hud_panel_itemstime_progressbar_maxtime = 30; +string autocvar_hud_panel_itemstime_progressbar_name = "progressbar"; +float autocvar_hud_panel_itemstime_progressbar_reduced; +bool autocvar_hud_panel_itemstime_hidespawned = 1; +bool autocvar_hud_panel_itemstime_hidebig = false; +int autocvar_hud_panel_itemstime_text = 1; +#define hud_panel_itemstime_hidebig autocvar_hud_panel_itemstime_hidebig +#else +#define hud_panel_itemstime_hidebig false +#endif + +bool Item_ItemsTime_SpectatorOnly(GameItem it) +{ + return (false + || it == ITEM_ArmorMega || (it == ITEM_ArmorBig && !hud_panel_itemstime_hidebig) + || it == ITEM_HealthMega || (it == ITEM_HealthBig && !hud_panel_itemstime_hidebig) + ); +} + +bool Item_ItemsTime_Allow(GameItem it) +{ + return (false + || it.instanceOfPowerup + || Item_ItemsTime_SpectatorOnly(it) + ); +} + +#ifdef SVQC + +// reserve one more spot for superweapons time +float it_times[Items_MAX + 1]; + +void Item_ItemsTime_Init() +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + it_times[it.m_id] = -1; + )); + it_times[Items_MAX] = -1; +} + +STATIC_INIT(ItemsTime_Init) { + // items time + Item_ItemsTime_Init(); +} + +void Item_ItemsTime_ResetTimes() +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + it_times[it.m_id] = (it_times[it.m_id] == -1) ? -1 : 0; + )); + it_times[Items_MAX] = (it_times[Items_MAX] == -1) ? -1 : 0; +} + +void Item_ItemsTime_ResetTimesForPlayer(entity e) +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + IT_Write(e, it.m_id, (it_times[it.m_id] == -1) ? -1 : 0); + )); + IT_Write(e, Items_MAX, (it_times[Items_MAX] == -1) ? -1 : 0); +} + +void Item_ItemsTime_SetTimesForPlayer(entity e) +{ + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + IT_Write(e, it.m_id, it_times[it.m_id]); + )); + IT_Write(e, Items_MAX, it_times[Items_MAX]); +} + +void Item_ItemsTime_SetTime(entity e, float t) +{ + if (!autocvar_sv_itemstime) + return; + + GameItem item = e.itemdef; + if (item.instanceOfGameItem) + { + if (!item.instanceOfWeaponPickup) + it_times[item.m_id] = t; + else if (e.weapons & WEPSET_SUPERWEAPONS) + it_times[Items_MAX] = t; + } +} + +void Item_ItemsTime_SetTimesForAllPlayers() +{ + FOREACH_CLIENT(IS_REAL_CLIENT(it) && (warmup_stage || !IS_PLAYER(it) || autocvar_sv_itemstime == 2), LAMBDA(Item_ItemsTime_SetTimesForPlayer(it))); +} + +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)))) + continue; + if (it.scheduledrespawntime <= time) + isavailable = true; + else if (t == 0 || it.scheduledrespawntime < t) + t = it.scheduledrespawntime; + }); + if (isavailable) + t = -t; // let know the client there's another available item + return t; +} + +MUTATOR_HOOKFUNCTION(itemstime, reset_map_global) +{ + Item_ItemsTime_ResetTimes(); + // ALL the times need to be reset before .reset()ing each item + // since Item_Reset schedules respawn of superweapons and powerups + IL_EACH(g_items, it.reset, + { + Item_ItemsTime_SetTime(it, 0); + }); + Item_ItemsTime_SetTimesForAllPlayers(); +} + +MUTATOR_HOOKFUNCTION(itemstime, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + Item_ItemsTime_SetTimesForPlayer(player); +} + +MUTATOR_HOOKFUNCTION(itemstime, ClientConnect, CBC_ORDER_LAST) +{ + entity player = M_ARGV(0, entity); + + if(IS_PLAYER(player)) + { + // client became player on connection skipping putObserverInServer step + if (IS_REAL_CLIENT(player)) + if (warmup_stage || autocvar_sv_itemstime == 2) + Item_ItemsTime_SetTimesForPlayer(player); + } +} + +MUTATOR_HOOKFUNCTION(itemstime, PlayerSpawn) +{ + if (warmup_stage || autocvar_sv_itemstime == 2) return; + entity player = M_ARGV(0, entity); + + Item_ItemsTime_ResetTimesForPlayer(player); +} + +#endif + +#ifdef CSQC + +void DrawItemsTimeItem(vector myPos, vector mySize, float ar, string item_icon, float item_time, bool item_available, float item_availableTime) +{ + float t = 0; + vector color = '0 0 0'; + float picalpha; + + if (autocvar_hud_panel_itemstime_hidespawned == 2) + picalpha = 1; + else if (item_available) + { + float BLINK_FACTOR = 0.15; + float BLINK_BASE = 0.85; + float BLINK_FREQ = 5; + picalpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); + } + else + picalpha = 0.5; + t = floor(item_time - time + 0.999); + if (t < 5) + color = '0.7 0 0'; + else if (t < 10) + color = '0.7 0.7 0'; + else + color = '1 1 1'; + + vector picpos, numpos; + if (autocvar_hud_panel_itemstime_iconalign) + { + numpos = myPos; + picpos = myPos + eX * (ar - 1) * mySize_y; + } + else + { + numpos = myPos + eX * mySize_y; + picpos = myPos; + } + + if (t > 0 && autocvar_hud_panel_itemstime_progressbar) + { + vector p_pos, p_size; + if (autocvar_hud_panel_itemstime_progressbar_reduced) + { + p_pos = numpos; + p_size = eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y; + } + else + { + p_pos = myPos; + p_size = mySize; + } + HUD_Panel_DrawProgressBar(p_pos, p_size, autocvar_hud_panel_itemstime_progressbar_name, t/autocvar_hud_panel_itemstime_progressbar_maxtime, 0, autocvar_hud_panel_itemstime_iconalign, color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + if(autocvar_hud_panel_itemstime_text) + { + if(t > 0) + drawstring_aspect(numpos, ftos(t), eX * ((ar - 1)/ar) * mySize_x + eY * mySize_y, color, panel_fg_alpha, DRAWFLAG_NORMAL); + else if(precache_pic("gfx/hud/default/checkmark")) // COMPAT: check if this image exists, as 0.8.1 clients lack it + drawpic_aspect_skin(numpos, "checkmark", eX * (ar - 1) * mySize_y + eY * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); + else // legacy code, if the image is missing just center the icon + picpos.x = myPos.x + mySize.x / 2 - mySize.y / 2; + } + if (item_availableTime) + drawpic_aspect_skin_expanding(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL, item_availableTime); + drawpic_aspect_skin(picpos, item_icon, '1 1 0' * mySize_y, '1 1 1', panel_fg_alpha * picalpha, DRAWFLAG_NORMAL); +} + +void HUD_ItemsTime() +{ + if (!autocvar__hud_configure) + { + if (!( + (autocvar_hud_panel_itemstime == 1 && spectatee_status != 0) + || (autocvar_hud_panel_itemstime == 2 && (spectatee_status != 0 || warmup_stage || STAT(ITEMSTIME) == 2)) + )) { return; } + } + else + { + ItemsTime_time[ITEM_ArmorMega.m_id] = time + 0; + ItemsTime_time[ITEM_HealthMega.m_id] = time + 8; + ItemsTime_time[ITEM_Strength.m_id] = time + 0; + ItemsTime_time[ITEM_Shield.m_id] = time + 4; + } + + int count = 0; + if (autocvar_hud_panel_itemstime_hidespawned == 1) + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] > time || -ItemsTime_time[it.m_id] > time); + )); + count += (ItemsTime_time[Items_MAX] > time || -ItemsTime_time[Items_MAX] > time); + } + else if (autocvar_hud_panel_itemstime_hidespawned == 2) + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] > time); + )); + count += (ItemsTime_time[Items_MAX] > time); + } + else + { + FOREACH(Items, Item_ItemsTime_Allow(it), LAMBDA( + count += (ItemsTime_time[it.m_id] != -1); + )); + count += (ItemsTime_time[Items_MAX] != -1); + } + if (count == 0) + return; + + HUD_Panel_LoadCvars(); + + vector pos, mySize; + pos = panel_pos; + mySize = panel_size; + + if (panel_bg_padding) + { + pos += '1 1 0' * panel_bg_padding; + mySize -= '2 2 0' * panel_bg_padding; + } + + float rows, columns; + float ar = max(2, autocvar_hud_panel_itemstime_ratio) + 1; + rows = HUD_GetRowCount(count, mySize, ar); + columns = ceil(count/rows); + + vector itemstime_size = eX * mySize.x*(1/columns) + eY * mySize.y*(1/rows); + + vector offset = '0 0 0'; + float newSize; + if (autocvar_hud_panel_itemstime_dynamicsize) + { + if (autocvar__hud_configure) + if (hud_configure_menu_open != 2) + HUD_Panel_DrawBg(); // also draw the bg of the entire panel + + // reduce panel to avoid spacing items + if (itemstime_size.x / itemstime_size.y < ar) + { + newSize = rows * itemstime_size.x / ar; + pos.y += (mySize.y - newSize) / 2; + mySize.y = newSize; + itemstime_size.y = mySize.y / rows; + } + else + { + newSize = columns * itemstime_size.y * ar; + pos.x += (mySize.x - newSize) / 2; + mySize.x = newSize; + itemstime_size.x = mySize.x / columns; + } + panel_pos = pos - '1 1 0' * panel_bg_padding; + panel_size = mySize + '2 2 0' * panel_bg_padding; + } + else + { + if (itemstime_size.x/itemstime_size.y > ar) + { + newSize = ar * itemstime_size.y; + offset.x = itemstime_size.x - newSize; + pos.x += offset.x/2; + itemstime_size.x = newSize; + } + else + { + newSize = 1/ar * itemstime_size.x; + offset.y = itemstime_size.y - newSize; + pos.y += offset.y/2; + itemstime_size.y = newSize; + } + } + + HUD_Scale_Enable(); + HUD_Panel_DrawBg(); + + float row = 0, column = 0; + bool item_available; + int id = 0; + string icon = ""; + FOREACH(Items, Item_ItemsTime_Allow(it) && ItemsTime_time[it.m_id] != -1, LAMBDA( + id = it.m_id; + icon = it.m_icon; + +LABEL(iteration) + float item_time = ItemsTime_time[id]; + if (item_time < -1) + { + item_available = true; + item_time = -item_time; + } + else + item_available = (item_time <= time); + + if (ItemsTime_time[id] >= 0) + { + if (time <= ItemsTime_time[id]) + ItemsTime_availableTime[id] = 0; + else if (ItemsTime_availableTime[id] == 0) + ItemsTime_availableTime[id] = time; + } + else if (ItemsTime_availableTime[id] == 0) + ItemsTime_availableTime[id] = time; + + float f = (time - ItemsTime_availableTime[id]) * 2; + f = (f > 1) ? 0 : bound(0, f, 1); + + if (autocvar_hud_panel_itemstime_hidespawned == 1) + if (!(ItemsTime_time[id] > time || -ItemsTime_time[id] > time)) + continue; + + if (autocvar_hud_panel_itemstime_hidespawned == 2) + if (!(ItemsTime_time[id] > time)) + continue; + + DrawItemsTimeItem(pos + eX * column * (itemstime_size.x + offset.x) + eY * row * (itemstime_size.y + offset.y), itemstime_size, ar, icon, item_time, item_available, f); + ++row; + if (row >= rows) + { + row = 0; + column = column + 1; + } + if(id == Items_MAX) // can happen only in the last fake iteration + break; + )); + // add another fake iteration for superweapons time + if(id < Items_MAX && ItemsTime_time[Items_MAX] != -1) + { + id = Items_MAX; + icon = "superweapons"; + goto iteration; + } +} + +#endif diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qh b/qcsrc/common/mutators/mutator/itemstime/itemstime.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/melee_only/_mod.inc b/qcsrc/common/mutators/mutator/melee_only/_mod.inc index db31be3fdd..da02f08086 100644 --- a/qcsrc/common/mutators/mutator/melee_only/_mod.inc +++ b/qcsrc/common/mutators/mutator/melee_only/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/_mod.qh b/qcsrc/common/mutators/mutator/melee_only/_mod.qh index 2228d64f18..297bb2b962 100644 --- a/qcsrc/common/mutators/mutator/melee_only/_mod.qh +++ b/qcsrc/common/mutators/mutator/melee_only/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/melee_only.qc deleted file mode 100644 index ecd5fc7c8e..0000000000 --- a/qcsrc/common/mutators/mutator/melee_only/melee_only.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !g_nexball); - -MUTATOR_HOOKFUNCTION(melee_only, SetStartItems) -{ - start_ammo_shells = warmup_start_ammo_shells = 0; - start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); -} - -MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, FilterItem) -{ - entity item = M_ARGV(0, entity); - - switch (item.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_ArmorSmall.m_itemid: - return false; - } - - return true; -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly"); -} - -MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/module.inc b/qcsrc/common/mutators/mutator/melee_only/module.inc deleted file mode 100644 index c711556ccf..0000000000 --- a/qcsrc/common/mutators/mutator/melee_only/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "melee_only.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc new file mode 100644 index 0000000000..a542921221 --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qc @@ -0,0 +1,44 @@ +#include "sv_melee_only.qh" + +REGISTER_MUTATOR(melee_only, cvar("g_melee_only") && !cvar("g_instagib") && !cvar("g_overkill") && !g_nexball); + +MUTATOR_HOOKFUNCTION(melee_only, SetStartItems, CBC_ORDER_LAST) +{ + start_ammo_shells = warmup_start_ammo_shells = 0; + start_weapons = warmup_start_weapons = WEPSET(SHOTGUN); +} + +MUTATOR_HOOKFUNCTION(melee_only, SetWeaponArena) +{ + // turn weapon arena off + M_ARGV(0, string) = "off"; +} + +MUTATOR_HOOKFUNCTION(melee_only, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, FilterItem) +{ + entity item = M_ARGV(0, entity); + + switch (item.itemdef) + { + case ITEM_HealthSmall: + case ITEM_ArmorSmall: + return false; + } + + return true; +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":MeleeOnly"); +} + +MUTATOR_HOOKFUNCTION(melee_only, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Melee Only Arena"); +} diff --git a/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/melee_only/sv_melee_only.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/midair/_mod.inc b/qcsrc/common/mutators/mutator/midair/_mod.inc index 8fcc96ea21..b144ca4b6a 100644 --- a/qcsrc/common/mutators/mutator/midair/_mod.inc +++ b/qcsrc/common/mutators/mutator/midair/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/midair/_mod.qh b/qcsrc/common/mutators/mutator/midair/_mod.qh index 48272b8709..f96da13867 100644 --- a/qcsrc/common/mutators/mutator/midair/_mod.qh +++ b/qcsrc/common/mutators/mutator/midair/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/midair/midair.qc b/qcsrc/common/mutators/mutator/midair/midair.qc deleted file mode 100644 index cda6b4c828..0000000000 --- a/qcsrc/common/mutators/mutator/midair/midair.qc +++ /dev/null @@ -1,49 +0,0 @@ -#ifdef IMPLEMENTATION - -float autocvar_g_midair_shieldtime; - -REGISTER_MUTATOR(midair, cvar("g_midair")); - -.float midair_shieldtime; - -MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - if(IS_PLAYER(frag_attacker)) - if(IS_PLAYER(frag_target)) - if(time < frag_target.midair_shieldtime) - M_ARGV(4, float) = 0; -} - -MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) -{ - entity player = M_ARGV(0, entity); - - if(time >= game_starttime) - if(IS_ONGROUND(player)) - { - player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); - player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime); - } -} - -MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(IS_BOT_CLIENT(player)) - player.bot_moveskill = 0; // disable bunnyhopping -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair"); -} - -MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/midair/module.inc b/qcsrc/common/mutators/mutator/midair/module.inc deleted file mode 100644 index 10b1789159..0000000000 --- a/qcsrc/common/mutators/mutator/midair/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "midair.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qc b/qcsrc/common/mutators/mutator/midair/sv_midair.qc new file mode 100644 index 0000000000..40747ff056 --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/sv_midair.qc @@ -0,0 +1,48 @@ +#include "sv_midair.qh" + +float autocvar_g_midair_shieldtime; + +REGISTER_MUTATOR(midair, cvar("g_midair")); + +.float midair_shieldtime; + +MUTATOR_HOOKFUNCTION(midair, PlayerDamage_Calculate) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + if(IS_PLAYER(frag_attacker)) + if(IS_PLAYER(frag_target)) + if(time < frag_target.midair_shieldtime) + M_ARGV(4, float) = 0; +} + +MUTATOR_HOOKFUNCTION(midair, PlayerPowerups) +{ + entity player = M_ARGV(0, entity); + + if(time >= game_starttime) + if(IS_ONGROUND(player)) + { + player.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); + player.midair_shieldtime = max(player.midair_shieldtime, time + autocvar_g_midair_shieldtime); + } +} + +MUTATOR_HOOKFUNCTION(midair, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(IS_BOT_CLIENT(player)) + player.bot_moveskill = 0; // disable bunnyhopping +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":midair"); +} + +MUTATOR_HOOKFUNCTION(midair, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Midair"); +} diff --git a/qcsrc/common/mutators/mutator/midair/sv_midair.qh b/qcsrc/common/mutators/mutator/midair/sv_midair.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/midair/sv_midair.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/multijump/module.inc b/qcsrc/common/mutators/mutator/multijump/module.inc deleted file mode 100644 index 3103320990..0000000000 --- a/qcsrc/common/mutators/mutator/multijump/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifndef MENUQC -#include "multijump.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qc b/qcsrc/common/mutators/mutator/multijump/multijump.qc index 780836d498..ecedc47596 100644 --- a/qcsrc/common/mutators/mutator/multijump/multijump.qc +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qc @@ -1,4 +1,7 @@ -#ifdef IMPLEMENTATION +#include "multijump.qh" + +#ifdef GAMEQC + #ifdef SVQC #include #endif @@ -60,7 +63,7 @@ MUTATOR_HOOKFUNCTION(multijump, PlayerJump) int phys_multijump = PHYS_MULTIJUMP(player); - if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) && + if(!M_ARGV(2, bool) && player.multijump_ready && (PHYS_MULTIJUMP_COUNT(player) < phys_multijump || phys_multijump == -1) && player.velocity_z > PHYS_MULTIJUMP_SPEED(player) && (!PHYS_MULTIJUMP_MAXSPEED(player) || vdist(player.velocity, <=, PHYS_MULTIJUMP_MAXSPEED(player)))) { if (PHYS_MULTIJUMP(player)) @@ -126,4 +129,5 @@ MUTATOR_HOOKFUNCTION(multijump, BuildMutatorsPrettyString) } #endif + #endif diff --git a/qcsrc/common/mutators/mutator/multijump/multijump.qh b/qcsrc/common/mutators/mutator/multijump/multijump.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/multijump/multijump.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/nades/module.inc b/qcsrc/common/mutators/mutator/nades/module.inc deleted file mode 100644 index e03900d6a5..0000000000 --- a/qcsrc/common/mutators/mutator/nades/module.inc +++ /dev/null @@ -1,4 +0,0 @@ -#include "nades.qc" -#ifndef MENUQC -#include "net.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.inc b/qcsrc/common/mutators/mutator/nades/nades.inc index 8a7337f610..bcdbe0cd92 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.inc +++ b/qcsrc/common/mutators/mutator/nades/nades.inc @@ -1,4 +1,4 @@ -#ifndef MENUQC +#ifdef GAMEQC #define NADE_PROJECTILE(i, projectile, trail) MACRO_BEGIN { \ this.m_projectile[i] = projectile; \ this.m_trail[i] = trail; \ diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 1d5a8c37b3..c09e221e46 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -1,7 +1,5 @@ #include "nades.qh" -#ifdef IMPLEMENTATION - #ifdef SVQC bool autocvar_g_nades_nade_small; float autocvar_g_nades_spread = 0.04; @@ -9,7 +7,7 @@ float autocvar_g_nades_spread = 0.04; REGISTER_STAT(NADES_SMALL, int, autocvar_g_nades_nade_small) -#ifndef MENUQC +#ifdef GAMEQC entity Nade_TrailEffect(int proj, int nade_team) { switch (proj) @@ -94,7 +92,7 @@ MUTATOR_HOOKFUNCTION(cl_nades, EditProjectile) proj.maxs = '16 16 16'; } proj.colormod = nade_type.m_color; - proj.move_movetype = MOVETYPE_BOUNCE; + set_movetype(proj, MOVETYPE_BOUNCE); settouch(proj, func_null); proj.scale = 1.5; proj.avelocity = randomvec() * 720; @@ -147,23 +145,25 @@ void DrawAmmoNades(vector myPos, vector mySize, bool draw_expanding, float expan #ifdef SVQC -#include -#include +#include +#include #include #include REGISTER_MUTATOR(nades, cvar("g_nades")); .float nade_time_primed; +.float nade_lifetime; .entity nade_spawnloc; + void nade_timer_think(entity this) { - this.skin = 8 - (this.owner.wait - time) / (autocvar_g_nades_nade_lifetime / 10); + this.skin = 8 - (this.owner.wait - time) / (this.owner.nade_lifetime / 10); this.nextthink = time; if(!this.owner || wasfreed(this.owner)) - remove(this); + delete(this); } void nade_burn_spawn(entity _nade) @@ -213,7 +213,7 @@ void napalm_damage(entity this, float dist, float damage, float edgedamage, floa if(d < dist) { e.fireball_impactvec = p; - RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); + RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e)); } } if(RandomSelection_chosen_ent) @@ -232,13 +232,13 @@ void napalm_ball_think(entity this) if(round_handler_IsActive()) if(!round_handler_IsRoundStarted()) { - remove(this); + delete(this); return; } if(time > this.pushltime) { - remove(this); + delete(this); return; } @@ -273,7 +273,7 @@ void nade_napalm_ball(entity this) proj.team = this.owner.team; proj.bot_dodge = true; proj.bot_dodgerating = autocvar_g_nades_napalm_ball_damage; - proj.movetype = MOVETYPE_BOUNCE; + set_movetype(proj, MOVETYPE_BOUNCE); proj.projectiledeathtype = DEATH_NADE_NAPALM.m_id; PROJECTILE_MAKETRIGGER(proj); setmodel(proj, MDL_Null); @@ -294,6 +294,8 @@ void nade_napalm_ball(entity this) proj.angles = vectoangles(proj.velocity); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; //CSQCProjectile(proj, true, PROJECTILE_NAPALM_FIRE, true); @@ -306,13 +308,13 @@ void napalm_fountain_think(entity this) if(round_handler_IsActive()) if(!round_handler_IsRoundStarted()) { - remove(this); + delete(this); return; } if(time >= this.ltime) { - remove(this); + delete(this); return; } @@ -350,13 +352,16 @@ void nade_napalm_boom(entity this) fountain.owner = this.owner; fountain.realowner = this.realowner; fountain.origin = this.origin; + fountain.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, fountain); + IL_PUSH(g_bot_dodge, fountain); setorigin(fountain, fountain.origin); setthink(fountain, napalm_fountain_think); fountain.nextthink = time; fountain.ltime = time + autocvar_g_nades_napalm_fountain_lifetime; fountain.pushltime = fountain.ltime; fountain.team = this.team; - fountain.movetype = MOVETYPE_TOSS; + set_movetype(fountain, MOVETYPE_TOSS); fountain.projectiledeathtype = DEATH_NADE_NAPALM.m_id; fountain.bot_dodge = true; fountain.bot_dodgerating = autocvar_g_nades_napalm_fountain_damage; @@ -379,7 +384,7 @@ void nade_ice_think(entity this) if(round_handler_IsActive()) if(!round_handler_IsRoundStarted()) { - remove(this); + delete(this); return; } @@ -396,7 +401,7 @@ void nade_ice_think(entity this) Damage_DamageInfo(this.origin, autocvar_g_nades_nade_damage, autocvar_g_nades_nade_edgedamage, autocvar_g_nades_nade_radius, '1 1 1' * autocvar_g_nades_nade_force, this.projectiledeathtype, 0, this); } - remove(this); + delete(this); return; } @@ -426,16 +431,13 @@ void nade_ice_think(entity this) float current_freeze_time = this.ltime - time - 0.1; - entity e; - for(e = findradius(this.origin, autocvar_g_nades_nade_radius); e; e = e.chain) - if(e != this) - if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(e, this.realowner) || e == this.realowner)) - if(e.takedamage && !IS_DEAD(e)) - if(e.health > 0) - if(!e.revival_time || ((time - e.revival_time) >= 1.5)) - if(!STAT(FROZEN, e)) - if(current_freeze_time > 0) - nade_ice_freeze(this, e, current_freeze_time); + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_nades_nade_radius, it != this && it.takedamage && !IS_DEAD(it) && it.health > 0 && current_freeze_time > 0, + { + if(!autocvar_g_nades_ice_teamcheck || (DIFF_TEAM(it, this.realowner) || it == this.realowner)) + if(!it.revival_time || ((time - it.revival_time) >= 1.5)) + if(!STAT(FROZEN, it)) + nade_ice_freeze(this, it, current_freeze_time); + }); } void nade_ice_boom(entity this) @@ -451,7 +453,7 @@ void nade_ice_boom(entity this) fountain.ltime = time + autocvar_g_nades_ice_freeze_time; fountain.pushltime = fountain.wait = fountain.ltime; fountain.team = this.team; - fountain.movetype = MOVETYPE_TOSS; + set_movetype(fountain, MOVETYPE_TOSS); fountain.projectiledeathtype = DEATH_NADE_ICE.m_id; fountain.bot_dodge = false; setsize(fountain, '-16 -16 -16', '16 16 16'); @@ -497,7 +499,7 @@ void nade_spawn_boom(entity this) entity spawnloc = spawn(); setorigin(spawnloc, this.origin); setsize(spawnloc, this.realowner.mins, this.realowner.maxs); - spawnloc.movetype = MOVETYPE_NONE; + set_movetype(spawnloc, MOVETYPE_NONE); spawnloc.solid = SOLID_NOT; spawnloc.drawonlytoclient = this.realowner; spawnloc.effects = EF_STARDUST; @@ -505,7 +507,7 @@ void nade_spawn_boom(entity this) if(this.realowner.nade_spawnloc) { - remove(this.realowner.nade_spawnloc); + delete(this.realowner.nade_spawnloc); this.realowner.nade_spawnloc = NULL; } @@ -516,7 +518,7 @@ void nades_orb_think(entity this) { if(time >= this.ltime) { - remove(this); + delete(this); return; } @@ -562,35 +564,33 @@ entity nades_spawn_orb(entity own, entity realown, vector org, float orb_ltime, return orb; } -void nade_entrap_touch(entity this) +void nade_entrap_touch(entity this, entity toucher) { - if(DIFF_TEAM(other, this.realowner)) // TODO: what if realowner changes team or disconnects? + if(DIFF_TEAM(toucher, this.realowner)) // TODO: what if realowner changes team or disconnects? { - if (!isPushable(other)) + if (!isPushable(toucher)) return; - float pushdeltatime = time - other.lastpushtime; + float pushdeltatime = time - toucher.lastpushtime; if (pushdeltatime > 0.15) pushdeltatime = 0; - other.lastpushtime = time; + toucher.lastpushtime = time; if(!pushdeltatime) return; // div0: ticrate independent, 1 = identity (not 20) -#ifdef SVQC - other.velocity = other.velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime); + toucher.velocity = toucher.velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime); - UpdateCSQCProjectile(other); -#elif defined(CSQC) - other.move_velocity = other.move_velocity * pow(autocvar_g_nades_entrap_strength, pushdeltatime); -#endif + #ifdef SVQC + UpdateCSQCProjectile(toucher); + #endif } - if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) || IS_MONSTER(other) ) + if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) || IS_MONSTER(toucher) ) { - entity show_tint = (IS_VEHICLE(other)) ? other.owner : other; + entity show_tint = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; STAT(ENTRAP_ORB, show_tint) = time + 0.1; float tint_alpha = 0.75; - if(SAME_TEAM(other, this.realowner)) + if(SAME_TEAM(toucher, this.realowner)) tint_alpha = 0.45; STAT(ENTRAP_ORB_ALPHA, show_tint) = tint_alpha * (this.ltime - time) / this.orb_lifetime; } @@ -604,43 +604,43 @@ void nade_entrap_boom(entity this) orb.colormod = NADE_TYPE_ENTRAP.m_color; } -void nade_heal_touch(entity this) +void nade_heal_touch(entity this, entity toucher) { float maxhealth; float health_factor; - if(IS_PLAYER(other) || IS_MONSTER(other)) - if(!IS_DEAD(other)) - if(!STAT(FROZEN, other)) + if(IS_PLAYER(toucher) || IS_MONSTER(toucher)) + if(!IS_DEAD(toucher)) + if(!STAT(FROZEN, toucher)) { health_factor = autocvar_g_nades_heal_rate*frametime/2; - if ( other != this.realowner ) + if ( toucher != this.realowner ) { - if ( SAME_TEAM(other,this) ) + if ( SAME_TEAM(toucher,this) ) health_factor *= autocvar_g_nades_heal_friend; else health_factor *= autocvar_g_nades_heal_foe; } if ( health_factor > 0 ) { - maxhealth = (IS_MONSTER(other)) ? other.max_health : g_pickup_healthmega_max; - if ( other.health < maxhealth ) + maxhealth = (IS_MONSTER(toucher)) ? toucher.max_health : g_pickup_healthmega_max; + if ( toucher.health < maxhealth ) { if ( this.nade_show_particles ) - Send_Effect(EFFECT_HEALING, other.origin, '0 0 0', 1); - other.health = min(other.health+health_factor, maxhealth); + Send_Effect(EFFECT_HEALING, toucher.origin, '0 0 0', 1); + toucher.health = min(toucher.health+health_factor, maxhealth); } - other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); } else if ( health_factor < 0 ) { - Damage(other,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,other.origin,'0 0 0'); + Damage(toucher,this,this.realowner,-health_factor,DEATH_NADE_HEAL.m_id,toucher.origin,'0 0 0'); } } - if ( IS_REAL_CLIENT(other) || IS_VEHICLE(other) ) + if ( IS_REAL_CLIENT(toucher) || IS_VEHICLE(toucher) ) { - entity show_red = (IS_VEHICLE(other)) ? other.owner : other; + entity show_red = (IS_VEHICLE(toucher)) ? toucher.owner : toucher; show_red.stat_healing_orb = time+0.1; show_red.stat_healing_orb_alpha = 0.75 * (this.ltime - time) / this.orb_lifetime; } @@ -656,7 +656,7 @@ void nade_heal_boom(entity this) void nade_monster_boom(entity this) { - entity e = spawnmonster(this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1); + entity e = spawnmonster(spawn(), this.pokenade_type, 0, this.realowner, this.realowner, this.origin, false, false, 1); if(autocvar_g_nades_pokenade_monster_lifetime > 0) e.monster_lifetime = time + autocvar_g_nades_pokenade_monster_lifetime; @@ -736,13 +736,12 @@ void nade_boom(entity this) case NADE_TYPE_ENTRAP: nade_entrap_boom(this); break; } - FOREACH_ENTITY_ENT(aiment, this, + IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, { - if(it.classname == "grapplinghook") - RemoveGrapplingHook(it.realowner); + RemoveGrapplingHook(it.realowner); }); - remove(this); + delete(this); } void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, string pntype); @@ -759,24 +758,24 @@ void nade_pickup(entity this, entity thenade) } bool CanThrowNade(entity this); -void nade_touch(entity this) +void nade_touch(entity this, entity toucher) { - if(other) + if(toucher) UpdateCSQCProjectile(this); - if(other == this.realowner) + if(toucher == this.realowner) return; // no this impacts if(autocvar_g_nades_pickup) if(time >= this.spawnshieldtime) - if(!other.nade && this.health == this.max_health) // no boosted shot pickups, thank you very much - if(!other.frozen) - if(CanThrowNade(other)) // prevent some obvious things, like dead players - if(IS_REAL_CLIENT(other)) // above checks for IS_PLAYER, don't need to do it here + if(!toucher.nade && this.health == this.max_health) // no boosted shot pickups, thank you very much + if(!STAT(FROZEN, toucher)) + if(CanThrowNade(toucher)) // prevent some obvious things, like dead players + if(IS_REAL_CLIENT(toucher)) // above checks for IS_PLAYER, don't need to do it here { - nade_pickup(other, this); + nade_pickup(toucher, this); sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - remove(this); + delete(this); return; } /*float is_weapclip = 0; @@ -786,16 +785,15 @@ void nade_touch(entity this) is_weapclip = 1;*/ if(ITEM_TOUCH_NEEDKILL()) // || is_weapclip) { - FOREACH_ENTITY_ENT(aiment, this, + IL_EACH(g_projectiles, it.classname == "grapplinghook" && it.aiment == this, { - if(it.classname == "grapplinghook") - RemoveGrapplingHook(it.realowner); + RemoveGrapplingHook(it.realowner); }); - remove(this); + delete(this); return; } - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); //setsize(this, '-2 -2 -2', '2 2 2'); //UpdateCSQCProjectile(this); @@ -805,7 +803,7 @@ void nade_touch(entity this) return; } - this.enemy = other; + this.enemy = toucher; nade_boom(this); } @@ -866,7 +864,7 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i if(this.health == this.max_health) { sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, 0.5 *(ATTEN_LARGE + ATTEN_MAX)); - this.nextthink = max(time + autocvar_g_nades_nade_lifetime, time); + this.nextthink = max(time + this.nade_lifetime, time); setthink(this, nade_beep); } @@ -889,12 +887,13 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time) entity _nade = e.nade; e.nade = NULL; - remove(e.fake_nade); + delete(e.fake_nade); e.fake_nade = NULL; makevectors(e.v_angle); - W_SetupShot(e, false, false, SND_Null, CH_WEAPON_A, 0); + // NOTE: always throw from first weapon entity? + W_SetupShot(e, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0); Kill_Notification(NOTIF_ONE_ONLY, e, MSG_CENTER, CPID_NADES); @@ -912,9 +911,9 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time) setsize(_nade, '-8 -8 -8', '8 8 8'); else setsize(_nade, '-16 -16 -16', '16 16 16'); - _nade.movetype = MOVETYPE_BOUNCE; + set_movetype(_nade, MOVETYPE_BOUNCE); - tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, false, _nade); + tracebox(_nade.origin, _nade.mins, _nade.maxs, _nade.origin, MOVE_NOMONSTERS, _nade); if (trace_startsolid) setorigin(_nade, e.origin); @@ -944,8 +943,11 @@ void toss_nade(entity e, bool set_owner, vector _velocity, float _time) _nade.gravity = 1; _nade.missile_flags = MIF_SPLASH | MIF_ARC; _nade.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, _nade); _nade.angles = vectoangles(_nade.velocity); _nade.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, _nade); + IL_PUSH(g_bot_dodge, _nade); _nade.projectiledeathtype = DEATH_NADE.m_id; _nade.toss_time = time; _nade.solid = SOLID_CORPSE; //((_nade.nade_type == NADE_TYPE_TRANSLOCATE) ? SOLID_CORPSE : SOLID_BBOX); @@ -1002,10 +1004,10 @@ MUTATOR_HOOKFUNCTION(nades, PutClientInServer) nades_RemoveBonus(player); } -float nade_customize(entity this) +bool nade_customize(entity this, entity client) { - //if(IS_SPEC(other)) { return false; } - if(other == this.exteriormodeltoclient || (IS_SPEC(other) && other.enemy == this.exteriormodeltoclient)) + //if(IS_SPEC(client)) { return false; } + if(client == this.exteriormodeltoclient || (IS_SPEC(client) && client.enemy == this.exteriormodeltoclient)) { // somewhat hide the model, but keep the glow //this.effects = 0; @@ -1048,6 +1050,7 @@ void spawn_held_nade(entity player, entity nowner, float ntime, int ntype, strin setthink(n, nade_beep); n.nextthink = max(n.wait - 3, time); n.projectiledeathtype = DEATH_NADE.m_id; + n.nade_lifetime = ntime; setmodel(fn, MDL_NADE_VIEW); .entity weaponentity = weaponentities[0]; // TODO: unhardcode @@ -1070,10 +1073,10 @@ void nade_prime(entity this) return; // only allow bonus nades if(this.nade) - remove(this.nade); + delete(this.nade); if(this.fake_nade) - remove(this.fake_nade); + delete(this.fake_nade); int ntype; string pntype = this.pokenade_type; @@ -1154,9 +1157,9 @@ void nades_CheckThrow(entity this) void nades_Clear(entity player) { if(player.nade) - remove(player.nade); + delete(player.nade); if(player.fake_nade) - remove(player.fake_nade); + delete(player.fake_nade); player.nade = player.fake_nade = NULL; player.nade_timer = 0; @@ -1174,18 +1177,6 @@ CLASS(NadeOffhand, OffhandWeapon) METHOD(NadeOffhand, offhand_think, void(NadeOffhand this, entity player, bool key_pressed)) { entity held_nade = player.nade; - if (held_nade) - { - player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / autocvar_g_nades_nade_lifetime, 1); - // LOG_TRACEF("%d %d\n", player.nade_timer, time - held_nade.nade_time_primed); - makevectors(player.angles); - held_nade.velocity = player.velocity; - setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); - held_nade.angles_y = player.angles.y; - - if (time + 0.1 >= held_nade.wait) - toss_nade(player, false, '0 0 0', time + 0.05); - } if (!CanThrowNade(player)) return; if (!(time > player.nade_refire)) return; @@ -1227,6 +1218,20 @@ MUTATOR_HOOKFUNCTION(nades, PlayerPreThink) if (player.nade && (player.offhand != OFFHAND_NADE || (player.weapons & WEPSET(HOOK)))) OFFHAND_NADE.offhand_think(OFFHAND_NADE, player, player.nade_altbutton); + entity held_nade = player.nade; + if (held_nade) + { + player.nade_timer = bound(0, (time - held_nade.nade_time_primed) / held_nade.nade_lifetime, 1); + // LOG_TRACEF("%d %d", player.nade_timer, time - held_nade.nade_time_primed); + makevectors(player.angles); + held_nade.velocity = player.velocity; + setorigin(held_nade, player.origin + player.view_ofs + v_forward * 8 + v_right * -8 + v_up * 0); + held_nade.angles_y = player.angles.y; + + if (time + 0.1 >= held_nade.wait) + toss_nade(player, false, '0 0 0', time + 0.05); + } + if(IS_PLAYER(player)) { if ( autocvar_g_nades_bonus && autocvar_g_nades ) @@ -1354,7 +1359,7 @@ MUTATOR_HOOKFUNCTION(nades, PlayerSpawn) if(player.nade_spawnloc.cnt <= 0) { - remove(player.nade_spawnloc); + delete(player.nade_spawnloc); player.nade_spawnloc = NULL; } } @@ -1432,7 +1437,7 @@ MUTATOR_HOOKFUNCTION(nades, MonsterDies) MUTATOR_HOOKFUNCTION(nades, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - + if(frag_target.nade) toss_nade(frag_target, true, '0 0 0', time + 0.05); } @@ -1488,4 +1493,3 @@ MUTATOR_HOOKFUNCTION(nades, BuildGameplayTipsString) } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/nades/nades.qh b/qcsrc/common/mutators/mutator/nades/nades.qh index 2f48d9579c..fd8d26902a 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qh +++ b/qcsrc/common/mutators/mutator/nades/nades.qh @@ -1,5 +1,4 @@ -#ifndef NADES_ALL_H -#define NADES_ALL_H +#pragma once #include @@ -28,12 +27,12 @@ REGISTRY_CHECK(Nades) #define REGISTER_NADE(id) REGISTER(Nades, NADE_TYPE, id, m_id, NEW(Nade)) CLASS(Nade, Object) - ATTRIB(Nade, m_id, int, 0) - ATTRIB(Nade, m_color, vector, '0 0 0') - ATTRIB(Nade, m_name, string, _("Grenade")) - ATTRIB(Nade, m_icon, string, "nade_normal") - ATTRIBARRAY(Nade, m_projectile, int, 2) - ATTRIBARRAY(Nade, m_trail, entity, 2) + ATTRIB(Nade, m_id, int, 0); + ATTRIB(Nade, m_color, vector, '0 0 0'); + ATTRIB(Nade, m_name, string, _("Grenade")); + ATTRIB(Nade, m_icon, string, "nade_normal"); + ATTRIBARRAY(Nade, m_projectile, int, 2); + ATTRIBARRAY(Nade, m_trail, entity, 2); METHOD(Nade, display, void(entity this, void(string name, string icon) returns)) { returns(this.m_name, sprintf("/gfx/hud/%s/%s", cvar_string("menu_skin"), this.m_icon)); } @@ -52,7 +51,7 @@ Nade Nade_FromProjectile(int proj) return NADE_TYPE_Null; } -#ifndef MENUQC +#ifdef GAMEQC #include "effects.inc" #endif @@ -86,7 +85,7 @@ bool orb_send(entity this, entity to, int sf); void nades_Clear(entity player); // Give a bonus grenade to a player -void(entity player, float score) nades_GiveBonus; +void nades_GiveBonus(entity player, float score); /** * called to adjust nade damage and force on hit @@ -102,5 +101,3 @@ void(entity player, float score) nades_GiveBonus; MUTATOR_HOOKABLE(Nade_Damage, EV_Nade_Damage); #endif - -#endif diff --git a/qcsrc/common/mutators/mutator/nades/net.qc b/qcsrc/common/mutators/mutator/nades/net.qc index af320d9c15..498d878d2d 100644 --- a/qcsrc/common/mutators/mutator/nades/net.qc +++ b/qcsrc/common/mutators/mutator/nades/net.qc @@ -1,6 +1,8 @@ -#include "nades.qh" +#include "net.qh" + +#ifdef GAMEQC -#ifdef IMPLEMENTATION +#include "nades.qh" #ifdef CSQC .float ltime; @@ -29,12 +31,13 @@ void orb_setup(entity e) e.orb_radius = e.orb_radius/model_radius*0.6; e.draw = orb_draw; + IL_PUSH(g_drawables, e); e.health = 255; - e.movetype = MOVETYPE_NONE; + set_movetype(e, MOVETYPE_NONE); e.solid = SOLID_NOT; e.drawmask = MASK_NORMAL; e.scale = 0.01; - e.avelocity = e.move_avelocity = '7 0 11'; + e.avelocity = '7 0 11'; e.renderflags |= RF_ADDITIVE; } #endif diff --git a/qcsrc/common/mutators/mutator/nades/net.qh b/qcsrc/common/mutators/mutator/nades/net.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/nades/net.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/new_toys/_mod.inc b/qcsrc/common/mutators/mutator/new_toys/_mod.inc index 90e9811ea6..67ee4f5345 100644 --- a/qcsrc/common/mutators/mutator/new_toys/_mod.inc +++ b/qcsrc/common/mutators/mutator/new_toys/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/_mod.qh b/qcsrc/common/mutators/mutator/new_toys/_mod.qh index ec3b8105fe..97f88a5192 100644 --- a/qcsrc/common/mutators/mutator/new_toys/_mod.qh +++ b/qcsrc/common/mutators/mutator/new_toys/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/module.inc b/qcsrc/common/mutators/mutator/new_toys/module.inc deleted file mode 100644 index 1217177c58..0000000000 --- a/qcsrc/common/mutators/mutator/new_toys/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "new_toys.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/new_toys.qc deleted file mode 100644 index a52fc46704..0000000000 --- a/qcsrc/common/mutators/mutator/new_toys/new_toys.qc +++ /dev/null @@ -1,228 +0,0 @@ -#ifdef IMPLEMENTATION -/* - -CORE laser vortex lg rl cry gl elec hagar fireb hook - vaporizer porto - tuba - -NEW rifle hlac minel seeker -IDEAS OPEN flak OPEN FUN FUN FUN FUN - - - -How this mutator works: - ======================= - -When a gun tries to spawn, this mutator is called. It will provide alternate -weaponreplace lists. - -Entity: - -{ -"classname" "weapon_vortex" -"new_toys" "rifle" -} --> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortext" -"new_toys" "vortex rifle" -} --> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. - -{ -"classname" "weapon_vortex" -"new_toys" "vortex" -} --> This is always a Vortex. - -If the map specifies no "new_toys" argument - -There will be two default replacements selectable: "replace all" and "replace random". -In "replace all" mode, e.g. Vortex will have the default replacement "rifle". -In "replace random" mode, Vortex will have the default replacement "vortex rifle". - -This mutator's replacements run BEFORE regular weaponreplace! - -The New Toys guns do NOT get a spawn function, so they can only ever be spawned -when this mutator is active. - -Likewise, warmup, give all, give ALL and impulse 99 will not give them unless -this mutator is active. - -Outside this mutator, they still can be spawned by: -- setting their start weapon cvar to 1 -- give weaponname -- weaponreplace -- weaponarena (but all and most weapons arena again won't include them) - -This mutator performs the default replacements on the DEFAULTS of the -start weapon selection. - -These weapons appear in the menu's priority list, BUT get a suffix -"(Mutator weapon)". - -Picking up a "new toys" weapon will not play standard weapon pickup sound, but -roflsound "New toys, new toys!" sound. - -*/ - -bool nt_IsNewToy(int w); - -REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - if(time > 1) // game loads at time 1 - error("This cannot be added at runtime\n"); - - // mark the guns as ok to use by e.g. impulse 99 - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(nt_IsNewToy(it.m_id)) - it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - )); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(nt_IsNewToy(it.m_id)) - it.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - )); - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.string new_toys; - -float autocvar_g_new_toys_autoreplace; -bool autocvar_g_new_toys_use_pickupsound = true; -const float NT_AUTOREPLACE_NEVER = 0; -const float NT_AUTOREPLACE_ALWAYS = 1; -const float NT_AUTOREPLACE_RANDOM = 2; - -MUTATOR_HOOKFUNCTION(nt, SetModname) -{ - M_ARGV(0, string) = "NewToys"; -} - -bool nt_IsNewToy(int w) -{ - switch(w) - { - case WEP_SEEKER.m_id: - case WEP_MINE_LAYER.m_id: - case WEP_HLAC.m_id: - case WEP_RIFLE.m_id: - return true; - default: - return false; - } -} - -string nt_GetFullReplacement(string w) -{ - switch(w) - { - case "hagar": return "seeker"; - case "devastator": return "minelayer"; - case "machinegun": return "hlac"; - case "vortex": return "rifle"; - //case "shotgun": return "shockwave"; - default: return string_null; - } -} - -string nt_GetReplacement(string w, float m) -{ - if(m == NT_AUTOREPLACE_NEVER) - return w; - string s = nt_GetFullReplacement(w); - if (!s) - return w; - if(m == NT_AUTOREPLACE_RANDOM) - s = strcat(w, " ", s); - return s; -} - -MUTATOR_HOOKFUNCTION(nt, SetStartItems) -{ - // rearrange start_weapon_default - // apply those bits that are set by start_weapon_defaultmask - // same for warmup - - float j, n; - - WepSet newdefault; - WepSet warmup_newdefault; - - newdefault = '0 0 0'; - warmup_newdefault = '0 0 0'; - - WepSet seti = '0 0 0'; - - FOREACH(Weapons, it != WEP_Null, LAMBDA( - seti = it.m_wepset; - n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace)); - - for(j = 0; j < n; ++j) - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(it.netname == argv(j)) - { - WepSet setk = it.m_wepset; - if(start_weapons & seti) newdefault |= setk; - if(warmup_start_weapons & seti) warmup_newdefault |= setk; - } - )); - )); - - newdefault &= start_weapons_defaultmask; - start_weapons &= ~start_weapons_defaultmask; - start_weapons |= newdefault; - - warmup_newdefault &= warmup_start_weapons_defaultmask; - warmup_start_weapons &= ~warmup_start_weapons_defaultmask; - warmup_start_weapons |= warmup_newdefault; -} - -MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) -{ - entity wep = M_ARGV(0, entity); - entity wepinfo = M_ARGV(1, entity); - string ret_string = M_ARGV(2, string); - - // otherwise, we do replace - if(wep.new_toys) - { - // map defined replacement: - ret_string = wep.new_toys; - } - else - { - // auto replacement: - ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace); - } - - // apply regular weaponreplace - ret_string = W_Apply_Weaponreplace(ret_string); - - M_ARGV(2, string) = ret_string; -} - -MUTATOR_HOOKFUNCTION(nt, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) { - item.item_pickupsound = string_null; - item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; - } -} -#endif diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc new file mode 100644 index 0000000000..6c0647b1e6 --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qc @@ -0,0 +1,229 @@ +#include "sv_new_toys.qh" + +/* + +CORE laser vortex lg rl cry gl elec hagar fireb hook + vaporizer porto + tuba + +NEW rifle hlac minel seeker +IDEAS OPEN flak OPEN FUN FUN FUN FUN + + + +How this mutator works: + ======================= + +When a gun tries to spawn, this mutator is called. It will provide alternate +weaponreplace lists. + +Entity: + +{ +"classname" "weapon_vortex" +"new_toys" "rifle" +} +-> This will spawn as Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortext" +"new_toys" "vortex rifle" +} +-> This will spawn as either Vortex or Rifle in this mutator ONLY, and as Vortex otherwise. + +{ +"classname" "weapon_vortex" +"new_toys" "vortex" +} +-> This is always a Vortex. + +If the map specifies no "new_toys" argument + +There will be two default replacements selectable: "replace all" and "replace random". +In "replace all" mode, e.g. Vortex will have the default replacement "rifle". +In "replace random" mode, Vortex will have the default replacement "vortex rifle". + +This mutator's replacements run BEFORE regular weaponreplace! + +The New Toys guns do NOT get a spawn function, so they can only ever be spawned +when this mutator is active. + +Likewise, warmup, give all, give ALL and impulse 99 will not give them unless +this mutator is active. + +Outside this mutator, they still can be spawned by: +- setting their start weapon cvar to 1 +- give weaponname +- weaponreplace +- weaponarena (but all and most weapons arena again won't include them) + +This mutator performs the default replacements on the DEFAULTS of the +start weapon selection. + +These weapons appear in the menu's priority list, BUT get a suffix +"(Mutator weapon)". + +Picking up a "new toys" weapon will not play standard weapon pickup sound, but +roflsound "New toys, new toys!" sound. + +*/ + +bool nt_IsNewToy(int w); + +REGISTER_MUTATOR(nt, cvar("g_new_toys") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + if(time > 1) // game loads at time 1 + error("This cannot be added at runtime\n"); + + // mark the guns as ok to use by e.g. impulse 99 + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(nt_IsNewToy(it.m_id)) + it.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + )); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(nt_IsNewToy(it.m_id)) + it.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + )); + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.string new_toys; + +float autocvar_g_new_toys_autoreplace; +bool autocvar_g_new_toys_use_pickupsound = true; +const float NT_AUTOREPLACE_NEVER = 0; +const float NT_AUTOREPLACE_ALWAYS = 1; +const float NT_AUTOREPLACE_RANDOM = 2; + +MUTATOR_HOOKFUNCTION(nt, SetModname) +{ + M_ARGV(0, string) = "NewToys"; +} + +bool nt_IsNewToy(int w) +{ + switch(w) + { + case WEP_SEEKER.m_id: + case WEP_MINE_LAYER.m_id: + case WEP_HLAC.m_id: + case WEP_RIFLE.m_id: + case WEP_SHOCKWAVE.m_id: + return true; + default: + return false; + } +} + +string nt_GetFullReplacement(string w) +{ + switch(w) + { + case "hagar": return "seeker"; + case "devastator": return "minelayer"; + case "machinegun": return "hlac"; + case "vortex": return "rifle"; + //case "shotgun": return "shockwave"; + default: return string_null; + } +} + +string nt_GetReplacement(string w, float m) +{ + if(m == NT_AUTOREPLACE_NEVER) + return w; + string s = nt_GetFullReplacement(w); + if (!s) + return w; + if(m == NT_AUTOREPLACE_RANDOM) + s = strcat(w, " ", s); + return s; +} + +MUTATOR_HOOKFUNCTION(nt, SetStartItems) +{ + // rearrange start_weapon_default + // apply those bits that are set by start_weapon_defaultmask + // same for warmup + + float j, n; + + WepSet newdefault; + WepSet warmup_newdefault; + + newdefault = '0 0 0'; + warmup_newdefault = '0 0 0'; + + WepSet seti = '0 0 0'; + + FOREACH(Weapons, it != WEP_Null, LAMBDA( + seti = it.m_wepset; + n = tokenize_console(nt_GetReplacement(it.netname, autocvar_g_new_toys_autoreplace)); + + for(j = 0; j < n; ++j) + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(it.netname == argv(j)) + { + WepSet setk = it.m_wepset; + if(start_weapons & seti) newdefault |= setk; + if(warmup_start_weapons & seti) warmup_newdefault |= setk; + } + )); + )); + + newdefault &= start_weapons_defaultmask; + start_weapons &= ~start_weapons_defaultmask; + start_weapons |= newdefault; + + warmup_newdefault &= warmup_start_weapons_defaultmask; + warmup_start_weapons &= ~warmup_start_weapons_defaultmask; + warmup_start_weapons |= warmup_newdefault; +} + +MUTATOR_HOOKFUNCTION(nt, SetWeaponreplace) +{ + entity wep = M_ARGV(0, entity); + entity wepinfo = M_ARGV(1, entity); + string ret_string = M_ARGV(2, string); + + // otherwise, we do replace + if(wep.new_toys) + { + // map defined replacement: + ret_string = wep.new_toys; + } + else + { + // auto replacement: + ret_string = nt_GetReplacement(wepinfo.netname, autocvar_g_new_toys_autoreplace); + } + + // apply regular weaponreplace + ret_string = W_Apply_Weaponreplace(ret_string); + + M_ARGV(2, string) = ret_string; +} + +MUTATOR_HOOKFUNCTION(nt, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(nt_IsNewToy(item.weapon) && autocvar_g_new_toys_use_pickupsound) { + item.item_pickupsound = string_null; + item.item_pickupsound_ent = SND_WEAPONPICKUP_NEW_TOYS; + } +} diff --git a/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/new_toys/sv_new_toys.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/nix/_mod.inc b/qcsrc/common/mutators/mutator/nix/_mod.inc index a669175da2..3c3141d6fb 100644 --- a/qcsrc/common/mutators/mutator/nix/_mod.inc +++ b/qcsrc/common/mutators/mutator/nix/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/nix/_mod.qh b/qcsrc/common/mutators/mutator/nix/_mod.qh index 6c012fe65c..affbae20a9 100644 --- a/qcsrc/common/mutators/mutator/nix/_mod.qh +++ b/qcsrc/common/mutators/mutator/nix/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/nix/module.inc b/qcsrc/common/mutators/mutator/nix/module.inc deleted file mode 100644 index fb4f9ec2bc..0000000000 --- a/qcsrc/common/mutators/mutator/nix/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "nix.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/nix/nix.qc b/qcsrc/common/mutators/mutator/nix/nix.qc deleted file mode 100644 index f52d75a9e9..0000000000 --- a/qcsrc/common/mutators/mutator/nix/nix.qc +++ /dev/null @@ -1,288 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_balance_nix_ammo_cells; -int autocvar_g_balance_nix_ammo_plasma; -int autocvar_g_balance_nix_ammo_fuel; -int autocvar_g_balance_nix_ammo_nails; -int autocvar_g_balance_nix_ammo_rockets; -int autocvar_g_balance_nix_ammo_shells; -int autocvar_g_balance_nix_ammoincr_cells; -int autocvar_g_balance_nix_ammoincr_plasma; -int autocvar_g_balance_nix_ammoincr_fuel; -int autocvar_g_balance_nix_ammoincr_nails; -int autocvar_g_balance_nix_ammoincr_rockets; -int autocvar_g_balance_nix_ammoincr_shells; -float autocvar_g_balance_nix_incrtime; -float autocvar_g_balance_nix_roundtime; -bool autocvar_g_nix_with_healtharmor; -bool autocvar_g_nix_with_blaster; -bool autocvar_g_nix_with_powerups; -int autocvar_g_pickup_cells_max; -int autocvar_g_pickup_plasma_max; -int autocvar_g_pickup_fuel_max; -int autocvar_g_pickup_nails_max; -int autocvar_g_pickup_rockets_max; -int autocvar_g_pickup_shells_max; - -float g_nix_with_blaster; -// WEAPONTODO -int nix_weapon; -float nix_nextchange; -float nix_nextweapon; -.float nix_lastchange_id; -.float nix_lastinfotime; -.float nix_nextincr; - -bool NIX_CanChooseWeapon(int wpn); - -REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) -{ - MUTATOR_ONADD - { - g_nix_with_blaster = autocvar_g_nix_with_blaster; - - nix_nextchange = 0; - nix_nextweapon = 0; - - FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it))); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - // as the PlayerSpawn hook will no longer run, NIX is turned off by this! - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { - it.ammo_cells = start_ammo_cells; - it.ammo_plasma = start_ammo_plasma; - it.ammo_shells = start_ammo_shells; - it.ammo_nails = start_ammo_nails; - it.ammo_rockets = start_ammo_rockets; - it.ammo_fuel = start_ammo_fuel; - it.weapons = start_weapons; - if(!client_hasweapon(it, PS(it).m_weapon, true, false)) - PS(it).m_switchweapon = w_getbestweapon(it); - }); - } - - return false; -} - -bool NIX_CanChooseWeapon(int wpn) -{ - entity e = Weapons_from(wpn); - if (e == WEP_Null) return false; // skip dummies - if(g_weaponarena) - { - if(!(g_weaponarena_weapons & e.m_wepset)) - return false; - } - else - { - if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) - return false; - if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) - return false; - if (!(e.spawnflags & WEP_FLAG_NORMAL)) - return false; - } - return true; -} -void NIX_ChooseNextWeapon() -{ - RandomSelection_Init(); - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(NIX_CanChooseWeapon(it.m_id)) - RandomSelection_Add(NULL, it.m_id, string_null, 1, (it.m_id != nix_weapon)); - )); - nix_nextweapon = RandomSelection_chosen_float; -} - -void NIX_GiveCurrentWeapon(entity this) -{ - float dt; - - if(!nix_nextweapon) - NIX_ChooseNextWeapon(); - - dt = ceil(nix_nextchange - time); - - if(dt <= 0) - { - nix_weapon = nix_nextweapon; - nix_nextweapon = 0; - if (!nix_nextchange) // no round played yet? - nix_nextchange = time; // start the first round now! - else - nix_nextchange = time + autocvar_g_balance_nix_roundtime; - // Weapon w = Weapons_from(nix_weapon); - // w.wr_init(w); // forget it, too slow - } - - // get weapon info - entity e = Weapons_from(nix_weapon); - - if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! - { - this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0; - - if(this.items & IT_UNLIMITED_WEAPON_AMMO) - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells = autocvar_g_pickup_shells_max; break; - case ammo_nails: this.ammo_nails = autocvar_g_pickup_nails_max; break; - case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break; - case ammo_cells: this.ammo_cells = autocvar_g_pickup_cells_max; break; - case ammo_plasma: this.ammo_plasma = autocvar_g_pickup_plasma_max; break; - case ammo_fuel: this.ammo_fuel = autocvar_g_pickup_fuel_max; break; - } - } - else - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; - case ammo_nails: this.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; - case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; - case ammo_cells: this.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; - case ammo_plasma: this.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; - case ammo_fuel: this.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; - } - } - - this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - if(dt >= 1 && dt <= 5) - this.nix_lastinfotime = -42; - else - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); - - e.wr_resetplayer(e, this); - - // all weapons must be fully loaded when we spawn - if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars - this.(weapon_load[nix_weapon]) = e.reloading_ammo; - - // vortex too - if(WEP_CVAR(vortex, charge)) - { - if(WEP_CVAR_SEC(vortex, chargepool)) - this.vortex_chargepool_ammo = 1; - this.vortex_charge = WEP_CVAR(vortex, charge_start); - } - - // set last change info - this.nix_lastchange_id = nix_nextchange; - } - if(this.nix_lastinfotime != dt) - { - this.nix_lastinfotime = dt; // initial value 0 should count as "not seen" - if(dt >= 1 && dt <= 5) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); - } - - if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) - { - switch(e.ammo_field) - { - case ammo_shells: this.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; - case ammo_nails: this.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; - case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; - case ammo_cells: this.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; - case ammo_plasma: this.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; - case ammo_fuel: this.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; - } - - this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; - } - - this.weapons = '0 0 0'; - if(g_nix_with_blaster) - this.weapons |= WEPSET(BLASTER); - this.weapons |= e.m_wepset; - - Weapon w = Weapons_from(nix_weapon); - if(PS(this).m_switchweapon != w) - if(!client_hasweapon(this, PS(this).m_switchweapon, true, false)) - { - if(client_hasweapon(this, w, true, false)) - W_SwitchWeapon(this, w); - } -} - -MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) -{ - return true; // no throwing in NIX -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX"); -} - -MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX"); -} - -MUTATOR_HOOKFUNCTION(nix, FilterItem) -{ - entity item = M_ARGV(0, entity); - - switch (item.items) - { - case ITEM_HealthSmall.m_itemid: - case ITEM_HealthMedium.m_itemid: - case ITEM_HealthLarge.m_itemid: - case ITEM_HealthMega.m_itemid: - case ITEM_ArmorSmall.m_itemid: - case ITEM_ArmorMedium.m_itemid: - case ITEM_ArmorLarge.m_itemid: - case ITEM_ArmorMega.m_itemid: - if (autocvar_g_nix_with_healtharmor) - return false; - break; - case ITEM_Strength.m_itemid: - case ITEM_Shield.m_itemid: - if (autocvar_g_nix_with_powerups) - return false; - break; - } - - return true; // delete all other items -} - -MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) -{ - entity ent = M_ARGV(0, entity); - - if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) - return true; -} - -MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(!intermission_running) - if(!IS_DEAD(player)) - if(IS_PLAYER(player)) - NIX_GiveCurrentWeapon(player); -} - -MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - player.nix_lastchange_id = -1; - NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning - player.items |= IT_UNLIMITED_SUPERWEAPONS; -} - -MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) -{ - M_ARGV(0, string) = "NIX"; -} -#endif diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qc b/qcsrc/common/mutators/mutator/nix/sv_nix.qc new file mode 100644 index 0000000000..76e735e550 --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.qc @@ -0,0 +1,277 @@ +#include "sv_nix.qh" + +int autocvar_g_balance_nix_ammo_cells; +int autocvar_g_balance_nix_ammo_plasma; +int autocvar_g_balance_nix_ammo_fuel; +int autocvar_g_balance_nix_ammo_nails; +int autocvar_g_balance_nix_ammo_rockets; +int autocvar_g_balance_nix_ammo_shells; +int autocvar_g_balance_nix_ammoincr_cells; +int autocvar_g_balance_nix_ammoincr_plasma; +int autocvar_g_balance_nix_ammoincr_fuel; +int autocvar_g_balance_nix_ammoincr_nails; +int autocvar_g_balance_nix_ammoincr_rockets; +int autocvar_g_balance_nix_ammoincr_shells; +float autocvar_g_balance_nix_incrtime; +float autocvar_g_balance_nix_roundtime; +bool autocvar_g_nix_with_healtharmor; +bool autocvar_g_nix_with_blaster; +bool autocvar_g_nix_with_powerups; +int autocvar_g_pickup_cells_max; +int autocvar_g_pickup_plasma_max; +int autocvar_g_pickup_fuel_max; +int autocvar_g_pickup_nails_max; +int autocvar_g_pickup_rockets_max; +int autocvar_g_pickup_shells_max; + +float g_nix_with_blaster; +// WEAPONTODO +int nix_weapon; +float nix_nextchange; +float nix_nextweapon; +.float nix_lastchange_id; +.float nix_lastinfotime; +.float nix_nextincr; + +bool NIX_CanChooseWeapon(int wpn); + +REGISTER_MUTATOR(nix, cvar("g_nix") && !cvar("g_instagib") && !cvar("g_overkill")) +{ + MUTATOR_ONADD + { + g_nix_with_blaster = autocvar_g_nix_with_blaster; + + nix_nextchange = 0; + nix_nextweapon = 0; + + FOREACH(Weapons, it != WEP_Null && NIX_CanChooseWeapon(it.m_id), LAMBDA(it.wr_init(it))); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + // as the PlayerSpawn hook will no longer run, NIX is turned off by this! + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it), { + it.ammo_cells = start_ammo_cells; + it.ammo_plasma = start_ammo_plasma; + it.ammo_shells = start_ammo_shells; + it.ammo_nails = start_ammo_nails; + it.ammo_rockets = start_ammo_rockets; + it.ammo_fuel = start_ammo_fuel; + it.weapons = start_weapons; + if(!client_hasweapon(it, PS(it).m_weapon, true, false)) + PS(it).m_switchweapon = w_getbestweapon(it); + }); + } + + return false; +} + +bool NIX_CanChooseWeapon(int wpn) +{ + entity e = Weapons_from(wpn); + if (e == WEP_Null) return false; // skip dummies + if(g_weaponarena) + { + if(!(g_weaponarena_weapons & e.m_wepset)) + return false; + } + else + { + if(wpn == WEP_BLASTER.m_id && g_nix_with_blaster) + return false; + if(e.spawnflags & WEP_FLAG_MUTATORBLOCKED) + return false; + if (!(e.spawnflags & WEP_FLAG_NORMAL)) + return false; + } + return true; +} +void NIX_ChooseNextWeapon() +{ + RandomSelection_Init(); + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(NIX_CanChooseWeapon(it.m_id)) + RandomSelection_AddFloat(it.m_id, 1, (it.m_id != nix_weapon)); + )); + nix_nextweapon = RandomSelection_chosen_float; +} + +void NIX_GiveCurrentWeapon(entity this) +{ + float dt; + + if(!nix_nextweapon) + NIX_ChooseNextWeapon(); + + dt = ceil(nix_nextchange - time); + + if(dt <= 0) + { + nix_weapon = nix_nextweapon; + nix_nextweapon = 0; + if (!nix_nextchange) // no round played yet? + nix_nextchange = time; // start the first round now! + else + nix_nextchange = time + autocvar_g_balance_nix_roundtime; + // Weapon w = Weapons_from(nix_weapon); + // w.wr_init(w); // forget it, too slow + } + + // get weapon info + entity e = Weapons_from(nix_weapon); + + if(nix_nextchange != this.nix_lastchange_id) // this shall only be called once per round! + { + this.ammo_shells = this.ammo_nails = this.ammo_rockets = this.ammo_cells = this.ammo_plasma = this.ammo_fuel = 0; + + if(this.items & IT_UNLIMITED_WEAPON_AMMO) + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells = autocvar_g_pickup_shells_max; break; + case ammo_nails: this.ammo_nails = autocvar_g_pickup_nails_max; break; + case ammo_rockets: this.ammo_rockets = autocvar_g_pickup_rockets_max; break; + case ammo_cells: this.ammo_cells = autocvar_g_pickup_cells_max; break; + case ammo_plasma: this.ammo_plasma = autocvar_g_pickup_plasma_max; break; + case ammo_fuel: this.ammo_fuel = autocvar_g_pickup_fuel_max; break; + } + } + else + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells = autocvar_g_balance_nix_ammo_shells; break; + case ammo_nails: this.ammo_nails = autocvar_g_balance_nix_ammo_nails; break; + case ammo_rockets: this.ammo_rockets = autocvar_g_balance_nix_ammo_rockets; break; + case ammo_cells: this.ammo_cells = autocvar_g_balance_nix_ammo_cells; break; + case ammo_plasma: this.ammo_plasma = autocvar_g_balance_nix_ammo_plasma; break; + case ammo_fuel: this.ammo_fuel = autocvar_g_balance_nix_ammo_fuel; break; + } + } + + this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + if(dt >= 1 && dt <= 5) + this.nix_lastinfotime = -42; + else + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_NEWWEAPON, nix_weapon); + + e.wr_resetplayer(e, this); + + // all weapons must be fully loaded when we spawn + if(e.spawnflags & WEP_FLAG_RELOADABLE) // prevent accessing undefined cvars + this.(weapon_load[nix_weapon]) = e.reloading_ammo; + + // vortex too + if(WEP_CVAR(vortex, charge)) + { + if(WEP_CVAR_SEC(vortex, chargepool)) + this.vortex_chargepool_ammo = 1; + this.vortex_charge = WEP_CVAR(vortex, charge_start); + } + + // set last change info + this.nix_lastchange_id = nix_nextchange; + } + if(this.nix_lastinfotime != dt) + { + this.nix_lastinfotime = dt; // initial value 0 should count as "not seen" + if(dt >= 1 && dt <= 5) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_NIX_COUNTDOWN, nix_nextweapon, dt); + } + + if(!(this.items & IT_UNLIMITED_WEAPON_AMMO) && time > this.nix_nextincr) + { + switch(e.ammo_field) + { + case ammo_shells: this.ammo_shells += autocvar_g_balance_nix_ammoincr_shells; break; + case ammo_nails: this.ammo_nails += autocvar_g_balance_nix_ammoincr_nails; break; + case ammo_rockets: this.ammo_rockets += autocvar_g_balance_nix_ammoincr_rockets; break; + case ammo_cells: this.ammo_cells += autocvar_g_balance_nix_ammoincr_cells; break; + case ammo_plasma: this.ammo_plasma += autocvar_g_balance_nix_ammoincr_plasma; break; + case ammo_fuel: this.ammo_fuel += autocvar_g_balance_nix_ammoincr_fuel; break; + } + + this.nix_nextincr = time + autocvar_g_balance_nix_incrtime; + } + + this.weapons = '0 0 0'; + if(g_nix_with_blaster) + this.weapons |= WEPSET(BLASTER); + this.weapons |= e.m_wepset; + + Weapon w = Weapons_from(nix_weapon); + if(PS(this).m_switchweapon != w) + if(!client_hasweapon(this, PS(this).m_switchweapon, true, false)) + { + if(client_hasweapon(this, w, true, false)) + W_SwitchWeapon(this, w); + } +} + +MUTATOR_HOOKFUNCTION(nix, ForbidThrowCurrentWeapon) +{ + return true; // no throwing in NIX +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":NIX"); +} + +MUTATOR_HOOKFUNCTION(nix, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", NIX"); +} + +MUTATOR_HOOKFUNCTION(nix, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(item.itemdef.instanceOfHealth || item.itemdef.instanceOfArmor) + { + return !autocvar_g_nix_with_healtharmor; + } + else if(item.itemdef.instanceOfPowerup) + { + return !autocvar_g_nix_with_powerups; + } + + return true; // delete all other items +} + +MUTATOR_HOOKFUNCTION(nix, OnEntityPreSpawn) +{ + entity ent = M_ARGV(0, entity); + + if(ent.classname == "target_items") // items triggers cannot work in nix (as they change weapons/ammo) + return true; +} + +MUTATOR_HOOKFUNCTION(nix, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(!gameover) + if(!IS_DEAD(player)) + if(IS_PLAYER(player)) + NIX_GiveCurrentWeapon(player); +} + +MUTATOR_HOOKFUNCTION(nix, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + player.nix_lastchange_id = -1; + NIX_GiveCurrentWeapon(player); // overrides the weapons you got when spawning + player.items |= IT_UNLIMITED_SUPERWEAPONS; +} + +MUTATOR_HOOKFUNCTION(nix, SetModname, CBC_ORDER_LAST) +{ + M_ARGV(0, string) = "NIX"; +} diff --git a/qcsrc/common/mutators/mutator/nix/sv_nix.qh b/qcsrc/common/mutators/mutator/nix/sv_nix.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/nix/sv_nix.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 5b42a4dd11..0552173c1a 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.inc +++ b/qcsrc/common/mutators/mutator/overkill/_mod.inc @@ -1,4 +1,10 @@ // generated file; do not modify #include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/mutators/mutator/overkill/_mod.qh b/qcsrc/common/mutators/mutator/overkill/_mod.qh index 7a46694446..13e42431b3 100644 --- a/qcsrc/common/mutators/mutator/overkill/_mod.qh +++ b/qcsrc/common/mutators/mutator/overkill/_mod.qh @@ -1,4 +1,10 @@ // generated file; do not modify #include #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc new file mode 100644 index 0000000000..eb21953955 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qc @@ -0,0 +1,11 @@ +#include "cl_overkill.qh" + +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/cl_overkill.qh b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/cl_overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qc b/qcsrc/common/mutators/mutator/overkill/hmg.qc index e0ae539318..024fdb1cc7 100644 --- a/qcsrc/common/mutators/mutator/overkill/hmg.qc +++ b/qcsrc/common/mutators/mutator/overkill/hmg.qc @@ -1,47 +1,5 @@ -#ifndef IMPLEMENTATION -CLASS(HeavyMachineGun, Weapon) -/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails) -/* 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, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(HeavyMachineGun, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(HeavyMachineGun, mdl, string, "ok_hmg"); -#ifndef MENUQC -/* 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)); +#include "hmg.qh" -#endif -#ifdef IMPLEMENTATION #ifdef SVQC REGISTER_MUTATOR(hmg_nadesupport, true); @@ -62,8 +20,7 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone return; } - if(!thiswep.wr_checkammo1(thiswep, actor)) - if(!(actor.items & IT_UNLIMITED_WEAPON_AMMO)) + if((!thiswep.wr_checkammo1(thiswep, actor) && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (!(actor.items & IT_SUPERWEAPON) && !(actor.items & IT_UNLIMITED_SUPERWEAPONS))) { W_SwitchWeapon_Force(actor, w_getbestweapon(actor)); w_ready(thiswep, actor, weaponentity, fire); @@ -72,7 +29,7 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone W_DecreaseAmmo(WEP_HMG, actor, WEP_CVAR(hmg, ammo)); - W_SetupShot (actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage)); + W_SetupShot (actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(hmg, damage)); if(!autocvar_g_norecoil) { @@ -87,11 +44,14 @@ void W_HeavyMachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weapone Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - W_MachineGun_MuzzleFlash(actor); - W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); if (autocvar_g_casings >= 2) // casing code - 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); + { + 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); @@ -143,7 +103,7 @@ METHOD(HeavyMachineGun, wr_checkammo2, bool(entity thiswep, entity actor)) METHOD(HeavyMachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, WEP_CVAR(hmg, ammo), SND_RELOAD); + W_Reload(actor, weaponentity, WEP_CVAR(hmg, ammo), SND_RELOAD); } METHOD(HeavyMachineGun, wr_suicidemessage, Notification(entity thiswep)) @@ -172,4 +132,3 @@ METHOD(HeavyMachineGun, wr_impacteffect, void(entity thiswep, entity actor)) } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/hmg.qh b/qcsrc/common/mutators/mutator/overkill/hmg.qh new file mode 100644 index 0000000000..7219fd1f80 --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/hmg.qh @@ -0,0 +1,42 @@ +#pragma once + +CLASS(HeavyMachineGun, Weapon) +/* ammotype */ ATTRIB(HeavyMachineGun, ammo_field, .int, ammo_nails); +/* 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, BOT_PICKUP_RATING_HIGH); +/* 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)); diff --git a/qcsrc/common/mutators/mutator/overkill/module.inc b/qcsrc/common/mutators/mutator/overkill/module.inc deleted file mode 100644 index a7acff5a09..0000000000 --- a/qcsrc/common/mutators/mutator/overkill/module.inc +++ /dev/null @@ -1,19 +0,0 @@ -#include "hmg.qc" -#include "rpc.qc" - -#ifdef SVQC - #include "overkill.qc" -#endif -#ifdef CSQC - #ifdef IMPLEMENTATION - 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"; - } - } - #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qc b/qcsrc/common/mutators/mutator/overkill/overkill.qc index 2fb75b3f8d..3cb64ce923 100644 --- a/qcsrc/common/mutators/mutator/overkill/overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/overkill.qc @@ -1,409 +1 @@ -#ifdef IMPLEMENTATION -bool autocvar_g_overkill_powerups_replace; -float autocvar_g_overkill_superguns_respawn_time; -bool autocvar_g_overkill_100h_anyway; -bool autocvar_g_overkill_100a_anyway; -bool autocvar_g_overkill_ammo_charge; -float autocvar_g_overkill_ammo_charge_notice; -float autocvar_g_overkill_ammo_charge_limit; - -.vector ok_deathloc; -.float ok_spawnsys_timer; -.float ok_lastwep; -.float ok_item; - -.float ok_notice_time; -.float ammo_charge[Weapons_MAX]; -.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE); -.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL); - -.float ok_pauseregen_finished; - -void(entity ent, float wep) ok_DecreaseCharge; - -void ok_Initialize(); - -REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") -{ - MUTATOR_ONADD - { - ok_Initialize(); - } - - MUTATOR_ONREMOVE - { - WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } -} - -MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo) -{ - entity actor = M_ARGV(0, entity); - if (actor.ok_use_ammocharge) - { - ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id); - return true; - } -} - -MUTATOR_HOOKFUNCTION(ok, W_Reload) -{ - entity actor = M_ARGV(0, entity); - return actor.ok_use_ammocharge; -} - -void W_Blaster_Attack(entity, float, float, float, float, float, float, float, float, float, float); -spawnfunc(weapon_hmg); -spawnfunc(weapon_rpc); - -void ok_DecreaseCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return; - - entity wepent = Weapons_from(wep); - - if (wepent == WEP_Null) return; // dummy - - ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -void ok_IncreaseCharge(entity ent, int wep) -{ - entity wepent = Weapons_from(wep); - - if (wepent == WEP_Null) return; // dummy - - if(ent.ok_use_ammocharge) - if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking? - ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); -} - -float ok_CheckWeaponCharge(entity ent, int wep) -{ - if(!ent.ok_use_ammocharge) return true; - - entity wepent = Weapons_from(wep); - - if(wepent == WEP_Null) return false; // dummy - - return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(3, float); - - if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) - if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) - { - if(frag_attacker != frag_target) - if(frag_target.health > 0) - if(STAT(FROZEN, frag_target) == 0) - if(!IS_DEAD(frag_target)) - { - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); - M_ARGV(6, vector) = '0 0 0'; - } - - M_ARGV(4, float) = 0; - } -} - -MUTATOR_HOOKFUNCTION(ok, PlayerDamage_SplitHealthArmor) -{ - entity frag_target = M_ARGV(2, entity); - float damage_take = M_ARGV(4, float); - - if(damage_take) - frag_target.ok_pauseregen_finished = max(frag_target.ok_pauseregen_finished, time + 2); -} - -void ok_DropItem(entity this, entity targ) -{ - entity e = new(droppedweapon); // hax - e.ok_item = true; - e.noalign = true; - e.pickup_anyway = true; - e.spawnfunc_checked = true; - spawnfunc_item_armor_small(e); - if (!wasfreed(e)) { // might have been blocked by a mutator - e.movetype = MOVETYPE_TOSS; - e.gravity = 1; - e.reset = SUB_Remove; - setorigin(e, this.origin + '0 0 32'); - e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500; - SUB_SetFade(e, time + 5, 1); - } -} - -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); - - ok_DropItem(frag_target, targ); - - frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id; -} - -MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) -{ - entity mon = M_ARGV(0, entity); - entity olditem = M_ARGV(1, entity); - entity frag_attacker = M_ARGV(2, entity); - - remove(olditem); - - M_ARGV(1, entity) = NULL; - - ok_DropItem(mon, frag_attacker); -} - -MUTATOR_HOOKFUNCTION(ok, PlayerRegen) -{ - entity player = M_ARGV(0, entity); - - // overkill's values are different, so use custom regen - if(!STAT(FROZEN, player)) - { - player.armorvalue = CalcRotRegen(player.armorvalue, autocvar_g_balance_armor_regenstable, autocvar_g_balance_armor_regen, autocvar_g_balance_armor_regenlinear, - 1 * frametime * (time > player.ok_pauseregen_finished), 0, 0, 1, 1 * frametime * (time > player.pauserotarmor_finished), autocvar_g_balance_armor_limit); - player.health = CalcRotRegen(player.health, autocvar_g_balance_health_regenstable, 0, 100, 1 * frametime * (time > player.ok_pauseregen_finished), 200, 0, - autocvar_g_balance_health_rotlinear, 1 * frametime * (time > player.pauserothealth_finished), autocvar_g_balance_health_limit); - - float minf, maxf, limitf; - - maxf = autocvar_g_balance_fuel_rotstable; - minf = autocvar_g_balance_fuel_regenstable; - limitf = autocvar_g_balance_fuel_limit; - - player.ammo_fuel = CalcRotRegen(player.ammo_fuel, minf, autocvar_g_balance_fuel_regen, autocvar_g_balance_fuel_regenlinear, - frametime * (time > player.pauseregen_finished) * ((player.items & ITEM_JetpackRegen.m_itemid) != 0), maxf, autocvar_g_balance_fuel_rot, autocvar_g_balance_fuel_rotlinear, frametime * (time > player.pauserotfuel_finished), limitf); - } - return true; // return true anyway, as frozen uses no regen -} - -MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) -{ - return true; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) -{ - if(intermission_running || gameover) - return; - - entity player = M_ARGV(0, entity); - - if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player)) - return; - - if(player.ok_lastwep) - { - Weapon newwep = Weapons_from(player.ok_lastwep); - if(player.ok_lastwep == WEP_HMG.m_id) - newwep = WEP_MACHINEGUN; - if(player.ok_lastwep == WEP_RPC.m_id) - newwep = WEP_VORTEX; - PS(player).m_switchweapon = newwep; - player.ok_lastwep = 0; - } - - ok_IncreaseCharge(player, PS(player).m_weapon.m_id); - - if(PHYS_INPUT_BUTTON_ATCK2(player)) - if(!forbidWeaponUse(player) || player.weapon_blocked) // allow if weapon is blocked - if(time >= player.jump_interval) - { - player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player); - makevectors(player.v_angle); - - Weapon oldwep = PS(player).m_weapon; - PS(player).m_weapon = WEP_BLASTER; - W_Blaster_Attack( - player, - WEP_BLASTER.m_id | HITTYPE_SECONDARY, - WEP_CVAR_SEC(vaporizer, shotangle), - WEP_CVAR_SEC(vaporizer, damage), - WEP_CVAR_SEC(vaporizer, edgedamage), - WEP_CVAR_SEC(vaporizer, radius), - WEP_CVAR_SEC(vaporizer, force), - WEP_CVAR_SEC(vaporizer, speed), - WEP_CVAR_SEC(vaporizer, spread), - WEP_CVAR_SEC(vaporizer, delay), - WEP_CVAR_SEC(vaporizer, lifetime) - ); - PS(player).m_weapon = oldwep; - } - - player.weapon_blocked = false; - - player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id]; - - if(player.ok_use_ammocharge) - if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id)) - { - if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon) - { - //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE); - player.ok_notice_time = time + 2; - play2(player, SND(DRYFIRE)); - } - Weapon wpn = PS(player).m_weapon; - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if(player.(weaponentity).state != WS_CLEAR) - w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1)); - - player.weapon_blocked = true; - } - - PHYS_INPUT_BUTTON_ATCK2(player) = false; -} - -MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - if(autocvar_g_overkill_ammo_charge) - { - FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit)); - - player.ok_use_ammocharge = 1; - player.ok_notice_time = time; - } - else - player.ok_use_ammocharge = 0; - - // if player changed their weapon while dead, don't switch to their death weapon - if(player.impulse) - player.ok_lastwep = 0; - - player.ok_pauseregen_finished = time + 2; -} - -void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); } -void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); } - -MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) -{ - entity ent = M_ARGV(0, entity); - - if(autocvar_g_powerups) - if(autocvar_g_overkill_powerups_replace) - { - if(ent.classname == "item_strength") - { - entity wep = new(weapon_hmg); - setorigin(wep, ent.origin); - setmodel(wep, MDL_OK_HMG); - wep.ok_item = true; - wep.noalign = ent.noalign; - wep.cnt = ent.cnt; - wep.team = ent.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.spawnfunc_checked = true; - setthink(wep, self_spawnfunc_weapon_hmg); - wep.nextthink = time + 0.1; - return true; - } - - if(ent.classname == "item_invincible") - { - entity wep = new(weapon_rpc); - setorigin(wep, ent.origin); - setmodel(wep, MDL_OK_RPC); - wep.ok_item = true; - wep.noalign = ent.noalign; - wep.cnt = ent.cnt; - wep.team = ent.team; - wep.respawntime = autocvar_g_overkill_superguns_respawn_time; - wep.pickup_anyway = true; - wep.spawnfunc_checked = true; - setthink(wep, self_spawnfunc_weapon_rpc); - wep.nextthink = time + 0.1; - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(ok, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(item.ok_item) - return; - - switch(item.items) - { - case ITEM_HealthMega.m_itemid: return !(autocvar_g_overkill_100h_anyway); - case ITEM_ArmorMega.m_itemid: return !(autocvar_g_overkill_100a_anyway); - } - - return true; -} - -MUTATOR_HOOKFUNCTION(ok, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id]; - client.ok_use_ammocharge = spectatee.ok_use_ammocharge; -} - -MUTATOR_HOOKFUNCTION(ok, SetStartItems) -{ - WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); - - if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } - if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } - - start_items |= IT_UNLIMITED_WEAPON_AMMO; - start_weapons = warmup_start_weapons = ok_start_items; -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK"); -} - -MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill"); -} - -MUTATOR_HOOKFUNCTION(ok, SetModname) -{ - M_ARGV(0, string) = "Overkill"; - return true; -} - -void ok_SetCvars() -{ - // hack to force overkill playermodels - cvar_settemp("sv_defaultcharacter", "1"); - cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); - cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); - cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); -} - -void ok_Initialize() -{ - ok_SetCvars(); - - precache_all_playermodels("models/ok_player/*.dpm"); - - WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - - WEP_SHOTGUN.mdl = "ok_shotgun"; - WEP_MACHINEGUN.mdl = "ok_mg"; - WEP_VORTEX.mdl = "ok_sniper"; -} -#endif +#include "overkill.qh" diff --git a/qcsrc/common/mutators/mutator/overkill/overkill.qh b/qcsrc/common/mutators/mutator/overkill/overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qc b/qcsrc/common/mutators/mutator/overkill/rpc.qc index 90c671a609..ad2ee3f029 100644 --- a/qcsrc/common/mutators/mutator/overkill/rpc.qc +++ b/qcsrc/common/mutators/mutator/overkill/rpc.qc @@ -1,72 +1,30 @@ -#ifndef IMPLEMENTATION -CLASS(RocketPropelledChainsaw, Weapon) -/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7) -/* 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, BOT_PICKUP_RATING_HIGH); -/* color */ ATTRIB(RocketPropelledChainsaw, wpcolor, vector, '0.5 0.5 0'); -/* modelname */ ATTRIB(RocketPropelledChainsaw, mdl, string, "ok_rl"); -#ifndef MENUQC -/* 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)); +#include "rpc.qh" -#endif -#ifdef IMPLEMENTATION #ifdef SVQC spawnfunc(weapon_rpc) { weapon_defaultspawnfunc(this, WEP_RPC); } -void W_RocketPropelledChainsaw_Explode(entity this) +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, other); + RadiusDamage (this, this.realowner, WEP_CVAR(rpc, damage), WEP_CVAR(rpc, edgedamage), WEP_CVAR(rpc, radius), NULL, NULL, WEP_CVAR(rpc, force), this.projectiledeathtype, directhitentity); + + delete (this); +} - remove (this); +void W_RocketPropelledChainsaw_Explode_think(entity this) +{ + W_RocketPropelledChainsaw_Explode(this, NULL); } -void W_RocketPropelledChainsaw_Touch (entity this) +void W_RocketPropelledChainsaw_Touch (entity this, entity toucher) { - if(WarpZone_Projectile_Touch(this)) + if(WarpZone_Projectile_Touch(this, toucher)) if(wasfreed(this)) return; - W_RocketPropelledChainsaw_Explode(this); + W_RocketPropelledChainsaw_Explode(this, toucher); } void W_RocketPropelledChainsaw_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -80,14 +38,14 @@ void W_RocketPropelledChainsaw_Damage(entity this, entity inflictor, entity atta this.health = this.health - damage; if (this.health <= 0) - W_PrepareExplosionByDamage(this, attacker, W_RocketPropelledChainsaw_Explode); + W_PrepareExplosionByDamage(this, attacker, W_RocketPropelledChainsaw_Explode_think); } void W_RocketPropelledChainsaw_Think(entity this) { if(this.cnt <= time) { - remove(this); + delete(this); return; } @@ -105,13 +63,13 @@ void W_RocketPropelledChainsaw_Think(entity this) this.nextthink = time; } -void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor) +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)); - W_SetupShot_ProjectileSize (actor, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage)); + W_SetupShot_ProjectileSize (actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(rpc, damage)); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); PROJECTILE_MAKETRIGGER(missile); @@ -124,7 +82,8 @@ void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor) missile.health = WEP_CVAR(rpc, health); missile.event_damage = W_RocketPropelledChainsaw_Damage; missile.damagedbycontents = true; - missile.movetype = MOVETYPE_FLY; + IL_PUSH(g_damagedbycontents, missile); + set_movetype(missile, MOVETYPE_FLY); missile.projectiledeathtype = WEP_RPC.m_id; setsize (missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot @@ -138,13 +97,15 @@ void W_RocketPropelledChainsaw_Attack (Weapon thiswep, entity actor) 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, flash, '5 0 0'); + W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); missile.pos1 = missile.velocity; MUTATOR_CALLHOOK(EditProjectile, actor, missile); @@ -165,7 +126,7 @@ METHOD(RocketPropelledChainsaw, wr_think, void(entity thiswep, entity actor, .en { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(rpc, refire))) { - W_RocketPropelledChainsaw_Attack(thiswep, actor); + W_RocketPropelledChainsaw_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(rpc, animtime), w_ready); } } @@ -191,7 +152,7 @@ METHOD(RocketPropelledChainsaw, wr_checkammo2, bool(entity thiswep, entity actor METHOD(RocketPropelledChainsaw, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, WEP_CVAR(rpc, ammo), SND_RELOAD); + W_Reload(actor, weaponentity, WEP_CVAR(rpc, ammo), SND_RELOAD); } METHOD(RocketPropelledChainsaw, wr_suicidemessage, Notification(entity thiswep)) @@ -226,4 +187,3 @@ METHOD(RocketPropelledChainsaw, wr_impacteffect, void(entity thiswep, entity act } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/overkill/rpc.qh b/qcsrc/common/mutators/mutator/overkill/rpc.qh new file mode 100644 index 0000000000..535fa5533a --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/rpc.qh @@ -0,0 +1,47 @@ +#pragma once + +CLASS(RocketPropelledChainsaw, Weapon) +/* ammotype */ ATTRIB(RocketPropelledChainsaw, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(RocketPropelledChainsaw, impulse, int, 7); +/* 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, BOT_PICKUP_RATING_HIGH); +/* 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)); diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc new file mode 100644 index 0000000000..56bc8d14cc --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -0,0 +1,385 @@ +#include "sv_overkill.qh" + +#include "hmg.qh" +#include "rpc.qh" + +bool autocvar_g_overkill_powerups_replace; +bool autocvar_g_overkill_ammo_charge; +float autocvar_g_overkill_ammo_charge_notice; +float autocvar_g_overkill_ammo_charge_limit; + +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_lastwep; +.float ok_item; + +.float ok_notice_time; +.float ammo_charge[Weapons_MAX]; +.float ok_use_ammocharge = _STAT(OK_AMMO_CHARGE); +.float ok_ammo_charge = _STAT(OK_AMMO_CHARGEPOOL); + +void(entity ent, float wep) ok_DecreaseCharge; + +void ok_Initialize(); + +REGISTER_MUTATOR(ok, cvar("g_overkill") && !cvar("g_instagib") && !g_nexball && cvar_string("g_mod_balance") == "Overkill") +{ + MUTATOR_ONADD + { + ok_Initialize(); + } + + MUTATOR_ONREMOVE + { + WEP_RPC.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } +} + +MUTATOR_HOOKFUNCTION(ok, W_DecreaseAmmo) +{ + entity actor = M_ARGV(0, entity); + if (actor.ok_use_ammocharge) + { + ok_DecreaseCharge(actor, PS(actor).m_weapon.m_id); + return true; + } +} + +MUTATOR_HOOKFUNCTION(ok, W_Reload) +{ + entity actor = M_ARGV(0, entity); + return actor.ok_use_ammocharge; +} + +void W_Blaster_Attack(entity, .entity, float, float, float, float, float, float, float, float, float, float); +spawnfunc(weapon_hmg); +spawnfunc(weapon_rpc); + +void ok_DecreaseCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return; + + entity wepent = Weapons_from(wep); + + if (wepent == WEP_Null) return; // dummy + + ent.ammo_charge[wep] -= max(0, cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +void ok_IncreaseCharge(entity ent, int wep) +{ + entity wepent = Weapons_from(wep); + + if (wepent == WEP_Null) return; // dummy + + if(ent.ok_use_ammocharge) + if(!PHYS_INPUT_BUTTON_ATCK(ent)) // not while attacking? + ent.ammo_charge[wep] = min(autocvar_g_overkill_ammo_charge_limit, ent.ammo_charge[wep] + cvar(sprintf("g_overkill_ammo_charge_rate_%s", wepent.netname)) * frametime / W_TICSPERFRAME); +} + +float ok_CheckWeaponCharge(entity ent, int wep) +{ + if(!ent.ok_use_ammocharge) return true; + + entity wepent = Weapons_from(wep); + + if(wepent == WEP_Null) return false; // dummy + + return (ent.ammo_charge[wep] >= cvar(sprintf("g_overkill_ammo_decharge_%s", wepent.netname))); +} + +MUTATOR_HOOKFUNCTION(ok, PlayerDamage_Calculate, CBC_ORDER_LAST) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float frag_deathtype = M_ARGV(3, float); + + if(IS_PLAYER(frag_attacker) && IS_PLAYER(frag_target)) + if(DEATH_ISWEAPON(frag_deathtype, WEP_BLASTER)) + { + if(frag_attacker != frag_target) + if(frag_target.health > 0) + if(STAT(FROZEN, frag_target) == 0) + if(!IS_DEAD(frag_target)) + { + Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_SECONDARY_NODAMAGE); + M_ARGV(6, vector) = '0 0 0'; // force + } + + M_ARGV(4, float) = 0; // damage + } +} + +void ok_DropItem(entity this, entity targ) +{ + entity e = new(droppedweapon); // hax + e.ok_item = true; + e.noalign = true; + e.pickup_anyway = true; + e.spawnfunc_checked = true; + spawnfunc_item_armor_small(e); + if (!wasfreed(e)) { // might have been blocked by a mutator + set_movetype(e, MOVETYPE_TOSS); + e.gravity = 1; + e.reset = SUB_Remove; + setorigin(e, this.origin + '0 0 32'); + e.velocity = '0 0 200' + normalize(targ.origin - this.origin) * 500; + SUB_SetFade(e, time + 5, 1); + } +} + +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); + + ok_DropItem(frag_target, targ); + + frag_target.ok_lastwep = PS(frag_target).m_switchweapon.m_id; +} + +MUTATOR_HOOKFUNCTION(ok, MonsterDropItem) +{ + entity mon = M_ARGV(0, entity); + entity olditem = M_ARGV(1, entity); + entity frag_attacker = M_ARGV(2, entity); + + delete(olditem); + + M_ARGV(1, entity) = NULL; + + ok_DropItem(mon, frag_attacker); +} + +MUTATOR_HOOKFUNCTION(ok, ForbidThrowCurrentWeapon) +{ + return true; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerPreThink) +{ + if(gameover) + return; + + entity player = M_ARGV(0, entity); + + if(IS_DEAD(player) || !IS_PLAYER(player) || STAT(FROZEN, player)) + return; + + if(player.ok_lastwep) + { + Weapon newwep = Weapons_from(player.ok_lastwep); + if(player.ok_lastwep == WEP_HMG.m_id) + newwep = WEP_MACHINEGUN; + if(player.ok_lastwep == WEP_RPC.m_id) + newwep = WEP_VORTEX; + PS(player).m_switchweapon = newwep; + player.ok_lastwep = 0; + } + + ok_IncreaseCharge(player, PS(player).m_weapon.m_id); + + if(PHYS_INPUT_BUTTON_ATCK2(player)) + if( !forbidWeaponUse(player) || player.weapon_blocked // allow if weapon is blocked + || (round_handler_IsActive() && !round_handler_IsRoundStarted()) ) + if(time >= player.jump_interval) + { + player.jump_interval = time + WEP_CVAR_PRI(blaster, refire) * W_WeaponRateFactor(player); + makevectors(player.v_angle); + + Weapon oldwep = PS(player).m_weapon; + PS(player).m_weapon = WEP_BLASTER; + W_Blaster_Attack( + player, + weaponentities[0], // TODO: unhardcode + WEP_BLASTER.m_id | HITTYPE_SECONDARY, + WEP_CVAR_SEC(vaporizer, shotangle), + WEP_CVAR_SEC(vaporizer, damage), + WEP_CVAR_SEC(vaporizer, edgedamage), + WEP_CVAR_SEC(vaporizer, radius), + WEP_CVAR_SEC(vaporizer, force), + WEP_CVAR_SEC(vaporizer, speed), + WEP_CVAR_SEC(vaporizer, spread), + WEP_CVAR_SEC(vaporizer, delay), + WEP_CVAR_SEC(vaporizer, lifetime) + ); + PS(player).m_weapon = oldwep; + } + + player.weapon_blocked = false; + + player.ok_ammo_charge = player.ammo_charge[PS(player).m_weapon.m_id]; + + if(player.ok_use_ammocharge) + if(!ok_CheckWeaponCharge(player, PS(player).m_weapon.m_id)) + { + if(autocvar_g_overkill_ammo_charge_notice && time > player.ok_notice_time && PHYS_INPUT_BUTTON_ATCK(player) && IS_REAL_CLIENT(player) && PS(player).m_weapon == PS(player).m_switchweapon) + { + //Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_OVERKILL_CHARGE); + player.ok_notice_time = time + 2; + play2(player, SND(DRYFIRE)); + } + Weapon wpn = PS(player).m_weapon; + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + if(player.(weaponentity).state != WS_CLEAR) + w_ready(wpn, player, weaponentity, PHYS_INPUT_BUTTON_ATCK(player) | (PHYS_INPUT_BUTTON_ATCK2(player) << 1)); + + player.weapon_blocked = true; + } + + PHYS_INPUT_BUTTON_ATCK2(player) = false; +} + +MUTATOR_HOOKFUNCTION(ok, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + if(autocvar_g_overkill_ammo_charge) + { + FOREACH(Weapons, it != WEP_Null, LAMBDA(player.ammo_charge[it.m_id] = autocvar_g_overkill_ammo_charge_limit)); + + player.ok_use_ammocharge = 1; + player.ok_notice_time = time; + } + else + player.ok_use_ammocharge = 0; + + // if player changed their weapon while dead, don't switch to their death weapon + if(player.impulse) + player.ok_lastwep = 0; +} + +void self_spawnfunc_weapon_hmg(entity this) { spawnfunc_weapon_hmg(this); } +void self_spawnfunc_weapon_rpc(entity this) { spawnfunc_weapon_rpc(this); } + +MUTATOR_HOOKFUNCTION(ok, OnEntityPreSpawn) +{ + entity ent = M_ARGV(0, entity); + + if(autocvar_g_powerups) + if(autocvar_g_overkill_powerups_replace) + { + if(ent.classname == "item_strength") + { + entity wep = new(weapon_hmg); + setorigin(wep, ent.origin); + setmodel(wep, MDL_OK_HMG); + wep.ok_item = true; + wep.noalign = ent.noalign; + wep.cnt = ent.cnt; + wep.team = ent.team; + wep.respawntime = g_pickup_respawntime_superweapon; + wep.pickup_anyway = true; + wep.spawnfunc_checked = true; + setthink(wep, self_spawnfunc_weapon_hmg); + wep.nextthink = time + 0.1; + return true; + } + + if(ent.classname == "item_invincible") + { + entity wep = new(weapon_rpc); + setorigin(wep, ent.origin); + setmodel(wep, MDL_OK_RPC); + wep.ok_item = true; + wep.noalign = ent.noalign; + wep.cnt = ent.cnt; + wep.team = ent.team; + wep.respawntime = g_pickup_respawntime_superweapon; + wep.pickup_anyway = true; + wep.spawnfunc_checked = true; + setthink(wep, self_spawnfunc_weapon_rpc); + wep.nextthink = time + 0.1; + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(ok, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(item.ok_item) + return false; + + switch(item.itemdef) + { + case ITEM_HealthMega: return autocvar_g_overkill_filter_healthmega; + case ITEM_ArmorMedium: return autocvar_g_overkill_filter_armormedium; + case ITEM_ArmorBig: return autocvar_g_overkill_filter_armorbig; + case ITEM_ArmorMega: return autocvar_g_overkill_filter_armormega; + } + + return true; +} + +MUTATOR_HOOKFUNCTION(ok, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + client.ammo_charge[PS(client).m_weapon.m_id] = spectatee.ammo_charge[PS(spectatee).m_weapon.m_id]; + client.ok_use_ammocharge = spectatee.ok_use_ammocharge; +} + +MUTATOR_HOOKFUNCTION(ok, SetStartItems, CBC_ORDER_LAST) +{ + WepSet ok_start_items = (WEPSET(MACHINEGUN) | WEPSET(VORTEX) | WEPSET(SHOTGUN)); + + if(WEP_RPC.weaponstart > 0) { ok_start_items |= WEPSET(RPC); } + if(WEP_HMG.weaponstart > 0) { ok_start_items |= WEPSET(HMG); } + + start_items |= IT_UNLIMITED_WEAPON_AMMO; + start_weapons = warmup_start_weapons = ok_start_items; +} + +MUTATOR_HOOKFUNCTION(ok, SetWeaponArena) +{ + // turn weapon arena off + M_ARGV(0, string) = "off"; +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":OK"); +} + +MUTATOR_HOOKFUNCTION(ok, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Overkill"); +} + +MUTATOR_HOOKFUNCTION(ok, SetModname) +{ + M_ARGV(0, string) = "Overkill"; + return true; +} + +void ok_SetCvars() +{ + // hack to force overkill playermodels + cvar_settemp("sv_defaultcharacter", "1"); + cvar_settemp("sv_defaultplayermodel", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); + cvar_settemp("sv_defaultplayermodel_red", "models/ok_player/okrobot1.dpm models/ok_player/okrobot2.dpm models/ok_player/okrobot3.dpm models/ok_player/okrobot4.dpm"); + cvar_settemp("sv_defaultplayermodel_blue", "models/ok_player/okmale1.dpm models/ok_player/okmale2.dpm models/ok_player/okmale3.dpm models/ok_player/okmale4.dpm"); +} + +void ok_Initialize() +{ + ok_SetCvars(); + + precache_all_playermodels("models/ok_player/*.dpm"); + + WEP_RPC.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + WEP_HMG.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + WEP_SHOTGUN.mdl = "ok_shotgun"; + WEP_MACHINEGUN.mdl = "ok_mg"; + WEP_VORTEX.mdl = "ok_sniper"; +} diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/physical_items/_mod.inc b/qcsrc/common/mutators/mutator/physical_items/_mod.inc index 4d4ef59f8a..e99d4e257a 100644 --- a/qcsrc/common/mutators/mutator/physical_items/_mod.inc +++ b/qcsrc/common/mutators/mutator/physical_items/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/_mod.qh b/qcsrc/common/mutators/mutator/physical_items/_mod.qh index a347cec04e..1aab8b0a84 100644 --- a/qcsrc/common/mutators/mutator/physical_items/_mod.qh +++ b/qcsrc/common/mutators/mutator/physical_items/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/module.inc b/qcsrc/common/mutators/mutator/physical_items/module.inc deleted file mode 100644 index 7ed9b039be..0000000000 --- a/qcsrc/common/mutators/mutator/physical_items/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "physical_items.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/physical_items.qc deleted file mode 100644 index 10ec0cf5a8..0000000000 --- a/qcsrc/common/mutators/mutator/physical_items/physical_items.qc +++ /dev/null @@ -1,141 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_physical_items; -float autocvar_g_physical_items_damageforcescale; -float autocvar_g_physical_items_reset; - -REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) -{ - // check if we have a physics engine - MUTATOR_ONADD - { - if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) - { - LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items.\n"); - return -1; - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // nothing to roll back - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This cannot be removed at runtime\n"); - return -1; - } - - return 0; -} - -.vector spawn_origin, spawn_angles; - -void physical_item_think(entity this) -{ - this.nextthink = time; - - this.alpha = this.owner.alpha; // apply fading and ghosting - - if(!this.cnt) // map item, not dropped - { - // copy ghost item properties - this.colormap = this.owner.colormap; - this.colormod = this.owner.colormod; - this.glowmod = this.owner.glowmod; - - // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there - if(autocvar_g_physical_items_reset) - { - if(this.owner.wait > time) // awaiting respawn - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - this.solid = SOLID_NOT; - this.alpha = -1; - this.movetype = MOVETYPE_NONE; - } - else - { - this.alpha = 1; - this.solid = SOLID_CORPSE; - this.movetype = MOVETYPE_PHYSICS; - } - } - } - - if(!this.owner.modelindex) - remove(this); // the real item is gone, remove this -} - -void physical_item_touch(entity this) -{ - if(!this.cnt) // not for dropped items - if (ITEM_TOUCH_NEEDKILL()) - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - } -} - -void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - if(!this.cnt) // not for dropped items - if(ITEM_DAMAGE_NEEDKILL(deathtype)) - { - setorigin(this, this.spawn_origin); - this.angles = this.spawn_angles; - } -} - -MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) -{ - entity item = M_ARGV(0, entity); - - if(item.owner == NULL && autocvar_g_physical_items <= 1) - return; - if (item.spawnflags & 1) // floating item - return; - - // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. - // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. - entity wep; - wep = spawn(); - _setmodel(wep, item.model); - setsize(wep, item.mins, item.maxs); - setorigin(wep, item.origin); - wep.angles = item.angles; - wep.velocity = item.velocity; - - wep.owner = item; - wep.solid = SOLID_CORPSE; - wep.movetype = MOVETYPE_PHYSICS; - wep.takedamage = DAMAGE_AIM; - wep.effects |= EF_NOMODELFLAGS; // disable the spinning - wep.colormap = item.owner.colormap; - wep.glowmod = item.owner.glowmod; - wep.damageforcescale = autocvar_g_physical_items_damageforcescale; - wep.dphitcontentsmask = item.dphitcontentsmask; - wep.cnt = (item.owner != NULL); - - setthink(wep, physical_item_think); - wep.nextthink = time; - settouch(wep, physical_item_touch); - wep.event_damage = physical_item_damage; - - if(!wep.cnt) - { - // fix the spawn origin - setorigin(wep, wep.origin + '0 0 1'); - droptofloor(wep); - } - - wep.spawn_origin = wep.origin; - wep.spawn_angles = item.angles; - - item.effects |= EF_NODRAW; // hide the original weapon - item.movetype = MOVETYPE_FOLLOW; - item.aiment = wep; // attach the original weapon - setSendEntity(item, func_null); -} -#endif diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc new file mode 100644 index 0000000000..62b30e2d14 --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qc @@ -0,0 +1,141 @@ +#include "sv_physical_items.qh" + +int autocvar_g_physical_items; +float autocvar_g_physical_items_damageforcescale; +float autocvar_g_physical_items_reset; + +REGISTER_MUTATOR(physical_items, cvar("g_physical_items")) +{ + // check if we have a physics engine + MUTATOR_ONADD + { + if (!(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE"))) + { + LOG_TRACE("Warning: Physical items are enabled but no physics engine can be used. Reverting to old items."); + return -1; + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // nothing to roll back + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This cannot be removed at runtime\n"); + return -1; + } + + return 0; +} + +.vector spawn_origin, spawn_angles; + +void physical_item_think(entity this) +{ + this.nextthink = time; + + this.alpha = this.owner.alpha; // apply fading and ghosting + + if(!this.cnt) // map item, not dropped + { + // copy ghost item properties + this.colormap = this.owner.colormap; + this.colormod = this.owner.colormod; + this.glowmod = this.owner.glowmod; + + // if the item is not spawned, make sure the invisible / ghost item returns to its origin and stays there + if(autocvar_g_physical_items_reset) + { + if(this.owner.wait > time) // awaiting respawn + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + this.solid = SOLID_NOT; + this.alpha = -1; + set_movetype(this, MOVETYPE_NONE); + } + else + { + this.alpha = 1; + this.solid = SOLID_CORPSE; + set_movetype(this, MOVETYPE_PHYSICS); + } + } + } + + if(!this.owner.modelindex) + delete(this); // the real item is gone, remove this +} + +void physical_item_touch(entity this, entity toucher) +{ + if(!this.cnt) // not for dropped items + if (ITEM_TOUCH_NEEDKILL()) + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + } +} + +void physical_item_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + if(!this.cnt) // not for dropped items + if(ITEM_DAMAGE_NEEDKILL(deathtype)) + { + setorigin(this, this.spawn_origin); + this.angles = this.spawn_angles; + } +} + +MUTATOR_HOOKFUNCTION(physical_items, Item_Spawn) +{ + entity item = M_ARGV(0, entity); + + if(item.owner == NULL && autocvar_g_physical_items <= 1) + return; + if (item.spawnflags & 1) // floating item + return; + + // The actual item can't be physical and trigger at the same time, so make it invisible and use a second entity for physics. + // Ugly hack, but unless SOLID_TRIGGER is gotten to work with MOVETYPE_PHYSICS in the engine it can't be fixed. + entity wep; + wep = spawn(); + _setmodel(wep, item.model); + setsize(wep, item.mins, item.maxs); + setorigin(wep, item.origin); + wep.angles = item.angles; + wep.velocity = item.velocity; + + wep.owner = item; + wep.solid = SOLID_CORPSE; + set_movetype(wep, MOVETYPE_PHYSICS); + wep.takedamage = DAMAGE_AIM; + wep.effects |= EF_NOMODELFLAGS; // disable the spinning + wep.colormap = item.owner.colormap; + wep.glowmod = item.owner.glowmod; + wep.damageforcescale = autocvar_g_physical_items_damageforcescale; + wep.dphitcontentsmask = item.dphitcontentsmask; + wep.cnt = (item.owner != NULL); + + setthink(wep, physical_item_think); + wep.nextthink = time; + settouch(wep, physical_item_touch); + wep.event_damage = physical_item_damage; + + if(!wep.cnt) + { + // fix the spawn origin + setorigin(wep, wep.origin + '0 0 1'); + droptofloor(wep); + } + + wep.spawn_origin = wep.origin; + wep.spawn_angles = item.angles; + + item.effects |= EF_NODRAW; // hide the original weapon + set_movetype(item, MOVETYPE_FOLLOW); + item.aiment = wep; // attach the original weapon + setSendEntity(item, func_null); +} diff --git a/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/physical_items/sv_physical_items.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/pinata/_mod.inc b/qcsrc/common/mutators/mutator/pinata/_mod.inc index a0bd94d00e..5859c55107 100644 --- a/qcsrc/common/mutators/mutator/pinata/_mod.inc +++ b/qcsrc/common/mutators/mutator/pinata/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/_mod.qh b/qcsrc/common/mutators/mutator/pinata/_mod.qh index 1602640e09..fdb51ed25b 100644 --- a/qcsrc/common/mutators/mutator/pinata/_mod.qh +++ b/qcsrc/common/mutators/mutator/pinata/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/pinata/module.inc b/qcsrc/common/mutators/mutator/pinata/module.inc deleted file mode 100644 index 4e22966863..0000000000 --- a/qcsrc/common/mutators/mutator/pinata/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "pinata.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/pinata/pinata.qc b/qcsrc/common/mutators/mutator/pinata/pinata.qc deleted file mode 100644 index 10866a2f16..0000000000 --- a/qcsrc/common/mutators/mutator/pinata/pinata.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); - -MUTATOR_HOOKFUNCTION(pinata, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(frag_target.weapons & WepSet_FromWeapon(it)) - if(PS(frag_target).m_switchweapon != 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'); - )); - - return true; -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata"); -} - -MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata"); -} - -#endif diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc new file mode 100644 index 0000000000..bc3887e860 --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qc @@ -0,0 +1,27 @@ +#include "sv_pinata.qh" + +REGISTER_MUTATOR(pinata, cvar("g_pinata") && !cvar("g_instagib") && !cvar("g_overkill")); + +MUTATOR_HOOKFUNCTION(pinata, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(frag_target.weapons & WepSet_FromWeapon(it)) + if(PS(frag_target).m_switchweapon != 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'); + )); + + return true; +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Pinata"); +} + +MUTATOR_HOOKFUNCTION(pinata, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Piñata"); +} diff --git a/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/pinata/sv_pinata.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/random_gravity/_mod.inc b/qcsrc/common/mutators/mutator/random_gravity/_mod.inc index feeaec8d69..846bd8bf82 100644 --- a/qcsrc/common/mutators/mutator/random_gravity/_mod.inc +++ b/qcsrc/common/mutators/mutator/random_gravity/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/_mod.qh b/qcsrc/common/mutators/mutator/random_gravity/_mod.qh index 99a11ed638..2cdf724023 100644 --- a/qcsrc/common/mutators/mutator/random_gravity/_mod.qh +++ b/qcsrc/common/mutators/mutator/random_gravity/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/module.inc b/qcsrc/common/mutators/mutator/random_gravity/module.inc deleted file mode 100644 index 91baa43102..0000000000 --- a/qcsrc/common/mutators/mutator/random_gravity/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "random_gravity.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc deleted file mode 100644 index a384a2b9c2..0000000000 --- a/qcsrc/common/mutators/mutator/random_gravity/random_gravity.qc +++ /dev/null @@ -1,50 +0,0 @@ -#ifdef IMPLEMENTATION -// Random Gravity -// -// Mutator by Mario -// Inspired by Player 2 - -float autocvar_g_random_gravity_negative_chance; -float autocvar_g_random_gravity_min; -float autocvar_g_random_gravity_max; -float autocvar_g_random_gravity_positive; -float autocvar_g_random_gravity_negative; -float autocvar_g_random_gravity_delay; - -REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) -{ - MUTATOR_ONADD - { - cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end - } -} - -float gravity_delay; - -MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) -{ - if(gameover || !cvar("g_random_gravity")) return false; - if(time < gravity_delay) return false; - if(time < game_starttime) return false; - if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; - - if(random() >= autocvar_g_random_gravity_negative_chance) - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); - else - cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); - - gravity_delay = time + autocvar_g_random_gravity_delay; - - LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity), "\n"); -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity"); -} - -MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc new file mode 100644 index 0000000000..f6f28a7dcb --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qc @@ -0,0 +1,50 @@ +#include "sv_random_gravity.qh" + +// Random Gravity + +// Mutator by Mario +// Inspired by Player 2 + +float autocvar_g_random_gravity_negative_chance; +float autocvar_g_random_gravity_min; +float autocvar_g_random_gravity_max; +float autocvar_g_random_gravity_positive; +float autocvar_g_random_gravity_negative; +float autocvar_g_random_gravity_delay; + +REGISTER_MUTATOR(random_gravity, cvar("g_random_gravity")) +{ + MUTATOR_ONADD + { + cvar_settemp("sv_gravity", cvar_string("sv_gravity")); // settemp current gravity so it's restored on match end + } +} + +float gravity_delay; + +MUTATOR_HOOKFUNCTION(random_gravity, SV_StartFrame) +{ + if(gameover || !cvar("g_random_gravity")) return false; + if(time < gravity_delay) return false; + if(time < game_starttime) return false; + if(round_handler_IsActive() && !round_handler_IsRoundStarted()) return false; + + if(random() >= autocvar_g_random_gravity_negative_chance) + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() - random() * -autocvar_g_random_gravity_negative, autocvar_g_random_gravity_max))); + else + cvar_set("sv_gravity", ftos(bound(autocvar_g_random_gravity_min, random() * autocvar_g_random_gravity_positive, autocvar_g_random_gravity_max))); + + gravity_delay = time + autocvar_g_random_gravity_delay; + + LOG_TRACE("Gravity is now: ", ftos(autocvar_sv_gravity)); +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RandomGravity"); +} + +MUTATOR_HOOKFUNCTION(random_gravity, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Random gravity"); +} diff --git a/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/random_gravity/sv_random_gravity.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/rocketflying/_mod.inc b/qcsrc/common/mutators/mutator/rocketflying/_mod.inc index 0841ae680b..537a2b37b0 100644 --- a/qcsrc/common/mutators/mutator/rocketflying/_mod.inc +++ b/qcsrc/common/mutators/mutator/rocketflying/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/_mod.qh b/qcsrc/common/mutators/mutator/rocketflying/_mod.qh index 75ca141bf0..b24545f8c4 100644 --- a/qcsrc/common/mutators/mutator/rocketflying/_mod.qh +++ b/qcsrc/common/mutators/mutator/rocketflying/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/module.inc b/qcsrc/common/mutators/mutator/rocketflying/module.inc deleted file mode 100644 index 7036bc49d6..0000000000 --- a/qcsrc/common/mutators/mutator/rocketflying/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "rocketflying.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc deleted file mode 100644 index da7e1c3ec0..0000000000 --- a/qcsrc/common/mutators/mutator/rocketflying/rocketflying.qc +++ /dev/null @@ -1,24 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); - -MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) -{ - entity proj = M_ARGV(1, entity); - - if(proj.classname == "rocket" || proj.classname == "mine") - { - // kill detonate delay of rockets - proj.spawnshieldtime = time; - } -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying"); -} - -MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc new file mode 100644 index 0000000000..9f0d8fbf0d --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qc @@ -0,0 +1,24 @@ +#include "sv_rocketflying.qh" + +REGISTER_MUTATOR(rocketflying, cvar("g_rocket_flying")); + +MUTATOR_HOOKFUNCTION(rocketflying, EditProjectile) +{ + entity proj = M_ARGV(1, entity); + + if(proj.classname == "rocket" || proj.classname == "mine") + { + // kill detonate delay of rockets + proj.spawnshieldtime = time; + } +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":RocketFlying"); +} + +MUTATOR_HOOKFUNCTION(rocketflying, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Rocket Flying"); +} diff --git a/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketflying/sv_rocketflying.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc b/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc index bc579ec512..bb75554bad 100644 --- a/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc +++ b/qcsrc/common/mutators/mutator/rocketminsta/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh b/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh index 29a367d3cf..832c5da2cb 100644 --- a/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh +++ b/qcsrc/common/mutators/mutator/rocketminsta/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/module.inc b/qcsrc/common/mutators/mutator/rocketminsta/module.inc deleted file mode 100644 index b7d02a9f6b..0000000000 --- a/qcsrc/common/mutators/mutator/rocketminsta/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "rocketminsta.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc deleted file mode 100644 index b0a740391f..0000000000 --- a/qcsrc/common/mutators/mutator/rocketminsta/rocketminsta.qc +++ /dev/null @@ -1,40 +0,0 @@ -#ifdef IMPLEMENTATION -#include -#include - -REGISTER_MUTATOR(rm, cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return; } - - 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(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) - if(frag_attacker == frag_target || frag_target.classname == "nade") - frag_damage = 0; - - if(autocvar_g_rm_laser) - if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) - frag_damage = 0; - - M_ARGV(4, float) = frag_damage; -} - -MUTATOR_HOOKFUNCTION(rm, PlayerDies) -{ - // we do it this way, so rm can be toggled during the match - if(!autocvar_g_rm) { return; } - - float frag_deathtype = M_ARGV(3, float); - - if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) - M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death -} - -#endif diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc new file mode 100644 index 0000000000..04d8099e02 --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qc @@ -0,0 +1,39 @@ +#include "sv_rocketminsta.qh" + +#include +#include + +REGISTER_MUTATOR(rm, cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(rm, PlayerDamage_Calculate) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return; } + + 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(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR)) + if(frag_attacker == frag_target || frag_target.classname == "nade") + frag_damage = 0; + + if(autocvar_g_rm_laser) + if(DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + if(frag_attacker == frag_target || (round_handler_IsActive() && !round_handler_IsRoundStarted())) + frag_damage = 0; + + M_ARGV(4, float) = frag_damage; +} + +MUTATOR_HOOKFUNCTION(rm, PlayerDies) +{ + // we do it this way, so rm can be toggled during the match + if(!autocvar_g_rm) { return; } + + float frag_deathtype = M_ARGV(3, float); + + if(DEATH_ISWEAPON(frag_deathtype, WEP_DEVASTATOR) || DEATH_ISWEAPON(frag_deathtype, WEP_ELECTRO)) + M_ARGV(4, float) = 1000; // always gib if it was a vaporizer death +} diff --git a/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/rocketminsta/sv_rocketminsta.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/running_guns/_mod.inc b/qcsrc/common/mutators/mutator/running_guns/_mod.inc index f88b36a534..d1db34cf75 100644 --- a/qcsrc/common/mutators/mutator/running_guns/_mod.inc +++ b/qcsrc/common/mutators/mutator/running_guns/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/_mod.qh b/qcsrc/common/mutators/mutator/running_guns/_mod.qh index 559be4c987..cc0c58eff4 100644 --- a/qcsrc/common/mutators/mutator/running_guns/_mod.qh +++ b/qcsrc/common/mutators/mutator/running_guns/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/module.inc b/qcsrc/common/mutators/mutator/running_guns/module.inc deleted file mode 100644 index 036b70ff62..0000000000 --- a/qcsrc/common/mutators/mutator/running_guns/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "running_guns.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/running_guns.qc deleted file mode 100644 index cad4d5f2fe..0000000000 --- a/qcsrc/common/mutators/mutator/running_guns/running_guns.qc +++ /dev/null @@ -1,14 +0,0 @@ -#ifdef IMPLEMENTATION - -bool autocvar_g_running_guns; - -REGISTER_MUTATOR(running_guns, autocvar_g_running_guns); - -MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha) -{ - default_player_alpha = -1; - default_weapon_alpha = +1; - return true; -} - -#endif diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc new file mode 100644 index 0000000000..797108e012 --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qc @@ -0,0 +1,11 @@ +#include "sv_running_guns.qh" + +bool autocvar_g_running_guns; +REGISTER_MUTATOR(running_guns, autocvar_g_running_guns); + +MUTATOR_HOOKFUNCTION(running_guns, SetDefaultAlpha) +{ + default_player_alpha = -1; + default_weapon_alpha = +1; + return true; +} diff --git a/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/running_guns/sv_running_guns.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/sandbox/_mod.inc b/qcsrc/common/mutators/mutator/sandbox/_mod.inc index 8e54c1f95e..569c25319b 100644 --- a/qcsrc/common/mutators/mutator/sandbox/_mod.inc +++ b/qcsrc/common/mutators/mutator/sandbox/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/_mod.qh b/qcsrc/common/mutators/mutator/sandbox/_mod.qh index 81e250c7fa..86b112934d 100644 --- a/qcsrc/common/mutators/mutator/sandbox/_mod.qh +++ b/qcsrc/common/mutators/mutator/sandbox/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/module.inc b/qcsrc/common/mutators/mutator/sandbox/module.inc deleted file mode 100644 index 0715d5b403..0000000000 --- a/qcsrc/common/mutators/mutator/sandbox/module.inc +++ /dev/null @@ -1,4 +0,0 @@ -#ifdef SVQC -#include "sandbox.qc" - -#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sandbox.qc deleted file mode 100644 index bc4cb67752..0000000000 --- a/qcsrc/common/mutators/mutator/sandbox/sandbox.qc +++ /dev/null @@ -1,828 +0,0 @@ -#ifdef IMPLEMENTATION -int autocvar_g_sandbox_info; -bool autocvar_g_sandbox_readonly; -string autocvar_g_sandbox_storage_name; -float autocvar_g_sandbox_storage_autosave; -bool autocvar_g_sandbox_storage_autoload; -float autocvar_g_sandbox_editor_flood; -int autocvar_g_sandbox_editor_maxobjects; -int autocvar_g_sandbox_editor_free; -float autocvar_g_sandbox_editor_distance_spawn; -float autocvar_g_sandbox_editor_distance_edit; -float autocvar_g_sandbox_object_scale_min; -float autocvar_g_sandbox_object_scale_max; -float autocvar_g_sandbox_object_material_velocity_min; -float autocvar_g_sandbox_object_material_velocity_factor; - -float autosave_time; -void sandbox_Database_Load(); - -REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) -{ - MUTATOR_ONADD - { - autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame - if(autocvar_g_sandbox_storage_autoload) - sandbox_Database_Load(); - } -} - -const float MAX_STORAGE_ATTACHMENTS = 16; -float object_count; -.float object_flood; -.entity object_attach; -.string material; - -.float touch_timer; -void sandbox_ObjectFunction_Touch(entity this) -{ - // apply material impact effects - - if(!this.material) - return; - if(this.touch_timer > time) - return; // don't execute each frame - this.touch_timer = time + 0.1; - - // make particle count and sound volume depend on impact speed - float intensity; - intensity = vlen(this.velocity) + vlen(other.velocity); - if(intensity) // avoid divisions by 0 - intensity /= 2; // average the two velocities - if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) - return; // impact not strong enough to do anything - // now offset intensity and apply it to the effects - intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity - intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); - - _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); - Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 -} - -void sandbox_ObjectFunction_Think(entity this) -{ - // decide if and how this object can be grabbed - if(autocvar_g_sandbox_readonly) - this.grab = 0; // no grabbing - else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp) - this.grab = 1; // owner only - else - this.grab = 3; // anyone - - // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). - // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, - // since if the owning player disconnects, the object's owner should also be reset. - - // bots can't have objects - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( - if(this.crypto_idfp == it.crypto_idfp) - { - this.realowner = it; - break; - } - this.realowner = NULL; - )); - - this.nextthink = time; - - CSQCMODEL_AUTOUPDATE(this); -} - -.float old_solid, old_movetype; -entity sandbox_ObjectEdit_Get(entity this, float permissions) -{ - // Returns the traced entity if the player can edit it, and NULL if not. - // If permissions if false, the object is returned regardless of editing rights. - // Attached objects are SOLID_NOT and do not get traced. - - crosshair_trace_plusvisibletriggers(this); - if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit)) - return NULL; // out of trace range - if(trace_ent.classname != "object") - return NULL; // entity is not an object - if(!permissions) - return trace_ent; // don't check permissions, anyone can edit this object - if(trace_ent.crypto_idfp == "") - return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it - if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2)) - return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server - return NULL; -} - -void sandbox_ObjectEdit_Scale(entity e, float f) -{ - e.scale = f; - if(e.scale) - { - e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); - _setmodel(e, e.model); // reset mins and maxs based on mesh - setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size - } -} - -void sandbox_ObjectAttach_Remove(entity e); -void sandbox_ObjectAttach_Set(entity e, entity parent, string s) -{ - // attaches e to parent on string s - - // we can't attach to an attachment, for obvious reasons - sandbox_ObjectAttach_Remove(e); - - e.old_solid = e.solid; // persist solidity - e.old_movetype = e.movetype; // persist physics - e.movetype = MOVETYPE_FOLLOW; - e.solid = SOLID_NOT; - e.takedamage = DAMAGE_NO; - - setattachment(e, parent, s); - e.owner = parent; -} - -void sandbox_ObjectAttach_Remove(entity e) -{ - // detaches any object attached to e - - entity head; - for(head = NULL; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - vector org; - org = gettaginfo(head, 0); - setattachment(head, NULL, ""); - head.owner = NULL; - - // objects change origin and angles when detached, so apply previous position - setorigin(head, org); - head.angles = e.angles; // don't allow detached objects to spin or roll - - head.solid = head.old_solid; // restore persisted solidity - head.movetype = head.old_movetype; // restore persisted physics - head.takedamage = DAMAGE_AIM; - } - } -} - -entity sandbox_ObjectSpawn(entity this, float database) -{ - // spawn a new object with default properties - - entity e = new(object); - e.takedamage = DAMAGE_AIM; - e.damageforcescale = 1; - e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly - e.movetype = MOVETYPE_TOSS; - e.frame = 0; - e.skin = 0; - e.material = string_null; - settouch(e, sandbox_ObjectFunction_Touch); - setthink(e, sandbox_ObjectFunction_Think); - e.nextthink = time; - //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? - - if(!database) - { - // set the object's owner via player UID - // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone - if(this.crypto_idfp != "") - e.crypto_idfp = strzone(this.crypto_idfp); - else - print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); - - // set public object information - e.netname = strzone(this.netname); // name of the owner - e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time - - // set origin and direction based on player position and view angle - makevectors(this.v_angle); - WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this); - setorigin(e, trace_endpos); - e.angles_y = this.v_angle.y; - } - - CSQCMODEL_AUTOINIT(e); - - object_count += 1; - return e; -} - -void sandbox_ObjectRemove(entity e) -{ - sandbox_ObjectAttach_Remove(e); // detach child objects - - // 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, LAMBDA(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; } - remove(e); - e = NULL; - - object_count -= 1; -} - -string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global - -string sandbox_ObjectPort_Save(entity e, float database) -{ - // save object properties, and return them as a string - float i = 0; - string s; - entity head; - - for(head = NULL; (head = find(head, classname, "object")); ) - { - // the main object needs to be first in the array [0] with attached objects following - float slot, physics, solidity; - if(head == e) // this is the main object, place it first - { - slot = 0; - solidity = head.solid; // applied solidity is normal solidity for children - physics = head.movetype; // applied physics are normal physics for parents - } - else if(head.owner == e) // child object, list them in order - { - i += 1; // children start from 1 - slot = i; - solidity = head.old_solid; // persisted solidity is normal solidity for children - physics = head.old_movetype; // persisted physics are normal physics for children - gettaginfo(head.owner, head.tag_index); // get the name of the tag our object is attached to, used further below - } - else - continue; - - // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- - if(slot) - { - // properties stored only for child objects - if(gettaginfo_name) port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - } - else - { - // properties stored only for parent objects - if(database) - { - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.origin), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.angles), " "); - } - } - // properties stored for all objects - port_string[slot] = strcat(port_string[slot], "\"", head.model, "\" "); - port_string[slot] = strcat(port_string[slot], ftos(head.skin), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.alpha), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.colormod), " "); - port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", head.glowmod), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.frame), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.scale), " "); - port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); - port_string[slot] = strcat(port_string[slot], ftos(physics), " "); - port_string[slot] = strcat(port_string[slot], ftos(head.damageforcescale), " "); - if(head.material) port_string[slot] = strcat(port_string[slot], "\"", head.material, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - if(database) - { - // properties stored only for the database - if(head.crypto_idfp) port_string[slot] = strcat(port_string[slot], "\"", head.crypto_idfp, "\" "); else port_string[slot] = strcat(port_string[slot], "\"\" "); // none - port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); - port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); - } - } - - // now apply the array to a simple string, with the ; symbol separating objects - s = ""; - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - { - if(port_string[i]) - s = strcat(s, port_string[i], "; "); - port_string[i] = string_null; // fully clear the string - } - - return s; -} - -entity sandbox_ObjectPort_Load(entity this, string s, float database) -{ - // load object properties, and spawn a new object with them - float n, i; - entity e = NULL, parent = NULL; - - // separate objects between the ; symbols - n = tokenizebyseparator(s, "; "); - for(i = 0; i < n; ++i) - port_string[i] = argv(i); - - // now separate and apply the properties of each object - for(i = 0; i < n; ++i) - { - float argv_num; - string tagname = string_null; - argv_num = 0; - tokenize_console(port_string[i]); - e = sandbox_ObjectSpawn(this, database); - - // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- - if(i) - { - // properties stored only for child objects - if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; - } - 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; - } - 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.movetype = e.old_movetype = stof(argv(argv_num)); ++argv_num; - 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; - 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; - } - - // attach last - if(i) - sandbox_ObjectAttach_Set(e, parent, tagname); - } - - for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) - port_string[i] = string_null; // fully clear the string - - return e; -} - -void sandbox_Database_Save() -{ - // saves all objects to the database file - entity head; - string file_name; - float file_get; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_WRITE); - fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); - fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); - - for(head = NULL; (head = find(head, classname, "object")); ) - { - // attached objects are persisted separately, ignore them here - if(head.owner != NULL) - continue; - - // use a line of text for each object, listing all properties - fputs(file_get, strcat(sandbox_ObjectPort_Save(head, true), "\n")); - } - fclose(file_get); -} - -void sandbox_Database_Load() -{ - // loads all objects from the database file - string file_read, file_name; - float file_get, i; - - file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); - file_get = fopen(file_name, FILE_READ); - if(file_get < 0) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); - } - else - { - for (;;) - { - file_read = fgets(file_get); - if(file_read == "") - break; - if(substring(file_read, 0, 2) == "//") - continue; - if(substring(file_read, 0, 1) == "#") - continue; - - entity e; - e = sandbox_ObjectPort_Load(NULL, file_read, true); - - if(e.material) - { - // since objects are being loaded for the first time, precache material sounds for each - for (i = 1; i <= 5; i++) // 5 sounds in total - precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); - } - } - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); - } - fclose(file_get); -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) -{ - if(MUTATOR_RETURNVALUE) // command was already handled? - return; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if(cmd_name == "g_sandbox") - { - if(autocvar_g_sandbox_readonly) - { - print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); - return true; - } - if(cmd_argc < 2) - { - print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); - return true; - } - - switch(argv(1)) - { - entity e; - float i; - string s; - - // ---------------- COMMAND: HELP ---------------- - case "help": - print_to(player, "You can use the following sandbox commands:"); - print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); - print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); - print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); - print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar"); - print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); - print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); - print_to(player, "^3get ^7- selects the object you are facing as the object to be attached"); - print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); - print_to(player, "^3remove ^7- detaches all objects from the object you are facing"); - print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); - print_to(player, "^3skin value ^7- changes the skin of the object"); - print_to(player, "^3alpha value ^7- sets object transparency"); - print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color"); - print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); - print_to(player, "^3frame value ^7- object animation frame, for self-animated models"); - print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); - print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); - print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); - print_to(player, "^3force value ^7- amount of force applied to objects that are shot"); - print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); - print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); - print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object"); - print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); - print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); - print_to(player, "^3attachments ^7- prints information about the object's attachments"); - print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); - return true; - - // ---------------- COMMAND: OBJECT, SPAWN ---------------- - case "object_spawn": - if(time < player.object_flood) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - player.object_flood = time + autocvar_g_sandbox_editor_flood; - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - if(cmd_argc < 3) - { - print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); - return true; - } - if (!(fexists(argv(2)))) - { - print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); - return true; - } - - e = sandbox_ObjectSpawn(player, false); - _setmodel(e, argv(2)); - - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); - return true; - - // ---------------- COMMAND: OBJECT, REMOVE ---------------- - case "object_remove": - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); - sandbox_ObjectRemove(e); - return true; - } - - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- - case "object_duplicate": - switch(argv(2)) - { - case "copy": - // copies customizable properties of the selected object to the clipboard cvar - e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? - if(e != NULL) - { - s = sandbox_ObjectPort_Save(e, false); - s = strreplace("\"", "\\\"", s); - stuffcmd(player, strcat("set ", argv(3), " \"", s, "\"")); - - print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard"); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); - return true; - - case "paste": - // spawns a new object using the properties in the player's clipboard cvar - if(time < player.object_flood) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); - return true; - } - player.object_flood = time + autocvar_g_sandbox_editor_flood; - if(argv(3) == "") // no object in clipboard - { - print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); - return true; - } - if(object_count >= autocvar_g_sandbox_editor_maxobjects) - { - print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); - return true; - } - e = sandbox_ObjectPort_Load(player, argv(3), false); - - print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully"); - if(autocvar_g_sandbox_info > 0) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, ATTACH ---------------- - case "object_attach": - switch(argv(2)) - { - case "get": - // select e as the object as meant to be attached - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - player.object_attach = e; - print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment"); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); - return true; - case "set": - if(player.object_attach == NULL) - { - print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); - return true; - } - - // attaches the previously selected object to e - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - sandbox_ObjectAttach_Set(player.object_attach, e, argv(3)); - player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment - print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); - return true; - case "remove": - // removes e if it was attached - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - sandbox_ObjectAttach_Remove(e); - print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully"); - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); - return true; - } - print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); - return true; - } - return true; - - // ---------------- COMMAND: OBJECT, EDIT ---------------- - case "object_edit": - if(argv(2) == "") - { - print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); - return true; - } - - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - switch(argv(2)) - { - case "skin": - e.skin = stof(argv(3)); - break; - case "alpha": - e.alpha = stof(argv(3)); - break; - case "color_main": - e.colormod = stov(argv(3)); - break; - case "color_glow": - e.glowmod = stov(argv(3)); - break; - case "frame": - e.frame = stof(argv(3)); - break; - case "scale": - sandbox_ObjectEdit_Scale(e, stof(argv(3))); - break; - case "solidity": - switch(argv(3)) - { - case "0": // non-solid - e.solid = SOLID_TRIGGER; - break; - case "1": // solid - e.solid = SOLID_BBOX; - break; - default: - break; - } - case "physics": - switch(argv(3)) - { - case "0": // static - e.movetype = MOVETYPE_NONE; - break; - case "1": // movable - e.movetype = MOVETYPE_TOSS; - break; - case "2": // physical - e.movetype = MOVETYPE_PHYSICS; - break; - default: - break; - } - break; - case "force": - e.damageforcescale = stof(argv(3)); - break; - case "material": - if(e.material) strunzone(e.material); - if(argv(3)) - { - for (i = 1; i <= 5; i++) // precache material sounds, 5 in total - precache_sound(strcat("object/impact_", argv(3), "_", ftos(i), ".wav")); - e.material = strzone(argv(3)); - } - else - e.material = string_null; // no material - break; - default: - print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); - return true; - } - - // update last editing time - if(e.message2) strunzone(e.message2); - e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); - - if(autocvar_g_sandbox_info > 1) - LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); - return true; - } - - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, CLAIM ---------------- - case "object_claim": - // if the player can edit an object but is not its owner, this can be used to claim that object - if(player.crypto_idfp == "") - { - print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); - return true; - } - e = sandbox_ObjectEdit_Get(player, true); - if(e != NULL) - { - // update the owner's name - // Do this before checking if you're already the owner and skipping if such, so we - // 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); - print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated"); - } - - if(e.crypto_idfp == player.crypto_idfp) - { - print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); - return true; - } - - if(e.crypto_idfp) strunzone(e.crypto_idfp); - e.crypto_idfp = strzone(player.crypto_idfp); - - print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully"); - } - print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); - return true; - - // ---------------- COMMAND: OBJECT, INFO ---------------- - case "object_info": - // prints public information about the object to the player - e = sandbox_ObjectEdit_Get(player, false); - if(e != NULL) - { - switch(argv(2)) - { - case "object": - print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); - return true; - case "mesh": - s = ""; - FOR_EACH_TAG(e) - s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); - print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); - return true; - case "attachments": - // this should show the same info as 'mesh' but for attachments - s = ""; - entity head; - i = 0; - for(head = NULL; (head = find(head, classname, "object")); ) - { - if(head.owner == e) - { - ++i; // start from 1 - gettaginfo(e, head.tag_index); - s = strcat(s, "^1attachment ", ftos(i), "^7 has mesh \"^3", head.model, "^7\" at animation frame ^3", ftos(head.frame)); - s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); - } - } - if(i) // object contains attachments - print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(i), "^7 attachment(s): ", s)); - else - print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments"); - return true; - } - } - print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); - return true; - - // ---------------- COMMAND: DEFAULT ---------------- - default: - print_to(player, "Invalid command. For usage information, type 'sandbox help'"); - return true; - } - } -} - -MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) -{ - if(!autocvar_g_sandbox_storage_autosave) - return; - if(time < autosave_time) - return; - autosave_time = time + autocvar_g_sandbox_storage_autosave; - - sandbox_Database_Save(); - - return true; -} -#endif diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc new file mode 100644 index 0000000000..a2211fe75a --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qc @@ -0,0 +1,823 @@ +#include "sv_sandbox.qh" + +int autocvar_g_sandbox_info; +bool autocvar_g_sandbox_readonly; +string autocvar_g_sandbox_storage_name; +float autocvar_g_sandbox_storage_autosave; +bool autocvar_g_sandbox_storage_autoload; +float autocvar_g_sandbox_editor_flood; +int autocvar_g_sandbox_editor_maxobjects; +int autocvar_g_sandbox_editor_free; +float autocvar_g_sandbox_editor_distance_spawn; +float autocvar_g_sandbox_editor_distance_edit; +float autocvar_g_sandbox_object_scale_min; +float autocvar_g_sandbox_object_scale_max; +float autocvar_g_sandbox_object_material_velocity_min; +float autocvar_g_sandbox_object_material_velocity_factor; + +float autosave_time; +void sandbox_Database_Load(); + +REGISTER_MUTATOR(sandbox, cvar("g_sandbox")) +{ + MUTATOR_ONADD + { + autosave_time = time + autocvar_g_sandbox_storage_autosave; // don't save the first server frame + if(autocvar_g_sandbox_storage_autoload) + sandbox_Database_Load(); + } +} + +const float MAX_STORAGE_ATTACHMENTS = 16; +float object_count; +.float object_flood; +.entity object_attach; +.string material; + +.float touch_timer; +void sandbox_ObjectFunction_Touch(entity this, entity toucher) +{ + // apply material impact effects + + if(!this.material) + return; + if(this.touch_timer > time) + return; // don't execute each frame + this.touch_timer = time + 0.1; + + // make particle count and sound volume depend on impact speed + float intensity; + intensity = vlen(this.velocity) + vlen(toucher.velocity); + if(intensity) // avoid divisions by 0 + intensity /= 2; // average the two velocities + if (!(intensity >= autocvar_g_sandbox_object_material_velocity_min)) + return; // impact not strong enough to do anything + // now offset intensity and apply it to the effects + intensity -= autocvar_g_sandbox_object_material_velocity_min; // start from minimum velocity, not actual velocity + intensity = bound(0, intensity * autocvar_g_sandbox_object_material_velocity_factor, 1); + + _sound(this, CH_TRIGGER, strcat("object/impact_", this.material, "_", ftos(ceil(random() * 5)) , ".wav"), VOL_BASE * intensity, ATTEN_NORM); + Send_Effect_(strcat("impact_", this.material), this.origin, '0 0 0', ceil(intensity * 10)); // allow a count from 1 to 10 +} + +void sandbox_ObjectFunction_Think(entity this) +{ + // decide if and how this object can be grabbed + if(autocvar_g_sandbox_readonly) + this.grab = 0; // no grabbing + else if(autocvar_g_sandbox_editor_free < 2 && this.crypto_idfp) + this.grab = 1; // owner only + else + this.grab = 3; // anyone + + // Object owner is stored via player UID, but we also need the owner as an entity (if the player is available on the server). + // Therefore, scan for all players, and update the owner as long as the player is present. We must always do this, + // since if the owning player disconnects, the object's owner should also be reset. + + // bots can't have objects + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( + if(this.crypto_idfp == it.crypto_idfp) + { + this.realowner = it; + break; + } + this.realowner = NULL; + )); + + this.nextthink = time; + + CSQCMODEL_AUTOUPDATE(this); +} + +.float old_solid, old_movetype; +entity sandbox_ObjectEdit_Get(entity this, float permissions) +{ + // Returns the traced entity if the player can edit it, and NULL if not. + // If permissions if false, the object is returned regardless of editing rights. + // Attached objects are SOLID_NOT and do not get traced. + + crosshair_trace_plusvisibletriggers(this); + if(vdist(this.origin - trace_ent.origin, >, autocvar_g_sandbox_editor_distance_edit)) + return NULL; // out of trace range + if(trace_ent.classname != "object") + return NULL; // entity is not an object + if(!permissions) + return trace_ent; // don't check permissions, anyone can edit this object + if(trace_ent.crypto_idfp == "") + return trace_ent; // the player who spawned this object did not have an UID, so anyone can edit it + if (!(trace_ent.realowner != this && autocvar_g_sandbox_editor_free < 2)) + return trace_ent; // object does not belong to the player, and players can only edit their own objects on this server + return NULL; +} + +void sandbox_ObjectEdit_Scale(entity e, float f) +{ + e.scale = f; + if(e.scale) + { + e.scale = bound(autocvar_g_sandbox_object_scale_min, e.scale, autocvar_g_sandbox_object_scale_max); + _setmodel(e, e.model); // reset mins and maxs based on mesh + setsize(e, e.mins * e.scale, e.maxs * e.scale); // adapt bounding box size to model size + } +} + +void sandbox_ObjectAttach_Remove(entity e); +void sandbox_ObjectAttach_Set(entity e, entity parent, string s) +{ + // attaches e to parent on string s + + // we can't attach to an attachment, for obvious reasons + sandbox_ObjectAttach_Remove(e); + + e.old_solid = e.solid; // persist solidity + e.old_movetype = e.move_movetype; // persist physics + set_movetype(e, MOVETYPE_FOLLOW); + e.solid = SOLID_NOT; + e.takedamage = DAMAGE_NO; + + setattachment(e, parent, s); + e.owner = parent; +} + +void sandbox_ObjectAttach_Remove(entity e) +{ + // detaches any object attached to e + + IL_EACH(g_sandbox_objects, it.owner == e, + { + vector org = gettaginfo(it, 0); + setattachment(it, NULL, ""); + it.owner = NULL; + + // objects change origin and angles when detached, so apply previous position + setorigin(it, org); + it.angles = e.angles; // don't allow detached objects to spin or roll + + it.solid = it.old_solid; // restore persisted solidity + set_movetype(it, it.old_movetype); // restore persisted physics + it.takedamage = DAMAGE_AIM; + }); +} + +entity sandbox_ObjectSpawn(entity this, float database) +{ + // spawn a new object with default properties + + entity e = new(object); + IL_PUSH(g_sandbox_objects, e); + e.takedamage = DAMAGE_AIM; + e.damageforcescale = 1; + e.solid = SOLID_BBOX; // SOLID_BSP would be best, but can lag the server badly + set_movetype(e, MOVETYPE_TOSS); + e.frame = 0; + e.skin = 0; + e.material = string_null; + settouch(e, sandbox_ObjectFunction_Touch); + setthink(e, sandbox_ObjectFunction_Think); + e.nextthink = time; + //e.effects |= EF_SELECTABLE; // don't do this all the time, maybe just when editing objects? + + if(!database) + { + // set the object's owner via player UID + // if the player does not have an UID, the owner cannot be stored and his objects may be edited by anyone + if(this.crypto_idfp != "") + e.crypto_idfp = strzone(this.crypto_idfp); + else + print_to(this, "^1SANDBOX - WARNING: ^7You spawned an object, but lack a player UID. ^1Your objects are not secured and can be edited by any player!"); + + // set public object information + e.netname = strzone(this.netname); // name of the owner + e.message = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // creation time + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); // last editing time + + // set origin and direction based on player position and view angle + makevectors(this.v_angle); + WarpZone_TraceLine(this.origin + this.view_ofs, this.origin + this.view_ofs + v_forward * autocvar_g_sandbox_editor_distance_spawn, MOVE_NORMAL, this); + setorigin(e, trace_endpos); + e.angles_y = this.v_angle.y; + } + + CSQCMODEL_AUTOINIT(e); + + object_count += 1; + return e; +} + +void sandbox_ObjectRemove(entity e) +{ + sandbox_ObjectAttach_Remove(e); // detach child objects + + // 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, LAMBDA(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; } + delete(e); + e = NULL; + + object_count -= 1; +} + +string port_string[MAX_STORAGE_ATTACHMENTS]; // fteqcc crashes if this isn't defined as a global + +string sandbox_ObjectPort_Save(entity e, bool database) +{ + // save object properties, and return them as a string + int o = 0; + + // order doesn't really matter, as we're writing the file fresh + IL_EACH(g_sandbox_objects, it == e || it.owner == e, LAMBDA( + // the main object needs to be first in the array [0] with attached objects following + int slot, physics, solidity; + if(it == e) // this is the main object, place it first + { + slot = 0; + solidity = it.solid; // applied solidity is normal solidity for children + physics = it.move_movetype; // applied physics are normal physics for parents + } + else if(it.owner == e) // child object, list them in order + { + o += 1; // children start from 1 + slot = o; + solidity = it.old_solid; // persisted solidity is normal solidity for children + physics = it.old_movetype; // persisted physics are normal physics for children + gettaginfo(it.owner, it.tag_index); // get the name of the tag our object is attached to, used further below + } + else + continue; + + // ---------------- OBJECT PROPERTY STORAGE: SAVE ---------------- + if(slot) + { + // properties stored only for child objects + if(gettaginfo_name) + port_string[slot] = strcat(port_string[slot], "\"", gettaginfo_name, "\" "); + else + port_string[slot] = strcat(port_string[slot], "\"\" "); // none + } + else + { + // properties stored only for parent objects + if(database) + { + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.origin), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.angles), " "); + } + } + // properties stored for all objects + port_string[slot] = strcat(port_string[slot], "\"", it.model, "\" "); + port_string[slot] = strcat(port_string[slot], ftos(it.skin), " "); + port_string[slot] = strcat(port_string[slot], ftos(it.alpha), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.colormod), " "); + port_string[slot] = strcat(port_string[slot], sprintf("\"%.9v\"", it.glowmod), " "); + port_string[slot] = strcat(port_string[slot], ftos(it.frame), " "); + port_string[slot] = strcat(port_string[slot], ftos(it.scale), " "); + port_string[slot] = strcat(port_string[slot], ftos(solidity), " "); + port_string[slot] = strcat(port_string[slot], ftos(physics), " "); + port_string[slot] = strcat(port_string[slot], ftos(it.damageforcescale), " "); + if(it.material) + port_string[slot] = strcat(port_string[slot], "\"", it.material, "\" "); + else + port_string[slot] = strcat(port_string[slot], "\"\" "); // none + if(database) + { + // properties stored only for the database + if(it.crypto_idfp) + port_string[slot] = strcat(port_string[slot], "\"", it.crypto_idfp, "\" "); + else + port_string[slot] = strcat(port_string[slot], "\"\" "); // none + port_string[slot] = strcat(port_string[slot], "\"", e.netname, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message, "\" "); + port_string[slot] = strcat(port_string[slot], "\"", e.message2, "\" "); + } + )); + + // now apply the array to a simple string, with the ; symbol separating objects + string s = ""; + for(int j = 0; j <= MAX_STORAGE_ATTACHMENTS; ++j) + { + if(port_string[j]) + s = strcat(s, port_string[j], "; "); + port_string[j] = string_null; // fully clear the string + } + + return s; +} + +entity sandbox_ObjectPort_Load(entity this, string s, float database) +{ + // load object properties, and spawn a new object with them + float n, i; + entity e = NULL, parent = NULL; + + // separate objects between the ; symbols + n = tokenizebyseparator(s, "; "); + for(i = 0; i < n; ++i) + port_string[i] = argv(i); + + // now separate and apply the properties of each object + for(i = 0; i < n; ++i) + { + float argv_num; + string tagname = string_null; + argv_num = 0; + tokenize_console(port_string[i]); + e = sandbox_ObjectSpawn(this, database); + + // ---------------- OBJECT PROPERTY STORAGE: LOAD ---------------- + if(i) + { + // properties stored only for child objects + if(argv(argv_num) != "") tagname = argv(argv_num); else tagname = string_null; ++argv_num; + } + 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; + } + 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; + 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; + 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; + } + + // attach last + if(i) + sandbox_ObjectAttach_Set(e, parent, tagname); + } + + for(i = 0; i <= MAX_STORAGE_ATTACHMENTS; ++i) + port_string[i] = string_null; // fully clear the string + + return e; +} + +void sandbox_Database_Save() +{ + // saves all objects to the database file + string file_name; + float file_get; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_WRITE); + fputs(file_get, strcat("// sandbox storage \"", autocvar_g_sandbox_storage_name, "\" for map \"", GetMapname(), "\" last updated ", strftime(true, "%d-%m-%Y %H:%M:%S"))); + fputs(file_get, strcat(" containing ", ftos(object_count), " objects\n")); + + IL_EACH(g_sandbox_objects, !it.owner, // attached objects are persisted separately, ignore them here + { + // use a line of text for each object, listing all properties + fputs(file_get, strcat(sandbox_ObjectPort_Save(it, true), "\n")); + }); + fclose(file_get); +} + +void sandbox_Database_Load() +{ + // loads all objects from the database file + string file_read, file_name; + float file_get, i; + + file_name = strcat("sandbox/storage_", autocvar_g_sandbox_storage_name, "_", GetMapname(), ".txt"); + file_get = fopen(file_name, FILE_READ); + if(file_get < 0) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7could not find storage file ^3", file_name, "^7, no objects were loaded\n")); + } + else + { + for (;;) + { + file_read = fgets(file_get); + if(file_read == "") + break; + if(substring(file_read, 0, 2) == "//") + continue; + if(substring(file_read, 0, 1) == "#") + continue; + + entity e; + e = sandbox_ObjectPort_Load(NULL, file_read, true); + + if(e.material) + { + // since objects are being loaded for the first time, precache material sounds for each + for (i = 1; i <= 5; i++) // 5 sounds in total + precache_sound(strcat("object/impact_", e.material, "_", ftos(i), ".wav")); + } + } + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7successfully loaded storage file ^3", file_name, "\n")); + } + fclose(file_get); +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_ParseClientCommand) +{ + if(MUTATOR_RETURNVALUE) // command was already handled? + return; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if(cmd_name == "g_sandbox") + { + if(autocvar_g_sandbox_readonly) + { + print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active, but in read-only mode. Sandbox commands cannot be used"); + return true; + } + if(cmd_argc < 2) + { + print_to(player, "^2SANDBOX - INFO: ^7Sandbox mode is active. For usage information, type 'sandbox help'"); + return true; + } + + switch(argv(1)) + { + entity e; + int j; + string s; + + // ---------------- COMMAND: HELP ---------------- + case "help": + print_to(player, "You can use the following sandbox commands:"); + print_to(player, "^7\"^2object_spawn ^3models/foo/bar.md3^7\" spawns a new object in front of the player, and gives it the specified model"); + print_to(player, "^7\"^2object_remove^7\" removes the object the player is looking at. Players can only remove their own objects"); + print_to(player, "^7\"^2object_duplicate ^3value^7\" duplicates the object, if the player has copying rights over the original"); + print_to(player, "^3copy value ^7- copies the properties of the object to the specified client cvar"); + print_to(player, "^3paste value ^7- spawns an object with the given properties. Properties or cvars must be specified as follows; eg1: \"0 1 2 ...\", eg2: \"$cl_cvar\""); + print_to(player, "^7\"^2object_attach ^3property value^7\" attaches one object to another. Players can only attach their own objects"); + print_to(player, "^3get ^7- selects the object you are facing as the object to be attached"); + print_to(player, "^3set value ^7- attaches the previously selected object to the object you are facing, on the specified bone"); + print_to(player, "^3remove ^7- detaches all objects from the object you are facing"); + print_to(player, "^7\"^2object_edit ^3property value^7\" edits the given property of the object. Players can only edit their own objects"); + print_to(player, "^3skin value ^7- changes the skin of the object"); + print_to(player, "^3alpha value ^7- sets object transparency"); + print_to(player, "^3colormod \"value_x value_y value_z\" ^7- main object color"); + print_to(player, "^3glowmod \"value_x value_y value_z\" ^7- glow object color"); + print_to(player, "^3frame value ^7- object animation frame, for self-animated models"); + print_to(player, "^3scale value ^7- changes object scale. 0.5 is half size and 2 is double size"); + print_to(player, "^3solidity value ^7- object collisions, 0 = non-solid, 1 = solid"); + print_to(player, "^3physics value ^7- object physics, 0 = static, 1 = movable, 2 = physical"); + print_to(player, "^3force value ^7- amount of force applied to objects that are shot"); + print_to(player, "^3material value ^7- sets the material of the object. Default materials are: metal, stone, wood, flesh"); + print_to(player, "^7\"^2object_claim^7\" sets the player as the owner of the object, if he has the right to edit it"); + print_to(player, "^7\"^2object_info ^3value^7\" shows public information about the object"); + print_to(player, "^3object ^7- prints general information about the object, such as owner and creation / editing date"); + print_to(player, "^3mesh ^7- prints information about the object's mesh, including skeletal bones"); + print_to(player, "^3attachments ^7- prints information about the object's attachments"); + print_to(player, "^7The ^1drag object ^7key can be used to grab and carry objects. Players can only grab their own objects"); + return true; + + // ---------------- COMMAND: OBJECT, SPAWN ---------------- + case "object_spawn": + if(time < player.object_flood) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + player.object_flood = time + autocvar_g_sandbox_editor_flood; + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + if(cmd_argc < 3) + { + print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object without specifying a model. Please specify the path to your model file after the 'object_spawn' command"); + return true; + } + if (!(fexists(argv(2)))) + { + print_to(player, "^1SANDBOX - WARNING: ^7Attempted to spawn an object with a non-existent model. Make sure the path to your model file is correct"); + return true; + } + + e = sandbox_ObjectSpawn(player, false); + _setmodel(e, argv(2)); + + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " spawned an object at origin ^3", vtos(e.origin), "\n")); + return true; + + // ---------------- COMMAND: OBJECT, REMOVE ---------------- + case "object_remove": + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " removed an object at origin ^3", vtos(e.origin), "\n")); + sandbox_ObjectRemove(e); + return true; + } + + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be removed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, DUPLICATE ---------------- + case "object_duplicate": + switch(argv(2)) + { + case "copy": + // copies customizable properties of the selected object to the clipboard cvar + e = sandbox_ObjectEdit_Get(player, autocvar_g_sandbox_editor_free); // can we copy objects we can't edit? + if(e != NULL) + { + s = sandbox_ObjectPort_Save(e, false); + s = strreplace("\"", "\\\"", s); + stuffcmd(player, strcat("set ", argv(3), " \"", s, "\"")); + + print_to(player, "^2SANDBOX - INFO: ^7Object copied to clipboard"); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be copied. Make sure you are facing an object that you have copy rights over"); + return true; + + case "paste": + // spawns a new object using the properties in the player's clipboard cvar + if(time < player.object_flood) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Flood protection active. Please wait ^3", ftos(player.object_flood - time), " ^7seconds beofore spawning another object")); + return true; + } + player.object_flood = time + autocvar_g_sandbox_editor_flood; + if(argv(3) == "") // no object in clipboard + { + print_to(player, "^1SANDBOX - WARNING: ^7No object in clipboard. You must copy an object before you can paste it"); + return true; + } + if(object_count >= autocvar_g_sandbox_editor_maxobjects) + { + print_to(player, strcat("^1SANDBOX - WARNING: ^7Cannot spawn any more objects. Up to ^3", ftos(autocvar_g_sandbox_editor_maxobjects), " ^7objects may exist at a time")); + return true; + } + e = sandbox_ObjectPort_Load(player, argv(3), false); + + print_to(player, "^2SANDBOX - INFO: ^7Object pasted successfully"); + if(autocvar_g_sandbox_info > 0) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " pasted an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, ATTACH ---------------- + case "object_attach": + switch(argv(2)) + { + case "get": + // select e as the object as meant to be attached + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + player.object_attach = e; + print_to(player, "^2SANDBOX - INFO: ^7Object selected for attachment"); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be selected for attachment. Make sure you are facing an object that you have edit rights over"); + return true; + case "set": + if(player.object_attach == NULL) + { + print_to(player, "^1SANDBOX - WARNING: ^7No object selected for attachment. Please select an object to be attached first."); + return true; + } + + // attaches the previously selected object to e + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + sandbox_ObjectAttach_Set(player.object_attach, e, argv(3)); + player.object_attach = NULL; // object was attached, no longer keep it scheduled for attachment + print_to(player, "^2SANDBOX - INFO: ^7Object attached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " attached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be attached to the parent. Make sure you are facing an object that you have edit rights over"); + return true; + case "remove": + // removes e if it was attached + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + sandbox_ObjectAttach_Remove(e); + print_to(player, "^2SANDBOX - INFO: ^7Child objects detached successfully"); + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " detached objects at origin ^3", vtos(e.origin), "\n")); + return true; + } + print_to(player, "^1SANDBOX - WARNING: ^7Child objects could not be detached. Make sure you are facing an object that you have edit rights over"); + return true; + } + return true; + + // ---------------- COMMAND: OBJECT, EDIT ---------------- + case "object_edit": + if(argv(2) == "") + { + print_to(player, "^1SANDBOX - WARNING: ^7Too few parameters. You must specify a property to edit"); + return true; + } + + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + switch(argv(2)) + { + case "skin": + e.skin = stof(argv(3)); + break; + case "alpha": + e.alpha = stof(argv(3)); + break; + case "color_main": + e.colormod = stov(argv(3)); + break; + case "color_glow": + e.glowmod = stov(argv(3)); + break; + case "frame": + e.frame = stof(argv(3)); + break; + case "scale": + sandbox_ObjectEdit_Scale(e, stof(argv(3))); + break; + case "solidity": + switch(argv(3)) + { + case "0": // non-solid + e.solid = SOLID_TRIGGER; + break; + case "1": // solid + e.solid = SOLID_BBOX; + break; + default: + break; + } + case "physics": + switch(argv(3)) + { + case "0": // static + set_movetype(e, MOVETYPE_NONE); + break; + case "1": // movable + set_movetype(e, MOVETYPE_TOSS); + break; + case "2": // physical + set_movetype(e, MOVETYPE_PHYSICS); + break; + default: + break; + } + break; + case "force": + e.damageforcescale = stof(argv(3)); + break; + case "material": + if(e.material) strunzone(e.material); + if(argv(3)) + { + for (j = 1; j <= 5; j++) // precache material sounds, 5 in total + precache_sound(strcat("object/impact_", argv(3), "_", ftos(j), ".wav")); + e.material = strzone(argv(3)); + } + else + e.material = string_null; // no material + break; + default: + print_to(player, "^1SANDBOX - WARNING: ^7Invalid object property. For usage information, type 'sandbox help'"); + return true; + } + + // update last editing time + if(e.message2) strunzone(e.message2); + e.message2 = strzone(strftime(true, "%d-%m-%Y %H:%M:%S")); + + if(autocvar_g_sandbox_info > 1) + LOG_INFO(strcat("^3SANDBOX - SERVER: ^7", player.netname, " edited property ^3", argv(2), " ^7of an object at origin ^3", vtos(e.origin), "\n")); + return true; + } + + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be edited. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, CLAIM ---------------- + case "object_claim": + // if the player can edit an object but is not its owner, this can be used to claim that object + if(player.crypto_idfp == "") + { + print_to(player, "^1SANDBOX - WARNING: ^7You do not have a player UID, and cannot claim objects"); + return true; + } + e = sandbox_ObjectEdit_Get(player, true); + if(e != NULL) + { + // update the owner's name + // Do this before checking if you're already the owner and skipping if such, so we + // 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); + print_to(player, "^2SANDBOX - INFO: ^7Object owner name updated"); + } + + if(e.crypto_idfp == player.crypto_idfp) + { + print_to(player, "^2SANDBOX - INFO: ^7Object is already yours, nothing to claim"); + return true; + } + + if(e.crypto_idfp) strunzone(e.crypto_idfp); + e.crypto_idfp = strzone(player.crypto_idfp); + + print_to(player, "^2SANDBOX - INFO: ^7Object claimed successfully"); + } + print_to(player, "^1SANDBOX - WARNING: ^7Object could not be claimed. Make sure you are facing an object that you have edit rights over"); + return true; + + // ---------------- COMMAND: OBJECT, INFO ---------------- + case "object_info": + // prints public information about the object to the player + e = sandbox_ObjectEdit_Get(player, false); + if(e != NULL) + { + switch(argv(2)) + { + case "object": + print_to(player, strcat("^2SANDBOX - INFO: ^7Object is owned by \"^7", e.netname, "^7\", created \"^3", e.message, "^7\", last edited \"^3", e.message2, "^7\"")); + return true; + case "mesh": + s = ""; + FOR_EACH_TAG(e) + s = strcat(s, "^7\"^5", gettaginfo_name, "^7\", "); + print_to(player, strcat("^2SANDBOX - INFO: ^7Object mesh is \"^3", e.model, "^7\" at animation frame ^3", ftos(e.frame), " ^7containing the following tags: ", s)); + return true; + case "attachments": + // this should show the same info as 'mesh' but for attachments + s = ""; + j = 0; + IL_EACH(g_sandbox_objects, it.owner == e, + { + ++j; // start from 1 + gettaginfo(e, it.tag_index); + s = strcat(s, "^1attachment ", ftos(j), "^7 has mesh \"^3", it.model, "^7\" at animation frame ^3", ftos(it.frame)); + s = strcat(s, "^7 and is attached to bone \"^5", gettaginfo_name, "^7\", "); + }); + if(j) // object contains attachments + print_to(player, strcat("^2SANDBOX - INFO: ^7Object contains the following ^1", ftos(j), "^7 attachment(s): ", s)); + else + print_to(player, "^2SANDBOX - INFO: ^7Object contains no attachments"); + return true; + } + } + print_to(player, "^1SANDBOX - WARNING: ^7No information could be found. Make sure you are facing an object"); + return true; + + // ---------------- COMMAND: DEFAULT ---------------- + default: + print_to(player, "Invalid command. For usage information, type 'sandbox help'"); + return true; + } + } +} + +MUTATOR_HOOKFUNCTION(sandbox, SV_StartFrame) +{ + if(!autocvar_g_sandbox_storage_autosave) + return; + if(time < autosave_time) + return; + autosave_time = time + autocvar_g_sandbox_storage_autosave; + + sandbox_Database_Save(); + + return true; +} diff --git a/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh new file mode 100644 index 0000000000..f6a0afdf40 --- /dev/null +++ b/qcsrc/common/mutators/mutator/sandbox/sv_sandbox.qh @@ -0,0 +1,4 @@ +#pragma once + +IntrusiveList g_sandbox_objects; +STATIC_INIT(g_sandbox_objects) { g_sandbox_objects = IL_NEW(); } diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc index b7d3af7f4d..fe3a6ebc5d 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh index 5f53e95c88..b34f8f8f16 100644 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc b/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc deleted file mode 100644 index f88a768a21..0000000000 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "spawn_near_teammate.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc b/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc deleted file mode 100644 index b16a6c9acf..0000000000 --- a/qcsrc/common/mutators/mutator/spawn_near_teammate/spawn_near_teammate.qc +++ /dev/null @@ -1,189 +0,0 @@ -#ifdef IMPLEMENTATION - -float autocvar_g_spawn_near_teammate_distance; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; -float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; -int autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; -bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; - -REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate")); - -.entity msnt_lookat; - -.float msnt_timer; -.vector msnt_deathloc; - -.float cvar_cl_spawn_near_teammate; - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) -{ - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - vector spawn_score = M_ARGV(2, vector); - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) - return; - - spawn_spot.msnt_lookat = NULL; - - if(!teamplay) - return; - - RandomSelection_Init(); - FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA( - if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance)) - continue; - if(vdist(spawn_spot.origin - it.origin, <, 48)) - continue; - if(!checkpvs(spawn_spot.origin, it)) - continue; - RandomSelection_Add(it, 0, string_null, 1, 1); - )); - - if(RandomSelection_chosen_ent) - { - spawn_spot.msnt_lookat = RandomSelection_chosen_ent; - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; - } - else if(player.team == spawn_spot.team) - spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate - - M_ARGV(2, vector) = spawn_score; -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) -{ - if(!teamplay) { return; } - entity player = M_ARGV(0, entity); - entity spawn_spot = M_ARGV(1, entity); - - int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0; - FOREACH_CLIENT(IS_PLAYER(it), - { - switch(it.team) - { - case NUM_TEAM_1: ++num_red; break; - case NUM_TEAM_2: ++num_blue; break; - case NUM_TEAM_3: ++num_yellow; break; - case NUM_TEAM_4: ++num_pink; break; - } - }); - - if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1) - return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage! - - // Note: when entering this, fixangle is already set. - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) - player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; - - entity best_mate = NULL; - vector best_spot = '0 0 0'; - float pc = 0, best_dist = 0, dist = 0; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if((autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health >= 0 && it.health >= autocvar_g_balance_health_regenstable) || autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health == 0) - if(!IS_DEAD(it)) - if(it.msnt_timer < time) - if(SAME_TEAM(player, it)) - if(time > it.spawnshieldtime) // spawn shielding - if(STAT(FROZEN, it) == 0) - if(it != player) - { - tracebox(it.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - '0 0 100', MOVE_NOMONSTERS, it); - if(trace_fraction != 1.0) - if(!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) - { - pc = pointcontents(trace_endpos + '0 0 1'); - if(pc == CONTENT_EMPTY) - { - if(vdist(it.velocity, >, 5)) - fixedmakevectors(vectoangles(it.velocity)); - else - fixedmakevectors(it.angles); - - for(pc = 0; pc < 4; ++pc) // test 4 diffrent spots close to mate - { - switch(pc) - { - case 0: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128, MOVE_NOMONSTERS, it); - break; - case 1: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 , MOVE_NOMONSTERS, it); - break; - case 2: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin + v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); - break; - case 3: - tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_right * 128 - v_forward * 64, MOVE_NOMONSTERS, it); - break; - //case 4: - //tracebox(it.origin , STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), it.origin - v_forward * 128, MOVE_NOMONSTERS, it); - //break; - } - - if(trace_fraction == 1.0) - { - traceline(trace_endpos + '0 0 4', trace_endpos - '0 0 100', MOVE_NOMONSTERS, it); - if(trace_fraction != 1.0) - { - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - { - dist = vlen(trace_endpos - player.msnt_deathloc); - if(dist < best_dist || best_dist == 0) - { - best_dist = dist; - best_spot = trace_endpos; - best_mate = it; - } - } - else - { - setorigin(player, trace_endpos); - player.angles = it.angles; - player.angles_z = 0; // never spawn tilted even if the spot says to - it.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - return; - } - } - } - } - } - } - } - )); - - if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) - if(best_dist) - { - setorigin(player, best_spot); - player.angles = best_mate.angles; - player.angles_z = 0; // never spawn tilted even if the spot says to - best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; - } - } - else if(spawn_spot.msnt_lookat) - { - player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin); - player.angles_x = -player.angles.x; - player.angles_z = 0; // never spawn tilted even if the spot says to - /* - sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); - sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n"); - sprint(player, "angles: ", vtos(player.angles), "\n"); - */ - } -} - -MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerDies) -{ - entity frag_target = M_ARGV(0, entity); - - frag_target.msnt_deathloc = frag_target.origin; -} - -REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate"); - -#endif 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 new file mode 100644 index 0000000000..a0017f94df --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qc @@ -0,0 +1,223 @@ +#include "sv_spawn_near_teammate.qh" + +const float FLOAT_MAX = 340282346638528859811704183484516925440.0f; + +float autocvar_g_spawn_near_teammate_distance; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint; +int autocvar_g_spawn_near_teammate_ignore_spawnpoint_max; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; +float autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; +bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_check_health; +bool autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath; + +REGISTER_MUTATOR(spawn_near_teammate, cvar("g_spawn_near_teammate")); + +.entity msnt_lookat; + +.float msnt_timer; + +.float cvar_cl_spawn_near_teammate; + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, Spawn_Score) +{ + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + vector spawn_score = M_ARGV(2, vector); + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) + return; + + spawn_spot.msnt_lookat = NULL; + + if(!teamplay) + return; + + RandomSelection_Init(); + FOREACH_CLIENT(IS_PLAYER(it) && it != player && SAME_TEAM(it, player) && !IS_DEAD(it), LAMBDA( + if(vdist(spawn_spot.origin - it.origin, >, autocvar_g_spawn_near_teammate_distance)) + continue; + if(vdist(spawn_spot.origin - it.origin, <, 48)) + continue; + if(!checkpvs(spawn_spot.origin, it)) + continue; + RandomSelection_AddEnt(it, 1, 1); + )); + + if(RandomSelection_chosen_ent) + { + spawn_spot.msnt_lookat = RandomSelection_chosen_ent; + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_FOUND; + } + else if(player.team == spawn_spot.team) + spawn_score.x += SPAWN_PRIO_NEAR_TEAMMATE_SAMETEAM; // prefer same team, if we can't find a spawn near teammate + + M_ARGV(2, vector) = spawn_score; +} + +MUTATOR_HOOKFUNCTION(spawn_near_teammate, PlayerSpawn) +{ + if(!teamplay) { return; } + entity player = M_ARGV(0, entity); + entity spawn_spot = M_ARGV(1, entity); + + int num_red = 0, num_blue = 0, num_yellow = 0, num_pink = 0; + FOREACH_CLIENT(IS_PLAYER(it), + { + switch(it.team) + { + case NUM_TEAM_1: ++num_red; break; + case NUM_TEAM_2: ++num_blue; break; + case NUM_TEAM_3: ++num_yellow; break; + case NUM_TEAM_4: ++num_pink; break; + } + }); + + if(num_red == 1 || num_blue == 1 || num_yellow == 1 || num_pink == 1) + return; // at least 1 team has only 1 player, let's not give the bigger team too much of an advantage! + + // Note: when entering this, fixangle is already set. + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint == 1 || (autocvar_g_spawn_near_teammate_ignore_spawnpoint == 2 && player.cvar_cl_spawn_near_teammate)) + { + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death) + player.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay_death; + + entity best_mate = NULL; + vector best_pos = '0 0 0'; + float best_dist2 = FLOAT_MAX; + int tested = 0; + FOREACH_CLIENT_RANDOM(IS_PLAYER(it), LAMBDA( + 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 (IS_DEAD(it)) continue; + if (time < it.msnt_timer) continue; + if (time < it.spawnshieldtime) continue; + if (forbidWeaponUse(it)) continue; + if (it == player) continue; + + tested++; // i consider a teammate to be available when he passes the checks above + + vector horiz_vel = vec2(it.velocity); + // when walking slowly sideways, we assume the player wants a clear shot ahead - spawn behind him according to where he's looking + // when running fast, spawn behind him according to his direction of movement to prevent colliding with the newly spawned player + if (vdist(horiz_vel, >, autocvar_sv_maxspeed + 50)) + fixedmakevectors(vectoangles(horiz_vel)); + else + fixedmakevectors(it.angles); // .angles is the angle of the model - usually/always 0 pitch + + // test different spots close to mate - trace upwards so it works on uneven surfaces + // don't spawn in front of player or directly behind to avoid players shooting each other + // test the potential spots in pairs (first pair is better than second and so on) but don't prefer one side + RandomSelection_Init(); + for(int i = 0; i < 6; ++i) + { + switch(i) + { + case 0: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 + v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it); + break; + case 1: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 64 - v_right * 128 + v_up * 64, MOVE_NOMONSTERS, it); + break; + case 2: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin + v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it); + break; + case 3: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_right * 192 + v_up * 64, MOVE_NOMONSTERS, it); + break; + case 4: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 + v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it); + break; + case 5: + tracebox(it.origin, STAT(PL_MIN, player), STAT(PL_MAX, player), it.origin - v_forward * 128 - v_right * 64 + v_up * 64, MOVE_NOMONSTERS, it); + break; + } + + vector horizontal_trace_endpos = trace_endpos; + //te_lightning1(NULL, it.origin, horizontal_trace_endpos); + if (trace_fraction != 1.0) goto skip; + + // 400 is about the height of a typical laser jump (in overkill) + // not traceline because we need space for the whole player, not just his origin + tracebox(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), horizontal_trace_endpos - '0 0 400', MOVE_NORMAL, it); + vector vectical_trace_endpos = trace_endpos; + //te_lightning1(NULL, horizontal_trace_endpos, vectical_trace_endpos); + if (trace_startsolid) goto skip; // inside another player + if (trace_fraction == 1.0) goto skip; // above void or too high + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) goto skip; + if (pointcontents(vectical_trace_endpos) != CONTENT_EMPTY) goto skip; // no lava or slime (or water which i assume would be annoying anyway) + if (tracebox_hits_trigger_hurt(horizontal_trace_endpos, STAT(PL_MIN, player), STAT(PL_MAX, player), vectical_trace_endpos)) goto skip; + + // make sure the spawned player will have floor ahead (or at least a wall - he shouldn't fall as soon as he starts moving) + vector floor_test_start = vectical_trace_endpos + v_up * STAT(PL_MAX, player).z + v_forward * STAT(PL_MAX, player).x; // top front of player's bbox - highest point we know is not inside solid + traceline(floor_test_start, floor_test_start + v_forward * 100 - v_up * 128, MOVE_NOMONSTERS, it); + //te_beam(NULL, floor_test_start, trace_endpos); + if (trace_fraction == 1.0) goto skip; + + if (autocvar_g_nades) { + bool nade_in_range = false; + IL_EACH(g_projectiles, it.classname == "nade", + { + if (vdist(it.origin - vectical_trace_endpos, <, autocvar_g_nades_nade_radius)) { + nade_in_range = true; + goto skip; + } + }); + if (nade_in_range) goto skip; + } + + // here, we know we found a good spot + RandomSelection_Add(it, 0, string_null, vectical_trace_endpos, 1, 1); + //te_lightning1(NULL, vectical_trace_endpos, vectical_trace_endpos + v_forward * 10); + +LABEL(skip) + if (i % 2 == 1 && RandomSelection_chosen_ent) + { + if (autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + { + float dist2 = vlen2(RandomSelection_chosen_ent.origin - player.death_origin); + if (dist2 < best_dist2) + { + best_dist2 = dist2; + best_pos = RandomSelection_chosen_vec; + best_mate = RandomSelection_chosen_ent; + } + } + else + { + setorigin(player, RandomSelection_chosen_vec); + player.angles = RandomSelection_chosen_ent.angles; + player.angles_z = 0; // never spawn tilted even if the spot says to + RandomSelection_chosen_ent.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + return; + } + break; // don't test the other spots near this teammate, go to the next one + } + } + )); + + if(autocvar_g_spawn_near_teammate_ignore_spawnpoint_closetodeath) + if(best_mate) + { + setorigin(player, best_pos); + player.angles = best_mate.angles; + player.angles_z = 0; // never spawn tilted even if the spot says to + best_mate.msnt_timer = time + autocvar_g_spawn_near_teammate_ignore_spawnpoint_delay; + } + } + else if(spawn_spot.msnt_lookat) + { + player.angles = vectoangles(spawn_spot.msnt_lookat.origin - player.origin); + player.angles_x = -player.angles.x; + player.angles_z = 0; // never spawn tilted even if the spot says to + /* + sprint(player, "You should be looking at ", spawn_spot.msnt_lookat.netname, "^7.\n"); + sprint(player, "distance: ", vtos(spawn_spot.msnt_lookat.origin - player.origin), "\n"); + sprint(player, "angles: ", vtos(player.angles), "\n"); + */ + } +} + +REPLICATE(cvar_cl_spawn_near_teammate, bool, "cl_spawn_near_teammate"); diff --git a/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/spawn_near_teammate/sv_spawn_near_teammate.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/superspec/_mod.inc b/qcsrc/common/mutators/mutator/superspec/_mod.inc index d5005242f2..262c7fcdbc 100644 --- a/qcsrc/common/mutators/mutator/superspec/_mod.inc +++ b/qcsrc/common/mutators/mutator/superspec/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/_mod.qh b/qcsrc/common/mutators/mutator/superspec/_mod.qh index b544ffc611..110087b674 100644 --- a/qcsrc/common/mutators/mutator/superspec/_mod.qh +++ b/qcsrc/common/mutators/mutator/superspec/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/superspec/module.inc b/qcsrc/common/mutators/mutator/superspec/module.inc deleted file mode 100644 index 8e0a998c2d..0000000000 --- a/qcsrc/common/mutators/mutator/superspec/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "superspec.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/superspec/superspec.qc b/qcsrc/common/mutators/mutator/superspec/superspec.qc deleted file mode 100644 index 786796866c..0000000000 --- a/qcsrc/common/mutators/mutator/superspec/superspec.qc +++ /dev/null @@ -1,459 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(superspec, cvar("g_superspectate")); - -#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" -#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false) - -const float ASF_STRENGTH = BIT(0); -const float ASF_SHIELD = BIT(1); -const float ASF_MEGA_AR = BIT(2); -const float ASF_MEGA_HP = BIT(3); -const float ASF_FLAG_GRAB = BIT(4); -const float ASF_OBSERVER_ONLY = BIT(5); -const float ASF_SHOWWHAT = BIT(6); -const float ASF_SSIM = BIT(7); -const float ASF_FOLLOWKILLER = BIT(8); -const float ASF_ALL = 0xFFFFFF; -.float autospec_flags; - -const float SSF_SILENT = 1; -const float SSF_VERBOSE = 2; -const float SSF_ITEMMSG = 4; -.float superspec_flags; - -.string superspec_itemfilter; //"classname1 classname2 ..." - -bool superspec_Spectate(entity this, entity targ) -{ - if(Spectate(this, targ) == 1) - TRANSMUTE(Spectator, this); - - return true; -} - -void superspec_save_client_conf(entity this) -{ - string fn = "superspec-local.options"; - float fh; - - if (!_ISLOCAL(this)) - { - if(this.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp)); - } - - fh = fopen(fn, FILE_WRITE); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing.\n"); - } - else - { - fputs(fh, _SSMAGIX); - fputs(fh, "\n"); - fputs(fh, ftos(this.autospec_flags)); - fputs(fh, "\n"); - fputs(fh, ftos(this.superspec_flags)); - fputs(fh, "\n"); - fputs(fh, this.superspec_itemfilter); - fputs(fh, "\n"); - fclose(fh); - } -} - -void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) -{ - sprint(_to, strcat(_con_title, _msg)); - - if(_to.superspec_flags & SSF_SILENT) - return; - - if(_spamlevel > 1) - if (!(_to.superspec_flags & SSF_VERBOSE)) - return; - - centerprint(_to, strcat(_center_title, _msg)); -} - -float superspec_filteritem(entity _for, entity _item) -{ - float i; - - if(_for.superspec_itemfilter == "") - return true; - - if(_for.superspec_itemfilter == "") - return true; - - float l = tokenize_console(_for.superspec_itemfilter); - for(i = 0; i < l; ++i) - { - if(argv(i) == _item.classname) - return true; - } - - return false; -} - -MUTATOR_HOOKFUNCTION(superspec, ItemTouch) -{ - entity item = M_ARGV(0, entity); - entity toucher = M_ARGV(1, entity); - - FOREACH_CLIENT(true, LAMBDA( - if(!IS_SPEC(it) && !IS_OBSERVER(it)) - continue; - if(it.superspec_flags & SSF_ITEMMSG) - if(superspec_filteritem(it, item)) - { - if(it.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1); - else - superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1); - if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher) - { - superspec_Spectate(it, toucher); - return MUT_ITEMTOUCH_CONTINUE; - } - } - - if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) || - (it.autospec_flags & ASF_STRENGTH && item.strength_finished) || - (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) || - (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) || - (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team")) - { - - if((it.enemy != toucher) || IS_OBSERVER(it)) - { - if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it)) - { - if(it.superspec_flags & SSF_VERBOSE) - superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2); - } - else - { - if(it.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2); - - superspec_Spectate(it, toucher); - } - } - } - )); - - return MUT_ITEMTOUCH_CONTINUE; -} - -MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) -{ -#define OPTIONINFO(flag,var,test,text,long,short) \ - var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ - var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") - - if(MUTATOR_RETURNVALUE) // command was already handled? - return; - - entity player = M_ARGV(0, entity); - string cmd_name = M_ARGV(1, string); - int cmd_argc = M_ARGV(2, int); - - if(IS_PLAYER(player)) - return; - - if(cmd_name == "superspec_itemfilter") - { - if(argv(1) == "help") - { - string _aspeco; - _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; - _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); - _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); - superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1); - } - else if(argv(1) == "clear") - { - if(player.superspec_itemfilter != "") - strunzone(player.superspec_itemfilter); - - player.superspec_itemfilter = ""; - } - else if(argv(1) == "show" || argv(1) == "") - { - if(player.superspec_itemfilter == "") - { - superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1); - return true; - } - float i; - float l = tokenize_console(player.superspec_itemfilter); - string _msg = ""; - for(i = 0; i < l; ++i) - _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); - //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); - - _msg = strcat(_msg,"\n"); - - superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1); - } - else - { - if(player.superspec_itemfilter != "") - strunzone(player.superspec_itemfilter); - - player.superspec_itemfilter = strzone(argv(1)); - } - - return true; - } - - if(cmd_name == "superspec") - { - string _aspeco; - - if(cmd_argc > 1) - { - float i, _bits = 0, _start = 1; - if(argv(1) == "help") - { - _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); - _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); - _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); - _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); - superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1); - return true; - } - - if(argv(1) == "clear") - { - player.superspec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - player.superspec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - player.superspec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; - if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; - if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; - } - } - } - - _aspeco = ""; - OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); - OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); - OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); - - superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1); - - return true; - } - -///////////////////// - - if(cmd_name == "autospec") - { - string _aspeco; - if(cmd_argc > 1) - { - if(argv(1) == "help") - { - _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; - _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); - _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); - _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); - _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); - _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); - _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); - _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); - _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); - _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); - _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); - superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1); - return true; - } - - float i, _bits = 0, _start = 1; - if(argv(1) == "clear") - { - player.autospec_flags = 0; - _start = 2; - } - - for(i = _start; i < cmd_argc; ++i) - { - if(argv(i) == "on" || argv(i) == "1") - { - player.autospec_flags |= _bits; - _bits = 0; - } - else if(argv(i) == "off" || argv(i) == "0") - { - if(_start == 1) - player.autospec_flags &= ~_bits; - - _bits = 0; - } - else - { - if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; - if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; - if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; - if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; - if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; - if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; - if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; - if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; - if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; - if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; - } - } - } - - _aspeco = ""; - OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); - OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); - - superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1); - return true; - } - - if(cmd_name == "followpowerup") - { - FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active powerup\n", 1); - return true; - } - - if(cmd_name == "followstrength") - { - FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active Strength\n", 1); - return true; - } - - if(cmd_name == "followshield") - { - FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it))); - - superspec_msg("", "", player, "No active Shield\n", 1); - return true; - } -#undef OPTIONINFO -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS"); -} - -MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators"); -} - -void superspec_hello(entity this) -{ - if(this.enemy.crypto_idfp == "") - Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); - - remove(this); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientConnect) -{ - entity player = M_ARGV(0, entity); - - if(!IS_REAL_CLIENT(player)) - return; - - string fn = "superspec-local.options"; - float fh; - - player.superspec_flags = SSF_VERBOSE; - player.superspec_itemfilter = ""; - - entity _hello = spawn(); - _hello.enemy = player; - setthink(_hello, superspec_hello); - _hello.nextthink = time + 5; - - if (!_ISLOCAL(player)) - { - if(player.crypto_idfp == "") - return; - - fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp)); - } - - fh = fopen(fn, FILE_READ); - if(fh < 0) - { - LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading.\n"); - } - else - { - string _magic = fgets(fh); - if(_magic != _SSMAGIX) - { - LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic\n"); - } - else - { - player.autospec_flags = stof(fgets(fh)); - player.superspec_flags = stof(fgets(fh)); - player.superspec_itemfilter = strzone(fgets(fh)); - } - fclose(fh); - } -} - -MUTATOR_HOOKFUNCTION(superspec, PlayerDies) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - - FOREACH_CLIENT(IS_SPEC(it), LAMBDA( - if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target) - { - if(it.autospec_flags & ASF_SHOWWHAT) - superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); - - superspec_Spectate(it, frag_attacker); - } - )); -} - -MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - superspec_save_client_conf(player); -} -#endif diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc new file mode 100644 index 0000000000..c423042a3c --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qc @@ -0,0 +1,459 @@ +#include "sv_superspec.qh" + +REGISTER_MUTATOR(superspec, cvar("g_superspectate")); + +#define _SSMAGIX "SUPERSPEC_OPTIONSFILE_V1" +#define _ISLOCAL(ent) ((edict_num(1) == (ent)) ? true : false) + +const float ASF_STRENGTH = BIT(0); +const float ASF_SHIELD = BIT(1); +const float ASF_MEGA_AR = BIT(2); +const float ASF_MEGA_HP = BIT(3); +const float ASF_FLAG_GRAB = BIT(4); +const float ASF_OBSERVER_ONLY = BIT(5); +const float ASF_SHOWWHAT = BIT(6); +const float ASF_SSIM = BIT(7); +const float ASF_FOLLOWKILLER = BIT(8); +const float ASF_ALL = 0xFFFFFF; +.float autospec_flags; + +const float SSF_SILENT = 1; +const float SSF_VERBOSE = 2; +const float SSF_ITEMMSG = 4; +.float superspec_flags; + +.string superspec_itemfilter; //"classname1 classname2 ..." + +bool superspec_Spectate(entity this, entity targ) +{ + if(Spectate(this, targ) == 1) + TRANSMUTE(Spectator, this); + + return true; +} + +void superspec_save_client_conf(entity this) +{ + string fn = "superspec-local.options"; + float fh; + + if (!_ISLOCAL(this)) + { + if(this.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(this.crypto_idfp)); + } + + fh = fopen(fn, FILE_WRITE); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for writing."); + } + else + { + fputs(fh, _SSMAGIX); + fputs(fh, "\n"); + fputs(fh, ftos(this.autospec_flags)); + fputs(fh, "\n"); + fputs(fh, ftos(this.superspec_flags)); + fputs(fh, "\n"); + fputs(fh, this.superspec_itemfilter); + fputs(fh, "\n"); + fclose(fh); + } +} + +void superspec_msg(string _center_title, string _con_title, entity _to, string _msg, float _spamlevel) +{ + sprint(_to, strcat(_con_title, _msg)); + + if(_to.superspec_flags & SSF_SILENT) + return; + + if(_spamlevel > 1) + if (!(_to.superspec_flags & SSF_VERBOSE)) + return; + + centerprint(_to, strcat(_center_title, _msg)); +} + +float superspec_filteritem(entity _for, entity _item) +{ + float i; + + if(_for.superspec_itemfilter == "") + return true; + + if(_for.superspec_itemfilter == "") + return true; + + float l = tokenize_console(_for.superspec_itemfilter); + for(i = 0; i < l; ++i) + { + if(argv(i) == _item.classname) + return true; + } + + return false; +} + +MUTATOR_HOOKFUNCTION(superspec, ItemTouch) +{ + entity item = M_ARGV(0, entity); + entity toucher = M_ARGV(1, entity); + + FOREACH_CLIENT(true, LAMBDA( + if(!IS_SPEC(it) && !IS_OBSERVER(it)) + continue; + if(it.superspec_flags & SSF_ITEMMSG) + if(superspec_filteritem(it, item)) + { + if(it.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n", toucher.netname, item.netname), 1); + else + superspec_msg("", "", it, sprintf("Player %s^7 just picked up ^3%s\n^8(%s^8)\n", toucher.netname, item.netname, item.classname), 1); + if((it.autospec_flags & ASF_SSIM) && it.enemy != toucher) + { + superspec_Spectate(it, toucher); + return MUT_ITEMTOUCH_CONTINUE; + } + } + + if((it.autospec_flags & ASF_SHIELD && item.invincible_finished) || + (it.autospec_flags & ASF_STRENGTH && item.strength_finished) || + (it.autospec_flags & ASF_MEGA_AR && item.itemdef == ITEM_ArmorMega) || + (it.autospec_flags & ASF_MEGA_HP && item.itemdef == ITEM_HealthMega) || + (it.autospec_flags & ASF_FLAG_GRAB && item.classname == "item_flag_team")) + { + + if((it.enemy != toucher) || IS_OBSERVER(it)) + { + if(it.autospec_flags & ASF_OBSERVER_ONLY && !IS_OBSERVER(it)) + { + if(it.superspec_flags & SSF_VERBOSE) + superspec_msg("", "", it, sprintf("^8Ignored that ^7%s^8 grabbed %s^8 since the observer_only option is ON\n", toucher.netname, item.netname), 2); + } + else + { + if(it.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", it, sprintf("^7Following %s^7 due to picking up %s\n", toucher.netname, item.netname), 2); + + superspec_Spectate(it, toucher); + } + } + } + )); + + return MUT_ITEMTOUCH_CONTINUE; +} + +MUTATOR_HOOKFUNCTION(superspec, SV_ParseClientCommand) +{ +#define OPTIONINFO(flag,var,test,text,long,short) \ + var = strcat(var, ((flag & test) ? "^2[ON] ^7" : "^1[OFF] ^7")); \ + var = strcat(var, text," ^7(^3 ", long, "^7 | ^3", short, " ^7)\n") + + if(MUTATOR_RETURNVALUE) // command was already handled? + return; + + entity player = M_ARGV(0, entity); + string cmd_name = M_ARGV(1, string); + int cmd_argc = M_ARGV(2, int); + + if(IS_PLAYER(player)) + return; + + if(cmd_name == "superspec_itemfilter") + { + if(argv(1) == "help") + { + string _aspeco; + _aspeco = "^7 superspec_itemfilter ^3\"item_classname1 item_classname2\"^7 only show thise items when ^2superspec ^3item_message^7 is on\n"; + _aspeco = strcat(_aspeco, "^3 clear^7 Remove the filter (show all pickups)\n"); + _aspeco = strcat(_aspeco, "^3 show ^7 Display current filter\n"); + superspec_msg("^3superspec_itemfilter help:\n\n\n", "\n^3superspec_itemfilter help:\n", player, _aspeco, 1); + } + else if(argv(1) == "clear") + { + if(player.superspec_itemfilter != "") + strunzone(player.superspec_itemfilter); + + player.superspec_itemfilter = ""; + } + else if(argv(1) == "show" || argv(1) == "") + { + if(player.superspec_itemfilter == "") + { + superspec_msg("^3superspec_itemfilter^7 is ^1not^7 set", "\n^3superspec_itemfilter^7 is ^1not^7 set\n", player, "", 1); + return true; + } + float i; + float l = tokenize_console(player.superspec_itemfilter); + string _msg = ""; + for(i = 0; i < l; ++i) + _msg = strcat(_msg, "^3#", ftos(i), " ^7", argv(i), "\n"); + //_msg = sprintf("^3#%d^7 %s\n%s", i, _msg, argv(i)); + + _msg = strcat(_msg,"\n"); + + superspec_msg("^3superspec_itemfilter is:\n\n\n", "\n^3superspec_itemfilter is:\n", player, _msg, 1); + } + else + { + if(player.superspec_itemfilter != "") + strunzone(player.superspec_itemfilter); + + player.superspec_itemfilter = strzone(argv(1)); + } + + return true; + } + + if(cmd_name == "superspec") + { + string _aspeco; + + if(cmd_argc > 1) + { + float i, _bits = 0, _start = 1; + if(argv(1) == "help") + { + _aspeco = "use cmd superspec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 silent ^7(short^5 si^7) supresses ALL messages from superspectate.\n"); + _aspeco = strcat(_aspeco, "^3 verbose ^7(short^5 ve^7) makes superspectate print some additional information.\n"); + _aspeco = strcat(_aspeco, "^3 item_message ^7(short^5 im^7) makes superspectate print items that were picked up.\n"); + _aspeco = strcat(_aspeco, "^7 Use cmd superspec_itemfilter \"item_class1 item_class2\" to set up a filter of what to show with ^3item_message.\n"); + superspec_msg("^2Available Super Spectate ^3options:\n\n\n", "\n^2Available Super Spectate ^3options:\n", player, _aspeco, 1); + return true; + } + + if(argv(1) == "clear") + { + player.superspec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + player.superspec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + player.superspec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "silent") || (argv(i) == "si")) _bits |= SSF_SILENT ; + if((argv(i) == "verbose") || (argv(i) == "ve")) _bits |= SSF_VERBOSE; + if((argv(i) == "item_message") || (argv(i) == "im")) _bits |= SSF_ITEMMSG; + } + } + } + + _aspeco = ""; + OPTIONINFO(player.superspec_flags, _aspeco, SSF_SILENT, "Silent", "silent", "si"); + OPTIONINFO(player.superspec_flags, _aspeco, SSF_VERBOSE, "Verbose", "verbose", "ve"); + OPTIONINFO(player.superspec_flags, _aspeco, SSF_ITEMMSG, "Item pickup messages", "item_message", "im"); + + superspec_msg("^3Current Super Spectate options are:\n\n\n\n\n", "\n^3Current Super Spectate options are:\n", player, _aspeco, 1); + + return true; + } + +///////////////////// + + if(cmd_name == "autospec") + { + string _aspeco; + if(cmd_argc > 1) + { + if(argv(1) == "help") + { + _aspeco = "use cmd autospec [option] [on|off] to set options\n\n"; + _aspeco = strcat(_aspeco, "^3 strength ^7(short^5 st^7) for automatic spectate on strength powerup\n"); + _aspeco = strcat(_aspeco, "^3 shield ^7(short^5 sh^7) for automatic spectate on shield powerup\n"); + _aspeco = strcat(_aspeco, "^3 mega_health ^7(short^5 mh^7) for automatic spectate on mega health\n"); + _aspeco = strcat(_aspeco, "^3 mega_armor ^7(short^5 ma^7) for automatic spectate on mega armor\n"); + _aspeco = strcat(_aspeco, "^3 flag_grab ^7(short^5 fg^7) for automatic spectate on CTF flag grab\n"); + _aspeco = strcat(_aspeco, "^3 observer_only ^7(short^5 oo^7) for automatic spectate only if in observer mode\n"); + _aspeco = strcat(_aspeco, "^3 show_what ^7(short^5 sw^7) to display what event triggered autospectate\n"); + _aspeco = strcat(_aspeco, "^3 item_msg ^7(short^5 im^7) to autospec when item_message in superspectate is triggered\n"); + _aspeco = strcat(_aspeco, "^3 followkiller ^7(short ^5fk^7) to autospec the killer/off\n"); + _aspeco = strcat(_aspeco, "^3 all ^7(short ^5aa^7) to turn everything on/off\n"); + superspec_msg("^2Available Auto Spectate ^3options:\n\n\n", "\n^2Available Auto Spectate ^3options:\n", player, _aspeco, 1); + return true; + } + + float i, _bits = 0, _start = 1; + if(argv(1) == "clear") + { + player.autospec_flags = 0; + _start = 2; + } + + for(i = _start; i < cmd_argc; ++i) + { + if(argv(i) == "on" || argv(i) == "1") + { + player.autospec_flags |= _bits; + _bits = 0; + } + else if(argv(i) == "off" || argv(i) == "0") + { + if(_start == 1) + player.autospec_flags &= ~_bits; + + _bits = 0; + } + else + { + if((argv(i) == "strength") || (argv(i) == "st")) _bits |= ASF_STRENGTH; + if((argv(i) == "shield") || (argv(i) == "sh")) _bits |= ASF_SHIELD; + if((argv(i) == "mega_health") || (argv(i) == "mh")) _bits |= ASF_MEGA_HP; + if((argv(i) == "mega_armor") || (argv(i) == "ma")) _bits |= ASF_MEGA_AR; + if((argv(i) == "flag_grab") || (argv(i) == "fg")) _bits |= ASF_FLAG_GRAB; + if((argv(i) == "observer_only") || (argv(i) == "oo")) _bits |= ASF_OBSERVER_ONLY; + if((argv(i) == "show_what") || (argv(i) == "sw")) _bits |= ASF_SHOWWHAT; + if((argv(i) == "item_msg") || (argv(i) == "im")) _bits |= ASF_SSIM; + if((argv(i) == "followkiller") || (argv(i) == "fk")) _bits |= ASF_FOLLOWKILLER; + if((argv(i) == "all") || (argv(i) == "aa")) _bits |= ASF_ALL; + } + } + } + + _aspeco = ""; + OPTIONINFO(player.autospec_flags, _aspeco, ASF_STRENGTH, "Strength", "strength", "st"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHIELD, "Shield", "shield", "sh"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_HP, "Mega Health", "mega_health", "mh"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_MEGA_AR, "Mega Armor", "mega_armor", "ma"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_FLAG_GRAB, "Flag grab", "flag_grab","fg"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_OBSERVER_ONLY, "Only switch if observer", "observer_only", "oo"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SHOWWHAT, "Show what item triggered spectate", "show_what", "sw"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_SSIM, "Switch on superspec item message", "item_msg", "im"); + OPTIONINFO(player.autospec_flags, _aspeco, ASF_FOLLOWKILLER, "Followkiller", "followkiller", "fk"); + + superspec_msg("^3Current auto spectate options are:\n\n\n\n\n", "\n^3Current auto spectate options are:\n", player, _aspeco, 1); + return true; + } + + if(cmd_name == "followpowerup") + { + FOREACH_CLIENT(IS_PLAYER(it) && (it.strength_finished > time || it.invincible_finished > time), LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active powerup\n", 1); + return true; + } + + if(cmd_name == "followstrength") + { + FOREACH_CLIENT(IS_PLAYER(it) && it.strength_finished > time, LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active Strength\n", 1); + return true; + } + + if(cmd_name == "followshield") + { + FOREACH_CLIENT(IS_PLAYER(it) && it.invincible_finished > time, LAMBDA(return superspec_Spectate(player, it))); + + superspec_msg("", "", player, "No active Shield\n", 1); + return true; + } +#undef OPTIONINFO +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":SS"); +} + +MUTATOR_HOOKFUNCTION(superspec, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Super Spectators"); +} + +void superspec_hello(entity this) +{ + if(this.enemy.crypto_idfp == "") + Send_Notification(NOTIF_ONE_ONLY, this.enemy, MSG_INFO, INFO_SUPERSPEC_MISSING_UID); + + delete(this); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientConnect) +{ + entity player = M_ARGV(0, entity); + + if(!IS_REAL_CLIENT(player)) + return; + + string fn = "superspec-local.options"; + float fh; + + player.superspec_flags = SSF_VERBOSE; + player.superspec_itemfilter = ""; + + entity _hello = spawn(); + _hello.enemy = player; + setthink(_hello, superspec_hello); + _hello.nextthink = time + 5; + + if (!_ISLOCAL(player)) + { + if(player.crypto_idfp == "") + return; + + fn = sprintf("superspec-%s.options", uri_escape(player.crypto_idfp)); + } + + fh = fopen(fn, FILE_READ); + if(fh < 0) + { + LOG_TRACE("^1ERROR: ^7 superspec can not open ", fn, " for reading."); + } + else + { + string _magic = fgets(fh); + if(_magic != _SSMAGIX) + { + LOG_TRACE("^1ERROR^7 While reading superspec options file: unknown magic"); + } + else + { + player.autospec_flags = stof(fgets(fh)); + player.superspec_flags = stof(fgets(fh)); + player.superspec_itemfilter = strzone(fgets(fh)); + } + fclose(fh); + } +} + +MUTATOR_HOOKFUNCTION(superspec, PlayerDies) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + + FOREACH_CLIENT(IS_SPEC(it), LAMBDA( + if(it.autospec_flags & ASF_FOLLOWKILLER && IS_PLAYER(frag_attacker) && it.enemy == frag_target) + { + if(it.autospec_flags & ASF_SHOWWHAT) + superspec_msg("", "", it, sprintf("^7Following %s^7 due to followkiller\n", frag_attacker.netname), 2); + + superspec_Spectate(it, frag_attacker); + } + )); +} + +MUTATOR_HOOKFUNCTION(superspec, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + superspec_save_client_conf(player); +} diff --git a/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/superspec/sv_superspec.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/touchexplode/_mod.inc b/qcsrc/common/mutators/mutator/touchexplode/_mod.inc index 42dad3926e..f341e2afe1 100644 --- a/qcsrc/common/mutators/mutator/touchexplode/_mod.inc +++ b/qcsrc/common/mutators/mutator/touchexplode/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/_mod.qh b/qcsrc/common/mutators/mutator/touchexplode/_mod.qh index ec71f52d74..18cdcc60f6 100644 --- a/qcsrc/common/mutators/mutator/touchexplode/_mod.qh +++ b/qcsrc/common/mutators/mutator/touchexplode/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/module.inc b/qcsrc/common/mutators/mutator/touchexplode/module.inc deleted file mode 100644 index d3b0ea5af9..0000000000 --- a/qcsrc/common/mutators/mutator/touchexplode/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "touchexplode.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc new file mode 100644 index 0000000000..38ac30595d --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qc @@ -0,0 +1,47 @@ +#include "sv_touchexplode.qh" + +float autocvar_g_touchexplode_radius; +float autocvar_g_touchexplode_damage; +float autocvar_g_touchexplode_edgedamage; +float autocvar_g_touchexplode_force; + +REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); + +.float touchexplode_time; + +void PlayerTouchExplode(entity p1, entity p2) +{ + vector org = (p1.origin + p2.origin) * 0.5; + org.z += (p1.mins.z + p2.mins.z) * 0.5; + + sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); + Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); + + entity e = spawn(); + setorigin(e, org); + RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL); + delete(e); +} + +MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + if(time > player.touchexplode_time) + if(!gameover) + if(!STAT(FROZEN, player)) + if(IS_PLAYER(player)) + if(!IS_DEAD(player)) + if(!IS_INDEPENDENT_PLAYER(player)) + FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( + if(time > it.touchexplode_time) + if(!STAT(FROZEN, it)) + if(!IS_DEAD(it)) + if (!IS_INDEPENDENT_PLAYER(it)) + if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax)) + { + PlayerTouchExplode(player, it); + player.touchexplode_time = it.touchexplode_time = time + 0.2; + } + )); +} diff --git a/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/touchexplode/sv_touchexplode.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc b/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc deleted file mode 100644 index 67e14403c5..0000000000 --- a/qcsrc/common/mutators/mutator/touchexplode/touchexplode.qc +++ /dev/null @@ -1,47 +0,0 @@ -#ifdef IMPLEMENTATION -float autocvar_g_touchexplode_radius; -float autocvar_g_touchexplode_damage; -float autocvar_g_touchexplode_edgedamage; -float autocvar_g_touchexplode_force; - -REGISTER_MUTATOR(touchexplode, cvar("g_touchexplode")); - -.float touchexplode_time; - -void PlayerTouchExplode(entity p1, entity p2) -{ - vector org = (p1.origin + p2.origin) * 0.5; - org.z += (p1.mins.z + p2.mins.z) * 0.5; - - sound(p1, CH_TRIGGER, SND_GRENADE_IMPACT, VOL_BASE, ATTEN_NORM); - Send_Effect(EFFECT_EXPLOSION_SMALL, org, '0 0 0', 1); - - entity e = spawn(); - setorigin(e, org); - RadiusDamage(e, NULL, autocvar_g_touchexplode_damage, autocvar_g_touchexplode_edgedamage, autocvar_g_touchexplode_radius, NULL, NULL, autocvar_g_touchexplode_force, DEATH_TOUCHEXPLODE.m_id, NULL); - remove(e); -} - -MUTATOR_HOOKFUNCTION(touchexplode, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - if(time > player.touchexplode_time) - if(!gameover) - if(!STAT(FROZEN, player)) - if(IS_PLAYER(player)) - if(!IS_DEAD(player)) - if(!IS_INDEPENDENT_PLAYER(player)) - FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( - if(time > it.touchexplode_time) - if(!STAT(FROZEN, it)) - if(!IS_DEAD(it)) - if (!IS_INDEPENDENT_PLAYER(it)) - if(boxesoverlap(player.absmin, player.absmax, it.absmin, it.absmax)) - { - PlayerTouchExplode(player, it); - player.touchexplode_time = it.touchexplode_time = time + 0.2; - } - )); -} -#endif diff --git a/qcsrc/common/mutators/mutator/vampire/_mod.inc b/qcsrc/common/mutators/mutator/vampire/_mod.inc index 856ed84c46..57b844451a 100644 --- a/qcsrc/common/mutators/mutator/vampire/_mod.inc +++ b/qcsrc/common/mutators/mutator/vampire/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/_mod.qh b/qcsrc/common/mutators/mutator/vampire/_mod.qh index 551184c77b..d7d8a4143c 100644 --- a/qcsrc/common/mutators/mutator/vampire/_mod.qh +++ b/qcsrc/common/mutators/mutator/vampire/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampire/module.inc b/qcsrc/common/mutators/mutator/vampire/module.inc deleted file mode 100644 index 864ea28b24..0000000000 --- a/qcsrc/common/mutators/mutator/vampire/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "vampire.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc new file mode 100644 index 0000000000..3a435c5ed1 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qc @@ -0,0 +1,28 @@ +#include "sv_vampire.qh" + +REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); + +MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) +{ + entity frag_attacker = M_ARGV(1, entity); + entity frag_target = M_ARGV(2, entity); + float damage_take = M_ARGV(4, float); + + if(time >= frag_target.spawnshieldtime) + if(frag_target != frag_attacker) + if(!IS_DEAD(frag_target)) + { + frag_attacker.health += bound(0, damage_take, frag_target.health); + frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); + } +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire"); +} + +MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) +{ + M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire"); +} diff --git a/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampire/sv_vampire.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/vampire/vampire.qc b/qcsrc/common/mutators/mutator/vampire/vampire.qc deleted file mode 100644 index d245c8059b..0000000000 --- a/qcsrc/common/mutators/mutator/vampire/vampire.qc +++ /dev/null @@ -1,28 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vampire, cvar("g_vampire") && !cvar("g_instagib")); - -MUTATOR_HOOKFUNCTION(vampire, PlayerDamage_SplitHealthArmor) -{ - entity frag_attacker = M_ARGV(1, entity); - entity frag_target = M_ARGV(2, entity); - float damage_take = M_ARGV(4, float); - - if(time >= frag_target.spawnshieldtime) - if(frag_target != frag_attacker) - if(!IS_DEAD(frag_target)) - { - frag_attacker.health += bound(0, damage_take, frag_target.health); - frag_attacker.health = bound(0, frag_attacker.health, autocvar_g_balance_health_limit); - } -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ":Vampire"); -} - -MUTATOR_HOOKFUNCTION(vampire, BuildMutatorsPrettyString) -{ - M_ARGV(0, string) = strcat(M_ARGV(0, string), ", Vampire"); -} -#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/_mod.inc b/qcsrc/common/mutators/mutator/vampirehook/_mod.inc index 868a4ef3fc..72b2bacb9c 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/_mod.inc +++ b/qcsrc/common/mutators/mutator/vampirehook/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/_mod.qh b/qcsrc/common/mutators/mutator/vampirehook/_mod.qh index 5d57816c96..eaa7b320a8 100644 --- a/qcsrc/common/mutators/mutator/vampirehook/_mod.qh +++ b/qcsrc/common/mutators/mutator/vampirehook/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/module.inc b/qcsrc/common/mutators/mutator/vampirehook/module.inc deleted file mode 100644 index 17ecf6005f..0000000000 --- a/qcsrc/common/mutators/mutator/vampirehook/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "vampirehook.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc new file mode 100644 index 0000000000..e2b0f57d76 --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qc @@ -0,0 +1,37 @@ +#include "sv_vampirehook.qh" + +REGISTER_MUTATOR(vh, cvar("g_vampirehook")); + +bool autocvar_g_vampirehook_teamheal; +float autocvar_g_vampirehook_damage; +float autocvar_g_vampirehook_damagerate; +float autocvar_g_vampirehook_health_steal; + +.float last_dmg; + +MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) +{ + entity thehook = M_ARGV(0, entity); + + entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment); + + if(IS_PLAYER(thehook.aiment)) + if(thehook.last_dmg < time) + 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(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, 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); + + if(dmgent == thehook.owner) + dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! + } +} diff --git a/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/vampirehook/sv_vampirehook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc b/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc deleted file mode 100644 index a54dc74e40..0000000000 --- a/qcsrc/common/mutators/mutator/vampirehook/vampirehook.qc +++ /dev/null @@ -1,38 +0,0 @@ -#ifdef IMPLEMENTATION -REGISTER_MUTATOR(vh, cvar("g_vampirehook")); - -bool autocvar_g_vampirehook_teamheal; -float autocvar_g_vampirehook_damage; -float autocvar_g_vampirehook_damagerate; -float autocvar_g_vampirehook_health_steal; - -.float last_dmg; - -MUTATOR_HOOKFUNCTION(vh, GrappleHookThink) -{ - entity thehook = M_ARGV(0, entity); - - entity dmgent = ((SAME_TEAM(thehook.owner, thehook.aiment) && autocvar_g_vampirehook_teamheal) ? thehook.owner : thehook.aiment); - - if(IS_PLAYER(thehook.aiment)) - if(thehook.last_dmg < time) - 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(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, 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); - - if(dmgent == thehook.owner) - dmgent.health -= autocvar_g_vampirehook_damage; // FIXME: friendly fire?! - } -} - -#endif diff --git a/qcsrc/common/mutators/mutator/walljump/_mod.inc b/qcsrc/common/mutators/mutator/walljump/_mod.inc new file mode 100644 index 0000000000..4f879c851e --- /dev/null +++ b/qcsrc/common/mutators/mutator/walljump/_mod.inc @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef GAMEQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/walljump/_mod.qh b/qcsrc/common/mutators/mutator/walljump/_mod.qh new file mode 100644 index 0000000000..59ab51518e --- /dev/null +++ b/qcsrc/common/mutators/mutator/walljump/_mod.qh @@ -0,0 +1,4 @@ +// generated file; do not modify +#ifdef GAMEQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/walljump/walljump.qc b/qcsrc/common/mutators/mutator/walljump/walljump.qc new file mode 100644 index 0000000000..96b81a5615 --- /dev/null +++ b/qcsrc/common/mutators/mutator/walljump/walljump.qc @@ -0,0 +1,70 @@ +#include "walljump.qh" + +#ifdef CSQC +REGISTER_MUTATOR(walljump, true); +#elif defined(SVQC) +REGISTER_MUTATOR(walljump, cvar("g_walljump")); +#endif + +#define PHYS_WALLJUMP(s) STAT(WALLJUMP, s) +#define PHYS_WALLJUMP_VELOCITY_Z_FACTOR(s) STAT(WALLJUMP_VELOCITY_Z_FACTOR, s) +#define PHYS_WALLJUMP_VELOCITY_XY_FACTOR(s) STAT(WALLJUMP_VELOCITY_XY_FACTOR, s) +#define PHYS_WALLJUMP_DELAY(s) STAT(WALLJUMP_DELAY, s) +#define PHYS_WALLJUMP_FORCE(s) STAT(WALLJUMP_FORCE, s) + +vector PlayerTouchWall(entity this) +{ +#define TRACE(newvec) \ + tracebox (start, this.mins, this.maxs, (newvec), true, this); \ + if (trace_fraction < 1 && vdist(this.origin - trace_endpos, <, dist) && trace_plane_normal_z < max_normal) \ + if (!(trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT)) \ + return trace_plane_normal; + + float dist = 10, max_normal = 0.2, scaler = 100; + vector start = this.origin; + TRACE(start + v_forward * scaler) + TRACE(start - v_forward * scaler) + TRACE(start + v_right * scaler) + TRACE(start - v_right * scaler) +#undef TRACE + return '0 0 0'; +} + +MUTATOR_HOOKFUNCTION(walljump, PlayerJump) +{ + entity player = M_ARGV(0, entity); + + if(PHYS_WALLJUMP(player)) + if(time - STAT(LASTWJ, player) > PHYS_WALLJUMP_DELAY(player)) // can't do this on client, as it's too stupid to obey counters + if(!IS_ONGROUND(player)) + if(player.move_movetype != MOVETYPE_NONE && player.move_movetype != MOVETYPE_FOLLOW && player.move_movetype != MOVETYPE_FLY && player.move_movetype != MOVETYPE_NOCLIP) + if(!IS_JUMP_HELD(player)) + if(!STAT(FROZEN, player)) + if(!IS_DEAD(player)) + { + vector plane_normal = PlayerTouchWall(player); + + if(plane_normal != '0 0 0') + { + float wj_force = PHYS_WALLJUMP_FORCE(player); + float wj_xy_factor = PHYS_WALLJUMP_VELOCITY_XY_FACTOR(player); + float wj_z_factor = PHYS_WALLJUMP_VELOCITY_Z_FACTOR(player); + player.velocity_x += plane_normal_x * wj_force; + player.velocity_x /= wj_xy_factor; + player.velocity_y += plane_normal_y * wj_force; + player.velocity_y /= wj_xy_factor; + player.velocity_z = PHYS_JUMPVELOCITY(player) * wj_z_factor; + if(PHYS_INPUT_BUTTON_CROUCH(player)) player.velocity_z *= -1; + +#ifdef SVQC + STAT(LASTWJ, player) = time; + player.oldvelocity = player.velocity; + Send_Effect(EFFECT_SMOKE_RING, trace_endpos, plane_normal, 5); + PlayerSound(player, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); + animdecide_setaction(player, ANIMACTION_JUMP, true); +#endif + + M_ARGV(2, bool) = true; // multijump + } + } +} diff --git a/qcsrc/common/mutators/mutator/walljump/walljump.qh b/qcsrc/common/mutators/mutator/walljump/walljump.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/walljump/walljump.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/waypoints/all.inc b/qcsrc/common/mutators/mutator/waypoints/all.inc index c74715ea71..73f22b836c 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.inc +++ b/qcsrc/common/mutators/mutator/waypoints/all.inc @@ -14,7 +14,6 @@ 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(Assault, _(""), '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(AssaultPush, _("Push"), '1 0.5 0', 1); @@ -27,6 +26,7 @@ REGISTER_WAYPOINT(FlagBaseRed, _("Red base"), '0.8 0.8 0', 1); REGISTER_WAYPOINT(FlagBaseBlue, _("Blue base"), '0.8 0.8 0', 1); REGISTER_WAYPOINT(FlagBaseYellow, _("Yellow base"), '0.8 0.8 0', 1); REGISTER_WAYPOINT(FlagBasePink, _("Pink base"), '0.8 0.8 0', 1); +REGISTER_WAYPOINT(FlagReturn, _("Return flag here"), '0 0.8 0.8', 1); REGISTER_WAYPOINT(DomNeut, _("Control point"), '0 1 1', 1); REGISTER_WAYPOINT(DomRed, _("Control point"), '0 1 1', 1); diff --git a/qcsrc/common/mutators/mutator/waypoints/all.qh b/qcsrc/common/mutators/mutator/waypoints/all.qh index 2b28784b7c..77c4312001 100644 --- a/qcsrc/common/mutators/mutator/waypoints/all.qh +++ b/qcsrc/common/mutators/mutator/waypoints/all.qh @@ -1,9 +1,8 @@ -#ifndef WAYPOINTS_ALL_H -#define WAYPOINTS_ALL_H +#pragma once #include "waypointsprites.qh" -REGISTRY(Waypoints, BITS(6)) +REGISTRY(Waypoints, BITS(7)) #define Waypoints_from(i) _Waypoints_from(i, WP_Null) REGISTER_REGISTRY(Waypoints) REGISTRY_CHECK(Waypoints) @@ -12,11 +11,11 @@ REGISTRY_CHECK(Waypoints) #define REGISTER_WAYPOINT_(id, init) REGISTER(Waypoints, WP, id, m_id, init) CLASS(Waypoint, Object) - ATTRIB(Waypoint, m_id, int, 0) - ATTRIB(Waypoint, netname, string, string_null) - ATTRIB(Waypoint, m_name, string, string_null) - ATTRIB(Waypoint, m_color, vector, '1 1 1') - ATTRIB(Waypoint, m_blink, int, 1) + ATTRIB(Waypoint, m_id, int, 0); + ATTRIB(Waypoint, netname, string); + ATTRIB(Waypoint, m_name, string); + ATTRIB(Waypoint, m_color, vector, '1 1 1'); + ATTRIB(Waypoint, m_blink, int, 1); CONSTRUCTOR(Waypoint, string _netname, string _name, vector _color, int _blink) { CONSTRUCT(Waypoint); this.netname = _netname; @@ -58,5 +57,3 @@ REGISTER_RADARICON(Vehicle, 1); REGISTER_RADARICON(Weapon, 1); #include "all.inc" - -#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/module.inc b/qcsrc/common/mutators/mutator/waypoints/module.inc deleted file mode 100644 index 50bb5b4d6e..0000000000 --- a/qcsrc/common/mutators/mutator/waypoints/module.inc +++ /dev/null @@ -1 +0,0 @@ -#include "waypointsprites.qc" diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc index 5fdb7ec428..bdedf295d8 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qc @@ -1,7 +1,5 @@ #include "waypointsprites.qh" -#ifdef IMPLEMENTATION - REGISTER_MUTATOR(waypointsprites, true); REGISTER_NET_LINKED(waypointsprites) @@ -18,12 +16,16 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) sendflags |= 0x80; int f = 0; - if(this.currentammo) + if(this.currentammo == 1) f |= 1; // hideable if(this.exteriormodeltoclient == to) f |= 2; // my own + if(this.currentammo == 2) + f |= 2; // radar only MUTATOR_CALLHOOK(SendWaypoint, this, to, sendflags, f); + sendflags = M_ARGV(2, int); + f = M_ARGV(3, int); WriteByte(MSG_ENTITY, sendflags); WriteByte(MSG_ENTITY, this.wp_extra); @@ -98,9 +100,9 @@ bool WaypointSprite_SendEntity(entity this, entity to, float sendflags) #endif #ifdef CSQC -void Ent_WaypointSprite(entity this); +void Ent_WaypointSprite(entity this, bool isnew); NET_HANDLE(waypointsprites, bool isnew) { - Ent_WaypointSprite(this); + Ent_WaypointSprite(this, isnew); return true; } @@ -112,7 +114,7 @@ void Ent_RemoveWaypointSprite(entity this) } /** flags origin [team displayrule] [spritename] [spritename2] [spritename3] [lifetime maxdistance hideable] */ -void Ent_WaypointSprite(entity this) +void Ent_WaypointSprite(entity this, bool isnew) { int sendflags = ReadByte(); this.wp_extra = ReadByte(); @@ -121,6 +123,10 @@ void Ent_WaypointSprite(entity this) this.spawntime = time; this.draw2d = Draw_WaypointSprite; + if (isnew) { + IL_PUSH(g_drawables_2d, this); + IL_PUSH(g_radaricons, this); + } InterpolateOrigin_Undo(this); this.iflags |= IFLAG_ORIGIN; @@ -225,6 +231,7 @@ float spritelookupblinkvalue(entity this, string s) return 2; } if (s == WP_Item.netname) return Items_from(this.wp_extra).m_waypointblink; + if(s == WP_FlagReturn.netname) return 2; return 1; } @@ -275,10 +282,10 @@ void drawrotpic(vector org, float rot, string pic, vector sz, vector hotspot, ve // rotate them, and make them absolute rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed - v1 = rotate(v1, rot) + org; - v2 = rotate(v2, rot) + org; - v3 = rotate(v3, rot) + org; - v4 = rotate(v4, rot) + org; + v1 = Rotate(v1, rot) + org; + v2 = Rotate(v2, rot) + org; + v3 = Rotate(v3, rot) + org; + v4 = Rotate(v4, rot) + org; // draw them R_BeginPolygon(pic, f); @@ -312,9 +319,9 @@ void drawhealthbar(vector org, float rot, float h, vector sz, vector hotspot, fl up = '0 1 0'; rot = -rot; // rotate by the opposite angle, as our coordinate system is reversed - o = rotate(o, rot) + org; - ri = rotate(ri, rot); - up = rotate(up, rot); + o = Rotate(o, rot) + org; + ri = Rotate(ri, rot); + up = Rotate(up, rot); owidth = width + 2 * border; o = o - up * (margin + border + theheight) + ri * (sz.x - owidth) * 0.5; @@ -341,19 +348,19 @@ vector drawspritearrow(vector o, float ang, vector rgb, float a, float t) R_BeginPolygon("", DRAWFLAG_NORMAL); R_PolygonVertex(o, '0 0 0', '0 0 0', a); - R_PolygonVertex(o + rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a); - R_PolygonVertex(o + rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a); - R_PolygonVertex(o + rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a); - R_PolygonVertex(o + rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a); + R_PolygonVertex(o + Rotate(arrowY - borderX, ang), '0 0 0', '0 0 0', a); + R_PolygonVertex(o + Rotate(borderY - borderX, ang), '0 0 0', '0 0 0', a); + R_PolygonVertex(o + Rotate(borderY + borderX, ang), '0 0 0', '0 0 0', a); + R_PolygonVertex(o + Rotate(arrowY + borderX, ang), '0 0 0', '0 0 0', a); R_EndPolygon(); R_BeginPolygon("", DRAWFLAG_ADDITIVE); - R_PolygonVertex(o + rotate(eY * borderDiag, ang), '0 0 0', rgb, a); - R_PolygonVertex(o + rotate(arrowY - arrowX, ang), '0 0 0', rgb, a); - R_PolygonVertex(o + rotate(arrowY + arrowX, ang), '0 0 0', rgb, a); + R_PolygonVertex(o + Rotate(eY * borderDiag, ang), '0 0 0', rgb, a); + R_PolygonVertex(o + Rotate(arrowY - arrowX, ang), '0 0 0', rgb, a); + R_PolygonVertex(o + Rotate(arrowY + arrowX, ang), '0 0 0', rgb, a); R_EndPolygon(); - return o + rotate(eY * (borderDiag+size+margin), ang); + return o + Rotate(eY * (borderDiag+size+margin), ang); } // returns location of sprite healthbar @@ -452,7 +459,7 @@ vector fixrgbexcess(vector rgb) void Draw_WaypointSprite(entity this) { - if (this.lifetime) + if (this.lifetime > 0) this.alpha = pow(bound(0, (this.fadetime - time) / this.lifetime, 1), waypointsprite_timealphaexponent); else this.alpha = 1; @@ -463,14 +470,12 @@ void Draw_WaypointSprite(entity this) if (autocvar_cl_hidewaypoints >= 2) return; - if (this.hideflags & 1) - if (autocvar_cl_hidewaypoints) - return; // fixed waypoint + if (this.hideflags & 1 && autocvar_cl_hidewaypoints) + return; // fixed waypoint InterpolateOrigin_Do(this); float t = entcs_GetTeam(player_localnum) + 1; - string spriteimage = ""; // choose the sprite @@ -479,7 +484,7 @@ void Draw_WaypointSprite(entity this) case SPRITERULE_SPECTATOR: if (!( (autocvar_g_waypointsprite_itemstime == 1 && t == NUM_SPECTATOR + 1) - || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage)) + || (autocvar_g_waypointsprite_itemstime == 2 && (t == NUM_SPECTATOR + 1 || warmup_stage || STAT(ITEMSTIME) == 2)) )) return; spriteimage = this.netname; @@ -513,11 +518,8 @@ void Draw_WaypointSprite(entity this) ++waypointsprite_newcount; - float dist; - dist = vlen(this.origin - view_origin); - - float a; - a = this.alpha * autocvar_hud_panel_fg_alpha; + float dist = vlen(this.origin - view_origin); + float a = this.alpha * autocvar_hud_panel_fg_alpha; if (this.maxdistance > waypointsprite_normdistance) a *= pow(bound(0, (this.maxdistance - dist) / (this.maxdistance - waypointsprite_normdistance), 1), waypointsprite_distancealphaexponent); @@ -535,7 +537,7 @@ void Draw_WaypointSprite(entity this) { if (this.helpme && time < this.helpme) a *= SPRITE_HELPME_BLINK; - else if (!this.lifetime) // fading out waypoints don't blink + else if (this.lifetime > 0) // fading out waypoints don't blink a *= spritelookupblinkvalue(this, spriteimage); } @@ -609,11 +611,9 @@ void Draw_WaypointSprite(entity this) (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o.x, (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o.y); - float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); - float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) ); - t = waypointsprite_scale * vidscale; + t = waypointsprite_scale; a *= waypointsprite_alpha; { @@ -861,9 +861,10 @@ void WaypointSprite_FadeOutIn(entity e, float t) // ensure: // (e.teleport_time - time) / wp.fade_time stays // e.teleport_time = time + fadetime - float current_fadetime; - current_fadetime = e.teleport_time - time; + float current_fadetime = e.teleport_time - time; e.teleport_time = time + t; + if (e.fade_time < 0) + e.fade_time = -e.fade_time; e.fade_time = e.fade_time * t / current_fadetime; } @@ -881,7 +882,7 @@ void WaypointSprite_Kill(entity wp) { if (!wp) return; if (wp.owner) wp.owner.(wp.owned_by_field) = NULL; - remove(wp); + delete(wp); } void WaypointSprite_Disown(entity wp, float fadetime) @@ -932,7 +933,7 @@ bool WaypointSprite_visible_for_player(entity this, entity player, entity view) { if (!autocvar_sv_itemstime) return false; - if (!warmup_stage && IS_PLAYER(view)) + if (!warmup_stage && IS_PLAYER(view) && autocvar_sv_itemstime != 2) return false; } else if (this.team && this.rule == SPRITERULE_DEFAULT) @@ -963,17 +964,17 @@ float WaypointSprite_isteammate(entity e, entity e2) return e2 == e; } -float WaypointSprite_Customize(entity this) +bool WaypointSprite_Customize(entity this, entity client) { // this is not in SendEntity because it shall run every frame, not just every update // make spectators see what the player would see - entity e = WaypointSprite_getviewentity(other); + entity e = WaypointSprite_getviewentity(client); - if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, other)) + if (MUTATOR_CALLHOOK(CustomizeWaypoint, this, client)) return false; - return this.waypointsprite_visible_for_player(this, other, e); + return this.waypointsprite_visible_for_player(this, client, e); } bool WaypointSprite_SendEntity(entity this, entity to, float sendflags); @@ -997,8 +998,10 @@ entity WaypointSprite_Spawn( ) { entity wp = new(sprite_waypoint); + wp.fade_time = _lifetime; // if negative tells client not to fade it out + if(_lifetime < 0) + _lifetime = -_lifetime; wp.teleport_time = time + _lifetime; - wp.fade_time = _lifetime; wp.exteriormodeltoclient = ref; if (ref) { @@ -1014,7 +1017,7 @@ entity WaypointSprite_Spawn( if (own) { if (own.(ownfield)) - remove(own.(ownfield)); + delete(own.(ownfield)); own.(ownfield) = wp; wp.owned_by_field = ownfield; } @@ -1142,4 +1145,3 @@ void WaypointSprite_PlayerGone(entity this) WaypointSprite_DetachCarrier(this); } #endif -#endif diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index 7fb578f824..62a7cac611 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -1,5 +1,4 @@ -#ifndef WAYPOINTSPRITES_H -#define WAYPOINTSPRITES_H +#pragma once #include "all.qh" @@ -74,8 +73,8 @@ float waypointsprite_distancefadescale; float waypointsprite_distancefadedistance; float waypointsprite_alpha; -const float SPRITE_HEALTHBAR_WIDTH = 144; -const float SPRITE_HEALTHBAR_HEIGHT = 9; +const float SPRITE_HEALTHBAR_WIDTH = 104; +const float SPRITE_HEALTHBAR_HEIGHT = 7; const float SPRITE_HEALTHBAR_MARGIN = 6; const float SPRITE_HEALTHBAR_BORDER = 2; const float SPRITE_HEALTHBAR_BORDERALPHA = 1; @@ -108,7 +107,7 @@ vector fixrgbexcess(vector rgb); void Ent_RemoveWaypointSprite(entity this); -void Ent_WaypointSprite(entity this); +void Ent_WaypointSprite(entity this, bool isnew); void WaypointSprite_Load_Frames(string ext); @@ -118,6 +117,8 @@ void Draw_WaypointSprite(entity this); #endif #ifdef SVQC +.entity sprite; + float autocvar_sv_waypointsprite_deadlifetime; float autocvar_sv_waypointsprite_deployed_lifetime; float autocvar_sv_waypointsprite_limitedrange; @@ -168,7 +169,7 @@ entity WaypointSprite_getviewentity(entity e); float WaypointSprite_isteammate(entity e, entity e2); -float WaypointSprite_Customize(entity this); +bool WaypointSprite_Customize(entity this, entity client); bool WaypointSprite_SendEntity(entity this, entity to, float sendflags); @@ -234,5 +235,3 @@ void WaypointSprite_PlayerDead(entity this); void WaypointSprite_PlayerGone(entity this); #endif - -#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc index 742510b883..034b51d117 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc +++ b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh index 68d6a24c42..05b87c73d3 100644 --- a/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh +++ b/qcsrc/common/mutators/mutator/weaponarena_random/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc b/qcsrc/common/mutators/mutator/weaponarena_random/module.inc deleted file mode 100644 index b7a5f66901..0000000000 --- a/qcsrc/common/mutators/mutator/weaponarena_random/module.inc +++ /dev/null @@ -1,3 +0,0 @@ -#ifdef SVQC -#include "weaponarena_random.qc" -#endif diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc new file mode 100644 index 0000000000..7ac4504ec9 --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qc @@ -0,0 +1,14 @@ +#include "sv_weaponarena_random.qh" + +// WEAPONTODO: rename the cvars +REGISTER_MUTATOR(weaponarena_random, true); + +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); +} diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/mutators/mutator/weaponarena_random/sv_weaponarena_random.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc b/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc deleted file mode 100644 index e4d400db70..0000000000 --- a/qcsrc/common/mutators/mutator/weaponarena_random/weaponarena_random.qc +++ /dev/null @@ -1,15 +0,0 @@ -#ifdef IMPLEMENTATION -// WEAPONTODO: rename the cvars -REGISTER_MUTATOR(weaponarena_random, true); - -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); -} - -#endif diff --git a/qcsrc/common/net_linked.qh b/qcsrc/common/net_linked.qh new file mode 100644 index 0000000000..9cd2094a68 --- /dev/null +++ b/qcsrc/common/net_linked.qh @@ -0,0 +1,55 @@ +#pragma once + +REGISTER_NET_TEMP(TE_CSQC_PICTURE) +REGISTER_NET_TEMP(TE_CSQC_RACE) +REGISTER_NET_TEMP(TE_CSQC_TEAMNAGGER) +REGISTER_NET_TEMP(TE_CSQC_PINGPLREPORT) +REGISTER_NET_TEMP(TE_CSQC_WEAPONCOMPLAIN) +REGISTER_NET_TEMP(TE_CSQC_VEHICLESETUP) + +const int RACE_NET_CHECKPOINT_HIT_QUALIFYING = 0; // byte checkpoint, short time, short recordtime, string recordholder +const int RACE_NET_CHECKPOINT_CLEAR = 1; +const int RACE_NET_CHECKPOINT_NEXT_QUALIFYING = 2; // byte nextcheckpoint, short recordtime, string recordholder +const int RACE_NET_CHECKPOINT_HIT_RACE = 3; // byte checkpoint, short delta, byte lapsdelta, string opponent +const int RACE_NET_CHECKPOINT_HIT_RACE_BY_OPPONENT = 4; // byte checkpoint, short delta, byte lapsdelta, string opponent +const int RACE_NET_CHECKPOINT_NEXT_SPEC_QUALIFYING = 5; // byte nextcheckpoint, float laptime, short recordtime, string recordholder +const int RACE_NET_PENALTY_RACE = 6; // byte penaltytime, string reason +const int RACE_NET_PENALTY_QUALIFYING = 7; // byte penaltytime, string reason +const int RACE_NET_SERVER_RECORD = 8; // server record, sent to client +const int RACE_NET_SPEED_AWARD = 9; // speed award, sent to client +const int RACE_NET_SPEED_AWARD_BEST = 10; // all time best speed award, sent to client +const int RACE_NET_SERVER_RANKINGS = 11; +const int RACE_NET_SERVER_STATUS = 12; + +REGISTER_NET_LINKED(_ENT_CLIENT_INIT) +#ifdef CSQC +NET_HANDLE(_ENT_CLIENT_INIT, bool isnew) { make_pure(this); return true; } +#endif +/** Sent as a temp entity from a persistent linked entity */ +REGISTER_NET_TEMP(ENT_CLIENT_INIT) + +REGISTER_NET_LINKED(ENT_CLIENT_SCORES_INFO) +REGISTER_NET_LINKED(ENT_CLIENT_SCORES) +REGISTER_NET_LINKED(ENT_CLIENT_TEAMSCORES) +REGISTER_NET_LINKED(ENT_CLIENT_NAGGER) // flags [votecalledvote] +REGISTER_NET_LINKED(ENT_CLIENT_RADARLINK) // flags [startorigin] [endorigin] [startcolor+16*endcolor] +REGISTER_NET_LINKED(ENT_CLIENT_PROJECTILE) +REGISTER_NET_LINKED(ENT_CLIENT_MAPVOTE) +REGISTER_NET_LINKED(ENT_CLIENT_CLIENTDATA) +REGISTER_NET_LINKED(ENT_CLIENT_RANDOMSEED) +REGISTER_NET_LINKED(ENT_CLIENT_ACCURACY) +REGISTER_NET_LINKED(ENT_CLIENT_ELIMINATEDPLAYERS) + +REGISTER_NET_LINKED(ENT_CLIENT_MODEL) + +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE) +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_CAMERA) +REGISTER_NET_LINKED(ENT_CLIENT_WARPZONE_TELEPORTED) + +REGISTER_NET_LINKED(ENT_CLIENT_ARC_BEAM) +REGISTER_NET_LINKED(ENT_CLIENT_HOOK) +REGISTER_NET_LINKED(ENT_CLIENT_TUBANOTE) + +REGISTER_NET_LINKED(ENT_CLIENT_SPAWNPOINT) +REGISTER_NET_LINKED(ENT_CLIENT_SPAWNEVENT) +REGISTER_NET_LINKED(ENT_CLIENT_WALL) diff --git a/qcsrc/common/net_notice.qc b/qcsrc/common/net_notice.qc index 1e1726ae45..20d1351ebc 100644 --- a/qcsrc/common/net_notice.qc +++ b/qcsrc/common/net_notice.qc @@ -41,24 +41,30 @@ NET_HANDLE(TE_CSQC_SVNOTICE, bool isNew) return true; } entity cl_notices; -STATIC_INIT(cl_notice) -{ - cl_notices = LL_NEW(); -} void cl_notice_read() { entity _notice = new_pure(sv_notice); _notice.netname = strzone(ReadString()); _notice.alpha = ReadLong() + time; _notice.skin = ReadByte(); + if(!cl_notices) + cl_notices = LL_NEW(); LL_PUSH(cl_notices, _notice); } void cl_notice_run() { + if (!cl_notices) + return; + bool flag = false; LL_EACH(cl_notices, it.alpha > time, { flag = true; break; }); - if (!flag) return; + if (!flag) + { + LL_DELETE(cl_notices); + return; + } + const int M1 = 30; const int M2 = 10; @@ -76,10 +82,18 @@ void cl_notice_run() vector v3 = v1 + '10 10 0'; #define OUT(s, z) MACRO_BEGIN { drawcolorcodedstring(v3, s, '1 1 0' * z, 1, DRAWFLAG_NORMAL); v3.y += z + 4; } MACRO_END + float cur_time = 0; + float time_width = 48; OUT(_("^1Server notices:"), 32); LL_EACH(cl_notices, it.alpha > time, { - string s = sprintf(_("^7%s (^3%d sec left)"), it.netname , rint(it.alpha - time)); - OUT(s, 16); + if(it.alpha - cur_time > 0.1) + { + cur_time = it.alpha; + string s = sprintf("^3%d", ceil(cur_time - time)); + drawcolorcodedstring(v3 + eX * 0.5 * (time_width - stringwidth(s, true, '1 1 0' * 16)), s, '1 1 0' * 16, 1, DRAWFLAG_NORMAL); + v3.x = v1.x + 10 + time_width; + } + OUT(it.netname, 16); }); #undef OUT } diff --git a/qcsrc/common/net_notice.qh b/qcsrc/common/net_notice.qh index b36f631fe2..1134205185 100644 --- a/qcsrc/common/net_notice.qh +++ b/qcsrc/common/net_notice.qh @@ -1,5 +1,4 @@ -#ifndef NET_NOTICE_H -#define NET_NOTICE_H +#pragma once #ifdef SVQC string autocvar_sv_join_notices; @@ -13,5 +12,3 @@ void sv_notice_join(entity _to); #ifdef CSQC void cl_notice_read(); #endif - -#endif diff --git a/qcsrc/common/notifications/all.inc b/qcsrc/common/notifications/all.inc index 42cf6a2149..92fd5df1ac 100644 --- a/qcsrc/common/notifications/all.inc +++ b/qcsrc/common/notifications/all.inc @@ -387,6 +387,8 @@ MSG_INFO_NOTIF(MONSTERS_DISABLED, 1, 0, 0, "", "", "", _("^BGMonsters are currently disabled"), "") + MULTITEAM_INFO(NEXBALL_RETURN_HELD, 4, 1, 0, 0, "", "", "", _("^BGThe ^TC^TT^BG team held the ball for too long"), "", NAME) + MSG_INFO_NOTIF(ONSLAUGHT_CAPTURE, 1, 2, 0, "s1 s2", "", "", _("^BG%s^BG captured %s^BG control point"), "") MULTITEAM_INFO(ONSLAUGHT_CPDESTROYED, 4, 1, 2, 0, "s1 s2", "", "", _("^TC^TT^BG team %s^BG control point has been destroyed by %s"), "", NAME) MULTITEAM_INFO(ONSLAUGHT_GENDESTROYED, 4, 1, 0, 0, "", "", "", _("^TC^TT^BG generator has been destroyed"), "", GENERATOR) @@ -478,7 +480,7 @@ MSG_INFO_NOTIF(WEAPON_SEEKER_MURDER_SPRAY, 1, 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, 1, 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, 1, 2, 1, "s1 s2loc spree_lost", "s1", "weaponseeker", _("^BG%s^K1 played with tiny Seeker rockets%s%s"), "") - MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER, 1, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshotgun", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "") + MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER, 1, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshockwave", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shockwave%s%s"), "") MSG_INFO_NOTIF(WEAPON_SHOCKWAVE_MURDER_SLAP, 1, 3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1", "notify_melee_shotgun", _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shockwave%s%s"), "") MSG_INFO_NOTIF(WEAPON_SHOTGUN_MURDER, 1, 3, 2, "spree_inf s1 s2 s3loc spree_end", "s2 s1", "weaponshotgun", _("^BG%s%s^K1 was gunned down by ^BG%s^K1's Shotgun%s%s"), "") MSG_INFO_NOTIF(WEAPON_SHOTGUN_MURDER_SLAP, 1, 3, 2, "spree_inf s2 s1 s3loc spree_end", "s2 s1", "notify_melee_shotgun", _("^BG%s%s^K1 slapped ^BG%s^K1 around a bit with a large Shotgun%s%s"), "") @@ -505,6 +507,7 @@ MSG_CENTER_NOTIF(ASSAULT_ATTACKING, 1, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are attacking!"), "") MSG_CENTER_NOTIF(ASSAULT_DEFENDING, 1, 0, 0, "", CPID_ASSAULT_ROLE, "0 0", _("^BGYou are defending!"), "") + MSG_CENTER_NOTIF(ASSAULT_OBJ_DESTROYED, 1, 0, 1, "f1time", CPID_ASSAULT_ROLE, "0 0", _("^BGObjective destroyed in ^F4%s^BG!"), "") MSG_CENTER_NOTIF(COUNTDOWN_BEGIN, 1, 0, 0, "", CPID_ROUND, "2 0", _("^F4Begin!"), "") MSG_CENTER_NOTIF(COUNTDOWN_GAMESTART, 1, 0, 1, "", CPID_ROUND, "1 f1", _("^F4Game starts in ^COUNT"), "") @@ -528,7 +531,7 @@ MSG_CENTER_NOTIF(CTF_PASS_OTHER_NEUTRAL, 1, 2, 0, "s1 s2", CPID_CTF_PASS, "0 0", _("^BG%s^BG passed the flag to %s"), "") MULTITEAM_CENTER(CTF_PASS_RECEIVED, 4, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the ^TC^TT^BG flag from %s"), "", FLAG) MSG_CENTER_NOTIF(CTF_PASS_RECEIVED_NEUTRAL, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou received the flag from %s"), "") - MSG_CENTER_NOTIF(CTF_PASS_REQUESTED, 1, 1, 0, "s1 pass_key", CPID_CTF_PASS, "0 0", _("^BG%s^BG requests you to pass the flag%s"), "") + MSG_CENTER_NOTIF(CTF_PASS_REQUESTED, 1, 1, 0, "pass_key s1", CPID_CTF_PASS, "0 0", _("^BGPress ^F2%s^BG to receive the flag from %s^BG"), "") MSG_CENTER_NOTIF(CTF_PASS_REQUESTING, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGRequesting %s^BG to pass you the flag"), "") MULTITEAM_CENTER(CTF_PASS_SENT, 4, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the ^TC^TT^BG flag to %s"), "", FLAG) MSG_CENTER_NOTIF(CTF_PASS_SENT_NEUTRAL, 1, 1, 0, "s1", CPID_CTF_PASS, "0 0", _("^BGYou passed the flag to %s"), "") @@ -546,20 +549,50 @@ MULTITEAM_CENTER(CTF_PICKUP_TEAM_VERBOSE, 4, 1, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the ^TC^TT^BG flag! Protect them!"), "", FLAG) MSG_CENTER_NOTIF(CTF_PICKUP_TEAM_NEUTRAL, 1, 1, 0, "s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate^BG got the flag! Protect them!"), "") MSG_CENTER_NOTIF(CTF_PICKUP_TEAM_VERBOSE_NEUTRAL, 1, 2, 0, "s1 s2 s1", CPID_CTF_LOWPRIO, "0 0", _("^BGYour %steam mate (^BG%s%s)^BG got the flag! Protect them!"), "") + MSG_CENTER_NOTIF(CTF_PICKUP_VISIBLE, 1, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGEnemies can now see you on radar!"), "") MULTITEAM_CENTER(CTF_RETURN, 4, 1, 0, 0, "", CPID_CTF_LOWPRIO, "0 0", _("^BGYou returned the ^TC^TT^BG flag!"), "", FLAG) MSG_CENTER_NOTIF(CTF_STALEMATE_CARRIER, 1, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGStalemate! Enemies can now see you on radar!"), "") MSG_CENTER_NOTIF(CTF_STALEMATE_OTHER, 1, 0, 0, "", CPID_STALEMATE, "0 0", _("^BGStalemate! Flag carriers can now be seen by enemies on radar!"), "") - MSG_CENTER_NOTIF(DEATH_MURDER_FRAG, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", _("^K3%sYou fragged ^BG%s"), _("^K3%sYou scored against ^BG%s")) - MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", _("^K1%sYou were fragged by ^BG%s"), _("^K1%sYou were scored against by ^BG%s")) - MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", _("^K1%sYou were fragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^BG%s")) - MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", _("^K3%sYou fragged ^BG%s^BG%s"), _("^K3%sYou scored against ^BG%s^BG%s")) - MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", _("^K1%sYou typefragged ^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing")) - MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", _("^K1%sYou were typefragged by ^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing!")) - MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", _("^K1%sYou were typefragged by ^BG%s^BG%s"), _("^K1%sYou were scored against by ^BG%s^K1 while typing^BG%s")) - MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", _("^K1%sYou typefragged ^BG%s^BG%s"), _("^K1%sYou scored against ^BG%s^K1 while they were typing^BG%s")) - - MSG_CENTER_NOTIF(NADE_THROW, 1, 0, 0, "", CPID_NADES, "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the nade!"), "") + #define VERBOSE_MURDER(type) strcat(MURDER_##type, "^BG%s") + + #define MURDER_FRAG _("^K3%sYou fragged ^BG%s") + #define MURDER_FRAG2 _("^K3%sYou scored against ^BG%s") + #define MURDER_FRAGGED _("^K1%sYou were fragged by ^BG%s") + #define MURDER_FRAGGED2 _("^K1%sYou were scored against by ^BG%s") + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAG, MURDER_FRAG2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAGGED, MURDER_FRAGGED2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", VERBOSE_MURDER(FRAGGED), VERBOSE_MURDER(FRAGGED2) ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", VERBOSE_MURDER(FRAG), VERBOSE_MURDER(FRAG2) ) + + #define MURDER_FRAG_FIRE _("^K3%sYou burned ^BG%s") + #define MURDER_FRAG_FIRE2 _("^K3%sYou scored against ^BG%s") + #define MURDER_FRAGGED_FIRE _("^K1%sYou were burned by ^BG%s") + #define MURDER_FRAGGED_FIRE2 _("^K1%sYou were scored against by ^BG%s") + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FIRE, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAG_FIRE, MURDER_FRAG_FIRE2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FIRE, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAGGED_FIRE, MURDER_FRAGGED_FIRE2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FIRE_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", VERBOSE_MURDER(FRAGGED_FIRE), VERBOSE_MURDER(FRAGGED_FIRE2) ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FIRE_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", VERBOSE_MURDER(FRAG_FIRE), VERBOSE_MURDER(FRAG_FIRE2) ) + + #define MURDER_FRAG_FREEZE _("^K3%sYou froze ^BG%s") + #define MURDER_FRAG_FREEZE2 _("^K3%sYou scored against ^BG%s") + #define MURDER_FRAGGED_FREEZE _("^K1%sYou were frozen by ^BG%s") + #define MURDER_FRAGGED_FREEZE2 _("^K1%sYou were scored against by ^BG%s") + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FREEZE, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAG_FREEZE, MURDER_FRAG_FREEZE2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FREEZE, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_FRAGGED_FREEZE, MURDER_FRAGGED_FREEZE2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAGGED_FREEZE_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", VERBOSE_MURDER(FRAGGED_FREEZE), VERBOSE_MURDER(FRAGGED_FREEZE2)) + MSG_CENTER_NOTIF(DEATH_MURDER_FRAG_FREEZE_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", VERBOSE_MURDER(FRAG_FREEZE), VERBOSE_MURDER(FRAG_FREEZE2) ) + + #define MURDER_TYPEFRAG _("^K1%sYou typefragged ^BG%s") + #define MURDER_TYPEFRAG2 _("^K1%sYou scored against ^BG%s^K1 while they were typing") + #define MURDER_TYPEFRAGGED _("^K1%sYou were typefragged by ^BG%s") + #define MURDER_TYPEFRAGGED2 _("^K1%sYou were scored against by ^BG%s^K1 while typing") + MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_TYPEFRAG, MURDER_TYPEFRAG2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED, 1, 1, 1, "spree_cen s1", CPID_Null, "0 0", MURDER_TYPEFRAGGED, MURDER_TYPEFRAGGED2 ) + MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAGGED_VERBOSE, 1, 1, 4, "spree_cen s1 frag_stats", CPID_Null, "0 0", VERBOSE_MURDER(TYPEFRAGGED), VERBOSE_MURDER(TYPEFRAGGED2) ) + MSG_CENTER_NOTIF(DEATH_MURDER_TYPEFRAG_VERBOSE, 1, 1, 2, "spree_cen s1 frag_ping", CPID_Null, "0 0", VERBOSE_MURDER(TYPEFRAG), VERBOSE_MURDER(TYPEFRAG2) ) + + MSG_CENTER_NOTIF(NADE_THROW, 1, 0, 0, "nade_key", CPID_NADES, "0 0", _("^BGPress ^F2%s^BG again to toss the nade!"), "") MSG_CENTER_NOTIF(NADE_BONUS, 1, 0, 0, "", CPID_NADES, "0 0", _("^F2You got a ^K1BONUS GRENADE^F2!"), "") MSG_CENTER_NOTIF(DEATH_SELF_AUTOTEAMCHANGE, 1, 0, 1, "death_team", CPID_Null, "0 0", _("^BGYou have been moved into a different team\nYou are now on: %s"), "") @@ -609,8 +642,6 @@ MSG_CENTER_NOTIF(EXTRALIVES, 1, 0, 0, "", CPID_Null, "0 0", _("^F2You picked up some extra lives"), "") - MSG_CENTER_NOTIF(FREEZETAG_FREEZE, 1, 1, 0, "s1", CPID_Null, "0 0", _("^K3You froze ^BG%s"), "") - MSG_CENTER_NOTIF(FREEZETAG_FROZEN, 1, 1, 0, "s1", CPID_Null, "0 0", _("^K1You were frozen by ^BG%s"), "") MSG_CENTER_NOTIF(FREEZETAG_REVIVE, 1, 1, 0, "s1", CPID_Null, "0 0", _("^K3You revived ^BG%s"), "") MSG_CENTER_NOTIF(FREEZETAG_REVIVE_SELF, 1, 0, 0, "", CPID_Null, "0 0", _("^K3You revived yourself"), "") MSG_CENTER_NOTIF(FREEZETAG_REVIVED, 1, 1, 0, "s1", CPID_Null, "0 0", _("^K3You were revived by ^BG%s"), "") @@ -667,15 +698,13 @@ MSG_CENTER_NOTIF(NIX_COUNTDOWN, 1, 0, 2, "item_wepname", CPID_NIX, "1 f2", _("^F2^COUNT^BG until weapon change...\nNext weapon: ^F1%s"), "") MSG_CENTER_NOTIF(NIX_NEWWEAPON, 1, 0, 1, "item_wepname", CPID_NIX, "0 0", _("^F2Active weapon: ^F1%s"), "") - MSG_CENTER_NOTIF(NADE, 1, 0, 0, "", CPID_Null, "0 0", _("^BGPress ^F2DROPWEAPON^BG again to toss the grenade!"), "") - MSG_CENTER_NOTIF(ONS_CAPTURE, 1, 1, 0, "s1", CPID_ONSLAUGHT, "0 0", _("^BGYou captured %s^BG control point"), "") MULTITEAM_CENTER(ONS_CAPTURE, 4, 1, 1, 0, "s1", CPID_ONSLAUGHT, "0 0", _("^TC^TT^BG team captured %s^BG control point"), "", NAME) MSG_CENTER_NOTIF(ONS_CONTROLPOINT_SHIELDED, 1, 0, 0, "", CPID_ONS_CAPSHIELD, "0 0", _("^BGThis control point currently cannot be captured"), "") MSG_CENTER_NOTIF(ONS_GENERATOR_SHIELDED, 1, 0, 0, "", CPID_ONS_CAPSHIELD, "0 0", _("^BGThe enemy generator cannot be destroyed yet\n^F2Capture some control points to unshield it"), "") MULTITEAM_CENTER(ONS_NOTSHIELDED, 4, 1, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^BGThe ^TCenemy^BG generator is no longer shielded!"), "", NAME) MSG_CENTER_NOTIF(ONS_NOTSHIELDED_TEAM, 1, 0, 0, "", CPID_ONSLAUGHT, "0 0", _("^K1Your generator is NOT shielded!\n^BGRe-capture control points to shield it!"), "") - MSG_CENTER_NOTIF(ONS_TELEPORT, 1, 0, 0, "pass_key", CPID_ONSLAUGHT, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to teleport"), "") + MSG_CENTER_NOTIF(ONS_TELEPORT, 1, 0, 0, "pass_key", CPID_ONSLAUGHT, "0 0", _("^BGPress ^F2%s^BG to teleport"), "") MSG_CENTER_NOTIF(ONS_TELEPORT_ANTISPAM, 1, 0, 1, "f1secs", CPID_ONSLAUGHT, "0 0", _("^BGTeleporting disabled for %s"), "") MSG_CENTER_NOTIF(OVERTIME_FRAG, 1, 0, 0, "", CPID_OVERTIME, "0 0", _("^F2Now playing ^F4OVERTIME^F2!\nKeep fragging until we have a winner!"), _("^F2Now playing ^F4OVERTIME^F2!\nKeep scoring until we have a winner!")) @@ -716,13 +745,13 @@ MSG_CENTER_NOTIF(TEAMCHANGE_SUICIDE, 1, 0, 1, "", CPID_TEAMCHANGE, "1 f1", _("^K1Suicide in ^COUNT"), "") MSG_CENTER_NOTIF(TIMEOUT_BEGINNING, 1, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout begins in ^COUNT"), "") - MSG_CENTER_NOTIF(TIMEOUT_ENDING, 1, 0, 1, "", CPID_TIMEOUT, "1 f1", _("^F4Timeout ends in ^COUNT"), "") + MSG_CENTER_NOTIF(TIMEOUT_ENDING, 1, 0, 1, "", CPID_TIMEIN, "1 f1", _("^F4Timeout ends in ^COUNT"), "") MSG_CENTER_NOTIF(JOIN_PREVENT_MINIGAME, 1, 0, 0, "", CPID_Null, "0 0", _("^K1Cannot join given minigame session!"), "" ) - MSG_CENTER_NOTIF(VEHICLE_ENTER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter/exit the vehicle"), "") - MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to enter the vehicle gunner"), "") - MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2DROPFLAG%s^BG to steal this vehicle"), "") + MSG_CENTER_NOTIF(VEHICLE_ENTER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to enter/exit the vehicle"), "") + MSG_CENTER_NOTIF(VEHICLE_ENTER_GUNNER, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to enter the vehicle gunner"), "") + MSG_CENTER_NOTIF(VEHICLE_ENTER_STEAL, 1, 0, 0, "pass_key", CPID_VEHICLES, "0 0", _("^BGPress ^F2%s^BG to steal this vehicle"), "") MSG_CENTER_NOTIF(VEHICLE_STEAL, 1, 0, 0, "", CPID_VEHICLES_OTHER, "0 0", _("^F2The enemy is stealing one of your vehicles!\n^F4Stop them!"), "") MSG_CENTER_NOTIF(VEHICLE_STEAL_SELF, 1, 0, 0, "", CPID_VEHICLES_OTHER, "4 0", _("^F2You have stolen the enemy's vehicle, you are now visible on their radar!"), "") @@ -922,5 +951,9 @@ MSG_CHOICE_NOTIF(CTF_PICKUP_ENEMY_TEAM, 1, 2, MSG_CENTER, CENTER_CTF_PICKUP_ENEMY_TEAM, CENTER_CTF_PICKUP_ENEMY_TEAM_VERBOSE) MSG_CHOICE_NOTIF(FRAG, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAG, CENTER_DEATH_MURDER_FRAG_VERBOSE) MSG_CHOICE_NOTIF(FRAGGED, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED, CENTER_DEATH_MURDER_FRAGGED_VERBOSE) + MSG_CHOICE_NOTIF(FRAG_FIRE, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAG_FIRE, CENTER_DEATH_MURDER_FRAG_FIRE_VERBOSE) + MSG_CHOICE_NOTIF(FRAGGED_FIRE, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED_FIRE, CENTER_DEATH_MURDER_FRAGGED_FIRE_VERBOSE) + MSG_CHOICE_NOTIF(FRAG_FREEZE, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAG_FREEZE, CENTER_DEATH_MURDER_FRAG_FREEZE_VERBOSE) + MSG_CHOICE_NOTIF(FRAGGED_FREEZE, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_FRAGGED_FREEZE, CENTER_DEATH_MURDER_FRAGGED_FREEZE_VERBOSE) MSG_CHOICE_NOTIF(TYPEFRAG, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAG, CENTER_DEATH_MURDER_TYPEFRAG_VERBOSE) MSG_CHOICE_NOTIF(TYPEFRAGGED, 1, 1, MSG_CENTER, CENTER_DEATH_MURDER_TYPEFRAGGED, CENTER_DEATH_MURDER_TYPEFRAGGED_VERBOSE) diff --git a/qcsrc/common/notifications/all.qc b/qcsrc/common/notifications/all.qc index 853280d6e0..5cf3e1c6b1 100644 --- a/qcsrc/common/notifications/all.qc +++ b/qcsrc/common/notifications/all.qc @@ -1,14 +1,15 @@ +#include "all.qh" #if defined(CSQC) #include #elif defined(MENUQC) #elif defined(SVQC) #include + #include #include #include #include #include - #include "all.qh" - #include + #include #endif // ================================================ @@ -153,7 +154,7 @@ void Destroy_Notification_Entity(entity notif) if (notif.nent_icon != "") strunzone(notif.nent_icon); if (notif.nent_durcnt != "") strunzone(notif.nent_durcnt); if (notif.nent_string != "") strunzone(notif.nent_string); - remove(notif); + delete(notif); } void Destroy_All_Notifications() @@ -572,7 +573,7 @@ void Create_Notification_Entity_InfoCenter(entity notif, } else if(icon != "") { - LOG_WARNINGF( + LOG_WARNF( ( "^1NOTIFICATION HAS ICON BUT NO HUDARGS: " "^7net_type = %s, net_name = %s.\n" @@ -589,7 +590,7 @@ void Create_Notification_Entity_InfoCenter(entity notif, if (cpid == CPID_Null && durcnt != "0 0") { - LOG_WARNINGF( + LOG_WARNF( ( "Notification has durcnt but no cpid: " "net_type = %s, net_name = %s." @@ -1210,7 +1211,7 @@ void Local_Notification(MSG net_type, Notification net_name, ...count) Get_Notif_TypeName(net_type) )); #endif - LOG_WARNINGF("Incorrect usage of Local_Notification: %s\n", "Null notification"); + LOG_WARNF("Incorrect usage of Local_Notification: %s", "Null notification"); return; } @@ -1491,7 +1492,7 @@ void Net_Notification_Remove(entity this) )); #endif for (int i = 0; i < this.nent_stringcount; ++i) { if (this.nent_strings[i]) strunzone(this.nent_strings[i]); } - remove(this); + delete(this); } bool Net_Write_Notification(entity this, entity client, int sf) @@ -1523,7 +1524,7 @@ void Kill_Notification( #endif string checkargs = Notification_CheckArgs(broadcast, client); - if (checkargs != "") { LOG_WARNINGF("Incorrect usage of Kill_Notification: %s", checkargs); return; } + if (checkargs != "") { LOG_WARNF("Incorrect usage of Kill_Notification: %s", checkargs); return; } entity net_notif = new_pure(net_kill_notification); net_notif.nent_broadcast = broadcast; @@ -1532,8 +1533,8 @@ void Kill_Notification( net_notif.nent_net_name = ORDINAL(net_cpid); Net_LinkEntity(net_notif, false, autocvar_notification_lifetime_runtime, Net_Write_Notification); - FOREACH_ENTITY_CLASS( - "net_notification", + IL_EACH( + g_notifications, (it.owner.nent_type == net_type || net_type == MSG_Null) && (it.owner.nent_cpid == net_cpid || net_cpid == CPID_Null), { it.nent_net_name = -1; @@ -1561,7 +1562,7 @@ void Send_Notification( if (!notif) { - LOG_WARNING("Send_Notification: Could not find notification entity!"); + LOG_WARN("Send_Notification: Could not find notification entity!"); return; } @@ -1570,7 +1571,7 @@ void Send_Notification( if (!net_name) { checkargs = sprintf("No notification provided! %s", checkargs); } if (checkargs != "") { - LOG_WARNINGF("Incorrect usage of Send_Notification: %s", checkargs); + LOG_WARNF("Incorrect usage of Send_Notification: %s", checkargs); return; } @@ -1594,7 +1595,7 @@ void Send_Notification( if ((notif.nent_stringcount + notif.nent_floatcount) != count) { - LOG_WARNINGF( + LOG_WARNF( "Argument mismatch for Send_Notification(%s, ...)! " "stringcount(%d) + floatcount(%d) != count(%d)\n" "Check the definition and function call for accuracy...?\n", @@ -1682,6 +1683,7 @@ void Send_Notification( else { entity net_notif = new_pure(net_notification); + IL_PUSH(g_notifications, net_notif); net_notif.owner = notif; net_notif.nent_broadcast = broadcast; net_notif.nent_client = client; diff --git a/qcsrc/common/notifications/all.qh b/qcsrc/common/notifications/all.qh index 7dcfec3534..5e5fbf1d60 100644 --- a/qcsrc/common/notifications/all.qh +++ b/qcsrc/common/notifications/all.qh @@ -1,7 +1,6 @@ -#ifndef NOTIFICATIONS_H -#define NOTIFICATIONS_H +#pragma once -#include +#include #include #include @@ -33,7 +32,7 @@ string Get_Notif_TypeName(MSG net_type) case MSG_MULTI: return "MSG_MULTI"; case MSG_CHOICE: return "MSG_CHOICE"; } - LOG_WARNINGF("Get_Notif_TypeName(%d): Improper net type!\n", ORDINAL(net_type)); + LOG_WARNF("Get_Notif_TypeName(%d): Improper net type!", ORDINAL(net_type)); return ""; } @@ -66,6 +65,7 @@ ENUMCLASS(CPID) CASE(CPID, RACE_FINISHLAP) CASE(CPID, TEAMCHANGE) CASE(CPID, TIMEOUT) + CASE(CPID, TIMEIN) CASE(CPID, VEHICLES) CASE(CPID, VEHICLES_OTHER) /** always last */ @@ -76,8 +76,7 @@ USING(Notification, entity); // used for notification system multi-team identifiers #define APP_TEAM_NUM(num, prefix) ((num == NUM_TEAM_1) ? prefix##_RED : ((num == NUM_TEAM_2) ? prefix##_BLUE : ((num == NUM_TEAM_3) ? prefix##_YELLOW : prefix##_PINK))) -/** @deprecated use APP_TEAM_NUM */ -#define APP_TEAM_ENT(ent, prefix) APP_TEAM_NUM(ent.team, prefix) +#define APP_NUM(num, prefix) ((num) ? APP_TEAM_NUM(num, prefix) : prefix##_NEUTRAL) #define EIGHT_VARS_TO_VARARGS_VARLIST \ VARITEM(1, 0, s1) \ @@ -160,7 +159,7 @@ GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt" { case CMD_REQUEST_COMMAND: { - #ifndef MENUQC + #ifdef GAMEQC string filename = argv(1); bool alsoprint = false; if (filename == "") @@ -201,8 +200,8 @@ GENERIC_COMMAND(dumpnotifs, "Dump all notifications into notifications_dump.txt" } } -float autocvar_notification_debug = false; #ifdef NOTIFICATIONS_DEBUG +bool autocvar_notification_debug = false; void Debug_Notification(string input) { switch (autocvar_notification_debug) @@ -226,6 +225,11 @@ string prev_soundfile; float prev_soundtime; #endif +#ifdef SVQC +IntrusiveList g_notifications; +STATIC_INIT(g_notifications) { g_notifications = IL_NEW(); } +#endif + #ifdef SVQC ENUMCLASS(NOTIF) /** send to one client and their spectators */ @@ -253,7 +257,7 @@ string Get_Notif_BroadcastName(NOTIF broadcast) case NOTIF_TEAM: return "NOTIF_TEAM"; case NOTIF_TEAM_EXCEPT: return "NOTIF_TEAM_EXCEPT"; } - LOG_WARNINGF("Get_Notif_BroadcastName(%d): Improper broadcast!\n", broadcast); + LOG_WARNF("Get_Notif_BroadcastName(%d): Improper broadcast!", broadcast); return ""; } @@ -293,8 +297,10 @@ float autocvar_notification_show_sprees_info = 3; // 0 = off, 1 = target only, 2 float autocvar_notification_show_sprees_info_newline = true; float autocvar_notification_show_sprees_info_specialonly = true; float autocvar_notification_errors_are_fatal = true; +#ifdef SVQC float autocvar_notification_lifetime_runtime = 0.5; float autocvar_notification_lifetime_mapload = 10; +#endif #ifdef SVQC .float FRAG_VERBOSE; @@ -349,6 +355,7 @@ float autocvar_notification_show_sprees_center_specialonly = true; race_diff: show time difference between f2 and f3 missing_teams: show which teams still need players pass_key: find the keybind for "passing" or "dropping" in CTF game mode + nade_key: find the keybind for nade throwing frag_ping: show the ping of a player frag_stats: show health/armor/ping of a player frag_pos: show score status and position in the match of a player @@ -408,7 +415,8 @@ string BUFF_NAME(int i); ARG_CASE(ARG_CS_SV, "race_col", CCR(((f1 == 1) ? "^F1" : "^F2"))) \ ARG_CASE(ARG_CS_SV, "race_diff", ((f2 > f3) ? sprintf(CCR("^1[+%s]"), mmssss(f2 - f3)) : sprintf(CCR("^2[-%s]"), mmssss(f3 - f2)))) \ ARG_CASE(ARG_CS, "missing_teams", notif_arg_missing_teams(f1)) \ - ARG_CASE(ARG_CS, "pass_key", ((((tmp_s = getcommandkey("pass", "+use")) != "pass") && !(strstrofs(tmp_s, "not bound", 0) >= 0)) ? sprintf(CCR(_(" ^F1(Press %s)")), tmp_s) : "")) \ + ARG_CASE(ARG_CS, "pass_key", getcommandkey(_("drop flag"), "+use")) \ + ARG_CASE(ARG_CS, "nade_key", getcommandkey(_("throw nade"), "dropweapon")) \ ARG_CASE(ARG_CS, "frag_ping", notif_arg_frag_ping(true, f2)) \ ARG_CASE(ARG_CS, "frag_stats", notif_arg_frag_stats(f2, f3, f4)) \ /*ARG_CASE(ARG_CS, "frag_pos", ((Should_Print_Score_Pos(f1)) ? sprintf("\n^BG%s", Read_Score_Pos(f1)) : ""))*/ \ @@ -685,7 +693,7 @@ Notification Get_Notif_Ent(MSG net_type, int net_name) { Notification it = _Notifications_from(net_name, NULL); if (it.nent_type != net_type) { - LOG_WARNINGF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!\n", + LOG_WARNF("Get_Notif_Ent(%s (%d), %s (%d)): Improper net type '%s'!", Get_Notif_TypeName(net_type), net_type, it.registered_id, net_name, Get_Notif_TypeName(it.nent_type) @@ -800,5 +808,3 @@ REGISTRY_END(Notifications) } #include "all.inc" - -#endif diff --git a/qcsrc/common/physics/_mod.inc b/qcsrc/common/physics/_mod.inc index 3a61cd4e9a..100aecae29 100644 --- a/qcsrc/common/physics/_mod.inc +++ b/qcsrc/common/physics/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/physics/_mod.qh b/qcsrc/common/physics/_mod.qh index 39dacad8e2..377a7b340d 100644 --- a/qcsrc/common/physics/_mod.qh +++ b/qcsrc/common/physics/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify #include #include + +#include diff --git a/qcsrc/common/physics/movelib.qh b/qcsrc/common/physics/movelib.qh index 250c3ba2b2..e0659a40c1 100644 --- a/qcsrc/common/physics/movelib.qh +++ b/qcsrc/common/physics/movelib.qh @@ -1,5 +1,4 @@ -#ifndef MOVELIB_H -#define MOVELIB_H +#pragma once #ifdef SVQC .vector moveto; @@ -49,5 +48,3 @@ Yed need to set v_up and v_forward (generally by calling makevectors) before cal #endif void movelib_groundalign4point(entity this, float spring_length, float spring_up, float blendrate, float _max); - -#endif diff --git a/qcsrc/common/physics/movetypes/_mod.inc b/qcsrc/common/physics/movetypes/_mod.inc index 4effcbd04a..5cb1d0bc4e 100644 --- a/qcsrc/common/physics/movetypes/_mod.inc +++ b/qcsrc/common/physics/movetypes/_mod.inc @@ -1,7 +1,6 @@ // generated file; do not modify #include #include -#include #include #include #include diff --git a/qcsrc/common/physics/movetypes/_mod.qh b/qcsrc/common/physics/movetypes/_mod.qh index 32ae3813c8..1b1241a0aa 100644 --- a/qcsrc/common/physics/movetypes/_mod.qh +++ b/qcsrc/common/physics/movetypes/_mod.qh @@ -1,7 +1,6 @@ // generated file; do not modify #include #include -#include #include #include #include diff --git a/qcsrc/common/physics/movetypes/all.inc b/qcsrc/common/physics/movetypes/all.inc index 322b3c4de1..70157d1862 100644 --- a/qcsrc/common/physics/movetypes/all.inc +++ b/qcsrc/common/physics/movetypes/all.inc @@ -1,4 +1,3 @@ -#include "push.qc" #include "toss.qc" #include "walk.qc" #include "step.qc" diff --git a/qcsrc/common/physics/movetypes/follow.qc b/qcsrc/common/physics/movetypes/follow.qc index 2d3e24f44c..3009069052 100644 --- a/qcsrc/common/physics/movetypes/follow.qc +++ b/qcsrc/common/physics/movetypes/follow.qc @@ -1,31 +1,30 @@ +#include "follow.qh" void _Movetype_Physics_Follow(entity this) // SV_Physics_Follow { - entity e = this.move_aiment; // TODO: networking? + entity e = this.aiment; - // LordHavoc: implemented rotation on MOVETYPE_FOLLOW objects - if(this.move_angles == this.move_punchangle) + if(e.angles == this.punchangle) { - this.move_origin = e.move_origin + this.view_ofs; + this.origin = e.origin + this.view_ofs; } else { vector ang, v; - ang_x = -this.move_punchangle_x; - ang_y = this.move_punchangle_y; - ang_z = this.move_punchangle_z; + ang_x = -this.punchangle_x; + ang_y = this.punchangle_y; + ang_z = this.punchangle_z; makevectors(ang); v_x = this.view_ofs_x * v_forward_x + this.view_ofs_y * v_right_x + this.view_ofs_z * v_up_x; v_y = this.view_ofs_x * v_forward_y + this.view_ofs_y * v_right_y + this.view_ofs_z * v_up_y; v_z = this.view_ofs_x * v_forward_z + this.view_ofs_y * v_right_z + this.view_ofs_z * v_up_z; - ang_x = -e.move_angles_x; - ang_y = e.move_angles_y; - ang_z = e.move_angles_z; + ang = e.angles; + ang_x = -e.angles_x; makevectors(ang); - this.move_origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.move_origin_x; - this.move_origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.move_origin_y; - this.move_origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.move_origin_z; + this.origin_x = v_x * v_forward_x + v_y * v_forward_y + v_z * v_forward_z + e.origin_x; + this.origin_x = v_x * v_right_x + v_y * v_right_y + v_z * v_right_z + e.origin_y; + this.origin_x = v_x * v_up_x + v_y * v_up_y + v_z * v_up_z + e.origin_z; } - this.move_angles = e.move_angles + this.v_angle; + this.angles = e.angles + this.v_angle; _Movetype_LinkEdict(this, false); } diff --git a/qcsrc/common/physics/movetypes/follow.qh b/qcsrc/common/physics/movetypes/follow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/follow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/movetypes.qc b/qcsrc/common/physics/movetypes/movetypes.qc index eee217200f..f0df80cd5d 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qc +++ b/qcsrc/common/physics/movetypes/movetypes.qc @@ -1,10 +1,10 @@ +#include "movetypes.qh" #include "../player.qh" #if defined(CSQC) #include #include #include - #include "movetypes.qh" #include #include #elif defined(MENUQC) @@ -12,6 +12,22 @@ #include #endif +#ifdef SVQC +void set_movetype(entity this, int mt) +{ + this.move_movetype = mt; + if (mt == MOVETYPE_PHYSICS || mt == MOVETYPE_PUSH || mt == MOVETYPE_FAKEPUSH) { + this.move_qcphysics = false; + } + this.movetype = (this.move_qcphysics) ? MOVETYPE_NONE : mt; +} +#elif defined(CSQC) +void set_movetype(entity this, int mt) +{ + this.move_movetype = mt; +} +#endif + void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction { /*float d, i; @@ -21,11 +37,11 @@ void _Movetype_WallFriction(entity this, vector stepnormal) // SV_WallFriction if(d < 0) { - i = (stepnormal * this.move_velocity); + i = (stepnormal * this.velocity); into = i * stepnormal; - side = this.move_velocity - into; - this.move_velocity_x = side.x * (1 * d); - this.move_velocity_y = side.y * (1 * d); + side = this.velocity - into; + this.velocity_x = side.x * (1 * d); + this.velocity_y = side.y * (1 * d); }*/ } @@ -46,23 +62,23 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma this.move_didgravity = 1; grav = dt * (PHYS_ENTGRAVITY(this) ? PHYS_ENTGRAVITY(this) : 1) * PHYS_GRAVITY(this); - if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) + if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this)) { if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.move_velocity_z -= grav * 0.5; + this.velocity_z -= grav * 0.5; else - this.move_velocity_z -= grav; + this.velocity_z -= grav; } } - original_velocity = primal_velocity = restore_velocity = this.move_velocity; + original_velocity = primal_velocity = restore_velocity = this.velocity; for(bumpcount = 0;bumpcount < MAX_CLIP_PLANES;bumpcount++) { - if(this.move_velocity == '0 0 0') + if(this.velocity == '0 0 0') break; - push = this.move_velocity * time_left; + push = this.velocity * time_left; _Movetype_PushEntity(this, push, true); if(trace_startsolid) { @@ -76,7 +92,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma // abort move if we're stuck in the world (and didn't make it out) if(trace_startsolid && trace_allsolid) { - this.move_velocity = restore_velocity; + this.velocity = restore_velocity; return 3; } @@ -99,14 +115,14 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma trace_ent = NULL; } - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; + SET_ONGROUND(this); + this.groundentity = trace_ent; } } else if(stepheight) { // step - handle it immediately - vector org = this.move_origin; + vector org = this.origin; vector steppush = '0 0 1' * stepheight; _Movetype_PushEntity(this, steppush, true); @@ -122,7 +138,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma break; } float trace2_fraction = trace_fraction; - steppush = '0 0 1' * (org_z - this.move_origin_z); + steppush = '0 0 1' * (org_z - this.origin_z); _Movetype_PushEntity(this, steppush, true); if(trace_startsolid) { @@ -131,15 +147,15 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma } // accept the new position if it made some progress... - if(fabs(this.move_origin_x - org_x) >= 0.03125 || fabs(this.move_origin_y - org_y) >= 0.03125) + if(fabs(this.origin_x - org_x) >= 0.03125 || fabs(this.origin_y - org_y) >= 0.03125) { - trace_endpos = this.move_origin; + trace_endpos = this.origin; time_left *= 1 - trace2_fraction; numplanes = 0; continue; } else - this.move_origin = org; + this.origin = org; } else { @@ -153,7 +169,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma if(my_trace_fraction >= 0.001) { // actually covered some distance - original_velocity = this.move_velocity; + original_velocity = this.velocity; numplanes = 0; } @@ -163,7 +179,7 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma if(numplanes >= MAX_CLIP_PLANES) { // this shouldn't really happen - this.move_velocity = '0 0 0'; + this.velocity = '0 0 0'; blocked = 3; break; } @@ -192,14 +208,14 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma if(i != numplanes) { // go along this plane - this.move_velocity = new_velocity; + this.velocity = new_velocity; } else { // go along the crease if(numplanes != 2) { - this.move_velocity = '0 0 0'; + this.velocity = '0 0 0'; blocked = 7; break; } @@ -211,29 +227,29 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma dir.x *= ilength; dir.y *= ilength; dir.z *= ilength; - float d = (dir * this.move_velocity); - this.move_velocity = dir * d; + float d = (dir * this.velocity); + this.velocity = dir * d; } // if current velocity is against the original velocity, // stop dead to avoid tiny occilations in sloping corners - if((this.move_velocity * primal_velocity) <= 0) + if((this.velocity * primal_velocity) <= 0) { - this.move_velocity = '0 0 0'; + this.velocity = '0 0 0'; break; } } // LordHavoc: this came from QW and allows you to get out of water more easily - if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.move_flags & FL_WATERJUMP) && !(blocked & 8)) - this.move_velocity = primal_velocity; + if(GAMEPLAYFIX_EASIERWATERJUMP(this) && (this.flags & FL_WATERJUMP) && !(blocked & 8)) + this.velocity = primal_velocity; if(applygravity) { - if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !(this.move_flags & FL_ONGROUND)) + if(!GAMEPLAYFIX_NOGRAVITYONGROUND || !IS_ONGROUND(this)) { if(GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.move_velocity_z -= grav * 0.5f; + this.velocity_z -= grav * 0.5f; } } @@ -242,98 +258,84 @@ int _Movetype_FlyMove(entity this, float dt, bool applygravity, vector stepnorma void _Movetype_CheckVelocity(entity this) // SV_CheckVelocity { - // if(vlen(this.move_velocity) < 0.0001) - // this.move_velocity = '0 0 0'; + // if(vlen(this.velocity) < 0.0001) + // this.velocity = '0 0 0'; } bool _Movetype_CheckWater(entity this) // SV_CheckWater { - vector point = this.move_origin; + vector point = this.origin; point.z += this.mins.z + 1; int nativecontents = pointcontents(point); - if(this.move_watertype && this.move_watertype != nativecontents) + if(this.watertype && this.watertype != nativecontents) { - // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.move_watertype, nativecontents); + // dprintf("_Movetype_CheckWater(): Original: '%d', New: '%d'\n", this.watertype, nativecontents); if(this.contentstransition) - this.contentstransition(this.move_watertype, nativecontents); + this.contentstransition(this.watertype, nativecontents); } - this.move_waterlevel = WATERLEVEL_NONE; - this.move_watertype = CONTENT_EMPTY; + this.waterlevel = WATERLEVEL_NONE; + this.watertype = CONTENT_EMPTY; int supercontents = Mod_Q1BSP_SuperContentsFromNativeContents(nativecontents); if(supercontents & DPCONTENTS_LIQUIDSMASK) { - this.move_watertype = nativecontents; - this.move_waterlevel = WATERLEVEL_WETFEET; - point.z = this.move_origin.z + (this.mins.z + this.maxs.z) * 0.5; + this.watertype = nativecontents; + this.waterlevel = WATERLEVEL_WETFEET; + point.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5; if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) { - this.move_waterlevel = WATERLEVEL_SWIMMING; - point.z = this.move_origin.z + this.view_ofs.z; + this.waterlevel = WATERLEVEL_SWIMMING; + point.z = this.origin.z + this.view_ofs.z; if(Mod_Q1BSP_SuperContentsFromNativeContents(pointcontents(point)) & DPCONTENTS_LIQUIDSMASK) - this.move_waterlevel = WATERLEVEL_SUBMERGED; + this.waterlevel = WATERLEVEL_SUBMERGED; } } - return this.move_waterlevel > 1; + return this.waterlevel > 1; } void _Movetype_CheckWaterTransition(entity ent) // SV_CheckWaterTransition { - int contents = pointcontents(ent.move_origin); + int contents = pointcontents(ent.origin); - if(!ent.move_watertype) + if(!ent.watertype) { // just spawned here - if(!autocvar_cl_gameplayfix_fixedcheckwatertransition) + if(!GAMEPLAYFIX_WATERTRANSITION(ent)) { - ent.move_watertype = contents; - ent.move_waterlevel = 1; + ent.watertype = contents; + ent.waterlevel = 1; return; } } - else if(ent.move_watertype != contents) + else if(ent.watertype != contents) { - // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.move_origin), pointcontents(ent.move_origin), ent.move_watertype, contents); + // dprintf("_Movetype_CheckWaterTransition(): Origin: %s, Direct: '%d', Original: '%d', New: '%d'\n", vtos(ent.origin), pointcontents(ent.origin), ent.watertype, contents); if(ent.contentstransition) - ent.contentstransition(ent.move_watertype, contents); + ent.contentstransition(ent.watertype, contents); } if(contents <= CONTENT_WATER) { - ent.move_watertype = contents; - ent.move_waterlevel = 1; + ent.watertype = contents; + ent.waterlevel = 1; } else { - ent.move_watertype = CONTENT_EMPTY; - ent.move_waterlevel = (autocvar_cl_gameplayfix_fixedcheckwatertransition ? 0 : contents); + ent.watertype = CONTENT_EMPTY; + ent.waterlevel = (GAMEPLAYFIX_WATERTRANSITION(ent) ? 0 : contents); } } void _Movetype_Impact(entity this, entity oth) // SV_Impact { - entity oldother = other; - if(gettouch(this)) - { - other = oth; - - gettouch(this)(this); - - other = oldother; - } + gettouch(this)(this, oth); if(gettouch(oth)) - { - other = this; - - gettouch(oth)(oth); - - other = oldother; - } + gettouch(oth)(oth, this); } void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGrid @@ -341,30 +343,24 @@ void _Movetype_LinkEdict_TouchAreaGrid(entity this) // SV_LinkEdict_TouchAreaGr if(this.solid == SOLID_NOT) return; - entity oldother = other; - FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, { if (it.solid == SOLID_TRIGGER && it != this) if (it.move_nomonsters != MOVE_NOMONSTERS && it.move_nomonsters != MOVE_WORLDONLY) if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax)) { - other = this; - trace_allsolid = false; trace_startsolid = false; trace_fraction = 1; trace_inwater = false; trace_inopen = true; - trace_endpos = it.move_origin; + trace_endpos = it.origin; trace_plane_normal = '0 0 1'; trace_plane_dist = 0; trace_ent = this; - gettouch(it)(it); + gettouch(it)(it, this); } }); - - other = oldother; } void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict @@ -381,10 +377,10 @@ void _Movetype_LinkEdict(entity this, bool touch_triggers) // SV_LinkEdict mi = this.mins; ma = this.maxs; } - mi += this.move_origin; - ma += this.move_origin; + mi += this.origin; + ma += this.origin; - if(this.move_flags & FL_ITEM) + if(this.flags & FL_ITEM) { mi.x -= 15; mi.y -= 15; @@ -414,18 +410,18 @@ entity _Movetype_TestEntityPosition_ent; bool _Movetype_TestEntityPosition(vector ofs) // SV_TestEntityPosition { entity this = _Movetype_TestEntityPosition_ent; -// vector org = this.move_origin + ofs; +// vector org = this.origin + ofs; int cont = this.dphitcontentsmask; this.dphitcontentsmask = DPCONTENTS_SOLID; - tracebox(this.move_origin, this.mins, this.maxs, this.move_origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); + tracebox(this.origin, this.mins, this.maxs, this.origin, ((this.move_movetype == MOVETYPE_FLY_WORLDONLY) ? MOVE_WORLDONLY : MOVE_NOMONSTERS), this); this.dphitcontentsmask = cont; if(trace_startsolid) return true; - if(vdist(trace_endpos - this.move_origin, >, 0.0001)) - this.move_origin = trace_endpos; + if(vdist(trace_endpos - this.origin, >, 0.0001)) + this.origin = trace_endpos; return false; } @@ -450,13 +446,13 @@ bool _Movetype_UnstickEntity(entity this) // SV_UnstickEntity X(17) #undef X { - LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)\n", - etof(this), this.classname, vtos(this.move_origin)); + LOG_DEBUGF("Can't unstick an entity (edict: %d, classname: %s, origin: %s)", + etof(this), this.classname, vtos(this.origin)); return false; } } - LOG_DEBUGF("Sucessfully unstuck an entity (edict: %d, classname: %s, origin: %s)\n", - etof(this), this.classname, vtos(this.move_origin)); + 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; } @@ -474,7 +470,7 @@ vector _Movetype_ClipVelocity(vector vel, vector norm, float f) // SV_ClipVeloc void _Movetype_PushEntityTrace(entity this, vector push) { - vector end = this.move_origin + push; + vector end = this.origin + push; int type; if(this.move_nomonsters) type = max(0, this.move_nomonsters); @@ -487,7 +483,7 @@ void _Movetype_PushEntityTrace(entity this, vector push) else type = MOVE_NORMAL; - tracebox(this.move_origin, this.mins, this.maxs, end, type, this); + tracebox(this.origin, this.mins, this.maxs, end, type, this); } float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // SV_PushEntity @@ -497,10 +493,10 @@ float _Movetype_PushEntity(entity this, vector push, bool failonstartsolid) // if(trace_startsolid && failonstartsolid) return trace_fraction; - this.move_origin = trace_endpos; + this.origin = trace_endpos; if(trace_fraction < 1) - if(this.solid >= SOLID_TRIGGER && (!(this.move_flags & FL_ONGROUND) || (this.move_groundentity != trace_ent))) + if(this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || (this.groundentity != trace_ent))) _Movetype_Impact(this, trace_ent); return trace_fraction; @@ -514,9 +510,9 @@ void makevectors_matrix(vector myangles) // AngleVectorsFLU { v_forward = v_right = v_up = '0 0 0'; - float y = myangles.y * (M_PI * 2 / 360); - float sy = sin(y); - float cy = cos(y); + float yy = myangles.y * (M_PI * 2 / 360); + float sy = sin(yy); + float cy = cos(yy); float p = myangles.x * (M_PI * 2 / 360); float sp = sin(p); float cp = cos(p); @@ -571,7 +567,7 @@ void _Movetype_Physics_Frame(entity this, float movedt) { case MOVETYPE_PUSH: case MOVETYPE_FAKEPUSH: - _Movetype_Physics_Pusher(this, movedt); + LOG_DEBUGF("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!"); break; case MOVETYPE_NONE: break; @@ -580,8 +576,8 @@ void _Movetype_Physics_Frame(entity this, float movedt) break; case MOVETYPE_NOCLIP: _Movetype_CheckWater(this); - this.move_origin = this.move_origin + TICRATE * this.move_velocity; - this.move_angles = this.move_angles + TICRATE * this.move_avelocity; + this.origin = this.origin + movedt * this.velocity; + this.angles = this.angles + movedt * this.avelocity; _Movetype_LinkEdict(this, false); break; case MOVETYPE_STEP: @@ -611,7 +607,7 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt) { case MOVETYPE_PUSH: case MOVETYPE_FAKEPUSH: - _Movetype_Physics_Pusher(this, movedt); + LOG_DEBUGF("Physics: Lacking QuakeC support for Push movetype, FIX ME by using engine physics!"); break; case MOVETYPE_NONE: break; @@ -620,8 +616,8 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt) break; case MOVETYPE_NOCLIP: _Movetype_CheckWater(this); - this.move_origin = this.move_origin + TICRATE * this.move_velocity; - this.move_angles = this.move_angles + TICRATE * this.move_avelocity; + this.origin = this.origin + movedt * this.velocity; + this.angles = this.angles + movedt * this.avelocity; _Movetype_LinkEdict(this, false); break; case MOVETYPE_STEP: @@ -643,6 +639,20 @@ void _Movetype_Physics_ClientFrame(entity this, float movedt) } } +void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient) // to be run every move frame +{ + this.move_time = time; + + if(isclient) + _Movetype_Physics_ClientFrame(this, movedt); + else + _Movetype_Physics_Frame(this, movedt); + if(wasfreed(this)) + return; + + setorigin(this, this.origin); +} + void Movetype_Physics_NoMatchServer(entity this) // optimized { float movedt = time - this.move_time; @@ -652,10 +662,7 @@ void Movetype_Physics_NoMatchServer(entity this) // optimized if(wasfreed(this)) return; - this.avelocity = this.move_avelocity; - this.velocity = this.move_velocity; - this.angles = this.move_angles; - setorigin(this, this.move_origin); + setorigin(this, this.origin); } void Movetype_Physics_MatchServer(entity this, bool sloppy) @@ -663,11 +670,49 @@ void Movetype_Physics_MatchServer(entity this, bool sloppy) Movetype_Physics_MatchTicrate(this, TICRATE, sloppy); } +.vector tic_origin; +.vector tic_velocity; +.int tic_flags; +.vector tic_avelocity; +.vector tic_angles; + +.vector tic_saved_origin; +.vector tic_saved_velocity; +.int tic_saved_flags; +.vector tic_saved_avelocity; +.vector tic_saved_angles; void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Physics_Entity { +#define X(s) \ + if(this.(s) != this.tic_saved_##s) \ + this.tic_##s = this.(s) + + X(origin); + X(velocity); + X(flags); + X(avelocity); + X(angles); +#undef X + if(tr <= 0) { + this.flags = this.tic_flags; + this.velocity = this.tic_velocity; + this.origin = this.tic_origin; + this.avelocity = this.tic_avelocity; + this.angles = this.tic_angles; Movetype_Physics_NoMatchServer(this); + this.tic_origin = this.origin; + this.tic_velocity = this.velocity; + this.tic_avelocity = this.avelocity; + this.tic_angles = this.angles; + this.tic_flags = this.flags; + + this.tic_saved_flags = this.flags; + this.tic_saved_velocity = this.velocity; + this.tic_saved_origin = this.origin; + this.tic_saved_avelocity = this.avelocity; + this.tic_saved_angles = this.angles; return; } @@ -678,21 +723,31 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Ph this.move_time += n * tr; if(!this.move_didgravity) - this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.move_flags & FL_ONGROUND)); + this.move_didgravity = ((this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) && !(this.tic_flags & FL_ONGROUND)); for (int i = 0; i < n; ++i) { + this.flags = this.tic_flags; + this.velocity = this.tic_velocity; + setorigin(this, this.tic_origin); + this.avelocity = this.tic_avelocity; + this.angles = this.tic_angles; _Movetype_Physics_Frame(this, tr); + this.tic_origin = this.origin; + this.tic_velocity = this.velocity; + this.tic_avelocity = this.avelocity; + this.tic_angles = this.angles; + this.tic_flags = this.flags; if(wasfreed(this)) return; } - this.avelocity = this.move_avelocity; + this.avelocity = this.tic_avelocity; - if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.move_flags & FL_ONGROUND)) + if(dt > 0 && this.move_movetype != MOVETYPE_NONE && !(this.tic_flags & FL_ONGROUND)) { // now continue the move from move_time to time - this.velocity = this.move_velocity; + this.velocity = this.tic_velocity; if(this.move_didgravity > 0) { @@ -702,15 +757,18 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Ph * PHYS_GRAVITY(this); } - this.angles = this.move_angles + dt * this.avelocity; + this.angles = this.tic_angles + dt * this.avelocity; if(sloppy || this.move_movetype == MOVETYPE_NOCLIP) { - setorigin(this, this.move_origin + dt * this.velocity); + setorigin(this, this.tic_origin + dt * this.velocity); } else { + vector oldorg = this.origin; + this.origin = this.tic_origin; _Movetype_PushEntityTrace(this, dt * this.velocity); + this.origin = oldorg; if(!trace_startsolid) setorigin(this, trace_endpos); } @@ -720,8 +778,14 @@ void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy) // SV_Ph } else { - this.velocity = this.move_velocity; - this.angles = this.move_angles; - setorigin(this, this.move_origin); + this.velocity = this.tic_velocity; + this.angles = this.tic_angles; + setorigin(this, this.tic_origin); } + + this.tic_saved_flags = this.flags; + this.tic_saved_velocity = this.velocity; + this.tic_saved_origin = this.origin; + this.tic_saved_avelocity = this.avelocity; + this.tic_saved_angles = this.angles; } diff --git a/qcsrc/common/physics/movetypes/movetypes.qh b/qcsrc/common/physics/movetypes/movetypes.qh index 61b7e94a95..46e80fbc3e 100644 --- a/qcsrc/common/physics/movetypes/movetypes.qh +++ b/qcsrc/common/physics/movetypes/movetypes.qh @@ -1,36 +1,37 @@ -#ifndef MOVETYPES_H -#define MOVETYPES_H +#pragma once #define IS_ONGROUND(s) boolean((s).flags & FL_ONGROUND) #define SET_ONGROUND(s) ((s).flags |= FL_ONGROUND) #define UNSET_ONGROUND(s) ((s).flags &= ~FL_ONGROUND) +#define IS_ONSLICK(s) boolean((s).flags & FL_ONSLICK) +#define SET_ONSLICK(s) ((s).flags |= FL_ONSLICK) +#define UNSET_ONSLICK(s) ((s).flags &= ~FL_ONSLICK) -.float move_ltime; -.void(entity this) move_think; -.float move_nextthink; -.void(entity this) move_blocked; +#ifdef CSQC +.float bouncestop; +.float bouncefactor; +#endif + +void set_movetype(entity this, int mt); .float move_movetype; .float move_time; -.vector move_origin; -.vector move_angles; -.vector move_velocity; -.vector move_avelocity; -.int move_flags; -.int move_watertype; -.int move_waterlevel; +//.vector move_origin; +//.vector move_angles; +//.vector move_velocity; +//.vector move_avelocity; +//.int move_flags; +//.int move_watertype; +//.int move_waterlevel; .void(float, float)contentstransition; -.float move_bounce_factor; -.float move_bounce_stopspeed; +//.float move_bounce_factor; +//.float move_bounce_stopspeed; .float move_nomonsters; // -1 for MOVE_NORMAL, otherwise a MOVE_ constant -.entity move_aiment; -.vector move_punchangle; +.entity aiment; +.vector punchangle; -// should match sv_gameplayfix_fixedcheckwatertransition -float autocvar_cl_gameplayfix_fixedcheckwatertransition = 1; - -.entity move_groundentity; // FIXME add move_groundnetworkentity? +.entity groundentity; // FIXME add move_groundnetworkentity? .float move_suspendedinair; .float move_didgravity; @@ -46,6 +47,7 @@ void _Movetype_PushEntityTrace(entity this, vector push); float _Movetype_PushEntity(entity this, vector push, float failonstartsolid); void makevectors_matrix(vector myangles); +void Movetype_Physics_NoMatchTicrate(entity this, float movedt, bool isclient); void Movetype_Physics_MatchTicrate(entity this, float tr, bool sloppy); void Movetype_Physics_MatchServer(entity this, bool sloppy); void Movetype_Physics_NoMatchServer(entity this); @@ -75,8 +77,13 @@ const int MOVETYPE_FLY_WORLDONLY = 33; const int FL_ITEM = 256; const int FL_ONGROUND = 512; +#elif defined(SVQC) +const int MOVETYPE_ANGLENOCLIP = 1; +const int MOVETYPE_ANGLECLIP = 2; #endif +const int FL_ONSLICK = BIT(20); + const int MOVETYPE_FAKEPUSH = 13; const int MOVEFLAG_VALID = BIT(23); @@ -87,5 +94,3 @@ const int MOVEFLAG_GRAVITYUNAFFECTEDBYTICRATE = BIT(2); #ifdef CSQC #define moveflags STAT(MOVEFLAGS) #endif - -#endif diff --git a/qcsrc/common/physics/movetypes/push.qc b/qcsrc/common/physics/movetypes/push.qc deleted file mode 100644 index 0d03bfff8f..0000000000 --- a/qcsrc/common/physics/movetypes/push.qc +++ /dev/null @@ -1,154 +0,0 @@ -void _Movetype_PushMove(entity this, float dt) // SV_PushMove -{ - if (this.move_velocity == '0 0 0' && this.move_avelocity == '0 0 0') - { - this.move_ltime += dt; - return; - } - - switch (this.solid) - { - // LordHavoc: valid pusher types - case SOLID_BSP: - case SOLID_BBOX: - case SOLID_SLIDEBOX: - case SOLID_CORPSE: // LordHavoc: this would be weird... - break; - // LordHavoc: no collisions - case SOLID_NOT: - case SOLID_TRIGGER: - this.move_origin = this.move_origin + dt * this.move_velocity; - this.move_angles = this.move_angles + dt * this.move_avelocity; - this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); - this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); - this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); - this.move_ltime += dt; - _Movetype_LinkEdict(this, true); - return; - default: - LOG_TRACEF("_Movetype_PushMove: entity %e, unrecognized solid type %d\n", this, this.solid); - return; - } - - bool rotated = (this.move_angles * this.move_angles) + (this.move_avelocity * this.move_avelocity) > 0; - - vector move1 = this.move_velocity * dt; - vector moveangle = this.move_avelocity * dt; - - makevectors_matrix(-moveangle); - -// vector pushorig = this.move_origin; -// vector pushang = this.move_angles; -// float pushltime = this.move_ltime; - -// move the pusher to its final position - - this.move_origin = this.move_origin + dt * this.move_velocity; - this.move_angles = this.move_angles + dt * this.move_avelocity; - - this.move_ltime += dt; - _Movetype_LinkEdict(this, true); - - int savesolid = this.solid; - - if (this.move_movetype != MOVETYPE_FAKEPUSH) - { - FOREACH_ENTITY_RADIUS(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, { - switch (it.move_movetype) - { - case MOVETYPE_NONE: - case MOVETYPE_PUSH: - case MOVETYPE_FOLLOW: - case MOVETYPE_NOCLIP: - case MOVETYPE_FLY_WORLDONLY: - continue; - default: - break; - } - - if (it.owner == this) - continue; - - if (this.owner == it) - continue; - - vector pivot = it.mins + 0.5 * (it.maxs - it.mins); - vector move; - if (rotated) - { - vector org = (it.move_origin - this.move_origin) + pivot; - vector org2; - org2.x = org * v_forward; - org2.y = org * v_right; - org2.z = org * v_up; - move = (org2 - org) + move1; - } - else - { - move = move1; - } - - // physics objects need better collisions than this code can do - if (it.move_movetype == 32) // MOVETYPE_PHYSICS - { - it.move_origin = it.move_origin + move; - _Movetype_LinkEdict(it, true); - continue; - } - - // try moving the contacted entity - this.solid = SOLID_NOT; - bool flag = false; - flag = _Movetype_PushEntity(it, move, true); - if (!flag) - { - // entity "it" got teleported - it.move_angles_y += trace_fraction * moveangle.y; - this.solid = savesolid; - continue; // pushed enough - } - // FIXME: turn players specially - it.move_angles_y += trace_fraction * moveangle.y; - this.solid = savesolid; - - // this trace.fraction < 1 check causes items to fall off of pushers - // if they pass under or through a wall - // the groundentity check causes items to fall off of ledges - if (it.move_movetype != MOVETYPE_WALK && (trace_fraction < 1 || it.move_groundentity != this)) - it.move_flags &= ~FL_ONGROUND; - }); - } - - this.move_angles_x -= 360.0 * floor(this.move_angles.x * (1.0 / 360.0)); - this.move_angles_y -= 360.0 * floor(this.move_angles.y * (1.0 / 360.0)); - this.move_angles_z -= 360.0 * floor(this.move_angles.z * (1.0 / 360.0)); -} - -void _Movetype_Physics_Pusher(entity this, float dt) // SV_Physics_Pusher -{ - float oldltime = this.move_ltime; - float thinktime = this.move_nextthink; - float movetime; - if (thinktime < this.move_ltime + dt) - { - movetime = thinktime - this.move_ltime; - if (movetime < 0) - movetime = 0; - } - else - { - movetime = dt; - } - - if (movetime) - // advances this.move_ltime if not blocked - _Movetype_PushMove(this, movetime); - - if (thinktime > oldltime && thinktime <= this.move_ltime) - { - this.move_nextthink = 0; - this.move_time = time; - other = NULL; - this.move_think(this); - } -} diff --git a/qcsrc/common/physics/movetypes/step.qc b/qcsrc/common/physics/movetypes/step.qc index d7a2d56274..a41269f79c 100644 --- a/qcsrc/common/physics/movetypes/step.qc +++ b/qcsrc/common/physics/movetypes/step.qc @@ -1,10 +1,11 @@ +#include "step.qh" void _Movetype_Physics_Step(entity this, float dt) // SV_Physics_Step { - if(this.move_flags & FL_ONGROUND) + if(IS_ONGROUND(this)) { if(this.velocity_z >= (1.0 / 32.0) && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) { - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); _Movetype_CheckVelocity(this); _Movetype_FlyMove(this, dt, true, '0 0 0', 0); _Movetype_LinkEdict(this, true); diff --git a/qcsrc/common/physics/movetypes/step.qh b/qcsrc/common/physics/movetypes/step.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/step.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/toss.qc b/qcsrc/common/physics/movetypes/toss.qc index f515a75f9f..498852135b 100644 --- a/qcsrc/common/physics/movetypes/toss.qc +++ b/qcsrc/common/physics/movetypes/toss.qc @@ -1,20 +1,21 @@ +#include "toss.qh" #include "../player.qh" void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss { - if (this.move_flags & FL_ONGROUND) + if (IS_ONGROUND(this)) { - if (this.move_velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) + if (this.velocity.z >= 1 / 32 && UPWARD_VELOCITY_CLEARS_ONGROUND(this)) { - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); } - else if (!this.move_groundentity) + else if (!this.groundentity) { return; } - else if (this.move_suspendedinair && wasfreed(this.move_groundentity)) + else if (this.move_suspendedinair && wasfreed(this.groundentity)) { - this.move_groundentity = NULL; + this.groundentity = NULL; return; } } @@ -23,21 +24,27 @@ void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss _Movetype_CheckVelocity(this); - if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) + /*if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) { this.move_didgravity = 1; - this.move_velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) + this.velocity_z -= (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1) * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + }*/ + + if (this.move_movetype == MOVETYPE_BOUNCE || this.move_movetype == MOVETYPE_TOSS) + { + this.move_didgravity = true; + this.velocity_z -= (((this.gravity) ? this.gravity : 1) * PHYS_GRAVITY(this) * dt); } - this.move_angles = this.move_angles + this.move_avelocity * dt; + this.angles = this.angles + this.avelocity * dt; float movetime = dt; for (int bump = 0; bump < MAX_CLIP_PLANES && movetime > 0; ++bump) { - vector move = this.move_velocity * movetime; + vector move = this.velocity * movetime; _Movetype_PushEntity(this, move, true); if (wasfreed(this)) return; @@ -57,45 +64,45 @@ void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss if (this.move_movetype == MOVETYPE_BOUNCEMISSILE) { - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 2.0); - this.move_flags &= ~FL_ONGROUND; + this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 2.0); + UNSET_ONGROUND(this); } else if (this.move_movetype == MOVETYPE_BOUNCE) { - float bouncefac = this.move_bounce_factor; if (!bouncefac) bouncefac = 0.5; - float bouncestop = this.move_bounce_stopspeed; if (!bouncestop) bouncestop = 60 / 800; - bouncestop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + float bouncefac = this.bouncefactor; if (!bouncefac) bouncefac = 0.5; + float bstop = this.bouncestop; if (!bstop) bstop = 60 / 800; + bstop *= (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1 + bouncefac); + this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 1 + bouncefac); - float d = trace_plane_normal * this.move_velocity; - if (trace_plane_normal.z > 0.7 && d < bouncestop && d > -bouncestop) + float d = trace_plane_normal * this.velocity; + if (trace_plane_normal.z > 0.7 && d < bstop && d > -bstop) { - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; - this.move_velocity = '0 0 0'; - this.move_avelocity = '0 0 0'; + SET_ONGROUND(this); + this.groundentity = trace_ent; + this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; } else { - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); } } else { - this.move_velocity = _Movetype_ClipVelocity(this.move_velocity, trace_plane_normal, 1.0); + this.velocity = _Movetype_ClipVelocity(this.velocity, trace_plane_normal, 1.0); if (trace_plane_normal.z > 0.7) { - this.move_flags |= FL_ONGROUND; - this.move_groundentity = trace_ent; + SET_ONGROUND(this); + this.groundentity = trace_ent; if (trace_ent.solid == SOLID_BSP) this.move_suspendedinair = true; - this.move_velocity = '0 0 0'; - this.move_avelocity = '0 0 0'; + this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; } else { - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); } } @@ -104,12 +111,12 @@ void _Movetype_Physics_Toss(entity this, float dt) // SV_Physics_Toss break; // DP revision 8918 (WHY...) - if (this.move_flags & FL_ONGROUND) + if (IS_ONGROUND(this)) break; } - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !(this.move_flags & FL_ONGROUND)) - this.move_velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); + //if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE && this.move_didgravity > 0 && !IS_ONGROUND(this)) + // this.velocity_z -= 0.5 * dt * (this.gravity ? this.gravity : 1) * PHYS_GRAVITY(this); _Movetype_CheckWaterTransition(this); } diff --git a/qcsrc/common/physics/movetypes/toss.qh b/qcsrc/common/physics/movetypes/toss.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/toss.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/movetypes/walk.qc b/qcsrc/common/physics/movetypes/walk.qc index e926246266..c20e82e834 100644 --- a/qcsrc/common/physics/movetypes/walk.qc +++ b/qcsrc/common/physics/movetypes/walk.qc @@ -1,3 +1,4 @@ +#include "walk.qh" void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove { vector stepnormal = '0 0 0'; @@ -9,15 +10,15 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove if (GAMEPLAYFIX_UNSTICKPLAYERS(this)) _Movetype_UnstickEntity(this); - bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.move_flags & FL_WATERJUMP)); + bool applygravity = (!_Movetype_CheckWater(this) && this.move_movetype == MOVETYPE_WALK && !(this.flags & FL_WATERJUMP)); _Movetype_CheckVelocity(this); // do a regular slide move unless it looks like you ran into a step - bool oldonground = (this.move_flags & FL_ONGROUND); + bool oldonground = IS_ONGROUND(this); - vector start_origin = this.move_origin; - vector start_velocity = this.move_velocity; + vector start_origin = this.origin; + vector start_velocity = this.velocity; int clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, GAMEPLAYFIX_STEPMULTIPLETIMES(this) ? PHYS_STEPHEIGHT(this) : 0); @@ -26,8 +27,8 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // only try this if there was no floor in the way in the trace (no, // this check seems to be not REALLY necessary, because if clip & 1, // our trace will hit that thing too) - vector upmove = this.move_origin + '0 0 1'; - vector downmove = this.move_origin - '0 0 1'; + vector upmove = this.origin + '0 0 1'; + vector downmove = this.origin - '0 0 1'; int type; if (this.move_movetype == MOVETYPE_FLYMISSILE) type = MOVE_MISSILE; @@ -43,7 +44,7 @@ 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)) - this.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(this); _Movetype_CheckVelocity(this); _Movetype_LinkEdict(this, true); @@ -51,17 +52,17 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove if (clip & 8) // teleport return; - if (this.move_flags & FL_WATERJUMP) + if (this.flags & FL_WATERJUMP) return; if (PHYS_NOSTEP(this)) return; - vector originalmove_origin = this.move_origin; - vector originalmove_velocity = this.move_velocity; + vector originalorigin = this.origin; + vector originalvelocity = this.velocity; // originalmove_clip = clip; - int originalmove_flags = this.move_flags; - entity originalmove_groundentity = this.move_groundentity; + int originalflags = this.flags; + entity originalmove_groundentity = this.groundentity; // if move didn't block on a step, return if (clip & 2) @@ -78,14 +79,14 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // return if attempting to jump while airborn (unless sv_jumpstep) if (!PHYS_JUMPSTEP(this)) - if (!oldonground && this.move_waterlevel == 0) + if (!oldonground && this.waterlevel == 0) return; } // try moving up and forward to go up a step // back to start pos - this.move_origin = start_origin; - this.move_velocity = start_velocity; + this.origin = start_origin; + this.velocity = start_velocity; // move up vector upmove = '0 0 1' * PHYS_STEPHEIGHT(this); @@ -99,9 +100,9 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove } // move forward - this.move_velocity_z = 0; + this.velocity_z = 0; clip = _Movetype_FlyMove(this, dt, applygravity, stepnormal, 0); - this.move_velocity_z += start_velocity.z; + this.velocity_z += start_velocity.z; if (clip & 8) { // we got teleported when upstepping... must abort the move @@ -115,16 +116,16 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // check for stuckness, possibly due to the limited precision of floats // in the clipping hulls if (clip - && fabs(originalmove_origin.y - this.move_origin.y) < 0.03125 - && fabs(originalmove_origin.x - this.move_origin.x) < 0.03125) + && fabs(originalorigin.y - this.origin.y) < 0.03125 + && fabs(originalorigin.x - this.origin.x) < 0.03125) { // Con_Printf("wall\n"); // stepping up didn't make any progress, revert to original move - this.move_origin = originalmove_origin; - this.move_velocity = originalmove_velocity; + this.origin = originalorigin; + this.velocity = originalvelocity; // clip = originalmove_clip; - this.move_flags = originalmove_flags; - this.move_groundentity = originalmove_groundentity; + this.flags = originalflags; + this.groundentity = originalmove_groundentity; // now try to unstick if needed // clip = SV_TryUnstick (ent, oldvel); return; @@ -137,7 +138,7 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove _Movetype_WallFriction(this, stepnormal); } // don't do the down move if stepdown is disabled, moving upward, not in water, or the move started offground or ended onground - else if (!GAMEPLAYFIX_STEPDOWN(this) || this.move_waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || (this.move_flags & FL_ONGROUND)) + else if (!GAMEPLAYFIX_STEPDOWN(this) || this.waterlevel >= 3 || start_velocity.z >= (1.0 / 32.0) || !oldonground || IS_ONGROUND(this)) { return; } @@ -165,10 +166,10 @@ void _Movetype_Physics_Walk(entity this, float dt) // SV_WalkMove // if the push down didn't end up on good ground, use the move without // the step up. This happens near wall / slope combinations, and can // cause the player to hop up higher on a slope too steep to climb - this.move_origin = originalmove_origin; - this.move_velocity = originalmove_velocity; - this.move_flags = originalmove_flags; - this.move_groundentity = originalmove_groundentity; + this.origin = originalorigin; + this.velocity = originalvelocity; + this.flags = originalflags; + this.groundentity = originalmove_groundentity; } _Movetype_CheckVelocity(this); diff --git a/qcsrc/common/physics/movetypes/walk.qh b/qcsrc/common/physics/movetypes/walk.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/physics/movetypes/walk.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/physics/player.qc b/qcsrc/common/physics/player.qc index 5079bd9761..58828c4101 100644 --- a/qcsrc/common/physics/player.qc +++ b/qcsrc/common/physics/player.qc @@ -13,7 +13,7 @@ bool Physics_Valid(string thecvar) return autocvar_g_physics_clientselect && strhasword(autocvar_g_physics_clientselect_options, thecvar); } -float Physics_ClientOption(entity this, string option) +float Physics_ClientOption(entity this, string option, float defaultval) { if(Physics_Valid(this.cvar_cl_physics)) { @@ -27,40 +27,48 @@ float Physics_ClientOption(entity this, string option) if(cvar_type(s) & CVAR_TYPEFLAG_EXISTS) return cvar(s); } - return cvar(strcat("sv_", option)); + return defaultval; } void Physics_UpdateStats(entity this, float maxspd_mod) { - STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw"), maxspd_mod); - STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw")) - ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw"), maxspd_mod) + STAT(MOVEVARS_AIRACCEL_QW, this) = AdjustAirAccelQW(Physics_ClientOption(this, "airaccel_qw", autocvar_sv_airaccel_qw), maxspd_mod); + STAT(MOVEVARS_AIRSTRAFEACCEL_QW, this) = (Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw)) + ? AdjustAirAccelQW(Physics_ClientOption(this, "airstrafeaccel_qw", autocvar_sv_airstrafeaccel_qw), maxspd_mod) : 0; - STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, this) = Physics_ClientOption(this, "airspeedlimit_nonqw") * maxspd_mod; - STAT(MOVEVARS_MAXSPEED, this) = Physics_ClientOption(this, "maxspeed") * maxspd_mod; // also slow walking + 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; // old stats // fix some new settings - STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor"); - STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed"); - STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed"); - STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate"); - STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel"); - STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction"); - STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol"); - STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power"); - STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty"); - STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel"); - STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed"); - STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel"); - STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio"); - STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction"); - STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate"); - STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed"); - STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate"); - STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate"); - STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity"); - STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump"); + STAT(MOVEVARS_AIRACCEL_QW_STRETCHFACTOR, this) = Physics_ClientOption(this, "airaccel_qw_stretchfactor", autocvar_sv_airaccel_qw_stretchfactor); + STAT(MOVEVARS_MAXAIRSTRAFESPEED, this) = Physics_ClientOption(this, "maxairstrafespeed", autocvar_sv_maxairstrafespeed); + STAT(MOVEVARS_MAXAIRSPEED, this) = Physics_ClientOption(this, "maxairspeed", autocvar_sv_maxairspeed); + STAT(MOVEVARS_AIRSTRAFEACCELERATE, this) = Physics_ClientOption(this, "airstrafeaccelerate", autocvar_sv_airstrafeaccelerate); + STAT(MOVEVARS_WARSOWBUNNY_TURNACCEL, this) = Physics_ClientOption(this, "warsowbunny_turnaccel", autocvar_sv_warsowbunny_turnaccel); + STAT(MOVEVARS_AIRACCEL_SIDEWAYS_FRICTION, this) = Physics_ClientOption(this, "airaccel_sideways_friction", autocvar_sv_airaccel_sideways_friction); + STAT(MOVEVARS_AIRCONTROL, this) = Physics_ClientOption(this, "aircontrol", autocvar_sv_aircontrol); + STAT(MOVEVARS_AIRCONTROL_POWER, this) = Physics_ClientOption(this, "aircontrol_power", autocvar_sv_aircontrol_power); + STAT(MOVEVARS_AIRCONTROL_BACKWARDS, this) = Physics_ClientOption(this, "aircontrol_backwards", autocvar_sv_aircontrol_backwards); + STAT(MOVEVARS_AIRCONTROL_PENALTY, this) = Physics_ClientOption(this, "aircontrol_penalty", autocvar_sv_aircontrol_penalty); + STAT(MOVEVARS_WARSOWBUNNY_AIRFORWARDACCEL, this) = Physics_ClientOption(this, "warsowbunny_airforwardaccel", autocvar_sv_warsowbunny_airforwardaccel); + STAT(MOVEVARS_WARSOWBUNNY_TOPSPEED, this) = Physics_ClientOption(this, "warsowbunny_topspeed", autocvar_sv_warsowbunny_topspeed); + STAT(MOVEVARS_WARSOWBUNNY_ACCEL, this) = Physics_ClientOption(this, "warsowbunny_accel", autocvar_sv_warsowbunny_accel); + STAT(MOVEVARS_WARSOWBUNNY_BACKTOSIDERATIO, this) = Physics_ClientOption(this, "warsowbunny_backtosideratio", autocvar_sv_warsowbunny_backtosideratio); + STAT(MOVEVARS_FRICTION, this) = Physics_ClientOption(this, "friction", autocvar_sv_friction); + STAT(MOVEVARS_ACCELERATE, this) = Physics_ClientOption(this, "accelerate", autocvar_sv_accelerate); + STAT(MOVEVARS_STOPSPEED, this) = Physics_ClientOption(this, "stopspeed", autocvar_sv_stopspeed); + STAT(MOVEVARS_AIRACCELERATE, this) = Physics_ClientOption(this, "airaccelerate", autocvar_sv_airaccelerate); + STAT(MOVEVARS_AIRSTOPACCELERATE, this) = Physics_ClientOption(this, "airstopaccelerate", autocvar_sv_airstopaccelerate); + STAT(MOVEVARS_JUMPVELOCITY, this) = Physics_ClientOption(this, "jumpvelocity", autocvar_sv_jumpvelocity); + STAT(MOVEVARS_TRACK_CANJUMP, this) = Physics_ClientOption(this, "track_canjump", autocvar_sv_track_canjump); } #endif @@ -73,70 +81,29 @@ float IsMoveInDirection(vector mv, float ang) // key mix factor return ang > 1 ? 0 : ang < -1 ? 0 : 1 - fabs(ang); } -float GeomLerp(float a, float lerp, float b) +float GeomLerp(float a, float _lerp, float b) { - return a == 0 ? (lerp < 1 ? 0 : b) - : b == 0 ? (lerp > 0 ? 0 : a) - : a * pow(fabs(b / a), lerp); -} - -#define unstick_offsets(X) \ -/* 1 no nudge (just return the original if this test passes) */ \ - X(' 0.000 0.000 0.000') \ -/* 6 simple nudges */ \ - X(' 0.000 0.000 0.125') X('0.000 0.000 -0.125') \ - X('-0.125 0.000 0.000') X('0.125 0.000 0.000') \ - X(' 0.000 -0.125 0.000') X('0.000 0.125 0.000') \ -/* 4 diagonal flat nudges */ \ - X('-0.125 -0.125 0.000') X('0.125 -0.125 0.000') \ - X('-0.125 0.125 0.000') X('0.125 0.125 0.000') \ -/* 8 diagonal upward nudges */ \ - X('-0.125 0.000 0.125') X('0.125 0.000 0.125') \ - X(' 0.000 -0.125 0.125') X('0.000 0.125 0.125') \ - X('-0.125 -0.125 0.125') X('0.125 -0.125 0.125') \ - X('-0.125 0.125 0.125') X('0.125 0.125 0.125') \ -/* 8 diagonal downward nudges */ \ - X('-0.125 0.000 -0.125') X('0.125 0.000 -0.125') \ - X(' 0.000 -0.125 -0.125') X('0.000 0.125 -0.125') \ - X('-0.125 -0.125 -0.125') X('0.125 -0.125 -0.125') \ - X('-0.125 0.125 -0.125') X('0.125 0.125 -0.125') \ -/**/ - -void PM_ClientMovement_Unstick(entity this) -{ - #define X(unstick_offset) \ - { \ - vector neworigin = unstick_offset + this.origin; \ - tracebox(neworigin, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL), neworigin, MOVE_NORMAL, this); \ - if (!trace_startsolid) \ - { \ - setorigin(this, neworigin); \ - return; \ - } \ - } - unstick_offsets(X); - #undef X + return a == 0 ? (_lerp < 1 ? 0 : b) + : b == 0 ? (_lerp > 0 ? 0 : a) + : a * pow(fabs(b / a), _lerp); } -void PM_ClientMovement_UpdateStatus(entity this, bool ground) +void PM_ClientMovement_UpdateStatus(entity this) { #ifdef CSQC if(!IS_PLAYER(this)) return; - // make sure player is not stuck - if(autocvar_cl_movement == 3) - PM_ClientMovement_Unstick(this); // set crouched bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); if(this.hook && !wasfreed(this.hook)) do_crouch = false; + if(this.waterlevel >= WATERLEVEL_SWIMMING) + do_crouch = false; if(hud != HUD_NORMAL) do_crouch = false; if(STAT(FROZEN, this)) do_crouch = false; - if((activeweapon == WEP_SHOCKWAVE || activeweapon == WEP_SHOTGUN) && viewmodel.animstate_startframe == viewmodel.anim_fire2_x && time < viewmodel.weapon_nextthink) - do_crouch = false; if (do_crouch) { @@ -148,156 +115,23 @@ void PM_ClientMovement_UpdateStatus(entity this, bool ground) // 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, NULL), STAT(PL_MAX, NULL), this.origin, MOVE_NORMAL, this); + tracebox(this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), this.origin, MOVE_NORMAL, this); if (!trace_startsolid) UNSET_DUCKED(this); } } - // set onground - vector origin1 = this.origin + '0 0 1'; - vector origin2 = this.origin - '0 0 1'; - - if (ground && autocvar_cl_movement == 3) - { - tracebox(origin1, this.mins, this.maxs, origin2, MOVE_NORMAL, this); - if (trace_fraction < 1.0 && trace_plane_normal.z > 0.7) - { - SET_ONGROUND(this); - - // this code actually "predicts" an impact; so let's clip velocity first - this.velocity -= this.velocity * trace_plane_normal * trace_plane_normal; - } - else - UNSET_ONGROUND(this); - } - - if(autocvar_cl_movement == 3) - { - // set watertype/waterlevel - origin1 = this.origin; - origin1.z += this.mins_z + 1; - this.waterlevel = WATERLEVEL_NONE; - - int thepoint = pointcontents(origin1); - - this.watertype = (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME); - - if (this.watertype) - { - this.waterlevel = WATERLEVEL_WETFEET; - origin1.z = this.origin.z + (this.mins.z + this.maxs.z) * 0.5; - thepoint = pointcontents(origin1); - if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) - { - this.waterlevel = WATERLEVEL_SWIMMING; - origin1.z = this.origin.z + 22; - thepoint = pointcontents(origin1); - if (thepoint == CONTENT_WATER || thepoint == CONTENT_LAVA || thepoint == CONTENT_SLIME) - this.waterlevel = WATERLEVEL_SUBMERGED; - } - } - } - - if (IS_ONGROUND(this) || this.velocity.z <= 0 || pmove_waterjumptime <= 0) - pmove_waterjumptime = 0; + if (IS_ONGROUND(this) || this.velocity.z <= 0 || PHYS_WATERJUMP_TIME(this) <= 0) + PHYS_WATERJUMP_TIME(this) = 0; #endif } -void PM_ClientMovement_Move(entity this) +void CPM_PM_Aircontrol(entity this, float dt, vector wishdir, float wishspeed) { -#ifdef CSQC - - PM_ClientMovement_UpdateStatus(this, false); - if(autocvar_cl_movement == 1) - return; - - int bump; - float t; - float f; - vector neworigin; - vector currentorigin2; - vector neworigin2; - vector primalvelocity; - - vector trace1_endpos = '0 0 0'; - vector trace2_endpos = '0 0 0'; - vector trace3_endpos = '0 0 0'; - float trace1_fraction = 0; - float trace2_fraction = 0; - float trace3_fraction = 0; - vector trace1_plane_normal = '0 0 0'; - vector trace2_plane_normal = '0 0 0'; - vector trace3_plane_normal = '0 0 0'; - - primalvelocity = this.velocity; - for(bump = 0, t = PHYS_INPUT_TIMELENGTH; bump < 8 && (this.velocity * this.velocity) > 0; bump++) - { - neworigin = this.origin + t * this.velocity; - tracebox(this.origin, this.mins, this.maxs, neworigin, MOVE_NORMAL, this); - trace1_endpos = trace_endpos; - trace1_fraction = trace_fraction; - trace1_plane_normal = trace_plane_normal; - if(trace1_fraction < 1 && trace1_plane_normal_z == 0) - { - // may be a step or wall, try stepping up - // first move forward at a higher level - currentorigin2 = this.origin; - currentorigin2_z += PHYS_STEPHEIGHT(this); - neworigin2 = neworigin; - neworigin2_z += PHYS_STEPHEIGHT(this); - tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); - trace2_endpos = trace_endpos; - trace2_fraction = trace_fraction; - trace2_plane_normal = trace_plane_normal; - if(!trace_startsolid) - { - // then move down from there - currentorigin2 = trace2_endpos; - neworigin2 = trace2_endpos; - neworigin2_z = this.origin_z; - tracebox(currentorigin2, this.mins, this.maxs, neworigin2, MOVE_NORMAL, this); - trace3_endpos = trace_endpos; - trace3_fraction = trace_fraction; - trace3_plane_normal = trace_plane_normal; - // accept the new trace if it made some progress - if(fabs(trace3_endpos_x - trace1_endpos_x) >= 0.03125 || fabs(trace3_endpos_y - trace1_endpos_y) >= 0.03125) - { - trace1_endpos = trace2_endpos; - trace1_fraction = trace2_fraction; - trace1_plane_normal = trace2_plane_normal; - trace1_endpos = trace3_endpos; - } - } - } - - // check if it moved at all - if(trace1_fraction >= 0.001) - setorigin(this, trace1_endpos); - - // check if it moved all the way - if(trace1_fraction == 1) - break; + float movity = IsMoveInDirection(this.movement, 0); + if(PHYS_AIRCONTROL_BACKWARDS(this)) + movity += IsMoveInDirection(this.movement, 180); - // this is only really needed for nogravityonground combined with gravityunaffectedbyticrate - // I'm pretty sure I commented it out solely because it seemed redundant - // this got commented out in a change that supposedly makes the code match QW better - // so if this is broken, maybe put it in an if(cls.protocol != PROTOCOL_QUAKEWORLD) block - if(trace1_plane_normal_z > 0.7) - SET_ONGROUND(this); - - t -= t * trace1_fraction; - - f = (this.velocity * trace1_plane_normal); - this.velocity = this.velocity + -f * trace1_plane_normal; - } - if(PHYS_TELEPORT_TIME(this) > 0) - this.velocity = primalvelocity; -#endif -} - -void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed) -{ - float k = 32 * (2 * IsMoveInDirection(this.movement, 0) - 1); + float k = 32 * (2 * movity - 1); if (k <= 0) return; @@ -312,7 +146,7 @@ void CPM_PM_Aircontrol(entity this, vector wishdir, float wishspeed) if (dot > 0) // we can't change direction while slowing down { - k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * PHYS_INPUT_TIMELENGTH; + k *= pow(dot, PHYS_AIRCONTROL_POWER(this)) * dt; xyspeed = max(0, xyspeed - PHYS_AIRCONTROL_PENALTY(this) * sqrt(max(0, 1 - dot*dot)) * k/32); k *= PHYS_AIRCONTROL(this); this.velocity = normalize(this.velocity * xyspeed + wishdir * k); @@ -332,7 +166,7 @@ float AdjustAirAccelQW(float accelqw, float factor) // sv_airaccel_sideways_friction 0 // prvm_globalset server speedclamp_mode 1 // (or 2) -void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit) +void PM_Accelerate(entity this, float dt, vector wishdir, float wishspeed, float wishspeed0, float accel, float accelqw, float stretchfactor, float sidefric, float speedlimit) { float speedclamp = stretchfactor > 0 ? stretchfactor : accelqw < 0 ? 1 // full clamping, no stretch @@ -348,7 +182,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed vector vel_xy = vec2(this.velocity); vector vel_perpend = vel_xy - vel_straight * wishdir; - float step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; + float step = accel * dt * wishspeed0; float vel_xy_current = vlen(vel_xy); if (speedlimit) @@ -361,7 +195,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed if (sidefric < 0 && (vel_perpend*vel_perpend)) // negative: only apply so much sideways friction to stay below the speed you could get by "braking" { - float f = max(0, 1 + PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + float f = max(0, 1 + dt * wishspeed * sidefric); float themin = (vel_xy_backward * vel_xy_backward - vel_straight * vel_straight) / (vel_perpend * vel_perpend); // assume: themin > 1 // vel_xy_backward*vel_xy_backward - vel_straight*vel_straight > vel_perpend*vel_perpend @@ -377,7 +211,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed } } else - vel_perpend *= max(0, 1 - PHYS_INPUT_TIMELENGTH * wishspeed * sidefric); + vel_perpend *= max(0, 1 - dt * wishspeed * sidefric); vel_xy = vel_straight * wishdir + vel_perpend; @@ -396,7 +230,7 @@ void PM_Accelerate(entity this, vector wishdir, float wishspeed, float wishspeed this.velocity = vel_xy + vel_z * '0 0 1'; } -void PM_AirAccelerate(entity this, vector wishdir, float wishspeed) +void PM_AirAccelerate(entity this, float dt, vector wishdir, float wishspeed) { if (wishspeed == 0) return; @@ -406,18 +240,18 @@ void PM_AirAccelerate(entity this, vector wishdir, float wishspeed) float curspeed = vlen(curvel); if (wishspeed > curspeed * 1.01) - wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); + wishspeed = min(wishspeed, curspeed + PHYS_WARSOWBUNNY_AIRFORWARDACCEL(this) * PHYS_MAXSPEED(this) * dt); else { float f = max(0, (PHYS_WARSOWBUNNY_TOPSPEED(this) - curspeed) / (PHYS_WARSOWBUNNY_TOPSPEED(this) - PHYS_MAXSPEED(this))); - wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH; + wishspeed = max(curspeed, PHYS_MAXSPEED(this)) + PHYS_WARSOWBUNNY_ACCEL(this) * f * PHYS_MAXSPEED(this) * dt; } vector wishvel = wishdir * wishspeed; vector acceldir = wishvel - curvel; float addspeed = vlen(acceldir); acceldir = normalize(acceldir); - float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * PHYS_INPUT_TIMELENGTH); + float accelspeed = min(addspeed, PHYS_WARSOWBUNNY_TURNACCEL(this) * PHYS_MAXSPEED(this) * dt); if (PHYS_WARSOWBUNNY_BACKTOSIDERATIO(this) < 1) { @@ -473,7 +307,7 @@ bool PlayerJump(entity this) } if (!doublejump) - if (!IS_ONGROUND(this)) + if (!IS_ONGROUND(this) && !IS_ONSLICK(this)) return IS_JUMP_HELD(this); bool track_jump = PHYS_CL_TRACK_CANJUMP(this); @@ -510,11 +344,11 @@ bool PlayerJump(entity this) } } - if (!WAS_ONGROUND(this)) + if (!WAS_ONGROUND(this) && !WAS_ONSLICK(this)) { #ifdef SVQC if(autocvar_speedmeter) - LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n")); + LOG_TRACE("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")"); #endif if(this.lastground < time - 0.3) { @@ -524,7 +358,7 @@ bool PlayerJump(entity this) } #ifdef SVQC if(this.jumppadcount > 1) - LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n")); + LOG_TRACE(ftos(this.jumppadcount), "x jumppad combo"); this.jumppadcount = 0; #endif } @@ -532,6 +366,7 @@ bool PlayerJump(entity this) this.velocity_z += mjumpheight; UNSET_ONGROUND(this); + UNSET_ONSLICK(this); SET_JUMP_HELD(this); #ifdef SVQC @@ -541,7 +376,7 @@ bool PlayerJump(entity this) animdecide_setaction(this, ANIMACTION_JUMP, true); if (autocvar_g_jump_grunt) - PlayerSound(this, playersound_jump, CH_PLAYER, VOICETYPE_PLAYERSOUND); + PlayerSound(this, playersound_jump, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); #endif return true; } @@ -570,7 +405,7 @@ void CheckWaterJump(entity this) #ifdef SVQC PHYS_TELEPORT_TIME(this) = time + 2; // safety net #elif defined(CSQC) - pmove_waterjumptime = 2; + PHYS_WATERJUMP_TIME(this) = 2; #endif } } @@ -646,15 +481,15 @@ float racecar_angle(float forward, float down) return ret * angle_mult; } +#ifdef SVQC string specialcommand = "xwxwxsxsxaxdxaxdx1x "; .float specialcommand_pos; void SpecialCommand(entity this) { -#ifdef SVQC if (!CheatImpulse(this, CHIMPULSE_GIVE_ALL.impulse)) LOG_INFO("A hollow voice says \"Plugh\".\n"); -#endif } +#endif bool PM_check_specialcommand(entity this, int buttons) { @@ -715,12 +550,12 @@ void PM_check_nickspam(entity this) #endif } -void PM_check_punch(entity this) +void PM_check_punch(entity this, float dt) { #ifdef SVQC if (this.punchangle != '0 0 0') { - float f = vlen(this.punchangle) - 10 * PHYS_INPUT_TIMELENGTH; + float f = vlen(this.punchangle) - 10 * dt; if (f > 0) this.punchangle = normalize(this.punchangle) * f; else @@ -729,7 +564,7 @@ void PM_check_punch(entity this) if (this.punchvector != '0 0 0') { - float f = vlen(this.punchvector) - 30 * PHYS_INPUT_TIMELENGTH; + float f = vlen(this.punchvector) - 30 * dt; if (f > 0) this.punchvector = normalize(this.punchvector) * f; else @@ -781,7 +616,8 @@ void PM_check_hitground(entity this) entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) ? GS_FALL_METAL : GS_FALL; - GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); + float vol = ((IS_DUCKED(this)) ? VOL_MUFFLED : VOL_BASE); + GlobalSound(this, gs, CH_PLAYER, vol, VOICETYPE_PLAYERSOUND); #endif } @@ -801,197 +637,40 @@ void PM_Footsteps(entity this) entity gs = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_METALSTEPS) ? GS_STEP_METAL : GS_STEP; - GlobalSound(this, gs, CH_PLAYER, VOICETYPE_PLAYERSOUND); + GlobalSound(this, gs, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); } #endif } -void PM_check_blocked(entity this) +void PM_check_slick(entity this) { -#ifdef SVQC - if (!this.player_blocked) + if(!IS_ONGROUND(this)) return; - this.movement = '0 0 0'; - this.disableclientprediction = 1; -#endif -} - -void PM_fly(entity this, float maxspd_mod) -{ - // noclipping or flying - UNSET_ONGROUND(this); - - this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)); - makevectors(this.v_angle); - //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z; - vector wishvel = v_forward * this.movement.x - + v_right * this.movement.y - + '0 0 1' * this.movement.z; - // acceleration - vector wishdir = normalize(wishvel); - float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod); -#ifdef SVQC - if(time >= PHYS_TELEPORT_TIME(this)) -#endif - PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0); - PM_ClientMovement_Move(this); -} - -void PM_swim(entity this, float maxspd_mod) -{ - // swimming - UNSET_ONGROUND(this); - float jump = PHYS_INPUT_BUTTON_JUMP(this); - // water jump only in certain situations - // this mimics quakeworld code - if (jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc) - { - vector yawangles = '0 1 0' * this.v_angle.y; - makevectors(yawangles); - vector forward = v_forward; - vector spot = this.origin + 24 * forward; - spot_z += 8; - traceline(spot, spot, MOVE_NOMONSTERS, this); - if (trace_startsolid) - { - spot_z += 24; - traceline(spot, spot, MOVE_NOMONSTERS, this); - if (!trace_startsolid) - { - this.velocity = forward * 50; - this.velocity_z = 310; - #ifdef CSQC - pmove_waterjumptime = 2; - #endif - UNSET_ONGROUND(this); - SET_JUMP_HELD(this); - } - } - } - makevectors(this.v_angle); - //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z; - vector wishvel = v_forward * this.movement.x - + v_right * this.movement.y - + '0 0 1' * this.movement.z; - if(this.viewloc) - wishvel.z = -160; // drift anyway - else if (wishvel == '0 0 0') - wishvel = '0 0 -60'; // drift towards bottom - - - vector wishdir = normalize(wishvel); - float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod) * 0.7; - - if (IS_DUCKED(this)) - wishspeed *= 0.5; - -// if (pmove_waterjumptime <= 0) // TODO: use - { - // water friction - float f = 1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this); - f = min(max(0, f), 1); - this.velocity *= f; - - f = wishspeed - this.velocity * wishdir; - if (f > 0) - { - float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, f); - this.velocity += accelspeed * wishdir; - } + if(!PHYS_SLICK_APPLYGRAVITY(this)) + return; - // holding jump button swims upward slowly - if (jump && !this.viewloc) - { -#if 0 - if (this.watertype & CONTENT_LAVA) - this.velocity_z = 50; - else if (this.watertype & CONTENT_SLIME) - this.velocity_z = 80; - else - { - if (IS_NEXUIZ_DERIVED(gamemode)) -#endif - this.velocity_z = 200; -#if 0 - else - this.velocity_z = 100; - } -#endif - } - } - if(this.viewloc) + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) { - const float addspeed = wishspeed - this.velocity * wishdir; - if (addspeed > 0) - { - const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed); - this.velocity += accelspeed * wishdir; - } + UNSET_ONGROUND(this); + SET_ONSLICK(this); } else - { - // water acceleration - PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this) * maxspd_mod, 1, 0, 0, 0); - PM_ClientMovement_Move(this); - } + UNSET_ONSLICK(this); } -.vector oldmovement; -void PM_ladder(entity this, float maxspd_mod) +void PM_check_blocked(entity this) { - // on a spawnfunc_func_ladder or swimming in spawnfunc_func_water - UNSET_ONGROUND(this); - - float g; - g = PHYS_GRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if (PHYS_ENTGRAVITY(this)) - g *= PHYS_ENTGRAVITY(this); - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - { - g *= 0.5; - this.velocity_z += g; - } - - this.velocity = this.velocity * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)); - makevectors(this.v_angle); - //wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z; - vector wishvel = v_forward * this.movement_x - + v_right * this.movement_y - + '0 0 1' * this.movement_z; - if(this.viewloc) - wishvel.z = this.oldmovement.x; - this.velocity_z += g; - if (this.ladder_entity.classname == "func_water") - { - float f = vlen(wishvel); - if (f > this.ladder_entity.speed) - wishvel *= (this.ladder_entity.speed / f); - - this.watertype = this.ladder_entity.skin; - f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z; - if ((this.origin_z + this.view_ofs_z) < f) - this.waterlevel = WATERLEVEL_SUBMERGED; - else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f) - this.waterlevel = WATERLEVEL_SWIMMING; - else if ((this.origin_z + this.mins_z + 1) < f) - this.waterlevel = WATERLEVEL_WETFEET; - else - { - this.waterlevel = WATERLEVEL_NONE; - this.watertype = CONTENT_EMPTY; - } - } - // acceleration - vector wishdir = normalize(wishvel); - float wishspeed = min(vlen(wishvel), PHYS_MAXSPEED(this) * maxspd_mod); - if(time >= PHYS_TELEPORT_TIME(this)) - // water acceleration - PM_Accelerate(this, wishdir, wishspeed, wishspeed, PHYS_ACCELERATE(this)*maxspd_mod, 1, 0, 0, 0); - PM_ClientMovement_Move(this); +#ifdef SVQC + if (!this.player_blocked) + return; + this.movement = '0 0 0'; + this.disableclientprediction = 1; +#endif } -void PM_jetpack(entity this, float maxspd_mod) +void PM_jetpack(entity this, float maxspd_mod, float dt) { //makevectors(this.v_angle.y * '0 1 0'); makevectors(this.v_angle); @@ -1012,11 +691,15 @@ void PM_jetpack(entity this, float maxspd_mod) float a_up = PHYS_JETPACK_ACCEL_UP(this); float a_add = PHYS_JETPACK_ANTIGRAVITY(this) * PHYS_GRAVITY(this); + if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { a_up = PHYS_JETPACK_REVERSE_THRUST(this); } + wishvel_x *= a_side; wishvel_y *= a_side; wishvel_z *= a_up; wishvel_z += a_add; + if(PHYS_JETPACK_REVERSE_THRUST(this) && PHYS_INPUT_BUTTON_CROUCH(self)) { wishvel_z *= -1; } + float best = 0; ////////////////////////////////////////////////////////////////////////////////////// // finding the maximum over all vectors of above form @@ -1072,7 +755,7 @@ void PM_jetpack(entity this, float maxspd_mod) fvel = min(1, vlen(wishvel) / best); if (PHYS_JETPACK_FUEL(this) && !(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel)); + f = min(1, PHYS_AMMO_FUEL(this) / (PHYS_JETPACK_FUEL(this) * dt * fvel)); else f = 1; @@ -1080,12 +763,12 @@ void PM_jetpack(entity this, float maxspd_mod) if (f > 0 && wishvel != '0 0 0') { - this.velocity = this.velocity + wishvel * f * PHYS_INPUT_TIMELENGTH; + this.velocity = this.velocity + wishvel * f * dt; UNSET_ONGROUND(this); #ifdef SVQC if (!(ITEMS_STAT(this) & IT_UNLIMITED_WEAPON_AMMO)) - this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * PHYS_INPUT_TIMELENGTH * fvel * f; + this.ammo_fuel -= PHYS_JETPACK_FUEL(this) * dt * fvel * f; ITEMS_STAT(this) |= IT_USING_JETPACK; @@ -1093,195 +776,6 @@ void PM_jetpack(entity this, float maxspd_mod) this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_fuel_regen); #endif } - -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - { - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - else - this.velocity_z -= g; - } - PM_ClientMovement_Move(this); - if(autocvar_cl_movement == 3) - { - if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - } -#endif -} - -void PM_walk(entity this, float maxspd_mod) -{ - if (!WAS_ONGROUND(this)) - { -#ifdef SVQC - if (autocvar_speedmeter) - LOG_TRACE(strcat("landing velocity: ", vtos(this.velocity), " (abs: ", ftos(vlen(this.velocity)), ")\n")); -#endif - if (this.lastground < time - 0.3) - this.velocity *= (1 - PHYS_FRICTION_ONLAND(this)); -#ifdef SVQC - if (this.jumppadcount > 1) - LOG_TRACE(strcat(ftos(this.jumppadcount), "x jumppad combo\n")); - this.jumppadcount = 0; -#endif - } - - // walking - makevectors(this.v_angle.y * '0 1 0'); - const vector wishvel = v_forward * this.movement.x - + v_right * this.movement.y; - // acceleration - const vector wishdir = normalize(wishvel); - float wishspeed = vlen(wishvel); - wishspeed = min(wishspeed, PHYS_MAXSPEED(this) * maxspd_mod); - if (IS_DUCKED(this)) wishspeed *= 0.5; - - // apply edge friction - const float f2 = vlen2(vec2(this.velocity)); - if (f2 > 0) - { - trace_dphitq3surfaceflags = 0; - tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); - // TODO: apply edge friction - // apply ground friction - const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) - ? PHYS_FRICTION_SLICK(this) - : PHYS_FRICTION(this); - - float f = sqrt(f2); - f = 1 - PHYS_INPUT_TIMELENGTH * realfriction * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1); - f = max(0, f); - this.velocity *= f; - /* - Mathematical analysis time! - - Our goal is to invert this mess. - - For the two cases we get: - v = v0 * (1 - PHYS_INPUT_TIMELENGTH * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this)) - = v0 - PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) - v0 = v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) - and - v = v0 * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) - v0 = v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) - - These cases would be chosen ONLY if: - v0 < PHYS_STOPSPEED(this) - v + PHYS_INPUT_TIMELENGTH * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this) - v < PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) - and, respectively: - v0 >= PHYS_STOPSPEED(this) - v / (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this) - v >= PHYS_STOPSPEED(this) * (1 - PHYS_INPUT_TIMELENGTH * PHYS_FRICTION(this)) - */ - } - const float addspeed = wishspeed - this.velocity * wishdir; - if (addspeed > 0) - { - const float accelspeed = min(PHYS_ACCELERATE(this) * PHYS_INPUT_TIMELENGTH * wishspeed, addspeed); - this.velocity += accelspeed * wishdir; - } -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - { - if (!(GAMEPLAYFIX_NOGRAVITYONGROUND)) - this.velocity_z -= g * (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE ? 0.5 : 1); - } - if (vdist(this.velocity, >, 0)) - PM_ClientMovement_Move(this); - if(autocvar_cl_movement == 3) - { - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - if (!IS_ONGROUND(this) || !GAMEPLAYFIX_NOGRAVITYONGROUND) - this.velocity_z -= g * 0.5; - } -#endif -} - -void PM_air(entity this, float buttons_prev, float maxspd_mod) -{ - makevectors(this.v_angle.y * '0 1 0'); - vector wishvel = v_forward * this.movement.x - + v_right * this.movement.y; - // acceleration - vector wishdir = normalize(wishvel); - float wishspeed = vlen(wishvel); - -#ifdef SVQC - if(time >= PHYS_TELEPORT_TIME(this)) -#elif defined(CSQC) - if(pmove_waterjumptime <= 0) -#endif - { - float maxairspd = PHYS_MAXAIRSPEED(this) * min(maxspd_mod, 1); - - // apply air speed limit - float airaccelqw = PHYS_AIRACCEL_QW(this); - float wishspeed0 = wishspeed; - wishspeed = min(wishspeed, maxairspd); - if (IS_DUCKED(this)) - wishspeed *= 0.5; - float airaccel = PHYS_AIRACCELERATE(this) * min(maxspd_mod, 1); - - float accelerating = (this.velocity * wishdir > 0); - float wishspeed2 = wishspeed; - - // CPM: air control - if (PHYS_AIRSTOPACCELERATE(this)) - { - vector curdir = normalize(vec2(this.velocity)); - airaccel += (PHYS_AIRSTOPACCELERATE(this)*maxspd_mod - airaccel) * max(0, -(curdir * wishdir)); - } - // note that for straight forward jumping: - // step = accel * PHYS_INPUT_TIMELENGTH * wishspeed0; - // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); - // --> - // dv/dt = accel * maxspeed (when slow) - // dv/dt = accel * maxspeed * (1 - accelqw) (when fast) - // log dv/dt = logaccel + logmaxspeed (when slow) - // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast) - float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero - if (PHYS_MAXAIRSTRAFESPEED(this)) - wishspeed = min(wishspeed, GeomLerp(PHYS_MAXAIRSPEED(this)*maxspd_mod, strafity, PHYS_MAXAIRSTRAFESPEED(this)*maxspd_mod)); - if (PHYS_AIRSTRAFEACCELERATE(this)) - airaccel = GeomLerp(airaccel, strafity, PHYS_AIRSTRAFEACCELERATE(this)*maxspd_mod); - if (PHYS_AIRSTRAFEACCEL_QW(this)) - airaccelqw = - (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1) - * - (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this)))); - // !CPM - - if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) - PM_AirAccelerate(this, wishdir, wishspeed2); - else { - float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0; - PM_Accelerate(this, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this)); - } - - if (PHYS_AIRCONTROL(this)) - CPM_PM_Aircontrol(this, wishdir, wishspeed2); - } -#ifdef CSQC - float g = PHYS_GRAVITY(this) * PHYS_ENTGRAVITY(this) * PHYS_INPUT_TIMELENGTH; - if(autocvar_cl_movement == 3) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; - else - this.velocity_z -= g; -#endif - PM_ClientMovement_Move(this); -#ifdef CSQC - if(autocvar_cl_movement == 3) - if (!IS_ONGROUND(this) || !(GAMEPLAYFIX_NOGRAVITYONGROUND)) - if (GAMEPLAYFIX_GRAVITYUNAFFECTEDBYTICRATE) - this.velocity_z -= g * 0.5; -#endif } // used for calculating airshots @@ -1297,262 +791,22 @@ bool IsFlying(entity this) return true; } -void PM_Main(entity this) -{ - int buttons = PHYS_INPUT_BUTTON_MASK(this); -#ifdef CSQC - this.items = STAT(ITEMS); - - this.movement = PHYS_INPUT_MOVEVALUES(this); - - this.spectatorspeed = STAT(SPECTATORSPEED); - - this.team = myteam + 1; // is this correct? - if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump - UNSET_JUMP_HELD(this); // canjump = true - pmove_waterjumptime -= PHYS_INPUT_TIMELENGTH; - - PM_ClientMovement_UpdateStatus(this, true); -#endif - - this.oldmovement = this.movement; - - -#ifdef SVQC - WarpZone_PlayerPhysics_FixVAngle(this); -#endif - float maxspeed_mod = 1; - maxspeed_mod *= PHYS_HIGHSPEED(this); - -#ifdef SVQC - Physics_UpdateStats(this, maxspeed_mod); - - if (this.PlayerPhysplug) - if (this.PlayerPhysplug(this)) - return; -#endif - -#ifdef SVQC - anticheat_physics(this); -#endif - - if (PM_check_specialcommand(this, buttons)) - return; -#ifdef SVQC - if (sv_maxidle > 0) - { - if (buttons != this.buttons_old || this.movement != this.movement_old || this.v_angle != this.v_angle_old) - this.parm_idlesince = time; - } -#endif - int buttons_prev = this.buttons_old; - this.buttons_old = buttons; - this.movement_old = this.movement; - this.v_angle_old = this.v_angle; - - PM_check_nickspam(this); - - PM_check_punch(this); -#ifdef SVQC - if (IS_BOT_CLIENT(this)) - { - if (playerdemo_read(this)) - return; - bot_think(this); - } -#endif - -#ifdef SVQC - if (IS_PLAYER(this)) - { - const bool allowed_to_move = (time >= game_starttime); - if (!allowed_to_move) - { - this.velocity = '0 0 0'; - this.movetype = MOVETYPE_NONE; - this.disableclientprediction = 2; - } - else if (this.disableclientprediction == 2) - { - if (this.movetype == MOVETYPE_NONE) - this.movetype = MOVETYPE_WALK; - this.disableclientprediction = 0; - } - } -#endif - -#ifdef SVQC - if (this.movetype == MOVETYPE_NONE) - return; - - // when we get here, disableclientprediction cannot be 2 - this.disableclientprediction = 0; -#endif - - viewloc_PlayerPhysics(this); - - PM_check_frozen(this); - - PM_check_blocked(this); - - maxspeed_mod = 1; - - if (this.in_swamp) - maxspeed_mod *= this.swamp_slowdown; //cvar("g_balance_swamp_moverate"); - - // conveyors: first fix velocity - if (this.conveyor.state) - this.velocity -= this.conveyor.movedir; - - MUTATOR_CALLHOOK(PlayerPhysics, this); - - if (!IS_PLAYER(this)) - { -#ifdef SVQC - maxspeed_mod = autocvar_sv_spectator_speed_multiplier; - if (!this.spectatorspeed) - this.spectatorspeed = maxspeed_mod; - if (this.impulse && this.impulse <= 19 || (this.impulse >= 200 && this.impulse <= 209) || (this.impulse >= 220 && this.impulse <= 229)) - { - if (this.lastclassname != STR_PLAYER) - { - if (this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) - this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); - else if (this.impulse == 11) - this.spectatorspeed = maxspeed_mod; - else if (this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) - this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5); - else if (this.impulse >= 1 && this.impulse <= 9) - this.spectatorspeed = 1 + 0.5 * (this.impulse - 1); - } // otherwise just clear - this.impulse = 0; - } -#endif - maxspeed_mod = this.spectatorspeed; - } -#ifdef SVQC - - float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod; - if(this.speed != spd) - { - this.speed = spd; - string temps = ftos(spd); - stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n")); - stuffcmd(this, strcat("cl_backspeed ", temps, "\n")); - stuffcmd(this, strcat("cl_sidespeed ", temps, "\n")); - stuffcmd(this, strcat("cl_upspeed ", temps, "\n")); - } - - if(this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min) - { - this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min; - stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min)); - } - if(this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max) - { - this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max; - stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max)); - } -#endif - - if(IS_DEAD(this)) - { - // handle water here - vector midpoint = ((this.absmin + this.absmax) * 0.5); - if(pointcontents(midpoint) == CONTENT_WATER) - { - this.velocity = this.velocity * 0.5; - - // do we want this? - //if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER) - //{ this.velocity_z = 70; } - } - goto end; - } - -#ifdef SVQC - if (!this.fixangle) - this.angles = '0 1 0' * this.v_angle.y; -#endif - - if (IS_PLAYER(this) && IS_ONGROUND(this)) - { - PM_check_hitground(this); - PM_Footsteps(this); - } - -#ifdef SVQC - if(IsFlying(this)) - this.wasFlying = 1; -#endif - - if (IS_PLAYER(this)) - CheckPlayerJump(this); - - if (this.flags & FL_WATERJUMP) - { - this.velocity_x = this.movedir.x; - this.velocity_y = this.movedir.y; - if (time > PHYS_TELEPORT_TIME(this) || this.waterlevel == WATERLEVEL_NONE - #ifdef CSQC - || pmove_waterjumptime <= 0 - #endif - ) - { - this.flags &= ~FL_WATERJUMP; - PHYS_TELEPORT_TIME(this) = 0; - #ifdef CSQC - pmove_waterjumptime = 0; - #endif - } - } - - else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod)) - { } - -#ifdef SVQC - else if (this.movetype == MOVETYPE_NOCLIP || this.movetype == MOVETYPE_FLY || this.movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this)) -#elif defined(CSQC) - else if (this.move_movetype == MOVETYPE_NOCLIP || this.move_movetype == MOVETYPE_FLY || this.move_movetype == MOVETYPE_FLY_WORLDONLY || MUTATOR_CALLHOOK(IsFlying, this)) -#endif - PM_fly(this, maxspeed_mod); - - else if (this.waterlevel >= WATERLEVEL_SWIMMING) - PM_swim(this, maxspeed_mod); - - else if (time < this.ladder_time) - PM_ladder(this, maxspeed_mod); - - else if (ITEMS_STAT(this) & IT_USING_JETPACK) - PM_jetpack(this, maxspeed_mod); - - else if (IS_ONGROUND(this)) - PM_walk(this, maxspeed_mod); - - else - PM_air(this, buttons_prev, maxspeed_mod); - -LABEL(end) - if (IS_ONGROUND(this)) - this.lastground = time; - - // conveyors: then break velocity again - if(this.conveyor.state) - this.velocity += this.conveyor.movedir; - - this.lastflags = this.flags; - - this.lastclassname = this.classname; -} +void sys_phys_update(entity this, float dt); #if defined(SVQC) -void SV_PlayerPhysics() +void SV_PlayerPhysics(entity this) #elif defined(CSQC) void CSQC_ClientMovement_PlayerMove_Frame(entity this) #endif { + sys_phys_update(this, PHYS_INPUT_TIMELENGTH); + #ifdef SVQC - ENGINE_EVENT(); + this.pm_frametime = frametime; +#elif defined(CSQC) + if((ITEMS_STAT(this) & IT_USING_JETPACK) && !IS_DEAD(this) && !intermission) + this.csqcmodel_modelflags |= MF_ROCKET; + else + this.csqcmodel_modelflags &= ~MF_ROCKET; #endif - PM_Main(this); } diff --git a/qcsrc/common/physics/player.qh b/qcsrc/common/physics/player.qh index fd1b610de6..39cc573ee9 100644 --- a/qcsrc/common/physics/player.qh +++ b/qcsrc/common/physics/player.qh @@ -1,8 +1,9 @@ -#ifndef COMMON_PHYSICS_H -#define COMMON_PHYSICS_H +#pragma once // Client/server mappings +.float pm_frametime; + .entity conveyor; .float race_penalty; @@ -23,7 +24,7 @@ .vector v_angle_old; .string lastclassname; -.float(entity) PlayerPhysplug; +.float(entity,float) PlayerPhysplug; float AdjustAirAccelQW(float accelqw, float factor); bool IsFlying(entity a); @@ -35,6 +36,7 @@ bool IsFlying(entity a); #define GAMEPLAYFIX_STEPDOWN(s) STAT(GAMEPLAYFIX_STEPDOWN, s) #define GAMEPLAYFIX_STEPMULTIPLETIMES(s) STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, s) #define GAMEPLAYFIX_UNSTICKPLAYERS(s) STAT(GAMEPLAYFIX_UNSTICKPLAYERS, s) +#define GAMEPLAYFIX_WATERTRANSITION(s) STAT(GAMEPLAYFIX_WATERTRANSITION, s) #define PHYS_ACCELERATE(s) STAT(MOVEVARS_ACCELERATE, s) #define PHYS_AIRACCELERATE(s) STAT(MOVEVARS_AIRACCELERATE, s) @@ -44,6 +46,7 @@ bool IsFlying(entity a); #define PHYS_AIRCONTROL(s) STAT(MOVEVARS_AIRCONTROL, s) #define PHYS_AIRCONTROL_PENALTY(s) STAT(MOVEVARS_AIRCONTROL_PENALTY, s) #define PHYS_AIRCONTROL_POWER(s) STAT(MOVEVARS_AIRCONTROL_POWER, s) +#define PHYS_AIRCONTROL_BACKWARDS(s) STAT(MOVEVARS_AIRCONTROL_BACKWARDS, s) #define PHYS_AIRSPEEDLIMIT_NONQW(s) STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, s) #define PHYS_AIRSTOPACCELERATE(s) STAT(MOVEVARS_AIRSTOPACCELERATE, s) #define PHYS_AIRSTRAFEACCELERATE(s) STAT(MOVEVARS_AIRSTRAFEACCELERATE, s) @@ -67,6 +70,7 @@ bool IsFlying(entity a); #define PHYS_JETPACK_FUEL(s) STAT(JETPACK_FUEL, s) #define PHYS_JETPACK_MAXSPEED_SIDE(s) STAT(JETPACK_MAXSPEED_SIDE, s) #define PHYS_JETPACK_MAXSPEED_UP(s) STAT(JETPACK_MAXSPEED_UP, s) +#define PHYS_JETPACK_REVERSE_THRUST(s) STAT(JETPACK_REVERSE_THRUST, s) #define PHYS_JUMPSPEEDCAP_DISABLE_ONRAMPS(s) STAT(MOVEVARS_JUMPSPEEDCAP_DISABLE_ONRAMPS, s) #define PHYS_JUMPSTEP(s) STAT(MOVEVARS_JUMPSTEP, s) @@ -93,6 +97,8 @@ bool IsFlying(entity a); #define UPWARD_VELOCITY_CLEARS_ONGROUND(s) STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, s) +#define PHYS_SLICK_APPLYGRAVITY(s) STAT(SLICK_APPLYGRAVITY, s) + #define PHYS_INPUT_BUTTON_ATCK(s) PHYS_INPUT_BUTTON_BUTTON1(s) #define PHYS_INPUT_BUTTON_JUMP(s) PHYS_INPUT_BUTTON_BUTTON2(s) #define PHYS_INPUT_BUTTON_ATCK2(s) PHYS_INPUT_BUTTON_BUTTON3(s) @@ -115,6 +121,7 @@ STATIC_INIT(PHYS_INPUT_BUTTON_HOOK) #define PHYS_INPUT_BUTTON_PRYDON(s) PHYS_INPUT_BUTTON_BUTTON_PRYDON(s) #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) #ifdef CSQC STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) @@ -122,6 +129,12 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) 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"); +} #endif // if more buttons are needed, start using impulse bits as buttons @@ -152,18 +165,23 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) #define UNSET_JUMP_HELD(s) ((s).flags |= FL_JUMPRELEASED) #define WAS_ONGROUND(s) boolean((s).lastflags & FL_ONGROUND) +#define WAS_ONSLICK(s) boolean((s).lastflags & FL_ONSLICK) #define ITEMS_STAT(s) ((s).items) .float teleport_time; +#define PHYS_TELEPORT_TIME(s) ((s).teleport_time) + +.float waterjump_time; +#define PHYS_WATERJUMP_TIME(s) ((s).waterjump_time) #ifdef CSQC + #define PHYS_FIXANGLE(s) ('0 0 0') + string autocvar_cl_jumpspeedcap_min; string autocvar_cl_jumpspeedcap_max; - noref float pmove_waterjumptime; - const int FL_WATERJUMP = 2048; // player jumping out of water const int FL_JUMPRELEASED = 4096; // for jump debouncing @@ -177,9 +195,9 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) .entity hook; // TODO - #define IS_CLIENT(s) ((s).isplayermodel) + #define IS_CLIENT(s) ((s).isplayermodel || (s) == csqcplayer) #define IS_PLAYER(s) ((s).isplayermodel) - #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel) + #define IS_NOT_A_CLIENT(s) (!(s).isplayermodel && (s) != csqcplayer) #define isPushable(s) ((s).isplayermodel || (s).pushable || ((s).flags & FL_PROJECTILE)) //float player_multijump; @@ -187,8 +205,6 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) #define PHYS_GRAVITY(s) STAT(MOVEVARS_GRAVITY, s) - #define PHYS_TELEPORT_TIME(s) ((s).teleport_time) - #define TICRATE ticrate #define PHYS_INPUT_ANGLES(s) input_angles @@ -237,8 +253,12 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) #elif defined(SVQC) + #define PHYS_FIXANGLE(s) ((s).fixangle) + bool Physics_Valid(string thecvar); + void Physics_UpdateStats(entity this, float maxspd_mod); + .float stat_sv_airspeedlimit_nonqw = _STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW); .float stat_sv_maxspeed = _STAT(MOVEVARS_MAXSPEED); @@ -246,8 +266,6 @@ STATIC_INIT(PHYS_INPUT_BUTTON_JETPACK) .string jumpspeedcap_min; .string jumpspeedcap_max; - #define PHYS_TELEPORT_TIME(s) ((s).teleport_time) - #define PHYS_GRAVITY(s) autocvar_sv_gravity #define TICRATE sys_frametime @@ -319,5 +337,3 @@ NET_HANDLE(setpause, bool) return true; } #endif - -#endif diff --git a/qcsrc/common/playerstats.qc b/qcsrc/common/playerstats.qc index 7e99bebea7..b879b63b09 100644 --- a/qcsrc/common/playerstats.qc +++ b/qcsrc/common/playerstats.qc @@ -1,13 +1,14 @@ +#include "playerstats.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include "constants.qh" #include "util.qh" - #include - #include "../server/weapons/accuracy.qh" + #include + #include "../server/anticheat.qh" #include "../server/defs.qh" - #include "playerstats.qh" #include "../server/scores.qh" + #include "../server/weapons/accuracy.qh" #endif #ifdef SVQC @@ -141,6 +142,7 @@ void PlayerStats_GameReport_FinalizePlayer(entity p) PS_GR_P_ADDVAL(p, PLAYERSTATS_JOINS, 1); PlayerStats_GameReport_Accuracy(p); + anticheat_report_to_playerstats(p); if(IS_REAL_CLIENT(p)) { @@ -249,6 +251,8 @@ void PlayerStats_GameReport_Init() // initiated before InitGameplayMode so that PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_BOTLIKE); PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTBLOOD); PlayerStats_GameReport_AddEvent(PLAYERSTATS_ACHIEVEMENT_FIRSTVICTIM); + + anticheat_register_to_playerstats(); } else { PlayerStats_GameReport_DelayMapVote = false; } } @@ -379,9 +383,9 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status) { // url_fclose is processing, we got a response for writing the data // this must come from HTTP - LOG_TRACE("Got response from player stats server:\n"); - while((s = url_fgets(fh))) { LOG_TRACE(" ", s, "\n"); } - LOG_TRACE("End of response.\n"); + LOG_TRACE("Got response from player stats server:"); + while((s = url_fgets(fh))) { LOG_TRACE(" ", s); } + LOG_TRACE("End of response."); url_fclose(fh); break; } @@ -389,7 +393,7 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status) case URL_READY_CLOSED: { // url_fclose has finished - LOG_TRACE("Player stats written\n"); + LOG_TRACE("Player stats written"); PlayerStats_GameReport_DelayMapVote = false; if(PS_GR_OUT_DB >= 0) { @@ -416,19 +420,21 @@ void PlayerStats_GameReport_Handler(entity fh, entity pass, float status) void PlayerStats_PlayerBasic(entity joiningplayer, float newrequest) { + PlayerScore_Add(joiningplayer, SP_ELO, -1); // http://stats.xonotic.org/player/GgXRw6piDtFIbMArMuiAi8JG4tiin8VLjZgsKB60Uds=/elo.txt if(autocvar_g_playerstats_playerbasic_uri != "") { string uri = autocvar_g_playerstats_playerbasic_uri; - if(joiningplayer.crypto_idfp != "") - { + if (joiningplayer.crypto_idfp == "") { + PlayerScore_Add(joiningplayer, SP_ELO, -1); + } else { // create the database if it doesn't already exist if(PS_B_IN_DB < 0) PS_B_IN_DB = db_create(); // now request the information uri = strcat(uri, "/player/", uri_escape(uri_escape(uri_escape(joiningplayer.crypto_idfp))), "/elo.txt"); - LOG_TRACE("Retrieving playerstats from URL: ", uri, "\n"); + LOG_TRACE("Retrieving playerstats from URL: ", uri); url_single_fopen( uri, FILE_APPEND, @@ -452,6 +458,7 @@ void PlayerStats_PlayerBasic(entity joiningplayer, float newrequest) else { // server has this disabled, kill the DB and set status to idle + PlayerScore_Add(joiningplayer, SP_ELO, -1); if(PS_B_IN_DB >= 0) { db_close(PS_B_IN_DB); @@ -466,12 +473,10 @@ void PlayerStats_PlayerBasic_CheckUpdate(entity joiningplayer) { // determine whether we should retrieve playerbasic information again - #if 0 - LOG_INFOF("PlayerStats_PlayerBasic_CheckUpdate('%s'): %f\n", + LOG_TRACEF("PlayerStats_PlayerBasic_CheckUpdate('%s'): %f", joiningplayer.netname, time ); - #endif // TODO: check to see if this playerid is inside the database already somehow... // for now we'll just check the field, but this won't work for players who disconnect and reconnect properly @@ -491,7 +496,7 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status) { case URL_READY_CANWRITE: { - LOG_TRACE("-- Sending data to player stats server\n"); + LOG_TRACE("-- Sending data to player stats server"); /*url_fputs(fh, "V 1\n"); #ifdef WATERMARK url_fputs(fh, sprintf("R %s\n", WATERMARK)); @@ -503,76 +508,76 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status) url_fputs(fh, sprintf("m %s %s\n", cvar_string("_cl_playermodel"), cvar_string("_cl_playerskin"))); // model/skin */url_fputs(fh, "\n"); url_fclose(fh); - break; + return; } case URL_READY_CANREAD: { - string s = ""; - LOG_TRACE("-- Got response from player stats server:\n"); - //string gametype = string_null; - while((s = url_fgets(fh))) - { - LOG_TRACE(" ", s, "\n"); - /* + bool handled = false; + string gt = string_null; + for (string s = ""; (s = url_fgets(fh)); ) { + int n = tokenizebyseparator(s, " "); // key value? data + if (n == 1) continue; string key = "", value = "", data = ""; - - n = tokenizebyseparator(s, " "); // key (value) data - if (n == 1) - continue; - else if (n == 2) - { - key = argv(0); - data = argv(1); - } - else if (n >= 3) - { - key = argv(0); - value = argv(1); - data = argv(2); + if (n == 2) { + key = argv(0); + data = argv(1); + } else if (n >= 3) { + key = argv(0); + value = argv(1); + data = argv(2); } - - if (data == "") - continue; - - if (key == "#") - continue; - else if (key == "V") - PlayerInfo_AddItem(p, "_version", data); - else if (key == "R") - PlayerInfo_AddItem(p, "_release", data); - else if (key == "T") - PlayerInfo_AddItem(p, "_time", data); - else if (key == "S") - PlayerInfo_AddItem(p, "_statsurl", data); - else if (key == "P") - PlayerInfo_AddItem(p, "_hashkey", data); - else if (key == "n") - PlayerInfo_AddItem(p, "_playernick", data); - else if (key == "i") - PlayerInfo_AddItem(p, "_playerid", data); - else if (key == "G") - gametype = data; - else if (key == "e" && value != "") - { - if (gametype == "") - PlayerInfo_AddItem(p, value, data); - else - PlayerInfo_AddItem(p, sprintf("%s/%s", gametype, value), data); - } - else - continue; - */ + switch (key) { + case "V": + // PlayerInfo_AddItem(p, "_version", data); + break; + case "R": + // PlayerInfo_AddItem(p, "_release", data); + break; + case "T": + // PlayerInfo_AddItem(p, "_time", data); + break; + case "S": + // PlayerInfo_AddItem(p, "_statsurl", data); + break; + case "P": + // PlayerInfo_AddItem(p, "_hashkey", data); + break; + case "n": + // PlayerInfo_AddItem(p, "_playernick", data); + break; + case "i": + // PlayerInfo_AddItem(p, "_playerid", data); + // p.xonstat_id = stof(data); + break; + case "G": + gt = data; + break; + case "e": + LOG_TRACE("G: ", gt); + LOG_TRACE("e: ", data); + if (gt == GetGametype()) { + handled = true; + float e = stof(data); + PlayerScore_Add(p, SP_ELO, +1 + e); + } + if (gt == "") { + // PlayerInfo_AddItem(p, value, data); + } else { + // PlayerInfo_AddItem(p, sprintf("%s/%s", gt, value), data); + } + break; + } } - LOG_TRACE("-- End of response.\n"); url_fclose(fh); + if (handled) return; break; } case URL_READY_CLOSED: { // url_fclose has finished LOG_INFO("Player stats synchronized with server\n"); - break; + return; } case URL_READY_ERROR: @@ -582,6 +587,7 @@ void PlayerStats_PlayerBasic_Handler(entity fh, entity p, float status) break; } } + PlayerScore_Add(p, SP_ELO, -1); } #endif // SVQC @@ -617,7 +623,7 @@ void PlayerStats_PlayerDetail_AddItem(string event, string data) // now actually set the event data db_put(PS_D_IN_DB, sprintf("#%s", event), data); - LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB\n"); + LOG_TRACE("Added item ", sprintf("#%s", event), "=", data, " to PS_D_IN_DB"); } void PlayerStats_PlayerDetail() @@ -630,7 +636,7 @@ void PlayerStats_PlayerDetail() PS_D_IN_DB = db_create(); //uri = strcat(uri, "/player/", uri_escape(crypto_getmyidfp(0))); - LOG_TRACE("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri, "\n"); + LOG_TRACE("Retrieving playerstats from URL: ", autocvar_g_playerstats_playerdetail_uri); url_single_fopen( autocvar_g_playerstats_playerdetail_uri, FILE_APPEND, @@ -685,7 +691,7 @@ void PlayerStats_PlayerDetail_Handler(entity fh, entity unused, float status) { case URL_READY_CANWRITE: { - LOG_TRACE("PlayerStats_PlayerDetail_Handler(): Sending data to player stats server...\n"); + LOG_TRACE("PlayerStats_PlayerDetail_Handler(): Sending data to player stats server..."); url_fputs(fh, "V 1\n"); #ifdef WATERMARK url_fputs(fh, sprintf("R %s\n", WATERMARK)); diff --git a/qcsrc/common/playerstats.qh b/qcsrc/common/playerstats.qh index ec309c4f4c..1fafb13978 100644 --- a/qcsrc/common/playerstats.qh +++ b/qcsrc/common/playerstats.qh @@ -1,5 +1,4 @@ -#ifndef PLAYERSTATS_H -#define PLAYERSTATS_H +#pragma once #ifdef SVQC //float PS_PM_IN_DB = -1; // playerstats_prematch_in_db // db for info COLLECTED at the beginning of a match @@ -41,6 +40,7 @@ const string PLAYERSTATS_SCOREBOARD_POS = "scoreboardpos"; const string PLAYERSTATS_TOTAL = "total-"; const string PLAYERSTATS_SCOREBOARD = "scoreboard-"; +const string PLAYERSTATS_ANTICHEAT = "anticheat-"; const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_3 = "achievement-kill-spree-3"; const string PLAYERSTATS_ACHIEVEMENT_KILL_SPREE_5 = "achievement-kill-spree-5"; @@ -116,4 +116,3 @@ void PlayerStats_PlayerDetail(); void PlayerStats_PlayerDetail_CheckUpdate(); void PlayerStats_PlayerDetail_Handler(entity fh, entity p, float status); #endif -#endif diff --git a/qcsrc/common/sounds/all.inc b/qcsrc/common/sounds/all.inc index 69a95be0cc..4394e17406 100644 --- a/qcsrc/common/sounds/all.inc +++ b/qcsrc/common/sounds/all.inc @@ -3,6 +3,7 @@ #include "../teams.qh" string W_Sound(string w_snd); +string Item_Sound(string it_snd); SOUND(ARC_FIRE, W_Sound("arc_fire")); SOUND(ARC_LOOP, W_Sound("arc_loop")); @@ -236,20 +237,20 @@ SOUND(NADE_BEEP, "overkill/grenadebip"); SOUND(BUFF_LOST, "relics/relic_effect"); -SOUND(POWEROFF, "misc/poweroff"); -SOUND(POWERUP, "misc/powerup"); -SOUND(SHIELD_RESPAWN, "misc/shield_respawn"); -SOUND(STRENGTH_RESPAWN, "misc/strength_respawn"); +SOUND(POWEROFF, Item_Sound("poweroff")); +SOUND(POWERUP, Item_Sound("powerup")); +SOUND(SHIELD_RESPAWN, Item_Sound("shield_respawn")); +SOUND(STRENGTH_RESPAWN, Item_Sound("strength_respawn")); -SOUND(ARMOR25, "misc/armor25"); +SOUND(ARMOR25, Item_Sound("armor25")); SOUND(ARMORIMPACT, "misc/armorimpact"); SOUND(BODYIMPACT1, "misc/bodyimpact1"); SOUND(BODYIMPACT2, "misc/bodyimpact2"); -SOUND(ITEMPICKUP, "misc/itempickup"); -SOUND(ITEMRESPAWNCOUNTDOWN, "misc/itemrespawncountdown"); -SOUND(ITEMRESPAWN, "misc/itemrespawn"); -SOUND(MEGAHEALTH, "misc/megahealth"); +SOUND(ITEMPICKUP, Item_Sound("itempickup")); +SOUND(ITEMRESPAWNCOUNTDOWN, Item_Sound("itemrespawncountdown")); +SOUND(ITEMRESPAWN, Item_Sound("itemrespawn")); +SOUND(MEGAHEALTH, Item_Sound("megahealth")); SOUND(LAVA, "player/lava"); SOUND(SLIME, "player/slime"); diff --git a/qcsrc/common/sounds/all.qc b/qcsrc/common/sounds/all.qc index 9096dddfa8..b3bdf99e91 100644 --- a/qcsrc/common/sounds/all.qc +++ b/qcsrc/common/sounds/all.qc @@ -1,3 +1,4 @@ +#include "all.qh" #ifdef SVQC bool autocvar_bot_sound_monopoly; diff --git a/qcsrc/common/sounds/all.qh b/qcsrc/common/sounds/all.qh index a7fe25f17d..b7b296341b 100644 --- a/qcsrc/common/sounds/all.qh +++ b/qcsrc/common/sounds/all.qh @@ -1,9 +1,8 @@ -#ifndef SOUNDS_ALL_H -#define SOUNDS_ALL_H +#pragma once #include "sound.qh" -REGISTRY(Sounds, BITS(8)) +REGISTRY(Sounds, BITS(9)) #define Sounds_from(i) _Sounds_from(i, SND_Null) REGISTER_REGISTRY(Sounds) @@ -21,4 +20,3 @@ PRECACHE(Sounds) { SOUND(Null, "misc/null"); #include "all.inc" #include "all.qc" -#endif diff --git a/qcsrc/common/sounds/sound.qh b/qcsrc/common/sounds/sound.qh index 90b2758aca..8c4aecbda0 100644 --- a/qcsrc/common/sounds/sound.qh +++ b/qcsrc/common/sounds/sound.qh @@ -1,5 +1,4 @@ -#ifndef SOUND_H -#define SOUND_H +#pragma once // negative = SVQC autochannels // positive = one per entity @@ -35,6 +34,7 @@ const float ATTEN_MAX = 3.984375; const float VOL_BASE = 0.7; const float VOL_BASEVOICE = 1.0; +const float VOL_MUFFLED = 0.35; // Play all sounds via sound7, for access to the extra channels. // Otherwise, channels 8 to 15 would be blocked for a weird QW feature. @@ -93,8 +93,8 @@ const float VOL_BASEVOICE = 1.0; } MACRO_END CLASS(Sound, Object) - ATTRIB(Sound, m_id, int, 0) - ATTRIB(Sound, sound_str, string(), func_null) + ATTRIB(Sound, m_id, int, 0); + ATTRIB(Sound, sound_str, string()); CONSTRUCTOR(Sound, string() path) { CONSTRUCT(Sound); @@ -114,7 +114,7 @@ CLASS(Sound, Object) /**/ #define tryext(ext) { string s = strcat(base, "." #ext); if (fexists(strcat("sound/", s))) return s; } extensions(tryext); - LOG_WARNINGF("Missing sound: \"%s\"\n", strcat("sound/", base)); + LOG_WARNF("Missing sound: \"%s\"", strcat("sound/", base)); #undef tryext #undef extensions return string_null; @@ -125,9 +125,7 @@ CLASS(Sound, Object) TC(Sound, this); string s = Sound_fixpath(this); if (!s) return; - LOG_DEBUGF("precache_sound(\"%s\")\n", s); + profile(sprintf("precache_sound(\"%s\")", s)); precache_sound(s); } ENDCLASS(Sound) - -#endif diff --git a/qcsrc/common/state.qc b/qcsrc/common/state.qc index 0ffe55eb4f..1a2fb1e7bf 100644 --- a/qcsrc/common/state.qc +++ b/qcsrc/common/state.qc @@ -15,9 +15,15 @@ void PlayerState_detach(entity this) PlayerState ps = PS(this); if (!ps) return; // initial connect PS(this) = NULL; + if (ps.m_client != this) return; // don't own state, spectator + ps.m_switchweapon = WEP_Null; + ps.m_weapon = WEP_Null; + ps.m_switchingweapon = WEP_Null; + ps.ps_push(ps, this); + FOREACH_CLIENT(PS(it) == ps, { PS(it) = NULL; }); - remove(ps); + delete(ps); Inventory_delete(this); } @@ -39,13 +45,12 @@ void ClientState_attach(entity this) GetCvars(this, 0); // get other cvars from player - if (IS_REAL_CLIENT(this)) { PlayerStats_PlayerBasic_CheckUpdate(this); } - // TODO: fold all of these into ClientState DecodeLevelParms(this); PlayerScore_Attach(this); + PlayerStats_PlayerBasic_CheckUpdate(this); ClientData_Attach(this); accuracy_init(this); entcs_attach(this); @@ -58,7 +63,7 @@ void ClientState_attach(entity this) void bot_clientdisconnect(entity this); void W_HitPlotClose(entity this); -void anticheat_report(entity this); +void anticheat_report_to_eventlog(entity this); void playerdemo_shutdown(entity this); void entcs_detach(entity this); void accuracy_free(entity this); @@ -67,7 +72,7 @@ void PlayerScore_Detach(entity this); void ClientState_detach(entity this) { - remove(CS(this)); + delete(CS(this)); this._cs = NULL; GetCvars(this, -1); // free cvars @@ -75,7 +80,7 @@ void ClientState_detach(entity this) bot_clientdisconnect(this); W_HitPlotClose(this); - anticheat_report(this); + anticheat_report_to_eventlog(this); playerdemo_shutdown(this); entcs_detach(this); accuracy_free(this); diff --git a/qcsrc/common/state.qh b/qcsrc/common/state.qh index 110df1c575..94b9bc325f 100644 --- a/qcsrc/common/state.qh +++ b/qcsrc/common/state.qh @@ -6,15 +6,15 @@ * Server: instance per client, clients decoupled from players */ CLASS(PlayerState, Object) - ATTRIB(PlayerState, m_client, entity, NULL) + ATTRIB(PlayerState, m_client, entity); CONSTRUCTOR(PlayerState, entity client) { CONSTRUCT(PlayerState); this.m_client = client; } - ATTRIB(PlayerState, m_switchingweapon, Weapon, Weapons_from(-1)) - ATTRIB(PlayerState, m_switchweapon, Weapon, Weapons_from(-1)) - ATTRIB(PlayerState, m_weapon, Weapon, Weapons_from(-1)) + ATTRIB(PlayerState, m_switchingweapon, Weapon, Weapons_from(-1)); + ATTRIB(PlayerState, m_switchweapon, Weapon, Weapons_from(-1)); + ATTRIB(PlayerState, m_weapon, Weapon, Weapons_from(-1)); METHOD(PlayerState, ps_push, void(PlayerState this, entity cl)) { TC(PlayerState, this); @@ -37,7 +37,7 @@ void PlayerState_detach(entity this); * Server: instance per client */ CLASS(ClientState, Object) - ATTRIB(ClientState, m_client, entity, NULL) + ATTRIB(ClientState, m_client, entity); CONSTRUCTOR(ClientState, entity client) { CONSTRUCT(ClientState); diff --git a/qcsrc/common/stats.qh b/qcsrc/common/stats.qh index 2c471f0db3..7649954b32 100644 --- a/qcsrc/common/stats.qh +++ b/qcsrc/common/stats.qh @@ -1,8 +1,7 @@ -#ifndef STATS_H -#define STATS_H +#pragma once #ifdef SVQC -#include +#include #endif // Full list of all stat constants, included in a single location for easy reference @@ -45,14 +44,14 @@ const int MAX_CL_STATS = 256; REGISTER_STAT(WEAPONS, vectori) REGISTER_STAT(WEAPONSINMAP, vectori) -REGISTER_STAT(PL_VIEW_OFS, vector, autocvar_sv_player_viewoffset) -REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector, autocvar_sv_player_crouch_viewoffset) +REGISTER_STAT(PL_VIEW_OFS, vector) +REGISTER_STAT(PL_CROUCH_VIEW_OFS, vector) -REGISTER_STAT(PL_MIN, vector, autocvar_sv_player_mins) -REGISTER_STAT(PL_CROUCH_MIN, vector, autocvar_sv_player_crouch_mins) +REGISTER_STAT(PL_MIN, vector) +REGISTER_STAT(PL_CROUCH_MIN, vector) -REGISTER_STAT(PL_MAX, vector, autocvar_sv_player_maxs) -REGISTER_STAT(PL_CROUCH_MAX, vector, autocvar_sv_player_crouch_maxs) +REGISTER_STAT(PL_MAX, vector) +REGISTER_STAT(PL_CROUCH_MAX, vector) REGISTER_STAT(KH_KEYS, int) @@ -64,9 +63,10 @@ REGISTER_STAT(WEAPON_NEXTTHINK, float) #ifdef SVQC SPECTATE_COPYFIELD(_STAT(WEAPON_NEXTTHINK)) float W_WeaponRateFactor(entity this); +float gameover; #endif REGISTER_STAT(WEAPONRATEFACTOR, float, W_WeaponRateFactor(this)) - +REGISTER_STAT(GAMEOVER, int, gameover) REGISTER_STAT(GAMESTARTTIME, float) REGISTER_STAT(STRENGTH_FINISHED, float) REGISTER_STAT(INVINCIBLE_FINISHED, float) @@ -122,8 +122,10 @@ REGISTER_STAT(REVIVE_PROGRESS, float) REGISTER_STAT(ROUNDLOST, int) REGISTER_STAT(BUFF_TIME, float) REGISTER_STAT(CTF_FLAGSTATUS, int) +REGISTER_STAT(CAPTURE_PROGRESS, float) REGISTER_STAT(ENTRAP_ORB, float) REGISTER_STAT(ENTRAP_ORB_ALPHA, float) +REGISTER_STAT(ITEMSTIME, int, autocvar_sv_itemstime) #ifdef SVQC int autocvar_g_multijump; @@ -174,12 +176,21 @@ REGISTER_STAT(BUGRIGS_SPEED_POW, float, g_bugrigs_speed_pow) REGISTER_STAT(BUGRIGS_SPEED_REF, float, g_bugrigs_speed_ref) REGISTER_STAT(BUGRIGS_STEER, float, g_bugrigs_steer) -REGISTER_STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, int, cvar("sv_gameplayfix_downtracesupportsongroundflag")) -REGISTER_STAT(GAMEPLAYFIX_EASIERWATERJUMP, int, cvar("sv_gameplayfix_easierwaterjump")) -REGISTER_STAT(GAMEPLAYFIX_STEPDOWN, int, cvar("sv_gameplayfix_stepdown")) -REGISTER_STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, int, cvar("sv_gameplayfix_stepmultipletimes")) -REGISTER_STAT(GAMEPLAYFIX_UNSTICKPLAYERS, int, cvar("sv_gameplayfix_unstickplayers")) +#ifdef SVQC +int autocvar_sv_gameplayfix_downtracesupportsongroundflag; +int autocvar_sv_gameplayfix_easierwaterjump; +int autocvar_sv_gameplayfix_stepdown; +int autocvar_sv_gameplayfix_stepmultipletimes; +int autocvar_sv_gameplayfix_unstickplayers; +int autocvar_sv_gameplayfix_fixedcheckwatertransition; +#endif +REGISTER_STAT(GAMEPLAYFIX_DOWNTRACEONGROUND, int, autocvar_sv_gameplayfix_downtracesupportsongroundflag) +REGISTER_STAT(GAMEPLAYFIX_EASIERWATERJUMP, int, autocvar_sv_gameplayfix_easierwaterjump) +REGISTER_STAT(GAMEPLAYFIX_STEPDOWN, int, autocvar_sv_gameplayfix_stepdown) +REGISTER_STAT(GAMEPLAYFIX_STEPMULTIPLETIMES, int, autocvar_sv_gameplayfix_stepmultipletimes) +REGISTER_STAT(GAMEPLAYFIX_UNSTICKPLAYERS, int, autocvar_sv_gameplayfix_unstickplayers) REGISTER_STAT(GAMEPLAYFIX_UPVELOCITYCLEARSONGROUND, int, autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag) +REGISTER_STAT(GAMEPLAYFIX_WATERTRANSITION, int, autocvar_sv_gameplayfix_fixedcheckwatertransition) REGISTER_STAT(MOVEVARS_JUMPSTEP, int, cvar("sv_jumpstep")) REGISTER_STAT(NOSTEP, int, cvar("sv_nostep")) @@ -205,6 +216,7 @@ float autocvar_sv_dodging_horiz_speed_frozen; float autocvar_sv_dodging_ramp_time; float autocvar_sv_dodging_up_speed; bool autocvar_sv_dodging_wall_dodging; +bool autocvar_sv_dodging_air_dodging; #endif REGISTER_STAT(DODGING, int, g_dodging) @@ -219,7 +231,8 @@ REGISTER_STAT(DODGING_RAMP_TIME, float, autocvar_sv_dodging_ramp_time) /** cvar loopback */ REGISTER_STAT(DODGING_TIMEOUT, float) REGISTER_STAT(DODGING_UP_SPEED, float, autocvar_sv_dodging_up_speed) -REGISTER_STAT(DODGING_WALL, int, autocvar_sv_dodging_wall_dodging) +REGISTER_STAT(DODGING_WALL, bool, autocvar_sv_dodging_wall_dodging) +REGISTER_STAT(DODGING_AIR, bool, autocvar_sv_dodging_air_dodging) REGISTER_STAT(JETPACK_ACCEL_SIDE, float, autocvar_g_jetpack_acceleration_side) REGISTER_STAT(JETPACK_ACCEL_UP, float, autocvar_g_jetpack_acceleration_up) @@ -227,9 +240,24 @@ REGISTER_STAT(JETPACK_ANTIGRAVITY, float, autocvar_g_jetpack_antigravity) REGISTER_STAT(JETPACK_FUEL, float, autocvar_g_jetpack_fuel) REGISTER_STAT(JETPACK_MAXSPEED_SIDE, float, autocvar_g_jetpack_maxspeed_side) REGISTER_STAT(JETPACK_MAXSPEED_UP, float, autocvar_g_jetpack_maxspeed_up) +REGISTER_STAT(JETPACK_REVERSE_THRUST, float, autocvar_g_jetpack_reverse_thrust) REGISTER_STAT(MOVEVARS_HIGHSPEED, float, autocvar_g_movement_highspeed) +#ifdef SVQC +AUTOCVAR(g_walljump, bool, false, "Enable wall jumping mutator"); +AUTOCVAR(g_walljump_delay, float, 1, "Minimum delay between wall jumps"); +AUTOCVAR(g_walljump_force, float, 300, "How far to bounce/jump off the wall"); +AUTOCVAR(g_walljump_velocity_xy_factor, float, 1.15, "How much to slow down along horizontal axis, higher value = higher deceleration, if factor is < 1, you accelerate by wall jumping"); +AUTOCVAR(g_walljump_velocity_z_factor, float, 0.5, "Upwards velocity factor, multiplied by normal jump velocity"); +#endif +REGISTER_STAT(WALLJUMP, int, autocvar_g_walljump) +REGISTER_STAT(WALLJUMP_VELOCITY_Z_FACTOR, float, autocvar_g_walljump_velocity_z_factor) +REGISTER_STAT(WALLJUMP_VELOCITY_XY_FACTOR, float, autocvar_g_walljump_velocity_xy_factor) +REGISTER_STAT(WALLJUMP_DELAY, float, autocvar_g_walljump_delay) +REGISTER_STAT(WALLJUMP_FORCE, float, autocvar_g_walljump_force) +REGISTER_STAT(LASTWJ, float) + // freeze tag, clan arena REGISTER_STAT(REDALIVE, int) REGISTER_STAT(BLUEALIVE, int) @@ -246,8 +274,15 @@ REGISTER_STAT(DOM_PPS_PINK, float) REGISTER_STAT(TELEPORT_MAXSPEED, float, autocvar_g_teleport_maxspeed) REGISTER_STAT(TELEPORT_TELEFRAG_AVOID, int, autocvar_g_telefrags_avoid) +REGISTER_STAT(CAMERA_SPECTATOR, int) + REGISTER_STAT(SPECTATORSPEED, float) +#ifdef SVQC +bool autocvar_sv_slick_applygravity; +#endif +REGISTER_STAT(SLICK_APPLYGRAVITY, bool, autocvar_sv_slick_applygravity) + #ifdef SVQC #include "physics/movetypes/movetypes.qh" #endif @@ -257,6 +292,7 @@ REGISTER_STAT(MOVEVARS_AIRCONTROL_PENALTY, float) REGISTER_STAT(MOVEVARS_AIRSPEEDLIMIT_NONQW, float) REGISTER_STAT(MOVEVARS_AIRSTRAFEACCEL_QW, float) REGISTER_STAT(MOVEVARS_AIRCONTROL_POWER, float) +REGISTER_STAT(MOVEVARS_AIRCONTROL_BACKWARDS, bool) noref bool autocvar_sv_gameplayfix_nogravityonground; REGISTER_STAT(MOVEFLAGS, int, MOVEFLAG_VALID | (autocvar_sv_gameplayfix_q2airaccelerate ? MOVEFLAG_Q2AIRACCELERATE : 0) @@ -308,6 +344,3 @@ REGISTER_STAT(GUNALIGN, int, this.cvar_cl_gunalign) #ifdef SVQC SPECTATE_COPYFIELD(_STAT(GUNALIGN)) #endif - - -#endif diff --git a/qcsrc/common/t_items.qc b/qcsrc/common/t_items.qc index a0255d7d61..9d89679e3b 100644 --- a/qcsrc/common/t_items.qc +++ b/qcsrc/common/t_items.qc @@ -1,13 +1,12 @@ #include "t_items.qh" -#include "items/all.qc" +#include "items/_mod.qh" #if defined(SVQC) - #include "../server/bot/bot.qh" - #include "../server/bot/waypoints.qh" + #include "../server/bot/api.qh" - #include + #include #include "../server/weapons/common.qh" #include "../server/weapons/selection.qh" @@ -19,14 +18,14 @@ #include "triggers/subs.qh" #include "util.qh" - #include + #include - #include + #include #include "../lib/warpzone/util_server.qh" #elif defined(CSQC) #include "physics/movetypes/movetypes.qh" - #include + #include #include "../lib/csqcmodel/cl_model.qh" #include "../lib/csqcmodel/common.qh" #endif @@ -35,6 +34,7 @@ REGISTER_NET_LINKED(ENT_CLIENT_ITEM) #ifdef CSQC bool autocvar_cl_ghost_items_vehicle = true; +.vector item_glowmod; void Item_SetAlpha(entity this) { bool veh_hud = (hud && autocvar_cl_ghost_items_vehicle); @@ -42,7 +42,8 @@ void Item_SetAlpha(entity this) if(!veh_hud && (this.ItemStatus & ITS_AVAILABLE)) { this.alpha = 1; - this.colormod = this.glowmod = '1 1 1'; + this.colormod = '1 1 1'; + this.glowmod = this.item_glowmod; } else { @@ -68,33 +69,40 @@ void ItemDraw(entity this) if(this.gravity) { Movetype_Physics_MatchServer(this, false); - if(this.move_flags & FL_ONGROUND) - { // For some reason move_avelocity gets set to '0 0 0' here ... + if(IS_ONGROUND(this)) + { // For some reason avelocity gets set to '0 0 0' here ... this.oldorigin = this.origin; this.gravity = 0; if(autocvar_cl_animate_items) { // ... so reset it if animations are requested. if(this.ItemStatus & ITS_ANIMATE1) - this.move_avelocity = '0 180 0'; + this.avelocity = '0 180 0'; if(this.ItemStatus & ITS_ANIMATE2) - this.move_avelocity = '0 -90 0'; + this.avelocity = '0 -90 0'; } + + // delay is for blocking item's position for a while; + // it's a workaround for dropped weapons that receive the position + // another time right after they spawn overriding animation position + this.onground_time = time + 0.5; } } else if (autocvar_cl_animate_items) { if(this.ItemStatus & ITS_ANIMATE1) { - this.angles += this.move_avelocity * frametime; - setorigin(this, '0 0 10' + this.oldorigin + '0 0 8' * sin(time * 2)); + this.angles += this.avelocity * frametime; + float fade_in = bound(0, time - this.onground_time, 1); + setorigin(this, this.oldorigin + fade_in * ('0 0 10' + '0 0 8' * sin((time - this.onground_time) * 2))); } if(this.ItemStatus & ITS_ANIMATE2) { - this.angles += this.move_avelocity * frametime; - setorigin(this, '0 0 8' + this.oldorigin + '0 0 4' * sin(time * 3)); + this.angles += this.avelocity * frametime; + float fade_in = bound(0, time - this.onground_time, 1); + setorigin(this, this.oldorigin + fade_in * ('0 0 8' + '0 0 4' * sin((time - this.onground_time) * 3))); } } @@ -107,7 +115,7 @@ void ItemDrawSimple(entity this) { Movetype_Physics_MatchServer(this, false); - if(this.move_flags & FL_ONGROUND) + if(IS_ONGROUND(this)) this.gravity = 0; } @@ -164,7 +172,6 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) this.angles_x = ReadAngle(); this.angles_y = ReadAngle(); this.angles_z = ReadAngle(); - this.move_angles = this.angles; } if(sf & ISF_SIZE) @@ -183,7 +190,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(this.ItemStatus & ITS_ALLOWFB) this.effects |= EF_FULLBRIGHT; - if(this.ItemStatus & ITS_POWERUP) + if(this.ItemStatus & ITS_GLOW) { if(this.ItemStatus & ITS_AVAILABLE) this.effects |= (EF_ADDITIVE | EF_FULLBRIGHT); @@ -195,10 +202,11 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(sf & ISF_MODEL) { this.drawmask = MASK_NORMAL; - this.move_movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); + if (isnew) IL_PUSH(g_drawables, this); this.draw = ItemDraw; this.solid = SOLID_TRIGGER; - //this.move_flags |= FL_ITEM; + //this.flags |= FL_ITEM; bool use_bigsize = ReadByte(); @@ -229,7 +237,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) else { this.draw = ItemDraw; - LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it\n"); + LOG_TRACE("Simple item requested for ", _fn, " but no model exists for it"); } } @@ -238,7 +246,7 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(this.mdl == "") - LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!\n"); + LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, ", tell tZork about this!"); precache_model(this.mdl); _setmodel(this, this.mdl); @@ -247,19 +255,23 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) } if(sf & ISF_COLORMAP) + { this.colormap = ReadShort(); + this.item_glowmod_x = ReadByte() / 255.0; + this.item_glowmod_y = ReadByte() / 255.0; + this.item_glowmod_z = ReadByte() / 255.0; + } if(sf & ISF_DROP) { this.gravity = 1; this.pushable = true; - //this.move_angles = '0 0 0'; - this.move_movetype = MOVETYPE_TOSS; - this.move_velocity_x = ReadCoord(); - this.move_velocity_y = ReadCoord(); - this.move_velocity_z = ReadCoord(); - this.velocity = this.move_velocity; - this.move_origin = this.oldorigin; + //this.angles = '0 0 0'; + set_movetype(this, MOVETYPE_TOSS); + this.velocity_x = ReadCoord(); + this.velocity_y = ReadCoord(); + this.velocity_z = ReadCoord(); + setorigin(this, this.oldorigin); if(!this.move_time) { @@ -273,10 +285,10 @@ NET_HANDLE(ENT_CLIENT_ITEM, bool isnew) if(autocvar_cl_animate_items) { if(this.ItemStatus & ITS_ANIMATE1) - this.move_avelocity = '0 180 0'; + this.avelocity = '0 180 0'; if(this.ItemStatus & ITS_ANIMATE2) - this.move_avelocity = '0 -90 0'; + this.avelocity = '0 -90 0'; } this.entremove = ItemRemove; @@ -329,14 +341,19 @@ bool ItemSend(entity this, entity to, int sf) WriteShort(MSG_ENTITY, this.fade_start); if(this.mdl == "") - LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "exspect a crash just aboute now\n"); + LOG_TRACE("^1WARNING!^7 this.mdl is unset for item ", this.classname, "expect a crash just about now"); WriteString(MSG_ENTITY, this.mdl); } if(sf & ISF_COLORMAP) + { WriteShort(MSG_ENTITY, this.colormap); + WriteByte(MSG_ENTITY, this.glowmod.x * 255.0); + WriteByte(MSG_ENTITY, this.glowmod.y * 255.0); + WriteByte(MSG_ENTITY, this.glowmod.z * 255.0); + } if(sf & ISF_DROP) { @@ -376,7 +393,7 @@ bool have_pickup_item(entity this) if(autocvar_g_pickup_items == 0) return false; if(g_weaponarena) - if(this.weapons || (this.items & IT_AMMO)) // no item or ammo pickups in weaponarena + if(this.weapons || this.itemdef.instanceOfAmmo) // no item or ammo pickups in weaponarena return false; } return true; @@ -413,6 +430,7 @@ void Item_Show (entity e, float mode) { e.effects &= ~(EF_ADDITIVE | EF_STARDUST | EF_FULLBRIGHT | EF_NODEPTHTEST); e.ItemStatus &= ~ITS_STAYWEP; + entity def = e.itemdef; if (mode > 0) { // make the item look normal, and be touchable @@ -430,7 +448,6 @@ void Item_Show (entity e, float mode) e.ItemStatus &= ~ITS_AVAILABLE; } else { - entity def = e.itemdef; bool nostay = def.instanceOfWeaponPickup ? !!(def.m_weapon.weapons & WEPSET_SUPERWEAPONS) : false // no weapon-stay on superweapons || e.team // weapon stay isn't supported for teamed weapons ; @@ -448,13 +465,13 @@ void Item_Show (entity e, float mode) //setmodel(e, "null"); e.solid = SOLID_NOT; e.colormod = '0 0 0'; - e.glowmod = e.colormod; + //e.glowmod = e.colormod; e.spawnshieldtime = 1; e.ItemStatus &= ~ITS_AVAILABLE; }} - if (e.items & ITEM_Strength.m_itemid || e.items & ITEM_Shield.m_itemid) - e.ItemStatus |= ITS_POWERUP; + if (def.m_glow) + e.ItemStatus |= ITS_GLOW; if (autocvar_g_nodepthtestitems) e.effects |= EF_NODEPTHTEST; @@ -487,13 +504,7 @@ void Item_ItemsTime_SetTimesForAllPlayers(); void Item_Respawn (entity this) { Item_Show(this, 1); - // this is ugly... - if(this.items == ITEM_Strength.m_itemid) - sound (this, CH_TRIGGER, SND_STRENGTH_RESPAWN, VOL_BASE, ATTEN_NORM); // play respawn sound - else if(this.items == ITEM_Shield.m_itemid) - sound (this, CH_TRIGGER, SND_SHIELD_RESPAWN, VOL_BASE, ATTEN_NORM); // play respawn sound - else - sound (this, CH_TRIGGER, SND_ITEMRESPAWN, VOL_BASE, ATTEN_NORM); // play respawn sound + 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) @@ -761,7 +772,7 @@ LABEL(skip) return 1; } -void Item_Touch(entity this) +void Item_Touch(entity this, entity toucher) { // remove the item if it's currnetly in a NODROP brush or hits a NOIMPACT surface (such as sky) @@ -769,26 +780,26 @@ void Item_Touch(entity this) { if (ITEM_TOUCH_NEEDKILL()) { - remove(this); + delete(this); return; } } - if(!(other.flags & FL_PICKUPITEMS) - || STAT(FROZEN, other) - || IS_DEAD(other) + if(!(toucher.flags & FL_PICKUPITEMS) + || STAT(FROZEN, toucher) + || IS_DEAD(toucher) || (this.solid != SOLID_TRIGGER) - || (this.owner == other) + || (this.owner == toucher) || (time < this.item_spawnshieldtime) ) { return; } - switch (MUTATOR_CALLHOOK(ItemTouch, this, other)) + switch (MUTATOR_CALLHOOK(ItemTouch, this, toucher)) { case MUT_ITEMTOUCH_RETURN: { return; } - case MUT_ITEMTOUCH_PICKUP: { other = M_ARGV(1, entity); goto pickup; } + case MUT_ITEMTOUCH_PICKUP: { toucher = M_ARGV(1, entity); goto pickup; } } - other = M_ARGV(1, entity); + toucher = M_ARGV(1, entity); if (this.classname == "droppedweapon") { @@ -797,7 +808,7 @@ void Item_Touch(entity this) this.superweapons_finished = max(0, this.superweapons_finished - time); } entity it = this.itemdef; - bool gave = ITEM_HANDLE(Pickup, it, this, other); + bool gave = ITEM_HANDLE(Pickup, it, this, toucher); if (!gave) { if (this.classname == "droppedweapon") @@ -812,28 +823,27 @@ void Item_Touch(entity this) LABEL(pickup) - other.last_pickup = time; + toucher.last_pickup = time; Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - _sound (other, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM); + _sound (toucher, (this.itemdef.instanceOfPowerup ? CH_TRIGGER_SINGLE : CH_TRIGGER), (this.item_pickupsound ? this.item_pickupsound : Sound_fixpath(this.item_pickupsound_ent)), VOL_BASE, ATTEN_NORM); if (this.classname == "droppedweapon") - remove (this); + delete (this); else if (this.spawnshieldtime) { entity e; if(this.team) { RandomSelection_Init(); - for(entity head = NULL; (head = findfloat(head, team, this.team)); ) + IL_EACH(g_items, it.team == this.team, { - if(head.flags & FL_ITEM) - if(head.classname != "item_flag_team" && head.classname != "item_key_team") + if(it.itemdef) // is a registered item { - Item_Show(head, -1); - RandomSelection_Add(head, 0, string_null, head.cnt, 0); + Item_Show(it, -1); + RandomSelection_AddEnt(it, it.cnt, 0); } - } + }); e = RandomSelection_chosen_ent; } @@ -868,23 +878,21 @@ void Item_FindTeam(entity this) if(this.effects & EF_NODRAW) { // marker for item team search - LOG_TRACE("Initializing item team ", ftos(this.team), "\n"); + LOG_TRACE("Initializing item team ", ftos(this.team)); RandomSelection_Init(); - FOREACH_ENTITY_FLOAT(team, this.team, + IL_EACH(g_items, it.team == this.team, { - if(it.flags & FL_ITEM) - if(it.classname != "item_flag_team" && it.classname != "item_key_team") - RandomSelection_Add(it, 0, string_null, it.cnt, 0); + if(it.itemdef) // is a registered item + RandomSelection_AddEnt(it, it.cnt, 0); }); e = RandomSelection_chosen_ent; e.state = 0; Item_Show(e, 1); - FOREACH_ENTITY_FLOAT(team, this.team, + IL_EACH(g_items, it.team == this.team, { - if(it.flags & FL_ITEM) - if(it.classname != "item_flag_team" && it.classname != "item_key_team") + if(it.itemdef) // is a registered item { if(it != e) { @@ -905,7 +913,7 @@ void RemoveItem(entity this) { if(wasfreed(this) || !this) { return; } Send_Effect(EFFECT_ITEM_PICKUP, CENTER_OR_VIEWOFS(this), '0 0 0', 1); - remove(this); + delete(this); } // pickup evaluation functions @@ -916,17 +924,20 @@ float generic_pickupevalfunc(entity player, entity item) {return item.bot_pickup float weapon_pickupevalfunc(entity player, entity item) { float c; + int rating = item.bot_pickupbasevalue; // See if I have it already - if(item.weapons & ~player.weapons) + if(player.weapons & item.weapons) { // If I can pick it up if(!item.spawnshieldtime) c = 0; else if(player.ammo_cells || player.ammo_shells || player.ammo_plasma || player.ammo_nails || player.ammo_rockets) { + if (rating > 0) + rating = BOT_PICKUP_RATING_LOW * 0.5 * (1 + rating / BOT_PICKUP_RATING_HIGH); // Skilled bots will grab more - c = bound(0, skill / 10, 1) * 0.5; + c = 1 + bound(0, skill / 10, 1) * 0.5; } else c = 0; @@ -934,58 +945,74 @@ float weapon_pickupevalfunc(entity player, entity item) else c = 1; + if (c <= 0) + return 0; + // If custom weapon priorities for bots is enabled rate most wanted weapons higher - if( bot_custom_weapon && c ) - { - // Find the highest position on any range - int position = -1; - for (int j = 0; j < WEP_LAST ; ++j){ - if( - bot_weapons_far[j] == item.weapon || - bot_weapons_mid[j] == item.weapon || - bot_weapons_close[j] == item.weapon - ) + if(bot_custom_weapon) + { + int best_ratio = 0; + int missing = 0; + + // evaluate weapon usefulness in all ranges + for(int list = 0; list < 3; list++) + { + int position = -1; + int wep_count = 0; + int wpn = item.weapon; + for (int j = 0; j < WEP_LAST; ++j) { - position = j; - break; + int list_wpn = 0; + if (list == 0) list_wpn = bot_weapons_far[j]; + else if (list == 1) list_wpn = bot_weapons_mid[j]; + else list_wpn = bot_weapons_close[j]; + + if (weaponsInMap & Weapons_from(list_wpn).m_wepset) // only if available + { + if (list_wpn > 0) + wep_count++; + if (position == -1 && list_wpn == wpn) + position = wep_count; + } + } + if (position == -1) + { + missing++; + position = wep_count; // if missing assume last + } + if (wep_count) + { + if (!best_ratio || position / wep_count < best_ratio) + best_ratio = position / wep_count; } } - // Rate it - if (position >= 0 ) - { - position = WEP_LAST - position; - // item.bot_pickupbasevalue is overwritten here - return (BOT_PICKUP_RATING_LOW + ( (BOT_PICKUP_RATING_HIGH - BOT_PICKUP_RATING_LOW) * (position / WEP_LAST ))) * c; - } + if (missing < 3 && best_ratio) + c = c - best_ratio * 0.3; } - return item.bot_pickupbasevalue * c; + return rating * c; } float commodity_pickupevalfunc(entity player, entity item) { - float c; - float need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false; - c = 0; + bool need_shells = false, need_nails = false, need_rockets = false, need_cells = false, need_plasma = false, need_fuel = false; + float c = 0; // Detect needed ammo FOREACH(Weapons, it != WEP_Null, { if(!(player.weapons & (it.m_wepset))) continue; - if(it.items & ITEM_Shells.m_itemid) - need_shells = true; - else if(it.items & ITEM_Bullets.m_itemid) - need_nails = true; - else if(it.items & ITEM_Rockets.m_itemid) - need_rockets = true; - else if(it.items & ITEM_Cells.m_itemid) - need_cells = true; - else if(it.items & ITEM_Plasma.m_itemid) - need_plasma = true; - else if(it.items & ITEM_JetpackFuel.m_itemid) - need_fuel = true; + switch(it.ammo_field) + { + case ammo_shells: need_shells = true; break; + case ammo_nails: need_nails = true; break; + case ammo_rockets: need_rockets = true; break; + case ammo_cells: need_cells = true; break; + case ammo_plasma: need_plasma = true; break; + case ammo_fuel: need_fuel = true; break; + } }); // TODO: figure out if the player even has the weapon this ammo is for? @@ -1066,11 +1093,12 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default this.weapons = WepSet_FromWeapon(Weapons_from(weaponid)); this.flags = FL_ITEM | itemflags; + IL_PUSH(g_items, this); if(MUTATOR_CALLHOOK(FilterItem, this)) // error means we do not want the item { startitem_failed = true; - remove(this); + delete(this); return; } @@ -1079,7 +1107,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default { this.reset = SUB_Remove; // it's a dropped weapon - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); // Savage: remove thrown items after a certain period of time ("garbage collection") setthink(this, RemoveItem); @@ -1099,7 +1127,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if (trace_dpstartcontents & DPCONTENTS_NODROP) { startitem_failed = true; - remove(this); + delete(this); return; } } @@ -1108,7 +1136,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(!have_pickup_item(this)) { startitem_failed = true; - remove (this); + delete (this); return; } @@ -1120,9 +1148,9 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if(this.spawnflags & 1) this.noalign = 1; if (this.noalign > 0) - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); else - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); // do item filtering according to game mode and other things if (this.noalign <= 0) { @@ -1156,7 +1184,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default // why not flags & fl_item? FOREACH_ENTITY_RADIUS(this.origin, 3, it.is_item, { LOG_TRACE("XXX Found duplicated item: ", itemname, vtos(this.origin)); - LOG_TRACE(" vs ", it.netname, vtos(it.origin), "\n"); + LOG_TRACE(" vs ", it.netname, vtos(it.origin)); error("Mapper sucks."); }); this.is_item = true; @@ -1206,7 +1234,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default this.gravity = 1; if (!(this.spawnflags & 1024)) this.ItemStatus |= ITS_ANIMATE1; - this.ItemStatus |= ISF_COLORMAP; + this.SendFlags |= ISF_COLORMAP; } this.state = 0; @@ -1227,7 +1255,7 @@ void _StartItem(entity this, entity def, float defaultrespawntime, float default if (MUTATOR_CALLHOOK(Item_Spawn, this)) { startitem_failed = true; - remove(this); + delete(this); return; } } @@ -1337,17 +1365,17 @@ spawnfunc(item_armor_big) this.max_armorvalue = g_pickup_armorbig_max; if(!this.pickup_anyway) this.pickup_anyway = g_pickup_armorbig_anyway; - StartItem(this, ITEM_ArmorLarge); + StartItem(this, ITEM_ArmorBig); } -spawnfunc(item_armor_large) +spawnfunc(item_armor_mega) { if(!this.armorvalue) - this.armorvalue = g_pickup_armorlarge; + this.armorvalue = g_pickup_armormega; if(!this.max_armorvalue) - this.max_armorvalue = g_pickup_armorlarge_max; + this.max_armorvalue = g_pickup_armormega_max; if(!this.pickup_anyway) - this.pickup_anyway = g_pickup_armorlarge_anyway; + this.pickup_anyway = g_pickup_armormega_anyway; StartItem(this, ITEM_ArmorMega); } @@ -1373,15 +1401,15 @@ spawnfunc(item_health_medium) StartItem(this, ITEM_HealthMedium); } -spawnfunc(item_health_large) +spawnfunc(item_health_big) { if(!this.max_health) - this.max_health = g_pickup_healthlarge_max; + this.max_health = g_pickup_healthbig_max; if(!this.health) - this.health = g_pickup_healthlarge; + this.health = g_pickup_healthbig; if(!this.pickup_anyway) - this.pickup_anyway = g_pickup_healthlarge_anyway; - StartItem(this, ITEM_HealthLarge); + this.pickup_anyway = g_pickup_healthbig_anyway; + StartItem(this, ITEM_HealthBig); } spawnfunc(item_health_mega) @@ -1397,9 +1425,11 @@ spawnfunc(item_health_mega) // support old misnamed entities spawnfunc(item_armor1) { spawnfunc_item_armor_small(this); } // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard -spawnfunc(item_armor25) { spawnfunc_item_armor_large(this); } +spawnfunc(item_armor25) { spawnfunc_item_armor_mega(this); } +spawnfunc(item_armor_large) { spawnfunc_item_armor_mega(this); } spawnfunc(item_health1) { spawnfunc_item_health_small(this); } spawnfunc(item_health25) { spawnfunc_item_health_medium(this); } +spawnfunc(item_health_large) { spawnfunc_item_health_big(this); } spawnfunc(item_health100) { spawnfunc_item_health_mega(this); } spawnfunc(item_strength) @@ -1421,12 +1451,10 @@ spawnfunc(item_quad) { this.classname = "item_strength";spawnfunc_item_strength( void target_items_use(entity this, entity actor, entity trigger) { - other = trigger; // TODO - if(actor.classname == "droppedweapon") { - EXACTTRIGGER_TOUCH; - remove(actor); + EXACTTRIGGER_TOUCH(this, trigger); + delete(actor); return; } @@ -1436,13 +1464,12 @@ void target_items_use(entity this, entity actor, entity trigger) return; if(trigger.solid == SOLID_TRIGGER) { - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, trigger); } - FOREACH_ENTITY_ENT(enemy, actor, + IL_EACH(g_items, it.enemy == actor && it.classname == "droppedweapon", { - if(it.classname == "droppedweapon") - remove(it); + delete(it); }); if(GiveItems(actor, 0, tokenize_console(this.netname))) @@ -1451,7 +1478,7 @@ void target_items_use(entity this, entity actor, entity trigger) spawnfunc(target_items) { - float n, i; + int n, j; string s; this.use = target_items_use; @@ -1469,20 +1496,20 @@ spawnfunc(target_items) } else { - for(i = 0; i < n; ++i) + for(j = 0; j < n; ++j) { - if (argv(i) == "unlimited_ammo") this.items |= IT_UNLIMITED_AMMO; - else if(argv(i) == "unlimited_weapon_ammo") this.items |= IT_UNLIMITED_WEAPON_AMMO; - else if(argv(i) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS; - else if(argv(i) == "strength") this.items |= ITEM_Strength.m_itemid; - else if(argv(i) == "invincible") this.items |= ITEM_Shield.m_itemid; - else if(argv(i) == "superweapons") this.items |= IT_SUPERWEAPON; - else if(argv(i) == "jetpack") this.items |= ITEM_Jetpack.m_itemid; - else if(argv(i) == "fuel_regen") this.items |= ITEM_JetpackRegen.m_itemid; + if (argv(j) == "unlimited_ammo") this.items |= IT_UNLIMITED_AMMO; + else if(argv(j) == "unlimited_weapon_ammo") this.items |= IT_UNLIMITED_WEAPON_AMMO; + else if(argv(j) == "unlimited_superweapons") this.items |= IT_UNLIMITED_SUPERWEAPONS; + else if(argv(j) == "strength") this.items |= ITEM_Strength.m_itemid; + else if(argv(j) == "invincible") this.items |= ITEM_Shield.m_itemid; + else if(argv(j) == "superweapons") this.items |= IT_SUPERWEAPON; + else if(argv(j) == "jetpack") this.items |= ITEM_Jetpack.m_itemid; + else if(argv(j) == "fuel_regen") this.items |= ITEM_JetpackRegen.m_itemid; else { FOREACH(Weapons, it != WEP_Null, { - s = W_UndeprecateName(argv(i)); + s = W_UndeprecateName(argv(j)); if(s == it.netname) { this.weapons |= (it.m_wepset); @@ -1543,9 +1570,9 @@ spawnfunc(target_items) //print(this.netname, "\n"); n = tokenize_console(this.netname); - for(i = 0; i < n; ++i) + for(j = 0; j < n; ++j) { - FOREACH(Weapons, it != WEP_Null && argv(i) == it.netname, { + FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(argv(j)) == it.netname, { it.wr_init(it); break; }); @@ -1768,7 +1795,7 @@ float GiveItems(entity e, float beginarg, float endarg) got += GiveValue(e, ammo_fuel, op, val); break; default: - FOREACH(Weapons, it != WEP_Null && cmd == it.netname, { + FOREACH(Weapons, it != WEP_Null && W_UndeprecateName(cmd) == it.netname, { got += GiveWeapon(e, it.m_id, op, val); break; }); @@ -1790,6 +1817,7 @@ float GiveItems(entity e, float beginarg, float endarg) }); 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); diff --git a/qcsrc/common/t_items.qh b/qcsrc/common/t_items.qh index 5ebc9d7e62..418ce39176 100644 --- a/qcsrc/common/t_items.qh +++ b/qcsrc/common/t_items.qh @@ -1,5 +1,4 @@ -#ifndef T_ITEMS_H -#define T_ITEMS_H +#pragma once const int AMMO_COUNT = 4; // amount of ammo types to show in the inventory panel @@ -13,7 +12,7 @@ const int ISF_STATUS = BIT(3); const int ITS_AVAILABLE = BIT(3); const int ITS_ALLOWFB = BIT(4); const int ITS_ALLOWSI = BIT(5); - const int ITS_POWERUP = BIT(6); + const int ITS_GLOW = BIT(6); const int ISF_COLORMAP = BIT(4); const int ISF_DROP = BIT(5); const int ISF_ANGLES = BIT(6); @@ -21,6 +20,7 @@ const int ISF_SIZE = BIT(7); .int ItemStatus; +.float onground_time; .float fade_start; .float fade_end; @@ -30,6 +30,7 @@ void StartItem(entity this, entity a); #ifdef CSQC +bool autocvar_cl_items_nofade; float autocvar_cl_animate_items = 1; float autocvar_cl_ghost_items = 0.45; vector autocvar_cl_ghost_items_color = '-1 -1 -1'; @@ -88,7 +89,7 @@ float Item_GiveAmmoTo(entity item, entity player, .float ammotype, float ammomax float Item_GiveTo(entity item, entity player); -void Item_Touch(entity this); +void Item_Touch(entity this, entity toucher); void Item_Reset(entity this); @@ -133,4 +134,3 @@ void GiveRot(entity e, float v0, float v1, .float rotfield, float rottime, .floa float GiveItems(entity e, float beginarg, float endarg); #endif -#endif diff --git a/qcsrc/common/teams.qh b/qcsrc/common/teams.qh index aeaf078dce..1f8a603dd0 100644 --- a/qcsrc/common/teams.qh +++ b/qcsrc/common/teams.qh @@ -1,5 +1,4 @@ -#ifndef TEAMS_H -#define TEAMS_H +#pragma once #ifdef TEAMNUMBERS_THAT_ARENT_STUPID const int NUM_TEAM_1 = 1; // red @@ -177,5 +176,3 @@ float Team_TeamToNumber(float teamid) // safe team comparisons #define SAME_TEAM(a,b) (teamplay ? (a.team == b.team) : (a == b)) #define DIFF_TEAM(a,b) (teamplay ? (a.team != b.team) : (a != b)) - -#endif diff --git a/qcsrc/common/triggers/_mod.inc b/qcsrc/common/triggers/_mod.inc index c049b10828..9b327de55d 100644 --- a/qcsrc/common/triggers/_mod.inc +++ b/qcsrc/common/triggers/_mod.inc @@ -4,3 +4,8 @@ #include #include #include + +#include +#include +#include +#include diff --git a/qcsrc/common/triggers/_mod.qh b/qcsrc/common/triggers/_mod.qh index 2fba604617..d3bb2dc3ad 100644 --- a/qcsrc/common/triggers/_mod.qh +++ b/qcsrc/common/triggers/_mod.qh @@ -4,3 +4,8 @@ #include #include #include + +#include +#include +#include +#include diff --git a/qcsrc/common/triggers/func/bobbing.qc b/qcsrc/common/triggers/func/bobbing.qc index 094673b5bb..b7034939ae 100644 --- a/qcsrc/common/triggers/func/bobbing.qc +++ b/qcsrc/common/triggers/func/bobbing.qc @@ -1,3 +1,4 @@ +#include "bobbing.qh" #ifdef SVQC .float height; void func_bobbing_controller_think(entity this) diff --git a/qcsrc/common/triggers/func/bobbing.qh b/qcsrc/common/triggers/func/bobbing.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/bobbing.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/breakable.qc b/qcsrc/common/triggers/func/breakable.qc index 1a65710a98..dc2e6f7dc0 100644 --- a/qcsrc/common/triggers/func/breakable.qc +++ b/qcsrc/common/triggers/func/breakable.qc @@ -1,8 +1,9 @@ +#include "breakable.qh" #ifdef SVQC #include #include -#include +#include #include #include #include @@ -59,7 +60,7 @@ void LaunchDebris (entity this, string debrisname, vector force) dbr.skin = this.debrisskin; dbr.colormap = this.colormap; // inherit team colors dbr.owner = this; // do not be affected by our own explosion - dbr.movetype = this.debrismovetype; + 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 @@ -88,8 +89,6 @@ void func_breakable_colormod(entity this) this.colormod = '1 0 0' + '0 1 0' * (2 * h - 0.5); else this.colormod = '1 1 1'; - - CSQCMODEL_AUTOUPDATE(this); } void func_breakable_look_destroyed(entity this) @@ -108,24 +107,22 @@ void func_breakable_look_destroyed(entity this) this.origin_z = floorZ; } _setmodel(this, this.mdl_dead); + ApplyMinMaxScaleAngles(this); this.effects &= ~EF_NODRAW; } - CSQCMODEL_AUTOUPDATE(this); - 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); - CSQCMODEL_AUTOUPDATE(this); - this.solid = SOLID_BSP; } @@ -133,6 +130,8 @@ 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; @@ -143,6 +142,13 @@ void func_breakable_behave_destroyed(entity this) 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; @@ -154,11 +160,17 @@ void func_breakable_behave_restore(entity this) 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 + //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); @@ -177,16 +189,12 @@ void func_breakable_destroyed(entity this) { func_breakable_look_destroyed(this); func_breakable_behave_destroyed(this); - - CSQCMODEL_AUTOUPDATE(this); } void func_breakable_restore(entity this, entity actor, entity trigger) { func_breakable_look_restore(this); func_breakable_behave_restore(this); - - CSQCMODEL_AUTOUPDATE(this); } void func_breakable_restore_self(entity this) @@ -221,6 +229,7 @@ void func_breakable_destroy(entity this, entity actor, entity trigger) if(this.respawntime) { + CSQCMODEL_AUTOUPDATE(this); setthink(this, func_breakable_restore_self); this.nextthink = time + this.respawntime + crandom() * this.respawntimejitter; } @@ -271,6 +280,7 @@ void func_breakable_damage(entity this, entity inflictor, entity attacker, float // 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); } } @@ -283,8 +293,6 @@ void func_breakable_reset(entity this) func_breakable_behave_destroyed(this); else func_breakable_behave_restore(this); - - CSQCMODEL_AUTOUPDATE(this); } // destructible walls that can be used to trigger target_objective_decrease @@ -350,7 +358,7 @@ spawnfunc(func_breakable) this.reset = func_breakable_reset; this.reset(this); - this.init_for_player_needed = 1; + IL_PUSH(g_initforplayer, this); this.init_for_player = func_breakable_init_for_player; CSQCMODEL_AUTOINIT(this); diff --git a/qcsrc/common/triggers/func/breakable.qh b/qcsrc/common/triggers/func/breakable.qh index 75cfb94c69..761a2c7a98 100644 --- a/qcsrc/common/triggers/func/breakable.qh +++ b/qcsrc/common/triggers/func/breakable.qh @@ -1,8 +1,5 @@ -#ifndef TRIGGERS_FUNC_BREAKABLE_H -#define TRIGGERS_FUNC_BREAKABLE_H +#pragma once #ifdef SVQC spawnfunc(func_breakable); #endif - -#endif diff --git a/qcsrc/common/triggers/func/button.qc b/qcsrc/common/triggers/func/button.qc index a8ec50a865..916ff8ca1f 100644 --- a/qcsrc/common/triggers/func/button.qc +++ b/qcsrc/common/triggers/func/button.qc @@ -1,3 +1,4 @@ +#include "button.qh" #ifdef SVQC // button and multiple button @@ -28,7 +29,7 @@ void button_return(entity this) } -void button_blocked(entity this) +void button_blocked(entity this, entity blocker) { // do nothing, just don't come all the way back out } @@ -68,17 +69,17 @@ void button_use(entity this, entity actor, entity trigger) button_fire(this); } -void button_touch(entity this) +void button_touch(entity this, entity toucher) { - if (!other) + if (!toucher) return; - if (!other.iscreature) + if (!toucher.iscreature) return; - if(other.velocity * this.movedir < 0) + if(toucher.velocity * this.movedir < 0) return; - this.enemy = other; - if (other.owner) - this.enemy = other.owner; + this.enemy = toucher; + if (toucher.owner) + this.enemy = toucher.owner; button_fire (this); } diff --git a/qcsrc/common/triggers/func/button.qh b/qcsrc/common/triggers/func/button.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/button.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/conveyor.qc b/qcsrc/common/triggers/func/conveyor.qc index e2335b8831..90328da230 100644 --- a/qcsrc/common/triggers/func/conveyor.qc +++ b/qcsrc/common/triggers/func/conveyor.qc @@ -1,3 +1,4 @@ +#include "conveyor.qh" REGISTER_NET_LINKED(ENT_CLIENT_CONVEYOR) void conveyor_think(entity this) @@ -8,47 +9,51 @@ void conveyor_think(entity this) this.move_time = time; if(dt <= 0) { return; } #endif - entity e; // set mythis as current conveyor where possible - for(e = NULL; (e = findentity(e, conveyor, this)); ) - e.conveyor = NULL; + IL_EACH(g_conveyed, it.conveyor == this, + { + it.conveyor = NULL; + IL_REMOVE(g_conveyed, it); + }); if(this.state) { - for(e = findradius((this.absmin + this.absmax) * 0.5, vlen(this.absmax - this.absmin) * 0.5 + 1); e; e = e.chain) - if(!e.conveyor.state) - if(isPushable(e)) + 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 { - 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.conveyor = this; + if(!it.conveyor) + IL_PUSH(g_conveyed, it); + it.conveyor = this; } + }); - for(e = NULL; (e = findentity(e, conveyor, this)); ) + IL_EACH(g_conveyed, it.conveyor == this, { - if(IS_CLIENT(e)) // doing it via velocity has quite some advantages + if(IS_CLIENT(it)) // doing it via velocity has quite some advantages continue; // done in SV_PlayerPhysics continue; - setorigin(e, e.origin + this.movedir * PHYS_INPUT_FRAMETIME); - move_out_of_solid(e); + setorigin(it, it.origin + this.movedir * PHYS_INPUT_FRAMETIME); + move_out_of_solid(it); #ifdef SVQC - UpdateCSQCProjectile(e); + UpdateCSQCProjectile(it); #endif /* // stupid conveyor code - tracebox(e.origin, e.mins, e.maxs, e.origin + this.movedir * sys_frametime, MOVE_NORMAL, e); + tracebox(it.origin, it.mins, it.maxs, it.origin + this.movedir * sys_frametime, MOVE_NORMAL, it); if(trace_fraction > 0) - setorigin(e, trace_endpos); + setorigin(it, trace_endpos); */ - } + }); } #ifdef SVQC @@ -141,7 +146,7 @@ spawnfunc(func_conveyor) { SetMovedir(this); InitMovingBrushTrigger(this); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); conveyor_init(this); } @@ -152,12 +157,12 @@ void conveyor_draw(entity this) { conveyor_think(this); } void conveyor_init(entity this) { this.draw = conveyor_draw; + IL_PUSH(g_drawables, this); this.drawmask = MASK_NORMAL; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.model = ""; this.solid = SOLID_TRIGGER; - this.move_origin = this.origin; this.move_time = time; } diff --git a/qcsrc/common/triggers/func/conveyor.qh b/qcsrc/common/triggers/func/conveyor.qh new file mode 100644 index 0000000000..c12b52d2dd --- /dev/null +++ b/qcsrc/common/triggers/func/conveyor.qh @@ -0,0 +1,4 @@ +#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 index f32a8d75de..8a82802607 100644 --- a/qcsrc/common/triggers/func/door.qc +++ b/qcsrc/common/triggers/func/door.qc @@ -1,3 +1,4 @@ +#include "door.qh" /* Doors are similar to buttons, but can spawn a fat trigger field around them @@ -23,33 +24,33 @@ THINK FUNCTIONS void door_go_down(entity this); void door_go_up(entity this); void door_rotating_go_down(entity this); -void door_rotating_go_up(entity this); +void door_rotating_go_up(entity this, entity oth); -void door_blocked(entity this) +void door_blocked(entity this, entity blocker) { if((this.spawnflags & 8) #ifdef SVQC - && (other.takedamage != DAMAGE_NO) + && (blocker.takedamage != DAMAGE_NO) #elif defined(CSQC) - && !IS_DEAD(other) + && !IS_DEAD(blocker) #endif ) { // KIll Kill Kill!! #ifdef SVQC - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); #endif } else { #ifdef SVQC - if((this.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); #endif // don't change direction for dead or dying stuff - if(IS_DEAD(other) + if(IS_DEAD(blocker) #ifdef SVQC - && (other.takedamage == DAMAGE_NO) + && (blocker.takedamage == DAMAGE_NO) #endif ) { @@ -61,7 +62,7 @@ void door_blocked(entity this) door_go_up (this); } else { - door_rotating_go_up (this); + door_rotating_go_up(this, blocker); } else if (this.classname == "door") @@ -77,8 +78,8 @@ void door_blocked(entity this) else { //gib dying stuff just to make sure - if((this.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); } #endif } @@ -235,15 +236,15 @@ void door_fire(entity this, entity actor, entity trigger) door_go_up(e); } else { // if the BIDIR spawnflag (==2) is set and the trigger has set trigger_reverse, reverse the opening direction - if ((e.spawnflags & 2) && other.trigger_reverse!=0 && e.lip != 666 && e.state == STATE_BOTTOM) { + 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) && (other.trigger_reverse == 0)) || ((e.lip != 666) && (other.trigger_reverse != 0))))) + && (((e.lip == 666) && (trigger.trigger_reverse == 0)) || ((e.lip != 666) && (trigger.trigger_reverse != 0))))) { - door_rotating_go_up(e); + door_rotating_go_up(e, trigger); } } e = e.enemy; @@ -289,9 +290,9 @@ Prints messages ================ */ -void door_touch(entity this) +void door_touch(entity this, entity toucher) { - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; if (this.owner.door_finished > time) return; @@ -301,36 +302,35 @@ void door_touch(entity this) #ifdef SVQC if (!(this.owner.dmg) && (this.owner.message != "")) { - if (IS_CLIENT(other)) - centerprint(other, this.owner.message); - play2(other, this.owner.noise); + if (IS_CLIENT(toucher)) + centerprint(toucher, this.owner.message); + play2(toucher, this.owner.noise); } #endif } -void door_generic_plat_blocked(entity this) +void door_generic_plat_blocked(entity this, entity blocker) { - - if((this.spawnflags & 8) && (other.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! + if((this.spawnflags & 8) && (blocker.takedamage != DAMAGE_NO)) { // Kill Kill Kill!! #ifdef SVQC - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); #endif } else { #ifdef SVQC - if((this.dmg) && (other.takedamage == DAMAGE_YES)) // Shall we bite? - Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if((this.dmg) && (blocker.takedamage == DAMAGE_YES)) // Shall we bite? + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); #endif //Dont chamge direction for dead or dying stuff - if(IS_DEAD(other) && (other.takedamage == DAMAGE_NO)) + if(IS_DEAD(blocker) && (blocker.takedamage == DAMAGE_NO)) { if (this.wait >= 0) { if (this.state == STATE_DOWN) - door_rotating_go_up (this); + door_rotating_go_up (this, blocker); else door_rotating_go_down (this); } @@ -339,8 +339,8 @@ void door_generic_plat_blocked(entity this) else { //gib dying stuff just to make sure - if((this.dmg) && (other.takedamage != DAMAGE_NO)) // Shall we bite? - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) // Shall we bite? + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); } #endif } @@ -383,7 +383,7 @@ void door_rotating_go_down(entity this) SUB_CalcAngleMove (this, this.pos1, TSPEED_LINEAR, this.speed, door_rotating_hit_bottom); } -void door_rotating_go_up(entity this) +void door_rotating_go_up(entity this, entity oth) { if (this.state == STATE_UP) return; // already going up @@ -401,7 +401,7 @@ void door_rotating_go_up(entity this) string oldmessage; oldmessage = this.message; this.message = ""; - SUB_UseTargets(this, NULL, other); // TODO: is other needed here? + SUB_UseTargets(this, NULL, oth); // TODO: is oth needed here? this.message = oldmessage; } @@ -414,13 +414,13 @@ Spawned if a door lacks a real activator ========================================= */ -void door_trigger_touch(entity this) +void door_trigger_touch(entity this, entity toucher) { - if (other.health < 1) + if (toucher.health < 1) #ifdef SVQC - if (!((other.iscreature || (other.flags & FL_PROJECTILE)) && !IS_DEAD(other))) + if (!((toucher.iscreature || (toucher.flags & FL_PROJECTILE)) && !IS_DEAD(toucher))) #elif defined(CSQC) - if(!((IS_CLIENT(other) || other.classname == "csqcprojectile") && !IS_DEAD(other))) + if(!((IS_CLIENT(toucher) || toucher.classname == "csqcprojectile") && !IS_DEAD(toucher))) #endif return; @@ -428,12 +428,12 @@ void door_trigger_touch(entity this) return; // check if door is locked - if (!door_check_keys(this, other)) + if (!door_check_keys(this, toucher)) return; this.door_finished = time + 1; - door_use(this.owner, other, NULL); + door_use(this.owner, toucher, NULL); } void door_spawnfield(entity this, vector fmins, vector fmaxs) @@ -442,7 +442,7 @@ void door_spawnfield(entity this, vector fmins, vector fmaxs) vector t1 = fmins, t2 = fmaxs; trigger = new(doortriggerfield); - trigger.movetype = MOVETYPE_NONE; + set_movetype(trigger, MOVETYPE_NONE); trigger.solid = SOLID_TRIGGER; trigger.owner = this; #ifdef SVQC @@ -526,7 +526,7 @@ void LinkDoors(entity this) break; } } - LOG_TRACE("\n"); + LOG_TRACE(""); // collect health, targetname, message, size cmins = this.absmin; @@ -816,7 +816,7 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) this.SUB_LTIME = ReadCoord(); this.solid = SOLID_BSP; - this.movetype = MOVETYPE_PUSH; + set_movetype(this, MOVETYPE_PUSH); this.use = door_use; LinkDoors(this); @@ -825,10 +825,7 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) door_init_startopen(this); this.move_time = time; - this.move_origin = this.origin; - this.move_movetype = MOVETYPE_PUSH; - this.move_angles = this.angles; - this.move_blocked = door_blocked; + set_movetype(this, MOVETYPE_PUSH); } if(sf & SF_TRIGGER_RESET) @@ -842,7 +839,6 @@ NET_HANDLE(ENT_CLIENT_DOOR, bool isnew) this.origin_y = ReadCoord(); this.origin_z = ReadCoord(); setorigin(this, this.origin); - this.move_origin = this.origin; this.pos1_x = ReadCoord(); this.pos1_y = ReadCoord(); diff --git a/qcsrc/common/triggers/func/door_rotating.qc b/qcsrc/common/triggers/func/door_rotating.qc index 31171899fc..2c72dc9cf0 100644 --- a/qcsrc/common/triggers/func/door_rotating.qc +++ b/qcsrc/common/triggers/func/door_rotating.qc @@ -1,3 +1,4 @@ +#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. diff --git a/qcsrc/common/triggers/func/door_rotating.qh b/qcsrc/common/triggers/func/door_rotating.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/door_rotating.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/door_secret.qc b/qcsrc/common/triggers/func/door_secret.qc index f02fc61f64..0b8930aec5 100644 --- a/qcsrc/common/triggers/func/door_secret.qc +++ b/qcsrc/common/triggers/func/door_secret.qc @@ -1,3 +1,4 @@ +#include "door_secret.qh" #ifdef SVQC void fd_secret_move1(entity this); void fd_secret_move2(entity this); @@ -19,6 +20,8 @@ void fd_secret_use(entity this, entity actor, entity trigger) 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... @@ -135,7 +138,7 @@ void fd_secret_done(entity this) .float door_finished; -void secret_blocked(entity this) +void secret_blocked(entity this, entity blocker) { if (time < this.door_finished) return; @@ -150,9 +153,9 @@ secret_touch Prints messages ================ */ -void secret_touch(entity this) +void secret_touch(entity this, entity toucher) { - if (!other.iscreature) + if (!toucher.iscreature) return; if (this.door_finished > time) return; @@ -161,9 +164,9 @@ void secret_touch(entity this) if (this.message) { - if (IS_CLIENT(other)) - centerprint(other, this.message); - play2(other, this.noise); + if (IS_CLIENT(toucher)) + centerprint(toucher, this.message); + play2(toucher, this.noise); } } diff --git a/qcsrc/common/triggers/func/door_secret.qh b/qcsrc/common/triggers/func/door_secret.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/door_secret.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/fourier.qc b/qcsrc/common/triggers/func/fourier.qc index 52eab115a0..600f393730 100644 --- a/qcsrc/common/triggers/func/fourier.qc +++ b/qcsrc/common/triggers/func/fourier.qc @@ -1,3 +1,4 @@ +#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. diff --git a/qcsrc/common/triggers/func/fourier.qh b/qcsrc/common/triggers/func/fourier.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/fourier.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/include.qh b/qcsrc/common/triggers/func/include.qh index 628355cb79..4e8b94cd1c 100644 --- a/qcsrc/common/triggers/func/include.qh +++ b/qcsrc/common/triggers/func/include.qh @@ -1,8 +1,5 @@ -#ifndef TRIGGERS_FUNC_INCLUDE_H -#define TRIGGERS_FUNC_INCLUDE_H +#pragma once #include "door.qh" #include "ladder.qh" #include "train.qh" - -#endif diff --git a/qcsrc/common/triggers/func/ladder.qc b/qcsrc/common/triggers/func/ladder.qc index af5065643b..5ff2bdcc24 100644 --- a/qcsrc/common/triggers/func/ladder.qc +++ b/qcsrc/common/triggers/func/ladder.qc @@ -1,21 +1,22 @@ +#include "ladder.qh" REGISTER_NET_LINKED(ENT_CLIENT_LADDER) -void func_ladder_touch(entity this) +void func_ladder_touch(entity this, entity toucher) { #ifdef SVQC - if (!other.iscreature) + if (!toucher.iscreature) return; - if(IS_VEHICLE(other)) + if(IS_VEHICLE(toucher)) return; #elif defined(CSQC) - if(!other.isplayermodel) + if(!toucher.isplayermodel) return; #endif - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); - other.ladder_time = time + 0.1; - other.ladder_entity = this; + toucher.ladder_time = time + 0.1; + toucher.ladder_entity = this; } #ifdef SVQC diff --git a/qcsrc/common/triggers/func/pendulum.qc b/qcsrc/common/triggers/func/pendulum.qc index 05f7b75a67..946712509b 100644 --- a/qcsrc/common/triggers/func/pendulum.qc +++ b/qcsrc/common/triggers/func/pendulum.qc @@ -1,3 +1,4 @@ +#include "pendulum.qh" #ifdef SVQC .float freq; void func_pendulum_controller_think(entity this) diff --git a/qcsrc/common/triggers/func/pendulum.qh b/qcsrc/common/triggers/func/pendulum.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/pendulum.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/plat.qc b/qcsrc/common/triggers/func/plat.qc index 6f7ebaad31..5d90924daa 100644 --- a/qcsrc/common/triggers/func/plat.qc +++ b/qcsrc/common/triggers/func/plat.qc @@ -1,3 +1,4 @@ +#include "plat.qh" REGISTER_NET_LINKED(ENT_CLIENT_PLAT) #ifdef SVQC @@ -168,17 +169,16 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) this.classname = "plat"; this.solid = SOLID_BSP; - this.movetype = MOVETYPE_PUSH; + 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 - this.move_movetype = MOVETYPE_PUSH; - this.move_origin = this.origin; - this.move_angles = this.angles; + set_movetype(this, MOVETYPE_PUSH); this.move_time = time; plat_spawn_inside_trigger(this); @@ -188,8 +188,6 @@ NET_HANDLE(ENT_CLIENT_PLAT, bool isnew) { plat_reset(this); - this.move_origin = this.origin; - this.move_angles = this.angles; this.move_time = time; } return true; diff --git a/qcsrc/common/triggers/func/plat.qh b/qcsrc/common/triggers/func/plat.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/plat.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/pointparticles.qc b/qcsrc/common/triggers/func/pointparticles.qc index 1705d4081e..a0773f249a 100644 --- a/qcsrc/common/triggers/func/pointparticles.qc +++ b/qcsrc/common/triggers/func/pointparticles.qc @@ -1,3 +1,4 @@ +#include "pointparticles.qh" REGISTER_NET_LINKED(ENT_CLIENT_POINTPARTICLES) #ifdef SVQC @@ -378,6 +379,7 @@ NET_HANDLE(ENT_CLIENT_POINTPARTICLES, bool isnew) 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 new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/pointparticles.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/rainsnow.qc b/qcsrc/common/triggers/func/rainsnow.qc index dc569f69a1..c8b4e29243 100644 --- a/qcsrc/common/triggers/func/rainsnow.qc +++ b/qcsrc/common/triggers/func/rainsnow.qc @@ -1,3 +1,4 @@ +#include "rainsnow.qh" REGISTER_NET_LINKED(ENT_CLIENT_RAINSNOW) #ifdef SVQC @@ -35,7 +36,7 @@ spawnfunc(func_rain) if (!this.dest) this.dest = '0 0 -700'; this.angles = '0 0 0'; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.solid = SOLID_NOT; SetBrushEntityModel(this); if (!this.cnt) @@ -73,7 +74,7 @@ spawnfunc(func_snow) if (!this.dest) this.dest = '0 0 -300'; this.angles = '0 0 0'; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.solid = SOLID_NOT; SetBrushEntityModel(this); if (!this.cnt) @@ -92,14 +93,24 @@ spawnfunc(func_snow) Net_LinkEntity(this, false, 0, rainsnow_SendEntity); } #elif defined(CSQC) +float autocvar_cl_rainsnow_maxdrawdist = 2048; + void Draw_Rain(entity this) { - te_particlerain(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); + 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) { - te_particlesnow(this.origin + this.mins, this.origin + this.maxs, this.velocity, floor(this.count * drawframetime + random()), this.glow_color); + 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) @@ -124,6 +135,7 @@ NET_HANDLE(ENT_CLIENT_RAINSNOW, bool isnew) 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 diff --git a/qcsrc/common/triggers/func/rainsnow.qh b/qcsrc/common/triggers/func/rainsnow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/rainsnow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/rotating.qc b/qcsrc/common/triggers/func/rotating.qc index 22f3dedea2..1adaea91d3 100644 --- a/qcsrc/common/triggers/func/rotating.qc +++ b/qcsrc/common/triggers/func/rotating.qc @@ -1,3 +1,4 @@ +#include "rotating.qh" #ifdef SVQC void func_rotating_setactive(entity this, int astate) { diff --git a/qcsrc/common/triggers/func/rotating.qh b/qcsrc/common/triggers/func/rotating.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/rotating.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/stardust.qc b/qcsrc/common/triggers/func/stardust.qc index db5081b61b..9c2fba8ada 100644 --- a/qcsrc/common/triggers/func/stardust.qc +++ b/qcsrc/common/triggers/func/stardust.qc @@ -1,3 +1,4 @@ +#include "stardust.qh" #ifdef SVQC spawnfunc(func_stardust) { diff --git a/qcsrc/common/triggers/func/stardust.qh b/qcsrc/common/triggers/func/stardust.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/stardust.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/func/train.qc b/qcsrc/common/triggers/func/train.qc index 3f6af7d001..2e4356bab3 100644 --- a/qcsrc/common/triggers/func/train.qc +++ b/qcsrc/common/triggers/func/train.qc @@ -1,4 +1,6 @@ +#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); @@ -13,7 +15,7 @@ void train_wait(entity this) { entity targ, cp; vector ang; - targ = find(NULL, targetname, this.target); + targ = this.future_target; if((this.spawnflags & 1) && targ.curvetarget) cp = find(NULL, targetname, targ.curvetarget); else @@ -40,7 +42,7 @@ void train_wait(entity this) #endif #ifdef SVQC - entity tg = find(NULL, targetname, this.target); + entity tg = this.future_target; if(tg.spawnflags & 4) { this.use = train_use; @@ -61,13 +63,34 @@ void train_wait(entity this) } } +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, cp = NULL; + entity targ = NULL, cp = NULL; vector cp_org = '0 0 0'; - targ = find(NULL, targetname, this.target); + 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) @@ -185,9 +208,11 @@ void train_use(entity this, entity actor, entity trigger) void func_train_find(entity this) { - entity targ; - targ = find(NULL, targetname, this.target); + 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"); SUB_SETORIGIN(this, targ.origin - this.view_ofs); @@ -307,9 +332,10 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) this.classname = "func_train"; this.solid = SOLID_BSP; - this.movetype = MOVETYPE_PUSH; + 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)) @@ -322,13 +348,9 @@ NET_HANDLE(ENT_CLIENT_TRAIN, bool isnew) //func_train_find(); // but we will need these - //this.move_nextthink = this.move_ltime + 0.1; - //this.move_think = train_next; train_next(this); - this.move_movetype = MOVETYPE_PUSH; - this.move_origin = this.origin; - this.move_angles = this.angles; + set_movetype(this, MOVETYPE_PUSH); this.move_time = time; } diff --git a/qcsrc/common/triggers/func/vectormamamam.qc b/qcsrc/common/triggers/func/vectormamamam.qc index accdc99835..18d58d6491 100644 --- a/qcsrc/common/triggers/func/vectormamamam.qc +++ b/qcsrc/common/triggers/func/vectormamamam.qc @@ -1,3 +1,4 @@ +#include "vectormamamam.qh" #ifdef SVQC // reusing some fields havocbots declared .entity wp00, wp01, wp02, wp03; diff --git a/qcsrc/common/triggers/func/vectormamamam.qh b/qcsrc/common/triggers/func/vectormamamam.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/func/vectormamamam.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/include.qh b/qcsrc/common/triggers/include.qh index 25688e3ecb..87c07c14df 100644 --- a/qcsrc/common/triggers/include.qh +++ b/qcsrc/common/triggers/include.qh @@ -1,8 +1,7 @@ -#ifndef TRIGGERS_INCLUDE_H -#define TRIGGERS_INCLUDE_H +#pragma once // some required common stuff -#ifdef CSQC +#ifdef SVQC #include #endif #include "triggers.qh" @@ -18,5 +17,3 @@ // trigger #include "trigger/include.qh" - -#endif diff --git a/qcsrc/common/triggers/misc/corner.qc b/qcsrc/common/triggers/misc/corner.qc index 38772a2955..dcc44710fc 100644 --- a/qcsrc/common/triggers/misc/corner.qc +++ b/qcsrc/common/triggers/misc/corner.qc @@ -1,3 +1,4 @@ +#include "corner.qh" REGISTER_NET_LINKED(ENT_CLIENT_CORNER) #ifdef SVQC diff --git a/qcsrc/common/triggers/misc/corner.qh b/qcsrc/common/triggers/misc/corner.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/corner.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/follow.qc b/qcsrc/common/triggers/misc/follow.qc index 69222bf2d8..63db2c18fa 100644 --- a/qcsrc/common/triggers/misc/follow.qc +++ b/qcsrc/common/triggers/misc/follow.qc @@ -1,3 +1,4 @@ +#include "follow.qh" // the way this entity works makes it no use to CSQC, as it removes itself instantly #ifdef SVQC @@ -41,13 +42,13 @@ void follow_init(entity this) } dst.solid = SOLID_NOT; // solid doesn't work with attachment - remove(this); + delete(this); } else { if(this.spawnflags & 2) { - dst.movetype = MOVETYPE_FOLLOW; + set_movetype(dst, MOVETYPE_FOLLOW); dst.aiment = src; // dst.punchangle = '0 0 0'; // keep unchanged dst.view_ofs = dst.origin; @@ -58,7 +59,7 @@ void follow_init(entity this) follow_sameorigin(dst, src); } - remove(this); + delete(this); } } diff --git a/qcsrc/common/triggers/misc/follow.qh b/qcsrc/common/triggers/misc/follow.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/follow.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/include.qc b/qcsrc/common/triggers/misc/include.qc index 8965c62906..bbe5ce03d6 100644 --- a/qcsrc/common/triggers/misc/include.qc +++ b/qcsrc/common/triggers/misc/include.qc @@ -1,3 +1,4 @@ +#include "include.qh" #include "corner.qc" #include "follow.qc" #include "laser.qc" diff --git a/qcsrc/common/triggers/misc/include.qh b/qcsrc/common/triggers/misc/include.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/include.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/laser.qc b/qcsrc/common/triggers/misc/laser.qc index 399ba5baf5..2059a8126d 100644 --- a/qcsrc/common/triggers/misc/laser.qc +++ b/qcsrc/common/triggers/misc/laser.qc @@ -1,3 +1,4 @@ +#include "laser.qh" #if defined(CSQC) #include #include @@ -377,5 +378,6 @@ NET_HANDLE(ENT_CLIENT_LASER, bool isnew) 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 new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/laser.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/misc/teleport_dest.qc b/qcsrc/common/triggers/misc/teleport_dest.qc index ab15a68919..fc3cec863a 100644 --- a/qcsrc/common/triggers/misc/teleport_dest.qc +++ b/qcsrc/common/triggers/misc/teleport_dest.qc @@ -1,3 +1,4 @@ +#include "teleport_dest.qh" REGISTER_NET_LINKED(ENT_CLIENT_TELEPORT_DEST) #ifdef SVQC diff --git a/qcsrc/common/triggers/misc/teleport_dest.qh b/qcsrc/common/triggers/misc/teleport_dest.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/misc/teleport_dest.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/platforms.qc b/qcsrc/common/triggers/platforms.qc index c089039a60..10af3e1944 100644 --- a/qcsrc/common/triggers/platforms.qc +++ b/qcsrc/common/triggers/platforms.qc @@ -1,17 +1,18 @@ -void generic_plat_blocked(entity this) +#include "platforms.qh" +void generic_plat_blocked(entity this, entity blocker) { #ifdef SVQC - if(this.dmg && other.takedamage != DAMAGE_NO) + if(this.dmg && blocker.takedamage != DAMAGE_NO) { if(this.dmgtime2 < time) { - Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); this.dmgtime2 = time + this.dmgtime; } // Gib dead/dying stuff - if(IS_DEAD(other)) - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); } #endif } @@ -23,7 +24,7 @@ void plat_spawn_inside_trigger(entity this) trigger = spawn(); settouch(trigger, plat_center_touch); - trigger.movetype = MOVETYPE_NONE; + set_movetype(trigger, MOVETYPE_NONE); trigger.solid = SOLID_TRIGGER; trigger.enemy = this; @@ -53,7 +54,7 @@ void plat_spawn_inside_trigger(entity this) } // otherwise, something is fishy... - remove(trigger); + delete(trigger); objerror(this, "plat_spawn_inside_trigger: platform has odd size or lip, can't spawn"); } @@ -86,18 +87,18 @@ void plat_go_up(entity this) SUB_CalcMove (this, this.pos1, TSPEED_LINEAR, this.speed, plat_hit_top); } -void plat_center_touch(entity this) +void plat_center_touch(entity this, entity toucher) { #ifdef SVQC - if (!other.iscreature) + if (!toucher.iscreature) return; - if (other.health <= 0) + if (toucher.health <= 0) return; #elif defined(CSQC) - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; - if(IS_DEAD(other)) + if(IS_DEAD(toucher)) return; #endif @@ -107,16 +108,16 @@ void plat_center_touch(entity this) this.enemy.SUB_NEXTTHINK = this.enemy.SUB_LTIME + 1; } -void plat_outside_touch(entity this) +void plat_outside_touch(entity this, entity toucher) { #ifdef SVQC - if (!other.iscreature) + if (!toucher.iscreature) return; - if (other.health <= 0) + if (toucher.health <= 0) return; #elif defined(CSQC) - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; #endif @@ -128,34 +129,29 @@ void plat_outside_touch(entity this) void plat_trigger_use(entity this, entity actor, entity trigger) { -#ifdef SVQC if (getthink(this)) return; // already activated -#elif defined(CSQC) - if(this.move_think) - return; -#endif plat_go_down(this); } -void plat_crush(entity this) +void plat_crush(entity this, entity blocker) { - if((this.spawnflags & 4) && (other.takedamage != DAMAGE_NO)) + if((this.spawnflags & 4) && (blocker.takedamage != DAMAGE_NO)) { // KIll Kill Kill!! #ifdef SVQC - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); #endif } else { #ifdef SVQC - if((this.dmg) && (other.takedamage != DAMAGE_NO)) + if((this.dmg) && (blocker.takedamage != DAMAGE_NO)) { // Shall we bite? - Damage (other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (blocker, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); // Gib dead/dying stuff - if(IS_DEAD(other)) - Damage (other, this, this, 10000, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + if(IS_DEAD(blocker)) + Damage (blocker, this, this, 10000, DEATH_HURTTRIGGER.m_id, blocker.origin, '0 0 0'); } #endif diff --git a/qcsrc/common/triggers/platforms.qh b/qcsrc/common/triggers/platforms.qh index 6bdfb23d68..c40467494f 100644 --- a/qcsrc/common/triggers/platforms.qh +++ b/qcsrc/common/triggers/platforms.qh @@ -1,16 +1,13 @@ -#ifndef PLATFORMS_H -#define PLATFORMS_H +#pragma once .float dmgtime2; -void plat_center_touch(entity this); -void plat_outside_touch(entity this); +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); +void plat_crush(entity this, entity blocker); const float PLAT_LOW_TRIGGER = 1; .float dmg; - -#endif diff --git a/qcsrc/common/triggers/subs.qc b/qcsrc/common/triggers/subs.qc index 51ef002736..67eb18a678 100644 --- a/qcsrc/common/triggers/subs.qc +++ b/qcsrc/common/triggers/subs.qc @@ -1,3 +1,4 @@ +#include "subs.qh" void SUB_NullThink(entity this) { } void SUB_CalcMoveDone(entity this); @@ -14,7 +15,7 @@ Applies some friction to this void SUB_Friction (entity this) { this.SUB_NEXTTHINK = time; - if(this.SUB_FLAGS & FL_ONGROUND) + if(IS_ONGROUND(this)) this.SUB_VELOCITY = this.SUB_VELOCITY * (1 - frametime * this.friction); } @@ -40,7 +41,7 @@ void SUB_VanishOrRemove (entity ent) else { // remove - remove (ent); + delete (ent); } } @@ -144,7 +145,7 @@ void SUB_CalcMove_controller_think (entity this) // derivative: delta + 2 * delta2 (e.g. for angle positioning) entity own = this.owner; SUB_THINK(own, this.think1); - remove(this); + delete(this); SUB_THUNK(own)(own); } } diff --git a/qcsrc/common/triggers/subs.qh b/qcsrc/common/triggers/subs.qh index 1ce3ea2e9e..4f98d5ae15 100644 --- a/qcsrc/common/triggers/subs.qh +++ b/qcsrc/common/triggers/subs.qh @@ -1,42 +1,14 @@ -#ifndef SUBS_H -#define SUBS_H - -#ifdef SVQC - - #define SUB_ANGLES(s) (s).angles - #define SUB_VELOCITY velocity - #define SUB_AVELOCITY avelocity - #define SUB_ORIGIN origin - #define SUB_SETORIGIN(s,v) setorigin((s), (v)) - #define SUB_NEXTTHINK nextthink - #define SUB_THINK(e, f) setthink(e, f) - #define SUB_THUNK(e) getthink(e) - #define SUB_LTIME ltime - #define SUB_FLAGS flags - -#elif defined(CSQC) - - void _Movetype_LinkEdict(entity this, float touch_triggers); - - #define SUB_ANGLES(s) (s).move_angles - #define SUB_VELOCITY move_velocity - #define SUB_AVELOCITY move_avelocity - #define SUB_ORIGIN move_origin - #define SUB_NEXTTHINK move_nextthink - #define SUB_THINK(e, f) ((e).move_think = (f)) - #define SUB_THUNK(e) ((e).move_think) - #define SUB_LTIME move_ltime - #define SUB_FLAGS move_flags - -.vector move_origin; - - void SUB_SETORIGIN(entity s, vector v) - { - s.move_origin = v; - _Movetype_LinkEdict(s, true); - } +#pragma once -#endif +#define SUB_ANGLES(s) (s).angles +#define SUB_VELOCITY velocity +#define SUB_AVELOCITY avelocity +#define SUB_ORIGIN origin +#define SUB_SETORIGIN(s,v) setorigin((s), (v)) +#define SUB_NEXTTHINK nextthink +#define SUB_THINK(e, f) setthink(e, f) +#define SUB_THUNK(e) getthink(e) +#define SUB_LTIME ltime void SUB_SetFade (entity ent, float when, float fading_time); @@ -96,5 +68,3 @@ float STATE_DOWN = 3; .float max_health; // players maximum health is stored here #endif - -#endif diff --git a/qcsrc/common/triggers/target/changelevel.qc b/qcsrc/common/triggers/target/changelevel.qc index d4bc850de1..6c006d42a9 100644 --- a/qcsrc/common/triggers/target/changelevel.qc +++ b/qcsrc/common/triggers/target/changelevel.qc @@ -1,3 +1,4 @@ +#include "changelevel.qh" #ifdef SVQC .string chmap, gametype; .entity chlevel_targ; diff --git a/qcsrc/common/triggers/target/changelevel.qh b/qcsrc/common/triggers/target/changelevel.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/changelevel.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/include.qh b/qcsrc/common/triggers/target/include.qh index 4e44b9762b..c0f7cad443 100644 --- a/qcsrc/common/triggers/target/include.qh +++ b/qcsrc/common/triggers/target/include.qh @@ -1,6 +1,3 @@ -#ifndef TRIGGERS_TARGET_INCLUDE_H -#define TRIGGERS_TARGET_INCLUDE_H +#pragma once #include "music.qh" - -#endif diff --git a/qcsrc/common/triggers/target/location.qc b/qcsrc/common/triggers/target/location.qc index 5a24d2c5ed..5259276e43 100644 --- a/qcsrc/common/triggers/target/location.qc +++ b/qcsrc/common/triggers/target/location.qc @@ -1,3 +1,4 @@ +#include "location.qh" #ifdef SVQC void target_push_init(entity this); diff --git a/qcsrc/common/triggers/target/location.qh b/qcsrc/common/triggers/target/location.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/location.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/music.qc b/qcsrc/common/triggers/target/music.qc index d43cbec769..1f8cb00cb5 100644 --- a/qcsrc/common/triggers/target/music.qc +++ b/qcsrc/common/triggers/target/music.qc @@ -1,7 +1,9 @@ +#include "music.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include + #include #include #include #endif @@ -11,6 +13,9 @@ 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 @@ -36,7 +41,8 @@ void target_music_reset(entity this) } void target_music_kill() { - FOREACH_ENTITY_CLASS("target_music", true, { + IL_EACH(g_targetmusic_list, true, + { it.volume = 0; if (it.targetname == "") target_music_sendto(it, MSG_ALL, 1); @@ -64,6 +70,7 @@ spawnfunc(target_music) 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 @@ -71,13 +78,13 @@ spawnfunc(target_music) } void TargetMusic_RestoreGame() { - for(entity e = NULL; (e = find(e, classname, "target_music")); ) + IL_EACH(g_targetmusic_list, true, { - if(e.targetname == "") - target_music_sendto(e, MSG_INIT, 1); + if(it.targetname == "") + target_music_sendto(it, MSG_INIT, 1); else - target_music_sendto(e, MSG_INIT, 0); - } + target_music_sendto(it, MSG_INIT, 0); + }); } // values: // volume @@ -223,7 +230,7 @@ void Net_TargetMusic() _sound(e, CH_BGM_SINGLE, e.noise, 0, ATTEN_NONE); if(getsoundtime(e, CH_BGM_SINGLE) < 0) { - LOG_TRACEF("Cannot initialize sound %s\n", e.noise); + LOG_TRACEF("Cannot initialize sound %s", e.noise); strunzone(e.noise); e.noise = string_null; } @@ -310,7 +317,7 @@ NET_HANDLE(ENT_CLIENT_TRIGGER_MUSIC, bool isnew) _sound(this, CH_BGM_SINGLE, this.noise, 0, ATTEN_NONE); if(getsoundtime(this, CH_BGM_SINGLE) < 0) { - LOG_TRACEF("Cannot initialize sound %s\n", this.noise); + LOG_TRACEF("Cannot initialize sound %s", this.noise); strunzone(this.noise); this.noise = string_null; } diff --git a/qcsrc/common/triggers/target/music.qh b/qcsrc/common/triggers/target/music.qh index a9232107eb..80b3684a4e 100644 --- a/qcsrc/common/triggers/target/music.qh +++ b/qcsrc/common/triggers/target/music.qh @@ -1,5 +1,4 @@ -#ifndef TARGET_MUSIC_H -#define TARGET_MUSIC_H +#pragma once .float lifetime; @@ -25,5 +24,3 @@ void Ent_TriggerMusic_Remove(entity this); #elif defined(SVQC) void target_music_kill(); #endif - -#endif diff --git a/qcsrc/common/triggers/target/spawn.qc b/qcsrc/common/triggers/target/spawn.qc index eced856d7e..4eed8ef345 100644 --- a/qcsrc/common/triggers/target/spawn.qc +++ b/qcsrc/common/triggers/target/spawn.qc @@ -1,3 +1,4 @@ +#include "spawn.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) @@ -287,8 +288,10 @@ void target_spawn_use(entity this, entity actor, entity trigger) else { // edit entity - for(entity e = NULL; (e = find(e, targetname, this.target)); ) - target_spawn_useon(e, this, actor, trigger); + FOREACH_ENTITY_STRING(targetname, this.target, + { + target_spawn_useon(it, this, actor, trigger); + }); } } diff --git a/qcsrc/common/triggers/target/spawn.qh b/qcsrc/common/triggers/target/spawn.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/spawn.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/speaker.qc b/qcsrc/common/triggers/target/speaker.qc index 7db93a3d1e..af327b443b 100644 --- a/qcsrc/common/triggers/target/speaker.qc +++ b/qcsrc/common/triggers/target/speaker.qc @@ -1,3 +1,4 @@ +#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); @@ -115,7 +116,7 @@ spawnfunc(target_speaker) else if(this.spawnflags & 1) // LOOPED_ON { ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - remove(this); + delete(this); } else if(this.spawnflags & 2) // LOOPED_OFF { @@ -125,7 +126,7 @@ spawnfunc(target_speaker) { // Quake/Nexuiz fallback ambientsound (this.origin, this.noise, VOL_BASE * this.volume, this.atten); - remove(this); + delete(this); } } #endif diff --git a/qcsrc/common/triggers/target/speaker.qh b/qcsrc/common/triggers/target/speaker.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/speaker.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/target/voicescript.qc b/qcsrc/common/triggers/target/voicescript.qc index bdf1e0f1fb..fe7155cc54 100644 --- a/qcsrc/common/triggers/target/voicescript.qc +++ b/qcsrc/common/triggers/target/voicescript.qc @@ -1,3 +1,4 @@ +#include "voicescript.qh" #ifdef SVQC .entity voicescript; // attached voice script .float voicescript_index; // index of next voice, or -1 to use the randomized ones diff --git a/qcsrc/common/triggers/target/voicescript.qh b/qcsrc/common/triggers/target/voicescript.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/target/voicescript.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/teleporters.qc b/qcsrc/common/triggers/teleporters.qc index ba472ac557..949f478fdd 100644 --- a/qcsrc/common/triggers/teleporters.qc +++ b/qcsrc/common/triggers/teleporters.qc @@ -92,7 +92,7 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle RandomSelection_Init(); FOREACH_WORD(teleporter.noise, true, { - RandomSelection_Add(NULL, 0, it, 1, 1); + RandomSelection_AddString(it, 1, 1); }); thesound = RandomSelection_chosen_string; } @@ -124,22 +124,20 @@ void TeleportPlayer(entity teleporter, entity player, vector to, vector to_angle UpdateCSQCProjectileAfterTeleport(player); UpdateItemAfterTeleport(player); #elif defined(CSQC) - from = player.move_origin; - player.move_origin = to; - player.move_angles = to_angles; - player.move_velocity = to_velocity; - player.move_flags &= ~FL_ONGROUND; + 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.isplayermodel) // not for anything but the main player + if(player == csqcplayer) // not for anything but the main player { - setproperty(VF_ANGLES, player.move_angles); - setproperty(VF_CL_VIEWANGLES, player.move_angles); + setproperty(VF_ANGLES, player.angles); + setproperty(VF_CL_VIEWANGLES, player.angles); } - - makevectors(player.move_angles); #endif #ifdef SVQC @@ -187,21 +185,21 @@ entity Simple_TeleportPlayer(entity teleporter, entity player) else { RandomSelection_Init(); - for(e = NULL; (e = find(e, targetname, teleporter.target)); ) + FOREACH_ENTITY_STRING(targetname, teleporter.target, { p = 1; if(STAT(TELEPORT_TELEFRAG_AVOID, player)) { #ifdef SVQC - locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); #elif defined(CSQC) - locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); + locout = it.origin + '0 0 1' * (1 - player.mins.z - 24); #endif if(check_tdeath(player, locout, '0 0 0', '0 0 0')) p = 0; } - RandomSelection_Add(e, 0, string_null, (e.cnt ? e.cnt : 1), p); - } + RandomSelection_AddEnt(it, (it.cnt ? it.cnt : 1), p); + }); e = RandomSelection_chosen_ent; } @@ -213,35 +211,17 @@ entity Simple_TeleportPlayer(entity teleporter, entity player) makevectors(e.mangle); -#ifdef SVQC if(e.speed) if(vdist(player.velocity, >, e.speed)) player.velocity = normalize(player.velocity) * max(0, e.speed); -#elif defined(CSQC) - if(e.speed) - if(vdist(player.move_velocity, >, e.speed)) - player.move_velocity = normalize(player.move_velocity) * max(0, e.speed); -#endif -#ifdef SVQC if(STAT(TELEPORT_MAXSPEED, player)) if(vdist(player.velocity, >, STAT(TELEPORT_MAXSPEED, player))) player.velocity = normalize(player.velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); -#elif defined(CSQC) - if(STAT(TELEPORT_MAXSPEED, player)) - if(vdist(player.move_velocity, >, STAT(TELEPORT_MAXSPEED, player))) - player.move_velocity = normalize(player.move_velocity) * max(0, STAT(TELEPORT_MAXSPEED, player)); -#endif -#ifdef SVQC 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); -#elif defined(CSQC) - locout = e.origin + '0 0 1' * (1 - player.mins.z - 24); - - TeleportPlayer(teleporter, player, locout, e.mangle, v_forward * vlen(player.move_velocity), '0 0 0', '0 0 0', TELEPORT_FLAGS_TELEPORTER); -#endif return e; } @@ -254,7 +234,7 @@ void teleport_findtarget(entity this) { ++n; #ifdef SVQC - if(e.movetype == MOVETYPE_NONE) + if(e.move_movetype == MOVETYPE_NONE) waypoint_spawnforteleporter(this, e.origin, 0); if(e.classname != "info_teleport_destination") LOG_INFO("^3MAPPER ERROR: teleporter does target an invalid teleport destination entity. Angles will not work.\n"); @@ -287,10 +267,10 @@ void teleport_findtarget(entity this) entity Teleport_Find(vector mi, vector ma) { - entity e; - for(e = NULL; (e = find(e, classname, "trigger_teleport")); ) - if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, NULL)) - return e; + IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), + { + return it; + }); return NULL; } @@ -310,7 +290,7 @@ void WarpZone_PostTeleportPlayer_Callback(entity pl) #ifdef SVQC if(!(pl.flags & FL_PROJECTILE)) #elif defined(CSQC) - if(!(pl.move_flags & BIT(15))) // FL_PROJECTILE + 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, ".\n"); pl.owner = NULL; diff --git a/qcsrc/common/triggers/teleporters.qh b/qcsrc/common/triggers/teleporters.qh index ef47b0c6c8..6f5f8cb76e 100644 --- a/qcsrc/common/triggers/teleporters.qh +++ b/qcsrc/common/triggers/teleporters.qh @@ -1,5 +1,7 @@ -#ifndef T_TELEPORTERS_H -#define T_TELEPORTERS_H +#pragma once + +IntrusiveList g_teleporters; +STATIC_INIT(g_teleporters) { g_teleporters = IL_NEW(); } .entity pusher; const float TELEPORT_FLAG_SOUND = 1; @@ -17,7 +19,7 @@ const float TELEPORT_SIMPLE = 2; // only do teleport, nothing special entity Simple_TeleportPlayer(entity teleporter, entity player); -void Teleport_Touch (entity this); +void Teleport_Touch(entity this, entity toucher); void teleport_findtarget(entity this); @@ -92,5 +94,3 @@ void WarpZone_PostTeleportPlayer_Callback(entity pl); if(head.isplayermodel) \ if(boxesoverlap(deathmin, deathmax, head.absmin, head.absmax)) #endif - -#endif diff --git a/qcsrc/common/triggers/trigger/counter.qc b/qcsrc/common/triggers/trigger/counter.qc index a4f850ba9a..8246aed7c3 100644 --- a/qcsrc/common/triggers/trigger/counter.qc +++ b/qcsrc/common/triggers/trigger/counter.qc @@ -1,3 +1,4 @@ +#include "counter.qh" #ifdef SVQC void counter_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/counter.qh b/qcsrc/common/triggers/trigger/counter.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/counter.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/delay.qc b/qcsrc/common/triggers/trigger/delay.qc index d6742fed32..dc1a781f81 100644 --- a/qcsrc/common/triggers/trigger/delay.qc +++ b/qcsrc/common/triggers/trigger/delay.qc @@ -1,8 +1,9 @@ +#include "delay.qh" #ifdef SVQC void delay_use(entity this, entity actor, entity trigger) { setthink(this, SUB_UseTargets_self); - this.nextthink = this.wait; + this.nextthink = time + this.wait; } void delay_reset(entity this) diff --git a/qcsrc/common/triggers/trigger/delay.qh b/qcsrc/common/triggers/trigger/delay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/delay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/disablerelay.qc b/qcsrc/common/triggers/trigger/disablerelay.qc index 1d30db0e08..6154f6bf0a 100644 --- a/qcsrc/common/triggers/trigger/disablerelay.qc +++ b/qcsrc/common/triggers/trigger/disablerelay.qc @@ -1,3 +1,4 @@ +#include "disablerelay.qh" #ifdef SVQC void trigger_disablerelay_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/disablerelay.qh b/qcsrc/common/triggers/trigger/disablerelay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/disablerelay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/flipflop.qc b/qcsrc/common/triggers/trigger/flipflop.qc index e4923bdf54..af212ff5a4 100644 --- a/qcsrc/common/triggers/trigger/flipflop.qc +++ b/qcsrc/common/triggers/trigger/flipflop.qc @@ -1,3 +1,4 @@ +#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 diff --git a/qcsrc/common/triggers/trigger/flipflop.qh b/qcsrc/common/triggers/trigger/flipflop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/flipflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/gamestart.qc b/qcsrc/common/triggers/trigger/gamestart.qc index efddf8c77f..72d76d1833 100644 --- a/qcsrc/common/triggers/trigger/gamestart.qc +++ b/qcsrc/common/triggers/trigger/gamestart.qc @@ -1,8 +1,9 @@ +#include "gamestart.qh" #ifdef SVQC void gamestart_use(entity this, entity actor, entity trigger) { SUB_UseTargets(this, this, trigger); - remove(this); + delete(this); } void gamestart_use_this(entity this) diff --git a/qcsrc/common/triggers/trigger/gamestart.qh b/qcsrc/common/triggers/trigger/gamestart.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/gamestart.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/gravity.qc b/qcsrc/common/triggers/trigger/gravity.qc index 9dcc710f03..3ea1562f08 100644 --- a/qcsrc/common/triggers/trigger/gravity.qc +++ b/qcsrc/common/triggers/trigger/gravity.qc @@ -1,3 +1,4 @@ +#include "gravity.qh" #ifdef SVQC .entity trigger_gravity_check; void trigger_gravity_remove(entity own) @@ -6,7 +7,7 @@ void trigger_gravity_remove(entity own) { UpdateCSQCProjectile(own); own.gravity = own.trigger_gravity_check.gravity; - remove(own.trigger_gravity_check); + delete(own.trigger_gravity_check); } else backtrace("Removing a trigger_gravity_check with no valid owner"); @@ -22,7 +23,7 @@ void trigger_gravity_check_think(entity this) if(this.owner.trigger_gravity_check == this) trigger_gravity_remove(this.owner); else - remove(this); + delete(this); return; } else @@ -37,50 +38,50 @@ void trigger_gravity_use(entity this, entity actor, entity trigger) this.state = !this.state; } -void trigger_gravity_touch(entity this) +void trigger_gravity_touch(entity this, entity toucher) { float g; if(this.state != true) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); g = this.gravity; if (!(this.spawnflags & 1)) { - if(other.trigger_gravity_check) + if(toucher.trigger_gravity_check) { - if(this == other.trigger_gravity_check.enemy) + if(this == toucher.trigger_gravity_check.enemy) { // same? - other.trigger_gravity_check.count = 2; // gravity one more frame... + toucher.trigger_gravity_check.count = 2; // gravity one more frame... return; } // compare prio - if(this.cnt > other.trigger_gravity_check.enemy.cnt) - trigger_gravity_remove(other); + if(this.cnt > toucher.trigger_gravity_check.enemy.cnt) + trigger_gravity_remove(toucher); else return; } - other.trigger_gravity_check = spawn(); - other.trigger_gravity_check.enemy = this; - other.trigger_gravity_check.owner = other; - other.trigger_gravity_check.gravity = other.gravity; - setthink(other.trigger_gravity_check, trigger_gravity_check_think); - other.trigger_gravity_check.nextthink = time; - other.trigger_gravity_check.count = 2; - if(other.gravity) - g *= other.gravity; + 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 (other.gravity != g) + if (toucher.gravity != g) { - other.gravity = g; + toucher.gravity = g; if(this.noise != "") - _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); UpdateCSQCProjectile(this.owner); } } diff --git a/qcsrc/common/triggers/trigger/gravity.qh b/qcsrc/common/triggers/trigger/gravity.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/gravity.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/heal.qc b/qcsrc/common/triggers/trigger/heal.qc index 41519e20cc..e7b3090628 100644 --- a/qcsrc/common/triggers/trigger/heal.qc +++ b/qcsrc/common/triggers/trigger/heal.qc @@ -1,25 +1,26 @@ +#include "heal.qh" #ifdef SVQC .float triggerhealtime; -void trigger_heal_touch(entity this) +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 (other.iscreature) + if (toucher.iscreature) { - if (other.takedamage) - if (!IS_DEAD(other)) - if (other.triggerhealtime < time) + if (toucher.takedamage) + if (!IS_DEAD(toucher)) + if (toucher.triggerhealtime < time) { - EXACTTRIGGER_TOUCH; - other.triggerhealtime = time + 1; + EXACTTRIGGER_TOUCH(this, toucher); + toucher.triggerhealtime = time + 1; - if (other.health < this.max_health) + if (toucher.health < this.max_health) { - other.health = min(other.health + this.health, this.max_health); - other.pauserothealth_finished = max(other.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); - _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + toucher.health = min(toucher.health + this.health, this.max_health); + toucher.pauserothealth_finished = max(toucher.pauserothealth_finished, time + autocvar_g_balance_pause_health_rot); + _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); } } } diff --git a/qcsrc/common/triggers/trigger/heal.qh b/qcsrc/common/triggers/trigger/heal.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/heal.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/hurt.qc b/qcsrc/common/triggers/trigger/hurt.qc index 92a4dd0299..d0ba4ebd19 100644 --- a/qcsrc/common/triggers/trigger/hurt.qc +++ b/qcsrc/common/triggers/trigger/hurt.qc @@ -1,3 +1,4 @@ +#include "hurt.qh" #ifdef SVQC void trigger_hurt_use(entity this, entity actor, entity trigger) { @@ -8,23 +9,23 @@ void trigger_hurt_use(entity this, entity actor, entity trigger) } .float triggerhurttime; -void trigger_hurt_touch(entity this) +void trigger_hurt_touch(entity this, entity toucher) { if (this.active != ACTIVE_ACTIVE) return; if(this.team) - if(((this.spawnflags & 4) == 0) == (this.team != other.team)) + if(((this.spawnflags & 4) == 0) == (this.team != toucher.team)) return; // only do the EXACTTRIGGER_TOUCH checks when really needed (saves some cpu) - if (other.iscreature) + if (toucher.iscreature) { - if (other.takedamage) - if (other.triggerhurttime < time) + if (toucher.takedamage) + if (toucher.triggerhurttime < time) { - EXACTTRIGGER_TOUCH; - other.triggerhurttime = time + 1; + EXACTTRIGGER_TOUCH(this, toucher); + toucher.triggerhurttime = time + 1; entity own; own = this.enemy; @@ -34,15 +35,15 @@ void trigger_hurt_touch(entity this) this.enemy = NULL; // I still hate you all } - Damage (other, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + Damage (toucher, this, own, this.dmg, DEATH_HURTTRIGGER.m_id, toucher.origin, '0 0 0'); } } - else if(other.damagedbytriggers) + else if(toucher.damagedbytriggers) { - if(other.takedamage) + if(toucher.takedamage) { - EXACTTRIGGER_TOUCH; - Damage(other, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, other.origin, '0 0 0'); + EXACTTRIGGER_TOUCH(this, toucher); + Damage(toucher, this, this, this.dmg, DEATH_HURTTRIGGER.m_id, toucher.origin, '0 0 0'); } } diff --git a/qcsrc/common/triggers/trigger/hurt.qh b/qcsrc/common/triggers/trigger/hurt.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/hurt.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/impulse.qc b/qcsrc/common/triggers/trigger/impulse.qc index 91381f4877..cb9c2d2935 100644 --- a/qcsrc/common/triggers/trigger/impulse.qc +++ b/qcsrc/common/triggers/trigger/impulse.qc @@ -1,5 +1,6 @@ +#include "impulse.qh" // targeted (directional) mode -void trigger_impulse_touch1(entity this) +void trigger_impulse_touch1(entity this, entity toucher) { entity targ; float pushdeltatime; @@ -8,24 +9,20 @@ void trigger_impulse_touch1(entity this) if (this.active != ACTIVE_ACTIVE) return; - if (!isPushable(other)) + if (!isPushable(toucher)) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); targ = find(NULL, targetname, this.target); if(!targ) { objerror(this, "trigger_force without a (valid) .target!\n"); - remove(this); + delete(this); return; } -#ifdef SVQC - str = min(this.radius, vlen(this.origin - other.origin)); -#elif defined(CSQC) - str = min(this.radius, vlen(this.origin - other.move_origin)); -#endif + str = min(this.radius, vlen(this.origin - toucher.origin)); if(this.falloff == 1) str = (str / this.radius) * this.strength; @@ -34,75 +31,58 @@ void trigger_impulse_touch1(entity this) else str = this.strength; - pushdeltatime = time - other.lastpushtime; + pushdeltatime = time - toucher.lastpushtime; if (pushdeltatime > 0.15) pushdeltatime = 0; - other.lastpushtime = time; + toucher.lastpushtime = time; if(!pushdeltatime) return; if(this.spawnflags & 64) { -#ifdef SVQC - float addspeed = str - other.velocity * normalize(targ.origin - this.origin); + float addspeed = str - toucher.velocity * normalize(targ.origin - this.origin); if (addspeed > 0) { float accelspeed = min(8 * pushdeltatime * str, addspeed); - other.velocity += accelspeed * normalize(targ.origin - this.origin); + toucher.velocity += accelspeed * normalize(targ.origin - this.origin); } -#elif defined(CSQC) - float addspeed = str - other.move_velocity * normalize(targ.origin - this.origin); - if (addspeed > 0) - { - float accelspeed = min(8 * pushdeltatime * str, addspeed); - other.move_velocity += accelspeed * normalize(targ.origin - this.origin); - } -#endif } else -#ifdef SVQC - other.velocity = other.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; -#elif defined(CSQC) - other.move_velocity = other.move_velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; -#endif + toucher.velocity = toucher.velocity + normalize(targ.origin - this.origin) * str * pushdeltatime; -#ifdef SVQC - UNSET_ONGROUND(other); + UNSET_ONGROUND(toucher); - UpdateCSQCProjectile(other); -#elif defined(CSQC) - other.move_flags &= ~FL_ONGROUND; +#ifdef SVQC + UpdateCSQCProjectile(toucher); #endif } // Directionless (accelerator/decelerator) mode -void trigger_impulse_touch2(entity this) +void trigger_impulse_touch2(entity this, entity toucher) { float pushdeltatime; if (this.active != ACTIVE_ACTIVE) return; - if (!isPushable(other)) + if (!isPushable(toucher)) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); - pushdeltatime = time - other.lastpushtime; + pushdeltatime = time - toucher.lastpushtime; if (pushdeltatime > 0.15) pushdeltatime = 0; - other.lastpushtime = time; + toucher.lastpushtime = time; if(!pushdeltatime) return; // div0: ticrate independent, 1 = identity (not 20) -#ifdef SVQC - other.velocity = other.velocity * pow(this.strength, pushdeltatime); + toucher.velocity = toucher.velocity * pow(this.strength, pushdeltatime); - UpdateCSQCProjectile(other); -#elif defined(CSQC) - other.move_velocity = other.move_velocity * pow(this.strength, pushdeltatime); +#ifdef SVQC + UpdateCSQCProjectile(toucher); #endif } // Spherical (gravity/repulsor) mode -void trigger_impulse_touch3(entity this) +void trigger_impulse_touch3(entity this, entity toucher) { float pushdeltatime; float str; @@ -110,23 +90,19 @@ void trigger_impulse_touch3(entity this) if (this.active != ACTIVE_ACTIVE) return; - if (!isPushable(other)) + if (!isPushable(toucher)) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); - pushdeltatime = time - other.lastpushtime; + pushdeltatime = time - toucher.lastpushtime; if (pushdeltatime > 0.15) pushdeltatime = 0; - other.lastpushtime = time; + toucher.lastpushtime = time; if(!pushdeltatime) return; setsize(this, '-1 -1 -1' * this.radius,'1 1 1' * this.radius); -#ifdef SVQC - str = min(this.radius, vlen(this.origin - other.origin)); -#elif defined(CSQC) - str = min(this.radius, vlen(this.origin - other.move_origin)); -#endif + str = min(this.radius, vlen(this.origin - toucher.origin)); if(this.falloff == 1) str = (1 - str / this.radius) * this.strength; // 1 in the inside @@ -135,12 +111,10 @@ void trigger_impulse_touch3(entity this) else str = this.strength; -#ifdef SVQC - other.velocity = other.velocity + normalize(other.origin - this.origin) * str * pushdeltatime; + toucher.velocity = toucher.velocity + normalize(toucher.origin - this.origin) * str * pushdeltatime; - UpdateCSQCProjectile(other); -#elif defined(CSQC) - other.move_velocity = other.move_velocity + normalize(other.move_origin - this.origin) * str * pushdeltatime; +#ifdef SVQC + UpdateCSQCProjectile(toucher); #endif } diff --git a/qcsrc/common/triggers/trigger/impulse.qh b/qcsrc/common/triggers/trigger/impulse.qh index 67d6361fbe..a6961f5d2e 100644 --- a/qcsrc/common/triggers/trigger/impulse.qh +++ b/qcsrc/common/triggers/trigger/impulse.qh @@ -1,10 +1,7 @@ -#ifndef TRIGGER_IMPULSE_H -#define TRIGGER_IMPULSE_H +#pragma once // tZorks trigger impulse / gravity .float radius; .float falloff; .float strength; .float lastpushtime; - -#endif diff --git a/qcsrc/common/triggers/trigger/include.qh b/qcsrc/common/triggers/trigger/include.qh index 63cdb01901..8aa6b2b172 100644 --- a/qcsrc/common/triggers/trigger/include.qh +++ b/qcsrc/common/triggers/trigger/include.qh @@ -1,5 +1,4 @@ -#ifndef TRIGGERS_TRIGGER_INCLUDE_H -#define TRIGGERS_TRIGGER_INCLUDE_H +#pragma once #include "multi.qh" #include "jumppads.qh" @@ -8,5 +7,3 @@ #include "keylock.qh" #include "impulse.qh" #include "viewloc.qh" - -#endif diff --git a/qcsrc/common/triggers/trigger/jumppads.qc b/qcsrc/common/triggers/trigger/jumppads.qc index 8100f6b797..5a279561d3 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qc +++ b/qcsrc/common/triggers/trigger/jumppads.qc @@ -1,3 +1,4 @@ +#include "jumppads.qh" // TODO: split target_push and put it in the target folder #ifdef SVQC #include "jumppads.qh" @@ -129,24 +130,14 @@ vector trigger_push_calculatevelocity(vector org, entity tgt, float ht) return sdir * vs + '0 0 1' * vz; } -void trigger_push_touch(entity this) +bool jumppad_push(entity this, entity targ) { - if (this.active == ACTIVE_NOT) - return; - - if (!isPushable(other)) - return; - - if(this.team) - if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, other))) - return; - - EXACTTRIGGER_TOUCH; + if (!isPushable(targ)) + return false; if(this.enemy) { - other.velocity = trigger_push_calculatevelocity(other.origin, this.enemy, this.height); - other.move_velocity = other.velocity; + targ.velocity = trigger_push_calculatevelocity(targ.origin, this.enemy, this.height); } else if(this.target && this.target != "") { @@ -155,112 +146,121 @@ void trigger_push_touch(entity this) for(e = NULL; (e = find(e, targetname, this.target)); ) { if(e.cnt) - RandomSelection_Add(e, 0, string_null, e.cnt, 1); + RandomSelection_AddEnt(e, e.cnt, 1); else - RandomSelection_Add(e, 0, string_null, 1, 1); + RandomSelection_AddEnt(e, 1, 1); } - other.velocity = trigger_push_calculatevelocity(other.origin, RandomSelection_chosen_ent, this.height); - other.move_velocity = other.velocity; + targ.velocity = trigger_push_calculatevelocity(targ.origin, RandomSelection_chosen_ent, this.height); } else { - other.velocity = this.movedir; - other.move_velocity = other.velocity; + targ.velocity = this.movedir; } -#ifdef SVQC - UNSET_ONGROUND(other); -#elif defined(CSQC) - other.move_flags &= ~FL_ONGROUND; + UNSET_ONGROUND(targ); - if (other.flags & FL_PROJECTILE) +#ifdef CSQC + if (targ.flags & FL_PROJECTILE) { - other.move_angles = vectoangles (other.move_velocity); - switch(other.move_movetype) + targ.angles = vectoangles (targ.velocity); + switch(targ.move_movetype) { case MOVETYPE_FLY: - other.move_movetype = MOVETYPE_TOSS; - other.gravity = 1; + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; break; case MOVETYPE_BOUNCEMISSILE: - other.move_movetype = MOVETYPE_BOUNCE; - other.gravity = 1; + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; break; } } #endif #ifdef SVQC - if (IS_PLAYER(other)) + if (IS_PLAYER(targ)) { // reset tracking of oldvelocity for impact damage (sudden velocity changes) - other.oldvelocity = other.velocity; + 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, other.origin, other.velocity, 1); - _sound (other, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + 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(other) || IS_BOT_CLIENT(other)) + if(IS_REAL_CLIENT(targ) || IS_BOT_CLIENT(targ)) { bool found = false; - for(int i = 0; i < other.jumppadcount && i < NUM_JUMPPADSUSED; ++i) - if(other.(jumppadsused[i]) == this) + for(int i = 0; i < targ.jumppadcount && i < NUM_JUMPPADSUSED; ++i) + if(targ.(jumppadsused[i]) == this) found = true; if(!found) { - other.(jumppadsused[other.jumppadcount % NUM_JUMPPADSUSED]) = this; - other.jumppadcount = other.jumppadcount + 1; + targ.(jumppadsused[targ.jumppadcount % NUM_JUMPPADSUSED]) = this; + targ.jumppadcount = targ.jumppadcount + 1; } - if(IS_REAL_CLIENT(other)) + if(IS_REAL_CLIENT(targ)) { if(this.message) - centerprint(other, this.message); + centerprint(targ, this.message); } else - other.lastteleporttime = time; + targ.lastteleporttime = time; - if (!IS_DEAD(other)) - animdecide_setaction(other, ANIMACTION_JUMP, true); + if (!IS_DEAD(targ)) + animdecide_setaction(targ, ANIMACTION_JUMP, true); } else - other.jumppadcount = true; + targ.jumppadcount = true; // reset tracking of who pushed you into a hazard (for kill credit) - other.pushltime = 0; - other.istypefrag = 0; + targ.pushltime = 0; + targ.istypefrag = 0; } if(this.enemy.target) - SUB_UseTargets(this.enemy, other, other); // TODO: do we need other as trigger too? + SUB_UseTargets(this.enemy, targ, targ); // TODO: do we need targ as trigger too? - if (other.flags & FL_PROJECTILE) + if (targ.flags & FL_PROJECTILE) { - other.angles = vectoangles (other.velocity); - switch(other.movetype) + targ.angles = vectoangles (targ.velocity); + targ.com_phys_gravity_factor = 1; + switch(targ.move_movetype) { case MOVETYPE_FLY: - other.movetype = MOVETYPE_TOSS; - other.gravity = 1; + set_movetype(targ, MOVETYPE_TOSS); + targ.gravity = 1; break; case MOVETYPE_BOUNCEMISSILE: - other.movetype = MOVETYPE_BOUNCE; - other.gravity = 1; + set_movetype(targ, MOVETYPE_BOUNCE); + targ.gravity = 1; break; } - UpdateCSQCProjectile(other); + UpdateCSQCProjectile(targ); } +#endif - /*if (other.flags & FL_ITEM) - { - ItemUpdate(other); - other.SendFlags |= ISF_DROP; - }*/ + 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); - if (this.spawnflags & PUSH_ONCE) + noref bool success = jumppad_push(this, toucher); + +#ifdef SVQC + if (success && (this.spawnflags & PUSH_ONCE)) { settouch(this, func_null); setthink(this, SUB_Remove); @@ -275,28 +275,25 @@ void trigger_push_updatelink(entity this); #endif void trigger_push_findtarget(entity this) { - entity t; - vector org; - // first calculate a typical start point for the jump - org = (this.absmin + this.absmax) * 0.5; - org_z = this.absmax.z - STAT(PL_MIN, NULL).z; + vector org = (this.absmin + this.absmax) * 0.5; + org.z = this.absmax.z - PL_MIN_CONST.z; if (this.target) { - float n = 0; - for(t = NULL; (t = find(t, targetname, this.target)); ) + int n = 0; + for(entity t = NULL; (t = find(t, targetname, this.target)); ) { ++n; #ifdef SVQC entity e = spawn(); setorigin(e, org); - setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); e.velocity = trigger_push_calculatevelocity(org, t, this.height); tracetoss(e, e); - if(e.movetype == MOVETYPE_NONE) + if(e.move_movetype == MOVETYPE_NONE) waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); - remove(e); + delete(e); #endif } @@ -324,14 +321,13 @@ void trigger_push_findtarget(entity this) { entity e = spawn(); setorigin(e, org); - setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + setsize(e, PL_MIN_CONST, PL_MAX_CONST); e.velocity = this.movedir; tracetoss(e, e); waypoint_spawnforteleporter(this, trace_endpos, vlen(trace_endpos - org) / vlen(e.velocity)); - remove(e); + delete(e); } - trigger_push_link(this); defer(this, 0.1, trigger_push_updatelink); #endif } @@ -396,6 +392,8 @@ spawnfunc(trigger_push) this.noise = "misc/jumppad.wav"; precache_sound (this.noise); + trigger_push_link(this); // link it now + // this must be called to spawn the teleport waypoints for bots InitializeEntity(this, trigger_push_findtarget, INITPRIO_FINDTARGET); } @@ -418,6 +416,11 @@ bool target_push_send(entity this, entity to, float sf) return true; } +void target_push_use(entity this, entity actor, entity trigger) +{ + jumppad_push(this, actor); +} + void target_push_link(entity this) { BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); @@ -432,7 +435,18 @@ void target_push_init(entity this) target_push_link(this); } -spawnfunc(target_push) { target_push_init(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); } diff --git a/qcsrc/common/triggers/trigger/jumppads.qh b/qcsrc/common/triggers/trigger/jumppads.qh index 4e8bf1809f..76d244da55 100644 --- a/qcsrc/common/triggers/trigger/jumppads.qh +++ b/qcsrc/common/triggers/trigger/jumppads.qh @@ -1,5 +1,4 @@ -#ifndef T_JUMPPADS_H -#define T_JUMPPADS_H +#pragma once const float PUSH_ONCE = 1; const float PUSH_SILENT = 2; @@ -35,7 +34,7 @@ void trigger_push_use(entity this, entity actor, entity trigger); vector trigger_push_calculatevelocity(vector org, entity tgt, float ht); -void trigger_push_touch(entity this); +void trigger_push_touch(entity this, entity toucher); .vector dest; void trigger_push_findtarget(entity this); @@ -59,4 +58,3 @@ spawnfunc(target_push); spawnfunc(info_notnull); spawnfunc(target_position); #endif -#endif diff --git a/qcsrc/common/triggers/trigger/keylock.qc b/qcsrc/common/triggers/trigger/keylock.qc index 1333978768..bf20d1e973 100644 --- a/qcsrc/common/triggers/trigger/keylock.qc +++ b/qcsrc/common/triggers/trigger/keylock.qc @@ -1,3 +1,4 @@ +#include "keylock.qh" /** * trigger given targets */ @@ -15,21 +16,21 @@ void trigger_keylock_kill(string s) { entity t; for(t = NULL; (t = find(t, targetname, s)); ) - remove(t); + delete(t); } -void trigger_keylock_touch(entity this) +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(other)) + if(!IS_PLAYER(toucher)) return; // check silver key if(this.itemkeys) - key_used = item_keys_usekey(this, other); + key_used = item_keys_usekey(this, toucher); if(this.itemkeys) { @@ -38,16 +39,16 @@ void trigger_keylock_touch(entity this) if(key_used) { // one or more keys were given, but others are still missing! - play2(other, this.noise1); - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_ALSONEED, item_keys_keylist(this.itemkeys)); - other.key_door_messagetime = time + 2; + 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(other.key_door_messagetime <= time) + else if(toucher.key_door_messagetime <= time) { // no keys were given - play2(other, this.noise2); - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_DOOR_LOCKED_NEED, item_keys_keylist(this.itemkeys)); - other.key_door_messagetime = time + 2; + 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 @@ -55,7 +56,7 @@ void trigger_keylock_touch(entity this) if(this.delay <= time || started_delay == true) if(this.target2) { - trigger_keylock_trigger(this, other, this.target2); + trigger_keylock_trigger(this, toucher, this.target2); started_delay = true; this.delay = time + this.wait; } @@ -64,17 +65,17 @@ void trigger_keylock_touch(entity this) { #ifdef SVQC // all keys were given! - play2(other, this.noise); - centerprint(other, this.message); + play2(toucher, this.noise); + centerprint(toucher, this.message); #endif if(this.target) - trigger_keylock_trigger(this, other, this.target); + trigger_keylock_trigger(this, toucher, this.target); if(this.killtarget) trigger_keylock_kill(this.killtarget); - remove(this); + delete(this); } } @@ -121,7 +122,7 @@ message2 and noise2 will be resent to the player every 2 seconds while he is in */ spawnfunc(trigger_keylock) { - if(!this.itemkeys) { remove(this); return; } + if(!this.itemkeys) { delete(this); return; } // set unlocked message if(this.message == "") diff --git a/qcsrc/common/triggers/trigger/magicear.qc b/qcsrc/common/triggers/trigger/magicear.qc index 53fda30e42..065d8c932a 100644 --- a/qcsrc/common/triggers/trigger/magicear.qc +++ b/qcsrc/common/triggers/trigger/magicear.qc @@ -1,3 +1,4 @@ +#include "magicear.qh" #ifdef SVQC float magicear_matched; float W_Tuba_HasPlayed(entity pl, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); diff --git a/qcsrc/common/triggers/trigger/magicear.qh b/qcsrc/common/triggers/trigger/magicear.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/magicear.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/monoflop.qc b/qcsrc/common/triggers/trigger/monoflop.qc index 018e20884b..a67baca16a 100644 --- a/qcsrc/common/triggers/trigger/monoflop.qc +++ b/qcsrc/common/triggers/trigger/monoflop.qc @@ -1,3 +1,4 @@ +#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" diff --git a/qcsrc/common/triggers/trigger/monoflop.qh b/qcsrc/common/triggers/trigger/monoflop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/monoflop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/multi.qc b/qcsrc/common/triggers/trigger/multi.qc index 14d7fda04b..7aa13c13eb 100644 --- a/qcsrc/common/triggers/trigger/multi.qc +++ b/qcsrc/common/triggers/trigger/multi.qc @@ -1,3 +1,4 @@ +#include "multi.qh" // NOTE: also contains trigger_once at bottom #ifdef SVQC @@ -66,34 +67,34 @@ void multi_use(entity this, entity actor, entity trigger) multi_trigger(this); } -void multi_touch(entity this) +void multi_touch(entity this, entity toucher) { if(!(this.spawnflags & 2)) - if(!other.iscreature) + if(!toucher.iscreature) return; if(this.team) - if(((this.spawnflags & 4) == 0) == (this.team != other.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 (other.angles); + 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) - if(IS_PLAYER(other)) // only for players - if(!(other.pressedkeys & this.pressedkeys)) + if(IS_PLAYER(toucher)) // only for players + if(!(toucher.pressedkeys & this.pressedkeys)) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); - this.enemy = other; - this.goalentity = other; + this.enemy = toucher; + this.goalentity = toucher; multi_trigger(this); } diff --git a/qcsrc/common/triggers/trigger/multivibrator.qc b/qcsrc/common/triggers/trigger/multivibrator.qc index 1a1850537d..d946efe5f1 100644 --- a/qcsrc/common/triggers/trigger/multivibrator.qc +++ b/qcsrc/common/triggers/trigger/multivibrator.qc @@ -1,3 +1,4 @@ +#include "multivibrator.qh" #ifdef SVQC void multivibrator_send(entity this) { diff --git a/qcsrc/common/triggers/trigger/multivibrator.qh b/qcsrc/common/triggers/trigger/multivibrator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/multivibrator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay.qc b/qcsrc/common/triggers/trigger/relay.qc index 1df446ba32..794f4dc112 100644 --- a/qcsrc/common/triggers/trigger/relay.qc +++ b/qcsrc/common/triggers/trigger/relay.qc @@ -1,3 +1,4 @@ +#include "relay.qh" #ifdef SVQC /*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. diff --git a/qcsrc/common/triggers/trigger/relay.qh b/qcsrc/common/triggers/trigger/relay.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_activators.qc b/qcsrc/common/triggers/trigger/relay_activators.qc index bbb49d0e5c..d713a05837 100644 --- a/qcsrc/common/triggers/trigger/relay_activators.qc +++ b/qcsrc/common/triggers/trigger/relay_activators.qc @@ -1,3 +1,4 @@ +#include "relay_activators.qh" #ifdef SVQC void relay_activators_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_activators.qh b/qcsrc/common/triggers/trigger/relay_activators.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_activators.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_if.qc b/qcsrc/common/triggers/trigger/relay_if.qc index ea90a06cb7..728252c704 100644 --- a/qcsrc/common/triggers/trigger/relay_if.qc +++ b/qcsrc/common/triggers/trigger/relay_if.qc @@ -1,3 +1,4 @@ +#include "relay_if.qh" #ifdef SVQC void trigger_relay_if_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_if.qh b/qcsrc/common/triggers/trigger/relay_if.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_if.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qc b/qcsrc/common/triggers/trigger/relay_teamcheck.qc index 2972d32bbf..4f9dab7fd1 100644 --- a/qcsrc/common/triggers/trigger/relay_teamcheck.qc +++ b/qcsrc/common/triggers/trigger/relay_teamcheck.qc @@ -1,3 +1,4 @@ +#include "relay_teamcheck.qh" #ifdef SVQC void trigger_relay_teamcheck_use(entity this, entity actor, entity trigger) { diff --git a/qcsrc/common/triggers/trigger/relay_teamcheck.qh b/qcsrc/common/triggers/trigger/relay_teamcheck.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/relay_teamcheck.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/secret.qc b/qcsrc/common/triggers/trigger/secret.qc index bf4178beae..e6e35c295a 100644 --- a/qcsrc/common/triggers/trigger/secret.qc +++ b/qcsrc/common/triggers/trigger/secret.qc @@ -1,9 +1,9 @@ +#include "secret.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include #include - #include "secret.qh" #endif #ifdef SVQC @@ -17,10 +17,10 @@ void secrets_setstatus(entity this) /** * A secret has been found (maybe :P) */ -void trigger_secret_touch(entity this) +void trigger_secret_touch(entity this, entity toucher) { // only a player can trigger this - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; // update secrets found counter @@ -29,12 +29,12 @@ void trigger_secret_touch(entity this) //print(ftos(secret_counter.count), "\n"); // centerprint message (multi_touch() doesn't always call centerprint()) - centerprint(other, this.message); + centerprint(toucher, this.message); this.message = ""; // handle normal trigger features - multi_touch(this); - remove(this); + multi_touch(this, toucher); + delete(this); } /*QUAKED trigger_secret (.5 .5 .5) ? diff --git a/qcsrc/common/triggers/trigger/secret.qh b/qcsrc/common/triggers/trigger/secret.qh index c2a155ce72..e483316a40 100644 --- a/qcsrc/common/triggers/trigger/secret.qh +++ b/qcsrc/common/triggers/trigger/secret.qh @@ -1,5 +1,4 @@ -#ifndef SECRET_H -#define SECRET_H +#pragma once #ifdef SVQC /** @@ -21,4 +20,3 @@ float secrets_found; */ void secrets_setstatus(entity this); #endif -#endif diff --git a/qcsrc/common/triggers/trigger/swamp.qc b/qcsrc/common/triggers/trigger/swamp.qc index fb792b94bb..71c5247c74 100644 --- a/qcsrc/common/triggers/trigger/swamp.qc +++ b/qcsrc/common/triggers/trigger/swamp.qc @@ -1,8 +1,9 @@ +#include "swamp.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include - #include + #include #include #include #endif @@ -22,7 +23,7 @@ #ifdef SVQC spawnfunc(trigger_swamp); #endif -void swamp_touch(entity this); +void swamp_touch(entity this, entity toucher); void swampslug_think(entity this); @@ -45,7 +46,7 @@ void swampslug_think(entity this) if(this.health <= 0) { this.owner.in_swamp = 0; - remove(this); + delete(this); //centerprint(this.owner,"Killing slug...\n"); return; } @@ -54,42 +55,42 @@ void swampslug_think(entity this) // 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, other.origin, '0 0 0'); + Damage (this.owner, this, this, this.dmg, DEATH_SWAMP.m_id, this.owner.origin, '0 0 0'); #endif this.nextthink = time + this.swamp_interval; } -void swamp_touch(entity this) +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(other) || IS_DEAD(other)) + if(!IS_PLAYER(toucher) || IS_DEAD(toucher)) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); // Chech if player alredy got a swampslug. - if(other.in_swamp != 1) + if(toucher.in_swamp != 1) { // If not attach one. - //centerprint(other,"Entering swamp!\n"); - other.swampslug = spawn(); - other.swampslug.health = 2; - setthink(other.swampslug, swampslug_think); - other.swampslug.nextthink = time; - other.swampslug.owner = other; - other.swampslug.dmg = this.dmg; - other.swampslug.swamp_interval = this.swamp_interval; - other.swamp_slowdown = this.swamp_slowdown; - other.in_swamp = 1; + //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; } - //other.in_swamp = 1; + //toucher.in_swamp = 1; //Revitalize players swampslug - other.swampslug.health = 2; + toucher.swampslug.health = 2; } REGISTER_NET_LINKED(ENT_CLIENT_SWAMP) diff --git a/qcsrc/common/triggers/trigger/swamp.qh b/qcsrc/common/triggers/trigger/swamp.qh index 7ae50bac42..f4df98378d 100644 --- a/qcsrc/common/triggers/trigger/swamp.qh +++ b/qcsrc/common/triggers/trigger/swamp.qh @@ -1,5 +1,4 @@ -#ifndef TRIGGER_SWAMP_H -#define TRIGGER_SWAMP_H +#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 (!?) @@ -7,5 +6,3 @@ .float in_swamp; // bool .entity swampslug; // Uses this to release from swamp ("untouch" fix) - -#endif diff --git a/qcsrc/common/triggers/trigger/teleport.qc b/qcsrc/common/triggers/trigger/teleport.qc index 8ee6b7a697..c3de654609 100644 --- a/qcsrc/common/triggers/trigger/teleport.qc +++ b/qcsrc/common/triggers/trigger/teleport.qc @@ -1,3 +1,4 @@ +#include "teleport.qh" REGISTER_NET_LINKED(ENT_CLIENT_TRIGGER_TELEPORT) #ifdef SVQC @@ -11,49 +12,49 @@ void trigger_teleport_use(entity this, entity actor, entity trigger) } #endif -void Teleport_Touch (entity this) +void Teleport_Touch(entity this, entity toucher) { if (this.active != ACTIVE_ACTIVE) return; #ifdef SVQC - if (!other.teleportable) + if (!toucher.teleportable) return; - if(other.vehicle) - if(!other.vehicle.teleportable) + if(toucher.vehicle) + if(!toucher.vehicle.teleportable) return; - if(IS_TURRET(other)) + if(IS_TURRET(toucher)) return; #elif defined(CSQC) - if(!IS_PLAYER(other)) + if(!IS_PLAYER(toucher)) return; #endif - if(IS_DEAD(other)) + if(IS_DEAD(toucher)) return; if(this.team) - if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, other))) + if(((this.spawnflags & 4) == 0) == (DIFF_TEAM(this, toucher))) return; - EXACTTRIGGER_TOUCH; + EXACTTRIGGER_TOUCH(this, toucher); #ifdef SVQC - if(IS_PLAYER(other)) - RemoveGrapplingHook(other); + if(IS_PLAYER(toucher)) + RemoveGrapplingHook(toucher); #endif entity e; - e = Simple_TeleportPlayer(this, other); + e = Simple_TeleportPlayer(this, toucher); #ifdef SVQC string s = this.target; this.target = string_null; - SUB_UseTargets(this, other, other); // TODO: should we be using other for trigger too? + SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for trigger too? if (!this.target) this.target = s; - SUB_UseTargets(e, other, other); + SUB_UseTargets(e, toucher, toucher); #endif } @@ -98,6 +99,8 @@ spawnfunc(trigger_teleport) return; } + IL_PUSH(g_teleporters, this); + this.teleport_next = teleport_first; teleport_first = this; } @@ -105,6 +108,8 @@ spawnfunc(trigger_teleport) 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(); @@ -114,7 +119,7 @@ NET_HANDLE(ENT_CLIENT_TRIGGER_TELEPORT, bool isnew) this.entremove = trigger_remove_generic; this.solid = SOLID_TRIGGER; - //this.move_touch = trigger_push_touch; + //settouch(this, trigger_push_touch); this.move_time = time; defer(this, 0.25, teleport_findtarget); diff --git a/qcsrc/common/triggers/trigger/teleport.qh b/qcsrc/common/triggers/trigger/teleport.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/triggers/trigger/teleport.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/triggers/trigger/viewloc.qc b/qcsrc/common/triggers/trigger/viewloc.qc index 9d675bbc73..a600263542 100644 --- a/qcsrc/common/triggers/trigger/viewloc.qc +++ b/qcsrc/common/triggers/trigger/viewloc.qc @@ -1,3 +1,4 @@ +#include "viewloc.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) @@ -71,7 +72,7 @@ void viewloc_init(entity this) break; } - if(!this.enemy) { LOG_INFO("^1FAIL!\n"); remove(this); return; } + if(!this.enemy) { LOG_INFO("^1FAIL!\n"); delete(this); return; } if(!this.goalentity) this.goalentity = this.enemy; // make them match so CSQC knows what to do @@ -85,7 +86,7 @@ void viewloc_init(entity this) spawnfunc(trigger_viewlocation) { // we won't check target2 here yet, as it may not even need to exist - if(this.target == "") { LOG_INFO("^1FAIL!\n"); remove(this); return; } + if(this.target == "") { LOG_INFO("^1FAIL!\n"); delete(this); return; } EXACTTRIGGER_INIT; InitializeEntity(this, viewloc_init, INITPRIO_FINDTARGET); @@ -101,9 +102,9 @@ bool viewloc_send(entity this, entity to, int sf) WriteCoord(MSG_ENTITY, this.origin_y); WriteCoord(MSG_ENTITY, this.origin_z); - WriteCoord(MSG_ENTITY, this.angles_x); - WriteCoord(MSG_ENTITY, this.angles_y); - WriteCoord(MSG_ENTITY, this.angles_z); + WriteAngle(MSG_ENTITY, this.angles_x); + WriteAngle(MSG_ENTITY, this.angles_y); + WriteAngle(MSG_ENTITY, this.angles_z); return true; } @@ -175,9 +176,9 @@ NET_HANDLE(ENT_CLIENT_VIEWLOC, bool isnew) this.origin_z = ReadCoord(); setorigin(this, this.origin); - this.movedir_x = ReadCoord(); - this.movedir_y = ReadCoord(); - this.movedir_z = ReadCoord(); + this.movedir_x = ReadAngle(); + this.movedir_y = ReadAngle(); + this.movedir_z = ReadAngle(); return = true; diff --git a/qcsrc/common/triggers/trigger/viewloc.qh b/qcsrc/common/triggers/trigger/viewloc.qh index 881197a3b8..167fc218f7 100644 --- a/qcsrc/common/triggers/trigger/viewloc.qh +++ b/qcsrc/common/triggers/trigger/viewloc.qh @@ -1,5 +1,4 @@ -#ifndef T_VIEWLOC_H -#define T_VIEWLOC_H +#pragma once .entity viewloc; @@ -8,5 +7,3 @@ .entity enemy; .vector movedir; #endif - -#endif diff --git a/qcsrc/common/triggers/triggers.qc b/qcsrc/common/triggers/triggers.qc index a9050357f6..1f5139896d 100644 --- a/qcsrc/common/triggers/triggers.qc +++ b/qcsrc/common/triggers/triggers.qc @@ -1,3 +1,4 @@ +#include "triggers.qh" void SUB_DontUseTargets(entity this, entity actor, entity trigger) { } void SUB_UseTargets(entity this, entity actor, entity trigger); @@ -5,7 +6,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger); void DelayThink(entity this) { SUB_UseTargets (this, this.enemy, NULL); - remove(this); + delete(this); } void FixSize(entity e) @@ -163,6 +164,7 @@ void trigger_remove_generic(entity this) } #endif + /* ============================== SUB_UseTargets @@ -182,7 +184,8 @@ match (string)this.target and call their .use function ============================== */ -void SUB_UseTargets(entity this, entity actor, entity trigger) + +void SUB_UseTargets_Ex(entity this, entity actor, entity trigger, bool preventReuse) { // // check for a delay @@ -200,6 +203,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) t.target2 = this.target2; t.target3 = this.target3; t.target4 = this.target4; + t.antiwall_flag = this.antiwall_flag; return; } @@ -225,7 +229,7 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) if (s != "") { for(entity t = NULL; (t = find(t, targetname, s)); ) - remove(t); + delete(t); } #endif @@ -253,11 +257,11 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) int aw_flag = this.antiwall_flag; for(entity t = NULL; (t = find(t, targetname, s)); ) { - if(t.use) + if(t.use && (t.sub_target_used != time || !preventReuse)) { if(this.target_random) { - RandomSelection_Add(t, 0, string_null, 1, 0); + RandomSelection_AddEnt(t, 1, 0); } else { @@ -265,6 +269,8 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) t.antiwall_flag = aw_flag; t.use(t, actor, this); + if(preventReuse) + t.sub_target_used = time; } } } @@ -272,9 +278,16 @@ void SUB_UseTargets(entity this, entity actor, entity trigger) } 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); } + void SUB_UseTargets_self(entity this) { SUB_UseTargets(this, NULL, NULL); diff --git a/qcsrc/common/triggers/triggers.qh b/qcsrc/common/triggers/triggers.qh index 82c0916c19..2b8274f4b8 100644 --- a/qcsrc/common/triggers/triggers.qh +++ b/qcsrc/common/triggers/triggers.qh @@ -1,5 +1,4 @@ -#ifndef TRIGGERS_H -#define TRIGGERS_H +#pragma once const float SF_TRIGGER_INIT = 1; const float SF_TRIGGER_UPDATE = 2; @@ -28,8 +27,12 @@ string trigger_magicear_processmessage_forallears(entity source, float teamsay, 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; @@ -50,5 +53,3 @@ const int ACTIVE_IDLE = 2; const int ACTIVE_BUSY = 2; const int ACTIVE_TOGGLE = 3; #endif - -#endif diff --git a/qcsrc/common/turrets/_all.inc b/qcsrc/common/turrets/_all.inc new file mode 100644 index 0000000000..8bc63f720a --- /dev/null +++ b/qcsrc/common/turrets/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "_mod.inc" diff --git a/qcsrc/common/turrets/_all.qh b/qcsrc/common/turrets/_all.qh new file mode 100644 index 0000000000..947026dd59 --- /dev/null +++ b/qcsrc/common/turrets/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "_mod.qh" diff --git a/qcsrc/common/turrets/_mod.inc b/qcsrc/common/turrets/_mod.inc index 40c3114bab..8fff9535c5 100644 --- a/qcsrc/common/turrets/_mod.inc +++ b/qcsrc/common/turrets/_mod.inc @@ -1,8 +1,13 @@ // generated file; do not modify #include #include -#include #include -#include #include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/turrets/_mod.qh b/qcsrc/common/turrets/_mod.qh index 6da539e852..06978f1d41 100644 --- a/qcsrc/common/turrets/_mod.qh +++ b/qcsrc/common/turrets/_mod.qh @@ -1,8 +1,13 @@ // generated file; do not modify #include #include -#include #include -#include #include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include diff --git a/qcsrc/common/turrets/all.qh b/qcsrc/common/turrets/all.qh index a993b9218a..1a77e98912 100644 --- a/qcsrc/common/turrets/all.qh +++ b/qcsrc/common/turrets/all.qh @@ -1,7 +1,6 @@ -#ifndef TURRETS_ALL_H -#define TURRETS_ALL_H +#pragma once -#include +#include #include "config.qh" #include "turret.qh" @@ -75,5 +74,3 @@ const int TUR_FIRST = 1; REGISTER_TURRET(Null, NEW(Turret)); #include "turret/_mod.inc" - -#endif diff --git a/qcsrc/common/turrets/checkpoint.qc b/qcsrc/common/turrets/checkpoint.qc index dc48fd1156..6c246a75b8 100644 --- a/qcsrc/common/turrets/checkpoint.qc +++ b/qcsrc/common/turrets/checkpoint.qc @@ -1,3 +1,4 @@ +#include "checkpoint.qh" /** turret_checkpoint **/ @@ -56,7 +57,7 @@ void turret_checkpoint_init(entity this) { this.enemy = find(NULL, targetname, this.target); if(this.enemy == NULL) - LOG_TRACE("A turret_checkpoint faild to find its target!\n"); + LOG_TRACE("A turret_checkpoint faild to find its target!"); } //setthink(this, turret_checkpoint_think); //this.nextthink = time + tc_acum + 0.25; diff --git a/qcsrc/common/turrets/checkpoint.qh b/qcsrc/common/turrets/checkpoint.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/checkpoint.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index 55719cb3f5..c21e327d44 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -1,6 +1,7 @@ +#include "cl_turrets.qh" void turret_remove(entity this) { - remove(this.tur_head); + delete(this.tur_head); //remove(this.enemy); this.tur_head = NULL; } @@ -34,7 +35,7 @@ void turret_draw(entity this) if(dt <= 0) return; - this.tur_head.angles += dt * this.tur_head.move_avelocity; + this.tur_head.angles += dt * this.tur_head.avelocity; if (this.health < 127) { @@ -155,17 +156,14 @@ void turret_draw2d(entity this) o_z = 0; - float edgedistance_min, crosshairdistance; - edgedistance_min = min((o_y - (vid_conheight * waypointsprite_edgeoffset_top)), + float edgedistance_min = min((o.y - (vid_conheight * waypointsprite_edgeoffset_top)), (o_x - (vid_conwidth * waypointsprite_edgeoffset_left)), (vid_conwidth - (vid_conwidth * waypointsprite_edgeoffset_right)) - o_x, (vid_conheight - (vid_conheight * waypointsprite_edgeoffset_bottom)) - o_y); - float vidscale = max(vid_conwidth / vid_width, vid_conheight / vid_height); + float crosshairdistance = sqrt( pow(o.x - vid_conwidth/2, 2) + pow(o.y - vid_conheight/2, 2) ); - crosshairdistance = sqrt( pow(o_x - vid_conwidth/2, 2) + pow(o_y - vid_conheight/2, 2) ); - - t = waypointsprite_scale * vidscale; + t = waypointsprite_scale; a *= waypointsprite_alpha; { @@ -202,7 +200,7 @@ void turret_draw2d(entity this) ); } -void turret_construct(entity this) +void turret_construct(entity this, bool isnew) { entity tur = get_turretinfo(this.m_id); @@ -224,14 +222,14 @@ void turret_construct(entity this) this.tur_head.classname = "turret_head"; this.tur_head.owner = this; - this.tur_head.move_movetype = MOVETYPE_NOCLIP; - this.move_movetype = MOVETYPE_NOCLIP; + set_movetype(this.tur_head, MOVETYPE_NOCLIP); + set_movetype(this, MOVETYPE_NOCLIP); this.tur_head.angles = this.angles; this.health = 255; this.solid = SOLID_BBOX; this.tur_head.solid = SOLID_NOT; - this.movetype = MOVETYPE_NOCLIP; - this.tur_head.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); + set_movetype(this.tur_head, MOVETYPE_NOCLIP); this.draw = turret_draw; this.entremove = turret_remove; this.drawmask = MASK_NORMAL; @@ -242,6 +240,12 @@ void turret_construct(entity this) this.teamradar_color = '1 0 0'; this.alpha = 1; + if(isnew) + { + IL_PUSH(g_drawables, this); + IL_PUSH(g_drawables_2d, this); + } + tur.tr_setup(tur, this); } @@ -258,14 +262,14 @@ void turret_gib_draw(entity this) if(time >= this.nextthink) { turret_gibboom(this); - remove(this); + delete(this); } } else { this.alpha = bound(0, this.nextthink - time, 1); if(this.alpha < ALPHA_MIN_VISIBLE) - remove(this); + delete(this); } } @@ -303,11 +307,10 @@ entity turret_gibtoss(string _model, vector _from, vector _to, vector _cmod, flo gib.nextthink = time + autocvar_cl_gibs_lifetime * (1 + prandom() * 0.15); gib.gravity = 1; - gib.move_movetype = MOVETYPE_BOUNCE; - gib.move_origin = _from; + set_movetype(gib, MOVETYPE_BOUNCE); setorigin(gib, _from); - gib.move_velocity = _to; - gib.move_avelocity = prandomvec() * 32; + gib.velocity = _to; + gib.avelocity = prandomvec() * 32; gib.move_time = time; gib.damageforcescale = 1; @@ -341,9 +344,9 @@ void turret_die(entity this) entity headgib = turret_gibtoss((get_turretinfo(this.m_id)).head_model, this.origin + '0 0 32', '0 0 200' + randomvec() * 200, '-1 -1 -1', true); if(headgib) { - headgib.angles = headgib.move_angles = this.tur_head.angles; - headgib.avelocity = headgib.move_avelocity = this.tur_head.move_avelocity + randomvec() * 45; - headgib.avelocity_y = headgib.move_avelocity_y = headgib.move_avelocity_y * 5; + headgib.angles = this.tur_head.angles; + headgib.avelocity = this.tur_head.avelocity + randomvec() * 45; + headgib.avelocity_y = headgib.avelocity_y * 5; headgib.gravity = 0.5; } } @@ -370,7 +373,7 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) this.angles_x = ReadAngle(); this.angles_y = ReadAngle(); - turret_construct(this); + turret_construct(this, isnew); this.colormap = 1024; this.glowmod = '0 1 1'; this.tur_head.colormap = this.colormap; @@ -382,10 +385,9 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) if(this.tur_head == NULL) // aparenly this can happpen before TNSF_SETUP. great. this.tur_head = spawn(); - this.tur_head.move_angles_x = ReadShort(); - this.tur_head.move_angles_y = ReadShort(); - //this.tur_head.angles = this.angles + this.tur_head.move_angles; - this.tur_head.angles = this.tur_head.move_angles; + this.tur_head.angles_x = ReadShort(); + this.tur_head.angles_y = ReadShort(); + //this.tur_head.angles = this.angles + this.tur_head.angles; } if(sf & TNSF_AVEL) @@ -393,8 +395,8 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) if(this.tur_head == NULL) // aparenly this can happpen before TNSF_SETUP. great. this.tur_head = spawn(); - this.tur_head.move_avelocity_x = ReadShort(); - this.tur_head.move_avelocity_y = ReadShort(); + this.tur_head.avelocity_x = ReadShort(); + this.tur_head.avelocity_y = ReadShort(); } if(sf & TNSF_MOVE) @@ -408,11 +410,9 @@ NET_HANDLE(ENT_CLIENT_TURRET, bool isnew) this.velocity_y = ReadShort(); this.velocity_z = ReadShort(); - this.move_angles_y = ReadShort(); + this.angles_y = ReadShort(); this.move_time = time; - this.move_velocity = this.velocity; - this.move_origin = this.origin; } if(sf & TNSF_ANIM) diff --git a/qcsrc/common/turrets/cl_turrets.qh b/qcsrc/common/turrets/cl_turrets.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/cl_turrets.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/config.qc b/qcsrc/common/turrets/config.qc index f5989b48d1..68cac08464 100644 --- a/qcsrc/common/turrets/config.qc +++ b/qcsrc/common/turrets/config.qc @@ -1,3 +1,4 @@ +#include "config.qh" // ========================== // Turret Config Generator // ========================== @@ -27,12 +28,12 @@ float T_Config_Queue_Compare(float root, float child, entity pass) void Dump_Turret_Settings() { - float x, totalsettings = 0; + int totalsettings = 0; FOREACH(Turrets, it != TUR_Null, { // step 1: clear the queue TUR_CONFIG_COUNT = 0; - for(x = 0; x <= MAX_TUR_CONFIG; ++x) - { tur_config_queue[x] = string_null; } + for(int j = 0; j <= MAX_TUR_CONFIG; ++j) + { tur_config_queue[j] = string_null; } // step 2: build new queue it.tr_config(it); @@ -42,8 +43,8 @@ void Dump_Turret_Settings() // step 4: write queue TUR_CONFIG_WRITETOFILE(sprintf("// {{{ #%d: %s\n", i, it.turret_name)) - for(x = 0; x <= TUR_CONFIG_COUNT; ++x) - { TUR_CONFIG_WRITETOFILE(tur_config_queue[x]) } + for(int j = 0; j <= TUR_CONFIG_COUNT; ++j) + { TUR_CONFIG_WRITETOFILE(tur_config_queue[j]) } TUR_CONFIG_WRITETOFILE("// }}}\n") // step 5: debug info @@ -53,8 +54,8 @@ void Dump_Turret_Settings() // clear queue now that we're finished TUR_CONFIG_COUNT = 0; - for(x = 0; x <= MAX_TUR_CONFIG; ++x) - { tur_config_queue[x] = string_null; } + for(int j = 0; j <= MAX_TUR_CONFIG; ++j) + { tur_config_queue[j] = string_null; } // extra information LOG_INFO(sprintf("Totals: %d turrets, %d settings\n", (Turrets_COUNT - 1), totalsettings)); diff --git a/qcsrc/common/turrets/config.qh b/qcsrc/common/turrets/config.qh index bb2a81b847..caa68a8648 100644 --- a/qcsrc/common/turrets/config.qh +++ b/qcsrc/common/turrets/config.qh @@ -1,5 +1,4 @@ -#ifndef TURRETS_CONFIG_H -#define TURRETS_CONFIG_H +#pragma once #ifdef SVQC @@ -17,5 +16,3 @@ string tur_config_queue[MAX_TUR_CONFIG]; #endif - -#endif diff --git a/qcsrc/common/turrets/sv_turrets.qc b/qcsrc/common/turrets/sv_turrets.qc index 57bb008fcd..c90eaff1fe 100644 --- a/qcsrc/common/turrets/sv_turrets.qc +++ b/qcsrc/common/turrets/sv_turrets.qc @@ -1,3 +1,4 @@ +#include "sv_turrets.qh" #ifdef SVQC #include @@ -32,7 +33,7 @@ vector turret_aim_generic(entity this) if(this.aim_flags & TFL_AIM_ZPREDICT) if(!IS_ONGROUND(this.enemy)) - if(this.enemy.movetype == MOVETYPE_WALK || this.enemy.movetype == MOVETYPE_TOSS || this.enemy.movetype == MOVETYPE_BOUNCE) + if(this.enemy.move_movetype == MOVETYPE_WALK || this.enemy.move_movetype == MOVETYPE_TOSS || this.enemy.move_movetype == MOVETYPE_BOUNCE) { float vz; prep_z = pre_pos_z; @@ -193,8 +194,8 @@ void turret_die(entity this) { tur.tr_death(tur, this); - remove(this.tur_head); - remove(this); + delete(this.tur_head); + delete(this); } else { @@ -440,12 +441,12 @@ void turret_projectile_explode(entity this) #else RadiusDamage (this, this.realowner, this.owner.shot_dmg, 0, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, NULL); #endif - remove(this); + delete(this); } -void turret_projectile_touch(entity this) +void turret_projectile_touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); turret_projectile_explode(this); } @@ -474,9 +475,11 @@ entity turret_projectile(entity actor, Sound _snd, float _size, float _health, f setthink(proj, turret_projectile_explode); settouch(proj, turret_projectile_touch); proj.nextthink = time + 9; - proj.movetype = MOVETYPE_FLYMISSILE; + set_movetype(proj, MOVETYPE_FLYMISSILE); proj.velocity = normalize(actor.tur_shotdir_updated + randomvec() * actor.shot_spread) * actor.shot_speed; - proj.flags = FL_PROJECTILE; + proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.enemy = actor.enemy; proj.totalfrags = _death; PROJECTILE_MAKETRIGGER(proj); @@ -824,7 +827,7 @@ float turret_validate_target(entity e_turret, entity e_target, float validate_fl */ #ifdef TURRET_DEBUG_TARGETSELECT - LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname,"\n"); + LOG_TRACE("Target:",e_target.netname," is a valid target for ",e_turret.netname); #endif return 1; @@ -1174,7 +1177,7 @@ void turret_think(entity this) */ void turret_use(entity this, entity actor, entity trigger) { - LOG_TRACE("Turret ",this.netname, " used by ", actor.classname, "\n"); + LOG_TRACE("Turret ",this.netname, " used by ", actor.classname); this.team = actor.team; @@ -1199,7 +1202,8 @@ void turrets_manager_think(entity this) if (autocvar_g_turrets_reloadcvars == 1) { - FOREACH_ENTITY(IS_TURRET(it), { + IL_EACH(g_turrets, true, + { load_unit_settings(it, true); Turret tur = get_turretinfo(it.m_id); tur.tr_think(tur, it); @@ -1248,6 +1252,8 @@ bool turret_initialize(entity this, Turret tur) // if tur_head exists, we can assume this turret re-spawned if(!this.tur_head) { tur.tr_precache(tur); + IL_PUSH(g_turrets, this); + IL_PUSH(g_bot_targets, this); } entity e = find(NULL, classname, "turret_manager"); @@ -1329,7 +1335,7 @@ bool turret_initialize(entity this, Turret tur) this.ammo_recharge *= this.ticrate; this.solid = SOLID_BBOX; this.takedamage = DAMAGE_AIM; - this.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); this.view_ofs = '0 0 0'; this.turret_firecheckfunc = turret_firecheck; this.event_damage = turret_damage; @@ -1349,7 +1355,7 @@ bool turret_initialize(entity this, Turret tur) this.tur_head.owner = this; this.tur_head.takedamage = DAMAGE_NO; this.tur_head.solid = SOLID_NOT; - this.tur_head.movetype = this.movetype; + set_movetype(this.tur_head, this.move_movetype); if(!this.tur_defend) if(this.target != "") @@ -1358,7 +1364,7 @@ bool turret_initialize(entity this, Turret tur) if (this.tur_defend == NULL) { this.target = ""; - LOG_TRACE("Turret has invalid defendpoint!\n"); + LOG_TRACE("Turret has invalid defendpoint!"); } } diff --git a/qcsrc/common/turrets/sv_turrets.qh b/qcsrc/common/turrets/sv_turrets.qh index 29d08c6290..62759c058f 100644 --- a/qcsrc/common/turrets/sv_turrets.qh +++ b/qcsrc/common/turrets/sv_turrets.qh @@ -1,5 +1,4 @@ -#ifndef SV_TURRETS_H -#define SV_TURRETS_H +#pragma once entity turret_projectile(entity actor, Sound _snd, float _size, float _health, float _death, float _proj_type, float _cull, float _cli_anim); void turret_projectile_explode(entity this); @@ -9,7 +8,6 @@ entity turret_select_target(entity this); // turret fields .float ticrate; // interal ai think rate -.vector aim_idle; // where to aim while idle .entity tur_head; // top part of the turret .entity tur_defend; // defend this entity .vector tur_shotorg; // shot origin @@ -119,5 +117,3 @@ vector tvt_tadv; // turret angle diff vector, updated by a successful call to tu float tvt_thadf; // turret head angle diff float, updated by a successful call to turret_validate_target float tvt_tadf; // turret angle diff float, updated by a successful call to turret_validate_target float tvt_dist; // turret distance, updated by a successful call to turret_validate_target - -#endif diff --git a/qcsrc/common/turrets/targettrigger.qc b/qcsrc/common/turrets/targettrigger.qc index 15dbaec4f0..152a7d6a27 100644 --- a/qcsrc/common/turrets/targettrigger.qc +++ b/qcsrc/common/turrets/targettrigger.qc @@ -1,13 +1,15 @@ +#include "targettrigger.qh" spawnfunc(turret_targettrigger); -void turret_targettrigger_touch(entity this); +void turret_targettrigger_touch(entity this, entity toucher); -void turret_targettrigger_touch(entity this) +void turret_targettrigger_touch(entity this, entity toucher) { if (this.cnt > time) return; - FOREACH_ENTITY_STRING_ORDERED(targetname, this.target, { + IL_EACH(g_turrets, it.targetname == this.target, + { if (!(it.turret_flags & TUR_FLAG_RECIEVETARGETS)) continue; if (!it.turret_addtarget) continue; - it.turret_addtarget(it, other, this); + it.turret_addtarget(it, toucher, this); }); this.cnt = time + 0.5; } @@ -16,7 +18,7 @@ void turret_targettrigger_touch(entity this) */ spawnfunc(turret_targettrigger) { - if(!autocvar_g_turrets) { remove(this); return; } + if(!autocvar_g_turrets) { delete(this); return; } InitTrigger(this); diff --git a/qcsrc/common/turrets/targettrigger.qh b/qcsrc/common/turrets/targettrigger.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/targettrigger.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/turret.qh b/qcsrc/common/turrets/turret.qh index a66952833a..8e52397028 100644 --- a/qcsrc/common/turrets/turret.qh +++ b/qcsrc/common/turrets/turret.qh @@ -1,27 +1,26 @@ -#ifndef TURRET_H -#define TURRET_H +#pragma once -#include +#include CLASS(Turret, Object) - ATTRIB(Turret, m_id, int, 0) + ATTRIB(Turret, m_id, int, 0); /** short name */ - ATTRIB(Turret, netname, string, string_null) + ATTRIB(Turret, netname, string); /** human readable name */ - ATTRIB(Turret, turret_name, string, _("Turret")) + ATTRIB(Turret, turret_name, string, _("Turret")); /** currently a copy of the model */ - ATTRIB(Turret, mdl, string, string_null) + ATTRIB(Turret, mdl, string); /** full name of model */ - ATTRIB(Turret, model, string, string_null) + ATTRIB(Turret, model, string); /** full name of tur_head model */ - ATTRIB(Turret, head_model, string, string_null) + ATTRIB(Turret, head_model, string); - ATTRIB(Turret, spawnflags, int, 0) + ATTRIB(Turret, spawnflags, int, 0); /** turret hitbox size */ - ATTRIB(Turret, mins, vector, '-0 -0 -0') + ATTRIB(Turret, mins, vector, '-0 -0 -0'); /** turret hitbox size */ - ATTRIB(Turret, maxs, vector, '0 0 0') + ATTRIB(Turret, maxs, vector, '0 0 0'); METHOD(Turret, display, void(Turret this, void(string name, string icon) returns)) { returns(this.turret_name, string_null); @@ -42,7 +41,7 @@ CLASS(Turret, Object) METHOD(Turret, tr_precache, void(Turret this)) { } - ATTRIB(Turret, m_weapon, Weapon, WEP_Null) + ATTRIB(Turret, m_weapon, Weapon); #ifdef SVQC /** (SERVER) called when turret attacks */ METHOD(Turret, tr_attack, void(Turret this, entity it)) { @@ -176,5 +175,3 @@ const int TNSF_MOVE = 64; const int TNSF_ANIM = 128; const int TNSF_FULL_UPDATE = 16777215; - -#endif diff --git a/qcsrc/common/turrets/turret/ewheel.qc b/qcsrc/common/turrets/turret/ewheel.qc index a72a9f016c..0a633c7844 100644 --- a/qcsrc/common/turrets/turret/ewheel.qc +++ b/qcsrc/common/turrets/turret/ewheel.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_EWHEEL_H -#define TURRET_EWHEEL_H - -//#define EWHEEL_FANCYPATH - -#include "ewheel_weapon.qh" - -CLASS(EWheel, Turret) -/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM); -/* mins */ ATTRIB(EWheel, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(EWheel, maxs, vector, '32 32 48'); -/* modelname */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3"); -/* model */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3")); -/* netname */ ATTRIB(EWheel, netname, string, "ewheel"); -/* fullname */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret")); - ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL); -ENDCLASS(EWheel) -REGISTER_TURRET(EWHEEL, NEW(EWheel)); - -#endif +#include "ewheel.qh" #ifdef IMPLEMENTATION @@ -133,7 +113,7 @@ void ewheel_move_idle(entity this) movelib_brake_simple(this, (autocvar_g_turrets_unit_ewheel_speed_stop)); } -spawnfunc(turret_ewheel) { if(!turret_initialize(this, TUR_EWHEEL)) remove(this); } +spawnfunc(turret_ewheel) { if(!turret_initialize(this, TUR_EWHEEL)) delete(this); } METHOD(EWheel, tr_think, void(EWheel thistur, entity it)) { @@ -184,7 +164,7 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it)) { entity e; - if(it.movetype == MOVETYPE_WALK) + if(it.move_movetype == MOVETYPE_WALK) { it.velocity = '0 0 0'; it.enemy = NULL; @@ -196,12 +176,12 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it)) e = find(NULL, targetname, it.target); if (!e) { - LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!\n"); + LOG_TRACE("Initital waypoint for ewheel does NOT exsist, fix your map!"); it.target = ""; } if (e.classname != "turret_checkpoint") - LOG_TRACE("Warning: not a turrret path\n"); + LOG_TRACE("Warning: not a turrret path"); else { @@ -218,7 +198,8 @@ METHOD(EWheel, tr_setup, void(EWheel this, entity it)) it.iscreature = true; it.teleportable = TELEPORT_NORMAL; it.damagedbycontents = true; - it.movetype = MOVETYPE_WALK; + IL_PUSH(g_damagedbycontents, it); + set_movetype(it, MOVETYPE_WALK); it.solid = SOLID_SLIDEBOX; it.takedamage = DAMAGE_AIM; it.idle_aim = '0 0 0'; @@ -247,8 +228,7 @@ void ewheel_draw(entity this) fixedmakevectors(this.angles); setorigin(this, this.origin + this.velocity * dt); - this.tur_head.angles += dt * this.tur_head.move_avelocity; - this.angles_y = this.move_angles_y; + this.tur_head.angles += dt * this.tur_head.avelocity; if (this.health < 127) if(random() < 0.05) @@ -258,9 +238,7 @@ void ewheel_draw(entity this) METHOD(EWheel, tr_setup, void(EWheel this, entity it)) { it.gravity = 1; - it.movetype = MOVETYPE_BOUNCE; - it.move_movetype = MOVETYPE_BOUNCE; - it.move_origin = it.origin; + set_movetype(it, MOVETYPE_BOUNCE); it.move_time = time; it.draw = ewheel_draw; } diff --git a/qcsrc/common/turrets/turret/ewheel.qh b/qcsrc/common/turrets/turret/ewheel.qh new file mode 100644 index 0000000000..b34adb2fe3 --- /dev/null +++ b/qcsrc/common/turrets/turret/ewheel.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define EWHEEL_FANCYPATH + +#include "ewheel_weapon.qh" + +CLASS(EWheel, Turret) +/* spawnflags */ ATTRIB(EWheel, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE | TUR_FLAG_ROAM); +/* mins */ ATTRIB(EWheel, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(EWheel, maxs, vector, '32 32 48'); +/* modelname */ ATTRIB(EWheel, mdl, string, "ewheel-base2.md3"); +/* model */ ATTRIB_STRZONE(EWheel, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(EWheel, head_model, string, strcat("models/turrets/", "ewheel-gun1.md3")); +/* netname */ ATTRIB(EWheel, netname, string, "ewheel"); +/* fullname */ ATTRIB(EWheel, turret_name, string, _("eWheel Turret")); + ATTRIB(EWheel, m_weapon, Weapon, WEP_EWHEEL); +ENDCLASS(EWheel) +REGISTER_TURRET(EWHEEL, NEW(EWheel)); diff --git a/qcsrc/common/turrets/turret/ewheel_weapon.qc b/qcsrc/common/turrets/turret/ewheel_weapon.qc index 17df7dac04..e77b534022 100644 --- a/qcsrc/common/turrets/turret/ewheel_weapon.qc +++ b/qcsrc/common/turrets/turret/ewheel_weapon.qc @@ -12,7 +12,7 @@ METHOD(EWheelAttack, wr_think, void(entity thiswep, entity actor, .entity weapon if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_EWheelAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_EWheelAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; diff --git a/qcsrc/common/turrets/turret/flac.qc b/qcsrc/common/turrets/turret/flac.qc index 64b32d4a7c..ab6e5f5cb6 100644 --- a/qcsrc/common/turrets/turret/flac.qc +++ b/qcsrc/common/turrets/turret/flac.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_FLAC_H -#define TURRET_FLAC_H - -#include "flac_weapon.qh" - -CLASS(Flac, Turret) -/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(Flac, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(Flac, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(Flac, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3")); -/* netname */ ATTRIB(Flac, netname, string, "flac"); -/* fullname */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon")); - ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC); -ENDCLASS(Flac) -REGISTER_TURRET(FLAC, NEW(Flac)); - -#endif +#include "flac.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_flac) { if (!turret_initialize(this, TUR_FLAC)) remove(this); } +spawnfunc(turret_flac) { if (!turret_initialize(this, TUR_FLAC)) delete(this); } METHOD(Flac, tr_setup, void(Flac this, entity it)) { diff --git a/qcsrc/common/turrets/turret/flac.qh b/qcsrc/common/turrets/turret/flac.qh new file mode 100644 index 0000000000..d53422c602 --- /dev/null +++ b/qcsrc/common/turrets/turret/flac.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "flac_weapon.qh" + +CLASS(Flac, Turret) +/* spawnflags */ ATTRIB(Flac, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(Flac, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(Flac, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(Flac, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(Flac, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(Flac, head_model, string, strcat("models/turrets/", "flac.md3")); +/* netname */ ATTRIB(Flac, netname, string, "flac"); +/* fullname */ ATTRIB(Flac, turret_name, string, _("FLAC Cannon")); + ATTRIB(Flac, m_weapon, Weapon, WEP_FLAC); +ENDCLASS(Flac) +REGISTER_TURRET(FLAC, NEW(Flac)); diff --git a/qcsrc/common/turrets/turret/flac_weapon.qc b/qcsrc/common/turrets/turret/flac_weapon.qc index 3037f65d0d..357bba975d 100644 --- a/qcsrc/common/turrets/turret/flac_weapon.qc +++ b/qcsrc/common/turrets/turret/flac_weapon.qc @@ -12,7 +12,7 @@ METHOD(FlacAttack, wr_think, void(entity thiswep, entity actor, .entity weaponen if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_FlacAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_FlacAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; @@ -49,7 +49,7 @@ void turret_flac_projectile_think_explode(entity this) #else RadiusDamage (this, this.realowner, this.owner.shot_dmg, this.owner.shot_dmg, this.owner.shot_radius, this, NULL, this.owner.shot_force, this.totalfrags, NULL); #endif - remove(this); + delete(this); } #endif diff --git a/qcsrc/common/turrets/turret/fusionreactor.qc b/qcsrc/common/turrets/turret/fusionreactor.qc index 945b35dd0a..163bffb9d7 100644 --- a/qcsrc/common/turrets/turret/fusionreactor.qc +++ b/qcsrc/common/turrets/turret/fusionreactor.qc @@ -1,19 +1,4 @@ -#ifndef TURRET_FUSIONREACTOR_H -#define TURRET_FUSIONREACTOR_H - -CLASS(FusionReactor, Turret) -/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE); -/* mins */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0'); -/* maxs */ ATTRIB(FusionReactor, maxs, vector, '34 34 90'); -/* modelname */ ATTRIB(FusionReactor, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3")); -/* netname */ ATTRIB(FusionReactor, netname, string, "fusionreactor"); -/* fullname */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor")); -ENDCLASS(FusionReactor) -REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor)); - -#endif +#include "fusionreactor.qh" #ifdef IMPLEMENTATION #ifdef SVQC @@ -46,7 +31,7 @@ bool turret_fusionreactor_firecheck(entity this) return true; } -spawnfunc(turret_fusionreactor) { if (!turret_initialize(this, TUR_FUSIONREACTOR)) remove(this); } +spawnfunc(turret_fusionreactor) { if (!turret_initialize(this, TUR_FUSIONREACTOR)) delete(this); } METHOD(FusionReactor, tr_attack, void(FusionReactor this, entity it)) { diff --git a/qcsrc/common/turrets/turret/fusionreactor.qh b/qcsrc/common/turrets/turret/fusionreactor.qh new file mode 100644 index 0000000000..134b805e9f --- /dev/null +++ b/qcsrc/common/turrets/turret/fusionreactor.qh @@ -0,0 +1,13 @@ +#pragma once + +CLASS(FusionReactor, Turret) +/* spawnflags */ ATTRIB(FusionReactor, spawnflags, int, TUR_FLAG_SUPPORT | TUR_FLAG_AMMOSOURCE); +/* mins */ ATTRIB(FusionReactor, mins, vector, '-34 -34 0'); +/* maxs */ ATTRIB(FusionReactor, maxs, vector, '34 34 90'); +/* modelname */ ATTRIB(FusionReactor, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(FusionReactor, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(FusionReactor, head_model, string, strcat("models/turrets/", "reactor.md3")); +/* netname */ ATTRIB(FusionReactor, netname, string, "fusreac"); +/* fullname */ ATTRIB(FusionReactor, turret_name, string, _("Fusion Reactor")); +ENDCLASS(FusionReactor) +REGISTER_TURRET(FUSIONREACTOR, NEW(FusionReactor)); diff --git a/qcsrc/common/turrets/turret/hellion.qc b/qcsrc/common/turrets/turret/hellion.qc index fde81bfea0..88a0170ea6 100644 --- a/qcsrc/common/turrets/turret/hellion.qc +++ b/qcsrc/common/turrets/turret/hellion.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_HELLION_H -#define TURRET_HELLION_H - -#include "hellion_weapon.qh" - -CLASS(Hellion, Turret) -/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(Hellion, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(Hellion, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(Hellion, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3")); -/* netname */ ATTRIB(Hellion, netname, string, "hellion"); -/* fullname */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret")); - ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION); -ENDCLASS(Hellion) -REGISTER_TURRET(HELLION, NEW(Hellion)); - -#endif +#include "hellion.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_hellion) { if (!turret_initialize(this, TUR_HELLION)) remove(this); } +spawnfunc(turret_hellion) { if (!turret_initialize(this, TUR_HELLION)) delete(this); } METHOD(Hellion, tr_think, void(Hellion thistur, entity it)) { diff --git a/qcsrc/common/turrets/turret/hellion.qh b/qcsrc/common/turrets/turret/hellion.qh new file mode 100644 index 0000000000..642645b85f --- /dev/null +++ b/qcsrc/common/turrets/turret/hellion.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "hellion_weapon.qh" + +CLASS(Hellion, Turret) +/* spawnflags */ ATTRIB(Hellion, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_FASTPROJ | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(Hellion, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(Hellion, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(Hellion, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(Hellion, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(Hellion, head_model, string, strcat("models/turrets/", "hellion.md3")); +/* netname */ ATTRIB(Hellion, netname, string, "hellion"); +/* fullname */ ATTRIB(Hellion, turret_name, string, _("Hellion Missile Turret")); + ATTRIB(Hellion, m_weapon, Weapon, WEP_HELLION); +ENDCLASS(Hellion) +REGISTER_TURRET(HELLION, NEW(Hellion)); diff --git a/qcsrc/common/turrets/turret/hellion_weapon.qc b/qcsrc/common/turrets/turret/hellion_weapon.qc index 9e737ea6cf..ea392ec4ed 100644 --- a/qcsrc/common/turrets/turret/hellion_weapon.qc +++ b/qcsrc/common/turrets/turret/hellion_weapon.qc @@ -15,7 +15,7 @@ METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weapo if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_HellionAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_HellionAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; @@ -33,7 +33,6 @@ METHOD(HellionAttack, wr_think, void(entity thiswep, entity actor, .entity weapo te_explosion (missile.origin); setthink(missile, turret_hellion_missile_think); missile.nextthink = time; - missile.flags = FL_PROJECTILE; missile.max_health = time + 9; missile.tur_aimpos = randomvec() * 128; missile.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; diff --git a/qcsrc/common/turrets/turret/hk.qc b/qcsrc/common/turrets/turret/hk.qc index da107922f1..255821e1c3 100644 --- a/qcsrc/common/turrets/turret/hk.qc +++ b/qcsrc/common/turrets/turret/hk.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_HK_H -#define TURRET_HK_H - -//#define TURRET_DEBUG_HK - -#include "hk_weapon.qh" - -CLASS(HunterKiller, Turret) -/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS); -/* mins */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(HunterKiller, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(HunterKiller, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3")); -/* netname */ ATTRIB(HunterKiller, netname, string, "hk"); -/* fullname */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret")); - ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK); -ENDCLASS(HunterKiller) -REGISTER_TURRET(HK, NEW(HunterKiller)); - -#endif +#include "hk.qh" #ifdef IMPLEMENTATION @@ -28,7 +8,7 @@ REGISTER_TURRET(HK, NEW(HunterKiller)); .float atime; #endif -spawnfunc(turret_hk) { if(!turret_initialize(this, TUR_HK)) remove(this); } +spawnfunc(turret_hk) { if(!turret_initialize(this, TUR_HK)) delete(this); } METHOD(HunterKiller, tr_think, void(HunterKiller thistur, entity it)) { diff --git a/qcsrc/common/turrets/turret/hk.qh b/qcsrc/common/turrets/turret/hk.qh new file mode 100644 index 0000000000..d7c9cfbbe8 --- /dev/null +++ b/qcsrc/common/turrets/turret/hk.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define TURRET_DEBUG_HK + +#include "hk_weapon.qh" + +CLASS(HunterKiller, Turret) +/* spawnflags */ ATTRIB(HunterKiller, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER | TUR_FLAG_RECIEVETARGETS); +/* mins */ ATTRIB(HunterKiller, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(HunterKiller, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(HunterKiller, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(HunterKiller, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(HunterKiller, head_model, string, strcat("models/turrets/", "hk.md3")); +/* netname */ ATTRIB(HunterKiller, netname, string, "hk"); +/* fullname */ ATTRIB(HunterKiller, turret_name, string, _("Hunter-Killer Turret")); + ATTRIB(HunterKiller, m_weapon, Weapon, WEP_HK); +ENDCLASS(HunterKiller) +REGISTER_TURRET(HK, NEW(HunterKiller)); diff --git a/qcsrc/common/turrets/turret/hk_weapon.qc b/qcsrc/common/turrets/turret/hk_weapon.qc index dc6f49cab3..5a2f05a483 100644 --- a/qcsrc/common/turrets/turret/hk_weapon.qc +++ b/qcsrc/common/turrets/turret/hk_weapon.qc @@ -20,7 +20,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_HunterKillerAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_HunterKillerAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; @@ -31,7 +31,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity setthink(missile, turret_hk_missile_think); missile.nextthink = time + 0.25; - missile.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(missile, MOVETYPE_BOUNCEMISSILE); missile.velocity = actor.tur_shotdir_updated * (actor.shot_speed * 0.75); missile.angles = vectoangles(missile.velocity); missile.cnt = time + 30; @@ -44,7 +44,7 @@ METHOD(HunterKillerAttack, wr_think, void(entity thiswep, entity actor, .entity } } -bool hk_is_valid_target(entity this, entity e_target); +bool hk_is_valid_target(entity this, entity proj, entity targ); void turret_hk_missile_think(entity this) { vector vu, vd, vf, vl, vr, ve; // Vector (direction) @@ -53,10 +53,7 @@ void turret_hk_missile_think(entity this) float lt_for; // Length of Trace FORwrad float lt_seek; // Length of Trace SEEK (left, right, up down) float pt_seek; // Pitch of Trace SEEK (How mutch to angele left, right up, down trace towards v_forward) - vector pre_pos; float myspeed; - entity e; - float ad,edist; this.nextthink = time + this.ticrate; @@ -69,19 +66,17 @@ void turret_hk_missile_think(entity this) // Pick the closest valid target. if (!this.enemy) { - e = findradius(this.origin, 5000); - while (e) + // in this case, the lighter check is to validate it first, and check distance if it is valid + IL_EACH(g_damagedbycontents, hk_is_valid_target(this.owner, this, it), { - if (hk_is_valid_target(this, e)) - { - if (!this.enemy) - this.enemy = e; - else - if (vlen2(this.origin - e.origin) < vlen2(this.origin - this.enemy.origin)) - this.enemy = e; - } - e = e.chain; - } + if(vdist(it.origin, >, 5000)) + continue; + + if(!this.enemy) + this.enemy = it; + else if(vlen2(this.origin - it.origin) < vlen2(this.origin - this.enemy.origin)) + this.enemy = it; + }); } this.angles = vectoangles(this.velocity); @@ -91,16 +86,15 @@ void turret_hk_missile_think(entity this) if (this.enemy) { - edist = vlen(this.origin - this.enemy.origin); // Close enougth to do decent damage? - if ( edist <= (this.owner.shot_radius * 0.25) ) + if(vdist(this.origin - this.enemy.origin, <=, (this.owner.shot_radius * 0.25))) { turret_projectile_explode(this); return; } // Get data on enemy position - pre_pos = this.enemy.origin + + vector pre_pos = this.enemy.origin + this.enemy.velocity * min((vlen(this.enemy.origin - this.origin) / vlen(this.velocity)),0.5); @@ -111,12 +105,11 @@ void turret_hk_missile_think(entity this) } else { - edist = 0; - ve = '0 0 0'; + ve = '0 0 0'; fe = 0; } - if ((fe != 1) || (this.enemy == NULL) || (edist > 1000)) + if ((fe != 1) || (this.enemy == NULL) || vdist(this.origin - this.enemy.origin, >, 1000)) { myspeed = vlen(this.velocity); @@ -129,7 +122,7 @@ void turret_hk_missile_think(entity this) ff = trace_fraction; // Find angular offset - ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles); + float ad = vlen(vectoangles(normalize(this.enemy.origin - this.origin)) - this.angles); // To close to something, Slow down! if ( ((ff < 0.7) || (ad > 4)) && (myspeed > (autocvar_g_turrets_unit_hk_shot_speed)) ) @@ -207,7 +200,7 @@ void turret_hk_missile_think(entity this) { this.cnt = time + 0.25; this.nextthink = 0; - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); return; } @@ -224,7 +217,7 @@ void turret_hk_missile_think(entity this) #ifdef TURRET_DEBUG_HK //if(this.atime < time) { - if ((fe <= 0.99)||(edist > 1000)) + if ((fe <= 0.99)||vdist(this.origin - this.enemy.origin, >, 1000)) { te_lightning2(NULL,this.origin, this.origin + vr * lt_seek); te_lightning2(NULL,this.origin, this.origin + vl * lt_seek); @@ -246,35 +239,39 @@ void turret_hk_missile_think(entity this) UpdateCSQCProjectile(this); } -bool hk_is_valid_target(entity this, entity e_target) +bool hk_is_valid_target(entity this, entity proj, entity targ) { - if (e_target == NULL) + if (!targ) + return false; + + // we know for sure pure entities are bad targets + if(is_pure(targ)) return false; // If only this was used more.. - if (e_target.flags & FL_NOTARGET) + if (targ.flags & FL_NOTARGET) return false; // Cant touch this - if ((e_target.takedamage == DAMAGE_NO) || (e_target.health < 0)) + if ((targ.takedamage == DAMAGE_NO) || (targ.health < 0)) return false; // player - if (IS_CLIENT(e_target)) + if (IS_PLAYER(targ)) { - if (this.owner.target_select_playerbias < 0) + if (this.target_select_playerbias < 0) return false; - if (IS_DEAD(e_target)) + if (IS_DEAD(targ)) return false; } // Missile - if ((e_target.flags & FL_PROJECTILE) && (this.owner.target_select_missilebias < 0)) + if ((targ.flags & FL_PROJECTILE) && (this.target_select_missilebias < 0)) return false; // Team check - if ((e_target.team == this.owner.team) || (this.owner.team == e_target.owner.team)) + if ((targ.team == this.team) || (this.team == targ.owner.team)) return false; return true; diff --git a/qcsrc/common/turrets/turret/machinegun.qc b/qcsrc/common/turrets/turret/machinegun.qc index 5dac49efc7..db3cb47bf2 100644 --- a/qcsrc/common/turrets/turret/machinegun.qc +++ b/qcsrc/common/turrets/turret/machinegun.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_MACHINEGUN_H -#define TURRET_MACHINEGUN_H - -#include "machinegun_weapon.qh" - -CLASS(MachineGunTurret, Turret) -/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER); -/* mins */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(MachineGunTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3")); -/* netname */ ATTRIB(MachineGunTurret, netname, string, "machinegun"); -/* fullname */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret")); - ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN); -ENDCLASS(MachineGunTurret) -REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret)); - -#endif +#include "machinegun.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_machinegun) { if (!turret_initialize(this, TUR_MACHINEGUN)) remove(this); } +spawnfunc(turret_machinegun) { if (!turret_initialize(this, TUR_MACHINEGUN)) delete(this); } METHOD(MachineGunTurret, tr_setup, void(MachineGunTurret this, entity it)) { diff --git a/qcsrc/common/turrets/turret/machinegun.qh b/qcsrc/common/turrets/turret/machinegun.qh new file mode 100644 index 0000000000..92a8fbaa4e --- /dev/null +++ b/qcsrc/common/turrets/turret/machinegun.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "machinegun_weapon.qh" + +CLASS(MachineGunTurret, Turret) +/* spawnflags */ ATTRIB(MachineGunTurret, spawnflags, int, TUR_FLAG_PLAYER); +/* mins */ ATTRIB(MachineGunTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(MachineGunTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(MachineGunTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(MachineGunTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(MachineGunTurret, head_model, string, strcat("models/turrets/", "machinegun.md3")); +/* netname */ ATTRIB(MachineGunTurret, netname, string, "machinegun"); +/* fullname */ ATTRIB(MachineGunTurret, turret_name, string, _("Machinegun Turret")); + ATTRIB(MachineGunTurret, m_weapon, Weapon, WEP_TUR_MACHINEGUN); +ENDCLASS(MachineGunTurret) +REGISTER_TURRET(MACHINEGUN, NEW(MachineGunTurret)); diff --git a/qcsrc/common/turrets/turret/machinegun_weapon.qc b/qcsrc/common/turrets/turret/machinegun_weapon.qc index 34cd32d742..44c8d64599 100644 --- a/qcsrc/common/turrets/turret/machinegun_weapon.qc +++ b/qcsrc/common/turrets/turret/machinegun_weapon.qc @@ -4,7 +4,7 @@ #ifdef SVQC -void W_MachineGun_MuzzleFlash(entity actor); +void W_MachineGun_MuzzleFlash(entity actor, .entity weaponentity); SOUND(MachineGunTurretAttack_FIRE, W_Sound("electro_fire")); METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { @@ -13,15 +13,15 @@ METHOD(MachineGunTurretAttack, wr_think, void(entity thiswep, entity actor, .ent if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_MachineGunTurretAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MachineGunTurretAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, 0, w_ready); } fireBullet (actor, actor.tur_shotorg, actor.tur_shotdir_updated, actor.shot_spread, 0, actor.shot_dmg, actor.shot_force, DEATH_TURRET_MACHINEGUN.m_id, 0); - W_MachineGun_MuzzleFlash(actor); - setattachment(actor.muzzle_flash, actor.tur_head, "tag_fire"); + W_MachineGun_MuzzleFlash(actor, weaponentity); + setattachment(actor.(weaponentity).muzzle_flash, actor.tur_head, "tag_fire"); } } diff --git a/qcsrc/common/turrets/turret/mlrs.qc b/qcsrc/common/turrets/turret/mlrs.qc index bce27118a2..472a0cb09a 100644 --- a/qcsrc/common/turrets/turret/mlrs.qc +++ b/qcsrc/common/turrets/turret/mlrs.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_MLRS_H -#define TURRET_MLRS_H - -#include "mlrs_weapon.qh" - -CLASS(MLRSTurret, Turret) -/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(MLRSTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3")); -/* netname */ ATTRIB(MLRSTurret, netname, string, "mlrs"); -/* fullname */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret")); - ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS); -ENDCLASS(MLRSTurret) -REGISTER_TURRET(MLRS, NEW(MLRSTurret)); - -#endif +#include "mlrs.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_mlrs) { if (!turret_initialize(this, TUR_MLRS)) remove(this); } +spawnfunc(turret_mlrs) { if (!turret_initialize(this, TUR_MLRS)) delete(this); } METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur, entity it)) { @@ -30,8 +12,8 @@ METHOD(MLRSTurret, tr_think, void(MLRSTurret thistur, entity it)) it.tur_head.frame = bound(0, 6 - floor(0.1 + it.ammo / it.shot_dmg), 6); if(it.tur_head.frame < 0) { - LOG_TRACE("ammo:",ftos(it.ammo),"\n"); - LOG_TRACE("shot_dmg:",ftos(it.shot_dmg),"\n"); + LOG_TRACE("ammo:",ftos(it.ammo)); + LOG_TRACE("shot_dmg:",ftos(it.shot_dmg)); } } METHOD(MLRSTurret, tr_setup, void(MLRSTurret this, entity it)) diff --git a/qcsrc/common/turrets/turret/mlrs.qh b/qcsrc/common/turrets/turret/mlrs.qh new file mode 100644 index 0000000000..b2a6a5c43d --- /dev/null +++ b/qcsrc/common/turrets/turret/mlrs.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "mlrs_weapon.qh" + +CLASS(MLRSTurret, Turret) +/* spawnflags */ ATTRIB(MLRSTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(MLRSTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(MLRSTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(MLRSTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(MLRSTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(MLRSTurret, head_model, string, strcat("models/turrets/", "mlrs.md3")); +/* netname */ ATTRIB(MLRSTurret, netname, string, "mlrs"); +/* fullname */ ATTRIB(MLRSTurret, turret_name, string, _("MLRS Turret")); + ATTRIB(MLRSTurret, m_weapon, Weapon, WEP_TUR_MLRS); +ENDCLASS(MLRSTurret) +REGISTER_TURRET(MLRS, NEW(MLRSTurret)); diff --git a/qcsrc/common/turrets/turret/mlrs_weapon.qc b/qcsrc/common/turrets/turret/mlrs_weapon.qc index 305392cd5a..cfd51c7320 100644 --- a/qcsrc/common/turrets/turret/mlrs_weapon.qc +++ b/qcsrc/common/turrets/turret/mlrs_weapon.qc @@ -11,7 +11,7 @@ METHOD(MLRSTurretAttack, wr_think, void(entity thiswep, entity actor, .entity we if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(machinegun, sustained_refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_MLRSTurretAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_MLRSTurretAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; diff --git a/qcsrc/common/turrets/turret/phaser.qc b/qcsrc/common/turrets/turret/phaser.qc index f604b04669..31ece9cb2b 100644 --- a/qcsrc/common/turrets/turret/phaser.qc +++ b/qcsrc/common/turrets/turret/phaser.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_PHASER_H -#define TURRET_PHASER_H - -#include "phaser_weapon.qh" - -CLASS(PhaserTurret, Turret) -/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(PhaserTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3")); -/* netname */ ATTRIB(PhaserTurret, netname, string, "phaser"); -/* fullname */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon")); - ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER); -ENDCLASS(PhaserTurret) -REGISTER_TURRET(PHASER, NEW(PhaserTurret)); - -#endif +#include "phaser.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_phaser) { if (!turret_initialize(this, TUR_PHASER)) remove(this); } +spawnfunc(turret_phaser) { if (!turret_initialize(this, TUR_PHASER)) delete(this); } .int fireflag; diff --git a/qcsrc/common/turrets/turret/phaser.qh b/qcsrc/common/turrets/turret/phaser.qh new file mode 100644 index 0000000000..fedbe66cba --- /dev/null +++ b/qcsrc/common/turrets/turret/phaser.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "phaser_weapon.qh" + +CLASS(PhaserTurret, Turret) +/* spawnflags */ ATTRIB(PhaserTurret, spawnflags, int, TUR_FLAG_SNIPER | TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(PhaserTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(PhaserTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(PhaserTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(PhaserTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(PhaserTurret, head_model, string, strcat("models/turrets/", "phaser.md3")); +/* netname */ ATTRIB(PhaserTurret, netname, string, "phaser"); +/* fullname */ ATTRIB(PhaserTurret, turret_name, string, _("Phaser Cannon")); + ATTRIB(PhaserTurret, m_weapon, Weapon, WEP_PHASER); +ENDCLASS(PhaserTurret) +REGISTER_TURRET(PHASER, NEW(PhaserTurret)); diff --git a/qcsrc/common/turrets/turret/phaser_weapon.qc b/qcsrc/common/turrets/turret/phaser_weapon.qc index ea48a64790..bf901d886a 100644 --- a/qcsrc/common/turrets/turret/phaser_weapon.qc +++ b/qcsrc/common/turrets/turret/phaser_weapon.qc @@ -14,7 +14,7 @@ METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_PhaserTurretAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_PhaserTurretAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; @@ -33,9 +33,10 @@ METHOD(PhaserTurretAttack, wr_think, void(entity thiswep, entity actor, .entity beam.owner = actor; beam.shot_dmg = actor.shot_dmg / (actor.shot_speed / beam.ticrate); beam.scale = actor.target_range / 256; - beam.movetype = MOVETYPE_NONE; + set_movetype(beam, MOVETYPE_NONE); beam.enemy = actor.enemy; beam.bot_dodge = true; + IL_PUSH(g_bot_dodge, beam); beam.bot_dodgerating = beam.shot_dmg; sound (beam, CH_SHOTS_SINGLE, SND_TUR_PHASER, VOL_BASE, ATTEN_NORM); actor.fireflag = 1; @@ -60,7 +61,7 @@ void beam_think(entity this) this.owner.fireflag = 2; this.owner.tur_head.frame = 10; sound (this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); - remove(this); + delete(this); return; } diff --git a/qcsrc/common/turrets/turret/plasma.qc b/qcsrc/common/turrets/turret/plasma.qc index 7b71ccfc7f..d161436ab0 100644 --- a/qcsrc/common/turrets/turret/plasma.qc +++ b/qcsrc/common/turrets/turret/plasma.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_PLASMA_H -#define TURRET_PLASMA_H - -#include "plasma_weapon.qh" - -CLASS(PlasmaTurret, Turret) -/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(PlasmaTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3")); -/* netname */ ATTRIB(PlasmaTurret, netname, string, "plasma"); -/* fullname */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon")); - ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA); -ENDCLASS(PlasmaTurret) -REGISTER_TURRET(PLASMA, NEW(PlasmaTurret)); - -#endif +#include "plasma.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_plasma) { if (!turret_initialize(this, TUR_PLASMA)) remove(this); } +spawnfunc(turret_plasma) { if (!turret_initialize(this, TUR_PLASMA)) delete(this); } METHOD(PlasmaTurret, tr_attack, void(PlasmaTurret this, entity it)) { diff --git a/qcsrc/common/turrets/turret/plasma.qh b/qcsrc/common/turrets/turret/plasma.qh new file mode 100644 index 0000000000..fc2a96de75 --- /dev/null +++ b/qcsrc/common/turrets/turret/plasma.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "plasma_weapon.qh" + +CLASS(PlasmaTurret, Turret) +/* spawnflags */ ATTRIB(PlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(PlasmaTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(PlasmaTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(PlasmaTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(PlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(PlasmaTurret, head_model, string, strcat("models/turrets/", "plasma.md3")); +/* netname */ ATTRIB(PlasmaTurret, netname, string, "plasma"); +/* fullname */ ATTRIB(PlasmaTurret, turret_name, string, _("Plasma Cannon")); + ATTRIB(PlasmaTurret, m_weapon, Weapon, WEP_PLASMA); +ENDCLASS(PlasmaTurret) +REGISTER_TURRET(PLASMA, NEW(PlasmaTurret)); diff --git a/qcsrc/common/turrets/turret/plasma_dual.qc b/qcsrc/common/turrets/turret/plasma_dual.qc index 86df15059b..9e6d80b2fd 100644 --- a/qcsrc/common/turrets/turret/plasma_dual.qc +++ b/qcsrc/common/turrets/turret/plasma_dual.qc @@ -1,34 +1,10 @@ -#ifndef TURRET_PLASMA_DUAL_H -#define TURRET_PLASMA_DUAL_H - -#include "plasma_weapon.qh" - -CLASS(PlasmaDualAttack, PlasmaAttack) -/* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); -/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); -ENDCLASS(PlasmaDualAttack) -REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack)); - -CLASS(DualPlasmaTurret, PlasmaTurret) -/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); -/* mins */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0'); -/* maxs */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64'); -/* modelname */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3"); -/* model */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3")); -/* netname */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual"); -/* fullname */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon")); - ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL); -ENDCLASS(DualPlasmaTurret) -REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret)); - -#endif +#include "plasma_dual.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) remove(this); } +spawnfunc(turret_plasma_dual) { if (!turret_initialize(this, TUR_PLASMA_DUAL)) delete(this); } METHOD(DualPlasmaTurret, tr_attack, void(DualPlasmaTurret thistur, entity it)) { diff --git a/qcsrc/common/turrets/turret/plasma_dual.qh b/qcsrc/common/turrets/turret/plasma_dual.qh new file mode 100644 index 0000000000..e4c7b0e7df --- /dev/null +++ b/qcsrc/common/turrets/turret/plasma_dual.qh @@ -0,0 +1,22 @@ +#pragma once + +#include "plasma_weapon.qh" + +CLASS(PlasmaDualAttack, PlasmaAttack) +/* refname */ ATTRIB(PlasmaDualAttack, netname, string, "turret_plasma_dual"); +/* wepname */ ATTRIB(PlasmaDualAttack, m_name, string, _("Dual plasma")); +ENDCLASS(PlasmaDualAttack) +REGISTER_WEAPON(PLASMA_DUAL, NEW(PlasmaDualAttack)); + +CLASS(DualPlasmaTurret, PlasmaTurret) +/* spawnflags */ ATTRIB(DualPlasmaTurret, spawnflags, int, TUR_FLAG_SPLASH | TUR_FLAG_MEDPROJ | TUR_FLAG_PLAYER); +/* mins */ ATTRIB(DualPlasmaTurret, mins, vector, '-32 -32 0'); +/* maxs */ ATTRIB(DualPlasmaTurret, maxs, vector, '32 32 64'); +/* modelname */ ATTRIB(DualPlasmaTurret, mdl, string, "base.md3"); +/* model */ ATTRIB_STRZONE(DualPlasmaTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(DualPlasmaTurret, head_model, string, strcat("models/turrets/", "plasmad.md3")); +/* netname */ ATTRIB(DualPlasmaTurret, netname, string, "plasma_dual"); +/* fullname */ ATTRIB(DualPlasmaTurret, turret_name, string, _("Dual Plasma Cannon")); + ATTRIB(DualPlasmaTurret, m_weapon, Weapon, WEP_PLASMA_DUAL); +ENDCLASS(DualPlasmaTurret) +REGISTER_TURRET(PLASMA_DUAL, NEW(DualPlasmaTurret)); diff --git a/qcsrc/common/turrets/turret/plasma_weapon.qc b/qcsrc/common/turrets/turret/plasma_weapon.qc index 535e8b4754..f6f717f85f 100644 --- a/qcsrc/common/turrets/turret/plasma_weapon.qc +++ b/qcsrc/common/turrets/turret/plasma_weapon.qc @@ -10,7 +10,7 @@ METHOD(PlasmaAttack, wr_think, void(entity thiswep, entity actor, .entity weapon if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_PlasmaAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_PlasmaAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; diff --git a/qcsrc/common/turrets/turret/tesla.qc b/qcsrc/common/turrets/turret/tesla.qc index cdc6ba3ceb..249fe18eb9 100644 --- a/qcsrc/common/turrets/turret/tesla.qc +++ b/qcsrc/common/turrets/turret/tesla.qc @@ -1,28 +1,10 @@ -#ifndef TURRET_TESLA_H -#define TURRET_TESLA_H - -#include "tesla_weapon.qh" - -CLASS(TeslaCoil, Turret) -/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); -/* mins */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0'); -/* maxs */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128'); -/* modelname */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3"); -/* model */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3")); -/* netname */ ATTRIB(TeslaCoil, netname, string, "tesla"); -/* fullname */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil")); - ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA); -ENDCLASS(TeslaCoil) -REGISTER_TURRET(TESLA, NEW(TeslaCoil)); - -#endif +#include "tesla.qh" #ifdef IMPLEMENTATION #ifdef SVQC -spawnfunc(turret_tesla) { if (!turret_initialize(this, TUR_TESLA)) remove(this); } +spawnfunc(turret_tesla) { if (!turret_initialize(this, TUR_TESLA)) delete(this); } METHOD(TeslaCoil, tr_think, void(TeslaCoil thistur, entity it)) { diff --git a/qcsrc/common/turrets/turret/tesla.qh b/qcsrc/common/turrets/turret/tesla.qh new file mode 100644 index 0000000000..c5f67b1b9d --- /dev/null +++ b/qcsrc/common/turrets/turret/tesla.qh @@ -0,0 +1,16 @@ +#pragma once + +#include "tesla_weapon.qh" + +CLASS(TeslaCoil, Turret) +/* spawnflags */ ATTRIB(TeslaCoil, spawnflags, int, TUR_FLAG_HITSCAN | TUR_FLAG_PLAYER | TUR_FLAG_MISSILE); +/* mins */ ATTRIB(TeslaCoil, mins, vector, '-60 -60 0'); +/* maxs */ ATTRIB(TeslaCoil, maxs, vector, '60 60 128'); +/* modelname */ ATTRIB(TeslaCoil, mdl, string, "tesla_base.md3"); +/* model */ ATTRIB_STRZONE(TeslaCoil, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(TeslaCoil, head_model, string, strcat("models/turrets/", "tesla_head.md3")); +/* netname */ ATTRIB(TeslaCoil, netname, string, "tesla"); +/* fullname */ ATTRIB(TeslaCoil, turret_name, string, _("Tesla Coil")); + ATTRIB(TeslaCoil, m_weapon, Weapon, WEP_TESLA); +ENDCLASS(TeslaCoil) +REGISTER_TURRET(TESLA, NEW(TeslaCoil)); diff --git a/qcsrc/common/turrets/turret/tesla_weapon.qc b/qcsrc/common/turrets/turret/tesla_weapon.qc index e21cd498f7..a173d8b675 100644 --- a/qcsrc/common/turrets/turret/tesla_weapon.qc +++ b/qcsrc/common/turrets/turret/tesla_weapon.qc @@ -12,7 +12,7 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_TeslaCoilTurretAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_TeslaCoilTurretAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; @@ -27,7 +27,7 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti actor.target_validate_flags = TFL_TARGETSELECT_PLAYERS | TFL_TARGETSELECT_MISSILES | TFL_TARGETSELECT_RANGELIMITS | TFL_TARGETSELECT_TEAMCHECK; entity t = toast(actor, e,r,d); - remove(e); + delete(e); if (t == NULL) return; @@ -42,12 +42,10 @@ METHOD(TeslaCoilTurretAttack, wr_think, void(entity thiswep, entity actor, .enti } - e = findchainfloat(railgunhit, 1); - while (e) { - e.railgunhit = 0; - e = e.chain; - } - + FOREACH_ENTITY_FLOAT(railgunhit, 1, + { + it.railgunhit = 0; + }); } } diff --git a/qcsrc/common/turrets/turret/walker.qc b/qcsrc/common/turrets/turret/walker.qc index 1a5c3f09ab..de744a65d7 100644 --- a/qcsrc/common/turrets/turret/walker.qc +++ b/qcsrc/common/turrets/turret/walker.qc @@ -1,24 +1,4 @@ -#ifndef TURRET_WALKER_H -#define TURRET_WALKER_H - -//#define WALKER_FANCYPATHING - -#include "walker_weapon.qh" - -CLASS(WalkerTurret, Turret) -/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE); -/* mins */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0'); -/* maxs */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95'); -/* modelname */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3"); -/* model */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl)); -/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3")); -/* netname */ ATTRIB(WalkerTurret, netname, string, "walker"); -/* fullname */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret")); - ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER); -ENDCLASS(WalkerTurret) -REGISTER_TURRET(WALKER, NEW(WalkerTurret)); - -#endif +#include "walker.qh" #ifdef IMPLEMENTATION @@ -100,7 +80,12 @@ void walker_setnoanim(entity this) void walker_rocket_explode(entity this) { RadiusDamage (this, this.owner, (autocvar_g_turrets_unit_walker_rocket_damage), 0, (autocvar_g_turrets_unit_walker_rocket_radius), this, NULL, (autocvar_g_turrets_unit_walker_rocket_force), DEATH_TURRET_WALK_ROCKET.m_id, NULL); - remove (this); + delete (this); +} + +void walker_rocket_touch(entity this, entity toucher) +{ + walker_rocket_explode(this); } void walker_rocket_damage(entity this, entity inflictor, entity attacker, float damage, float deathtype, vector hitloc, vector vforce) @@ -250,11 +235,13 @@ void walker_fire_rocket(entity this, vector org) rocket.event_damage = walker_rocket_damage; rocket.nextthink = time; - rocket.movetype = MOVETYPE_FLY; + set_movetype(rocket, MOVETYPE_FLY); rocket.velocity = normalize((v_forward + v_up * 0.5) + (randomvec() * 0.2)) * (autocvar_g_turrets_unit_walker_rocket_speed); rocket.angles = vectoangles(rocket.velocity); - settouch(rocket, walker_rocket_explode); - rocket.flags = FL_PROJECTILE; + settouch(rocket, walker_rocket_touch); + rocket.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, rocket); + IL_PUSH(g_bot_dodge, rocket); rocket.solid = SOLID_BBOX; rocket.max_health = time + 9; rocket.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_GUIDED_HEAT; @@ -339,7 +326,7 @@ void walker_move_path(entity this) #endif } -spawnfunc(turret_walker) { if(!turret_initialize(this, TUR_WALKER)) remove(this); } +spawnfunc(turret_walker) { if(!turret_initialize(this, TUR_WALKER)) delete(this); } METHOD(WalkerTurret, tr_think, void(WalkerTurret thistur, entity it)) { @@ -566,7 +553,7 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) entity e; // Respawn is called & first spawn to, to set team. need to make sure we do not move the initial spawn. - if(it.movetype == MOVETYPE_WALK) + if(it.move_movetype == MOVETYPE_WALK) { if(it.pos1) setorigin(it, it.pos1); @@ -583,9 +570,10 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) it.iscreature = true; it.teleportable = TELEPORT_NORMAL; it.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, it); it.solid = SOLID_SLIDEBOX; it.takedamage = DAMAGE_AIM; - if(it.movetype != MOVETYPE_WALK) + if(it.move_movetype != MOVETYPE_WALK) { setorigin(it, it.origin); tracebox(it.origin + '0 0 128', it.mins, it.maxs, it.origin - '0 0 10000', MOVE_NORMAL, it); @@ -593,7 +581,7 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) it.pos1 = it.origin; it.pos2 = it.angles; } - it.movetype = MOVETYPE_WALK; + set_movetype(it, MOVETYPE_WALK); it.idle_aim = '0 0 0'; it.turret_firecheckfunc = walker_firecheck; @@ -602,12 +590,12 @@ METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) e = find(NULL, targetname, it.target); if (!e) { - LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!\n"); + LOG_TRACE("Initital waypoint for walker does NOT exsist, fix your map!"); it.target = ""; } if (e.classname != "turret_checkpoint") - LOG_TRACE("Warning: not a turrret path\n"); + LOG_TRACE("Warning: not a turrret path"); else { #ifdef WALKER_FANCYPATHING @@ -637,8 +625,7 @@ void walker_draw(entity this) fixedmakevectors(this.angles); movelib_groundalign4point(this, 300, 100, 0.25, 45); setorigin(this, this.origin + this.velocity * dt); - this.tur_head.angles += dt * this.tur_head.move_avelocity; - this.angles_y = this.move_angles_y; + this.tur_head.angles += dt * this.tur_head.avelocity; if (this.health < 127) if(random() < 0.15) @@ -648,9 +635,7 @@ void walker_draw(entity this) METHOD(WalkerTurret, tr_setup, void(WalkerTurret this, entity it)) { it.gravity = 1; - it.movetype = MOVETYPE_BOUNCE; - it.move_movetype = MOVETYPE_BOUNCE; - it.move_origin = it.origin; + set_movetype(it, MOVETYPE_BOUNCE); it.move_time = time; it.draw = walker_draw; } diff --git a/qcsrc/common/turrets/turret/walker.qh b/qcsrc/common/turrets/turret/walker.qh new file mode 100644 index 0000000000..54a908bd1c --- /dev/null +++ b/qcsrc/common/turrets/turret/walker.qh @@ -0,0 +1,18 @@ +#pragma once + +//#define WALKER_FANCYPATHING + +#include "walker_weapon.qh" + +CLASS(WalkerTurret, Turret) +/* spawnflags */ ATTRIB(WalkerTurret, spawnflags, int, TUR_FLAG_PLAYER | TUR_FLAG_MOVE); +/* mins */ ATTRIB(WalkerTurret, mins, vector, '-70 -70 0'); +/* maxs */ ATTRIB(WalkerTurret, maxs, vector, '70 70 95'); +/* modelname */ ATTRIB(WalkerTurret, mdl, string, "walker_body.md3"); +/* model */ ATTRIB_STRZONE(WalkerTurret, model, string, strcat("models/turrets/", this.mdl)); +/* head_model */ ATTRIB_STRZONE(WalkerTurret, head_model, string, strcat("models/turrets/", "walker_head_minigun.md3")); +/* netname */ ATTRIB(WalkerTurret, netname, string, "walker"); +/* fullname */ ATTRIB(WalkerTurret, turret_name, string, _("Walker Turret")); + ATTRIB(WalkerTurret, m_weapon, Weapon, WEP_WALKER); +ENDCLASS(WalkerTurret) +REGISTER_TURRET(WALKER, NEW(WalkerTurret)); diff --git a/qcsrc/common/turrets/turret/walker_weapon.qc b/qcsrc/common/turrets/turret/walker_weapon.qc index 91e4345a95..88b18b9198 100644 --- a/qcsrc/common/turrets/turret/walker_weapon.qc +++ b/qcsrc/common/turrets/turret/walker_weapon.qc @@ -11,7 +11,7 @@ METHOD(WalkerTurretAttack, wr_think, void(entity thiswep, entity actor, .entity if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { if (isPlayer) { turret_initparams(actor); - W_SetupShot_Dir(actor, v_forward, false, 0, SND_WalkerTurretAttack_FIRE, CH_WEAPON_B, 0); + W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_WalkerTurretAttack_FIRE, CH_WEAPON_B, 0); actor.tur_shotdir_updated = w_shotdir; actor.tur_shotorg = w_shotorg; actor.tur_head = actor; diff --git a/qcsrc/common/turrets/turrets.qc b/qcsrc/common/turrets/turrets.qc new file mode 100644 index 0000000000..a53f859524 --- /dev/null +++ b/qcsrc/common/turrets/turrets.qc @@ -0,0 +1 @@ +#include "turrets.qh" diff --git a/qcsrc/common/turrets/turrets.qh b/qcsrc/common/turrets/turrets.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/turrets/turrets.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/turrets/util.qc b/qcsrc/common/turrets/util.qc index 4928262f2f..5fef364580 100644 --- a/qcsrc/common/turrets/util.qc +++ b/qcsrc/common/turrets/util.qc @@ -1,3 +1,4 @@ +#include "util.qh" /* * Update this.tur_shotorg by getting up2date bone info * NOTICE this func overwrites the global v_forward, v_right and v_up vectors. @@ -6,7 +7,7 @@ float turret_tag_fire_update(entity this) { if(!this.tur_head) { - LOG_DEBUG("Call to turret_tag_fire_update with this.tur_head missing!\n"); + LOG_DEBUG("Call to turret_tag_fire_update with this.tur_head missing!"); this.tur_shotorg = '0 0 0'; return false; } @@ -115,7 +116,7 @@ void mark_error(vector where,float lifetime) entity err = new(error_marker); setmodel(err, MDL_MARKER); setorigin(err, where); - err.movetype = MOVETYPE_NONE; + set_movetype(err, MOVETYPE_NONE); setthink(err, marker_think); err.nextthink = time; err.skin = 0; @@ -128,7 +129,7 @@ void mark_info(vector where,float lifetime) entity err = spawn(info_marker); setmodel(err, MDL_MARKER); setorigin(err, where); - err.movetype = MOVETYPE_NONE; + set_movetype(err, MOVETYPE_NONE); setthink(err, marker_think); err.nextthink = time; err.skin = 1; @@ -141,7 +142,7 @@ entity mark_misc(vector where,float lifetime) entity err = spawn(mark_misc); setmodel(err, MDL_MARKER); setorigin(err, where); - err.movetype = MOVETYPE_NONE; + set_movetype(err, MOVETYPE_NONE); setthink(err, marker_think); err.nextthink = time; err.skin = 3; @@ -167,7 +168,7 @@ void paint_target(entity onwho, float f_size, vector v_color, float f_time) //setattachment(e,onwho,""); setorigin(e, onwho.origin + '0 0 1'); e.alpha = 0.15; - e.movetype = MOVETYPE_FLY; + set_movetype(e, MOVETYPE_FLY); e.velocity = (v_color * 32); // + '0 0 1' * 64; @@ -186,7 +187,7 @@ void paint_target2(entity onwho, float f_size, vector v_color, float f_time) setorigin(e, onwho.origin + '0 0 1'); e.alpha = 0.15; - e.movetype = MOVETYPE_FLY; + set_movetype(e, MOVETYPE_FLY); e.velocity = (v_color * 32); // + '0 0 1' * 64; e.avelocity_x = -128; @@ -203,7 +204,7 @@ void paint_target3(vector where, float f_size, vector v_color, float f_time) e.scale = (f_size/512); setsize(e, '0 0 0', '0 0 0'); setorigin(e, where + '0 0 1'); - e.movetype = MOVETYPE_NONE; + set_movetype(e, MOVETYPE_NONE); e.velocity = '0 0 0'; e.colormod = v_color; SUB_SetFade(e,time,f_time); diff --git a/qcsrc/common/turrets/util.qh b/qcsrc/common/turrets/util.qh index d5c948d573..5f52695baf 100644 --- a/qcsrc/common/turrets/util.qh +++ b/qcsrc/common/turrets/util.qh @@ -1,5 +1,4 @@ -#ifndef TURRETS_UTIL_H -#define TURRETS_UTIL_H +#pragma once float shortangle_f(float ang1, float ang2); float anglemods(float v); @@ -8,5 +7,3 @@ vector shortangle_vxy(vector ang1, vector ang2); vector angleofs(entity from, entity to); vector angleofs3(vector from, vector from_a, entity to); void FireImoBeam(entity this, vector start, vector end, vector smin, vector smax, float bforce, float f_dmg, float f_velfactor, float deathtype); - -#endif diff --git a/qcsrc/common/util.qc b/qcsrc/common/util.qc index d04fdecd68..d83871080d 100644 --- a/qcsrc/common/util.qc +++ b/qcsrc/common/util.qc @@ -18,7 +18,7 @@ #include "mapinfo.qh" #endif -#ifndef MENUQC +#ifdef GAMEQC /* * Get "real" origin, in worldspace, even if ent is attached to something else. */ @@ -55,8 +55,7 @@ string wordwrap(string s, float l) return r; } -#ifndef MENUQC -#ifndef CSQC +#ifdef SVQC entity _wordwrap_buffer_sprint_ent; void wordwrap_buffer_sprint(string s) { @@ -80,7 +79,6 @@ void wordwrap_sprint(entity to, string s, float l) return; } #endif -#endif #ifndef SVQC string draw_UseSkinFor(string pic) @@ -230,13 +228,13 @@ string ScoreString(int pFlags, float pValue) float lengthLogTable[128]; -float invertLengthLog(float x) +float invertLengthLog(float dist) { int l, r, m; - if(x >= lengthLogTable[127]) + if(dist >= lengthLogTable[127]) return 127; - if(x <= lengthLogTable[0]) + if(dist <= lengthLogTable[0]) return 0; l = 0; @@ -245,15 +243,15 @@ float invertLengthLog(float x) while(r - l > 1) { m = floor((l + r) / 2); - if(lengthLogTable[m] < x) + if(lengthLogTable[m] < dist) l = m; else r = m; } // now: r is >=, l is < - float lerr = (x - lengthLogTable[l]); - float rerr = (lengthLogTable[r] - x); + float lerr = (dist - lengthLogTable[l]); + float rerr = (lengthLogTable[r] - dist); if(lerr < rerr) return l; return r; @@ -265,26 +263,26 @@ vector decompressShortVector(int data) if(data == 0) return '0 0 0'; float p = (data & 0xF000) / 0x1000; - float y = (data & 0x0F80) / 0x80; + float q = (data & 0x0F80) / 0x80; int len = (data & 0x007F); - //print("\ndecompress: p ", ftos(p)); print("y ", ftos(y)); print("len ", ftos(len), "\n"); + //print("\ndecompress: p ", ftos(p)); print("q ", ftos(q)); print("len ", ftos(len), "\n"); if(p == 0) { out.x = 0; out.y = 0; - if(y == 31) + if(q == 31) out.z = -1; else out.z = +1; } else { - y = .19634954084936207740 * y; + q = .19634954084936207740 * q; p = .19634954084936207740 * p - 1.57079632679489661922; - out.x = cos(y) * cos(p); - out.y = sin(y) * cos(p); + out.x = cos(q) * cos(p); + out.y = sin(q) * cos(p); out.z = -sin(p); } @@ -325,7 +323,7 @@ float compressShortVector(vector vec) return (p * 0x1000) + (y * 0x80) + len; } -void compressShortVector_init() +STATIC_INIT(compressShortVector) { float l = 1; float f = pow(2, 1/8); @@ -352,7 +350,7 @@ void compressShortVector_init() } } -#ifndef MENUQC +#ifdef GAMEQC float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz) { traceline(v0, v0 + dvx, true, forent); if(trace_fraction < 1) return 0; @@ -399,6 +397,9 @@ string fixPriorityList(string order, float from, float to, float subtract, float n = tokenize_console(neworder); for(w = to; w >= from; --w) { + int wflags = Weapons_from(w).spawnflags; + if((wflags & WEP_FLAG_HIDDEN) && (wflags & WEP_FLAG_MUTATORBLOCKED) && !(wflags & WEP_FLAG_NORMAL)) + continue; for(i = 0; i < n; ++i) if(stof(argv(i)) == w) break; @@ -448,7 +449,7 @@ string swapInPriorityList(string order, float i, float j) return order; } -#ifndef MENUQC +#ifdef GAMEQC void get_mi_min_max(float mode) { vector mi, ma; @@ -472,7 +473,7 @@ void get_mi_min_max(float mode) mi_min = mi; mi_max = ma; - MapInfo_Get_ByName(mi_shortname, 0, 0); + MapInfo_Get_ByName(mi_shortname, 0, NULL); if(MapInfo_Map_mins.x < MapInfo_Map_maxs.x) { mi_min = MapInfo_Map_mins; @@ -592,7 +593,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value) if (!(tmp_cvar || tmp_value)) { - LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !\n"); + LOG_TRACE("Error: Invalid usage of cvar_settemp(string, string); !"); return 0; } @@ -602,7 +603,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value) return 0; } - FOREACH_ENTITY_CLASS("saved_cvar_value", it.netname == tmp_cvar, + IL_EACH(g_saved_cvars, it.netname == tmp_cvar, { created_saved_value = -1; // skip creation break; // no need to continue @@ -612,6 +613,7 @@ float cvar_settemp(string tmp_cvar, string tmp_value) { // creating a new entity to keep track of this cvar entity e = new_pure(saved_cvar_value); + IL_PUSH(g_saved_cvars, e); e.netname = strzone(tmp_cvar); e.message = strzone(cvar_string(tmp_cvar)); created_saved_value = 1; @@ -635,7 +637,7 @@ int cvar_settemp_restore() cvar_set(it.netname, it.message); strunzone(it.netname); strunzone(it.message); - remove(it); + delete(it); ++j; } else @@ -649,7 +651,7 @@ int cvar_settemp_restore() if(cvar_type(e.netname)) { cvar_set(e.netname, e.message); - remove(e); + delete(e); ++j; } else @@ -660,6 +662,58 @@ int cvar_settemp_restore() return j; } +bool isCaretEscaped(string theText, float pos) +{ + int i = 0; + while(pos - i >= 1 && substring(theText, pos - i - 1, 1) == "^") + ++i; + return (i & 1); +} + +int skipIncompleteTag(string theText, float pos, int len) +{ + int i = 0, ch = 0; + int tag_start = -1; + + if(substring(theText, pos - 1, 1) == "^") + { + if(isCaretEscaped(theText, pos - 1) || pos >= len) + return 0; + + ch = str2chr(theText, pos); + if(ch >= '0' && ch <= '9') + return 1; // ^[0-9] color code found + else if (ch == 'x') + tag_start = pos - 1; // ^x tag found + else + return 0; + } + else + { + for(i = 2; pos - i >= 0 && i <= 4; ++i) + { + if(substring(theText, pos - i, 2) == "^x") + { + tag_start = pos - i; // ^x tag found + break; + } + } + } + + if(tag_start >= 0) + { + if(tag_start + 5 < len) + if(IS_HEXDIGIT(substring(theText, tag_start + 2, 1))) + if(IS_HEXDIGIT(substring(theText, tag_start + 3, 1))) + if(IS_HEXDIGIT(substring(theText, tag_start + 4, 1))) + { + if(!isCaretEscaped(theText, tag_start)) + return 5 - (pos - tag_start); // ^xRGB color code found + } + } + return 0; +} + float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLengthUpToWidth_widthFunction_t w) { // STOP. @@ -670,57 +724,25 @@ float textLengthUpToWidth(string theText, float maxWidth, vector theSize, textLe if(w(theText, theSize) <= maxWidth) return strlen(theText); // yeah! + bool colors = (w("^7", theSize) == 0); + // binary search for right place to cut string - float ch; - float left, right, middle; // this always works + int len, left, right, middle; left = 0; - right = strlen(theText); // this always fails + right = len = strlen(theText); + int ofs = 0; do { middle = floor((left + right) / 2); - if(w(substring(theText, 0, middle), theSize) <= maxWidth) - left = middle; + if(colors) + ofs = skipIncompleteTag(theText, middle, len); + if(w(substring(theText, 0, middle + ofs), theSize) <= maxWidth) + left = middle + ofs; else right = middle; } while(left < right - 1); - if(w("^7", theSize) == 0) // detect color codes support in the width function - { - // NOTE: when color codes are involved, this binary search is, - // mathematically, BROKEN. However, it is obviously guaranteed to - // terminate, as the range still halves each time - but nevertheless, it is - // guaranteed that it finds ONE valid cutoff place (where "left" is in - // range, and "right" is outside). - - // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4) - // and decrease left on the basis of the chars detected of the truncated tag - // Even if the ^xrgb tag is not complete/correct, left is decreased - // (sometimes too much but with a correct result) - // it fixes also ^[0-9] - while(left >= 1 && substring(theText, left-1, 1) == "^") - left-=1; - - if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/ - left-=2; - else if (left >= 3 && substring(theText, left-3, 2) == "^x") - { - ch = str2chr(theText, left-1); - if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/ - left-=3; - } - else if (left >= 4 && substring(theText, left-4, 2) == "^x") - { - ch = str2chr(theText, left-2); - if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) - { - ch = str2chr(theText, left-1); - if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/ - left-=4; - } - } - } - return left; } @@ -734,57 +756,25 @@ float textLengthUpToLength(string theText, float maxWidth, textLengthUpToLength_ if(w(theText) <= maxWidth) return strlen(theText); // yeah! + bool colors = (w("^7") == 0); + // binary search for right place to cut string - float ch; - float left, right, middle; // this always works + int len, left, right, middle; left = 0; - right = strlen(theText); // this always fails + right = len = strlen(theText); + int ofs = 0; do { middle = floor((left + right) / 2); - if(w(substring(theText, 0, middle)) <= maxWidth) - left = middle; + if(colors) + ofs = skipIncompleteTag(theText, middle, len); + if(w(substring(theText, 0, middle + ofs)) <= maxWidth) + left = middle + ofs; else right = middle; } while(left < right - 1); - if(w("^7") == 0) // detect color codes support in the width function - { - // NOTE: when color codes are involved, this binary search is, - // mathematically, BROKEN. However, it is obviously guaranteed to - // terminate, as the range still halves each time - but nevertheless, it is - // guaranteed that it finds ONE valid cutoff place (where "left" is in - // range, and "right" is outside). - - // terencehill: the following code detects truncated ^xrgb tags (e.g. ^x or ^x4) - // and decrease left on the basis of the chars detected of the truncated tag - // Even if the ^xrgb tag is not complete/correct, left is decreased - // (sometimes too much but with a correct result) - // it fixes also ^[0-9] - while(left >= 1 && substring(theText, left-1, 1) == "^") - left-=1; - - if (left >= 2 && substring(theText, left-2, 2) == "^x") // ^x/ - left-=2; - else if (left >= 3 && substring(theText, left-3, 2) == "^x") - { - ch = str2chr(theText, left-1); - if( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xr/ - left-=3; - } - else if (left >= 4 && substring(theText, left-4, 2) == "^x") - { - ch = str2chr(theText, left-2); - if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) - { - ch = str2chr(theText, left-1); - if ( (ch >= '0' && ch <= '9') || (ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F') ) // ^xrg/ - left-=4; - } - } - } - return left; } @@ -808,14 +798,14 @@ string find_last_color_code(string s) if (carets & 1) { if(i+1 <= len) - if(strstrofs("0123456789", substring(s, i+1, 1), 0) >= 0) + if(IS_DIGIT(substring(s, i+1, 1))) return substring(s, i, 2); if(i+4 <= len) if(substring(s, i+1, 1) == "x") - if(strstrofs("0123456789abcdefABCDEF", substring(s, i+2, 1), 0) >= 0) - if(strstrofs("0123456789abcdefABCDEF", substring(s, i+3, 1), 0) >= 0) - if(strstrofs("0123456789abcdefABCDEF", substring(s, i+4, 1), 0) >= 0) + if(IS_HEXDIGIT(substring(s, i + 2, 1))) + if(IS_HEXDIGIT(substring(s, i + 3, 1))) + if(IS_HEXDIGIT(substring(s, i + 4, 1))) return substring(s, i, 5); } i -= carets; // this also skips one char before the carets @@ -932,7 +922,7 @@ string textShortenToLength(string theText, float maxWidth, textLengthUpToLength_ return strcat(substring(theText, 0, textLengthUpToLength(theText, maxWidth - tw("..."), tw)), "..."); } -float isGametypeInFilter(float gt, float tp, float ts, string pattern) +float isGametypeInFilter(Gametype gt, float tp, float ts, string pattern) { string subpattern, subpattern2, subpattern3, subpattern4; subpattern = strcat(",", MapInfo_Type_ToString(gt), ","); @@ -1083,26 +1073,25 @@ vector get_shotvelocity(vector myvel, vector mydir, float spd, float newton_styl float compressShotOrigin(vector v) { - float x, y, z; - x = rint(v.x * 2); - y = rint(v.y * 4) + 128; - z = rint(v.z * 4) + 128; - if(x > 255 || x < 0) + float rx = rint(v.x * 2); + float ry = rint(v.y * 4) + 128; + float rz = rint(v.z * 4) + 128; + if(rx > 255 || rx < 0) { LOG_INFO("shot origin ", vtos(v), " x out of bounds\n"); - x = bound(0, x, 255); + rx = bound(0, rx, 255); } - if(y > 255 || y < 0) + if(ry > 255 || ry < 0) { LOG_INFO("shot origin ", vtos(v), " y out of bounds\n"); - y = bound(0, y, 255); + ry = bound(0, ry, 255); } - if(z > 255 || z < 0) + if(rz > 255 || rz < 0) { LOG_INFO("shot origin ", vtos(v), " z out of bounds\n"); - z = bound(0, z, 255); + rz = bound(0, rz, 255); } - return x * 0x10000 + y * 0x100 + z; + return rx * 0x10000 + ry * 0x100 + rz; } vector decompressShotOrigin(int f) { @@ -1113,7 +1102,7 @@ vector decompressShotOrigin(int f) return v; } -#ifndef MENUQC +#ifdef GAMEQC vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype) { // NOTE: we'll always choose the SMALLER value... @@ -1227,8 +1216,9 @@ float get_model_parameters(string m, float sk) get_model_parameters_bone_aimweight[i] = 0; } get_model_parameters_fixbone = 0; + get_model_parameters_hidden = false; -#ifndef MENUQC +#ifdef GAMEQC MUTATOR_CALLHOOK(ClearModelParams); #endif @@ -1293,7 +1283,7 @@ float get_model_parameters(string m, float sk) get_model_parameters_bone_upperbody = s; if(c == "bone_weapon") get_model_parameters_bone_weapon = s; - #ifndef MENUQC + #ifdef GAMEQC MUTATOR_CALLHOOK(GetModelParams, c, s); #endif for(int i = 0; i < MAX_AIM_BONES; ++i) @@ -1304,6 +1294,8 @@ float get_model_parameters(string m, float sk) } if(c == "fixbone") get_model_parameters_fixbone = stof(s); + if(c == "hidden") + get_model_parameters_hidden = stob(s); } while((s = fgets(fh))) @@ -1386,7 +1378,7 @@ void m_shutdown() cvar_settemp_restore(); // this must be done LAST, but in any case } -#ifndef MENUQC +#ifdef GAMEQC .float skeleton_bones_index; void Skeleton_SetBones(entity e) { @@ -1464,7 +1456,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t queue_start.FindConnectedComponent_processing = 0; } -#ifndef MENUQC +#ifdef GAMEQC vector animfixfps(entity e, vector a, vector b) { // multi-frame anim: keep as-is @@ -1483,7 +1475,7 @@ vector animfixfps(entity e, vector a, vector b) } #endif -#ifndef MENUQC +#ifdef GAMEQC Notification Announcer_PickNumber(int type, int num) { return = NULL; @@ -1595,7 +1587,7 @@ Notification Announcer_PickNumber(int type, int num) } #endif -#ifndef MENUQC +#ifdef GAMEQC int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents) { switch(nativecontents) diff --git a/qcsrc/common/util.qh b/qcsrc/common/util.qh index fa17a06a32..05e4a4ea27 100644 --- a/qcsrc/common/util.qh +++ b/qcsrc/common/util.qh @@ -1,19 +1,19 @@ -#ifndef COMMON_UTIL_H -#define COMMON_UTIL_H +#pragma once -#ifndef MENUQC +#ifdef GAMEQC vector real_origin(entity ent); #endif +IntrusiveList g_saved_cvars; +STATIC_INIT(g_saved_cvars) { g_saved_cvars = IL_NEW(); } + // this returns a tempstring containing a copy of s with additional \n newlines added, it also replaces \n in the text with a real newline // NOTE: s IS allowed to be a tempstring string wordwrap(string s, float l); -#ifndef MENUQC -#ifndef CSQC +#ifdef SVQC void wordwrap_sprint(entity to, string s, float l); #endif -#endif void wordwrap_cb(string s, float l, void(string) callback); #ifndef SVQC @@ -64,11 +64,10 @@ const float TIME_FACTOR = 100; string ScoreString(float vflags, float value); -void compressShortVector_init(); vector decompressShortVector(float data); float compressShortVector(vector vec); -#ifndef MENUQC +#ifdef GAMEQC float CheckWireframeBox(entity forent, vector v0, vector dvx, vector dvy, vector dvz); #endif @@ -81,7 +80,7 @@ float cvar_value_issafe(string s); float cvar_settemp(string pKey, string pValue); float cvar_settemp_restore(); -#ifndef MENUQC +#ifdef GAMEQC // modes: 0 = trust q3map2 (_mini images) // 1 = trust tracebox (_radar images) // in both modes, mapinfo's "size" overrides @@ -104,7 +103,7 @@ float almost_equals(float a, float b); float almost_in_bounds(float a, float b, float c); float power2of(float e); -float log2of(float x); +float log2of(float e); vector rgb_to_hsl(vector rgb); vector hsl_to_rgb(vector hsl); @@ -126,7 +125,7 @@ string getWrappedLine_remaining; string getWrappedLine(float w, vector size, textLengthUpToWidth_widthFunction_t tw); string getWrappedLineLen(float w, textLengthUpToLength_lenFunction_t tw); -float isGametypeInFilter(float gt, float tp, float ts, string pattern); +float isGametypeInFilter(entity gt, float tp, float ts, string pattern); string swapwords(string str, float i, float j); string shufflewords(string str); @@ -150,14 +149,14 @@ string rankings_reply, ladder_reply, lsmaps_reply, maplist_reply, monsterlist_re string records_reply[10]; #endif -#ifndef MENUQC +#ifdef GAMEQC vector healtharmor_maxdamage(float h, float a, float armorblock, int deathtype); // returns vector: maxdamage, armorideal, 1 if fully armored vector healtharmor_applydamage(float a, float armorblock, int deathtype, float damage); // returns vector: take, save, 0 #endif string getcurrentmod(); -#ifndef MENUQC +#ifdef GAMEQC #ifdef CSQC int ReadInt24_t(); #else @@ -182,6 +181,7 @@ float get_model_parameters_species; string get_model_parameters_sex; float get_model_parameters_weight; float get_model_parameters_age; +bool get_model_parameters_hidden; string get_model_parameters_description; string get_model_parameters_bone_upperbody; string get_model_parameters_bone_weapon; @@ -192,7 +192,7 @@ float get_model_parameters_fixbone; string get_model_parameters_desc; float get_model_parameters(string mod, float skn); // call with string_null to clear; skin -1 means mod is the filename of the txt file and is to be split -#ifndef MENUQC +#ifdef GAMEQC vector NearestPointOnBox(entity box, vector org); #endif @@ -207,7 +207,7 @@ const float XENCODE_LEN = 5; string xencode(float f); float xdecode(string s); -#ifndef MENUQC +#ifdef GAMEQC string strtolower(string s); #endif @@ -216,7 +216,7 @@ string MakeConsoleSafe(string input); // generic shutdown handler void Shutdown(); -#ifndef MENUQC +#ifdef GAMEQC .float skeleton_bones; void Skeleton_SetBones(entity e); // loops through the tags of model v using counter tagnum @@ -233,15 +233,12 @@ float ReadApproxPastTime(); void execute_next_frame(); void queue_to_execute_next_frame(string s); -// for marking written-to values as unused where it's a good idea to do this -noref float unused_float; - // a function f with: // f(0) = 0 // f(1) = 1 // f'(0) = startspeedfactor // f'(1) = endspeedfactor -float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x); +float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd); // checks whether f'(x) = 0 anywhere from 0 to 1 // because if this is the case, the function is not usable for platforms @@ -261,7 +258,7 @@ void FindConnectedComponent(entity e, .entity fld, findNextEntityNearFunction_t string CCR(string input); -#ifndef MENUQC +#ifdef GAMEQC #ifdef CSQC #define GENTLE (autocvar_cl_gentle || autocvar_cl_gentle_messages) #else @@ -270,11 +267,11 @@ string CCR(string input); #define normal_or_gentle(normal, gentle) (GENTLE ? ((gentle != "") ? gentle : normal) : normal) #endif -#ifndef MENUQC +#ifdef GAMEQC vector animfixfps(entity e, vector a, vector b); #endif -#ifndef MENUQC +#ifdef GAMEQC const float CNT_NORMAL = 1; const float CNT_GAMESTART = 2; const float CNT_IDLE = 3; @@ -284,7 +281,7 @@ const float CNT_ROUNDSTART = 6; entity Announcer_PickNumber(float type, float num); #endif -#ifndef MENUQC +#ifdef GAMEQC int Mod_Q1BSP_SuperContentsFromNativeContents(int nativecontents); int Mod_Q1BSP_NativeContentsFromSuperContents(int supercontents); #endif @@ -297,4 +294,3 @@ vector bezier_quadratic_getderivative(vector a, vector p, vector b, float t); // Returns the correct difference between two always increasing numbers #define COMPARE_INCREASING(to,from) (to < from ? from + to + 2 : to - from) -#endif diff --git a/qcsrc/common/vehicles/_all.inc b/qcsrc/common/vehicles/_all.inc new file mode 100644 index 0000000000..8bc63f720a --- /dev/null +++ b/qcsrc/common/vehicles/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "_mod.inc" diff --git a/qcsrc/common/vehicles/_all.qh b/qcsrc/common/vehicles/_all.qh new file mode 100644 index 0000000000..947026dd59 --- /dev/null +++ b/qcsrc/common/vehicles/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "_mod.qh" diff --git a/qcsrc/common/vehicles/_mod.inc b/qcsrc/common/vehicles/_mod.inc index 269858f847..ed26659daa 100644 --- a/qcsrc/common/vehicles/_mod.inc +++ b/qcsrc/common/vehicles/_mod.inc @@ -1,4 +1,9 @@ // generated file; do not modify #include -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/vehicles/_mod.qh b/qcsrc/common/vehicles/_mod.qh index d21e829f7a..4892b0f317 100644 --- a/qcsrc/common/vehicles/_mod.qh +++ b/qcsrc/common/vehicles/_mod.qh @@ -1,4 +1,9 @@ // generated file; do not modify #include -#include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/common/vehicles/all.qc b/qcsrc/common/vehicles/all.qc index 251df22839..4aef11cad5 100644 --- a/qcsrc/common/vehicles/all.qc +++ b/qcsrc/common/vehicles/all.qc @@ -1,8 +1,7 @@ +#include "all.qh" #ifndef VEHICLES_ALL_C #define VEHICLES_ALL_C -#include "all.qh" - REGISTER_NET_LINKED(ENT_CLIENT_AUXILIARYXHAIR) #if defined(SVQC) diff --git a/qcsrc/common/vehicles/all.qh b/qcsrc/common/vehicles/all.qh index b58389d4ab..158492f661 100644 --- a/qcsrc/common/vehicles/all.qh +++ b/qcsrc/common/vehicles/all.qh @@ -1,5 +1,4 @@ -#ifndef VEHICLES_ALL_H -#define VEHICLES_ALL_H +#pragma once #include "vehicle.qh" @@ -22,5 +21,3 @@ const int VEH_FIRST = 1; REGISTER_VEHICLE(Null, NEW(Vehicle)); #include "vehicle/_mod.inc" - -#endif diff --git a/qcsrc/common/vehicles/cl_vehicles.qc b/qcsrc/common/vehicles/cl_vehicles.qc index 57f5990bb7..afb48c918b 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qc +++ b/qcsrc/common/vehicles/cl_vehicles.qc @@ -1,3 +1,4 @@ +#include "cl_vehicles.qh" const string vCROSS_BURST = "gfx/vehicles/crosshair_burst.tga"; const string vCROSS_DROP = "gfx/vehicles/crosshair_drop.tga"; const string vCROSS_GUIDE = "gfx/vehicles/crosshair_guide.tga"; @@ -33,7 +34,7 @@ void AuxiliaryXhair_Draw2D(entity this) return; vector size = draw_getimagesize(this.axh_image) * autocvar_cl_vehicles_crosshair_size; - vector pos = project_3d_to_2d(this.move_origin) - 0.5 * size; + vector pos = project_3d_to_2d(this.origin) - 0.5 * size; if (!(pos.z < 0 || pos.x < 0 || pos.y < 0 || pos.x > vid_conwidth || pos.y > vid_conheight)) { @@ -48,12 +49,14 @@ void AuxiliaryXhair_Draw2D(entity this) NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew) { + int sf = ReadByte(); + int axh_id = bound(0, ReadByte(), MAX_AXH); - entity axh = AuxiliaryXhair[axh_id]; + entity axh = AuxiliaryXhair[axh_id]; - if(axh == NULL || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + if(axh == NULL || wasfreed(axh)) { - axh = spawn(); + axh = new(auxiliary_crosshair); axh.draw2d = func_null; axh.drawmask = MASK_NORMAL; axh.axh_drawflag = DRAWFLAG_ADDITIVE; @@ -61,14 +64,23 @@ NET_HANDLE(ENT_CLIENT_AUXILIARYXHAIR, bool isnew) axh.axh_image = vCROSS_HINT; axh.alpha = 1; AuxiliaryXhair[axh_id] = axh; + IL_PUSH(g_drawables_2d, axh); + } + + if(sf & 2) + { + axh.origin_x = ReadCoord(); + axh.origin_y = ReadCoord(); + axh.origin_z = ReadCoord(); + } + + if(sf & 4) + { + axh.colormod_x = ReadByte() / 255; + axh.colormod_y = ReadByte() / 255; + axh.colormod_z = ReadByte() / 255; } - axh.move_origin_x = ReadCoord(); - axh.move_origin_y = ReadCoord(); - axh.move_origin_z = ReadCoord(); - axh.colormod_x = ReadByte() / 255; - axh.colormod_y = ReadByte() / 255; - axh.colormod_z = ReadByte() / 255; axh.cnt = time; axh.draw2d = AuxiliaryXhair_Draw2D; return true; @@ -80,10 +92,22 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew) return = true; // hud_id == 0 means we exited a vehicle, so stop alarm sound/s - if(hud_id == 0) + // note: HUD_NORMAL is set to 0 currently too, but we'll check both just in case + if(hud_id == 0 || hud_id == HUD_NORMAL) { sound(this, CH_TRIGGER_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE); sound(this, CH_PAIN_SINGLE, SND_Null, VOL_BASEVOICE, ATTEN_NONE); + + for(int i = 0; i < MAX_AXH; ++i) + { + entity axh = AuxiliaryXhair[i]; + + if(axh != NULL && !wasfreed(axh)) + { + AuxiliaryXhair[i] = NULL; + delete(axh); + } + } return; } @@ -93,7 +117,7 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew) entity axh = AuxiliaryXhair[i]; if(axh != NULL && !wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) - remove(axh); + delete(axh); axh = spawn(); axh.draw2d = func_null; @@ -103,6 +127,7 @@ NET_HANDLE(TE_CSQC_VEHICLESETUP, bool isnew) axh.axh_image = vCROSS_HINT; axh.alpha = 1; AuxiliaryXhair[i] = axh; + IL_PUSH(g_drawables_2d, axh); } if(hud_id == HUD_BUMBLEBEE_GUN) @@ -163,7 +188,7 @@ void Vehicles_drawHUD( tmpPos.x = vehicleHud_Pos.x + vehicleHud_Size.x * (96/256) - tmpSize.x; tmpPos.y = vehicleHud_Pos.y; tmpSize = '1 1 1' * hud_fontsize; - drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey("dropweapon", "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL); + drawstring(tmpPos, sprintf(_("Press %s"), getcommandkey(_("drop weapon"), "dropweapon")), tmpSize, '1 0 0' + '0 1 1' * tmpblinkValue, hudAlpha, DRAWFLAG_NORMAL); } // Model diff --git a/qcsrc/common/vehicles/cl_vehicles.qh b/qcsrc/common/vehicles/cl_vehicles.qh index 5293030c19..327074b986 100644 --- a/qcsrc/common/vehicles/cl_vehicles.qh +++ b/qcsrc/common/vehicles/cl_vehicles.qh @@ -1,5 +1,4 @@ -#ifndef CL_VEHICLES_H -#define CL_VEHICLES_H +#pragma once vector vehicleHud_Size; vector vehicleHud_Pos; @@ -8,5 +7,3 @@ void RaptorCBShellfragDraw(entity this); void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang); #define weapon2mode STAT(VEHICLESTAT_W2MODE) - -#endif diff --git a/qcsrc/common/vehicles/sv_vehicles.qc b/qcsrc/common/vehicles/sv_vehicles.qc index a797f84c8d..9c8d05ce4e 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qc +++ b/qcsrc/common/vehicles/sv_vehicles.qc @@ -1,83 +1,62 @@ #include "sv_vehicles.qh" -#if 0 -bool vehicle_send(entity to, int sf) +bool SendAuxiliaryXhair(entity this, entity to, int sf) { - WriteByte(MSG_ENTITY, ENT_CLIENT_VEHICLE); + WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR); WriteByte(MSG_ENTITY, sf); - if(sf & VSF_SPAWN) - { - WriteByte(MSG_ENTITY, this.vehicleid); - } - - if(sf & VSF_SETUP) - { - // send stuff? - } - - if(sf & VSF_ENTER) - { - // player handles the .vehicle stuff, we need only set ourselves up for driving - - // send stuff? - } + WriteByte(MSG_ENTITY, this.cnt); - if(sf & VSF_EXIT) + if(sf & 2) { - // senf stuff? + WriteCoord(MSG_ENTITY, this.origin_x); + WriteCoord(MSG_ENTITY, this.origin_y); + WriteCoord(MSG_ENTITY, this.origin_z); } - if(sf & VSF_PRECACHE) + if(sf & 4) { - // send stuff?! + WriteByte(MSG_ENTITY, rint(this.colormod_x * 255)); + WriteByte(MSG_ENTITY, rint(this.colormod_y * 255)); + WriteByte(MSG_ENTITY, rint(this.colormod_z * 255)); } return true; } -#endif - -bool SendAuxiliaryXhair(entity this, entity to, int sf) -{ - WriteHeader(MSG_ENTITY, ENT_CLIENT_AUXILIARYXHAIR); - - WriteByte(MSG_ENTITY, this.cnt); - - WriteCoord(MSG_ENTITY, this.origin_x); - WriteCoord(MSG_ENTITY, this.origin_y); - WriteCoord(MSG_ENTITY, this.origin_z); - - WriteByte(MSG_ENTITY, rint(this.colormod_x * 255)); - WriteByte(MSG_ENTITY, rint(this.colormod_y * 255)); - WriteByte(MSG_ENTITY, rint(this.colormod_z * 255)); - - return true; -} +.vector axh_prevorigin; +.vector axh_prevcolors; void UpdateAuxiliaryXhair(entity own, vector loc, vector clr, int axh_id) { if(!IS_REAL_CLIENT(own)) return; - entity axh; - axh_id = bound(0, axh_id, MAX_AXH); - axh = own.(AuxiliaryXhair[axh_id]); + entity axh = own.(AuxiliaryXhair[axh_id]); - if(axh == NULL || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist?) + if(axh == NULL || wasfreed(axh)) // MADNESS? THIS IS QQQQCCCCCCCCC (wasfreed, why do you exsist? Mario: because of sloppy code like this) { - axh = spawn(); - axh.cnt = axh_id; - axh.drawonlytoclient = own; - axh.owner = own; + axh = new(auxiliary_xhair); + axh.cnt = axh_id; + axh.drawonlytoclient = own; + axh.owner = own; Net_LinkEntity(axh, false, 0, SendAuxiliaryXhair); } - setorigin(axh, loc); - axh.colormod = clr; - axh.SendFlags = 0x01; - own.(AuxiliaryXhair[axh_id]) = axh; + if(loc != axh.axh_prevorigin) + { + setorigin(axh, loc); + axh.SendFlags |= 2; + } + + if(clr != axh.axh_prevcolors) + { + axh.colormod = clr; + axh.SendFlags |= 4; + } + + own.(AuxiliaryXhair[axh_id]) = axh; // set it anyway...? } void CSQCVehicleSetup(entity own, int vehicle_id) @@ -164,6 +143,12 @@ void vehicles_locktarget(entity this, float incr, float decr, float _lock_time) } } +float vehicle_altitude(entity this, float amax) +{ + tracebox(this.origin, this.mins, this.maxs, this.origin - ('0 0 1' * amax), MOVE_WORLDONLY, this); + return vlen(this.origin - trace_endpos); +} + vector vehicles_force_fromtag_hover(entity this, string tag_name, float spring_length, float max_power) { force_fromtag_origin = gettaginfo(this, gettagindex(this, tag_name)); @@ -213,28 +198,33 @@ void vehicles_projectile_damage(entity this, entity inflictor, entity attacker, } } -void vehicles_projectile_explode(entity this) +void vehicles_projectile_explode(entity this, entity toucher) { - if(this.owner && other != NULL) + if(this.owner && toucher != NULL) { - if(other == this.owner.vehicle) + if(toucher == this.owner.vehicle) return; - if(other == this.owner.vehicle.tur_head) + if(toucher == this.owner.vehicle.tur_head) return; } - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); this.event_damage = func_null; - RadiusDamage (this, this.realowner, this.shot_dmg, 0, this.shot_radius, this, NULL, this.shot_force, this.totalfrags, other); + RadiusDamage (this, this.realowner, this.shot_dmg, 0, this.shot_radius, this, NULL, this.shot_force, this.totalfrags, toucher); - remove (this); + delete (this); +} + +void vehicles_projectile_explode_think(entity this) +{ + vehicles_projectile_explode(this, NULL); } void vehicles_projectile_explode_use(entity this, entity actor, entity trigger) { - vehicles_projectile_explode(this); + vehicles_projectile_explode(this, trigger); } entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, @@ -256,8 +246,10 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, proj.shot_force = _force; proj.totalfrags = _deahtype; proj.solid = SOLID_BBOX; - proj.movetype = MOVETYPE_FLYMISSILE; - proj.flags = FL_PROJECTILE; + set_movetype(proj, MOVETYPE_FLYMISSILE); + proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.bot_dodge = true; proj.bot_dodgerating = _dmg; proj.velocity = _vel; @@ -275,7 +267,7 @@ entity vehicles_projectile(entity this, string _mzlfx, Sound _mzlsound, proj.health = _health; } else - proj.flags = FL_PROJECTILE | FL_NOTARGET; + proj.flags |= FL_NOTARGET; if(_mzlsound != SND_Null) sound (this, CH_WEAPON_A, _mzlsound, VOL_BASE, ATTEN_NORM); @@ -295,14 +287,19 @@ void vehicles_gib_explode(entity this) sound (this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); Send_Effect(EFFECT_EXPLOSION_SMALL, randomvec() * 80 + (this.origin + '0 0 100'), '0 0 0', 1); Send_Effect(EFFECT_EXPLOSION_SMALL, this.wp00.origin + '0 0 64', '0 0 0', 1); - remove(this); + delete(this); +} + +void vehicles_gib_touch(entity this, entity toucher) +{ + vehicles_gib_explode(this); } void vehicles_gib_think(entity this) { this.alpha -= 0.1; if(this.cnt >= time) - remove(this); + delete(this); else this.nextthink = time + 0.1; } @@ -314,7 +311,7 @@ entity vehicle_tossgib(entity this, entity _template, vector _vel, string _tag, vector org = gettaginfo(this, gettagindex(this, _tag)); setorigin(_gib, org); _gib.velocity = _vel; - _gib.movetype = MOVETYPE_TOSS; + set_movetype(_gib, MOVETYPE_TOSS); _gib.solid = SOLID_CORPSE; _gib.colormod = '-0.5 -0.5 -0.5'; _gib.effects = EF_LOWPRECISION; @@ -327,7 +324,7 @@ entity vehicle_tossgib(entity this, entity _template, vector _vel, string _tag, { setthink(_gib, vehicles_gib_explode); _gib.nextthink = time + random() * _explode; - settouch(_gib, vehicles_gib_explode); + settouch(_gib, vehicles_gib_touch); } else { @@ -343,7 +340,7 @@ bool vehicle_addplayerslot( entity _owner, entity _slot, int _hud, Model _hud_model, - bool(entity) _framefunc, + bool(entity,float) _framefunc, void(entity,bool) _exitfunc, float(entity, entity) _enterfunc) { if(!(_owner.vehicle_flags & VHF_MULTISLOT)) @@ -436,21 +433,17 @@ void vehicles_reset_colors(entity this) void vehicles_clearreturn(entity veh) { - // Remove "return helper", if any. - for (entity ret = findchain(classname, "vehicle_return"); ret; ret = ret.chain) + // Remove "return helper" entities, if any. + IL_EACH(g_vehicle_returners, it.wp00 == veh, { - if(ret.wp00 == veh) - { - ret.classname = ""; - setthink(ret, SUB_Remove); - ret.nextthink = time + 0.1; - - if(ret.waypointsprite_attached) - WaypointSprite_Kill(ret.waypointsprite_attached); + it.classname = ""; + setthink(it, SUB_Remove); + it.nextthink = time + 0.1; + IL_REMOVE(g_vehicle_returners, it); - return; - } - } + if(it.waypointsprite_attached) + WaypointSprite_Kill(it.waypointsprite_attached); + }); } void vehicles_spawn(entity this); @@ -464,7 +457,7 @@ void vehicles_return(entity this) if(this.waypointsprite_attached) WaypointSprite_Kill(this.waypointsprite_attached); - remove(this); + delete(this); } void vehicles_showwp_goaway(entity this) @@ -472,14 +465,11 @@ void vehicles_showwp_goaway(entity this) if(this.waypointsprite_attached) WaypointSprite_Kill(this.waypointsprite_attached); - remove(this); - + delete(this); } void vehicles_showwp(entity this) { - vector rgb; - entity ent = this; if(ent.cnt) @@ -493,7 +483,6 @@ void vehicles_showwp(entity this) ent.nextthink = time + 1; ent = spawn(); - setmodel(ent, MDL_Null); ent.team = this.wp00.team; ent.wp00 = this.wp00; setorigin(ent, this.wp00.pos1); @@ -502,6 +491,7 @@ void vehicles_showwp(entity this) setthink(ent, vehicles_showwp_goaway); } + vector rgb; if(teamplay && ent.team) rgb = Team_ColorRGB(ent.team); else @@ -519,11 +509,10 @@ void vehicles_showwp(entity this) void vehicles_setreturn(entity veh) { - entity ret; - vehicles_clearreturn(veh); - ret = new(vehicle_return); + entity ret = new(vehicle_return); + IL_PUSH(g_vehicle_returners, ret); ret.wp00 = veh; ret.team = veh.team; setthink(ret, vehicles_showwp); @@ -538,14 +527,12 @@ void vehicles_setreturn(entity veh) ret.nextthink = min(time + veh.respawntime, time + veh.respawntime - 1); } - setmodel(ret, MDL_Null); setorigin(ret, veh.pos1 + '0 0 96'); - } void vehicle_use(entity this, entity actor, entity trigger) { - LOG_TRACE("vehicle ",this.netname, " used by ", actor.classname, "\n"); + LOG_DEBUG("vehicle ", this.netname, " used by ", actor.classname); this.tur_head.team = actor.team; @@ -556,7 +543,7 @@ void vehicle_use(entity this, entity actor, entity trigger) if(this.active == ACTIVE_ACTIVE && !IS_DEAD(this) && !gameover) { - LOG_TRACE("Respawning vehicle: ", this.netname, "\n"); + LOG_DEBUG("Respawning vehicle: ", this.netname); if(this.effects & EF_NODRAW) { setthink(this, vehicles_spawn); @@ -634,20 +621,15 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag // WEAPONTODO if(DEATH_ISWEAPON(deathtype, WEP_VORTEX)) damage *= autocvar_g_vehicles_vortex_damagerate; - - if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) + else if(DEATH_ISWEAPON(deathtype, WEP_MACHINEGUN)) damage *= autocvar_g_vehicles_machinegun_damagerate; - - if(DEATH_ISWEAPON(deathtype, WEP_RIFLE)) + else if(DEATH_ISWEAPON(deathtype, WEP_RIFLE)) damage *= autocvar_g_vehicles_rifle_damagerate; - - if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) + else if(DEATH_ISWEAPON(deathtype, WEP_VAPORIZER)) damage *= autocvar_g_vehicles_vaporizer_damagerate; - - if(DEATH_ISWEAPON(deathtype, WEP_SEEKER)) + else if(DEATH_ISWEAPON(deathtype, WEP_SEEKER)) damage *= autocvar_g_vehicles_tag_damagerate; - - if(DEATH_WEAPONOF(deathtype) != WEP_Null) + else if(DEATH_WEAPONOF(deathtype) != WEP_Null) damage *= autocvar_g_vehicles_weapon_damagerate; this.enemy = attacker; @@ -721,7 +703,7 @@ void vehicles_damage(entity this, entity inflictor, entity attacker, float damag } } -float vehicles_crushable(entity e) +bool vehicles_crushable(entity e) { if(IS_PLAYER(e) && time >= e.vehicle_enter_delay) return true; @@ -757,49 +739,24 @@ void vehicles_impact(entity this, float _minspeed, float _speedfac, float _maxpa // vehicle enter/exit handling vector vehicles_findgoodexit(entity this, vector prefer_spot) { - //vector exitspot; - float mysize; - - tracebox(this.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, this.owner); + // TODO: we actually want the player's size here + tracebox(this.origin + '0 0 32', PL_MIN_CONST, PL_MAX_CONST, prefer_spot, MOVE_NORMAL, this.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return prefer_spot; - mysize = 1.5 * vlen(this.maxs - this.mins); - float i; - vector v, v2; - v2 = 0.5 * (this.absmin + this.absmax); - for(i = 0; i < 100; ++i) + float mysize = 1.5 * vlen(this.maxs - this.mins); + vector v; + vector v2 = 0.5 * (this.absmin + this.absmax); + for(int i = 0; i < autocvar_g_vehicles_exit_attempts; ++i) { v = randomvec(); v_z = 0; v = v2 + normalize(v) * mysize; - tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, this.owner); + tracebox(v2, PL_MIN_CONST, PL_MAX_CONST, v, MOVE_NORMAL, this.owner); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return v; } - /* - exitspot = (this.origin + '0 0 48') + v_forward * mysize; - tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner); - if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) - return exitspot; - - exitspot = (this.origin + '0 0 48') - v_forward * mysize; - tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner); - if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) - return exitspot; - - exitspot = (this.origin + '0 0 48') + v_right * mysize; - tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner); - if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) - return exitspot; - - exitspot = (this.origin + '0 0 48') - v_right * mysize; - tracebox(this.origin + '0 0 32', PL_MIN, PL_MAX, exitspot, MOVE_NORMAL, this.owner); - if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) - return exitspot; - */ - return this.origin; } @@ -809,7 +766,7 @@ void vehicles_exit(entity vehic, bool eject) if(vehicles_exit_running) { - LOG_TRACE("^1vehicles_exit already running! this is not good...\n"); + LOG_TRACE("^1vehicles_exit already running! this is not good..."); return; } @@ -837,17 +794,17 @@ void vehicles_exit(entity vehic, bool eject) WriteAngle(MSG_ONE, 0); } - setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + setsize(player, STAT(PL_MIN,player), STAT(PL_MAX, player)); player.takedamage = DAMAGE_AIM; player.solid = SOLID_SLIDEBOX; - player.movetype = MOVETYPE_WALK; + set_movetype(player, MOVETYPE_WALK); player.effects &= ~EF_NODRAW; player.teleportable = TELEPORT_NORMAL; player.alpha = 1; player.PlayerPhysplug = func_null; player.vehicle = NULL; - player.view_ofs = STAT(PL_VIEW_OFS, NULL); + player.view_ofs = STAT(PL_VIEW_OFS, player); player.event_damage = PlayerDamage; player.hud = HUD_NORMAL; PS(player).m_switchweapon = vehic.m_switchweapon; @@ -891,21 +848,21 @@ void vehicles_exit(entity vehic, bool eject) vehicles_exit_running = false; } -void vehicles_touch(entity this) +void vehicles_touch(entity this, entity toucher) { - if(MUTATOR_CALLHOOK(VehicleTouch, this, other)) + if(MUTATOR_CALLHOOK(VehicleTouch, this, toucher)) return; // Vehicle currently in use if(this.owner) { if(!forbidWeaponUse(this.owner)) - if(other != NULL) - if((this.origin_z + this.maxs_z) > (other.origin_z)) - if(vehicles_crushable(other)) + if(toucher != NULL) + if((this.origin_z + this.maxs_z) > (toucher.origin_z)) + if(vehicles_crushable(toucher)) { if(vdist(this.velocity, >=, 30)) - Damage(other, this, this.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH.m_id, '0 0 0', normalize(other.origin - this.origin) * autocvar_g_vehicles_crush_force); + Damage(toucher, this, this.owner, autocvar_g_vehicles_crush_dmg, DEATH_VH_CRUSH.m_id, '0 0 0', normalize(toucher.origin - this.origin) * autocvar_g_vehicles_crush_force); return; // Dont do selfdamage when hitting "soft targets". } @@ -921,7 +878,7 @@ void vehicles_touch(entity this) if(autocvar_g_vehicles_enter) return; - vehicles_enter(other, this); + vehicles_enter(toucher, this); } bool vehicle_impulse(entity this, int imp) @@ -956,23 +913,20 @@ void vehicles_enter(entity pl, entity veh) || (pl.vehicle) ) { return; } + Vehicle info = Vehicles_from(veh.vehicleid); + if(autocvar_g_vehicles_enter) // vehicle's touch function should handle this if entering via use key is disabled (TODO) if(veh.vehicle_flags & VHF_MULTISLOT) - if(veh.owner) + if(veh.owner && SAME_TEAM(pl, veh)) { - if(!veh.gunner1) - if(time >= veh.gun1.phase) - if(veh.gun1.vehicle_enter) - if(veh.gun1.vehicle_enter(veh, pl)) - return; - - if(!veh.gunner2) - if(time >= veh.gun2.phase) - if(veh.gun2.vehicle_enter) - if(veh.gun2.vehicle_enter(veh, pl)) - return; + // we don't need a return value or anything here + // if successful the owner check below will prevent anything weird + info.vr_gunner_enter(info, veh, pl); } + if(veh.owner) + return; // got here and didn't enter the gunner, return + if(teamplay) if(veh.team) if(DIFF_TEAM(pl, veh)) @@ -1005,17 +959,17 @@ void vehicles_enter(entity pl, entity veh) veh.vehicle_hudmodel.viewmodelforclient = pl; - tracebox(pl.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), pl.origin, false, pl); pl.crouch = false; - pl.view_ofs = STAT(PL_VIEW_OFS, NULL); - setsize (pl, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + pl.view_ofs = STAT(PL_VIEW_OFS, pl); + setsize (pl, STAT(PL_MIN, pl), STAT(PL_MAX, pl)); veh.event_damage = vehicles_damage; veh.nextthink = 0; pl.angles = veh.angles; pl.takedamage = DAMAGE_NO; pl.solid = SOLID_NOT; - pl.movetype = MOVETYPE_NOCLIP; + pl.disableclientprediction = 1; // physics is no longer run, so this won't be reset + set_movetype(pl, MOVETYPE_NOCLIP); pl.teleportable = false; pl.alpha = -1; pl.event_damage = func_null; @@ -1073,7 +1027,6 @@ void vehicles_enter(entity pl, entity veh) MUTATOR_CALLHOOK(VehicleEnter, pl, veh); CSQCModel_UnlinkEntity(veh); - Vehicle info = Vehicles_from(veh.vehicleid); info.vr_enter(info, veh); antilag_clear(pl, CS(pl)); @@ -1081,7 +1034,7 @@ void vehicles_enter(entity pl, entity veh) void vehicles_think(entity this) { - this.nextthink = time; + this.nextthink = time + autocvar_g_vehicles_thinkrate; if(this.owner) this.owner.vehicle_weapon2mode = this.vehicle_weapon2mode; @@ -1094,10 +1047,21 @@ void vehicles_think(entity this) CSQCMODEL_AUTOUPDATE(this); } +void vehicles_reset(entity this) +{ + if(this.owner) + vehicles_exit(this, VHEF_RELEASE); + + vehicles_clearreturn(this); + + if(this.active != ACTIVE_NOT) + vehicles_spawn(this); +} + // initialization void vehicles_spawn(entity this) { - LOG_TRACE("Spawning vehicle: ", this.classname, "\n"); + LOG_DEBUG("Spawning vehicle: ", this.classname); // disown & reset this.vehicle_hudmodel.viewmodelforclient = this; @@ -1105,13 +1069,16 @@ void vehicles_spawn(entity this) this.owner = NULL; settouch(this, vehicles_touch); this.event_damage = vehicles_damage; + this.reset = vehicles_reset; this.iscreature = true; this.teleportable = false; // no teleporting for vehicles, too buggy this.damagedbycontents = true; - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); this.solid = SOLID_SLIDEBOX; this.takedamage = DAMAGE_AIM; this.deadflag = DEAD_NO; + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); this.bot_attack = true; this.flags = FL_NOTARGET; this.avelocity = '0 0 0'; @@ -1152,14 +1119,17 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) return false; if(!this.tur_head) + { info.vr_precache(info); + IL_PUSH(g_vehicles, this); + } if(this.targetname && this.targetname != "") { this.vehicle_controller = find(NULL, target, this.targetname); if(!this.vehicle_controller) { - bprint("^1WARNING: ^7Vehicle with invalid .targetname\n"); + LOG_DEBUG("^1WARNING: ^7Vehicle with invalid .targetname"); this.active = ACTIVE_ACTIVE; } else @@ -1194,9 +1164,11 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) this.tur_head.owner = this; this.takedamage = DAMAGE_NO; this.bot_attack = true; + IL_PUSH(g_bot_targets, this); this.iscreature = true; this.teleportable = false; // no teleporting for vehicles, too buggy this.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, this); this.vehicleid = info.vehicleid; this.PlayerPhysplug = info.PlayerPhysplug; this.event_damage = func_null; @@ -1234,6 +1206,8 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) setsize(this, info.mins, info.maxs); + info.vr_setup(info, this); + if(!nodrop) { setorigin(this, this.origin); @@ -1245,8 +1219,6 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) this.pos2 = this.angles; this.tur_head.team = this.team; - info.vr_setup(info, this); - if(this.active == ACTIVE_NOT) this.nextthink = 0; // wait until activated else if(autocvar_g_vehicles_delayspawn) @@ -1254,7 +1226,7 @@ bool vehicle_initialize(entity this, Vehicle info, bool nodrop) else this.nextthink = time + game_starttime; - if(MUTATOR_CALLHOOK(VehicleSpawn, this)) + if(MUTATOR_CALLHOOK(VehicleInit, this)) return false; return true; diff --git a/qcsrc/common/vehicles/sv_vehicles.qh b/qcsrc/common/vehicles/sv_vehicles.qh index 8c53724b57..948427db33 100644 --- a/qcsrc/common/vehicles/sv_vehicles.qh +++ b/qcsrc/common/vehicles/sv_vehicles.qh @@ -1,5 +1,4 @@ -#ifndef VEHICLES_DEF_H -#define VEHICLES_DEF_H +#pragma once #ifdef SVQC #include @@ -16,6 +15,8 @@ float autocvar_g_vehicles_crush_force = 50; bool autocvar_g_vehicles_delayspawn = true; float autocvar_g_vehicles_delayspawn_jitter = 10; float autocvar_g_vehicles_allow_bots; +int autocvar_g_vehicles_exit_attempts = 25; +float autocvar_g_vehicles_thinkrate = 0.1; AUTOCVAR(g_vehicles_teams, bool, true, "allow team specific vehicles"); float autocvar_g_vehicles_teleportable; @@ -105,6 +106,9 @@ void vehicles_exit(entity vehic, int eject); 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); + +IntrusiveList g_vehicle_returners; +STATIC_INIT(g_vehicle_returners) { g_vehicle_returners = IL_NEW(); } -#endif #endif diff --git a/qcsrc/common/vehicles/vehicle.qh b/qcsrc/common/vehicles/vehicle.qh index 45cdacf398..cb4225a27d 100644 --- a/qcsrc/common/vehicles/vehicle.qh +++ b/qcsrc/common/vehicles/vehicle.qh @@ -1,40 +1,39 @@ -#ifndef VEHICLE_H -#define VEHICLE_H +#pragma once CLASS(Vehicle, Object) - ATTRIB(Vehicle, vehicleid, int, 0) + ATTRIB(Vehicle, vehicleid, int, 0); /** hud icon */ - ATTRIB(Vehicle, m_icon, string, string_null) + ATTRIB(Vehicle, m_icon, string); /** short name */ - ATTRIB(Vehicle, netname, string, "") + ATTRIB(Vehicle, netname, string, ""); /** human readable name */ - ATTRIB(Vehicle, vehicle_name, string, "Vehicle") + ATTRIB(Vehicle, vehicle_name, string, "Vehicle"); /** full name of model */ - ATTRIB(Vehicle, model, string, "") + ATTRIB(Vehicle, model, string, ""); /** currently a copy of the model */ - ATTRIB(Vehicle, mdl, string, "") + ATTRIB(Vehicle, mdl, string, ""); /** full name of tur_head model */ - ATTRIB(Vehicle, head_model, string, "") + ATTRIB(Vehicle, head_model, string, ""); /** cockpit model */ - ATTRIB(Vehicle, hud_model, string, "") + ATTRIB(Vehicle, hud_model, string, ""); /** tur_head model tag */ - ATTRIB(Vehicle, tag_head, string, string_null) + ATTRIB(Vehicle, tag_head, string); /** hud model tag */ - ATTRIB(Vehicle, tag_hud, string, string_null) + ATTRIB(Vehicle, tag_hud, string); /** cockpit model tag */ - ATTRIB(Vehicle, tag_view, string, string_null) + ATTRIB(Vehicle, tag_view, string); /** player physics mod */ - ATTRIB(Vehicle, PlayerPhysplug, bool(entity), func_null) + ATTRIB(Vehicle, PlayerPhysplug, bool(entity,float)); /** */ - ATTRIB(Vehicle, spawnflags, int, 0) + ATTRIB(Vehicle, spawnflags, int, 0); /** vehicle hitbox size */ - ATTRIB(Vehicle, mins, vector, '-0 -0 -0') + ATTRIB(Vehicle, mins, vector, '-0 -0 -0'); /** vehicle hitbox size */ - ATTRIB(Vehicle, maxs, vector, '0 0 0') + ATTRIB(Vehicle, maxs, vector, '0 0 0'); /** vehicle 3rd person view offset */ - ATTRIB(Vehicle, view_ofs, vector, '0 0 0') + ATTRIB(Vehicle, view_ofs, vector, '0 0 0'); /** vehicle 3rd person view distance */ - ATTRIB(Vehicle, height, float, 0) + ATTRIB(Vehicle, height, float, 0); /** (BOTH) setup vehicle data */ METHOD(Vehicle, vr_setup, void(Vehicle this, entity instance)) { } @@ -46,6 +45,8 @@ CLASS(Vehicle, Object) METHOD(Vehicle, vr_precache, void(Vehicle this)) { } /** (SERVER) called when a player enters this vehicle */ METHOD(Vehicle, vr_enter, void(Vehicle this, entity instance)) { } + /** (SERVER) called when a player enters this vehicle while occupied */ + METHOD(Vehicle, vr_gunner_enter, void(Vehicle this, entity instance, entity actor)) { } /** (SERVER) called when the vehicle re-spawns */ METHOD(Vehicle, vr_spawn, void(Vehicle this, entity instance)) { } /** (SERVER) called when a vehicle hits something */ @@ -57,22 +58,20 @@ CLASS(Vehicle, Object) ENDCLASS(Vehicle) // vehicle spawn flags (need them here for common registrations) -const int VHF_ISVEHICLE = 2; /// Indicates vehicle -const int VHF_HASSHIELD = 4; /// Vehicle has shileding -const int VHF_SHIELDREGEN = 8; /// Vehicles shield regenerates -const int VHF_HEALTHREGEN = 16; /// Vehicles health regenerates -const int VHF_ENERGYREGEN = 32; /// Vehicles energy regenerates -const int VHF_DEATHEJECT = 64; /// Vehicle ejects pilot upon fatal damage -const int VHF_MOVE_GROUND = 128; /// Vehicle moves on gound -const int VHF_MOVE_HOVER = 256; /// Vehicle hover close to gound -const int VHF_MOVE_FLY = 512; /// Vehicle is airborn -const int VHF_DMGSHAKE = 1024; /// Add random velocity each frame if health < 50% -const int VHF_DMGROLL = 2048; /// Add random angles each frame if health < 50% -const int VHF_DMGHEADROLL = 4096; /// Add random head angles each frame if health < 50% -const int VHF_MULTISLOT = 8192; /// Vehicle has multiple player slots -const int VHF_PLAYERSLOT = 16384; /// This ent is a player slot on a multi-person vehicle +const int VHF_ISVEHICLE = BIT(1); /// Indicates vehicle +const int VHF_HASSHIELD = BIT(2); /// Vehicle has shileding +const int VHF_SHIELDREGEN = BIT(3); /// Vehicles shield regenerates +const int VHF_HEALTHREGEN = BIT(4); /// Vehicles health regenerates +const int VHF_ENERGYREGEN = BIT(5); /// Vehicles energy regenerates +const int VHF_DEATHEJECT = BIT(6); /// Vehicle ejects pilot upon fatal damage +const int VHF_MOVE_GROUND = BIT(7); /// Vehicle moves on gound +const int VHF_MOVE_HOVER = BIT(8); /// Vehicle hover close to gound +const int VHF_MOVE_FLY = BIT(9); /// Vehicle is airborn +const int VHF_DMGSHAKE = BIT(10); /// Add random velocity each frame if health < 50% +const int VHF_DMGROLL = BIT(11); /// Add random angles each frame if health < 50% +const int VHF_DMGHEADROLL = BIT(12); /// Add random head angles each frame if health < 50% +const int VHF_MULTISLOT = BIT(13); /// Vehicle has multiple player slots +const int VHF_PLAYERSLOT = BIT(14); /// This ent is a player slot on a multi-person vehicle // fields: .entity tur_head; - -#endif diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qc b/qcsrc/common/vehicles/vehicle/bumblebee.qc index 401483735c..573e46aa77 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qc @@ -1,34 +1,5 @@ -#ifndef VEHICLE_BUMBLEBEE -#define VEHICLE_BUMBLEBEE #include "bumblebee.qh" -#include "bumblebee_weapons.qh" - -CLASS(Bumblebee, Vehicle) -/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE); -/* mins */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130'); -/* maxs */ ATTRIB(Bumblebee, maxs, vector, '230 130 130'); -/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300'); -/* view dist */ ATTRIB(Bumblebee, height, float, 450); -/* model */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm"); -/* model */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm"); -/* head_model */ ATTRIB(Bumblebee, head_model, string, ""); -/* hud_model */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); -/* tags */ ATTRIB(Bumblebee, tag_head, string, ""); -/* tags */ ATTRIB(Bumblebee, tag_hud, string, ""); -/* tags */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport"); -/* netname */ ATTRIB(Bumblebee, netname, string, "bumblebee"); -/* fullname */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee")); -/* icon */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble"); -ENDCLASS(Bumblebee) -REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee)); - -#ifndef MENUQC - MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm"); -#endif - -#endif - #ifdef IMPLEMENTATION const float BRG_SETUP = 2; @@ -46,6 +17,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; float autocvar_g_vehicle_bumblebee_energy = 500; float autocvar_g_vehicle_bumblebee_energy_regen = 50; @@ -98,7 +70,7 @@ vector autocvar_g_vehicle_bumblebee_bouncepain = '1 100 200'; bool autocvar_g_vehicle_bumblebee = true; -bool bumblebee_gunner_frame(entity this) +bool bumblebee_gunner_frame(entity this, float dt) { entity vehic = this.vehicle.owner; entity gun = this.vehicle; @@ -138,7 +110,7 @@ bool bumblebee_gunner_frame(entity this) gun.enemy = NULL; if(trace_ent) - if(trace_ent.movetype) + if(trace_ent.move_movetype) if(trace_ent.takedamage) if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent)) { @@ -156,7 +128,7 @@ bool bumblebee_gunner_frame(entity this) vector vf = real_origin(gun.enemy); vector _vel = gun.enemy.velocity; - if(gun.enemy.movetype == MOVETYPE_WALK) + if(gun.enemy.move_movetype == MOVETYPE_WALK) _vel.z *= 0.1; @@ -212,11 +184,11 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p //vector exitspot; float mysize; - tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), prefer_spot, MOVE_NORMAL, player); + tracebox(gunner.origin + '0 0 32', STAT(PL_MIN, player), STAT(PL_MAX, player), prefer_spot, MOVE_NORMAL, player); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return prefer_spot; - mysize = 1.5 * vlen(STAT(PL_MAX, NULL) - STAT(PL_MIN, NULL)); // can't use gunner's size, as they don't have a size + mysize = 1.5 * vlen(STAT(PL_MAX, player) - STAT(PL_MIN, player)); // can't use gunner's size, as they don't have a size float i; vector v, v2; v2 = 0.5 * (gunner.absmin + gunner.absmax); @@ -225,7 +197,7 @@ vector bumblebee_gunner_findgoodexit(vector prefer_spot, entity gunner, entity p v = randomvec(); v_z = 0; v = v2 + normalize(v) * mysize; - tracebox(v2, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, MOVE_NORMAL, player); + tracebox(v2, STAT(PL_MIN, player), STAT(PL_MAX, player), v, MOVE_NORMAL, player); if(trace_fraction == 1.0 && !trace_startsolid && !trace_allsolid) return v; } @@ -252,15 +224,15 @@ void bumblebee_gunner_exit(entity this, int _exitflag) } CSQCVehicleSetup(player, HUD_NORMAL); - setsize(player, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + setsize(player, STAT(PL_MIN, player), STAT(PL_MAX, player)); player.takedamage = DAMAGE_AIM; player.solid = SOLID_SLIDEBOX; - player.movetype = MOVETYPE_WALK; + set_movetype(player, MOVETYPE_WALK); player.effects &= ~EF_NODRAW; player.alpha = 1; player.PlayerPhysplug = func_null; - player.view_ofs = STAT(PL_VIEW_OFS, NULL); + player.view_ofs = STAT(PL_VIEW_OFS, player); player.event_damage = PlayerDamage; player.hud = HUD_NORMAL; player.teleportable = TELEPORT_NORMAL; @@ -297,27 +269,29 @@ bool bumblebee_gunner_enter(entity this, entity player) if(!vehic.gunner1 && !vehic.gunner2 && ((time >= vehic.gun1.phase) + (time >= vehic.gun2.phase)) == 2) { // we can have some fun - if(vlen2(real_origin(vehic.gun2) - player.origin) < vlen2(real_origin(vehic.gun1) - player.origin)) + vector v1 = gettaginfo(vehic, gettagindex(vehic, "cannon_right")); + vector v2 = gettaginfo(vehic, gettagindex(vehic, "cannon_left")); + if(vlen2(player.origin - v1) < vlen2(player.origin - v2)) { - gunner = vehic.gun2; - vehic.gunner2 = player; + gunner = vehic.gun1; + vehic.gunner1 = player; } else { - gunner = vehic.gun1; - vehic.gunner1 = player; + gunner = vehic.gun2; + vehic.gunner2 = player; } } else if(!vehic.gunner1 && time >= vehic.gun1.phase) { gunner = vehic.gun1; vehic.gunner1 = player; } else if(!vehic.gunner2 && time >= vehic.gun2.phase) { gunner = vehic.gun2; vehic.gunner2 = player; } - else { LOG_TRACE("Vehicle is full, fail\n"); return false; } + else { LOG_TRACE("Vehicle is full, fail"); return false; } player.vehicle = gunner; player.angles = vehic.angles; player.takedamage = DAMAGE_NO; player.solid = SOLID_NOT; player.alpha = -1; - player.movetype = MOVETYPE_NOCLIP; + set_movetype(player, MOVETYPE_NOCLIP); player.event_damage = func_null; player.view_ofs = '0 0 0'; player.hud = gunner.hud; @@ -328,7 +302,7 @@ bool bumblebee_gunner_enter(entity this, entity player) player.vehicle_reload1 = vehic.vehicle_reload1; player.vehicle_reload2 = vehic.vehicle_reload2; player.vehicle_energy = vehic.vehicle_energy; - player.flags &= ~FL_ONGROUND; + UNSET_ONGROUND(player); RemoveGrapplingHook(player); @@ -369,58 +343,59 @@ bool vehicles_valid_pilot(entity this, entity toucher) return true; } -void bumblebee_touch(entity this) +void bumblebee_touch(entity this, entity toucher) { if(autocvar_g_vehicles_enter) { return; } if(this.gunner1 != NULL && this.gunner2 != NULL) { - vehicles_touch(this); + vehicles_touch(this, toucher); return; } - if(vehicles_valid_pilot(this, other)) + if(vehicles_valid_pilot(this, toucher)) { float phase_time = (time >= this.gun1.phase) + (time >= this.gun2.phase); - if(time >= other.vehicle_enter_delay && phase_time) - if(bumblebee_gunner_enter(this, other)) + if(time >= toucher.vehicle_enter_delay && phase_time) + if(bumblebee_gunner_enter(this, toucher)) return; } - vehicles_touch(this); + vehicles_touch(this, toucher); } -void bumblebee_regen(entity this) +void bumblebee_regen(entity this, float dt) { if(this.gun1.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) this.gun1.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, - this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + this.gun1.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt); if(this.gun2.delay + autocvar_g_vehicle_bumblebee_cannon_ammo_regen_pause < time) this.gun2.vehicle_energy = min(autocvar_g_vehicle_bumblebee_cannon_ammo, - this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * frametime); + this.gun2.vehicle_energy + autocvar_g_vehicle_bumblebee_cannon_ammo_regen * dt); if(this.vehicle_flags & VHF_SHIELDREGEN) - 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, frametime, true); + 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, frametime, false); + 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); 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, frametime, false); + 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); } -bool bumblebee_pilot_frame(entity this) +bool bumblebee_pilot_frame(entity this, float dt) { entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { - vehic.velocity = '0 0 0'; - vehic.avelocity = '0 0 0'; + vehic.solid = SOLID_NOT; + vehic.takedamage = DAMAGE_NO; + set_movetype(vehic, MOVETYPE_NONE); return; } @@ -432,7 +407,7 @@ bool bumblebee_pilot_frame(entity this) return; } - bumblebee_regen(vehic); + bumblebee_regen(vehic, dt); crosshair_trace(this); @@ -483,7 +458,7 @@ bool bumblebee_pilot_frame(entity this) else if(this.movement.y > 0) newvel += v_right * autocvar_g_vehicle_bumblebee_speed_strafe; ftmp = newvel * v_right; - ftmp *= frametime * 0.1; + ftmp *= dt * 0.1; vehic.angles_z = bound(-15, vehic.angles.z + ftmp, 15); } else @@ -498,7 +473,7 @@ bool bumblebee_pilot_frame(entity this) else if(PHYS_INPUT_BUTTON_JUMP(this)) newvel += v_up * autocvar_g_vehicle_bumblebee_speed_up; - vehic.velocity += newvel * frametime; + vehic.velocity += newvel * dt; this.velocity = this.movement = vehic.velocity; @@ -508,7 +483,7 @@ bool bumblebee_pilot_frame(entity this) vehic.tur_head.enemy = NULL; if(trace_ent) - if(trace_ent.movetype) + if(trace_ent.move_movetype) if(trace_ent.takedamage) if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent)) { @@ -539,7 +514,7 @@ bool bumblebee_pilot_frame(entity this) autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides * -1, autocvar_g_vehicle_bumblebee_raygun_turnlimit_sides, autocvar_g_vehicle_bumblebee_raygun_turnspeed); if(!forbidWeaponUse(this)) - if((PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) && (vehic.vehicle_energy > autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime || autocvar_g_vehicle_bumblebee_raygun == 0)) + 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)) { vehic.gun3.enemy.realowner = this; vehic.gun3.enemy.effects &= ~EF_NODRAW; @@ -553,8 +528,8 @@ bool bumblebee_pilot_frame(entity this) { if(autocvar_g_vehicle_bumblebee_raygun) { - Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * sys_frametime, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * sys_frametime); - vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * sys_frametime; + Damage(trace_ent, vehic, this, autocvar_g_vehicle_bumblebee_raygun_dps * PHYS_INPUT_FRAMETIME, DEATH_GENERIC.m_id, trace_endpos, v_forward * autocvar_g_vehicle_bumblebee_raygun_fps * PHYS_INPUT_FRAMETIME); + vehic.vehicle_energy -= autocvar_g_vehicle_bumblebee_raygun_aps * PHYS_INPUT_FRAMETIME; } else { @@ -562,28 +537,28 @@ bool bumblebee_pilot_frame(entity this) if((teamplay && trace_ent.team == this.team) || !teamplay) { - if(trace_ent.vehicle_flags & VHF_ISVEHICLE) + if(IS_VEHICLE(trace_ent)) { if(autocvar_g_vehicle_bumblebee_healgun_sps && trace_ent.vehicle_health <= trace_ent.max_health) - trace_ent.vehicle_shield = min(trace_ent.vehicle_shield + autocvar_g_vehicle_bumblebee_healgun_sps * frametime, trace_ent.tur_head.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 * frametime, trace_ent.max_health); + 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 * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + 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 * frametime, autocvar_g_vehicle_bumblebee_healgun_amax); + 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 * frametime, autocvar_g_vehicle_bumblebee_healgun_hmax); + 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 * frametime, trace_ent.max_health); + 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; @@ -629,8 +604,8 @@ void bumblebee_land(entity this) { float hgt; - hgt = raptor_altitude(this, 512); - this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime); + hgt = vehicle_altitude(this, 512); + this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME); this.angles_x *= 0.95; this.angles_z *= 0.95; @@ -658,7 +633,7 @@ void bumblebee_exit(entity this, int eject) this.nextthink = time; } - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); if(!this.owner) return; @@ -698,7 +673,12 @@ void bumblebee_blowup(entity this) if(this.owner.deadflag == DEAD_DYING) this.owner.deadflag = DEAD_DEAD; - remove(this); + delete(this); +} + +void bumblebee_dead_touch(entity this, entity toucher) +{ + bumblebee_blowup(this); } void bumblebee_diethink(entity this) @@ -717,8 +697,8 @@ void bumblebee_diethink(entity this) spawnfunc(vehicle_bumblebee) { - if(!autocvar_g_vehicle_bumblebee) { remove(this); return; } - if(!vehicle_initialize(this, VEH_BUMBLEBEE, false)) { remove(this); return; } + if(!autocvar_g_vehicle_bumblebee) { delete(this); return; } + if(!vehicle_initialize(this, VEH_BUMBLEBEE, false)) { delete(this); return; } } METHOD(Bumblebee, vr_impact, void(Bumblebee thisveh, entity instance)) @@ -730,7 +710,21 @@ METHOD(Bumblebee, vr_enter, void(Bumblebee thisveh, entity instance)) { settouch(instance, bumblebee_touch); instance.nextthink = 0; - instance.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(instance, MOVETYPE_BOUNCEMISSILE); +} +METHOD(Bumblebee, vr_gunner_enter, void(Bumblebee thisveh, entity instance, entity actor)) +{ + if(!instance.gunner1) + if(time >= instance.gun1.phase) + if(instance.gun1.vehicle_enter) + if(instance.gun1.vehicle_enter(instance, actor)) + return; + + if(!instance.gunner2) + if(time >= instance.gun2.phase) + if(instance.gun2.vehicle_enter) + if(instance.gun2.vehicle_enter(instance, actor)) + return; } METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance)) { @@ -745,11 +739,8 @@ METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance)) { entity e = instance.gunner1; instance.gun1.vehicle_exit(instance.gun1, VHEF_EJECT); - entity oldother = other; - other = e; instance.phase = 0; - gettouch(instance)(instance); - other = oldother; + gettouch(instance)(instance, e); return; } @@ -757,11 +748,8 @@ METHOD(Bumblebee, vr_think, void(Bumblebee thisveh, entity instance)) { entity e = instance.gunner2; instance.gun2.vehicle_exit(instance.gun2, VHEF_EJECT); - entity oldother = other; - other = e; instance.phase = 0; - gettouch(instance)(instance); - other = oldother; + gettouch(instance)(instance, e); return; } } @@ -790,7 +778,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance)) entity _body = vehicle_tossgib(instance, instance, instance.velocity + randomvec() * 200, "", rint(random()), rint(random()), 6, randomvec() * 100); if(random() > 0.5) - settouch(_body, bumblebee_blowup); + settouch(_body, bumblebee_dead_touch); else settouch(_body, func_null); @@ -809,7 +797,7 @@ METHOD(Bumblebee, vr_death, void(Bumblebee thisveh, entity instance)) instance.solid = SOLID_NOT; instance.takedamage = DAMAGE_NO; instance.deadflag = DEAD_DYING; - instance.movetype = MOVETYPE_NONE; + set_movetype(instance, MOVETYPE_NONE); instance.effects = EF_NODRAW; instance.colormod = '0 0 0'; instance.avelocity = '0 0 0'; @@ -886,10 +874,13 @@ 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; instance.vehicle_shield = autocvar_g_vehicle_bumblebee_shield; instance.solid = SOLID_BBOX; - instance.movetype = MOVETYPE_TOSS; + set_movetype(instance, MOVETYPE_TOSS); instance.damageforcescale = 0.025; instance.PlayerPhysplug = bumblebee_pilot_frame; diff --git a/qcsrc/common/vehicles/vehicle/bumblebee.qh b/qcsrc/common/vehicles/vehicle/bumblebee.qh index 7a9fc2ccbe..2c90b7c40b 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee.qh @@ -1,13 +1,30 @@ -#ifndef BUMBLEBEE_H -#define BUMBLEBEE_H +#pragma once -#ifdef CSQC +#include "bumblebee_weapons.qh" -void CSQC_BUMBLE_GUN_HUD(); -#endif +CLASS(Bumblebee, Vehicle) +/* spawnflags */ ATTRIB(Bumblebee, spawnflags, int, VHF_DMGSHAKE); +/* mins */ ATTRIB(Bumblebee, mins, vector, '-245 -130 -130'); +/* maxs */ ATTRIB(Bumblebee, maxs, vector, '230 130 130'); +/* view offset*/ ATTRIB(Bumblebee, view_ofs, vector, '0 0 300'); +/* view dist */ ATTRIB(Bumblebee, height, float, 450); +/* model */ ATTRIB(Bumblebee, mdl, string, "models/vehicles/bumblebee_body.dpm"); +/* model */ ATTRIB(Bumblebee, model, string, "models/vehicles/bumblebee_body.dpm"); +/* head_model */ ATTRIB(Bumblebee, head_model, string, ""); +/* hud_model */ ATTRIB(Bumblebee, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); +/* tags */ ATTRIB(Bumblebee, tag_head, string, ""); +/* tags */ ATTRIB(Bumblebee, tag_hud, string, ""); +/* tags */ ATTRIB(Bumblebee, tag_view, string, "tag_viewport"); +/* netname */ ATTRIB(Bumblebee, netname, string, "bumblebee"); +/* fullname */ ATTRIB(Bumblebee, vehicle_name, string, _("Bumblebee")); +/* icon */ ATTRIB(Bumblebee, m_icon, string, "vehicle_bumble"); +ENDCLASS(Bumblebee) +REGISTER_VEHICLE(BUMBLEBEE, NEW(Bumblebee)); -#ifdef SVQC -float raptor_altitude(entity this, float amax); +#ifdef GAMEQC + MODEL(VEH_BUMBLEBEE_GUNCOCKPIT, "models/vehicles/wakizashi_cockpit.dpm"); #endif +#ifdef CSQC +void CSQC_BUMBLE_GUN_HUD(); #endif diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc index b2e838f944..9cb8b74c2b 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qc @@ -50,6 +50,8 @@ bool bumble_raygun_send(entity this, entity to, float sf) void bumble_raygun_draw(entity this); +.vector bumble_origin; + NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew) { int sf = ReadByte(); @@ -69,6 +71,7 @@ NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew) this.lip = particleeffectnum(EFFECT_BUMBLEBEE_HEAL_IMPACT); this.draw = bumble_raygun_draw; + if (isnew) IL_PUSH(g_drawables, this); } @@ -82,9 +85,9 @@ NET_HANDLE(ENT_CLIENT_BUMBLE_RAYGUN, bool isnew) if(sf & BRG_END) { - this.move_origin_x = ReadCoord(); - this.move_origin_y = ReadCoord(); - this.move_origin_z = ReadCoord(); + this.bumble_origin_x = ReadCoord(); + this.bumble_origin_y = ReadCoord(); + this.bumble_origin_z = ReadCoord(); } return true; } @@ -96,13 +99,13 @@ void bumble_raygun_draw(entity this) vector _dir; vector _vtmp1, _vtmp2; - _len = vlen(this.origin - this.move_origin); - _dir = normalize(this.move_origin - this.origin); + _len = vlen(this.origin - this.bumble_origin); + _dir = normalize(this.bumble_origin - this.origin); if(this.bumble_raygun_nextdraw < time) { boxparticles(particleeffectnum(Effects_from(this.traileffect)), this, this.origin, this.origin + _dir * -64, _dir * -_len , _dir * -_len, 1, PARTICLES_USEALPHA); - boxparticles(this.lip, this, this.move_origin, this.move_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA); + boxparticles(this.lip, this, this.bumble_origin, this.bumble_origin + _dir * -64, _dir * -200 , _dir * -200, 1, PARTICLES_USEALPHA); this.bumble_raygun_nextdraw = time + 0.1; } @@ -124,7 +127,7 @@ void bumble_raygun_draw(entity this) _vtmp1 += randomvec() * (_len * 0.2) * (frametime * 10); //this.raygun_l3; Draw_CylindricLine(_vtmp2, _vtmp1, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin); - Draw_CylindricLine(_vtmp1, this.move_origin + randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin); + Draw_CylindricLine(_vtmp1, this.bumble_origin + randomvec() * 32, sz, "gfx/colors/white.tga", 1, 1, this.colormod, al, df, view_origin); } } diff --git a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh index 73fed55e1c..e9c5bf41d0 100644 --- a/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/bumblebee_weapons.qh @@ -1,7 +1,8 @@ #pragma once -#include +#include +#ifdef SVQC float autocvar_g_vehicle_bumblebee_cannon_cost = 2; float autocvar_g_vehicle_bumblebee_cannon_damage = 60; float autocvar_g_vehicle_bumblebee_cannon_radius = 225; @@ -9,6 +10,7 @@ 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_force = -35; +#endif #ifdef SVQC void bumblebee_fire_cannon(entity this, entity _gun, string _tagname, entity _owner); diff --git a/qcsrc/common/vehicles/vehicle/racer.qc b/qcsrc/common/vehicles/vehicle/racer.qc index 254f9bfc4a..80c28a8ab9 100644 --- a/qcsrc/common/vehicles/vehicle/racer.qc +++ b/qcsrc/common/vehicles/vehicle/racer.qc @@ -1,28 +1,4 @@ -#ifndef VEHICLE_RACER -#define VEHICLE_RACER - -#include "racer_weapon.qh" - -CLASS(Racer, Vehicle) -/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); -/* mins */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5); -/* maxs */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5); -/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50'); -/* view dist */ ATTRIB(Racer, height, float, 200); -/* model */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm"); -/* model */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm"); -/* head_model */ ATTRIB(Racer, head_model, string, "null"); -/* hud_model */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm"); -/* tags */ ATTRIB(Racer, tag_head, string, ""); -/* tags */ ATTRIB(Racer, tag_hud, string, ""); -/* tags */ ATTRIB(Racer, tag_view, string, "tag_viewport"); -/* netname */ ATTRIB(Racer, netname, string, "racer"); -/* fullname */ ATTRIB(Racer, vehicle_name, string, _("Racer")); -/* icon */ ATTRIB(Racer, m_icon, string, "vehicle_racer"); -ENDCLASS(Racer) -REGISTER_VEHICLE(RACER, NEW(Racer)); - -#endif +#include "racer.qh" #ifdef IMPLEMENTATION @@ -59,7 +35,7 @@ float autocvar_g_vehicle_racer_friction = 0.45; float autocvar_g_vehicle_racer_water_time = 5; -float autocvar_g_vehicle_racer_collision_multiplier = 0.05; +//float autocvar_g_vehicle_racer_collision_multiplier = 0.05; // 0 = hover, != 0 = maglev int autocvar_g_vehicle_racer_hovertype = 0; @@ -170,15 +146,16 @@ void racer_fire_rocket_aim(entity player, string tagname, entity trg) racer_fire_rocket(player, v, v_forward, trg); } -bool racer_frame(entity this) +bool racer_frame(entity this, float dt) { entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { - vehic.velocity = '0 0 0'; - vehic.avelocity = '0 0 0'; + vehic.solid = SOLID_NOT; + vehic.takedamage = DAMAGE_NO; + set_movetype(vehic, MOVETYPE_NONE); return; } @@ -186,7 +163,7 @@ bool racer_frame(entity this) traceline(vehic.origin, vehic.origin + '0 0 1', MOVE_NOMONSTERS, this); int cont = trace_dpstartcontents; - if(cont & DPCONTENTS_WATER) + if(!(cont & DPCONTENTS_WATER)) vehic.air_finished = time + autocvar_g_vehicle_racer_water_time; if(IS_DEAD(vehic)) @@ -195,28 +172,22 @@ bool racer_frame(entity this) return; } - racer_align4point(vehic, PHYS_INPUT_TIMELENGTH); + racer_align4point(vehic, dt); PHYS_INPUT_BUTTON_ZOOM(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false; - if(time >= vehic.vehicle_last_trace) - { - crosshair_trace(this); - vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate; - } - vehic.angles_x *= -1; // Yaw - float ftmp = autocvar_g_vehicle_racer_turnspeed * PHYS_INPUT_TIMELENGTH; + float ftmp = autocvar_g_vehicle_racer_turnspeed * dt; ftmp = bound(-ftmp, shortangle_f(this.v_angle_y - vehic.angles_y, vehic.angles_y), ftmp); vehic.angles_y = anglemods(vehic.angles_y + ftmp); // Roll - vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * PHYS_INPUT_TIMELENGTH; + vehic.angles_z += -ftmp * autocvar_g_vehicle_racer_turnroll * dt; // Pitch - ftmp = autocvar_g_vehicle_racer_pitchspeed * PHYS_INPUT_TIMELENGTH; + ftmp = autocvar_g_vehicle_racer_pitchspeed * dt; ftmp = bound(-ftmp, shortangle_f(this.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); @@ -262,7 +233,7 @@ bool racer_frame(entity this) #endif // Afterburn - if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH)) + if (PHYS_INPUT_BUTTON_JUMP(this) && vehic.vehicle_energy >= (autocvar_g_vehicle_racer_afterburn_cost * dt)) { #ifdef SVQC if(time - vehic.wait > 0.2) @@ -273,12 +244,12 @@ bool racer_frame(entity this) if(cont & DPCONTENTS_LIQUIDSMASK) { - vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * PHYS_INPUT_TIMELENGTH; + vehic.vehicle_energy -= autocvar_g_vehicle_racer_waterburn_cost * dt; df += (v_forward * autocvar_g_vehicle_racer_waterburn_speed); } else { - vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * PHYS_INPUT_TIMELENGTH; + vehic.vehicle_energy -= autocvar_g_vehicle_racer_afterburn_cost * dt; df += (v_forward * autocvar_g_vehicle_racer_speed_afterburn); } @@ -313,7 +284,7 @@ bool racer_frame(entity this) dforce = autocvar_g_vehicle_racer_water_downforce; df -= v_up * (vlen(vehic.velocity) * dforce); - this.movement = vehic.velocity += df * PHYS_INPUT_TIMELENGTH; + this.movement = vehic.velocity += df * dt; #ifdef SVQC @@ -337,10 +308,16 @@ bool racer_frame(entity this) if(autocvar_g_vehicle_racer_rocket_locktarget) { - if(vehic.vehicle_last_trace == time + autocvar_g_vehicle_racer_thinkrate) - vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * frametime, - (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * frametime, - autocvar_g_vehicle_racer_rocket_locked_time); + if(time >= vehic.vehicle_last_trace) + { + crosshair_trace(this); + + vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_racer_rocket_locking_time) * dt, + (1 / autocvar_g_vehicle_racer_rocket_locking_releasetime) * dt, + autocvar_g_vehicle_racer_rocket_locked_time); + + vehic.vehicle_last_trace = time + autocvar_g_vehicle_racer_thinkrate; + } if(vehic.lock_target) { @@ -382,13 +359,13 @@ bool racer_frame(entity this) this.vehicle_reload2 = bound(0, 100 * ((time - vehic.lip) / (vehic.delay - vehic.lip)), 100); 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, frametime, true); + 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, frametime, false); + 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_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, frametime, false); + 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); @@ -439,7 +416,7 @@ void racer_exit(entity this, int eject) setthink(this, racer_think); this.nextthink = time; - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); sound (this.tur_head, CH_TRIGGER_SINGLE, SND_Null, VOL_VEHICLEENGINE, ATTEN_NORM); if(!this.owner) @@ -490,7 +467,7 @@ void racer_blowup(entity this) this.nextthink = time + autocvar_g_vehicle_racer_respawntime; setthink(this, vehicles_spawn); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.effects = EF_NODRAW; this.solid = SOLID_NOT; @@ -511,7 +488,7 @@ void racer_blowup_think(entity this) CSQCMODEL_AUTOUPDATE(this); } -void racer_deadtouch(entity this) +void racer_deadtouch(entity this, entity toucher) { this.avelocity_x *= 0.7; this.cnt -= 1; @@ -521,47 +498,12 @@ void racer_deadtouch(entity this) spawnfunc(vehicle_racer) { - if(!autocvar_g_vehicle_racer) { remove(this); return; } - if(!vehicle_initialize(this, VEH_RACER, false)) { remove(this); return; } + if(!autocvar_g_vehicle_racer) { delete(this); return; } + if(!vehicle_initialize(this, VEH_RACER, false)) { delete(this); return; } } #endif // SVQC -#ifdef CSQC -#if 0 -void racer_draw(entity this) -{ - float pushdeltatime = time - this.lastpushtime; - if (pushdeltatime > 0.15) pushdeltatime = 0; - this.lastpushtime = time; - if(!pushdeltatime) return; - - tracebox(this.move_origin, this.mins, this.maxs, this.move_origin - ('0 0 1' * STAT(VEH_RACER_SPRINGLENGTH)), MOVE_NOMONSTERS, this); - - vector df = this.move_velocity * -STAT(VEH_RACER_FRICTION); - df_z += (1 - trace_fraction) * STAT(VEH_RACER_HOVERPOWER) + sin(time * 2) * (STAT(VEH_RACER_SPRINGLENGTH) * 2); - - float forced = STAT(VEH_RACER_UPFORCEDAMPER); - - int cont = pointcontents(this.move_origin - '0 0 64'); - if(cont == CONTENT_WATER || cont == CONTENT_LAVA || cont == CONTENT_SLIME) - { - forced = STAT(VEH_RACER_WATER_UPFORCEDAMPER); - this.move_velocity_z += 200; - } - - this.move_velocity += df * pushdeltatime; - if(this.move_velocity_z > 0) - this.move_velocity_z *= 1 - forced * pushdeltatime; - - this.move_angles_x *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime); - this.move_angles_z *= 1 - (STAT(VEH_RACER_ANGLESTABILIZER) * pushdeltatime); - - Movetype_Physics_MatchServer(this, false); -} -#endif -#endif - METHOD(Racer, vr_impact, void(Racer thisveh, entity instance)) { #ifdef SVQC @@ -573,14 +515,14 @@ METHOD(Racer, vr_impact, void(Racer thisveh, entity instance)) METHOD(Racer, vr_enter, void(Racer thisveh, entity instance)) { #ifdef SVQC - instance.movetype = MOVETYPE_BOUNCE; + set_movetype(instance, MOVETYPE_BOUNCE); instance.owner.vehicle_health = (instance.vehicle_health / autocvar_g_vehicle_racer_health) * 100; instance.owner.vehicle_shield = (instance.vehicle_shield / autocvar_g_vehicle_racer_shield) * 100; if(instance.owner.flagcarried) setorigin(instance.owner.flagcarried, '-190 0 96'); #elif defined(CSQC) - instance.move_movetype = MOVETYPE_BOUNCE; + set_movetype(instance, MOVETYPE_BOUNCE); #endif } @@ -607,7 +549,7 @@ METHOD(Racer, vr_spawn, void(Racer thisveh, entity instance)) instance.vehicle_health = autocvar_g_vehicle_racer_health; instance.vehicle_shield = autocvar_g_vehicle_racer_shield; - instance.movetype = MOVETYPE_TOSS; + set_movetype(instance, MOVETYPE_TOSS); instance.solid = SOLID_SLIDEBOX; instance.delay = time; instance.scale = 0.5; @@ -631,7 +573,7 @@ METHOD(Racer, vr_death, void(Racer thisveh, entity instance)) instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; instance.deadflag = DEAD_DYING; - instance.movetype = MOVETYPE_BOUNCE; + set_movetype(instance, MOVETYPE_BOUNCE); instance.wait = time; instance.delay = 2 + time + random() * 3; instance.cnt = 1 + random() * 2; diff --git a/qcsrc/common/vehicles/vehicle/racer.qh b/qcsrc/common/vehicles/vehicle/racer.qh new file mode 100644 index 0000000000..dd14144132 --- /dev/null +++ b/qcsrc/common/vehicles/vehicle/racer.qh @@ -0,0 +1,22 @@ +#pragma once + +#include "racer_weapon.qh" + +CLASS(Racer, Vehicle) +/* spawnflags */ ATTRIB(Racer, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); +/* mins */ ATTRIB(Racer, mins, vector, '-120 -120 -40' * 0.5); +/* maxs */ ATTRIB(Racer, maxs, vector, '120 120 40' * 0.5); +/* view offset*/ ATTRIB(Racer, view_ofs, vector, '0 0 50'); +/* view dist */ ATTRIB(Racer, height, float, 200); +/* model */ ATTRIB(Racer, mdl, string, "models/vehicles/wakizashi.dpm"); +/* model */ ATTRIB(Racer, model, string, "models/vehicles/wakizashi.dpm"); +/* head_model */ ATTRIB(Racer, head_model, string, "null"); +/* hud_model */ ATTRIB(Racer, hud_model, string, "models/vehicles/wakizashi_cockpit.dpm"); +/* tags */ ATTRIB(Racer, tag_head, string, ""); +/* tags */ ATTRIB(Racer, tag_hud, string, ""); +/* tags */ ATTRIB(Racer, tag_view, string, "tag_viewport"); +/* netname */ ATTRIB(Racer, netname, string, "racer"); +/* fullname */ ATTRIB(Racer, vehicle_name, string, _("Racer")); +/* icon */ ATTRIB(Racer, m_icon, string, "vehicle_racer"); +ENDCLASS(Racer) +REGISTER_VEHICLE(RACER, NEW(Racer)); diff --git a/qcsrc/common/vehicles/vehicle/racer_weapon.qc b/qcsrc/common/vehicles/vehicle/racer_weapon.qc index 1af09990bc..d20210c611 100644 --- a/qcsrc/common/vehicles/vehicle/racer_weapon.qc +++ b/qcsrc/common/vehicles/vehicle/racer_weapon.qc @@ -16,7 +16,7 @@ METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weapone veh.vehicle_energy -= autocvar_g_vehicle_racer_cannon_cost; veh.wait = time; } - if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); + if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); vector org = w_shotorg; vector dir = w_shotdir; entity bolt = vehicles_projectile(veh, EFFECT_RACER_MUZZLEFLASH.eent_eff_name, SND_LASERGUN_FIRE, @@ -28,7 +28,7 @@ METHOD(RacerAttack, wr_think, void(entity thiswep, entity actor, .entity weapone } if (fire & 2) if (!isPlayer || weapon_prepareattack(thiswep, actor, weaponentity, false, 0.2)) { - if (isPlayer) W_SetupShot_Dir(actor, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); + if (isPlayer) W_SetupShot_Dir(actor, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); racer_fire_rocket(player, w_shotorg, w_shotdir, NULL); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, 0, w_ready); } diff --git a/qcsrc/common/vehicles/vehicle/racer_weapon.qh b/qcsrc/common/vehicles/vehicle/racer_weapon.qh index fc9e352542..f8b18a86ff 100644 --- a/qcsrc/common/vehicles/vehicle/racer_weapon.qh +++ b/qcsrc/common/vehicles/vehicle/racer_weapon.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include CLASS(RacerAttack, PortoLaunch) /* flags */ ATTRIB(RacerAttack, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); @@ -15,6 +15,7 @@ REGISTER_WEAPON(RACER, NEW(RacerAttack)); void racer_fire_rocket(entity player, vector org, vector dir, entity trg); #endif +#ifdef SVQC float autocvar_g_vehicle_racer_cannon_cost = 2; float autocvar_g_vehicle_racer_cannon_damage = 15; float autocvar_g_vehicle_racer_cannon_radius = 100; @@ -33,3 +34,4 @@ float autocvar_g_vehicle_racer_rocket_refire = 3; float autocvar_g_vehicle_racer_rocket_climbspeed = 1600; float autocvar_g_vehicle_racer_rocket_locked_maxangle = 1.8; +#endif diff --git a/qcsrc/common/vehicles/vehicle/raptor.qc b/qcsrc/common/vehicles/vehicle/raptor.qc index 28716fc266..7c433f9c76 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qc +++ b/qcsrc/common/vehicles/vehicle/raptor.qc @@ -1,30 +1,5 @@ -#ifndef VEHICLE_RAPTOR -#define VEHICLE_RAPTOR #include "raptor.qh" -#include "raptor_weapons.qh" - -CLASS(Raptor, Vehicle) -/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); -/* mins */ ATTRIB(Raptor, mins, vector, '-80 -80 0'); -/* maxs */ ATTRIB(Raptor, maxs, vector, '80 80 70'); -/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160'); -/* view dist */ ATTRIB(Raptor, height, float, 200); -/* model */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm"); -/* model */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm"); -/* head_model */ ATTRIB(Raptor, head_model, string, ""); -/* hud_model */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm"); -/* tags */ ATTRIB(Raptor, tag_head, string, ""); -/* tags */ ATTRIB(Raptor, tag_hud, string, "tag_hud"); -/* tags */ ATTRIB(Raptor, tag_view, string, "tag_camera"); -/* netname */ ATTRIB(Raptor, netname, string, "raptor"); -/* fullname */ ATTRIB(Raptor, vehicle_name, string, _("Raptor")); -/* icon */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor"); -ENDCLASS(Raptor) -REGISTER_VEHICLE(RAPTOR, NEW(Raptor)); - -#endif - #ifdef IMPLEMENTATION #ifdef SVQC @@ -47,6 +22,8 @@ float autocvar_g_vehicle_raptor_speed_up = 1700; float autocvar_g_vehicle_raptor_speed_down = 1700; float autocvar_g_vehicle_raptor_friction = 2; +bool autocvar_g_vehicle_raptor_swim = false; + float autocvar_g_vehicle_raptor_cannon_turnspeed = 120; float autocvar_g_vehicle_raptor_cannon_turnlimit = 20; float autocvar_g_vehicle_raptor_cannon_pitchlimit_up = 12; @@ -77,23 +54,16 @@ vector autocvar_g_vehicle_raptor_bouncepain = '1 4 1000'; .entity bomb1; .entity bomb2; -float raptor_altitude(entity this, float amax) -{ - tracebox(this.origin, this.mins, this.maxs, this.origin - ('0 0 1' * amax), MOVE_WORLDONLY, this); - return vlen(this.origin - trace_endpos); -} - void raptor_land(entity this) { float hgt; - hgt = raptor_altitude(this, 512); - this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * sys_frametime); + hgt = vehicle_altitude(this, 512); + this.velocity = (this.velocity * 0.9) + ('0 0 -1800' * (hgt / 256) * PHYS_INPUT_FRAMETIME); this.angles_x *= 0.95; this.angles_z *= 0.95; - if(hgt < 128) - if(hgt > 0) + if(hgt < 128 && hgt > 0) this.frame = (hgt / 128) * 25; this.bomb1.gun1.avelocity_y = 90 + ((this.frame / 25) * 2000); @@ -101,7 +71,7 @@ void raptor_land(entity this) if(hgt < 16) { - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); setthink(this, vehicles_think); this.frame = 0; } @@ -113,19 +83,19 @@ void raptor_land(entity this) void raptor_exit(entity this, int eject) { - vector spot; this.tur_head.exteriormodeltoclient = NULL; if(!IS_DEAD(this)) { setthink(this, raptor_land); - this.nextthink = time; + this.nextthink = time; } if(!this.owner) return; makevectors(this.angles); + vector spot; if(eject) { spot = this.origin + v_forward * 100 + '0 0 64'; @@ -158,21 +128,21 @@ void raptor_exit(entity this, int eject) this.owner = NULL; } -bool raptor_frame(entity this) +bool raptor_frame(entity this, float dt) { entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { - vehic.velocity = '0 0 0'; - vehic.avelocity = '0 0 0'; + vehic.solid = SOLID_NOT; + vehic.takedamage = DAMAGE_NO; + set_movetype(vehic, MOVETYPE_NONE); return; } vehicles_frame(vehic, this); - float ftmp = 0; /* ftmp = vlen(vehic.velocity); if(ftmp > autocvar_g_vehicle_raptor_speed_forward) @@ -186,7 +156,7 @@ bool raptor_frame(entity this) vehic.sound_nexttime = time + 7.955812; //sound (vehic.tur_head, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_FLY, 1 - ftmp, ATTEN_NORM ); sound (vehic, CH_TRIGGER_SINGLE, SND_VEH_RAPTOR_SPEED, 1, ATTEN_NORM); - vehic.wait = ftmp; + vehic.wait = 0; } /* else if(fabs(ftmp - vehic.wait) > 0.2) @@ -226,7 +196,7 @@ bool raptor_frame(entity this) if(df_y > 180) df_y -= 360; if(df_y < -180) df_y += 360; - ftmp = shortangle_f(this.v_angle_y - vang_y, vang_y); + float ftmp = shortangle_f(this.v_angle_y - vang_y, vang_y); if(ftmp > 180) ftmp -= 360; if(ftmp < -180) ftmp += 360; vehic.avelocity_y = bound(-autocvar_g_vehicle_raptor_turnspeed, ftmp + vehic.avelocity_y * 0.9, autocvar_g_vehicle_raptor_turnspeed); @@ -279,7 +249,7 @@ bool raptor_frame(entity this) else if (PHYS_INPUT_BUTTON_JUMP(this)) df += v_up * autocvar_g_vehicle_raptor_speed_up; - vehic.velocity += df * frametime; + vehic.velocity += df * dt; this.velocity = this.movement = vehic.velocity; setorigin(this, vehic.origin + '0 0 32'); @@ -293,7 +263,7 @@ bool raptor_frame(entity this) vehic.gun1.enemy = NULL; if(trace_ent) - if(trace_ent.movetype) + if(trace_ent.move_movetype) if(trace_ent.takedamage) if(!IS_DEAD(trace_ent) && !STAT(FROZEN, trace_ent)) { @@ -319,7 +289,7 @@ bool raptor_frame(entity this) vf = real_origin(vehic.gun1.enemy); UpdateAuxiliaryXhair(this, vf, '1 0 0', 1); vector _vel = vehic.gun1.enemy.velocity; - if(vehic.gun1.enemy.movetype == MOVETYPE_WALK) + if(vehic.gun1.enemy.move_movetype == MOVETYPE_WALK) _vel_z *= 0.1; if(autocvar_g_vehicle_raptor_cannon_predicttarget) @@ -337,8 +307,8 @@ bool raptor_frame(entity this) else if(autocvar_g_vehicle_raptor_cannon_locktarget == 1) { - vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * frametime, - (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * frametime, + vehicles_locktarget(vehic, (1 / autocvar_g_vehicle_raptor_cannon_locking_time) * dt, + (1 / autocvar_g_vehicle_raptor_cannon_locking_releasetime) * dt, autocvar_g_vehicle_raptor_cannon_locked_time); if(vehic.lock_target != NULL) @@ -395,13 +365,13 @@ bool raptor_frame(entity this) } if(vehic.vehicle_flags & VHF_SHIELDREGEN) - 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, frametime, true); + 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, frametime, false); + 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); 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, frametime, false); + 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); Weapon wep2a = WEP_RAPTOR_BOMB; if(!forbidWeaponUse(this)) @@ -436,9 +406,8 @@ bool raptor_frame(entity this) if(vehic.bomb1.cnt < time) { bool incoming = false; - FOREACH_ENTITY_ENT(enemy, vehic, + IL_EACH(g_projectiles, it.enemy == vehic, { - if(it.flags & FL_PROJECTILE) if(MISSILE_IS_TRACKING(it)) if(vdist(vehic.origin - it.origin, <, 2 * autocvar_g_vehicle_raptor_flare_range)) { @@ -465,7 +434,7 @@ bool raptor_frame(entity this) PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false; } -bool raptor_takeoff(entity this) +bool raptor_takeoff(entity this, float dt) { entity vehic = this.vehicle; return = true; @@ -483,7 +452,7 @@ bool raptor_takeoff(entity this) // Takeoff sequense if(vehic.frame < 25) { - vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / sys_frametime); + vehic.frame += 25 / (autocvar_g_vehicle_raptor_takeofftime / PHYS_INPUT_FRAMETIME); vehic.velocity_z = min(vehic.velocity_z * 1.5, 256); vehic.bomb1.gun1.avelocity_y = 90 + ((vehic.frame / 25) * 25000); vehic.bomb1.gun2.avelocity_y = -vehic.bomb1.gun1.avelocity_y; @@ -495,13 +464,13 @@ bool raptor_takeoff(entity this) this.PlayerPhysplug = raptor_frame; if(vehic.vehicle_flags & VHF_SHIELDREGEN) - 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, frametime, true); + 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, frametime, false); + 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); 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, frametime, false); + 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); vehic.bomb1.alpha = vehic.bomb2.alpha = (time - vehic.lip) / (vehic.delay - vehic.lip); @@ -516,14 +485,14 @@ bool raptor_takeoff(entity this) PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = PHYS_INPUT_BUTTON_CROUCH(this) = false; } -void raptor_blowup(entity this) +void raptor_blowup(entity this, entity toucher) { this.deadflag = DEAD_DEAD; this.vehicle_exit(this, VHEF_NORMAL); RadiusDamage (this, this.enemy, 250, 15, 250, NULL, NULL, 250, DEATH_VH_RAPT_DEATH.m_id, NULL); this.alpha = -1; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.effects = EF_NODRAW; this.colormod = '0 0 0'; this.avelocity = '0 0 0'; @@ -537,7 +506,10 @@ void raptor_blowup(entity this) void raptor_diethink(entity this) { if(time >= this.wait) - setthink(this, raptor_blowup); + { + raptor_blowup(this, NULL); + return; + } if(random() < 0.05) { @@ -603,8 +575,8 @@ bool raptor_impulse(entity this, int _imp) spawnfunc(vehicle_raptor) { - if(!autocvar_g_vehicle_raptor) { remove(this); return; } - if(!vehicle_initialize(this, VEH_RAPTOR, false)) { remove(this); return; } + if(!autocvar_g_vehicle_raptor) { delete(this); return; } + if(!vehicle_initialize(this, VEH_RAPTOR, false)) { delete(this); return; } } METHOD(Raptor, vr_impact, void(Raptor thisveh, entity instance)) @@ -616,11 +588,11 @@ METHOD(Raptor, vr_enter, void(Raptor thisveh, entity instance)) { instance.vehicle_weapon2mode = RSM_BOMB; instance.owner.PlayerPhysplug = raptor_takeoff; - instance.movetype = MOVETYPE_BOUNCEMISSILE; + 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_shield = (instance.vehicle_shield / autocvar_g_vehicle_raptor_shield) * 100; - instance.velocity_z = 1; // Nudge upwards to takeoff sequense can work. + instance.velocity = '0 0 1'; // nudge upwards so takeoff sequence can work instance.tur_head.exteriormodeltoclient = instance.owner; instance.delay = time + autocvar_g_vehicle_raptor_bombs_refire; @@ -638,7 +610,7 @@ METHOD(Raptor, vr_death, void(Raptor thisveh, entity instance)) instance.solid = SOLID_CORPSE; instance.takedamage = DAMAGE_NO; instance.deadflag = DEAD_DYING; - instance.movetype = MOVETYPE_BOUNCE; + set_movetype(instance, MOVETYPE_BOUNCE); setthink(instance, raptor_diethink); instance.nextthink = time; instance.wait = time + 5 + (random() * 5); @@ -705,7 +677,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance)) spinner.owner = instance; setmodel(spinner, MDL_VEH_RAPTOR_PROP); setattachment(spinner, instance, "engine_left"); - spinner.movetype = MOVETYPE_NOCLIP; + set_movetype(spinner, MOVETYPE_NOCLIP); spinner.avelocity = '0 90 0'; instance.bomb1.gun1 = spinner; @@ -713,7 +685,7 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance)) spinner.owner = instance; setmodel(spinner, MDL_VEH_RAPTOR_PROP); setattachment(spinner, instance, "engine_right"); - spinner.movetype = MOVETYPE_NOCLIP; + set_movetype(spinner, MOVETYPE_NOCLIP); spinner.avelocity = '0 -90 0'; instance.bomb1.gun2 = spinner; @@ -727,10 +699,13 @@ METHOD(Raptor, vr_spawn, void(Raptor thisveh, entity instance)) instance.frame = 0; instance.vehicle_health = autocvar_g_vehicle_raptor_health; instance.vehicle_shield = autocvar_g_vehicle_raptor_shield; - instance.movetype = MOVETYPE_TOSS; + set_movetype(instance, MOVETYPE_TOSS); instance.solid = SOLID_SLIDEBOX; instance.vehicle_energy = 1; + if(!autocvar_g_vehicle_raptor_swim) + instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK; + instance.PlayerPhysplug = raptor_frame; instance.bomb1.gun1.avelocity_y = 90; @@ -763,6 +738,9 @@ METHOD(Raptor, vr_setup, void(Raptor thisveh, entity instance)) instance.vehicle_health = autocvar_g_vehicle_raptor_health; instance.vehicle_shield = autocvar_g_vehicle_raptor_shield; instance.max_health = instance.vehicle_health; + + if(!autocvar_g_vehicle_raptor_swim) + instance.dphitcontentsmask |= DPCONTENTS_LIQUIDSMASK; } #endif @@ -786,7 +764,7 @@ METHOD(Raptor, vr_crosshair, void(Raptor thisveh, entity player)) } vector tmpSize = '0 0 0'; - if(weapon2mode != RSM_FLARE) + if(weapon2mode != RSM_FLARE && !spectatee_status) { vector where; diff --git a/qcsrc/common/vehicles/vehicle/raptor.qh b/qcsrc/common/vehicles/vehicle/raptor.qh index f24939ba9d..12666523c7 100644 --- a/qcsrc/common/vehicles/vehicle/raptor.qh +++ b/qcsrc/common/vehicles/vehicle/raptor.qh @@ -1,9 +1,27 @@ -#ifndef RAPTOR_H -#define RAPTOR_H +#pragma once + +#include "raptor_weapons.qh" + +CLASS(Raptor, Vehicle) +/* spawnflags */ ATTRIB(Raptor, spawnflags, int, VHF_DMGSHAKE | VHF_DMGROLL); +/* mins */ ATTRIB(Raptor, mins, vector, '-80 -80 0'); +/* maxs */ ATTRIB(Raptor, maxs, vector, '80 80 70'); +/* view offset*/ ATTRIB(Raptor, view_ofs, vector, '0 0 160'); +/* view dist */ ATTRIB(Raptor, height, float, 200); +/* model */ ATTRIB(Raptor, mdl, string, "models/vehicles/raptor.dpm"); +/* model */ ATTRIB(Raptor, model, string, "models/vehicles/raptor.dpm"); +/* head_model */ ATTRIB(Raptor, head_model, string, ""); +/* hud_model */ ATTRIB(Raptor, hud_model, string, "models/vehicles/raptor_cockpit.dpm"); +/* tags */ ATTRIB(Raptor, tag_head, string, ""); +/* tags */ ATTRIB(Raptor, tag_hud, string, "tag_hud"); +/* tags */ ATTRIB(Raptor, tag_view, string, "tag_camera"); +/* netname */ ATTRIB(Raptor, netname, string, "raptor"); +/* fullname */ ATTRIB(Raptor, vehicle_name, string, _("Raptor")); +/* icon */ ATTRIB(Raptor, m_icon, string, "vehicle_raptor"); +ENDCLASS(Raptor) +REGISTER_VEHICLE(RAPTOR, NEW(Raptor)); const int RSM_FIRST = 1; const int RSM_BOMB = 1; const int RSM_FLARE = 2; const int RSM_LAST = 2; - -#endif diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc index 56c3e8c610..4e16efbcc9 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qc @@ -12,7 +12,7 @@ METHOD(RaptorCannon, wr_think, void(entity thiswep, entity actor, .entity weapon float t = autocvar_g_vehicle_raptor_cannon_refire * (1 + veh.misc_bulletcounter == 4); if (fire & 1) if (weapon_prepareattack(thiswep, player, weaponentity, false, t)) { - if (isPlayer) W_SetupShot_Dir(player, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); + if (isPlayer) W_SetupShot_Dir(player, weaponentity, v_forward, false, 0, SND_Null, CH_WEAPON_B, 0); vector org = w_shotorg; vector dir = w_shotdir; if (veh) { @@ -53,7 +53,7 @@ METHOD(RaptorBomb, wr_think, void(entity thiswep, entity actor, .entity weaponen void raptor_flare_think(entity this); void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); -void raptor_flare_touch(entity this); +void raptor_flare_touch(entity this, entity toucher); METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { bool isPlayer = IS_PLAYER(actor); @@ -67,7 +67,7 @@ METHOD(RaptorFlare, wr_think, void(entity thiswep, entity actor, .entity weapone _flare.effects = EF_LOWPRECISION | EF_FLAME; _flare.scale = 0.5; setorigin(_flare, actor.origin - '0 0 16'); - _flare.movetype = MOVETYPE_TOSS; + set_movetype(_flare, MOVETYPE_TOSS); _flare.gravity = 0.15; _flare.velocity = 0.25 * actor.velocity + (v_forward + randomvec() * 0.25)* -500; setthink(_flare, raptor_flare_think); @@ -91,15 +91,15 @@ void raptor_bomblet_boom(entity this) autocvar_g_vehicle_raptor_bomblet_edgedamage, autocvar_g_vehicle_raptor_bomblet_radius, NULL, NULL, autocvar_g_vehicle_raptor_bomblet_force, DEATH_VH_RAPT_BOMB.m_id, NULL); - remove(this); + delete(this); } -void raptor_bomblet_touch(entity this) +void raptor_bomblet_touch(entity this, entity toucher) { - if(other == this.owner) + if(toucher == this.owner) return; - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); setthink(this, raptor_bomblet_boom); this.nextthink = time + random() * autocvar_g_vehicle_raptor_bomblet_explode_delay; } @@ -128,7 +128,7 @@ void raptor_bomb_burst(entity this) bomblet = spawn(); setorigin(bomblet, this.origin); - bomblet.movetype = MOVETYPE_TOSS; + set_movetype(bomblet, MOVETYPE_TOSS); settouch(bomblet, raptor_bomblet_touch); setthink(bomblet, raptor_bomblet_boom); bomblet.nextthink = time + 5; @@ -140,7 +140,12 @@ void raptor_bomb_burst(entity this) CSQCProjectile(bomblet, true, PROJECTILE_RAPTORBOMBLET, true); } - remove(this); + delete(this); +} + +void raptor_bomb_touch(entity this, entity toucher) +{ + raptor_bomb_burst(this); } void raptor_bombdrop(entity this) @@ -155,10 +160,11 @@ void raptor_bombdrop(entity this) org = gettaginfo(this, gettagindex(this, "bombmount_right")); setorigin(bomb_2, org); - bomb_1.movetype = bomb_2.movetype = MOVETYPE_BOUNCE; + set_movetype(bomb_1, MOVETYPE_BOUNCE); + set_movetype(bomb_2, MOVETYPE_BOUNCE); bomb_1.velocity = bomb_2.velocity = this.velocity; - settouch(bomb_1, raptor_bomb_burst); - settouch(bomb_2, raptor_bomb_burst); + settouch(bomb_1, raptor_bomb_touch); + settouch(bomb_2, raptor_bomb_touch); setthink(bomb_1, raptor_bomb_burst); setthink(bomb_2, raptor_bomb_burst); bomb_1.cnt = bomb_2.cnt = time + 10; @@ -180,33 +186,30 @@ void raptor_bombdrop(entity this) CSQCProjectile(bomb_2, true, PROJECTILE_RAPTORBOMB, true); } -void raptor_flare_touch(entity this) +void raptor_flare_touch(entity this, entity toucher) { - remove(this); + delete(this); } void raptor_flare_damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) { this.health -= damage; if(this.health <= 0) - remove(this); + delete(this); } void raptor_flare_think(entity this) { this.nextthink = time + 0.1; - entity _missile = findchainentity(enemy, this.owner); - while(_missile) + IL_EACH(g_projectiles, it.enemy == this.owner, { - if(_missile.flags & FL_PROJECTILE) - if(vdist(this.origin - _missile.origin, <, autocvar_g_vehicle_raptor_flare_range)) + if(vdist(this.origin - it.origin, <, autocvar_g_vehicle_raptor_flare_range)) if(random() > autocvar_g_vehicle_raptor_flare_chase) - _missile.enemy = this; - _missile = _missile.chain; - } + it.enemy = this; + }); if(this.tur_impacttime < time) - remove(this); + delete(this); } #endif @@ -219,14 +222,14 @@ void RaptorCBShellfragDraw(entity this) return; Movetype_Physics_MatchTicrate(this, autocvar_cl_gibs_ticrate, autocvar_cl_gibs_sloppy); - this.move_avelocity += randomvec() * 15; + this.avelocity += randomvec() * 15; this.renderflags = 0; if(this.cnt < time) this.alpha = bound(0, this.nextthink - time, 1); if(this.alpha < ALPHA_MIN_VISIBLE) - remove(this); + delete(this); } void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang) @@ -235,16 +238,16 @@ void RaptorCBShellfragToss(vector _org, vector _vel, vector _ang) setmodel(sfrag, MDL_VEH_RAPTOR_CB_FRAGMENT); setorigin(sfrag, _org); - sfrag.move_movetype = MOVETYPE_BOUNCE; + set_movetype(sfrag, MOVETYPE_BOUNCE); sfrag.gravity = 0.15; sfrag.solid = SOLID_CORPSE; sfrag.draw = RaptorCBShellfragDraw; + IL_PUSH(g_drawables, sfrag); - sfrag.move_origin = sfrag.origin = _org; - sfrag.move_velocity = _vel; - sfrag.move_avelocity = prandomvec() * vlen(sfrag.move_velocity); - sfrag.angles = sfrag.move_angles = _ang; + sfrag.velocity = _vel; + sfrag.avelocity = prandomvec() * vlen(sfrag.velocity); + sfrag.angles = _ang; sfrag.move_time = time; sfrag.damageforcescale = 4; diff --git a/qcsrc/common/vehicles/vehicle/raptor_weapons.qh b/qcsrc/common/vehicles/vehicle/raptor_weapons.qh index 0b3af4169d..a7908d50d3 100644 --- a/qcsrc/common/vehicles/vehicle/raptor_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/raptor_weapons.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include CLASS(RaptorCannon, PortoLaunch) /* flags */ ATTRIB(RaptorCannon, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); @@ -26,7 +26,7 @@ CLASS(RaptorFlare, PortoLaunch) ENDCLASS(RaptorFlare) REGISTER_WEAPON(RAPTOR_FLARE, NEW(RaptorFlare)); - +#ifdef SVQC float autocvar_g_vehicle_raptor_cannon_cost = 1; float autocvar_g_vehicle_raptor_cannon_damage = 10; float autocvar_g_vehicle_raptor_cannon_radius = 60; @@ -51,3 +51,4 @@ float autocvar_g_vehicle_raptor_flare_refire = 5; float autocvar_g_vehicle_raptor_flare_lifetime = 10; float autocvar_g_vehicle_raptor_flare_chase = 0.9; float autocvar_g_vehicle_raptor_flare_range = 2000; +#endif diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qc b/qcsrc/common/vehicles/vehicle/spiderbot.qc index d41e81ce61..6ad46946d7 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qc @@ -1,29 +1,4 @@ -#ifndef VEHICLE_SPIDERBOT -#define VEHICLE_SPIDERBOT - -#include "spiderbot_weapons.qh" - -CLASS(Spiderbot, Vehicle) -/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE); -/* mins */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10'); -/* maxs */ ATTRIB(Spiderbot, maxs, vector, '75 75 125'); -/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70'); -/* view dist */ ATTRIB(Spiderbot, height, float, 170); -/* model */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm"); -/* model */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm"); -/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm"); -/* hud_model */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); -/* tags */ ATTRIB(Spiderbot, tag_head, string, "tag_head"); -/* tags */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud"); -/* tags */ ATTRIB(Spiderbot, tag_view, string, ""); -/* netname */ ATTRIB(Spiderbot, netname, string, "spiderbot"); -/* fullname */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot")); -/* icon */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider"); -ENDCLASS(Spiderbot) - -REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot)); - -#endif +#include "spiderbot.qh" #ifdef IMPLEMENTATION @@ -68,15 +43,16 @@ float autocvar_g_vehicle_spiderbot_shield_regen_pause = 0.35; vector autocvar_g_vehicle_spiderbot_bouncepain = '0 0 0'; .float jump_delay; -bool spiderbot_frame(entity this) +bool spiderbot_frame(entity this, float dt) { entity vehic = this.vehicle; return = true; - if(intermission_running) + if(gameover) { - vehic.velocity = '0 0 0'; - vehic.avelocity = '0 0 0'; + vehic.solid = SOLID_NOT; + vehic.takedamage = DAMAGE_NO; + set_movetype(vehic, MOVETYPE_NONE); return; } @@ -116,7 +92,7 @@ bool spiderbot_frame(entity this) //UpdateAuxiliaryXhair(this, trace_endpos, ('1 0 0' * this.vehicle_reload2) + ('0 1 0' * (1 - this.vehicle_reload2)), 2); // Rotate head - float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * sys_frametime; + float ftmp = autocvar_g_vehicle_spiderbot_head_turnspeed * PHYS_INPUT_FRAMETIME; ad_y = bound(-ftmp, ad_y, ftmp); vehic.tur_head.angles_y = bound(autocvar_g_vehicle_spiderbot_head_turnlimit * -1, vehic.tur_head.angles_y + ad_y, autocvar_g_vehicle_spiderbot_head_turnlimit); @@ -193,9 +169,9 @@ bool spiderbot_frame(entity this) { // Turn Body if(this.movement_x == 0 && this.movement_y != 0) - ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * sys_frametime; + ftmp = autocvar_g_vehicle_spiderbot_turnspeed_strafe * PHYS_INPUT_FRAMETIME; else - ftmp = autocvar_g_vehicle_spiderbot_turnspeed * sys_frametime; + ftmp = autocvar_g_vehicle_spiderbot_turnspeed * PHYS_INPUT_FRAMETIME; ftmp = bound(-ftmp, vehic.tur_head.angles_y, ftmp); vehic.angles_y = anglemods(vehic.angles_y + ftmp); @@ -221,7 +197,7 @@ bool spiderbot_frame(entity this) vehic.velocity_z = oldvelz; float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1); if(vehic.velocity_z <= 20) // not while jumping - vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity; + vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity; if(IS_ONGROUND(vehic)) if(vehic.sound_nexttime < time || vehic.delay != 1) { @@ -251,7 +227,7 @@ bool spiderbot_frame(entity this) vehic.velocity_z = oldvelz; float g = ((autocvar_sv_gameplayfix_gravityunaffectedbyticrate) ? 0.5 : 1); if(vehic.velocity_z <= 20) // not while jumping - vehic.velocity_z -= g * sys_frametime * autocvar_sv_gravity; + vehic.velocity_z -= g * PHYS_INPUT_FRAMETIME * autocvar_sv_gravity; if(IS_ONGROUND(vehic)) if(vehic.sound_nexttime < time || vehic.delay != 2) { @@ -306,16 +282,16 @@ bool spiderbot_frame(entity this) else vehicles_regen(vehic, vehic.cnt, vehicle_ammo1, autocvar_g_vehicle_spiderbot_minigun_ammo_max, autocvar_g_vehicle_spiderbot_minigun_ammo_regen_pause, - autocvar_g_vehicle_spiderbot_minigun_ammo_regen, frametime, false); + autocvar_g_vehicle_spiderbot_minigun_ammo_regen, dt, false); spiderbot_rocket_do(vehic); if(vehic.vehicle_flags & VHF_SHIELDREGEN) - 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, frametime, true); + 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, frametime, false); + 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); PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; //this.vehicle_ammo2 = vehic.tur_head.frame; @@ -337,24 +313,18 @@ bool spiderbot_frame(entity this) void spiderbot_exit(entity this, int eject) { - entity e; vector spot; - e = findchain(classname,"spiderbot_rocket"); - while(e) + IL_EACH(g_projectiles, it.owner == this.owner && it.classname == "spiderbot_rocket", { - if(e.owner == this.owner) - { - e.realowner = this.owner; - e.owner = NULL; - } - e = e.chain; - } + it.realowner = this.owner; + it.owner = NULL; + }); setthink(this, vehicles_think); this.nextthink = time; this.frame = 5; - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); if(!this.owner) return; @@ -405,7 +375,7 @@ void spiderbot_headfade(entity this) sound (this, CH_SHOTS, SND_ROCKET_IMPACT, VOL_BASE, ATTEN_NORM); Send_Effect(EFFECT_EXPLOSION_BIG, this.origin + '0 0 100', '0 0 0', 1); } - remove(this); + delete(this); } } @@ -440,7 +410,7 @@ void spiderbot_blowup(entity this) vector org = gettaginfo(this, gettagindex(this, "tag_head")); setorigin(h, org); - h.movetype = MOVETYPE_BOUNCE; + set_movetype(h, MOVETYPE_BOUNCE); h.solid = SOLID_BBOX; h.velocity = v_up * (500 + random() * 500) + randomvec() * 128; h.modelflags = MF_ROCKET; @@ -456,14 +426,14 @@ void spiderbot_blowup(entity this) org = gettaginfo(this.tur_head, gettagindex(this.tur_head, "tag_hardpoint01")); setorigin(g1, org); - g1.movetype = MOVETYPE_TOSS; + set_movetype(g1, MOVETYPE_TOSS); g1.solid = SOLID_CORPSE; g1.velocity = v_forward * 700 + (randomvec() * 32); g1.avelocity = randomvec() * 180; org = gettaginfo(this.tur_head, gettagindex(this.tur_head, "tag_hardpoint02")); setorigin(g2, org); - g2.movetype = MOVETYPE_TOSS; + set_movetype(g2, MOVETYPE_TOSS); g2.solid = SOLID_CORPSE; g2.velocity = v_forward * 700 + (randomvec() * 32); g2.avelocity = randomvec() * 180; @@ -478,7 +448,7 @@ void spiderbot_blowup(entity this) RadiusDamage (this, this.enemy, 250, 15, 250, NULL, NULL, 250, DEATH_VH_SPID_DEATH.m_id, NULL); this.alpha = this.tur_head.alpha = this.gun1.alpha = this.gun2.alpha = -1; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.deadflag = DEAD_DEAD; this.solid = SOLID_NOT; this.tur_head.effects &= ~EF_FLAME; @@ -536,8 +506,8 @@ bool spiderbot_impulse(entity this, int _imp) spawnfunc(vehicle_spiderbot) { - if(!autocvar_g_vehicle_spiderbot) { remove(this); return; } - if(!vehicle_initialize(this, VEH_SPIDERBOT, false)) { remove(this); return; } + if(!autocvar_g_vehicle_spiderbot) { delete(this); return; } + if(!vehicle_initialize(this, VEH_SPIDERBOT, false)) { delete(this); return; } } METHOD(Spiderbot, vr_impact, void(Spiderbot thisveh, entity instance)) @@ -548,7 +518,7 @@ METHOD(Spiderbot, vr_impact, void(Spiderbot thisveh, entity instance)) METHOD(Spiderbot, vr_enter, void(Spiderbot thisveh, entity instance)) { instance.vehicle_weapon2mode = SBRM_GUIDE; - instance.movetype = MOVETYPE_WALK; + 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_shield = (instance.vehicle_shield / autocvar_g_vehicle_spiderbot_shield) * 100; @@ -578,7 +548,7 @@ METHOD(Spiderbot, vr_death, void(Spiderbot thisveh, entity instance)) instance.tur_head.effects |= EF_FLAME; instance.colormod = instance.tur_head.colormod = '-1 -1 -1'; instance.frame = 10; - instance.movetype = MOVETYPE_TOSS; + set_movetype(instance, MOVETYPE_TOSS); CSQCModel_UnlinkEntity(instance); // networking the death scene would be a nightmare } @@ -599,7 +569,7 @@ METHOD(Spiderbot, vr_spawn, void(Spiderbot thisveh, entity instance)) instance.frame = 5; instance.tur_head.frame = 1; - instance.movetype = MOVETYPE_WALK; + set_movetype(instance, MOVETYPE_WALK); instance.solid = SOLID_SLIDEBOX; instance.alpha = instance.tur_head.alpha = instance.gun1.alpha = instance.gun2.alpha = 1; instance.tur_head.angles = '0 0 0'; @@ -633,8 +603,8 @@ METHOD(Spiderbot, vr_setup, void(Spiderbot thisveh, entity instance)) #endif // SVQC #ifdef CSQC -float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; -float autocvar_cl_vehicle_spiderbot_cross_size = 1; +//float autocvar_cl_vehicle_spiderbot_cross_alpha = 0.6; +//float autocvar_cl_vehicle_spiderbot_cross_size = 1; METHOD(Spiderbot, vr_hud, void(Spiderbot thisveh)) { diff --git a/qcsrc/common/vehicles/vehicle/spiderbot.qh b/qcsrc/common/vehicles/vehicle/spiderbot.qh new file mode 100644 index 0000000000..a594ace048 --- /dev/null +++ b/qcsrc/common/vehicles/vehicle/spiderbot.qh @@ -0,0 +1,23 @@ +#pragma once + +#include "spiderbot_weapons.qh" + +CLASS(Spiderbot, Vehicle) +/* spawnflags */ ATTRIB(Spiderbot, spawnflags, int, VHF_DMGSHAKE); +/* mins */ ATTRIB(Spiderbot, mins, vector, '-75 -75 10'); +/* maxs */ ATTRIB(Spiderbot, maxs, vector, '75 75 125'); +/* view offset*/ ATTRIB(Spiderbot, view_ofs, vector, '0 0 70'); +/* view dist */ ATTRIB(Spiderbot, height, float, 170); +/* model */ ATTRIB(Spiderbot, mdl, string, "models/vehicles/spiderbot.dpm"); +/* model */ ATTRIB(Spiderbot, model, string, "models/vehicles/spiderbot.dpm"); +/* head_model */ ATTRIB(Spiderbot, head_model, string, "models/vehicles/spiderbot_top.dpm"); +/* hud_model */ ATTRIB(Spiderbot, hud_model, string, "models/vehicles/spiderbot_cockpit.dpm"); +/* tags */ ATTRIB(Spiderbot, tag_head, string, "tag_head"); +/* tags */ ATTRIB(Spiderbot, tag_hud, string, "tag_hud"); +/* tags */ ATTRIB(Spiderbot, tag_view, string, ""); +/* netname */ ATTRIB(Spiderbot, netname, string, "spiderbot"); +/* fullname */ ATTRIB(Spiderbot, vehicle_name, string, _("Spiderbot")); +/* icon */ ATTRIB(Spiderbot, m_icon, string, "vehicle_spider"); +ENDCLASS(Spiderbot) + +REGISTER_VEHICLE(SPIDERBOT, NEW(Spiderbot)); diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc index 27177019ef..5f7a2c31c8 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc +++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qc @@ -48,21 +48,18 @@ void spiderbot_rocket_guided(entity this) void spiderbot_guide_release(entity this) { - entity rkt; - rkt = findchainentity(realowner, this.owner); - if(!rkt) - return; - - crosshair_trace(this.owner); - while(rkt) + bool donetrace = false; + IL_EACH(g_projectiles, it.realowner == this.owner && getthink(it) == spiderbot_rocket_guided, { - if(getthink(rkt) == spiderbot_rocket_guided) + if(!donetrace) // something exists, let's trace! { - rkt.pos1 = trace_endpos; - setthink(rkt, spiderbot_rocket_unguided); + donetrace = true; + crosshair_trace(this.owner); } - rkt = rkt.chain; - } + + it.pos1 = trace_endpos; + setthink(it, spiderbot_rocket_unguided); + }); } float spiberbot_calcartillery_flighttime; @@ -140,7 +137,7 @@ vector spiberbot_calcartillery(vector org, vector tgt, float ht) } void spiderbot_rocket_do(entity this) -{; +{ vector v; entity rocket = NULL; @@ -196,7 +193,7 @@ void spiderbot_rocket_do(entity this) float _dist = (random() * autocvar_g_vehicle_spiderbot_rocket_radius) + vlen(v - trace_endpos); _dist -= (random() * autocvar_g_vehicle_spiderbot_rocket_radius) ; rocket.nextthink = time + (_dist / autocvar_g_vehicle_spiderbot_rocket_speed); - setthink(rocket, vehicles_projectile_explode); + setthink(rocket, vehicles_projectile_explode_think); if(PHYS_INPUT_BUTTON_ATCK2(this.owner) && this.tur_head.frame == 1) this.wait = -10; @@ -232,7 +229,7 @@ void spiderbot_rocket_do(entity this) float h2 = 0.75 * vlen(rocket.pos1 - v); rocket.velocity = spiberbot_calcartillery(v, rocket.pos1, ((h1 < h2) ? h1 : h2)); - rocket.movetype = MOVETYPE_TOSS; + set_movetype(rocket, MOVETYPE_TOSS); rocket.gravity = 1; //setthink(rocket, spiderbot_rocket_artillery); break; diff --git a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh index 61e2b02501..199d9cfa47 100644 --- a/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh +++ b/qcsrc/common/vehicles/vehicle/spiderbot_weapons.qh @@ -1,11 +1,12 @@ #pragma once -#include +#include #ifdef SVQC void spiderbot_rocket_do(entity this); #endif +#ifdef SVQC // 400 (x2) DPS float autocvar_g_vehicle_spiderbot_minigun_damage = 24; float autocvar_g_vehicle_spiderbot_minigun_refire = 0.06; @@ -30,3 +31,4 @@ float autocvar_g_vehicle_spiderbot_rocket_health = 100; float autocvar_g_vehicle_spiderbot_rocket_noise = 0.2; float autocvar_g_vehicle_spiderbot_rocket_turnrate = 0.25; float autocvar_g_vehicle_spiderbot_rocket_lifetime = 20; +#endif diff --git a/qcsrc/common/vehicles/vehicles.qc b/qcsrc/common/vehicles/vehicles.qc new file mode 100644 index 0000000000..5e7736501b --- /dev/null +++ b/qcsrc/common/vehicles/vehicles.qc @@ -0,0 +1 @@ +#include "vehicles.qh" diff --git a/qcsrc/common/vehicles/vehicles.qh b/qcsrc/common/vehicles/vehicles.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/vehicles/vehicles.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/viewloc.qc b/qcsrc/common/viewloc.qc index 23b3079222..300aed4fc7 100644 --- a/qcsrc/common/viewloc.qc +++ b/qcsrc/common/viewloc.qc @@ -1,3 +1,4 @@ +#include "viewloc.qh" #include "util.qh" #if defined(CSQC) @@ -74,6 +75,10 @@ void viewloc_SetTags(entity this) } vector old_camera_angle = '0 0 0'; +bool autocvar_cam_snap_close; +bool autocvar_cam_track; +bool autocvar_cam_snap_hard; +bool autocvar_cam_snap_unlock; void viewloc_SetViewLocation() { entity view = CSQCModel_server2csqc(player_localentnum - 1); @@ -81,7 +86,7 @@ void viewloc_SetViewLocation() //NOTE: the "cam_" cvars sould probably be changed out with a spawnflag or an entity key. I have it like this for my testing -- Player_2 if(view.viewloc && !wasfreed(view.viewloc) && view.viewloc.enemy && view.viewloc.goalentity) { - vector position_a, position_b, camera_position, camera_angle, forward, backward; + vector position_a, position_b, camera_position, camera_angle = '0 0 0', forward, backward; //vector scratch; position_a = view.viewloc.enemy.origin; @@ -99,20 +104,18 @@ void viewloc_SetViewLocation() camera_position = vec_bounds_in(view.origin, position_a, position_b); - camera_angle = '0 0 0'; - // a tracking camera follows the player when it leaves the world box - if (cvar("cam_track")) { + if (autocvar_cam_track) { camera_angle = aim_vec (camera_position, view.origin); } // hard snap changes the angle as soon as it crosses over the nearest 90 degree mark - if (cvar("cam_snap_hard")){ + if (autocvar_cam_snap_hard){ camera_angle = angle_snap_vec(aim_vec(camera_position, view.origin), 90); } // tries to avoid snapping unless it *really* needs to - if (cvar("cam_snap_close")){ + if (autocvar_cam_snap_close){ // like hard snap, but don't snap angles yet. camera_angle = aim_vec(camera_position, view.origin); @@ -130,15 +133,15 @@ void viewloc_SetViewLocation() } //unlocking this allows the camera to look up and down. this also allows a top-down view. - if (!cvar("cam_snap_unlock")) { + if (!autocvar_cam_snap_unlock) { camera_angle_x = 0; camera_angle_z = 0; } #if 0 - LOG_TRACE(vtos(camera_position), "\n"); - LOG_TRACE(vtos(old_camera_angle), "\n"); - LOG_TRACE(vtos(camera_angle), "\n"); + LOG_TRACE(vtos(camera_position)); + LOG_TRACE(vtos(old_camera_angle)); + LOG_TRACE(vtos(camera_angle)); #endif freeze_org = getpropertyvec(VF_ORIGIN); diff --git a/qcsrc/common/viewloc.qh b/qcsrc/common/viewloc.qh index 4c4d05f926..7725d2d8fd 100644 --- a/qcsrc/common/viewloc.qh +++ b/qcsrc/common/viewloc.qh @@ -1,5 +1,4 @@ -#ifndef VIEWLOC_H -#define VIEWLOC_H +#pragma once .entity viewloc; @@ -11,5 +10,3 @@ void viewloc_SetViewLocation(); void viewloc_SetTags(entity this); #endif - -#endif diff --git a/qcsrc/common/weapons/_all.inc b/qcsrc/common/weapons/_all.inc new file mode 100644 index 0000000000..213c39c621 --- /dev/null +++ b/qcsrc/common/weapons/_all.inc @@ -0,0 +1,2 @@ +#include "_all.qh" +#include "all.qc" diff --git a/qcsrc/common/weapons/_all.qh b/qcsrc/common/weapons/_all.qh new file mode 100644 index 0000000000..671dde06df --- /dev/null +++ b/qcsrc/common/weapons/_all.qh @@ -0,0 +1,2 @@ +#pragma once +#include "all.qh" diff --git a/qcsrc/common/weapons/all.qc b/qcsrc/common/weapons/all.qc index 897e2caef2..763dd6e8de 100644 --- a/qcsrc/common/weapons/all.qc +++ b/qcsrc/common/weapons/all.qc @@ -1,8 +1,7 @@ +#include "all.qh" #ifndef WEAPONS_ALL_C #define WEAPONS_ALL_C -#include "all.qh" - #if defined(CSQC) #include #include "../constants.qh" @@ -19,6 +18,7 @@ #include #elif defined(MENUQC) #elif defined(SVQC) + #include #include #include #include @@ -27,7 +27,7 @@ #include "../stats.qh" #include "../teams.qh" #include "../util.qh" - #include "../monsters/all.qh" + #include "../monsters/_mod.qh" #include "config.qh" #include #include @@ -37,14 +37,14 @@ #include #include "../notifications/all.qh" #include "../deathtypes/all.qh" - #include + #include #include "../mapinfo.qh" - #include + #include #include #include #include #endif -#ifndef MENUQC +#ifdef GAMEQC #include "calculations.qc" #endif #ifdef SVQC @@ -204,7 +204,7 @@ void W_RandomWeapons(entity e, float n) RandomSelection_Init(); FOREACH(Weapons, it != WEP_Null, { if (remaining & (it.m_wepset)) - RandomSelection_Add(it, 0, string_null, 1, 1); + RandomSelection_AddEnt(it, 1, 1); }); Weapon w = RandomSelection_chosen_ent; result |= WepSet_FromWeapon(w); @@ -275,7 +275,7 @@ string W_Model(string w_mdl) return M_ARGV(1, string); } -#ifndef MENUQC +#ifdef GAMEQC vector shotorg_adjustfromclient(vector vecs, float y_is_right, float algn) { switch (algn) @@ -392,7 +392,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) if (name == "") { this.model = ""; - if (this.weaponchild) remove(this.weaponchild); + if (this.weaponchild) delete(this.weaponchild); this.weaponchild = NULL; this.movedir = '0 0 0'; this.spawnorigin = '0 0 0'; @@ -436,7 +436,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } else { - if (this.weaponchild) remove(this.weaponchild); + if (this.weaponchild) delete(this.weaponchild); this.weaponchild = NULL; } @@ -462,7 +462,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } else { - LOG_WARNINGF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong\n", + LOG_WARNF("weapon model %s does not support the 'shot' tag, will display shots TOTALLY wrong", this.model); this.movedir = '0 0 0'; } @@ -481,7 +481,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } else { - LOG_WARNINGF("weapon model %s does not support the 'shell' tag, will display casings wrong\n", + LOG_WARNF("weapon model %s does not support the 'shell' tag, will display casings wrong", this.model); this.spawnorigin = this.movedir; } @@ -503,7 +503,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } else { - LOG_WARNINGF( + LOG_WARNF( "weapon model %s does not support the 'handle' tag " "and neither does the v_ model support the 'shot' tag, " "will display muzzle flashes TOTALLY wrong\n", @@ -547,7 +547,7 @@ void CL_WeaponEntity_SetModel(entity this, string name, bool _anim) } #endif -#ifndef MENUQC +#ifdef GAMEQC REGISTER_NET_TEMP(wframe) #ifdef CSQC diff --git a/qcsrc/common/weapons/all.qh b/qcsrc/common/weapons/all.qh index dd691a6921..a805ce5fd4 100644 --- a/qcsrc/common/weapons/all.qh +++ b/qcsrc/common/weapons/all.qh @@ -1,7 +1,6 @@ -#ifndef WEAPONS_ALL_H -#define WEAPONS_ALL_H +#pragma once -#include +#include #include #include "config.qh" @@ -19,22 +18,22 @@ WepSet ReadWepSet(); #include "weapon.qh" -#ifndef MENUQC +#ifdef GAMEQC #include "calculations.qh" #include #endif #include -#ifdef SVQC -#include -#endif - REGISTRY(Weapons, 72) // Increase as needed. Can be up to 72. #define Weapons_from(i) _Weapons_from(i, WEP_Null) REGISTER_REGISTRY(Weapons) STATIC_INIT(WeaponPickup) { FOREACH(Weapons, true, it.m_pickup = NEW(WeaponPickup, it)); } +#ifdef SVQC +#include +#endif + .WepSet m_wepset; #define WEPSET(id) (WEP_##id.m_wepset) #define WepSet_FromWeapon(it) ((it).m_wepset) @@ -144,10 +143,12 @@ X(weaponreplace, string) X(weaponstartoverride, float) X(weaponstart, float) X(weaponthrowable, float) +#ifdef SVQC X(reload_ammo, float) .float reloading_ammo = reload_ammo; X(reload_time, float) .float reloading_time = reload_time; +#endif #undef X @@ -299,6 +300,7 @@ REGISTRY_CHECK(Weapons) STATIC_INIT(register_weapons_done) { + string inaccessible = ""; FOREACH(Weapons, true, { WepSet set = it.m_wepset = _WepSet_FromWeapon(it.m_id = i); WEPSET_ALL |= set; @@ -309,8 +311,9 @@ STATIC_INIT(register_weapons_done) if (imp <= WEP_IMPULSE_END) localcmd(sprintf("alias weapon_%s \"impulse %d\"\n", it.netname, imp)); else - LOG_TRACEF("Impulse limit exceeded, weapon will not be directly accessible: %s\n", it.netname); + inaccessible = strcat(inaccessible, "\n", it.netname); }); + if (inaccessible) LOG_TRACEF("Impulse limit exceeded, weapon(s) will not be directly accessible: %s", inaccessible); #ifdef CSQC FOREACH(Weapons, true, it.wr_init(it)); #endif @@ -321,14 +324,14 @@ STATIC_INIT(register_weapons_done) weaponorder_byid = strzone(substring(weaponorder_byid, 1, -1)); } -#ifndef MENUQC +#ifdef GAMEQC .entity weaponchild; .entity exteriorweaponentity; -vector weaponentity_glowmod(Weapon wep, int c) +vector weaponentity_glowmod(Weapon wep, entity actor, int c) { vector g; - if (!(g = wep.wr_glow(wep))) g = colormapPaletteColor(c & 0x0F, true) * 2; + if (!(g = wep.wr_glow(wep, actor))) g = colormapPaletteColor(c & 0x0F, true) * 2; return g; } @@ -358,5 +361,3 @@ 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); #endif - -#endif diff --git a/qcsrc/common/weapons/calculations.qc b/qcsrc/common/weapons/calculations.qc index e8307f39d2..c35b1930c4 100644 --- a/qcsrc/common/weapons/calculations.qc +++ b/qcsrc/common/weapons/calculations.qc @@ -1,3 +1,5 @@ +#include "calculations.qh" + // ============================= // Explosion Force Calculation // ============================= @@ -256,7 +258,7 @@ vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float default: error("g_projectiles_spread_style must be 0 (sphere), 1 (flattened sphere), 2 (circle), 3 (gauss 3D), 4 (gauss plane), 5 (linear falloff), 6 (quadratic falloff), 7 (stronger falloff)!"); } - + return '0 0 0'; /* * how to derive falloff functions: diff --git a/qcsrc/common/weapons/calculations.qh b/qcsrc/common/weapons/calculations.qh index 4feed9fc54..c349eeca4f 100644 --- a/qcsrc/common/weapons/calculations.qh +++ b/qcsrc/common/weapons/calculations.qh @@ -1,6 +1,6 @@ -#ifndef CALCULATIONS_H -#define CALCULATIONS_H +#pragma once + vector damage_explosion_calcpush(vector explosion_f, vector target_v, float speedfactor); vector W_CalculateSpread(vector forward, float spread, float spreadfactor, float spreadstyle); int W_GetGunAlignment(entity player); -#endif +float explosion_calcpush_getmultiplier(vector explosion_v, vector target_v); diff --git a/qcsrc/common/weapons/config.qc b/qcsrc/common/weapons/config.qc index 26a05d10ac..4f6177b478 100644 --- a/qcsrc/common/weapons/config.qc +++ b/qcsrc/common/weapons/config.qc @@ -1,8 +1,8 @@ +#include "config.qh" #if defined(CSQC) #elif defined(MENUQC) #elif defined(SVQC) #include "../util.qh" - #include "config.qh" #include "all.qh" #endif diff --git a/qcsrc/common/weapons/config.qh b/qcsrc/common/weapons/config.qh index d88e18194b..753147307b 100644 --- a/qcsrc/common/weapons/config.qh +++ b/qcsrc/common/weapons/config.qh @@ -1,5 +1,4 @@ -#ifndef WEAPONS_CONFIG_H -#define WEAPONS_CONFIG_H +#pragma once #ifdef SVQC // ========================== @@ -37,4 +36,3 @@ string wep_config_queue[MAX_WEP_CONFIG]; cvar(sprintf("g_balance_%s_%s", #wepname, #name)))) } #endif -#endif diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index 4de39db177..b287b148f3 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -1,5 +1,5 @@ -#ifndef WEAPON_H -#define WEAPON_H +#pragma once + #include #include @@ -54,7 +54,7 @@ const int WS_READY = 4; /** fields which are explicitly/manually set are marked with "M", fields set automatically are marked with "A" */ CLASS(Weapon, Object) - ATTRIB(Weapon, m_id, int, 0) + ATTRIB(Weapon, m_id, int, 0); /** A: WEPSET_id : WEPSET_... */ ATTRIB(Weapon, weapons, WepSet, '0 0 0'); /** M: ammotype : main ammo field */ @@ -70,7 +70,7 @@ CLASS(Weapon, Object) /** M: modelname : name of model (without g_ v_ or h_ prefixes) */ ATTRIB(Weapon, mdl, string, ""); /** M: model MDL_id_ITEM */ - ATTRIB(Weapon, m_model, entity, NULL); + ATTRIB(Weapon, m_model, entity); /** M: crosshair : per-weapon crosshair: "CrosshairImage Size" */ ATTRIB(Weapon, w_crosshair, string, "gfx/crosshair1"); /** A: crosshair : per-weapon crosshair size (argument two of "crosshair" field) */ @@ -82,7 +82,7 @@ CLASS(Weapon, Object) /** M: wepname : human readable name */ ATTRIB(Weapon, m_name, string, "AOL CD Thrower"); - ATTRIB(Weapon, m_pickup, entity, NULL); + ATTRIB(Weapon, m_pickup, entity); /** (SERVER) setup weapon data */ METHOD(Weapon, wr_setup, void(Weapon this, entity actor)) {} @@ -107,9 +107,9 @@ CLASS(Weapon, Object) /** (CLIENT) impact effect for weapon explosion */ METHOD(Weapon, wr_impacteffect, void(Weapon this, entity actor)) {} /** (SERVER) called whenever a player dies */ - METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor)) {} + METHOD(Weapon, wr_playerdeath, void(Weapon this, entity actor, .entity weaponentity)) {} /** (SERVER) logic to run when weapon is lost */ - METHOD(Weapon, wr_gonethink, void(Weapon this, entity actor)) {} + METHOD(Weapon, wr_gonethink, void(Weapon this, entity actor, .entity weaponentity)) {} /** (ALL) dump weapon cvars to config in data directory (see: sv_cmd dumpweapons) */ METHOD(Weapon, wr_config, void(Weapon this)) {} /** (CLIENT) weapon specific zoom reticle */ @@ -117,8 +117,10 @@ CLASS(Weapon, Object) // no weapon specific image for this weapon return false; } + /** (CLIENT) weapon specific view model */ + METHOD(Weapon, wr_viewmodel, string(Weapon this, entity wep)) { return string_null; } /** (CLIENT) weapon specific glow */ - METHOD(Weapon, wr_glow, vector(Weapon this)) { return '0 0 0'; } + METHOD(Weapon, wr_glow, vector(Weapon this, entity actor)) { return '0 0 0'; } /** (SERVER) the weapon is dropped */ METHOD(Weapon, wr_drop, void(Weapon this, entity actor)) {} /** (SERVER) a weapon is picked up */ @@ -130,23 +132,23 @@ CLASS(Weapon, Object) } ENDCLASS(Weapon) -#include +#include CLASS(WeaponPickup, Pickup) - ATTRIB(WeaponPickup, m_weapon, Weapon, NULL) - ATTRIB(WeaponPickup, m_name, string, string_null) -#ifndef MENUQC - ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP) + ATTRIB(WeaponPickup, m_weapon, Weapon); + ATTRIB(WeaponPickup, m_name, string); +#ifdef GAMEQC + ATTRIB(WeaponPickup, m_sound, Sound, SND_WEAPONPICKUP); #endif #ifdef SVQC - ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON) + ATTRIB(WeaponPickup, m_itemflags, int, FL_WEAPON); float weapon_pickupevalfunc(entity player, entity item); - ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc) + ATTRIB(WeaponPickup, m_pickupevalfunc, float(entity player, entity item), weapon_pickupevalfunc); #endif CONSTRUCTOR(WeaponPickup, Weapon w) { CONSTRUCT(WeaponPickup); this.m_weapon = w; this.m_name = w.m_name; -#ifndef MENUQC +#ifdef GAMEQC this.m_model = w.m_model; #endif #ifdef SVQC @@ -158,7 +160,7 @@ CLASS(WeaponPickup, Pickup) { bool b = Item_GiveTo(item, player); if (b) { - LOG_TRACEF("entity %i picked up %s\n", player, this.m_name); + LOG_TRACEF("entity %i picked up %s", player, this.m_name); } return b; } @@ -175,11 +177,6 @@ ENDCLASS(OffhandWeapon) const int MAX_SHOT_DISTANCE = 32768; -// weapon pickup ratings for bot logic -const int BOT_PICKUP_RATING_LOW = 2500; -const int BOT_PICKUP_RATING_MID = 5000; -const int BOT_PICKUP_RATING_HIGH = 10000; - // weapon flags const int WEP_TYPE_OTHER = 0x00; // not for damaging people const int WEP_TYPE_SPLASH = 0x01; // splash damage @@ -191,6 +188,8 @@ const int WEP_FLAG_HIDDEN = 0x40; // hides from menu const int WEP_FLAG_RELOADABLE = 0x80; // can has reload const int WEP_FLAG_SUPERWEAPON = 0x100; // powerup timer const int WEP_FLAG_MUTATORBLOCKED = 0x200; // hides from impulse 99 etc. (mutators are allowed to clear this flag) +const int WEP_TYPE_MELEE_PRI = 0x400; // primary attack is melee swing (for animation) +const int WEP_TYPE_MELEE_SEC = 0x800; // secondary attack is melee swing (for animation) // variables: string weaponorder_byid; @@ -214,5 +213,3 @@ int GetAmmoStat(.int ammotype); string W_Sound(string w_snd); string W_Model(string w_mdl); - -#endif diff --git a/qcsrc/common/weapons/weapon/arc.qc b/qcsrc/common/weapons/weapon/arc.qc index 9262698daa..4c9415305a 100644 --- a/qcsrc/common/weapons/weapon/arc.qc +++ b/qcsrc/common/weapons/weapon/arc.qc @@ -1,12 +1,13 @@ +#include "arc.qh" #ifndef IMPLEMENTATION CLASS(Arc, Weapon) -/* ammotype */ ATTRIB(Arc, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(Arc, impulse, int, 3) +/* ammotype */ ATTRIB(Arc, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(Arc, impulse, int, 3); /* flags */ ATTRIB(Arc, spawnflags, int, WEP_FLAG_NORMAL); /* rating */ ATTRIB(Arc, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Arc, wpcolor, vector, '1 1 1'); /* modelname */ ATTRIB(Arc, mdl, string, "arc"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Arc, m_model, Model, MDL_ARC_ITEM); #endif /* crosshair */ ATTRIB(Arc, w_crosshair, string, "gfx/crosshairhlac"); @@ -74,7 +75,7 @@ ENDCLASS(Arc) REGISTER_WEAPON(ARC, arc, NEW(Arc)); -#ifndef MENUQC +#ifdef GAMEQC const float ARC_MAX_SEGMENTS = 20; vector arc_shotorigin[4]; .vector beam_start; @@ -101,7 +102,7 @@ const int ARC_SF_LOCALMASK = ARC_SF_START | ARC_SF_WANTDIR | ARC_SF_BEAMDIR; #endif #ifdef SVQC .entity arc_beam; -.bool arc_BUTTON_ATCK_prev; // for better animation control +.bool arc_BUTTON_ATCK_prev[MAX_WEAPONSLOTS]; // for better animation control .float beam_prev; .float beam_initialized; .float beam_bursting; @@ -199,14 +200,17 @@ bool W_Arc_Beam_Send(entity this, entity to, int sf) void Reset_ArcBeam(entity player, vector forward) { - if (!player.arc_beam) { - return; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(!player.(weaponentity).arc_beam) + continue; + player.(weaponentity).arc_beam.beam_dir = forward; + player.(weaponentity).arc_beam.beam_teleporttime = time; } - player.arc_beam.beam_dir = forward; - player.arc_beam.beam_teleporttime = time; } -float Arc_GetHeat_Percent(entity player) +float Arc_GetHeat_Percent(entity player, .entity weaponentity) { if ( WEP_CVAR(arc, overheat_max) <= 0 || WEP_CVAR(arc, overheat_max) <= 0 ) { @@ -214,8 +218,8 @@ float Arc_GetHeat_Percent(entity player) return 0; } - if ( player.arc_beam ) - return player.arc_beam.beam_heat/WEP_CVAR(arc, overheat_max); + if ( player.(weaponentity).arc_beam ) + return player.(weaponentity).arc_beam.beam_heat/WEP_CVAR(arc, overheat_max); if ( player.arc_overheat > time ) { @@ -225,23 +229,23 @@ float Arc_GetHeat_Percent(entity player) return 0; } -void Arc_Player_SetHeat(entity player) +void Arc_Player_SetHeat(entity player, .entity weaponentity) { - player.arc_heat_percent = Arc_GetHeat_Percent(player); + player.arc_heat_percent = Arc_GetHeat_Percent(player, weaponentity); //dprint("Heat: ",ftos(player.arc_heat_percent*100),"%\n"); } -void W_Arc_Bolt_Explode(entity this) +void W_Arc_Bolt_Explode(entity this, entity directhitentity) { this.event_damage = func_null; - RadiusDamage(this, this.realowner, WEP_CVAR(arc, bolt_damage), WEP_CVAR(arc, bolt_edgedamage), WEP_CVAR(arc, bolt_radius), NULL, NULL, WEP_CVAR(arc, bolt_force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR(arc, bolt_damage), WEP_CVAR(arc, bolt_edgedamage), WEP_CVAR(arc, bolt_radius), NULL, NULL, WEP_CVAR(arc, bolt_force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } void W_Arc_Bolt_Explode_use(entity this, entity actor, entity trigger) { - W_Arc_Bolt_Explode(this); + W_Arc_Bolt_Explode(this, trigger); } void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -259,25 +263,26 @@ void W_Arc_Bolt_Damage(entity this, entity inflictor, entity attacker, float dam W_PrepareExplosionByDamage(this, attacker, getthink(this)); } -void W_Arc_Bolt_Touch(entity this) +void W_Arc_Bolt_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - this.use(this, NULL, NULL); + PROJECTILE_TOUCH(this, toucher); + this.use(this, NULL, toucher); } -void W_Arc_Attack_Bolt(Weapon thiswep, entity actor) +void W_Arc_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; W_DecreaseAmmo(thiswep, actor, WEP_CVAR(arc, bolt_ammo)); - W_SetupShot(actor, false, 2, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage)); + W_SetupShot(actor, weaponentity, false, 2, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR(arc, bolt_damage)); Send_Effect(EFFECT_ARC_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = new(missile); missile.owner = missile.realowner = actor; missile.bot_dodge = true; + IL_PUSH(g_bot_dodge, missile); missile.bot_dodgerating = WEP_CVAR(arc, bolt_damage); missile.takedamage = DAMAGE_YES; @@ -285,6 +290,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor) missile.damageforcescale = WEP_CVAR(arc, bolt_damageforcescale); missile.event_damage = W_Arc_Bolt_Damage; missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); settouch(missile, W_Arc_Bolt_Touch); missile.use = W_Arc_Bolt_Explode_use; @@ -295,7 +301,7 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor) setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); W_SetupProjVelocity_PRE(missile, arc, bolt_); missile.angles = vectoangles(missile.velocity); @@ -309,13 +315,13 @@ void W_Arc_Attack_Bolt(Weapon thiswep, entity actor) void W_Arc_Beam_Think(entity this) { - if(this != this.owner.arc_beam) + .entity weaponentity = this.weaponentity_fld; + if(this != this.owner.(weaponentity).arc_beam) { - remove(this); + delete(this); return; } - float burst = 0; if( (PHYS_INPUT_BUTTON_ATCK2(this.owner) && !WEP_CVAR(arc, bolt)) || this.beam_bursting) { @@ -333,11 +339,11 @@ void W_Arc_Beam_Think(entity this) || IS_DEAD(this.owner) || - gameover + forbidWeaponUse(this.owner) || - (!PHYS_INPUT_BUTTON_ATCK(this.owner) && !burst ) + PS(this.owner).m_switchweapon != WEP_ARC || - STAT(FROZEN, this.owner) + (!PHYS_INPUT_BUTTON_ATCK(this.owner) && !burst ) || this.owner.vehicle || @@ -371,7 +377,7 @@ void W_Arc_Beam_Think(entity this) } } - if(this == this.owner.arc_beam) { this.owner.arc_beam = NULL; } + if(this == this.owner.(weaponentity).arc_beam) { this.owner.(weaponentity).arc_beam = NULL; } entity own = this.owner; Weapon w = WEP_ARC; if(!w.wr_checkammo1(w, own) && !w.wr_checkammo2(w, own)) @@ -380,7 +386,7 @@ void W_Arc_Beam_Think(entity this) // note: this doesn't force the switch W_SwitchToOtherWeapon(own); } - remove(this); + delete(this); return; } @@ -407,6 +413,7 @@ void W_Arc_Beam_Think(entity this) W_SetupShot_Range( this.owner, + weaponentity, // TODO true, 0, SND_Null, @@ -674,29 +681,31 @@ void W_Arc_Beam_Think(entity this) this.nextthink = time; } -void W_Arc_Beam(float burst, entity actor) +void W_Arc_Beam(float burst, entity actor, .entity weaponentity) { // only play fire sound if 1 sec has passed since player let go the fire button if(time - actor.beam_prev > 1) sound(actor, CH_WEAPON_A, SND_ARC_FIRE, VOL_BASE, ATTN_NORM); - entity beam = actor.arc_beam = new(W_Arc_Beam); + entity beam = actor.(weaponentity).arc_beam = new(W_Arc_Beam); + beam.weaponentity_fld = weaponentity; beam.solid = SOLID_NOT; setthink(beam, W_Arc_Beam_Think); beam.owner = actor; - beam.movetype = MOVETYPE_NONE; + set_movetype(beam, MOVETYPE_NONE); beam.bot_dodge = true; + IL_PUSH(g_bot_dodge, beam); beam.bot_dodgerating = WEP_CVAR(arc, beam_damage); beam.beam_bursting = burst; Net_LinkEntity(beam, false, 0, W_Arc_Beam_Send); getthink(beam)(beam); } -void Arc_Smoke(entity actor) +void Arc_Smoke(entity actor, .entity weaponentity) { makevectors(actor.v_angle); - W_SetupShot_Range(actor,true,0,SND_Null,0,0,0); + W_SetupShot_Range(actor,weaponentity,true,0,SND_Null,0,0,0); vector smoke_origin = w_shotorg + actor.velocity*frametime; if ( actor.arc_overheat > time ) @@ -713,10 +722,10 @@ void Arc_Smoke(entity actor) } } } - else if ( actor.arc_beam && WEP_CVAR(arc, overheat_max) > 0 && - actor.arc_beam.beam_heat > WEP_CVAR(arc, overheat_min) ) + else if ( actor.(weaponentity).arc_beam && WEP_CVAR(arc, overheat_max) > 0 && + actor.(weaponentity).arc_beam.beam_heat > WEP_CVAR(arc, overheat_min) ) { - if ( random() < (actor.arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) / + if ( random() < (actor.(weaponentity).arc_beam.beam_heat-WEP_CVAR(arc, overheat_min)) / ( WEP_CVAR(arc, overheat_max)-WEP_CVAR(arc, overheat_min) ) ) Send_Effect(EFFECT_ARC_SMOKE, smoke_origin, '0 0 0', 1 ); } @@ -754,16 +763,17 @@ METHOD(Arc, wr_aim, void(entity thiswep, entity actor)) } METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { - Arc_Player_SetHeat(actor); - Arc_Smoke(actor); + Arc_Player_SetHeat(actor, weaponentity); + Arc_Smoke(actor, weaponentity); bool beam_fire2 = ((fire & 2) && !WEP_CVAR(arc, bolt)); + int slot = weaponslot(weaponentity); if (time >= actor.arc_overheat) - if ((fire & 1) || beam_fire2 || actor.arc_beam.beam_bursting) + if ((fire & 1) || beam_fire2 || actor.(weaponentity).arc_beam.beam_bursting) { - if(actor.arc_BUTTON_ATCK_prev) + if(actor.arc_BUTTON_ATCK_prev[slot]) { #if 0 if(actor.animstate_startframe == actor.anim_shoot.x && actor.animstate_numframes == actor.anim_shoot.y) @@ -773,16 +783,16 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i weapon_thinkf(actor, weaponentity, WFRAME_DONTCHANGE, WEP_CVAR(arc, beam_animtime), w_ready); } - if((!actor.arc_beam) || wasfreed(actor.arc_beam)) + if((!actor.(weaponentity).arc_beam) || wasfreed(actor.(weaponentity).arc_beam)) { if(weapon_prepareattack(thiswep, actor, weaponentity, boolean(beam_fire2), 0)) { - W_Arc_Beam(boolean(beam_fire2), actor); + W_Arc_Beam(boolean(beam_fire2), actor, weaponentity); - if(!actor.arc_BUTTON_ATCK_prev) + if(!actor.arc_BUTTON_ATCK_prev[slot]) { weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, beam_animtime), w_ready); - actor.arc_BUTTON_ATCK_prev = true; + actor.arc_BUTTON_ATCK_prev[slot] = true; } } } @@ -793,19 +803,18 @@ METHOD(Arc, wr_think, void(entity thiswep, entity actor, .entity weaponentity, i { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(arc, bolt_refire))) { - W_Arc_Attack_Bolt(thiswep, actor); + W_Arc_Attack_Bolt(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(arc, bolt_refire), w_ready); } } - if(actor.arc_BUTTON_ATCK_prev) + if(actor.arc_BUTTON_ATCK_prev[slot]) { 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); - int slot = weaponslot(weaponentity); ATTACK_FINISHED(actor, slot) = time + WEP_CVAR(arc, beam_refire) * W_WeaponRateFactor(actor); } - actor.arc_BUTTON_ATCK_prev = false; + actor.arc_BUTTON_ATCK_prev[slot] = false; #if 0 if(fire & 2) @@ -857,6 +866,8 @@ METHOD(Arc, wr_drop, void(entity thiswep, entity actor)) weapon_dropevent_item.arc_cooldown = actor.arc_cooldown; actor.arc_overheat = 0; actor.arc_cooldown = 0; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + actor.arc_BUTTON_ATCK_prev[slot] = false; } METHOD(Arc, wr_pickup, void(entity thiswep, entity actor)) { @@ -867,6 +878,20 @@ METHOD(Arc, wr_pickup, void(entity thiswep, entity actor)) actor.arc_cooldown = weapon_dropevent_item.arc_cooldown; } } +METHOD(Arc, wr_resetplayer, void(entity thiswep, entity actor)) +{ + actor.arc_overheat = 0; + actor.arc_cooldown = 0; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + actor.arc_BUTTON_ATCK_prev[slot] = false; +} +METHOD(Arc, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponentity)) +{ + actor.arc_overheat = 0; + actor.arc_cooldown = 0; + int slot = weaponslot(weaponentity); + actor.arc_BUTTON_ATCK_prev[slot] = false; +} #endif #ifdef CSQC bool autocvar_cl_arcbeam_teamcolor = true; @@ -1268,7 +1293,7 @@ void Draw_ArcBeam(entity this) void Remove_ArcBeam(entity this) { - remove(this.beam_muzzleentity); + delete(this.beam_muzzleentity); sound(this, CH_SHOTS_SINGLE, SND_Null, VOL_BASE, ATTEN_NORM); } @@ -1285,6 +1310,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) // set other main attributes of the beam this.draw = Draw_ArcBeam; + IL_PUSH(g_drawables, this); this.entremove = Remove_ArcBeam; this.move_time = time; loopsound(this, CH_SHOTS_SINGLE, SND(ARC_LOOP), VOL_BASE, ATTEN_NORM); @@ -1377,7 +1403,7 @@ NET_HANDLE(ENT_CLIENT_ARC_BEAM, bool isnew) { this.beam_type = ReadByte(); - vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true) : '1 1 1'); + vector beamcolor = ((autocvar_cl_arcbeam_teamcolor) ? colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true) : '1 1 1'); switch(this.beam_type) { case ARC_BT_MISS: diff --git a/qcsrc/common/weapons/weapon/arc.qh b/qcsrc/common/weapons/weapon/arc.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/arc.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/blaster.qc b/qcsrc/common/weapons/weapon/blaster.qc index a4fb13c44e..2eb8f00b41 100644 --- a/qcsrc/common/weapons/weapon/blaster.qc +++ b/qcsrc/common/weapons/weapon/blaster.qc @@ -1,12 +1,13 @@ +#include "blaster.qh" #ifndef IMPLEMENTATION CLASS(Blaster, Weapon) -/* ammotype */ //ATTRIB(Blaster, ammo_field, .int, ammo_none) -/* impulse */ ATTRIB(Blaster, impulse, int, 1) +/* ammotype */ //ATTRIB(Blaster, ammo_field, .int, ammo_none); +/* impulse */ ATTRIB(Blaster, impulse, int, 1); /* flags */ ATTRIB(Blaster, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Blaster, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(Blaster, wpcolor, vector, '1 0.5 0.5'); /* modelname */ ATTRIB(Blaster, mdl, string, "laser"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Blaster, m_model, Model, MDL_BLASTER_ITEM); #endif /* crosshair */ ATTRIB(Blaster, w_crosshair, string, "gfx/crosshairlaser"); @@ -56,9 +57,9 @@ REGISTER_WEAPON(BLASTER, blaster, NEW(Blaster)); spawnfunc(weapon_blaster) { weapon_defaultspawnfunc(this, WEP_BLASTER); } spawnfunc(weapon_laser) { spawnfunc_weapon_blaster(this); } -void W_Blaster_Touch(entity this) +void W_Blaster_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); this.event_damage = func_null; @@ -72,15 +73,15 @@ void W_Blaster_Touch(entity this) NULL, this.blaster_force, this.projectiledeathtype, - other + toucher ); - remove(this); + delete(this); } void W_Blaster_Think(entity this) { - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); setthink(this, SUB_Remove); this.nextthink = time + this.blaster_lifetime; CSQCProjectile(this, true, PROJECTILE_BLASTER, true); @@ -88,6 +89,7 @@ void W_Blaster_Think(entity this) void W_Blaster_Attack( entity actor, + .entity weaponentity, float atk_deathtype, float atk_shotangle, float atk_damage, @@ -101,7 +103,7 @@ void W_Blaster_Attack( { vector s_forward = v_forward * cos(atk_shotangle * DEG2RAD) + v_up * sin(atk_shotangle * DEG2RAD); - W_SetupShot_Dir(actor, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage); + W_SetupShot_Dir(actor, weaponentity, s_forward, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, atk_damage); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); entity missile = new(blasterbolt); @@ -137,6 +139,8 @@ void W_Blaster_Attack( settouch(missile, W_Blaster_Touch); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; missile.projectiledeathtype = atk_deathtype; setthink(missile, W_Blaster_Think); @@ -171,6 +175,7 @@ METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponenti { W_Blaster_Attack( actor, + weaponentity, WEP_BLASTER.m_id, WEP_CVAR_PRI(blaster, shotangle), WEP_CVAR_PRI(blaster, damage), @@ -202,6 +207,7 @@ METHOD(Blaster, wr_think, void(Blaster thiswep, entity actor, .entity weaponenti { W_Blaster_Attack( actor, + weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(blaster, shotangle), WEP_CVAR_SEC(blaster, damage), diff --git a/qcsrc/common/weapons/weapon/blaster.qh b/qcsrc/common/weapons/weapon/blaster.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/blaster.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/crylink.qc b/qcsrc/common/weapons/weapon/crylink.qc index eb3e72f8ac..58214bf390 100644 --- a/qcsrc/common/weapons/weapon/crylink.qc +++ b/qcsrc/common/weapons/weapon/crylink.qc @@ -1,12 +1,13 @@ +#include "crylink.qh" #ifndef IMPLEMENTATION CLASS(Crylink, Weapon) -/* ammotype */ ATTRIB(Crylink, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(Crylink, impulse, int, 6) +/* ammotype */ ATTRIB(Crylink, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(Crylink, impulse, int, 6); /* flags */ ATTRIB(Crylink, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Crylink, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Crylink, wpcolor, vector, '1 0.5 1'); /* modelname */ ATTRIB(Crylink, mdl, string, "crylink"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Crylink, m_model, Model, MDL_CRYLINK_ITEM); #endif /* crosshair */ ATTRIB(Crylink, w_crosshair, string, "gfx/crosshaircrylink"); @@ -114,11 +115,11 @@ void W_Crylink_Dequeue(entity e) void W_Crylink_Reset(entity this) { W_Crylink_Dequeue(this); - remove(this); + delete(this); } // force projectile to explode -void W_Crylink_LinkExplode(entity e, entity e2) +void W_Crylink_LinkExplode(entity e, entity e2, entity directhitentity) { float a; @@ -132,12 +133,13 @@ void W_Crylink_LinkExplode(entity e, entity e2) float isprimary = !(e.projectiledeathtype & HITTYPE_SECONDARY); - RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, other); + RadiusDamage(e, e.realowner, WEP_CVAR_BOTH(crylink, isprimary, damage) * a, WEP_CVAR_BOTH(crylink, isprimary, edgedamage) * a, WEP_CVAR_BOTH(crylink, isprimary, radius), + NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * a, e.projectiledeathtype, directhitentity); - W_Crylink_LinkExplode(e.queuenext, e2); + W_Crylink_LinkExplode(e.queuenext, e2, directhitentity); e.classname = "spike_oktoremove"; - remove(e); + delete(e); } // adjust towards center @@ -261,13 +263,13 @@ void W_Crylink_LinkJoinEffect_Think(entity this) NULL, WEP_CVAR_BOTH(crylink, isprimary, joinexplode_force) * n, e.projectiledeathtype, - other + NULL ); Send_Effect(EFFECT_CRYLINK_JOINEXPLODE, this.origin, '0 0 0', n); } } } - remove(this); + delete(this); } float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) @@ -293,17 +295,17 @@ float W_Crylink_Touch_WouldHitFriendly(entity projectile, float rad) } // NO bounce protection, as bounces are limited! -void W_Crylink_Touch(entity this) +void W_Crylink_Touch(entity this, entity toucher) { float finalhit; float f; float isprimary = !(this.projectiledeathtype & HITTYPE_SECONDARY); - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); float a; a = bound(0, 1 - (time - this.fade_time) * this.fade_rate, 1); - finalhit = ((this.cnt <= 0) || (other.takedamage != DAMAGE_NO)); + finalhit = ((this.cnt <= 0) || (toucher.takedamage != DAMAGE_NO)); if(finalhit) f = 1; else @@ -311,22 +313,22 @@ void W_Crylink_Touch(entity this) 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), NULL, NULL, WEP_CVAR_BOTH(crylink, isprimary, force) * f, this.projectiledeathtype, other); + 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, 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))))) { if(this == this.realowner.crylink_lastgroup) this.realowner.crylink_lastgroup = NULL; - W_Crylink_LinkExplode(this.queuenext, this); + W_Crylink_LinkExplode(this.queuenext, this, toucher); this.classname = "spike_oktoremove"; - remove(this); + delete(this); return; } else if(finalhit) { // just unlink W_Crylink_Dequeue(this); - remove(this); + delete(this); return; } this.cnt = this.cnt - 1; @@ -341,10 +343,10 @@ void W_Crylink_Touch(entity this) void W_Crylink_Fadethink(entity this) { W_Crylink_Dequeue(this); - remove(this); + delete(this); } -void W_Crylink_Attack(Weapon thiswep, entity actor) +void W_Crylink_Attack(Weapon thiswep, entity actor, .entity weaponentity) { float counter, shots; entity proj, prevproj, firstproj; @@ -359,7 +361,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor) if(WEP_CVAR_PRI(crylink, joinexplode)) maxdmg += WEP_CVAR_PRI(crylink, joinexplode_damage); - W_SetupShot(actor, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg); + W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, maxdmg); forward = v_forward; right = v_right; up = v_up; @@ -394,7 +396,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor) prevproj = proj; - proj.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(proj, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(proj); proj.projectiledeathtype = WEP_CRYLINK.m_id; //proj.gravity = 0.001; @@ -438,6 +440,8 @@ void W_Crylink_Attack(Weapon thiswep, entity actor) //proj.glow_size = 20; proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true); @@ -452,7 +456,7 @@ void W_Crylink_Attack(Weapon thiswep, entity actor) } } -void W_Crylink_Attack2(Weapon thiswep, entity actor) +void W_Crylink_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { float counter, shots; entity proj, prevproj, firstproj; @@ -467,7 +471,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor) if(WEP_CVAR_SEC(crylink, joinexplode)) maxdmg += WEP_CVAR_SEC(crylink, joinexplode_damage); - W_SetupShot(actor, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg); + W_SetupShot(actor, weaponentity, false, 2, SND_CRYLINK_FIRE2, CH_WEAPON_A, maxdmg); forward = v_forward; right = v_right; up = v_up; @@ -502,7 +506,7 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor) prevproj = proj; - proj.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(proj, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(proj); proj.projectiledeathtype = WEP_CRYLINK.m_id | HITTYPE_SECONDARY; //proj.gravity = 0.001; @@ -553,6 +557,8 @@ void W_Crylink_Attack2(Weapon thiswep, entity actor) //proj.glow_size = 20; proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, true, (proj.cnt ? PROJECTILE_CRYLINK_BOUNCING : PROJECTILE_CRYLINK), true); @@ -585,7 +591,7 @@ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentit if(actor.crylink_waitrelease != 1) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(crylink, refire))) { - W_Crylink_Attack(thiswep, actor); + W_Crylink_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(crylink, animtime), w_ready); } } @@ -595,7 +601,7 @@ METHOD(Crylink, wr_think, void(entity thiswep, entity actor, .entity weaponentit if(actor.crylink_waitrelease != 2) if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(crylink, refire))) { - W_Crylink_Attack2(thiswep, actor); + W_Crylink_Attack2(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(crylink, animtime), w_ready); } } @@ -652,7 +658,7 @@ METHOD(Crylink, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Crylink, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(crylink, ammo), WEP_CVAR_SEC(crylink, ammo)), SND_RELOAD); } METHOD(Crylink, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/crylink.qh b/qcsrc/common/weapons/weapon/crylink.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/crylink.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/devastator.qc b/qcsrc/common/weapons/weapon/devastator.qc index f2ca426554..7e63e760ec 100644 --- a/qcsrc/common/weapons/weapon/devastator.qc +++ b/qcsrc/common/weapons/weapon/devastator.qc @@ -1,12 +1,13 @@ +#include "devastator.qh" #ifndef IMPLEMENTATION CLASS(Devastator, Weapon) -/* ammotype */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(Devastator, impulse, int, 9) +/* ammotype */ ATTRIB(Devastator, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(Devastator, impulse, int, 9); /* flags */ ATTRIB(Devastator, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Devastator, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Devastator, wpcolor, vector, '1 1 0'); /* modelname */ ATTRIB(Devastator, mdl, string, "rl"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Devastator, m_model, Model, MDL_DEVASTATOR_ITEM); #endif /* crosshair */ ATTRIB(Devastator, w_crosshair, string, "gfx/crosshairrocketlauncher"); @@ -39,6 +40,7 @@ CLASS(Devastator, Weapon) P(class, prefix, remote_edgedamage, float, NONE) \ P(class, prefix, remote_force, float, NONE) \ P(class, prefix, remote_jump_damage, float, NONE) \ + P(class, prefix, remote_jump_force, float, NONE) \ P(class, prefix, remote_jump_radius, float, NONE) \ P(class, prefix, remote_jump_velocity_z_add, float, NONE) \ P(class, prefix, remote_jump_velocity_z_max, float, NONE) \ @@ -61,7 +63,7 @@ ENDCLASS(Devastator) REGISTER_WEAPON(DEVASTATOR, devastator, NEW(Devastator)); #ifdef SVQC -.float rl_release; +.float rl_release[MAX_WEAPONSLOTS]; .float rl_detonate_later; #endif #endif @@ -76,20 +78,25 @@ void W_Devastator_Unregister(entity this) { if(this.realowner && this.realowner.lastrocket == this) { - this.realowner.lastrocket = NULL; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(this.realowner.(weaponentity).lastrocket == this) + this.realowner.(weaponentity).lastrocket = NULL; + } // this.realowner.rl_release = 1; } } -void W_Devastator_Explode(entity this) +void W_Devastator_Explode(entity this, entity directhitentity) { W_Devastator_Unregister(this); - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); this.event_damage = func_null; @@ -105,7 +112,7 @@ void W_Devastator_Explode(entity this) NULL, WEP_CVAR(devastator, force), this.projectiledeathtype, - other + directhitentity ); Weapon thiswep = WEP_DEVASTATOR; @@ -120,7 +127,12 @@ void W_Devastator_Explode(entity this) PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner); } } - remove(this); + delete(this); +} + +void W_Devastator_Explode_think(entity this) +{ + W_Devastator_Explode(this, NULL); } void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) @@ -130,50 +142,56 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) this.event_damage = func_null; this.takedamage = DAMAGE_NO; - float handled_as_rocketjump = false; + bool handled_as_rocketjump = false; + entity head = NULL; - entity head = WarpZone_FindRadius( - this.origin, - WEP_CVAR(devastator, remote_jump_radius), - false - ); - - while(head) + if(WEP_CVAR(devastator, remote_jump_radius)) { - if(head.takedamage && (head == this.realowner)) + head = WarpZone_FindRadius( + this.origin, + WEP_CVAR(devastator, remote_jump_radius), + false + ); + + while(head) { - float distance_to_head = vlen(this.origin - head.WarpZone_findradius_nearest); - if(distance_to_head <= WEP_CVAR(devastator, remote_jump_radius)) + if(head.takedamage && (head == this.realowner)) { - // we handled this as a rocketjump :) - handled_as_rocketjump = true; - - // modify velocity - head.velocity_x *= 0.9; - head.velocity_y *= 0.9; - head.velocity_z = bound( - WEP_CVAR(devastator, remote_jump_velocity_z_min), - head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add), - WEP_CVAR(devastator, remote_jump_velocity_z_max) - ); - - // now do the damage - RadiusDamage( - this, - head, - WEP_CVAR(devastator, remote_jump_damage), - WEP_CVAR(devastator, remote_jump_damage), - WEP_CVAR(devastator, remote_jump_radius), - NULL, - head, - 0, - this.projectiledeathtype | HITTYPE_BOUNCE, - NULL - ); - break; + if(vdist(this.origin - head.WarpZone_findradius_nearest, <=, WEP_CVAR(devastator, remote_jump_radius))) + { + // we handled this as a rocketjump :) + handled_as_rocketjump = true; + + // modify velocity + if(WEP_CVAR(devastator, remote_jump_velocity_z_add)) + { + head.velocity_x *= 0.9; + head.velocity_y *= 0.9; + head.velocity_z = bound( + WEP_CVAR(devastator, remote_jump_velocity_z_min), + head.velocity.z + WEP_CVAR(devastator, remote_jump_velocity_z_add), + WEP_CVAR(devastator, remote_jump_velocity_z_max) + ); + } + + // now do the damage + RadiusDamage( + this, + head, + WEP_CVAR(devastator, remote_jump_damage), + WEP_CVAR(devastator, remote_jump_damage), + WEP_CVAR(devastator, remote_jump_radius), + NULL, + head, + (WEP_CVAR(devastator, remote_jump_force) ? WEP_CVAR(devastator, remote_jump_force) : 0), + this.projectiledeathtype | HITTYPE_BOUNCE, + NULL + ); + break; + } } + head = head.chain; } - head = head.chain; } RadiusDamage( @@ -201,13 +219,13 @@ void W_Devastator_DoRemoteExplode(entity this, .entity weaponentity) PS(this.realowner).m_switchweapon = w_getbestweapon(this.realowner); } } - remove(this); + delete(this); } void W_Devastator_RemoteExplode(entity this, .entity weaponentity) { if(!IS_DEAD(this.realowner)) - if(this.realowner.lastrocket) + if(this.realowner.(weaponentity).lastrocket) { if((this.spawnshieldtime >= 0) ? (time >= this.spawnshieldtime) // timer @@ -259,9 +277,8 @@ void W_Devastator_Think(entity this) this.nextthink = time; if(time > this.cnt) { - other = NULL; this.projectiledeathtype |= HITTYPE_BOUNCE; - W_Devastator_Explode(this); + W_Devastator_Explode(this, NULL); return; } @@ -274,8 +291,11 @@ void W_Devastator_Think(entity this) // laser guided, or remote detonation if(PS(this.realowner).m_weapon == WEP_DEVASTATOR) { - if(this == this.realowner.lastrocket) - if(!this.realowner.rl_release) + .entity weaponentity = this.weaponentity_fld; + int slot = weaponslot(weaponentity); + + if(this == this.realowner.(weaponentity).lastrocket) + if(!this.realowner.rl_release[slot]) if(!PHYS_INPUT_BUTTON_ATCK2(this)) if(WEP_CVAR(devastator, guiderate)) if(time > this.pushltime) @@ -311,7 +331,6 @@ void W_Devastator_Think(entity this) } } - .entity weaponentity = weaponentities[0]; // TODO: unhardcode if(this.rl_detonate_later) W_Devastator_RemoteExplode(this, weaponentity); } @@ -320,16 +339,16 @@ void W_Devastator_Think(entity this) UpdateCSQCProjectile(this); } -void W_Devastator_Touch(entity this) +void W_Devastator_Touch(entity this, entity toucher) { - if(WarpZone_Projectile_Touch(this)) + if(WarpZone_Projectile_Touch(this, toucher)) { if(wasfreed(this)) W_Devastator_Unregister(this); return; } W_Devastator_Unregister(this); - W_Devastator_Explode(this); + W_Devastator_Explode(this, toucher); } void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -344,19 +363,20 @@ void W_Devastator_Damage(entity this, entity inflictor, entity attacker, float d this.angles = vectoangles(this.velocity); if(this.health <= 0) - W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode); + W_PrepareExplosionByDamage(this, attacker, W_Devastator_Explode_think); } -void W_Devastator_Attack(Weapon thiswep, entity actor) +void W_Devastator_Attack(Weapon thiswep, entity actor, .entity weaponentity) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR(devastator, ammo)); - W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 5, SND_ROCKET_FIRE, CH_WEAPON_A, WEP_CVAR(devastator, damage)); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); entity missile = WarpZone_RefSys_SpawnSameRefSys(actor); + missile.weaponentity_fld = weaponentity; missile.owner = missile.realowner = actor; - actor.lastrocket = missile; + actor.(weaponentity).lastrocket = missile; if(WEP_CVAR(devastator, detonatedelay) >= 0) missile.spawnshieldtime = time + WEP_CVAR(devastator, detonatedelay); else @@ -371,8 +391,9 @@ void W_Devastator_Attack(Weapon thiswep, entity actor) missile.health = WEP_CVAR(devastator, health); missile.event_damage = W_Devastator_Damage; missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); missile.projectiledeathtype = WEP_DEVASTATOR.m_id; setsize(missile, '-3 -3 -3', '3 3 3'); // give it some size so it can be shot @@ -386,6 +407,8 @@ void W_Devastator_Attack(Weapon thiswep, entity actor) missile.nextthink = time; missile.cnt = time + WEP_CVAR(devastator, lifetime); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, WEP_CVAR(devastator, guiderate) == 0 && WEP_CVAR(devastator, speedaccel) == 0, PROJECTILE_ROCKET, false); // because of fly sound @@ -395,50 +418,20 @@ void W_Devastator_Attack(Weapon thiswep, entity actor) setmodel(flash, MDL_DEVASTATOR_MUZZLEFLASH); // precision set below SUB_SetFade(flash, time, 0.1); flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(actor, flash, '5 0 0'); + W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); // common properties MUTATOR_CALLHOOK(EditProjectile, actor, missile); } -#if 0 METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) { - entity this = actor; // aim and decide to fire if appropriate PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! { // decide whether to detonate rockets - entity missile, targetlist, targ; - targetlist = findchainfloat(bot_attack, true); - for(missile = NULL; (missile = find(missile, classname, "rocket")); ) if(missile.realowner == actor) - { - targ = targetlist; - while(targ) - { - if(targ != missile.realowner && vlen(targ.origin - missile.origin) < WEP_CVAR(devastator, radius)) - { - PHYS_INPUT_BUTTON_ATCK2(actor) = true; - break; - } - targ = targ.chain; - } - } - - if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false; - } -} -#else -METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) -{ - // aim and decide to fire if appropriate - PHYS_INPUT_BUTTON_ATCK(actor) = bot_aim(actor, WEP_CVAR(devastator, speed), 0, WEP_CVAR(devastator, lifetime), false); - if(skill >= 2) // skill 0 and 1 bots won't detonate rockets! - { - // decide whether to detonate rockets - entity targetlist, targ; - float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; + float edgedamage, coredamage, edgeradius, recipricoledgeradius; float selfdamage, teamdamage, enemydamage; edgedamage = WEP_CVAR(devastator, edgedamage); coredamage = WEP_CVAR(devastator, damage); @@ -447,25 +440,21 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) selfdamage = 0; teamdamage = 0; enemydamage = 0; - targetlist = findchainfloat(bot_attack, true); - FOREACH_ENTITY_ENT(realowner, actor, + IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket", { - if(it.classname != "rocket") continue; - - targ = targetlist; - while(targ) + entity rocket = it; + IL_EACH(g_bot_targets, it.bot_attack, { - d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - it.origin); - d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); - // count potential damage according to type of target - if(targ == actor) - selfdamage = selfdamage + d; - else if(targ.team == actor.team && teamplay) - teamdamage = teamdamage + d; - else if(bot_shouldattack(actor, targ)) - enemydamage = enemydamage + d; - targ = targ.chain; - } + float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - rocket.origin); + d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); + // count potential damage according to type of target + if(it == actor) + selfdamage = selfdamage + d; + else if(SAME_TEAM(it, actor)) + teamdamage = teamdamage + d; + else if(bot_shouldattack(actor, it)) + enemydamage = enemydamage + d; + }); }); float desirabledamage; desirabledamage = enemydamage; @@ -474,36 +463,33 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) if(teamplay && actor.team) desirabledamage = desirabledamage - teamdamage; - FOREACH_ENTITY_ENT(realowner, actor, + makevectors(actor.v_angle); + IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket", { - if(it.classname != "rocket") continue; - - makevectors(it.v_angle); - targ = targetlist; if(skill > 9) // normal players only do this for the target they are tracking { - targ = targetlist; - while(targ) - { - if( - (v_forward * normalize(it.origin - targ.origin)< 0.1) - && desirabledamage > 0.1*coredamage - ) PHYS_INPUT_BUTTON_ATCK2(actor) = true; - targ = targ.chain; - } - } - else - { - float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000); + entity rocket = it; + IL_EACH(g_bot_targets, it.bot_attack, + { + if((v_forward * normalize(rocket.origin - it.origin) < 0.1) + && desirabledamage > 0.1 * coredamage + ) PHYS_INPUT_BUTTON_ATCK2(actor) = true; + }); + } + else + { //As the distance gets larger, a correct detonation gets near imposible //Bots are assumed to use the rocket spawnfunc_light to see if the rocket gets near a player - if(v_forward * normalize(it.origin - actor.enemy.origin)< 0.1) - if(IS_PLAYER(actor.enemy)) - if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) - PHYS_INPUT_BUTTON_ATCK2(actor) = true; - // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); - } + if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1) + && IS_PLAYER(actor.enemy) + && (desirabledamage >= 0.1 * coredamage) + ) + { + float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000); + if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1)) + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + } + } }); // if we would be doing at X percent of the core damage, detonate it // but don't fire a new shot at the same time! @@ -516,38 +502,38 @@ METHOD(Devastator, wr_aim, void(entity thiswep, entity actor)) if(PHYS_INPUT_BUTTON_ATCK2(actor)) PHYS_INPUT_BUTTON_ATCK(actor) = false; } } -#endif + METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponentity, int fire)) { if(WEP_CVAR(devastator, reload_ammo) && actor.clip_load < WEP_CVAR(devastator, ammo)) { // forced reload thiswep.wr_reload(thiswep, actor, weaponentity); } else { + int slot = weaponslot(weaponentity); if(fire & 1) { - if(actor.rl_release || WEP_CVAR(devastator, guidestop)) + if(actor.rl_release[slot] || WEP_CVAR(devastator, guidestop)) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(devastator, refire))) { - W_Devastator_Attack(thiswep, actor); + W_Devastator_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(devastator, animtime), w_ready); - actor.rl_release = 0; + actor.rl_release[slot] = 0; } } else - actor.rl_release = 1; + actor.rl_release[slot] = 1; if(fire & 2) if(PS(actor).m_switchweapon == WEP_DEVASTATOR) { - entity rock; bool rockfound = false; - for(rock = NULL; (rock = find(rock, classname, "rocket")); ) if(rock.realowner == actor) + IL_EACH(g_projectiles, it.realowner == actor && it.classname == "rocket", { - if(!rock.rl_detonate_later) + if(!it.rl_detonate_later) { - rock.rl_detonate_later = true; + it.rl_detonate_later = true; rockfound = true; } - } + }); if(rockfound) sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM); } @@ -555,7 +541,8 @@ METHOD(Devastator, wr_think, void(entity thiswep, entity actor, .entity weaponen } METHOD(Devastator, wr_setup, void(entity thiswep, entity actor)) { - actor.rl_release = 1; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + actor.rl_release[slot] = 1; } METHOD(Devastator, wr_checkammo1, bool(entity thiswep, entity actor)) { @@ -599,12 +586,16 @@ METHOD(Devastator, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Devastator, wr_resetplayer, void(entity thiswep, entity actor)) { - actor.lastrocket = NULL; // stop rocket guiding, no revenge from the grave! - actor.rl_release = 0; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + actor.(weaponentity).lastrocket = NULL; // stop rocket guiding, no revenge from the grave! + actor.rl_release[slot] = 0; + } } METHOD(Devastator, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, WEP_CVAR(devastator, ammo), SND_RELOAD); + W_Reload(actor, weaponentity, WEP_CVAR(devastator, ammo), SND_RELOAD); } METHOD(Devastator, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/devastator.qh b/qcsrc/common/weapons/weapon/devastator.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/devastator.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/electro.qc b/qcsrc/common/weapons/weapon/electro.qc index 09cb77c8bd..319f0d1d70 100644 --- a/qcsrc/common/weapons/weapon/electro.qc +++ b/qcsrc/common/weapons/weapon/electro.qc @@ -1,12 +1,13 @@ +#include "electro.qh" #ifndef IMPLEMENTATION CLASS(Electro, Weapon) -/* ammotype */ ATTRIB(Electro, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(Electro, impulse, int, 5) +/* ammotype */ ATTRIB(Electro, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(Electro, impulse, int, 5); /* flags */ ATTRIB(Electro, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Electro, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Electro, wpcolor, vector, '0 0.5 1'); /* modelname */ ATTRIB(Electro, mdl, string, "electro"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Electro, m_model, Model, MDL_ELECTRO_ITEM); #endif /* crosshair */ ATTRIB(Electro, w_crosshair, string, "gfx/crosshairelectro"); @@ -50,6 +51,7 @@ CLASS(Electro, Weapon) P(class, prefix, speed_up, float, SEC) \ P(class, prefix, speed_z, float, SEC) \ P(class, prefix, spread, float, BOTH) \ + P(class, prefix, stick, float, SEC) \ P(class, prefix, switchdelay_drop, float, NONE) \ P(class, prefix, switchdelay_raise, float, NONE) \ P(class, prefix, touchexplode, float, SEC) \ @@ -141,22 +143,22 @@ void W_Electro_ExplodeCombo(entity this) NULL ); - remove(this); + delete(this); } -void W_Electro_Explode(entity this) +void W_Electro_Explode(entity this, entity directhitentity) { - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - if(this.movetype == MOVETYPE_BOUNCE) + if(this.move_movetype == MOVETYPE_BOUNCE || this.classname == "electro_orb") // TODO: classname is more reliable anyway? { RadiusDamage( this, @@ -168,7 +170,7 @@ void W_Electro_Explode(entity this) NULL, WEP_CVAR_SEC(electro, force), this.projectiledeathtype, - other + directhitentity ); } else @@ -184,26 +186,30 @@ void W_Electro_Explode(entity this) NULL, WEP_CVAR_PRI(electro, force), this.projectiledeathtype, - other + directhitentity ); } - remove(this); + delete(this); } void W_Electro_Explode_use(entity this, entity actor, entity trigger) { - W_Electro_Explode(this); + W_Electro_Explode(this, trigger); } -void W_Electro_TouchExplode(entity this) +void W_Electro_TouchExplode(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - W_Electro_Explode(this); + PROJECTILE_TOUCH(this, toucher); + W_Electro_Explode(this, toucher); } + +void sys_phys_update_single(entity this); + void W_Electro_Bolt_Think(entity this) { + // sys_phys_update_single(this); if(time >= this.ltime) { this.use(this, NULL, NULL); @@ -252,9 +258,10 @@ void W_Electro_Bolt_Think(entity this) { this.nextthink = min(time + WEP_CVAR_PRI(electro, midaircombo_interval), this.ltime); } } else { this.nextthink = this.ltime; } + // this.nextthink = time; } -void W_Electro_Attack_Bolt(Weapon thiswep, entity actor) +void W_Electro_Attack_Bolt(Weapon thiswep, entity actor, .entity weaponentity) { entity proj; @@ -262,6 +269,7 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor) W_SetupShot_ProjectileSize( actor, + weaponentity, '0 0 -3', '0 0 -3', false, @@ -285,29 +293,77 @@ void W_Electro_Attack_Bolt(Weapon thiswep, entity actor) proj.projectiledeathtype = WEP_ELECTRO.m_id; setorigin(proj, w_shotorg); - proj.movetype = MOVETYPE_FLY; + // if (IS_CSQC) + set_movetype(proj, MOVETYPE_FLY); W_SetupProjVelocity_PRI(proj, electro); proj.angles = vectoangles(proj.velocity); settouch(proj, W_Electro_TouchExplode); setsize(proj, '0 0 -3', '0 0 -3'); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, true, PROJECTILE_ELECTRO_BEAM, true); MUTATOR_CALLHOOK(EditProjectile, actor, proj); + // proj.com_phys_pos = proj.origin; + // proj.com_phys_vel = proj.velocity; +} + +void W_Electro_Orb_Stick(entity this, entity to) +{ + entity newproj = spawn(); + newproj.classname = this.classname; + + newproj.bot_dodge = this.bot_dodge; + newproj.bot_dodgerating = this.bot_dodgerating; + + newproj.owner = this.owner; + newproj.realowner = this.realowner; + setsize(newproj, this.mins, this.maxs); + setorigin(newproj, this.origin); + setmodel(newproj, MDL_PROJECTILE_ELECTRO); + newproj.angles = vectoangles(-trace_plane_normal); // face against the surface + + newproj.takedamage = this.takedamage; + newproj.damageforcescale = this.damageforcescale; + newproj.health = this.health; + newproj.event_damage = this.event_damage; + newproj.spawnshieldtime = this.spawnshieldtime; + newproj.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, newproj); + + set_movetype(newproj, MOVETYPE_NONE); // lock the orb in place + newproj.projectiledeathtype = this.projectiledeathtype; + + settouch(newproj, func_null); + setthink(newproj, getthink(this)); + newproj.nextthink = this.nextthink; + newproj.use = this.use; + newproj.flags = this.flags; + IL_PUSH(g_projectiles, newproj); + IL_PUSH(g_bot_dodge, newproj); + + delete(this); + + if(to) + SetMovetypeFollow(this, to); } -void W_Electro_Orb_Touch(entity this) +void W_Electro_Orb_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - if(other.takedamage == DAMAGE_AIM) - { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(this); } } + PROJECTILE_TOUCH(this, toucher); + if(toucher.takedamage == DAMAGE_AIM) + { if(WEP_CVAR_SEC(electro, touchexplode)) { W_Electro_Explode(this, toucher); } } else { //UpdateCSQCProjectile(this); spamsound(this, CH_SHOTS, SND(ELECTRO_BOUNCE), VOL_BASE, ATTEN_NORM); this.projectiledeathtype |= HITTYPE_BOUNCE; + + if(WEP_CVAR_SEC(electro, stick)) + W_Electro_Orb_Stick(this, toucher); } } @@ -353,14 +409,15 @@ void W_Electro_Orb_Damage(entity this, entity inflictor, entity attacker, float } } -void W_Electro_Attack_Orb(Weapon thiswep, entity actor) +void W_Electro_Attack_Orb(Weapon thiswep, entity actor, .entity weaponentity) { W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(electro, ammo)); W_SetupShot_ProjectileSize( actor, - '0 0 -4', - '0 0 -4', + weaponentity, + '-4 -4 -4', + '4 4 4', false, 2, SND_ELECTRO_FIRE2, @@ -385,16 +442,20 @@ void W_Electro_Attack_Orb(Weapon thiswep, entity actor) //proj.glow_size = 50; //proj.glow_color = 45; - proj.movetype = MOVETYPE_BOUNCE; + set_movetype(proj, MOVETYPE_BOUNCE); W_SetupProjVelocity_UP_SEC(proj, electro); settouch(proj, W_Electro_Orb_Touch); - setsize(proj, '0 0 -4', '0 0 -4'); + 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); proj.event_damage = W_Electro_Orb_Damage; proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.damagedbycontents = (WEP_CVAR_SEC(electro, damagedbycontents)); + if(proj.damagedbycontents) + IL_PUSH(g_damagedbycontents, proj); proj.bouncefactor = WEP_CVAR_SEC(electro, bouncefactor); proj.bouncestop = WEP_CVAR_SEC(electro, bouncestop); @@ -419,7 +480,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); + W_Electro_Attack_Orb(WEP_ELECTRO, actor, weaponentity); actor.electro_count -= 1; weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); return; @@ -479,7 +540,7 @@ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentit { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(electro, refire))) { - W_Electro_Attack_Bolt(thiswep, actor); + W_Electro_Attack_Bolt(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(electro, animtime), w_ready); } } @@ -488,7 +549,7 @@ METHOD(Electro, wr_think, void(entity thiswep, entity actor, .entity weaponentit if(time >= actor.electro_secondarytime) if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(electro, refire))) { - W_Electro_Attack_Orb(thiswep, actor); + W_Electro_Attack_Orb(thiswep, actor, weaponentity); actor.electro_count = WEP_CVAR_SEC(electro, count); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(electro, animtime), W_Electro_CheckAttack); actor.electro_secondarytime = time + WEP_CVAR_SEC(electro, refire2) * W_WeaponRateFactor(actor); @@ -522,7 +583,7 @@ METHOD(Electro, wr_resetplayer, void(entity thiswep, entity actor)) } METHOD(Electro, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(electro, ammo), WEP_CVAR_SEC(electro, ammo)), SND_RELOAD); } METHOD(Electro, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/electro.qh b/qcsrc/common/weapons/weapon/electro.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/electro.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/fireball.qc b/qcsrc/common/weapons/weapon/fireball.qc index d07324701a..ae1e48bb2c 100644 --- a/qcsrc/common/weapons/weapon/fireball.qc +++ b/qcsrc/common/weapons/weapon/fireball.qc @@ -1,12 +1,13 @@ +#include "fireball.qh" #ifndef IMPLEMENTATION CLASS(Fireball, Weapon) -/* ammotype */ //ATTRIB(Fireball, ammo_field, .int, ammo_none) -/* impulse */ ATTRIB(Fireball, impulse, int, 9) +/* ammotype */ //ATTRIB(Fireball, ammo_field, .int, ammo_none); +/* impulse */ ATTRIB(Fireball, impulse, int, 9); /* flags */ ATTRIB(Fireball, spawnflags, int, WEP_FLAG_SUPERWEAPON | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Fireball, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Fireball, wpcolor, vector, '1 0.5 0'); /* modelname */ ATTRIB(Fireball, mdl, string, "fireball"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Fireball, m_model, Model, MDL_FIREBALL_ITEM); #endif /* crosshair */ ATTRIB(Fireball, w_crosshair, string, "gfx/crosshairfireball"); @@ -55,14 +56,14 @@ REGISTER_WEAPON(FIREBALL, fireball, NEW(Fireball)); #ifdef SVQC .float bot_primary_fireballmooth; // whatever a mooth is .vector fireball_impactvec; -.float fireball_primarytime; +.float fireball_primarytime[MAX_WEAPONSLOTS]; #endif #endif #ifdef IMPLEMENTATION #ifdef SVQC spawnfunc(weapon_fireball) { weapon_defaultspawnfunc(this, WEP_FIREBALL); } -void W_Fireball_Explode(entity this) +void W_Fireball_Explode(entity this, entity directhitentity) { entity e; float dist; @@ -75,7 +76,7 @@ void W_Fireball_Explode(entity this) // 1. dist damage d = (this.realowner.health + this.realowner.armorvalue); - 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, other); + 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, directhitentity); if(this.realowner.health + this.realowner.armorvalue >= d) if(!this.cnt) { @@ -109,18 +110,23 @@ void W_Fireball_Explode(entity this) } } - remove(this); + delete(this); +} + +void W_Fireball_Explode_think(entity this) +{ + W_Fireball_Explode(this, NULL); } void W_Fireball_Explode_use(entity this, entity actor, entity trigger) { - W_Fireball_Explode(this); + W_Fireball_Explode(this, trigger); } -void W_Fireball_TouchExplode(entity this) +void W_Fireball_TouchExplode(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - W_Fireball_Explode(this); + PROJECTILE_TOUCH(this, toucher); + W_Fireball_Explode(this, toucher); } void W_Fireball_LaserPlay(entity this, float dt, float dist, float damage, float edgedamage, float burntime) @@ -144,7 +150,7 @@ void W_Fireball_LaserPlay(entity this, float dt, float dist, float damage, float if(d < dist) { e.fireball_impactvec = p; - RandomSelection_Add(e, 0, string_null, 1 / (1 + d), !Fire_IsBurning(e)); + RandomSelection_AddEnt(e, 1 / (1 + d), !Fire_IsBurning(e)); } } if(RandomSelection_chosen_ent) @@ -163,7 +169,7 @@ void W_Fireball_Think(entity this) { this.cnt = 1; this.projectiledeathtype |= HITTYPE_SPLASH; - W_Fireball_Explode(this); + W_Fireball_Explode(this, NULL); return; } @@ -184,19 +190,17 @@ void W_Fireball_Damage(entity this, entity inflictor, entity attacker, float dam if(this.health <= 0) { this.cnt = 1; - W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode); + W_PrepareExplosionByDamage(this, attacker, W_Fireball_Explode_think); } } -void W_Fireball_Attack1(entity actor) +void W_Fireball_Attack1(entity actor, .entity weaponentity) { - entity proj; - - W_SetupShot_ProjectileSize(actor, '-16 -16 -16', '16 16 16', false, 2, SND_FIREBALL_FIRE2, CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-16 -16 -16', '16 16 16', false, 2, SND_FIREBALL_FIRE2, CH_WEAPON_A, WEP_CVAR_PRI(fireball, damage) + WEP_CVAR_PRI(fireball, bfgdamage)); Send_Effect(EFFECT_FIREBALL_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - proj = new(plasma_prim); + entity proj = new(plasma_prim); proj.owner = proj.realowner = actor; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_PRI(fireball, damage); @@ -213,12 +217,14 @@ void W_Fireball_Attack1(entity actor) proj.projectiledeathtype = WEP_FIREBALL.m_id; setorigin(proj, w_shotorg); - proj.movetype = MOVETYPE_FLY; + set_movetype(proj, MOVETYPE_FLY); W_SetupProjVelocity_PRI(proj, fireball); proj.angles = vectoangles(proj.velocity); settouch(proj, W_Fireball_TouchExplode); setsize(proj, '-16 -16 -16', '16 16 16'); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH | MIF_PROXY; CSQCProjectile(proj, true, PROJECTILE_FIREBALL, true); @@ -226,40 +232,40 @@ void W_Fireball_Attack1(entity actor) MUTATOR_CALLHOOK(EditProjectile, actor, proj); } -void W_Fireball_AttackEffect(entity actor, float i, vector f_diff) +void W_Fireball_AttackEffect(entity actor, .entity weaponentity, float i, vector f_diff) { - W_SetupShot_ProjectileSize(actor, '-16 -16 -16', '16 16 16', false, 0, SND_Null, 0, 0); + W_SetupShot_ProjectileSize(actor, weaponentity, '-16 -16 -16', '16 16 16', false, 0, SND_Null, 0, 0); w_shotorg += f_diff.x * v_up + f_diff.y * v_right; Send_Effect(EFFECT_FIREBALL_PRE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); } void W_Fireball_Attack1_Frame4(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_Fireball_Attack1(actor); + W_Fireball_Attack1(actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), w_ready); } void W_Fireball_Attack1_Frame3(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_Fireball_AttackEffect(actor, 0, '+1.25 +3.75 0'); + W_Fireball_AttackEffect(actor, weaponentity, 0, '+1.25 +3.75 0'); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame4); } void W_Fireball_Attack1_Frame2(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_Fireball_AttackEffect(actor, 0, '-1.25 +3.75 0'); + W_Fireball_AttackEffect(actor, weaponentity, 0, '-1.25 +3.75 0'); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame3); } void W_Fireball_Attack1_Frame1(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_Fireball_AttackEffect(actor, 1, '+1.25 -3.75 0'); + W_Fireball_AttackEffect(actor, weaponentity, 1, '+1.25 -3.75 0'); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame2); } void W_Fireball_Attack1_Frame0(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_Fireball_AttackEffect(actor, 0, '-1.25 -3.75 0'); + W_Fireball_AttackEffect(actor, weaponentity, 0, '-1.25 -3.75 0'); sound(actor, CH_WEAPON_SINGLE, SND_FIREBALL_PREFIRE2, VOL_BASE, ATTEN_NORM); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(fireball, animtime), W_Fireball_Attack1_Frame1); } @@ -268,7 +274,7 @@ void W_Fireball_Firemine_Think(entity this) { if(time > this.pushltime) { - remove(this); + delete(this); return; } @@ -290,19 +296,19 @@ void W_Fireball_Firemine_Think(entity this) this.nextthink = time + 0.1; } -void W_Fireball_Firemine_Touch(entity this) +void W_Fireball_Firemine_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - if(other.takedamage == DAMAGE_AIM) - if(Fire_AddDamage(other, this.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), this.projectiledeathtype) >= 0) + PROJECTILE_TOUCH(this, toucher); + if(toucher.takedamage == DAMAGE_AIM) + if(Fire_AddDamage(toucher, this.realowner, WEP_CVAR_SEC(fireball, damage), WEP_CVAR_SEC(fireball, damagetime), this.projectiledeathtype) >= 0) { - remove(this); + delete(this); return; } this.projectiledeathtype |= HITTYPE_BOUNCE; } -void W_Fireball_Attack2(entity actor) +void W_Fireball_Attack2(entity actor, .entity weaponentity) { entity proj; vector f_diff; @@ -325,7 +331,7 @@ void W_Fireball_Attack2(entity actor) f_diff = '+1.25 +3.75 0'; break; } - W_SetupShot_ProjectileSize(actor, '-4 -4 -4', '4 4 4', false, 2, SND_FIREBALL_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 2, SND_FIREBALL_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(fireball, damage)); traceline(w_shotorg, w_shotorg + f_diff_x * v_up + f_diff_y * v_right, MOVE_NORMAL, actor); w_shotorg = trace_endpos; @@ -335,7 +341,7 @@ void W_Fireball_Attack2(entity actor) proj.owner = proj.realowner = actor; proj.bot_dodge = true; proj.bot_dodgerating = WEP_CVAR_SEC(fireball, damage); - proj.movetype = MOVETYPE_BOUNCE; + set_movetype(proj, MOVETYPE_BOUNCE); proj.projectiledeathtype = WEP_FIREBALL.m_id | HITTYPE_SECONDARY; settouch(proj, W_Fireball_Firemine_Touch); PROJECTILE_MAKETRIGGER(proj); @@ -349,6 +355,8 @@ void W_Fireball_Attack2(entity actor) proj.angles = vectoangles(proj.velocity); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH | MIF_PROXY | MIF_ARC; CSQCProjectile(proj, true, PROJECTILE_FIREMINE, true); @@ -381,18 +389,19 @@ METHOD(Fireball, wr_think, void(entity thiswep, entity actor, .entity weaponenti { if(fire & 1) { - if(time >= actor.fireball_primarytime) + int slot = weaponslot(weaponentity); + if(time >= actor.fireball_primarytime[slot]) if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(fireball, refire))) { W_Fireball_Attack1_Frame0(thiswep, actor, weaponentity, fire); - actor.fireball_primarytime = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(actor); + actor.fireball_primarytime[slot] = time + WEP_CVAR_PRI(fireball, refire2) * W_WeaponRateFactor(actor); } } else if(fire & 2) { if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(fireball, refire))) { - W_Fireball_Attack2(actor); + W_Fireball_Attack2(actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(fireball, animtime), w_ready); } } @@ -411,7 +420,8 @@ METHOD(Fireball, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Fireball, wr_resetplayer, void(entity thiswep, entity actor)) { - actor.fireball_primarytime = time; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + actor.fireball_primarytime[slot] = time; } METHOD(Fireball, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/fireball.qh b/qcsrc/common/weapons/weapon/fireball.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/fireball.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hagar.qc b/qcsrc/common/weapons/weapon/hagar.qc index d41ad95b93..684ee75d37 100644 --- a/qcsrc/common/weapons/weapon/hagar.qc +++ b/qcsrc/common/weapons/weapon/hagar.qc @@ -1,12 +1,13 @@ +#include "hagar.qh" #ifndef IMPLEMENTATION CLASS(Hagar, Weapon) -/* ammotype */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(Hagar, impulse, int, 8) +/* ammotype */ ATTRIB(Hagar, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(Hagar, impulse, int, 8); /* flags */ ATTRIB(Hagar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Hagar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Hagar, wpcolor, vector, '1 1 0.5'); /* modelname */ ATTRIB(Hagar, mdl, string, "hagar"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Hagar, m_model, Model, MDL_HAGAR_ITEM); #endif /* crosshair */ ATTRIB(Hagar, w_crosshair, string, "gfx/crosshairhagar"); @@ -63,30 +64,30 @@ spawnfunc(weapon_hagar) { weapon_defaultspawnfunc(this, WEP_HAGAR); } // NO bounce protection, as bounces are limited! -void W_Hagar_Explode(entity this) +void W_Hagar_Explode(entity this, entity directhitentity) { this.event_damage = func_null; - RadiusDamage(this, this.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), NULL, NULL, WEP_CVAR_PRI(hagar, force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR_PRI(hagar, damage), WEP_CVAR_PRI(hagar, edgedamage), WEP_CVAR_PRI(hagar, radius), NULL, NULL, WEP_CVAR_PRI(hagar, force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } void W_Hagar_Explode_use(entity this, entity actor, entity trigger) { - W_Hagar_Explode(this); + W_Hagar_Explode(this, trigger); } -void W_Hagar_Explode2(entity this) +void W_Hagar_Explode2(entity this, entity directhitentity) { this.event_damage = func_null; - RadiusDamage(this, this.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), NULL, NULL, WEP_CVAR_SEC(hagar, force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR_SEC(hagar, damage), WEP_CVAR_SEC(hagar, edgedamage), WEP_CVAR_SEC(hagar, radius), NULL, NULL, WEP_CVAR_SEC(hagar, force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } void W_Hagar_Explode2_use(entity this, entity actor, entity trigger) { - W_Hagar_Explode2(this); + W_Hagar_Explode2(this, trigger); } void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -113,18 +114,18 @@ void W_Hagar_Damage(entity this, entity inflictor, entity attacker, float damage W_PrepareExplosionByDamage(this, attacker, getthink(this)); } -void W_Hagar_Touch(entity this) +void W_Hagar_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - this.use(this, NULL, NULL); + PROJECTILE_TOUCH(this, toucher); + this.use(this, NULL, toucher); } -void W_Hagar_Touch2(entity this) +void W_Hagar_Touch2(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); - if(this.cnt > 0 || other.takedamage == DAMAGE_AIM) { - this.use(this, NULL, NULL); + if(this.cnt > 0 || toucher.takedamage == DAMAGE_AIM) { + this.use(this, NULL, toucher); } else { this.cnt++; Send_Effect(EFFECT_HAGAR_BOUNCE, this.origin, this.velocity, 1); @@ -134,13 +135,13 @@ void W_Hagar_Touch2(entity this) } } -void W_Hagar_Attack(Weapon thiswep, entity actor) +void W_Hagar_Attack(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(hagar, ammo)); - W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hagar, damage)); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -154,6 +155,7 @@ void W_Hagar_Attack(Weapon thiswep, entity actor) missile.damageforcescale = WEP_CVAR_PRI(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); settouch(missile, W_Hagar_Touch); missile.use = W_Hagar_Explode_use; @@ -164,11 +166,13 @@ void W_Hagar_Attack(Weapon thiswep, entity actor) setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); W_SetupProjVelocity_PRI(missile, hagar); missile.angles = vectoangles(missile.velocity); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, true, PROJECTILE_HAGAR, true); @@ -176,13 +180,13 @@ void W_Hagar_Attack(Weapon thiswep, entity actor) MUTATOR_CALLHOOK(EditProjectile, actor, missile); } -void W_Hagar_Attack2(Weapon thiswep, entity actor) +void W_Hagar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hagar, ammo)); - W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -196,6 +200,7 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor) missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); settouch(missile, W_Hagar_Touch2); missile.cnt = 0; @@ -207,11 +212,13 @@ void W_Hagar_Attack2(Weapon thiswep, entity actor) setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); - missile.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(missile, MOVETYPE_BOUNCEMISSILE); W_SetupProjVelocity_SEC(missile, hagar); missile.angles = vectoangles(missile.velocity); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; CSQCProjectile(missile, true, PROJECTILE_HAGAR_BOUNCING, true); @@ -234,7 +241,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) weapon_prepareattack_do(actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire)); - W_SetupShot(actor, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); + W_SetupShot(actor, weaponentity, false, 2, SND_HAGAR_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hagar, damage)); Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); forward = v_forward; @@ -255,6 +262,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) missile.damageforcescale = WEP_CVAR_SEC(hagar, damageforcescale); missile.event_damage = W_Hagar_Damage; missile.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, missile); settouch(missile, W_Hagar_Touch); // not bouncy missile.use = W_Hagar_Explode2_use; @@ -264,7 +272,7 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) missile.projectiledeathtype = WEP_HAGAR.m_id | HITTYPE_SECONDARY; setorigin(missile, w_shotorg); setsize(missile, '0 0 0', '0 0 0'); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); missile.missile_flags = MIF_SPLASH; // per-shot spread calculation: the more shots there are, the less spread is applied (based on the bias cvar) @@ -288,6 +296,8 @@ void W_Hagar_Attack2_Load_Release(entity actor, .entity weaponentity) missile.angles = vectoangles(missile.velocity); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); CSQCProjectile(missile, true, PROJECTILE_HAGAR, true); @@ -409,7 +419,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.hagar_load || actor.hagar_loadblock) + if(!(fire & 1) || actor.hagar_load || actor.hagar_loadblock || PS(actor).m_switchweapon != WEP_HAGAR) { w_ready(thiswep, actor, weaponentity, fire); return; @@ -423,7 +433,7 @@ void W_Hagar_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity, int return; } - W_Hagar_Attack(thiswep, actor); + W_Hagar_Attack(thiswep, actor, weaponentity); int slot = weaponslot(weaponentity); ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hagar, refire) * W_WeaponRateFactor(actor); @@ -463,19 +473,18 @@ METHOD(Hagar, wr_think, void(entity thiswep, entity actor, .entity weaponentity, { if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hagar, refire))) { - W_Hagar_Attack2(thiswep, actor); + W_Hagar_Attack2(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hagar, refire), w_ready); } } } -METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor)) +METHOD(Hagar, wr_gonethink, void(entity thiswep, entity actor, .entity weaponentity)) { // we lost the weapon and want to prepare switching away if(actor.hagar_load) { - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - actor.(weaponentity).state = WS_READY; - W_Hagar_Attack2_Load_Release(actor, weaponentity); + actor.(weaponentity).state = WS_READY; + W_Hagar_Attack2_Load_Release(actor, weaponentity); } } METHOD(Hagar, wr_setup, void(entity thiswep, entity actor)) @@ -504,17 +513,16 @@ METHOD(Hagar, wr_resetplayer, void(entity thiswep, entity actor)) { actor.hagar_load = 0; } -METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor)) +METHOD(Hagar, wr_playerdeath, void(entity thiswep, entity actor, .entity weaponentity)) { - .entity weaponentity = weaponentities[0]; // TODO: unhardcode // if we have any rockets loaded when we die, release them if(actor.hagar_load && WEP_CVAR_SEC(hagar, load_releasedeath)) - W_Hagar_Attack2_Load_Release(actor, weaponentity); + W_Hagar_Attack2_Load_Release(actor, weaponentity); } METHOD(Hagar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { if(!actor.hagar_load) // require releasing loaded rockets first - W_Reload(actor, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(hagar, ammo), WEP_CVAR_SEC(hagar, ammo)), SND_RELOAD); } METHOD(Hagar, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/hagar.qh b/qcsrc/common/weapons/weapon/hagar.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hagar.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hlac.qc b/qcsrc/common/weapons/weapon/hlac.qc index c176e84c67..79bf08193e 100644 --- a/qcsrc/common/weapons/weapon/hlac.qc +++ b/qcsrc/common/weapons/weapon/hlac.qc @@ -1,12 +1,13 @@ +#include "hlac.qh" #ifndef IMPLEMENTATION CLASS(HLAC, Weapon) -/* ammotype */ ATTRIB(HLAC, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(HLAC, impulse, int, 6) +/* ammotype */ ATTRIB(HLAC, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(HLAC, impulse, int, 6); /* flags */ ATTRIB(HLAC, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH); /* rating */ ATTRIB(HLAC, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(HLAC, wpcolor, vector, '0 1 0'); /* modelname */ ATTRIB(HLAC, mdl, string, "hlac"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(HLAC, m_model, Model, MDL_HLAC_ITEM); #endif /* crosshair */ ATTRIB(HLAC, w_crosshair, string, "gfx/crosshairhlac"); @@ -54,22 +55,22 @@ REGISTER_WEAPON(HLAC, hlac, NEW(HLAC)); #ifdef SVQC spawnfunc(weapon_hlac) { weapon_defaultspawnfunc(this, WEP_HLAC); } -void W_HLAC_Touch(entity this) +void W_HLAC_Touch(entity this, entity toucher) { float isprimary; - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); this.event_damage = func_null; 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), NULL, NULL, WEP_CVAR_BOTH(hlac, isprimary, force), this.projectiledeathtype, other); + 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, toucher); - remove(this); + delete(this); } -void W_HLAC_Attack(Weapon thiswep, entity actor) +void W_HLAC_Attack(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; float spread; @@ -81,7 +82,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor) if(actor.crouch) spread = spread * WEP_CVAR_PRI(hlac, spread_crouchmod); - W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); + W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(hlac, damage)); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); if(!autocvar_g_norecoil) { @@ -95,7 +96,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor) missile.bot_dodgerating = WEP_CVAR_PRI(hlac, damage); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); setorigin(missile, w_shotorg); @@ -110,6 +111,8 @@ void W_HLAC_Attack(Weapon thiswep, entity actor) missile.nextthink = time + WEP_CVAR_PRI(hlac, lifetime); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.projectiledeathtype = WEP_HLAC.m_id; CSQCProjectile(missile, true, PROJECTILE_HLAC, true); @@ -117,7 +120,7 @@ void W_HLAC_Attack(Weapon thiswep, entity actor) MUTATOR_CALLHOOK(EditProjectile, actor, missile); } -void W_HLAC_Attack2(entity actor) +void W_HLAC_Attack2(entity actor, .entity weaponentity) { entity missile; float spread; @@ -128,7 +131,7 @@ void W_HLAC_Attack2(entity actor) if(actor.crouch) spread = spread * WEP_CVAR_SEC(hlac, spread_crouchmod); - W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); + W_SetupShot(actor, weaponentity, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hlac, damage)); Send_Effect(EFFECT_BLASTER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); missile = new(hlacbolt); @@ -137,7 +140,7 @@ void W_HLAC_Attack2(entity actor) missile.bot_dodgerating = WEP_CVAR_SEC(hlac, damage); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(missile); setorigin(missile, w_shotorg); @@ -152,6 +155,8 @@ void W_HLAC_Attack2(entity actor) missile.nextthink = time + WEP_CVAR_SEC(hlac, lifetime); missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; missile.projectiledeathtype = WEP_HLAC.m_id | HITTYPE_SECONDARY; @@ -181,7 +186,7 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int int slot = weaponslot(weaponentity); ATTACK_FINISHED(actor, slot) = time + WEP_CVAR_PRI(hlac, refire) * W_WeaponRateFactor(actor); - W_HLAC_Attack(WEP_HLAC, actor); + W_HLAC_Attack(WEP_HLAC, actor, weaponentity); actor.misc_bulletcounter = actor.misc_bulletcounter + 1; weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); } @@ -191,14 +196,14 @@ void W_HLAC_Attack_Frame(Weapon thiswep, entity actor, .entity weaponentity, int } } -void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor) +void W_HLAC_Attack2_Frame(Weapon thiswep, entity actor, .entity weaponentity) { float i; W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hlac, ammo)); for(i=WEP_CVAR_SEC(hlac, shots);i>0;--i) - W_HLAC_Attack2(actor); + W_HLAC_Attack2(actor, weaponentity); if(!autocvar_g_norecoil) { @@ -220,7 +225,7 @@ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(hlac, refire))) { actor.misc_bulletcounter = 0; - W_HLAC_Attack(thiswep, actor); + W_HLAC_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(hlac, refire), W_HLAC_Attack_Frame); } } @@ -229,7 +234,7 @@ METHOD(HLAC, wr_think, void(entity thiswep, entity actor, .entity weaponentity, { if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hlac, refire))) { - W_HLAC_Attack2_Frame(thiswep, actor); + W_HLAC_Attack2_Frame(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hlac, animtime), w_ready); } } @@ -248,7 +253,7 @@ METHOD(HLAC, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(HLAC, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(hlac, ammo), WEP_CVAR_SEC(hlac, ammo)), SND_RELOAD); } METHOD(HLAC, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/hlac.qh b/qcsrc/common/weapons/weapon/hlac.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hlac.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/hook.qc b/qcsrc/common/weapons/weapon/hook.qc index 5963b3bc7d..f5dd96e4c3 100644 --- a/qcsrc/common/weapons/weapon/hook.qc +++ b/qcsrc/common/weapons/weapon/hook.qc @@ -1,12 +1,13 @@ +#include "hook.qh" #ifndef IMPLEMENTATION CLASS(Hook, Weapon) -/* ammotype */ ATTRIB(Hook, ammo_field, .int, ammo_fuel) -/* impulse */ ATTRIB(Hook, impulse, int, 0) +/* ammotype */ ATTRIB(Hook, ammo_field, .int, ammo_fuel); +/* impulse */ ATTRIB(Hook, impulse, int, 0); /* flags */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Hook, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0'); /* modelname */ ATTRIB(Hook, mdl, string, "hookgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Hook, m_model, Model, MDL_HOOK_ITEM); #endif /* crosshair */ ATTRIB(Hook, w_crosshair, string, "gfx/crosshairhook"); @@ -14,7 +15,7 @@ CLASS(Hook, Weapon) /* wepimg */ ATTRIB(Hook, model2, string, "weaponhook"); /* refname */ ATTRIB(Hook, netname, string, "hook"); /* wepname */ ATTRIB(Hook, m_name, string, _("Grappling Hook")); - ATTRIB(Hook, ammo_factor, float, 1) + ATTRIB(Hook, ammo_factor, float, 1); #define X(BEGIN, P, END, class, prefix) \ BEGIN(class) \ @@ -96,7 +97,7 @@ void W_Hook_ExplodeThink(entity this) if(dt < this.dmg_duration) this.nextthink = time + 0.05; // soon else - remove(this); + delete(this); } void W_Hook_Explode2(entity this) @@ -115,7 +116,7 @@ void W_Hook_Explode2(entity this) this.dmg_duration = WEP_CVAR_SEC(hook, duration); this.teleport_time = time; this.dmg_last = 1; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); } void W_Hook_Explode2_use(entity this, entity actor, entity trigger) @@ -137,22 +138,22 @@ void W_Hook_Damage(entity this, entity inflictor, entity attacker, float damage, W_PrepareExplosionByDamage(this, this.realowner, W_Hook_Explode2); } -void W_Hook_Touch2(entity this) +void W_Hook_Touch2(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); this.use(this, NULL, NULL); } -void W_Hook_Attack2(Weapon thiswep, entity actor) +void W_Hook_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { //W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(hook, ammo)); // WEAPONTODO: Figure out how to handle ammo with hook secondary (gravitybomb) - W_SetupShot(actor, false, 4, SND_HOOKBOMB_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); + W_SetupShot(actor, weaponentity, false, 4, SND_HOOKBOMB_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(hook, damage)); entity gren = new(hookbomb); gren.owner = gren.realowner = actor; gren.bot_dodge = true; gren.bot_dodgerating = WEP_CVAR_SEC(hook, damage); - gren.movetype = MOVETYPE_TOSS; + set_movetype(gren, MOVETYPE_TOSS); PROJECTILE_MAKETRIGGER(gren); gren.projectiledeathtype = WEP_HOOK.m_id | HITTYPE_SECONDARY; setorigin(gren, w_shotorg); @@ -168,6 +169,7 @@ void W_Hook_Attack2(Weapon thiswep, entity actor) gren.damageforcescale = WEP_CVAR_SEC(hook, damageforcescale); gren.event_damage = W_Hook_Damage; gren.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, gren); gren.missile_flags = MIF_SPLASH | MIF_ARC; gren.velocity = '0 0 1' * WEP_CVAR_SEC(hook, speed); @@ -179,6 +181,8 @@ void W_Hook_Attack2(Weapon thiswep, entity actor) gren.angles = '0 0 0'; gren.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, gren); + IL_PUSH(g_bot_dodge, gren); CSQCProjectile(gren, true, PROJECTILE_HOOKBOMB, true); @@ -207,7 +211,7 @@ METHOD(Hook, wr_think, void(entity thiswep, entity actor, .entity weaponentity, { if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(hook, refire))) { - W_Hook_Attack2(thiswep, actor); + W_Hook_Attack2(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(hook, animtime), w_ready); } } @@ -380,13 +384,16 @@ void Draw_GrapplingHook(entity this) break; } - if((this.owner.sv_entnum == player_localentnum - 1) && autocvar_chase_active <= 0) + if((this.owner.sv_entnum == player_localentnum - 1)) { switch(this.HookType) { default: case NET_ENT_CLIENT_HOOK: - a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z; + if(autocvar_chase_active > 0) + a = csqcplayer.origin; + else + a = view_origin + view_forward * vs.x + view_right * -vs.y + view_up * vs.z; b = this.origin; break; case NET_ENT_CLIENT_ARC_BEAM: @@ -441,6 +448,10 @@ void Draw_GrapplingHook(entity this) break; } + MUTATOR_CALLHOOK(DrawGrapplingHook, this, tex, rgb, t); + tex = M_ARGV(1, string); + rgb = M_ARGV(2, vector); + Draw_GrapplingHook_trace_callback_tex = tex; Draw_GrapplingHook_trace_callback_rnd = offset; Draw_GrapplingHook_trace_callback_rgb = rgb; @@ -537,6 +548,7 @@ NET_HANDLE(ENT_CLIENT_HOOK, bool bIsNew) if(bIsNew || !this.teleport_time) { this.draw = Draw_GrapplingHook; + IL_PUSH(g_drawables, this); this.entremove = Remove_GrapplingHook; switch(this.HookType) diff --git a/qcsrc/common/weapons/weapon/hook.qh b/qcsrc/common/weapons/weapon/hook.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/hook.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index 331e2dd929..81da28ddb0 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -1,12 +1,13 @@ +#include "machinegun.qh" #ifndef IMPLEMENTATION CLASS(MachineGun, Weapon) -/* ammotype */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails) -/* impulse */ ATTRIB(MachineGun, impulse, int, 3) +/* ammotype */ ATTRIB(MachineGun, ammo_field, .int, ammo_nails); +/* impulse */ ATTRIB(MachineGun, impulse, int, 3); /* flags */ ATTRIB(MachineGun, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN); /* rating */ ATTRIB(MachineGun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(MachineGun, wpcolor, vector, '1 1 0'); /* modelname */ ATTRIB(MachineGun, mdl, string, "uzi"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(MachineGun, m_model, Model, MDL_MACHINEGUN_ITEM); #endif /* crosshair */ ATTRIB(MachineGun, w_crosshair, string, "gfx/crosshairuzi"); @@ -88,27 +89,29 @@ void W_MachineGun_MuzzleFlash_Think(entity this) } -void W_MachineGun_MuzzleFlash(entity actor) +void W_MachineGun_MuzzleFlash(entity actor, .entity weaponentity) { - if(actor.muzzle_flash == NULL) - actor.muzzle_flash = spawn(); + entity wepent = actor.(weaponentity); + + if(wepent.muzzle_flash == NULL) + wepent.muzzle_flash = spawn(); // muzzle flash for 1st person view - setmodel(actor.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below - - actor.muzzle_flash.scale = 0.75; - setthink(actor.muzzle_flash, W_MachineGun_MuzzleFlash_Think); - actor.muzzle_flash.nextthink = time + 0.02; - actor.muzzle_flash.frame = 2; - actor.muzzle_flash.alpha = 0.75; - actor.muzzle_flash.angles_z = random() * 180; - actor.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - actor.muzzle_flash.owner = actor.muzzle_flash.realowner = actor; + setmodel(wepent.muzzle_flash, MDL_MACHINEGUN_MUZZLEFLASH); // precision set below + + wepent.muzzle_flash.scale = 0.75; + setthink(wepent.muzzle_flash, W_MachineGun_MuzzleFlash_Think); + wepent.muzzle_flash.nextthink = time + 0.02; + wepent.muzzle_flash.frame = 2; + wepent.muzzle_flash.alpha = 0.75; + wepent.muzzle_flash.angles_z = random() * 180; + wepent.muzzle_flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; + wepent.muzzle_flash.owner = wepent.muzzle_flash.realowner = wepent; } void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity weaponentity) { - W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, ((actor.misc_bulletcounter == 1) ? WEP_CVAR(machinegun, first_damage) : WEP_CVAR(machinegun, sustained_damage))); if(!autocvar_g_norecoil) { actor.punchangle_x = random() - 0.5; @@ -125,12 +128,15 @@ void W_MachineGun_Attack(Weapon thiswep, int deathtype, entity actor, .entity we Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - W_MachineGun_MuzzleFlash(actor); - W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); // casing code if(autocvar_g_casings >= 2) - 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); + { + 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); + } if(actor.misc_bulletcounter == 1) W_DecreaseAmmo(thiswep, actor, WEP_CVAR(machinegun, first_ammo)); @@ -184,7 +190,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity W_DecreaseAmmo(WEP_MACHINEGUN, actor, WEP_CVAR(machinegun, sustained_ammo)); - W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); if(!autocvar_g_norecoil) { actor.punchangle_x = random() - 0.5; @@ -198,11 +204,14 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - W_MachineGun_MuzzleFlash(actor); - W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); if(autocvar_g_casings >= 2) // casing code - 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); + { + 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(machinegun, first_refire) * W_WeaponRateFactor(actor); @@ -211,7 +220,7 @@ void W_MachineGun_Attack_Auto(Weapon thiswep, entity actor, .entity weaponentity void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentity, int fire) { - W_SetupShot(actor, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); + W_SetupShot(actor, weaponentity, true, 0, SND_UZI_FIRE, CH_WEAPON_A, WEP_CVAR(machinegun, sustained_damage)); if(!autocvar_g_norecoil) { actor.punchangle_x = random() - 0.5; @@ -222,11 +231,14 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - W_MachineGun_MuzzleFlash(actor); - W_AttachToShotorg(actor, actor.muzzle_flash, '5 0 0'); + W_MachineGun_MuzzleFlash(actor, weaponentity); + W_AttachToShotorg(actor, weaponentity, actor.(weaponentity).muzzle_flash, '5 0 0'); if(autocvar_g_casings >= 2) // casing code - 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); + { + 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); + } actor.misc_bulletcounter = actor.misc_bulletcounter + 1; if(actor.misc_bulletcounter == 0) @@ -336,7 +348,7 @@ METHOD(MachineGun, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(MachineGun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(max(WEP_CVAR(machinegun, sustained_ammo), WEP_CVAR(machinegun, first_ammo)), WEP_CVAR(machinegun, burst_ammo)), SND_RELOAD); } METHOD(MachineGun, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/machinegun.qh b/qcsrc/common/weapons/weapon/machinegun.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/machinegun.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/minelayer.qc b/qcsrc/common/weapons/weapon/minelayer.qc index a406e75098..7df6984baa 100644 --- a/qcsrc/common/weapons/weapon/minelayer.qc +++ b/qcsrc/common/weapons/weapon/minelayer.qc @@ -1,12 +1,13 @@ +#include "minelayer.qh" #ifndef IMPLEMENTATION CLASS(MineLayer, Weapon) -/* ammotype */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(MineLayer, impulse, int, 4) +/* ammotype */ ATTRIB(MineLayer, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(MineLayer, impulse, int, 4); /* flags */ ATTRIB(MineLayer, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH); /* rating */ ATTRIB(MineLayer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(MineLayer, wpcolor, vector, '0.75 1 0'); /* modelname */ ATTRIB(MineLayer, mdl, string, "minelayer"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(MineLayer, m_model, Model, MDL_MINELAYER_ITEM); #endif /* crosshair */ ATTRIB(MineLayer, w_crosshair, string, "gfx/crosshairminelayer"); @@ -70,6 +71,7 @@ void W_MineLayer_Stick(entity this, entity to) // in order for mines to face properly when sticking to the ground, they must be a server side entity rather than a csqc projectile entity newmine = spawn(); + IL_PUSH(g_mines, newmine); newmine.classname = this.classname; newmine.bot_dodge = this.bot_dodge; @@ -90,8 +92,9 @@ void W_MineLayer_Stick(entity this, entity to) newmine.event_damage = this.event_damage; newmine.spawnshieldtime = this.spawnshieldtime; newmine.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, newmine); - newmine.movetype = MOVETYPE_NONE; // lock the mine in place + set_movetype(newmine, MOVETYPE_NONE); // lock the mine in place newmine.projectiledeathtype = this.projectiledeathtype; newmine.mine_time = this.mine_time; @@ -101,26 +104,28 @@ void W_MineLayer_Stick(entity this, entity to) newmine.nextthink = time; newmine.cnt = this.cnt; newmine.flags = this.flags; + IL_PUSH(g_projectiles, newmine); + IL_PUSH(g_bot_dodge, newmine); - remove(this); + delete(this); if(to) SetMovetypeFollow(newmine, to); } -void W_MineLayer_Explode(entity this) +void W_MineLayer_Explode(entity this, entity directhitentity) { - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), NULL, NULL, WEP_CVAR(minelayer, force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR(minelayer, damage), WEP_CVAR(minelayer, edgedamage), WEP_CVAR(minelayer, radius), NULL, NULL, WEP_CVAR(minelayer, force), this.projectiledeathtype, directhitentity); if(PS(this.realowner).m_weapon == WEP_MINE_LAYER) { @@ -129,13 +134,19 @@ void W_MineLayer_Explode(entity this) if(!w.wr_checkammo1(w, own)) { own.cnt = WEP_MINE_LAYER.m_id; - int slot = 0; // TODO: unhardcode + .entity weaponentity = this.weaponentity_fld; + int slot = weaponslot(weaponentity); ATTACK_FINISHED(own, slot) = time; PS(own).m_switchweapon = w_getbestweapon(own); } } this.realowner.minelayer_mines -= 1; - remove(this); + delete(this); +} + +void W_MineLayer_Explode_think(entity this) +{ + W_MineLayer_Explode(this, NULL); } void W_MineLayer_DoRemoteExplode(entity this) @@ -143,7 +154,7 @@ void W_MineLayer_DoRemoteExplode(entity this) this.event_damage = func_null; this.takedamage = DAMAGE_NO; - if(this.movetype == MOVETYPE_NONE || this.movetype == MOVETYPE_FOLLOW) + 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), NULL, NULL, WEP_CVAR(minelayer, remote_force), this.projectiledeathtype | HITTYPE_BOUNCE, NULL); @@ -155,13 +166,14 @@ void W_MineLayer_DoRemoteExplode(entity this) if(!w.wr_checkammo1(w, own)) { own.cnt = WEP_MINE_LAYER.m_id; - int slot = 0; // TODO: unhardcode + .entity weaponentity = this.weaponentity_fld; + int slot = weaponslot(weaponentity); ATTACK_FINISHED(own, slot) = time; PS(own).m_switchweapon = w_getbestweapon(own); } } this.realowner.minelayer_mines -= 1; - remove(this); + delete(this); } void W_MineLayer_RemoteExplode(entity this) @@ -192,15 +204,16 @@ void W_MineLayer_ProximityExplode(entity this) } this.mine_time = 0; - W_MineLayer_Explode(this); + W_MineLayer_Explode(this, NULL); } int W_MineLayer_Count(entity e) { int minecount = 0; - entity mine; - for(mine = NULL; (mine = find(mine, classname, "mine")); ) if(mine.realowner == e) + IL_EACH(g_mines, it.realowner == e, + { minecount += 1; + }); return minecount; } @@ -211,12 +224,12 @@ void W_MineLayer_Think(entity this) this.nextthink = time; - if(this.movetype == MOVETYPE_FOLLOW) + if(this.move_movetype == MOVETYPE_FOLLOW) { if(LostMovetypeFollow(this)) { UnsetMovetypeFollow(this); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); } } @@ -234,9 +247,8 @@ void W_MineLayer_Think(entity this) // TODO: Do this on team change too -- Samual: But isn't a player killed when they switch teams? if(!IS_PLAYER(this.realowner) || IS_DEAD(this.realowner) || STAT(FROZEN, this.realowner)) { - other = NULL; this.projectiledeathtype |= HITTYPE_BOUNCE; - W_MineLayer_Explode(this); + W_MineLayer_Explode(this, NULL); return; } @@ -268,26 +280,26 @@ void W_MineLayer_Think(entity this) W_MineLayer_RemoteExplode(this); } -void W_MineLayer_Touch(entity this) +void W_MineLayer_Touch(entity this, entity toucher) { - if(this.movetype == MOVETYPE_NONE || this.movetype == MOVETYPE_FOLLOW) + 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)) + if(WarpZone_Projectile_Touch(this, toucher)) { if(wasfreed(this)) this.realowner.minelayer_mines -= 1; return; } - if(other && IS_PLAYER(other) && !IS_DEAD(other)) + if(toucher && IS_PLAYER(toucher) && !IS_DEAD(toucher)) { // hit a player // don't stick } else { - W_MineLayer_Stick(this, other); + W_MineLayer_Stick(this, toucher); } } @@ -305,10 +317,10 @@ void W_MineLayer_Damage(entity this, entity inflictor, entity attacker, float da this.angles = vectoangles(this.velocity); if(this.health <= 0) - W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode); + W_PrepareExplosionByDamage(this, attacker, W_MineLayer_Explode_think); } -void W_MineLayer_Attack(Weapon thiswep, entity actor) +void W_MineLayer_Attack(Weapon thiswep, entity actor, .entity weaponentity) { entity mine; entity flash; @@ -327,10 +339,12 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor) W_DecreaseAmmo(thiswep, actor, WEP_CVAR(minelayer, ammo)); - W_SetupShot_ProjectileSize(actor, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-4 -4 -4', '4 4 4', false, 5, SND_MINE_FIRE, CH_WEAPON_A, WEP_CVAR(minelayer, damage)); Send_Effect(EFFECT_ROCKET_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); mine = WarpZone_RefSys_SpawnSameRefSys(actor); + mine.weaponentity_fld = weaponentity; + IL_PUSH(g_mines, mine); mine.owner = mine.realowner = actor; if(WEP_CVAR(minelayer, detonatedelay) >= 0) mine.spawnshieldtime = time + WEP_CVAR(minelayer, detonatedelay); @@ -345,8 +359,9 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor) mine.health = WEP_CVAR(minelayer, health); mine.event_damage = W_MineLayer_Damage; mine.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, mine); - mine.movetype = MOVETYPE_TOSS; + set_movetype(mine, MOVETYPE_TOSS); PROJECTILE_MAKETRIGGER(mine); mine.projectiledeathtype = WEP_MINE_LAYER.m_id; setsize(mine, '-4 -4 -4', '4 4 4'); // give it some size so it can be shot @@ -360,6 +375,8 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor) mine.nextthink = time; mine.cnt = (WEP_CVAR(minelayer, lifetime) - WEP_CVAR(minelayer, lifetime_countdown)); mine.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, mine); + IL_PUSH(g_bot_dodge, mine); mine.missile_flags = MIF_SPLASH | MIF_ARC | MIF_PROXY; if(mine.cnt > 0) { mine.cnt += time; } @@ -371,7 +388,7 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor) setmodel(flash, MDL_MINELAYER_MUZZLEFLASH); // precision set below SUB_SetFade(flash, time, 0.1); flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(actor, flash, '5 0 0'); + W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); // common properties @@ -380,24 +397,23 @@ void W_MineLayer_Attack(Weapon thiswep, entity actor) actor.minelayer_mines = W_MineLayer_Count(actor); } -float W_MineLayer_PlacedMines(entity this, float detonate) +bool W_MineLayer_PlacedMines(entity this, bool detonate) { - entity mine; - float minfound = 0; + bool minfound = false; - for(mine = NULL; (mine = find(mine, classname, "mine")); ) if(mine.realowner == this) + IL_EACH(g_mines, it.realowner == this, { if(detonate) { - if(!mine.minelayer_detonate) + if(!it.minelayer_detonate) { - mine.minelayer_detonate = true; - minfound = 1; + it.minelayer_detonate = true; + minfound = true; } } else - minfound = 1; - } + minfound = true; + }); return minfound; } @@ -411,8 +427,7 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor)) if(skill >= 2) // skill 0 and 1 bots won't detonate mines! { // decide whether to detonate mines - entity targetlist, targ; - float edgedamage, coredamage, edgeradius, recipricoledgeradius, d; + float edgedamage, coredamage, edgeradius, recipricoledgeradius; float selfdamage, teamdamage, enemydamage; edgedamage = WEP_CVAR(minelayer, edgedamage); coredamage = WEP_CVAR(minelayer, damage); @@ -421,31 +436,24 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor)) selfdamage = 0; teamdamage = 0; enemydamage = 0; - targetlist = findchainfloat(bot_attack, true); - entity mine = find(NULL, classname, "mine"); - while(mine) + + IL_EACH(g_mines, it.realowner == actor, { - if(mine.realowner != actor) - { - mine = find(mine, classname, "mine"); - continue; - } - targ = targetlist; - while(targ) - { - d = vlen(targ.origin + (targ.mins + targ.maxs) * 0.5 - mine.origin); - d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); + entity mine = it; + IL_EACH(g_bot_targets, it.bot_attack, + { + float d = vlen(it.origin + (it.mins + it.maxs) * 0.5 - mine.origin); + d = bound(0, edgedamage + (coredamage - edgedamage) * sqrt(1 - d * recipricoledgeradius), 10000); // count potential damage according to type of target - if(targ == actor) + if(it == actor) selfdamage = selfdamage + d; - else if(targ.team == actor.team && teamplay) + else if(SAME_TEAM(it, actor)) teamdamage = teamdamage + d; - else if(bot_shouldattack(actor, targ)) + else if(bot_shouldattack(actor, it)) enemydamage = enemydamage + d; - targ = targ.chain; - } - mine = find(mine, classname, "mine"); - } + }); + }); + float desirabledamage; desirabledamage = enemydamage; if(time > actor.invincible_finished && time > actor.spawnshieldtime) @@ -453,41 +461,35 @@ METHOD(MineLayer, wr_aim, void(entity thiswep, entity actor)) if(teamplay && actor.team) desirabledamage = desirabledamage - teamdamage; - mine = find(NULL, classname, "mine"); - while(mine) + makevectors(actor.v_angle); + IL_EACH(g_mines, it.realowner == actor, { - if(mine.realowner != actor) - { - mine = find(mine, classname, "mine"); - continue; - } - makevectors(mine.v_angle); - targ = targetlist; if(skill > 9) // normal players only do this for the target they are tracking { - targ = targetlist; - while(targ) - { - if( - (v_forward * normalize(mine.origin - targ.origin)< 0.1) - && desirabledamage > 0.1*coredamage - ) PHYS_INPUT_BUTTON_ATCK2(actor) = true; - targ = targ.chain; - } - }else{ - float distance; distance= bound(300,vlen(actor.origin-actor.enemy.origin),30000); + entity mine = it; + IL_EACH(g_bot_targets, it.bot_attack, + { + if((v_forward * normalize(mine.origin - it.origin) < 0.1) + && desirabledamage > 0.1 * coredamage + ) PHYS_INPUT_BUTTON_ATCK2(actor) = true; + }); + } + else + { //As the distance gets larger, a correct detonation gets near imposible //Bots are assumed to use the mine spawnfunc_light to see if the mine gets near a player - if(v_forward * normalize(mine.origin - actor.enemy.origin)< 0.1) - if(IS_PLAYER(actor.enemy)) - if(desirabledamage >= 0.1*coredamage) - if(random()/distance*300 > frametime*bound(0,(10-skill)*0.2,1)) - PHYS_INPUT_BUTTON_ATCK2(actor) = true; - // dprint(ftos(random()/distance*300),">");dprint(ftos(frametime*bound(0,(10-skill)*0.2,1)),"\n"); - } - - mine = find(mine, classname, "mine"); - } + if((v_forward * normalize(it.origin - actor.enemy.origin) < 0.1) + && IS_PLAYER(actor.enemy) + && (desirabledamage >= 0.1 * coredamage) + ) + { + float distance = bound(300, vlen(actor.origin - actor.enemy.origin), 30000); + if(random() / distance * 300 > frametime * bound(0, (10 - skill) * 0.2, 1)) + PHYS_INPUT_BUTTON_ATCK2(actor) = true; + } + } + }); + // if we would be doing at X percent of the core damage, detonate it // 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 @@ -512,7 +514,7 @@ METHOD(MineLayer, wr_think, void(entity thiswep, entity actor, .entity weaponent { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(minelayer, refire))) { - W_MineLayer_Attack(thiswep, actor); + W_MineLayer_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(minelayer, animtime), w_ready); } } @@ -548,7 +550,7 @@ METHOD(MineLayer, wr_resetplayer, void(entity thiswep, entity actor)) } METHOD(MineLayer, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, WEP_CVAR(minelayer, ammo), SND_RELOAD); + W_Reload(actor, weaponentity, WEP_CVAR(minelayer, ammo), SND_RELOAD); } METHOD(MineLayer, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/minelayer.qh b/qcsrc/common/weapons/weapon/minelayer.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/minelayer.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/mortar.qc b/qcsrc/common/weapons/weapon/mortar.qc index 5f56f19db1..89ff5bbd88 100644 --- a/qcsrc/common/weapons/weapon/mortar.qc +++ b/qcsrc/common/weapons/weapon/mortar.qc @@ -1,12 +1,13 @@ +#include "mortar.qh" #ifndef IMPLEMENTATION CLASS(Mortar, Weapon) -/* ammotype */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(Mortar, impulse, int, 4) +/* ammotype */ ATTRIB(Mortar, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(Mortar, impulse, int, 4); /* flags */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Mortar, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Mortar, wpcolor, vector, '1 0 0'); /* modelname */ ATTRIB(Mortar, mdl, string, "gl"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Mortar, m_model, Model, MDL_MORTAR_ITEM); #endif /* crosshair */ ATTRIB(Mortar, w_crosshair, string, "gfx/crosshairgrenadelauncher"); @@ -64,54 +65,54 @@ REGISTER_WEAPON(MORTAR, mortar, NEW(Mortar)); spawnfunc(weapon_mortar) { weapon_defaultspawnfunc(this, WEP_MORTAR); } spawnfunc(weapon_grenadelauncher) { spawnfunc_weapon_mortar(this); } -void W_Mortar_Grenade_Explode(entity this) +void W_Mortar_Grenade_Explode(entity this, entity directhitentity) { - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - if(this.movetype == MOVETYPE_NONE) + if(this.move_movetype == MOVETYPE_NONE) this.velocity = this.oldvelocity; - RadiusDamage(this, this.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), NULL, NULL, WEP_CVAR_PRI(mortar, force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR_PRI(mortar, damage), WEP_CVAR_PRI(mortar, edgedamage), WEP_CVAR_PRI(mortar, radius), NULL, NULL, WEP_CVAR_PRI(mortar, force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } void W_Mortar_Grenade_Explode_use(entity this, entity actor, entity trigger) { - W_Mortar_Grenade_Explode(this); + W_Mortar_Grenade_Explode(this, trigger); } -void W_Mortar_Grenade_Explode2(entity this) +void W_Mortar_Grenade_Explode2(entity this, entity directhitentity) { - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_AIRSHOT); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - if(this.movetype == MOVETYPE_NONE) + if(this.move_movetype == MOVETYPE_NONE) this.velocity = this.oldvelocity; - RadiusDamage(this, this.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), NULL, NULL, WEP_CVAR_SEC(mortar, force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR_SEC(mortar, damage), WEP_CVAR_SEC(mortar, edgedamage), WEP_CVAR_SEC(mortar, radius), NULL, NULL, WEP_CVAR_SEC(mortar, force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } void W_Mortar_Grenade_Explode2_use(entity this, entity actor, entity trigger) { - W_Mortar_Grenade_Explode2(this); + W_Mortar_Grenade_Explode2(this, trigger); } void W_Mortar_Grenade_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -133,21 +134,20 @@ void W_Mortar_Grenade_Think1(entity this) this.nextthink = time; if(time > this.cnt) { - other = NULL; this.projectiledeathtype |= HITTYPE_BOUNCE; - W_Mortar_Grenade_Explode(this); + W_Mortar_Grenade_Explode(this, NULL); return; } if(this.gl_detonate_later && this.gl_bouncecnt >= WEP_CVAR_PRI(mortar, remote_minbouncecnt)) - W_Mortar_Grenade_Explode(this); + W_Mortar_Grenade_Explode(this, NULL); } -void W_Mortar_Grenade_Touch1(entity this) +void W_Mortar_Grenade_Touch1(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - if(other.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile + PROJECTILE_TOUCH(this, toucher); + if(toucher.takedamage == DAMAGE_AIM || WEP_CVAR_PRI(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile { - this.use(this, NULL, NULL); + this.use(this, NULL, toucher); } else if(WEP_CVAR_PRI(mortar, type) == 1) // bounce { @@ -156,14 +156,14 @@ void W_Mortar_Grenade_Touch1(entity this) this.projectiledeathtype |= HITTYPE_BOUNCE; this.gl_bouncecnt += 1; } - else if(WEP_CVAR_PRI(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick + else if(WEP_CVAR_PRI(mortar, type) == 2 && (!toucher || (toucher.takedamage != DAMAGE_AIM && toucher.move_movetype == MOVETYPE_NONE))) // stick { spamsound(this, CH_SHOTS, SND(GRENADE_STICK), VOL_BASE, ATTN_NORM); // let it stick whereever it is this.oldvelocity = this.velocity; this.velocity = '0 0 0'; - this.movetype = MOVETYPE_NONE; // also disables gravity + set_movetype(this, MOVETYPE_NONE); // also disables gravity this.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO UpdateCSQCProjectile(this); @@ -174,12 +174,12 @@ void W_Mortar_Grenade_Touch1(entity this) } } -void W_Mortar_Grenade_Touch2(entity this) +void W_Mortar_Grenade_Touch2(entity this, entity toucher) { - PROJECTILE_TOUCH(this); - if(other.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile + PROJECTILE_TOUCH(this, toucher); + if(toucher.takedamage == DAMAGE_AIM || WEP_CVAR_SEC(mortar, type) == 0) // always explode when hitting a player, or if normal mortar projectile { - this.use(this, NULL, NULL); + this.use(this, NULL, toucher); } else if(WEP_CVAR_SEC(mortar, type) == 1) // bounce { @@ -192,14 +192,14 @@ void W_Mortar_Grenade_Touch2(entity this) this.nextthink = time + WEP_CVAR_SEC(mortar, lifetime_bounce); } - else if(WEP_CVAR_SEC(mortar, type) == 2 && (!other || (other.takedamage != DAMAGE_AIM && other.movetype == MOVETYPE_NONE))) // stick + else if(WEP_CVAR_SEC(mortar, type) == 2 && (!toucher || (toucher.takedamage != DAMAGE_AIM && toucher.move_movetype == MOVETYPE_NONE))) // stick { spamsound(this, CH_SHOTS, SND(GRENADE_STICK), VOL_BASE, ATTN_NORM); // let it stick whereever it is this.oldvelocity = this.velocity; this.velocity = '0 0 0'; - this.movetype = MOVETYPE_NONE; // also disables gravity + set_movetype(this, MOVETYPE_NONE); // also disables gravity this.gravity = 0; // nope, it does NOT! maybe a bug in CSQC code? TODO UpdateCSQCProjectile(this); @@ -210,22 +210,20 @@ void W_Mortar_Grenade_Touch2(entity this) } } -void W_Mortar_Attack(Weapon thiswep, entity actor) +void W_Mortar_Attack(Weapon thiswep, entity actor, .entity weaponentity) { - entity gren; - W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(mortar, ammo)); - W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_PRI(mortar, damage)); w_shotdir = v_forward; // no TrueAim for grenades please Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); - gren = new(grenade); + entity gren = new(grenade); gren.owner = gren.realowner = actor; gren.bot_dodge = true; gren.bot_dodgerating = WEP_CVAR_PRI(mortar, damage); - gren.movetype = MOVETYPE_BOUNCE; + set_movetype(gren, MOVETYPE_BOUNCE); gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); gren.bouncestop = WEP_CVAR(mortar, bouncestop); PROJECTILE_MAKETRIGGER(gren); @@ -244,11 +242,14 @@ void W_Mortar_Attack(Weapon thiswep, entity actor) gren.damageforcescale = WEP_CVAR_PRI(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, gren); gren.missile_flags = MIF_SPLASH | MIF_ARC; W_SetupProjVelocity_UP_PRI(gren, mortar); gren.angles = vectoangles(gren.velocity); gren.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, gren); + IL_PUSH(g_bot_dodge, gren); if(WEP_CVAR_PRI(mortar, type) == 0 || WEP_CVAR_PRI(mortar, type) == 2) CSQCProjectile(gren, true, PROJECTILE_GRENADE, true); @@ -258,13 +259,13 @@ void W_Mortar_Attack(Weapon thiswep, entity actor) MUTATOR_CALLHOOK(EditProjectile, actor, gren); } -void W_Mortar_Attack2(Weapon thiswep, entity actor) +void W_Mortar_Attack2(Weapon thiswep, entity actor, .entity weaponentity) { entity gren; W_DecreaseAmmo(thiswep, actor, WEP_CVAR_SEC(mortar, ammo)); - W_SetupShot_ProjectileSize(actor, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-3 -3 -3', '3 3 3', false, 4, SND_GRENADE_FIRE, CH_WEAPON_A, WEP_CVAR_SEC(mortar, damage)); w_shotdir = v_forward; // no TrueAim for grenades please Send_Effect(EFFECT_GRENADE_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -273,7 +274,7 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor) gren.owner = gren.realowner = actor; gren.bot_dodge = true; gren.bot_dodgerating = WEP_CVAR_SEC(mortar, damage); - gren.movetype = MOVETYPE_BOUNCE; + set_movetype(gren, MOVETYPE_BOUNCE); gren.bouncefactor = WEP_CVAR(mortar, bouncefactor); gren.bouncestop = WEP_CVAR(mortar, bouncestop); PROJECTILE_MAKETRIGGER(gren); @@ -291,11 +292,14 @@ void W_Mortar_Attack2(Weapon thiswep, entity actor) gren.damageforcescale = WEP_CVAR_SEC(mortar, damageforcescale); gren.event_damage = W_Mortar_Grenade_Damage; gren.damagedbycontents = true; + IL_PUSH(g_damagedbycontents, gren); gren.missile_flags = MIF_SPLASH | MIF_ARC; W_SetupProjVelocity_UP_SEC(gren, mortar); gren.angles = vectoangles(gren.velocity); gren.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, gren); + IL_PUSH(g_bot_dodge, gren); if(WEP_CVAR_SEC(mortar, type) == 0 || WEP_CVAR_SEC(mortar, type) == 2) CSQCProjectile(gren, true, PROJECTILE_GRENADE, true); @@ -352,7 +356,7 @@ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(mortar, refire))) { - W_Mortar_Attack(thiswep, actor); + W_Mortar_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(mortar, animtime), w_ready); } } @@ -361,21 +365,20 @@ METHOD(Mortar, wr_think, void(entity thiswep, entity actor, .entity weaponentity if(WEP_CVAR_SEC(mortar, remote_detonateprimary)) { bool nadefound = false; - entity nade; - for(nade = NULL; (nade = find(nade, classname, "grenade")); ) if(nade.realowner == actor) + IL_EACH(g_projectiles, it.realowner == actor && it.classname == "grenade", { - if(!nade.gl_detonate_later) + if(!it.gl_detonate_later) { - nade.gl_detonate_later = true; + it.gl_detonate_later = true; nadefound = true; } - } + }); if(nadefound) sound(actor, CH_WEAPON_B, SND_ROCKET_DET, VOL_BASE, ATTN_NORM); } else if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(mortar, refire))) { - W_Mortar_Attack2(thiswep, actor); + W_Mortar_Attack2(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(mortar, animtime), w_ready); } } @@ -394,7 +397,7 @@ METHOD(Mortar, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Mortar, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND_RELOAD); // WEAPONTODO + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(mortar, ammo), WEP_CVAR_SEC(mortar, ammo)), SND_RELOAD); // WEAPONTODO } METHOD(Mortar, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/mortar.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index 3b047dc8c9..b998bc9ee8 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -1,12 +1,13 @@ +#include "porto.qh" #ifndef IMPLEMENTATION CLASS(PortoLaunch, Weapon) -/* ammotype */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none) -/* impulse */ ATTRIB(PortoLaunch, impulse, int, 0) +/* ammotype */ ATTRIB(PortoLaunch, ammo_field, .int, ammo_none); +/* impulse */ ATTRIB(PortoLaunch, impulse, int, 0); /* flags */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON); /* rating */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(PortoLaunch, wpcolor, vector, '0.5 0.5 0.5'); /* modelname */ ATTRIB(PortoLaunch, mdl, string, "porto"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(PortoLaunch, m_model, Model, MDL_PORTO_ITEM); #endif /* crosshair */ ATTRIB(PortoLaunch, w_crosshair, string, "gfx/crosshairporto"); @@ -62,7 +63,7 @@ void W_Porto_Success(entity this) } this.realowner.porto_current = NULL; - remove(this); + delete(this); } string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vector velo); @@ -89,6 +90,7 @@ void W_Porto_Fail(entity this, float failhard) if(move_out_of_solid(this)) { this.flags = FL_ITEM; + IL_PUSH(g_items, this); this.velocity = trigger_push_calculatevelocity(this.origin, this.realowner, 128); tracetoss(this, this); if(vdist(trace_endpos - this.realowner.origin, <, 128)) @@ -98,7 +100,7 @@ void W_Porto_Fail(entity this, float failhard) } } } - remove(this); + delete(this); } void W_Porto_Remove(entity p) @@ -113,25 +115,26 @@ void W_Porto_Think(entity this) { trace_plane_normal = '0 0 0'; if(this.realowner.playerid != this.playerid) - remove(this); + delete(this); else W_Porto_Fail(this, 0); } -void W_Porto_Touch(entity this) +void W_Porto_Touch(entity this, entity toucher) { vector norm; // do not use PROJECTILE_TOUCH here // FIXME but DO handle warpzones! - if(other.classname == "portal") + if(toucher.classname == "portal") return; // handled by the portal norm = trace_plane_normal; if(trace_ent.iscreature) { - traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * STAT(PL_MIN, NULL).z, MOVE_WORLDONLY, this); + // TODO: why not use entity size? + traceline(trace_ent.origin, trace_ent.origin + '0 0 2' * PL_MIN_CONST.z, MOVE_WORLDONLY, this); if(trace_fraction >= 1) return; if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) @@ -143,7 +146,7 @@ void W_Porto_Touch(entity this) if(this.realowner.playerid != this.playerid) { sound(this, CH_SHOTS, SND_PORTO_UNSUPPORTED, VOL_BASE, ATTEN_NORM); - remove(this); + delete(this); } else if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) { @@ -240,11 +243,11 @@ void W_Porto_Touch(entity this) } } -void W_Porto_Attack(entity actor, float type) +void W_Porto_Attack(entity actor, .entity weaponentity, float type) { entity gren; - W_SetupShot(actor, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0); + W_SetupShot(actor, weaponentity, false, 4, SND_PORTO_FIRE, CH_WEAPON_A, 0); // 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; @@ -257,7 +260,7 @@ void W_Porto_Attack(entity actor, float type) gren.playerid = actor.playerid; gren.bot_dodge = true; gren.bot_dodgerating = 200; - gren.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(gren, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(gren); gren.effects = EF_RED; gren.scale = 4; @@ -275,6 +278,8 @@ void W_Porto_Attack(entity actor, float type) gren.angles = vectoangles(gren.velocity); gren.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, gren); + IL_PUSH(g_bot_dodge, gren); gren.portal_id = time; actor.porto_current = gren; @@ -309,7 +314,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, 0); + W_Porto_Attack(actor, weaponentity, 0); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } @@ -318,7 +323,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, 1); + W_Porto_Attack(actor, weaponentity, 1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR_SEC(porto, animtime), w_ready); } } @@ -351,7 +356,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, -1); + W_Porto_Attack(actor, weaponentity, -1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(porto, animtime), w_ready); } } @@ -377,7 +382,7 @@ METHOD(PortoLaunch, wr_resetplayer, void(entity thiswep, entity actor)) #endif #ifdef CSQC METHOD(PortoLaunch, wr_impacteffect, void(entity this, entity actor)) { - LOG_WARNING("Since when does Porto send DamageInfo?\n"); + LOG_WARN("Since when does Porto send DamageInfo?"); } #endif #endif diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/porto.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/rifle.qc b/qcsrc/common/weapons/weapon/rifle.qc index 13e4453102..478a315253 100644 --- a/qcsrc/common/weapons/weapon/rifle.qc +++ b/qcsrc/common/weapons/weapon/rifle.qc @@ -1,12 +1,13 @@ +#include "rifle.qh" #ifndef IMPLEMENTATION CLASS(Rifle, Weapon) -/* ammotype */ ATTRIB(Rifle, ammo_field, .int, ammo_nails) -/* impulse */ ATTRIB(Rifle, impulse, int, 7) +/* ammotype */ ATTRIB(Rifle, ammo_field, .int, ammo_nails); +/* impulse */ ATTRIB(Rifle, impulse, int, 7); /* flags */ ATTRIB(Rifle, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN); /* rating */ ATTRIB(Rifle, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Rifle, wpcolor, vector, '0.5 1 0'); /* modelname */ ATTRIB(Rifle, mdl, string, "campingrifle"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Rifle, m_model, Model, MDL_RIFLE_ITEM); #endif /* crosshair */ ATTRIB(Rifle, w_crosshair, string, "gfx/crosshairrifle"); @@ -56,13 +57,13 @@ spawnfunc(weapon_rifle) { weapon_defaultspawnfunc(this, WEP_RIFLE); } spawnfunc(weapon_campingrifle) { spawnfunc_weapon_rifle(this); } spawnfunc(weapon_sniperrifle) { spawnfunc_weapon_rifle(this); } -void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor) +void W_Rifle_FireBullet(Weapon thiswep, .entity weaponentity, float pSpread, float pDamage, float pForce, float pSolidPenetration, float pAmmo, int deathtype, float pTracer, float pShots, Sound pSound, entity actor) { float i; W_DecreaseAmmo(thiswep, actor, pAmmo); - W_SetupShot(actor, true, 2, pSound, CH_WEAPON_A, pDamage * pShots); + W_SetupShot(actor, weaponentity, true, 2, pSound, CH_WEAPON_A, pDamage * pShots); Send_Effect(EFFECT_RIFLE_MUZZLEFLASH, w_shotorg, w_shotdir * 2000, 1); @@ -76,20 +77,23 @@ void W_Rifle_FireBullet(Weapon thiswep, float pSpread, float pDamage, float pFor fireBullet(actor, w_shotorg, w_shotdir, pSpread, pSolidPenetration, pDamage, pForce, deathtype, (pTracer ? EF_RED : EF_BLUE)); if(autocvar_g_casings >= 2) - 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); + { + 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); + } } -void W_Rifle_Attack(entity actor) +void W_Rifle_Attack(entity actor, .entity weaponentity) { - W_Rifle_FireBullet(WEP_RIFLE, 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(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); } -void W_Rifle_Attack2(entity actor) +void W_Rifle_Attack2(entity actor, .entity weaponentity) { - W_Rifle_FireBullet(WEP_RIFLE, 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(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); } -.void(entity actor) rifle_bullethail_attackfunc; +.void(entity actor, .entity weaponentity) rifle_bullethail_attackfunc; .WFRAME rifle_bullethail_frame; .float rifle_bullethail_animtime; .float rifle_bullethail_refire; @@ -107,7 +111,7 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent PS(actor).m_switchweapon = sw; if(r) { - actor.rifle_bullethail_attackfunc(actor); + actor.rifle_bullethail_attackfunc(actor, weaponentity); weapon_thinkf(actor, weaponentity, actor.rifle_bullethail_frame, actor.rifle_bullethail_animtime, W_Rifle_BulletHail_Continue); } else @@ -116,10 +120,10 @@ void W_Rifle_BulletHail_Continue(Weapon thiswep, entity actor, .entity weaponent } } -void W_Rifle_BulletHail(entity actor, .entity weaponentity, float mode, void(entity actor) AttackFunc, WFRAME fr, float animtime, float refire) +void W_Rifle_BulletHail(entity actor, .entity weaponentity, float mode, void(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); + AttackFunc(actor, weaponentity); if(mode) { // continue hail @@ -214,7 +218,7 @@ METHOD(Rifle, wr_resetplayer, void(entity thiswep, entity actor)) } METHOD(Rifle, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(rifle, ammo), WEP_CVAR_SEC(rifle, ammo)), SND_RELOAD); } METHOD(Rifle, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/rifle.qh b/qcsrc/common/weapons/weapon/rifle.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/rifle.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/seeker.qc b/qcsrc/common/weapons/weapon/seeker.qc index a39c3958f1..0e73adf2d3 100644 --- a/qcsrc/common/weapons/weapon/seeker.qc +++ b/qcsrc/common/weapons/weapon/seeker.qc @@ -1,12 +1,13 @@ +#include "seeker.qh" #ifndef IMPLEMENTATION CLASS(Seeker, Weapon) -/* ammotype */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets) -/* impulse */ ATTRIB(Seeker, impulse, int, 8) +/* ammotype */ ATTRIB(Seeker, ammo_field, .int, ammo_rockets); +/* impulse */ ATTRIB(Seeker, impulse, int, 8); /* flags */ ATTRIB(Seeker, spawnflags, int, WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_RELOADABLE | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Seeker, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Seeker, wpcolor, vector, '0.5 1 0'); /* modelname */ ATTRIB(Seeker, mdl, string, "seeker"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Seeker, m_model, Model, MDL_SEEKER_ITEM); #endif /* crosshair */ ATTRIB(Seeker, w_crosshair, string, "gfx/crosshairseeker"); @@ -94,19 +95,24 @@ spawnfunc(weapon_seeker) { weapon_defaultspawnfunc(this, WEP_SEEKER); } // ============================ // Begin: Missile functions, these are general functions to be manipulated by other code // ============================ -void W_Seeker_Missile_Explode(entity this) +void W_Seeker_Missile_Explode(entity this, entity directhitentity) { this.event_damage = func_null; - RadiusDamage(this, this.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), NULL, NULL, WEP_CVAR(seeker, missile_force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR(seeker, missile_damage), WEP_CVAR(seeker, missile_edgedamage), WEP_CVAR(seeker, missile_radius), NULL, NULL, WEP_CVAR(seeker, missile_force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); } -void W_Seeker_Missile_Touch(entity this) +void W_Seeker_Missile_Explode_think(entity this) { - PROJECTILE_TOUCH(this); + W_Seeker_Missile_Explode(this, NULL); +} + +void W_Seeker_Missile_Touch(entity this, entity toucher) +{ + PROJECTILE_TOUCH(this, toucher); - W_Seeker_Missile_Explode(this); + W_Seeker_Missile_Explode(this, toucher); } void W_Seeker_Missile_Think(entity this) @@ -120,7 +126,7 @@ void W_Seeker_Missile_Think(entity this) if(time > this.cnt) { this.projectiledeathtype |= HITTYPE_SPLASH; - W_Seeker_Missile_Explode(this); + W_Seeker_Missile_Explode(this, NULL); } spd = vlen(this.velocity); @@ -146,7 +152,7 @@ void W_Seeker_Missile_Think(entity this) // Do evasive maneuvers for world objects? ( this should be a cpu hog. :P ) if(WEP_CVAR(seeker, missile_smart) && (dist > WEP_CVAR(seeker, missile_smart_mindist))) { - // Is it a better idea (shorter distance) to trace to the target itthis? + // Is it a better idea (shorter distance) to trace to the target itself? if( vdist(this.origin + olddir * this.wait, <, dist)) traceline(this.origin, this.origin + olddir * this.wait, false, this); else @@ -178,7 +184,7 @@ void W_Seeker_Missile_Think(entity this) { if(this.autoswitch <= time) { - W_Seeker_Missile_Explode(this); + W_Seeker_Missile_Explode(this, NULL); this.autoswitch = 0; } } @@ -220,7 +226,7 @@ void W_Seeker_Missile_Damage(entity this, entity inflictor, entity attacker, flo this.health = this.health - damage; if(this.health <= 0) - W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode); + W_PrepareExplosionByDamage(this, attacker, W_Seeker_Missile_Explode_think); } /* @@ -239,29 +245,27 @@ void W_Seeker_Missile_Animate(entity this) this.nextthink = time;// + cvar("g_balance_seeker_missile_activate_delay"); // cant dealy with csqc projectiles if(autocvar_g_balance_seeker_missile_proxy) - this.movetype = MOVETYPE_BOUNCEMISSILE; + this.move_movetype = MOVETYPE_BOUNCEMISSILE; else - this.movetype = MOVETYPE_FLYMISSILE; + this.move_movetype = MOVETYPE_FLYMISSILE; } UpdateCSQCProjectile(this); } */ -void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m_target) +void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, .entity weaponentity, vector f_diff, entity m_target) { - entity missile; - W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, missile_ammo)); makevectors(actor.v_angle); - W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0); + W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_SEEKER_FIRE, CH_WEAPON_A, 0); w_shotorg += f_diff; Send_Effect(EFFECT_SEEKER_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); //actor.detornator = false; - missile = new(seeker_missile); + entity missile = new(seeker_missile); missile.owner = missile.realowner = actor; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR(seeker, missile_damage); @@ -278,6 +282,7 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m missile.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) @@ -288,8 +293,10 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m setorigin(missile, w_shotorg); setsize(missile, '-4 -4 -4', '4 4 4'); - missile.movetype = MOVETYPE_FLYMISSILE; - missile.flags = FL_PROJECTILE; + set_movetype(missile, MOVETYPE_FLYMISSILE); + missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH | MIF_GUIDED_TAG; W_SetupProjVelocity_UP_PRE(missile, seeker, missile_); @@ -304,21 +311,26 @@ void W_Seeker_Fire_Missile(Weapon thiswep, entity actor, vector f_diff, entity m // ============================ // Begin: FLAC, close range attack meant for defeating rockets which are coming at you. // ============================ -void W_Seeker_Flac_Explode(entity this) +void W_Seeker_Flac_Explode(entity this, entity directhitentity) { this.event_damage = func_null; - RadiusDamage(this, this.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), NULL, NULL, WEP_CVAR(seeker, flac_force), this.projectiledeathtype, other); + RadiusDamage(this, this.realowner, WEP_CVAR(seeker, flac_damage), WEP_CVAR(seeker, flac_edgedamage), WEP_CVAR(seeker, flac_radius), NULL, NULL, WEP_CVAR(seeker, flac_force), this.projectiledeathtype, directhitentity); - remove(this); + delete(this); +} + +void W_Seeker_Flac_Touch(entity this, entity toucher) +{ + W_Seeker_Flac_Explode(this, toucher); } void W_Seeker_Flac_Explode_use(entity this, entity actor, entity trigger) { - W_Seeker_Flac_Explode(this); + W_Seeker_Flac_Explode(this, trigger); } -void W_Seeker_Fire_Flac(Weapon thiswep, entity actor) +void W_Seeker_Fire_Flac(Weapon thiswep, entity actor, .entity weaponentity) { entity missile; vector f_diff; @@ -343,7 +355,7 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor) f_diff = '+1.25 +3.75 0'; break; } - W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); + W_SetupShot_ProjectileSize(actor, weaponentity, '-2 -2 -2', '2 2 2', false, 2, SND_FLAC_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, flac_damage)); w_shotorg += f_diff; Send_Effect(EFFECT_HAGAR_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -352,15 +364,17 @@ void W_Seeker_Fire_Flac(Weapon thiswep, entity actor) missile.owner = missile.realowner = actor; missile.bot_dodge = true; missile.bot_dodgerating = WEP_CVAR(seeker, flac_damage); - settouch(missile, W_Seeker_Flac_Explode); + settouch(missile, W_Seeker_Flac_Touch); missile.use = W_Seeker_Flac_Explode_use; setthink(missile, adaptor_think2use_hittype_splash); missile.nextthink = time + WEP_CVAR(seeker, flac_lifetime) + WEP_CVAR(seeker, flac_lifetime_rand); missile.solid = SOLID_BBOX; - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); missile.projectiledeathtype = WEP_SEEKER.m_id; missile.projectiledeathtype = WEP_SEEKER.m_id | HITTYPE_SECONDARY; - missile.flags = FL_PROJECTILE; + missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); missile.missile_flags = MIF_SPLASH; // csqc projectiles @@ -389,7 +403,7 @@ entity W_Seeker_Tagged_Info(entity isowner, entity istarget) return NULL; } -void W_Seeker_Attack(entity actor) +void W_Seeker_Attack(entity actor, .entity weaponentity) { entity tracker, closest_target; @@ -409,7 +423,7 @@ void W_Seeker_Attack(entity actor) if((!closest_target) || ((trace_fraction < 1) && (trace_ent != closest_target))) closest_target = NULL; - W_Seeker_Fire_Missile(WEP_SEEKER, actor, '0 0 0', closest_target); + W_Seeker_Fire_Missile(WEP_SEEKER, actor, weaponentity, '0 0 0', closest_target); } void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seeker_Attack @@ -421,7 +435,7 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek Weapon thiswep = WEP_SEEKER; if((!(this.realowner.items & IT_UNLIMITED_AMMO) && this.realowner.(thiswep.ammo_field) < WEP_CVAR(seeker, missile_ammo)) || (this.cnt <= -1) || (IS_DEAD(this.realowner)) || (PS(this.realowner).m_switchweapon != WEP_SEEKER)) { - remove(this); + delete(this); return; } @@ -431,22 +445,23 @@ void W_Seeker_Vollycontroller_Think(entity this) // TODO: Merge this with W_Seek oldenemy = own.enemy; own.enemy = this.enemy; + .entity weaponentity = this.weaponentity_fld; c = own.cnt % 4; switch(c) { case 0: - W_Seeker_Fire_Missile(WEP_SEEKER, own, '-1.25 -3.75 0', own.enemy); + W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 -3.75 0', own.enemy); // TODO break; case 1: - W_Seeker_Fire_Missile(WEP_SEEKER, own, '+1.25 -3.75 0', own.enemy); + W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 -3.75 0', own.enemy); // TODO break; case 2: - W_Seeker_Fire_Missile(WEP_SEEKER, own, '-1.25 +3.75 0', own.enemy); + W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '-1.25 +3.75 0', own.enemy); // TODO break; case 3: default: - W_Seeker_Fire_Missile(WEP_SEEKER, own, '+1.25 +3.75 0', own.enemy); + W_Seeker_Fire_Missile(WEP_SEEKER, own, weaponentity, '+1.25 +3.75 0', own.enemy); // TODO break; } @@ -462,7 +477,7 @@ void W_Seeker_Tracker_Think(entity this) if(this) { WaypointSprite_Kill(this.tag_target.wps_tag_tracker); - remove(this); + delete(this); } return; } @@ -478,9 +493,9 @@ void W_Seeker_Tag_Explode(entity this) { //if(other==this.realowner) // return; - Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, other.species, this); + Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE, 0, this); - remove(this); + delete(this); } void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) @@ -492,13 +507,13 @@ void W_Seeker_Tag_Damage(entity this, entity inflictor, entity attacker, float d W_Seeker_Tag_Explode(this); } -void W_Seeker_Tag_Touch(entity this) +void W_Seeker_Tag_Touch(entity this, entity toucher) { vector dir; vector org2; entity e; - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); dir = normalize(this.realowner.origin - this.origin); org2 = findbetterlocation(this.origin, 8); @@ -506,37 +521,38 @@ void W_Seeker_Tag_Touch(entity this) te_knightspike(org2); this.event_damage = func_null; - Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, other.species, this); + Damage_DamageInfo(this.origin, 0, 0, 0, this.velocity, WEP_SEEKER.m_id | HITTYPE_BOUNCE | HITTYPE_SECONDARY, toucher.species, this); - if(other.takedamage == DAMAGE_AIM && !IS_DEAD(other)) + if(toucher.takedamage == DAMAGE_AIM && !IS_DEAD(toucher)) { // check to see if this person is already tagged by me - entity tag = W_Seeker_Tagged_Info(this.realowner, other); + entity tag = W_Seeker_Tagged_Info(this.realowner, toucher); if(tag != NULL) { - if(other.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first - WaypointSprite_Kill(other.wps_tag_tracker); + if(toucher.wps_tag_tracker && (WEP_CVAR(seeker, type) == 1)) // don't attach another waypointsprite without killing the old one first + WaypointSprite_Kill(toucher.wps_tag_tracker); tag.tag_time = time; } else { - //sprint(this.realowner, strcat("You just tagged ^2", other.netname, "^7 with a tracking device!\n")); + //sprint(this.realowner, strcat("You just tagged ^2", toucher.netname, "^7 with a tracking device!\n")); e = new(tag_tracker); + e.weaponentity_fld = this.weaponentity_fld; e.cnt = WEP_CVAR(seeker, missile_count); e.owner = this.owner; e.realowner = this.realowner; if(WEP_CVAR(seeker, type) == 1) { - e.tag_target = other; + e.tag_target = toucher; e.tag_time = time; setthink(e, W_Seeker_Tracker_Think); } else { - e.enemy = other; + e.enemy = toucher; setthink(e, W_Seeker_Vollycontroller_Think); } @@ -545,30 +561,30 @@ void W_Seeker_Tag_Touch(entity this) if(WEP_CVAR(seeker, type) == 1) { - WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(seeker, tag_tracker_lifetime), 0, other, '0 0 64', this.realowner, 0, other, wps_tag_tracker, true, RADARICON_TAGGED); - WaypointSprite_UpdateRule(other.wps_tag_tracker, 0, SPRITERULE_DEFAULT); + WaypointSprite_Spawn(WP_Seeker, WEP_CVAR(seeker, tag_tracker_lifetime), 0, toucher, '0 0 64', this.realowner, 0, toucher, wps_tag_tracker, true, RADARICON_TAGGED); + WaypointSprite_UpdateRule(toucher.wps_tag_tracker, 0, SPRITERULE_DEFAULT); } } - remove(this); + delete(this); return; } -void W_Seeker_Fire_Tag(Weapon thiswep, entity actor) +void W_Seeker_Fire_Tag(Weapon thiswep, entity actor, .entity weaponentity) { - entity missile; W_DecreaseAmmo(thiswep, actor, WEP_CVAR(seeker, tag_ammo)); - W_SetupShot_ProjectileSize(actor, '-2 -2 -2', '2 2 2', false, 2, SND_TAG_FIRE, CH_WEAPON_A, WEP_CVAR(seeker, missile_damage) * WEP_CVAR(seeker, missile_count)); + 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)); - missile = new(seeker_tag); + entity missile = new(seeker_tag); + missile.weaponentity_fld = weaponentity; missile.owner = missile.realowner = actor; missile.bot_dodge = true; missile.bot_dodgerating = 50; settouch(missile, W_Seeker_Tag_Touch); setthink(missile, SUB_Remove); missile.nextthink = time + WEP_CVAR(seeker, tag_lifetime); - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); missile.solid = SOLID_BBOX; missile.takedamage = DAMAGE_YES; @@ -579,10 +595,12 @@ void W_Seeker_Fire_Tag(Weapon thiswep, entity actor) setorigin(missile, w_shotorg); setsize(missile, '-2 -2 -2', '2 2 2'); - missile.flags = FL_PROJECTILE; + missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); //missile.missile_flags = MIF_..?; - missile.movetype = MOVETYPE_FLY; + set_movetype(missile, MOVETYPE_FLY); W_SetupProjVelocity_PRE(missile, seeker, tag_); missile.angles = vectoangles(missile.velocity); @@ -615,7 +633,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); + W_Seeker_Attack(actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, missile_animtime), w_ready); } } @@ -623,7 +641,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire))) { - W_Seeker_Fire_Tag(thiswep, actor); + W_Seeker_Fire_Tag(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); } } @@ -635,7 +653,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, tag_refire))) { - W_Seeker_Fire_Tag(thiswep, actor); + W_Seeker_Fire_Tag(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, tag_animtime), w_ready); } } @@ -643,7 +661,7 @@ METHOD(Seeker, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR(seeker, flac_refire))) { - W_Seeker_Fire_Flac(thiswep, actor); + W_Seeker_Fire_Flac(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE2, WEP_CVAR(seeker, flac_animtime), w_ready); } } @@ -681,7 +699,7 @@ METHOD(Seeker, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Seeker, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR(seeker, missile_ammo), WEP_CVAR(seeker, tag_ammo)), SND_RELOAD); } METHOD(Seeker, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/seeker.qh b/qcsrc/common/weapons/weapon/seeker.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/seeker.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/shockwave.qc b/qcsrc/common/weapons/weapon/shockwave.qc index 786671c31f..28cbe69bbe 100644 --- a/qcsrc/common/weapons/weapon/shockwave.qc +++ b/qcsrc/common/weapons/weapon/shockwave.qc @@ -1,17 +1,18 @@ +#include "shockwave.qh" #ifndef IMPLEMENTATION CLASS(Shockwave, Weapon) -/* ammotype */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none) -/* impulse */ ATTRIB(Shockwave, impulse, int, 2) -/* flags */ ATTRIB(Shockwave, spawnflags, int, WEP_FLAG_NORMAL | WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_FLAG_MUTATORBLOCKED); +/* ammotype */ //ATTRIB(Shockwave, ammo_field, .int, ammo_none); +/* impulse */ ATTRIB(Shockwave, impulse, int, 2); +/* flags */ ATTRIB(Shockwave, spawnflags, int, WEP_TYPE_HITSCAN | WEP_FLAG_CANCLIMB | WEP_TYPE_MELEE_SEC); /* rating */ ATTRIB(Shockwave, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW); /* color */ ATTRIB(Shockwave, wpcolor, vector, '0.5 0.25 0'); /* modelname */ ATTRIB(Shockwave, mdl, string, "shotgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Shockwave, m_model, Model, MDL_SHOCKWAVE_ITEM); #endif /* crosshair */ ATTRIB(Shockwave, w_crosshair, string, "gfx/crosshairshotgun"); /* crosshair */ ATTRIB(Shockwave, w_crosshair_size, float, 0.7); -/* wepimg */ ATTRIB(Shockwave, model2, string, "weaponshotgun"); +/* wepimg */ ATTRIB(Shockwave, model2, string, "weaponshockwave"); /* refname */ ATTRIB(Shockwave, netname, string, "shockwave"); /* wepname */ ATTRIB(Shockwave, m_name, string, _("Shockwave")); @@ -122,7 +123,7 @@ void W_Shockwave_Melee_Think(entity this) // check to see if we can still continue, otherwise give up now if(IS_DEAD(this.realowner) && WEP_CVAR(shockwave, melee_no_doubleslap)) { - remove(this); + delete(this); return; } @@ -214,7 +215,7 @@ void W_Shockwave_Melee_Think(entity this) } else { - remove(this); + delete(this); return; } } @@ -223,7 +224,7 @@ void W_Shockwave_Melee_Think(entity this) if(time >= this.cnt + meleetime) { // melee is finished - remove(this); + delete(this); return; } else @@ -243,7 +244,7 @@ void W_Shockwave_Melee(Weapon thiswep, entity actor, .entity weaponentity, int f meleetemp.owner = meleetemp.realowner = actor; setthink(meleetemp, W_Shockwave_Melee_Think); meleetemp.nextthink = time + WEP_CVAR(shockwave, melee_delay) * W_WeaponRateFactor(actor); - W_SetupShot_Range(actor, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range)); + W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR(shockwave, melee_damage), WEP_CVAR(shockwave, melee_range)); } // SHOCKWAVE ATTACK MODE @@ -355,7 +356,7 @@ void W_Shockwave_Send(entity actor) WriteByte(MSG_BROADCAST, etof(actor)); } -void W_Shockwave_Attack(entity actor) +void W_Shockwave_Attack(entity actor, .entity weaponentity) { // declarations float multiplier, multiplier_from_accuracy, multiplier_from_distance; @@ -366,7 +367,7 @@ void W_Shockwave_Attack(entity actor) float i, queue = 0; // set up the shot direction - W_SetupShot(actor, false, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); + W_SetupShot(actor, weaponentity, true, 3, SND_LASERGUN_FIRE, CH_WEAPON_B, WEP_CVAR(shockwave, blast_damage)); 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; @@ -397,6 +398,22 @@ void W_Shockwave_Attack(entity actor) false ); + float lag = ANTILAG_LATENCY(actor); + if(lag < 0.001) + lag = 0; + if (!IS_REAL_CLIENT(actor)) + lag = 0; + if(autocvar_g_antilag == 0 || actor.cvar_cl_noantilag) + lag = 0; // only do hitscan, but no antilag + if(lag) + { + FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_takeback(it, CS(it), time - lag)); + IL_EACH(g_monsters, it != actor, + { + antilag_takeback(it, it, time - lag); + }); + } + while(head) { if(head.takedamage) @@ -570,7 +587,7 @@ void W_Shockwave_Attack(entity actor) vector nearest_on_line = (w_shotorg + a * w_shotdir); vector nearest_to_attacker = WarpZoneLib_NearestPointOnBox(center + head.mins, center + head.maxs, nearest_on_line); - if((vlen(head.WarpZone_findradius_dist) <= WEP_CVAR(shockwave, blast_distance)) + if((vdist(head.WarpZone_findradius_dist, <=, WEP_CVAR(shockwave, blast_distance))) && (W_Shockwave_Attack_IsVisible(actor, head, nearest_on_line, w_shotorg, attack_endpos))) { // calculate importance of distance and accuracy for this attack @@ -651,11 +668,8 @@ void W_Shockwave_Attack(entity actor) final_force ); - if(accuracy_isgooddamage(actor.realowner, head)) - { - LOG_INFO("wtf\n"); - accuracy_add(actor.realowner, WEP_SHOCKWAVE.m_id, 0, final_damage); - } + if(accuracy_isgooddamage(actor, head)) + accuracy_add(actor, WEP_SHOCKWAVE.m_id, 0, final_damage); #ifdef DEBUG_SHOCKWAVE LOG_INFO(sprintf( @@ -670,6 +684,15 @@ void W_Shockwave_Attack(entity actor) shockwave_hit_force[i-1] = '0 0 0'; shockwave_hit_damage[i-1] = 0; } + + if(lag) + { + FOREACH_CLIENT(IS_PLAYER(it) && it != actor, antilag_restore(it, CS(it))); + IL_EACH(g_monsters, it != actor, + { + antilag_restore(it, it); + }); + } } METHOD(Shockwave, wr_aim, void(entity thiswep, entity actor)) @@ -687,7 +710,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); + W_Shockwave_Attack(actor, weaponentity); actor.shockwave_blasttime = time + WEP_CVAR(shockwave, blast_refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR(shockwave, blast_animtime), w_ready); } @@ -696,7 +719,6 @@ METHOD(Shockwave, wr_think, void(entity thiswep, entity actor, .entity weaponent else if(fire & 2) { //if(actor.clip_load >= 0) // we are not currently reloading - if(!actor.crouch) // no crouchmelee please if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR(shockwave, melee_refire))) { // attempt forcing playback of the anim by switching to another anim (that we never play) here... @@ -735,7 +757,7 @@ void Draw_Shockwave(entity this) { // fading/removal control float a = bound(0, (SW_MAXALPHA - ((time - this.sw_time) / SW_FADETIME)), SW_MAXALPHA); - if(a < ALPHA_MIN_VISIBLE) { remove(this); } + if(a < ALPHA_MIN_VISIBLE) { delete(this); } // WEAPONTODO: save this only once when creating the entity vector sw_color = entcs_GetColor(this.sv_entnum - 1); // GetTeamRGB(entcs_GetTeam(this.sv_entnum)); @@ -840,6 +862,7 @@ void Net_ReadShockwaveParticle() entity shockwave; shockwave = spawn(); shockwave.draw = Draw_Shockwave; + IL_PUSH(g_drawables, shockwave); shockwave.sw_shotorg_x = ReadCoord(); shockwave.sw_shotorg_y = ReadCoord(); shockwave.sw_shotorg_z = ReadCoord(); shockwave.sw_shotdir_x = ReadCoord(); shockwave.sw_shotdir_y = ReadCoord(); shockwave.sw_shotdir_z = ReadCoord(); diff --git a/qcsrc/common/weapons/weapon/shockwave.qh b/qcsrc/common/weapons/weapon/shockwave.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/shockwave.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/shotgun.qc b/qcsrc/common/weapons/weapon/shotgun.qc index 34d2e462c7..d45cf31cb3 100644 --- a/qcsrc/common/weapons/weapon/shotgun.qc +++ b/qcsrc/common/weapons/weapon/shotgun.qc @@ -1,12 +1,13 @@ +#include "shotgun.qh" #ifndef IMPLEMENTATION CLASS(Shotgun, Weapon) -/* ammotype */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells) -/* impulse */ ATTRIB(Shotgun, impulse, int, 2) -/* flags */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN); +/* ammotype */ ATTRIB(Shotgun, ammo_field, .int, ammo_shells); +/* impulse */ ATTRIB(Shotgun, impulse, int, 2); +/* flags */ ATTRIB(Shotgun, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN | WEP_TYPE_MELEE_SEC); /* rating */ ATTRIB(Shotgun, bot_pickupbasevalue, float, BOT_PICKUP_RATING_LOW); /* color */ ATTRIB(Shotgun, wpcolor, vector, '0.5 0.25 0'); /* modelname */ ATTRIB(Shotgun, mdl, string, "shotgun"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Shotgun, m_model, Model, MDL_SHOTGUN_ITEM); #endif /* crosshair */ ATTRIB(Shotgun, w_crosshair, string, "gfx/crosshairshotgun"); @@ -58,31 +59,31 @@ REGISTER_WEAPON(SHOTGUN, shotgun, NEW(Shotgun)); #ifdef SVQC spawnfunc(weapon_shotgun) { weapon_defaultspawnfunc(this, WEP_SHOTGUN); } -void W_Shotgun_Attack(Weapon thiswep, entity actor, float isprimary) +void W_Shotgun_Attack(Weapon thiswep, entity actor, .entity weaponentity, float isprimary) { - float sc; - entity flash; - W_DecreaseAmmo(thiswep, actor, WEP_CVAR_PRI(shotgun, ammo)); - W_SetupShot(actor, true, 5, SND_SHOTGUN_FIRE, ((isprimary) ? CH_WEAPON_A : CH_WEAPON_SINGLE), WEP_CVAR_PRI(shotgun, damage) * WEP_CVAR_PRI(shotgun, bullets)); - for(sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1) + 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)); + for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, bullets);sc = sc + 1) fireBullet(actor, 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); Send_Effect(EFFECT_SHOTGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, WEP_CVAR_PRI(shotgun, ammo)); // casing code if(autocvar_g_casings >= 1) - for(sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1) - SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor); + { + makevectors(actor.v_angle); // for some reason, this is lost + //for(int sc = 0;sc < WEP_CVAR_PRI(shotgun, ammo);sc = sc + 1) + SpawnCasing(((random() * 50 + 50) * v_right) - (v_forward * (random() * 25 + 25)) - ((random() * 5 - 30) * v_up), 2, vectoangles(v_forward),'0 250 0', 100, 1, actor, weaponentity); + } // muzzle flash for 1st person view - flash = spawn(); + entity flash = spawn(); setmodel(flash, MDL_SHOTGUN_MUZZLEFLASH); // precision set below setthink(flash, SUB_Remove); flash.nextthink = time + 0.06; flash.effects = EF_ADDITIVE | EF_FULLBRIGHT | EF_LOWPRECISION; - W_AttachToShotorg(actor, flash, '5 0 0'); + W_AttachToShotorg(actor, weaponentity, flash, '5 0 0'); } .float swing_prev; @@ -110,7 +111,7 @@ void W_Shotgun_Melee_Think(entity this) // check to see if we can still continue, otherwise give up now if(IS_DEAD(this.realowner) && WEP_CVAR_SEC(shotgun, melee_no_doubleslap)) { - remove(this); + delete(this); return; } @@ -163,7 +164,7 @@ void W_Shotgun_Melee_Think(entity this) } else { - remove(this); + delete(this); return; } } @@ -172,7 +173,7 @@ void W_Shotgun_Melee_Think(entity this) if(time >= this.cnt + meleetime) { // melee is finished - remove(this); + delete(this); return; } else @@ -192,7 +193,7 @@ void W_Shotgun_Attack2(Weapon thiswep, entity actor, .entity weaponentity, int f meleetemp.realowner = actor; setthink(meleetemp, W_Shotgun_Melee_Think); meleetemp.nextthink = time + WEP_CVAR_SEC(shotgun, melee_delay) * W_WeaponRateFactor(actor); - W_SetupShot_Range(actor, true, 0, SND_Null, 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range)); + W_SetupShot_Range(actor, weaponentity, true, 0, SND_Null, 0, WEP_CVAR_SEC(shotgun, damage), WEP_CVAR_SEC(shotgun, melee_range)); } // alternate secondary weapon frames @@ -207,7 +208,7 @@ 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, true); // actually is secondary, but we trick the last shot into playing full reload sound + W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, true); // 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) @@ -220,11 +221,11 @@ void W_Shotgun_Attack3_Frame1(Weapon thiswep, entity actor, .entity weaponentity return; } - W_Shotgun_Attack(WEP_SHOTGUN, actor, false); + W_Shotgun_Attack(WEP_SHOTGUN, actor, weaponentity, false); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(shotgun, alt_animtime), W_Shotgun_Attack3_Frame2); } -.float shotgun_primarytime; +.float shotgun_primarytime[MAX_WEAPONSLOTS]; METHOD(Shotgun, wr_aim, void(entity thiswep, entity actor)) { @@ -246,31 +247,32 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit { if(fire & 1) { - if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary + int slot = weaponslot(weaponentity); + if(time >= actor.shotgun_primarytime[slot]) // handle refire separately so the secondary can be fired straight after a primary { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(shotgun, animtime))) { - W_Shotgun_Attack(thiswep, actor, true); - actor.shotgun_primarytime = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor); + W_Shotgun_Attack(thiswep, actor, weaponentity, true); + actor.shotgun_primarytime[slot] = time + WEP_CVAR_PRI(shotgun, refire) * W_WeaponRateFactor(actor); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(shotgun, animtime), w_ready); } } } else if((fire & 2) && WEP_CVAR(shotgun, secondary) == 2) { - if(time >= actor.shotgun_primarytime) // handle refire separately so the secondary can be fired straight after a primary + int slot = weaponslot(weaponentity); + if(time >= actor.shotgun_primarytime[slot]) // handle refire separately so the secondary can be fired straight after a primary { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(shotgun, alt_animtime))) { - W_Shotgun_Attack(thiswep, actor, false); - actor.shotgun_primarytime = time + WEP_CVAR_SEC(shotgun, alt_refire) * W_WeaponRateFactor(actor); + W_Shotgun_Attack(thiswep, actor, weaponentity, false); + actor.shotgun_primarytime[slot] = 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); } } } } if(actor.clip_load >= 0) // we are not currently reloading - if(!actor.crouch) // no crouchmelee please if(WEP_CVAR(shotgun, secondary) == 1) if(((fire & 1) && actor.(thiswep.ammo_field) <= 0 && !(actor.items & IT_UNLIMITED_WEAPON_AMMO)) || (fire & 2)) if(weapon_prepareattack(thiswep, actor, weaponentity, true, WEP_CVAR_SEC(shotgun, refire))) @@ -281,7 +283,7 @@ METHOD(Shotgun, wr_think, void(entity thiswep, entity actor, .entity weaponentit } METHOD(Shotgun, wr_setup, void(entity thiswep, entity actor)) { - actor.ammo_field = ammo_none; + actor.ammo_field = ammo_shells; } METHOD(Shotgun, wr_checkammo1, bool(entity thiswep, entity actor)) { @@ -308,7 +310,7 @@ METHOD(Shotgun, wr_checkammo2, bool(entity thiswep, entity actor)) } METHOD(Shotgun, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, WEP_CVAR_PRI(shotgun, ammo), SND_RELOAD); // WEAPONTODO + W_Reload(actor, weaponentity, WEP_CVAR_PRI(shotgun, ammo), SND_RELOAD); // WEAPONTODO } METHOD(Shotgun, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/shotgun.qh b/qcsrc/common/weapons/weapon/shotgun.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/shotgun.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/tuba.qc b/qcsrc/common/weapons/weapon/tuba.qc index c0e0ac89ba..e3e1f5228d 100644 --- a/qcsrc/common/weapons/weapon/tuba.qc +++ b/qcsrc/common/weapons/weapon/tuba.qc @@ -1,11 +1,12 @@ +#include "tuba.qh" #ifndef IMPLEMENTATION CLASS(Tuba, Weapon) -/* impulse */ ATTRIB(Tuba, impulse, int, 1) +/* impulse */ ATTRIB(Tuba, impulse, int, 1); /* flags */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH); /* rating */ ATTRIB(Tuba, bot_pickupbasevalue, float, BOT_PICKUP_RATING_MID); /* color */ ATTRIB(Tuba, wpcolor, vector, '0 1 0'); /* modelname */ ATTRIB(Tuba, mdl, string, "tuba"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Tuba, m_model, Model, MDL_TUBA_ITEM); #endif /* crosshair */ ATTRIB(Tuba, w_crosshair, string, "gfx/crosshairtuba"); @@ -179,7 +180,7 @@ void W_Tuba_NoteOff(entity this) } } } - remove(this); + delete(this); } int W_Tuba_GetNote(entity pl, int hittype) @@ -309,12 +310,12 @@ void W_Tuba_NoteThink(entity this) }); } -void W_Tuba_NoteOn(entity actor, float hittype) +void W_Tuba_NoteOn(entity actor, .entity weaponentity, float hittype) { vector o; float n; - W_SetupShot(actor, false, 2, SND_Null, 0, WEP_CVAR(tuba, damage)); + W_SetupShot(actor, weaponentity, false, 2, SND_Null, 0, WEP_CVAR(tuba, damage)); n = W_Tuba_GetNote(actor, hittype); @@ -377,13 +378,13 @@ METHOD(Tuba, wr_think, void(Tuba this, entity actor, .entity weaponentity, int f if (fire & 1) if (weapon_prepareattack(this, actor, weaponentity, false, WEP_CVAR(tuba, refire))) { - W_Tuba_NoteOn(actor, 0); + W_Tuba_NoteOn(actor, weaponentity, 0); weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); } if (fire & 2) if (weapon_prepareattack(this, actor, weaponentity, true, WEP_CVAR(tuba, refire))) { - W_Tuba_NoteOn(actor, HITTYPE_SECONDARY); + W_Tuba_NoteOn(actor, weaponentity, HITTYPE_SECONDARY); weapon_thinkf(actor, weaponentity, WFRAME_IDLE, WEP_CVAR(tuba, animtime), w_ready); } if (actor.tuba_note) @@ -395,10 +396,12 @@ METHOD(Tuba, wr_think, void(Tuba this, entity actor, .entity weaponentity, int f } } +void tuba_instrument_send(entity this, int instr); METHOD(Tuba, wr_setup, void(Tuba this, entity actor)) { actor.ammo_field = ammo_none; actor.tuba_instrument = 0; + tuba_instrument_send(actor, actor.tuba_instrument); } #endif @@ -411,6 +414,7 @@ NET_HANDLE(tuba_instrument, bool) string s = (i == 0) ? "tuba" : (i == 1) ? "akordeon" : "kleinbottle" ; + viewmodel.tuba_instrument = i; CL_WeaponEntity_SetModel(viewmodel, s, true); } #endif @@ -418,6 +422,8 @@ NET_HANDLE(tuba_instrument, bool) void tuba_instrument_send(entity this, int instr) { msg_entity = this; + if (!IS_REAL_CLIENT(this)) + return; int chan = MSG_ONE; WriteHeader(chan, tuba_instrument); WriteByte(chan, instr); @@ -448,7 +454,7 @@ METHOD(Tuba, wr_reload, void(Tuba this, entity actor, .entity weaponentity)) break; } tuba_instrument_send(actor, actor.tuba_instrument); - W_SetupShot(actor, false, 0, SND_Null, 0, 0); + W_SetupShot(actor, weaponentity, false, 0, SND_Null, 0, 0); Send_Effect(EFFECT_TELEPORT, w_shotorg, '0 0 0', 1); actor.(weaponentity).state = WS_INUSE; weapon_thinkf(actor, weaponentity, WFRAME_RELOAD, 0.5, w_ready); @@ -481,6 +487,15 @@ METHOD(Tuba, wr_killmessage, Notification(Tuba this)) return WEAPON_TUBA_MURDER; } +#elif defined(CSQC) + +METHOD(Tuba, wr_viewmodel, string(Tuba this, entity wep)) +{ + return (wep.tuba_instrument == 0) ? "tuba" : + (wep.tuba_instrument == 1) ? "akordeon" : + "kleinbottle"; +} + #endif #ifdef CSQC @@ -491,13 +506,6 @@ const int TUBA_MIN = -18; const int TUBA_MAX = 27; const int TUBA_INSTRUMENTS = 3; -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; - int Tuba_PitchStep; void tubasound(entity e, bool restart) @@ -563,9 +571,9 @@ void Ent_TubaNote_Think(entity this) sound(this, CH_TUBA_SINGLE, SND_Null, 0, 0); if (this.enemy) { sound(this.enemy, CH_TUBA_SINGLE, SND_Null, 0, 0); - remove(this.enemy); + delete(this.enemy); } - remove(this); + delete(this); } else { tubasound(this, 0); } @@ -643,7 +651,7 @@ PRECACHE(Tuba) Tuba_PitchStep = autocvar_g_balance_tuba_pitchstep; if (Tuba_PitchStep) { if (!checkextension("DP_SND_SOUND7_WIP2") && !checkextension("DP_SND_SOUND7")) { - LOG_WARNING("requested pitch shifting, but not supported by this engine build"); + LOG_WARN("requested pitch shifting, but not supported by this engine build"); Tuba_PitchStep = 0; } } diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh new file mode 100644 index 0000000000..ce6b6ede73 --- /dev/null +++ b/qcsrc/common/weapons/weapon/tuba.qh @@ -0,0 +1,10 @@ +#pragma once + +#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; +#endif diff --git a/qcsrc/common/weapons/weapon/vaporizer.qc b/qcsrc/common/weapons/weapon/vaporizer.qc index 8b061b72a5..91042b613b 100644 --- a/qcsrc/common/weapons/weapon/vaporizer.qc +++ b/qcsrc/common/weapons/weapon/vaporizer.qc @@ -1,12 +1,13 @@ +#include "vaporizer.qh" #ifndef IMPLEMENTATION CLASS(Vaporizer, Weapon) -/* ammotype */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(Vaporizer, impulse, int, 7) +/* ammotype */ ATTRIB(Vaporizer, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(Vaporizer, impulse, int, 7); /* flags */ ATTRIB(Vaporizer, spawnflags, int, WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_FLAG_SUPERWEAPON | WEP_TYPE_HITSCAN); /* rating */ ATTRIB(Vaporizer, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Vaporizer, wpcolor, vector, '0.5 1 1'); /* modelname */ ATTRIB(Vaporizer, mdl, string, "minstanex"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Vaporizer, m_model, Model, MDL_VAPORIZER_ITEM); #endif /* crosshair */ ATTRIB(Vaporizer, w_crosshair, string, "gfx/crosshairminstanex"); @@ -111,7 +112,7 @@ void VaporizerBeam_Draw(entity this) //entity e = CSQCModel_server2csqc(this.sv_entnum - 1); //if (e == NULL) //{ - rgb = colormapPaletteColor(stof(getplayerkeyvalue(this.sv_entnum - 1, "colors")) & 0x0F, true); + rgb = colormapPaletteColor(entcs_GetClientColors(this.sv_entnum - 1) & 0x0F, true); //rgb = '1 1 1'; //} //else @@ -140,6 +141,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew) setthink(this, SUB_Remove); this.nextthink = time + bound(0, autocvar_cl_vaporizerbeam_lifetime, 10); this.draw = VaporizerBeam_Draw; + if (isNew) IL_PUSH(g_drawables, this); this.drawmask = MASK_NORMAL; this.vorg1_x = ReadCoord(); this.vorg1_y = ReadCoord(); this.vorg1_z = ReadCoord(); @@ -157,7 +159,7 @@ NET_HANDLE(TE_CSQC_VAPORBEAMPARTICLE, bool isNew) WarpZone_TrailParticles(NULL, particleeffectnum(((this.cnt) ? EFFECT_VAPORIZER_HIT(this.team) : EFFECT_VAPORIZER(this.team))), this.vorg1, this.vorg2); this.draw = func_null; this.drawmask = MASK_NORMAL; - remove(this); + delete(this); } return true; @@ -176,15 +178,15 @@ void W_RocketMinsta_Explosion(entity actor, vector loc) dmgent.owner = dmgent.realowner = actor; setorigin(dmgent, loc); RadiusDamage (dmgent, actor, autocvar_g_rm_damage, autocvar_g_rm_edgedamage, autocvar_g_rm_radius, NULL, NULL, autocvar_g_rm_force, WEP_DEVASTATOR.m_id | HITTYPE_SPLASH, other); - remove(dmgent); + delete(dmgent); } -void W_Vaporizer_Attack(Weapon thiswep, entity actor) +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, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage); + W_SetupShot(actor, weaponentity, true, 0, SND_Null, CH_WEAPON_A, vaporizer_damage); // 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); @@ -213,35 +215,35 @@ void W_Vaporizer_Attack(Weapon thiswep, entity actor) W_DecreaseAmmo(thiswep, actor, ((g_instagib) ? 1 : WEP_CVAR_PRI(vaporizer, ammo))); } -void W_RocketMinsta_Laser_Explode (entity this) +void W_RocketMinsta_Laser_Explode (entity this, entity directhitentity) { - if(other.takedamage == DAMAGE_AIM) - if(IS_PLAYER(other)) - if(DIFF_TEAM(this.realowner, other)) - if(!IS_DEAD(other)) - if(IsFlying(other)) + if(directhitentity.takedamage == DAMAGE_AIM) + if(IS_PLAYER(directhitentity)) + if(DIFF_TEAM(this.realowner, directhitentity)) + if(!IS_DEAD(directhitentity)) + if(IsFlying(directhitentity)) Send_Notification(NOTIF_ONE, this.realowner, MSG_ANNCE, ANNCE_ACHIEVEMENT_ELECTROBITCH); this.event_damage = func_null; this.takedamage = DAMAGE_NO; - RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, other); - remove(this); + RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, directhitentity); + delete(this); } void W_RocketMinsta_Laser_Explode_use(entity this, entity actor, entity trigger) { - W_RocketMinsta_Laser_Explode(this); + W_RocketMinsta_Laser_Explode(this, trigger); // we probably don't want trigger used here, but this matches closest to old behaviour } -void W_RocketMinsta_Laser_Touch (entity this) +void W_RocketMinsta_Laser_Touch(entity this, entity toucher) { - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); //W_RocketMinsta_Laser_Explode (); - RadiusDamage (this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, other); - remove(this); + RadiusDamage(this, this.realowner, this.rm_damage, this.rm_edmg, autocvar_g_rm_laser_radius, NULL, NULL, this.rm_force, this.projectiledeathtype, toucher); + delete(this); } -void W_RocketMinsta_Attack2(entity actor) +void W_RocketMinsta_Attack2(entity actor, .entity weaponentity) { makevectors(actor.v_angle); @@ -253,7 +255,7 @@ void W_RocketMinsta_Attack2(entity actor) Weapon w = PS(actor).m_weapon; PS(actor).m_weapon = WEP_ELECTRO; - W_SetupShot_ProjectileSize (actor, '0 0 -3', '0 0 -3', false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, autocvar_g_rm_laser_damage); + W_SetupShot_ProjectileSize (actor, weaponentity, '0 0 -3', '0 0 -3', false, 2, SND_CRYLINK_FIRE, CH_WEAPON_A, autocvar_g_rm_laser_damage); PS(actor).m_weapon = w; Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -277,7 +279,7 @@ void W_RocketMinsta_Attack2(entity actor) //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed); - proj.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(proj, MOVETYPE_BOUNCEMISSILE); //W_SETUPPROJECTILEVELOCITY(proj, g_balance_minstanex_laser); proj.velocity = (w_shotdir + (((counter + 0.5) / total) * 2 - 1) * v_right * (spread * (rndspread ? random() : 1))) * cvar("g_rm_laser_speed"); proj.velocity_z = proj.velocity_z + cvar("g_rm_laser_zspread") * (random() - 0.5); @@ -286,6 +288,8 @@ void W_RocketMinsta_Attack2(entity actor) settouch(proj, W_RocketMinsta_Laser_Touch); setsize(proj, '0 0 -3', '0 0 -3'); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true); @@ -295,7 +299,7 @@ void W_RocketMinsta_Attack2(entity actor) } } -void W_RocketMinsta_Attack3 (entity actor) +void W_RocketMinsta_Attack3 (entity actor, .entity weaponentity) { makevectors(actor.v_angle); @@ -305,7 +309,7 @@ void W_RocketMinsta_Attack3 (entity actor) Weapon w = PS(actor).m_weapon; PS(actor).m_weapon = WEP_ELECTRO; - W_SetupShot_ProjectileSize (actor, '0 0 -3', '0 0 -3', false, 2, SND_ELECTRO_FIRE2, CH_WEAPON_A, autocvar_g_rm_laser_damage); + W_SetupShot_ProjectileSize (actor, weaponentity, '0 0 -3', '0 0 -3', false, 2, SND_ELECTRO_FIRE2, CH_WEAPON_A, autocvar_g_rm_laser_damage); PS(actor).m_weapon = w; Send_Effect(EFFECT_ELECTRO_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); @@ -329,13 +333,15 @@ void W_RocketMinsta_Attack3 (entity actor) //W_SetupProjectileVelocity(proj, autocvar_g_rm_laser_speed, spread * (rndspread ? random() : 1) * autocvar_g_rm_laser_speed); - proj.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(proj, MOVETYPE_BOUNCEMISSILE); proj.velocity = w_shotdir * autocvar_g_rm_laser_speed; proj.velocity = W_CalculateProjectileVelocity(actor, actor.velocity, proj.velocity, true); proj.angles = vectoangles(proj.velocity); settouch(proj, W_RocketMinsta_Laser_Touch); setsize(proj, '0 0 -3', '0 0 -3'); proj.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, proj); + IL_PUSH(g_bot_dodge, proj); proj.missile_flags = MIF_SPLASH; CSQCProjectile(proj, true, PROJECTILE_ROCKETMINSTA_LASER, true); @@ -365,7 +371,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vaporizer, refire))) { - W_Vaporizer_Attack(thiswep, actor); + W_Vaporizer_Attack(thiswep, actor, weaponentity); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vaporizer, animtime), w_ready); } } @@ -381,13 +387,13 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent actor.jump_interval = time + autocvar_g_rm_laser_refire; actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_delay; damage_goodhits = 0; - W_RocketMinsta_Attack2(actor); + W_RocketMinsta_Attack2(actor, weaponentity); } else if(rapid && actor.jump_interval2 <= time && actor.held_down) { actor.jump_interval2 = time + autocvar_g_rm_laser_rapid_refire; damage_goodhits = 0; - W_RocketMinsta_Attack3(actor); + W_RocketMinsta_Attack3(actor, weaponentity); //weapon_thinkf(actor, WFRAME_FIRE2, autocvar_g_rm_laser_rapid_animtime, w_ready); } } @@ -406,6 +412,7 @@ METHOD(Vaporizer, wr_think, void(entity thiswep, entity actor, .entity weaponent PS(actor).m_weapon = WEP_BLASTER; W_Blaster_Attack( actor, + weaponentity, WEP_BLASTER.m_id | HITTYPE_SECONDARY, WEP_CVAR_SEC(vaporizer, shotangle), WEP_CVAR_SEC(vaporizer, damage), @@ -459,7 +466,7 @@ METHOD(Vaporizer, wr_reload, void(entity thiswep, entity actor, .entity weaponen else used_ammo = vaporizer_ammo; - W_Reload(actor, used_ammo, SND_RELOAD); + W_Reload(actor, weaponentity, used_ammo, SND_RELOAD); } METHOD(Vaporizer, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/vaporizer.qh b/qcsrc/common/weapons/weapon/vaporizer.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/vaporizer.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/common/weapons/weapon/vortex.qc b/qcsrc/common/weapons/weapon/vortex.qc index 640cd2c402..bf3ce8185f 100644 --- a/qcsrc/common/weapons/weapon/vortex.qc +++ b/qcsrc/common/weapons/weapon/vortex.qc @@ -1,12 +1,13 @@ +#include "vortex.qh" #ifndef IMPLEMENTATION CLASS(Vortex, Weapon) -/* ammotype */ ATTRIB(Vortex, ammo_field, .int, ammo_cells) -/* impulse */ ATTRIB(Vortex, impulse, int, 7) +/* ammotype */ ATTRIB(Vortex, ammo_field, .int, ammo_cells); +/* impulse */ ATTRIB(Vortex, impulse, int, 7); /* flags */ ATTRIB(Vortex, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_TYPE_HITSCAN); /* rating */ ATTRIB(Vortex, bot_pickupbasevalue, float, BOT_PICKUP_RATING_HIGH); /* color */ ATTRIB(Vortex, wpcolor, vector, '0.5 1 1'); /* modelname */ ATTRIB(Vortex, mdl, string, "nex"); -#ifndef MENUQC +#ifdef GAMEQC /* model */ ATTRIB(Vortex, m_model, Model, MDL_VORTEX_ITEM); #endif /* crosshair */ ATTRIB(Vortex, w_crosshair, string, "gfx/crosshairnex"); @@ -68,7 +69,7 @@ REGISTER_WEAPON(VORTEX, vortex, NEW(Vortex)); REGISTER_STAT(WEP_CVAR_vortex_charge, bool, WEP_CVAR(vortex, charge)) REGISTER_STAT(WEP_CVAR_vortex_charge_animlimit, float, WEP_CVAR(vortex, charge_animlimit)) -#if defined(CSQC) +#if defined(GAMEQC) float autocvar_g_weapon_charge_colormod_red_full; float autocvar_g_weapon_charge_colormod_red_half; float autocvar_g_weapon_charge_colormod_green_full; @@ -77,11 +78,11 @@ float autocvar_g_weapon_charge_colormod_blue_half; float autocvar_g_weapon_charge_colormod_green_half; float autocvar_g_weapon_charge_colormod_hdrmultiplier; -METHOD(Vortex, wr_glow, vector(Vortex this)) +METHOD(Vortex, wr_glow, vector(Vortex this, entity actor)) { - if (!STAT(WEP_CVAR_vortex_charge)) return '0 0 0'; - float charge = STAT(VORTEX_CHARGE); - float animlimit = STAT(WEP_CVAR_vortex_charge_animlimit); + if (!STAT(WEP_CVAR_vortex_charge, actor)) return '0 0 0'; + float charge = STAT(VORTEX_CHARGE, actor); + float animlimit = STAT(WEP_CVAR_vortex_charge_animlimit, actor); 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); @@ -157,7 +158,7 @@ MUTATOR_HOOKFUNCTION(vortex_charge, GetPressedKeys) } } -void W_Vortex_Attack(Weapon thiswep, entity actor, float issecondary) +void W_Vortex_Attack(Weapon thiswep, entity actor, .entity weaponentity, float issecondary) { float mydmg, myforce, mymindist, mymaxdist, myhalflife, myforcehalflife, myammo, charge; @@ -184,7 +185,7 @@ void W_Vortex_Attack(Weapon thiswep, entity actor, float issecondary) mydmg *= charge; myforce *= charge; - W_SetupShot(actor, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg); + W_SetupShot(actor, weaponentity, true, 5, SND_NEXFIRE, CH_WEAPON_A, mydmg); 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); @@ -243,7 +244,7 @@ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_PRI(vortex, refire))) { - W_Vortex_Attack(thiswep, actor, 0); + W_Vortex_Attack(thiswep, actor, weaponentity, 0); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_PRI(vortex, animtime), w_ready); } } @@ -315,7 +316,7 @@ METHOD(Vortex, wr_think, void(entity thiswep, entity actor, .entity weaponentity { if(weapon_prepareattack(thiswep, actor, weaponentity, false, WEP_CVAR_SEC(vortex, refire))) { - W_Vortex_Attack(thiswep, actor, 1); + W_Vortex_Attack(thiswep, actor, weaponentity, 1); weapon_thinkf(actor, weaponentity, WFRAME_FIRE1, WEP_CVAR_SEC(vortex, animtime), w_ready); } } @@ -358,7 +359,7 @@ METHOD(Vortex, wr_resetplayer, void(entity thiswep, entity actor)) } METHOD(Vortex, wr_reload, void(entity thiswep, entity actor, .entity weaponentity)) { - W_Reload(actor, min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND_RELOAD); + W_Reload(actor, weaponentity, min(WEP_CVAR_PRI(vortex, ammo), WEP_CVAR_SEC(vortex, ammo)), SND_RELOAD); } METHOD(Vortex, wr_suicidemessage, Notification(entity thiswep)) { diff --git a/qcsrc/common/weapons/weapon/vortex.qh b/qcsrc/common/weapons/weapon/vortex.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/common/weapons/weapon/vortex.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/dpdefs/csprogsdefs.qh b/qcsrc/dpdefs/csprogsdefs.qh index f424d1a5de..29d797585e 100644 --- a/qcsrc/dpdefs/csprogsdefs.qh +++ b/qcsrc/dpdefs/csprogsdefs.qh @@ -1,5 +1,4 @@ -#ifndef CSPROGSDEFS_H -#define CSPROGSDEFS_H +#pragma once #pragma noref 1 @@ -44,5 +43,3 @@ #define use use1 .void(entity this, entity actor, entity trigger) use; #define touch move_touch - -#endif diff --git a/qcsrc/dpdefs/doc.md b/qcsrc/dpdefs/doc.md index d279ced478..b49bb6f57a 100644 --- a/qcsrc/dpdefs/doc.md +++ b/qcsrc/dpdefs/doc.md @@ -18,6 +18,7 @@ bool CSQC_InputEvent(int eventtype, int x, int y); void CSQC_UpdateView(int width, int height); +// catch commands registered with registercommand bool CSQC_ConsoleCommand(string cmd); bool CSQC_Parse_TempEntity(); bool CSQC_Parse_StuffCmd(string msg); @@ -54,6 +55,21 @@ float sound_starttime; # SVQC +Main loop: +* SV_Physics() + * StartFrame() + * if (force_retouch) + * foreach entity: + * .touch() + * foreach client: + * PlayerPreThink() + * .think() + * PlayerPostThink() + * foreach nonclient: + * .think() + * EndFrame() + + ``` .entity clientcamera; @@ -198,6 +214,10 @@ void SV_PlayerPhysics(); // self void SV_ParseClientCommand(string cmd); +// qcstatus server field +string worldstatus; +.string clientstatus; + ``` # MENUQC @@ -229,3 +249,85 @@ void URI_Get_Callback(int id, int status, string data); void GameCommand(string cmd); ``` + +# Misc + +## Trace + +### tracebox + + void tracebox(vector v1, vector min, vector max, vector v2, int tryents, entity ignoreentity); + +attempt to move an object from v1 to v2 of given size + +tryents: + * MOVE_NORMAL (0) + * MOVE_NOMONSTERS (1): ignore monsters + * MOVE_MISSILE (2): +15 to every extent + * MOVE_WORLDONLY (3): ignore everything except bsp + * MOVE_HITMODEL (4): hit model, not bbox + +### traceline + + void traceline(vector v1, vector v2, int tryents, entity ignoreentity); + +degenerate case of tracebox when min and max are equal + +### result globals + + bool trace_allsolid; + +trace never left solid + + bool trace_startsolid; + +trace started inside solid + + float trace_fraction; + +distance before collision: 0..1, 1 if no collision + + vector trace_endpos; + +v1 + (v2 - v1) * trace_fraction + + vector trace_plane_normal; + +normalized plane normal, '0 0 0' if no collision. +May be present if edges touch without clipping, use `trace_fraction < 1` as a determinant instead + + float trace_plane_dist; + + + + entity trace_ent; + +entity hit, if any + + bool trace_inopen; + + + + bool trace_inwater; + + + + int trace_dpstartcontents; + +DPCONTENTS_ value at start position of trace + + int trace_dphitcontents; + +DPCONTENTS_ value of impacted surface (not contents at impact point, just contents of the surface that was hit) + + int trace_dphitq3surfaceflags; + +Q3SURFACEFLAG_ value of impacted surface + + string trace_dphittexturename; + +texture name of impacted surface + + int trace_networkentity; + + diff --git a/qcsrc/dpdefs/dpextensions.qh b/qcsrc/dpdefs/dpextensions.qh index ff9214844b..2ed0f90638 100644 --- a/qcsrc/dpdefs/dpextensions.qh +++ b/qcsrc/dpdefs/dpextensions.qh @@ -1,5 +1,4 @@ -#ifndef DPEXTENSIONS_H -#define DPEXTENSIONS_H +#pragma once #pragma noref 1 @@ -64,5 +63,3 @@ int() _buf_create = #460; #define buf_create _buf_create #pragma noref 0 - -#endif diff --git a/qcsrc/dpdefs/keycodes.qh b/qcsrc/dpdefs/keycodes.qh index f4c49f143b..0267be8824 100644 --- a/qcsrc/dpdefs/keycodes.qh +++ b/qcsrc/dpdefs/keycodes.qh @@ -1,5 +1,4 @@ -#ifndef KEYCODES_H -#define KEYCODES_H +#pragma once #pragma noref 1 @@ -10,5 +9,3 @@ //#undef float #pragma noref 0 - -#endif diff --git a/qcsrc/dpdefs/menudefs.qh b/qcsrc/dpdefs/menudefs.qh index c0e6d3ba72..036d87ff2f 100644 --- a/qcsrc/dpdefs/menudefs.qh +++ b/qcsrc/dpdefs/menudefs.qh @@ -1,5 +1,4 @@ -#ifndef MENUDEFS_H -#define MENUDEFS_H +#pragma once #pragma noref 1 @@ -40,6 +39,6 @@ int(string s1, string s2, int len) _strncasecmp = #230; int() _buf_create = #440; #define buf_create _buf_create -#pragma noref 0 +bool(entity ent) wasfreed = #353; -#endif +#pragma noref 0 diff --git a/qcsrc/dpdefs/post.qh b/qcsrc/dpdefs/post.qh index db8752d2bb..16fd934503 100644 --- a/qcsrc/dpdefs/post.qh +++ b/qcsrc/dpdefs/post.qh @@ -6,7 +6,9 @@ #undef error #undef movetogoal #undef objerror +#undef remove #undef walkmove +#undef setcolor #ifdef MENUQC #define NULL (0, null_entity) diff --git a/qcsrc/dpdefs/pre.qh b/qcsrc/dpdefs/pre.qh index b24d0120a4..63cebbc1ac 100644 --- a/qcsrc/dpdefs/pre.qh +++ b/qcsrc/dpdefs/pre.qh @@ -6,4 +6,6 @@ #define error builtin_error #define movetogoal builtin_movetogoal #define objerror builtin_objerror +#define remove builtin_remove #define walkmove builtin_walkmove +#define setcolor builtin_setcolor diff --git a/qcsrc/dpdefs/progsdefs.qh b/qcsrc/dpdefs/progsdefs.qh index e2668a54b7..ccdf9bc0c4 100644 --- a/qcsrc/dpdefs/progsdefs.qh +++ b/qcsrc/dpdefs/progsdefs.qh @@ -1,5 +1,4 @@ -#ifndef PROGSDEFS_H -#define PROGSDEFS_H +#pragma once #pragma noref 1 @@ -30,5 +29,3 @@ MACRO_END #define use use1 .void(entity this, entity actor, entity trigger) use; - -#endif diff --git a/qcsrc/dpdefs/upstream/dpextensions.qc b/qcsrc/dpdefs/upstream/dpextensions.qc index 21060c82ef..2f49d97bb8 100644 --- a/qcsrc/dpdefs/upstream/dpextensions.qc +++ b/qcsrc/dpdefs/upstream/dpextensions.qc @@ -2599,3 +2599,12 @@ void coverage() = #642; // Reports a coverage event. The engine counts for each float(string url, float id, string content_type, string delim, float buf, float keyid) crypto_uri_postbuf = #513; //description: //use -1 as buffer handle to justs end delim as postdata + +//DP_USERMOVETYPES +//idea: divVerent +//darkplaces implementation: Mario +//movetype definitions: +float MOVETYPE_USER_FIRST = 128; +float MOVETYPE_USER_LAST = 191; +//description: +//user defined movetypes can be added between the start and end points, without producing unknown movetype warnings diff --git a/qcsrc/ecs/README.md b/qcsrc/ecs/README.md new file mode 100644 index 0000000000..d49094f7b4 --- /dev/null +++ b/qcsrc/ecs/README.md @@ -0,0 +1,47 @@ +# Xonotic entity component system + +## guidelines + +* avoid #if and #ifdef +* avoid string +* avoid declaring entity fields outside of components +* uncrustify relentlessly +* shared code in $file, prog specific code uses prefix: { client: cl_, server: sv_, menu: ui_ }. $file must exist +* component naming =~ com_$component_$name +* system naming =~ sys_$system_$name +* event naming =~ evt_$component_$name +* global naming =~ g_$name +* cvar naming =~ xon_$name + +## components + + COMPONENT($component); + .int com_$component_$property; + +## entities + + entity e = new(foo); + e.com_$component = true; + e.com_$component_$property = 42; + +## systems + + SYSTEM($system, 30, 10); + sys_$system_update(entity this, float dt) { + code; + } + +## events + +### declaring + + EVENT($component_$name, (entity this)); + +### emitting + + emit($event, it); + +### listening + + entity listener = new_pure(someListener); + subscribe(listener, $event, void(entity this) { code; }); diff --git a/qcsrc/ecs/_mod.inc b/qcsrc/ecs/_mod.inc new file mode 100644 index 0000000000..48b7069b24 --- /dev/null +++ b/qcsrc/ecs/_mod.inc @@ -0,0 +1,6 @@ +// generated file; do not modify +#include + +#include +#include +#include diff --git a/qcsrc/ecs/_mod.qh b/qcsrc/ecs/_mod.qh new file mode 100644 index 0000000000..0d1ff4482f --- /dev/null +++ b/qcsrc/ecs/_mod.qh @@ -0,0 +1,6 @@ +// generated file; do not modify +#include + +#include +#include +#include diff --git a/qcsrc/ecs/components/_mod.inc b/qcsrc/ecs/components/_mod.inc new file mode 100644 index 0000000000..097587129f --- /dev/null +++ b/qcsrc/ecs/components/_mod.inc @@ -0,0 +1,3 @@ +// generated file; do not modify +#include +#include diff --git a/qcsrc/ecs/components/_mod.qh b/qcsrc/ecs/components/_mod.qh new file mode 100644 index 0000000000..64fad31b30 --- /dev/null +++ b/qcsrc/ecs/components/_mod.qh @@ -0,0 +1,3 @@ +// generated file; do not modify +#include +#include diff --git a/qcsrc/ecs/components/input.qc b/qcsrc/ecs/components/input.qc new file mode 100644 index 0000000000..13b341367b --- /dev/null +++ b/qcsrc/ecs/components/input.qc @@ -0,0 +1,3 @@ +#include "input.qh" + +void com_in_interpolate(entity it, float a) { } diff --git a/qcsrc/ecs/components/input.qh b/qcsrc/ecs/components/input.qh new file mode 100644 index 0000000000..bc1c66ace4 --- /dev/null +++ b/qcsrc/ecs/components/input.qh @@ -0,0 +1,7 @@ +#pragma once + +COMPONENT(in); +.vector com_in_move, com_in_move_prev; +.vector com_in_angles; +.bool com_in_jump; +.bool com_in_crouch; diff --git a/qcsrc/ecs/components/physics.qc b/qcsrc/ecs/components/physics.qc new file mode 100644 index 0000000000..5094ad17a8 --- /dev/null +++ b/qcsrc/ecs/components/physics.qc @@ -0,0 +1,10 @@ +#include "physics.qh" + +bool autocvar_xon_com_phys_interpolate = true; + +void com_phys_interpolate(entity it, float a) +{ + if (!autocvar_xon_com_phys_interpolate) a = 1; + it.origin = it.com_phys_pos_prev * (1 - a) + it.com_phys_pos * a; + it.angles = it.com_phys_ang_prev * (1 - a) + it.com_phys_ang * a; // TODO: slerp, not lerp +} diff --git a/qcsrc/ecs/components/physics.qh b/qcsrc/ecs/components/physics.qh new file mode 100644 index 0000000000..f150a296c2 --- /dev/null +++ b/qcsrc/ecs/components/physics.qh @@ -0,0 +1,26 @@ +#pragma once + +COMPONENT(phys); +.vector com_phys_pos, com_phys_pos_prev; +.vector com_phys_ang, com_phys_ang_prev; +.vector com_phys_vel; +.float com_phys_vel_max; +.float com_phys_vel_max_air; +.float com_phys_vel_max_air_strafe; +.vector com_phys_acc; +.float com_phys_acc_rate; +.float com_phys_acc_rate_air; +.float com_phys_acc_rate_air_strafe; +.float com_phys_acc_rate_air_stop; +.float com_phys_friction; + +.vector com_phys_gravity; +.float com_phys_gravity_factor; +// TODO: remove +.bool com_phys_ground; +.bool com_phys_air; +.bool com_phys_ladder; +.bool com_phys_vel_2d; +.bool com_phys_water; +.bool com_phys_friction_air; +.bool move_qcphysics; diff --git a/qcsrc/ecs/events/_mod.inc b/qcsrc/ecs/events/_mod.inc new file mode 100644 index 0000000000..c139d72b51 --- /dev/null +++ b/qcsrc/ecs/events/_mod.inc @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/ecs/events/_mod.qh b/qcsrc/ecs/events/_mod.qh new file mode 100644 index 0000000000..1e8c791ce9 --- /dev/null +++ b/qcsrc/ecs/events/_mod.qh @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/ecs/events/physics.qc b/qcsrc/ecs/events/physics.qc new file mode 100644 index 0000000000..909f45c1db --- /dev/null +++ b/qcsrc/ecs/events/physics.qc @@ -0,0 +1 @@ +#include "physics.qh" diff --git a/qcsrc/ecs/events/physics.qh b/qcsrc/ecs/events/physics.qh new file mode 100644 index 0000000000..df32c557d1 --- /dev/null +++ b/qcsrc/ecs/events/physics.qh @@ -0,0 +1,3 @@ +#pragma once + +EVENT(phys_land, (entity this)); diff --git a/qcsrc/ecs/lib.qh b/qcsrc/ecs/lib.qh new file mode 100644 index 0000000000..2d48e577b8 --- /dev/null +++ b/qcsrc/ecs/lib.qh @@ -0,0 +1,57 @@ +#pragma once + +/** Components always interpolate from the previous state */ +#define COMPONENT(com) \ + void com_##com##_interpolate(entity it, float a); \ + .bool com_##com + +#define FOREACH_COMPONENT(com, body) FOREACH_ENTITY_FLOAT(com_##com, true, body) + + +#define EVENT(T, args) .bool evt_##T##_listener; .void args evt_##T + +#define emit(T, ...) \ + MACRO_BEGIN \ + FOREACH_ENTITY_FLOAT_ORDERED(evt_##T##_listener, true, it.evt_##T(__VA_ARGS__)); \ + MACRO_END + +#define subscribe(listener, T, fn) \ + MACRO_BEGIN \ + listener.evt_##T = (fn); \ + listener.evt_##T##_listener = true; \ + MACRO_END + + +/** + * framelimit 0 is no limit, interpolation does not apply + * framerate below minfps will result in less than 100% speed + */ +#define SYSTEM(sys, frameLimit, minfps) \ + void sys_##sys##_update(entity this, float dt); \ + noref float autocvar_xon_sys_##sys##_dt = ((frameLimit) ? (1 / (frameLimit)) : 0); \ + noref float autocvar_xon_sys_##sys##_minfps = (1 / (1 / (minfps))) + +#define SYSTEM_UPDATE(sys) \ + MACRO_BEGIN \ + static float t = 0; \ + float dt = autocvar_xon_sys_##sys##_dt; \ + float minfps = autocvar_xon_sys_##sys##_minfps; \ + static float accumulator = 0; \ + float a = 0; \ + if (dt) { \ + accumulator += min(frametime, 1 / (minfps)); \ + } else { \ + accumulator += frametime; \ + dt = accumulator; \ + a = 1; \ + } \ + while (accumulator >= dt) \ + { \ + time = t; \ + FOREACH_COMPONENT(sys, sys_##sys##_update(it, dt)); \ + t += dt; \ + accumulator -= dt; \ + } \ + if (!a) a = accumulator / dt; \ + FOREACH_COMPONENT(sys, com_##sys##_interpolate(it, a)); \ + MACRO_END diff --git a/qcsrc/ecs/main.qc b/qcsrc/ecs/main.qc new file mode 100644 index 0000000000..29be3f367f --- /dev/null +++ b/qcsrc/ecs/main.qc @@ -0,0 +1,12 @@ +#include "main.qh" + +#include "components/_mod.qh" +#include "events/_mod.qh" +#include "systems/_mod.qh" + +void systems_update() +{ + float realtime = time; + SYSTEM_UPDATE(phys); + time = realtime; +} diff --git a/qcsrc/ecs/main.qh b/qcsrc/ecs/main.qh new file mode 100644 index 0000000000..724cb1ef89 --- /dev/null +++ b/qcsrc/ecs/main.qh @@ -0,0 +1,5 @@ +#pragma once + +#include "lib.qh" + +void systems_update(); diff --git a/qcsrc/ecs/systems/_mod.inc b/qcsrc/ecs/systems/_mod.inc new file mode 100644 index 0000000000..a5ada6cfbe --- /dev/null +++ b/qcsrc/ecs/systems/_mod.inc @@ -0,0 +1,9 @@ +// generated file; do not modify +#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif diff --git a/qcsrc/ecs/systems/_mod.qh b/qcsrc/ecs/systems/_mod.qh new file mode 100644 index 0000000000..869aefd557 --- /dev/null +++ b/qcsrc/ecs/systems/_mod.qh @@ -0,0 +1,3 @@ +// generated file; do not modify +#include +#include diff --git a/qcsrc/ecs/systems/cl_physics.qc b/qcsrc/ecs/systems/cl_physics.qc new file mode 100644 index 0000000000..f74e9c2d55 --- /dev/null +++ b/qcsrc/ecs/systems/cl_physics.qc @@ -0,0 +1,29 @@ +#include "physics.qh" + +void sys_phys_fix(entity this, float dt) +{ + this.team = myteam + 1; // is this correct? + PHYS_WATERJUMP_TIME(this) -= dt; + this.movement = PHYS_INPUT_MOVEVALUES(this); + this.items = STAT(ITEMS, this); + this.spectatorspeed = STAT(SPECTATORSPEED, this); + if (!(PHYS_INPUT_BUTTON_JUMP(this))) // !jump + UNSET_JUMP_HELD(this); // canjump = true + PM_ClientMovement_UpdateStatus(this); +} + +bool sys_phys_override(entity this, float dt) +{ + // no vehicle prediction + return hud != HUD_NORMAL; +} + +void sys_phys_monitor(entity this, float dt) {} + +void sys_phys_ai(entity this) {} + +void sys_phys_pregame_hold(entity this) {} + +void sys_phys_spectator_control(entity this) {} + +void sys_phys_fixspeed(entity this, float maxspeed_mod) {} diff --git a/qcsrc/ecs/systems/input.qc b/qcsrc/ecs/systems/input.qc new file mode 100644 index 0000000000..eac36250ba --- /dev/null +++ b/qcsrc/ecs/systems/input.qc @@ -0,0 +1,7 @@ +#include "input.qh" + +void sys_in_update(entity this, float dt) +{ + this.com_in_jump = PHYS_INPUT_BUTTON_JUMP(this); + this.com_in_crouch = PHYS_INPUT_BUTTON_CROUCH(this); +} diff --git a/qcsrc/ecs/systems/input.qh b/qcsrc/ecs/systems/input.qh new file mode 100644 index 0000000000..fc3f11adaf --- /dev/null +++ b/qcsrc/ecs/systems/input.qh @@ -0,0 +1,3 @@ +#pragma once + +SYSTEM(in, 30, 10); diff --git a/qcsrc/ecs/systems/physics.qc b/qcsrc/ecs/systems/physics.qc new file mode 100644 index 0000000000..0c18e58cf8 --- /dev/null +++ b/qcsrc/ecs/systems/physics.qc @@ -0,0 +1,491 @@ +#include "physics.qh" +#include "input.qh" + +.int disableclientprediction; + +void sys_phys_simulate(entity this, float dt); +void sys_phys_simulate_simple(entity this, float dt); + +void sys_phys_update(entity this, float dt) +{ + if (!IS_CLIENT(this)) { + sys_phys_simulate_simple(this, dt); + return; + } + sys_in_update(this, dt); + + sys_phys_fix(this, dt); + if (sys_phys_override(this, dt)) { return; } sys_phys_monitor(this, dt); + + this.buttons_old = PHYS_INPUT_BUTTON_MASK(this); + this.movement_old = this.movement; + this.v_angle_old = this.v_angle; + + sys_phys_ai(this); + + sys_phys_pregame_hold(this); + + if (IS_SVQC) { + if (this.move_movetype == MOVETYPE_NONE) { return; } + // when we get here, disableclientprediction cannot be 2 + this.disableclientprediction = (this.move_qcphysics) ? -1 : 0; + } + + viewloc_PlayerPhysics(this); + + PM_check_frozen(this); + + PM_check_blocked(this); + + 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; } + MUTATOR_CALLHOOK(PlayerPhysics, this, dt); + + if (!IS_PLAYER(this)) { + sys_phys_spectator_control(this); + maxspeed_mod = this.spectatorspeed; + } + sys_phys_fixspeed(this, maxspeed_mod); + + if (IS_DEAD(this)) { + // handle water here + vector midpoint = ((this.absmin + this.absmax) * 0.5); + if (pointcontents(midpoint) == CONTENT_WATER) { + this.velocity = this.velocity * 0.5; + + // do we want this? + // if(pointcontents(midpoint + '0 0 2') == CONTENT_WATER) + // { this.velocity_z = 70; } + } + goto end; + } + + PM_check_slick(this); + + if (IS_SVQC && !PHYS_FIXANGLE(this)) { this.angles = '0 1 0' * this.v_angle.y; } + if (IS_PLAYER(this)) { + if (IS_ONGROUND(this)) { + PM_check_hitground(this); + PM_Footsteps(this); + } else if (IsFlying(this)) { + this.wasFlying = true; + } + CheckPlayerJump(this); + } + + if (this.flags & FL_WATERJUMP) { + this.velocity_x = this.movedir.x; + this.velocity_y = this.movedir.y; + if (this.waterlevel == WATERLEVEL_NONE + || time > PHYS_TELEPORT_TIME(this) + || PHYS_WATERJUMP_TIME(this) <= 0 + ) { + this.flags &= ~FL_WATERJUMP; + PHYS_TELEPORT_TIME(this) = 0; + PHYS_WATERJUMP_TIME(this) = 0; + } + } else if (MUTATOR_CALLHOOK(PM_Physics, this, maxspeed_mod, dt)) { + // handled + } else if (this.move_movetype == MOVETYPE_NOCLIP + || this.move_movetype == MOVETYPE_FLY + || this.move_movetype == MOVETYPE_FLY_WORLDONLY + || MUTATOR_CALLHOOK(IsFlying, this)) { + this.com_phys_friction = PHYS_FRICTION(this); + this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod; + this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod; + this.com_phys_friction_air = true; + sys_phys_simulate(this, dt); + this.com_phys_friction_air = false; + } else if (this.waterlevel >= WATERLEVEL_SWIMMING) { + this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod; + this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod; + this.com_phys_water = true; + sys_phys_simulate(this, dt); + this.com_phys_water = false; + } else if (time < this.ladder_time) { + this.com_phys_friction = PHYS_FRICTION(this); + this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod; + this.com_phys_acc_rate = PHYS_ACCELERATE(this) * maxspeed_mod; + this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt; + if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); } + this.com_phys_ladder = true; + this.com_phys_friction_air = true; + sys_phys_simulate(this, dt); + this.com_phys_friction_air = false; + this.com_phys_ladder = false; + this.com_phys_gravity = '0 0 0'; + } else if (ITEMS_STAT(this) & IT_USING_JETPACK) { + PM_jetpack(this, maxspeed_mod, dt); + } else if (IS_ONGROUND(this)) { + if (!WAS_ONGROUND(this)) { + emit(phys_land, this); + if (this.lastground < time - 0.3) { + this.velocity *= (1 - PHYS_FRICTION_ONLAND(this)); + } + } + this.com_phys_vel_max = PHYS_MAXSPEED(this) * maxspeed_mod; + this.com_phys_gravity = '0 0 -1' * PHYS_GRAVITY(this) * dt; + if (PHYS_ENTGRAVITY(this)) { this.com_phys_gravity *= PHYS_ENTGRAVITY(this); } + this.com_phys_ground = true; + this.com_phys_vel_2d = true; + sys_phys_simulate(this, dt); + this.com_phys_vel_2d = false; + this.com_phys_ground = false; + this.com_phys_gravity = '0 0 0'; + } else { + this.com_phys_acc_rate_air = PHYS_AIRACCELERATE(this) * min(maxspeed_mod, 1); + this.com_phys_acc_rate_air_stop = PHYS_AIRSTOPACCELERATE(this) * maxspeed_mod; + this.com_phys_acc_rate_air_strafe = PHYS_AIRSTRAFEACCELERATE(this) * maxspeed_mod; + this.com_phys_vel_max_air_strafe = PHYS_MAXAIRSTRAFESPEED(this) * maxspeed_mod; + this.com_phys_vel_max_air = PHYS_MAXAIRSPEED(this) * maxspeed_mod; + this.com_phys_vel_max = PHYS_MAXAIRSPEED(this) * min(maxspeed_mod, 1); + this.com_phys_air = true; + this.com_phys_vel_2d = true; + sys_phys_simulate(this, dt); + this.com_phys_vel_2d = false; + this.com_phys_air = false; + } + + LABEL(end) + if (IS_ONGROUND(this)) { this.lastground = time; } +// conveyors: then break velocity again + if (this.conveyor.state) { this.velocity += this.conveyor.movedir; } + this.lastflags = this.flags; + + this.lastclassname = this.classname; +} + +/** for players */ +void sys_phys_simulate(entity this, float dt) +{ + if (!this.com_phys_ground && !this.com_phys_air) { + // noclipping + // flying + // on a spawnfunc_func_ladder + // swimming in spawnfunc_func_water + // swimming + UNSET_ONGROUND(this); + + if (this.com_phys_friction_air) { + const vector g = -this.com_phys_gravity; + this.velocity_z += g.z / 2; + this.velocity = this.velocity * (1 - dt * this.com_phys_friction); + this.velocity_z += g.z / 2; + } + } + + if (this.com_phys_water) { + // water jump only in certain situations + // this mimics quakeworld code + if (this.com_in_jump && this.waterlevel == WATERLEVEL_SWIMMING && this.velocity_z >= -180 && !this.viewloc) { + vector yawangles = '0 1 0' * this.v_angle.y; + makevectors(yawangles); + vector forward = v_forward; + vector spot = this.origin + 24 * forward; + spot_z += 8; + traceline(spot, spot, MOVE_NOMONSTERS, this); + if (trace_startsolid) { + spot_z += 24; + traceline(spot, spot, MOVE_NOMONSTERS, this); + if (!trace_startsolid) { + this.velocity = forward * 50; + this.velocity_z = 310; + UNSET_ONGROUND(this); + SET_JUMP_HELD(this); + } + } + } + } + makevectors(vmul(this.v_angle, (this.com_phys_vel_2d ? '0 1 0' : '1 1 1'))); + // wishvel = v_forward * this.movement.x + v_right * this.movement.y + v_up * this.movement.z; + vector wishvel = v_forward * this.movement.x + + v_right * this.movement.y + + '0 0 1' * this.movement.z * (this.com_phys_vel_2d ? 0 : 1); + if (this.com_phys_water) { + if (PHYS_INPUT_BUTTON_CROUCH(this)) { + wishvel.z = -PHYS_MAXSPEED(this); + } + if (this.viewloc) { + wishvel.z = -160; // drift anyway + } else if (wishvel == '0 0 0') { + wishvel = '0 0 -60'; // drift towards bottom + } + } + if (this.com_phys_ladder) { + if (this.viewloc) { + wishvel.z = this.movement_old.x; + } + if (this.ladder_entity.classname == "func_water") { + float f = vlen(wishvel); + if (f > this.ladder_entity.speed) { + wishvel *= (this.ladder_entity.speed / f); + } + + this.watertype = this.ladder_entity.skin; + f = this.ladder_entity.origin_z + this.ladder_entity.maxs_z; + if ((this.origin_z + this.view_ofs_z) < f) { + this.waterlevel = WATERLEVEL_SUBMERGED; + } else if ((this.origin_z + (this.mins_z + this.maxs_z) * 0.5) < f) { + this.waterlevel = WATERLEVEL_SWIMMING; + } else if ((this.origin_z + this.mins_z + 1) < f) { + this.waterlevel = WATERLEVEL_WETFEET; + } else { + this.waterlevel = WATERLEVEL_NONE; + this.watertype = CONTENT_EMPTY; + } + } + } + // acceleration + const vector wishdir = normalize(wishvel); + float wishspeed = min(vlen(wishvel), this.com_phys_vel_max); + + if (this.com_phys_air) { + if ((IS_SVQC && time >= PHYS_TELEPORT_TIME(this)) + || (IS_CSQC && PHYS_WATERJUMP_TIME(this) <= 0)) { + // apply air speed limit + float airaccelqw = PHYS_AIRACCEL_QW(this); + float wishspeed0 = wishspeed; + const float maxairspd = this.com_phys_vel_max; + wishspeed = min(wishspeed, maxairspd); + if (IS_DUCKED(this)) { + wishspeed *= 0.5; + } + float airaccel = this.com_phys_acc_rate_air; + + float accelerating = (this.velocity * wishdir > 0); + float wishspeed2 = wishspeed; + + // CPM: air control + if (PHYS_AIRSTOPACCELERATE(this)) { + vector curdir = normalize(vec2(this.velocity)); + airaccel += (this.com_phys_acc_rate_air_stop - airaccel) * max(0, -(curdir * wishdir)); + } + // note that for straight forward jumping: + // step = accel * dt * wishspeed0; + // accel = bound(0, wishspeed - vel_xy_current, step) * accelqw + step * (1 - accelqw); + // --> + // dv/dt = accel * maxspeed (when slow) + // dv/dt = accel * maxspeed * (1 - accelqw) (when fast) + // log dv/dt = logaccel + logmaxspeed (when slow) + // log dv/dt = logaccel + logmaxspeed + log(1 - accelqw) (when fast) + float strafity = IsMoveInDirection(this.movement, -90) + IsMoveInDirection(this.movement, +90); // if one is nonzero, other is always zero + if (PHYS_MAXAIRSTRAFESPEED(this)) { + wishspeed = + min(wishspeed, + GeomLerp(this.com_phys_vel_max_air, strafity, this.com_phys_vel_max_air_strafe)); + } + if (PHYS_AIRSTRAFEACCELERATE(this)) { + airaccel = GeomLerp(airaccel, strafity, this.com_phys_acc_rate_air_strafe); + } + if (PHYS_AIRSTRAFEACCEL_QW(this)) { + airaccelqw = + (((strafity > 0.5 ? PHYS_AIRSTRAFEACCEL_QW(this) : PHYS_AIRACCEL_QW(this)) >= 0) ? +1 : -1) + * + (1 - GeomLerp(1 - fabs(PHYS_AIRACCEL_QW(this)), strafity, 1 - fabs(PHYS_AIRSTRAFEACCEL_QW(this)))); + } + // !CPM + + if (PHYS_WARSOWBUNNY_TURNACCEL(this) && accelerating && this.movement.y == 0 && this.movement.x != 0) { + PM_AirAccelerate(this, dt, wishdir, wishspeed2); + } else { + float sidefric = maxairspd ? (PHYS_AIRACCEL_SIDEWAYS_FRICTION(this) / maxairspd) : 0; + PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed0, airaccel, airaccelqw, + PHYS_AIRACCEL_QW_STRETCHFACTOR(this), sidefric, PHYS_AIRSPEEDLIMIT_NONQW(this)); + } + + if (PHYS_AIRCONTROL(this)) { + CPM_PM_Aircontrol(this, dt, wishdir, wishspeed2); + } + } + } else { + if (this.com_phys_ground && IS_DUCKED(this)) { wishspeed *= 0.5; } + if (this.com_phys_water) { + wishspeed *= 0.7; + + // if (PHYS_WATERJUMP_TIME(this) <= 0) // TODO: use + { + // water friction + float f = 1 - dt * PHYS_FRICTION(this); + f = min(max(0, f), 1); + this.velocity *= f; + + f = wishspeed - this.velocity * wishdir; + if (f > 0) { + float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, f); + this.velocity += accelspeed * wishdir; + } + + // holding jump button swims upward slowly + if (this.com_in_jump && !this.viewloc) { + // was: + // lava: 50 + // slime: 80 + // water: 100 + // idea: double those + this.velocity_z = 200; + if (this.waterlevel >= WATERLEVEL_SUBMERGED) { + this.velocity_z = PHYS_MAXSPEED(this) * 0.7; + } + } + } + if (this.viewloc) { + const float addspeed = wishspeed - this.velocity * wishdir; + if (addspeed > 0) { + const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed); + this.velocity += accelspeed * wishdir; + } + } else { + // water acceleration + PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0); + } + return; + } + if (this.com_phys_ground) { + // apply edge friction + const float f2 = vlen2(vec2(this.velocity)); + if (f2 > 0) { + trace_dphitq3surfaceflags = 0; + tracebox(this.origin, this.mins, this.maxs, this.origin - '0 0 1', MOVE_NOMONSTERS, this); + // TODO: apply edge friction + // apply ground friction + const int realfriction = (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK) + ? PHYS_FRICTION_SLICK(this) + : PHYS_FRICTION(this); + + float f = sqrt(f2); + f = 1 - dt * realfriction + * ((f < PHYS_STOPSPEED(this)) ? (PHYS_STOPSPEED(this) / f) : 1); + f = max(0, f); + this.velocity *= f; + /* + Mathematical analysis time! + + Our goal is to invert this mess. + + For the two cases we get: + v = v0 * (1 - dt * (PHYS_STOPSPEED(this) / v0) * PHYS_FRICTION(this)) + = v0 - dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) + v0 = v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) + and + v = v0 * (1 - dt * PHYS_FRICTION(this)) + v0 = v / (1 - dt * PHYS_FRICTION(this)) + + These cases would be chosen ONLY if: + v0 < PHYS_STOPSPEED(this) + v + dt * PHYS_STOPSPEED(this) * PHYS_FRICTION(this) < PHYS_STOPSPEED(this) + v < PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this)) + and, respectively: + v0 >= PHYS_STOPSPEED(this) + v / (1 - dt * PHYS_FRICTION(this)) >= PHYS_STOPSPEED(this) + v >= PHYS_STOPSPEED(this) * (1 - dt * PHYS_FRICTION(this)) + */ + } + const float addspeed = wishspeed - this.velocity * wishdir; + if (addspeed > 0) { + const float accelspeed = min(PHYS_ACCELERATE(this) * dt * wishspeed, addspeed); + this.velocity += accelspeed * wishdir; + } + return; + } + + if (IS_CSQC ? PHYS_WATERJUMP_TIME(this) <= 0 : time >= PHYS_TELEPORT_TIME(this)) { + PM_Accelerate(this, dt, wishdir, wishspeed, wishspeed, this.com_phys_acc_rate, 1, 0, 0, 0); + } + } +} + +.entity groundentity; +/** for other entities */ +void sys_phys_simulate_simple(entity this, float dt) +{ + vector mn = this.mins; + vector mx = this.maxs; + + vector g = '0 0 0'; + if (this.com_phys_gravity_factor && !g) g = '0 0 -1' * PHYS_GRAVITY(NULL); + + vector acc = this.com_phys_acc; + vector vel = this.com_phys_vel; + vector pos = this.com_phys_pos; + + // SV_Physics_Toss + + vel += g * dt; + + this.angles += dt * this.avelocity; + float movetime = dt; + for (int i = 0; i < MAX_CLIP_PLANES && movetime > 0; i++) { + vector push = vel * movetime; + vector p0 = pos; + vector p1 = p0 + push; + // SV_PushEntity + tracebox(p0, mn, mx, p1, MOVE_NORMAL, this); + if (!trace_startsolid) { + bool hit = trace_fraction < 1; + pos = trace_endpos; + entity ent = trace_ent; + // SV_LinkEdict_TouchAreaGrid + if (this.solid != SOLID_NOT) { + FOREACH_ENTITY_RADIUS_ORDERED(0.5 * (this.absmin + this.absmax), 0.5 * vlen(this.absmax - this.absmin), true, { + if (it.solid != SOLID_TRIGGER || it == this) continue; + if (gettouch(it) && boxesoverlap(it.absmin, it.absmax, this.absmin, this.absmax)) { + // SV_LinkEdict_TouchAreaGrid_Call + trace_allsolid = false; + trace_startsolid = false; + trace_fraction = 1; + trace_inwater = false; + trace_inopen = true; + trace_endpos = it.origin; + trace_plane_normal = '0 0 1'; + trace_plane_dist = 0; + trace_ent = this; + trace_dpstartcontents = 0; + trace_dphitcontents = 0; + trace_dphitq3surfaceflags = 0; + trace_dphittexturename = string_null; + gettouch(it)(this, it); + vel = this.velocity; + } + }); + } + if (hit && this.solid >= SOLID_TRIGGER && (!IS_ONGROUND(this) || this.groundentity != ent)) { + // SV_Impact (ent, trace); + tracebox(p0, mn, mx, p1, MOVE_NORMAL, this); + void(entity, entity) touched = gettouch(this); + if (touched && this.solid != SOLID_NOT) { + touched(ent, this); + } + void(entity, entity) touched2 = gettouch(ent); + if (this && ent && touched2 && ent.solid != SOLID_NOT) { + trace_endpos = ent.origin; + trace_plane_normal *= -1; + trace_plane_dist *= -1; + trace_ent = this; + trace_dpstartcontents = 0; + trace_dphitcontents = 0; + trace_dphitq3surfaceflags = 0; + trace_dphittexturename = string_null; + touched2(this, ent); + } + } + } + // end SV_PushEntity + if (wasfreed(this)) { return; } + tracebox(p0, mn, mx, p1, MOVE_NORMAL, this); + if (trace_fraction == 1) { break; } + movetime *= 1 - min(1, trace_fraction); + ClipVelocity(vel, trace_plane_normal, vel, 1); + } + + this.com_phys_acc = acc; + this.com_phys_vel = vel; + this.com_phys_pos = pos; + setorigin(this, this.com_phys_pos); +} + +void sys_phys_update_single(entity this) +{ + sys_phys_simulate_simple(this, frametime); +} diff --git a/qcsrc/ecs/systems/physics.qh b/qcsrc/ecs/systems/physics.qh new file mode 100644 index 0000000000..cef9916f3d --- /dev/null +++ b/qcsrc/ecs/systems/physics.qh @@ -0,0 +1,11 @@ +#pragma once + +SYSTEM(phys, 30, 10); + +void sys_phys_fix(entity this, float dt); +bool sys_phys_override(entity this, float dt); +void sys_phys_monitor(entity this, float dt); +void sys_phys_pregame_hold(entity this); +void sys_phys_ai(entity this); +void sys_phys_spectator_control(entity this); +void sys_phys_fixspeed(entity this, float maxspeed_mod); diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc new file mode 100644 index 0000000000..4973a18297 --- /dev/null +++ b/qcsrc/ecs/systems/sv_physics.qc @@ -0,0 +1,117 @@ +#include "physics.qh" + +void sys_phys_fix(entity this, float dt) +{ + WarpZone_PlayerPhysics_FixVAngle(this); + Physics_UpdateStats(this, PHYS_HIGHSPEED(this)); +} + +bool sys_phys_override(entity this, float dt) +{ + int buttons = PHYS_INPUT_BUTTON_MASK(this); + if (PM_check_specialcommand(this, buttons)) { return true; } + if (this.PlayerPhysplug && this.PlayerPhysplug(this, dt)) { return true; } + return false; +} + +void sys_phys_monitor(entity this, float dt) +{ + int buttons = PHYS_INPUT_BUTTON_MASK(this); + anticheat_physics(this); + if (sv_maxidle > 0) { + if (buttons != this.buttons_old + || this.movement != this.movement_old + || this.v_angle != this.v_angle_old) { this.parm_idlesince = time; } + } + PM_check_nickspam(this); + PM_check_punch(this, dt); +} + +void sys_phys_ai(entity this) +{ + if (!IS_BOT_CLIENT(this)) { return; } + if (playerdemo_read(this)) { return; } + bot_think(this); +} + +void sys_phys_pregame_hold(entity this) +{ + if (!IS_PLAYER(this)) { return; } + const bool allowed_to_move = (time >= game_starttime && !gameover); + if (!allowed_to_move) { + this.velocity = '0 0 0'; + set_movetype(this, MOVETYPE_NONE); + this.disableclientprediction = 2; + } else if (this.disableclientprediction == 2) { + if (this.move_movetype == MOVETYPE_NONE) { set_movetype(this, MOVETYPE_WALK); } + this.disableclientprediction = 0; + } +} + +void sys_phys_spectator_control(entity this) +{ + float maxspeed_mod = autocvar_sv_spectator_speed_multiplier; + if (!this.spectatorspeed) { this.spectatorspeed = maxspeed_mod; } + if ((this.impulse >= 1 && this.impulse <= 19) + || (this.impulse >= 200 && this.impulse <= 209) + || (this.impulse >= 220 && this.impulse <= 229) + ) { + if (this.lastclassname != STR_PLAYER) { + if (this.impulse == 10 + || this.impulse == 15 + || this.impulse == 18 + || (this.impulse >= 200 && this.impulse <= 209) + ) { this.spectatorspeed = bound(1, this.spectatorspeed + 0.5, 5); } else if (this.impulse == 11) { + this.spectatorspeed = maxspeed_mod; + } else if (this.impulse == 12 + || this.impulse == 16 + || this.impulse == 19 + || (this.impulse >= 220 && this.impulse <= 229) + ) { + this.spectatorspeed = bound(1, this.spectatorspeed - 0.5, 5); + } else if (this.impulse >= 1 && this.impulse <= 9) { + this.spectatorspeed = 1 + 0.5 * (this.impulse - 1); + } + } // otherwise just clear + this.impulse = 0; + } +} + +void sys_phys_fixspeed(entity this, float maxspeed_mod) +{ + float spd = max(PHYS_MAXSPEED(this), PHYS_MAXAIRSPEED(this)) * maxspeed_mod; + if (this.speed != spd) { + this.speed = spd; + string temps = ftos(spd); + stuffcmd(this, strcat("cl_forwardspeed ", temps, "\n")); + stuffcmd(this, strcat("cl_backspeed ", temps, "\n")); + stuffcmd(this, strcat("cl_sidespeed ", temps, "\n")); + stuffcmd(this, strcat("cl_upspeed ", temps, "\n")); + } + + if (this.jumpspeedcap_min != autocvar_sv_jumpspeedcap_min) { + this.jumpspeedcap_min = autocvar_sv_jumpspeedcap_min; + stuffcmd(this, sprintf("\ncl_jumpspeedcap_min \"%s\"\n", autocvar_sv_jumpspeedcap_min)); + } + if (this.jumpspeedcap_max != autocvar_sv_jumpspeedcap_max) { + this.jumpspeedcap_max = autocvar_sv_jumpspeedcap_max; + stuffcmd(this, sprintf("\ncl_jumpspeedcap_max \"%s\"\n", autocvar_sv_jumpspeedcap_max)); + } +} + +void sys_phys_land(entity this) +{ + if (autocvar_speedmeter) { + LOG_TRACEF("landing velocity: %v (abs: %f)", this.velocity, vlen(this.velocity)); + } + if (this.jumppadcount > 1) { + LOG_TRACEF("%dx jumppad combo", this.jumppadcount); + } + this.jumppadcount = 0; +} + +STATIC_INIT(sys_phys) +{ + entity listener = new_pure(sys_phys); + subscribe(listener, phys_land, sys_phys_land); +} diff --git a/qcsrc/lib/_all.inc b/qcsrc/lib/_all.inc index 03b9436258..65dc080c2c 100644 --- a/qcsrc/lib/_all.inc +++ b/qcsrc/lib/_all.inc @@ -2,6 +2,18 @@ #define COMPAT_NO_MOD_IS_XONOTIC #endif +#ifdef CSQC +#define IS_CSQC 1 +#else +#define IS_CSQC 0 +#endif + +#ifdef SVQC +#define IS_SVQC 1 +#else +#define IS_SVQC 0 +#endif + #include "compiler.qh" #ifndef QCC_SUPPORT_INT @@ -39,28 +51,28 @@ #include "macro.qh" #if NDEBUG - #define TC(T, sym) MACRO_BEGIN MACRO_END + #define TC(T, sym) MACRO_BEGIN MACRO_END #else - #define TC(T, sym) MACRO_BEGIN \ - if (!is_##T(sym)) { \ - LOG_WARNINGF("Type check failed: " #sym " :: " #T); \ - isnt_##T(sym); \ - } \ - MACRO_END + #define TC(T, sym) MACRO_BEGIN \ + if (!is_##T(sym)) { \ + LOG_WARNF("Type check failed: " #sym " :: " #T); \ + isnt_##T(sym); \ + } \ + MACRO_END #endif -#define is_float( this) (true || ftoe(this)) -#define isnt_float( this) -#define is_vector( this) (true || vtos(this)) -#define isnt_vector( this) -#define is_string( this) (true || stof(this)) -#define isnt_string( this) -#define is_entity( this) (true || etof(this)) -#define isnt_entity( this) -bool is_int( float this) { return this == floor(this); } -void isnt_int( float this) { print(ftos(this)); } -bool is_bool( float this) { return this == true || this == false; } -void isnt_bool( float this) { print(ftos(this)); } +#define is_float(this) (true || ftoe(this)) +#define isnt_float(this) +#define is_vector(this) (true || vtos(this)) +#define isnt_vector(this) +#define is_string(this) (true || stof(this)) +#define isnt_string(this) +#define is_entity(this) (true || etof(this)) +#define isnt_entity(this) +bool is_int(float this) { return this == floor(this); } +void isnt_int(float this) { print(ftos(this)); } +bool is_bool(float this) { return this == true || this == false; } +void isnt_bool(float this) { print(ftos(this)); } #include "warpzone/mathlib.qc" @@ -77,6 +89,7 @@ void isnt_bool( float this) { print(ftos(this)); } #include "file.qh" #include "functional.qh" #include "i18n.qh" +#include "intrusivelist.qh" #include "iter.qh" #include "json.qc" #include "lazy.qh" @@ -110,3 +123,157 @@ void isnt_bool( float this) { print(ftos(this)); } #include "yenc.qh" #include "matrix/_mod.inc" + +#ifndef SVQC +#define objerror_safe(e) +#else +void make_safe_for_remove(entity this); + #define objerror_safe(e) make_safe_for_remove(e) +#endif + +#define objerror(this, msg) MACRO_BEGIN { \ + LOG_WARN("======OBJECT ERROR======"); \ + entity _e = (this); \ + eprint(_e); \ + objerror_safe(_e); \ + delete(_e); \ + LOG_WARNF("%s OBJECT ERROR in %s:\n%s\nTip: read above for entity information", PROGNAME, __FUNC__, msg); \ +} MACRO_END + +#ifdef MENUQC + void _m_init(); + void m_init() { if (_m_init) _m_init(); } + #define m_init _m_init + + void _m_shutdown(); + void m_shutdown() { if (_m_shutdown) _m_shutdown(); } + #define m_shutdown _m_shutdown + + void _m_draw(float width, float height); + void m_draw(float width, float height) { if (_m_draw) _m_draw(width, height); } + #define m_draw _m_draw + + void _m_keydown(int keynr, int ascii); + void m_keydown(int keynr, int ascii) { if (_m_keydown) _m_keydown(keynr, ascii); } + #define m_keydown _m_keydown + + void _m_toggle(int mode); + void m_toggle(int mode) { if (_m_toggle) _m_toggle(mode); } + #define m_toggle _m_toggle +#endif + +#ifdef SVQC + void _main(); + void main() { if (_main) _main(); } + #define main _main + + void _SV_Shutdown(); + void SV_Shutdown() { if (_SV_Shutdown) _SV_Shutdown(); } + #define SV_Shutdown _SV_Shutdown + + void _StartFrame(); + void StartFrame() { if (_StartFrame) _StartFrame(); } + #define StartFrame _StartFrame + + void _SetNewParms(); + void SetNewParms() { if (_SetNewParms) _SetNewParms(); } + #define SetNewParms _SetNewParms + + void _SetChangeParms(entity this); + void SetChangeParms() { ENGINE_EVENT(); if (_SetChangeParms) _SetChangeParms(this); } + #define SetChangeParms _SetChangeParms + +#ifdef DP_EXT_PRECONNECT + void _ClientPreConnect(entity this); + void ClientPreConnect() { ENGINE_EVENT(); if (_ClientPreConnect) _ClientPreConnect(this); } + #define ClientPreConnect _ClientPreConnect +#endif + + void _ClientConnect(entity this); + void ClientConnect() { ENGINE_EVENT(); if (_ClientConnect) _ClientConnect(this); } + #define ClientConnect _ClientConnect + + void _ClientDisconnect(entity this); + void ClientDisconnect() { ENGINE_EVENT(); if (_ClientDisconnect) _ClientDisconnect(this); } + #define ClientDisconnect _ClientDisconnect + + void _PutClientInServer(entity this); + void PutClientInServer() { ENGINE_EVENT(); if (_PutClientInServer) _PutClientInServer(this); } + #define PutClientInServer _PutClientInServer + + void _ClientKill(entity this); + void ClientKill() { ENGINE_EVENT(); if (_ClientKill) _ClientKill(this); } + #define ClientKill _ClientKill + + void _PlayerPreThink(entity this); + void PlayerPreThink() { ENGINE_EVENT(); if (_PlayerPreThink) _PlayerPreThink(this); } + #define PlayerPreThink _PlayerPreThink + + void _PlayerPostThink(entity this); + void PlayerPostThink() { ENGINE_EVENT(); if (_PlayerPostThink) _PlayerPostThink(this); } + #define PlayerPostThink _PlayerPostThink + + void _SV_PlayerPhysics(entity this); + void SV_PlayerPhysics() { ENGINE_EVENT(); if (_SV_PlayerPhysics) _SV_PlayerPhysics(this); } + #define SV_PlayerPhysics _SV_PlayerPhysics + + void _SV_OnEntityPreSpawnFunction(entity this); + void SV_OnEntityPreSpawnFunction() + { + ENGINE_EVENT(); + if (_SV_OnEntityPreSpawnFunction) _SV_OnEntityPreSpawnFunction(this); + } + #define SV_OnEntityPreSpawnFunction _SV_OnEntityPreSpawnFunction + + void _SV_ChangeTeam(entity this, int _color); + void SV_ChangeTeam(int _color) { ENGINE_EVENT(); if (_SV_ChangeTeam) _SV_ChangeTeam(this, _color); } + #define SV_ChangeTeam _SV_ChangeTeam + + void _SV_ParseClientCommand(entity this, string command); + void SV_ParseClientCommand(string command) + { + ENGINE_EVENT(); + if (_SV_ParseClientCommand) _SV_ParseClientCommand(this, command); + } + #define SV_ParseClientCommand _SV_ParseClientCommand +#endif + +#ifdef CSQC + void _CSQC_Init(); + void CSQC_Init() { if (_CSQC_Init) _CSQC_Init(); } + #define CSQC_Init _CSQC_Init + + void _CSQC_Shutdown(); + void CSQC_Shutdown() { if (_CSQC_Shutdown) _CSQC_Shutdown(); } + #define CSQC_Shutdown _CSQC_Shutdown + + void _CSQC_UpdateView(entity this, float w, float h); + void CSQC_UpdateView(float w, float h) { ENGINE_EVENT(); if (_CSQC_UpdateView) _CSQC_UpdateView(this, w, h); } + #define CSQC_UpdateView _CSQC_UpdateView + + bool _CSQC_InputEvent(int inputType, float nPrimary, float nSecondary); + bool CSQC_InputEvent(int inputType, float nPrimary, float nSecondary) + { + return _CSQC_InputEvent ? _CSQC_InputEvent(inputType, nPrimary, nSecondary) : false; + } + #define CSQC_InputEvent _CSQC_InputEvent + + bool _CSQC_ConsoleCommand(string s); + bool CSQC_ConsoleCommand(string s) { return _CSQC_ConsoleCommand ? _CSQC_ConsoleCommand(s) : false; } + #define CSQC_ConsoleCommand _CSQC_ConsoleCommand + + void _CSQC_Ent_Update(entity this, bool isNew); + void CSQC_Ent_Update(bool isNew) { ENGINE_EVENT(); if (_CSQC_Ent_Update) _CSQC_Ent_Update(this, isNew); } + #define CSQC_Ent_Update _CSQC_Ent_Update + + void _CSQC_Ent_Remove(entity this); + void CSQC_Ent_Remove() { ENGINE_EVENT(); if (_CSQC_Ent_Remove) _CSQC_Ent_Remove(this); } + #define CSQC_Ent_Remove _CSQC_Ent_Remove +#endif +#undef ENGINE_EVENT + +#if XONOTIC +#ifdef GAMEQC + #include +#endif +#endif diff --git a/qcsrc/lib/angle.qh b/qcsrc/lib/angle.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/angle.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/arraylist.qh b/qcsrc/lib/arraylist.qh index c0809ecdf8..18305177bf 100644 --- a/qcsrc/lib/arraylist.qh +++ b/qcsrc/lib/arraylist.qh @@ -20,7 +20,7 @@ USING(ArrayList, entity); MACRO_BEGIN \ { \ buf_del(this.al_buf); \ - remove(this); \ + delete(this); \ this = NULL; \ } MACRO_END diff --git a/qcsrc/lib/counting.qh b/qcsrc/lib/counting.qh index d12ae42501..e430a648d1 100644 --- a/qcsrc/lib/counting.qh +++ b/qcsrc/lib/counting.qh @@ -9,57 +9,57 @@ #define count_years_decs(time, decs) sprintf(CTX(_("CI_DEC^%s years")), ftos_decimals(time, decs)) #define count_years(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d years")), /* zeroth */ \ - CTX(_("CI_FIR^%d year")), /* first */ \ - CTX(_("CI_SEC^%d years")), /* year */ \ - CTX(_("CI_THI^%d years")), /* third */ \ - CTX(_("CI_MUL^%d years"))) /* multi */ + _("CI_ZER^%d years"), /* zeroth */ \ + _("CI_FIR^%d year"), /* first */ \ + _("CI_SEC^%d years"), /* year */ \ + _("CI_THI^%d years"), /* third */ \ + _("CI_MUL^%d years")) /* multi */ #define count_weeks_decs(time, decs) sprintf(CTX(_("CI_DEC^%s weeks")), ftos_decimals(time, decs)) #define count_weeks(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d weeks")), /* zeroth */ \ - CTX(_("CI_FIR^%d week")), /* first */ \ - CTX(_("CI_SEC^%d weeks")), /* week */ \ - CTX(_("CI_THI^%d weeks")), /* third */ \ - CTX(_("CI_MUL^%d weeks"))) /* multi */ + _("CI_ZER^%d weeks"), /* zeroth */ \ + _("CI_FIR^%d week"), /* first */ \ + _("CI_SEC^%d weeks"), /* week */ \ + _("CI_THI^%d weeks"), /* third */ \ + _("CI_MUL^%d weeks")) /* multi */ #define count_days_decs(time, decs) sprintf(CTX(_("CI_DEC^%s days")), ftos_decimals(time, decs)) #define count_days(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d days")), /* zeroth */ \ - CTX(_("CI_FIR^%d day")), /* first */ \ - CTX(_("CI_SEC^%d days")), /* day */ \ - CTX(_("CI_THI^%d days")), /* third */ \ - CTX(_("CI_MUL^%d days"))) /* multi */ + _("CI_ZER^%d days"), /* zeroth */ \ + _("CI_FIR^%d day"), /* first */ \ + _("CI_SEC^%d days"), /* day */ \ + _("CI_THI^%d days"), /* third */ \ + _("CI_MUL^%d days")) /* multi */ #define count_hours_decs(time, decs) sprintf(CTX(_("CI_DEC^%s hours")), ftos_decimals(time, decs)) #define count_hours(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d hours")), /* zeroth */ \ - CTX(_("CI_FIR^%d hour")), /* first */ \ - CTX(_("CI_SEC^%d hours")), /* hour */ \ - CTX(_("CI_THI^%d hours")), /* third */ \ - CTX(_("CI_MUL^%d hours"))) /* multi */ + _("CI_ZER^%d hours"), /* zeroth */ \ + _("CI_FIR^%d hour"), /* first */ \ + _("CI_SEC^%d hours"), /* hour */ \ + _("CI_THI^%d hours"), /* third */ \ + _("CI_MUL^%d hours")) /* multi */ #define count_minutes_decs(time, decs) sprintf(CTX(_("CI_DEC^%s minutes")), ftos_decimals(time, decs)) #define count_minutes(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d minutes")), /* zeroth */ \ - CTX(_("CI_FIR^%d minute")), /* first */ \ - CTX(_("CI_SEC^%d minutes")), /* minute */ \ - CTX(_("CI_THI^%d minutes")), /* third */ \ - CTX(_("CI_MUL^%d minutes"))) /* multi */ + _("CI_ZER^%d minutes"), /* zeroth */ \ + _("CI_FIR^%d minute"), /* first */ \ + _("CI_SEC^%d minutes"), /* minute */ \ + _("CI_THI^%d minutes"), /* third */ \ + _("CI_MUL^%d minutes")) /* multi */ #define count_seconds_decs(time, decs) sprintf(CTX(_("CI_DEC^%s seconds")), ftos_decimals(time, decs)) #define count_seconds(time) \ count_fill(time, \ - CTX(_("CI_ZER^%d seconds")), /* zeroth */ \ - CTX(_("CI_FIR^%d second")), /* first */ \ - CTX(_("CI_SEC^%d seconds")), /* second */ \ - CTX(_("CI_THI^%d seconds")), /* third */ \ - CTX(_("CI_MUL^%d seconds"))) /* multi */ + _("CI_ZER^%d seconds"), /* zeroth */ \ + _("CI_FIR^%d second"), /* first */ \ + _("CI_SEC^%d seconds"), /* second */ \ + _("CI_THI^%d seconds"), /* third */ \ + _("CI_MUL^%d seconds")) /* multi */ string count_ordinal(int interval) { @@ -102,16 +102,16 @@ string count_fill(float interval, string zeroth, string first, string second, st switch (floor(interval)) { - case 0: return sprintf(zeroth, interval); + case 0: return sprintf(CTX(zeroth), interval); case 1: { if (interval == 1) // EXACTLY value of 1 - return sprintf(first, interval); - else return sprintf(multi, interval); + return sprintf(CTX(first), interval); + else return sprintf(CTX(multi), interval); } - case 2: return sprintf(second, interval); - case 3: return sprintf(third, interval); - default: return sprintf(multi, interval); + case 2: return sprintf(CTX(second), interval); + case 3: return sprintf(CTX(third), interval); + default: return sprintf(CTX(multi), interval); } return ""; } diff --git a/qcsrc/lib/csqcmodel/_mod.inc b/qcsrc/lib/csqcmodel/_mod.inc index a2012c8fa2..cd9569e501 100644 --- a/qcsrc/lib/csqcmodel/_mod.inc +++ b/qcsrc/lib/csqcmodel/_mod.inc @@ -1,5 +1,13 @@ // generated file; do not modify -#include -#include #include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/lib/csqcmodel/_mod.qh b/qcsrc/lib/csqcmodel/_mod.qh index de3102fa85..1b05351928 100644 --- a/qcsrc/lib/csqcmodel/_mod.qh +++ b/qcsrc/lib/csqcmodel/_mod.qh @@ -1,5 +1,13 @@ // generated file; do not modify -#include -#include #include -#include +#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif +#include +#ifdef CSQC + #include +#endif diff --git a/qcsrc/lib/csqcmodel/cl_model.qc b/qcsrc/lib/csqcmodel/cl_model.qc index 789f115d14..e4e1d536aa 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qc +++ b/qcsrc/lib/csqcmodel/cl_model.qc @@ -194,6 +194,14 @@ void CSQCModel_Draw(entity this) CSQCModel_Hook_PreDraw(this, isplayer); + if(isplayer) + { + if(this.entnum == player_localentnum) + this.renderflags |= RF_EXTERNALMODEL; + else + this.renderflags &= ~RF_EXTERNALMODEL; + } + // inherit draw flags easily entity root = this; while(root.tag_entity) @@ -267,6 +275,9 @@ NET_HANDLE(ENT_CLIENT_MODEL, bool isnew) this.csqcmodel_teleported = 1; } + if(sf & BIT(14)) + viewloc_SetTags(this); + CSQCModel_InterpolateAnimation_Note(this, sf); InterpolateOrigin_Note(this); CSQCPlayer_PostUpdate(this); @@ -300,6 +311,6 @@ entity CSQCModel_server2csqc(int i) { if (i < maxclients) return CSQCModel_players[i]; ++i; - LOG_DEBUGF("player out of bounds: %d\n", i); + LOG_DEBUGF("player out of bounds: %d", i); return findfloat(NULL, entnum, i); } diff --git a/qcsrc/lib/csqcmodel/cl_model.qh b/qcsrc/lib/csqcmodel/cl_model.qh index 3b20972e25..b5d9f8bd36 100644 --- a/qcsrc/lib/csqcmodel/cl_model.qh +++ b/qcsrc/lib/csqcmodel/cl_model.qh @@ -19,8 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef LIB_CSQCMODEL_CL_MODEL_H -#define LIB_CSQCMODEL_CL_MODEL_H +#pragma once #include "common.qh" @@ -50,4 +49,3 @@ void CSQCModel_InterpolateAnimation_1To2_Note(entity this, int sf, float set_tim void CSQCModel_InterpolateAnimation_2To4_Do(entity this); void CSQCModel_InterpolateAnimation_1To2_Do(entity this); // will overwrite lerpfrac, lerpfrac3, lerpfrac4, and possibly clear frame*time if they are undisplayed according to lerpfracs -#endif diff --git a/qcsrc/lib/csqcmodel/cl_player.qc b/qcsrc/lib/csqcmodel/cl_player.qc index 0b0de75649..9e26e70fb7 100644 --- a/qcsrc/lib/csqcmodel/cl_player.qc +++ b/qcsrc/lib/csqcmodel/cl_player.qc @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -35,6 +36,7 @@ #include float autocvar_cl_movement_errorcompensation = 0; +bool autocvar_cl_movement_intermissionrunning = false; // engine stuff float pmove_onground; // weird engine flag we shouldn't really use but have to for now @@ -96,7 +98,7 @@ void CSQCPlayer_SetPredictionError(vector o, vector v, float onground_diff) void CSQCPlayer_Unpredict(entity this) { if (csqcplayer_status == CSQCPLAYERSTATUS_UNPREDICTED) return; - if (csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED) LOG_FATALF("Cannot unpredict in current status (%d)\n", csqcplayer_status); + if (csqcplayer_status != CSQCPLAYERSTATUS_PREDICTED) LOG_FATALF("Cannot unpredict in current status (%d)", csqcplayer_status); this.origin = csqcplayer_origin; this.velocity = csqcplayer_velocity; csqcplayer_moveframe = csqcplayer_sequence + 1; // + 1 because the recieved frame has the move already done (server side) @@ -105,17 +107,17 @@ void CSQCPlayer_Unpredict(entity this) void CSQCPlayer_SetMinsMaxs(entity this) { - if ((this.flags & FL_DUCKED) || !this.isplayermodel) + if (IS_DUCKED(this) || !this.isplayermodel) { - this.mins = STAT(PL_CROUCH_MIN, NULL); - this.maxs = STAT(PL_CROUCH_MAX, NULL); - this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, NULL); + this.mins = STAT(PL_CROUCH_MIN, this); + this.maxs = STAT(PL_CROUCH_MAX, this); + this.view_ofs = STAT(PL_CROUCH_VIEW_OFS, this); } else { - this.mins = STAT(PL_MIN, NULL); - this.maxs = STAT(PL_MAX, NULL); - this.view_ofs = STAT(PL_VIEW_OFS, NULL); + this.mins = STAT(PL_MIN, this); + this.maxs = STAT(PL_MAX, this); + this.view_ofs = STAT(PL_VIEW_OFS, this); } } @@ -129,72 +131,31 @@ void CSQCPlayer_SavePrediction(entity this) } void CSQC_ClientMovement_PlayerMove_Frame(entity this); -void _Movetype_Physics_ClientFrame(entity this, float movedt); - -void Movetype_Physics_Spam(entity this) // optimized -{ - _Movetype_Physics_ClientFrame(this, PHYS_INPUT_TIMELENGTH); - if(wasfreed(this)) - return; - - this.avelocity = this.move_avelocity; - this.velocity = this.move_velocity; - this.angles = this.move_angles; - this.flags = BITSET(this.flags, FL_ONGROUND, boolean(this.move_flags & FL_ONGROUND)); - this.flags = BITSET(this.flags, FL_WATERJUMP, boolean(this.move_flags & FL_WATERJUMP)); - this.waterlevel = this.move_waterlevel; - this.watertype = this.move_watertype; - setorigin(this, this.move_origin); -} - -void CSQCPlayer_CheckWater(entity this) -{ - this.move_origin = this.origin; - this.move_waterlevel = this.waterlevel; - this.move_watertype = this.watertype; - _Movetype_CheckWater(this); - this.waterlevel = this.move_waterlevel; - this.watertype = this.move_watertype; -} void CSQCPlayer_Physics(entity this) { - if(autocvar_cl_movement) - { - if(autocvar_cl_movement == 1) - CSQCPlayer_CheckWater(this); // we apparently need to check water *before* physics so it can use this for water jump + if(!autocvar_cl_movement) { return; } - vector oldv_angle = this.v_angle; - vector oldangles = this.angles; // we need to save these, as they're abused by other code - this.v_angle = PHYS_INPUT_ANGLES(this); - this.angles = PHYS_WORLD_ANGLES(this); + _Movetype_CheckWater(this); // we apparently need to check water *before* physics so it can use this for water jump - CSQC_ClientMovement_PlayerMove_Frame(this); + vector oldv_angle = this.v_angle; + vector oldangles = this.angles; // we need to save these, as they're abused by other code + this.v_angle = PHYS_INPUT_ANGLES(this); + this.angles = PHYS_WORLD_ANGLES(this); - if(autocvar_cl_movement == 1) - { - this.move_origin = this.origin; - this.move_angles = this.angles; - //this.move_movetype = MOVETYPE_WALK; // temp - this.move_velocity = this.velocity; - this.move_avelocity = this.avelocity; - this.move_flags = BITSET(this.move_flags, FL_ONGROUND, IS_ONGROUND(this)); - this.move_flags = BITSET(this.move_flags, FL_WATERJUMP, boolean(this.flags & FL_WATERJUMP)); - this.move_waterlevel = this.waterlevel; - this.move_watertype = this.watertype; - Movetype_Physics_Spam(this); - } + CSQC_ClientMovement_PlayerMove_Frame(this); - view_angles = this.v_angle; - input_angles = this.angles; - this.v_angle = oldv_angle; - this.angles = oldangles; + Movetype_Physics_NoMatchTicrate(this, PHYS_INPUT_TIMELENGTH, true); - this.pmove_flags = - ((this.flags & FL_DUCKED) ? PMF_DUCKED : 0) | - (!(this.flags & FL_JUMPRELEASED) ? PMF_JUMP_HELD : 0) | - ((IS_ONGROUND(this)) ? PMF_ONGROUND : 0); - } + view_angles = this.v_angle; + input_angles = this.angles; + this.v_angle = oldv_angle; + this.angles = oldangles; + + this.pmove_flags = + ((IS_DUCKED(this)) ? PMF_DUCKED : 0) | + ((IS_JUMP_HELD(this)) ? PMF_JUMP_HELD : 0) | + ((IS_ONGROUND(this)) ? PMF_ONGROUND : 0); } void CSQCPlayer_PredictTo(entity this, float endframe, bool apply_error) @@ -230,6 +191,15 @@ void CSQCPlayer_PredictTo(entity this, float endframe, bool apply_error) do { if (!getinputstate(csqcplayer_moveframe)) break; + /*if (input_timelength > 0.0005) + { + if (input_timelength > 0.05) + { + input_timelength /= 2; + CSQCPlayer_Physics(this); + } + CSQCPlayer_Physics(this); + }*/ CSQCPlayer_Physics(this); CSQCPlayer_SetMinsMaxs(this); ++csqcplayer_moveframe; @@ -254,10 +224,10 @@ void CSQCPlayer_SetViewLocation() /** Called once per CSQC_UpdateView() */ void CSQCPlayer_SetCamera() { - const vector v0 = pmove_vel; // TRICK: pmove_vel is set by the engine when we get here. No need to network velocity + 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 = STAT(VIEWHEIGHT); - const vector pl_viewofs = STAT(PL_VIEW_OFS, NULL); - const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS, NULL); + const vector pl_viewofs = STAT(PL_VIEW_OFS); + const vector pl_viewofs_crouch = STAT(PL_CROUCH_VIEW_OFS); const entity e = csqcplayer; if (e) { diff --git a/qcsrc/lib/csqcmodel/cl_player.qh b/qcsrc/lib/csqcmodel/cl_player.qh index 1c97199c8e..297e2e69d6 100644 --- a/qcsrc/lib/csqcmodel/cl_player.qh +++ b/qcsrc/lib/csqcmodel/cl_player.qh @@ -19,10 +19,9 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef LIB_CSQCMODEL_CL_PLAYER_H -#define LIB_CSQCMODEL_CL_PLAYER_H +#pragma once -int autocvar_cl_movement = 1; +bool autocvar_cl_movement = true; entity csqcplayer; float csqcplayer_status; @@ -42,4 +41,3 @@ void CSQCPlayer_SetCamera(); float CSQCPlayer_PreUpdate(entity this); float CSQCPlayer_PostUpdate(entity this); float CSQCPlayer_IsLocalPlayer(entity this); -#endif diff --git a/qcsrc/lib/csqcmodel/common.qh b/qcsrc/lib/csqcmodel/common.qh index a700f3af2b..37b88997ef 100644 --- a/qcsrc/lib/csqcmodel/common.qh +++ b/qcsrc/lib/csqcmodel/common.qh @@ -19,8 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef LIB_CSQCMODEL_COMMON_H -#define LIB_CSQCMODEL_COMMON_H +#pragma once #include @@ -94,4 +93,3 @@ const int CSQCMODEL_PROPERTY_SIZE = BIT(15); #else #define ALLPROPERTIES ALLPROPERTIES_COMMON #endif -#endif diff --git a/qcsrc/lib/csqcmodel/interpolate.qh b/qcsrc/lib/csqcmodel/interpolate.qh index 2f4828053b..3ea385cbcd 100644 --- a/qcsrc/lib/csqcmodel/interpolate.qh +++ b/qcsrc/lib/csqcmodel/interpolate.qh @@ -19,8 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef LIB_CSQCMODEL_INTERPOLATE_H -#define LIB_CSQCMODEL_INTERPOLATE_H +#pragma once .int iflags; const int IFLAG_VELOCITY = BIT(0); @@ -49,4 +48,3 @@ void InterpolateOrigin_Do(entity this); // in case we interpolate that: .vector v_angle; -#endif diff --git a/qcsrc/lib/csqcmodel/model.qc b/qcsrc/lib/csqcmodel/model.qc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qcsrc/lib/csqcmodel/model.qh b/qcsrc/lib/csqcmodel/model.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/csqcmodel/model.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/csqcmodel/player.qc b/qcsrc/lib/csqcmodel/player.qc new file mode 100644 index 0000000000..e69de29bb2 diff --git a/qcsrc/lib/csqcmodel/player.qh b/qcsrc/lib/csqcmodel/player.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/csqcmodel/player.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/csqcmodel/settings.qh b/qcsrc/lib/csqcmodel/settings.qh index 85b8a4fcbb..359cf1738f 100644 --- a/qcsrc/lib/csqcmodel/settings.qh +++ b/qcsrc/lib/csqcmodel/settings.qh @@ -1,5 +1,4 @@ -#ifndef LIB_CSQCMODEL_SETTINGS_H -#define LIB_CSQCMODEL_SETTINGS_H +#pragma once // define this if svqc code wants to use .frame2 and .lerpfrac //#define CSQCMODEL_HAVE_TWO_FRAMES @@ -24,4 +23,3 @@ //vector PL_CROUCH_MIN = ...; //vector PL_CROUCH_MAX = ...; //vector PL_CROUCH_VIEW_OFS = ...; -#endif diff --git a/qcsrc/lib/csqcmodel/sv_model.qc b/qcsrc/lib/csqcmodel/sv_model.qc index d495ae0154..dd8d5d33aa 100644 --- a/qcsrc/lib/csqcmodel/sv_model.qc +++ b/qcsrc/lib/csqcmodel/sv_model.qc @@ -24,6 +24,7 @@ #include "common.qh" #include #include +#include #include #include #include @@ -64,13 +65,9 @@ bool CSQCModel_Send(entity this, entity to, int sf) void CSQCModel_CheckUpdate(entity e) { // some nice flags for CSQCMODEL_IF - float isplayer = IS_CLIENT(e); - float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags - float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags - - unused_float = isplayer; - unused_float = islocalplayer; - unused_float = isnolocalplayer; + noref float isplayer = IS_CLIENT(e); + noref float islocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags + noref float isnolocalplayer = isplayer; // we set BOTH to 1 here as we need the sendflags #if CSQCPLAYER_FORCE_UPDATES if(isplayer && time > e.csqcmodel_nextforcedupdate) diff --git a/qcsrc/lib/csqcmodel/sv_model.qh b/qcsrc/lib/csqcmodel/sv_model.qh index 3e043d3532..e963f12a1b 100644 --- a/qcsrc/lib/csqcmodel/sv_model.qh +++ b/qcsrc/lib/csqcmodel/sv_model.qh @@ -19,8 +19,7 @@ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS * IN THE SOFTWARE. */ -#ifndef LIB_CSQCMODEL_SV_MODEL_H -#define LIB_CSQCMODEL_SV_MODEL_H +#pragma once #include "common.qh" @@ -41,4 +40,3 @@ void CSQCModel_UnlinkEntity(entity e); #undef CSQCMODEL_PROPERTY #undef CSQCMODEL_ENDIF #undef CSQCMODEL_IF -#endif diff --git a/qcsrc/lib/defer.qh b/qcsrc/lib/defer.qh index 3e7e020c67..4f34bb4853 100644 --- a/qcsrc/lib/defer.qh +++ b/qcsrc/lib/defer.qh @@ -1,6 +1,6 @@ #pragma once -#ifndef MENUQC +#ifdef GAMEQC #include "oo.qh" #include "self.qh" @@ -12,7 +12,7 @@ /** Remove entity */ void SUB_Remove(entity this) { - remove(this); + delete(this); } void defer_think(entity this) diff --git a/qcsrc/lib/draw.qh b/qcsrc/lib/draw.qh index 698c926237..fbb4a09b55 100644 --- a/qcsrc/lib/draw.qh +++ b/qcsrc/lib/draw.qh @@ -5,7 +5,7 @@ #include "i18n.qh" #include "vector.qh" - #include + float vid_conwidth; void Draw_CylindricLine(vector from, vector to, float thickness, string texture, float aspect, float shift, vector rgb, float theAlpha, float drawflag, vector vieworg) { diff --git a/qcsrc/lib/i18n.qh b/qcsrc/lib/i18n.qh index c66d7b9a88..8ebedb76f6 100644 --- a/qcsrc/lib/i18n.qh +++ b/qcsrc/lib/i18n.qh @@ -49,7 +49,7 @@ string CTX(string s) int p = strstrofs(s, "^", 0); string ret = (p < 0) ? s : substring(s, p + 1, -1); #if CTX_CACHE - LOG_DEBUGF("CTX(\"%s\")\n", s); + LOG_DEBUGF("CTX(\"%s\")", s); HM_sets(CTX_cache, s, ret); #endif return ret; diff --git a/qcsrc/lib/intrusivelist.qh b/qcsrc/lib/intrusivelist.qh new file mode 100644 index 0000000000..9d3ec54c8e --- /dev/null +++ b/qcsrc/lib/intrusivelist.qh @@ -0,0 +1,245 @@ +#pragma once + +#include "iter.qh" + +const int IL_MAX = 128; + +void IL_INIT(entity this); +void IL_DTOR(entity this); +void IL_ENDFRAME(); + +/** + * limitations: + * NULL cannot be present + * elements can only be present once + * a maximum of `IL_MAX` lists can exist at one time + * freed entities must be removed from the list + */ +CLASS(IntrusiveList, Object) + ATTRIB(IntrusiveList, il_head, entity); + ATTRIB(IntrusiveList, il_tail, entity); + ATTRIB(IntrusiveList, il_nextfld, .entity, nil); + ATTRIB(IntrusiveList, il_prevfld, .entity, nil); + INIT(IntrusiveList) { IL_INIT(this); } + DESTRUCTOR(IntrusiveList) { IL_DTOR(this); } +ENDCLASS(IntrusiveList) + +// bitflags +.vector il_lists; +// bitflags +.vector il_listmask; + +#define IL_NEW() NEW(IntrusiveList) + +#define IL_EMPTY(this) (this.il_head == NULL) + +#define IL_FIRST(this) (this.il_head) +#define IL_LAST(this) (this.il_tail) +#define IL_PEEK(this) (this.il_tail) + +bool IL_CONTAINS(IntrusiveList this, entity it) +{ + assert(this, return false); + return it.(this.il_nextfld) || this.il_head == it || this.il_tail == it; +} + +/** + * Push to tail + */ +entity IL_PUSH(IntrusiveList this, entity it) +{ + assert(this, return NULL); + assert(it, return NULL); + .entity il_next = this.il_nextfld; + .entity il_prev = this.il_prevfld; + assert(!IL_CONTAINS(this, it), return NULL); + + entity tail = it.(il_prev) = this.il_tail; + tail ? (tail.(il_next) = it) : this.il_head = it; + this.il_tail = it; + it.il_lists |= this.il_listmask; + return it; +} + +/** + * Push to head + */ +entity IL_UNSHIFT(IntrusiveList this, entity it) +{ + assert(this, return NULL); + assert(it, return NULL); + .entity il_next = this.il_nextfld; + .entity il_prev = this.il_prevfld; + assert(!IL_CONTAINS(this, it), return NULL); + + entity head = it.(il_next) = this.il_head; + head ? (head.(il_prev) = it) : this.il_tail = it; + this.il_head = it; + it.il_lists |= this.il_listmask; + return it; +} + +/** + * Pop from tail + */ +entity IL_POP(IntrusiveList this) +{ + assert(this, return NULL); + .entity il_next = this.il_nextfld; + .entity il_prev = this.il_prevfld; + + if (!this.il_tail) return NULL; + entity it = this.il_tail; + entity prev = it.(il_prev); + if (prev) (this.il_tail = prev).(il_next) = NULL; + else this.il_head = this.il_tail = NULL; + return it; +} + +/** + * Pop from head + */ +entity IL_SHIFT(IntrusiveList this) +{ + assert(this, return NULL); + .entity il_next = this.il_nextfld; + .entity il_prev = this.il_prevfld; + + if (!this.il_head) return NULL; + entity it = this.il_head; + entity next = it.(il_next); + if (next) (this.il_head = next).(il_prev) = NULL; + else this.il_head = this.il_tail = NULL; + return it; +} + +/** + * Remove any element, anywhere in the list + */ +void IL_REMOVE(IntrusiveList this, entity it) +{ + assert(this, return); + .entity il_next = this.il_nextfld; + .entity il_prev = this.il_prevfld; + entity next = it.(il_next); + entity prev = it.(il_prev); + entity ohead = this.il_head; + entity otail = this.il_tail; + next ? next.(il_prev) = prev : this.il_tail = prev; + prev ? prev.(il_next) = next : this.il_head = next; + LOG_DEBUGF("remove %i (%i :: %i), head: %i -> %i, tail: %i -> %i", it, it.(il_prev), it.(il_next), ohead, this.il_head, otail, this.il_tail); + it.(il_next) = it.(il_prev) = NULL; +} + +/** + * Remove all elements + */ +#define IL_CLEAR(this) \ + MACRO_BEGIN \ + { \ + IntrusiveList __il = this; \ + assert(__il); \ + .entity il_prev = __il.il_prevfld; \ + IL_EACH(__il, true, it.(il_next) = it.(il_prev) = NULL); \ + __il.il_head = __il.il_tail = NULL; \ + } MACRO_END + +/** + * Delete the list + */ +#define IL_DELETE(this) \ + MACRO_BEGIN \ + { \ + delete(this); \ + this = NULL; \ + } MACRO_END + +#define IL_EACH(this, cond, body) \ + MACRO_BEGIN \ + { \ + IntrusiveList _il = this; \ + assert(_il); \ + .entity il_next = _il.il_nextfld; \ + noref int i = 0; \ + for (entity _next, _it = _il.il_head; _it; (_it = _next, ++i)) \ + { \ + const noref entity it = _it; \ + _next = it.(il_next); \ + if (cond) { LAMBDA(body) } \ + } \ + } MACRO_END + +.int il_id; +IntrusiveList il_links[IL_MAX]; +.entity il_links_flds[IL_MAX * 2]; +int il_links_ptr; + +#define IL_FLOOR(n) ((n) | 0) +#define IL_CEIL(n) IL_FLOOR((n) + 0.5) + +#define IL_LISTS_PER_BIT IL_CEIL(IL_MAX / (3 * 24)) + +void IL_INIT(IntrusiveList this) +{ + .entity nextfld, prevfld; + for (int i = il_links_ptr; i < il_links_ptr + IL_MAX; ++i) { + int idx = i; + if (idx >= IL_MAX) idx -= IL_MAX; + int id = idx; + idx *= 2; + if (!il_links[idx]) { + il_links[idx] = this; + nextfld = il_links_flds[idx + 0]; + prevfld = il_links_flds[idx + 1]; + this.il_id = id; + int bit = IL_FLOOR(id / IL_LISTS_PER_BIT); + if (bit < (1 * 24)) this.il_listmask = '1 0 0' * (1 << (bit - (0 * 24))); + else if (bit < (2 * 24)) this.il_listmask = '0 1 0' * (1 << (bit - (1 * 24))); + else if (bit < (3 * 24)) this.il_listmask = '0 0 1' * (1 << (bit - (2 * 24))); + else assert(false); + il_links_ptr = id + 1; + if (il_links_ptr >= IL_MAX) il_links_ptr -= IL_MAX; + this.il_nextfld = nextfld; + this.il_prevfld = prevfld; + return; + } + } + LOG_WARNF("IntrusiveList overflow"); +} + +void IL_DTOR(IntrusiveList this) +{ + IL_CLEAR(this); + il_links[this.il_id] = NULL; +} + +void IL_ENDFRAME() +{ +#if 0 + // incompatible with CSQC, remove() clears entities + for (int i = 0; i < IL_MAX; ++i) { + IntrusiveList list = il_links[i]; + if (list) { + .entity nextfld = list.il_nextfld; + for (entity next, it = list.il_head; it; it = next) { + next = it.(nextfld); + if (wasfreed(it)) { + IL_REMOVE(list, it); + } + } + } + } +#endif +} + +void ONREMOVE(entity this) +{ + if (this.il_lists) { + for (int i = 0; i < IL_MAX; ++i) { + IntrusiveList list = il_links[i]; + if (this.il_lists & list.il_listmask && IL_CONTAINS(list, this)) { + IL_REMOVE(list, this); + } + } + } +} diff --git a/qcsrc/lib/iter.qh b/qcsrc/lib/iter.qh index c21d021213..e3cf7410fb 100644 --- a/qcsrc/lib/iter.qh +++ b/qcsrc/lib/iter.qh @@ -23,10 +23,11 @@ MACRO_BEGIN \ { \ int _i = 0; \ - for (entity _it = list##_first; _it; (_it = _it.next, ++_i)) \ + for (entity _it = list##_first, _next = NULL; _it; (_it = _next, ++_i)) \ { \ const noref int i = _i; \ ITER_CONST noref entity it = _it; \ + _next = _it.next; \ if (cond) { LAMBDA(body) } \ } \ } MACRO_END @@ -122,13 +123,19 @@ if (cond) LAMBDA(body) \ } \ } MACRO_END +#define MUTEX_LOCK(this) MACRO_BEGIN \ + if (this) LOG_SEVEREF("Loop mutex held by %s", this); \ + this = __FUNC__; \ +MACRO_END +#define MUTEX_UNLOCK(this) MACRO_BEGIN \ + this = string_null; \ +MACRO_END #define _FOREACH_ENTITY_FIND_UNORDERED(id, T, fld, match, cond, body) \ MACRO_BEGIN { \ - if (_FOREACH_ENTITY_FIND_##T##_##id##mutex) LOG_SEVEREF("Loop mutex held by %s", _FOREACH_ENTITY_FIND_##T##_##id##mutex); \ - _FOREACH_ENTITY_FIND_##T##_##id##mutex = __FUNC__; \ + MUTEX_LOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \ entity _foundchain_first = _findchain##T##_tofield(fld, match, _FOREACH_ENTITY_FIND_##T##_next##id); \ FOREACH_LIST(_foundchain, _FOREACH_ENTITY_FIND_##T##_next##id, cond, body); \ - _FOREACH_ENTITY_FIND_##T##_##id##mutex = string_null; \ + MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_##T##_##id##mutex); \ } MACRO_END #define FOREACH_ENTITY(cond, body) ORDERED(FOREACH_ENTITY)(cond, body) @@ -152,11 +159,20 @@ .entity _FOREACH_ENTITY_FIND_flags_next; noref string _FOREACH_ENTITY_FIND_flags_mutex; #define FOREACH_ENTITY_FLAGS_UNORDERED(fld, match, body) _FOREACH_ENTITY_FIND_UNORDERED(, flags, fld, match, true, body) -#ifndef MENUQC +#ifdef GAMEQC entity(vector org, float rad, .entity tofield) _findchainradius_tofield = #22; -#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body) +#define FOREACH_ENTITY_RADIUS(org, dist, cond, body) ORDERED(FOREACH_ENTITY_RADIUS)(org, dist, cond, body) .entity _FOREACH_ENTITY_FIND_radius_next; noref string _FOREACH_ENTITY_FIND_radius_mutex; #define FOREACH_ENTITY_RADIUS_UNORDERED(org, dist, cond, body) _FOREACH_ENTITY_FIND_UNORDERED(, radius, org, dist, cond, body) +.entity _FOREACH_ENTITY_FIND_radius_nexttmp; noref string _FOREACH_ENTITY_FIND_radius_tmpmutex; +#define FOREACH_ENTITY_RADIUS_ORDERED(org, dist, cond, body) \ +MACRO_BEGIN \ + entity _rev_first = NULL; \ + _FOREACH_ENTITY_FIND_UNORDERED(tmp, radius, org, dist, cond, (it._FOREACH_ENTITY_FIND_radius_nexttmp = _rev_first, _rev_first = it)); \ + MUTEX_LOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \ + FOREACH_LIST(_rev, _FOREACH_ENTITY_FIND_radius_nexttmp, true, body); \ + MUTEX_UNLOCK(_FOREACH_ENTITY_FIND_radius_tmpmutex); \ +MACRO_END #endif #define FOREACH_ENTITY_FLOAT(fld, match, body) ORDERED(FOREACH_ENTITY_FLOAT)(fld, match, body) diff --git a/qcsrc/lib/json.qh b/qcsrc/lib/json.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/lib/json.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/lib/lazy.qh b/qcsrc/lib/lazy.qh index ad30115a2e..62d04b32e9 100644 --- a/qcsrc/lib/lazy.qh +++ b/qcsrc/lib/lazy.qh @@ -3,7 +3,7 @@ #include "oo.qh" CLASS(Lazy, Object) - ATTRIB(Lazy, m_get, entity(), func_null); + ATTRIB(Lazy, m_get, entity()); CONSTRUCTOR(Lazy, entity() _compute) { this.m_get = _compute; diff --git a/qcsrc/lib/linkedlist.qh b/qcsrc/lib/linkedlist.qh index 6afc862ed0..0dd430722f 100644 --- a/qcsrc/lib/linkedlist.qh +++ b/qcsrc/lib/linkedlist.qh @@ -1,14 +1,14 @@ #pragma once CLASS(LinkedListNode, Object) - ATTRIB(LinkedListNode, ll_data, entity, NULL) - ATTRIB(LinkedListNode, ll_prev, LinkedListNode, NULL) - ATTRIB(LinkedListNode, ll_next, LinkedListNode, NULL) + ATTRIB(LinkedListNode, ll_data, entity); + ATTRIB(LinkedListNode, ll_prev, LinkedListNode); + ATTRIB(LinkedListNode, ll_next, LinkedListNode); ENDCLASS(LinkedListNode) CLASS(LinkedList, Object) - ATTRIB(LinkedList, ll_head, LinkedListNode, NULL); - ATTRIB(LinkedList, ll_tail, LinkedListNode, NULL); + ATTRIB(LinkedList, ll_head, LinkedListNode); + ATTRIB(LinkedList, ll_tail, LinkedListNode); ENDCLASS(LinkedList) #define LL_NEW() NEW(LinkedList) @@ -40,7 +40,7 @@ entity LL_POP(LinkedList this) LinkedListNode prev = n.ll_prev; if (prev) (this.ll_tail = prev).ll_next = NULL; else this.ll_head = this.ll_tail = NULL; - remove(n); + delete(n); return e; } @@ -57,7 +57,7 @@ entity LL_POP(LinkedList this) entity it = LL_POP(_ll); \ if (!it) continue; \ dtor \ - remove(it); \ + delete(it); \ } \ } MACRO_END @@ -68,7 +68,7 @@ entity LL_POP(LinkedList this) MACRO_BEGIN \ { \ LL_CLEAR_2(this, dtor); \ - remove(this); \ + delete(this); \ this = NULL; \ } MACRO_END diff --git a/qcsrc/lib/log.qh b/qcsrc/lib/log.qh index 370d96be56..7f5bc72041 100644 --- a/qcsrc/lib/log.qh +++ b/qcsrc/lib/log.qh @@ -33,46 +33,50 @@ #if defined(MENUQC) -string(string...) strcat0n = #53; +string(string, string...) strcat1n = #53; #else -string(string...) strcat0n = #115; +string(string, string...) strcat1n = #115; #endif -#define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9"__FILE__"^7" ":" "^9"STR(__LINE__)"^7" ")", __FUNC__)) +// would be nice if __FUNC__ could be concatenated at compile time +#if 0 + // less work, bigger binary + #define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9"__FILE__"^7" ":" "^9"STR(__LINE__)"^7" ")", __FUNC__)) +#else + #define __SOURCELOC__ (sprintf("^7%s^9" "(" "^9%s^7" ":" "^9%s^7" ")", __FUNC__, __FILE__, STR(__LINE__))) +#endif +#define _LOG_HEADER(level) "^9[::" "^7"PROGNAME"^9" "::" level"^9" "] ", __SOURCELOC__ #define _LOG(f, level, s) \ MACRO_BEGIN { \ - f(sprintf("^9[::" "^7"PROGNAME"^9" "::" level"^9" "] %s\n^7%s\n", __SOURCELOC__, s)); \ + f(strcat1n(_LOG_HEADER(level), "\n^7", s, "\n")); \ } MACRO_END -#define LOG_FATAL(...) _LOG_FATAL(strcat0n(__VA_ARGS__)) +#define LOG_FATAL(...) _LOG_FATAL(strcat1n(__VA_ARGS__)) #define LOG_FATALF(...) _LOG_FATAL(sprintf(__VA_ARGS__)) #define _LOG_FATAL(s) _LOG(error, "^1FATAL", s) -#define LOG_SEVERE(...) _LOG_SEVERE(strcat0n(__VA_ARGS__)) +#define LOG_SEVERE(...) _LOG_SEVERE(strcat1n(__VA_ARGS__)) #define LOG_SEVEREF(...) _LOG_SEVERE(sprintf(__VA_ARGS__)) #define _LOG_SEVERE(s) _LOG(backtrace, "^1SEVERE", s) -#define LOG_WARNING(...) _LOG_WARNING(strcat0n(__VA_ARGS__)) -#define LOG_WARNINGF(...) _LOG_WARNING(sprintf(__VA_ARGS__)) -#define _LOG_WARNING(s) _LOG(print, "^3WARNING", s) +#define LOG_WARN(...) _LOG_WARN(strcat1n(__VA_ARGS__)) +#define LOG_WARNF(...) _LOG_WARN(sprintf(__VA_ARGS__)) +#define _LOG_WARN(s) _LOG(print, "^3WARNING", s) -#define LOG_INFO(...) _LOG_INFO(strcat0n(__VA_ARGS__)) +#define LOG_INFO(...) _LOG_INFO(strcat1n(__VA_ARGS__)) #define LOG_INFOF(...) _LOG_INFO(sprintf(__VA_ARGS__)) #define _LOG_INFO(s) \ MACRO_BEGIN { \ - string ___s = s; \ - if (autocvar_developer) \ - _LOG(print, "^5INFO", ___s); \ - else \ - print(___s); \ + dprint(_LOG_HEADER("^5INFO")); \ + print("\n^7", s); \ } MACRO_END -#define LOG_TRACE(...) _LOG_TRACE(strcat0n(__VA_ARGS__)) +#define LOG_TRACE(...) _LOG_TRACE(strcat1n(__VA_ARGS__)) #define LOG_TRACEF(...) _LOG_TRACE(sprintf(__VA_ARGS__)) #define _LOG_TRACE(s) _LOG(dprint, "^6TRACE", s) -#define LOG_DEBUG(...) _LOG_DEBUG(strcat0n(__VA_ARGS__)) +#define LOG_DEBUG(...) _LOG_DEBUG(strcat1n(__VA_ARGS__)) #define LOG_DEBUGF(...) _LOG_DEBUG(sprintf(__VA_ARGS__)) #define _LOG_DEBUG(s) _LOG(dprint2, "^2DEBUG", s) @@ -82,13 +86,7 @@ string(string...) strcat0n = #115; } MACRO_END // TODO: this sucks, lets find a better way to do backtraces? -#ifdef SVQC - void builtin_remove(entity); - #define _backtrace() builtin_remove(NULL) -#else - void remove(entity); - #define _backtrace() remove(NULL) -#endif +#define _backtrace() builtin_remove(NULL) noref int autocvar_developer; noref bool autocvar_prvm_backtraceforwarnings; diff --git a/qcsrc/lib/macro.qh b/qcsrc/lib/macro.qh index dff3710a5f..1541b9997c 100644 --- a/qcsrc/lib/macro.qh +++ b/qcsrc/lib/macro.qh @@ -14,3 +14,6 @@ #define _STR(it) #it #define STR(it) _STR(it) + +#define EMPTY() +#define DEFER(id) id EMPTY() diff --git a/qcsrc/lib/map.qh b/qcsrc/lib/map.qh index 57b8f8c679..55da4d63f0 100644 --- a/qcsrc/lib/map.qh +++ b/qcsrc/lib/map.qh @@ -9,7 +9,7 @@ void db_save(int db, string filename) int fh = fopen(filename, FILE_WRITE); if (fh < 0) { - LOG_WARNINGF("^1Can't write DB to %s\n", filename); + LOG_WARNF("^1Can't write DB to %s", filename); return; } fputs(fh, strcat(ftos(DB_BUCKETS), "\n")); @@ -64,7 +64,7 @@ int db_load(string filename) void db_dump(int db, string filename) { int fh = fopen(filename, FILE_WRITE); - if (fh < 0) LOG_FATALF("Can't dump DB to %s\n"); + if (fh < 0) LOG_FATALF("Can't dump DB to %s"); fputs(fh, "0\n"); for (int i = 0, n = buf_getsize(db); i < n; ++i) { diff --git a/qcsrc/lib/math.qh b/qcsrc/lib/math.qh index feb7a82474..92d45bf142 100644 --- a/qcsrc/lib/math.qh +++ b/qcsrc/lib/math.qh @@ -82,12 +82,12 @@ vector bezier_quadratic_getderivative(vector a, vector b, vector c, float t) + (b - a) * 2; } -float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float x) +float cubic_speedfunc(float startspeedfactor, float endspeedfactor, float spd) { return (((startspeedfactor + endspeedfactor - 2 - ) * x - 2 * startspeedfactor - endspeedfactor + 3 - ) * x + startspeedfactor - ) * x; + ) * spd - 2 * startspeedfactor - endspeedfactor + 3 + ) * spd + startspeedfactor + ) * spd; } bool cubic_speedfunc_is_sane(float startspeedfactor, float endspeedfactor) @@ -195,54 +195,54 @@ float power2of(float e) return pow(2, e); } -float log2of(float x) +float log2of(float e) { // NOTE: generated code - if (x > 2048) - if (x > 131072) - if (x > 1048576) - if (x > 4194304) return 23; + if (e > 2048) + if (e > 131072) + if (e > 1048576) + if (e > 4194304) return 23; else - if (x > 2097152) return 22; + if (e > 2097152) return 22; else return 21; else - if (x > 524288) return 20; + if (e > 524288) return 20; else - if (x > 262144) return 19; + if (e > 262144) return 19; else return 18; else - if (x > 16384) - if (x > 65536) return 17; + if (e > 16384) + if (e > 65536) return 17; else - if (x > 32768) return 16; + if (e > 32768) return 16; else return 15; else - if (x > 8192) return 14; + if (e > 8192) return 14; else - if (x > 4096) return 13; + if (e > 4096) return 13; else return 12; else - if (x > 32) - if (x > 256) - if (x > 1024) return 11; + if (e > 32) + if (e > 256) + if (e > 1024) return 11; else - if (x > 512) return 10; + if (e > 512) return 10; else return 9; else - if (x > 128) return 8; + if (e > 128) return 8; else - if (x > 64) return 7; + if (e > 64) return 7; else return 6; else - if (x > 4) - if (x > 16) return 5; + if (e > 4) + if (e > 16) return 5; else - if (x > 8) return 4; + if (e > 8) return 4; else return 3; else - if (x > 2) return 2; + if (e > 2) return 2; else - if (x > 1) return 1; + if (e > 1) return 1; else return 0; } diff --git a/qcsrc/lib/matrix/_mod.inc b/qcsrc/lib/matrix/_mod.inc index 4854f092a0..d68dc7d968 100644 --- a/qcsrc/lib/matrix/_mod.inc +++ b/qcsrc/lib/matrix/_mod.inc @@ -1,3 +1,5 @@ // generated file; do not modify -#include +#if XONOTIC + #include +#endif #include diff --git a/qcsrc/lib/matrix/_mod.qh b/qcsrc/lib/matrix/_mod.qh index 9658122972..c351afe8f7 100644 --- a/qcsrc/lib/matrix/_mod.qh +++ b/qcsrc/lib/matrix/_mod.qh @@ -1,3 +1,5 @@ // generated file; do not modify -#include +#if XONOTIC + #include +#endif #include diff --git a/qcsrc/lib/matrix/command.qc b/qcsrc/lib/matrix/command.qc index 27c7ec959c..449aa373b3 100644 --- a/qcsrc/lib/matrix/command.qc +++ b/qcsrc/lib/matrix/command.qc @@ -1,6 +1,6 @@ #include "command.qh" -#include +#include GENERIC_COMMAND(mx, "Send a matrix command") { switch (argv(1)) { diff --git a/qcsrc/lib/matrix/matrix.qc b/qcsrc/lib/matrix/matrix.qc index 23ec02e823..c399c2aa6c 100644 --- a/qcsrc/lib/matrix/matrix.qc +++ b/qcsrc/lib/matrix/matrix.qc @@ -22,7 +22,7 @@ void MX_Nick_(entity fh, entity pass, int status) fh.url_verb = "PUT"; fh.url_content_type = "application/json"; url_fputs(fh, sprintf("{\"displayname\": \"%s\"}", pass.message)); - remove(pass); + delete(pass); url_fclose(fh); break; } @@ -46,7 +46,7 @@ void MX_Messages_(entity fh, entity pass, int status) { switch (status) { default: { - LOG_WARNINGF("status: %d", status); + LOG_WARNF("status: %d", status); break; } case URL_READY_CLOSED: break; @@ -90,7 +90,7 @@ void MX_Sync_(entity fh, entity pass, int status) { switch (status) { default: { - LOG_WARNINGF("status: %d", status); + LOG_WARNF("status: %d", status); break; } case URL_READY_CLOSED: break; @@ -175,7 +175,7 @@ void MX_Typing_(entity fh, entity pass, int status) fh.url_verb = "PUT"; fh.url_content_type = "application/json"; url_fputs(fh, sprintf("{\"typing\": %s, \"timeout\": 30000}", pass.message)); - remove(pass); + delete(pass); url_fclose(fh); break; } @@ -204,7 +204,7 @@ 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); remove(pass); + strunzone(pass.message); delete(pass); url_fclose(fh); break; } diff --git a/qcsrc/lib/misc.qh b/qcsrc/lib/misc.qh index 950000f047..21e0c5239f 100644 --- a/qcsrc/lib/misc.qh +++ b/qcsrc/lib/misc.qh @@ -1,9 +1,6 @@ #pragma once #ifdef __STDC__ - #define EMPTY() - #define DEFER(id) id EMPTY() - #define EVAL(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__))) #define EVAL1(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__))) #define EVAL2(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__))) diff --git a/qcsrc/lib/net.qh b/qcsrc/lib/net.qh index 4e09d4b010..56c80e02d1 100644 --- a/qcsrc/lib/net.qh +++ b/qcsrc/lib/net.qh @@ -98,6 +98,9 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } .int Version; // deprecated, use SendFlags .int SendFlags; + IntrusiveList g_uncustomizables; + STATIC_INIT(g_uncustomizables) { g_uncustomizables = IL_NEW(); } + void Net_LinkEntity(entity e, bool docull, float dt, bool(entity this, entity to, int sendflags) sendfunc) { if (e.classname == "") e.classname = "net_linked"; @@ -130,16 +133,18 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } .void(entity this) uncustomizeentityforclient; .float uncustomizeentityforclient_set; - void SetCustomizer(entity e, bool(entity this) customizer, void(entity this) uncustomizer) + void SetCustomizer(entity e, bool(entity this, entity client) customizer, void(entity this) uncustomizer) { setcefc(e, customizer); e.uncustomizeentityforclient = uncustomizer; e.uncustomizeentityforclient_set = !!uncustomizer; + if(uncustomizer) + IL_PUSH(g_uncustomizables, e); } void UncustomizeEntitiesRun() { - FOREACH_ENTITY_FLOAT(uncustomizeentityforclient_set, true, it.uncustomizeentityforclient(it)); + IL_EACH(g_uncustomizables, it.uncustomizeentityforclient_set, it.uncustomizeentityforclient(it)); } STRING_ITERATOR(g_buf, string_null, 0); @@ -156,13 +161,13 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } { entity reader = C2S_Protocol_from(C2S); if (reader && reader.m_read && reader.m_read(NULL, sender, true)) continue; - LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d\n", C2S); + LOG_SEVEREF("Net_ClientCommand() with malformed C2S=%d", C2S); return; } g_buf_i--; int expected = strlen(buf); - if (g_buf_i > expected) LOG_WARNINGF("Underflow: %d", g_buf_i - expected); - if (g_buf_i < expected) LOG_WARNINGF("Overrflow: %d", expected - g_buf_i); + if (g_buf_i > expected) LOG_WARNF("Underflow: %d", g_buf_i - expected); + if (g_buf_i < expected) LOG_WARNF("Overrflow: %d", expected - g_buf_i); } #endif @@ -176,7 +181,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } } MACRO_END #define Net_Reject() \ MACRO_BEGIN { \ - if (this) remove(this); \ + if (this) delete(this); \ } MACRO_END string g_buf; @@ -203,6 +208,54 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } } MACRO_END #endif +// serialization: new style + +USING(Stream, int); +#if defined(SVQC) + #define stream_reading(stream) false + #define stream_writing(stream) true +#elif defined(CSQC) + #define stream_reading(stream) true + #define stream_writing(stream) false +#endif + +#define serialize(T, stream, ...) serialize_##T(stream, __VA_ARGS__) + +#if defined(SVQC) + #define serialize_byte(stream, this) \ + MACRO_BEGIN \ + WriteByte(stream, this); \ + MACRO_END +#elif defined(CSQC) + #define serialize_byte(stream, this) \ + MACRO_BEGIN \ + this = ReadByte(); \ + MACRO_END +#endif + +#if defined(SVQC) + #define serialize_float(stream, this) \ + MACRO_BEGIN \ + WriteCoord(stream, this); \ + MACRO_END +#elif defined(CSQC) + #define serialize_float(stream, this) \ + MACRO_BEGIN \ + this = ReadCoord(); \ + MACRO_END +#endif + +#define serialize_vector(stream, this) \ + MACRO_BEGIN \ + vector _v = this; \ + serialize_float(stream, _v.x); \ + serialize_float(stream, _v.y); \ + serialize_float(stream, _v.z); \ + this = _v; \ + MACRO_END + +// serialization: old + #define ReadRegistered(r) r##_from(Read_byte()) #define WriteRegistered(r, to, it) Write_byte(to, it.m_id) @@ -239,12 +292,13 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } #define Read_string() ReadString() #define Write_string(to, f) WriteString(to, f) -#ifndef MENUQC +#ifdef GAMEQC const float APPROXPASTTIME_ACCURACY_REQUIREMENT = 0.05; #define APPROXPASTTIME_MAX (16384 * APPROXPASTTIME_ACCURACY_REQUIREMENT) #define APPROXPASTTIME_RANGE (64 * APPROXPASTTIME_ACCURACY_REQUIREMENT) #ifdef CSQC + float servertime; entity ReadCSQCEntity() { int f = ReadShort(); @@ -260,7 +314,7 @@ STATIC_INIT(C2S_Protocol_renumber) { FOREACH(C2S_Protocol, true, it.m_id = i); } #define ReadInt48_t() vec3(ReadInt24_t(), ReadInt24_t(), 0) #define ReadInt72_t() vec3(ReadInt24_t(), ReadInt24_t(), ReadInt24_t()) - int _ReadSByte; + noref int _ReadSByte; #define ReadSByte() (_ReadSByte = ReadByte(), (_ReadSByte & BIT(7) ? -128 : 0) + (_ReadSByte & BITS(7))) #define ReadFloat() ReadCoord() #define ReadVector() vec3(ReadFloat(), ReadFloat(), ReadFloat()) diff --git a/qcsrc/lib/oo.qh b/qcsrc/lib/oo.qh index 3708552d59..7a7a72641a 100644 --- a/qcsrc/lib/oo.qh +++ b/qcsrc/lib/oo.qh @@ -71,10 +71,17 @@ 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) {} + +#ifndef SVQC + #define delete_fn builtin_remove +#endif + #define delete(this) MACRO_BEGIN { \ entity _this = (this); \ void(entity) _dtor = _this.dtor; \ - if (_dtor) _dtor(_this); else remove(_this); \ + ONREMOVE(this); \ + if (_dtor) _dtor(_this); else delete_fn(_this); \ /* this = NULL; */ \ } MACRO_END @@ -220,13 +227,17 @@ STATIC_INIT(RegisterClasses) } \ STATIC_METHOD(cname, dtorimpl, void(cname this)) -#define ATTRIB(cname, name, type, val) \ - class(cname).type name; \ +#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; \ @@ -257,7 +268,7 @@ STATIC_INIT(RegisterClasses) } #define ATTRIBARRAY(cname, name, type, cnt) \ - class(cname).type name[cnt]; + class(cname) .type name[cnt] #define ENDCLASS(cname) \ INIT(cname) \ @@ -271,7 +282,7 @@ STATIC_INIT(RegisterClasses) #define spawn_1(this) #define _vtbl NULL CLASS(Object, ); - DESTRUCTOR(Object) { remove(this); } + DESTRUCTOR(Object) { builtin_remove(this); } #define remove(this) delete(this) METHOD(Object, describe, string(Object this)) { diff --git a/qcsrc/lib/p99.qh b/qcsrc/lib/p99.qh index d3228d47c8..927410cde1 100644 --- a/qcsrc/lib/p99.qh +++ b/qcsrc/lib/p99.qh @@ -12,8 +12,7 @@ /* without even the implied warranty of merchantability or fitness for a */ /* particular purpose. */ /* */ -#ifndef P99_H_ -#define P99_H_ +#pragma once #define P99_MAX_NUMBER 16 #define P00_ARG( \ @@ -83,5 +82,3 @@ P99_PASTE2(P99_PASTE3(_1, _2, _3), _4) #define P99_PASTE5(_1, _2, _3, _4, _5) \ P99_PASTE2(P99_PASTE4(_1, _2, _3, _4), _5) - -#endif /* !P99_H_ */ diff --git a/qcsrc/lib/random.qc b/qcsrc/lib/random.qc index aff961c55d..627fec11a9 100644 --- a/qcsrc/lib/random.qc +++ b/qcsrc/lib/random.qc @@ -9,7 +9,7 @@ void RandomSelection_Init() RandomSelection_best_priority = -1; } -void RandomSelection_Add(entity e, float f, string s, float weight, float priority) +void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority) { if (priority > RandomSelection_best_priority) { @@ -17,6 +17,7 @@ void RandomSelection_Add(entity e, float f, string s, float weight, float priori RandomSelection_chosen_ent = e; RandomSelection_chosen_float = f; RandomSelection_chosen_string = s; + RandomSelection_chosen_vec = v; RandomSelection_totalweight = weight; } else if (priority == RandomSelection_best_priority) @@ -27,6 +28,7 @@ void RandomSelection_Add(entity e, float f, string s, float weight, float priori RandomSelection_chosen_ent = e; RandomSelection_chosen_float = f; RandomSelection_chosen_string = s; + RandomSelection_chosen_vec = v; } } } @@ -38,8 +40,7 @@ void DistributeEvenly_Init(float amount, float totalweight) { if (DistributeEvenly_amount) { - LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for "); - LOG_TRACE(ftos(DistributeEvenly_totalweight), " left!)\n"); + LOG_TRACE("DistributeEvenly_Init: UNFINISHED DISTRIBUTION (", ftos(DistributeEvenly_amount), " for ", ftos(DistributeEvenly_totalweight), " left!)"); } if (totalweight == 0) DistributeEvenly_amount = 0; else DistributeEvenly_amount = amount; @@ -97,7 +98,7 @@ float gsl_ran_gaussian(float sigma) prandom_seed = c; #ifdef USE_PRANDOM_DEBUG - LOG_TRACE("RANDOM -> ", ftos(c), "\n"); + LOG_TRACE("RANDOM -> ", ftos(c)); #endif return c / 65536; // in [0..1[ @@ -122,14 +123,14 @@ float gsl_ran_gaussian(float sigma) { prandom_seed = seed; #ifdef USE_PRANDOM_DEBUG - LOG_TRACE("SRANDOM ", ftos(seed), "\n"); + LOG_TRACE("SRANDOM ", ftos(seed)); #endif } #ifdef USE_PRANDOM_DEBUG void prandom_debug() { - LOG_TRACE("Current random seed = ", ftos(prandom_seed), "\n"); + LOG_TRACE("Current random seed = ", ftos(prandom_seed)); } #endif #endif diff --git a/qcsrc/lib/random.qh b/qcsrc/lib/random.qh index 1834a97a33..b2fc53f0ca 100644 --- a/qcsrc/lib/random.qh +++ b/qcsrc/lib/random.qh @@ -5,9 +5,14 @@ float RandomSelection_best_priority; entity RandomSelection_chosen_ent; float RandomSelection_chosen_float; string RandomSelection_chosen_string; +vector RandomSelection_chosen_vec; void RandomSelection_Init(); -void RandomSelection_Add(entity e, float f, string s, float weight, float priority); +void RandomSelection_Add(entity e, float f, string s, vector v, float weight, float priority); +#define RandomSelection_AddEnt(e, weight, priority) RandomSelection_Add(e, 0, string_null, '0 0 0', weight, priority) +#define RandomSelection_AddFloat(f, weight, priority) RandomSelection_Add(NULL, f, string_null, '0 0 0', weight, priority) +#define RandomSelection_AddString(s, weight, priority) RandomSelection_Add(NULL, 0, s, '0 0 0', weight, priority) +#define RandomSelection_AddVec(v, weight, priority) RandomSelection_Add(NULL, 0, string_null, v, weight, priority) // prandom - PREDICTABLE random number generator diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index 7ce923c537..9d171e031a 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -71,10 +71,13 @@ REGISTRY(Registries, BITS(8)) REGISTER_INIT(id) {} \ void Register_##id() \ { \ - if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%d)", registry##_MAX); \ - entity this = id = inst; \ - this.registered_id = #id; \ - REGISTRY_PUSH(registry, fld, this); \ + entity this = id; \ + if (this == NULL) { \ + if (registry##_COUNT >= registry##_MAX) LOG_FATALF("Registry capacity exceeded (%d)", registry##_MAX); \ + this = id = inst; \ + this.registered_id = #id; \ + REGISTRY_PUSH(registry, fld, this); \ + } \ Register_##id##_init(this); \ } \ ACCUMULATE_FUNCTION(_Register##registry, Register_##id) \ @@ -157,7 +160,7 @@ void Registry_send(string id, string hash); FOREACH(id, true, s = strcat(s, join, it.registered_id)); \ s = substring(s, strlen(join), -1); \ string h = REGISTRY_HASH(id) = strzone(digest_hex(algo, s)); \ - LOG_DEBUGF(#id ": %s\n[%s]\n", h, s); \ + LOG_DEBUGF(#id ": %s\n[%s]", h, s); \ } \ void Registry_check(string r, string sv) \ { \ @@ -166,7 +169,7 @@ void Registry_send(string id, string hash); string cl = REGISTRY_HASH(id); \ if (cl != sv) \ { \ - LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s\n", r, cl, sv); \ + LOG_FATALF("client/server mismatch (%s).\nCL: %s\nSV: %s", r, cl, sv); \ } \ } \ } \ @@ -178,7 +181,11 @@ void Registry_send(string id, string hash); #define REGISTER_REGISTRY_2(id, str) \ ACCUMULATE_FUNCTION(__static_init, Register##id) \ CLASS(id##Registry, Object) \ - ATTRIB(id##Registry, m_name, string, str) \ - ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first) \ + ATTRIB(id##Registry, m_name, string, str); \ + ATTRIB(id##Registry, REGISTRY_NEXT, entity, id##_first); \ + METHOD(id##Registry, m_reload, void()); \ ENDCLASS(id##Registry) \ - REGISTER(Registries, REGISTRY, id, m_id, NEW(id##Registry)); + REGISTER(Registries, REGISTRY, id, m_id, NEW(id##Registry)); \ + METHOD(id##Registry, m_reload, void()) { \ + Register##id(); \ + } diff --git a/qcsrc/lib/replicate.qh b/qcsrc/lib/replicate.qh index f69b6072be..a36466d2e0 100644 --- a/qcsrc/lib/replicate.qh +++ b/qcsrc/lib/replicate.qh @@ -1,6 +1,6 @@ #pragma once -#ifndef MENUQC +#ifdef GAMEQC /** * Replicate a client cvar into a server field diff --git a/qcsrc/lib/self.qh b/qcsrc/lib/self.qh index ec43a34022..f4c246f330 100644 --- a/qcsrc/lib/self.qh +++ b/qcsrc/lib/self.qh @@ -48,7 +48,7 @@ #define SELFWRAP(T, R, oldargs, args, forward) \ .R oldargs T; \ - .R oldargs __##T = T; \ + noref .R oldargs __##T = T; \ .R args self##T; \ R T##_self oldargs { ENGINE_EVENT(); return this.self##T forward; } @@ -66,20 +66,26 @@ SELFWRAP(think, void, (), (entity this), (this)) #define setthink(e, f) SELFWRAP_SET(think, e, f) #define getthink(e) SELFWRAP_GET(think, e) -SELFWRAP(touch, void, (), (entity this), (this)) +#ifdef GAMEQC +SELFWRAP(touch, void, (), (entity this, entity toucher), (this, other)) #define settouch(e, f) SELFWRAP_SET(touch, e, f) #define gettouch(e) SELFWRAP_GET(touch, e) +#endif -SELFWRAP(blocked, void, (), (entity this), (this)) +#ifdef GAMEQC +SELFWRAP(blocked, void, (), (entity this, entity blocker), (this, other)) #define setblocked(e, f) SELFWRAP_SET(blocked, e, f) #define blocked stopusingthis +#endif SELFWRAP(predraw, void, (), (entity this), (this)) #define setpredraw(e, f) SELFWRAP_SET(predraw, e, f) -SELFWRAP(customizeentityforclient, bool, (), (entity this), (this)) +#ifdef GAMEQC +SELFWRAP(customizeentityforclient, bool, (), (entity this, entity client), (this, other)) #define setcefc(e, f) SELFWRAP_SET(customizeentityforclient, e, f) #define getcefc(e) SELFWRAP_GET(customizeentityforclient, e) +#endif SELFWRAP(camera_transform, vector, (vector org, vector ang), (entity this, vector org, vector ang), (this, org, ang)) #define setcamera_transform(e, f) SELFWRAP_SET(camera_transform, e, f) @@ -96,14 +102,8 @@ SELFWRAP(SendEntity, bool, (entity to, int sendflags), (entity this, entity to, #endif #define error(...) (__self = (NULL), builtin_error(__VA_ARGS__)) #define movetogoal(e, ...) (__self = (e), builtin_movetogoal(__VA_ARGS__)) -#ifndef SVQC - #define objerror(e, ...) (__self = (e), builtin_objerror(__VA_ARGS__)) -#else - void make_safe_for_remove(entity this); - #define objerror(e, ...) (__self = (e), make_safe_for_remove(__self), builtin_objerror(__VA_ARGS__)) -#endif #define walkmove(e, ...) (__self = (e), builtin_walkmove(__VA_ARGS__)) -#ifndef MENUQC +#ifdef GAMEQC void adaptor_think2use(entity this) { if (this.use) this.use(this, NULL, NULL); } #endif diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index 0463979c1a..e8aeaecae6 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -20,7 +20,7 @@ noref bool require_spawnfunc_prefix; #define _spawnfunc_checktypes(fld) \ if (fieldname == #fld) \ - if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted\n", fieldname); + if (!entityfieldassignablefromeditor(i)) LOG_FATALF("Entity field '%s' cannot be whitelisted", fieldname); #else #define _spawnfunc_checktypes(fld) #endif @@ -29,7 +29,7 @@ noref bool require_spawnfunc_prefix; noref bool __spawnfunc_expecting; noref entity __spawnfunc_expect; - bool __spawnfunc_unreachable_workaround = true; + noref bool __spawnfunc_unreachable_workaround = true; #define spawnfunc_1(id) spawnfunc_2(id, FIELDS_UNION) #define spawnfunc_2(id, whitelist) \ @@ -62,7 +62,7 @@ noref bool require_spawnfunc_prefix; if (fieldname == "") continue; \ FIELDS_COMMON(_spawnfunc_check) \ whitelist(_spawnfunc_check) \ - LOG_WARNINGF(_("Entity field %s.%s (%s) is not whitelisted. If you believe this is an error, please file an issue.\n"), #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, fieldname, value); \ } \ this.spawnfunc_checked = true; \ } \ @@ -144,6 +144,7 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, monster_moveflags) \ FIELD_SCALAR(fld, monster_name) \ FIELD_SCALAR(fld, movetype) \ + FIELD_SCALAR(fld, move_movetype) \ FIELD_SCALAR(fld, netname) \ FIELD_SCALAR(fld, nextthink) \ FIELD_SCALAR(fld, noalign) \ @@ -164,6 +165,7 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, sound1) \ FIELD_SCALAR(fld, sounds) \ FIELD_SCALAR(fld, spawnflags) \ + FIELD_SCALAR(fld, spawnmob) \ FIELD_SCALAR(fld, speed) \ FIELD_SCALAR(fld, strength) \ FIELD_SCALAR(fld, target2) \ diff --git a/qcsrc/lib/static.qh b/qcsrc/lib/static.qh index e1cfeb8721..6b5febe6fb 100644 --- a/qcsrc/lib/static.qh +++ b/qcsrc/lib/static.qh @@ -9,8 +9,23 @@ void __static_init_precache() {} void __shutdown() {} #define shutdownhooks() CALL_ACCUMULATED_FUNCTION(__shutdown) +#define GETTIME_REALTIME 1 +#ifdef MENUQC +float(int tmr) _gettime = #67; +#else +float(int tmr) _gettime = #519; +#endif + +void profile(string s) +{ + static float g_starttime; + float rt = _gettime(GETTIME_REALTIME); + if (!g_starttime) g_starttime = rt; + LOG_TRACEF("[%f] %s", rt - g_starttime, s); +} + #define _STATIC_INIT(where, func) \ - void _static_##func(); \ + [[accumulate]] void _static_##func() { profile(#func); } \ ACCUMULATE_FUNCTION(where, _static_##func) \ void _static_##func() diff --git a/qcsrc/lib/stats.qh b/qcsrc/lib/stats.qh index bd8b792967..4642f76403 100644 --- a/qcsrc/lib/stats.qh +++ b/qcsrc/lib/stats.qh @@ -8,6 +8,24 @@ .int m_id; USING(vectori, vector); +const int STATS_ENGINE_RESERVE = 32; +// must be listed in ascending order +#define MAGIC_STATS(_, x) \ + _(x, MOVEVARS_TIMESCALE, 241) \ + /**/ + +int g_magic_stats_hole = 0; + +#define MAGIC_STATS_FIX_MANUAL(it, var, id) \ + if (it.registered_id == "STAT_" #var) { --g_magic_stats_hole; it.m_id = id; } else + +#define MAGIC_STATS_FIX_AUTO(it, var, id) \ + if (it.m_id == id) { ++g_magic_stats_hole; ++it.m_id; } + +#define MAGIC_STATS_FIX(it) \ + it.m_id += g_magic_stats_hole; \ + MAGIC_STATS(MAGIC_STATS_FIX_MANUAL, it) { MAGIC_STATS(MAGIC_STATS_FIX_AUTO, it) } + #define REGISTER_STAT(...) EVAL_REGISTER_STAT(OVERLOAD(REGISTER_STAT, __VA_ARGS__)) #define EVAL_REGISTER_STAT(...) __VA_ARGS__ #if defined(CSQC) @@ -27,7 +45,7 @@ USING(vectori, vector); #define _STAT(id) g_stat_##id #define REGISTER_STAT_2(id, T) \ T _STAT(id); \ - T CAT(_STAT(id), _prev); \ + /* T CAT(_STAT(id), _prev); */ \ REGISTER(Stats, STAT_##id, m_id, new_pure(stat)) \ { \ if (#T == "vector" || #T == "vectori") { \ @@ -38,8 +56,8 @@ USING(vectori, vector); [[accumulate]] void stats_get() \ { \ T it = getstat_##T(STAT_##id.m_id); \ - if (it != CAT(_STAT(id), _prev)) \ - CAT(_STAT(id), _prev) = _STAT(id) = it; \ + /* if (it != CAT(_STAT(id), _prev)) \ + CAT(_STAT(id), _prev) = */ _STAT(id) = it; \ } #define REGISTER_STAT_3(x, T, expr) REGISTER_STAT_2(x, T) #elif defined(SVQC) @@ -68,8 +86,11 @@ USING(vectori, vector); /** Prevent engine stats being sent */ STATIC_INIT(stats_clear) { - int r = 32; + int r = STATS_ENGINE_RESERVE; for (int i = 0, n = 256 - r; i < n; ++i) { + #define X(_, name, id) if (i == id) continue; + MAGIC_STATS(X, ); + #undef X addstat(r + i, AS_INT, __stat_null); } } @@ -99,15 +120,16 @@ USING(vectori, vector); #define REGISTER_STAT_3(id, T, expr) #endif -const int STATS_ENGINE_RESERVE = 32; - REGISTRY(Stats, 256 - STATS_ENGINE_RESERVE) REGISTER_REGISTRY(Stats) REGISTRY_SORT(Stats) REGISTRY_CHECK(Stats) STATIC_INIT(RegisterStats_renumber) { - FOREACH(Stats, true, it.m_id = STATS_ENGINE_RESERVE + i); + FOREACH(Stats, true, { + it.m_id = STATS_ENGINE_RESERVE + i; + MAGIC_STATS_FIX(it); + }); } #ifdef SVQC STATIC_INIT(stats_add) { stats_add(); } diff --git a/qcsrc/lib/string.qh b/qcsrc/lib/string.qh index 2ade296912..617891d8b6 100644 --- a/qcsrc/lib/string.qh +++ b/qcsrc/lib/string.qh @@ -339,7 +339,12 @@ int vercmp(string v1, string v2) return vercmp_recursive(v1, v2); } +const string HEXDIGITS_MINSET = "0123456789ABCDEFabcdef"; const string HEXDIGITS = "0123456789ABCDEF0123456789abcdef"; #define HEXDIGIT_TO_DEC_RAW(d) (strstrofs(HEXDIGITS, (d), 0)) #define HEXDIGIT_TO_DEC(d) ((HEXDIGIT_TO_DEC_RAW(d) | 0x10) - 0x10) -#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS, (d), 1)) +#define DEC_TO_HEXDIGIT(d) (substring(HEXDIGITS_MINSET, (d), 1)) +#define IS_HEXDIGIT(d) (strstrofs(HEXDIGITS_MINSET, (d), 0) >= 0) + +const string DIGITS = "0123456789"; +#define IS_DIGIT(d) (strstrofs(DIGITS, (d), 0) >= 0) diff --git a/qcsrc/lib/test.qh b/qcsrc/lib/test.qh index d05ae28cd9..ff6f2d23d5 100644 --- a/qcsrc/lib/test.qh +++ b/qcsrc/lib/test.qh @@ -15,7 +15,7 @@ #define SUCCEED() (TEST_ok = true) /** Add a failure, but continue */ -#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARNINGF(msg); } MACRO_END +#define ADD_FAILURE(msg) MACRO_BEGIN { ++TEST_failed; LOG_WARN(msg); } MACRO_END /** Add a failure and return */ #define FAIL(msg) _TEST_ASSERT(ADD_FAILURE(msg)) @@ -87,7 +87,7 @@ int TEST_failed; #define EXPECT_NO_FATAL_FAILURE_(statement, then) \ EXPECT_NO_FATAL_FAILURE__(statement, { \ - LOG_WARNINGF( \ + LOG_WARNF( \ " Actual: %d fatal failures\n" \ "Expected: no fatal failures\n", \ TEST_fatal - TEST_prevfatal \ diff --git a/qcsrc/lib/unsafe.qh b/qcsrc/lib/unsafe.qh index 97e1c79584..60ad3d88da 100644 --- a/qcsrc/lib/unsafe.qh +++ b/qcsrc/lib/unsafe.qh @@ -1,10 +1,13 @@ #pragma once #define reinterpret_cast(T, it) _unsafe_cast_##T(0, it) -#define X(T) T _unsafe_cast_##T(int dummy, ...) { return ...(0, T); } +#define X(T) \ + T _unsafe_cast_##T(int dummy, ...) { return ...(0, T); } \ + USING(T##_fld, .T); T##_fld _unsafe_cast_##T##_fld(int dummy, ...) { return ...(0, T##_fld); } X(bool) X(int) X(float) +X(vector) X(entity) X(string) USING(rawfunc, float(...)); @@ -24,3 +27,5 @@ STATIC_INIT(INTEGER_ONE) { INTEGER_ONE = reinterpret_cast(int, _unsafe_fld2) - reinterpret_cast(int, _unsafe_fld1); } + +#define ARRAY_INDEX(T, arr, idx) (reinterpret_cast(T##_fld, reinterpret_cast(int, arr[0]) + FTOI(idx))) diff --git a/qcsrc/lib/urllib.qc b/qcsrc/lib/urllib.qc index c4c700a72d..d1d5c65ed2 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\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); strunzone(e.url_url); - remove(e); + delete(e); return 1; } e.url_rbufpos = 0; @@ -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\n"); e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); strunzone(e.url_url); - remove(e); + delete(e); return 1; } for (i = 0; i < n; ++i) @@ -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); - remove(e); + delete(e); return 1; } } @@ -107,7 +107,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\n"); rdy(e, pass, URL_READY_ERROR); strunzone(e.url_url); - remove(e); + delete(e); return; } e.url_wbufpos = 0; @@ -229,7 +229,7 @@ void url_fclose(entity e) e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); buf_del(e.url_wbuf); strunzone(e.url_url); - remove(e); + delete(e); return; } } @@ -241,7 +241,7 @@ void url_fclose(entity e) e.url_ready(e, e.url_ready_pass, URL_READY_ERROR); buf_del(e.url_wbuf); strunzone(e.url_url); - remove(e); + delete(e); return; } @@ -262,20 +262,20 @@ void url_fclose(entity e) e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); buf_del(e.url_rbuf); strunzone(e.url_url); - remove(e); + delete(e); } } else if (e.url_fh == URL_FH_STDOUT) { e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle - remove(e); + delete(e); } else { // file fclose(e.url_fh); e.url_ready(e, e.url_ready_pass, URL_READY_CLOSED); // closing creates no reading handle - remove(e); + delete(e); } } @@ -316,7 +316,7 @@ void url_fputs(entity e, string s) else if (e.url_fh == URL_FH_STDOUT) { // stdout - LOG_INFO(s); + print(s); } else { @@ -336,7 +336,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\n"); me.url_ready(fh, me.url_ready_pass, status); strunzone(me.url_url); - remove(me); + delete(me); return; } me.url_attempt += 1; @@ -345,7 +345,7 @@ void url_multi_ready(entity fh, entity me, float status) { me.url_ready(fh, me.url_ready_pass, status); strunzone(me.url_url); - remove(me); + delete(me); return; } url_single_fopen(argv(me.url_attempt), me.url_mode, url_multi_ready, me); diff --git a/qcsrc/lib/vector.qh b/qcsrc/lib/vector.qh index def1dae2d4..0a887cfebb 100644 --- a/qcsrc/lib/vector.qh +++ b/qcsrc/lib/vector.qh @@ -34,6 +34,13 @@ vector cross(vector a, vector b) } #endif +noref vector _vmul_a, _vmul_b; +#define vmul(a, b) \ + (_vmul_a = (a), _vmul_b = (b), \ + '1 0 0' * (_vmul_a.x * _vmul_b.x) \ + + '0 1 0' * (_vmul_a.y * _vmul_b.y) \ + + '0 0 1' * (_vmul_a.z * _vmul_b.z)) + const vector eX = '1 0 0'; const vector eY = '0 1 0'; const vector eZ = '0 0 1'; @@ -94,7 +101,7 @@ noref vector _vec2; noref vector _vec3; #define vec3(_x, _y, _z) (_vec3.x = (_x), _vec3.y = (_y), _vec3.z = (_z), _vec3) -vector rotate(vector v, float a) +vector Rotate(vector v, float a) { float a_sin = sin(a), a_cos = cos(a); vector r = '0 0 0'; @@ -116,7 +123,26 @@ vector reflect(vector dir, vector norm) return dir - 2 * (dir * norm) * norm; } -#ifndef MENUQC +/** + * clip vel along the plane defined by norm (assuming 0 distance away), bounciness determined by bounce 0..1 + */ +vector vec_reflect(vector vel, vector norm, float bounce) +{ + return vel - (1 + bounce) * (vel * norm) * norm; +} + +vector vec_epsilon(vector this, float eps) +{ + if (this.x > -eps && this.x < eps) this.x = 0; + if (this.y > -eps && this.y < eps) this.y = 0; + if (this.z > -eps && this.z < eps) this.z = 0; + return this; +} + +#define ClipVelocity(in, normal, out, overbounce) \ + (out = vec_epsilon(vec_reflect(in, normal, (overbounce) - 1), 0.1)) + +#ifdef GAMEQC vector get_corner_position(entity box, int corner) { switch (corner) diff --git a/qcsrc/lib/warpzone/anglestransform.qh b/qcsrc/lib/warpzone/anglestransform.qh index 0a10843c00..b287651a10 100644 --- a/qcsrc/lib/warpzone/anglestransform.qh +++ b/qcsrc/lib/warpzone/anglestransform.qh @@ -1,5 +1,4 @@ -#ifndef LIB_WARPZONE_ANGLETRANSFORM_H -#define LIB_WARPZONE_ANGLETRANSFORM_H +#pragma once #ifndef POSITIVE_PITCH_IS_DOWN #define POSITIVE_PITCH_IS_DOWN 1 @@ -42,4 +41,3 @@ vector AnglesTransform_ToVAngles(vector v); // transformed = original * transform + postshift vector AnglesTransform_Multiply_GetPostShift(vector sf0, vector st0, vector t1, vector st1); vector AnglesTransform_PrePostShift_GetPostShift(vector sf, vector t, vector st); -#endif diff --git a/qcsrc/lib/warpzone/client.qc b/qcsrc/lib/warpzone/client.qc index 5a1e320e93..15a3ca4c3c 100644 --- a/qcsrc/lib/warpzone/client.qc +++ b/qcsrc/lib/warpzone/client.qc @@ -25,7 +25,7 @@ void WarpZone_Fade_PreDraw(entity this) this.drawmask = MASK_NORMAL; } -void WarpZone_Touch (entity this); +void WarpZone_Touch(entity this, entity toucher); NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) { warpzone_warpzones_exist = 1; @@ -35,6 +35,9 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) } this.classname = "trigger_warpzone"; + if(isnew) + IL_PUSH(g_warpzones, this); + int f = ReadByte(); this.warpzone_isboxy = (f & 1); if(f & 4) @@ -89,7 +92,7 @@ NET_HANDLE(ENT_CLIENT_WARPZONE, bool isnew) // engine currently wants this setpredraw(this, WarpZone_Fade_PreDraw); - //this.move_touch = WarpZone_Touch; + //settouch(this, WarpZone_Touch); return true; } diff --git a/qcsrc/lib/warpzone/client.qh b/qcsrc/lib/warpzone/client.qh index 8f3e643b5c..47ff241368 100644 --- a/qcsrc/lib/warpzone/client.qh +++ b/qcsrc/lib/warpzone/client.qh @@ -1,5 +1,4 @@ -#ifndef LIB_WARPZONE_CLIENT_H -#define LIB_WARPZONE_CLIENT_H +#pragma once void WarpZone_FixPMove(); void WarpZone_FixView(); @@ -8,4 +7,3 @@ void WarpZone_Shutdown(); vector warpzone_save_view_origin; vector warpzone_save_view_angles; -#endif diff --git a/qcsrc/lib/warpzone/common.qc b/qcsrc/lib/warpzone/common.qc index 5acacc7734..5a3929e1e9 100644 --- a/qcsrc/lib/warpzone/common.qc +++ b/qcsrc/lib/warpzone/common.qc @@ -4,7 +4,7 @@ #include #elif defined(MENUQC) #elif defined(SVQC) - #include + #include #endif void WarpZone_Accumulator_Clear(entity acc) @@ -112,7 +112,7 @@ float WarpZoneLib_BoxTouchesBrush_Recurse() #ifdef CSQC if (trace_networkentity) { - LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush\n"); + LOG_TRACE("hit a network ent, cannot continue WarpZoneLib_BoxTouchesBrush"); // we cannot continue, as a player blocks us... // so, abort return 0; @@ -154,31 +154,33 @@ float WarpZoneLib_BoxTouchesBrush(vector mi, vector ma, entity e, entity ig) entity WarpZone_Find(vector mi, vector ma) { // if we are near any warpzone planes - MOVE AWAY (work around nearclip) - entity e; if(!warpzone_warpzones_exist) return NULL; - for(e = NULL; (e = find(e, classname, "trigger_warpzone")); ) - if(WarpZoneLib_BoxTouchesBrush(mi, ma, e, NULL)) - return e; + IL_EACH(g_warpzones, WarpZoneLib_BoxTouchesBrush(mi, ma, it, NULL), + { + return it; + }); return NULL; } void WarpZone_MakeAllSolid() { - entity e; if(!warpzone_warpzones_exist) return; - for(e = NULL; (e = find(e, classname, "trigger_warpzone")); ) - e.solid = SOLID_BSP; + IL_EACH(g_warpzones, true, + { + it.solid = SOLID_BSP; + }); } void WarpZone_MakeAllOther() { - entity e; if(!warpzone_warpzones_exist) return; - for(e = NULL; (e = find(e, classname, "trigger_warpzone")); ) - e.solid = SOLID_TRIGGER; + IL_EACH(g_warpzones, true, + { + it.solid = SOLID_TRIGGER; + }); } void WarpZone_Trace_InitTransform() @@ -271,7 +273,7 @@ void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, { if(--i < 1) { - LOG_TRACE("Too many warpzones in sequence, aborting trace.\n"); + LOG_TRACE("Too many warpzones in sequence, aborting trace."); trace_ent = NULL; break; } @@ -299,7 +301,7 @@ void WarpZone_TraceBox_ThroughZone(vector org, vector mi, vector ma, vector end, if(trace_ent == wz) { // FIXME can this check be removed? Do we really need it? - LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n"); + LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace"); trace_ent = NULL; break; } @@ -395,7 +397,7 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo { if(--i < 1) { - LOG_TRACE("Too many warpzones in sequence, aborting trace.\n"); + LOG_TRACE("Too many warpzones in sequence, aborting trace."); trace_ent = NULL; break; } @@ -413,7 +415,7 @@ void WarpZone_TraceToss_ThroughZone(entity e, entity forent, entity zone, WarpZo if(trace_ent == wz) { // FIXME can this check be removed? Do we really need it? - LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace\n"); + LOG_TRACE("I transformed into the same zone again, wtf, aborting the trace"); trace_ent = NULL; break; } @@ -671,7 +673,7 @@ void WarpZone_RefSys_GC(entity this) // garbage collect unused reference systems this.nextthink = time + 1; if(this.owner.WarpZone_refsys != this) - remove(this); + delete(this); } void WarpZone_RefSys_CheckCreate(entity me) { @@ -688,7 +690,7 @@ void WarpZone_RefSys_Clear(entity me) { if(me.WarpZone_refsys) { - remove(me.WarpZone_refsys); + delete(me.WarpZone_refsys); me.WarpZone_refsys = NULL; } } diff --git a/qcsrc/lib/warpzone/common.qh b/qcsrc/lib/warpzone/common.qh index 2cfa7ef9bc..26c0e80fe4 100644 --- a/qcsrc/lib/warpzone/common.qh +++ b/qcsrc/lib/warpzone/common.qh @@ -1,9 +1,11 @@ -#ifndef LIB_WARPZONE_COMMON_H -#define LIB_WARPZONE_COMMON_H +#pragma once // uncomment this if your mod uses the roll angle in fixangle // #define KEEP_ROLL +IntrusiveList g_warpzones; +STATIC_INIT(g_warpzones) { g_warpzones = IL_NEW(); } + float warpzone_warpzones_exist; float warpzone_cameras_exist; @@ -111,6 +113,5 @@ float WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher); void WarpZoneLib_ExactTrigger_Init(entity this); // WARNING: this kills the trace globals -#define EXACTTRIGGER_TOUCH if(WarpZoneLib_ExactTrigger_Touch(this, other)) return +#define EXACTTRIGGER_TOUCH(e,t) if(WarpZoneLib_ExactTrigger_Touch((e), (t))) return #define EXACTTRIGGER_INIT WarpZoneLib_ExactTrigger_Init(this) -#endif diff --git a/qcsrc/lib/warpzone/mathlib.qc b/qcsrc/lib/warpzone/mathlib.qc index ac3b65ff1a..e190518275 100644 --- a/qcsrc/lib/warpzone/mathlib.qc +++ b/qcsrc/lib/warpzone/mathlib.qc @@ -4,174 +4,172 @@ #elif defined(SVQC) #endif -int fpclassify(float x) +int fpclassify(float e) { - if(isnan(x)) + if(isnan(e)) return FP_NAN; - if(isinf(x)) + if(isinf(e)) return FP_INFINITE; - if(x == 0) + if(e == 0) return FP_ZERO; return FP_NORMAL; } -bool isfinite(float x) +bool isfinite(float e) { - return !(isnan(x) || isinf(x)); + return !(isnan(e) || isinf(e)); } -bool isinf(float x) +bool isinf(float e) { - return (x != 0) && (x + x == x); + return (e != 0) && (e + e == e); } -bool isnan(float x) +bool isnan(float e) { - float y; - y = x; - return (x != y); + float f = e; + return (e != f); } -bool isnormal(float x) +bool isnormal(float e) { - return isfinite(x); + return isfinite(e); } -bool signbit(float x) +bool signbit(float e) { - return (x < 0); + return (e < 0); } -float acosh(float x) +float acosh(float e) { - return log(x + sqrt(x*x - 1)); + return log(e + sqrt(e*e - 1)); } -float asinh(float x) +float asinh(float e) { - return log(x + sqrt(x*x + 1)); + return log(e + sqrt(e*e + 1)); } -float atanh(float x) +float atanh(float e) { - return 0.5 * log((1+x) / (1-x)); + return 0.5 * log((1+e) / (1-e)); } -float cosh(float x) +float cosh(float e) { - return 0.5 * (exp(x) + exp(-x)); + return 0.5 * (exp(e) + exp(-e)); } -float sinh(float x) +float sinh(float e) { - return 0.5 * (exp(x) - exp(-x)); + return 0.5 * (exp(e) - exp(-e)); } -float tanh(float x) +float tanh(float e) { - return sinh(x) / cosh(x); + return sinh(e) / cosh(e); } -float exp(float x) +float exp(float e) { - return pow(M_E, x); + return pow(M_E, e); } -float exp2(float x) +float exp2(float e) { - return pow(2, x); + return pow(2, e); } -float expm1(float x) +float expm1(float e) { - return exp(x) - 1; + return exp(e) - 1; } -vector frexp(float x) +vector frexp(float e) { vector v; v.z = 0; - v.y = ilogb(x) + 1; - v.x = x / exp2(v.y); + v.y = ilogb(e) + 1; + v.x = e / exp2(v.y); return v; } -int ilogb(float x) +int ilogb(float e) { - return floor(log2(fabs(x))); + return floor(log2(fabs(e))); } -float ldexp(float x, int e) +float ldexp(float e, int e) { - return x * pow(2, e); + return e * pow(2, e); } -float logn(float x, float base) +float logn(float e, float base) { - return log(x) / log(base); + return log(e) / log(base); } -float log10(float x) +float log10(float e) { - return log(x) * M_LOG10E; + return log(e) * M_LOG10E; } -float log1p(float x) +float log1p(float e) { - return log(x + 1); + return log(e + 1); } -float log2(float x) +float log2(float e) { - return log(x) * M_LOG2E; + return log(e) * M_LOG2E; } -float logb(float x) +float logb(float e) { - return floor(log2(fabs(x))); + return floor(log2(fabs(e))); } vector modf(float f) { return '1 0 0' * (f - trunc(f)) + '0 1 0' * trunc(f); } -float scalbn(float x, int n) +float scalbn(float e, int n) { - return x * pow(2, n); + return e * pow(2, n); } -float cbrt(float x) +float cbrt(float e) { - return copysign(pow(fabs(x), 1.0/3.0), x); + return copysign(pow(fabs(e), 1.0/3.0), e); } -float hypot(float x, float y) +float hypot(float e, float f) { - return sqrt(x*x + y*y); + return sqrt(e*e + f*f); } -float erf(float x) +float erf(float e) { // approximation taken from wikipedia - float y; - y = x*x; - return copysign(sqrt(1 - exp(-y * (1.273239544735163 + 0.14001228868667 * y) / (1 + 0.14001228868667 * y))), x); + float f; + f = e*e; + return copysign(sqrt(1 - exp(-f * (1.273239544735163 + 0.14001228868667 * f) / (1 + 0.14001228868667 * f))), e); } -float erfc(float x) +float erfc(float e) { - return 1.0 - erf(x); + return 1.0 - erf(e); } -vector lgamma(float x) +vector lgamma(float e) { // TODO improve accuracy - if(!isfinite(x)) - return fabs(x) * '1 0 0' + copysign(1, x) * '0 1 0'; - if(x < 1 && x == floor(x)) + if(!isfinite(e)) + return fabs(e) * '1 0 0' + copysign(1, e) * '0 1 0'; + if(e < 1 && e == floor(e)) return nan("gamma") * '1 1 1'; - if(x < 0.1) + if(e < 0.1) { vector v; - v = lgamma(1.0 - x); + v = lgamma(1.0 - e); // reflection formula: // gamma(1-z) * gamma(z) = pi / sin(pi*z) // lgamma(1-z) + lgamma(z) = log(pi) - log(sin(pi*z)) // sign of gamma(1-z) = sign of gamma(z) * sign of sin(pi*z) - v.z = sin(M_PI * x); + v.z = sin(M_PI * e); v.x = log(M_PI) - log(fabs(v.z)) - v.x; if(v.z < 0) v.y = -v.y; v.z = 0; return v; } - if(x < 1.1) - return lgamma(x + 1) - log(x) * '1 0 0'; - x -= 1; - return (0.5 * log(2 * M_PI * x) + x * (log(x) - 1)) * '1 0 0' + '0 1 0'; + if(e < 1.1) + return lgamma(e + 1) - log(e) * '1 0 0'; + e -= 1; + return (0.5 * log(2 * M_PI * e) + e * (log(e) - 1)) * '1 0 0' + '0 1 0'; } -float tgamma(float x) +float tgamma(float e) { - vector v; - v = lgamma(x); + vector v = lgamma(e); return exp(v.x) * v.y; } @@ -184,109 +182,109 @@ float tgamma(float x) * 1 % -2 == -1 * -1 % -2 == -1 */ -float pymod(float x, float y) +float pymod(float e, float f) { - return x - y * floor(x / y); + return e - f * floor(e / f); } -float nearbyint(float x) +float nearbyint(float e) { - return rint(x); + return rint(e); } -float trunc(float x) +float trunc(float e) { - return (x>=0) ? floor(x) : ceil(x); + return (e>=0) ? floor(e) : ceil(e); } -float fmod(float x, float y) +float fmod(float e, float f) { - return x - y * trunc(x / y); + return e - f * trunc(e / f); } -float remainder(float x, float y) +float remainder(float e, float f) { - return x - y * rint(x / y); + return e - f * rint(e / f); } -vector remquo(float x, float y) +vector remquo(float e, float f) { vector v; v.z = 0; - v.y = rint(x / y); - v.x = x - y * v.y; + v.y = rint(e / f); + v.x = e - f * v.y; return v; } -float copysign(float x, float y) +float copysign(float e, float f) { - return fabs(x) * ((y>0) ? 1 : -1); + return fabs(e) * ((f>0) ? 1 : -1); } float nan(string tag) { return sqrt(-1); } -float nextafter(float x, float y) +float nextafter(float e, float f) { // TODO very crude - if(x == y) + if(e == f) return nan("nextafter"); - if(x > y) - return -nextafter(-x, -y); - // now we know that x < y - // so we need the next number > x + if(e > f) + return -nextafter(-e, -f); + // now we know that e < f + // so we need the next number > e float d, a, b; - d = max(fabs(x), 0.00000000000000000000001); - a = x + d; + d = max(fabs(e), 0.00000000000000000000001); + a = e + d; do { d *= 0.5; b = a; - a = x + d; + a = e + d; } - while(a != x); + while(a != e); return b; } -float nexttoward(float x, float y) +float nexttoward(float e, float f) { - return nextafter(x, y); + return nextafter(e, f); } -float fdim(float x, float y) +float fdim(float e, float f) { - return max(x-y, 0); + return max(e-f, 0); } -float fmax(float x, float y) +float fmax(float e, float f) { - return max(x, y); + return max(e, f); } -float fmin(float x, float y) +float fmin(float e, float f) { - return min(x, y); + return min(e, f); } -float fma(float x, float y, float z) +float fma(float e, float f, float g) { - return x * y + z; + return e * f + g; } -int isgreater(float x, float y) +int isgreater(float e, float f) { - return x > y; + return e > f; } -int isgreaterequal(float x, float y) +int isgreaterequal(float e, float f) { - return x >= y; + return e >= f; } -int isless(float x, float y) +int isless(float e, float f) { - return x < y; + return e < f; } -int islessequal(float x, float y) +int islessequal(float e, float f) { - return x <= y; + return e <= f; } -int islessgreater(float x, float y) +int islessgreater(float e, float f) { - return x < y || x > y; + return e < f || e > f; } -int isunordered(float x, float y) +int isunordered(float e, float f) { - return !(x < y || x == y || x > y); + return !(e < f || e == f || e > f); } diff --git a/qcsrc/lib/warpzone/mathlib.qh b/qcsrc/lib/warpzone/mathlib.qh index c3de3838dd..f5d8f63e74 100644 --- a/qcsrc/lib/warpzone/mathlib.qh +++ b/qcsrc/lib/warpzone/mathlib.qh @@ -1,5 +1,4 @@ -#ifndef LIB_WARPZONE_MATHLIB_H -#define LIB_WARPZONE_MATHLIB_H +#pragma once // @@ -11,55 +10,55 @@ const int FP_INFINITE = 1; const int FP_ZERO = 2; const int FP_SUBNORMAL = 3; const int FP_NORMAL = 4; -int fpclassify(float x); -bool isfinite(float x); -bool isinf(float x); -bool isnan(float x); -bool isnormal(float x); -bool signbit(float x); - -//float acos(float x); -//float asin(float x); -//float atan(float x); -//float atan2(float y, float x); -//float cos(float x); -//float sin(float x); -//float tan(float x); - -float acosh(float x); -float asinh(float x); -float atanh(float x); -float cosh(float x); -float sinh(float x); -float tanh(float x); - -float exp(float x); -float exp2(float x); -float expm1(float x); - -vector frexp(float x); // returns mantissa as _x, exponent as _y -int ilogb(float x); -float ldexp(float x, int e); -//float log(float x); -float logn(float x, float base); -float log10(float x); -float log1p(float x); -float log2(float x); -float logb(float x); +int fpclassify(float e); +bool isfinite(float e); +bool isinf(float e); +bool isnan(float e); +bool isnormal(float e); +bool signbit(float e); + +//float acos(float e); +//float asin(float e); +//float atan(float e); +//float atan2(float f, float e); +//float cos(float e); +//float sin(float e); +//float tan(float e); + +float acosh(float e); +float asinh(float e); +float atanh(float e); +float cosh(float e); +float sinh(float e); +float tanh(float e); + +float exp(float e); +float exp2(float e); +float expm1(float e); + +vector frexp(float e); // returns mantissa as _x, exponent as _y +int ilogb(float e); +float ldexp(float e, int e); +//float log(float e); +float logn(float e, float base); +float log10(float e); +float log1p(float e); +float log2(float e); +float logb(float e); vector modf(float f); // fraction as _x, integer as _y -float scalbn(float x, int n); +float scalbn(float e, int n); -float cbrt(float x); -//float fabs(float x); -float hypot(float x, float y); -//float pow(float x, float y); -//float sqrt(float x, float y); +float cbrt(float e); +//float fabs(float e); +float hypot(float e, float f); +//float pow(float e, float f); +//float sqrt(float e, float f); -float erf(float x); -float erfc(float x); -vector lgamma(float x); // value in _x, sign in _y -float tgamma(float x); +float erf(float e); +float erfc(float e); +vector lgamma(float e); // value in _x, sign in _y +float tgamma(float e); /** * Pythonic mod: @@ -70,35 +69,35 @@ float tgamma(float x); * 1 % -2 == -1 * -1 % -2 == -1 */ -float pymod(float x, float y); +float pymod(float e, float f); -//float ceil(float x); -//float floor(float x); -float nearbyint(float x); -//float rint(float x); -//float round(float x); -float trunc(float x); +//float ceil(float e); +//float floor(float e); +float nearbyint(float e); +//float rint(float e); +//float round(float e); +float trunc(float e); -float fmod(float x, float y); -float remainder(float x, float y); -vector remquo(float x, float y); +float fmod(float e, float f); +float remainder(float e, float f); +vector remquo(float e, float f); -float copysign(float x, float y); +float copysign(float e, float f); float nan(string tag); -float nextafter(float x, float y); -float nexttoward(float x, float y); +float nextafter(float e, float f); +float nexttoward(float e, float f); -float fdim(float x, float y); -float fmax(float x, float y); -float fmin(float x, float y); -float fma(float x, float y, float z); +float fdim(float e, float f); +float fmax(float e, float f); +float fmin(float e, float f); +float fma(float e, float f, float g); -int isgreater(float x, float y); -int isgreaterequal(float x, float y); -int isless(float x, float y); -int islessequal(float x, float y); -int islessgreater(float x, float y); -int isunordered(float x, float y); +int isgreater(float e, float f); +int isgreaterequal(float e, float f); +int isless(float e, float f); +int islessequal(float e, float f); +int islessgreater(float e, float f); +int isunordered(float e, float f); const float M_E = 2.7182818284590452354; /* e */ const float M_LOG2E = 1.4426950408889634074; /* log_2 e */ @@ -114,5 +113,3 @@ const float M_2_PI = 0.63661977236758134308; /* 2/pi */ const float M_2_SQRTPI = 1.12837916709551257390; /* 2/sqrt(pi) */ const float M_SQRT2 = 1.41421356237309504880; /* sqrt(2) */ const float M_SQRT1_2 = 0.70710678118654752440; /* 1/sqrt(2) */ - -#endif diff --git a/qcsrc/lib/warpzone/server.qc b/qcsrc/lib/warpzone/server.qc index 7ddb96cbbf..116ad00023 100644 --- a/qcsrc/lib/warpzone/server.qc +++ b/qcsrc/lib/warpzone/server.qc @@ -5,9 +5,9 @@ #elif defined(MENUQC) #elif defined(SVQC) #include + #include #include #include - #include #include #include #endif @@ -28,44 +28,27 @@ .float warpzone_teleport_finishtime; .entity warpzone_teleport_zone; -#ifdef SVQC - #define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \ - entity e = e_; \ - e.warpzone_oldorigin = e.origin; \ - e.warpzone_oldvelocity = e.velocity; \ - e.warpzone_oldangles = e.angles; \ - } MACRO_END -#elif defined(CSQC) - #define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \ - entity e = e_; \ - e.warpzone_oldorigin = e.move_origin; \ - e.warpzone_oldvelocity = e.move_velocity; \ - e.warpzone_oldangles = e.move_angles; \ - } MACRO_END -#endif +#define WarpZone_StoreProjectileData(e_) MACRO_BEGIN { \ + entity e = e_; \ + e.warpzone_oldorigin = e.origin; \ + e.warpzone_oldvelocity = e.velocity; \ + e.warpzone_oldangles = e.angles; \ + } MACRO_END void WarpZone_TeleportPlayer(entity teleporter, entity player, vector to, vector to_angles, vector to_velocity) { -#ifdef SVQC setorigin(player, to); // NOTE: this also aborts the move, when this is called by touch +#ifdef SVQC player.oldorigin = to; // for DP's unsticking - player.angles = to_angles; player.fixangle = true; - player.velocity = to_velocity; -#elif defined(CSQC) - player.move_origin = to; - player.move_angles = to_angles; - player.move_velocity = to_velocity; #endif + player.angles = to_angles; + player.velocity = to_velocity; BITXOR_ASSIGN(player.effects, EF_TELEPORT_BIT); if(IS_PLAYER(player)) -#ifdef SVQC BITCLR_ASSIGN(player.flags, FL_ONGROUND); -#elif defined(CSQC) - BITCLR_ASSIGN(player.move_flags, FL_ONGROUND); -#endif WarpZone_PostTeleportPlayer_Callback(player); } @@ -85,15 +68,9 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) { vector o0, a0, v0, o1, a1, v1, o10; -#ifdef SVQC o0 = player.origin + player.view_ofs; v0 = player.velocity; a0 = player.angles; -#elif defined(CSQC) - o0 = player.move_origin + player.view_ofs; - v0 = player.move_velocity; - a0 = player.move_angles; -#endif o10 = o1 = WarpZone_TransformOrigin(wz, o0); v1 = WarpZone_TransformVelocity(wz, v0); @@ -189,30 +166,26 @@ float WarpZone_Teleport(entity wz, entity player, float f0, float f1) return 1; } -void WarpZone_Touch (entity this) +void WarpZone_Touch(entity this, entity toucher) { - if(other.classname == "trigger_warpzone") + if(toucher.classname == "trigger_warpzone") return; - if(time <= other.warpzone_teleport_finishtime) // already teleported this frame + if(time <= toucher.warpzone_teleport_finishtime) // already teleported this frame return; // FIXME needs a better check to know what is safe to teleport and what not -#ifdef SVQC - if(other.movetype == MOVETYPE_NONE || other.movetype == MOVETYPE_FOLLOW || other.tag_entity) -#elif defined(CSQC) - if(other.move_movetype == MOVETYPE_NONE || other.move_movetype == MOVETYPE_FOLLOW || other.tag_networkentity) + if((toucher.move_movetype == MOVETYPE_NONE && toucher.move_movetype == MOVETYPE_NONE) || toucher.move_movetype == MOVETYPE_FOLLOW || toucher.move_movetype == MOVETYPE_FOLLOW || toucher.tag_entity +#ifdef CSQC + || tag_networkentity #endif + ) return; - if(WarpZoneLib_ExactTrigger_Touch(this, other)) + if(WarpZoneLib_ExactTrigger_Touch(this, toucher)) return; -#ifdef SVQC - if(WarpZone_PlaneDist(this, other.origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet) -#elif defined(CSQC) - if(WarpZone_PlaneDist(this, other.move_origin + other.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet) -#endif + if(WarpZone_PlaneDist(this, toucher.origin + toucher.view_ofs) >= 0) // wrong side of the trigger_warpzone (don't teleport yet) return; float f; @@ -227,36 +200,32 @@ void WarpZone_Touch (entity this) // 24/(0.25/frametime) // 96*frametime float d; - d = 24 + max(vlen(other.mins), vlen(other.maxs)); - if(IS_NOT_A_CLIENT(other)) - #ifdef SVQC - f = -d / bound(frametime * d * 1, frametime * vlen(other.velocity), d); - #elif defined(CSQC) - f = -d / bound(frametime * d * 1, frametime * vlen(other.move_velocity), d); - #endif + d = 24 + max(vlen(toucher.mins), vlen(toucher.maxs)); + if(IS_NOT_A_CLIENT(toucher)) + f = -d / bound(frametime * d * 1, frametime * vlen(toucher.velocity), d); else f = -1; - if(WarpZone_Teleport(this, other, f, 0)) + if(WarpZone_Teleport(this, toucher, f, 0)) { #ifdef SVQC string save1, save2; save1 = this.target; this.target = string_null; save2 = this.target3; this.target3 = string_null; - SUB_UseTargets(this, other, other); // use other too? + SUB_UseTargets(this, toucher, toucher); // use toucher too? if (!this.target) this.target = save1; if (!this.target3) this.target3 = save2; save1 = this.target; this.target = string_null; save2 = this.target2; this.target2 = string_null; - SUB_UseTargets(this.enemy, other, other); // use other too? + SUB_UseTargets(this.enemy, toucher, toucher); // use toucher too? if (!this.target) this.target = save1; if (!this.target2) this.target2 = save2; #endif } else { - LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))\n"); + LOG_TRACE("WARPZONE FAIL AHAHAHAHAH))"); } } @@ -365,17 +334,8 @@ float WarpZone_CheckProjectileImpact(entity player) { vector o0, v0; - .vector orgvec, velvec; -#ifdef SVQC - orgvec = origin; - velvec = velocity; -#elif defined(CSQC) - orgvec = move_origin; - velvec = move_velocity; -#endif - - o0 = player.orgvec + player.view_ofs; - v0 = player.velvec; + o0 = player.origin + player.view_ofs; + v0 = player.velocity; // if we teleported shortly before, abort if(time <= player.warpzone_teleport_finishtime + 0.1) @@ -393,19 +353,15 @@ float WarpZone_CheckProjectileImpact(entity player) LOG_INFO("impactfilter found something - and it even gets handled correctly - please tell divVerent that this code apparently gets triggered again\n"); #endif LOG_INFO("Entity type: ", player.classname, "\n"); - LOG_INFO("Origin: ", vtos(player.orgvec), "\n"); - LOG_INFO("Velocity: ", vtos(player.velvec), "\n"); + LOG_INFO("Origin: ", vtos(player.origin), "\n"); + LOG_INFO("Velocity: ", vtos(player.velocity), "\n"); #ifdef WARPZONELIB_REMOVEHACK return 0; #else // retry previous move -#ifdef SVQC setorigin(player, player.warpzone_oldorigin); -#elif defined(CSQC) - player.move_origin = player.warpzone_oldorigin; -#endif - player.velvec = player.warpzone_oldvelocity; + player.velocity = player.warpzone_oldvelocity; if(WarpZone_Teleport(wz, player, 0, 1)) { string save1, save2; @@ -425,7 +381,7 @@ float WarpZone_CheckProjectileImpact(entity player) else { setorigin(player, o0 - player.view_ofs); - player.velvec = v0; + player.velocity = v0; } return +1; @@ -434,9 +390,9 @@ float WarpZone_CheckProjectileImpact(entity player) #endif #endif -float WarpZone_Projectile_Touch(entity this) +float WarpZone_Projectile_Touch(entity this, entity toucher) { - if(other.classname == "trigger_warpzone") + if(toucher.classname == "trigger_warpzone") return true; // no further impacts if we teleported this frame! @@ -496,7 +452,7 @@ float WarpZone_Projectile_Touch(entity this) } #endif - if(WarpZone_Projectile_Touch_ImpactFilter_Callback(this, other)) + if(WarpZone_Projectile_Touch_ImpactFilter_Callback(this, toucher)) return true; #endif @@ -675,7 +631,6 @@ void WarpZone_InitStep_ClearTarget(entity this) this.enemy = NULL; } -entity warpzone_first; .entity warpzone_next; void WarpZone_InitStep_FindTarget(entity this) { float i; @@ -775,6 +730,8 @@ spawnfunc(trigger_warpzone) BITSET_ASSIGN(this.effects, EF_NODEPTHTEST); this.warpzone_next = warpzone_first; warpzone_first = this; + + IL_PUSH(g_warpzones, this); } spawnfunc(func_camera) { @@ -847,46 +804,51 @@ void WarpZone_StartFrame() WarpZone_PostInitialize_Callback(); } - entity oldother = other; + if(warpzone_warpzones_exist) + { + IL_EACH(g_projectiles, true, + { + WarpZone_StoreProjectileData(it); + }); + } + - FOREACH_ENTITY(!is_pure(it), + FOREACH_CLIENT(true, { if(warpzone_warpzones_exist) - WarpZone_StoreProjectileData(it); + WarpZone_StoreProjectileData(it); // TODO: not actually needed if(IS_OBSERVER(it) || it.solid == SOLID_NOT) if(IS_CLIENT(it)) // we don't care about it being a bot { - other = it; // player - // warpzones if (warpzone_warpzones_exist) { entity e = WarpZone_Find(it.origin + it.mins, it.origin + it.maxs); if (e) - if (!WarpZoneLib_ExactTrigger_Touch(e, other)) + if (!WarpZoneLib_ExactTrigger_Touch(e, it)) if (WarpZone_PlaneDist(e, it.origin + it.view_ofs) <= 0) WarpZone_Teleport(e, it, -1, 0); // NOT triggering targets by this! } // teleporters - if(other.teleportable) + if(it.teleportable) { entity ent = Teleport_Find(it.origin + it.mins, it.origin + it.maxs); if (ent) - if (!WarpZoneLib_ExactTrigger_Touch(ent, other)) - Simple_TeleportPlayer(ent, other); // NOT triggering targets by this! + if (!WarpZoneLib_ExactTrigger_Touch(ent, it)) + Simple_TeleportPlayer(ent, it); // NOT triggering targets by this! } } }); - other = oldother; } .float warpzone_reconnecting; bool visible_to_some_client(entity ent) { - FOREACH_ENTITY(!IS_NOT_A_CLIENT(it), LAMBDA( - if (IS_PLAYER(it) && IS_REAL_CLIENT(it) && checkpvs(it.origin + it.view_ofs, ent)) return true; - )); + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && checkpvs(it.origin + it.view_ofs, ent), + { + return true; + }); return false; } void trigger_warpzone_reconnect_use(entity this, entity actor, entity trigger) diff --git a/qcsrc/lib/warpzone/server.qh b/qcsrc/lib/warpzone/server.qh index 9abea2a689..4287ef60de 100644 --- a/qcsrc/lib/warpzone/server.qh +++ b/qcsrc/lib/warpzone/server.qh @@ -1,9 +1,10 @@ -#ifndef LIB_WARPZONE_SERVER_H -#define LIB_WARPZONE_SERVER_H +#pragma once #ifdef SVQC +entity warpzone_first; .entity warpzone_next; + void WarpZone_StartFrame(); -float WarpZone_Projectile_Touch(entity this); +float WarpZone_Projectile_Touch(entity this, entity toucher); // THESE must be defined by calling QC code: void WarpZone_PostTeleportPlayer_Callback(entity pl); @@ -17,5 +18,3 @@ void WarpZone_PlayerPhysics_FixVAngle(entity this); void WarpZone_PostInitialize_Callback(); #endif - -#endif diff --git a/qcsrc/lib/warpzone/util_server.qc b/qcsrc/lib/warpzone/util_server.qc index 73f09c0e07..d5533647f5 100644 --- a/qcsrc/lib/warpzone/util_server.qc +++ b/qcsrc/lib/warpzone/util_server.qc @@ -43,7 +43,7 @@ void WarpZoneLib_ExactTrigger_Init(entity this) setsize(this, this.mins * this.scale, this.maxs * this.scale); else setsize(this, this.mins, this.maxs); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.solid = SOLID_TRIGGER; this.model = ""; } diff --git a/qcsrc/lib/warpzone/util_server.qh b/qcsrc/lib/warpzone/util_server.qh index 39d1d24699..b73289927b 100644 --- a/qcsrc/lib/warpzone/util_server.qh +++ b/qcsrc/lib/warpzone/util_server.qh @@ -1,9 +1,7 @@ -#ifndef LIB_WARPZONE_UTIL_SERVER_H -#define LIB_WARPZONE_UTIL_SERVER_H +#pragma once float WarpZoneLib_MoveOutOfSolid(entity e); float WarpZoneLib_ExactTrigger_Touch(entity this, entity toucher); #ifdef SVQC void WarpZoneLib_ExactTrigger_Init(entity this); #endif -#endif diff --git a/qcsrc/menu/_all.inc b/qcsrc/menu/_all.inc new file mode 100644 index 0000000000..e5198f5916 --- /dev/null +++ b/qcsrc/menu/_all.inc @@ -0,0 +1,10 @@ +#include +#include "_mod.inc" + +#include "anim/_mod.inc" +#include "command/_mod.inc" +#include "item/_mod.inc" +#include "mutators/_mod.inc" +#include "xonotic/_mod.inc" + +#include diff --git a/qcsrc/menu/anim/animation.qh b/qcsrc/menu/anim/animation.qh index 0427f322ae..009feb577c 100644 --- a/qcsrc/menu/anim/animation.qh +++ b/qcsrc/menu/anim/animation.qh @@ -15,14 +15,14 @@ CLASS(Animation, Object) METHOD(Animation, resumeAnim, void(Animation this)); METHOD(Animation, isFinished, float(Animation this)); METHOD(Animation, finishAnim, void(Animation this)); - ATTRIB(Animation, object, entity, NULL) + ATTRIB(Animation, object, entity); void setterDummy(Animation this, float) {} - ATTRIB(Animation, setter, void(Animation this, float), setterDummy) - ATTRIB(Animation, value, float, 0) - ATTRIB(Animation, startTime, float, 0) - ATTRIB(Animation, duration, float, 0) - ATTRIB(Animation, startValue, float, 0) - ATTRIB(Animation, delta, float, 0) - ATTRIB(Animation, stopped, float, false) - ATTRIB(Animation, finished, float, false) + ATTRIB(Animation, setter, void(Animation this, float), setterDummy); + ATTRIB(Animation, value, float, 0); + ATTRIB(Animation, startTime, float, 0); + ATTRIB(Animation, duration, float, 0); + ATTRIB(Animation, startValue, float, 0); + ATTRIB(Animation, delta, float, 0); + ATTRIB(Animation, stopped, float, false); + ATTRIB(Animation, finished, float, false); ENDCLASS(Animation) diff --git a/qcsrc/menu/anim/animhost.qc b/qcsrc/menu/anim/animhost.qc index 5e39191dd6..3009ab4eaf 100644 --- a/qcsrc/menu/anim/animhost.qc +++ b/qcsrc/menu/anim/animhost.qc @@ -37,7 +37,7 @@ if (n) n.prevSibling = p; else this.lastChild = p; - remove(other); + delete(other); } METHOD(AnimHost, removeAllAnim, void(entity this)) diff --git a/qcsrc/menu/anim/animhost.qh b/qcsrc/menu/anim/animhost.qh index 1292b7d8b4..85b03aecf3 100644 --- a/qcsrc/menu/anim/animhost.qh +++ b/qcsrc/menu/anim/animhost.qh @@ -14,6 +14,6 @@ CLASS(AnimHost, Object) METHOD(AnimHost, finishAllAnim, void(entity)); METHOD(AnimHost, finishObjAnim, void(entity, entity)); METHOD(AnimHost, tickAll, void(entity)); - ATTRIB(AnimHost, firstChild, entity, NULL) - ATTRIB(AnimHost, lastChild, entity, NULL) + ATTRIB(AnimHost, firstChild, entity); + ATTRIB(AnimHost, lastChild, entity); ENDCLASS(AnimHost) diff --git a/qcsrc/menu/anim/easing.qh b/qcsrc/menu/anim/easing.qh index 832a87c73b..bb2c0dbc0a 100644 --- a/qcsrc/menu/anim/easing.qh +++ b/qcsrc/menu/anim/easing.qh @@ -10,5 +10,5 @@ float easingQuadInOut(float, float, float, float); CLASS(Easing, Animation) METHOD(Easing, calcValue, float(entity, float, float, float, float)); METHOD(Easing, setMath, void(entity, float(float, float, float, float))); - ATTRIB(Easing, math, float(float, float, float, float), easingLinear) + ATTRIB(Easing, math, float(float, float, float, float), easingLinear); ENDCLASS(Easing) diff --git a/qcsrc/menu/anim/keyframe.qh b/qcsrc/menu/anim/keyframe.qh index d21db6920b..abf18de96f 100644 --- a/qcsrc/menu/anim/keyframe.qh +++ b/qcsrc/menu/anim/keyframe.qh @@ -5,9 +5,9 @@ CLASS(Keyframe, Animation) METHOD(Keyframe, addEasing, entity(entity, float, float, float(float, float, float, float))); METHOD(Keyframe, addAnim, void(entity, entity)); METHOD(Keyframe, calcValue, float(entity, float, float, float, float)); - ATTRIB(Keyframe, currentChild, entity, NULL) - ATTRIB(Keyframe, firstChild, entity, NULL) - ATTRIB(Keyframe, lastChild, entity, NULL) + ATTRIB(Keyframe, currentChild, entity); + ATTRIB(Keyframe, firstChild, entity); + ATTRIB(Keyframe, lastChild, entity); ENDCLASS(Keyframe) entity makeHostedKeyframe(entity, void(entity, float), float, float, float); entity makeKeyframe(entity, void(entity, float), float, float, float); diff --git a/qcsrc/menu/auto-super.pl b/qcsrc/menu/auto-super.pl deleted file mode 100644 index 00926d0617..0000000000 --- a/qcsrc/menu/auto-super.pl +++ /dev/null @@ -1,101 +0,0 @@ -my %classoffile = (); -my %classes = (); -my %baseclass = (); -my %methods = (); -my %attrs = (); -my %methodnames = (); -my %old2new = (); - -print STDERR "Scanning...\n"; -for my $f(@ARGV) -{ - open my $fh, '<', $f; - while(<$fh>) - { - if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/) - { - $classes{$1} = defined($2) ? $2 : "Object"; - $classoffile{$f} = $1; - } - if(/^\s*METHOD\(([^),]*),\s*([^),]*)/) - { - $methods{$1}{$2} = $1; - $methodnames{"$1"."_"."$2"} = $f; - $old2new{"$2$1"} = "$1"."_"."$2"; - } - if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/) - { - $attrs{$1}{$2} = $1; - } - } - close $fh; -} - -# propagate down methods etc. -print STDERR "Propagating...\n"; -for my $class(keys %classes) -{ - print STDERR "$class"; - my $base = $class; - for(;;) - { - $base = $classes{$base}; - last if not defined $base; - print STDERR " -> $base"; - while(my ($method, $definingclass) = each %{$methods{$base}}) - { - $methods{$class}{$method} = $definingclass - if not defined $methods{$class}{$method}; - } - while(my ($attr, $definingclass) = each %{$attrs{$base}}) - { - $attrs{$class}{$attr} = $definingclass - if not defined $attrs{$class}{$attr}; - } - } - print STDERR "\n"; -} - -# change all calls to base method to super, complain about skipping -print STDERR "Fixing...\n"; -for my $f(@ARGV) -{ - open my $fh, '<', $f; - my $s = do { undef local $/; <$fh>; }; - my $s0 = $s; - close $fh; - - my $class = $classoffile{$f}; - my $base = $classes{$class}; - next if not defined $base; - - for(keys %old2new) - { - $s =~ s/\b$_\b/$old2new{$_}/g; - } - - my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}}; - for(@methods_super) - { - my ($search, $replace) = @$_; - my $n = ($s =~ s/\b$search\b/$replace/g); - print STDERR "[$f] $search -> $replace... $n replacements\n" - if $n; - } - - for(grep { $methodnames{$_} ne $f } keys %methodnames) - { - if($s =~ /\b$_\b/) - { - print STDERR "[$f] calls non-super external method directly: $_\n"; - } - } - - if($s ne $s0) - { - print STDERR "Rewriting $f...\n"; - open my $fh, '>', $f; - print $fh $s; - close $fh; - } -} diff --git a/qcsrc/menu/command/_mod.inc b/qcsrc/menu/command/_mod.inc index e721f357a3..0bcef50de7 100644 --- a/qcsrc/menu/command/_mod.inc +++ b/qcsrc/menu/command/_mod.inc @@ -1,3 +1,2 @@ // generated file; do not modify -#include #include diff --git a/qcsrc/menu/command/_mod.qh b/qcsrc/menu/command/_mod.qh index 5e66557301..91c0a8f35e 100644 --- a/qcsrc/menu/command/_mod.qh +++ b/qcsrc/menu/command/_mod.qh @@ -1,3 +1,2 @@ // generated file; do not modify -#include #include diff --git a/qcsrc/menu/command/all.qc b/qcsrc/menu/command/all.qc deleted file mode 100644 index 283cca0c1b..0000000000 --- a/qcsrc/menu/command/all.qc +++ /dev/null @@ -1 +0,0 @@ -#include diff --git a/qcsrc/menu/command/menu_cmd.qc b/qcsrc/menu/command/menu_cmd.qc index 0f87cbb095..72aadf6247 100644 --- a/qcsrc/menu/command/menu_cmd.qc +++ b/qcsrc/menu/command/menu_cmd.qc @@ -5,7 +5,7 @@ #include "../mutators/events.qh" -#include +#include .entity firstChild, nextSibling; diff --git a/qcsrc/menu/draw.qc b/qcsrc/menu/draw.qc index b2ce50382a..01184a4bf5 100644 --- a/qcsrc/menu/draw.qc +++ b/qcsrc/menu/draw.qc @@ -271,7 +271,7 @@ void draw_BorderPicture(vector theOrigin, string pic, vector theSize, vector the void draw_Text(vector theOrigin, string theText, vector theSize, vector theColor, float theAlpha, float ICanHasKallerz) { if(theSize.x <= 0 || theSize.y <= 0) { - LOG_TRACE("Drawing zero size text?\n"); + LOG_TRACE("Drawing zero size text?"); return; } diff --git a/qcsrc/menu/item.qh b/qcsrc/menu/item.qh index 118a454267..00dae2ed37 100644 --- a/qcsrc/menu/item.qh +++ b/qcsrc/menu/item.qh @@ -19,12 +19,12 @@ CLASS(Item, Object) 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, NULL) - ATTRIB(Item, preferredFocusPriority, float, 0) - ATTRIB(Item, origin, vector, '0 0 0') - ATTRIB(Item, size, vector, '0 0 0') - ATTRIB(Item, tooltip, string, string_null) + 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) diff --git a/qcsrc/menu/item/borderimage.qh b/qcsrc/menu/item/borderimage.qh index c956d362d2..9db9360f8f 100644 --- a/qcsrc/menu/item/borderimage.qh +++ b/qcsrc/menu/item/borderimage.qh @@ -5,21 +5,21 @@ CLASS(BorderImage, Label) METHOD(BorderImage, configureBorderImage, void(entity, string, float, vector, string, float)); METHOD(BorderImage, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(BorderImage, recalcPositionWithText, void(entity, string)); - ATTRIB(BorderImage, isBold, float, 1) + ATTRIB(BorderImage, isBold, float, 1); METHOD(BorderImage, draw, void(entity)); - ATTRIB(BorderImage, src, string, string_null) - ATTRIB(BorderImage, borderHeight, float, 0) - ATTRIB(BorderImage, borderVec, vector, '0 0 0') - ATTRIB(BorderImage, color, vector, '1 1 1') - ATTRIB(BorderImage, closeButton, entity, NULL) - ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0') - ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0') - ATTRIB(BorderImage, isNexposeeTitleBar, float, 0) - ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0) - ATTRIB(BorderImage, zoomedOutTitleBar, float, 0) - ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0') - ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0') - ATTRIB(BorderImage, saveRelSize, vector, '0 0 0') + ATTRIB(BorderImage, src, string); + ATTRIB(BorderImage, borderHeight, float, 0); + ATTRIB(BorderImage, borderVec, vector, '0 0 0'); + ATTRIB(BorderImage, color, vector, '1 1 1'); + ATTRIB(BorderImage, closeButton, entity); + ATTRIB(BorderImage, realFontSize_Nexposeed, vector, '0 0 0'); + ATTRIB(BorderImage, realOrigin_Nexposeed, vector, '0 0 0'); + ATTRIB(BorderImage, isNexposeeTitleBar, float, 0); + ATTRIB(BorderImage, zoomedOutTitleBarPosition, float, 0); + ATTRIB(BorderImage, zoomedOutTitleBar, float, 0); + ATTRIB(BorderImage, overrideRealOrigin, vector, '0 1 0'); + ATTRIB(BorderImage, saveRelOrigin, vector, '0 0 0'); + ATTRIB(BorderImage, saveRelSize, vector, '0 0 0'); ENDCLASS(BorderImage) .vector colorC, colorF; diff --git a/qcsrc/menu/item/button.qh b/qcsrc/menu/item/button.qh index 6fd2bc8882..1a164245c8 100644 --- a/qcsrc/menu/item/button.qh +++ b/qcsrc/menu/item/button.qh @@ -13,30 +13,30 @@ CLASS(Button, Label) METHOD(Button, mouseDrag, float(entity, vector)); METHOD(Button, mouseRelease, float(entity, vector)); METHOD(Button, playClickSound, void(entity)); - ATTRIB(Button, onClick, void(entity, entity), func_null) - ATTRIB(Button, onClickEntity, entity, NULL) - ATTRIB(Button, src, string, string_null) - ATTRIB(Button, srcSuffix, string, string_null) - ATTRIB(Button, src2, string, string_null) // is centered, same aspect, and stretched to label size - ATTRIB(Button, src2scale, float, 1) - ATTRIB(Button, srcMulti, float, 1) // 0: button square left, text right; 1: button stretched, text over it - ATTRIB(Button, buttonLeftOfText, float, 0) - ATTRIB(Button, focusable, float, 1) - ATTRIB(Button, allowFocusSound, float, 1) - ATTRIB(Button, pressed, float, 0) - ATTRIB(Button, clickTime, float, 0) - ATTRIB(Button, applyButton, entity, NULL) - ATTRIB(Button, disableOnClick, bool, false) - ATTRIB(Button, disabled, float, 0) - ATTRIB(Button, disabledAlpha, float, 0.3) - ATTRIB(Button, forcePressed, float, 0) - ATTRIB(Button, color, vector, '1 1 1') - ATTRIB(Button, colorC, vector, '1 1 1') - ATTRIB(Button, colorF, vector, '1 1 1') - ATTRIB(Button, colorD, vector, '1 1 1') - ATTRIB(Button, color2, vector, '1 1 1') - ATTRIB(Button, alpha2, float, 1) + ATTRIB(Button, onClick, void(entity, entity)); + ATTRIB(Button, onClickEntity, entity); + ATTRIB(Button, src, string); + ATTRIB(Button, srcSuffix, string); + ATTRIB(Button, src2, string); // is centered, same aspect, and stretched to label size + ATTRIB(Button, src2scale, float, 1); + ATTRIB(Button, srcMulti, float, 1); // 0: button square left, text right; 1: button stretched, text over it + ATTRIB(Button, buttonLeftOfText, float, 0); + ATTRIB(Button, focusable, float, 1); + ATTRIB(Button, allowFocusSound, float, 1); + ATTRIB(Button, pressed, float, 0); + ATTRIB(Button, clickTime, float, 0); + ATTRIB(Button, applyButton, entity); + ATTRIB(Button, disableOnClick, bool, false); + ATTRIB(Button, disabled, float, 0); + ATTRIB(Button, disabledAlpha, float, 0.3); + ATTRIB(Button, forcePressed, float, 0); + ATTRIB(Button, color, vector, '1 1 1'); + ATTRIB(Button, colorC, vector, '1 1 1'); + ATTRIB(Button, colorF, vector, '1 1 1'); + ATTRIB(Button, colorD, vector, '1 1 1'); + ATTRIB(Button, color2, vector, '1 1 1'); + ATTRIB(Button, alpha2, float, 1); - ATTRIB(Button, origin, vector, '0 0 0') - ATTRIB(Button, size, vector, '0 0 0') + ATTRIB(Button, origin, vector, '0 0 0'); + ATTRIB(Button, size, vector, '0 0 0'); ENDCLASS(Button) diff --git a/qcsrc/menu/item/checkbox.qh b/qcsrc/menu/item/checkbox.qh index 32f6b0f502..931e4f79c8 100644 --- a/qcsrc/menu/item/checkbox.qh +++ b/qcsrc/menu/item/checkbox.qh @@ -7,10 +7,10 @@ CLASS(CheckBox, Button) METHOD(CheckBox, playClickSound, void(entity)); METHOD(CheckBox, toString, string(entity)); METHOD(CheckBox, setChecked, void(entity, float)); - ATTRIB(CheckBox, useDownAsChecked, float, 0) - ATTRIB(CheckBox, checked, float, 0) + ATTRIB(CheckBox, useDownAsChecked, float, 0); + ATTRIB(CheckBox, checked, float, 0); void CheckBox_Click(entity me, entity other); - ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click) - ATTRIB(CheckBox, srcMulti, float, 0) - ATTRIB(CheckBox, disabled, float, 0) + ATTRIB(CheckBox, onClick, void(entity, entity), CheckBox_Click); + ATTRIB(CheckBox, srcMulti, float, 0); + ATTRIB(CheckBox, disabled, float, 0); ENDCLASS(CheckBox) diff --git a/qcsrc/menu/item/container.qh b/qcsrc/menu/item/container.qh index dbb4cf2211..8273f5ddde 100644 --- a/qcsrc/menu/item/container.qh +++ b/qcsrc/menu/item/container.qh @@ -25,12 +25,12 @@ CLASS(Container, Item) METHOD(Container, showNotify, void(entity)); METHOD(Container, hideNotify, void(entity)); METHOD(Container, preferredFocusedGrandChild, entity(entity)); - ATTRIB(Container, focusable, float, 0) - ATTRIB(Container, firstChild, entity, NULL) - ATTRIB(Container, lastChild, entity, NULL) - ATTRIB(Container, focusedChild, entity, NULL) - ATTRIB(Container, savedFocus, entity, NULL) - ATTRIB(Container, shown, float, 0) + ATTRIB(Container, focusable, float, 0); + ATTRIB(Container, firstChild, entity); + ATTRIB(Container, lastChild, entity); + ATTRIB(Container, focusedChild, entity); + ATTRIB(Container, savedFocus, entity); + ATTRIB(Container, shown, float, 0); METHOD(Container, enterSubitem, void(entity, entity)); METHOD(Container, enterLieSubitem, void(entity, vector, vector, vector, float)); diff --git a/qcsrc/menu/item/dialog.qc b/qcsrc/menu/item/dialog.qc index c4a78fa568..9e0ea95eba 100644 --- a/qcsrc/menu/item/dialog.qc +++ b/qcsrc/menu/item/dialog.qc @@ -103,8 +103,17 @@ void Dialog_close(entity me) { - if (me.parent.instanceOfNexposee) ExposeeCloseButton_Click(me, me.parent); - else if (me.parent.instanceOfModalController) DialogCloseButton_Click(me, me); + if (me.parent.instanceOfNexposee) + { + ExposeeCloseButton_Click(me, me.parent); + if(me.hideMenuOnClose) + { + me.hideMenuOnClose = false; + m_hide(); + } + } + else if (me.parent.instanceOfModalController) + DialogCloseButton_Click(me, me); } float Dialog_keyDown(entity me, float key, float ascii, float shift) @@ -118,5 +127,8 @@ return 1; } } - return SUPER(Dialog).keyDown(me, key, ascii, shift); + float r = SUPER(Dialog).keyDown(me, key, ascii, shift); + if (!me.closable && key == K_ESCAPE) + return 1; + return r; } diff --git a/qcsrc/menu/item/dialog.qh b/qcsrc/menu/item/dialog.qh index 17343e7cd2..6fa9b26ab3 100644 --- a/qcsrc/menu/item/dialog.qh +++ b/qcsrc/menu/item/dialog.qh @@ -30,43 +30,45 @@ CLASS(Dialog, InputContainer) METHOD(Dialog, TR, void(entity)); METHOD(Dialog, gotoRC, void(entity, float, float)); - ATTRIB(Dialog, isTabRoot, float, 1) - ATTRIB(Dialog, closeButton, entity, NULL) - ATTRIB(Dialog, intendedHeight, float, 0) - ATTRIB(Dialog, itemOrigin, vector, '0 0 0') - ATTRIB(Dialog, itemSize, vector, '0 0 0') - ATTRIB(Dialog, itemSpacing, vector, '0 0 0') - ATTRIB(Dialog, currentRow, float, 0) - ATTRIB(Dialog, currentColumn, float, 0) - ATTRIB(Dialog, firstColumn, float, 0) + ATTRIB(Dialog, isTabRoot, float, 1); + ATTRIB(Dialog, closeButton, entity); + ATTRIB(Dialog, intendedHeight, float, 0); + ATTRIB(Dialog, itemOrigin, vector, '0 0 0'); + ATTRIB(Dialog, itemSize, vector, '0 0 0'); + ATTRIB(Dialog, itemSpacing, vector, '0 0 0'); + ATTRIB(Dialog, currentRow, float, 0); + ATTRIB(Dialog, currentColumn, float, 0); + ATTRIB(Dialog, firstColumn, float, 0); // to be customized - ATTRIB(Dialog, closable, float, 1) - ATTRIB(Dialog, title, string, "Form1") // ;) - ATTRIB(Dialog, color, vector, '1 0.5 1') - ATTRIB(Dialog, intendedWidth, float, 0) - ATTRIB(Dialog, rows, float, 3) - ATTRIB(Dialog, columns, float, 2) + ATTRIB(Dialog, closable, float, 1); + ATTRIB(Dialog, title, string, "Form1"); + ATTRIB(Dialog, color, vector, '1 0.5 1'); + ATTRIB(Dialog, intendedWidth, float, 0); + ATTRIB(Dialog, rows, float, 3); + ATTRIB(Dialog, columns, float, 2); - ATTRIB(Dialog, marginTop, float, 0) // pixels - ATTRIB(Dialog, marginBottom, float, 0) // pixels - ATTRIB(Dialog, marginLeft, float, 0) // pixels - ATTRIB(Dialog, marginRight, float, 0) // pixels - ATTRIB(Dialog, columnSpacing, float, 0) // pixels - ATTRIB(Dialog, rowSpacing, float, 0) // pixels - ATTRIB(Dialog, rowHeight, float, 0) // pixels - ATTRIB(Dialog, titleHeight, float, 0) // pixels - ATTRIB(Dialog, titleFontSize, float, 0) // pixels; if 0, title causes no margin - ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0) - ATTRIB(Dialog, zoomedOutTitleBar, float, 0) + ATTRIB(Dialog, marginTop, float, 0); // pixels + ATTRIB(Dialog, marginBottom, float, 0); // pixels + ATTRIB(Dialog, marginLeft, float, 0); // pixels + ATTRIB(Dialog, marginRight, float, 0); // pixels + ATTRIB(Dialog, columnSpacing, float, 0); // pixels + ATTRIB(Dialog, rowSpacing, float, 0); // pixels + ATTRIB(Dialog, rowHeight, float, 0); // pixels + ATTRIB(Dialog, titleHeight, float, 0); // pixels + ATTRIB(Dialog, titleFontSize, float, 0); // pixels; if 0, title causes no margin + ATTRIB(Dialog, zoomedOutTitleBarPosition, float, 0); + ATTRIB(Dialog, zoomedOutTitleBar, float, 0); - ATTRIB(Dialog, requiresConnection, float, 0) // set to true if the dialog requires a connection to be opened + ATTRIB(Dialog, requiresConnection, float, 0); // set to true if the dialog requires a connection to be opened - ATTRIB(Dialog, backgroundImage, string, string_null) - ATTRIB(Dialog, borderLines, float, 1) - ATTRIB(Dialog, closeButtonImage, string, string_null) + ATTRIB(Dialog, backgroundImage, string); + ATTRIB(Dialog, borderLines, float, 1); + ATTRIB(Dialog, closeButtonImage, string); - ATTRIB(Dialog, frame, entity, NULL) + ATTRIB(Dialog, hideMenuOnClose, bool, false); + + ATTRIB(Dialog, frame, entity); ENDCLASS(Dialog) void Dialog_Close(entity button, entity me); diff --git a/qcsrc/menu/item/image.qc b/qcsrc/menu/item/image.qc index 06ac91f025..59405ec169 100644 --- a/qcsrc/menu/item/image.qc +++ b/qcsrc/menu/item/image.qc @@ -137,13 +137,13 @@ } return 1; } - void Image_setZoom(entity me, float z, float atMousePosition) + void Image_setZoom(entity me, float newzoom, float atMousePosition) { float prev_zoomFactor; prev_zoomFactor = me.zoomFactor; - if (z < 0) // multiply by the current zoomFactor (but can also snap to real dimensions or to box) + if (newzoom < 0) // multiply by the current zoomFactor (but can also snap to real dimensions or to box) { - me.zoomFactor *= -z; + me.zoomFactor *= -newzoom; float realSize_in_the_middle, boxSize_in_the_middle; realSize_in_the_middle = ((prev_zoomFactor - 1) * (me.zoomFactor - 1) < 0); boxSize_in_the_middle = (me.zoomBox > 0 && (prev_zoomFactor - me.zoomBox) * (me.zoomFactor - me.zoomBox) < 0); @@ -162,14 +162,14 @@ me.zoomFactor = me.zoomBox; // snap to box } } - else if (z == 0) // reset (no zoom) + else if (newzoom == 0) // reset (no zoom) { if (me.zoomBox > 0) me.zoomFactor = me.zoomBox; else me.zoomFactor = 1; } else // directly set { - me.zoomFactor = z; + me.zoomFactor = newzoom; } me.zoomFactor = bound(1 / 16, me.zoomFactor, 16); if (me.zoomMax > 0 && me.zoomFactor > me.zoomMax) me.zoomFactor = me.zoomMax; diff --git a/qcsrc/menu/item/image.qh b/qcsrc/menu/item/image.qh index 35bca5985a..726f328600 100644 --- a/qcsrc/menu/item/image.qh +++ b/qcsrc/menu/item/image.qh @@ -11,18 +11,18 @@ CLASS(Image, Item) METHOD(Image, setZoom, void(entity, float, float)); METHOD(Image, drag_setStartPos, float(entity, vector)); METHOD(Image, drag, float(entity, vector)); - ATTRIB(Image, src, string, string_null) - ATTRIB(Image, color, vector, '1 1 1') - ATTRIB(Image, forcedAspect, float, 0) // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size - ATTRIB(Image, zoomBox, float, 0) // used by forcedAspect -2 when the image is larger than the containing box - ATTRIB(Image, zoomFactor, float, 1) - ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0') - ATTRIB(Image, zoomSnapToTheBox, float, 1) // snap the zoomed in image to the box borders when zooming/dragging it - ATTRIB(Image, zoomTime, float, 0) - ATTRIB(Image, zoomLimitedByTheBox, float, 0) // forbids zoom if image would be larger than the containing box - ATTRIB(Image, zoomMax, float, 0) - ATTRIB(Image, start_zoomOffset, vector, '0 0 0') - ATTRIB(Image, start_coords, vector, '0 0 0') - ATTRIB(Image, imgOrigin, vector, '0 0 0') - ATTRIB(Image, imgSize, vector, '0 0 0') + ATTRIB(Image, src, string); + ATTRIB(Image, color, vector, '1 1 1'); + ATTRIB(Image, forcedAspect, float, 0); // special values: -1 keep image aspect ratio, -2 keep image size but bound to the containing box, -3 always keep image size + ATTRIB(Image, zoomBox, float, 0); // used by forcedAspect -2 when the image is larger than the containing box + ATTRIB(Image, zoomFactor, float, 1); + ATTRIB(Image, zoomOffset, vector, '0.5 0.5 0'); + ATTRIB(Image, zoomSnapToTheBox, float, 1); // snap the zoomed in image to the box borders when zooming/dragging it + ATTRIB(Image, zoomTime, float, 0); + ATTRIB(Image, zoomLimitedByTheBox, float, 0); // forbids zoom if image would be larger than the containing box + ATTRIB(Image, zoomMax, float, 0); + ATTRIB(Image, start_zoomOffset, vector, '0 0 0'); + ATTRIB(Image, start_coords, vector, '0 0 0'); + ATTRIB(Image, imgOrigin, vector, '0 0 0'); + ATTRIB(Image, imgSize, vector, '0 0 0'); ENDCLASS(Image) diff --git a/qcsrc/menu/item/inputbox.qh b/qcsrc/menu/item/inputbox.qh index cfb576cfaa..e3eede653b 100644 --- a/qcsrc/menu/item/inputbox.qh +++ b/qcsrc/menu/item/inputbox.qh @@ -14,31 +14,31 @@ CLASS(InputBox, Label) METHOD(InputBox, showNotify, void(entity)); METHOD(InputBox, resizeNotify, void(entity, vector, vector, vector, vector)); - ATTRIB(InputBox, src, string, string_null) + ATTRIB(InputBox, src, string); - ATTRIB(InputBox, cursorPos, float, 0) // characters - ATTRIB(InputBox, scrollPos, float, 0) // widths + ATTRIB(InputBox, cursorPos, float, 0); // characters + ATTRIB(InputBox, scrollPos, float, 0); // widths - ATTRIB(InputBox, focusable, float, 1) - ATTRIB(InputBox, allowFocusSound, float, 1) - ATTRIB(InputBox, disabled, float, 0) - ATTRIB(InputBox, lastChangeTime, float, 0) - ATTRIB(InputBox, dragScrollTimer, float, 0) - ATTRIB(InputBox, dragScrollPos, vector, '0 0 0') - ATTRIB(InputBox, pressed, float, 0) - ATTRIB(InputBox, editColorCodes, float, 1) - ATTRIB(InputBox, forbiddenCharacters, string, "") - ATTRIB(InputBox, color, vector, '1 1 1') - ATTRIB(InputBox, colorF, vector, '1 1 1') - ATTRIB(InputBox, maxLength, float, 255) // if negative, it counts bytes, not chars - ATTRIB(InputBox, applyButton, entity, NULL) + ATTRIB(InputBox, focusable, float, 1); + ATTRIB(InputBox, allowFocusSound, float, 1); + ATTRIB(InputBox, disabled, float, 0); + ATTRIB(InputBox, lastChangeTime, float, 0); + ATTRIB(InputBox, dragScrollTimer, float, 0); + ATTRIB(InputBox, dragScrollPos, vector, '0 0 0'); + ATTRIB(InputBox, pressed, float, 0); + ATTRIB(InputBox, editColorCodes, float, 1); + ATTRIB(InputBox, forbiddenCharacters, string, ""); + ATTRIB(InputBox, color, vector, '1 1 1'); + ATTRIB(InputBox, colorF, vector, '1 1 1'); + ATTRIB(InputBox, maxLength, float, 255); // if negative, it counts bytes, not chars + ATTRIB(InputBox, applyButton, entity); - ATTRIB(InputBox, enableClearButton, float, 1) - ATTRIB(InputBox, clearButton, entity, NULL) - ATTRIB(InputBox, cb_width, float, 0) - ATTRIB(InputBox, cb_pressed, float, 0) - ATTRIB(InputBox, cb_focused, float, 0) - ATTRIB(InputBox, cb_color, vector, '1 1 1') - ATTRIB(InputBox, cb_colorF, vector, '1 1 1') - ATTRIB(InputBox, cb_colorC, vector, '1 1 1') + ATTRIB(InputBox, enableClearButton, float, 1); + ATTRIB(InputBox, clearButton, entity); + ATTRIB(InputBox, cb_width, float, 0); + ATTRIB(InputBox, cb_pressed, float, 0); + ATTRIB(InputBox, cb_focused, float, 0); + ATTRIB(InputBox, cb_color, vector, '1 1 1'); + ATTRIB(InputBox, cb_colorF, vector, '1 1 1'); + ATTRIB(InputBox, cb_colorC, vector, '1 1 1'); ENDCLASS(InputBox) diff --git a/qcsrc/menu/item/inputcontainer.qc b/qcsrc/menu/item/inputcontainer.qc index 3840721e35..37c46240f7 100644 --- a/qcsrc/menu/item/inputcontainer.qc +++ b/qcsrc/menu/item/inputcontainer.qc @@ -20,7 +20,8 @@ float InputContainer_keyDown(entity me, float scan, float ascii, float shift) { entity f, ff; - if (SUPER(InputContainer).keyDown(me, scan, ascii, shift)) return 1; + if (SUPER(InputContainer).keyDown(me, scan, ascii, shift)) + return 1; if (scan == K_ESCAPE) { f = me.focusedChild; diff --git a/qcsrc/menu/item/inputcontainer.qh b/qcsrc/menu/item/inputcontainer.qh index 5d84189faf..9ae4a53750 100644 --- a/qcsrc/menu/item/inputcontainer.qh +++ b/qcsrc/menu/item/inputcontainer.qh @@ -11,6 +11,6 @@ CLASS(InputContainer, Container) METHOD(InputContainer, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(InputContainer, _changeFocusXY, bool(entity this, vector pos)); - ATTRIB(InputContainer, mouseFocusedChild, entity, NULL) - ATTRIB(InputContainer, isTabRoot, float, 0) + ATTRIB(InputContainer, mouseFocusedChild, entity); + ATTRIB(InputContainer, isTabRoot, float, 0); ENDCLASS(InputContainer) diff --git a/qcsrc/menu/item/label.qc b/qcsrc/menu/item/label.qc index ca9ec00b09..d21b5676bd 100644 --- a/qcsrc/menu/item/label.qc +++ b/qcsrc/menu/item/label.qc @@ -38,7 +38,7 @@ { if (!me.overrideRealOrigin_x) me.realOrigin_x = me.keepspaceLeft; if (!me.overrideCondenseFactor) me.condenseFactor = spaceAvail / spaceUsed; - LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f\n", t, me.condenseFactor); + LOG_TRACEF("NOTE: label text %s too wide for label, condensed by factor %f", t, me.condenseFactor); } if (!me.overrideRealOrigin_y) diff --git a/qcsrc/menu/item/label.qh b/qcsrc/menu/item/label.qh index 428958c201..f91ae8ad3c 100644 --- a/qcsrc/menu/item/label.qh +++ b/qcsrc/menu/item/label.qh @@ -8,27 +8,27 @@ CLASS(Label, Item) METHOD(Label, setText, void(entity, string)); METHOD(Label, toString, string(entity)); METHOD(Label, recalcPositionWithText, void(entity, string)); - ATTRIB(Label, isBold, float, 0) - ATTRIB(Label, text, string, string_null) - ATTRIB(Label, currentText, string, string_null) - ATTRIB(Label, fontSize, float, 8) - ATTRIB(Label, align, float, 0.5) - ATTRIB(Label, allowCut, float, 0) - ATTRIB(Label, allowColors, float, 0) - ATTRIB(Label, keepspaceLeft, float, 0) // for use by subclasses (radiobuttons for example) - ATTRIB(Label, keepspaceRight, float, 0) - ATTRIB(Label, marginLeft, float, 0) // alternate way to specify keepspace* (in characters from the font) - ATTRIB(Label, marginRight, float, 0) - ATTRIB(Label, realFontSize, vector, '0 0 0') - ATTRIB(Label, realOrigin, vector, '0 0 0') - ATTRIB(Label, alpha, float, 0.7) - ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT) - ATTRIB(Label, disabled, float, 0) - ATTRIB(Label, disabledAlpha, float, 0.3) - ATTRIB(Label, textEntity, entity, NULL) - ATTRIB(Label, allowWrap, float, 0) - ATTRIB(Label, recalcPos, float, 0) - ATTRIB(Label, condenseFactor, float, 1) - ATTRIB(Label, overrideRealOrigin, vector, '0 0 0') - ATTRIB(Label, overrideCondenseFactor, float, 0) + ATTRIB(Label, isBold, float, 0); + ATTRIB(Label, text, string); + ATTRIB(Label, currentText, string); + ATTRIB(Label, fontSize, float, 8); + ATTRIB(Label, align, float, 0.5); + ATTRIB(Label, allowCut, float, 0); + ATTRIB(Label, allowColors, float, 0); + ATTRIB(Label, keepspaceLeft, float, 0); // for use by subclasses (radiobuttons for example); + ATTRIB(Label, keepspaceRight, float, 0); + ATTRIB(Label, marginLeft, float, 0); // alternate way to specify keepspace* (in characters from the font); + ATTRIB(Label, marginRight, float, 0); + ATTRIB(Label, realFontSize, vector, '0 0 0'); + ATTRIB(Label, realOrigin, vector, '0 0 0'); + ATTRIB(Label, alpha, float, 0.7); + ATTRIB(Label, colorL, vector, SKINCOLOR_TEXT); + ATTRIB(Label, disabled, float, 0); + ATTRIB(Label, disabledAlpha, float, 0.3); + ATTRIB(Label, textEntity, entity); + ATTRIB(Label, allowWrap, float, 0); + ATTRIB(Label, recalcPos, float, 0); + ATTRIB(Label, condenseFactor, float, 1); + ATTRIB(Label, overrideRealOrigin, vector, '0 0 0'); + ATTRIB(Label, overrideCondenseFactor, float, 0); ENDCLASS(Label) diff --git a/qcsrc/menu/item/listbox.qc b/qcsrc/menu/item/listbox.qc index d9d63ab8c5..aa10c20b3b 100644 --- a/qcsrc/menu/item/listbox.qc +++ b/qcsrc/menu/item/listbox.qc @@ -379,17 +379,16 @@ oldshift = draw_shift; oldscale = draw_scale; - float y; i = me.getItemAtPos(me, me.scrollPos); - y = me.getItemStart(me, i) - me.scrollPos; - for ( ; i < me.nItems && y < 1; ++i) + float j = me.getItemStart(me, i) - me.scrollPos; + for ( ; i < me.nItems && j < 1; ++i) { - draw_shift = boxToGlobal(eY * y, oldshift, oldscale); + draw_shift = boxToGlobal(eY * j, oldshift, oldscale); vector relSize = eX * (1 - me.controlWidth) + eY * me.getItemHeight(me, i); absSize = boxToGlobalSize(relSize, me.size); draw_scale = boxToGlobalSize(relSize, oldscale); me.drawListBoxItem(me, i, absSize, (me.selectedItem == i), (me.focusedItem == i)); - y += relSize.y; + j += relSize.y; } draw_ClearClip(); diff --git a/qcsrc/menu/item/listbox.qh b/qcsrc/menu/item/listbox.qh index 461125e303..56fda91cc1 100644 --- a/qcsrc/menu/item/listbox.qh +++ b/qcsrc/menu/item/listbox.qh @@ -11,46 +11,46 @@ CLASS(ListBox, Item) METHOD(ListBox, mouseDrag, float(entity, vector)); METHOD(ListBox, mouseRelease, float(entity, vector)); METHOD(ListBox, focusLeave, void(entity)); - ATTRIB(ListBox, focusable, float, 1) - ATTRIB(ListBox, focusedItem, int, -1) - ATTRIB(ListBox, focusedItemAlpha, float, 0.3) + ATTRIB(ListBox, focusable, float, 1); + ATTRIB(ListBox, focusedItem, int, -1); + ATTRIB(ListBox, focusedItemAlpha, float, 0.3); METHOD(ListBox, setFocusedItem, void(entity, int)); - ATTRIB(ListBox, mouseMoveOffset, float, -1) // let know where the cursor is when the list scrolls without moving the cursor - ATTRIB(ListBox, allowFocusSound, float, 1) - ATTRIB(ListBox, selectedItem, int, 0) - ATTRIB(ListBox, size, vector, '0 0 0') - ATTRIB(ListBox, origin, vector, '0 0 0') - ATTRIB(ListBox, scrollPos, float, 0) // measured in window heights, fixed when needed - ATTRIB(ListBox, scrollPosTarget, float, 0) + ATTRIB(ListBox, mouseMoveOffset, float, -1); // let know where the cursor is when the list scrolls without moving the cursor + ATTRIB(ListBox, allowFocusSound, float, 1); + ATTRIB(ListBox, selectedItem, int, 0); + ATTRIB(ListBox, size, vector, '0 0 0'); + ATTRIB(ListBox, origin, vector, '0 0 0'); + ATTRIB(ListBox, scrollPos, float, 0); // measured in window heights, fixed when needed + ATTRIB(ListBox, scrollPosTarget, float, 0); METHOD(ListBox, isScrolling, bool(entity)); - ATTRIB(ListBox, needScrollToItem, float, -1) + ATTRIB(ListBox, needScrollToItem, float, -1); METHOD(ListBox, scrollToItem, void(entity, int)); - ATTRIB(ListBox, previousValue, float, 0) - ATTRIB(ListBox, pressed, float, 0) // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released - ATTRIB(ListBox, pressOffset, float, 0) + ATTRIB(ListBox, previousValue, float, 0); + ATTRIB(ListBox, pressed, float, 0); // 0 = normal, 1 = scrollbar dragging, 2 = item dragging, 3 = released + ATTRIB(ListBox, pressOffset, float, 0); METHOD(ListBox, updateControlTopBottom, void(entity)); - ATTRIB(ListBox, controlTop, float, 0) - ATTRIB(ListBox, controlBottom, float, 0) - ATTRIB(ListBox, controlWidth, float, 0) - ATTRIB(ListBox, dragScrollPos, vector, '0 0 0') - ATTRIB(ListBox, selectionDoesntMatter, bool, false) // improves scrolling by keys for lists that don't need to show an active selection + ATTRIB(ListBox, controlTop, float, 0); + ATTRIB(ListBox, controlBottom, float, 0); + ATTRIB(ListBox, controlWidth, float, 0); + ATTRIB(ListBox, dragScrollPos, vector, '0 0 0'); + ATTRIB(ListBox, selectionDoesntMatter, bool, false); // improves scrolling by keys for lists that don't need to show an active selection - ATTRIB(ListBox, src, string, string_null) // scrollbar - ATTRIB(ListBox, color, vector, '1 1 1') - ATTRIB(ListBox, color2, vector, '1 1 1') - ATTRIB(ListBox, colorC, vector, '1 1 1') - ATTRIB(ListBox, colorF, vector, '1 1 1') - ATTRIB(ListBox, tolerance, vector, '0 0 0') // drag tolerance - ATTRIB(ListBox, scrollbarWidth, float, 0) // pixels - ATTRIB(ListBox, nItems, float, 42) // FIXME: why?!? - ATTRIB(ListBox, itemHeight, float, 0) - ATTRIB(ListBox, itemAbsSize, vector, '0 0 0') - ATTRIB(ListBox, colorBG, vector, '0 0 0') - ATTRIB(ListBox, alphaBG, float, 0) + ATTRIB(ListBox, src, string); // scrollbar + ATTRIB(ListBox, color, vector, '1 1 1'); + ATTRIB(ListBox, color2, vector, '1 1 1'); + ATTRIB(ListBox, colorC, vector, '1 1 1'); + ATTRIB(ListBox, colorF, vector, '1 1 1'); + ATTRIB(ListBox, tolerance, vector, '0 0 0'); // drag tolerance + ATTRIB(ListBox, scrollbarWidth, float, 0); // pixels + ATTRIB(ListBox, nItems, float, 42); // FIXME: why?!? + ATTRIB(ListBox, itemHeight, float, 0); + ATTRIB(ListBox, itemAbsSize, vector, '0 0 0'); + ATTRIB(ListBox, colorBG, vector, '0 0 0'); + ATTRIB(ListBox, alphaBG, float, 0); - ATTRIB(ListBox, lastClickedItem, float, -1) - ATTRIB(ListBox, lastClickedTime, float, 0) + ATTRIB(ListBox, lastClickedItem, float, -1); + ATTRIB(ListBox, lastClickedTime, float, 0); METHOD(ListBox, drawListBoxItem, void(entity, int, vector, bool, bool)); // item number, width/height, isSelected, isFocused METHOD(ListBox, clickListBoxItem, void(entity, float, vector)); // item number, relative clickpos diff --git a/qcsrc/menu/item/modalcontroller.qh b/qcsrc/menu/item/modalcontroller.qh index 27faaa86bf..e950b8d87e 100644 --- a/qcsrc/menu/item/modalcontroller.qh +++ b/qcsrc/menu/item/modalcontroller.qh @@ -34,10 +34,10 @@ CLASS(ModalController, Container) METHOD(ModalController, initializeDialog, void(entity, entity)); METHOD(ModalController, switchState, void(entity, entity, float, float)); - ATTRIB(ModalController, origin, vector, '0 0 0') - ATTRIB(ModalController, size, vector, '0 0 0') - ATTRIB(ModalController, previousButton, entity, NULL) - ATTRIB(ModalController, fadedAlpha, float, 0.3) + ATTRIB(ModalController, origin, vector, '0 0 0'); + ATTRIB(ModalController, size, vector, '0 0 0'); + ATTRIB(ModalController, previousButton, entity); + ATTRIB(ModalController, fadedAlpha, float, 0.3); ENDCLASS(ModalController) .float ModalController_state; diff --git a/qcsrc/menu/item/nexposee.qh b/qcsrc/menu/item/nexposee.qh index 2d8e3ec49d..4159648f97 100644 --- a/qcsrc/menu/item/nexposee.qh +++ b/qcsrc/menu/item/nexposee.qh @@ -13,14 +13,14 @@ CLASS(Nexposee, Container) METHOD(Nexposee, focusEnter, void(entity)); METHOD(Nexposee, close, void(entity)); - ATTRIB(Nexposee, animationState, float, -1) - ATTRIB(Nexposee, animationFactor, float, 0) - ATTRIB(Nexposee, selectedChild, entity, NULL) - ATTRIB(Nexposee, mouseFocusedChild, entity, NULL) + ATTRIB(Nexposee, animationState, float, -1); + ATTRIB(Nexposee, animationFactor, float, 0); + ATTRIB(Nexposee, selectedChild, entity); + ATTRIB(Nexposee, mouseFocusedChild, entity); METHOD(Nexposee, addItem, void(entity, entity, vector, vector, float)); METHOD(Nexposee, calc, void(entity)); METHOD(Nexposee, setNexposee, void(entity, entity, vector, float, float)); - ATTRIB(Nexposee, mousePosition, vector, '0 0 0') + ATTRIB(Nexposee, mousePosition, vector, '0 0 0'); METHOD(Nexposee, pullNexposee, void(entity, entity, vector)); ENDCLASS(Nexposee) diff --git a/qcsrc/menu/item/radiobutton.qh b/qcsrc/menu/item/radiobutton.qh index 53aa7ff2e8..7bd7377876 100644 --- a/qcsrc/menu/item/radiobutton.qh +++ b/qcsrc/menu/item/radiobutton.qh @@ -4,8 +4,8 @@ void RadioButton_Click(entity me, entity other); CLASS(RadioButton, CheckBox) METHOD(RadioButton, configureRadioButton, void(entity, string, float, string, float, float)); - ATTRIB(RadioButton, checked, float, 0) - ATTRIB(RadioButton, group, float, 0) - ATTRIB(RadioButton, allowDeselect, float, 0) - ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click) + ATTRIB(RadioButton, checked, float, 0); + ATTRIB(RadioButton, group, float, 0); + ATTRIB(RadioButton, allowDeselect, float, 0); + ATTRIB(RadioButton, onClick, void(entity, entity), RadioButton_Click); ENDCLASS(RadioButton) diff --git a/qcsrc/menu/item/slider.qh b/qcsrc/menu/item/slider.qh index f5e8fd496f..3b7327788c 100644 --- a/qcsrc/menu/item/slider.qh +++ b/qcsrc/menu/item/slider.qh @@ -20,31 +20,31 @@ CLASS(Slider, Label) METHOD(Slider, setValue, void(entity, float)); METHOD(Slider, setSliderValue, void(entity, float)); METHOD(Slider, showNotify, void(entity)); - ATTRIB(Slider, src, string, string_null) - ATTRIB(Slider, focusable, float, 1) - ATTRIB(Slider, allowFocusSound, float, 1) - ATTRIB(Slider, value, float, 0) - ATTRIB(Slider, animated, float, 1) - ATTRIB(Slider, sliderValue, float, 0) - ATTRIB(Slider, sliderAnim, entity, NULL) - ATTRIB(Slider, valueMin, float, 0) - ATTRIB(Slider, valueMax, float, 0) - ATTRIB(Slider, valueStep, float, 0) - ATTRIB(Slider, valueDigits, float, 0) - ATTRIB(Slider, valueKeyStep, float, 0) - ATTRIB(Slider, valuePageStep, float, 0) - ATTRIB(Slider, valueDisplayMultiplier, float, 1.0) - ATTRIB(Slider, textSpace, float, 0) - ATTRIB(Slider, controlWidth, float, 0) - ATTRIB(Slider, pressed, float, 0) - ATTRIB(Slider, pressOffset, float, 0) - ATTRIB(Slider, previousValue, float, 0) - ATTRIB(Slider, tolerance, vector, '0 0 0') - ATTRIB(Slider, disabled, float, 0) - ATTRIB(Slider, color, vector, '1 1 1') - ATTRIB(Slider, color2, vector, '1 1 1') - ATTRIB(Slider, colorD, vector, '1 1 1') - ATTRIB(Slider, colorC, vector, '1 1 1') - ATTRIB(Slider, colorF, vector, '1 1 1') - ATTRIB(Slider, disabledAlpha, float, 0.3) + ATTRIB(Slider, src, string); + ATTRIB(Slider, focusable, float, 1); + ATTRIB(Slider, allowFocusSound, float, 1); + ATTRIB(Slider, value, float, 0); + ATTRIB(Slider, animated, float, 1); + ATTRIB(Slider, sliderValue, float, 0); + ATTRIB(Slider, sliderAnim, entity); + ATTRIB(Slider, valueMin, float, 0); + ATTRIB(Slider, valueMax, float, 0); + ATTRIB(Slider, valueStep, float, 0); + ATTRIB(Slider, valueDigits, float, 0); + ATTRIB(Slider, valueKeyStep, float, 0); + ATTRIB(Slider, valuePageStep, float, 0); + ATTRIB(Slider, valueDisplayMultiplier, float, 1.0); + ATTRIB(Slider, textSpace, float, 0); + ATTRIB(Slider, controlWidth, float, 0); + ATTRIB(Slider, pressed, float, 0); + ATTRIB(Slider, pressOffset, float, 0); + ATTRIB(Slider, previousValue, float, 0); + ATTRIB(Slider, tolerance, vector, '0 0 0'); + ATTRIB(Slider, disabled, float, 0); + ATTRIB(Slider, color, vector, '1 1 1'); + ATTRIB(Slider, color2, vector, '1 1 1'); + ATTRIB(Slider, colorD, vector, '1 1 1'); + ATTRIB(Slider, colorC, vector, '1 1 1'); + ATTRIB(Slider, colorF, vector, '1 1 1'); + ATTRIB(Slider, disabledAlpha, float, 0.3); ENDCLASS(Slider) diff --git a/qcsrc/menu/item/tab.qh b/qcsrc/menu/item/tab.qh index e9ba81a903..18c9e49597 100644 --- a/qcsrc/menu/item/tab.qh +++ b/qcsrc/menu/item/tab.qh @@ -2,25 +2,25 @@ #include "dialog.qh" CLASS(Tab, Dialog) - ATTRIB(Tab, isTabRoot, float, 0) - ATTRIB(Tab, closable, float, 0) - ATTRIB(Tab, rootDialog, float, 0) - ATTRIB(Tab, title, string, string_null) - ATTRIB(Tab, titleFontSize, float, 0) // pixels + ATTRIB(Tab, isTabRoot, float, 0); + ATTRIB(Tab, closable, float, 0); + ATTRIB(Tab, rootDialog, float, 0); + ATTRIB(Tab, title, string); + ATTRIB(Tab, titleFontSize, float, 0); // pixels // still to be customized - ATTRIB(Tab, intendedWidth, float, 0) - ATTRIB(Tab, rows, float, 3) - ATTRIB(Tab, columns, float, 2) + ATTRIB(Tab, intendedWidth, float, 0); + ATTRIB(Tab, rows, float, 3); + ATTRIB(Tab, columns, float, 2); - ATTRIB(Tab, marginTop, float, 0) // pixels - ATTRIB(Tab, marginBottom, float, 0) // pixels - ATTRIB(Tab, marginLeft, float, 0) // pixels - ATTRIB(Tab, marginRight, float, 0) // pixels - ATTRIB(Tab, columnSpacing, float, 0) // pixels - ATTRIB(Tab, rowSpacing, float, 0) // pixels - ATTRIB(Tab, rowHeight, float, 0) // pixels - ATTRIB(Tab, titleHeight, float, 0) // pixels + ATTRIB(Tab, marginTop, float, 0); // pixels + ATTRIB(Tab, marginBottom, float, 0); // pixels + ATTRIB(Tab, marginLeft, float, 0); // pixels + ATTRIB(Tab, marginRight, float, 0); // pixels + ATTRIB(Tab, columnSpacing, float, 0); // pixels + ATTRIB(Tab, rowSpacing, float, 0); // pixels + ATTRIB(Tab, rowHeight, float, 0); // pixels + ATTRIB(Tab, titleHeight, float, 0); // pixels - ATTRIB(Tab, backgroundImage, string, string_null) + ATTRIB(Tab, backgroundImage, string); ENDCLASS(Tab) diff --git a/qcsrc/menu/item/textslider.qh b/qcsrc/menu/item/textslider.qh index 0703942f95..9a6a586a16 100644 --- a/qcsrc/menu/item/textslider.qh +++ b/qcsrc/menu/item/textslider.qh @@ -14,7 +14,7 @@ CLASS(TextSlider, Slider) METHOD(TextSlider, addValue, void(entity, string, string)); METHOD(TextSlider, insertValue, void(entity, float, string, string)); METHOD(TextSlider, configureTextSliderValues, void(entity, string)); - ATTRIBARRAY(TextSlider, valueStrings, string, 256) - ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256) - ATTRIB(TextSlider, nValues, int, 0) + ATTRIBARRAY(TextSlider, valueStrings, string, 256); + ATTRIBARRAY(TextSlider, valueIdentifiers, string, 256); + ATTRIB(TextSlider, nValues, int, 0); ENDCLASS(TextSlider) diff --git a/qcsrc/menu/matrix.qc b/qcsrc/menu/matrix.qc index b0c4ec880c..3039b4a7d4 100644 --- a/qcsrc/menu/matrix.qc +++ b/qcsrc/menu/matrix.qc @@ -1,3 +1,5 @@ +#include "matrix.qh" + var void MX_Handle(int buf, string ancestor) { string type = json_get(buf, strcat(ancestor, ".type")); diff --git a/qcsrc/menu/matrix.qh b/qcsrc/menu/matrix.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/menu/matrix.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/menu/menu.qc b/qcsrc/menu/menu.qc index 96d98b9c50..f0e6646ca9 100644 --- a/qcsrc/menu/menu.qc +++ b/qcsrc/menu/menu.qc @@ -17,8 +17,8 @@ #include "xonotic/util.qh" -#include "../common/items/all.qh" -#include +#include "../common/items/_mod.qh" +#include #include "../common/mapinfo.qh" #include "../common/mutators/base.qh" @@ -237,7 +237,7 @@ void m_keyup(float key, float ascii) if (mouseButtonsPressed < 0) { mouseButtonsPressed = 0; - LOG_TRACE("Warning: released an already released button\n"); + LOG_TRACE("Warning: released an already released button"); } } if (key == K_ALT) menuShiftState &= ~S_ALT; @@ -270,7 +270,8 @@ void m_keydown(float key, float ascii) else { draw_reset_cropped(); - if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3) main.mousePress(main, menuMousePos); + if (!mouseButtonsPressed && key >= K_MOUSE1 && key <= K_MOUSE3) + main.mousePress(main, menuMousePos); if (!main.keyDown(main, key, ascii, menuShiftState)) { // disable menu on unhandled ESC @@ -285,7 +286,7 @@ void m_keydown(float key, float ascii) if (mouseButtonsPressed > 10) { mouseButtonsPressed = 10; - LOG_TRACE("Warning: pressed an already pressed button\n"); + LOG_TRACE("Warning: pressed an already pressed button"); } } if (key == K_ALT) menuShiftState |= S_ALT; @@ -848,6 +849,7 @@ void m_draw(float width, float height) postMenuDraw(); frametime = 0; + IL_ENDFRAME(); } void m_display() @@ -975,6 +977,8 @@ void m_goto(string itemname) if ((e) && (!e.requiresConnection || (gamestatus & (GAME_ISSERVER | GAME_CONNECTED)))) { + if(!Menu_Active) + e.hideMenuOnClose = true; m_hide(); m_activate_window(e); m_setpointerfocus(e); diff --git a/qcsrc/menu/progs.inc b/qcsrc/menu/progs.inc index 9b15d8caa6..404c6f2e5a 100644 --- a/qcsrc/menu/progs.inc +++ b/qcsrc/menu/progs.inc @@ -1,14 +1,9 @@ #include -#include "../menu/_mod.inc" -#include "anim/_mod.inc" -#include "command/_mod.inc" -#include "item/_mod.inc" -#include "mutators/_mod.inc" -#include "xonotic/_mod.inc" - -#include +#if XONOTIC +#include +#endif -#if BUILD_MOD -#include "../../mod/menu/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/menu/skin.qh b/qcsrc/menu/skin.qh index 7cc2d2d3b4..4287e24e59 100644 --- a/qcsrc/menu/skin.qh +++ b/qcsrc/menu/skin.qh @@ -19,7 +19,7 @@ //#define SKINSTRING(name,def) case #name: break #define SKINSTRING(name,def) case #name: SKIN##name = strzone(_value); break // I know this leaks memory when skin is read multiple times. Screw it. -#define SKINEND case "": break; case "//": break; default: LOG_TRACE("Invalid key in skin file: ", key, "\n"); } } +#define SKINEND case "": break; case "//": break; default: LOG_TRACE("Invalid key in skin file: ", key); } } #include "skin-customizables.inc" #undef SKINEND #undef SKINSTRING diff --git a/qcsrc/menu/xonotic/_mod.inc b/qcsrc/menu/xonotic/_mod.inc index 867f77b840..577c822580 100644 --- a/qcsrc/menu/xonotic/_mod.inc +++ b/qcsrc/menu/xonotic/_mod.inc @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/menu/xonotic/_mod.qh b/qcsrc/menu/xonotic/_mod.qh index adfefc05d7..b6e34eff24 100644 --- a/qcsrc/menu/xonotic/_mod.qh +++ b/qcsrc/menu/xonotic/_mod.qh @@ -79,6 +79,7 @@ #include #include #include +#include #include #include #include diff --git a/qcsrc/menu/xonotic/bigbutton.qh b/qcsrc/menu/xonotic/bigbutton.qh index 14b6893a7d..b34014bc38 100644 --- a/qcsrc/menu/xonotic/bigbutton.qh +++ b/qcsrc/menu/xonotic/bigbutton.qh @@ -3,7 +3,7 @@ #include "button.qh" CLASS(XonoticBigButton, XonoticButton) METHOD(XonoticBigButton, configureXonoticBigButton, void(entity, string, vector)); - ATTRIB(XonoticBigButton, image, string, SKINGFX_BUTTON_BIG) - ATTRIB(XonoticBigButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY) + ATTRIB(XonoticBigButton, image, string, SKINGFX_BUTTON_BIG); + ATTRIB(XonoticBigButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY); ENDCLASS(XonoticBigButton) entity makeXonoticBigButton(string theText, vector theColor); diff --git a/qcsrc/menu/xonotic/bigcommandbutton.qh b/qcsrc/menu/xonotic/bigcommandbutton.qh index 4713fa3709..b6bb782aee 100644 --- a/qcsrc/menu/xonotic/bigcommandbutton.qh +++ b/qcsrc/menu/xonotic/bigcommandbutton.qh @@ -3,8 +3,8 @@ #include "commandbutton.qh" CLASS(XonoticBigCommandButton, XonoticCommandButton) METHOD(XonoticBigCommandButton, configureXonoticBigCommandButton, void(entity, string, vector, string, float, string)); - ATTRIB(XonoticBigCommandButton, image, string, SKINGFX_BUTTON_BIG) - ATTRIB(XonoticBigCommandButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY) + ATTRIB(XonoticBigCommandButton, image, string, SKINGFX_BUTTON_BIG); + ATTRIB(XonoticBigCommandButton, grayImage, string, SKINGFX_BUTTON_BIG_GRAY); ENDCLASS(XonoticBigCommandButton) entity makeXonoticBigCommandButton_T(string theText, vector theColor, string theCommand, float closesMenu, string theTooltip); entity makeXonoticBigCommandButton(string theText, vector theColor, string theCommand, float closesMenu); diff --git a/qcsrc/menu/xonotic/button.qh b/qcsrc/menu/xonotic/button.qh index c7e85fabe0..e4a520d2b6 100644 --- a/qcsrc/menu/xonotic/button.qh +++ b/qcsrc/menu/xonotic/button.qh @@ -3,17 +3,17 @@ #include "../item/button.qh" CLASS(XonoticButton, Button) METHOD(XonoticButton, configureXonoticButton, void(entity, string, vector, string)); - ATTRIB(XonoticButton, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticButton, image, string, SKINGFX_BUTTON) - ATTRIB(XonoticButton, grayImage, string, SKINGFX_BUTTON_GRAY) - ATTRIB(XonoticButton, color, vector, SKINCOLOR_BUTTON_N) - ATTRIB(XonoticButton, colorC, vector, SKINCOLOR_BUTTON_C) - ATTRIB(XonoticButton, colorF, vector, SKINCOLOR_BUTTON_F) - ATTRIB(XonoticButton, colorD, vector, SKINCOLOR_BUTTON_D) - ATTRIB(XonoticButton, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticButton, disabledAlpha, float, SKINALPHA_DISABLED) - ATTRIB(XonoticButton, marginLeft, float, SKINMARGIN_BUTTON) // chars - ATTRIB(XonoticButton, marginRight, float, SKINMARGIN_BUTTON) // chars + ATTRIB(XonoticButton, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticButton, image, string, SKINGFX_BUTTON); + ATTRIB(XonoticButton, grayImage, string, SKINGFX_BUTTON_GRAY); + ATTRIB(XonoticButton, color, vector, SKINCOLOR_BUTTON_N); + ATTRIB(XonoticButton, colorC, vector, SKINCOLOR_BUTTON_C); + ATTRIB(XonoticButton, colorF, vector, SKINCOLOR_BUTTON_F); + ATTRIB(XonoticButton, colorD, vector, SKINCOLOR_BUTTON_D); + ATTRIB(XonoticButton, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticButton, disabledAlpha, float, SKINALPHA_DISABLED); + ATTRIB(XonoticButton, marginLeft, float, SKINMARGIN_BUTTON); // chars + ATTRIB(XonoticButton, marginRight, float, SKINMARGIN_BUTTON); // chars ENDCLASS(XonoticButton) entity makeXonoticButton_T(string theText, vector theColor, string theTooltip); diff --git a/qcsrc/menu/xonotic/campaign.qh b/qcsrc/menu/xonotic/campaign.qh index 17585a86d4..784926f7df 100644 --- a/qcsrc/menu/xonotic/campaign.qh +++ b/qcsrc/menu/xonotic/campaign.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticCampaignList, XonoticListBox) METHOD(XonoticCampaignList, configureXonoticCampaignList, void(entity)); - ATTRIB(XonoticCampaignList, rowsPerItem, float, 10) + ATTRIB(XonoticCampaignList, rowsPerItem, float, 10); METHOD(XonoticCampaignList, draw, void(entity)); METHOD(XonoticCampaignList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticCampaignList, doubleClickListBoxItem, void(entity, float, vector)); @@ -13,31 +13,31 @@ CLASS(XonoticCampaignList, XonoticListBox) METHOD(XonoticCampaignList, campaignGo, void(entity, float)); METHOD(XonoticCampaignList, destroy, void(entity)); - ATTRIB(XonoticCampaignList, campaignGlob, float, 0) - ATTRIB(XonoticCampaignList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticCampaignList, columnPreviewOrigin, float, 0) - ATTRIB(XonoticCampaignList, columnPreviewSize, float, 0) - ATTRIB(XonoticCampaignList, columnNameOrigin, float, 0) - ATTRIB(XonoticCampaignList, columnNameSize, float, 0) - ATTRIB(XonoticCampaignList, columnCheckMarkOrigin, float, 0) - ATTRIB(XonoticCampaignList, columnCheckMarkSize, float, 0) - ATTRIB(XonoticCampaignList, checkMarkOrigin, vector, '0 0 0') - ATTRIB(XonoticCampaignList, checkMarkSize, vector, '0 0 0') - ATTRIB(XonoticCampaignList, realUpperMargin1, float, 0) - ATTRIB(XonoticCampaignList, realUpperMargin2, float, 0) + ATTRIB(XonoticCampaignList, campaignGlob, float, 0); + ATTRIB(XonoticCampaignList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticCampaignList, columnPreviewOrigin, float, 0); + ATTRIB(XonoticCampaignList, columnPreviewSize, float, 0); + ATTRIB(XonoticCampaignList, columnNameOrigin, float, 0); + ATTRIB(XonoticCampaignList, columnNameSize, float, 0); + ATTRIB(XonoticCampaignList, columnCheckMarkOrigin, float, 0); + ATTRIB(XonoticCampaignList, columnCheckMarkSize, float, 0); + ATTRIB(XonoticCampaignList, checkMarkOrigin, vector, '0 0 0'); + ATTRIB(XonoticCampaignList, checkMarkSize, vector, '0 0 0'); + ATTRIB(XonoticCampaignList, realUpperMargin1, float, 0); + ATTRIB(XonoticCampaignList, realUpperMargin2, float, 0); - ATTRIB(XonoticCampaignList, origin, vector, '0 0 0') - ATTRIB(XonoticCampaignList, itemAbsSize, vector, '0 0 0') - ATTRIB(XonoticCampaignList, emptyLineHeight, float, 0.5) + ATTRIB(XonoticCampaignList, origin, vector, '0 0 0'); + ATTRIB(XonoticCampaignList, itemAbsSize, vector, '0 0 0'); + ATTRIB(XonoticCampaignList, emptyLineHeight, float, 0.5); - ATTRIB(XonoticCampaignList, campaignIndex, float, 0) - ATTRIB(XonoticCampaignList, cvarName, string, string_null) + ATTRIB(XonoticCampaignList, campaignIndex, float, 0); + ATTRIB(XonoticCampaignList, cvarName, string); METHOD(XonoticCampaignList, loadCvars, void(entity)); METHOD(XonoticCampaignList, saveCvars, void(entity)); - ATTRIB(XonoticCampaignList, buttonNext, entity, NULL) - ATTRIB(XonoticCampaignList, buttonPrev, entity, NULL) - ATTRIB(XonoticCampaignList, labelTitle, entity, NULL) + ATTRIB(XonoticCampaignList, buttonNext, entity); + ATTRIB(XonoticCampaignList, buttonPrev, entity); + ATTRIB(XonoticCampaignList, labelTitle, entity); ENDCLASS(XonoticCampaignList) entity makeXonoticCampaignList(); void CampaignList_LoadMap(entity btn, entity me); diff --git a/qcsrc/menu/xonotic/charmap.qh b/qcsrc/menu/xonotic/charmap.qh index c8d5b31349..3993e1dbc0 100644 --- a/qcsrc/menu/xonotic/charmap.qh +++ b/qcsrc/menu/xonotic/charmap.qh @@ -6,15 +6,15 @@ CLASS(XonoticCharmap, XonoticPicker) METHOD(XonoticCharmap, focusLeave, void(entity)); METHOD(XonoticCharmap, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticCharmap, keyDown, float(entity, float, float, float)); - ATTRIB(XonoticCharmap, inputBox, entity, NULL) - ATTRIB(XonoticCharmap, realFontSize, vector, '0 0 0') + ATTRIB(XonoticCharmap, inputBox, entity); + ATTRIB(XonoticCharmap, realFontSize, vector, '0 0 0'); - ATTRIB(XonoticCharmap, rows, float, 10) - ATTRIB(XonoticCharmap, columns, float, 14) + ATTRIB(XonoticCharmap, rows, float, 10); + ATTRIB(XonoticCharmap, columns, float, 14); METHOD(XonoticCharmap, cellSelect, void(entity, vector)); METHOD(XonoticCharmap, cellIsValid, bool(entity, vector)); METHOD(XonoticCharmap, cellDraw, void(entity, vector, vector)); - ATTRIB(XonoticCharmap, charOffset, vector, '0 0 0') + ATTRIB(XonoticCharmap, charOffset, vector, '0 0 0'); ENDCLASS(XonoticCharmap) entity makeXonoticCharmap(entity controlledInputBox); diff --git a/qcsrc/menu/xonotic/checkbox.qc b/qcsrc/menu/xonotic/checkbox.qc index b863518981..949b01c41d 100644 --- a/qcsrc/menu/xonotic/checkbox.qc +++ b/qcsrc/menu/xonotic/checkbox.qc @@ -2,28 +2,28 @@ entity makeXonoticCheckBox_T(float isInverted, string theCvar, string theText, string theTooltip) { - float y, n; + float m, n; if(isInverted > 1) { n = isInverted - 1; - y = -n; + m = -n; } else if(isInverted < -1) { n = isInverted + 1; - y = -n; + m = -n; } else if(isInverted == 1) { n = 1; - y = 0; + m = 0; } else { n = 0; - y = 1; + m = 1; } - return makeXonoticCheckBoxEx_T(y, n, theCvar, theText, theTooltip); + return makeXonoticCheckBoxEx_T(m, n, theCvar, theText, theTooltip); } entity makeXonoticCheckBox(float isInverted, string theCvar, string theText) { diff --git a/qcsrc/menu/xonotic/checkbox.qh b/qcsrc/menu/xonotic/checkbox.qh index 90a1bf5d4b..e46c9ce4a0 100644 --- a/qcsrc/menu/xonotic/checkbox.qh +++ b/qcsrc/menu/xonotic/checkbox.qh @@ -4,24 +4,24 @@ CLASS(XonoticCheckBox, CheckBox) METHOD(XonoticCheckBox, configureXonoticCheckBox, void(entity, float, float, string, string, string)); METHOD(XonoticCheckBox, setChecked, void(entity, float)); - ATTRIB(XonoticCheckBox, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticCheckBox, image, string, SKINGFX_CHECKBOX) - ATTRIB(XonoticCheckBox, yesValue, float, 1) - ATTRIB(XonoticCheckBox, noValue, float, 0) + ATTRIB(XonoticCheckBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticCheckBox, image, string, SKINGFX_CHECKBOX); + ATTRIB(XonoticCheckBox, yesValue, float, 1); + ATTRIB(XonoticCheckBox, noValue, float, 0); - ATTRIB(XonoticCheckBox, color, vector, SKINCOLOR_CHECKBOX_N) - ATTRIB(XonoticCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C) - ATTRIB(XonoticCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F) - ATTRIB(XonoticCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D) + ATTRIB(XonoticCheckBox, color, vector, SKINCOLOR_CHECKBOX_N); + ATTRIB(XonoticCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C); + ATTRIB(XonoticCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F); + ATTRIB(XonoticCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D); - ATTRIB(XonoticCheckBox, cvarName, string, string_null) + ATTRIB(XonoticCheckBox, cvarName, string); METHOD(XonoticCheckBox, loadCvars, void(entity)); METHOD(XonoticCheckBox, saveCvars, void(entity)); - ATTRIB(XonoticCheckBox, sendCvars, float, 0) + ATTRIB(XonoticCheckBox, sendCvars, float, 0); - ATTRIB(XonoticCheckBox, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticCheckBox, disabledAlpha, float, SKINALPHA_DISABLED) - ATTRIB(XonoticCheckBox, linkedCheckBox, entity, NULL) + ATTRIB(XonoticCheckBox, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticCheckBox, disabledAlpha, float, SKINALPHA_DISABLED); + ATTRIB(XonoticCheckBox, linkedCheckBox, entity); ENDCLASS(XonoticCheckBox) entity makeXonoticCheckBox_T(float, string, string, string); entity makeXonoticCheckBox(float, string, string); diff --git a/qcsrc/menu/xonotic/checkbox_slider_invalid.qh b/qcsrc/menu/xonotic/checkbox_slider_invalid.qh index d9c144c6ab..259c2104fe 100644 --- a/qcsrc/menu/xonotic/checkbox_slider_invalid.qh +++ b/qcsrc/menu/xonotic/checkbox_slider_invalid.qh @@ -5,20 +5,20 @@ CLASS(XonoticSliderCheckBox, CheckBox) METHOD(XonoticSliderCheckBox, configureXonoticSliderCheckBox, void(entity, float, float, entity, string)); METHOD(XonoticSliderCheckBox, setChecked, void(entity, float)); METHOD(XonoticSliderCheckBox, draw, void(entity)); - ATTRIB(XonoticSliderCheckBox, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticSliderCheckBox, image, string, SKINGFX_CHECKBOX) + ATTRIB(XonoticSliderCheckBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticSliderCheckBox, image, string, SKINGFX_CHECKBOX); - ATTRIB(XonoticSliderCheckBox, color, vector, SKINCOLOR_CHECKBOX_N) - ATTRIB(XonoticSliderCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C) - ATTRIB(XonoticSliderCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F) - ATTRIB(XonoticSliderCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D) + ATTRIB(XonoticSliderCheckBox, color, vector, SKINCOLOR_CHECKBOX_N); + ATTRIB(XonoticSliderCheckBox, colorC, vector, SKINCOLOR_CHECKBOX_C); + ATTRIB(XonoticSliderCheckBox, colorF, vector, SKINCOLOR_CHECKBOX_F); + ATTRIB(XonoticSliderCheckBox, colorD, vector, SKINCOLOR_CHECKBOX_D); - ATTRIB(XonoticSliderCheckBox, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticSliderCheckBox, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticSliderCheckBox, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticSliderCheckBox, disabledAlpha, float, SKINALPHA_DISABLED); - ATTRIB(XonoticSliderCheckBox, controlledSlider, entity, NULL) - ATTRIB(XonoticSliderCheckBox, offValue, float, -1) - ATTRIB(XonoticSliderCheckBox, inverted, float, 0) - ATTRIB(XonoticSliderCheckBox, savedValue, float, -1) + ATTRIB(XonoticSliderCheckBox, controlledSlider, entity); + ATTRIB(XonoticSliderCheckBox, offValue, float, -1); + ATTRIB(XonoticSliderCheckBox, inverted, float, 0); + ATTRIB(XonoticSliderCheckBox, savedValue, float, -1); ENDCLASS(XonoticSliderCheckBox) entity makeXonoticSliderCheckBox(float, float, entity, string); diff --git a/qcsrc/menu/xonotic/checkbox_string.qh b/qcsrc/menu/xonotic/checkbox_string.qh index b4dc8e5d20..e73a7144fc 100644 --- a/qcsrc/menu/xonotic/checkbox_string.qh +++ b/qcsrc/menu/xonotic/checkbox_string.qh @@ -4,22 +4,22 @@ CLASS(XonoticCheckBoxString, CheckBox) METHOD(XonoticCheckBoxString, configureXonoticCheckBoxString, void(entity, string, string, string, string)); METHOD(XonoticCheckBoxString, setChecked, void(entity, float)); - ATTRIB(XonoticCheckBoxString, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticCheckBoxString, image, string, SKINGFX_CHECKBOX) - ATTRIB(XonoticCheckBoxString, yesString, string, string_null) - ATTRIB(XonoticCheckBoxString, noString, string, string_null) + ATTRIB(XonoticCheckBoxString, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticCheckBoxString, image, string, SKINGFX_CHECKBOX); + ATTRIB(XonoticCheckBoxString, yesString, string); + ATTRIB(XonoticCheckBoxString, noString, string); - ATTRIB(XonoticCheckBoxString, color, vector, SKINCOLOR_CHECKBOX_N) - ATTRIB(XonoticCheckBoxString, colorC, vector, SKINCOLOR_CHECKBOX_C) - ATTRIB(XonoticCheckBoxString, colorF, vector, SKINCOLOR_CHECKBOX_F) - ATTRIB(XonoticCheckBoxString, colorD, vector, SKINCOLOR_CHECKBOX_D) + ATTRIB(XonoticCheckBoxString, color, vector, SKINCOLOR_CHECKBOX_N); + ATTRIB(XonoticCheckBoxString, colorC, vector, SKINCOLOR_CHECKBOX_C); + ATTRIB(XonoticCheckBoxString, colorF, vector, SKINCOLOR_CHECKBOX_F); + ATTRIB(XonoticCheckBoxString, colorD, vector, SKINCOLOR_CHECKBOX_D); - ATTRIB(XonoticCheckBoxString, cvarName, string, string_null) + ATTRIB(XonoticCheckBoxString, cvarName, string); METHOD(XonoticCheckBoxString, loadCvars, void(entity)); METHOD(XonoticCheckBoxString, saveCvars, void(entity)); - ATTRIB(XonoticCheckBoxString, sendCvars, float, 0) + ATTRIB(XonoticCheckBoxString, sendCvars, float, 0); - ATTRIB(XonoticCheckBoxString, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticCheckBoxString, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticCheckBoxString, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticCheckBoxString, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticCheckBoxString) entity makeXonoticCheckBoxString(string, string, string, string); diff --git a/qcsrc/menu/xonotic/colorbutton.qh b/qcsrc/menu/xonotic/colorbutton.qh index c253f8f112..9ff9639c58 100644 --- a/qcsrc/menu/xonotic/colorbutton.qh +++ b/qcsrc/menu/xonotic/colorbutton.qh @@ -5,14 +5,14 @@ CLASS(XonoticColorButton, RadioButton) METHOD(XonoticColorButton, configureXonoticColorButton, void(entity, float, float, float)); METHOD(XonoticColorButton, setChecked, void(entity, float)); METHOD(XonoticColorButton, draw, void(entity)); - ATTRIB(XonoticColorButton, fontSize, float, 0) - ATTRIB(XonoticColorButton, image, string, SKINGFX_COLORBUTTON) + ATTRIB(XonoticColorButton, fontSize, float, 0); + ATTRIB(XonoticColorButton, image, string, SKINGFX_COLORBUTTON); - ATTRIB(XonoticColorButton, useDownAsChecked, float, 1) + ATTRIB(XonoticColorButton, useDownAsChecked, float, 1); - ATTRIB(XonoticColorButton, cvarPart, float, 0) - ATTRIB(XonoticColorButton, cvarName, string, string_null) - ATTRIB(XonoticColorButton, cvarValueFloat, float, 0) + ATTRIB(XonoticColorButton, cvarPart, float, 0); + ATTRIB(XonoticColorButton, cvarName, string); + ATTRIB(XonoticColorButton, cvarValueFloat, float, 0); METHOD(XonoticColorButton, loadCvars, void(entity)); METHOD(XonoticColorButton, saveCvars, void(entity)); ENDCLASS(XonoticColorButton) diff --git a/qcsrc/menu/xonotic/colorpicker.qc b/qcsrc/menu/xonotic/colorpicker.qc index b88d5d9a7d..357276e16a 100644 --- a/qcsrc/menu/xonotic/colorpicker.qc +++ b/qcsrc/menu/xonotic/colorpicker.qc @@ -70,7 +70,7 @@ float XonoticColorpicker_mouseDrag(entity me, vector coords) while (i - 2 - carets >= 0 && substring(me.controlledTextbox.text, i - 2 - carets, 1) == "^") ++carets; if (carets & 1) - if(strstrofs("0123456789", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0) + if(IS_DIGIT(substring(me.controlledTextbox.text, i-1, 1))) { me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0); me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0); @@ -87,9 +87,9 @@ float XonoticColorpicker_mouseDrag(entity me, vector coords) while (i - 5 - carets >= 0 && substring(me.controlledTextbox.text, i - 5 - carets, 1) == "^") ++carets; if (carets & 1) - if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-3, 1), 0) >= 0) - if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-2, 1), 0) >= 0) - if(strstrofs("0123456789abcdefABCDEF", substring(me.controlledTextbox.text, i-1, 1), 0) >= 0) + if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 3, 1))) + if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 2, 1))) + if(IS_HEXDIGIT(substring(me.controlledTextbox.text, i - 1, 1))) { me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0); me.controlledTextbox.keyDown(me.controlledTextbox, K_BACKSPACE, 8, 0); diff --git a/qcsrc/menu/xonotic/colorpicker.qh b/qcsrc/menu/xonotic/colorpicker.qh index 03567dfa45..b1c9d1e80a 100644 --- a/qcsrc/menu/xonotic/colorpicker.qh +++ b/qcsrc/menu/xonotic/colorpicker.qh @@ -6,10 +6,10 @@ CLASS(XonoticColorpicker, Image) METHOD(XonoticColorpicker, mousePress, float(entity, vector)); METHOD(XonoticColorpicker, mouseRelease, float(entity, vector)); METHOD(XonoticColorpicker, mouseDrag, float(entity, vector)); - ATTRIB(XonoticColorpicker, controlledTextbox, entity, NULL) - ATTRIB(XonoticColorpicker, image, string, SKINGFX_COLORPICKER) - ATTRIB(XonoticColorpicker, imagemargin, vector, SKINMARGIN_COLORPICKER) - ATTRIB(XonoticColorpicker, focusable, float, 1) + ATTRIB(XonoticColorpicker, controlledTextbox, entity); + ATTRIB(XonoticColorpicker, image, string, SKINGFX_COLORPICKER); + ATTRIB(XonoticColorpicker, imagemargin, vector, SKINMARGIN_COLORPICKER); + ATTRIB(XonoticColorpicker, focusable, float, 1); METHOD(XonoticColorpicker, focusLeave, void(entity)); METHOD(XonoticColorpicker, keyDown, float(entity, float, float, float)); METHOD(XonoticColorpicker, draw, void(entity)); diff --git a/qcsrc/menu/xonotic/colorpicker_string.qh b/qcsrc/menu/xonotic/colorpicker_string.qh index 3caf9d9bf0..e990234b3f 100644 --- a/qcsrc/menu/xonotic/colorpicker_string.qh +++ b/qcsrc/menu/xonotic/colorpicker_string.qh @@ -9,15 +9,15 @@ CLASS(XonoticColorpickerString, Image) METHOD(XonoticColorpickerString, mouseRelease, float(entity, vector)); METHOD(XonoticColorpickerString, mouseDrag, float(entity, vector)); - ATTRIB(XonoticColorpickerString, cvarName, string, string_null) + ATTRIB(XonoticColorpickerString, cvarName, string); METHOD(XonoticColorpickerString, loadCvars, void(entity)); METHOD(XonoticColorpickerString, saveCvars, void(entity)); - ATTRIB(XonoticColorpickerString, prevcoords, vector, '0 0 0') - ATTRIB(XonoticColorpickerString, image, string, SKINGFX_COLORPICKER) - ATTRIB(XonoticColorpickerString, imagemargin, vector, SKINMARGIN_COLORPICKER) - ATTRIB(XonoticColorpickerString, focusable, float, 1) + ATTRIB(XonoticColorpickerString, prevcoords, vector, '0 0 0'); + ATTRIB(XonoticColorpickerString, image, string, SKINGFX_COLORPICKER); + ATTRIB(XonoticColorpickerString, imagemargin, vector, SKINMARGIN_COLORPICKER); + ATTRIB(XonoticColorpickerString, focusable, float, 1); METHOD(XonoticColorpickerString, draw, void(entity)); - ATTRIB(XonoticColorpickerString, disabledAlpha, float, 0.3) + ATTRIB(XonoticColorpickerString, disabledAlpha, float, 0.3); ENDCLASS(XonoticColorpickerString) entity makeXonoticColorpickerString(string theCvar, string theDefaultCvar); diff --git a/qcsrc/menu/xonotic/commandbutton.qh b/qcsrc/menu/xonotic/commandbutton.qh index 072890ab1e..5de59ee07b 100644 --- a/qcsrc/menu/xonotic/commandbutton.qh +++ b/qcsrc/menu/xonotic/commandbutton.qh @@ -3,8 +3,8 @@ #include "button.qh" CLASS(XonoticCommandButton, XonoticButton) METHOD(XonoticCommandButton, configureXonoticCommandButton, void(entity, string, vector, string, float, string)); - ATTRIB(XonoticCommandButton, onClickCommand, string, string_null) - ATTRIB(XonoticCommandButton, flags, float, 0) + ATTRIB(XonoticCommandButton, onClickCommand, string); + ATTRIB(XonoticCommandButton, flags, float, 0); ENDCLASS(XonoticCommandButton) entity makeXonoticCommandButton_T(string theText, vector theColor, string theCommand, float closesMenu, string theTooltip); diff --git a/qcsrc/menu/xonotic/credits.qc b/qcsrc/menu/xonotic/credits.qc index d30ab3d447..a28b086386 100644 --- a/qcsrc/menu/xonotic/credits.qc +++ b/qcsrc/menu/xonotic/credits.qc @@ -14,17 +14,21 @@ PERSON(Zac "Mario" Jardine) \ NL() \ TITLE(_("Extended Team")) \ + PERSON(AllieWay) \ PERSON(Antonio "terencehill" Piu) \ PERSON(Archer) \ PERSON(BuddyFriendGuy) \ PERSON(Debugger) \ + PERSON(Diomedes) \ + PERSON(Freddy) \ PERSON(GATTS) \ PERSON(Halogene) \ PERSON(IDWMaster) \ PERSON(Jan "zykure" Behrens) \ PERSON(JH0nny) \ - PERSON(Luigi) \ + PERSON(Jubilant) \ PERSON(Łukasz "kuniu the frogg" Polek) \ + PERSON(martin-t) \ PERSON(Matthias "matthiaskrgr" Krüger) \ PERSON(Mattia "Melanosuchus" Basaglia) \ PERSON(MrBougo) \ @@ -64,10 +68,10 @@ FUNCTION(_("Level Design")) \ PERSON(Amadeusz "amade/proraide" Sławiński) \ PERSON(Ben "MooKow" Banker) \ - PERSON(Calinou) \ PERSON(Cortez) \ PERSON(Cuinn "Cuinnton" Herrick) \ PERSON(Debugger) \ + PERSON(Hugo "Calinou" Locurcio) \ PERSON(Jakob "tZork" Markström Gröhn) \ PERSON(Konrad "Justin" Slawinski) \ PERSON(Maddin) \ @@ -149,14 +153,24 @@ PERSON(Mihail "meequz" Varantsou) \ NL() \ FUNCTION(_("Bulgarian")) \ + PERSON(Alexander "alex4o" Bonin) \ + PERSON(ifohancroft) \ PERSON(lokster) \ PERSON(set_killer) \ + PERSON(ubone) \ NL() \ FUNCTION(_("Chinese (China)")) \ - PERSON(Antonidas) \ PERSON(kalawore) \ PERSON(sapphireliu) \ NL() \ + FUNCTION(_("Chinese (Taiwan)")) \ + PERSON(Alisha) \ + PERSON(Armcoon) \ + PERSON(Jeff "s8321414" Huang) \ + NL() \ + FUNCTION(_("Cornish")) \ + PERSON(Nicky "nrowe" Rowe) \ + NL() \ FUNCTION(_("Czech")) \ PERSON(shogun assassin/woky) \ PERSON(Superovoce) \ @@ -164,21 +178,26 @@ NL() \ FUNCTION(_("Dutch")) \ PERSON(Alexander "freefang" van Dam) \ + PERSON(Jonathan "Jonakeys" van der Steege) \ PERSON(PinkRobot) \ PERSON(vegiburger) \ NL() \ FUNCTION(_("English (Australia)")) \ PERSON(Laurene "sunflowers" Albrand) \ + PERSON(Stuart "Cefiar" Young) \ PERSON(Zac "Mario" Jardine) \ NL() \ FUNCTION(_("Finnish")) \ + PERSON(Jonas "PowaTree" Sahlberg) \ PERSON(Henry "Exitium" Sanmark) \ PERSON(Rasmus "FruitieX" Eskola) \ NL() \ FUNCTION(_("French")) \ - PERSON(Calinou) \ + PERSON(Hugo "Calinou" Locurcio) \ + PERSON(Kim "coughingmouse" Lee) \ PERSON(Maxime "Taximus" Paradis) \ PERSON(RedGuff) \ + PERSON(Thomas "illwieckz" Debesse) \ PERSON(Yannick "SpiKe" Le Guen) \ NL() \ FUNCTION(_("German")) \ @@ -187,7 +206,9 @@ PERSON(Erik "Ablu" Schilling) \ PERSON(Jope "Sless" Withers) \ PERSON(Marvin "Mirio" Beck) \ + PERSON(Paul "Snapper") \ PERSON(Rudolf "divVerent" Polzer) \ + PERSON(Wuzzy) \ PERSON(Yepoleb) \ NL() \ FUNCTION(_("Greek")) \ @@ -206,12 +227,23 @@ PERSON(stdi) \ PERSON(XCostaX) \ NL() \ + FUNCTION(_("Kazakh")) \ + PERSON("Артем \"bystrov.arterm\" Быстров") \ + NL() \ + FUNCTION(_("Korean")) \ + PERSON(Jisoo "s6e9x" Lim) \ + PERSON(Kim "coughingmouse" Lee) \ + NL() \ FUNCTION(_("Polish")) \ PERSON(4m) \ PERSON(Alex "tiprogrammierer.alex" Progger) \ PERSON(Amadeusz "amade/proraide" Sławiński) \ + PERSON(Artur "artur9010" Motyka) \ + PERSON(Jakub "KubeQ11" Pędziszewski) \ NL() \ FUNCTION(_("Portuguese")) \ + PERSON(Ivan Paulos "greylica" Tomé) \ + PERSON(Jean Trindade "Muleke_Trairao" Pereira) \ PERSON(Ricardo Manuel "Hellgardia" da Cruz Coelho da Silva) \ PERSON(xXxCHAOTICxXx) \ NL() \ @@ -219,12 +251,14 @@ PERSON(Adrian-Ciprian "adrian.tinjala" Tînjală) \ PERSON(BusterDBK) \ PERSON(Mircea "Taoki" Kitsune) \ + PERSON(Sorin "unic_sorin" Botirla) \ PERSON(Tudor "TropiKo" Ionel) \ NL() \ FUNCTION(_("Russian")) \ PERSON(Alex "alextalker7" Talker) \ PERSON(Alexandr "zrg") \ PERSON(Andrei "adem4ik" Stepanov) \ + PERSON(Andrey "dekrY" P.) \ PERSON(gravicappa) \ PERSON(Hot Dog) \ PERSON(Lord Canistra) \ @@ -251,6 +285,7 @@ PERSON(marcus256) \ NL() \ FUNCTION(_("Ukrainian")) \ + PERSON(Dmitro "Gamebot" Sokhin) \ PERSON(Oleh "BlaXpirit" Prypin) \ PERSON(Vasyl "Harmata" Melnyk) \ PERSON(Yuriy "herrniemand" Ackermann) \ @@ -402,7 +437,6 @@ void XonoticCreditsList_resizeNotify(entity me, vector relOrigin, vector relSize } void XonoticCreditsList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused) { - // layout: Ping, Credits name, Map name, NP, TP, MP string s; float theAlpha; vector theColor; diff --git a/qcsrc/menu/xonotic/credits.qh b/qcsrc/menu/xonotic/credits.qh index d376beaa77..8abc12c199 100644 --- a/qcsrc/menu/xonotic/credits.qh +++ b/qcsrc/menu/xonotic/credits.qh @@ -3,20 +3,20 @@ #include "listbox.qh" CLASS(XonoticCreditsList, XonoticListBox) METHOD(XonoticCreditsList, configureXonoticCreditsList, void(entity)); - ATTRIB(XonoticCreditsList, rowsPerItem, float, 1) + ATTRIB(XonoticCreditsList, rowsPerItem, float, 1); METHOD(XonoticCreditsList, draw, void(entity)); METHOD(XonoticCreditsList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticCreditsList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticCreditsList, keyDown, float(entity, float, float, float)); METHOD(XonoticCreditsList, destroy, void(entity)); - ATTRIB(XonoticCreditsList, selectionDoesntMatter, bool, true) + ATTRIB(XonoticCreditsList, selectionDoesntMatter, bool, true); - ATTRIB(XonoticCreditsList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticCreditsList, realUpperMargin, float, 0) - ATTRIB(XonoticCreditsList, bufferIndex, float, 0) - ATTRIB(XonoticCreditsList, scrolling, float, 0) + ATTRIB(XonoticCreditsList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticCreditsList, realUpperMargin, float, 0); + ATTRIB(XonoticCreditsList, bufferIndex, float, 0); + ATTRIB(XonoticCreditsList, scrolling, float, 0); - ATTRIB(XonoticCreditsList, alphaBG, float, 0) + ATTRIB(XonoticCreditsList, alphaBG, float, 0); ENDCLASS(XonoticCreditsList) entity makeXonoticCreditsList(); diff --git a/qcsrc/menu/xonotic/crosshairpicker.qh b/qcsrc/menu/xonotic/crosshairpicker.qh index b6f39457d2..78cb5df0c5 100644 --- a/qcsrc/menu/xonotic/crosshairpicker.qh +++ b/qcsrc/menu/xonotic/crosshairpicker.qh @@ -4,8 +4,8 @@ CLASS(XonoticCrosshairPicker, XonoticPicker) METHOD(XonoticCrosshairPicker, configureXonoticCrosshairPicker, void(entity)); - ATTRIB(XonoticCrosshairPicker, rows, float, 3) - ATTRIB(XonoticCrosshairPicker, columns, float, 12) + ATTRIB(XonoticCrosshairPicker, rows, float, 3); + ATTRIB(XonoticCrosshairPicker, columns, float, 12); METHOD(XonoticCrosshairPicker, cellSelect, void(entity, vector)); METHOD(XonoticCrosshairPicker, cellIsValid, bool(entity, vector)); diff --git a/qcsrc/menu/xonotic/crosshairpreview.qh b/qcsrc/menu/xonotic/crosshairpreview.qh index dcc7be5ac6..7bf363def7 100644 --- a/qcsrc/menu/xonotic/crosshairpreview.qh +++ b/qcsrc/menu/xonotic/crosshairpreview.qh @@ -4,9 +4,9 @@ CLASS(XonoticCrosshairPreview, Item) METHOD(XonoticCrosshairPreview, configureXonoticCrosshairPreview, void(entity)); METHOD(XonoticCrosshairPreview, draw, void(entity)); - ATTRIB(XonoticCrosshairPreview, src, string, string_null) - ATTRIB(XonoticCrosshairPreview, src2, string, string_null) - ATTRIB(XonoticCrosshairPreview, disabled, float, 0) - ATTRIB(XonoticCrosshairPreview, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticCrosshairPreview, src, string); + ATTRIB(XonoticCrosshairPreview, src2, string); + ATTRIB(XonoticCrosshairPreview, disabled, float, 0); + ATTRIB(XonoticCrosshairPreview, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticCrosshairPreview) entity makeXonoticCrosshairPreview(); diff --git a/qcsrc/menu/xonotic/cvarlist.qh b/qcsrc/menu/xonotic/cvarlist.qh index 2c99866cf7..1260ae447a 100644 --- a/qcsrc/menu/xonotic/cvarlist.qh +++ b/qcsrc/menu/xonotic/cvarlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticCvarList, XonoticListBox) METHOD(XonoticCvarList, configureXonoticCvarList, void(entity)); - ATTRIB(XonoticCvarList, rowsPerItem, float, 1) + ATTRIB(XonoticCvarList, rowsPerItem, float, 1); METHOD(XonoticCvarList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticCvarList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticCvarList, keyDown, float(entity, float, float, float)); @@ -12,30 +12,30 @@ CLASS(XonoticCvarList, XonoticListBox) METHOD(XonoticCvarList, destroy, void(entity)); - ATTRIB(XonoticCvarList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticCvarList, realUpperMargin, float, 0) - ATTRIB(XonoticCvarList, columnNameOrigin, float, 0) - ATTRIB(XonoticCvarList, columnNameSize, float, 0) - ATTRIB(XonoticCvarList, columnValueOrigin, float, 0) - ATTRIB(XonoticCvarList, columnValueSize, float, 0) + ATTRIB(XonoticCvarList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticCvarList, realUpperMargin, float, 0); + ATTRIB(XonoticCvarList, columnNameOrigin, float, 0); + ATTRIB(XonoticCvarList, columnNameSize, float, 0); + ATTRIB(XonoticCvarList, columnValueOrigin, float, 0); + ATTRIB(XonoticCvarList, columnValueSize, float, 0); METHOD(XonoticCvarList, mouseRelease, float(entity, vector)); METHOD(XonoticCvarList, setSelected, void(entity, float)); METHOD(XonoticCvarList, updateCvarType, float(entity)); - ATTRIB(XonoticCvarList, controlledTextbox, entity, NULL) - ATTRIB(XonoticCvarList, cvarNameBox, entity, NULL) - ATTRIB(XonoticCvarList, cvarDescriptionBox, entity, NULL) - ATTRIB(XonoticCvarList, cvarTypeBox, entity, NULL) - ATTRIB(XonoticCvarList, cvarValueBox, entity, NULL) - ATTRIB(XonoticCvarList, cvarDefaultBox, entity, NULL) - ATTRIB(XonoticCvarList, cvarNeedsForcing, float, 0) + ATTRIB(XonoticCvarList, controlledTextbox, entity); + ATTRIB(XonoticCvarList, cvarNameBox, entity); + ATTRIB(XonoticCvarList, cvarDescriptionBox, entity); + ATTRIB(XonoticCvarList, cvarTypeBox, entity); + ATTRIB(XonoticCvarList, cvarValueBox, entity); + ATTRIB(XonoticCvarList, cvarDefaultBox, entity); + ATTRIB(XonoticCvarList, cvarNeedsForcing, float, 0); - ATTRIB(XonoticCvarList, handle, float, -1) - ATTRIB(XonoticCvarList, cvarName, string, string_null) - ATTRIB(XonoticCvarList, cvarDescription, string, string_null) - ATTRIB(XonoticCvarList, cvarType, string, string_null) - ATTRIB(XonoticCvarList, cvarDefault, string, string_null) + ATTRIB(XonoticCvarList, handle, float, -1); + ATTRIB(XonoticCvarList, cvarName, string); + ATTRIB(XonoticCvarList, cvarDescription, string); + ATTRIB(XonoticCvarList, cvarType, string); + ATTRIB(XonoticCvarList, cvarDefault, string); ENDCLASS(XonoticCvarList) entity makeXonoticCvarList(); void CvarList_Filter_Change(entity box, entity me); diff --git a/qcsrc/menu/xonotic/datasource.qh b/qcsrc/menu/xonotic/datasource.qh index 1ca3d5811f..87c614ab73 100644 --- a/qcsrc/menu/xonotic/datasource.qh +++ b/qcsrc/menu/xonotic/datasource.qh @@ -19,15 +19,15 @@ ENDCLASS(DataSource) CLASS(StringSource, DataSource) - ATTRIB(StringSource, StringSource_str, string, string_null) - ATTRIB(StringSource, StringSource_sep, string, string_null) + ATTRIB(StringSource, StringSource_str, string); + ATTRIB(StringSource, StringSource_sep, string); CONSTRUCTOR(StringSource, string str, string sep); METHOD(StringSource, getEntry, entity(entity this, int i, void(string name, string icon) returns)); METHOD(StringSource, reload, int(entity this, string filter)); ENDCLASS(StringSource) CLASS(CvarStringSource, StringSource) - ATTRIB(CvarStringSource, CvarStringSource_cvar, string, string_null) + ATTRIB(CvarStringSource, CvarStringSource_cvar, string); CONSTRUCTOR(CvarStringSource, string cv, string sep); METHOD(CvarStringSource, getEntry, entity(entity this, int i, void(string name, string icon) returns)); METHOD(CvarStringSource, reload, int(entity this, string filter)); diff --git a/qcsrc/menu/xonotic/demolist.qh b/qcsrc/menu/xonotic/demolist.qh index 547bf6695d..6341db1a3e 100644 --- a/qcsrc/menu/xonotic/demolist.qh +++ b/qcsrc/menu/xonotic/demolist.qh @@ -4,7 +4,7 @@ #include "listbox.qh" CLASS(XonoticDemoList, XonoticListBox) METHOD(XonoticDemoList, configureXonoticDemoList, void(entity)); - ATTRIB(XonoticDemoList, rowsPerItem, float, 1) + ATTRIB(XonoticDemoList, rowsPerItem, float, 1); METHOD(XonoticDemoList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticDemoList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticDemoList, getDemos, void(entity)); @@ -16,15 +16,15 @@ CLASS(XonoticDemoList, XonoticListBox) METHOD(XonoticDemoList, destroy, void(entity)); METHOD(XonoticDemoList, showNotify, void(entity)); - ATTRIB(XonoticDemoList, listDemo, float, -1) - ATTRIB(XonoticDemoList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticDemoList, columnNameOrigin, float, 0) - ATTRIB(XonoticDemoList, columnNameSize, float, 0) - ATTRIB(XonoticDemoList, realUpperMargin, float, 0) - ATTRIB(XonoticDemoList, origin, vector, '0 0 0') - ATTRIB(XonoticDemoList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticDemoList, listDemo, float, -1); + ATTRIB(XonoticDemoList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticDemoList, columnNameOrigin, float, 0); + ATTRIB(XonoticDemoList, columnNameSize, float, 0); + ATTRIB(XonoticDemoList, realUpperMargin, float, 0); + ATTRIB(XonoticDemoList, origin, vector, '0 0 0'); + ATTRIB(XonoticDemoList, itemAbsSize, vector, '0 0 0'); - ATTRIB(XonoticDemoList, filterString, string, string_null) + ATTRIB(XonoticDemoList, filterString, string); ENDCLASS(XonoticDemoList) entity demolist; // for reference elsewhere diff --git a/qcsrc/menu/xonotic/dialog.qh b/qcsrc/menu/xonotic/dialog.qh index a3612698ff..847233d6ac 100644 --- a/qcsrc/menu/xonotic/dialog.qh +++ b/qcsrc/menu/xonotic/dialog.qh @@ -4,30 +4,30 @@ CLASS(XonoticDialog, Dialog) // still to be customized by user /* - ATTRIB(XonoticDialog, closable, float, 1) - ATTRIB(XonoticDialog, title, string, _("Form1")) // ;) - ATTRIB(XonoticDialog, color, vector, '1 0.5 1') - ATTRIB(XonoticDialog, intendedWidth, float, 0) - ATTRIB(XonoticDialog, rows, float, 3) - ATTRIB(XonoticDialog, columns, float, 2) + ATTRIB(XonoticDialog, closable, float, 1); + ATTRIB(XonoticDialog, title, string, _("Form1")); // ;); + ATTRIB(XonoticDialog, color, vector, '1 0.5 1'); + ATTRIB(XonoticDialog, intendedWidth, float, 0); + ATTRIB(XonoticDialog, rows, float, 3); + ATTRIB(XonoticDialog, columns, float, 2); */ - ATTRIB(XonoticDialog, marginTop, float, SKINMARGIN_TOP) // pixels - ATTRIB(XonoticDialog, marginBottom, float, SKINMARGIN_BOTTOM) // pixels - ATTRIB(XonoticDialog, marginLeft, float, SKINMARGIN_LEFT) // pixels - ATTRIB(XonoticDialog, marginRight, float, SKINMARGIN_RIGHT) // pixels - ATTRIB(XonoticDialog, columnSpacing, float, SKINMARGIN_COLUMNS) // pixels - ATTRIB(XonoticDialog, rowSpacing, float, SKINMARGIN_ROWS) // pixels - ATTRIB(XonoticDialog, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL) // pixels - ATTRIB(XonoticDialog, titleHeight, float, SKINFONTSIZE_TITLE * SKINHEIGHT_TITLE) // pixels - ATTRIB(XonoticDialog, titleFontSize, float, SKINFONTSIZE_TITLE) // pixels + ATTRIB(XonoticDialog, marginTop, float, SKINMARGIN_TOP); // pixels + ATTRIB(XonoticDialog, marginBottom, float, SKINMARGIN_BOTTOM); // pixels + ATTRIB(XonoticDialog, marginLeft, float, SKINMARGIN_LEFT); // pixels + ATTRIB(XonoticDialog, marginRight, float, SKINMARGIN_RIGHT); // pixels + ATTRIB(XonoticDialog, columnSpacing, float, SKINMARGIN_COLUMNS); // pixels + ATTRIB(XonoticDialog, rowSpacing, float, SKINMARGIN_ROWS); // pixels + ATTRIB(XonoticDialog, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL); // pixels + ATTRIB(XonoticDialog, titleHeight, float, SKINFONTSIZE_TITLE * SKINHEIGHT_TITLE); // pixels + ATTRIB(XonoticDialog, titleFontSize, float, SKINFONTSIZE_TITLE); // pixels - ATTRIB(XonoticDialog, backgroundImage, string, SKINGFX_DIALOGBORDER) - ATTRIB(XonoticDialog, borderLines, float, SKINHEIGHT_DIALOGBORDER) - ATTRIB(XonoticDialog, closeButtonImage, string, SKINGFX_CLOSEBUTTON) - ATTRIB(XonoticDialog, zoomedOutTitleBarPosition, float, SKINHEIGHT_ZOOMEDTITLE * 0.5 - 0.5) - ATTRIB(XonoticDialog, zoomedOutTitleBar, float, SKINHEIGHT_ZOOMEDTITLE != 0) + ATTRIB(XonoticDialog, backgroundImage, string, SKINGFX_DIALOGBORDER); + ATTRIB(XonoticDialog, borderLines, float, SKINHEIGHT_DIALOGBORDER); + ATTRIB(XonoticDialog, closeButtonImage, string, SKINGFX_CLOSEBUTTON); + ATTRIB(XonoticDialog, zoomedOutTitleBarPosition, float, SKINHEIGHT_ZOOMEDTITLE * 0.5 - 0.5); + ATTRIB(XonoticDialog, zoomedOutTitleBar, float, SKINHEIGHT_ZOOMEDTITLE != 0); - ATTRIB(XonoticDialog, alpha, float, SKINALPHA_TEXT) + ATTRIB(XonoticDialog, alpha, float, SKINALPHA_TEXT); METHOD(XonoticDialog, configureDialog, void(entity)); ENDCLASS(XonoticDialog) diff --git a/qcsrc/menu/xonotic/dialog_credits.qh b/qcsrc/menu/xonotic/dialog_credits.qh index 90d722ed35..3be2e2b3f5 100644 --- a/qcsrc/menu/xonotic/dialog_credits.qh +++ b/qcsrc/menu/xonotic/dialog_credits.qh @@ -4,11 +4,11 @@ CLASS(XonoticCreditsDialog, XonoticDialog) METHOD(XonoticCreditsDialog, fill, void(entity)); METHOD(XonoticCreditsDialog, focusEnter, void(entity)); - ATTRIB(XonoticCreditsDialog, title, string, _("Credits")) - ATTRIB(XonoticCreditsDialog, tooltip, string, _("The Xonotic credits")) - ATTRIB(XonoticCreditsDialog, color, vector, SKINCOLOR_DIALOG_CREDITS) - ATTRIB(XonoticCreditsDialog, intendedWidth, float, SKINWIDTH_CREDITS) - ATTRIB(XonoticCreditsDialog, rows, float, SKINROWS_CREDITS) - ATTRIB(XonoticCreditsDialog, columns, float, 2) - ATTRIB(XonoticCreditsDialog, creditsList, entity, NULL) + ATTRIB(XonoticCreditsDialog, title, string, _("Credits")); + ATTRIB(XonoticCreditsDialog, tooltip, string, _("The Xonotic credits")); + ATTRIB(XonoticCreditsDialog, color, vector, SKINCOLOR_DIALOG_CREDITS); + ATTRIB(XonoticCreditsDialog, intendedWidth, float, SKINWIDTH_CREDITS); + ATTRIB(XonoticCreditsDialog, rows, float, SKINROWS_CREDITS); + ATTRIB(XonoticCreditsDialog, columns, float, 2); + ATTRIB(XonoticCreditsDialog, creditsList, entity); ENDCLASS(XonoticCreditsDialog) diff --git a/qcsrc/menu/xonotic/dialog_firstrun.qh b/qcsrc/menu/xonotic/dialog_firstrun.qh index ff7099b6b9..51a56bb6aa 100644 --- a/qcsrc/menu/xonotic/dialog_firstrun.qh +++ b/qcsrc/menu/xonotic/dialog_firstrun.qh @@ -3,14 +3,14 @@ #include "rootdialog.qh" CLASS(XonoticFirstRunDialog, XonoticRootDialog) METHOD(XonoticFirstRunDialog, fill, void(entity)); - ATTRIB(XonoticFirstRunDialog, title, string, _("Welcome")) - ATTRIB(XonoticFirstRunDialog, color, vector, SKINCOLOR_DIALOG_FIRSTRUN) - ATTRIB(XonoticFirstRunDialog, intendedWidth, float, 0.7) - ATTRIB(XonoticFirstRunDialog, rows, float, 16) - ATTRIB(XonoticFirstRunDialog, columns, float, 6) - ATTRIB(XonoticFirstRunDialog, name, string, "FirstRun") - ATTRIB(XonoticFirstRunDialog, playerNameLabel, entity, NULL) - ATTRIB(XonoticFirstRunDialog, playerNameLabelAlpha, float, 0) + ATTRIB(XonoticFirstRunDialog, title, string, _("Welcome")); + ATTRIB(XonoticFirstRunDialog, color, vector, SKINCOLOR_DIALOG_FIRSTRUN); + ATTRIB(XonoticFirstRunDialog, intendedWidth, float, 0.7); + ATTRIB(XonoticFirstRunDialog, rows, float, 16); + ATTRIB(XonoticFirstRunDialog, columns, float, 6); + ATTRIB(XonoticFirstRunDialog, name, string, "FirstRun"); + ATTRIB(XonoticFirstRunDialog, playerNameLabel, entity); + ATTRIB(XonoticFirstRunDialog, playerNameLabelAlpha, float, 0); - ATTRIB(XonoticFirstRunDialog, closable, float, 0) + ATTRIB(XonoticFirstRunDialog, closable, float, 0); ENDCLASS(XonoticFirstRunDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh index 237bcbbd15..9113a02794 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_ammo.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDAmmoDialog, XonoticRootDialog) METHOD(XonoticHUDAmmoDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDAmmoDialog, name, string, "HUDammo") - ATTRIB(XonoticHUDAmmoDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDAmmoDialog, name, string, "HUDammo"); + ATTRIB(XonoticHUDAmmoDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDAmmoDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh index 370dbe4eb8..9fc6846eb9 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_centerprint.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDCenterprintDialog, XonoticRootDialog) METHOD(XonoticHUDCenterprintDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDCenterprintDialog, name, string, "HUDcenterprint") - ATTRIB(XonoticHUDCenterprintDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDCenterprintDialog, name, string, "HUDcenterprint"); + ATTRIB(XonoticHUDCenterprintDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDCenterprintDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh index b30ea9fb2c..570c2d616c 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_chat.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDChatDialog, XonoticRootDialog) METHOD(XonoticHUDChatDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDChatDialog, name, string, "HUDchat") - ATTRIB(XonoticHUDChatDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDChatDialog, name, string, "HUDchat"); + ATTRIB(XonoticHUDChatDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDChatDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh index a28897d8c5..b741465024 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_engineinfo.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDEngineInfoDialog, XonoticRootDialog) METHOD(XonoticHUDEngineInfoDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDEngineInfoDialog, name, string, "HUDengineinfo") - ATTRIB(XonoticHUDEngineInfoDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDEngineInfoDialog, name, string, "HUDengineinfo"); + ATTRIB(XonoticHUDEngineInfoDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDEngineInfoDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh index 495047c5fc..b37f41b76e 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_healtharmor.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDHealthArmorDialog, XonoticRootDialog) METHOD(XonoticHUDHealthArmorDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDHealthArmorDialog, name, string, "HUDhealtharmor") - ATTRIB(XonoticHUDHealthArmorDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDHealthArmorDialog, name, string, "HUDhealtharmor"); + ATTRIB(XonoticHUDHealthArmorDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDHealthArmorDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh index 093bc00dfd..5d9032ffb0 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_infomessages.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDInfoMessagesDialog, XonoticRootDialog) METHOD(XonoticHUDInfoMessagesDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDInfoMessagesDialog, name, string, "HUDinfomessages") - ATTRIB(XonoticHUDInfoMessagesDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDInfoMessagesDialog, name, string, "HUDinfomessages"); + ATTRIB(XonoticHUDInfoMessagesDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDInfoMessagesDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc index f060383775..aeb8c8c774 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc +++ b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qc @@ -34,7 +34,7 @@ void XonoticHUDItemsTimeDialog_fill(entity me) me.TR(me); me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidespawned", _("Hide spawned items"))); me.TR(me); - me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidelarge", _("Hide large armor and health"))); + me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_hidebig", _("Hide big armor and health"))); me.TR(me); me.TD(me, 1, 4, e = makeXonoticCheckBox(0, "hud_panel_itemstime_dynamicsize", _("Dynamic size"))); } diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh index 71b53e6909..507dedb7c9 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_itemstime.qh @@ -3,10 +3,10 @@ #include "rootdialog.qh" CLASS(XonoticHUDItemsTimeDialog, XonoticRootDialog) METHOD(XonoticHUDItemsTimeDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDItemsTimeDialog, name, string, "HUDitemstime") + 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, columns, float, 4); + ATTRIB(XonoticHUDItemsTimeDialog, name, string, "HUDitemstime"); ENDCLASS(XonoticHUDItemsTimeDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh index feb36e206c..3a3b72f6c9 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_modicons.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDModIconsDialog, XonoticRootDialog) METHOD(XonoticHUDModIconsDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDModIconsDialog, name, string, "HUDmodicons") - ATTRIB(XonoticHUDModIconsDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDModIconsDialog, name, string, "HUDmodicons"); + ATTRIB(XonoticHUDModIconsDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDModIconsDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh index ad150076d0..f816e4ffe0 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_notification.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDNotificationDialog, XonoticRootDialog) METHOD(XonoticHUDNotificationDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDNotificationDialog, name, string, "HUDnotify") - ATTRIB(XonoticHUDNotificationDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDNotificationDialog, name, string, "HUDnotify"); + ATTRIB(XonoticHUDNotificationDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDNotificationDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh index 1d9c29289c..f6f19135d1 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_physics.qh @@ -3,12 +3,12 @@ #include "rootdialog.qh" CLASS(XonoticHUDPhysicsDialog, XonoticRootDialog) METHOD(XonoticHUDPhysicsDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDPhysicsDialog, name, string, "HUDphysics") - ATTRIB(XonoticHUDPhysicsDialog, sliderTopspeedTime, entity, NULL) - ATTRIB(XonoticHUDPhysicsDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDPhysicsDialog, name, string, "HUDphysics"); + ATTRIB(XonoticHUDPhysicsDialog, sliderTopspeedTime, entity); + ATTRIB(XonoticHUDPhysicsDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDPhysicsDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh index ebb09b0ba5..7f67fa6007 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_powerups.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDPowerupsDialog, XonoticRootDialog) METHOD(XonoticHUDPowerupsDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDPowerupsDialog, name, string, "HUDpowerups") - ATTRIB(XonoticHUDPowerupsDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDPowerupsDialog, name, string, "HUDpowerups"); + ATTRIB(XonoticHUDPowerupsDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDPowerupsDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh index c4b7a90969..cc82959e64 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_pressedkeys.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDPressedKeysDialog, XonoticRootDialog) METHOD(XonoticHUDPressedKeysDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDPressedKeysDialog, name, string, "HUDpressedkeys") - ATTRIB(XonoticHUDPressedKeysDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDPressedKeysDialog, name, string, "HUDpressedkeys"); + ATTRIB(XonoticHUDPressedKeysDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDPressedKeysDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh index 396f62b263..16f93c13d5 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_quickmenu.qh @@ -3,10 +3,10 @@ #include "rootdialog.qh" CLASS(XonoticHUDQuickMenuDialog, XonoticRootDialog) METHOD(XonoticHUDQuickMenuDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDQuickMenuDialog, name, string, "HUDquickmenu") + 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, columns, float, 4); + ATTRIB(XonoticHUDQuickMenuDialog, name, string, "HUDquickmenu"); ENDCLASS(XonoticHUDQuickMenuDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh index 81dca51261..7c814e3823 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_racetimer.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDRaceTimerDialog, XonoticRootDialog) METHOD(XonoticHUDRaceTimerDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDRaceTimerDialog, name, string, "HUDracetimer") - ATTRIB(XonoticHUDRaceTimerDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDRaceTimerDialog, name, string, "HUDracetimer"); + ATTRIB(XonoticHUDRaceTimerDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDRaceTimerDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh index 6744e22741..04617b677f 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_radar.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDRadarDialog, XonoticRootDialog) METHOD(XonoticHUDRadarDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDRadarDialog, name, string, "HUDradar") - ATTRIB(XonoticHUDRadarDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDRadarDialog, name, string, "HUDradar"); + ATTRIB(XonoticHUDRadarDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDRadarDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_score.qh b/qcsrc/menu/xonotic/dialog_hudpanel_score.qh index 68015eaa64..d97787404e 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_score.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_score.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDScoreDialog, XonoticRootDialog) METHOD(XonoticHUDScoreDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDScoreDialog, name, string, "HUDscore") - ATTRIB(XonoticHUDScoreDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDScoreDialog, name, string, "HUDscore"); + ATTRIB(XonoticHUDScoreDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDScoreDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh index 7f75097131..61955495ed 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_timer.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDTimerDialog, XonoticRootDialog) METHOD(XonoticHUDTimerDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDTimerDialog, name, string, "HUDtimer") - ATTRIB(XonoticHUDTimerDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDTimerDialog, name, string, "HUDtimer"); + ATTRIB(XonoticHUDTimerDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDTimerDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh index 1ee05a2f88..061c69e56c 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_vote.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDVoteDialog, XonoticRootDialog) METHOD(XonoticHUDVoteDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDVoteDialog, name, string, "HUDvote") - ATTRIB(XonoticHUDVoteDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDVoteDialog, name, string, "HUDvote"); + ATTRIB(XonoticHUDVoteDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDVoteDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh index 649f7ddd1c..fad14749e6 100644 --- a/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh +++ b/qcsrc/menu/xonotic/dialog_hudpanel_weapons.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDWeaponsDialog, XonoticRootDialog) METHOD(XonoticHUDWeaponsDialog, fill, void(entity)); - 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, columns, float, 4) - ATTRIB(XonoticHUDWeaponsDialog, name, string, "HUDweapons") - ATTRIB(XonoticHUDWeaponsDialog, requiresConnection, float, true) + 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, columns, float, 4); + ATTRIB(XonoticHUDWeaponsDialog, name, string, "HUDweapons"); + ATTRIB(XonoticHUDWeaponsDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDWeaponsDialog) diff --git a/qcsrc/menu/xonotic/dialog_hudsetup_exit.qh b/qcsrc/menu/xonotic/dialog_hudsetup_exit.qh index e26cfad9b6..8a4479c6f8 100644 --- a/qcsrc/menu/xonotic/dialog_hudsetup_exit.qh +++ b/qcsrc/menu/xonotic/dialog_hudsetup_exit.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticHUDExitDialog, XonoticRootDialog) METHOD(XonoticHUDExitDialog, fill, void(entity)); - ATTRIB(XonoticHUDExitDialog, title, string, _("Panel HUD Setup")) - ATTRIB(XonoticHUDExitDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT) - ATTRIB(XonoticHUDExitDialog, intendedWidth, float, 0.8) - ATTRIB(XonoticHUDExitDialog, rows, float, 18) - ATTRIB(XonoticHUDExitDialog, columns, float, 8.2) - ATTRIB(XonoticHUDExitDialog, name, string, "HUDExit") - ATTRIB(XonoticHUDExitDialog, requiresConnection, float, true) + ATTRIB(XonoticHUDExitDialog, title, string, _("Panel HUD Setup")); + ATTRIB(XonoticHUDExitDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); + ATTRIB(XonoticHUDExitDialog, intendedWidth, float, 0.8); + ATTRIB(XonoticHUDExitDialog, rows, float, 18); + ATTRIB(XonoticHUDExitDialog, columns, float, 8.2); + ATTRIB(XonoticHUDExitDialog, name, string, "HUDExit"); + ATTRIB(XonoticHUDExitDialog, requiresConnection, float, true); ENDCLASS(XonoticHUDExitDialog) diff --git a/qcsrc/menu/xonotic/dialog_monstertools.qh b/qcsrc/menu/xonotic/dialog_monstertools.qh index 2b6f8b44da..1a6b3388ab 100644 --- a/qcsrc/menu/xonotic/dialog_monstertools.qh +++ b/qcsrc/menu/xonotic/dialog_monstertools.qh @@ -3,10 +3,10 @@ #include "rootdialog.qh" CLASS(XonoticMonsterToolsDialog, XonoticRootDialog) METHOD(XonoticMonsterToolsDialog, fill, void(entity)); - ATTRIB(XonoticMonsterToolsDialog, title, string, _("Monster Tools")) - ATTRIB(XonoticMonsterToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS) - ATTRIB(XonoticMonsterToolsDialog, intendedWidth, float, 0.8) - ATTRIB(XonoticMonsterToolsDialog, rows, float, 16) - ATTRIB(XonoticMonsterToolsDialog, columns, float, 4) - ATTRIB(XonoticMonsterToolsDialog, name, string, "MonsterTools") + ATTRIB(XonoticMonsterToolsDialog, title, string, _("Monster Tools")); + ATTRIB(XonoticMonsterToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS); + ATTRIB(XonoticMonsterToolsDialog, intendedWidth, float, 0.8); + ATTRIB(XonoticMonsterToolsDialog, rows, float, 16); + ATTRIB(XonoticMonsterToolsDialog, columns, float, 4); + ATTRIB(XonoticMonsterToolsDialog, name, string, "MonsterTools"); ENDCLASS(XonoticMonsterToolsDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer.qh b/qcsrc/menu/xonotic/dialog_multiplayer.qh index b18ca55607..74b5285942 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer.qh @@ -3,10 +3,10 @@ #include "dialog.qh" CLASS(XonoticMultiplayerDialog, XonoticDialog) METHOD(XonoticMultiplayerDialog, fill, void(entity)); - ATTRIB(XonoticMultiplayerDialog, title, string, _("Multiplayer")) - ATTRIB(XonoticMultiplayerDialog, tooltip, string, _("Play online, against your friends in LAN, view demos or change player settings")) - ATTRIB(XonoticMultiplayerDialog, color, vector, SKINCOLOR_DIALOG_MULTIPLAYER) - ATTRIB(XonoticMultiplayerDialog, intendedWidth, float, 0.96) - ATTRIB(XonoticMultiplayerDialog, rows, float, 24) - ATTRIB(XonoticMultiplayerDialog, columns, float, 4) + ATTRIB(XonoticMultiplayerDialog, title, string, _("Multiplayer")); + ATTRIB(XonoticMultiplayerDialog, tooltip, string, _("Play online, against your friends in LAN, view demos or change player settings")); + ATTRIB(XonoticMultiplayerDialog, color, vector, SKINCOLOR_DIALOG_MULTIPLAYER); + ATTRIB(XonoticMultiplayerDialog, intendedWidth, float, 0.96); + ATTRIB(XonoticMultiplayerDialog, rows, float, 24); + ATTRIB(XonoticMultiplayerDialog, columns, float, 4); ENDCLASS(XonoticMultiplayerDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc index 85e0e9e698..481914200a 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create.qc @@ -58,6 +58,27 @@ void GameType_ConfigureSliders(entity me, string pLabel, float pMin, float pMax, t.configureXonoticTextSliderValues(t); } +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; + } +} + entity makeXonoticServerCreateTab() { entity me; @@ -210,28 +231,12 @@ void XonoticServerCreateTab_fill(entity me) e.onClickEntity = me.mapListBox; me.mapListBox.startButton = e; - me.gameTypeChangeNotify(me); + GameType_ConfigureSliders_for_CurrentGametype(me); } void XonoticServerCreateTab_gameTypeChangeNotify(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_ConfigureSliders_for_CurrentGametype(me); me.mapListBox.refilter(me.mapListBox); } diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create.qh b/qcsrc/menu/xonotic/dialog_multiplayer_create.qh index 5a747a9453..e275522864 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create.qh @@ -5,15 +5,15 @@ CLASS(XonoticServerCreateTab, XonoticTab) METHOD(XonoticServerCreateTab, fill, void(entity)); METHOD(XonoticServerCreateTab, gameTypeChangeNotify, void(entity)); METHOD(XonoticServerCreateTab, gameTypeSelectNotify, void(entity)); - ATTRIB(XonoticServerCreateTab, intendedWidth, float, 0.9) - ATTRIB(XonoticServerCreateTab, rows, float, 23) - ATTRIB(XonoticServerCreateTab, columns, float, 6.2) // added extra .2 for center space + ATTRIB(XonoticServerCreateTab, intendedWidth, float, 0.9); + ATTRIB(XonoticServerCreateTab, rows, float, 23); + ATTRIB(XonoticServerCreateTab, columns, float, 6.2); // added extra .2 for center space - ATTRIB(XonoticServerCreateTab, mapListBox, entity, NULL) - ATTRIB(XonoticServerCreateTab, sliderFraglimit, entity, NULL) - ATTRIB(XonoticServerCreateTab, sliderTeams, entity, NULL) - ATTRIB(XonoticServerCreateTab, sliderTimelimit, entity, NULL) - ATTRIB(XonoticServerCreateTab, labelFraglimit, entity, NULL) - ATTRIB(XonoticServerCreateTab, labelTeams, entity, NULL) + ATTRIB(XonoticServerCreateTab, mapListBox, entity); + ATTRIB(XonoticServerCreateTab, sliderFraglimit, entity); + ATTRIB(XonoticServerCreateTab, sliderTeams, entity); + ATTRIB(XonoticServerCreateTab, sliderTimelimit, entity); + ATTRIB(XonoticServerCreateTab, labelFraglimit, entity); + ATTRIB(XonoticServerCreateTab, labelTeams, entity); ENDCLASS(XonoticServerCreateTab) entity makeXonoticServerCreateTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc index 9bc82ebc28..2f2ab901a8 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qc @@ -35,11 +35,11 @@ void XonoticMapInfoDialog_loadMapInfo(entity me, int i, entity mlb) else me.previewImage.src = me.currentMapPreviewImage; - for(i = 0; i < GameType_GetCount(); ++i) + for(i = 0; i < GameType_GetTotalCount(); ++i) { entity e; e = me.(typeLabels[i]); - e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i)); + e.disabled = !(MapInfo_Map_supportedGametypes & GameType_GetID(i).m_flags); } MapInfo_ClearTemps(); @@ -69,9 +69,9 @@ void XonoticMapInfoDialog_fill(entity me) me.TR(me); me.TD(me, 1, w, e = makeXonoticTextLabel(0, _("Game types:"))); - n = ceil(GameType_GetCount() / (me.rows - 6)); + n = ceil(GameType_GetTotalCount() / (me.rows - 6)); wgt = (w - 0.2) / n; - for(i = 0; i < GameType_GetCount(); ++i) + for(i = 0; i < GameType_GetTotalCount(); ++i) { if(mod(i, n) == 0) { diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh index 13acfcc777..b2efb97619 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mapinfo.qh @@ -4,24 +4,24 @@ CLASS(XonoticMapInfoDialog, XonoticDialog) METHOD(XonoticMapInfoDialog, fill, void(entity)); METHOD(XonoticMapInfoDialog, loadMapInfo, void(entity, float, entity)); - ATTRIB(XonoticMapInfoDialog, title, string, _("Map Information")) - ATTRIB(XonoticMapInfoDialog, color, vector, SKINCOLOR_DIALOG_MAPINFO) - ATTRIB(XonoticMapInfoDialog, intendedWidth, float, 1.0) - ATTRIB(XonoticMapInfoDialog, rows, float, 11) - ATTRIB(XonoticMapInfoDialog, columns, float, 10) + ATTRIB(XonoticMapInfoDialog, title, string, _("Map Information")); + ATTRIB(XonoticMapInfoDialog, color, vector, SKINCOLOR_DIALOG_MAPINFO); + ATTRIB(XonoticMapInfoDialog, intendedWidth, float, 1.0); + ATTRIB(XonoticMapInfoDialog, rows, float, 11); + ATTRIB(XonoticMapInfoDialog, columns, float, 10); - ATTRIB(XonoticMapInfoDialog, previewImage, entity, NULL) - ATTRIB(XonoticMapInfoDialog, titleLabel, entity, NULL) - ATTRIB(XonoticMapInfoDialog, authorLabel, entity, NULL) - ATTRIB(XonoticMapInfoDialog, descriptionLabel, entity, NULL) - ATTRIB(XonoticMapInfoDialog, featuresLabel, entity, NULL) + ATTRIB(XonoticMapInfoDialog, previewImage, entity); + ATTRIB(XonoticMapInfoDialog, titleLabel, entity); + ATTRIB(XonoticMapInfoDialog, authorLabel, entity); + ATTRIB(XonoticMapInfoDialog, descriptionLabel, entity); + ATTRIB(XonoticMapInfoDialog, featuresLabel, entity); - ATTRIBARRAY(XonoticMapInfoDialog, typeLabels, entity, 24) + ATTRIBARRAY(XonoticMapInfoDialog, typeLabels, entity, 24); - ATTRIB(XonoticMapInfoDialog, currentMapIndex, float, 0) - ATTRIB(XonoticMapInfoDialog, currentMapBSPName, string, string_null) - ATTRIB(XonoticMapInfoDialog, currentMapTitle, string, string_null) - ATTRIB(XonoticMapInfoDialog, currentMapAuthor, string, string_null) - ATTRIB(XonoticMapInfoDialog, currentMapDescription, string, string_null) - ATTRIB(XonoticMapInfoDialog, currentMapPreviewImage, string, string_null) + ATTRIB(XonoticMapInfoDialog, currentMapIndex, float, 0); + ATTRIB(XonoticMapInfoDialog, currentMapBSPName, string); + ATTRIB(XonoticMapInfoDialog, currentMapTitle, string); + ATTRIB(XonoticMapInfoDialog, currentMapAuthor, string); + ATTRIB(XonoticMapInfoDialog, currentMapDescription, string); + ATTRIB(XonoticMapInfoDialog, currentMapPreviewImage, string); ENDCLASS(XonoticMapInfoDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc index f2f7f5c8e4..ab52949741 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qc @@ -1,5 +1,5 @@ #include "dialog_multiplayer_create_mutators.qh" -#include +#include #include "weaponarenacheckbox.qh" #include "checkbox.qh" @@ -53,8 +53,6 @@ string WeaponArenaString() return weaponarenastring; } -AUTOCVAR(g_grappling_hook, bool, _("let players spawn with the grappling hook which allows them to pull themselves up")); - string XonoticMutatorsDialog_toString(entity me) { string s; @@ -73,13 +71,13 @@ string XonoticMutatorsDialog_toString(entity me) s = strcat(s, ", ", _("Invincible Projectiles")); if(cvar_string("g_weaponarena") != "0") s = strcat(s, ", ", WeaponArenaString()); - else if(cvar("g_balance_blaster_weaponstart") == 0) + else if(cvar("g_balance_blaster_weaponstartoverride") == 0) s = strcat(s, ", ", _("No start weapons")); if(cvar("sv_gravity") < stof(cvar_defstring("sv_gravity"))) s = strcat(s, ", ", _("Low gravity")); if(cvar("g_cloaked")) s = strcat(s, ", ", _("Cloaked")); - if(autocvar_g_grappling_hook) + if(cvar("g_grappling_hook")) s = strcat(s, ", ", _("Hook")); if(cvar("g_midair")) s = strcat(s, ", ", _("Midair")); @@ -145,7 +143,7 @@ float checkCompatibility_weaponarena_weapon(entity me) return 0; if(cvar_string("g_weaponarena") == "0") return 0; - if(cvar_string("g_balance_blaster_weaponstart") == "0") + if(cvar_string("g_balance_blaster_weaponstartoverride") == "0") return 0; return 1; } @@ -281,9 +279,9 @@ void XonoticMutatorsDialog_fill(entity me) setDependent(e, "g_nix", 1, 1); me.TR(me); me.TDempty(me, 0.2); - me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstart", "0", _("No start weapons"), "-")); + me.TD(me, 1, 1.8, e = makeXonoticRadioButton_T(1, "g_balance_blaster_weaponstartoverride", "0", _("No start weapons"), "-")); e.cvarOffValue = "-1"; - makeMulti(e, "g_balance_shotgun_weaponstart g_balance_machinegun_weaponstart g_balance_devastator_weaponstart g_balance_minelayer_weaponstart g_balance_electro_weaponstart g_balance_crylink_weaponstart g_balance_hagar_weaponstart g_balance_porto_weaponstart g_balance_vaporizer_weaponstart g_balance_hook_weaponstart g_balance_rifle_weaponstart g_balance_fireball_weaponstart g_balance_seeker_weaponstart g_balance_tuba_weaponstart g_balance_arc_weaponstart g_balance_vortex_weaponstart g_balance_mortar_weaponstart"); + makeMulti(e, "g_balance_shotgun_weaponstartoverride g_balance_machinegun_weaponstartoverride g_balance_devastator_weaponstartoverride g_balance_minelayer_weaponstartoverride g_balance_electro_weaponstartoverride g_balance_crylink_weaponstartoverride g_balance_hagar_weaponstartoverride g_balance_porto_weaponstartoverride g_balance_vaporizer_weaponstartoverride g_balance_hook_weaponstartoverride g_balance_rifle_weaponstartoverride g_balance_fireball_weaponstartoverride g_balance_seeker_weaponstartoverride g_balance_tuba_weaponstartoverride g_balance_arc_weaponstartoverride g_balance_vortex_weaponstartoverride g_balance_mortar_weaponstartoverride"); me.gotoRC(me, me.rows - 1, 0); me.TD(me, 1, me.columns, e = makeXonoticButton(_("OK"), '0 0 0')); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh index ede8f4cb1f..48ccc6b48e 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_create_mutators.qh @@ -6,10 +6,10 @@ CLASS(XonoticMutatorsDialog, XonoticDialog) METHOD(XonoticMutatorsDialog, fill, void(entity)); METHOD(XonoticMutatorsDialog, showNotify, void(entity)); METHOD(XonoticMutatorsDialog, close, void(entity)); - ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators")) - ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS) - ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9) - ATTRIB(XonoticMutatorsDialog, rows, float, 20) - ATTRIB(XonoticMutatorsDialog, columns, float, 6) - ATTRIB(XonoticMutatorsDialog, refilterEntity, entity, NULL) + ATTRIB(XonoticMutatorsDialog, title, string, _("Mutators")); + ATTRIB(XonoticMutatorsDialog, color, vector, SKINCOLOR_DIALOG_MUTATORS); + ATTRIB(XonoticMutatorsDialog, intendedWidth, float, 0.9); + ATTRIB(XonoticMutatorsDialog, rows, float, 20); + ATTRIB(XonoticMutatorsDialog, columns, float, 6); + ATTRIB(XonoticMutatorsDialog, refilterEntity, entity); ENDCLASS(XonoticMutatorsDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join.qc index f387f429b6..a34d0d80a1 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join.qc @@ -14,6 +14,13 @@ entity makeXonoticServerListTab() me.configureDialog(me); return me; } + +void XonoticServerListTab_refresh(entity this, entity slist) +{ + bool clear = false; + slist.refreshServerList(slist, clear ? REFRESHSERVERLIST_RESET : REFRESHSERVERLIST_ASK); +} + void XonoticServerListTab_fill(entity me) { entity e, slist; @@ -21,13 +28,13 @@ void XonoticServerListTab_fill(entity me) slist = makeXonoticServerList(); me.gotoRC(me, 0.5, 0); - me.TD(me, 1, 0.6, e = makeXonoticTextLabel(1, _("Filter:"))); - me.TD(me, 1, 2.8, e = makeXonoticInputBox(0, string_null)); + me.TD(me, 1, 0.5, e = makeXonoticTextLabel(1, _("Filter:"))); + me.TD(me, 1, 2, e = makeXonoticInputBox(0, string_null)); e.onChange = ServerList_Filter_Change; e.onChangeEntity = slist; slist.controlledTextbox = e; - me.gotoRC(me, 0.5, 3.6); + me.gotoRC(me, 0.5, 2.6); me.TD(me, 1, 0.9, e = makeXonoticCheckBox(0, "menu_slist_categories", ZCTX(_("SRVS^Categories")))); e.onClickEntity = slist; e.onClick = ServerList_Categories_Click; @@ -43,6 +50,9 @@ void XonoticServerListTab_fill(entity me) e.onClick = ServerList_ShowFull_Click; me.TD(me, 1, 0.6, e = makeXonoticCheckBox_T(0, "net_slist_pause", _("Pause"), _("Pause updating the server list to prevent servers from \"jumping around\""))); + me.TD(me, 1, 1, e = makeXonoticButton_T(_("Refresh"), '0 0 0', _("Reload the server list"))); + e.onClick = XonoticServerListTab_refresh; + e.onClickEntity = slist; me.gotoRC(me, 2, 0); me.TD(me, 1, 1, slist.sortButton1 = makeXonoticButton(string_null, '0 0 0')); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join.qh b/qcsrc/menu/xonotic/dialog_multiplayer_join.qh index 7fa837976c..a663eb527b 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join.qh @@ -3,8 +3,8 @@ #include "tab.qh" CLASS(XonoticServerListTab, XonoticTab) METHOD(XonoticServerListTab, fill, void(entity)); - ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9) - ATTRIB(XonoticServerListTab, rows, float, 23) - ATTRIB(XonoticServerListTab, columns, float, 6.5) + ATTRIB(XonoticServerListTab, intendedWidth, float, 0.9); + ATTRIB(XonoticServerListTab, rows, float, 23); + ATTRIB(XonoticServerListTab, columns, float, 6.5); ENDCLASS(XonoticServerListTab) entity makeXonoticServerListTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc index d5532bcd92..2f37eb7e2b 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qc @@ -10,7 +10,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) { bool pure_available; - float m, pure_violations, freeslots, j, numh, maxp, numb, sflags; + float m, pure_violations, freeslots, numh, maxp, numb, sflags; string s, typestr, versionstr, k, v, modname; // ==================================== @@ -96,7 +96,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) freeslots = -1; sflags = -1; modname = ""; - for(j = 2; j < m; ++j) + for(int j = 2; j < m; ++j) { if(argv(j) == "") break; @@ -124,7 +124,7 @@ void XonoticServerInfoDialog_loadServerInfo(entity me, float i) if(s != "data") modname = sprintf("%s (%s)", modname, s); - j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type + Gametype j = MapInfo_Type_FromString(typestr); // try and get the real name of the game type if(j) { typestr = MapInfo_Type_ToText(j); } // only set it if we actually found it me.currentServerType = strzone(typestr); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh index 201f737e91..68f5ab8ca0 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_join_serverinfo.qh @@ -4,42 +4,42 @@ CLASS(XonoticServerInfoDialog, XonoticDialog) METHOD(XonoticServerInfoDialog, fill, void(entity)); METHOD(XonoticServerInfoDialog, loadServerInfo, void(entity, float)); - ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information")) - ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO) - ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8) - ATTRIB(XonoticServerInfoDialog, rows, float, 18) - ATTRIB(XonoticServerInfoDialog, columns, float, 6.2) + ATTRIB(XonoticServerInfoDialog, title, string, _("Server Information")); + ATTRIB(XonoticServerInfoDialog, color, vector, SKINCOLOR_DIALOG_SERVERINFO); + ATTRIB(XonoticServerInfoDialog, intendedWidth, float, 0.8); + ATTRIB(XonoticServerInfoDialog, rows, float, 18); + ATTRIB(XonoticServerInfoDialog, columns, float, 6.2); - ATTRIB(XonoticServerInfoDialog, currentServerName, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerCName, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerType, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerMap, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerMod, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerVersion, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerKey, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerID, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string, string_null) - ATTRIB(XonoticServerInfoDialog, currentServerPure, string, string_null) + ATTRIB(XonoticServerInfoDialog, currentServerName, string); + ATTRIB(XonoticServerInfoDialog, currentServerCName, string); + ATTRIB(XonoticServerInfoDialog, currentServerType, string); + ATTRIB(XonoticServerInfoDialog, currentServerMap, string); + ATTRIB(XonoticServerInfoDialog, currentServerPlayers, string); + ATTRIB(XonoticServerInfoDialog, currentServerNumPlayers, string); + ATTRIB(XonoticServerInfoDialog, currentServerNumBots, string); + ATTRIB(XonoticServerInfoDialog, currentServerNumFreeSlots, string); + ATTRIB(XonoticServerInfoDialog, currentServerMod, string); + ATTRIB(XonoticServerInfoDialog, currentServerVersion, string); + ATTRIB(XonoticServerInfoDialog, currentServerKey, string); + ATTRIB(XonoticServerInfoDialog, currentServerID, string); + ATTRIB(XonoticServerInfoDialog, currentServerEncrypt, string); + ATTRIB(XonoticServerInfoDialog, currentServerPure, string); - ATTRIB(XonoticServerInfoDialog, nameLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, cnameLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, typeLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, mapLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity, NULL) - ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, modLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, versionLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, keyLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, idLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, encryptLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity, NULL) - ATTRIB(XonoticServerInfoDialog, pureLabel, entity, NULL) + ATTRIB(XonoticServerInfoDialog, nameLabel, entity); + ATTRIB(XonoticServerInfoDialog, cnameLabel, entity); + ATTRIB(XonoticServerInfoDialog, typeLabel, entity); + ATTRIB(XonoticServerInfoDialog, mapLabel, entity); + ATTRIB(XonoticServerInfoDialog, rawPlayerList, entity); + ATTRIB(XonoticServerInfoDialog, numPlayersLabel, entity); + ATTRIB(XonoticServerInfoDialog, numBotsLabel, entity); + ATTRIB(XonoticServerInfoDialog, numFreeSlotsLabel, entity); + ATTRIB(XonoticServerInfoDialog, modLabel, entity); + ATTRIB(XonoticServerInfoDialog, versionLabel, entity); + ATTRIB(XonoticServerInfoDialog, keyLabel, entity); + ATTRIB(XonoticServerInfoDialog, idLabel, entity); + ATTRIB(XonoticServerInfoDialog, encryptLabel, entity); + ATTRIB(XonoticServerInfoDialog, canConnectLabel, entity); + ATTRIB(XonoticServerInfoDialog, pureLabel, entity); ENDCLASS(XonoticServerInfoDialog) void Join_Click(entity btn, entity me); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media.qh index 96c1aeb75d..9713e82c2d 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media.qh @@ -3,9 +3,9 @@ #include "tab.qh" CLASS(XonoticMediaTab, XonoticTab) METHOD(XonoticMediaTab, fill, void(entity)); - ATTRIB(XonoticMediaTab, intendedWidth, float, 0.9) - ATTRIB(XonoticMediaTab, rows, float, 23) - ATTRIB(XonoticMediaTab, columns, float, 3) - ATTRIB(XonoticMediaTab, name, string, "Media") + ATTRIB(XonoticMediaTab, intendedWidth, float, 0.9); + ATTRIB(XonoticMediaTab, rows, float, 23); + ATTRIB(XonoticMediaTab, columns, float, 3); + ATTRIB(XonoticMediaTab, name, string, "Media"); ENDCLASS(XonoticMediaTab) entity makeXonoticMediaTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qh index f41bb9e812..cf3548d774 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo.qh @@ -3,10 +3,10 @@ #include "tab.qh" CLASS(XonoticDemoBrowserTab, XonoticTab) METHOD(XonoticDemoBrowserTab, fill, void(entity)); - ATTRIB(XonoticDemoBrowserTab, intendedWidth, float, 0.9) - ATTRIB(XonoticDemoBrowserTab, rows, float, 21) - ATTRIB(XonoticDemoBrowserTab, columns, float, 6.5) - ATTRIB(XonoticDemoBrowserTab, name, string, "DemoBrowser") - ATTRIB(XonoticDemoBrowserTab, democlicktype, float, 0) + ATTRIB(XonoticDemoBrowserTab, intendedWidth, float, 0.9); + ATTRIB(XonoticDemoBrowserTab, rows, float, 21); + ATTRIB(XonoticDemoBrowserTab, columns, float, 6.5); + ATTRIB(XonoticDemoBrowserTab, name, string, "DemoBrowser"); + ATTRIB(XonoticDemoBrowserTab, democlicktype, float, 0); ENDCLASS(XonoticDemoBrowserTab) entity makeXonoticDemoBrowserTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh index 598dd5503f..934490f755 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_startconfirm.qh @@ -3,9 +3,9 @@ #include "dialog.qh" CLASS(XonoticDemoStartConfirmDialog, XonoticDialog) METHOD(XonoticDemoStartConfirmDialog, fill, void(entity)); - ATTRIB(XonoticDemoStartConfirmDialog, title, string, _("Disconnect")) - ATTRIB(XonoticDemoStartConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM) - ATTRIB(XonoticDemoStartConfirmDialog, intendedWidth, float, 0.5) - ATTRIB(XonoticDemoStartConfirmDialog, rows, float, 4) - ATTRIB(XonoticDemoStartConfirmDialog, columns, float, 2) + ATTRIB(XonoticDemoStartConfirmDialog, title, string, _("Disconnect")); + ATTRIB(XonoticDemoStartConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM); + ATTRIB(XonoticDemoStartConfirmDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticDemoStartConfirmDialog, rows, float, 4); + ATTRIB(XonoticDemoStartConfirmDialog, columns, float, 2); ENDCLASS(XonoticDemoStartConfirmDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh index 96e2d63103..80c2766de2 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_demo_timeconfirm.qh @@ -3,9 +3,9 @@ #include "dialog.qh" CLASS(XonoticDemoTimeConfirmDialog, XonoticDialog) METHOD(XonoticDemoTimeConfirmDialog, fill, void(entity)); - ATTRIB(XonoticDemoTimeConfirmDialog, title, string, _("Disconnect")) - ATTRIB(XonoticDemoTimeConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM) - ATTRIB(XonoticDemoTimeConfirmDialog, intendedWidth, float, 0.5) - ATTRIB(XonoticDemoTimeConfirmDialog, rows, float, 4) - ATTRIB(XonoticDemoTimeConfirmDialog, columns, float, 2) + ATTRIB(XonoticDemoTimeConfirmDialog, title, string, _("Disconnect")); + ATTRIB(XonoticDemoTimeConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM); + ATTRIB(XonoticDemoTimeConfirmDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticDemoTimeConfirmDialog, rows, float, 4); + ATTRIB(XonoticDemoTimeConfirmDialog, columns, float, 2); ENDCLASS(XonoticDemoTimeConfirmDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qh index 4d98f6ec17..6172dae03c 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_musicplayer.qh @@ -3,9 +3,9 @@ #include "tab.qh" CLASS(XonoticMusicPlayerTab, XonoticTab) METHOD(XonoticMusicPlayerTab, fill, void(entity)); - ATTRIB(XonoticMusicPlayerTab, intendedWidth, float, 0.9) - ATTRIB(XonoticMusicPlayerTab, rows, float, 21) - ATTRIB(XonoticMusicPlayerTab, columns, float, 6.5) - ATTRIB(XonoticMusicPlayerTab, name, string, "MusicPlayer") + ATTRIB(XonoticMusicPlayerTab, intendedWidth, float, 0.9); + ATTRIB(XonoticMusicPlayerTab, rows, float, 21); + ATTRIB(XonoticMusicPlayerTab, columns, float, 6.5); + ATTRIB(XonoticMusicPlayerTab, name, string, "MusicPlayer"); ENDCLASS(XonoticMusicPlayerTab) entity makeXonoticMusicPlayerTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qh index 62ea8438a0..66dc3cf077 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot.qh @@ -3,13 +3,13 @@ #include "tab.qh" CLASS(XonoticScreenshotBrowserTab, XonoticTab) METHOD(XonoticScreenshotBrowserTab, fill, void(entity)); - ATTRIB(XonoticScreenshotBrowserTab, intendedWidth, float, 1) - ATTRIB(XonoticScreenshotBrowserTab, rows, float, 21) - ATTRIB(XonoticScreenshotBrowserTab, columns, float, 6.5) - ATTRIB(XonoticScreenshotBrowserTab, name, string, "ScreenshotBrowser") + ATTRIB(XonoticScreenshotBrowserTab, intendedWidth, float, 1); + ATTRIB(XonoticScreenshotBrowserTab, rows, float, 21); + ATTRIB(XonoticScreenshotBrowserTab, columns, float, 6.5); + ATTRIB(XonoticScreenshotBrowserTab, name, string, "ScreenshotBrowser"); METHOD(XonoticScreenshotBrowserTab, loadPreviewScreenshot, void(entity, string)); - ATTRIB(XonoticScreenshotBrowserTab, screenshotImage, entity, NULL) - ATTRIB(XonoticScreenshotBrowserTab, currentScrPath, string, string_null) + ATTRIB(XonoticScreenshotBrowserTab, screenshotImage, entity); + ATTRIB(XonoticScreenshotBrowserTab, currentScrPath, string); ENDCLASS(XonoticScreenshotBrowserTab) entity makeXonoticScreenshotBrowserTab(); diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qh b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qh index c4f7e61262..ad1656e4f7 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_media_screenshot_viewer.qh @@ -9,14 +9,14 @@ CLASS(XonoticScreenshotViewerDialog, XonoticDialog) METHOD(XonoticScreenshotViewerDialog, keyDown, float(entity, float, float, float)); METHOD(XonoticScreenshotViewerDialog, loadScreenshot, void(entity, string)); METHOD(XonoticScreenshotViewerDialog, close, void(entity)); - ATTRIB(XonoticScreenshotViewerDialog, title, string, "Screenshot Viewer") - ATTRIB(XonoticScreenshotViewerDialog, name, string, "ScreenshotViewer") - ATTRIB(XonoticScreenshotViewerDialog, intendedWidth, float, 1) - ATTRIB(XonoticScreenshotViewerDialog, rows, float, 25) - ATTRIB(XonoticScreenshotViewerDialog, columns, float, 4) - ATTRIB(XonoticScreenshotViewerDialog, color, vector, SKINCOLOR_DIALOG_SCREENSHOTVIEWER) - ATTRIB(XonoticScreenshotViewerDialog, scrList, entity, NULL) - ATTRIB(XonoticScreenshotViewerDialog, screenshotImage, entity, NULL) - ATTRIB(XonoticScreenshotViewerDialog, slideShowButton, entity, NULL) - ATTRIB(XonoticScreenshotViewerDialog, currentScrPath, string, string_null) + ATTRIB(XonoticScreenshotViewerDialog, title, string, "Screenshot Viewer"); + ATTRIB(XonoticScreenshotViewerDialog, name, string, "ScreenshotViewer"); + ATTRIB(XonoticScreenshotViewerDialog, intendedWidth, float, 1); + ATTRIB(XonoticScreenshotViewerDialog, rows, float, 25); + ATTRIB(XonoticScreenshotViewerDialog, columns, float, 4); + ATTRIB(XonoticScreenshotViewerDialog, color, vector, SKINCOLOR_DIALOG_SCREENSHOTVIEWER); + ATTRIB(XonoticScreenshotViewerDialog, scrList, entity); + ATTRIB(XonoticScreenshotViewerDialog, screenshotImage, entity); + ATTRIB(XonoticScreenshotViewerDialog, slideShowButton, entity); + ATTRIB(XonoticScreenshotViewerDialog, currentScrPath, string); ENDCLASS(XonoticScreenshotViewerDialog) diff --git a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh index f60e9e0bb3..2285efcafa 100644 --- a/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh +++ b/qcsrc/menu/xonotic/dialog_multiplayer_profile.qh @@ -4,10 +4,10 @@ CLASS(XonoticProfileTab, XonoticTab) METHOD(XonoticProfileTab, fill, void(entity)); METHOD(XonoticProfileTab, draw, void(entity)); - ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9) - ATTRIB(XonoticProfileTab, rows, float, 23) - ATTRIB(XonoticProfileTab, columns, float, 6.1) // added extra .2 for center space - ATTRIB(XonoticProfileTab, playerNameLabel, entity, NULL) - ATTRIB(XonoticProfileTab, playerNameLabelAlpha, float, SKINALPHA_HEADER) + ATTRIB(XonoticProfileTab, intendedWidth, float, 0.9); + ATTRIB(XonoticProfileTab, rows, float, 23); + ATTRIB(XonoticProfileTab, columns, float, 6.1); // added extra .2 for center space + ATTRIB(XonoticProfileTab, playerNameLabel, entity); + ATTRIB(XonoticProfileTab, playerNameLabelAlpha, float, SKINALPHA_HEADER); ENDCLASS(XonoticProfileTab) entity makeXonoticProfileTab(); diff --git a/qcsrc/menu/xonotic/dialog_quit.qh b/qcsrc/menu/xonotic/dialog_quit.qh index 0b06c30d6a..6e8c9fea9a 100644 --- a/qcsrc/menu/xonotic/dialog_quit.qh +++ b/qcsrc/menu/xonotic/dialog_quit.qh @@ -3,11 +3,11 @@ #include "dialog.qh" CLASS(XonoticQuitDialog, XonoticDialog) METHOD(XonoticQuitDialog, fill, void(entity)); - ATTRIB(XonoticQuitDialog, title, string, _("Quit")) - ATTRIB(XonoticQuitDialog, tooltip, string, _("Quit the game")) - ATTRIB(XonoticQuitDialog, color, vector, SKINCOLOR_DIALOG_QUIT) - ATTRIB(XonoticQuitDialog, intendedWidth, float, 0.5) - ATTRIB(XonoticQuitDialog, rows, float, 3) - ATTRIB(XonoticQuitDialog, columns, float, 2) - ATTRIB(XonoticQuitDialog, name, string, "Quit") + ATTRIB(XonoticQuitDialog, title, string, _("Quit")); + ATTRIB(XonoticQuitDialog, tooltip, string, _("Quit the game")); + ATTRIB(XonoticQuitDialog, color, vector, SKINCOLOR_DIALOG_QUIT); + ATTRIB(XonoticQuitDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticQuitDialog, rows, float, 3); + ATTRIB(XonoticQuitDialog, columns, float, 2); + ATTRIB(XonoticQuitDialog, name, string, "Quit"); ENDCLASS(XonoticQuitDialog) diff --git a/qcsrc/menu/xonotic/dialog_sandboxtools.qh b/qcsrc/menu/xonotic/dialog_sandboxtools.qh index ab49ec867d..2ef0d79739 100644 --- a/qcsrc/menu/xonotic/dialog_sandboxtools.qh +++ b/qcsrc/menu/xonotic/dialog_sandboxtools.qh @@ -3,11 +3,11 @@ #include "rootdialog.qh" CLASS(XonoticSandboxToolsDialog, XonoticRootDialog) METHOD(XonoticSandboxToolsDialog, fill, void(entity)); - ATTRIB(XonoticSandboxToolsDialog, title, string, _("Sandbox Tools")) - ATTRIB(XonoticSandboxToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS) - ATTRIB(XonoticSandboxToolsDialog, intendedWidth, float, 0.8) - ATTRIB(XonoticSandboxToolsDialog, rows, float, 16) - ATTRIB(XonoticSandboxToolsDialog, columns, float, 4) - ATTRIB(XonoticSandboxToolsDialog, name, string, "SandboxTools") - ATTRIB(XonoticSandboxToolsDialog, requiresConnection, float, true) + ATTRIB(XonoticSandboxToolsDialog, title, string, _("Sandbox Tools")); + ATTRIB(XonoticSandboxToolsDialog, color, vector, SKINCOLOR_DIALOG_SANDBOXTOOLS); + ATTRIB(XonoticSandboxToolsDialog, intendedWidth, float, 0.8); + ATTRIB(XonoticSandboxToolsDialog, rows, float, 16); + ATTRIB(XonoticSandboxToolsDialog, columns, float, 4); + ATTRIB(XonoticSandboxToolsDialog, name, string, "SandboxTools"); + ATTRIB(XonoticSandboxToolsDialog, requiresConnection, float, true); ENDCLASS(XonoticSandboxToolsDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings.qh b/qcsrc/menu/xonotic/dialog_settings.qh index 6c7eacb7ca..3564a02e87 100644 --- a/qcsrc/menu/xonotic/dialog_settings.qh +++ b/qcsrc/menu/xonotic/dialog_settings.qh @@ -3,10 +3,10 @@ #include "dialog.qh" CLASS(XonoticSettingsDialog, XonoticDialog) METHOD(XonoticSettingsDialog, fill, void(entity)); - ATTRIB(XonoticSettingsDialog, title, string, _("Settings")) - ATTRIB(XonoticSettingsDialog, tooltip, string, _("Change the game settings")) - ATTRIB(XonoticSettingsDialog, color, vector, SKINCOLOR_DIALOG_SETTINGS) - ATTRIB(XonoticSettingsDialog, intendedWidth, float, 0.96) - ATTRIB(XonoticSettingsDialog, rows, float, 18) - ATTRIB(XonoticSettingsDialog, columns, float, 6) + ATTRIB(XonoticSettingsDialog, title, string, _("Settings")); + ATTRIB(XonoticSettingsDialog, tooltip, string, _("Change the game settings")); + ATTRIB(XonoticSettingsDialog, color, vector, SKINCOLOR_DIALOG_SETTINGS); + ATTRIB(XonoticSettingsDialog, intendedWidth, float, 0.96); + ATTRIB(XonoticSettingsDialog, rows, float, 18); + ATTRIB(XonoticSettingsDialog, columns, float, 6); ENDCLASS(XonoticSettingsDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_audio.qh b/qcsrc/menu/xonotic/dialog_settings_audio.qh index a3e8a8c532..5794a70ccf 100644 --- a/qcsrc/menu/xonotic/dialog_settings_audio.qh +++ b/qcsrc/menu/xonotic/dialog_settings_audio.qh @@ -3,9 +3,9 @@ #include "tab.qh" CLASS(XonoticAudioSettingsTab, XonoticTab) METHOD(XonoticAudioSettingsTab, fill, void(entity)); - ATTRIB(XonoticAudioSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticAudioSettingsTab, rows, float, 15.5) - ATTRIB(XonoticAudioSettingsTab, columns, float, 6.2) // added extra .2 for center space - ATTRIB(XonoticAudioSettingsTab, hiddenMenuSoundsSlider, entity, NULL) + ATTRIB(XonoticAudioSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticAudioSettingsTab, rows, float, 15.5); + ATTRIB(XonoticAudioSettingsTab, columns, float, 6.2); // added extra .2 for center space + ATTRIB(XonoticAudioSettingsTab, hiddenMenuSoundsSlider, entity); ENDCLASS(XonoticAudioSettingsTab) entity makeXonoticAudioSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_effects.qh b/qcsrc/menu/xonotic/dialog_settings_effects.qh index 97f3854fbb..369c68d913 100644 --- a/qcsrc/menu/xonotic/dialog_settings_effects.qh +++ b/qcsrc/menu/xonotic/dialog_settings_effects.qh @@ -3,8 +3,8 @@ #include "tab.qh" CLASS(XonoticEffectsSettingsTab, XonoticTab) METHOD(XonoticEffectsSettingsTab, fill, void(entity)); - ATTRIB(XonoticEffectsSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticEffectsSettingsTab, rows, float, 15.5) - ATTRIB(XonoticEffectsSettingsTab, columns, float, 6.2) // added extra .2 for center space + ATTRIB(XonoticEffectsSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticEffectsSettingsTab, rows, float, 15.5); + ATTRIB(XonoticEffectsSettingsTab, columns, float, 6.2); // added extra .2 for center space ENDCLASS(XonoticEffectsSettingsTab) entity makeXonoticEffectsSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_game.qh b/qcsrc/menu/xonotic/dialog_settings_game.qh index c415e96732..b9231642b1 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game.qh @@ -9,19 +9,19 @@ ENDCLASS(SettingSource) #include "listbox.qh" CLASS(XonoticRegisteredSettingsList, XonoticListBox) - ATTRIB(XonoticRegisteredSettingsList, alphaBG, float, 0) - ATTRIB(XonoticRegisteredSettingsList, itemAbsSize, vector, '0 0 0') - ATTRIB(XonoticRegisteredSettingsList, origin, vector, '0 0 0') - ATTRIB(XonoticRegisteredSettingsList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticRegisteredSettingsList, realUpperMargin, float, 0) - ATTRIB(XonoticRegisteredSettingsList, rowsPerItem, float, 2) - ATTRIB(XonoticRegisteredSettingsList, stringFilterBox, entity, NULL) - ATTRIB(XonoticRegisteredSettingsList, stringFilter, string, string_null) - ATTRIB(XonoticRegisteredSettingsList, typeToSearchString, string, string_null) - ATTRIB(XonoticRegisteredSettingsList, typeToSearchTime, float, 0) - ATTRIB(XonoticRegisteredSettingsList, source, DataSource, NULL) - ATTRIB(XonoticRegisteredSettingsList, onChange, void(entity, entity), func_null) - ATTRIB(XonoticRegisteredSettingsList, onChangeEntity, entity, NULL) + ATTRIB(XonoticRegisteredSettingsList, alphaBG, float, 0); + ATTRIB(XonoticRegisteredSettingsList, itemAbsSize, vector, '0 0 0'); + ATTRIB(XonoticRegisteredSettingsList, origin, vector, '0 0 0'); + ATTRIB(XonoticRegisteredSettingsList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticRegisteredSettingsList, realUpperMargin, float, 0); + ATTRIB(XonoticRegisteredSettingsList, rowsPerItem, float, 2); + ATTRIB(XonoticRegisteredSettingsList, stringFilterBox, entity); + ATTRIB(XonoticRegisteredSettingsList, stringFilter, string); + ATTRIB(XonoticRegisteredSettingsList, typeToSearchString, string); + ATTRIB(XonoticRegisteredSettingsList, typeToSearchTime, float, 0); + ATTRIB(XonoticRegisteredSettingsList, source, DataSource); + ATTRIB(XonoticRegisteredSettingsList, onChange, void(entity, entity)); + ATTRIB(XonoticRegisteredSettingsList, onChangeEntity, entity); METHOD(XonoticRegisteredSettingsList, focusedItemChangeNotify, void(entity)); METHOD(XonoticRegisteredSettingsList, drawListBoxItem, void(entity this, int i, vector absSize, bool isSelected, bool isFocused)); METHOD(XonoticRegisteredSettingsList, focusedItemChangeNotify, void(entity this)); @@ -33,13 +33,13 @@ ENDCLASS(XonoticRegisteredSettingsList) #include "tab.qh" CLASS(XonoticGameSettingsTab, XonoticTab) - ATTRIB(XonoticGameSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameSettingsTab, columns, float, 6.5) - ATTRIB(XonoticGameSettingsTab, source, DataSource, NEW(SettingSource)) - ATTRIB(XonoticGameSettingsTab, topicList, entity, NEW(XonoticRegisteredSettingsList, this.source)) - ATTRIB(XonoticGameSettingsTab, currentPanel, entity, NEW(XonoticTab)) - ATTRIB(XonoticGameSettingsTab, currentItem, entity, NULL) + ATTRIB(XonoticGameSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameSettingsTab, columns, float, 6.5); + ATTRIB(XonoticGameSettingsTab, source, DataSource, NEW(SettingSource)); + ATTRIB(XonoticGameSettingsTab, topicList, entity, NEW(XonoticRegisteredSettingsList, this.source)); + ATTRIB(XonoticGameSettingsTab, currentPanel, entity, NEW(XonoticTab)); + ATTRIB(XonoticGameSettingsTab, currentItem, entity); METHOD(XonoticGameSettingsTab, topicChangeNotify, void(entity, entity this)); METHOD(XonoticGameSettingsTab, fill, void(entity this)); INIT(XonoticGameSettingsTab) diff --git a/qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh b/qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh index 3e12220561..33576a3ec0 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_crosshair.qh @@ -4,10 +4,10 @@ CLASS(XonoticGameCrosshairSettingsTab, XonoticTab) METHOD(XonoticGameCrosshairSettingsTab, fill, void(entity)); METHOD(XonoticGameCrosshairSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameCrosshairSettingsTab, title, string, _("Crosshair")) - ATTRIB(XonoticGameCrosshairSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameCrosshairSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameCrosshairSettingsTab, columns, float, 6.2) + ATTRIB(XonoticGameCrosshairSettingsTab, title, string, _("Crosshair")); + ATTRIB(XonoticGameCrosshairSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameCrosshairSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameCrosshairSettingsTab, columns, float, 6.2); ENDCLASS(XonoticGameCrosshairSettingsTab) entity makeXonoticGameCrosshairSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_game_hud.qc b/qcsrc/menu/xonotic/dialog_settings_game_hud.qc index 6525e2779c..e16693675b 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_hud.qc +++ b/qcsrc/menu/xonotic/dialog_settings_game_hud.qc @@ -43,20 +43,18 @@ void XonoticGameHUDSettingsTab_fill(entity me) me.TD(me, 1, 3, e = makeXonoticHeaderLabel(_("Scoreboard"))); me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Alpha:"))); - me.TD(me, 1, 2, e = makeXonoticSlider(0, 1, 0.05, "scoreboard_alpha_bg")); + me.TD(me, 1, 2, e = makeXonoticSlider(0, 1, 0.05, "hud_panel_scoreboard_bg_alpha")); me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Fading speed:"))); me.TD(me, 1, 2, e = makeXonoticScoreboardFadeTimeSlider()); me.TR(me); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Side padding:"))); - me.TD(me, 1, 2, e = makeXonoticSlider(0.05, 0.3, 0.01, "scoreboard_offset_left")); - makeMulti(e, "scoreboard_offset_right"); + me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_table_highlight", _("Enable rows / columns highlighting"))); me.TR(me); //me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "scoreboard_respawntime_decimals", _("Show decimals in respawn countdown"))); + me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_respawntime_decimals", _("Show decimals in respawn countdown"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "scoreboard_accuracy", _("Show accuracy underneath scoreboard"))); + me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "hud_panel_scoreboard_accuracy", _("Show accuracy underneath scoreboard"))); me.TR(me); me.TR(me); diff --git a/qcsrc/menu/xonotic/dialog_settings_game_hud.qh b/qcsrc/menu/xonotic/dialog_settings_game_hud.qh index 1db5890be8..a9851dc634 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_hud.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_hud.qh @@ -4,10 +4,10 @@ CLASS(XonoticGameHUDSettingsTab, XonoticTab) METHOD(XonoticGameHUDSettingsTab, fill, void(entity)); METHOD(XonoticGameHUDSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameHUDSettingsTab, title, string, _("HUD")) - ATTRIB(XonoticGameHUDSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameHUDSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameHUDSettingsTab, columns, float, 6.2) + ATTRIB(XonoticGameHUDSettingsTab, title, string, _("HUD")); + ATTRIB(XonoticGameHUDSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameHUDSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameHUDSettingsTab, columns, float, 6.2); ENDCLASS(XonoticGameHUDSettingsTab) entity makeXonoticGameHUDSettingsTab(); void HUDSetup_Start(entity me, entity btn); diff --git a/qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh b/qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh index 6e8421eecd..bef13432ec 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_hudconfirm.qh @@ -3,9 +3,9 @@ #include "dialog.qh" CLASS(XonoticHUDConfirmDialog, XonoticDialog) METHOD(XonoticHUDConfirmDialog, fill, void(entity)); - ATTRIB(XonoticHUDConfirmDialog, title, string, _("Enter HUD editor")) - ATTRIB(XonoticHUDConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM) - ATTRIB(XonoticHUDConfirmDialog, intendedWidth, float, 0.5) - ATTRIB(XonoticHUDConfirmDialog, rows, float, 4) - ATTRIB(XonoticHUDConfirmDialog, columns, float, 2) + ATTRIB(XonoticHUDConfirmDialog, title, string, _("Enter HUD editor")); + ATTRIB(XonoticHUDConfirmDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM); + ATTRIB(XonoticHUDConfirmDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticHUDConfirmDialog, rows, float, 4); + ATTRIB(XonoticHUDConfirmDialog, columns, float, 2); ENDCLASS(XonoticHUDConfirmDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_game_messages.qc b/qcsrc/menu/xonotic/dialog_settings_game_messages.qc index 0ea55f637e..dc28dc9ba6 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_messages.qc +++ b/qcsrc/menu/xonotic/dialog_settings_game_messages.qc @@ -56,7 +56,7 @@ void XonoticGameMessageSettingsTab_fill(entity me) setDependentAND(e, "notification_show_sprees", 1, 1, "notification_show_sprees_info", 1, 3); me.TR(me); me.TD(me, 1, 3, e = makeXonoticCheckBoxEx_T(2, 1, "notification_CHOICE_FRAG", _("Add extra frag information to centerprint when available"), "-")); - makeMulti(e, "notification_CHOICE_FRAGGED notification_CHOICE_TYPEFRAG notification_CHOICE_TYPEFRAGGED"); + makeMulti(e, "notification_CHOICE_FRAGGED notification_CHOICE_TYPEFRAG notification_CHOICE_TYPEFRAGGED notification_CHOICE_FRAG_FIRE notification_CHOICE_FRAGGED_FIRE notification_CHOICE_FRAG_FREEZE notification_CHOICE_FRAGGED_FREEZE"); e.sendCvars = true; me.TR(me); me.TD(me, 1, 3, e = makeXonoticCheckBox_T(0, "notification_show_location", _("Add frag location to death messages when available"), "-")); diff --git a/qcsrc/menu/xonotic/dialog_settings_game_messages.qh b/qcsrc/menu/xonotic/dialog_settings_game_messages.qh index 56d13bcb96..28a7698626 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_messages.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_messages.qh @@ -4,11 +4,11 @@ CLASS(XonoticGameMessageSettingsTab, XonoticTab) METHOD(XonoticGameMessageSettingsTab, fill, void(entity)); METHOD(XonoticGameMessageSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameMessageSettingsTab, title, string, _("Messages")) - ATTRIB(XonoticGameMessageSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameMessageSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameMessageSettingsTab, columns, float, 6) - ATTRIB(XonoticGameMessageSettingsTab, weaponsList, entity, NULL) + ATTRIB(XonoticGameMessageSettingsTab, title, string, _("Messages")); + ATTRIB(XonoticGameMessageSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameMessageSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameMessageSettingsTab, columns, float, 6); + ATTRIB(XonoticGameMessageSettingsTab, weaponsList, entity); ENDCLASS(XonoticGameMessageSettingsTab) entity makeXonoticGameMessageSettingsTab(); #include "../gamesettings.qh" diff --git a/qcsrc/menu/xonotic/dialog_settings_game_model.qc b/qcsrc/menu/xonotic/dialog_settings_game_model.qc index f0f95d74b9..b57d7cae8a 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_model.qc +++ b/qcsrc/menu/xonotic/dialog_settings_game_model.qc @@ -50,7 +50,12 @@ void XonoticGameModelSettingsTab_fill(entity me) me.TR(me); me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayermodels", _("Force player models to mine"))); me.TR(me); - me.TD(me, 1, 3, e = makeXonoticCheckBox(0, "cl_forceplayercolors", _("Force player colors to mine"))); + me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Force player colors to mine"))); + me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_forceplayercolors")); + e.addValue(e, _("Never"), "0"); + e.addValue(e, _("In non teamplay modes only"), "1"); + e.addValue(e, _("Always"), "2"); + e.configureXonoticTextSliderValues(e); me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Body fading:"))); me.TD(me, 1, 2, e = makeXonoticSlider(0, 2, 0.2, "cl_deathglow")); diff --git a/qcsrc/menu/xonotic/dialog_settings_game_model.qh b/qcsrc/menu/xonotic/dialog_settings_game_model.qh index 8dd6eaecb3..c3caabee72 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_model.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_model.qh @@ -4,11 +4,11 @@ CLASS(XonoticGameModelSettingsTab, XonoticTab) METHOD(XonoticGameModelSettingsTab, fill, void(entity)); METHOD(XonoticGameModelSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameModelSettingsTab, title, string, _("Models")) - ATTRIB(XonoticGameModelSettingsTab, titleTooltip, string, _("Customize how players and items are displayed in game")) - ATTRIB(XonoticGameModelSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameModelSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameModelSettingsTab, columns, float, 5) + ATTRIB(XonoticGameModelSettingsTab, title, string, _("Models")); + ATTRIB(XonoticGameModelSettingsTab, titleTooltip, string, _("Customize how players and items are displayed in game")); + ATTRIB(XonoticGameModelSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameModelSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameModelSettingsTab, columns, float, 5); ENDCLASS(XonoticGameModelSettingsTab) entity makeXonoticGameModelSettingsTab(); #include "../gamesettings.qh" diff --git a/qcsrc/menu/xonotic/dialog_settings_game_view.qh b/qcsrc/menu/xonotic/dialog_settings_game_view.qh index 7ec4c79366..f9d6001199 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_view.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_view.qh @@ -4,10 +4,10 @@ CLASS(XonoticGameViewSettingsTab, XonoticTab) METHOD(XonoticGameViewSettingsTab, fill, void(entity)); METHOD(XonoticGameViewSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameViewSettingsTab, title, string, _("View")) - ATTRIB(XonoticGameViewSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameViewSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameViewSettingsTab, columns, float, 6.2) + ATTRIB(XonoticGameViewSettingsTab, title, string, _("View")); + ATTRIB(XonoticGameViewSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameViewSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameViewSettingsTab, columns, float, 6.2); ENDCLASS(XonoticGameViewSettingsTab) entity makeXonoticGameViewSettingsTab(); #include "../gamesettings.qh" diff --git a/qcsrc/menu/xonotic/dialog_settings_game_weapons.qh b/qcsrc/menu/xonotic/dialog_settings_game_weapons.qh index 73ee66fb27..ded41a5abd 100644 --- a/qcsrc/menu/xonotic/dialog_settings_game_weapons.qh +++ b/qcsrc/menu/xonotic/dialog_settings_game_weapons.qh @@ -4,11 +4,11 @@ CLASS(XonoticGameWeaponsSettingsTab, XonoticTab) METHOD(XonoticGameWeaponsSettingsTab, fill, void(entity)); METHOD(XonoticGameWeaponsSettingsTab, showNotify, void(entity)); - ATTRIB(XonoticGameWeaponsSettingsTab, title, string, _("Weapons")) - ATTRIB(XonoticGameWeaponsSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticGameWeaponsSettingsTab, rows, float, 15.5) - ATTRIB(XonoticGameWeaponsSettingsTab, columns, float, 6) - ATTRIB(XonoticGameWeaponsSettingsTab, weaponsList, entity, NULL) + ATTRIB(XonoticGameWeaponsSettingsTab, title, string, _("Weapons")); + ATTRIB(XonoticGameWeaponsSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticGameWeaponsSettingsTab, rows, float, 15.5); + ATTRIB(XonoticGameWeaponsSettingsTab, columns, float, 6); + ATTRIB(XonoticGameWeaponsSettingsTab, weaponsList, entity); ENDCLASS(XonoticGameWeaponsSettingsTab) entity makeXonoticGameWeaponsSettingsTab(); #include "../gamesettings.qh" diff --git a/qcsrc/menu/xonotic/dialog_settings_input.qh b/qcsrc/menu/xonotic/dialog_settings_input.qh index f8e6feda6c..0409082989 100644 --- a/qcsrc/menu/xonotic/dialog_settings_input.qh +++ b/qcsrc/menu/xonotic/dialog_settings_input.qh @@ -3,8 +3,8 @@ #include "tab.qh" CLASS(XonoticInputSettingsTab, XonoticTab) METHOD(XonoticInputSettingsTab, fill, void(entity)); - ATTRIB(XonoticInputSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticInputSettingsTab, rows, float, 15.5) - ATTRIB(XonoticInputSettingsTab, columns, float, 6.2) // added extra .2 for center space + ATTRIB(XonoticInputSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticInputSettingsTab, rows, float, 15.5); + ATTRIB(XonoticInputSettingsTab, columns, float, 6.2); // added extra .2 for center space ENDCLASS(XonoticInputSettingsTab) entity makeXonoticInputSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_input_userbind.qh b/qcsrc/menu/xonotic/dialog_settings_input_userbind.qh index 5ff3a2e0bc..561a04b32b 100644 --- a/qcsrc/menu/xonotic/dialog_settings_input_userbind.qh +++ b/qcsrc/menu/xonotic/dialog_settings_input_userbind.qh @@ -4,14 +4,14 @@ CLASS(XonoticUserbindEditDialog, XonoticDialog) METHOD(XonoticUserbindEditDialog, loadUserBind, void(entity, string, string, string)); METHOD(XonoticUserbindEditDialog, fill, void(entity)); - ATTRIB(XonoticUserbindEditDialog, title, string, _("User defined key bind")) - ATTRIB(XonoticUserbindEditDialog, color, vector, SKINCOLOR_DIALOG_USERBIND) - ATTRIB(XonoticUserbindEditDialog, intendedWidth, float, 0.7) - ATTRIB(XonoticUserbindEditDialog, rows, float, 4) - ATTRIB(XonoticUserbindEditDialog, columns, float, 3) - ATTRIB(XonoticUserbindEditDialog, keybindBox, entity, NULL) + ATTRIB(XonoticUserbindEditDialog, title, string, _("User defined key bind")); + ATTRIB(XonoticUserbindEditDialog, color, vector, SKINCOLOR_DIALOG_USERBIND); + ATTRIB(XonoticUserbindEditDialog, intendedWidth, float, 0.7); + ATTRIB(XonoticUserbindEditDialog, rows, float, 4); + ATTRIB(XonoticUserbindEditDialog, columns, float, 3); + ATTRIB(XonoticUserbindEditDialog, keybindBox, entity); - ATTRIB(XonoticUserbindEditDialog, nameBox, entity, NULL) - ATTRIB(XonoticUserbindEditDialog, commandPressBox, entity, NULL) - ATTRIB(XonoticUserbindEditDialog, commandReleaseBox, entity, NULL) + ATTRIB(XonoticUserbindEditDialog, nameBox, entity); + ATTRIB(XonoticUserbindEditDialog, commandPressBox, entity); + ATTRIB(XonoticUserbindEditDialog, commandReleaseBox, entity); ENDCLASS(XonoticUserbindEditDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_misc.qc b/qcsrc/menu/xonotic/dialog_settings_misc.qc index d951275db1..8f98df5816 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc.qc +++ b/qcsrc/menu/xonotic/dialog_settings_misc.qc @@ -9,6 +9,8 @@ #include "mainwindow.qh" #define ADDVALUE_FPS(i) e.addValue(e, strzone(sprintf(_("%d fps"), i)), #i) +#define ADDVALUE_SPEED_KB(i) e.addValue(e, strzone(sprintf(_("%d kb/s"), i)), #i) +#define ADDVALUE_SPEED_MB(i, j) e.addValue(e, strzone(sprintf(_("%d MB/s"), i)), #j) entity makeXonoticMiscSettingsTab() { entity me; @@ -51,9 +53,16 @@ void XonoticMiscSettingsTab_fill(entity me) me.TD(me, 1, 2, e = makeXonoticSlider_T(1, 5, 1, "cl_curl_maxdownloads", _("Maximum number of concurrent HTTP/FTP downloads"))); me.TR(me); - me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Speed (kB/s):"))); - me.TD(me, 1, 2, e = makeXonoticSlider_T(10, 2000, 50, "cl_curl_maxspeed", - _("Maximum download speed"))); + me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Download speed:"))); + me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_curl_maxspeed")); + ADDVALUE_SPEED_KB(50); + ADDVALUE_SPEED_KB(100); + ADDVALUE_SPEED_KB(300); + ADDVALUE_SPEED_KB(500); + ADDVALUE_SPEED_MB(1, 1000); + ADDVALUE_SPEED_MB(2, 2000); + e.addValue(e, strzone(_("Unlimited")), "0"); + e.configureXonoticTextSliderValues(e); me.TR(me); if(cvar("developer")) { @@ -77,16 +86,15 @@ void XonoticMiscSettingsTab_fill(entity me) me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Maximum:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider("cl_maxfps")); - ADDVALUE_FPS(5); - ADDVALUE_FPS(10); - ADDVALUE_FPS(20); ADDVALUE_FPS(30); ADDVALUE_FPS(40); ADDVALUE_FPS(50); ADDVALUE_FPS(60); ADDVALUE_FPS(70); + ADDVALUE_FPS(80); ADDVALUE_FPS(100); ADDVALUE_FPS(125); + ADDVALUE_FPS(150); ADDVALUE_FPS(200); e.addValue(e, ZCTX(_("MAXFPS^Unlimited")), "0"); e.configureXonoticTextSliderValues(e); @@ -98,8 +106,10 @@ void XonoticMiscSettingsTab_fill(entity me) ADDVALUE_FPS(40); ADDVALUE_FPS(50); ADDVALUE_FPS(60); + ADDVALUE_FPS(80); ADDVALUE_FPS(100); ADDVALUE_FPS(125); + ADDVALUE_FPS(150); ADDVALUE_FPS(200); e.configureXonoticTextSliderValues(e); me.TR(me); diff --git a/qcsrc/menu/xonotic/dialog_settings_misc.qh b/qcsrc/menu/xonotic/dialog_settings_misc.qh index f3f367e686..a2a86b7e1f 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc.qh +++ b/qcsrc/menu/xonotic/dialog_settings_misc.qh @@ -3,8 +3,8 @@ #include "tab.qh" CLASS(XonoticMiscSettingsTab, XonoticTab) METHOD(XonoticMiscSettingsTab, fill, void(entity)); - ATTRIB(XonoticMiscSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticMiscSettingsTab, rows, float, 15.5) - ATTRIB(XonoticMiscSettingsTab, columns, float, 6.2) + ATTRIB(XonoticMiscSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticMiscSettingsTab, rows, float, 15.5); + ATTRIB(XonoticMiscSettingsTab, columns, float, 6.2); ENDCLASS(XonoticMiscSettingsTab) entity makeXonoticMiscSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh b/qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh index f5011f7717..4fddf65867 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh +++ b/qcsrc/menu/xonotic/dialog_settings_misc_cvars.qh @@ -4,9 +4,9 @@ CLASS(XonoticCvarsDialog, XonoticDialog) METHOD(XonoticCvarsDialog, fill, void(entity)); METHOD(XonoticCvarsDialog, showNotify, void(entity)); - ATTRIB(XonoticCvarsDialog, title, string, _("Advanced settings")) - ATTRIB(XonoticCvarsDialog, color, vector, SKINCOLOR_DIALOG_CVARS) - ATTRIB(XonoticCvarsDialog, intendedWidth, float, 0.8) - ATTRIB(XonoticCvarsDialog, rows, float, 24) - ATTRIB(XonoticCvarsDialog, columns, float, 6) + ATTRIB(XonoticCvarsDialog, title, string, _("Advanced settings")); + ATTRIB(XonoticCvarsDialog, color, vector, SKINCOLOR_DIALOG_CVARS); + ATTRIB(XonoticCvarsDialog, intendedWidth, float, 0.8); + ATTRIB(XonoticCvarsDialog, rows, float, 24); + ATTRIB(XonoticCvarsDialog, columns, float, 6); ENDCLASS(XonoticCvarsDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_misc_reset.qh b/qcsrc/menu/xonotic/dialog_settings_misc_reset.qh index 1cbc7773ab..f1d02ff909 100644 --- a/qcsrc/menu/xonotic/dialog_settings_misc_reset.qh +++ b/qcsrc/menu/xonotic/dialog_settings_misc_reset.qh @@ -3,10 +3,10 @@ #include "dialog.qh" CLASS(XonoticResetDialog, XonoticDialog) METHOD(XonoticResetDialog, fill, void(entity)); - ATTRIB(XonoticResetDialog, title, string, _("Factory reset")) - ATTRIB(XonoticResetDialog, color, vector, SKINCOLOR_DIALOG_QUIT) - ATTRIB(XonoticResetDialog, intendedWidth, float, 0.5) - ATTRIB(XonoticResetDialog, rows, float, 4) - ATTRIB(XonoticResetDialog, columns, float, 2) - ATTRIB(XonoticResetDialog, name, string, "Factory reset") + ATTRIB(XonoticResetDialog, title, string, _("Factory reset")); + ATTRIB(XonoticResetDialog, color, vector, SKINCOLOR_DIALOG_QUIT); + ATTRIB(XonoticResetDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticResetDialog, rows, float, 4); + ATTRIB(XonoticResetDialog, columns, float, 2); + ATTRIB(XonoticResetDialog, name, string, "Factory reset"); ENDCLASS(XonoticResetDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_user.qh b/qcsrc/menu/xonotic/dialog_settings_user.qh index 3a5ccc3b65..22f84d0f0b 100644 --- a/qcsrc/menu/xonotic/dialog_settings_user.qh +++ b/qcsrc/menu/xonotic/dialog_settings_user.qh @@ -3,8 +3,8 @@ #include "tab.qh" CLASS(XonoticUserSettingsTab, XonoticTab) METHOD(XonoticUserSettingsTab, fill, void(entity)); - ATTRIB(XonoticUserSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticUserSettingsTab, rows, float, 15.5) - ATTRIB(XonoticUserSettingsTab, columns, float, 6) + ATTRIB(XonoticUserSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticUserSettingsTab, rows, float, 15.5); + ATTRIB(XonoticUserSettingsTab, columns, float, 6); ENDCLASS(XonoticUserSettingsTab) entity makeXonoticUserSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh b/qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh index 6028a441ca..ba5c8a5d1c 100644 --- a/qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh +++ b/qcsrc/menu/xonotic/dialog_settings_user_languagewarning.qh @@ -3,9 +3,9 @@ #include "dialog.qh" CLASS(XonoticLanguageWarningDialog, XonoticDialog) METHOD(XonoticLanguageWarningDialog, fill, void(entity)); - ATTRIB(XonoticLanguageWarningDialog, title, string, _("Warning")) - ATTRIB(XonoticLanguageWarningDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM) - ATTRIB(XonoticLanguageWarningDialog, intendedWidth, float, 0.6) - ATTRIB(XonoticLanguageWarningDialog, rows, float, 5) - ATTRIB(XonoticLanguageWarningDialog, columns, float, 4) + ATTRIB(XonoticLanguageWarningDialog, title, string, _("Warning")); + ATTRIB(XonoticLanguageWarningDialog, color, vector, SKINCOLOR_DIALOG_HUDCONFIRM); + ATTRIB(XonoticLanguageWarningDialog, intendedWidth, float, 0.6); + ATTRIB(XonoticLanguageWarningDialog, rows, float, 5); + ATTRIB(XonoticLanguageWarningDialog, columns, float, 4); ENDCLASS(XonoticLanguageWarningDialog) diff --git a/qcsrc/menu/xonotic/dialog_settings_video.qc b/qcsrc/menu/xonotic/dialog_settings_video.qc index 9320a6ebf2..8ee51fde97 100644 --- a/qcsrc/menu/xonotic/dialog_settings_video.qc +++ b/qcsrc/menu/xonotic/dialog_settings_video.qc @@ -46,6 +46,7 @@ void XonoticVideoSettingsTab_fill(entity me) e.addValue(e, ZCTX(_("SZ^Gigantic")), "0.75"); e.addValue(e, ZCTX(_("SZ^Colossal")), "1"); e.configureXonoticTextSliderValues(e); + e.applyButton = videoApplyButton; me.TR(me); me.TD(me, 1, 1, e = makeXonoticTextLabel(0, _("Color depth:"))); me.TD(me, 1, 2, e = makeXonoticTextSlider_T("vid_bitsperpixel", diff --git a/qcsrc/menu/xonotic/dialog_settings_video.qh b/qcsrc/menu/xonotic/dialog_settings_video.qh index 0abe322c38..61d8a368e0 100644 --- a/qcsrc/menu/xonotic/dialog_settings_video.qh +++ b/qcsrc/menu/xonotic/dialog_settings_video.qh @@ -3,9 +3,9 @@ #include "tab.qh" CLASS(XonoticVideoSettingsTab, XonoticTab) METHOD(XonoticVideoSettingsTab, fill, void(entity)); - ATTRIB(XonoticVideoSettingsTab, intendedWidth, float, 0.9) - ATTRIB(XonoticVideoSettingsTab, rows, float, 15.5) - ATTRIB(XonoticVideoSettingsTab, columns, float, 6.2) // added extra .2 for center space - ATTRIB(XonoticVideoSettingsTab, name, string, "videosettings") + ATTRIB(XonoticVideoSettingsTab, intendedWidth, float, 0.9); + ATTRIB(XonoticVideoSettingsTab, rows, float, 15.5); + ATTRIB(XonoticVideoSettingsTab, columns, float, 6.2); // added extra .2 for center space + ATTRIB(XonoticVideoSettingsTab, name, string, "videosettings"); ENDCLASS(XonoticVideoSettingsTab) entity makeXonoticVideoSettingsTab(); diff --git a/qcsrc/menu/xonotic/dialog_singleplayer.qh b/qcsrc/menu/xonotic/dialog_singleplayer.qh index b3dabaf6ce..c7691fbba4 100644 --- a/qcsrc/menu/xonotic/dialog_singleplayer.qh +++ b/qcsrc/menu/xonotic/dialog_singleplayer.qh @@ -3,11 +3,11 @@ #include "dialog.qh" CLASS(XonoticSingleplayerDialog, XonoticDialog) METHOD(XonoticSingleplayerDialog, fill, void(entity)); - ATTRIB(XonoticSingleplayerDialog, title, string, _("Singleplayer")) - ATTRIB(XonoticSingleplayerDialog, tooltip, string, _("Play the singleplayer campaign or instant action matches against bots")) - ATTRIB(XonoticSingleplayerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER) - ATTRIB(XonoticSingleplayerDialog, intendedWidth, float, 0.80) - ATTRIB(XonoticSingleplayerDialog, rows, float, 24) - ATTRIB(XonoticSingleplayerDialog, columns, float, 5) - ATTRIB(XonoticSingleplayerDialog, campaignBox, entity, NULL) + ATTRIB(XonoticSingleplayerDialog, title, string, _("Singleplayer")); + ATTRIB(XonoticSingleplayerDialog, tooltip, string, _("Play the singleplayer campaign or instant action matches against bots")); + ATTRIB(XonoticSingleplayerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER); + ATTRIB(XonoticSingleplayerDialog, intendedWidth, float, 0.80); + ATTRIB(XonoticSingleplayerDialog, rows, float, 24); + ATTRIB(XonoticSingleplayerDialog, columns, float, 5); + ATTRIB(XonoticSingleplayerDialog, campaignBox, entity); ENDCLASS(XonoticSingleplayerDialog) diff --git a/qcsrc/menu/xonotic/dialog_singleplayer_winner.qh b/qcsrc/menu/xonotic/dialog_singleplayer_winner.qh index 1de958b8b7..6a6b2d37bf 100644 --- a/qcsrc/menu/xonotic/dialog_singleplayer_winner.qh +++ b/qcsrc/menu/xonotic/dialog_singleplayer_winner.qh @@ -4,9 +4,9 @@ CLASS(XonoticWinnerDialog, XonoticDialog) METHOD(XonoticWinnerDialog, fill, void(entity)); METHOD(XonoticWinnerDialog, focusEnter, void(entity)); - ATTRIB(XonoticWinnerDialog, title, string, _("Winner")) - ATTRIB(XonoticWinnerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER) - ATTRIB(XonoticWinnerDialog, intendedWidth, float, 0.32) - ATTRIB(XonoticWinnerDialog, rows, float, 12) - ATTRIB(XonoticWinnerDialog, columns, float, 3) + ATTRIB(XonoticWinnerDialog, title, string, _("Winner")); + ATTRIB(XonoticWinnerDialog, color, vector, SKINCOLOR_DIALOG_SINGLEPLAYER); + ATTRIB(XonoticWinnerDialog, intendedWidth, float, 0.32); + ATTRIB(XonoticWinnerDialog, rows, float, 12); + ATTRIB(XonoticWinnerDialog, columns, float, 3); ENDCLASS(XonoticWinnerDialog) diff --git a/qcsrc/menu/xonotic/dialog_teamselect.qh b/qcsrc/menu/xonotic/dialog_teamselect.qh index b4056bb3b1..0bb0c306e3 100644 --- a/qcsrc/menu/xonotic/dialog_teamselect.qh +++ b/qcsrc/menu/xonotic/dialog_teamselect.qh @@ -4,15 +4,15 @@ CLASS(XonoticTeamSelectDialog, XonoticRootDialog) METHOD(XonoticTeamSelectDialog, fill, void(entity)); METHOD(XonoticTeamSelectDialog, showNotify, void(entity)); - ATTRIB(XonoticTeamSelectDialog, title, string, _("Team Selection")) - ATTRIB(XonoticTeamSelectDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT) - ATTRIB(XonoticTeamSelectDialog, intendedWidth, float, 0.4) - ATTRIB(XonoticTeamSelectDialog, rows, float, 5) - ATTRIB(XonoticTeamSelectDialog, columns, float, 4) - ATTRIB(XonoticTeamSelectDialog, name, string, "TeamSelect") - ATTRIB(XonoticTeamSelectDialog, team1, entity, NULL) - ATTRIB(XonoticTeamSelectDialog, team2, entity, NULL) - ATTRIB(XonoticTeamSelectDialog, team3, entity, NULL) - ATTRIB(XonoticTeamSelectDialog, team4, entity, NULL) - ATTRIB(XonoticTeamSelectDialog, requiresConnection, float, true) + ATTRIB(XonoticTeamSelectDialog, title, string, _("Team Selection")); + ATTRIB(XonoticTeamSelectDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); + ATTRIB(XonoticTeamSelectDialog, intendedWidth, float, 0.4); + ATTRIB(XonoticTeamSelectDialog, rows, float, 5); + ATTRIB(XonoticTeamSelectDialog, columns, float, 4); + ATTRIB(XonoticTeamSelectDialog, name, string, "TeamSelect"); + ATTRIB(XonoticTeamSelectDialog, team1, entity); + ATTRIB(XonoticTeamSelectDialog, team2, entity); + ATTRIB(XonoticTeamSelectDialog, team3, entity); + ATTRIB(XonoticTeamSelectDialog, team4, entity); + ATTRIB(XonoticTeamSelectDialog, requiresConnection, float, true); ENDCLASS(XonoticTeamSelectDialog) diff --git a/qcsrc/menu/xonotic/dialog_uid2name.qc b/qcsrc/menu/xonotic/dialog_uid2name.qc new file mode 100644 index 0000000000..1cd77b0cad --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_uid2name.qc @@ -0,0 +1,18 @@ +#include "dialog_uid2name.qh" + +#include "textlabel.qh" +#include "commandbutton.qh" + +void XonoticUid2NameDialog_fill(entity me) +{ + entity e; + me.TR(me); + me.TD(me, 1, 2, makeXonoticTextLabel(0.5, _("Allow player statistics to use your nickname?"))); + me.TR(me); + me.TD(me, 1, 2, makeXonoticTextLabel(0.5, _("Answering \"No\" you will appear as \"Anonymous player\""))); + me.TR(me); + me.TR(me); + me.TD(me, 1, 1, e = makeXonoticCommandButton(_("Yes"), '0 0 0', "vyes; setreport cl_allow_uid2name 1", COMMANDBUTTON_CLOSE)); + e.preferredFocusPriority = 1; + me.TD(me, 1, 1, e = makeXonoticCommandButton(_("No"), '0 0 0', "vno; setreport cl_allow_uid2name 0", COMMANDBUTTON_CLOSE)); +} diff --git a/qcsrc/menu/xonotic/dialog_uid2name.qh b/qcsrc/menu/xonotic/dialog_uid2name.qh new file mode 100644 index 0000000000..187d08068e --- /dev/null +++ b/qcsrc/menu/xonotic/dialog_uid2name.qh @@ -0,0 +1,13 @@ +#pragma once + +#include "rootdialog.qh" +CLASS(XonoticUid2NameDialog, XonoticRootDialog) + METHOD(XonoticUid2NameDialog, fill, void(entity)); + ATTRIB(XonoticUid2NameDialog, title, string, ""); + ATTRIB(XonoticUid2NameDialog, color, vector, SKINCOLOR_DIALOG_TEAMSELECT); + ATTRIB(XonoticUid2NameDialog, intendedWidth, float, 0.5); + ATTRIB(XonoticUid2NameDialog, rows, float, 4); + ATTRIB(XonoticUid2NameDialog, columns, float, 2); + ATTRIB(XonoticUid2NameDialog, name, string, "Uid2Name"); + ATTRIB(XonoticUid2NameDialog, closable, float, 0); +ENDCLASS(XonoticUid2NameDialog) diff --git a/qcsrc/menu/xonotic/gametypelist.qc b/qcsrc/menu/xonotic/gametypelist.qc index 9ee031dc78..a08e4dbbc5 100644 --- a/qcsrc/menu/xonotic/gametypelist.qc +++ b/qcsrc/menu/xonotic/gametypelist.qc @@ -17,7 +17,7 @@ void XonoticGametypeList_configureXonoticGametypeList(entity me) if(SKINBOOL_GAMETYPELIST_ICON_BLUR) { - for(int i = 0; i < GameType_GetCount(); ++i) + for(int i = 0; i < GameType_GetTotalCount(); ++i) draw_PreloadPictureWithFlags(GameType_GetIcon(i), PRECACHE_PIC_MIPMAP); } @@ -30,8 +30,7 @@ void XonoticGametypeList_setSelected(entity me, float i) } void XonoticGametypeList_loadCvars(entity me) { - float t; - t = MapInfo_CurrentGametype(); + Gametype t = MapInfo_CurrentGametype(); float i; for(i = 0; i < GameType_GetCount(); ++i) if(t == GameType_GetID(i)) @@ -49,7 +48,7 @@ void XonoticGametypeList_loadCvars(entity me) } void XonoticGametypeList_saveCvars(entity me) { - int t = GameType_GetID(me.selectedItem); + Gametype t = GameType_GetID(me.selectedItem); if (t == MapInfo_CurrentGametype()) { return; } @@ -59,6 +58,15 @@ void XonoticGametypeList_saveCvars(entity me) owner.gameTypeChangeNotify(owner); } } +void XonoticGametypeList_draw(entity me) +{ + if(me.nItems != GameType_GetCount()) + { + me.nItems = GameType_GetCount(); + me.setSelected(me, 0); + } + SUPER(XonoticGametypeList).draw(me); +} void XonoticGametypeList_drawListBoxItem(entity me, int i, vector absSize, bool isSelected, bool isFocused) { string s1, s2; diff --git a/qcsrc/menu/xonotic/gametypelist.qh b/qcsrc/menu/xonotic/gametypelist.qh index 95eceaa09b..bbc50aba43 100644 --- a/qcsrc/menu/xonotic/gametypelist.qh +++ b/qcsrc/menu/xonotic/gametypelist.qh @@ -3,7 +3,8 @@ #include "listbox.qh" CLASS(XonoticGametypeList, XonoticListBox) METHOD(XonoticGametypeList, configureXonoticGametypeList, void(entity)); - ATTRIB(XonoticGametypeList, rowsPerItem, float, 2) + ATTRIB(XonoticGametypeList, rowsPerItem, float, 2); + METHOD(XonoticGametypeList, draw, void(entity)); METHOD(XonoticGametypeList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticGametypeList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticGametypeList, setSelected, void(entity, float)); @@ -13,11 +14,11 @@ CLASS(XonoticGametypeList, XonoticListBox) METHOD(XonoticGametypeList, clickListBoxItem, void(entity, float, vector)); METHOD(XonoticGametypeList, focusedItemChangeNotify, void(entity)); - ATTRIB(XonoticGametypeList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticGametypeList, realUpperMargin, float, 0) - ATTRIB(XonoticGametypeList, columnIconOrigin, float, 0) - ATTRIB(XonoticGametypeList, columnIconSize, float, 0) - ATTRIB(XonoticGametypeList, columnNameOrigin, float, 0) - ATTRIB(XonoticGametypeList, columnNameSize, float, 0) + ATTRIB(XonoticGametypeList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticGametypeList, realUpperMargin, float, 0); + ATTRIB(XonoticGametypeList, columnIconOrigin, float, 0); + ATTRIB(XonoticGametypeList, columnIconSize, float, 0); + ATTRIB(XonoticGametypeList, columnNameOrigin, float, 0); + ATTRIB(XonoticGametypeList, columnNameSize, float, 0); ENDCLASS(XonoticGametypeList) entity makeXonoticGametypeList(); diff --git a/qcsrc/menu/xonotic/hudskinlist.qh b/qcsrc/menu/xonotic/hudskinlist.qh index f5808698cc..557f2e165c 100644 --- a/qcsrc/menu/xonotic/hudskinlist.qh +++ b/qcsrc/menu/xonotic/hudskinlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticHUDSkinList, XonoticListBox) METHOD(XonoticHUDSkinList, configureXonoticHUDSkinList, void(entity)); - ATTRIB(XonoticHUDSkinList, rowsPerItem, float, 1) + ATTRIB(XonoticHUDSkinList, rowsPerItem, float, 1); METHOD(XonoticHUDSkinList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticHUDSkinList, draw, void(entity)); METHOD(XonoticHUDSkinList, drawListBoxItem, void(entity, int, vector, bool, bool)); @@ -18,17 +18,17 @@ CLASS(XonoticHUDSkinList, XonoticListBox) METHOD(XonoticHUDSkinList, destroy, void(entity)); METHOD(XonoticHUDSkinList, showNotify, void(entity)); - ATTRIB(XonoticHUDSkinList, listHUDSkin, float, -1) - ATTRIB(XonoticHUDSkinList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticHUDSkinList, columnNameOrigin, float, 0) - ATTRIB(XonoticHUDSkinList, columnNameSize, float, 0) - ATTRIB(XonoticHUDSkinList, realUpperMargin, float, 0) - ATTRIB(XonoticHUDSkinList, origin, vector, '0 0 0') - ATTRIB(XonoticHUDSkinList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticHUDSkinList, listHUDSkin, float, -1); + ATTRIB(XonoticHUDSkinList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticHUDSkinList, columnNameOrigin, float, 0); + ATTRIB(XonoticHUDSkinList, columnNameSize, float, 0); + ATTRIB(XonoticHUDSkinList, realUpperMargin, float, 0); + ATTRIB(XonoticHUDSkinList, origin, vector, '0 0 0'); + ATTRIB(XonoticHUDSkinList, itemAbsSize, vector, '0 0 0'); - ATTRIB(XonoticHUDSkinList, filterString, string, string_null) - ATTRIB(XonoticHUDSkinList, delayedRefreshTime, float, 0) - ATTRIB(XonoticHUDSkinList, savedName, string, string_null) + ATTRIB(XonoticHUDSkinList, filterString, string); + ATTRIB(XonoticHUDSkinList, delayedRefreshTime, float, 0); + ATTRIB(XonoticHUDSkinList, savedName, string); ENDCLASS(XonoticHUDSkinList) entity hudskinlist; entity makeXonoticHUDSkinList(); diff --git a/qcsrc/menu/xonotic/inputbox.qh b/qcsrc/menu/xonotic/inputbox.qh index c09817943b..7be855cd9d 100644 --- a/qcsrc/menu/xonotic/inputbox.qh +++ b/qcsrc/menu/xonotic/inputbox.qh @@ -6,32 +6,32 @@ CLASS(XonoticInputBox, InputBox) METHOD(XonoticInputBox, focusLeave, void(entity)); METHOD(XonoticInputBox, setText, void(entity, string)); METHOD(XonoticInputBox, keyDown, float(entity, float, float, float)); - ATTRIB(XonoticInputBox, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticInputBox, image, string, SKINGFX_INPUTBOX) - ATTRIB(XonoticInputBox, onChange, void(entity, entity), func_null) - ATTRIB(XonoticInputBox, onChangeEntity, entity, NULL) - ATTRIB(XonoticInputBox, onEnter, void(entity, entity), func_null) - ATTRIB(XonoticInputBox, onEnterEntity, entity, NULL) - ATTRIB(XonoticInputBox, marginLeft, float, SKINMARGIN_INPUTBOX_CHARS) - ATTRIB(XonoticInputBox, marginRight, float, SKINMARGIN_INPUTBOX_CHARS) - ATTRIB(XonoticInputBox, color, vector, SKINCOLOR_INPUTBOX_N) - ATTRIB(XonoticInputBox, colorF, vector, SKINCOLOR_INPUTBOX_F) + ATTRIB(XonoticInputBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticInputBox, image, string, SKINGFX_INPUTBOX); + ATTRIB(XonoticInputBox, onChange, void(entity, entity)); + ATTRIB(XonoticInputBox, onChangeEntity, entity); + ATTRIB(XonoticInputBox, onEnter, void(entity, entity)); + ATTRIB(XonoticInputBox, onEnterEntity, entity); + ATTRIB(XonoticInputBox, marginLeft, float, SKINMARGIN_INPUTBOX_CHARS); + ATTRIB(XonoticInputBox, marginRight, float, SKINMARGIN_INPUTBOX_CHARS); + ATTRIB(XonoticInputBox, color, vector, SKINCOLOR_INPUTBOX_N); + ATTRIB(XonoticInputBox, colorF, vector, SKINCOLOR_INPUTBOX_F); - ATTRIB(XonoticInputBox, alpha, float, SKINALPHA_TEXT) + ATTRIB(XonoticInputBox, alpha, float, SKINALPHA_TEXT); // Clear button attributes - ATTRIB(XonoticInputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON) // bound to range -1, 0 - ATTRIB(XonoticInputBox, cb_src, string, SKINGFX_CLEARBUTTON) - ATTRIB(XonoticInputBox, cb_color, vector, SKINCOLOR_CLEARBUTTON_N) - ATTRIB(XonoticInputBox, cb_colorF, vector, SKINCOLOR_CLEARBUTTON_F) - ATTRIB(XonoticInputBox, cb_colorC, vector, SKINCOLOR_CLEARBUTTON_C) + ATTRIB(XonoticInputBox, cb_offset, float, SKINOFFSET_CLEARBUTTON); // bound to range -1, 0 + ATTRIB(XonoticInputBox, cb_src, string, SKINGFX_CLEARBUTTON); + ATTRIB(XonoticInputBox, cb_color, vector, SKINCOLOR_CLEARBUTTON_N); + ATTRIB(XonoticInputBox, cb_colorF, vector, SKINCOLOR_CLEARBUTTON_F); + ATTRIB(XonoticInputBox, cb_colorC, vector, SKINCOLOR_CLEARBUTTON_C); - ATTRIB(XonoticInputBox, cvarName, string, string_null) + ATTRIB(XonoticInputBox, cvarName, string); METHOD(XonoticInputBox, loadCvars, void(entity)); METHOD(XonoticInputBox, saveCvars, void(entity)); - ATTRIB(XonoticInputBox, sendCvars, float, 0) + ATTRIB(XonoticInputBox, sendCvars, float, 0); - ATTRIB(XonoticInputBox, saveImmediately, float, 0) + ATTRIB(XonoticInputBox, saveImmediately, float, 0); ENDCLASS(XonoticInputBox) entity makeXonoticInputBox_T(float, string, string theTooltip); entity makeXonoticInputBox(float, string); diff --git a/qcsrc/menu/xonotic/keybinder.qc b/qcsrc/menu/xonotic/keybinder.qc index ca2db83573..4546a62b10 100644 --- a/qcsrc/menu/xonotic/keybinder.qc +++ b/qcsrc/menu/xonotic/keybinder.qc @@ -1,6 +1,6 @@ #include "keybinder.qh" -#include +#include .int flags; #include "button.qh" @@ -40,12 +40,13 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("+fire" , _("primary fire")); KEYBIND_DEF("+fire2" , _("secondary fire")); KEYBIND_DEF("" , ""); - KEYBIND_DEF("" , _("Weapon switching")); - KEYBIND_DEF("weapprev" , _("previous")); - KEYBIND_DEF("weapnext" , _("next")); - KEYBIND_DEF("weaplast" , _("previously used")); - KEYBIND_DEF("weapbest" , _("best")); + KEYBIND_DEF("" , _("Weapons")); + KEYBIND_DEF("weapprev" , CTX(_("WEAPON^previous"))); + KEYBIND_DEF("weapnext" , CTX(_("WEAPON^next"))); + KEYBIND_DEF("weaplast" , CTX(_("WEAPON^previously used"))); + KEYBIND_DEF("weapbest" , CTX(_("WEAPON^best"))); KEYBIND_DEF("reload" , _("reload")); + KEYBIND_DEF("dropweapon" , _("drop weapon / throw nade")); int i; @@ -59,9 +60,9 @@ void Xonotic_KeyBinds_Read() for(int imp = 1; imp <= 9; ++imp) { string w_list = ""; - ADD_TO_W_LIST(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN) && !(it.spawnflags & WEP_FLAG_SUPERWEAPON)); - ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_SUPERWEAPON) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN)); - ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_TYPE_OTHER) && !(it.spawnflags & WEP_FLAG_HIDDEN)); + ADD_TO_W_LIST(!(it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_FLAG_HIDDEN) && !(it.spawnflags & WEP_FLAG_SUPERWEAPON)); + ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_SUPERWEAPON) && !(it.spawnflags & WEP_FLAG_HIDDEN)); + ADD_TO_W_LIST((it.spawnflags & WEP_FLAG_MUTATORBLOCKED) && !(it.spawnflags & WEP_FLAG_HIDDEN)); if(w_list) KEYBIND_DEF(strcat("weapon_group_", itos(imp)), substring(w_list, 0, -4)); if(imp == 0) @@ -78,6 +79,8 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("+showscores" , _("show scores")); KEYBIND_DEF("screenshot" , _("screen shot")); KEYBIND_DEF("+hud_panel_radar_maximized" , _("maximize radar")); + KEYBIND_DEF("toggle chase_active" , _("3rd person view")); + KEYBIND_DEF("spec" , _("enter spectator mode")); KEYBIND_DEF("" , ""); KEYBIND_DEF("" , _("Communicate")); KEYBIND_DEF("messagemode" , _("public chat")); @@ -97,12 +100,12 @@ void Xonotic_KeyBinds_Read() KEYBIND_DEF("messagemode2" , _("team chat")); KEYBIND_DEF("team_auto" , _("auto-join team")); KEYBIND_DEF("menu_showteamselect" , _("team menu")); - KEYBIND_DEF("menu_showsandboxtools" , _("sandbox menu")); - KEYBIND_DEF("spec" , _("enter spectator mode")); - KEYBIND_DEF("dropweapon" , _("drop weapon")); KEYBIND_DEF("+use" , _("drop key / drop flag")); + KEYBIND_DEF("" , ""); + KEYBIND_DEF("" , _("Misc")); + KEYBIND_DEF("quickmenu" , _("quick menu")); + KEYBIND_DEF("menu_showsandboxtools" , _("sandbox menu")); KEYBIND_DEF("+button8" , _("drag object")); - KEYBIND_DEF("toggle chase_active" , _("3rd person view")); KEYBIND_DEF("" , ""); KEYBIND_DEF("" , _("User defined")); diff --git a/qcsrc/menu/xonotic/keybinder.qh b/qcsrc/menu/xonotic/keybinder.qh index 4d0482dd9d..764059f768 100644 --- a/qcsrc/menu/xonotic/keybinder.qh +++ b/qcsrc/menu/xonotic/keybinder.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticKeyBinder, XonoticListBox) METHOD(XonoticKeyBinder, configureXonoticKeyBinder, void(entity)); - ATTRIB(XonoticKeyBinder, rowsPerItem, int, 1) + ATTRIB(XonoticKeyBinder, rowsPerItem, int, 1); METHOD(XonoticKeyBinder, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticKeyBinder, doubleClickListBoxItem, void(entity, float, vector)); METHOD(XonoticKeyBinder, resizeNotify, void(entity, vector, vector, vector, vector)); @@ -13,20 +13,20 @@ CLASS(XonoticKeyBinder, XonoticListBox) METHOD(XonoticKeyBinder, keyGrabbed, void(entity, float, float)); METHOD(XonoticKeyBinder, destroy, void(entity)); - ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0') - ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0) - ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0) - ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0) - ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0) - ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0) + ATTRIB(XonoticKeyBinder, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticKeyBinder, realUpperMargin, float, 0); + ATTRIB(XonoticKeyBinder, columnFunctionOrigin, float, 0); + ATTRIB(XonoticKeyBinder, columnFunctionSize, float, 0); + ATTRIB(XonoticKeyBinder, columnKeysOrigin, float, 0); + ATTRIB(XonoticKeyBinder, columnKeysSize, float, 0); METHOD(XonoticKeyBinder, loadKeyBinds, void(entity)); - ATTRIB(XonoticKeyBinder, previouslySelected, int, -1) - ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0) - ATTRIB(XonoticKeyBinder, userbindEditButton, entity, NULL) - ATTRIB(XonoticKeyBinder, keyGrabButton, entity, NULL) - ATTRIB(XonoticKeyBinder, clearButton, entity, NULL) - ATTRIB(XonoticKeyBinder, userbindEditDialog, entity, NULL) + ATTRIB(XonoticKeyBinder, previouslySelected, int, -1); + ATTRIB(XonoticKeyBinder, inMouseHandler, float, 0); + ATTRIB(XonoticKeyBinder, userbindEditButton, entity); + ATTRIB(XonoticKeyBinder, keyGrabButton, entity); + ATTRIB(XonoticKeyBinder, clearButton, entity); + ATTRIB(XonoticKeyBinder, userbindEditDialog, entity); METHOD(XonoticKeyBinder, editUserbind, void(entity, string, string, string)); ENDCLASS(XonoticKeyBinder) entity makeXonoticKeyBinder(); diff --git a/qcsrc/menu/xonotic/languagelist.qh b/qcsrc/menu/xonotic/languagelist.qh index 8febd64678..daf249e450 100644 --- a/qcsrc/menu/xonotic/languagelist.qh +++ b/qcsrc/menu/xonotic/languagelist.qh @@ -3,31 +3,31 @@ #include "listbox.qh" CLASS(XonoticLanguageList, XonoticListBox) METHOD(XonoticLanguageList, configureXonoticLanguageList, void(entity)); - ATTRIB(XonoticLanguageList, rowsPerItem, float, 1) + ATTRIB(XonoticLanguageList, rowsPerItem, float, 1); METHOD(XonoticLanguageList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticLanguageList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticLanguageList, setSelected, void(entity, float)); METHOD(XonoticLanguageList, loadCvars, void(entity)); METHOD(XonoticLanguageList, saveCvars, void(entity)); - ATTRIB(XonoticLanguageList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticLanguageList, realUpperMargin, float, 0) - ATTRIB(XonoticLanguageList, columnNameOrigin, float, 0) - ATTRIB(XonoticLanguageList, columnNameSize, float, 0) - ATTRIB(XonoticLanguageList, columnPercentageOrigin, float, 0) - ATTRIB(XonoticLanguageList, columnPercentageSize, float, 0) + ATTRIB(XonoticLanguageList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticLanguageList, realUpperMargin, float, 0); + ATTRIB(XonoticLanguageList, columnNameOrigin, float, 0); + ATTRIB(XonoticLanguageList, columnNameSize, float, 0); + ATTRIB(XonoticLanguageList, columnPercentageOrigin, float, 0); + ATTRIB(XonoticLanguageList, columnPercentageSize, float, 0); METHOD(XonoticLanguageList, doubleClickListBoxItem, void(entity, float, vector)); METHOD(XonoticLanguageList, keyDown, float(entity, float, float, float)); // enter handling METHOD(XonoticLanguageList, destroy, void(entity)); - ATTRIB(XonoticLanguageList, languagelist, float, -1) + ATTRIB(XonoticLanguageList, languagelist, float, -1); METHOD(XonoticLanguageList, getLanguages, void(entity)); METHOD(XonoticLanguageList, setLanguage, void(entity)); METHOD(XonoticLanguageList, languageParameter, string(entity, float, float)); - ATTRIB(XonoticLanguageList, name, string, "languageselector") // change this to make it noninteractive (for first run dialog) + ATTRIB(XonoticLanguageList, name, string, "languageselector"); // change this to make it noninteractive (for first run dialog); ENDCLASS(XonoticLanguageList) entity makeXonoticLanguageList(); diff --git a/qcsrc/menu/xonotic/listbox.qh b/qcsrc/menu/xonotic/listbox.qh index 3e789e772d..14272cb19b 100644 --- a/qcsrc/menu/xonotic/listbox.qh +++ b/qcsrc/menu/xonotic/listbox.qh @@ -3,17 +3,17 @@ #include "../item/listbox.qh" CLASS(XonoticListBox, ListBox) METHOD(XonoticListBox, configureXonoticListBox, void(entity)); - ATTRIB(XonoticListBox, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticListBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR) - ATTRIB(XonoticListBox, src, string, SKINGFX_SCROLLBAR) - ATTRIB(XonoticListBox, tolerance, vector, SKINTOLERANCE_SLIDER) - ATTRIB(XonoticListBox, rowsPerItem, float, 1) + ATTRIB(XonoticListBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticListBox, scrollbarWidth, float, SKINWIDTH_SCROLLBAR); + ATTRIB(XonoticListBox, src, string, SKINGFX_SCROLLBAR); + ATTRIB(XonoticListBox, tolerance, vector, SKINTOLERANCE_SLIDER); + ATTRIB(XonoticListBox, rowsPerItem, float, 1); METHOD(XonoticListBox, resizeNotify, void(entity, vector, vector, vector, vector)); - ATTRIB(XonoticListBox, color, vector, SKINCOLOR_SCROLLBAR_N) - ATTRIB(XonoticListBox, colorF, vector, SKINCOLOR_SCROLLBAR_F) - ATTRIB(XonoticListBox, color2, vector, SKINCOLOR_SCROLLBAR_S) - ATTRIB(XonoticListBox, colorC, vector, SKINCOLOR_SCROLLBAR_C) - ATTRIB(XonoticListBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND) - ATTRIB(XonoticListBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND) + ATTRIB(XonoticListBox, color, vector, SKINCOLOR_SCROLLBAR_N); + ATTRIB(XonoticListBox, colorF, vector, SKINCOLOR_SCROLLBAR_F); + ATTRIB(XonoticListBox, color2, vector, SKINCOLOR_SCROLLBAR_S); + ATTRIB(XonoticListBox, colorC, vector, SKINCOLOR_SCROLLBAR_C); + ATTRIB(XonoticListBox, colorBG, vector, SKINCOLOR_LISTBOX_BACKGROUND); + ATTRIB(XonoticListBox, alphaBG, float, SKINALPHA_LISTBOX_BACKGROUND); ENDCLASS(XonoticListBox) entity makeXonoticListBox(); diff --git a/qcsrc/menu/xonotic/mainwindow.qc b/qcsrc/menu/xonotic/mainwindow.qc index fb25eebdef..0e071c2e71 100644 --- a/qcsrc/menu/xonotic/mainwindow.qc +++ b/qcsrc/menu/xonotic/mainwindow.qc @@ -39,6 +39,7 @@ #include "dialog_sandboxtools.qh" #include "dialog_monstertools.qh" #include "dialog_teamselect.qh" +#include "dialog_uid2name.qh" #include "dialog_singleplayer.qh" #include "dialog_multiplayer.qh" #include "dialog_settings.qh" @@ -224,6 +225,10 @@ void MainWindow_configureMainWindow(entity me) i.configureDialog(i); me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z); + i = NEW(XonoticUid2NameDialog); + i.configureDialog(i); + me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z); + i = NEW(XonoticMonsterToolsDialog); i.configureDialog(i); me.addItemCentered(me, i, i.intendedWidth * eX + i.intendedHeight * eY, SKINALPHAS_MAINMENU_z * SKINALPHA_DIALOG_SANDBOXTOOLS); diff --git a/qcsrc/menu/xonotic/mainwindow.qh b/qcsrc/menu/xonotic/mainwindow.qh index daf5e0571c..2397d41431 100644 --- a/qcsrc/menu/xonotic/mainwindow.qh +++ b/qcsrc/menu/xonotic/mainwindow.qh @@ -5,22 +5,22 @@ CLASS(MainWindow, ModalController) METHOD(MainWindow, configureMainWindow, void(entity)); METHOD(MainWindow, draw, void(entity)); - ATTRIB(MainWindow, firstRunDialog, entity, NULL) - ATTRIB(MainWindow, advancedDialog, entity, NULL) - ATTRIB(MainWindow, mutatorsDialog, entity, NULL) - ATTRIB(MainWindow, mapInfoDialog, entity, NULL) - ATTRIB(MainWindow, userbindEditDialog, entity, NULL) - ATTRIB(MainWindow, winnerDialog, entity, NULL) - ATTRIB(MainWindow, serverInfoDialog, entity, NULL) - ATTRIB(MainWindow, cvarsDialog, entity, NULL) - ATTRIB(MainWindow, screenshotViewerDialog, entity, NULL) - ATTRIB(MainWindow, viewDialog, entity, NULL) - ATTRIB(MainWindow, hudconfirmDialog, entity, NULL) - ATTRIB(MainWindow, languageWarningDialog, entity, NULL) - ATTRIB(MainWindow, mainNexposee, entity, NULL) - ATTRIB(MainWindow, fadedAlpha, float, SKINALPHA_BEHIND) - ATTRIB(MainWindow, dialogToShow, entity, NULL) - ATTRIB(MainWindow, demostartconfirmDialog, entity, NULL) - ATTRIB(MainWindow, demotimeconfirmDialog, entity, NULL) - ATTRIB(MainWindow, resetDialog, entity, NULL) + ATTRIB(MainWindow, firstRunDialog, entity); + ATTRIB(MainWindow, advancedDialog, entity); + ATTRIB(MainWindow, mutatorsDialog, entity); + ATTRIB(MainWindow, mapInfoDialog, entity); + ATTRIB(MainWindow, userbindEditDialog, entity); + ATTRIB(MainWindow, winnerDialog, entity); + ATTRIB(MainWindow, serverInfoDialog, entity); + ATTRIB(MainWindow, cvarsDialog, entity); + ATTRIB(MainWindow, screenshotViewerDialog, entity); + ATTRIB(MainWindow, viewDialog, entity); + ATTRIB(MainWindow, hudconfirmDialog, entity); + ATTRIB(MainWindow, languageWarningDialog, entity); + ATTRIB(MainWindow, mainNexposee, entity); + ATTRIB(MainWindow, fadedAlpha, float, SKINALPHA_BEHIND); + ATTRIB(MainWindow, dialogToShow, entity); + ATTRIB(MainWindow, demostartconfirmDialog, entity); + ATTRIB(MainWindow, demotimeconfirmDialog, entity); + ATTRIB(MainWindow, resetDialog, entity); ENDCLASS(MainWindow) diff --git a/qcsrc/menu/xonotic/maplist.qc b/qcsrc/menu/xonotic/maplist.qc index 4b41f5bd3b..8feee96ed7 100644 --- a/qcsrc/menu/xonotic/maplist.qc +++ b/qcsrc/menu/xonotic/maplist.qc @@ -172,9 +172,8 @@ void XonoticMapList_refilter(entity me) { float i, j, n; string s; - float gt, f; - gt = MapInfo_CurrentGametype(); - f = MapInfo_CurrentFeatures(); + Gametype gt = MapInfo_CurrentGametype(); + int f = MapInfo_CurrentFeatures(); MapInfo_FilterGametype(gt, f, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); if (me.stringFilter) MapInfo_FilterString(me.stringFilter); @@ -256,7 +255,7 @@ void MapList_Add_All(entity btn, entity me) { float i; string s; - MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all + _MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, MapInfo_ForbiddenFlags(), 0); // all s = ""; for(i = 0; i < MapInfo_count; ++i) s = strcat(s, " ", MapInfo_BSPName_ByID(i)); diff --git a/qcsrc/menu/xonotic/maplist.qh b/qcsrc/menu/xonotic/maplist.qh index de9d7e28a3..ae6f6185e5 100644 --- a/qcsrc/menu/xonotic/maplist.qh +++ b/qcsrc/menu/xonotic/maplist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticMapList, XonoticListBox) METHOD(XonoticMapList, configureXonoticMapList, void(entity)); - ATTRIB(XonoticMapList, rowsPerItem, float, 4) + ATTRIB(XonoticMapList, rowsPerItem, float, 4); METHOD(XonoticMapList, draw, void(entity)); METHOD(XonoticMapList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticMapList, clickListBoxItem, void(entity, float, vector)); @@ -13,39 +13,39 @@ CLASS(XonoticMapList, XonoticListBox) METHOD(XonoticMapList, refilterCallback, void(entity, entity)); METHOD(XonoticMapList, keyDown, float(entity, float, float, float)); - ATTRIB(XonoticMapList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticMapList, columnPreviewOrigin, float, 0) - ATTRIB(XonoticMapList, columnPreviewSize, float, 0) - ATTRIB(XonoticMapList, columnNameOrigin, float, 0) - ATTRIB(XonoticMapList, columnNameSize, float, 0) - ATTRIB(XonoticMapList, checkMarkOrigin, vector, '0 0 0') - ATTRIB(XonoticMapList, checkMarkSize, vector, '0 0 0') - ATTRIB(XonoticMapList, realUpperMargin1, float, 0) - ATTRIB(XonoticMapList, realUpperMargin2, float, 0) + ATTRIB(XonoticMapList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticMapList, columnPreviewOrigin, float, 0); + ATTRIB(XonoticMapList, columnPreviewSize, float, 0); + ATTRIB(XonoticMapList, columnNameOrigin, float, 0); + ATTRIB(XonoticMapList, columnNameSize, float, 0); + ATTRIB(XonoticMapList, checkMarkOrigin, vector, '0 0 0'); + ATTRIB(XonoticMapList, checkMarkSize, vector, '0 0 0'); + ATTRIB(XonoticMapList, realUpperMargin1, float, 0); + ATTRIB(XonoticMapList, realUpperMargin2, float, 0); - ATTRIB(XonoticMapList, lastGametype, float, 0) - ATTRIB(XonoticMapList, lastFeatures, float, 0) + ATTRIB(XonoticMapList, lastGametype, entity); + ATTRIB(XonoticMapList, lastFeatures, float, 0); - ATTRIB(XonoticMapList, origin, vector, '0 0 0') - ATTRIB(XonoticMapList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticMapList, origin, vector, '0 0 0'); + ATTRIB(XonoticMapList, itemAbsSize, vector, '0 0 0'); - ATTRIB(XonoticMapList, g_maplistCache, string, string_null) + ATTRIB(XonoticMapList, g_maplistCache, string); METHOD(XonoticMapList, g_maplistCacheToggle, void(entity, float)); METHOD(XonoticMapList, g_maplistCacheQuery, float(entity, float)); - ATTRIB(XonoticMapList, stringFilter, string, string_null) - ATTRIB(XonoticMapList, stringFilterBox, entity, NULL) + ATTRIB(XonoticMapList, stringFilter, string); + ATTRIB(XonoticMapList, stringFilterBox, entity); - ATTRIB(XonoticMapList, startButton, entity, NULL) + ATTRIB(XonoticMapList, startButton, entity); METHOD(XonoticMapList, loadCvars, void(entity)); - ATTRIB(XonoticMapList, typeToSearchString, string, string_null) - ATTRIB(XonoticMapList, typeToSearchTime, float, 0) + ATTRIB(XonoticMapList, typeToSearchString, string); + ATTRIB(XonoticMapList, typeToSearchTime, float, 0); METHOD(XonoticMapList, destroy, void(entity)); - ATTRIB(XonoticMapList, alphaBG, float, 0) + ATTRIB(XonoticMapList, alphaBG, float, 0); ENDCLASS(XonoticMapList) entity makeXonoticMapList(); void MapList_StringFilterBox_Change(entity box, entity me); diff --git a/qcsrc/menu/xonotic/picker.qh b/qcsrc/menu/xonotic/picker.qh index c530c7ca9c..b8e19cf19e 100644 --- a/qcsrc/menu/xonotic/picker.qh +++ b/qcsrc/menu/xonotic/picker.qh @@ -9,23 +9,23 @@ CLASS(XonoticPicker, Item) METHOD(XonoticPicker, mouseDrag, float(entity, vector)); METHOD(XonoticPicker, keyDown, float(entity, float, float, float)); METHOD(XonoticPicker, draw, void(entity)); - ATTRIB(XonoticPicker, focusable, float, 1) - ATTRIB(XonoticPicker, disabled, float, 0) - ATTRIB(XonoticPicker, alpha, float, 1) - ATTRIB(XonoticPicker, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticPicker, focusable, float, 1); + ATTRIB(XonoticPicker, disabled, float, 0); + ATTRIB(XonoticPicker, alpha, float, 1); + ATTRIB(XonoticPicker, disabledAlpha, float, SKINALPHA_DISABLED); - ATTRIB(XonoticPicker, rows, float, 3) - ATTRIB(XonoticPicker, columns, float, 2) + ATTRIB(XonoticPicker, rows, float, 3); + ATTRIB(XonoticPicker, columns, float, 2); METHOD(XonoticPicker, moveFocus, void(entity, vector, vector)); METHOD(XonoticPicker, cellSelect, void(entity, vector)); METHOD(XonoticPicker, cellDraw, void(entity, vector, vector)); METHOD(XonoticPicker, cellIsValid, bool(entity, vector)); - ATTRIB(XonoticPicker, realCellSize, vector, '0 0 0') - ATTRIB(XonoticPicker, selectedCell, vector, '-1 -1 0') - ATTRIB(XonoticPicker, focusedCell, vector, '-1 -1 0') - ATTRIB(XonoticPicker, focusedCellAlpha, float, 0) - ATTRIB(XonoticPicker, focusedCellTime, float, 0) - ATTRIB(XonoticPicker, pressedCell, vector, '-1 -1 0') + ATTRIB(XonoticPicker, realCellSize, vector, '0 0 0'); + ATTRIB(XonoticPicker, selectedCell, vector, '-1 -1 0'); + ATTRIB(XonoticPicker, focusedCell, vector, '-1 -1 0'); + ATTRIB(XonoticPicker, focusedCellAlpha, float, 0); + ATTRIB(XonoticPicker, focusedCellTime, float, 0); + ATTRIB(XonoticPicker, pressedCell, vector, '-1 -1 0'); ENDCLASS(XonoticPicker) entity makeXonoticPicker(); diff --git a/qcsrc/menu/xonotic/playerlist.qh b/qcsrc/menu/xonotic/playerlist.qh index 8474b9140d..a35ed966d9 100644 --- a/qcsrc/menu/xonotic/playerlist.qh +++ b/qcsrc/menu/xonotic/playerlist.qh @@ -2,21 +2,21 @@ #include "listbox.qh" CLASS(XonoticPlayerList, XonoticListBox) - ATTRIB(XonoticPlayerList, rowsPerItem, float, 1) + ATTRIB(XonoticPlayerList, rowsPerItem, float, 1); METHOD(XonoticPlayerList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticPlayerList, drawListBoxItem, void(entity, int, vector, bool, bool)); - ATTRIB(XonoticPlayerList, allowFocusSound, float, 0) - ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0) - ATTRIB(XonoticPlayerList, columnNameSize, float, 0) - ATTRIB(XonoticPlayerList, columnScoreOrigin, float, 0) - ATTRIB(XonoticPlayerList, columnScoreSize, float, 0) - ATTRIB(XonoticPlayerList, realUpperMargin, float, 0) - ATTRIB(XonoticPlayerList, origin, vector, '0 0 0') - ATTRIB(XonoticPlayerList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticPlayerList, allowFocusSound, float, 0); + ATTRIB(XonoticPlayerList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticPlayerList, columnNameOrigin, float, 0); + ATTRIB(XonoticPlayerList, columnNameSize, float, 0); + ATTRIB(XonoticPlayerList, columnScoreOrigin, float, 0); + ATTRIB(XonoticPlayerList, columnScoreSize, float, 0); + ATTRIB(XonoticPlayerList, realUpperMargin, float, 0); + ATTRIB(XonoticPlayerList, origin, vector, '0 0 0'); + ATTRIB(XonoticPlayerList, itemAbsSize, vector, '0 0 0'); METHOD(XonoticPlayerList, setPlayerList, void(entity, string)); METHOD(XonoticPlayerList, getPlayerList, string(entity, float, float)); - ATTRIB(XonoticPlayerList, playerList, float, -1) - ATTRIB(XonoticPlayerList, selectionDoesntMatter, bool, true) + ATTRIB(XonoticPlayerList, playerList, float, -1); + ATTRIB(XonoticPlayerList, selectionDoesntMatter, bool, true); ENDCLASS(XonoticPlayerList) entity makeXonoticPlayerList(); diff --git a/qcsrc/menu/xonotic/playermodel.qc b/qcsrc/menu/xonotic/playermodel.qc index 1a90fa7378..c679d4449d 100644 --- a/qcsrc/menu/xonotic/playermodel.qc +++ b/qcsrc/menu/xonotic/playermodel.qc @@ -38,6 +38,8 @@ void XonoticPlayerModelSelector_loadModels(entity me) fn = search_getfilename(glob, i); if(!get_model_parameters(fn, -1)) continue; + if(get_model_parameters_hidden) + continue; bufstr_add(sortbuf, sprintf("%-128s%s", get_model_parameters_name, fn), 1); } search_end(glob); @@ -49,6 +51,8 @@ void XonoticPlayerModelSelector_loadModels(entity me) fn = substring(bufstr_get(sortbuf, i), 128, -1); if(!get_model_parameters(fn, -1)) error("But it JUST worked!"); + if(get_model_parameters_hidden) + continue; bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_TITLE, get_model_parameters_name); bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_IMAGE, strcat("/", substring(get_model_datafilename(get_model_parameters_modelname, get_model_parameters_modelskin, "tga"), 0, -5))); bufstr_set(me.bufModels, BUFMODELS_COUNT*i+BUFMODELS_MODEL, get_model_parameters_modelname); diff --git a/qcsrc/menu/xonotic/playermodel.qh b/qcsrc/menu/xonotic/playermodel.qh index 664f22c7c0..4635c6ccdb 100644 --- a/qcsrc/menu/xonotic/playermodel.qh +++ b/qcsrc/menu/xonotic/playermodel.qh @@ -9,21 +9,21 @@ CLASS(XonoticPlayerModelSelector, XonoticImage) METHOD(XonoticPlayerModelSelector, draw, void(entity)); METHOD(XonoticPlayerModelSelector, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticPlayerModelSelector, showNotify, void(entity)); - ATTRIB(XonoticPlayerModelSelector, currentModel, string, string_null) - ATTRIB(XonoticPlayerModelSelector, currentSkin, float, 0) - ATTRIB(XonoticPlayerModelSelector, currentModelImage, string, string_null) - ATTRIB(XonoticPlayerModelSelector, currentModelTitle, string, string_null) - ATTRIB(XonoticPlayerModelSelector, currentModelDescription, string, string_null) + ATTRIB(XonoticPlayerModelSelector, currentModel, string); + ATTRIB(XonoticPlayerModelSelector, currentSkin, float, 0); + ATTRIB(XonoticPlayerModelSelector, currentModelImage, string); + ATTRIB(XonoticPlayerModelSelector, currentModelTitle, string); + ATTRIB(XonoticPlayerModelSelector, currentModelDescription, string); METHOD(XonoticPlayerModelSelector, go, void(entity, float)); METHOD(XonoticPlayerModelSelector, destroy, void(entity)); - ATTRIB(XonoticPlayerModelSelector, origin, vector, '0 0 0') - ATTRIB(XonoticPlayerModelSelector, size, vector, '0 0 0') - ATTRIB(XonoticPlayerModelSelector, realFontSize, vector, '0 0 0') - ATTRIB(XonoticPlayerModelSelector, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticPlayerModelSelector, titleFontSize, float, SKINFONTSIZE_TITLE) - ATTRIB(XonoticPlayerModelSelector, bufModels, float, -1) - ATTRIB(XonoticPlayerModelSelector, numModels, float, -1) - ATTRIB(XonoticPlayerModelSelector, idxModels, float, -1) + ATTRIB(XonoticPlayerModelSelector, origin, vector, '0 0 0'); + ATTRIB(XonoticPlayerModelSelector, size, vector, '0 0 0'); + ATTRIB(XonoticPlayerModelSelector, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticPlayerModelSelector, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticPlayerModelSelector, titleFontSize, float, SKINFONTSIZE_TITLE); + ATTRIB(XonoticPlayerModelSelector, bufModels, float, -1); + ATTRIB(XonoticPlayerModelSelector, numModels, float, -1); + ATTRIB(XonoticPlayerModelSelector, idxModels, float, -1); ENDCLASS(XonoticPlayerModelSelector) entity makeXonoticPlayerModelSelector(); void PlayerModelSelector_Next_Click(entity btn, entity me); diff --git a/qcsrc/menu/xonotic/playlist.qh b/qcsrc/menu/xonotic/playlist.qh index 6bee05bbc8..bd9f71988f 100644 --- a/qcsrc/menu/xonotic/playlist.qh +++ b/qcsrc/menu/xonotic/playlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticPlayList, XonoticListBox) METHOD(XonoticPlayList, configureXonoticPlayList, void(entity)); - ATTRIB(XonoticPlayList, rowsPerItem, float, 1) + ATTRIB(XonoticPlayList, rowsPerItem, float, 1); METHOD(XonoticPlayList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticPlayList, draw, void(entity)); METHOD(XonoticPlayList, drawListBoxItem, void(entity, int, vector, bool, bool)); @@ -17,16 +17,16 @@ CLASS(XonoticPlayList, XonoticListBox) METHOD(XonoticPlayList, addToPlayList, void(entity, string)); METHOD(XonoticPlayList, removeSelectedFromPlayList, void(entity)); - ATTRIB(XonoticPlayList, playingTrack, float, -1) + ATTRIB(XonoticPlayList, playingTrack, float, -1); - ATTRIB(XonoticPlayList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticPlayList, columnNameOrigin, float, 0) - ATTRIB(XonoticPlayList, columnNameSize, float, 0) - ATTRIB(XonoticPlayList, columnNumberOrigin, float, 0) - ATTRIB(XonoticPlayList, columnNumberSize, float, 0) - ATTRIB(XonoticPlayList, realUpperMargin, float, 0) - ATTRIB(XonoticPlayList, origin, vector, '0 0 0') - ATTRIB(XonoticPlayList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticPlayList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticPlayList, columnNameOrigin, float, 0); + ATTRIB(XonoticPlayList, columnNameSize, float, 0); + ATTRIB(XonoticPlayList, columnNumberOrigin, float, 0); + ATTRIB(XonoticPlayList, columnNumberSize, float, 0); + ATTRIB(XonoticPlayList, realUpperMargin, float, 0); + ATTRIB(XonoticPlayList, origin, vector, '0 0 0'); + ATTRIB(XonoticPlayList, itemAbsSize, vector, '0 0 0'); ENDCLASS(XonoticPlayList) entity makeXonoticPlayList(); diff --git a/qcsrc/menu/xonotic/radiobutton.qh b/qcsrc/menu/xonotic/radiobutton.qh index 36c228fcf4..cd671a338f 100644 --- a/qcsrc/menu/xonotic/radiobutton.qh +++ b/qcsrc/menu/xonotic/radiobutton.qh @@ -5,22 +5,22 @@ CLASS(XonoticRadioButton, RadioButton) METHOD(XonoticRadioButton, configureXonoticRadioButton, void(entity, float, string, string, string, string)); METHOD(XonoticRadioButton, draw, void(entity)); METHOD(XonoticRadioButton, setChecked, void(entity, float)); - ATTRIB(XonoticRadioButton, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticRadioButton, image, string, SKINGFX_RADIOBUTTON) - ATTRIB(XonoticRadioButton, color, vector, SKINCOLOR_RADIOBUTTON_N) - ATTRIB(XonoticRadioButton, colorC, vector, SKINCOLOR_RADIOBUTTON_C) - ATTRIB(XonoticRadioButton, colorF, vector, SKINCOLOR_RADIOBUTTON_F) - ATTRIB(XonoticRadioButton, colorD, vector, SKINCOLOR_RADIOBUTTON_D) + ATTRIB(XonoticRadioButton, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticRadioButton, image, string, SKINGFX_RADIOBUTTON); + ATTRIB(XonoticRadioButton, color, vector, SKINCOLOR_RADIOBUTTON_N); + ATTRIB(XonoticRadioButton, colorC, vector, SKINCOLOR_RADIOBUTTON_C); + ATTRIB(XonoticRadioButton, colorF, vector, SKINCOLOR_RADIOBUTTON_F); + ATTRIB(XonoticRadioButton, colorD, vector, SKINCOLOR_RADIOBUTTON_D); - ATTRIB(XonoticRadioButton, cvarName, string, string_null) - ATTRIB(XonoticRadioButton, cvarValue, string, string_null) - ATTRIB(XonoticRadioButton, cvarOffValue, string, string_null) - ATTRIB(XonoticRadioButton, cvarValueIsAnotherCvar, float, 0) + ATTRIB(XonoticRadioButton, cvarName, string); + ATTRIB(XonoticRadioButton, cvarValue, string); + ATTRIB(XonoticRadioButton, cvarOffValue, string); + ATTRIB(XonoticRadioButton, cvarValueIsAnotherCvar, float, 0); METHOD(XonoticRadioButton, loadCvars, void(entity)); METHOD(XonoticRadioButton, saveCvars, void(entity)); - ATTRIB(XonoticRadioButton, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticRadioButton, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticRadioButton, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticRadioButton, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticRadioButton) entity makeXonoticRadioButton_T(float, string, string, string, string theTooltip); entity makeXonoticRadioButton(float, string, string, string); diff --git a/qcsrc/menu/xonotic/rootdialog.qh b/qcsrc/menu/xonotic/rootdialog.qh index 0bfd3364cb..a2efa672e9 100644 --- a/qcsrc/menu/xonotic/rootdialog.qh +++ b/qcsrc/menu/xonotic/rootdialog.qh @@ -4,12 +4,12 @@ CLASS(XonoticRootDialog, XonoticDialog) // still to be customized by user /* - ATTRIB(XonoticDialog, closable, float, 1) - ATTRIB(XonoticDialog, title, string, _("Form1")) // ;) - ATTRIB(XonoticDialog, color, vector, '1 0.5 1') - ATTRIB(XonoticDialog, intendedWidth, float, 0) - ATTRIB(XonoticDialog, rows, float, 3) - ATTRIB(XonoticDialog, columns, float, 2) + ATTRIB(XonoticDialog, closable, float, 1); + ATTRIB(XonoticDialog, title, string, _("Form1")); // ;); + ATTRIB(XonoticDialog, color, vector, '1 0.5 1'); + ATTRIB(XonoticDialog, intendedWidth, float, 0); + ATTRIB(XonoticDialog, rows, float, 3); + ATTRIB(XonoticDialog, columns, float, 2); */ METHOD(XonoticRootDialog, close, void(entity)); ENDCLASS(XonoticRootDialog) diff --git a/qcsrc/menu/xonotic/screenshotimage.qc b/qcsrc/menu/xonotic/screenshotimage.qc index 82ff4ba7b2..42e168b4b5 100644 --- a/qcsrc/menu/xonotic/screenshotimage.qc +++ b/qcsrc/menu/xonotic/screenshotimage.qc @@ -56,12 +56,11 @@ void XonoticScreenshotImage_draw(entity me) if (time < me.zoomTime + 2) // 1 seconds at full alpha, 1 second fading out { string zoomString; - float z; - z = me.zoomFactor * 100; - if (z - floor(z) == 0) - zoomString = sprintf("%d%%", z); + float myzoom = me.zoomFactor * 100; + if (myzoom - floor(myzoom) == 0) + zoomString = sprintf("%d%%", myzoom); else - zoomString = sprintf("%.2f%%", z); + zoomString = sprintf("%.2f%%", myzoom); theAlpha = (2 - (time - me.zoomTime)); draw_Text('0.05 0.95 0', zoomString, me.realFontSize, '1 1 1', theAlpha, false); } diff --git a/qcsrc/menu/xonotic/screenshotimage.qh b/qcsrc/menu/xonotic/screenshotimage.qh index eb20c73a24..8920bed287 100644 --- a/qcsrc/menu/xonotic/screenshotimage.qh +++ b/qcsrc/menu/xonotic/screenshotimage.qh @@ -5,15 +5,15 @@ CLASS(XonoticScreenshotImage, XonoticImage) METHOD(XonoticScreenshotImage, configureXonoticScreenshotImage, void(entity)); METHOD(XonoticScreenshotImage, load, void(entity, string)); METHOD(XonoticScreenshotImage, draw, void(entity)); - ATTRIB(XonoticScreenshotImage, focusable, float, 1) // mousePress and mouseDrag work only if focusable is set + ATTRIB(XonoticScreenshotImage, focusable, float, 1); // mousePress and mouseDrag work only if focusable is set METHOD(XonoticScreenshotImage, mousePress, float(entity, vector)); METHOD(XonoticScreenshotImage, mouseDrag, float(entity, vector)); METHOD(XonoticScreenshotImage, mouseMove, float(entity, vector)); METHOD(XonoticScreenshotImage, resizeNotify, void(entity, vector, vector, vector, vector)); - ATTRIB(XonoticScreenshotImage, realFontSize, vector, '0 0 0') - ATTRIB(XonoticScreenshotImage, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticScreenshotImage, showTitle, float, 1) - ATTRIB(XonoticScreenshotImage, screenshotTime, float, 0) - ATTRIB(XonoticScreenshotImage, screenshotTitle, string, string_null) + ATTRIB(XonoticScreenshotImage, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticScreenshotImage, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticScreenshotImage, showTitle, float, 1); + ATTRIB(XonoticScreenshotImage, screenshotTime, float, 0); + ATTRIB(XonoticScreenshotImage, screenshotTitle, string); ENDCLASS(XonoticScreenshotImage) entity makeXonoticScreenshotImage(); diff --git a/qcsrc/menu/xonotic/screenshotlist.qh b/qcsrc/menu/xonotic/screenshotlist.qh index 1279c4ad9c..63071fc740 100644 --- a/qcsrc/menu/xonotic/screenshotlist.qh +++ b/qcsrc/menu/xonotic/screenshotlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticScreenshotList, XonoticListBox) METHOD(XonoticScreenshotList, configureXonoticScreenshotList, void(entity)); - ATTRIB(XonoticScreenshotList, rowsPerItem, float, 1) + ATTRIB(XonoticScreenshotList, rowsPerItem, float, 1); METHOD(XonoticScreenshotList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticScreenshotList, setSelected, void(entity, float)); METHOD(XonoticScreenshotList, draw, void(entity)); @@ -16,23 +16,23 @@ CLASS(XonoticScreenshotList, XonoticListBox) METHOD(XonoticScreenshotList, keyDown, float(entity, float, float, float)); METHOD(XonoticScreenshotList, destroy, void(entity)); METHOD(XonoticScreenshotList, showNotify, void(entity)); - ATTRIB(XonoticScreenshotList, listScreenshot, float, -1) - ATTRIB(XonoticScreenshotList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticScreenshotList, columnNameOrigin, float, 0) - ATTRIB(XonoticScreenshotList, columnNameSize, float, 0) - ATTRIB(XonoticScreenshotList, realUpperMargin, float, 0) - ATTRIB(XonoticScreenshotList, origin, vector, '0 0 0') - ATTRIB(XonoticScreenshotList, itemAbsSize, vector, '0 0 0') - ATTRIB(XonoticScreenshotList, filterString, string, string_null) - ATTRIB(XonoticScreenshotList, filterBox, entity, NULL) - ATTRIB(XonoticScreenshotList, filterTime, float, 0) + ATTRIB(XonoticScreenshotList, listScreenshot, float, -1); + ATTRIB(XonoticScreenshotList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticScreenshotList, columnNameOrigin, float, 0); + ATTRIB(XonoticScreenshotList, columnNameSize, float, 0); + ATTRIB(XonoticScreenshotList, realUpperMargin, float, 0); + ATTRIB(XonoticScreenshotList, origin, vector, '0 0 0'); + ATTRIB(XonoticScreenshotList, itemAbsSize, vector, '0 0 0'); + ATTRIB(XonoticScreenshotList, filterString, string); + ATTRIB(XonoticScreenshotList, filterBox, entity); + ATTRIB(XonoticScreenshotList, filterTime, float, 0); - ATTRIB(XonoticScreenshotList, newScreenshotTime, float, 0) - ATTRIB(XonoticScreenshotList, newSlideShowScreenshotTime, float, 0) + ATTRIB(XonoticScreenshotList, newScreenshotTime, float, 0); + ATTRIB(XonoticScreenshotList, newSlideShowScreenshotTime, float, 0); - ATTRIB(XonoticScreenshotList, screenshotBrowserDialog, entity, NULL) - ATTRIB(XonoticScreenshotList, screenshotPreview, entity, NULL) - ATTRIB(XonoticScreenshotList, screenshotViewerDialog, entity, NULL) + ATTRIB(XonoticScreenshotList, screenshotBrowserDialog, entity); + ATTRIB(XonoticScreenshotList, screenshotPreview, entity); + ATTRIB(XonoticScreenshotList, screenshotViewerDialog, entity); METHOD(XonoticScreenshotList, goScreenshot, void(entity, float)); METHOD(XonoticScreenshotList, startSlideShow, void(entity)); METHOD(XonoticScreenshotList, stopSlideShow, void(entity)); diff --git a/qcsrc/menu/xonotic/serverlist.qc b/qcsrc/menu/xonotic/serverlist.qc index fc2ba573bb..bbb9dcac05 100644 --- a/qcsrc/menu/xonotic/serverlist.qc +++ b/qcsrc/menu/xonotic/serverlist.qc @@ -212,7 +212,7 @@ int CheckCategoryForEntry(int entry) case "cts": case "xdf": { return CAT_DEFRAG; } - default: { LOG_TRACEF("Found strange mod type: %s\n", modtype); return CAT_MODIFIED; } + default: { LOG_TRACEF("Found strange mod type: %s", modtype); return CAT_MODIFIED; } } } @@ -471,9 +471,8 @@ void XonoticServerList_draw(entity me) // entire list, otherwise there is no way to know which item is first in its category. // binary search method suggested by div - float x; float begin = 0; - for(x = 1; x <= category_ent_count; ++x) { + for(int j = 1; j <= category_ent_count; ++j) { float first = begin; float last = (itemcount - 1); if (first > last) { @@ -482,42 +481,42 @@ void XonoticServerList_draw(entity me) } float catf = gethostcachenumber(SLIST_FIELD_CATEGORY, first); float catl = gethostcachenumber(SLIST_FIELD_CATEGORY, last); - if (catf > x) { - // The first one is already > x. - // Therefore, category x does not exist. + if (catf > j) { + // The first one is already > j. + // Therefore, category j does not exist. // Higher numbered categories do exist though. - } else if (catl < x) { - // The last one is < x. + } else if (catl < j) { + // The last one is < j. // Thus this category - and any following - // don't exist. break; - } else if (catf == x) { + } else if (catf == j) { // Starts at first. This breaks the loop // invariant in the binary search and thus has // to be handled separately. - if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != x) + if(gethostcachenumber(SLIST_FIELD_CATEGORY, first) != j) error("Category mismatch I"); if(first > 0) - if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == x) + if(gethostcachenumber(SLIST_FIELD_CATEGORY, first - 1) == j) error("Category mismatch II"); - category_name[category_draw_count] = x; + category_name[category_draw_count] = j; category_item[category_draw_count] = first; ++category_draw_count; begin = first + 1; } else { - // At this point, catf <= x < catl, thus + // At this point, catf <= j < catl, thus // catf < catl, thus first < last. // INVARIANTS: // last - first >= 1 // catf == gethostcachenumber(SLIST_FIELD_CATEGORY(first) // catl == gethostcachenumber(SLIST_FIELD_CATEGORY(last) - // catf < x - // catl >= x + // catf < j + // catl >= j while (last - first > 1) { float middle = floor((first + last) / 2); // By loop condition, middle != first && middle != last. float cat = gethostcachenumber(SLIST_FIELD_CATEGORY, middle); - if (cat >= x) { + if (cat >= j) { last = middle; catl = cat; } else { @@ -525,13 +524,13 @@ void XonoticServerList_draw(entity me) catf = cat; } } - if (catl == x) { - if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != x) + if (catl == j) { + if(gethostcachenumber(SLIST_FIELD_CATEGORY, last) != j) error("Category mismatch III"); if(last > 0) - if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == x) + if(gethostcachenumber(SLIST_FIELD_CATEGORY, last - 1) == j) error("Category mismatch IV"); - category_name[category_draw_count] = x; + category_name[category_draw_count] = j; category_item[category_draw_count] = last; ++category_draw_count; begin = last + 1; // already scanned through these, skip 'em @@ -630,10 +629,8 @@ void ServerList_PlayerSort_Click(entity btn, entity me) } void ServerList_TypeSort_Click(entity btn, entity me) { - string s, t; - float i, m; - s = me.filterString; - m = strstrofs(s, ":", 0); + string s = me.filterString; + int m = strstrofs(s, ":", 0); if(m >= 0) { s = substring(s, 0, m); @@ -643,30 +640,23 @@ void ServerList_TypeSort_Click(entity btn, entity me) else s = ""; - for(i = 1; ; i *= 2) // 20 modes ought to be enough for anyone - { - t = MapInfo_Type_ToString(i); - if(i > 1) - if(t == "") // it repeats (default case) - { - // no type was found - // choose the first one - s = MapInfo_Type_ToString(1); - break; - } - if(s == t) - { - // the type was found - // choose the next one - s = MapInfo_Type_ToString(i * 2); - if(s == "") - s = MapInfo_Type_ToString(1); - break; - } + Gametype first = NULL; FOREACH(Gametypes, !first, first = it; break); + bool flag = false; + FOREACH(Gametypes, s == MapInfo_Type_ToString(it), { + // the type was found + // choose the next one + flag = true; + s = MapInfo_Type_ToString(Gametypes_from(it.m_id + 1)); + if (s == "") s = MapInfo_Type_ToString(first); + break; + }); + if (!flag) { + // no type was found + // choose the first one + s = MapInfo_Type_ToString(first); } - if(s != "") - s = strcat(s, ":"); + if(s != "") s = strcat(s, ":"); s = strcat(s, substring(me.filterString, m+1, strlen(me.filterString) - m - 1)); me.controlledTextbox.setText(me.controlledTextbox, s); @@ -970,7 +960,7 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is isv6 = true; me.seenIPv6 += 1; } - else if(strstrofs("0123456789", substring(s, 0, 1), 0) >= 0) + else if(IS_DIGIT(substring(s, 0, 1))) { isv4 = true; me.seenIPv4 += 1; @@ -1070,7 +1060,7 @@ void XonoticServerList_drawListBoxItem(entity me, int i, vector absSize, bool is t = strcat(t, _("encryption:"), " ", (q ? sprintf(_("AES level %d"), q) : ZCTX(_("ENC^none"))), ", "); t = strcat(t, sprintf(_("mod: %s"), ((modname == "xonotic") ? ZCTX(_("MOD^Default")) : original_modname))); if(pure_available) - t = strcat(t, sprintf(_(" (%s)"), (pure) ? _("official settings") : _("modified settings"))); + t = strcat(t, sprintf(" (%s)", (pure) ? _("official settings") : _("modified settings"))); t = strcat(t, ", "); t = strcat(t, ((sflags >= 0 && (sflags & SERVERFLAG_PLAYERSTATS)) ? _("stats enabled") : _("stats disabled"))); setZonedTooltip(me, t, string_null); diff --git a/qcsrc/menu/xonotic/serverlist.qh b/qcsrc/menu/xonotic/serverlist.qh index ac726b222a..e45abfda33 100644 --- a/qcsrc/menu/xonotic/serverlist.qh +++ b/qcsrc/menu/xonotic/serverlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticServerList, XonoticListBox) METHOD(XonoticServerList, configureXonoticServerList, void(entity)); - ATTRIB(XonoticServerList, rowsPerItem, float, 1) + ATTRIB(XonoticServerList, rowsPerItem, float, 1); METHOD(XonoticServerList, draw, void(entity)); METHOD(XonoticServerList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticServerList, doubleClickListBoxItem, void(entity, float, vector)); @@ -11,56 +11,56 @@ CLASS(XonoticServerList, XonoticListBox) METHOD(XonoticServerList, keyDown, float(entity, float, float, float)); METHOD(XonoticServerList, toggleFavorite, void(entity, string)); - ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85) + ATTRIB(XonoticServerList, iconsSizeFactor, float, 0.85); METHOD(XonoticServerList, mouseMove, float(entity, vector)); - ATTRIB(XonoticServerList, mouseOverIcons, bool, false) + ATTRIB(XonoticServerList, mouseOverIcons, bool, false); METHOD(XonoticServerList, focusedItemChangeNotify, void(entity)); - ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticServerList, realUpperMargin, float, 0) - ATTRIB(XonoticServerList, columnIconsOrigin, float, 0) - ATTRIB(XonoticServerList, columnIconsSize, float, 0) - ATTRIB(XonoticServerList, columnPingOrigin, float, 0) - ATTRIB(XonoticServerList, columnPingSize, float, 0) - ATTRIB(XonoticServerList, columnNameOrigin, float, 0) - ATTRIB(XonoticServerList, columnNameSize, float, 0) - ATTRIB(XonoticServerList, columnMapOrigin, float, 0) - ATTRIB(XonoticServerList, columnMapSize, float, 0) - ATTRIB(XonoticServerList, columnTypeOrigin, float, 0) - 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, selectedServer, string, string_null) // to restore selected server when needed + ATTRIB(XonoticServerList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticServerList, realUpperMargin, float, 0); + ATTRIB(XonoticServerList, columnIconsOrigin, float, 0); + ATTRIB(XonoticServerList, columnIconsSize, float, 0); + ATTRIB(XonoticServerList, columnPingOrigin, float, 0); + ATTRIB(XonoticServerList, columnPingSize, float, 0); + ATTRIB(XonoticServerList, columnNameOrigin, float, 0); + ATTRIB(XonoticServerList, columnNameSize, float, 0); + ATTRIB(XonoticServerList, columnMapOrigin, float, 0); + ATTRIB(XonoticServerList, columnMapSize, float, 0); + ATTRIB(XonoticServerList, columnTypeOrigin, float, 0); + 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, selectedServer, string); // to restore selected server when needed METHOD(XonoticServerList, setSelected, void(entity, float)); METHOD(XonoticServerList, setSortOrder, void(entity, float, float)); - ATTRIB(XonoticServerList, filterShowEmpty, float, 1) - ATTRIB(XonoticServerList, filterShowFull, float, 1) - ATTRIB(XonoticServerList, filterString, string, string_null) - ATTRIB(XonoticServerList, controlledTextbox, entity, NULL) - ATTRIB(XonoticServerList, ipAddressBox, entity, NULL) - ATTRIB(XonoticServerList, favoriteButton, entity, NULL) - ATTRIB(XonoticServerList, nextRefreshTime, float, 0) + ATTRIB(XonoticServerList, filterShowEmpty, float, 1); + ATTRIB(XonoticServerList, filterShowFull, float, 1); + ATTRIB(XonoticServerList, filterString, string); + ATTRIB(XonoticServerList, controlledTextbox, entity); + ATTRIB(XonoticServerList, ipAddressBox, entity); + ATTRIB(XonoticServerList, favoriteButton, entity); + ATTRIB(XonoticServerList, nextRefreshTime, float, 0); METHOD(XonoticServerList, refreshServerList, void(entity, float)); // refresh mode: REFRESHSERVERLIST_* - ATTRIB(XonoticServerList, needsRefresh, float, 1) + ATTRIB(XonoticServerList, needsRefresh, float, 1); METHOD(XonoticServerList, focusEnter, void(entity)); METHOD(XonoticServerList, positionSortButton, void(entity, entity, float, float, string, void(entity, entity))); - ATTRIB(XonoticServerList, sortButton1, entity, NULL) - ATTRIB(XonoticServerList, sortButton2, entity, NULL) - ATTRIB(XonoticServerList, sortButton3, entity, NULL) - ATTRIB(XonoticServerList, sortButton4, entity, NULL) - ATTRIB(XonoticServerList, sortButton5, entity, NULL) - ATTRIB(XonoticServerList, connectButton, entity, NULL) - ATTRIB(XonoticServerList, infoButton, entity, NULL) - ATTRIB(XonoticServerList, currentSortOrder, float, 0) - ATTRIB(XonoticServerList, currentSortField, float, -1) - - ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1) - - ATTRIB(XonoticServerList, seenIPv4, float, 0) - ATTRIB(XonoticServerList, seenIPv6, float, 0) - ATTRIB(XonoticServerList, categoriesHeight, float, 1.25) + ATTRIB(XonoticServerList, sortButton1, entity); + ATTRIB(XonoticServerList, sortButton2, entity); + ATTRIB(XonoticServerList, sortButton3, entity); + ATTRIB(XonoticServerList, sortButton4, entity); + ATTRIB(XonoticServerList, sortButton5, entity); + ATTRIB(XonoticServerList, connectButton, entity); + ATTRIB(XonoticServerList, infoButton, entity); + ATTRIB(XonoticServerList, currentSortOrder, float, 0); + ATTRIB(XonoticServerList, currentSortField, float, -1); + + ATTRIB(XonoticServerList, ipAddressBoxFocused, float, -1); + + ATTRIB(XonoticServerList, seenIPv4, float, 0); + ATTRIB(XonoticServerList, seenIPv6, float, 0); + ATTRIB(XonoticServerList, categoriesHeight, float, 1.25); METHOD(XonoticServerList, getTotalHeight, float(entity)); METHOD(XonoticServerList, getItemAtPos, float(entity, float)); @@ -154,8 +154,8 @@ int category_draw_count; SLIST_CATEGORY(CAT_SERVERS, "CAT_NORMAL", "CAT_SERVERS", CTX(_("SLCAT^Servers"))) \ SLIST_CATEGORY(CAT_XPM, "CAT_NORMAL", "CAT_SERVERS", CTX(_("SLCAT^Competitive Mode"))) \ SLIST_CATEGORY(CAT_MODIFIED, "", "CAT_SERVERS", CTX(_("SLCAT^Modified Servers"))) \ - SLIST_CATEGORY(CAT_OVERKILL, "", "CAT_SERVERS", CTX(_("SLCAT^Overkill Mode"))) \ - SLIST_CATEGORY(CAT_INSTAGIB, "", "CAT_SERVERS", CTX(_("SLCAT^InstaGib Mode"))) \ + SLIST_CATEGORY(CAT_OVERKILL, "", "CAT_SERVERS", CTX(_("SLCAT^Overkill"))) \ + SLIST_CATEGORY(CAT_INSTAGIB, "", "CAT_SERVERS", CTX(_("SLCAT^InstaGib"))) \ SLIST_CATEGORY(CAT_DEFRAG, "", "CAT_SERVERS", CTX(_("SLCAT^Defrag Mode"))) #define SLIST_CATEGORY_AUTOCVAR(name) autocvar_menu_slist_categories_##name##_override diff --git a/qcsrc/menu/xonotic/skinlist.qh b/qcsrc/menu/xonotic/skinlist.qh index 616fc233dc..9ea9aa54c6 100644 --- a/qcsrc/menu/xonotic/skinlist.qh +++ b/qcsrc/menu/xonotic/skinlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticSkinList, XonoticListBox) METHOD(XonoticSkinList, configureXonoticSkinList, void(entity)); - ATTRIB(XonoticSkinList, rowsPerItem, float, 4) + ATTRIB(XonoticSkinList, rowsPerItem, float, 4); METHOD(XonoticSkinList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticSkinList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticSkinList, getSkins, void(entity)); @@ -15,18 +15,18 @@ CLASS(XonoticSkinList, XonoticListBox) METHOD(XonoticSkinList, keyDown, float(entity, float, float, float)); METHOD(XonoticSkinList, destroy, void(entity)); - ATTRIB(XonoticSkinList, skinlist, float, -1) - ATTRIB(XonoticSkinList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticSkinList, columnPreviewOrigin, float, 0) - ATTRIB(XonoticSkinList, columnPreviewSize, float, 0) - ATTRIB(XonoticSkinList, columnNameOrigin, float, 0) - ATTRIB(XonoticSkinList, columnNameSize, float, 0) - ATTRIB(XonoticSkinList, realUpperMargin1, float, 0) - ATTRIB(XonoticSkinList, realUpperMargin2, float, 0) - ATTRIB(XonoticSkinList, origin, vector, '0 0 0') - ATTRIB(XonoticSkinList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticSkinList, skinlist, float, -1); + ATTRIB(XonoticSkinList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticSkinList, columnPreviewOrigin, float, 0); + ATTRIB(XonoticSkinList, columnPreviewSize, float, 0); + ATTRIB(XonoticSkinList, columnNameOrigin, float, 0); + ATTRIB(XonoticSkinList, columnNameSize, float, 0); + ATTRIB(XonoticSkinList, realUpperMargin1, float, 0); + ATTRIB(XonoticSkinList, realUpperMargin2, float, 0); + ATTRIB(XonoticSkinList, origin, vector, '0 0 0'); + ATTRIB(XonoticSkinList, itemAbsSize, vector, '0 0 0'); - ATTRIB(XonoticSkinList, name, string, "skinselector") + ATTRIB(XonoticSkinList, name, string, "skinselector"); ENDCLASS(XonoticSkinList) entity makeXonoticSkinList(); diff --git a/qcsrc/menu/xonotic/slider.qh b/qcsrc/menu/xonotic/slider.qh index 17287c4284..9204c5eb35 100644 --- a/qcsrc/menu/xonotic/slider.qh +++ b/qcsrc/menu/xonotic/slider.qh @@ -5,24 +5,24 @@ CLASS(XonoticSlider, Slider) METHOD(XonoticSlider, configureXonoticSlider, void(entity, float, float, float, string, string)); METHOD(XonoticSlider, setValue, void(entity, float)); METHOD(XonoticSlider, setValue_noAnim, void(entity, float)); - ATTRIB(XonoticSlider, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT) - ATTRIB(XonoticSlider, image, string, SKINGFX_SLIDER) - ATTRIB(XonoticSlider, tolerance, vector, SKINTOLERANCE_SLIDER) - ATTRIB(XonoticSlider, align, float, 0.5) - ATTRIB(XonoticSlider, color, vector, SKINCOLOR_SLIDER_N) - ATTRIB(XonoticSlider, colorC, vector, SKINCOLOR_SLIDER_C) - ATTRIB(XonoticSlider, colorF, vector, SKINCOLOR_SLIDER_F) - ATTRIB(XonoticSlider, colorD, vector, SKINCOLOR_SLIDER_D) - ATTRIB(XonoticSlider, color2, vector, SKINCOLOR_SLIDER_S) + ATTRIB(XonoticSlider, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT); + ATTRIB(XonoticSlider, image, string, SKINGFX_SLIDER); + ATTRIB(XonoticSlider, tolerance, vector, SKINTOLERANCE_SLIDER); + ATTRIB(XonoticSlider, align, float, 0.5); + ATTRIB(XonoticSlider, color, vector, SKINCOLOR_SLIDER_N); + ATTRIB(XonoticSlider, colorC, vector, SKINCOLOR_SLIDER_C); + ATTRIB(XonoticSlider, colorF, vector, SKINCOLOR_SLIDER_F); + ATTRIB(XonoticSlider, colorD, vector, SKINCOLOR_SLIDER_D); + ATTRIB(XonoticSlider, color2, vector, SKINCOLOR_SLIDER_S); - ATTRIB(XonoticSlider, cvarName, string, string_null) + ATTRIB(XonoticSlider, cvarName, string); METHOD(XonoticSlider, loadCvars, void(entity)); METHOD(XonoticSlider, saveCvars, void(entity)); - ATTRIB(XonoticSlider, sendCvars, float, 0) + ATTRIB(XonoticSlider, sendCvars, float, 0); - ATTRIB(XonoticSlider, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticSlider, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticSlider, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticSlider, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticSlider) entity makeXonoticSlider_T(float, float, float, string, string theTooltip); entity makeXonoticSlider(float, float, float, string); diff --git a/qcsrc/menu/xonotic/slider_decibels.qc b/qcsrc/menu/xonotic/slider_decibels.qc index 2b78141383..dcc2c85b06 100644 --- a/qcsrc/menu/xonotic/slider_decibels.qc +++ b/qcsrc/menu/xonotic/slider_decibels.qc @@ -7,9 +7,9 @@ float toDecibelOfSquare(float f, float mi) { // linear scale part float t = 1 / A + mi; - float y = exp(1 + A * mi); - if(f <= y) - return mi + (t - mi) * (f / y); + float u = exp(1 + A * mi); + if(f <= u) + return mi + (t - mi) * (f / u); } return log(f) / A; } @@ -21,9 +21,9 @@ float fromDecibelOfSquare(float f, float mi) { // linear scale part float t = 1 / A + mi; - float y = exp(1 + A * mi); + float u = exp(1 + A * mi); if(f <= t) - return y * ((f - mi) / (t - mi)); + return u * ((f - mi) / (t - mi)); } return exp(A * f); } diff --git a/qcsrc/menu/xonotic/slider_picmip.qh b/qcsrc/menu/xonotic/slider_picmip.qh index d25ba994be..3661fd4936 100644 --- a/qcsrc/menu/xonotic/slider_picmip.qh +++ b/qcsrc/menu/xonotic/slider_picmip.qh @@ -5,6 +5,6 @@ CLASS(XonoticPicmipSlider, XonoticTextSlider) METHOD(XonoticPicmipSlider, configureXonoticPicmipSlider, void(entity)); METHOD(XonoticPicmipSlider, draw, void(entity)); METHOD(XonoticPicmipSlider, autofix, void(entity)); - ATTRIB(XonoticPicmipSlider, have_s3tc, float, 0) + ATTRIB(XonoticPicmipSlider, have_s3tc, float, 0); ENDCLASS(XonoticPicmipSlider) entity makeXonoticPicmipSlider(); // note: you still need to call addValue and configureXonoticTextSliderValues! diff --git a/qcsrc/menu/xonotic/slider_resolution.qc b/qcsrc/menu/xonotic/slider_resolution.qc index 762b216f9b..9a55f88403 100644 --- a/qcsrc/menu/xonotic/slider_resolution.qc +++ b/qcsrc/menu/xonotic/slider_resolution.qc @@ -39,8 +39,8 @@ float updateConwidths(float width, float height, float pixelheight) minfactor = min(1, 640 / c.x); // can be > 1 only if c_x is <640 maxfactor = max(1, r.x / c.x, r.y / c.y); // can be < 1 only if r_x < c_x and r_y < c_y - LOG_TRACE("min factor: ", ftos(minfactor), "\n"); - LOG_TRACE("max factor: ", ftos(maxfactor), "\n"); + LOG_TRACE("min factor: ", ftos(minfactor)); + LOG_TRACE("max factor: ", ftos(maxfactor)); if(sz < 0) f = 1 - (maxfactor - 1) * sz; @@ -103,10 +103,10 @@ void XonoticResolutionSlider_addResolution(entity me, float w, float h, float pi bestdenom = denom; } } - me.insertValue(me, i, strzone(sprintf(_("%dx%d (%d:%d)"), w, h, bestnum, bestdenom)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight)))); + me.insertValue(me, i, strzone(sprintf("%dx%d (%d:%d)", w, h, bestnum, bestdenom)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight)))); } else - me.insertValue(me, i, strzone(sprintf(_("%dx%d"), w, h)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight)))); + me.insertValue(me, i, strzone(sprintf("%dx%d", w, h)), strzone(strcat(ftos(w), " ", ftos(h), " ", ftos(pixelheight)))); } float autocvar_menu_vid_allowdualscreenresolution; void XonoticResolutionSlider_configureXonoticResolutionSlider(entity me) @@ -149,7 +149,7 @@ void XonoticResolutionSlider_loadResolutions(entity me, float fullscreen) r = getresolution(-1); if(r.x != 0 || r.y != 0) me.addResolution(me, r.x, r.y, r.z); - LOG_TRACE("Added system resolutions.\n"); + LOG_TRACE("Added system resolutions."); } if(me.nValues == 0) @@ -184,9 +184,9 @@ void XonoticResolutionSlider_loadResolutions(entity me, float fullscreen) me.addResolution(me, 1280, 960, 1); // pc res me.addResolution(me, 1280, 1024, 1); // pc res me.addResolution(me, 1920, 1080, 1); // 1080p - LOG_TRACE("Added default resolutions.\n"); + LOG_TRACE("Added default resolutions."); } - LOG_TRACE("Total number of resolutions detected: ", ftos(me.nValues), "\n"); + LOG_TRACE("Total number of resolutions detected: ", ftos(me.nValues)); me.vid_fullscreen = fullscreen; diff --git a/qcsrc/menu/xonotic/slider_resolution.qh b/qcsrc/menu/xonotic/slider_resolution.qh index 40b7bbbc24..3b56c2a8fd 100644 --- a/qcsrc/menu/xonotic/slider_resolution.qh +++ b/qcsrc/menu/xonotic/slider_resolution.qh @@ -8,9 +8,9 @@ CLASS(XonoticResolutionSlider, XonoticTextSlider) METHOD(XonoticResolutionSlider, loadCvars, void(entity)); METHOD(XonoticResolutionSlider, saveCvars, void(entity)); METHOD(XonoticResolutionSlider, draw, void(entity)); - ATTRIB(XonoticResolutionSlider, vid_fullscreen, float, -1) - ATTRIB(XonoticResolutionSlider, maxAllowedWidth, float, 0) - ATTRIB(XonoticResolutionSlider, maxAllowedHeight, float, 0) + ATTRIB(XonoticResolutionSlider, vid_fullscreen, float, -1); + ATTRIB(XonoticResolutionSlider, maxAllowedWidth, float, 0); + ATTRIB(XonoticResolutionSlider, maxAllowedHeight, float, 0); ENDCLASS(XonoticResolutionSlider) entity makeXonoticResolutionSlider(); float updateConwidths(float width, float height, float pixelheight); diff --git a/qcsrc/menu/xonotic/slider_sbfadetime.qc b/qcsrc/menu/xonotic/slider_sbfadetime.qc index 1e4bdbca21..048a42184c 100644 --- a/qcsrc/menu/xonotic/slider_sbfadetime.qc +++ b/qcsrc/menu/xonotic/slider_sbfadetime.qc @@ -9,7 +9,7 @@ entity makeXonoticScoreboardFadeTimeSlider() } void XonoticScoreboardFadeTimeSlider_configureXonoticScoreboardFadeTimeSlider(entity me) { - me.configureXonoticTextSlider(me, "scoreboard_fadeinspeed", string_null); + me.configureXonoticTextSlider(me, "hud_panel_scoreboard_fadeinspeed", string_null); me.addValue(me, ZCTX(_("PART^Slow")), "5 2.5"); me.addValue(me, ZCTX(_("PART^Normal")), "10 5"); me.addValue(me, ZCTX(_("PART^Fast")), "15 7.5"); @@ -19,8 +19,8 @@ void XonoticScoreboardFadeTimeSlider_configureXonoticScoreboardFadeTimeSlider(en void XonoticScoreboardFadeTimeSlider_loadCvars(entity me) { me.setValueFromIdentifier_noAnim(me, sprintf("%s %s", - cvar_string("scoreboard_fadeinspeed"), - cvar_string("scoreboard_fadeoutspeed") + cvar_string("hud_panel_scoreboard_fadeinspeed"), + cvar_string("hud_panel_scoreboard_fadeoutspeed") )); } void XonoticScoreboardFadeTimeSlider_saveCvars(entity me) @@ -28,7 +28,7 @@ void XonoticScoreboardFadeTimeSlider_saveCvars(entity me) if(me.value >= 0 || me.value < me.nValues) { tokenize_console(me.getIdentifier(me)); - cvar_set("scoreboard_fadeinspeed", argv(0)); - cvar_set("scoreboard_fadeoutspeed", argv(1)); + cvar_set("hud_panel_scoreboard_fadeinspeed", argv(0)); + cvar_set("hud_panel_scoreboard_fadeoutspeed", argv(1)); } } diff --git a/qcsrc/menu/xonotic/soundlist.qh b/qcsrc/menu/xonotic/soundlist.qh index 86dc4c773f..1e30baa11d 100644 --- a/qcsrc/menu/xonotic/soundlist.qh +++ b/qcsrc/menu/xonotic/soundlist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticSoundList, XonoticListBox) METHOD(XonoticSoundList, configureXonoticSoundList, void(entity)); - ATTRIB(XonoticSoundList, rowsPerItem, float, 1) + ATTRIB(XonoticSoundList, rowsPerItem, float, 1); METHOD(XonoticSoundList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticSoundList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticSoundList, getSounds, void(entity)); @@ -13,18 +13,18 @@ CLASS(XonoticSoundList, XonoticListBox) METHOD(XonoticSoundList, destroy, void(entity)); METHOD(XonoticSoundList, showNotify, void(entity)); - ATTRIB(XonoticSoundList, listSound, int, -1) - ATTRIB(XonoticSoundList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticSoundList, columnNameOrigin, float, 0) - ATTRIB(XonoticSoundList, columnNameSize, float, 0) - ATTRIB(XonoticSoundList, columnNumberOrigin, float, 0) - ATTRIB(XonoticSoundList, columnNumberSize, float, 0) - ATTRIB(XonoticSoundList, realUpperMargin, float, 0) - ATTRIB(XonoticSoundList, origin, vector, '0 0 0') - ATTRIB(XonoticSoundList, itemAbsSize, vector, '0 0 0') + ATTRIB(XonoticSoundList, listSound, int, -1); + ATTRIB(XonoticSoundList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticSoundList, columnNameOrigin, float, 0); + ATTRIB(XonoticSoundList, columnNameSize, float, 0); + ATTRIB(XonoticSoundList, columnNumberOrigin, float, 0); + ATTRIB(XonoticSoundList, columnNumberSize, float, 0); + ATTRIB(XonoticSoundList, realUpperMargin, float, 0); + ATTRIB(XonoticSoundList, origin, vector, '0 0 0'); + ATTRIB(XonoticSoundList, itemAbsSize, vector, '0 0 0'); - ATTRIB(XonoticSoundList, filterString, string, string_null) - ATTRIB(XonoticSoundList, playlist, entity, NULL) + ATTRIB(XonoticSoundList, filterString, string); + ATTRIB(XonoticSoundList, playlist, entity); ENDCLASS(XonoticSoundList) entity makeXonoticSoundList(); diff --git a/qcsrc/menu/xonotic/statslist.qc b/qcsrc/menu/xonotic/statslist.qc index 7fdbf9c164..3e94738a9a 100644 --- a/qcsrc/menu/xonotic/statslist.qc +++ b/qcsrc/menu/xonotic/statslist.qc @@ -51,7 +51,7 @@ string XonoticStatsList_convertDate(string input) void XonoticStatsList_getStats(entity me) { - LOG_TRACE("XonoticStatsList_getStats() at time: ", ftos(time), "\n"); + LOG_TRACE("XonoticStatsList_getStats() at time: ", ftos(time)); // delete the old buffer if it exists if(me.listStats >= 0) buf_del(me.listStats); diff --git a/qcsrc/menu/xonotic/statslist.qh b/qcsrc/menu/xonotic/statslist.qh index ebef28ac01..01fb93645b 100644 --- a/qcsrc/menu/xonotic/statslist.qh +++ b/qcsrc/menu/xonotic/statslist.qh @@ -3,7 +3,7 @@ #include "listbox.qh" CLASS(XonoticStatsList, XonoticListBox) METHOD(XonoticStatsList, configureXonoticStatsList, void(entity)); - ATTRIB(XonoticStatsList, rowsPerItem, float, 1.4) + ATTRIB(XonoticStatsList, rowsPerItem, float, 1.4); METHOD(XonoticStatsList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticStatsList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticStatsList, getStats, void(entity)); @@ -11,13 +11,13 @@ CLASS(XonoticStatsList, XonoticListBox) METHOD(XonoticStatsList, keyDown, float(entity, float, float, float)); METHOD(XonoticStatsList, destroy, void(entity)); METHOD(XonoticStatsList, showNotify, void(entity)); - ATTRIB(XonoticStatsList, selectionDoesntMatter, bool, true) + ATTRIB(XonoticStatsList, selectionDoesntMatter, bool, true); - ATTRIB(XonoticStatsList, listStats, float, -1) - ATTRIB(XonoticStatsList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticStatsList, realUpperMargin, float, 0) - ATTRIB(XonoticStatsList, columnNameOrigin, float, 0) - ATTRIB(XonoticStatsList, columnNameSize, float, 0) + ATTRIB(XonoticStatsList, listStats, float, -1); + ATTRIB(XonoticStatsList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticStatsList, realUpperMargin, float, 0); + ATTRIB(XonoticStatsList, columnNameOrigin, float, 0); + ATTRIB(XonoticStatsList, columnNameSize, float, 0); ENDCLASS(XonoticStatsList) entity statslist; // for reference elsewhere diff --git a/qcsrc/menu/xonotic/tab.qh b/qcsrc/menu/xonotic/tab.qh index b296d24918..ff561d57e6 100644 --- a/qcsrc/menu/xonotic/tab.qh +++ b/qcsrc/menu/xonotic/tab.qh @@ -4,24 +4,24 @@ CLASS(XonoticTab, Tab) // still to be customized by user /* - ATTRIB(XonoticTab, intendedWidth, float, 0) - ATTRIB(XonoticTab, rows, float, 3) - ATTRIB(XonoticTab, columns, float, 2) + ATTRIB(XonoticTab, intendedWidth, float, 0); + ATTRIB(XonoticTab, rows, float, 3); + ATTRIB(XonoticTab, columns, float, 2); */ METHOD(XonoticTab, showNotify, void(entity)); - ATTRIB(XonoticTab, marginTop, float, 0) // pixels - ATTRIB(XonoticTab, marginBottom, float, 0) // pixels - ATTRIB(XonoticTab, marginLeft, float, 0) // pixels - ATTRIB(XonoticTab, marginRight, float, 0) // pixels - ATTRIB(XonoticTab, columnSpacing, float, SKINMARGIN_COLUMNS) // pixels - ATTRIB(XonoticTab, rowSpacing, float, SKINMARGIN_ROWS) // pixels - ATTRIB(XonoticTab, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL) // pixels + ATTRIB(XonoticTab, marginTop, float, 0); // pixels + ATTRIB(XonoticTab, marginBottom, float, 0); // pixels + ATTRIB(XonoticTab, marginLeft, float, 0); // pixels + ATTRIB(XonoticTab, marginRight, float, 0); // pixels + ATTRIB(XonoticTab, columnSpacing, float, SKINMARGIN_COLUMNS); // pixels + ATTRIB(XonoticTab, rowSpacing, float, SKINMARGIN_ROWS); // pixels + ATTRIB(XonoticTab, rowHeight, float, SKINFONTSIZE_NORMAL * SKINHEIGHT_NORMAL); // pixels - ATTRIB(XonoticTab, backgroundImage, string, string_null) + ATTRIB(XonoticTab, backgroundImage, string); // using "titleTooltip" instead of "tooltip" so that // the tooltip search function doesn't find it // .tooltip should be set only in the item displaying the tab title - ATTRIB(XonoticTab, titleTooltip, string, string_null) + ATTRIB(XonoticTab, titleTooltip, string); ENDCLASS(XonoticTab) diff --git a/qcsrc/menu/xonotic/tabcontroller.qh b/qcsrc/menu/xonotic/tabcontroller.qh index eb4d48f8d9..46d140eb08 100644 --- a/qcsrc/menu/xonotic/tabcontroller.qh +++ b/qcsrc/menu/xonotic/tabcontroller.qh @@ -5,8 +5,8 @@ CLASS(XonoticTabController, ModalController) METHOD(XonoticTabController, configureXonoticTabController, void(entity, float)); METHOD(XonoticTabController, makeTabButton_T, entity(entity, string, entity, string)); METHOD(XonoticTabController, makeTabButton, entity(entity, string, entity)); - ATTRIB(XonoticTabController, rows, float, 0) - ATTRIB(XonoticTabController, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticTabController, image, string, SKINGFX_BUTTON) + ATTRIB(XonoticTabController, rows, float, 0); + ATTRIB(XonoticTabController, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticTabController, image, string, SKINGFX_BUTTON); ENDCLASS(XonoticTabController) entity makeXonoticTabController(float theRows); diff --git a/qcsrc/menu/xonotic/textlabel.qh b/qcsrc/menu/xonotic/textlabel.qh index fde05de8a3..94f7a74228 100644 --- a/qcsrc/menu/xonotic/textlabel.qh +++ b/qcsrc/menu/xonotic/textlabel.qh @@ -4,9 +4,9 @@ CLASS(XonoticTextLabel, Label) METHOD(XonoticTextLabel, configureXonoticTextLabel, void(entity, float, string)); METHOD(XonoticTextLabel, draw, void(entity)); - ATTRIB(XonoticTextLabel, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticTextLabel, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticTextLabel, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticTextLabel, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticTextLabel, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticTextLabel, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticTextLabel) entity makeXonoticTextLabel(float theAlign, string theText); entity makeXonoticHeaderLabel(string theText); diff --git a/qcsrc/menu/xonotic/textslider.qh b/qcsrc/menu/xonotic/textslider.qh index ae81f36e87..58fe8e86bb 100644 --- a/qcsrc/menu/xonotic/textslider.qh +++ b/qcsrc/menu/xonotic/textslider.qh @@ -6,24 +6,24 @@ CLASS(XonoticTextSlider, TextSlider) METHOD(XonoticTextSlider, setValue, void(entity, float)); METHOD(XonoticTextSlider, setValue_noAnim, void(entity, float)); METHOD(XonoticTextSlider, configureXonoticTextSliderValues, void(entity)); - ATTRIB(XonoticTextSlider, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticTextSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT) - ATTRIB(XonoticTextSlider, image, string, SKINGFX_SLIDER) - ATTRIB(XonoticTextSlider, tolerance, vector, SKINTOLERANCE_SLIDER) - ATTRIB(XonoticTextSlider, align, float, 0.5) - ATTRIB(XonoticTextSlider, color, vector, SKINCOLOR_SLIDER_N) - ATTRIB(XonoticTextSlider, colorC, vector, SKINCOLOR_SLIDER_C) - ATTRIB(XonoticTextSlider, colorF, vector, SKINCOLOR_SLIDER_F) - ATTRIB(XonoticTextSlider, colorD, vector, SKINCOLOR_SLIDER_D) - ATTRIB(XonoticTextSlider, color2, vector, SKINCOLOR_SLIDER_S) + ATTRIB(XonoticTextSlider, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticTextSlider, valueSpace, float, SKINWIDTH_SLIDERTEXT); + ATTRIB(XonoticTextSlider, image, string, SKINGFX_SLIDER); + ATTRIB(XonoticTextSlider, tolerance, vector, SKINTOLERANCE_SLIDER); + ATTRIB(XonoticTextSlider, align, float, 0.5); + ATTRIB(XonoticTextSlider, color, vector, SKINCOLOR_SLIDER_N); + ATTRIB(XonoticTextSlider, colorC, vector, SKINCOLOR_SLIDER_C); + ATTRIB(XonoticTextSlider, colorF, vector, SKINCOLOR_SLIDER_F); + ATTRIB(XonoticTextSlider, colorD, vector, SKINCOLOR_SLIDER_D); + ATTRIB(XonoticTextSlider, color2, vector, SKINCOLOR_SLIDER_S); - ATTRIB(XonoticTextSlider, cvarName, string, string_null) + ATTRIB(XonoticTextSlider, cvarName, string); METHOD(XonoticTextSlider, loadCvars, void(entity)); METHOD(XonoticTextSlider, saveCvars, void(entity)); - ATTRIB(XonoticTextSlider, sendCvars, float, 0) + ATTRIB(XonoticTextSlider, sendCvars, float, 0); - ATTRIB(XonoticTextSlider, alpha, float, SKINALPHA_TEXT) - ATTRIB(XonoticTextSlider, disabledAlpha, float, SKINALPHA_DISABLED) + ATTRIB(XonoticTextSlider, alpha, float, SKINALPHA_TEXT); + ATTRIB(XonoticTextSlider, disabledAlpha, float, SKINALPHA_DISABLED); ENDCLASS(XonoticTextSlider) entity makeXonoticTextSlider_T(string, string theTooltip); entity makeXonoticTextSlider(string); // note: you still need to call addValue and configureXonoticTextSliderValues! diff --git a/qcsrc/menu/xonotic/util.qc b/qcsrc/menu/xonotic/util.qc index 4d704606d3..e746bd7257 100644 --- a/qcsrc/menu/xonotic/util.qc +++ b/qcsrc/menu/xonotic/util.qc @@ -7,7 +7,7 @@ #include #include #include -#include +#include float GL_CheckExtension(string ext) { @@ -321,22 +321,22 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data) if(_Nex_ExtResponseSystem_UpdateTo) { - LOG_TRACE("error: UpdateNotification_URI_Get_Callback has been called before\n"); + LOG_TRACE("error: UpdateNotification_URI_Get_Callback has been called before"); return; } if(status != 0) { - LOG_TRACEF("error receiving update notification: status is %d\n", status); + LOG_TRACEF("error receiving update notification: status is %d", status); return; } if(substring(data, 0, 1) == "<") { - LOG_TRACE("error: received HTML instead of an update notification\n"); + LOG_TRACE("error: received HTML instead of an update notification"); return; } if(strstrofs(data, "\r", 0) != -1) { - LOG_TRACE("error: received carriage returns from update notification server\n"); + LOG_TRACE("error: received carriage returns from update notification server"); return; } @@ -429,6 +429,12 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data) } } + if(un_bannedservers != "") + { + _Nex_ExtResponseSystem_BannedServers = strzone(un_bannedservers); + _Nex_ExtResponseSystem_BannedServersNeedsRefresh = 1; + } + if(un_emergency_pk3s != "") { _Nex_ExtResponseSystem_Packs = strzone(un_emergency_pk3s); @@ -509,7 +515,7 @@ float preMenuInit() MapInfo_Cache_Create(); MapInfo_Enumerate(); - if(!MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) + if(!_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) { draw_reset_cropped(); @@ -679,53 +685,57 @@ float updateCompression() GAMETYPE(MAPINFO_TYPE_NEXBALL) \ GAMETYPE(MAPINFO_TYPE_ONSLAUGHT) \ GAMETYPE(MAPINFO_TYPE_ASSAULT) \ - if (cvar("developer")) GAMETYPE(MAPINFO_TYPE_RACE) \ - GAMETYPE(MAPINFO_TYPE_CTS) \ /* GAMETYPE(MAPINFO_TYPE_INVASION) */ \ /**/ -int GameType_GetID(int cnt) +// hidden gametypes come last so indexing always works correctly +#define HIDDEN_GAMETYPES \ + GAMETYPE(MAPINFO_TYPE_RACE) \ + GAMETYPE(MAPINFO_TYPE_CTS) \ + /**/ + +Gametype GameType_GetID(int cnt) { int i = 0; - - #define GAMETYPE(id) { if (i++ == cnt) return id; } + #define GAMETYPE(it) { if (i++ == cnt) return it; } GAMETYPES + HIDDEN_GAMETYPES #undef GAMETYPE - - unused_float = i; - - return 0; + return NULL; } int GameType_GetCount() { int i = 0; - #define GAMETYPE(id) ++i; GAMETYPES #undef GAMETYPE + #define GAMETYPE(it) if (cvar("developer")) ++i; + HIDDEN_GAMETYPES + #undef GAMETYPE + return i; +} +int GameType_GetTotalCount() +{ + int i = 0; + #define GAMETYPE(id) ++i; + GAMETYPES + HIDDEN_GAMETYPES + #undef GAMETYPE return i; } string GameType_GetName(int cnt) { - int i = GameType_GetID(cnt); - - if(i) - return MapInfo_Type_ToText(i); - - return ""; + Gametype i = GameType_GetID(cnt); + return i ? MapInfo_Type_ToText(i) : ""; } string GameType_GetIcon(int cnt) { - int i = GameType_GetID(cnt); - - if(i) - return strcat("gametype_", MapInfo_Type_ToString(i)); - - return ""; + Gametype i = GameType_GetID(cnt); + return i ? strcat("gametype_", MapInfo_Type_ToString(i)) : ""; } .void(entity) TR; diff --git a/qcsrc/menu/xonotic/util.qh b/qcsrc/menu/xonotic/util.qh index d433352cf5..96fef2ad40 100644 --- a/qcsrc/menu/xonotic/util.qh +++ b/qcsrc/menu/xonotic/util.qh @@ -29,11 +29,12 @@ void UpdateNotification_URI_Get_Callback(float id, float status, string data); // game type list box stuff (does not NEED to contain all game types, other // types stay available via console) -int GameType_GetID(int cnt); +entity GameType_GetID(int cnt); string GameType_GetName(int cnt); string GameType_GetIcon(int cnt); //string GameType_GetTeams(float cnt); int GameType_GetCount(); +int GameType_GetTotalCount(); void dialog_hudpanel_common_notoggle(entity me, string panelname); #define DIALOG_HUDPANEL_COMMON_NOTOGGLE() \ diff --git a/qcsrc/menu/xonotic/weaponarenacheckbox.qh b/qcsrc/menu/xonotic/weaponarenacheckbox.qh index 0b8e214c80..c3c155bb2b 100644 --- a/qcsrc/menu/xonotic/weaponarenacheckbox.qh +++ b/qcsrc/menu/xonotic/weaponarenacheckbox.qh @@ -4,9 +4,9 @@ CLASS(XonoticWeaponarenaCheckBox, CheckBox) METHOD(XonoticWeaponarenaCheckBox, configureXonoticWeaponarenaCheckBox, void(entity, string, string)); METHOD(XonoticWeaponarenaCheckBox, setChecked, void(entity, float)); - ATTRIB(XonoticWeaponarenaCheckBox, fontSize, float, SKINFONTSIZE_NORMAL) - ATTRIB(XonoticWeaponarenaCheckBox, image, string, SKINGFX_CHECKBOX) - ATTRIB(XonoticWeaponarenaCheckBox, netname, string, string_null) + ATTRIB(XonoticWeaponarenaCheckBox, fontSize, float, SKINFONTSIZE_NORMAL); + ATTRIB(XonoticWeaponarenaCheckBox, image, string, SKINGFX_CHECKBOX); + ATTRIB(XonoticWeaponarenaCheckBox, netname, string); METHOD(XonoticWeaponarenaCheckBox, loadCvars, void(entity)); METHOD(XonoticWeaponarenaCheckBox, saveCvars, void(entity)); diff --git a/qcsrc/menu/xonotic/weaponslist.qc b/qcsrc/menu/xonotic/weaponslist.qc index ed74631dea..7b3d7375b7 100644 --- a/qcsrc/menu/xonotic/weaponslist.qc +++ b/qcsrc/menu/xonotic/weaponslist.qc @@ -1,6 +1,6 @@ #include "weaponslist.qh" -#include +#include .bool disabled; diff --git a/qcsrc/menu/xonotic/weaponslist.qh b/qcsrc/menu/xonotic/weaponslist.qh index 953a40cc0d..4726d8553d 100644 --- a/qcsrc/menu/xonotic/weaponslist.qh +++ b/qcsrc/menu/xonotic/weaponslist.qh @@ -4,16 +4,16 @@ CLASS(XonoticWeaponsList, XonoticListBox) METHOD(XonoticWeaponsList, configureXonoticWeaponsList, void(entity)); METHOD(XonoticWeaponsList, toString, string(entity)); - ATTRIB(XonoticWeaponsList, rowsPerItem, float, 1) + ATTRIB(XonoticWeaponsList, rowsPerItem, float, 1); METHOD(XonoticWeaponsList, draw, void(entity)); METHOD(XonoticWeaponsList, drawListBoxItem, void(entity, int, vector, bool, bool)); METHOD(XonoticWeaponsList, resizeNotify, void(entity, vector, vector, vector, vector)); METHOD(XonoticWeaponsList, keyDown, float(entity, float, float, float)); - ATTRIB(XonoticWeaponsList, realFontSize, vector, '0 0 0') - ATTRIB(XonoticWeaponsList, realUpperMargin, float, 0) + ATTRIB(XonoticWeaponsList, realFontSize, vector, '0 0 0'); + ATTRIB(XonoticWeaponsList, realUpperMargin, float, 0); METHOD(XonoticWeaponsList, mouseDrag, float(entity, vector)); - ATTRIB(XonoticWeaponsList, applyButton, entity, NULL) + ATTRIB(XonoticWeaponsList, applyButton, entity); ENDCLASS(XonoticWeaponsList) entity makeXonoticWeaponsList(); void WeaponsList_MoveUp_Click(entity btn, entity me); diff --git a/qcsrc/server-testcase/framework.qc b/qcsrc/server-testcase/framework.qc index 1ec7115ef2..1735e1c113 100644 --- a/qcsrc/server-testcase/framework.qc +++ b/qcsrc/server-testcase/framework.qc @@ -7,7 +7,7 @@ float test(); spawnfunc(worldspawn) { float r; - LOG_TRACE("TESTCASE: START\n"); + LOG_TRACE("TESTCASE: START"); r = test(); if(r == 1) error("TESTCASE: PASS"); diff --git a/qcsrc/server/_all.inc b/qcsrc/server/_all.inc new file mode 100644 index 0000000000..95bf715310 --- /dev/null +++ b/qcsrc/server/_all.inc @@ -0,0 +1,19 @@ +#include +#include "_mod.inc" + +#include "bot/_mod.inc" +#include "command/_mod.inc" +#include "compat/_mod.inc" +#include "mutators/_mod.inc" +#include "pathlib/_mod.inc" +#include "weapons/_mod.inc" + +#include +#include + +#include + +#include +#include +#include +#include diff --git a/qcsrc/server/_all.qh b/qcsrc/server/_all.qh index a32f8b5832..1fabe4d0f9 100644 --- a/qcsrc/server/_all.qh +++ b/qcsrc/server/_all.qh @@ -43,7 +43,38 @@ const string STR_OBSERVER = "observer"; #define FOREACH_CLIENT(cond, body) FOREACH_CLIENTSLOT(IS_CLIENT(it) && (cond), body) -// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { code; }); +// using the "inside out" version of knuth-fisher-yates shuffle +// https://en.wikipedia.org/wiki/Fisher–Yates_shuffle +entity _FCR_clients[255]; +bool _FCR_entered = false; +#define FOREACH_CLIENT_RANDOM(cond, body) \ + MACRO_BEGIN { \ + if (_FCR_entered) LOG_FATAL("FOREACH_CLIENT_RANDOM must not be nested"); \ + _FCR_entered = true; \ + int _cnt = 0; \ + FOREACH_CLIENT(cond, LAMBDA( \ + int _j = floor(random() * (_cnt + 1)); \ + if (_j == _cnt) \ + { \ + _FCR_clients[_cnt] = it; \ + } \ + else \ + { \ + _FCR_clients[_cnt] = _FCR_clients[_j]; \ + _FCR_clients[_j] = it; \ + } \ + _cnt++; \ + )); \ + for (int _i = 0; _i < _cnt; ++_i) \ + { \ + const noref int i = _i; \ + ITER_CONST noref entity it = _FCR_clients[i]; \ + if (cond) { LAMBDA(body) } \ + } \ + _FCR_entered = false; \ + } MACRO_END + +// NOTE: FOR_EACH_MONSTER deprecated! Use the following instead: IL_EACH(g_monsters, true, { code; }); #include #include diff --git a/qcsrc/server/_mod.inc b/qcsrc/server/_mod.inc index f22742f1dd..eb74529a64 100644 --- a/qcsrc/server/_mod.inc +++ b/qcsrc/server/_mod.inc @@ -3,20 +3,20 @@ #include #include #include -#include -#include -#include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,9 +25,8 @@ #include #include #include -#include +#ifdef SVQC + #include +#endif #include #include -#include -#include -#include diff --git a/qcsrc/server/_mod.qh b/qcsrc/server/_mod.qh index 17e0a829d4..a0d160d57c 100644 --- a/qcsrc/server/_mod.qh +++ b/qcsrc/server/_mod.qh @@ -3,20 +3,20 @@ #include #include #include -#include -#include -#include +#include #include #include #include #include #include #include +#include #include #include #include #include #include +#include #include #include #include @@ -25,9 +25,8 @@ #include #include #include -#include +#ifdef SVQC + #include +#endif #include #include -#include -#include -#include diff --git a/qcsrc/server/anticheat.qc b/qcsrc/server/anticheat.qc index 4861fe2a79..4e794cb3fc 100644 --- a/qcsrc/server/anticheat.qc +++ b/qcsrc/server/anticheat.qc @@ -6,6 +6,7 @@ #include "miscfunctions.qh" #include "command/common.qh" +#include #include .float anticheat_jointime; @@ -158,67 +159,66 @@ void anticheat_prethink(entity this) CS(this).anticheat_div0_evade_offset = 0; } -string anticheat_display(float f, float tmin, float mi, float ma) +string anticheat_display(float f, float t, float tmin, float mi, float ma) { string s; s = ftos(f); - if(f <= mi) - return strcat(s, ":N"); - if(f >= ma) - return strcat(s, ":Y"); + if (t >= tmin) { + if(f <= mi) + return strcat(s, ":N"); + if(f >= ma) + return strcat(s, ":Y"); + } return strcat(s, ":-"); } -void anticheat_report(entity this) -{ +#define ANTICHEATS(ANTICHEAT) \ + ANTICHEAT("speedhack", MEAN_EVALUATE(CS(this), anticheat_speedhack), 240, 0, 9999); /* Actually this one seems broken. */ \ + ANTICHEAT("speedhack_m1", MEAN_EVALUATE(CS(this), anticheat_speedhack_m1), 240, 1.01, 1.25); \ + ANTICHEAT("speedhack_m2", MEAN_EVALUATE(CS(this), anticheat_speedhack_m2), 240, 1.01, 1.25); \ + ANTICHEAT("speedhack_m3", MEAN_EVALUATE(CS(this), anticheat_speedhack_m3), 240, 1.01, 1.25); \ + ANTICHEAT("speedhack_m4", MEAN_EVALUATE(CS(this), anticheat_speedhack_m4), 240, 1.01, 1.25); \ + ANTICHEAT("speedhack_m5", MEAN_EVALUATE(CS(this), anticheat_speedhack_m5), 240, 1.01, 1.25); \ + ANTICHEAT("div0_strafebot_old", MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old), 120, 0.15, 0.4); \ + ANTICHEAT("div0_strafebot_new", MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new), 120, 0.25, 0.8); \ + ANTICHEAT("div0_evade", MEAN_EVALUATE(CS(this), anticheat_div0_evade), 120, 0.2, 0.5); \ + ANTICHEAT("idle_snapaim", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_signal", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_noise", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_m2", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_m3", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_m4", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_m7", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7), 120, 0, 9999); \ + ANTICHEAT("idle_snapaim_m10", MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10), 120, 0, 9999) + +void anticheat_report_to_eventlog(entity this) { if(!autocvar_sv_eventlog) return; - // TODO(divVerent): Use xonstat to acquire good thresholds. GameLogEcho(strcat(":anticheat:_time:", ftos(this.playerid), ":", ftos(servertime - CS(this).anticheat_jointime))); - GameLogEcho(strcat(":anticheat:speedhack:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack), 240, 0, 9999))); // Actually this one seems broken. - GameLogEcho(strcat(":anticheat:speedhack_m1:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m1), 240, 1.01, 1.25))); - GameLogEcho(strcat(":anticheat:speedhack_m2:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m2), 240, 1.01, 1.25))); - GameLogEcho(strcat(":anticheat:speedhack_m3:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m3), 240, 1.01, 1.25))); - GameLogEcho(strcat(":anticheat:speedhack_m4:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m4), 240, 1.01, 1.25))); - GameLogEcho(strcat(":anticheat:speedhack_m5:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_speedhack_m5), 240, 1.01, 1.25))); - GameLogEcho(strcat(":anticheat:div0_strafebot_old:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old), 120, 0.15, 0.4))); - GameLogEcho(strcat(":anticheat:div0_strafebot_new:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new), 120, 0.25, 0.8))); - GameLogEcho(strcat(":anticheat:div0_evade:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_div0_evade), 120, 0.2, 0.5))); - GameLogEcho(strcat(":anticheat:idle_snapaim:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_signal:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_noise:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_m2:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_m3:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_m4:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_m7:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7), 120, 0, 9999))); - GameLogEcho(strcat(":anticheat:idle_snapaim_m10:", ftos(this.playerid), ":", anticheat_display(MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10), 120, 0, 9999))); +#define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \ + GameLogEcho(strcat(":anticheat:", name, ":", anticheat_display(f, servertime - CS(this).anticheat_jointime, tmin, mi, ma))) + ANTICHEATS(ANTICHEAT_REPORT_ONE); +#undef ANTICHEAT_REPORT_ONE } -float anticheat_getvalue(entity this, string id) -{ - switch(id) { - case "_time": return servertime - CS(this).anticheat_jointime; - case "speedhack": return MEAN_EVALUATE(CS(this), anticheat_speedhack); - case "speedhack_m1": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m1); - case "speedhack_m2": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m2); - case "speedhack_m3": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m3); - case "speedhack_m4": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m4); - case "speedhack_m5": return MEAN_EVALUATE(CS(this), anticheat_speedhack_m5); - case "div0_strafebot_old": return MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_old); - case "div0_strafebot_new": return MEAN_EVALUATE(CS(this), anticheat_div0_strafebot_new); - case "div0_evade": return MEAN_EVALUATE(CS(this), anticheat_div0_evade); - case "idle_snapaim": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal) - MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise); - case "idle_snapaim_signal": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_signal); - case "idle_snapaim_noise": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_noise); - case "idle_snapaim_m2": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m2); - case "idle_snapaim_m3": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m3); - case "idle_snapaim_m4": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m4); - case "idle_snapaim_m7": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m7); - case "idle_snapaim_m10": return MEAN_EVALUATE(CS(this), anticheat_idle_snapaim_m10); - } - return -1; +void anticheat_report_to_playerstats(entity this) { + PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, "_time"), servertime - CS(this).anticheat_jointime); +#define ANTICHEAT_REPORT_ONE(name, f, tmin, mi, ma) \ + PS_GR_P_ADDVAL(this, strcat(PLAYERSTATS_ANTICHEAT, name), f) + ANTICHEATS(ANTICHEAT_REPORT_ONE); +#undef ANTICHEAT_REPORT_ONE } +void anticheat_register_to_playerstats() { + PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_ANTICHEAT, "_time")); +#define ANTICHEAT_REGISTER_ONE(name, unused_f, unused_tmin, unused_mi, unused_ma) \ + PlayerStats_GameReport_AddEvent(strcat(PLAYERSTATS_ANTICHEAT, name)) + ANTICHEATS(ANTICHEAT_REGISTER_ONE); +#undef ANTICHEAT_REGISTER_ONE +} + +#undef ANTICHEATS + void anticheat_startframe() { anticheat_div0_evade_evasion_delta += frametime * (0.5 + random()); diff --git a/qcsrc/server/anticheat.qh b/qcsrc/server/anticheat.qh index 2b0522b991..dd870dd397 100644 --- a/qcsrc/server/anticheat.qh +++ b/qcsrc/server/anticheat.qh @@ -1,14 +1,14 @@ #pragma once void anticheat_init(entity this); -void anticheat_report(entity this); +void anticheat_report_to_eventlog(entity this); +void anticheat_report_to_playerstats(entity this); +void anticheat_register_to_playerstats(); void anticheat_physics(entity this); void anticheat_spectatecopy(entity this, entity spectatee); void anticheat_prethink(entity this); -float anticheat_getvalue(entity this, string name); - void anticheat_startframe(); void anticheat_endframe(); diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index 314858780b..c4f77dfad3 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -5,61 +5,9 @@ bool autocvar__campaign_testrun; int autocvar__campaign_index; string autocvar__campaign_name; bool autocvar__sv_init; -float autocvar_bot_ai_aimskill_blendrate; -float autocvar_bot_ai_aimskill_firetolerance_distdegrees; -float autocvar_bot_ai_aimskill_firetolerance_maxdegrees; -float autocvar_bot_ai_aimskill_firetolerance_mindegrees; -float autocvar_bot_ai_aimskill_fixedrate; -float autocvar_bot_ai_aimskill_mouse; -float autocvar_bot_ai_aimskill_offset; -float autocvar_bot_ai_aimskill_order_filter_1st; -float autocvar_bot_ai_aimskill_order_filter_2nd; -float autocvar_bot_ai_aimskill_order_filter_3th; -float autocvar_bot_ai_aimskill_order_filter_4th; -float autocvar_bot_ai_aimskill_order_filter_5th; -float autocvar_bot_ai_aimskill_order_mix_1st; -float autocvar_bot_ai_aimskill_order_mix_2nd; -float autocvar_bot_ai_aimskill_order_mix_3th; -float autocvar_bot_ai_aimskill_order_mix_4th; -float autocvar_bot_ai_aimskill_order_mix_5th; -float autocvar_bot_ai_aimskill_think; -float autocvar_bot_ai_bunnyhop_firstjumpdelay; -float autocvar_bot_ai_bunnyhop_skilloffset; -float autocvar_bot_ai_bunnyhop_startdistance; -float autocvar_bot_ai_bunnyhop_stopdistance; -float autocvar_bot_ai_chooseweaponinterval; -string autocvar_bot_ai_custom_weapon_priority_close; -string autocvar_bot_ai_custom_weapon_priority_distances; -string autocvar_bot_ai_custom_weapon_priority_far; -string autocvar_bot_ai_custom_weapon_priority_mid; -float autocvar_bot_ai_dangerdetectioninterval; -float autocvar_bot_ai_dangerdetectionupdates; -float autocvar_bot_ai_enemydetectioninterval; -float autocvar_bot_ai_enemydetectionradius; -float autocvar_bot_ai_friends_aware_pickup_radius; -float autocvar_bot_ai_ignoregoal_timeout; -float autocvar_bot_ai_keyboard_distance; -float autocvar_bot_ai_keyboard_threshold; -float autocvar_bot_ai_navigation_jetpack; -float autocvar_bot_ai_navigation_jetpack_mindistance; float autocvar_bot_ai_strategyinterval; -float autocvar_bot_ai_thinkinterval; -bool autocvar_bot_ai_weapon_combo; -float autocvar_bot_ai_weapon_combo_threshold; -string autocvar_bot_config_file; -bool autocvar_bot_god; -bool autocvar_bot_ignore_bots; -bool autocvar_bot_join_empty; -bool autocvar_bot_navigation_ignoreplayers; -bool autocvar_bot_nofire; #define autocvar_bot_number cvar("bot_number") -#define autocvar_bot_prefix cvar_string("bot_prefix") -#define autocvar_bot_suffix cvar_string("bot_suffix") -bool autocvar_bot_usemodelnames; int autocvar_bot_vs_human; -bool autocvar_bot_debug_tracewalk; -bool autocvar_bot_debug_goalstack; -bool autocvar_bot_wander_enable; int autocvar_captureleadlimit_override; #define autocvar_capturelimit_override cvar("capturelimit_override") float autocvar_ekg; @@ -144,6 +92,7 @@ float autocvar_g_balance_teams_scorefactor; float autocvar_g_ballistics_density_corpse; float autocvar_g_ballistics_density_player; float autocvar_g_ballistics_mindistance; +bool autocvar_g_ballistics_penetrate_clips; float autocvar_g_ban_default_bantime; float autocvar_g_ban_default_masksize; float autocvar_g_ban_sync_interval; @@ -172,7 +121,6 @@ float autocvar_g_chat_flood_spl_tell; int autocvar_g_chat_nospectators; bool autocvar_g_chat_teamcolors; bool autocvar_g_chat_tellprivacy; -bool autocvar_g_debug_bot_commands; bool autocvar_g_forced_respawn; string autocvar_g_forced_team_blue; string autocvar_g_forced_team_otherwise; @@ -197,6 +145,7 @@ float autocvar_g_jetpack_antigravity; int autocvar_g_jetpack_fuel; float autocvar_g_jetpack_maxspeed_side; float autocvar_g_jetpack_maxspeed_up; +float autocvar_g_jetpack_reverse_thrust; #define autocvar_g_maplist cvar_string("g_maplist") bool autocvar_g_maplist_check_waypoints; int autocvar_g_maplist_index; @@ -295,7 +244,6 @@ float autocvar_g_turrets_targetscan_maxdelay; float autocvar_g_turrets_targetscan_mindelay; bool autocvar_g_use_ammunition; bool autocvar_g_waypointeditor; -int autocvar_g_waypointeditor_auto; bool autocvar_g_waypoints_for_items; #define autocvar_g_weapon_stay cvar("g_weapon_stay") bool autocvar_g_weapon_throwable; @@ -320,7 +268,6 @@ int autocvar_rescan_pending; bool autocvar_samelevel; string autocvar_sessionid; #define autocvar_skill cvar("skill") -float autocvar_skill_auto; #define autocvar_slowmo cvar("slowmo") float autocvar_snd_soundradius; int autocvar_spawn_debug; @@ -356,7 +303,7 @@ float autocvar_sv_gameplayfix_q2airaccelerate; int autocvar_sv_gentle; #define autocvar_sv_gravity cvar("sv_gravity") string autocvar_sv_intermission_cdtrack; -float autocvar_sv_itemstime; +int autocvar_sv_itemstime; string autocvar_sv_jumpspeedcap_max; float autocvar_sv_jumpspeedcap_max_disable_on_ramps; string autocvar_sv_jumpspeedcap_min; @@ -428,7 +375,6 @@ float autocvar_timelimit_overtime; int autocvar_timelimit_overtimes; float autocvar_timelimit_suddendeath; #define autocvar_utf8_enable cvar("utf8_enable") -bool autocvar_waypoint_benchmark; float autocvar_sv_gameplayfix_gravityunaffectedbyticrate; bool autocvar_sv_gameplayfix_upwardvelocityclearsongroundflag; float autocvar_g_trueaim_minrange; @@ -460,7 +406,8 @@ float autocvar_g_monsters_respawn_delay; bool autocvar_g_monsters_respawn; float autocvar_g_monsters_armor_blockpercent; float autocvar_g_monsters_healthbars; -float autocvar_g_monsters_lineofsight; +bool autocvar_g_monsters_lineofsight = true; +//bool autocvar_g_monsters_ignoretraces = true; #define autocvar_g_bloodloss cvar("g_bloodloss") bool autocvar_g_nades; bool autocvar_g_nades_override_dropweapon = true; @@ -552,3 +499,26 @@ float autocvar_g_frozen_revive_falldamage; int autocvar_g_frozen_revive_falldamage_health; bool autocvar_g_frozen_damage_trigger; float autocvar_g_frozen_force; +float autocvar_sv_airaccel_qw; +float autocvar_sv_airstrafeaccel_qw; +float autocvar_sv_airspeedlimit_nonqw; +float autocvar_sv_airaccel_qw_stretchfactor; +float autocvar_sv_maxairstrafespeed; +float autocvar_sv_airstrafeaccelerate; +float autocvar_sv_warsowbunny_turnaccel; +float autocvar_sv_airaccel_sideways_friction; +float autocvar_sv_aircontrol; +float autocvar_sv_aircontrol_power; +float autocvar_sv_aircontrol_backwards; +float autocvar_sv_aircontrol_penalty; +float autocvar_sv_warsowbunny_airforwardaccel; +float autocvar_sv_warsowbunny_topspeed; +float autocvar_sv_warsowbunny_accel; +float autocvar_sv_warsowbunny_backtosideratio; +float autocvar_sv_friction; +float autocvar_sv_accelerate; +float autocvar_sv_stopspeed; +float autocvar_sv_airaccelerate; +float autocvar_sv_airstopaccelerate; +float autocvar_sv_track_canjump; +bool autocvar_sv_showspectators; diff --git a/qcsrc/server/bot/_mod.inc b/qcsrc/server/bot/_mod.inc index 787939110d..7c0c2882e9 100644 --- a/qcsrc/server/bot/_mod.inc +++ b/qcsrc/server/bot/_mod.inc @@ -1,6 +1,5 @@ // generated file; do not modify -#include -#include -#include -#include -#include +#include + +#include +#include diff --git a/qcsrc/server/bot/_mod.qh b/qcsrc/server/bot/_mod.qh index 802d391878..3678fc69f1 100644 --- a/qcsrc/server/bot/_mod.qh +++ b/qcsrc/server/bot/_mod.qh @@ -1,6 +1,5 @@ // generated file; do not modify -#include -#include -#include -#include -#include +#include + +#include +#include diff --git a/qcsrc/server/bot/aim.qc b/qcsrc/server/bot/aim.qc deleted file mode 100644 index b2fca55e86..0000000000 --- a/qcsrc/server/bot/aim.qc +++ /dev/null @@ -1,393 +0,0 @@ -#include "aim.qh" - -#include "bot.qh" - -#include -#include - -#include "../weapons/weaponsystem.qh" - -#include "../mutators/all.qh" - -// traces multiple trajectories to find one that will impact the target -// 'end' vector is the place it aims for, -// returns true only if it hit targ (don't target non-solid entities) - -float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore) -{ - float c, savesolid, shottime; - vector dir, end, v, o; - if (shotspeed < 1) - return false; // could cause division by zero if calculated - if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER - return false; // could never hit it - if (!tracetossent) - tracetossent = new(tracetossent); - tracetossent.owner = ignore; - setsize(tracetossent, m1, m2); - savesolid = targ.solid; - targ.solid = SOLID_NOT; - o = (targ.absmin + targ.absmax) * 0.5; - shottime = ((vlen(o - org) / shotspeed) + shotdelay); - v = targ.velocity * shottime + o; - tracebox(o, targ.mins, targ.maxs, v, false, targ); - v = trace_endpos; - end = v + (targ.mins + targ.maxs) * 0.5; - if ((vlen(end - org) / shotspeed + 0.2) > maxtime) - { - // out of range - targ.solid = savesolid; - return false; - } - - if (!tracetossfaketarget) - tracetossfaketarget = new(tracetossfaketarget); - tracetossfaketarget.solid = savesolid; - tracetossfaketarget.movetype = targ.movetype; - _setmodel(tracetossfaketarget, targ.model); // no low precision - tracetossfaketarget.model = targ.model; - tracetossfaketarget.modelindex = targ.modelindex; - setsize(tracetossfaketarget, targ.mins, targ.maxs); - setorigin(tracetossfaketarget, v); - - c = 0; - dir = normalize(end - org); - while (c < 10) // 10 traces - { - setorigin(tracetossent, org); // reset - tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1'; - tracetoss(tracetossent, ignore); // love builtin functions... - if (trace_ent == tracetossfaketarget) // done - { - targ.solid = savesolid; - - // make it disappear - tracetossfaketarget.solid = SOLID_NOT; - tracetossfaketarget.movetype = MOVETYPE_NONE; - tracetossfaketarget.model = ""; - tracetossfaketarget.modelindex = 0; - // relink to remove it from physics considerations - setorigin(tracetossfaketarget, v); - - return true; - } - dir.z = dir.z + 0.1; // aim up a little more - c = c + 1; - } - targ.solid = savesolid; - - // make it disappear - tracetossfaketarget.solid = SOLID_NOT; - tracetossfaketarget.movetype = MOVETYPE_NONE; - tracetossfaketarget.model = ""; - tracetossfaketarget.modelindex = 0; - // relink to remove it from physics considerations - setorigin(tracetossfaketarget, v); - - // leave a valid one even if it won't reach - findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1'; - return false; -} - -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;} -} - -float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) -{ - if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;} - if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;} - if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;} - if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;} - if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;} - // no room for it (what is the best thing to do here??) - return false; -} - -bool bot_shouldattack(entity this, entity targ) -{ - if (targ.team == this.team) - { - if (targ == this) - return false; - if (teamplay) - if (targ.team != 0) - return false; - } - - if(STAT(FROZEN, targ)) - return false; - - if(teamplay) - { - if(targ.team==0) - return false; - } - else if(bot_ignore_bots) - if(IS_BOT_CLIENT(targ)) - return false; - - if (!targ.takedamage) - return false; - if (IS_DEAD(targ)) - return false; - if (PHYS_INPUT_BUTTON_CHAT(targ)) - return false; - if(targ.flags & FL_NOTARGET) - return false; - - if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ)) - return false; - - return true; -} - -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 = this.ping; // FIXME? Shouldn't this be in the lag item? - //this.bot_aimorigin = v1; - //this.bot_aimvelocity = v2; - this.bot_aimtargorigin = v3; - this.bot_aimtargvelocity = v4; - if(skill <= 0) - this.bot_canfire = (random() < 0.8); - else if(skill <= 1) - this.bot_canfire = (random() < 0.9); - else if(skill <= 2) - this.bot_canfire = (random() < 0.95); - else - this.bot_canfire = 1; -} - -float bot_aimdir(entity this, vector v, float maxfiredeviation) -{ - float dist, delta_t, blend; - vector desiredang, diffang; - - //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; - this.v_angle_z = 0; - - // get the desired angles to aim at - //dprint(" at:", vtos(v)); - v = normalize(v); - //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200); - if (time >= this.bot_badaimtime) - { - this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time); - this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset; - } - desiredang = vectoangles(v) + this.bot_badaimoffset; - //dprint(" desired:", vtos(desiredang)); - if (desiredang.x >= 180) - desiredang.x = desiredang.x - 360; - desiredang.x = bound(-90, 0 - desiredang.x, 90); - desiredang.z = this.v_angle.z; - //dprint(" / ", vtos(desiredang)); - - //// pain throws off aim - //if (this.bot_painintensity) - //{ - // // shake from pain - // desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2; - //} - - // calculate turn angles - diffang = (desiredang - this.bot_olddesiredang); - // wrap yaw turn - diffang.y = diffang.y - floor(diffang.y / 360) * 360; - if (diffang.y >= 180) - diffang.y = diffang.y - 360; - this.bot_olddesiredang = desiredang; - //dprint(" diff:", vtos(diffang)); - - delta_t = time-this.bot_prevaimtime; - this.bot_prevaimtime = time; - // Here we will try to anticipate the comming aiming direction - this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter - + (diffang * (1 / delta_t) - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1); - this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter - + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1); - this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter - + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1); - this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter - + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1); - this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter - + (this.bot_4th_order_aimfilter - this.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1); - - //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing ! - blend = bound(0,skill+this.bot_aimskill,10)*0.1; - desiredang = desiredang + blend * - ( - this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st - + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd - + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th - + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th - + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th - ); - - // calculate turn angles - diffang = desiredang - this.bot_mouseaim; - // wrap yaw turn - diffang.y = diffang.y - floor(diffang.y / 360) * 360; - if (diffang.y >= 180) - diffang.y = diffang.y - 360; - //dprint(" diff:", vtos(diffang)); - - if (time >= this.bot_aimthinktime) - { - this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time); - this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10)); - } - - //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1); - - diffang = this.bot_mouseaim - desiredang; - // wrap yaw turn - diffang.y = diffang.y - floor(diffang.y / 360) * 360; - if (diffang.y >= 180) - diffang.y = diffang.y - 360; - desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1); - - // calculate turn angles - diffang = desiredang - this.v_angle; - // wrap yaw turn - diffang.y = diffang.y - floor(diffang.y / 360) * 360; - if (diffang.y >= 180) - diffang.y = diffang.y - 360; - //dprint(" diff:", vtos(diffang)); - - // jitter tracking - dist = vlen(diffang); - //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3))); - - // turn - float r, fixedrate, blendrate; - fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000); - blendrate = autocvar_bot_ai_aimskill_blendrate; - r = max(fixedrate, blendrate); - //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1); - this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1); - this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1); - //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1); - //this.v_angle = this.v_angle + diffang * (1/ blendrate); - this.v_angle_z = 0; - this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360; - //dprint(" turn:", vtos(this.v_angle)); - - makevectors(this.v_angle); - shotorg = this.origin + this.view_ofs; - shotdir = v_forward; - - //dprint(" dir:", vtos(v_forward)); - //te_lightning2(NULL, shotorg, shotorg + shotdir * 100); - - // calculate turn angles again - //diffang = desiredang - this.v_angle; - //diffang_y = diffang_y - floor(diffang_y / 360) * 360; - //if (diffang_y >= 180) - // diffang_y = diffang_y - 360; - - //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n"); - - // decide whether to fire this time - // note the maxfiredeviation is in degrees so this has to convert to radians first - //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) - if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) - if(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1)) - this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5); - //traceline(shotorg,shotorg+shotdir*1000,false,NULL); - //dprint(ftos(maxfiredeviation),"\n"); - //dprint(" diff:", vtos(diffang), "\n"); - - return this.bot_canfire && (time < this.bot_firetimer); -} - -vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay) -{ - // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast... - return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed); -} - -bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity) -{ - float f, r, hf, distanceratio; - vector v; - /* - eprint(this); - dprint("bot_aim(", ftos(shotspeed)); - dprint(", ", ftos(shotspeedupward)); - dprint(", ", ftos(maxshottime)); - dprint(", ", ftos(applygravity)); - dprint(");\n"); - */ - - hf = this.dphitcontentsmask; - this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - - shotspeed *= W_WeaponSpeedFactor(this); - shotspeedupward *= W_WeaponSpeedFactor(this); - if (!shotspeed) - { - LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!\n"); - shotspeed = 1000000; - } - if (!maxshottime) - { - LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!\n"); - maxshottime = 1; - } - makevectors(this.v_angle); - shotorg = this.origin + this.view_ofs; - shotdir = v_forward; - v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.bot_aimlatency); - distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees; - distanceratio = bound(0,distanceratio,1); - r = (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees) - * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees; - if (applygravity && this.bot_aimtarg) - { - if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this)) - { - this.dphitcontentsmask = hf; - return false; - } - - f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r); - } - else - { - f = bot_aimdir(this, v - shotorg, r); - //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n"); - //traceline(shotorg, shotorg + shotdir * 10000, false, this); - //if (trace_ent.takedamage) - //if (trace_fraction < 1) - //if (!bot_shouldattack(this, trace_ent)) - // return false; - traceline(shotorg, this.bot_aimtargorigin, false, this); - if (trace_fraction < 1) - if (trace_ent != this.enemy) - if (!bot_shouldattack(this, trace_ent)) - { - this.dphitcontentsmask = hf; - return false; - } - } - - //if (r > maxshottime * shotspeed) - // return false; - this.dphitcontentsmask = hf; - return true; -} diff --git a/qcsrc/server/bot/aim.qh b/qcsrc/server/bot/aim.qh deleted file mode 100644 index dfe10e2656..0000000000 --- a/qcsrc/server/bot/aim.qh +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once -/* - * Globals and Fields - */ - -entity tracetossent; -entity tracetossfaketarget; -vector findtrajectory_velocity; - - - -vector shotorg; -vector shotdir; - -// lag simulation -// upto 5 queued messages -.float lag1_time; -.float lag1_float1; -.float lag1_float2; -.entity lag1_entity1; -.vector lag1_vec1; -.vector lag1_vec2; -.vector lag1_vec3; -.vector lag1_vec4; - -.float lag2_time; -.float lag2_float1; -.float lag2_float2; -.entity lag2_entity1; -.vector lag2_vec1; -.vector lag2_vec2; -.vector lag2_vec3; -.vector lag2_vec4; - -.float lag3_time; -.float lag3_float1; -.float lag3_float2; -.entity lag3_entity1; -.vector lag3_vec1; -.vector lag3_vec2; -.vector lag3_vec3; -.vector lag3_vec4; - -.float lag4_time; -.float lag4_float1; -.float lag4_float2; -.entity lag4_entity1; -.vector lag4_vec1; -.vector lag4_vec2; -.vector lag4_vec3; -.vector lag4_vec4; - -.float lag5_time; -.float lag5_float1; -.float lag5_float2; -.entity lag5_entity1; -.vector lag5_vec1; -.vector lag5_vec2; -.vector lag5_vec3; -.vector lag5_vec4; - -.float bot_badaimtime; -.float bot_aimthinktime; -.float bot_prevaimtime; -.float bot_firetimer; -.float bot_aimlatency; - -.vector bot_mouseaim; -.vector bot_badaimoffset; -.vector bot_1st_order_aimfilter; -.vector bot_2nd_order_aimfilter; -.vector bot_3th_order_aimfilter; -.vector bot_4th_order_aimfilter; -.vector bot_5th_order_aimfilter; -.vector bot_olddesiredang; - -//.vector bot_aimorigin; -//.vector bot_aimvelocity; -.vector bot_aimtargorigin; -.vector bot_aimtargvelocity; - -.entity bot_aimtarg; - -/* - * Functions - */ - -float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4); -void lag_update(entity this); -void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4); - -float bot_shouldattack(entity this, entity targ); -float bot_aimdir(entity this, vector v, float maxfiredeviation); -bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity); -float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore); - -vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay); - -.void(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func; diff --git a/qcsrc/server/bot/api.qc b/qcsrc/server/bot/api.qc new file mode 100644 index 0000000000..274b034033 --- /dev/null +++ b/qcsrc/server/bot/api.qc @@ -0,0 +1 @@ +#include "api.qh" diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh new file mode 100644 index 0000000000..dbe3197a7c --- /dev/null +++ b/qcsrc/server/bot/api.qh @@ -0,0 +1,90 @@ +#pragma once + +#include + +const int WAYPOINTFLAG_GENERATED = BIT(23); +const int WAYPOINTFLAG_ITEM = BIT(22); +const int WAYPOINTFLAG_TELEPORT = BIT(21); +const int WAYPOINTFLAG_NORELINK = BIT(20); +const int WAYPOINTFLAG_PERSONAL = BIT(19); +const int WAYPOINTFLAG_PROTECTED = BIT(18); // Useless WP detection never kills these. +const int WAYPOINTFLAG_USEFUL = BIT(17); // Useless WP detection temporary flag. +const int WAYPOINTFLAG_DEAD_END = BIT(16); // Useless WP detection temporary flag. + +entity kh_worldkeylist; +.entity kh_worldkeynext; + +float bot_custom_weapon; +float bot_weapons_close[Weapons_MAX]; +float bot_weapons_far[Weapons_MAX]; +float bot_weapons_mid[Weapons_MAX]; +float skill; + +.float bot_attack; +.float bot_dodgerating; +.float bot_dodge; +.float bot_forced_team; +.float bot_moveskill; // moving technique +.float bot_pickup; +.float(entity player, entity item) bot_pickupevalfunc; +.float bot_strategytime; +.string cleanname; +.float havocbot_role_timeout; +.float isbot; // true if this client is actually a bot +.float lastteleporttime; +.float navigation_hasgoals; +.float nearestwaypointtimeout; +.entity nearestwaypoint; +.float speed; +.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15; +.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31; +.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost; +.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost; +.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost; +.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost; +.float wpconsidered; +.float wpcost; +.int wpflags; + +bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity); +void bot_clientconnect(entity this); +void bot_clientdisconnect(entity this); +void bot_cmdhelp(string scmd); +void bot_endgame(); +bool bot_fixcount(); +void bot_list_commands(); +void bot_queuecommand(entity bot, string cmdstring); +void bot_clear(entity this); +void bot_relinkplayerlist(); +void bot_resetqueues(); +void bot_serverframe(); +bool bot_shouldattack(entity this, entity e); +void bot_think(entity this); + +entity find_bot_by_name(string name); +entity find_bot_by_number(float number); + +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius); +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius); + +entity navigation_findnearestwaypoint(entity ent, float walkfromwp); +void navigation_goalrating_end(entity this); +void navigation_goalrating_start(entity this); +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); + +bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode); + +void waypoint_remove(entity e); +void waypoint_saveall(); +void waypoint_schedulerelinkall(); +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); +void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken); +entity waypoint_spawn(vector m1, vector m2, float f); + +.entity goalcurrent; +void navigation_clearroute(entity this); diff --git a/qcsrc/server/bot/bot.qc b/qcsrc/server/bot/bot.qc deleted file mode 100644 index f9afb39d8a..0000000000 --- a/qcsrc/server/bot/bot.qc +++ /dev/null @@ -1,712 +0,0 @@ -#include "bot.qh" - -#include "aim.qh" -#include "navigation.qh" -#include "scripting.qh" -#include "waypoints.qh" - -#include "havocbot/havocbot.qh" -#include "havocbot/scripting.qh" - -#include "../teamplay.qh" - -#include "../antilag.qh" -#include "../autocvars.qh" -#include "../campaign.qh" -#include "../cl_client.qh" -#include "../constants.qh" -#include "../defs.qh" -#include "../race.qh" -#include - -#include "../mutators/all.qh" - -#include "../weapons/accuracy.qh" - -#include -#include -#include -#include -#include - -#include - -#include - -#include -#include - -entity bot_spawn() -{ - entity bot = spawnclient(); - if (bot) - { - currentbots = currentbots + 1; - bot_setnameandstuff(bot); - WITHSELF(bot, ClientConnect()); - WITHSELF(bot, PutClientInServer()); - } - return bot; -} - -void bot_think(entity this) -{ - if (this.bot_nextthink > time) - return; - - this.flags &= ~FL_GODMODE; - if(autocvar_bot_god) - this.flags |= FL_GODMODE; - - this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill); - //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)) - { - this.bot_nextthink = time + 0.5; - return; - } - - if (this.fixangle) - { - this.v_angle = this.angles; - this.v_angle_z = 0; - this.fixangle = false; - } - - this.dmg_take = 0; - this.dmg_save = 0; - this.dmg_inflictor = NULL; - - // calculate an aiming latency based on the skill setting - // (simulated network latency + naturally delayed reflexes) - //this.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think') - // minimum ping 20+10 random - this.ping = bound(0,0.07 - bound(0, (skill + this.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server - // skill 10 = ping 0.2 (adrenaline) - // skill 0 = ping 0.7 (slightly drunk) - - // clear buttons - PHYS_INPUT_BUTTON_ATCK(this) = false; - PHYS_INPUT_BUTTON_JUMP(this) = false; - PHYS_INPUT_BUTTON_ATCK2(this) = false; - PHYS_INPUT_BUTTON_ZOOM(this) = false; - PHYS_INPUT_BUTTON_CROUCH(this) = false; - PHYS_INPUT_BUTTON_HOOK(this) = false; - PHYS_INPUT_BUTTON_INFO(this) = false; - PHYS_INPUT_BUTTON_DRAG(this) = false; - PHYS_INPUT_BUTTON_CHAT(this) = false; - PHYS_INPUT_BUTTON_USE(this) = false; - - if (time < game_starttime) - { - // block the bot during the countdown to game start - this.movement = '0 0 0'; - this.bot_nextthink = game_starttime; - return; - } - - // if dead, just wait until we can respawn - if (IS_DEAD(this)) - { - if (this.deadflag == DEAD_DEAD) - { - PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn - this.bot_strategytime = 0; - } - } - else if(this.aistatus & AI_STATUS_STUCK) - navigation_unstuck(this); - - // now call the current bot AI (havocbot for example) - this.bot_ai(this); -} - -void bot_setnameandstuff(entity this) -{ - string readfile, s; - float file, tokens, prio; - - string bot_name, bot_model, bot_skin, bot_shirt, bot_pants; - string name, prefix, suffix; - - if(autocvar_g_campaign) - { - prefix = ""; - suffix = ""; - } - else - { - prefix = autocvar_bot_prefix; - suffix = autocvar_bot_suffix; - } - - file = fopen(autocvar_bot_config_file, FILE_READ); - - if(file < 0) - { - LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n")); - readfile = ""; - } - else - { - RandomSelection_Init(); - while((readfile = fgets(file))) - { - if(substring(readfile, 0, 2) == "//") - continue; - if(substring(readfile, 0, 1) == "#") - continue; - tokens = tokenizebyseparator(readfile, "\t"); - if(tokens == 0) - continue; - s = argv(0); - prio = 1; - FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( - if(s == it.cleanname) - { - prio = 0; - break; - } - )); - RandomSelection_Add(NULL, 0, readfile, 1, prio); - } - readfile = RandomSelection_chosen_string; - fclose(file); - } - - tokens = tokenizebyseparator(readfile, "\t"); - if(argv(0) != "") bot_name = argv(0); - else bot_name = "Bot"; - - if(argv(1) != "") bot_model = argv(1); - else bot_model = ""; - - if(argv(2) != "") bot_skin = argv(2); - else bot_skin = "0"; - - if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3); - else bot_shirt = ftos(floor(random() * 15)); - - if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4); - else bot_pants = ftos(floor(random() * 15)); - - this.bot_forced_team = stof(argv(5)); - - prio = 6; - - #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio - //print(bot_name, ": ping=", argv(9), "\n"); - - READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill - READSKILL(bot_moveskill, 2, 0); // move skill - READSKILL(bot_dodgeskill, 2, 0); // dodge skill - - READSKILL(bot_pingskill, 0.5, 0); // ping skill - - READSKILL(bot_weaponskill, 2, 0); // weapon skill - READSKILL(bot_aggresskill, 1, 0); // aggre skill - READSKILL(bot_rangepreference, 1, 0); // read skill - - READSKILL(bot_aimskill, 2, 0); // aim skill - READSKILL(bot_offsetskill, 2, 0.5); // offset skill - READSKILL(bot_mouseskill, 1, 0.5); // mouse skill - - READSKILL(bot_thinkskill, 1, 0.5); // think skill - READSKILL(bot_aiskill, 2, 0); // "ai" skill - - this.bot_config_loaded = true; - - // this is really only a default, JoinBestTeam is called later - setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants)); - this.bot_preferredcolors = this.clientcolors; - - // pick the name - if (autocvar_bot_usemodelnames) - name = bot_model; - else - name = bot_name; - - // number bots with identical names - int j = 0; - FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( - 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 - if(substring(bot_model, -4, 1) != ".") - bot_model = strcat(bot_model, ".iqm"); - this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model)); - this.playerskin = this.playerskin_freeme = strzone(bot_skin); - - this.cvar_cl_accuracy_data_share = 1; // share the bots weapon accuracy data with the NULL - this.cvar_cl_accuracy_data_receive = 0; // don't receive any weapon accuracy data -} - -void bot_custom_weapon_priority_setup() -{ - float tokens, i, w; - - bot_custom_weapon = false; - - if( autocvar_bot_ai_custom_weapon_priority_far == "" || - autocvar_bot_ai_custom_weapon_priority_mid == "" || - autocvar_bot_ai_custom_weapon_priority_close == "" || - autocvar_bot_ai_custom_weapon_priority_distances == "" - ) - return; - - // Parse distances - tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," "); - - if (tokens!=2) - return; - - bot_distance_far = stof(argv(0)); - bot_distance_close = stof(argv(1)); - - if(bot_distance_far < bot_distance_close){ - bot_distance_far = stof(argv(1)); - bot_distance_close = stof(argv(0)); - } - - // Initialize list of weapons - bot_weapons_far[0] = -1; - bot_weapons_mid[0] = -1; - bot_weapons_close[0] = -1; - - // Parse far distance weapon priorities - tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," "); - - int c = 0; - for(i=0; i < tokens && c < Weapons_COUNT; ++i){ - w = stof(argv(i)); - if ( w >= WEP_FIRST && w <= WEP_LAST) { - bot_weapons_far[c] = w; - ++c; - } - } - if(c < Weapons_COUNT) - bot_weapons_far[c] = -1; - - // Parse mid distance weapon priorities - tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," "); - - c = 0; - for(i=0; i < tokens && c < Weapons_COUNT; ++i){ - w = stof(argv(i)); - if ( w >= WEP_FIRST && w <= WEP_LAST) { - bot_weapons_mid[c] = w; - ++c; - } - } - if(c < Weapons_COUNT) - bot_weapons_mid[c] = -1; - - // Parse close distance weapon priorities - tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," "); - - c = 0; - for(i=0; i < tokens && i < Weapons_COUNT; ++i){ - w = stof(argv(i)); - if ( w >= WEP_FIRST && w <= WEP_LAST) { - bot_weapons_close[c] = w; - ++c; - } - } - if(c < Weapons_COUNT) - bot_weapons_close[c] = -1; - - bot_custom_weapon = true; -} - -void bot_endgame() -{ - entity e; - //dprint("bot_endgame\n"); - e = bot_list; - while (e) - { - setcolor(e, e.bot_preferredcolors); - e = e.nextbot; - } - // if dynamic waypoints are ever implemented, save them here -} - -void bot_relinkplayerlist() -{ - entity e; - entity prevbot; - player_count = 0; - currentbots = 0; - player_list = e = findchainflags(flags, FL_CLIENT); - bot_list = NULL; - prevbot = NULL; - while (e) - { - player_count = player_count + 1; - e.nextplayer = e.chain; - if (IS_BOT_CLIENT(e)) - { - if (prevbot) - prevbot.nextbot = e; - else - { - bot_list = e; - bot_list.nextbot = NULL; - } - prevbot = e; - currentbots = currentbots + 1; - } - e = e.chain; - } - LOG_TRACE(strcat("relink: ", ftos(currentbots), " bots seen.\n")); - bot_strategytoken = bot_list; - bot_strategytoken_taken = true; -} - -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; - if(this.bot_cmd_current) - remove(this.bot_cmd_current); - if(bot_waypoint_queue_owner==this) - bot_waypoint_queue_owner = NULL; -} - -void bot_clientconnect(entity this) -{ - if (!IS_BOT_CLIENT(this)) return; - this.bot_preferredcolors = this.clientcolors; - this.bot_nextthink = time - random(); - this.lag_func = bot_lagfunc; - this.isbot = true; - this.createdtime = this.bot_nextthink; - - if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches - bot_setnameandstuff(this); - - if(this.bot_forced_team==1) - this.team = NUM_TEAM_1; - else if(this.bot_forced_team==2) - this.team = NUM_TEAM_2; - else if(this.bot_forced_team==3) - this.team = NUM_TEAM_3; - else if(this.bot_forced_team==4) - this.team = NUM_TEAM_4; - else - JoinBestTeam(this, false, true); - - havocbot_setupbot(this); -} - -void bot_removefromlargestteam() -{ - float besttime, bestcount, thiscount; - entity best, head; - CheckAllowedTeams(NULL); - GetTeamCounts(NULL); - head = findchainfloat(isbot, true); - if (!head) - return; - best = head; - besttime = head.createdtime; - bestcount = 0; - while (head) - { - if(head.team == NUM_TEAM_1) - thiscount = c1; - else if(head.team == NUM_TEAM_2) - thiscount = c2; - else if(head.team == NUM_TEAM_3) - thiscount = c3; - else if(head.team == NUM_TEAM_4) - thiscount = c4; - else - thiscount = 0; - if (thiscount > bestcount) - { - bestcount = thiscount; - besttime = head.createdtime; - best = head; - } - else if (thiscount == bestcount && besttime < head.createdtime) - { - besttime = head.createdtime; - best = head; - } - head = head.chain; - } - currentbots = currentbots - 1; - dropclient(best); -} - -void bot_removenewest() -{ - float besttime; - entity best, head; - - if(teamplay) - { - bot_removefromlargestteam(); - return; - } - - head = findchainfloat(isbot, true); - if (!head) - return; - best = head; - besttime = head.createdtime; - while (head) - { - if (besttime < head.createdtime) - { - besttime = head.createdtime; - best = head; - } - head = head.chain; - } - currentbots = currentbots - 1; - dropclient(best); -} - -void autoskill(float factor) -{ - float bestbot; - float bestplayer; - - bestbot = -1; - bestplayer = -1; - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if(IS_REAL_CLIENT(it)) - bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck); - else - bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck); - )); - - LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", "); - LOG_TRACE("best bot got ", ftos(bestbot), "; "); - if(bestbot < 0 || bestplayer < 0) - { - LOG_TRACE("not doing anything\n"); - // don't return, let it reset all counters below - } - else if(bestbot <= bestplayer * factor - 2) - { - if(autocvar_skill < 17) - { - LOG_TRACE("2 frags difference, increasing skill\n"); - cvar_set("skill", ftos(autocvar_skill + 1)); - bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n"); - } - } - else if(bestbot >= bestplayer * factor + 2) - { - if(autocvar_skill > 0) - { - LOG_TRACE("2 frags difference, decreasing skill\n"); - cvar_set("skill", ftos(autocvar_skill - 1)); - bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n"); - } - } - else - { - LOG_TRACE("not doing anything\n"); - return; - // don't reset counters, wait for them to accumulate - } - - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags)); -} - -void bot_calculate_stepheightvec() -{ - stepheightvec = autocvar_sv_stepheight * '0 0 1'; - jumpstepheightvec = stepheightvec + - ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85'; - // 0.75 factor is for safety to make the jumps easy -} - -float bot_fixcount() -{ - int activerealplayers = 0; - int realplayers = 0; - if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) { - activerealplayers = M_ARGV(0, int); - realplayers = M_ARGV(1, int); - } else { - FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA( - if(IS_PLAYER(it)) - ++activerealplayers; - ++realplayers; - )); - } - - 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; - if (teamplay && autocvar_bot_vs_human && (c3==-1 && c4==-1)) - 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)); - - float realminbots, minbots; - realminbots = autocvar_bot_number; - minbots = max(0, floor(realminbots)); - - bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers); - if(bots > minbots) - bots_would_leave = true; - } - else - { - // if there are no players, remove bots - bots = 0; - } - - // only add one bot per frame to avoid utter chaos - if(time > botframe_nextthink) - { - //dprint(ftos(bots), " ? ", ftos(currentbots), "\n"); - while (currentbots < bots) - { - if (bot_spawn() == NULL) - { - bprint("Can not add bot, server full.\n"); - return false; - } - } - while (currentbots > bots) - bot_removenewest(); - } - - return true; -} - -void bot_serverframe() -{ - if (intermission_running) - return; - - if (time < 2) - return; - - bot_calculate_stepheightvec(); - bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); - - if(time > autoskill_nextthink) - { - float a; - a = autocvar_skill_auto; - if(a) - autoskill(a); - autoskill_nextthink = time + 5; - } - - if(time > botframe_nextthink) - { - if(!bot_fixcount()) - botframe_nextthink = time + 10; - } - - bot_ignore_bots = autocvar_bot_ignore_bots; - - if(botframe_spawnedwaypoints) - { - if(autocvar_waypoint_benchmark) - localcmd("quit\n"); - } - - if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto) - if (botframe_spawnedwaypoints) - { - if(botframe_cachedwaypointlinks) - { - if(!botframe_loadedforcedlinks) - waypoint_load_links_hardwired(); - } - else - { - // TODO: Make this check cleaner - entity wp = findchain(classname, "waypoint"); - if(time - wp.nextthink > 10) - waypoint_save_links(); - } - } - else - { - botframe_spawnedwaypoints = true; - waypoint_loadall(); - if(!waypoint_load_links()) - waypoint_schedulerelinkall(); - } - - if (bot_list) - { - // cycle the goal token from one bot to the next each frame - // (this prevents them from all doing spawnfunc_waypoint searches on the same - // frame, which causes choppy framerates) - if (bot_strategytoken_taken) - { - bot_strategytoken_taken = false; - if (bot_strategytoken) - bot_strategytoken = bot_strategytoken.nextbot; - if (!bot_strategytoken) - bot_strategytoken = bot_list; - } - - if (botframe_nextdangertime < time) - { - float interval; - interval = autocvar_bot_ai_dangerdetectioninterval; - if (botframe_nextdangertime < time - interval * 1.5) - botframe_nextdangertime = time; - botframe_nextdangertime = botframe_nextdangertime + interval; - botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates); - } - } - - if (autocvar_g_waypointeditor) - botframe_showwaypointlinks(); - - if (autocvar_g_waypointeditor_auto) - botframe_autowaypoints(); - - if(time > bot_cvar_nextthink) - { - if(currentbots>0) - bot_custom_weapon_priority_setup(); - bot_cvar_nextthink = time + 5; - } -} diff --git a/qcsrc/server/bot/bot.qh b/qcsrc/server/bot/bot.qh deleted file mode 100644 index a28c94b7f7..0000000000 --- a/qcsrc/server/bot/bot.qh +++ /dev/null @@ -1,116 +0,0 @@ -#pragma once -/* - * Globals and Fields - */ - -const int AI_STATUS_ROAMING = BIT(0); // Bot is just crawling the map. No enemies at sight -const int AI_STATUS_ATTACKING = BIT(1); // There are enemies at sight -const int AI_STATUS_RUNNING = BIT(2); // Bot is bunny hopping -const int AI_STATUS_DANGER_AHEAD = BIT(3); // There is lava/slime/trigger_hurt ahead -const int AI_STATUS_OUT_JUMPPAD = BIT(4); // Trying to get out of a "vertical" jump pad -const int AI_STATUS_OUT_WATER = BIT(5); // Trying to get out of water -const int AI_STATUS_WAYPOINT_PERSONAL_LINKING = BIT(6); // Waiting for the personal waypoint to be linked -const int AI_STATUS_WAYPOINT_PERSONAL_GOING = BIT(7); // Going to a personal waypoint -const int AI_STATUS_WAYPOINT_PERSONAL_REACHED = BIT(8); // Personal waypoint reached -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 -.int aistatus; - -// Skill system -float skill; -float autoskill_nextthink; - -// havocbot_keyboardskill // keyboard movement -.float bot_moveskill; // moving technique -.float bot_dodgeskill; // dodging - -.float bot_pingskill; // ping offset - -.float bot_weaponskill; // weapon usage skill (combos, e.g.) -.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour -.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer") - -.float bot_aimskill; // aim accuracy -.float bot_offsetskill; // aim breakage -.float bot_mouseskill; // mouse "speed" - -.float bot_thinkskill; // target choice -.float bot_aiskill; // strategy choice - -.float totalfrags_lastcheck; - -// Custom weapon priorities -float bot_custom_weapon; -float bot_distance_far; -float bot_distance_close; - -float bot_weapons_far[Weapons_MAX]; -float bot_weapons_mid[Weapons_MAX]; -float bot_weapons_close[Weapons_MAX]; - -entity bot_list; -entity player_list; -.entity nextbot; -.entity nextplayer; -.string cleanname; -.string netname_freeme; -.string playermodel_freeme; -.string playerskin_freeme; - -.float bot_nextthink; - -.float createdtime; -.float bot_preferredcolors; -.float bot_attack; -.float bot_dodge; -.float bot_dodgerating; - -.float bot_pickup; -.float bot_pickupbasevalue; -.float bot_canfire; -.float bot_strategytime; - -.float bot_forced_team; -.float bot_config_loaded; - -float bot_strategytoken_taken; -entity bot_strategytoken; - -float botframe_spawnedwaypoints; -float botframe_nextthink; -float botframe_nextdangertime; -float bot_cvar_nextthink; -float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay) - -/* - * Functions - */ - -entity bot_spawn(); -float bot_fixcount(); - -void bot_think(entity this); -void bot_setnameandstuff(entity this); -void bot_custom_weapon_priority_setup(); -void bot_endgame(); -void bot_relinkplayerlist(); -void bot_clientdisconnect(entity this); -void bot_clientconnect(entity this); -void bot_removefromlargestteam(); -void bot_removenewest(); -void autoskill(float factor); -void bot_serverframe(); - -.void(entity this) bot_ai; -.float(entity player, entity item) bot_pickupevalfunc; - -/* - * Imports - */ - -void(entity this) havocbot_setupbot; - -void bot_calculate_stepheightvec(); diff --git a/qcsrc/server/bot/default/_mod.inc b/qcsrc/server/bot/default/_mod.inc new file mode 100644 index 0000000000..03fb7e0f5b --- /dev/null +++ b/qcsrc/server/bot/default/_mod.inc @@ -0,0 +1,9 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include + +#include diff --git a/qcsrc/server/bot/default/_mod.qh b/qcsrc/server/bot/default/_mod.qh new file mode 100644 index 0000000000..04896279bf --- /dev/null +++ b/qcsrc/server/bot/default/_mod.qh @@ -0,0 +1,9 @@ +// generated file; do not modify +#include +#include +#include +#include +#include +#include + +#include diff --git a/qcsrc/server/bot/default/aim.qc b/qcsrc/server/bot/default/aim.qc new file mode 100644 index 0000000000..1624676b82 --- /dev/null +++ b/qcsrc/server/bot/default/aim.qc @@ -0,0 +1,395 @@ +#include "aim.qh" + +#include "cvars.qh" + +#include "bot.qh" + +#include +#include + +#include "../../weapons/weaponsystem.qh" + +#include "../../mutators/_mod.qh" + +// traces multiple trajectories to find one that will impact the target +// 'end' vector is the place it aims for, +// returns true only if it hit targ (don't target non-solid entities) + +float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore) +{ + float c, savesolid, shottime; + vector dir, end, v, o; + if (shotspeed < 1) + return false; // could cause division by zero if calculated + if (targ.solid < SOLID_BBOX) // SOLID_NOT and SOLID_TRIGGER + return false; // could never hit it + if (!tracetossent) + tracetossent = new(tracetossent); + tracetossent.owner = ignore; + setsize(tracetossent, m1, m2); + savesolid = targ.solid; + targ.solid = SOLID_NOT; + o = (targ.absmin + targ.absmax) * 0.5; + shottime = ((vlen(o - org) / shotspeed) + shotdelay); + v = targ.velocity * shottime + o; + tracebox(o, targ.mins, targ.maxs, v, false, targ); + v = trace_endpos; + end = v + (targ.mins + targ.maxs) * 0.5; + if ((vlen(end - org) / shotspeed + 0.2) > maxtime) + { + // out of range + targ.solid = savesolid; + return false; + } + + if (!tracetossfaketarget) + tracetossfaketarget = new(tracetossfaketarget); + tracetossfaketarget.solid = savesolid; + set_movetype(tracetossfaketarget, targ.move_movetype); + _setmodel(tracetossfaketarget, targ.model); // no low precision + tracetossfaketarget.model = targ.model; + tracetossfaketarget.modelindex = targ.modelindex; + setsize(tracetossfaketarget, targ.mins, targ.maxs); + setorigin(tracetossfaketarget, v); + + c = 0; + dir = normalize(end - org); + while (c < 10) // 10 traces + { + setorigin(tracetossent, org); // reset + tracetossent.velocity = findtrajectory_velocity = normalize(dir) * shotspeed + shotspeedupward * '0 0 1'; + tracetoss(tracetossent, ignore); // love builtin functions... + if (trace_ent == tracetossfaketarget) // done + { + targ.solid = savesolid; + + // make it disappear + tracetossfaketarget.solid = SOLID_NOT; + set_movetype(tracetossfaketarget, MOVETYPE_NONE); + tracetossfaketarget.model = ""; + tracetossfaketarget.modelindex = 0; + // relink to remove it from physics considerations + setorigin(tracetossfaketarget, v); + + return true; + } + dir.z = dir.z + 0.1; // aim up a little more + c = c + 1; + } + targ.solid = savesolid; + + // make it disappear + tracetossfaketarget.solid = SOLID_NOT; + set_movetype(tracetossfaketarget, MOVETYPE_NONE); + tracetossfaketarget.model = ""; + tracetossfaketarget.modelindex = 0; + // relink to remove it from physics considerations + setorigin(tracetossfaketarget, v); + + // leave a valid one even if it won't reach + findtrajectory_velocity = normalize(end - org) * shotspeed + shotspeedupward * '0 0 1'; + return false; +} + +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;} +} + +float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) +{ + if (this.lag1_time == 0) {this.lag1_time = t;this.lag1_float1 = f1;this.lag1_float2 = f2;this.lag1_entity1 = e1;this.lag1_vec1 = v1;this.lag1_vec2 = v2;this.lag1_vec3 = v3;this.lag1_vec4 = v4;return true;} + if (this.lag2_time == 0) {this.lag2_time = t;this.lag2_float1 = f1;this.lag2_float2 = f2;this.lag2_entity1 = e1;this.lag2_vec1 = v1;this.lag2_vec2 = v2;this.lag2_vec3 = v3;this.lag2_vec4 = v4;return true;} + if (this.lag3_time == 0) {this.lag3_time = t;this.lag3_float1 = f1;this.lag3_float2 = f2;this.lag3_entity1 = e1;this.lag3_vec1 = v1;this.lag3_vec2 = v2;this.lag3_vec3 = v3;this.lag3_vec4 = v4;return true;} + if (this.lag4_time == 0) {this.lag4_time = t;this.lag4_float1 = f1;this.lag4_float2 = f2;this.lag4_entity1 = e1;this.lag4_vec1 = v1;this.lag4_vec2 = v2;this.lag4_vec3 = v3;this.lag4_vec4 = v4;return true;} + if (this.lag5_time == 0) {this.lag5_time = t;this.lag5_float1 = f1;this.lag5_float2 = f2;this.lag5_entity1 = e1;this.lag5_vec1 = v1;this.lag5_vec2 = v2;this.lag5_vec3 = v3;this.lag5_vec4 = v4;return true;} + // no room for it (what is the best thing to do here??) + return false; +} + +bool bot_shouldattack(entity this, entity targ) +{ + if (targ.team == this.team) + { + if (targ == this) + return false; + if (teamplay) + if (targ.team != 0) + return false; + } + + if(STAT(FROZEN, targ)) + return false; + + if(teamplay) + { + if(targ.team==0) + return false; + } + else if(bot_ignore_bots) + if(IS_BOT_CLIENT(targ)) + return false; + + if (!targ.takedamage) + return false; + if (IS_DEAD(targ)) + return false; + if (PHYS_INPUT_BUTTON_CHAT(targ)) + return false; + if(targ.flags & FL_NOTARGET) + return false; + + if(MUTATOR_CALLHOOK(BotShouldAttack, this, targ)) + return false; + + return true; +} + +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 = this.ping; // FIXME? Shouldn't this be in the lag item? + //this.bot_aimorigin = v1; + //this.bot_aimvelocity = v2; + this.bot_aimtargorigin = v3; + this.bot_aimtargvelocity = v4; + if(skill <= 0) + this.bot_canfire = (random() < 0.8); + else if(skill <= 1) + this.bot_canfire = (random() < 0.9); + else if(skill <= 2) + this.bot_canfire = (random() < 0.95); + else + this.bot_canfire = 1; +} + +float bot_aimdir(entity this, vector v, float maxfiredeviation) +{ + float dist, delta_t, blend; + vector desiredang, diffang; + + //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; + this.v_angle_z = 0; + + // get the desired angles to aim at + //dprint(" at:", vtos(v)); + v = normalize(v); + //te_lightning2(NULL, this.origin + this.view_ofs, this.origin + this.view_ofs + v * 200); + if (time >= this.bot_badaimtime) + { + this.bot_badaimtime = max(this.bot_badaimtime + 0.3, time); + this.bot_badaimoffset = randomvec() * bound(0, 5 - 0.5 * (skill+this.bot_offsetskill), 5) * autocvar_bot_ai_aimskill_offset; + } + desiredang = vectoangles(v) + this.bot_badaimoffset; + //dprint(" desired:", vtos(desiredang)); + if (desiredang.x >= 180) + desiredang.x = desiredang.x - 360; + desiredang.x = bound(-90, 0 - desiredang.x, 90); + desiredang.z = this.v_angle.z; + //dprint(" / ", vtos(desiredang)); + + //// pain throws off aim + //if (this.bot_painintensity) + //{ + // // shake from pain + // desiredang = desiredang + randomvec() * this.bot_painintensity * 0.2; + //} + + // calculate turn angles + diffang = (desiredang - this.bot_olddesiredang); + // wrap yaw turn + diffang.y = diffang.y - floor(diffang.y / 360) * 360; + if (diffang.y >= 180) + diffang.y = diffang.y - 360; + this.bot_olddesiredang = desiredang; + //dprint(" diff:", vtos(diffang)); + + delta_t = time-this.bot_prevaimtime; + this.bot_prevaimtime = time; + // Here we will try to anticipate the comming aiming direction + this.bot_1st_order_aimfilter= this.bot_1st_order_aimfilter + + (diffang * (1 / delta_t) - this.bot_1st_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_1st,1); + this.bot_2nd_order_aimfilter= this.bot_2nd_order_aimfilter + + (this.bot_1st_order_aimfilter - this.bot_2nd_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_2nd,1); + this.bot_3th_order_aimfilter= this.bot_3th_order_aimfilter + + (this.bot_2nd_order_aimfilter - this.bot_3th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_3th,1); + this.bot_4th_order_aimfilter= this.bot_4th_order_aimfilter + + (this.bot_3th_order_aimfilter - this.bot_4th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_4th,1); + this.bot_5th_order_aimfilter= this.bot_5th_order_aimfilter + + (this.bot_4th_order_aimfilter - this.bot_5th_order_aimfilter) * bound(0, autocvar_bot_ai_aimskill_order_filter_5th,1); + + //blend = (bound(0,skill,10)*0.1)*pow(1-bound(0,skill,10)*0.05,2.5)*5.656854249; //Plot formule before changing ! + blend = bound(0,skill+this.bot_aimskill,10)*0.1; + desiredang = desiredang + blend * + ( + this.bot_1st_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_1st + + this.bot_2nd_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_2nd + + this.bot_3th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_3th + + this.bot_4th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_4th + + this.bot_5th_order_aimfilter * autocvar_bot_ai_aimskill_order_mix_5th + ); + + // calculate turn angles + diffang = desiredang - this.bot_mouseaim; + // wrap yaw turn + diffang.y = diffang.y - floor(diffang.y / 360) * 360; + if (diffang.y >= 180) + diffang.y = diffang.y - 360; + //dprint(" diff:", vtos(diffang)); + + if (time >= this.bot_aimthinktime) + { + this.bot_aimthinktime = max(this.bot_aimthinktime + 0.5 - 0.05*(skill+this.bot_thinkskill), time); + this.bot_mouseaim = this.bot_mouseaim + diffang * (1-random()*0.1*bound(1,10-(skill+this.bot_thinkskill),10)); + } + + //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1); + + diffang = this.bot_mouseaim - desiredang; + // wrap yaw turn + diffang.y = diffang.y - floor(diffang.y / 360) * 360; + if (diffang.y >= 180) + diffang.y = diffang.y - 360; + desiredang = desiredang + diffang * bound(0,autocvar_bot_ai_aimskill_think,1); + + // calculate turn angles + diffang = desiredang - this.v_angle; + // wrap yaw turn + diffang.y = diffang.y - floor(diffang.y / 360) * 360; + if (diffang.y >= 180) + diffang.y = diffang.y - 360; + //dprint(" diff:", vtos(diffang)); + + // jitter tracking + dist = vlen(diffang); + //diffang = diffang + randomvec() * (dist * 0.05 * (3.5 - bound(0, skill, 3))); + + // turn + float r, fixedrate, blendrate; + fixedrate = autocvar_bot_ai_aimskill_fixedrate / bound(1,dist,1000); + blendrate = autocvar_bot_ai_aimskill_blendrate; + r = max(fixedrate, blendrate); + //this.v_angle = this.v_angle + diffang * bound(frametime, r * frametime * (2+skill*skill*0.05-random()*0.05*(10-skill)), 1); + this.v_angle = this.v_angle + diffang * bound(delta_t, r * delta_t * (2+pow(skill+this.bot_mouseskill,3)*0.005-random()), 1); + this.v_angle = this.v_angle * bound(0,autocvar_bot_ai_aimskill_mouse,1) + desiredang * bound(0,(1-autocvar_bot_ai_aimskill_mouse),1); + //this.v_angle = this.v_angle + diffang * bound(0, r * frametime * (skill * 0.5 + 2), 1); + //this.v_angle = this.v_angle + diffang * (1/ blendrate); + this.v_angle_z = 0; + this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360; + //dprint(" turn:", vtos(this.v_angle)); + + makevectors(this.v_angle); + shotorg = this.origin + this.view_ofs; + shotdir = v_forward; + + //dprint(" dir:", vtos(v_forward)); + //te_lightning2(NULL, shotorg, shotorg + shotdir * 100); + + // calculate turn angles again + //diffang = desiredang - this.v_angle; + //diffang_y = diffang_y - floor(diffang_y / 360) * 360; + //if (diffang_y >= 180) + // diffang_y = diffang_y - 360; + + //dprint("e ", vtos(diffang), " < ", ftos(maxfiredeviation), "\n"); + + // decide whether to fire this time + // note the maxfiredeviation is in degrees so this has to convert to radians first + //if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) + if ((normalize(v) * shotdir) >= cos(maxfiredeviation * (3.14159265358979323846 / 180))) + if(vdist(trace_endpos-shotorg, <, 500 + 500 * bound(0, skill + this.bot_aggresskill, 10)) || random()*random()>bound(0,(skill+this.bot_aggresskill)*0.05,1)) + this.bot_firetimer = time + bound(0.1, 0.5-(skill+this.bot_aggresskill)*0.05, 0.5); + //traceline(shotorg,shotorg+shotdir*1000,false,NULL); + //dprint(ftos(maxfiredeviation),"\n"); + //dprint(" diff:", vtos(diffang), "\n"); + + return this.bot_canfire && (time < this.bot_firetimer); +} + +vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay) +{ + // Try to add code here that predicts gravity effect here, no clue HOW to though ... well not yet atleast... + return targorigin + targvelocity * (shotdelay + vlen(targorigin - shotorg) / shotspeed); +} + +bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity) +{ + float f, r, hf, distanceratio; + vector v; + /* + eprint(this); + dprint("bot_aim(", ftos(shotspeed)); + dprint(", ", ftos(shotspeedupward)); + dprint(", ", ftos(maxshottime)); + dprint(", ", ftos(applygravity)); + dprint(");\n"); + */ + + hf = this.dphitcontentsmask; + this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + + shotspeed *= W_WeaponSpeedFactor(this); + shotspeedupward *= W_WeaponSpeedFactor(this); + if (!shotspeed) + { + LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " shotspeed is zero!"); + shotspeed = 1000000; + } + if (!maxshottime) + { + LOG_TRACE("bot_aim: WARNING: weapon ", PS(this).m_weapon.m_name, " maxshottime is zero!"); + maxshottime = 1; + } + makevectors(this.v_angle); + shotorg = this.origin + this.view_ofs; + shotdir = v_forward; + v = bot_shotlead(this.bot_aimtargorigin, this.bot_aimtargvelocity, shotspeed, this.bot_aimlatency); + distanceratio = sqrt(bound(0,skill,10000))*0.3*(vlen(v-shotorg)-100)/autocvar_bot_ai_aimskill_firetolerance_distdegrees; + distanceratio = bound(0,distanceratio,1); + r = (autocvar_bot_ai_aimskill_firetolerance_maxdegrees-autocvar_bot_ai_aimskill_firetolerance_mindegrees) + * (1-distanceratio) + autocvar_bot_ai_aimskill_firetolerance_mindegrees; + if (applygravity && this.bot_aimtarg) + { + if (!findtrajectorywithleading(shotorg, '0 0 0', '0 0 0', this.bot_aimtarg, shotspeed, shotspeedupward, maxshottime, 0, this)) + { + this.dphitcontentsmask = hf; + return false; + } + + f = bot_aimdir(this, findtrajectory_velocity - shotspeedupward * '0 0 1', r); + } + else + { + f = bot_aimdir(this, v - shotorg, r); + //dprint("AIM: ");dprint(vtos(this.bot_aimtargorigin));dprint(" + ");dprint(vtos(this.bot_aimtargvelocity));dprint(" * ");dprint(ftos(this.bot_aimlatency + vlen(this.bot_aimtargorigin - shotorg) / shotspeed));dprint(" = ");dprint(vtos(v));dprint(" : aimdir = ");dprint(vtos(normalize(v - shotorg)));dprint(" : ");dprint(vtos(shotdir));dprint("\n"); + //traceline(shotorg, shotorg + shotdir * 10000, false, this); + //if (trace_ent.takedamage) + //if (trace_fraction < 1) + //if (!bot_shouldattack(this, trace_ent)) + // return false; + traceline(shotorg, this.bot_aimtargorigin, false, this); + if (trace_fraction < 1) + if (trace_ent != this.enemy) + if (!bot_shouldattack(this, trace_ent)) + { + this.dphitcontentsmask = hf; + return false; + } + } + + //if (r > maxshottime * shotspeed) + // return false; + this.dphitcontentsmask = hf; + return true; +} diff --git a/qcsrc/server/bot/default/aim.qh b/qcsrc/server/bot/default/aim.qh new file mode 100644 index 0000000000..dfe10e2656 --- /dev/null +++ b/qcsrc/server/bot/default/aim.qh @@ -0,0 +1,99 @@ +#pragma once +/* + * Globals and Fields + */ + +entity tracetossent; +entity tracetossfaketarget; +vector findtrajectory_velocity; + + + +vector shotorg; +vector shotdir; + +// lag simulation +// upto 5 queued messages +.float lag1_time; +.float lag1_float1; +.float lag1_float2; +.entity lag1_entity1; +.vector lag1_vec1; +.vector lag1_vec2; +.vector lag1_vec3; +.vector lag1_vec4; + +.float lag2_time; +.float lag2_float1; +.float lag2_float2; +.entity lag2_entity1; +.vector lag2_vec1; +.vector lag2_vec2; +.vector lag2_vec3; +.vector lag2_vec4; + +.float lag3_time; +.float lag3_float1; +.float lag3_float2; +.entity lag3_entity1; +.vector lag3_vec1; +.vector lag3_vec2; +.vector lag3_vec3; +.vector lag3_vec4; + +.float lag4_time; +.float lag4_float1; +.float lag4_float2; +.entity lag4_entity1; +.vector lag4_vec1; +.vector lag4_vec2; +.vector lag4_vec3; +.vector lag4_vec4; + +.float lag5_time; +.float lag5_float1; +.float lag5_float2; +.entity lag5_entity1; +.vector lag5_vec1; +.vector lag5_vec2; +.vector lag5_vec3; +.vector lag5_vec4; + +.float bot_badaimtime; +.float bot_aimthinktime; +.float bot_prevaimtime; +.float bot_firetimer; +.float bot_aimlatency; + +.vector bot_mouseaim; +.vector bot_badaimoffset; +.vector bot_1st_order_aimfilter; +.vector bot_2nd_order_aimfilter; +.vector bot_3th_order_aimfilter; +.vector bot_4th_order_aimfilter; +.vector bot_5th_order_aimfilter; +.vector bot_olddesiredang; + +//.vector bot_aimorigin; +//.vector bot_aimvelocity; +.vector bot_aimtargorigin; +.vector bot_aimtargvelocity; + +.entity bot_aimtarg; + +/* + * Functions + */ + +float lag_additem(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4); +void lag_update(entity this); +void bot_lagfunc(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4); + +float bot_shouldattack(entity this, entity targ); +float bot_aimdir(entity this, vector v, float maxfiredeviation); +bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, bool applygravity); +float findtrajectorywithleading(vector org, vector m1, vector m2, entity targ, float shotspeed, float shotspeedupward, float maxtime, float shotdelay, entity ignore); + +vector bot_shotlead(vector targorigin, vector targvelocity, float shotspeed, float shotdelay); + +.void(entity this, float t, float f1, float f2, entity e1, vector v1, vector v2, vector v3, vector v4) lag_func; diff --git a/qcsrc/server/bot/default/bot.qc b/qcsrc/server/bot/default/bot.qc new file mode 100644 index 0000000000..fb6f1e046d --- /dev/null +++ b/qcsrc/server/bot/default/bot.qc @@ -0,0 +1,766 @@ +#include "bot.qh" + +#include "cvars.qh" + +#include "aim.qh" +#include "navigation.qh" +#include "scripting.qh" +#include "waypoints.qh" + +#include "havocbot/havocbot.qh" +#include "havocbot/scripting.qh" + +#include "../../teamplay.qh" + +#include "../../antilag.qh" +#include "../../autocvars.qh" +#include "../../campaign.qh" +#include "../../client.qh" +#include "../../constants.qh" +#include "../../defs.qh" +#include "../../race.qh" +#include + +#include "../../mutators/_mod.qh" + +#include "../../weapons/accuracy.qh" + +#include +#include +#include +#include +#include +#include + +#include + +#include + +#include + +#include +#include + +entity bot_spawn() +{ + entity bot = spawnclient(); + if (bot) + { + currentbots = currentbots + 1; + bot_setnameandstuff(bot); + ClientConnect(bot); + PutClientInServer(bot); + } + return bot; +} + +void bot_think(entity this) +{ + if (this.bot_nextthink > time) + return; + + this.flags &= ~FL_GODMODE; + if(autocvar_bot_god) + this.flags |= FL_GODMODE; + + this.bot_nextthink = this.bot_nextthink + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill); + if(this.bot_nextthink < time) + this.bot_nextthink = time + autocvar_bot_ai_thinkinterval * pow(0.5, this.bot_aiskill); + //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)) + { + this.movement = '0 0 0'; + this.bot_nextthink = time + 0.5; + return; + } + + if (this.fixangle) + { + this.v_angle = this.angles; + this.v_angle_z = 0; + this.fixangle = false; + } + + this.dmg_take = 0; + this.dmg_save = 0; + this.dmg_inflictor = NULL; + + // calculate an aiming latency based on the skill setting + // (simulated network latency + naturally delayed reflexes) + //this.ping = 0.7 - bound(0, 0.05 * skill, 0.5); // moved the reflexes to bot_aimdir (under the name 'think') + // minimum ping 20+10 random + this.ping = bound(0,0.07 - bound(0, (skill + this.bot_pingskill) * 0.005,0.05)+random()*0.01,0.65); // Now holds real lag to server, and higer skill players take a less laggy server + // skill 10 = ping 0.2 (adrenaline) + // skill 0 = ping 0.7 (slightly drunk) + + // clear buttons + PHYS_INPUT_BUTTON_ATCK(this) = false; + PHYS_INPUT_BUTTON_JUMP(this) = false; + PHYS_INPUT_BUTTON_ATCK2(this) = false; + PHYS_INPUT_BUTTON_ZOOM(this) = false; + PHYS_INPUT_BUTTON_CROUCH(this) = false; + PHYS_INPUT_BUTTON_HOOK(this) = false; + PHYS_INPUT_BUTTON_INFO(this) = false; + PHYS_INPUT_BUTTON_DRAG(this) = false; + PHYS_INPUT_BUTTON_CHAT(this) = false; + PHYS_INPUT_BUTTON_USE(this) = false; + + if (time < game_starttime) + { + // block the bot during the countdown to game start + this.movement = '0 0 0'; + this.bot_nextthink = game_starttime; + return; + } + + // if dead, just wait until we can respawn + if (IS_DEAD(this)) + { + this.movement = '0 0 0'; + if (this.deadflag == DEAD_DEAD) + { + PHYS_INPUT_BUTTON_JUMP(this) = true; // press jump to respawn + this.bot_strategytime = 0; + } + } + else if(this.aistatus & AI_STATUS_STUCK) + navigation_unstuck(this); + + // now call the current bot AI (havocbot for example) + this.bot_ai(this); +} + +void bot_setnameandstuff(entity this) +{ + string readfile, s; + float file, tokens, prio; + + string bot_name, bot_model, bot_skin, bot_shirt, bot_pants; + string name, prefix, suffix; + + if(autocvar_g_campaign) + { + prefix = ""; + suffix = ""; + } + else + { + prefix = autocvar_bot_prefix; + suffix = autocvar_bot_suffix; + } + + file = fopen(autocvar_bot_config_file, FILE_READ); + + if(file < 0) + { + LOG_INFO(strcat("Error: Can not open the bot configuration file '",autocvar_bot_config_file,"'\n")); + readfile = ""; + } + else + { + RandomSelection_Init(); + while((readfile = fgets(file))) + { + if(substring(readfile, 0, 2) == "//") + continue; + if(substring(readfile, 0, 1) == "#") + continue; + tokens = tokenizebyseparator(readfile, "\t"); + if(tokens == 0) + continue; + s = argv(0); + prio = 1; + FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( + if(s == it.cleanname) + { + prio = 0; + break; + } + )); + RandomSelection_AddString(readfile, 1, prio); + } + readfile = RandomSelection_chosen_string; + fclose(file); + } + + tokens = tokenizebyseparator(readfile, "\t"); + if(argv(0) != "") bot_name = argv(0); + else bot_name = "Bot"; + + if(argv(1) != "") bot_model = argv(1); + else bot_model = ""; + + if(argv(2) != "") bot_skin = argv(2); + else bot_skin = "0"; + + if(argv(3) != "" && stof(argv(3)) >= 0) bot_shirt = argv(3); + else bot_shirt = ftos(floor(random() * 15)); + + if(argv(4) != "" && stof(argv(4)) >= 0) bot_pants = argv(4); + else bot_pants = ftos(floor(random() * 15)); + + this.bot_forced_team = stof(argv(5)); + + prio = 6; + + #define READSKILL(f,w,r) if(argv(prio) != "") this.f = stof(argv(prio)) * (w); else this.f = (!autocvar_g_campaign) * (2 * random() - 1) * (r) * (w); ++prio + //print(bot_name, ": ping=", argv(9), "\n"); + + READSKILL(havocbot_keyboardskill, 0.5, 0.5); // keyboard skill + READSKILL(bot_moveskill, 2, 0); // move skill + READSKILL(bot_dodgeskill, 2, 0); // dodge skill + + READSKILL(bot_pingskill, 0.5, 0); // ping skill + + READSKILL(bot_weaponskill, 2, 0); // weapon skill + READSKILL(bot_aggresskill, 1, 0); // aggre skill + READSKILL(bot_rangepreference, 1, 0); // read skill + + READSKILL(bot_aimskill, 2, 0); // aim skill + READSKILL(bot_offsetskill, 2, 0.5); // offset skill + READSKILL(bot_mouseskill, 1, 0.5); // mouse skill + + READSKILL(bot_thinkskill, 1, 0.5); // think skill + READSKILL(bot_aiskill, 2, 0); // "ai" skill + + this.bot_config_loaded = true; + + // this is really only a default, JoinBestTeam is called later + setcolor(this, stof(bot_shirt) * 16 + stof(bot_pants)); + this.bot_preferredcolors = this.clientcolors; + + // pick the name + if (autocvar_bot_usemodelnames) + name = bot_model; + else + name = bot_name; + + // number bots with identical names + int j = 0; + FOREACH_CLIENT(IS_BOT_CLIENT(it), LAMBDA( + 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 + if(substring(bot_model, -4, 1) != ".") + bot_model = strcat(bot_model, ".iqm"); + this.playermodel = this.playermodel_freeme = strzone(strcat("models/player/", bot_model)); + this.playerskin = this.playerskin_freeme = strzone(bot_skin); + + this.cvar_cl_accuracy_data_share = 1; // share the bots weapon accuracy data with the NULL + this.cvar_cl_accuracy_data_receive = 0; // don't receive any weapon accuracy data +} + +void bot_custom_weapon_priority_setup() +{ + float tokens, i, w; + + bot_custom_weapon = false; + + if( autocvar_bot_ai_custom_weapon_priority_far == "" || + autocvar_bot_ai_custom_weapon_priority_mid == "" || + autocvar_bot_ai_custom_weapon_priority_close == "" || + autocvar_bot_ai_custom_weapon_priority_distances == "" + ) + return; + + // Parse distances + tokens = tokenizebyseparator(autocvar_bot_ai_custom_weapon_priority_distances," "); + + if (tokens!=2) + return; + + bot_distance_far = stof(argv(0)); + bot_distance_close = stof(argv(1)); + + if(bot_distance_far < bot_distance_close){ + bot_distance_far = stof(argv(1)); + bot_distance_close = stof(argv(0)); + } + + // Initialize list of weapons + bot_weapons_far[0] = -1; + bot_weapons_mid[0] = -1; + bot_weapons_close[0] = -1; + + // Parse far distance weapon priorities + tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_far)," "); + + int c = 0; + for(i=0; i < tokens && c < Weapons_COUNT; ++i){ + w = stof(argv(i)); + if ( w >= WEP_FIRST && w <= WEP_LAST) { + bot_weapons_far[c] = w; + ++c; + } + } + if(c < Weapons_COUNT) + bot_weapons_far[c] = -1; + + // Parse mid distance weapon priorities + tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_mid)," "); + + c = 0; + for(i=0; i < tokens && c < Weapons_COUNT; ++i){ + w = stof(argv(i)); + if ( w >= WEP_FIRST && w <= WEP_LAST) { + bot_weapons_mid[c] = w; + ++c; + } + } + if(c < Weapons_COUNT) + bot_weapons_mid[c] = -1; + + // Parse close distance weapon priorities + tokens = tokenizebyseparator(W_NumberWeaponOrder(autocvar_bot_ai_custom_weapon_priority_close)," "); + + c = 0; + for(i=0; i < tokens && i < Weapons_COUNT; ++i){ + w = stof(argv(i)); + if ( w >= WEP_FIRST && w <= WEP_LAST) { + bot_weapons_close[c] = w; + ++c; + } + } + if(c < Weapons_COUNT) + bot_weapons_close[c] = -1; + + bot_custom_weapon = true; +} + +void bot_endgame() +{ + bot_relinkplayerlist(); + entity e = bot_list; + while (e) + { + setcolor(e, e.bot_preferredcolors); + e = e.nextbot; + } + // if dynamic waypoints are ever implemented, save them here +} + +void bot_relinkplayerlist() +{ + player_count = 0; + currentbots = 0; + bot_list = NULL; + + entity prevbot = NULL; + FOREACH_CLIENT(true, + { + ++player_count; + + if(IS_BOT_CLIENT(it)) + { + if(prevbot) + prevbot.nextbot = it; + else + { + bot_list = it; + bot_list.nextbot = NULL; + } + prevbot = it; + ++currentbots; + } + }); + LOG_TRACE("relink: ", ftos(currentbots), " bots seen."); + bot_strategytoken = bot_list; + bot_strategytoken_taken = true; +} + +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; + if(this.bot_cmd_current) + delete(this.bot_cmd_current); + if(bot_waypoint_queue_owner == this) + bot_waypoint_queue_owner = NULL; +} + +void bot_clientconnect(entity this) +{ + if (!IS_BOT_CLIENT(this)) return; + this.bot_preferredcolors = this.clientcolors; + this.bot_nextthink = time - random(); + this.lag_func = bot_lagfunc; + this.isbot = true; + this.createdtime = this.bot_nextthink; + + if(!this.bot_config_loaded) // This is needed so team overrider doesn't break between matches + bot_setnameandstuff(this); + + if(this.bot_forced_team==1) + this.team = NUM_TEAM_1; + else if(this.bot_forced_team==2) + this.team = NUM_TEAM_2; + else if(this.bot_forced_team==3) + this.team = NUM_TEAM_3; + else if(this.bot_forced_team==4) + this.team = NUM_TEAM_4; + else + JoinBestTeam(this, false, true); + + havocbot_setupbot(this); +} + +void bot_removefromlargestteam() +{ + CheckAllowedTeams(NULL); + GetTeamCounts(NULL); + + entity best = NULL; + float besttime = 0; + int bestcount = 0; + + int bcount = 0; + FOREACH_CLIENT(it.isbot, + { + ++bcount; + + if(!best) + { + best = it; + besttime = it.createdtime; + } + + int thiscount = 0; + + switch(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; + } + + if(thiscount > bestcount) + { + bestcount = thiscount; + besttime = it.createdtime; + best = it; + } + else if(thiscount == bestcount && besttime < it.createdtime) + { + besttime = it.createdtime; + best = it; + } + }); + if(!bcount) + return; // no bots to remove + currentbots = currentbots - 1; + dropclient(best); +} + +void bot_removenewest() +{ + if(teamplay) + { + bot_removefromlargestteam(); + return; + } + + float besttime = 0; + entity best = NULL; + int bcount = 0; + + FOREACH_CLIENT(it.isbot, + { + ++bcount; + + if(!best) + { + best = it; + besttime = it.createdtime; + } + + if(besttime < it.createdtime) + { + besttime = it.createdtime; + best = it; + } + }); + + if(!bcount) + return; // no bots to remove + + currentbots = currentbots - 1; + dropclient(best); +} + +void autoskill(float factor) +{ + float bestbot; + float bestplayer; + + bestbot = -1; + bestplayer = -1; + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( + if(IS_REAL_CLIENT(it)) + bestplayer = max(bestplayer, it.totalfrags - it.totalfrags_lastcheck); + else + bestbot = max(bestbot, it.totalfrags - it.totalfrags_lastcheck); + )); + + LOG_TRACE("autoskill: best player got ", ftos(bestplayer), ", "); + LOG_TRACE("best bot got ", ftos(bestbot), "; "); + if(bestbot < 0 || bestplayer < 0) + { + LOG_TRACE("not doing anything"); + // don't return, let it reset all counters below + } + else if(bestbot <= bestplayer * factor - 2) + { + if(autocvar_skill < 17) + { + LOG_TRACE("2 frags difference, increasing skill"); + cvar_set("skill", ftos(autocvar_skill + 1)); + bprint("^2SKILL UP!^7 Now at level ", ftos(autocvar_skill), "\n"); + } + } + else if(bestbot >= bestplayer * factor + 2) + { + if(autocvar_skill > 0) + { + LOG_TRACE("2 frags difference, decreasing skill"); + cvar_set("skill", ftos(autocvar_skill - 1)); + bprint("^1SKILL DOWN!^7 Now at level ", ftos(autocvar_skill), "\n"); + } + } + else + { + LOG_TRACE("not doing anything"); + return; + // don't reset counters, wait for them to accumulate + } + + FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(it.totalfrags_lastcheck = it.totalfrags)); +} + +void bot_calculate_stepheightvec() +{ + stepheightvec = autocvar_sv_stepheight * '0 0 1'; + jumpstepheightvec = stepheightvec + + ((autocvar_sv_jumpvelocity * autocvar_sv_jumpvelocity) / (2 * autocvar_sv_gravity)) * '0 0 0.85'; + // 0.75 factor is for safety to make the jumps easy +} + +float bot_fixcount() +{ + int activerealplayers = 0; + int realplayers = 0; + if (MUTATOR_CALLHOOK(Bot_FixCount, activerealplayers, realplayers)) { + activerealplayers = M_ARGV(0, int); + realplayers = M_ARGV(1, int); + } else { + FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA( + if(IS_PLAYER(it)) + ++activerealplayers; + ++realplayers; + )); + } + + 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; + if (teamplay && autocvar_bot_vs_human && AvailableTeams() == 2) + 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)); + + float realminbots, minbots; + realminbots = autocvar_bot_number; + minbots = max(0, floor(realminbots)); + + bots = min(max(minbots, minplayers - activerealplayers), maxclients - realplayers); + if(bots > minbots) + bots_would_leave = true; + } + else + { + // if there are no players, remove bots + bots = 0; + } + + // only add one bot per frame to avoid utter chaos + if(time > botframe_nextthink) + { + //dprint(ftos(bots), " ? ", ftos(currentbots), "\n"); + while (currentbots < bots) + { + if (bot_spawn() == NULL) + { + bprint("Can not add bot, server full.\n"); + return false; + } + } + while (currentbots > bots) + bot_removenewest(); + } + + return true; +} + +void bot_remove_from_bot_list(entity this) +{ + entity e = bot_list; + entity prev_bot = NULL; + while (e) + { + if(e == this) + { + if(!prev_bot) + bot_list = this.nextbot; + else + prev_bot.nextbot = this.nextbot; + if(bot_strategytoken == this) + { + bot_strategytoken = this.nextbot; + bot_strategytoken_taken = true; + } + this.nextbot = NULL; + break; + } + prev_bot = e; + e = e.nextbot; + } +} + +void bot_clear(entity this) +{ + bot_remove_from_bot_list(this); + if(bot_waypoint_queue_owner == this) + bot_waypoint_queue_owner = NULL; + this.aistatus &= ~AI_STATUS_STUCK; // otherwise bot_waypoint_queue_owner will be set again to this by navigation_unstuck +} + +void bot_serverframe() +{ + if (gameover) + return; + + if (time < 2) + return; + + bot_calculate_stepheightvec(); + bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); + + if(time > autoskill_nextthink) + { + float a; + a = autocvar_skill_auto; + if(a) + autoskill(a); + autoskill_nextthink = time + 5; + } + + if(time > botframe_nextthink) + { + if(!bot_fixcount()) + botframe_nextthink = time + 10; + } + + bot_ignore_bots = autocvar_bot_ignore_bots; + + if(botframe_spawnedwaypoints) + { + if(autocvar_waypoint_benchmark) + localcmd("quit\n"); + } + + if (currentbots > 0 || autocvar_g_waypointeditor || autocvar_g_waypointeditor_auto) + if (botframe_spawnedwaypoints) + { + if(botframe_cachedwaypointlinks) + { + if(!botframe_loadedforcedlinks) + waypoint_load_links_hardwired(); + } + else + { + // TODO: Make this check cleaner + IL_EACH(g_waypoints, time - it.nextthink > 10, + { + waypoint_save_links(); + break; + }); + } + } + else + { + botframe_spawnedwaypoints = true; + waypoint_loadall(); + if(!waypoint_load_links()) + waypoint_schedulerelinkall(); + } + + if (bot_list) + { + // cycle the goal token from one bot to the next each frame + // (this prevents them from all doing spawnfunc_waypoint searches on the same + // frame, which causes choppy framerates) + if (bot_strategytoken_taken) + { + bot_strategytoken_taken = false; + if (bot_strategytoken) + bot_strategytoken = bot_strategytoken.nextbot; + if (!bot_strategytoken) + bot_strategytoken = bot_list; + } + + if (botframe_nextdangertime < time) + { + float interval; + interval = autocvar_bot_ai_dangerdetectioninterval; + if (botframe_nextdangertime < time - interval * 1.5) + botframe_nextdangertime = time; + botframe_nextdangertime = botframe_nextdangertime + interval; + botframe_updatedangerousobjects(autocvar_bot_ai_dangerdetectionupdates); + } + } + + if (autocvar_g_waypointeditor) + botframe_showwaypointlinks(); + + if (autocvar_g_waypointeditor_auto) + botframe_autowaypoints(); + + if(time > bot_cvar_nextthink) + { + if(currentbots>0) + bot_custom_weapon_priority_setup(); + bot_cvar_nextthink = time + 5; + } +} diff --git a/qcsrc/server/bot/default/bot.qh b/qcsrc/server/bot/default/bot.qh new file mode 100644 index 0000000000..f615c04036 --- /dev/null +++ b/qcsrc/server/bot/default/bot.qh @@ -0,0 +1,109 @@ +#pragma once +/* + * Globals and Fields + */ + +const int AI_STATUS_ROAMING = BIT(0); // Bot is just crawling the map. No enemies at sight +const int AI_STATUS_ATTACKING = BIT(1); // There are enemies at sight +const int AI_STATUS_RUNNING = BIT(2); // Bot is bunny hopping +const int AI_STATUS_DANGER_AHEAD = BIT(3); // There is lava/slime/trigger_hurt ahead +const int AI_STATUS_OUT_JUMPPAD = BIT(4); // Trying to get out of a "vertical" jump pad +const int AI_STATUS_OUT_WATER = BIT(5); // Trying to get out of water +const int AI_STATUS_WAYPOINT_PERSONAL_LINKING = BIT(6); // Waiting for the personal waypoint to be linked +const int AI_STATUS_WAYPOINT_PERSONAL_GOING = BIT(7); // Going to a personal waypoint +const int AI_STATUS_WAYPOINT_PERSONAL_REACHED = BIT(8); // Personal waypoint reached +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 +.int aistatus; + +// Skill system +float autoskill_nextthink; + +// havocbot_keyboardskill // keyboard movement +.float bot_moveskill; // moving technique +.float bot_dodgeskill; // dodging + +.float bot_pingskill; // ping offset + +.float bot_weaponskill; // weapon usage skill (combos, e.g.) +.float bot_aggresskill; // aggressivity, controls "think before fire" behaviour +.float bot_rangepreference; // weapon choice offset for range (>0 = prefer long range earlier "sniper", <0 = prefer short range "spammer") + +.float bot_aimskill; // aim accuracy +.float bot_offsetskill; // aim breakage +.float bot_mouseskill; // mouse "speed" + +.float bot_thinkskill; // target choice +.float bot_aiskill; // strategy choice + +.float totalfrags_lastcheck; + +// Custom weapon priorities +float bot_distance_far; +float bot_distance_close; + +entity bot_list; +.entity nextbot; +.string cleanname; +.string netname_freeme; +.string playermodel_freeme; +.string playerskin_freeme; + +.float bot_nextthink; + +.float createdtime; +.float bot_preferredcolors; +.float bot_attack; +.float bot_dodge; +.float bot_dodgerating; + +.float bot_pickup; +.float bot_pickupbasevalue; +.float bot_canfire; +.float bot_strategytime; + +.float bot_forced_team; +.float bot_config_loaded; + +float bot_strategytoken_taken; +entity bot_strategytoken; + +float botframe_spawnedwaypoints; +float botframe_nextthink; +float botframe_nextdangertime; +float bot_cvar_nextthink; +float bot_ignore_bots; // let bots not attack other bots (only works in non-teamplay) + +/* + * Functions + */ + +entity bot_spawn(); +float bot_fixcount(); + +void bot_think(entity this); +void bot_setnameandstuff(entity this); +void bot_custom_weapon_priority_setup(); +void bot_endgame(); +void bot_relinkplayerlist(); +void bot_clear(entity this); +void bot_clientdisconnect(entity this); +void bot_clientconnect(entity this); +void bot_removefromlargestteam(); +void bot_removenewest(); +void autoskill(float factor); +void bot_serverframe(); + +.void(entity this) bot_ai; +.float(entity player, entity item) bot_pickupevalfunc; + +/* + * Imports + */ + +void(entity this) havocbot_setupbot; + +void bot_calculate_stepheightvec(); diff --git a/qcsrc/server/bot/default/cvars.qc b/qcsrc/server/bot/default/cvars.qc new file mode 100644 index 0000000000..779e7f0cf9 --- /dev/null +++ b/qcsrc/server/bot/default/cvars.qc @@ -0,0 +1 @@ +#include "cvars.qh" diff --git a/qcsrc/server/bot/default/cvars.qh b/qcsrc/server/bot/default/cvars.qh new file mode 100644 index 0000000000..d612c7ab98 --- /dev/null +++ b/qcsrc/server/bot/default/cvars.qh @@ -0,0 +1,58 @@ +#pragma once + +float autocvar_bot_ai_aimskill_blendrate; +float autocvar_bot_ai_aimskill_firetolerance_distdegrees; +float autocvar_bot_ai_aimskill_firetolerance_maxdegrees; +float autocvar_bot_ai_aimskill_firetolerance_mindegrees; +float autocvar_bot_ai_aimskill_fixedrate; +float autocvar_bot_ai_aimskill_mouse; +float autocvar_bot_ai_aimskill_offset; +float autocvar_bot_ai_aimskill_order_filter_1st; +float autocvar_bot_ai_aimskill_order_filter_2nd; +float autocvar_bot_ai_aimskill_order_filter_3th; +float autocvar_bot_ai_aimskill_order_filter_4th; +float autocvar_bot_ai_aimskill_order_filter_5th; +float autocvar_bot_ai_aimskill_order_mix_1st; +float autocvar_bot_ai_aimskill_order_mix_2nd; +float autocvar_bot_ai_aimskill_order_mix_3th; +float autocvar_bot_ai_aimskill_order_mix_4th; +float autocvar_bot_ai_aimskill_order_mix_5th; +float autocvar_bot_ai_aimskill_think; +float autocvar_bot_ai_bunnyhop_firstjumpdelay; +float autocvar_bot_ai_bunnyhop_skilloffset; +float autocvar_bot_ai_bunnyhop_startdistance; +float autocvar_bot_ai_bunnyhop_stopdistance; +float autocvar_bot_ai_chooseweaponinterval; +string autocvar_bot_ai_custom_weapon_priority_close; +string autocvar_bot_ai_custom_weapon_priority_distances; +string autocvar_bot_ai_custom_weapon_priority_far; +string autocvar_bot_ai_custom_weapon_priority_mid; +float autocvar_bot_ai_dangerdetectioninterval; +float autocvar_bot_ai_dangerdetectionupdates; +float autocvar_bot_ai_enemydetectioninterval; +float autocvar_bot_ai_enemydetectionradius; +float autocvar_bot_ai_friends_aware_pickup_radius; +float autocvar_bot_ai_ignoregoal_timeout; +float autocvar_bot_ai_keyboard_distance; +float autocvar_bot_ai_keyboard_threshold; +float autocvar_bot_ai_navigation_jetpack; +float autocvar_bot_ai_navigation_jetpack_mindistance; +float autocvar_bot_ai_thinkinterval; +bool autocvar_bot_ai_weapon_combo; +float autocvar_bot_ai_weapon_combo_threshold; +string autocvar_bot_config_file; +bool autocvar_bot_god; +bool autocvar_bot_ignore_bots; +bool autocvar_bot_join_empty; +bool autocvar_bot_navigation_ignoreplayers; +bool autocvar_bot_nofire; +#define autocvar_bot_prefix cvar_string("bot_prefix") +#define autocvar_bot_suffix cvar_string("bot_suffix") +bool autocvar_bot_usemodelnames; +bool autocvar_bot_debug_tracewalk; +bool autocvar_bot_debug_goalstack; +bool autocvar_bot_wander_enable; +bool autocvar_g_debug_bot_commands; +int autocvar_g_waypointeditor_auto; +float autocvar_skill_auto; +bool autocvar_waypoint_benchmark; diff --git a/qcsrc/server/bot/default/havocbot/_mod.inc b/qcsrc/server/bot/default/havocbot/_mod.inc new file mode 100644 index 0000000000..3ecfda161f --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/_mod.inc @@ -0,0 +1,3 @@ +// generated file; do not modify +#include +#include diff --git a/qcsrc/server/bot/default/havocbot/_mod.qh b/qcsrc/server/bot/default/havocbot/_mod.qh new file mode 100644 index 0000000000..01fcd46ef4 --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/_mod.qh @@ -0,0 +1,3 @@ +// generated file; do not modify +#include +#include diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc new file mode 100644 index 0000000000..8e6b3912b3 --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -0,0 +1,1317 @@ +#include "havocbot.qh" + +#include "../cvars.qh" + +#include "../aim.qh" +#include "../bot.qh" +#include "../navigation.qh" +#include "../scripting.qh" +#include "../waypoints.qh" + +#include +#include +#include +#include +#include + +#include +#include + +#include + +.float speed; + +void havocbot_ai(entity this) +{ + if(this.draggedby) + return; + + if(bot_execute_commands(this)) + return; + + if (bot_strategytoken == this) + if (!bot_strategytoken_taken) + { + if(this.havocbot_blockhead) + { + this.havocbot_blockhead = false; + } + else + { + if (!this.jumppadcount) + this.havocbot_role(this); // little too far down the rabbit hole + } + + // TODO: tracewalk() should take care of this job (better path finding under water) + // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it + if(!(IS_DEAD(this))) + if(!this.goalcurrent) + if(this.waterlevel == WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER)) + { + // Look for the closest waypoint out of water + entity newgoal = NULL; + IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 10000), + { + if(it.origin.z < this.origin.z) + continue; + + if(it.origin.z - this.origin.z - this.view_ofs.z > 100) + continue; + + if (pointcontents(it.origin + it.maxs + '0 0 1') != CONTENT_EMPTY) + continue; + + traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this); + + if(trace_fraction < 1) + continue; + + if(!newgoal || vlen2(it.origin - this.origin) < vlen2(newgoal.origin - this.origin)) + newgoal = it; + }); + + if(newgoal) + { + // te_wizspike(newgoal.origin); + navigation_pushroute(this, newgoal); + } + } + + // token has been used this frame + bot_strategytoken_taken = true; + } + + if(IS_DEAD(this)) + return; + + havocbot_chooseenemy(this); + if (this.bot_chooseweapontime < time ) + { + this.bot_chooseweapontime = time + autocvar_bot_ai_chooseweaponinterval; + havocbot_chooseweapon(this); + } + havocbot_aim(this); + lag_update(this); + if (this.bot_aimtarg) + { + this.aistatus |= AI_STATUS_ATTACKING; + this.aistatus &= ~AI_STATUS_ROAMING; + + if(this.weapons) + { + Weapon w = PS(this).m_weapon; + w.wr_aim(w, this); + if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this)) + { + PHYS_INPUT_BUTTON_ATCK(this) = false; + PHYS_INPUT_BUTTON_ATCK2(this) = false; + } + else + { + if(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this)) + this.lastfiredweapon = PS(this).m_weapon.m_id; + } + } + else + { + if(IS_PLAYER(this.bot_aimtarg)) + bot_aimdir(this, this.bot_aimtarg.origin + this.bot_aimtarg.view_ofs - this.origin - this.view_ofs , -1); + } + } + else if (this.goalcurrent) + { + this.aistatus |= AI_STATUS_ROAMING; + this.aistatus &= ~AI_STATUS_ATTACKING; + + vector now,v,next;//,heading; + float aimdistance,skillblend,distanceblend,blend; + next = now = ( (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5) - (this.origin + this.view_ofs); + aimdistance = vlen(now); + //heading = this.velocity; + //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 the bot is not attacking, consider reloading weapons + if (!(this.aistatus & AI_STATUS_ATTACKING)) + { + // we are currently holding a weapon that's not fully loaded, reload it + if(skill >= 2) // bots can only reload the held weapon on purpose past this skill + if(this.clip_load < this.clip_size) + this.impulse = 20; // "press" the reload button, not sure if this is done right + + // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next + // the code above executes next frame, starting the reloading then + if(skill >= 5) // bots can only look for unloaded weapons past this skill + if(this.clip_load >= 0) // only if we're not reloading a weapon already + { + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo)) + PS(this).m_switchweapon = it; + )); + } + } +} + +void havocbot_keyboard_movement(entity this, vector destorg) +{ + vector keyboard; + float blend, maxspeed; + float sk; + + sk = skill + this.bot_moveskill; + + maxspeed = autocvar_sv_maxspeed; + + if (time < this.havocbot_keyboardtime) + return; + + this.havocbot_keyboardtime = + max( + this.havocbot_keyboardtime + + 0.05/max(1, sk+this.havocbot_keyboardskill) + + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill) + , time); + keyboard = this.movement * (1.0 / maxspeed); + + float trigger, trigger1; + blend = bound(0,sk*0.1,1); + trigger = autocvar_bot_ai_keyboard_threshold; + trigger1 = 0 - trigger; + + // categorize forward movement + // at skill < 1.5 only forward + // at skill < 2.5 only individual directions + // at skill < 4.5 only individual directions, and forward diagonals + // at skill >= 4.5, all cases allowed + if (keyboard.x > trigger) + { + keyboard.x = 1; + if (sk < 2.5) + keyboard.y = 0; + } + else if (keyboard.x < trigger1 && sk > 1.5) + { + keyboard.x = -1; + if (sk < 4.5) + keyboard.y = 0; + } + else + { + keyboard.x = 0; + if (sk < 1.5) + keyboard.y = 0; + } + if (sk < 4.5) + keyboard.z = 0; + + if (keyboard.y > trigger) + keyboard.y = 1; + else if (keyboard.y < trigger1) + keyboard.y = -1; + else + keyboard.y = 0; + + if (keyboard.z > trigger) + keyboard.z = 1; + else if (keyboard.z < trigger1) + keyboard.z = -1; + else + keyboard.z = 0; + + this.havocbot_keyboard = keyboard * maxspeed; + if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true; + + keyboard = this.havocbot_keyboard; + blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree + //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); + this.movement = this.movement + (keyboard - this.movement) * blend; +} + +void havocbot_bunnyhop(entity this, vector dir) +{ + float bunnyhopdistance; + vector deviation; + float maxspeed; + vector gco, gno; + + // Don't jump when attacking + if(this.aistatus & AI_STATUS_ATTACKING) + return; + + if(IS_PLAYER(this.goalcurrent)) + return; + + maxspeed = autocvar_sv_maxspeed; + + if(this.aistatus & AI_STATUS_DANGER_AHEAD) + { + this.aistatus &= ~AI_STATUS_RUNNING; + PHYS_INPUT_BUTTON_JUMP(this) = false; + this.bot_canruntogoal = 0; + this.bot_timelastseengoal = 0; + return; + } + + if(this.waterlevel > WATERLEVEL_WETFEET) + { + this.aistatus &= ~AI_STATUS_RUNNING; + return; + } + + if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING)) + { + this.bot_canruntogoal = 0; + this.bot_timelastseengoal = 0; + } + + gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; + bunnyhopdistance = vlen(this.origin - gco); + + // Run only to visible goals + if(IS_ONGROUND(this)) + if(this.speed==maxspeed) + if(checkpvs(this.origin + this.view_ofs, this.goalcurrent)) + { + this.bot_lastseengoal = this.goalcurrent; + + // seen it before + if(this.bot_timelastseengoal) + { + // for a period of time + if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay) + { + float checkdistance; + checkdistance = true; + + // don't run if it is too close + if(this.bot_canruntogoal==0) + { + if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance) + this.bot_canruntogoal = 1; + else + this.bot_canruntogoal = -1; + } + + if(this.bot_canruntogoal != 1) + return; + + if(this.aistatus & AI_STATUS_ROAMING) + if(this.goalcurrent.classname=="waypoint") + if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)) + if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z) + if(this.goalstack01 && !wasfreed(this.goalstack01)) + { + gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; + deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin); + while (deviation.y < -180) deviation.y = deviation.y + 360; + while (deviation.y > 180) deviation.y = deviation.y - 360; + + if(fabs(deviation.y) < 20) + if(bunnyhopdistance < vlen(this.origin - gno)) + if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z) + { + if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance)) + if(checkpvs(this.origin + this.view_ofs, this.goalstack01)) + { + checkdistance = false; + } + } + } + + if(checkdistance) + { + this.aistatus &= ~AI_STATUS_RUNNING; + // increase stop distance in case the goal is on a slope or a lower platform + if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance + (this.origin.z - gco.z)) + PHYS_INPUT_BUTTON_JUMP(this) = true; + } + else + { + this.aistatus |= AI_STATUS_RUNNING; + PHYS_INPUT_BUTTON_JUMP(this) = true; + } + } + } + else + { + this.bot_timelastseengoal = time; + } + } + else + { + this.bot_timelastseengoal = 0; + } + +#if 0 + // Release jump button + if(!cvar("sv_pogostick")) + if((IS_ONGROUND(this)) == 0) + { + if(this.velocity.z < 0 || vlen(this.velocity)maxspeed) + { + deviation = vectoangles(dir) - vectoangles(this.velocity); + while (deviation.y < -180) deviation.y = deviation.y + 360; + while (deviation.y > 180) deviation.y = deviation.y - 360; + + if(fabs(deviation.y)>10) + this.movement_x = 0; + + if(deviation.y>10) + this.movement_y = maxspeed * -1; + else if(deviation.y<10) + this.movement_y = maxspeed; + + } + } +#endif +} + +void havocbot_movetogoal(entity this) +{ + vector destorg; + vector diff; + vector dir; + vector flatdir; + vector m1; + vector m2; + vector evadeobstacle; + vector evadelava; + float s; + float maxspeed; + vector gco; + //float dist; + vector dodge; + //if (this.goalentity) + // te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5); + this.movement = '0 0 0'; + maxspeed = autocvar_sv_maxspeed; + + // Jetpack navigation + if(this.goalcurrent) + if(this.navigation_jetpack_goal) + if(this.goalcurrent==this.navigation_jetpack_goal) + if(this.ammo_fuel) + { + if(autocvar_bot_debug_goalstack) + { + debuggoalstack(this); + te_wizspike(this.navigation_jetpack_point); + } + + // Take off + if (!(this.aistatus & AI_STATUS_JETPACK_FLYING)) + { + // Brake almost completely so it can get a good direction + if(vdist(this.velocity, >, 10)) + return; + this.aistatus |= AI_STATUS_JETPACK_FLYING; + } + + makevectors(this.v_angle.y * '0 1 0'); + dir = normalize(this.navigation_jetpack_point - this.origin); + + // Landing + if(this.aistatus & AI_STATUS_JETPACK_LANDING) + { + // Calculate brake distance in xy + float db, v, d; + vector dxy; + + dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0; + d = vlen(dxy); + v = vlen(this.velocity - this.velocity.z * '0 0 1'); + db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100; + // dprint("distance ", ftos(ceil(d)), " velocity ", ftos(ceil(v)), " brake at ", ftos(ceil(db)), "\n"); + if(d < db || d < 500) + { + // Brake + if(fabs(this.velocity.x)>maxspeed*0.3) + { + this.movement_x = dir * v_forward * -maxspeed; + return; + } + // Switch to normal mode + this.navigation_jetpack_goal = NULL; + this.aistatus &= ~AI_STATUS_JETPACK_LANDING; + this.aistatus &= ~AI_STATUS_JETPACK_FLYING; + return; + } + } + else if(checkpvs(this.origin,this.goalcurrent)) + { + // If I can see the goal switch to landing code + this.aistatus &= ~AI_STATUS_JETPACK_FLYING; + this.aistatus |= AI_STATUS_JETPACK_LANDING; + return; + } + + // Flying + PHYS_INPUT_BUTTON_HOOK(this) = true; + if(this.navigation_jetpack_point.z - STAT(PL_MAX, this).z + STAT(PL_MIN, this).z < this.origin.z) + { + this.movement_x = dir * v_forward * maxspeed; + this.movement_y = dir * v_right * maxspeed; + } + return; + } + + // Handling of jump pads + if(this.jumppadcount) + { + // If got stuck on the jump pad try to reach the farthest visible waypoint + // but with some randomness so it can try out different paths + if(this.aistatus & AI_STATUS_OUT_JUMPPAD) + { + if(fabs(this.velocity.z)<50) + { + entity newgoal = NULL; + IL_EACH(g_waypoints, vdist(it.origin - this.origin, <=, 1000), + { + traceline(this.origin + this.view_ofs, ((it.absmin + it.absmax) * 0.5), true, this); + + if(trace_fraction < 1) + continue; + + if(!newgoal || ((random() < 0.8) && vlen2(it.origin - this.origin) > vlen2(newgoal.origin - this.origin))) + newgoal = it; + }); + + if(newgoal) + { + this.ignoregoal = this.goalcurrent; + this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout; + navigation_clearroute(this); + navigation_routetogoal(this, newgoal, this.origin); + if(autocvar_bot_debug_goalstack) + debuggoalstack(this); + this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; + } + } + else + return; + } + else + { + if(this.velocity.z>0) + { + float threshold; + vector velxy = this.velocity; velxy_z = 0; + threshold = maxspeed * 0.2; + if(vdist(velxy, <, threshold)) + { + LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now"); + this.aistatus |= AI_STATUS_OUT_JUMPPAD; + } + return; + } + + // Don't chase players while using a jump pad + if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01)) + return; + } + } + else if(this.aistatus & AI_STATUS_OUT_JUMPPAD) + this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; + + // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump + if(skill>6) + if (!(IS_ONGROUND(this))) + { + tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this); + if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos )) + if(this.items & IT_JETPACK) + { + tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this); + if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' )) + { + if(this.velocity.z<0) + { + PHYS_INPUT_BUTTON_HOOK(this) = true; + } + } + else + PHYS_INPUT_BUTTON_HOOK(this) = true; + + // If there is no goal try to move forward + + if(this.goalcurrent==NULL) + dir = v_forward; + else + dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin); + + vector xyvelocity = this.velocity; xyvelocity_z = 0; + float xyspeed = xyvelocity * dir; + + if(xyspeed < (maxspeed / 2)) + { + makevectors(this.v_angle.y * '0 1 0'); + tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this); + if(trace_fraction==1) + { + this.movement_x = dir * v_forward * maxspeed; + this.movement_y = dir * v_right * maxspeed; + if (skill < 10) + havocbot_keyboard_movement(this, this.origin + dir * 100); + } + } + + this.havocbot_blockhead = true; + + return; + } + else if(this.health>WEP_CVAR(devastator, damage)*0.5) + { + if(this.velocity.z < 0) + if(client_hasweapon(this, WEP_DEVASTATOR, true, false)) + { + this.movement_x = maxspeed; + + if(this.rocketjumptime) + { + if(time > this.rocketjumptime) + { + PHYS_INPUT_BUTTON_ATCK2(this) = true; + this.rocketjumptime = 0; + } + return; + } + + PS(this).m_switchweapon = WEP_DEVASTATOR; + this.v_angle_x = 90; + PHYS_INPUT_BUTTON_ATCK(this) = true; + this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay); + return; + } + } + else + { + // If there is no goal try to move forward + if(this.goalcurrent==NULL) + this.movement_x = maxspeed; + } + } + + // If we are under water with no goals, swim up + if(this.waterlevel) + if(this.goalcurrent==NULL) + { + dir = '0 0 0'; + if(this.waterlevel>WATERLEVEL_SWIMMING) + dir.z = 1; + else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER)) + PHYS_INPUT_BUTTON_JUMP(this) = true; + else + PHYS_INPUT_BUTTON_JUMP(this) = false; + makevectors(this.v_angle.y * '0 1 0'); + this.movement_x = dir * v_forward * maxspeed; + this.movement_y = dir * v_right * maxspeed; + this.movement_z = dir * v_up * maxspeed; + } + + // if there is nowhere to go, exit + if (this.goalcurrent == NULL) + return; + + navigation_poptouchedgoals(this); + + // if ran out of goals try to use an alternative goal or get a new strategy asap + if(this.goalcurrent == NULL) + { + this.bot_strategytime = 0; + return; + } + + + if(autocvar_bot_debug_goalstack) + debuggoalstack(this); + + m1 = this.goalcurrent.origin + this.goalcurrent.mins; + m2 = this.goalcurrent.origin + this.goalcurrent.maxs; + destorg = this.origin; + destorg.x = bound(m1_x, destorg.x, m2_x); + destorg.y = bound(m1_y, destorg.y, m2_y); + destorg.z = bound(m1_z, destorg.z, m2_z); + diff = destorg - this.origin; + //dist = vlen(diff); + dir = normalize(diff); + flatdir = diff;flatdir.z = 0; + flatdir = normalize(flatdir); + gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; + + //if (this.bot_dodgevector_time < time) + { + // this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval"); + // this.bot_dodgevector_jumpbutton = 1; + evadeobstacle = '0 0 0'; + evadelava = '0 0 0'; + + if (this.waterlevel) + { + if(this.waterlevel>WATERLEVEL_SWIMMING) + { + // flatdir_z = 1; + this.aistatus |= AI_STATUS_OUT_WATER; + } + else + { + if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) && + ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER)) + PHYS_INPUT_BUTTON_JUMP(this) = true; + else + PHYS_INPUT_BUTTON_JUMP(this) = false; + } + dir = normalize(flatdir); + makevectors(this.v_angle.y * '0 1 0'); + } + else + { + if(this.aistatus & AI_STATUS_OUT_WATER) + this.aistatus &= ~AI_STATUS_OUT_WATER; + + // jump if going toward an obstacle that doesn't look like stairs we + // can walk up directly + tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this); + if (trace_fraction < 1) + if (trace_plane_normal.z < 0.7) + { + s = trace_fraction; + tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this); + if (trace_fraction < s + 0.01) + if (trace_plane_normal.z < 0.7) + { + s = trace_fraction; + tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this); + if (trace_fraction > s) + PHYS_INPUT_BUTTON_JUMP(this) = true; + } + } + + // avoiding dangers and obstacles + vector dst_ahead = this.origin + this.view_ofs + this.velocity * 0.5; + vector dst_down = dst_ahead - '0 0 3000'; + + // Look ahead + traceline(this.origin + this.view_ofs, dst_ahead, true, NULL); + + // Check head-banging against walls + if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER)) + { + PHYS_INPUT_BUTTON_JUMP(this) = true; + if(this.facingwalltime && time > this.facingwalltime) + { + this.ignoregoal = this.goalcurrent; + this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout; + this.bot_strategytime = 0; + return; + } + else + { + this.facingwalltime = time + 0.05; + } + } + else + { + this.facingwalltime = 0; + + if(this.ignoregoal != NULL && time > this.ignoregoaltime) + { + this.ignoregoal = NULL; + this.ignoregoaltime = 0; + } + } + + // Check for water/slime/lava and dangerous edges + // (only when the bot is on the ground or jumping intentionally) + this.aistatus &= ~AI_STATUS_DANGER_AHEAD; + + if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired ) + if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || (this.aistatus & AI_STATUS_ROAMING) || PHYS_INPUT_BUTTON_JUMP(this)) + { + // Look downwards + traceline(dst_ahead , dst_down, true, NULL); + //te_lightning2(NULL, this.origin + this.view_ofs, dst_ahead); // Draw "ahead" look + //te_lightning2(NULL, dst_ahead, dst_down); // Draw "downwards" look + if(trace_endpos.z < this.origin.z + this.mins.z) + { + s = pointcontents(trace_endpos + '0 0 1'); + if (s != CONTENT_SOLID) + if (s == CONTENT_LAVA || s == CONTENT_SLIME) + evadelava = normalize(this.velocity) * -1; + else if (s == CONTENT_SKY) + evadeobstacle = normalize(this.velocity) * -1; + else if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos)) + { + // the traceline check isn't enough but is good as optimization, + // when not true (most of the time) this tracebox call is avoided + tracebox(dst_ahead, this.mins, this.maxs, dst_down, true, this); + if (tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos)) + { + if (gco.z > this.origin.z + jumpstepheightvec.z) + { + // the goal is probably on an upper platform, assume bot can't get there + LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared"); + navigation_clearroute(this); + this.bot_strategytime = 0; + } + else + evadelava = normalize(this.velocity) * -1; + } + } + } + } + + dir = flatdir; + evadeobstacle.z = 0; + evadelava.z = 0; + makevectors(this.v_angle.y * '0 1 0'); + + if(evadeobstacle!='0 0 0'||evadelava!='0 0 0') + this.aistatus |= AI_STATUS_DANGER_AHEAD; + } + + dodge = havocbot_dodge(this); + dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1); + 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); + // this.bot_dodgevector = dir; + // this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this); + } + + 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; + } + else + { + if(this.origin.z + this.mins.z > this.ladder_entity.origin.z + this.ladder_entity.mins.z) + dir.z = -1; + } + } + + //dir = this.bot_dodgevector; + //if (this.bot_dodgevector_jumpbutton) + // PHYS_INPUT_BUTTON_JUMP(this) = true; + this.movement_x = dir * v_forward * maxspeed; + this.movement_y = dir * v_right * maxspeed; + this.movement_z = dir * v_up * maxspeed; + + // Emulate keyboard interface + if (skill < 10) + havocbot_keyboard_movement(this, destorg); + + // Bunnyhop! +// if(this.aistatus & AI_STATUS_ROAMING) + if(this.goalcurrent) + if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset) + havocbot_bunnyhop(this, dir); + + if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true; + if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true; + if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10); +} + +entity havocbot_gettarget(entity this, bool secondary) +{ + entity best = NULL; + vector eye = CENTER_OR_VIEWOFS(this); + IL_EACH(g_bot_targets, boolean((secondary) ? it.classname == "misc_breakablemodel" : it.classname != "misc_breakablemodel"), + { + vector v = CENTER_OR_VIEWOFS(it); + if(vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius)) + if(!best || vlen2(CENTER_OR_VIEWOFS(best) - eye) > vlen2(v - eye)) + if(bot_shouldattack(this, it)) + { + traceline(eye, v, true, this); + if (trace_ent == it || trace_fraction >= 1) + best = it; + } + }); + + return best; +} + +void havocbot_chooseenemy(entity this) +{ + if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this)) + { + this.enemy = NULL; + return; + } + if (this.enemy) + { + if (!bot_shouldattack(this, this.enemy)) + { + // enemy died or something, find a new target + this.enemy = NULL; + this.havocbot_chooseenemy_finished = time; + } + else if (this.havocbot_stickenemy) + { + // tracking last chosen enemy + // if enemy is visible + // and not really really far away + // and we're not severely injured + // then keep tracking for a half second into the future + 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) + { + // remain tracking him for a shot while (case he went after a small corner or pilar + this.havocbot_chooseenemy_finished = time + 0.5; + return; + } + // enemy isn't visible, or is far away, or we're injured severely + // so stop preferring this enemy + // (it will still take a half second until a new one is chosen) + this.havocbot_stickenemy = 0; + } + } + if (time < this.havocbot_chooseenemy_finished) + return; + this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval; + vector eye = this.origin + this.view_ofs; + entity best = NULL; + float bestrating = 100000000; + + // Backup hit flags + int hf = this.dphitcontentsmask; + + // Search for enemies, if no enemy can be seen directly try to look through transparent objects + + this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; + + bool scan_transparent = false; + bool scan_secondary_targets = false; + bool have_secondary_targets = false; + while(true) + { + scan_secondary_targets = false; +LABEL(scan_targets) + IL_EACH(g_bot_targets, it.bot_attack, + { + if(!scan_secondary_targets) + { + if(it.classname == "misc_breakablemodel") + { + have_secondary_targets = true; + continue; + } + } + else if(it.classname != "misc_breakablemodel") + continue; + + vector v = (it.absmin + it.absmax) * 0.5; + float rating = vlen2(v - eye); + if (vdist(v - eye, <, autocvar_bot_ai_enemydetectionradius)) + if (bestrating > rating) + if (bot_shouldattack(this, it)) + { + traceline(eye, v, true, this); + if (trace_ent == it || trace_fraction >= 1) + { + best = it; + bestrating = rating; + } + } + }); + + if(!best && have_secondary_targets && !scan_secondary_targets) + { + scan_secondary_targets = true; + // restart the loop + bestrating = 100000000; + goto 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 + break; + if(scan_transparent) + break; + + // Set flags to see through transparent objects + this.dphitcontentsmask |= DPCONTENTS_OPAQUE; + + scan_transparent = true; + } + + // Restore hit flags + this.dphitcontentsmask = hf; + + this.enemy = best; + this.havocbot_stickenemy = true; + if(best && best.classname == "misc_breakablemodel") + this.havocbot_stickenemy = false; +} + +float havocbot_chooseweapon_checkreload(entity this, int new_weapon) +{ + // bots under this skill cannot find unloaded weapons to reload idly when not in combat, + // so skip this for them, or they'll never get to reload their weapons at all. + // this also allows bots under this skill to be more stupid, and reload more often during combat :) + if(skill < 5) + return false; + + // if this weapon is scheduled for reloading, don't switch to it during combat + if (this.weapon_load[new_weapon] < 0) + { + bool other_weapon_available = false; + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this)) + other_weapon_available = true; + )); + if(other_weapon_available) + return true; + } + + return false; +} + +void havocbot_chooseweapon(entity this) +{ + int i; + + // ;) + if(g_weaponarena_weapons == WEPSET(TUBA)) + { + PS(this).m_switchweapon = WEP_TUBA; + return; + } + + // TODO: clean this up by moving it to weapon code + if(this.enemy==NULL) + { + // If no weapon was chosen get the first available weapon + if(PS(this).m_weapon==WEP_Null) + FOREACH(Weapons, it != WEP_Null, LAMBDA( + if(client_hasweapon(this, it, true, false)) + { + PS(this).m_switchweapon = it; + return; + } + )); + return; + } + + // Do not change weapon during the next second after a combo + float f = time - this.lastcombotime; + if(f < 1) + return; + + float w; + float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000); + + // Should it do a weapon combo? + float af, ct, combo_time, combo; + + af = ATTACK_FINISHED(this, 0); + ct = autocvar_bot_ai_weapon_combo_threshold; + + // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos + // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold + combo_time = time + ct + (ct * ((-0.3*(skill+this.bot_weaponskill))+3)); + + combo = false; + + if(autocvar_bot_ai_weapon_combo) + if(PS(this).m_weapon.m_id == this.lastfiredweapon) + if(af > combo_time) + { + combo = true; + this.lastcombotime = time; + } + + distance *= pow(2, this.bot_rangepreference); + + // Custom weapon list based on distance to the enemy + if(bot_custom_weapon){ + + // Choose weapons for far distance + if ( distance > bot_distance_far ) { + for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){ + w = bot_weapons_far[i]; + if ( client_hasweapon(this, Weapons_from(w), true, false) ) + { + if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) + continue; + PS(this).m_switchweapon = Weapons_from(w); + return; + } + } + } + + // Choose weapons for mid distance + if ( distance > bot_distance_close) { + for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){ + w = bot_weapons_mid[i]; + if ( client_hasweapon(this, Weapons_from(w), true, false) ) + { + if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) + continue; + PS(this).m_switchweapon = Weapons_from(w); + return; + } + } + } + + // Choose weapons for close distance + for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){ + w = bot_weapons_close[i]; + if ( client_hasweapon(this, Weapons_from(w), true, false) ) + { + if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) + continue; + PS(this).m_switchweapon = Weapons_from(w); + return; + } + } + } +} + +void havocbot_aim(entity this) +{ + vector myvel, enemyvel; +// if(this.flags & FL_INWATER) +// return; + if (time < this.nextaim) + return; + this.nextaim = time + 0.1; + myvel = this.velocity; + if (!this.waterlevel) + myvel.z = 0; + if (this.enemy) + { + enemyvel = this.enemy.velocity; + if (!this.enemy.waterlevel) + enemyvel.z = 0; + lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel); + } + else + lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0'); +} + +bool havocbot_moveto_refresh_route(entity this) +{ + // Refresh path to goal if necessary + entity wp; + wp = this.havocbot_personal_waypoint; + navigation_goalrating_start(this); + navigation_routerating(this, wp, 10000, 10000); + navigation_goalrating_end(this); + return this.navigation_hasgoals; +} + +float havocbot_moveto(entity this, vector pos) +{ + entity wp; + + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) + { + // Step 4: Move to waypoint + if(this.havocbot_personal_waypoint==NULL) + { + LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint"); + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; + return CMD_STATUS_ERROR; + } + + if (!bot_strategytoken_taken) + if(this.havocbot_personal_waypoint_searchtime= 30) + { + LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin)); + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING; + delete(this.havocbot_personal_waypoint); + return CMD_STATUS_ERROR; + } + else + LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later"); + } + } + + if(autocvar_bot_debug_goalstack) + debuggoalstack(this); + + // Heading + vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs); + dir.z = 0; + bot_aimdir(this, dir, -1); + + // Go! + havocbot_movetogoal(this); + + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED) + { + // Step 5: Waypoint reached + LOG_TRACE(this.netname, "'s personal waypoint reached"); + delete(this.havocbot_personal_waypoint); + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED; + return CMD_STATUS_FINISHED; + } + + return CMD_STATUS_EXECUTING; + } + + // Step 2: Linking waypoint + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING) + { + // Wait until it is linked + if(!this.havocbot_personal_waypoint.wplinked) + { + LOG_TRACE(this.netname, " waiting for personal waypoint to be linked"); + return CMD_STATUS_EXECUTING; + } + + this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING; + + // Step 3: Route to waypoint + LOG_TRACE(this.netname, " walking to its personal waypoint"); + + return CMD_STATUS_EXECUTING; + } + + // Step 1: Spawning waypoint + wp = waypoint_spawnpersonal(this, pos); + if(wp==NULL) + { + LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos)); + return CMD_STATUS_ERROR; + } + + this.havocbot_personal_waypoint = wp; + this.havocbot_personal_waypoint_failcounter = 0; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING; + + // if pos is inside a teleport, then let's mark it as teleport waypoint + IL_EACH(g_teleporters, WarpZoneLib_BoxTouchesBrush(pos, pos, it, NULL), + { + wp.wpflags |= WAYPOINTFLAG_TELEPORT; + this.lastteleporttime = 0; + }); + +/* + if(wp.wpflags & WAYPOINTFLAG_TELEPORT) + print("routing to a teleporter\n"); + else + print("routing to a non-teleporter\n"); +*/ + + return CMD_STATUS_EXECUTING; +} + +float havocbot_resetgoal(entity this) +{ + navigation_clearroute(this); + return CMD_STATUS_FINISHED; +} + +void havocbot_setupbot(entity this) +{ + this.bot_ai = havocbot_ai; + this.cmd_moveto = havocbot_moveto; + this.cmd_resetgoal = havocbot_resetgoal; + + havocbot_chooserole(this); +} + +vector havocbot_dodge(entity this) +{ + // LordHavoc: disabled because this is too expensive + return '0 0 0'; +#if 0 + entity head; + vector dodge, v, n; + float danger, bestdanger, vl, d; + dodge = '0 0 0'; + bestdanger = -20; + // check for dangerous objects near bot or approaching bot + head = findchainfloat(bot_dodge, true); + while(head) + { + if (head.owner != this) + { + vl = vlen(head.velocity); + if (vl > autocvar_sv_maxspeed * 0.3) + { + n = normalize(head.velocity); + v = this.origin - head.origin; + d = v * n; + if (d > (0 - head.bot_dodgerating)) + if (d < (vl * 0.2 + head.bot_dodgerating)) + { + // calculate direction and distance from the flight path, by removing the forward axis + v = v - (n * (v * n)); + danger = head.bot_dodgerating - vlen(v); + if (bestdanger < danger) + { + bestdanger = danger; + // dodge to the side of the object + dodge = normalize(v); + } + } + } + else + { + danger = head.bot_dodgerating - vlen(head.origin - this.origin); + if (bestdanger < danger) + { + bestdanger = danger; + dodge = normalize(this.origin - head.origin); + } + } + } + head = head.chain; + } + return dodge; +#endif +} diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qh b/qcsrc/server/bot/default/havocbot/havocbot.qh new file mode 100644 index 0000000000..4a391b6e7c --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/havocbot.qh @@ -0,0 +1,64 @@ +#pragma once + +/* + * Globals and Fields + */ + +.float havocbot_keyboardskill; +.float facingwalltime, ignoregoaltime; +.float lastfiredweapon; +.float lastcombotime; +.float havocbot_blockhead; + +.float havocbot_keyboardtime; +.float havocbot_ducktime; +.float bot_timelastseengoal; +.float bot_canruntogoal; +.float bot_chooseweapontime; +.float rocketjumptime; +.float nextaim; +.float havocbot_personal_waypoint_searchtime; +.float havocbot_personal_waypoint_failcounter; +.float havocbot_chooseenemy_finished; +.float havocbot_stickenemy; +.float havocbot_role_timeout; + +.entity ignoregoal; +.entity bot_lastseengoal; +.entity havocbot_personal_waypoint; + +.vector havocbot_keyboard; + +/* + * Functions + */ + +void havocbot_ai(entity this); +void havocbot_aim(entity this); +void havocbot_setupbot(entity this); +void havocbot_movetogoal(entity this); +void havocbot_chooserole(entity this); +void havocbot_chooseenemy(entity this); +void havocbot_chooseweapon(entity this); +void havocbot_bunnyhop(entity this, vector dir); +void havocbot_keyboard_movement(entity this, vector destorg); + +float havocbot_resetgoal(entity this); +float havocbot_moveto(entity this, vector pos); +float havocbot_moveto_refresh_route(entity this); + +vector havocbot_dodge(entity this); + +.void(entity this) havocbot_role; +.void(entity this) havocbot_previous_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; + +/* + * Imports + */ + +.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 new file mode 100644 index 0000000000..536c8b205e --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/roles.qc @@ -0,0 +1,197 @@ +#include "roles.qh" + +#include "havocbot.qh" + +#include "../cvars.qh" + +#include "../bot.qh" +#include "../navigation.qh" + +.float max_armorvalue; +.float havocbot_role_timeout; + +.void(entity this) havocbot_previous_role; +.void(entity this) havocbot_role; + +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) +{ + float rating, d, discard, friend_distance, enemy_distance; + vector o; + ratingscale = ratingscale * 0.0001; // items are rated around 10000 already + + IL_EACH(g_items, it.bot_pickup, + { + o = (it.absmin + it.absmax) * 0.5; + friend_distance = 10000; enemy_distance = 10000; + rating = 0; + + if(!it.solid || vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) ) + continue; + + // Check if the item can be picked up safely + if(it.classname == "droppedweapon") + { + traceline(o, o + '0 0 -1500', true, NULL); + + d = pointcontents(trace_endpos + '0 0 1'); + if(d == CONTENT_WATER || d == CONTENT_SLIME || d == CONTENT_LAVA) + continue; + if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos)) + continue; + } + else + { + // Ignore items under water + traceline(it.origin + it.maxs, it.origin + it.maxs, MOVE_NORMAL, it); + if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) + continue; + } + + if(teamplay) + { + discard = false; + + entity picker = it; + FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), + { + d = vlen(it.origin - o); // distance between player and item + + if ( it.team == this.team ) + { + if ( !IS_REAL_CLIENT(it) || discard ) + continue; + + if( d > friend_distance) + continue; + + friend_distance = d; + + discard = true; + + if( picker.health && it.health > this.health ) + continue; + + if( picker.armorvalue && it.armorvalue > this.armorvalue) + continue; + + if( picker.weapons ) + if( picker.weapons & ~it.weapons ) + continue; + + if (picker.ammo_shells && it.ammo_shells > this.ammo_shells) + continue; + + if (picker.ammo_nails && it.ammo_nails > this.ammo_nails) + continue; + + if (picker.ammo_rockets && it.ammo_rockets > this.ammo_rockets) + continue; + + if (picker.ammo_cells && it.ammo_cells > this.ammo_cells) + continue; + + if (picker.ammo_plasma && it.ammo_plasma > this.ammo_plasma) + continue; + + discard = false; + } + else + { + // If enemy only track distances + // TODO: track only if visible ? + if( d < enemy_distance ) + enemy_distance = d; + } + }); + + // Rate the item only if no one needs it, or if an enemy is closer to it + if ( (enemy_distance < friend_distance && vdist(o - org, <, enemy_distance)) || + (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard ) + rating = it.bot_pickupevalfunc(this, it); + + } + else + rating = it.bot_pickupevalfunc(this, it); + + if(rating > 0) + navigation_routerating(this, it, rating * ratingscale, 2000); + }); +} + +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) +{ + if (autocvar_bot_nofire) + return; + + // don't chase players if we're under water + if(this.waterlevel>WATERLEVEL_WETFEET) + return; + + int t; + + FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA( + // TODO: Merge this logic with the bot_shouldattack function + if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius)) + continue; + + // rate only visible enemies + /* + traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this); + if (trace_fraction < 1 || trace_ent != it) + continue; + */ + + if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND)) + continue; + + // not falling + if(!IS_ONGROUND(it)) + { + traceline(it.origin, it.origin + '0 0 -1500', true, NULL); + t = pointcontents(trace_endpos + '0 0 1'); + if(t != CONTENT_SOLID ) + if(t == CONTENT_WATER || t == CONTENT_SLIME || t == CONTENT_LAVA) + continue; + if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos)) + continue; + } + + // TODO: rate waypoints near the targeted player at that moment, instead of the player itself + // adding a player as a goal seems to be quite dangerous, especially on space maps + // remove hack in navigation_poptouchedgoals() after performing this change + + t = (this.health + this.armorvalue ) / (it.health + it.armorvalue ); + navigation_routerating(this, it, t * ratingscale, 2000); + )); +} + +// legacy bot role for standard gamemodes +// go to best items +void havocbot_role_generic(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.bot_strategytime < time) + { + this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + navigation_goalrating_start(this); + havocbot_goalrating_items(this, 10000, this.origin, 10000); + havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000); + //havocbot_goalrating_waypoints(1, this.origin, 1000); + navigation_goalrating_end(this); + } +} + +void havocbot_chooserole_generic(entity this) +{ + this.havocbot_role = havocbot_role_generic; +} + +void havocbot_chooserole(entity this) +{ + LOG_TRACE("choosing a role..."); + this.bot_strategytime = 0; + if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this)) + havocbot_chooserole_generic(this); +} diff --git a/qcsrc/server/bot/default/havocbot/roles.qh b/qcsrc/server/bot/default/havocbot/roles.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/roles.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/bot/default/havocbot/scripting.qh b/qcsrc/server/bot/default/havocbot/scripting.qh new file mode 100644 index 0000000000..07cb4d6e60 --- /dev/null +++ b/qcsrc/server/bot/default/havocbot/scripting.qh @@ -0,0 +1,3 @@ +#pragma once + +void bot_clearqueue(entity bot); diff --git a/qcsrc/server/bot/default/navigation.qc b/qcsrc/server/bot/default/navigation.qc new file mode 100644 index 0000000000..4c8982e945 --- /dev/null +++ b/qcsrc/server/bot/default/navigation.qc @@ -0,0 +1,1219 @@ +#include "navigation.qh" + +#include "cvars.qh" + +#include "bot.qh" +#include "waypoints.qh" + +#include + +#include + +#include +#include +#include + +.float speed; + +// rough simulation of walking from one point to another to test if a path +// can be traveled, used for waypoint linking and havocbot + +bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) +{ + vector org; + vector move; + vector dir; + float dist; + float totaldist; + float stepdist; + float yaw; + float ignorehazards; + float swimming; + + if(autocvar_bot_debug_tracewalk) + { + debugresetnodes(); + debugnode(e, start); + } + + move = end - start; + move.z = 0; + org = start; + dist = totaldist = vlen(move); + dir = normalize(move); + stepdist = 32; + ignorehazards = false; + swimming = false; + + // Analyze starting point + traceline(start, start, MOVE_NORMAL, e); + if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) + ignorehazards = true; + else + { + traceline( start, start + '0 0 -65536', MOVE_NORMAL, e); + if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) + { + ignorehazards = true; + swimming = true; + } + } + tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e); + if (trace_startsolid) + { + // Bad start + if(autocvar_bot_debug_tracewalk) + debugnodestatus(start, DEBUG_NODE_FAIL); + + //print("tracewalk: ", vtos(start), " is a bad start\n"); + return false; + } + + // Movement loop + yaw = vectoyaw(move); + move = end - org; + for (;;) + { + if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1')) + { + // Succeeded + if(autocvar_bot_debug_tracewalk) + debugnodestatus(org, DEBUG_NODE_SUCCESS); + + //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n"); + return true; + } + if(autocvar_bot_debug_tracewalk) + debugnode(e, org); + + if (dist <= 0) + break; + if (stepdist > dist) + stepdist = dist; + dist = dist - stepdist; + traceline(org, org, MOVE_NORMAL, e); + if (!ignorehazards) + { + if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) + { + // hazards blocking path + if(autocvar_bot_debug_tracewalk) + debugnodestatus(org, DEBUG_NODE_FAIL); + + //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n"); + return false; + } + } + if (trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) + { + move = normalize(end - org); + tracebox(org, m1, m2, org + move * stepdist, movemode, e); + + if(autocvar_bot_debug_tracewalk) + debugnode(e, trace_endpos); + + if (trace_fraction < 1) + { + swimming = true; + org = trace_endpos - normalize(org - trace_endpos) * stepdist; + for (; org.z < end.z + e.maxs.z; org.z += stepdist) + { + if(autocvar_bot_debug_tracewalk) + debugnode(e, org); + + if(pointcontents(org) == CONTENT_EMPTY) + break; + } + + if(pointcontents(org + '0 0 1') != CONTENT_EMPTY) + { + if(autocvar_bot_debug_tracewalk) + debugnodestatus(org, DEBUG_NODE_FAIL); + + return false; + //print("tracewalk: ", vtos(start), " failed under water\n"); + } + continue; + + } + else + org = trace_endpos; + } + else + { + move = dir * stepdist + org; + tracebox(org, m1, m2, move, movemode, e); + + if(autocvar_bot_debug_tracewalk) + debugnode(e, trace_endpos); + + // hit something + if (trace_fraction < 1) + { + // check if we can walk over this obstacle, possibly by jumpstepping + tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e); + if (trace_fraction < 1 || trace_startsolid) + { + tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e); + if (trace_fraction < 1 || trace_startsolid) + { + if(autocvar_bot_debug_tracewalk) + debugnodestatus(trace_endpos, DEBUG_NODE_WARNING); + + // check for doors + traceline( org, move, movemode, e); + if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door") + { + vector nextmove; + move = trace_endpos; + while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door") + { + nextmove = move + (dir * stepdist); + traceline( move, nextmove, movemode, e); + move = nextmove; + } + } + else + { + if(autocvar_bot_debug_tracewalk) + debugnodestatus(trace_endpos, DEBUG_NODE_FAIL); + + //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n"); + //te_explosion(trace_endpos); + //print(ftos(e.dphitcontentsmask), "\n"); + return false; // failed + } + } + else + move = trace_endpos; + } + else + move = trace_endpos; + } + else + move = trace_endpos; + + // trace down from stepheight as far as possible and move there, + // if this starts in solid we try again without the stepup, and + // if that also fails we assume it is a wall + // (this is the same logic as the Quake walkmove function used) + tracebox(move, m1, m2, move + '0 0 -65536', movemode, e); + + // moved successfully + if(swimming) + { + float c; + c = pointcontents(org + '0 0 1'); + if (!(c == CONTENT_WATER || c == CONTENT_LAVA || c == CONTENT_SLIME)) + swimming = false; + else + continue; + } + + org = trace_endpos; + } + } + + //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n"); + + // moved but didn't arrive at the intended destination + if(autocvar_bot_debug_tracewalk) + debugnodestatus(org, DEBUG_NODE_FAIL); + + return false; +} + +///////////////////////////////////////////////////////////////////////////// +// goal stack +///////////////////////////////////////////////////////////////////////////// + +// completely empty the goal stack, used when deciding where to go +void navigation_clearroute(entity this) +{ + //print("bot ", etos(this), " clear\n"); + this.navigation_hasgoals = false; + this.goalcurrent = NULL; + this.goalstack01 = NULL; + this.goalstack02 = NULL; + this.goalstack03 = NULL; + this.goalstack04 = NULL; + this.goalstack05 = NULL; + this.goalstack06 = NULL; + this.goalstack07 = NULL; + this.goalstack08 = NULL; + this.goalstack09 = NULL; + this.goalstack10 = NULL; + this.goalstack11 = NULL; + this.goalstack12 = NULL; + this.goalstack13 = NULL; + this.goalstack14 = NULL; + this.goalstack15 = NULL; + this.goalstack16 = NULL; + this.goalstack17 = NULL; + this.goalstack18 = NULL; + this.goalstack19 = NULL; + this.goalstack20 = NULL; + this.goalstack21 = NULL; + this.goalstack22 = NULL; + this.goalstack23 = NULL; + this.goalstack24 = NULL; + this.goalstack25 = NULL; + this.goalstack26 = NULL; + this.goalstack27 = NULL; + this.goalstack28 = NULL; + this.goalstack29 = NULL; + this.goalstack30 = NULL; + this.goalstack31 = NULL; +} + +// add a new goal at the beginning of the stack +// (in other words: add a new prerequisite before going to the later goals) +// NOTE: when a waypoint is added, the WP gets pushed first, then the +// next-closest WP on the shortest path to the WP +// That means, if the stack overflows, the bot will know how to do the FIRST 32 +// steps to the goal, and then recalculate the path. +void navigation_pushroute(entity this, entity e) +{ + //print("bot ", etos(this), " push ", etos(e), "\n"); + this.goalstack31 = this.goalstack30; + this.goalstack30 = this.goalstack29; + this.goalstack29 = this.goalstack28; + this.goalstack28 = this.goalstack27; + this.goalstack27 = this.goalstack26; + this.goalstack26 = this.goalstack25; + this.goalstack25 = this.goalstack24; + this.goalstack24 = this.goalstack23; + this.goalstack23 = this.goalstack22; + this.goalstack22 = this.goalstack21; + this.goalstack21 = this.goalstack20; + this.goalstack20 = this.goalstack19; + this.goalstack19 = this.goalstack18; + this.goalstack18 = this.goalstack17; + this.goalstack17 = this.goalstack16; + this.goalstack16 = this.goalstack15; + this.goalstack15 = this.goalstack14; + this.goalstack14 = this.goalstack13; + this.goalstack13 = this.goalstack12; + this.goalstack12 = this.goalstack11; + this.goalstack11 = this.goalstack10; + this.goalstack10 = this.goalstack09; + this.goalstack09 = this.goalstack08; + this.goalstack08 = this.goalstack07; + this.goalstack07 = this.goalstack06; + this.goalstack06 = this.goalstack05; + this.goalstack05 = this.goalstack04; + this.goalstack04 = this.goalstack03; + this.goalstack03 = this.goalstack02; + this.goalstack02 = this.goalstack01; + this.goalstack01 = this.goalcurrent; + this.goalcurrent = e; +} + +// remove first goal from stack +// (in other words: remove a prerequisite for reaching the later goals) +// (used when a spawnfunc_waypoint is reached) +void navigation_poproute(entity this) +{ + //print("bot ", etos(this), " pop\n"); + this.goalcurrent = this.goalstack01; + this.goalstack01 = this.goalstack02; + this.goalstack02 = this.goalstack03; + this.goalstack03 = this.goalstack04; + this.goalstack04 = this.goalstack05; + this.goalstack05 = this.goalstack06; + this.goalstack06 = this.goalstack07; + this.goalstack07 = this.goalstack08; + this.goalstack08 = this.goalstack09; + this.goalstack09 = this.goalstack10; + this.goalstack10 = this.goalstack11; + this.goalstack11 = this.goalstack12; + this.goalstack12 = this.goalstack13; + this.goalstack13 = this.goalstack14; + this.goalstack14 = this.goalstack15; + this.goalstack15 = this.goalstack16; + this.goalstack16 = this.goalstack17; + this.goalstack17 = this.goalstack18; + this.goalstack18 = this.goalstack19; + this.goalstack19 = this.goalstack20; + this.goalstack20 = this.goalstack21; + this.goalstack21 = this.goalstack22; + this.goalstack22 = this.goalstack23; + this.goalstack23 = this.goalstack24; + this.goalstack24 = this.goalstack25; + this.goalstack25 = this.goalstack26; + this.goalstack26 = this.goalstack27; + this.goalstack27 = this.goalstack28; + this.goalstack28 = this.goalstack29; + this.goalstack29 = this.goalstack30; + this.goalstack30 = this.goalstack31; + this.goalstack31 = NULL; +} + +float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist) +{ + float dist; + dist = vlen(v - org); + if (bestdist > dist) + { + traceline(v, org, true, ent); + if (trace_fraction == 1) + { + if (walkfromwp) + { + if (tracewalk(ent, v, PL_MIN_CONST, PL_MAX_CONST, org, bot_navigation_movemode)) + return true; + } + else + { + if (tracewalk(ent, org, PL_MIN_CONST, PL_MAX_CONST, v, bot_navigation_movemode)) + return true; + } + } + } + return false; +} + +// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon +entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except) +{ + vector pm1 = ent.origin + ent.mins; + vector pm2 = ent.origin + ent.maxs; + + // do two scans, because box test is cheaper + IL_EACH(g_waypoints, it != ent && it != except, + { + if(boxesoverlap(pm1, pm2, it.absmin, it.absmax)) + return it; + }); + + vector org = ent.origin + 0.5 * (ent.mins + ent.maxs); + org.z = ent.origin.z + ent.mins.z - PL_MIN_CONST.z; // player height + // TODO possibly make other code have the same support for bboxes + if(ent.tag_entity) + org = org + ent.tag_entity.origin; + if (navigation_testtracewalk) + te_plasmaburn(org); + + entity best = NULL; + vector v; + + // box check failed, try walk + IL_EACH(g_waypoints, it != ent, + { + if(it.wpisbox) + { + vector wm1 = it.origin + it.mins; + vector wm2 = it.origin + it.maxs; + v.x = bound(wm1_x, org.x, wm2_x); + v.y = bound(wm1_y, org.y, wm2_y); + v.z = bound(wm1_z, org.z, wm2_z); + } + else + v = it.origin; + if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist)) + { + bestdist = vlen(v - org); + best = it; + } + }); + return best; +} +entity navigation_findnearestwaypoint(entity ent, float walkfromwp) +{ + entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL); + if (autocvar_g_waypointeditor_auto) + { + entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp); + if (wp && !wp2) + wp.wpflags |= WAYPOINTFLAG_PROTECTED; + } + return wp; +} + +// finds the waypoints near the bot initiating a navigation query +float navigation_markroutes_nearestwaypoints(entity this, float maxdist) +{ + vector v, m1, m2; +// navigation_testtracewalk = true; + int c = 0; + IL_EACH(g_waypoints, !it.wpconsidered, + { + if (it.wpisbox) + { + m1 = it.origin + it.mins; + m2 = it.origin + it.maxs; + v = this.origin; + v.x = bound(m1_x, v.x, m2_x); + v.y = bound(m1_y, v.y, m2_y); + v.z = bound(m1_z, v.z, m2_z); + } + else + v = it.origin; + vector diff = v - this.origin; + diff.z = max(0, diff.z); + if(vdist(diff, <, maxdist)) + { + it.wpconsidered = true; + if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode)) + { + it.wpnearestpoint = v; + it.wpcost = vlen(v - this.origin) + it.dmg; + it.wpfire = 1; + it.enemy = NULL; + c = c + 1; + } + } + }); + //navigation_testtracewalk = false; + return c; +} + +// updates a path link if a spawnfunc_waypoint link is better than the current one +void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p) +{ + vector m1; + vector m2; + vector v; + if (wp.wpisbox) + { + m1 = wp.absmin; + m2 = wp.absmax; + v.x = bound(m1_x, p.x, m2_x); + v.y = bound(m1_y, p.y, m2_y); + v.z = bound(m1_z, p.z, m2_z); + } + else + v = wp.origin; + cost2 = cost2 + vlen(v - p); + if (wp.wpcost > cost2) + { + wp.wpcost = cost2; + wp.enemy = w; + wp.wpfire = 1; + wp.wpnearestpoint = v; + } +} + +// queries the entire spawnfunc_waypoint network for pathes leading away from the bot +void navigation_markroutes(entity this, entity fixed_source_waypoint) +{ + float cost, cost2; + vector p; + + IL_EACH(g_waypoints, true, + { + it.wpconsidered = false; + it.wpnearestpoint = '0 0 0'; + it.wpcost = 10000000; + it.wpfire = 0; + it.enemy = NULL; + }); + + if(fixed_source_waypoint) + { + fixed_source_waypoint.wpconsidered = true; + fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs); + fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; + fixed_source_waypoint.wpfire = 1; + fixed_source_waypoint.enemy = NULL; + } + else + { + // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found + // as this search is expensive we will use lower values if the bot is on the air + float increment, maxdistance; + if(IS_ONGROUND(this)) + { + increment = 750; + maxdistance = 50000; + } + else + { + increment = 500; + maxdistance = 1500; + } + + for(int j = increment; !navigation_markroutes_nearestwaypoints(this, j) && j < maxdistance; j += increment); + } + + bool searching = true; + while (searching) + { + searching = false; + IL_EACH(g_waypoints, it.wpfire, + { + searching = true; + it.wpfire = 0; + cost = it.wpcost; + p = it.wpnearestpoint; + entity wp; + wp = it.wp00;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp00mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp01mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp02mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp03mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp04mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp05mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp06mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp07mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp08mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp09mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp10mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp11mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp12mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp13mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp14mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp15mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp16mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp17mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp18mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp19mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp20mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp21mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp22mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp23mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp24mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp25mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp26mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp27mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp28mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp29mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp30mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + wp = it.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + it.wp31mincost) navigation_markroutes_checkwaypoint(it, wp, cost2, p); + }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} + }); + } +} + +// queries the entire spawnfunc_waypoint network for pathes leading to the bot +void navigation_markroutes_inverted(entity fixed_source_waypoint) +{ + float cost, cost2; + vector p; + IL_EACH(g_waypoints, true, + { + it.wpconsidered = false; + it.wpnearestpoint = '0 0 0'; + it.wpcost = 10000000; + it.wpfire = 0; + it.enemy = NULL; + }); + + if(fixed_source_waypoint) + { + fixed_source_waypoint.wpconsidered = true; + fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs); + fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint + fixed_source_waypoint.wpfire = 1; + fixed_source_waypoint.enemy = NULL; + } + else + { + error("need to start with a waypoint\n"); + } + + bool searching = true; + while (searching) + { + searching = false; + IL_EACH(g_waypoints, it.wpfire, + { + searching = true; + it.wpfire = 0; + cost = it.wpcost; // cost to walk from it to home + p = it.wpnearestpoint; + entity wp = it; + IL_EACH(g_waypoints, true, + { + if(wp != it.wp00) if(wp != it.wp01) if(wp != it.wp02) if(wp != it.wp03) + if(wp != it.wp04) if(wp != it.wp05) if(wp != it.wp06) if(wp != it.wp07) + if(wp != it.wp08) if(wp != it.wp09) if(wp != it.wp10) if(wp != it.wp11) + if(wp != it.wp12) if(wp != it.wp13) if(wp != it.wp14) if(wp != it.wp15) + if(wp != it.wp16) if(wp != it.wp17) if(wp != it.wp18) if(wp != it.wp19) + if(wp != it.wp20) if(wp != it.wp21) if(wp != it.wp22) if(wp != it.wp23) + if(wp != it.wp24) if(wp != it.wp25) if(wp != it.wp26) if(wp != it.wp27) + if(wp != it.wp28) if(wp != it.wp29) if(wp != it.wp30) if(wp != it.wp31) + continue; + cost2 = cost + it.dmg; + navigation_markroutes_checkwaypoint(wp, it, cost2, p); + }); + }); + } +} + +// 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) +{ + entity nwp; + vector o; + if (!e) + return; + + if(e.blacklisted) + return; + + o = (e.absmin + e.absmax) * 0.5; + + //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 - o, >, autocvar_bot_ai_navigation_jetpack_mindistance)) + { + vector pointa, pointb; + + LOG_DEBUG("jetpack ai: evaluating path for ", e.classname); + + // Point A + traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this); + pointa = trace_endpos - '0 0 1'; + + // Point B + traceline(o, o + '0 0 65535', MOVE_NORMAL, e); + pointb = trace_endpos - '0 0 1'; + + // Can I see these two points from the sky? + traceline(pointa, pointb, MOVE_NORMAL, this); + + if(trace_fraction==1) + { + LOG_DEBUG("jetpack ai: can bridge these two points"); + + // Lower the altitude of these points as much as possible + float zdistance, xydistance, cost, t, fuel; + vector down, npa, npb; + + down = '0 0 -1' * (STAT(PL_MAX, this).z - STAT(PL_MIN, this).z) * 10; + + do{ + npa = pointa + down; + npb = pointb + down; + + if(npa.z<=this.absmax.z) + break; + + if(npb.z<=e.absmax.z) + break; + + traceline(npa, npb, MOVE_NORMAL, this); + if(trace_fraction==1) + { + pointa = npa; + pointb = npb; + } + } + while(trace_fraction == 1); + + + // Rough estimation of fuel consumption + // (ignores acceleration and current xyz velocity) + xydistance = vlen(pointa - pointb); + zdistance = fabs(pointa.z - this.origin.z); + + t = zdistance / autocvar_g_jetpack_maxspeed_up; + 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)); + + // enough fuel ? + if(this.ammo_fuel>fuel) + { + // Estimate cost + // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship + // - between air and ground speeds) + + cost = xydistance / (autocvar_g_jetpack_maxspeed_side/autocvar_sv_maxspeed); + cost += zdistance / (autocvar_g_jetpack_maxspeed_up/autocvar_sv_maxspeed); + cost *= 1.5; + + // Compare against other goals + f = f * rangebias / (rangebias + cost); + + if (navigation_bestrating < f) + { + LOG_DEBUG("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")"); + navigation_bestrating = f; + navigation_bestgoal = e; + this.navigation_jetpack_goal = e; + this.navigation_jetpack_point = pointb; + } + return; + } + } + } + + //te_wizspike(e.origin); + //bprint(etos(e)); + //bprint("\n"); + // update the cached spawnfunc_waypoint link on a dynamic item entity + if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL)) + { + nwp = e; + } + else + { + float search; + + search = true; + + if(e.flags & FL_ITEM) + { + if (!(e.flags & FL_WEAPON)) + if(e.nearestwaypoint) + search = false; + } + else if (e.flags & FL_WEAPON) + { + if(e.classname != "droppedweapon") + if(e.nearestwaypoint) + search = false; + } + + if(search) + if (time > e.nearestwaypointtimeout) + { + nwp = navigation_findnearestwaypoint(e, true); + if(nwp) + e.nearestwaypoint = nwp; + else + { + LOG_DEBUG("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e)); + + if(e.flags & FL_ITEM) + e.blacklisted = true; + else if (e.flags & FL_WEAPON) + { + if(e.classname != "droppedweapon") + e.blacklisted = true; + } + + if(e.blacklisted) + { + LOG_DEBUG("The entity '", e.classname, "' is going to be excluded from path finding during this match"); + return; + } + } + + // TODO: Cleaner solution, probably handling this timeout from ctf.qc + if(e.classname=="item_flag_team") + e.nearestwaypointtimeout = time + 2; + else + e.nearestwaypointtimeout = time + random() * 3 + 5; + } + nwp = e.nearestwaypoint; + } + + LOG_DEBUG("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")"); + if (nwp) + if (nwp.wpcost < 10000000) + { + //te_wizspike(nwp.wpnearestpoint); + LOG_DEBUG(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = "); + f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint))); + LOG_DEBUG("considering ", e.classname, " (with rating ", ftos(f), ")"); + if (navigation_bestrating < f) + { + LOG_DEBUG("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")"); + navigation_bestrating = f; + navigation_bestgoal = e; + } + } +} + +// adds an item to the the goal stack with the path to a given item +bool navigation_routetogoal(entity this, entity e, vector startposition) +{ + this.goalentity = e; + + // if there is no goal, just exit + if (!e) + return false; + + this.navigation_hasgoals = true; + + // put the entity on the goal stack + //print("routetogoal ", etos(e), "\n"); + navigation_pushroute(this, e); + + if(g_jetpack) + if(e==this.navigation_jetpack_goal) + return true; + + // if it can reach the goal there is nothing more to do + if (tracewalk(this, startposition, STAT(PL_MIN, this), STAT(PL_MAX, this), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode)) + return true; + + // see if there are waypoints describing a path to the item + if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL)) + e = e.nearestwaypoint; + else + e = e.enemy; // we already have added it, so... + + if(e == NULL) + return false; + + for (;;) + { + // add the spawnfunc_waypoint to the path + navigation_pushroute(this, e); + e = e.enemy; + + if(e==NULL) + break; + } + + return false; +} + +// removes any currently touching waypoints from the goal stack +// (this is how bots detect if they reached a goal) +void navigation_poptouchedgoals(entity this) +{ + vector org, m1, m2; + org = this.origin; + m1 = org + this.mins; + m2 = org + this.maxs; + + while(this.goalcurrent && wasfreed(this.goalcurrent)) + navigation_poproute(this); + + if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) + { + // 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) + if(time - this.lastteleporttime < ((this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL) ? 2 : 0.15)) + { + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) + if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) + { + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; + } + navigation_poproute(this); + return; + } + } + + // If for some reason the bot is closer to the next goal, pop the current one + if(this.goalstack01 && !wasfreed(this.goalstack01)) + if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin)) + if(checkpvs(this.origin + this.view_ofs, this.goalstack01)) + if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode)) + { + LOG_DEBUG("path optimized for ", this.netname, ", removed a goal from the queue"); + navigation_poproute(this); + // TODO this may also be a nice idea to do "early" (e.g. by + // manipulating the vlen() comparisons) to shorten paths in + // general - this would make bots walk more "on rails" than + // "zigzagging" which they currently do with sufficiently + // random-like waypoints, and thus can make a nice bot + // personality property + } + + // HACK: remove players/bots as goals, they can lead a bot to unexpected places (cliffs, lava, etc) + // TODO: rate waypoints near the targetted player at that moment, instead of the player itself + if(IS_PLAYER(this.goalcurrent)) + navigation_poproute(this); + + // Loose goal touching check when running + if(this.aistatus & AI_STATUS_RUNNING) + if(this.speed >= autocvar_sv_maxspeed) // if -really- running + if(this.goalcurrent.classname=="waypoint") + if(!(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)) + { + if(vdist(this.origin - this.goalcurrent.origin, <, 150)) + { + traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL); + if(trace_fraction==1) + { + // Detect personal waypoints + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) + if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) + { + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; + } + + navigation_poproute(this); + } + } + } + + while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax)) + { + if((this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT)) + break; + + // Detect personal waypoints + if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) + if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) + { + this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; + this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; + } + + navigation_poproute(this); + } +} + +// begin a goal selection session (queries spawnfunc_waypoint network) +void navigation_goalrating_start(entity this) +{ + if(this.aistatus & AI_STATUS_STUCK) + return; + + this.navigation_jetpack_goal = NULL; + navigation_bestrating = -1; + this.navigation_hasgoals = false; + navigation_clearroute(this); + navigation_bestgoal = NULL; + navigation_markroutes(this, NULL); +} + +// ends a goal selection session (updates goal stack to the best goal) +void navigation_goalrating_end(entity this) +{ + if(this.aistatus & AI_STATUS_STUCK) + return; + + navigation_routetogoal(this, navigation_bestgoal, this.origin); + LOG_DEBUG("best goal ", this.goalcurrent.classname); + + // If the bot got stuck then try to reach the farthest waypoint + if (!this.navigation_hasgoals) + if (autocvar_bot_wander_enable) + { + if (!(this.aistatus & AI_STATUS_STUCK)) + { + LOG_DEBUG(this.netname, " cannot walk to any goal"); + this.aistatus |= AI_STATUS_STUCK; + } + + this.navigation_hasgoals = false; // Reset this value + } +} + +void botframe_updatedangerousobjects(float maxupdate) +{ + vector m1, m2, v, o; + float c, d, danger; + c = 0; + IL_EACH(g_waypoints, true, + { + danger = 0; + m1 = it.mins; + m2 = it.maxs; + IL_EACH(g_bot_dodge, it.bot_dodge, + { + v = it.origin; + v.x = bound(m1_x, v.x, m2_x); + v.y = bound(m1_y, v.y, m2_y); + v.z = bound(m1_z, v.z, m2_z); + o = (it.absmin + it.absmax) * 0.5; + d = it.bot_dodgerating - vlen(o - v); + if (d > 0) + { + traceline(o, v, true, NULL); + if (trace_fraction == 1) + danger = danger + d; + } + }); + it.dmg = danger; + c = c + 1; + if (c >= maxupdate) + break; + }); +} + +void navigation_unstuck(entity this) +{ + float search_radius = 1000; + + if (!autocvar_bot_wander_enable) + return; + + if (!bot_waypoint_queue_owner) + { + LOG_DEBUG(this.netname, " stuck, taking over the waypoints queue"); + bot_waypoint_queue_owner = this; + bot_waypoint_queue_bestgoal = NULL; + bot_waypoint_queue_bestgoalrating = 0; + } + + if(bot_waypoint_queue_owner!=this) + return; + + if (bot_waypoint_queue_goal) + { + // evaluate the next goal on the queue + float d = vlen2(this.origin - bot_waypoint_queue_goal.origin); + LOG_DEBUG(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d)); + if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, this), STAT(PL_MAX, this), bot_waypoint_queue_goal.origin, bot_navigation_movemode)) + { + if( d > bot_waypoint_queue_bestgoalrating) + { + bot_waypoint_queue_bestgoalrating = d; + bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal; + } + } + bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal; + + if (!bot_waypoint_queue_goal) + { + if (bot_waypoint_queue_bestgoal) + { + LOG_DEBUG(this.netname, " stuck, reachable waypoint found, heading to it"); + navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin); + this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; + this.aistatus &= ~AI_STATUS_STUCK; + } + else + { + LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all"); + } + + bot_waypoint_queue_owner = NULL; + } + } + else + { + if(bot_strategytoken!=this) + return; + + // build a new queue + LOG_DEBUG(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu"); + + entity first = NULL; + + FOREACH_ENTITY_RADIUS(this.origin, search_radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED), + { + if(bot_waypoint_queue_goal) + bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = it; + else + first = it; + + bot_waypoint_queue_goal = it; + bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL; + }); + + if (first) + bot_waypoint_queue_goal = first; + else + { + LOG_DEBUG(this.netname, " stuck, cannot walk to any waypoint at all"); + bot_waypoint_queue_owner = NULL; + } + } +} + +// Support for debugging tracewalk visually + +void debugresetnodes() +{ + debuglastnode = '0 0 0'; +} + +void debugnode(entity this, vector node) +{ + if (!IS_PLAYER(this)) + return; + + if(debuglastnode=='0 0 0') + { + debuglastnode = node; + return; + } + + te_lightning2(NULL, node, debuglastnode); + debuglastnode = node; +} + +void debugnodestatus(vector position, float status) +{ + vector c; + + switch (status) + { + case DEBUG_NODE_SUCCESS: + c = '0 15 0'; + break; + case DEBUG_NODE_WARNING: + c = '15 15 0'; + break; + case DEBUG_NODE_FAIL: + c = '15 0 0'; + break; + default: + c = '15 15 15'; + } + + te_customflash(position, 40, 2, c); +} + +// Support for debugging the goal stack visually + +.float goalcounter; +.vector lastposition; + +// Debug the goal stack visually +void debuggoalstack(entity this) +{ + entity goal; + vector org, go; + + if(this.goalcounter==0)goal=this.goalcurrent; + else if(this.goalcounter==1)goal=this.goalstack01; + else if(this.goalcounter==2)goal=this.goalstack02; + else if(this.goalcounter==3)goal=this.goalstack03; + else if(this.goalcounter==4)goal=this.goalstack04; + else if(this.goalcounter==5)goal=this.goalstack05; + else if(this.goalcounter==6)goal=this.goalstack06; + else if(this.goalcounter==7)goal=this.goalstack07; + else if(this.goalcounter==8)goal=this.goalstack08; + else if(this.goalcounter==9)goal=this.goalstack09; + else if(this.goalcounter==10)goal=this.goalstack10; + else if(this.goalcounter==11)goal=this.goalstack11; + else if(this.goalcounter==12)goal=this.goalstack12; + else if(this.goalcounter==13)goal=this.goalstack13; + else if(this.goalcounter==14)goal=this.goalstack14; + else if(this.goalcounter==15)goal=this.goalstack15; + else if(this.goalcounter==16)goal=this.goalstack16; + else if(this.goalcounter==17)goal=this.goalstack17; + else if(this.goalcounter==18)goal=this.goalstack18; + else if(this.goalcounter==19)goal=this.goalstack19; + else if(this.goalcounter==20)goal=this.goalstack20; + else if(this.goalcounter==21)goal=this.goalstack21; + else if(this.goalcounter==22)goal=this.goalstack22; + else if(this.goalcounter==23)goal=this.goalstack23; + else if(this.goalcounter==24)goal=this.goalstack24; + else if(this.goalcounter==25)goal=this.goalstack25; + else if(this.goalcounter==26)goal=this.goalstack26; + else if(this.goalcounter==27)goal=this.goalstack27; + else if(this.goalcounter==28)goal=this.goalstack28; + else if(this.goalcounter==29)goal=this.goalstack29; + else if(this.goalcounter==30)goal=this.goalstack30; + else if(this.goalcounter==31)goal=this.goalstack31; + else goal=NULL; + + if(goal==NULL) + { + this.goalcounter = 0; + this.lastposition='0 0 0'; + return; + } + + if(this.lastposition=='0 0 0') + org = this.origin; + else + org = this.lastposition; + + + go = ( goal.absmin + goal.absmax ) * 0.5; + te_lightning2(NULL, org, go); + this.lastposition = go; + + this.goalcounter++; +} diff --git a/qcsrc/server/bot/default/navigation.qh b/qcsrc/server/bot/default/navigation.qh new file mode 100644 index 0000000000..ad01776652 --- /dev/null +++ b/qcsrc/server/bot/default/navigation.qh @@ -0,0 +1,77 @@ +#pragma once +/* + * Globals and Fields + */ + +float navigation_bestrating; +float bot_navigation_movemode; +float navigation_testtracewalk; + +vector jumpstepheightvec; +vector stepheightvec; + +entity navigation_bestgoal; + +// stack of current goals (the last one of which may be an item or other +// desirable object, the rest are typically waypoints to reach it) +.entity goalcurrent, goalstack01, goalstack02, goalstack03; +.entity goalstack04, goalstack05, goalstack06, goalstack07; +.entity goalstack08, goalstack09, goalstack10, goalstack11; +.entity goalstack12, goalstack13, goalstack14, goalstack15; +.entity goalstack16, goalstack17, goalstack18, goalstack19; +.entity goalstack20, goalstack21, goalstack22, goalstack23; +.entity goalstack24, goalstack25, goalstack26, goalstack27; +.entity goalstack28, goalstack29, goalstack30, goalstack31; +.entity nearestwaypoint; + +.float nearestwaypointtimeout; +.float navigation_hasgoals; +.float lastteleporttime; + +.float blacklisted; + +.entity navigation_jetpack_goal; +.vector navigation_jetpack_point; + +const float DEBUG_NODE_SUCCESS = 1; +const float DEBUG_NODE_WARNING = 2; +const float DEBUG_NODE_FAIL = 3; +vector debuglastnode; + +entity bot_waypoint_queue_owner; // Owner of the temporary list of goals +entity bot_waypoint_queue_goal; // Head of the temporary list of goals +.entity bot_waypoint_queue_nextgoal; +entity bot_waypoint_queue_bestgoal; +float bot_waypoint_queue_bestgoalrating; + +/* + * Functions + */ + +void debugresetnodes(); +void debugnode(entity this, vector node); +void debugnodestatus(vector position, float status); + +void debuggoalstack(entity this); + +float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode); + +float navigation_markroutes_nearestwaypoints(entity this, float maxdist); +float navigation_routetogoal(entity this, entity e, vector startposition); + +void navigation_clearroute(entity this); +void navigation_pushroute(entity this, entity e); +void navigation_poproute(entity this); +void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p); +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_poptouchedgoals(entity this); +void navigation_goalrating_start(entity this); +void navigation_goalrating_end(entity this); +void navigation_unstuck(entity this); + +void botframe_updatedangerousobjects(float maxupdate); + +entity navigation_findnearestwaypoint(entity ent, float walkfromwp); +float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist); diff --git a/qcsrc/server/bot/default/scripting.qc b/qcsrc/server/bot/default/scripting.qc new file mode 100644 index 0000000000..0d34ae3c24 --- /dev/null +++ b/qcsrc/server/bot/default/scripting.qc @@ -0,0 +1,1333 @@ +#include "scripting.qh" + +#include "cvars.qh" + +#include +#include + +#include "bot.qh" + +.int state; + +.float bot_cmdqueuebuf_allocated; +.float bot_cmdqueuebuf; +.float bot_cmdqueuebuf_start; +.float bot_cmdqueuebuf_end; + +void bot_clearqueue(entity bot) +{ + if(!bot.bot_cmdqueuebuf_allocated) + return; + buf_del(bot.bot_cmdqueuebuf); + bot.bot_cmdqueuebuf_allocated = false; + LOG_TRACE("bot ", bot.netname, " queue cleared"); +} + +void bot_queuecommand(entity bot, string cmdstring) +{ + if(!bot.bot_cmdqueuebuf_allocated) + { + bot.bot_cmdqueuebuf = buf_create(); + bot.bot_cmdqueuebuf_allocated = true; + bot.bot_cmdqueuebuf_start = 0; + bot.bot_cmdqueuebuf_end = 0; + } + + bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring); + + // if the command was a "sound" command, precache the sound NOW + // this prevents lagging! + { + float sp; + string parm; + string cmdstr; + + sp = strstrofs(cmdstring, " ", 0); + if(sp >= 0) + { + parm = substring(cmdstring, sp + 1, -1); + cmdstr = substring(cmdstring, 0, sp); + if(cmdstr == "sound") + { + // find the LAST word + for (;;) + { + sp = strstrofs(parm, " ", 0); + if(sp < 0) + break; + parm = substring(parm, sp + 1, -1); + } + precache_sound(parm); + } + } + } + + bot.bot_cmdqueuebuf_end += 1; +} + +void bot_dequeuecommand(entity bot, float idx) +{ + if(!bot.bot_cmdqueuebuf_allocated) + error("dequeuecommand but no queue allocated"); + if(idx < bot.bot_cmdqueuebuf_start) + error("dequeueing a command in the past"); + if(idx >= bot.bot_cmdqueuebuf_end) + error("dequeueing a command in the future"); + bufstr_set(bot.bot_cmdqueuebuf, idx, ""); + if(idx == bot.bot_cmdqueuebuf_start) + bot.bot_cmdqueuebuf_start += 1; + if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end) + bot_clearqueue(bot); +} + +string bot_readcommand(entity bot, float idx) +{ + if(!bot.bot_cmdqueuebuf_allocated) + error("readcommand but no queue allocated"); + if(idx < bot.bot_cmdqueuebuf_start) + error("reading a command in the past"); + if(idx >= bot.bot_cmdqueuebuf_end) + error("reading a command in the future"); + return bufstr_get(bot.bot_cmdqueuebuf, idx); +} + +bool bot_havecommand(entity this, int idx) +{ + if(!this.bot_cmdqueuebuf_allocated) + return false; + if(idx < this.bot_cmdqueuebuf_start) + return false; + if(idx >= this.bot_cmdqueuebuf_end) + return false; + return true; +} + +const int MAX_BOT_PLACES = 4; +.float bot_places_count; +.entity bot_places[MAX_BOT_PLACES]; +.string bot_placenames[MAX_BOT_PLACES]; +entity bot_getplace(entity this, string placename) +{ + entity e; + if(substring(placename, 0, 1) == "@") + { + int i, p; + placename = substring(placename, 1, -1); + string s, s2; + for(i = 0; i < this.bot_places_count; ++i) + if(this.(bot_placenames[i]) == placename) + return this.(bot_places[i]); + // now: i == this.bot_places_count + s = s2 = cvar_string(placename); + p = strstrofs(s2, " ", 0); + if(p >= 0) + { + s = substring(s2, 0, p); + //print("places: ", placename, " -> ", cvar_string(placename), "\n"); + cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s)); + //print("places: ", placename, " := ", cvar_string(placename), "\n"); + } + e = find(NULL, targetname, s); + if(!e) + LOG_INFO("invalid place ", s, "\n"); + if(i < MAX_BOT_PLACES) + { + this.(bot_placenames[i]) = strzone(placename); + this.(bot_places[i]) = e; + this.bot_places_count += 1; + } + return e; + } + else + { + e = find(NULL, targetname, placename); + if(!e) + LOG_INFO("invalid place ", placename, "\n"); + return e; + } +} + + +// Initialize global commands list +// NOTE: New commands should be initialized here +void bot_commands_init() +{ + bot_cmd_string[BOT_CMD_NULL] = ""; + bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_PAUSE] = "pause"; + bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_CONTINUE] = "continue"; + bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_WAIT] = "wait"; + bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmd_string[BOT_CMD_TURN] = "turn"; + bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmd_string[BOT_CMD_MOVETO] = "moveto"; + bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR; + + bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget"; + bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal"; + bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_CC] = "cc"; + bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_IF] = "if"; + bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_ELSE] = "else"; + bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_FI] = "fi"; + bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim"; + bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_AIM] = "aim"; + bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget"; + bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey"; + bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey"; + bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon"; + bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmd_string[BOT_CMD_IMPULSE] = "impulse"; + bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until"; + bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmd_string[BOT_CMD_BARRIER] = "barrier"; + bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE; + + bot_cmd_string[BOT_CMD_CONSOLE] = "console"; + bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_SOUND] = "sound"; + bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING; + + bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire"; + bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT; + + bot_cmds_initialized = true; +} + +// Returns first bot with matching name +entity find_bot_by_name(string name) +{ + FOREACH_CLIENT(IS_BOT_CLIENT(it) && it.netname == name, + { + return it; + }); + + return NULL; +} + +// Returns a bot by number on list +entity find_bot_by_number(float number) +{ + entity bot; + float c = 0; + + if(!number) + return NULL; + + bot = findchainflags(flags, FL_CLIENT); // TODO: doesn't findchainflags loop backwards through entities? + while (bot) + { + if(IS_BOT_CLIENT(bot)) + { + if(++c==number) + return bot; + } + bot = bot.chain; + } + + return NULL; +} + +float bot_decodecommand(string cmdstring) +{ + float cmd_parm_type; + float sp; + string parm; + + sp = strstrofs(cmdstring, " ", 0); + if(sp < 0) + { + parm = ""; + } + else + { + parm = substring(cmdstring, sp + 1, -1); + cmdstring = substring(cmdstring, 0, sp); + } + + if(!bot_cmds_initialized) + bot_commands_init(); + + int i; + for(i=1;i \n")); + + LOG_INFO("Description: "); + switch(i) + { + case BOT_CMD_PAUSE: + LOG_INFO("Stops the bot completely. Any command other than 'continue' will be ignored."); + break; + case BOT_CMD_CONTINUE: + LOG_INFO("Disable paused status"); + break; + case BOT_CMD_WAIT: + LOG_INFO("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed"); + break; + case BOT_CMD_WAIT_UNTIL: + LOG_INFO("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed"); + break; + case BOT_CMD_BARRIER: + LOG_INFO("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed"); + break; + case BOT_CMD_TURN: + LOG_INFO("Look to the right or left N degrees. For turning to the left use positive numbers."); + break; + case BOT_CMD_MOVETO: + LOG_INFO("Walk to an specific coordinate on the map. Usage: moveto \"x y z\""); + break; + case BOT_CMD_MOVETOTARGET: + LOG_INFO("Walk to the specific target on the map"); + break; + case BOT_CMD_RESETGOAL: + LOG_INFO("Resets the goal stack"); + break; + case BOT_CMD_CC: + LOG_INFO("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;"); + break; + case BOT_CMD_IF: + LOG_INFO("Perform simple conditional execution.\n"); + LOG_INFO("Syntax: \n"); + LOG_INFO(" sv_cmd .. if \"condition\"\n"); + LOG_INFO(" sv_cmd .. \n"); + LOG_INFO(" sv_cmd .. \n"); + LOG_INFO(" sv_cmd .. else\n"); + LOG_INFO(" sv_cmd .. \n"); + LOG_INFO(" sv_cmd .. \n"); + LOG_INFO(" sv_cmd .. fi\n"); + LOG_INFO("Conditions: a=b, a>b, a50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;"); + break; + case BOT_CMD_RESETAIM: + LOG_INFO("Points the aim to the coordinates x,y 0,0"); + break; + case BOT_CMD_AIM: + LOG_INFO("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n"); + LOG_INFO("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n"); + LOG_INFO("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n"); + LOG_INFO(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds"); + break; + case BOT_CMD_AIMTARGET: + LOG_INFO("Points the aim to given target"); + break; + case BOT_CMD_PRESSKEY: + LOG_INFO("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n"); + LOG_INFO("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called"); + LOG_INFO("Note: The script will not return the control to the bot ai until all keys are released"); + break; + case BOT_CMD_RELEASEKEY: + LOG_INFO("Release previoulsy used keys. Use the parameter \"all\" to release all keys"); + break; + case BOT_CMD_SOUND: + LOG_INFO("play sound file at bot location"); + break; + case BOT_CMD_DEBUG_ASSERT_CANFIRE: + LOG_INFO("verify the state of the weapon entity"); + break; + default: + LOG_INFO("This command has no description yet."); + break; + } + LOG_INFO("\n"); + } +} + +void bot_list_commands() +{ + int i; + string ptype; + + if(!bot_cmds_initialized) + bot_commands_init(); + + LOG_INFO("List of all available commands:\n"); + LOG_INFO(" Command - Parameter Type\n"); + + for(i=1;i \n")); + } +} + +// Commands code +.int bot_exec_status; + +float bot_cmd_cc(entity this) +{ + SV_ParseClientCommand(this, bot_cmd.bot_cmd_parm_string); + return CMD_STATUS_FINISHED; +} + +float bot_cmd_impulse(entity this) +{ + this.impulse = bot_cmd.bot_cmd_parm_float; + return CMD_STATUS_FINISHED; +} + +float bot_cmd_continue(entity this) +{ + bot_relinkplayerlist(); + this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED; + return CMD_STATUS_FINISHED; +} + +.float bot_cmd_wait_time; +float bot_cmd_wait(entity this) +{ + if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING) + { + if(time>=this.bot_cmd_wait_time) + { + this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING; + return CMD_STATUS_FINISHED; + } + else + return CMD_STATUS_EXECUTING; + } + + this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float; + this.bot_exec_status |= BOT_EXEC_STATUS_WAITING; + return CMD_STATUS_EXECUTING; +} + +float bot_cmd_wait_until(entity this) +{ + if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime) + { + this.bot_exec_status |= BOT_EXEC_STATUS_WAITING; + return CMD_STATUS_EXECUTING; + } + this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING; + return CMD_STATUS_FINISHED; +} + +float bot_cmd_barrier(entity this) +{ + // 0 = no barrier, 1 = waiting, 2 = waiting finished + + if(this.bot_barrier == 0) // initialization + { + this.bot_barrier = 1; + + //this.colormod = '4 4 0'; + } + + if(this.bot_barrier == 1) // find other bots + { + FOREACH_CLIENT(it.isbot, LAMBDA( + if(it.bot_cmdqueuebuf_allocated) + if(it.bot_barrier != 1) + return CMD_STATUS_EXECUTING; // not all are at the barrier yet + )); + + // all bots hit the barrier! + + // acknowledge barrier + FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2)); + + bot_barriertime = time; + } + + // if we get here, the barrier is finished + // so end it... + this.bot_barrier = 0; + //this.colormod = '0 0 0'; + + return CMD_STATUS_FINISHED; +} + +float bot_cmd_turn(entity this) +{ + this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float; + this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360; + return CMD_STATUS_FINISHED; +} + +float bot_cmd_select_weapon(entity this) +{ + float id = bot_cmd.bot_cmd_parm_float; + + if(id < WEP_FIRST || id > WEP_LAST) + return CMD_STATUS_ERROR; + + if(client_hasweapon(this, Weapons_from(id), true, false)) + PS(this).m_switchweapon = Weapons_from(id); + else + return CMD_STATUS_ERROR; + + return CMD_STATUS_FINISHED; +} + +.int bot_cmd_condition_status; + +const int CMD_CONDITION_NONE = 0; +const int CMD_CONDITION_true = 1; +const int CMD_CONDITION_false = 2; +const int CMD_CONDITION_true_BLOCK = 4; +const int CMD_CONDITION_false_BLOCK = 8; + +float bot_cmd_eval(entity this, string expr) +{ + // Search for numbers + if(IS_DIGIT(substring(expr, 0, 1))) + return stof(expr); + + // Search for cvars + if(substring(expr, 0, 5)=="cvar.") + return cvar(substring(expr, 5, strlen(expr))); + + // Search for fields + switch(expr) + { + case "health": + return this.health; + case "speed": + return vlen(this.velocity); + case "flagcarrier": + return ((this.flagcarried!=NULL)); + } + + LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n")); + return 0; +} + +float bot_cmd_if(entity this) +{ + string expr, val_a, val_b; + float cmpofs; + + if(this.bot_cmd_condition_status != CMD_CONDITION_NONE) + { + // Only one "if" block is allowed at time + LOG_INFO("ERROR: Only one conditional block can be processed at time"); + bot_clearqueue(this); + return CMD_STATUS_ERROR; + } + + this.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK; + + // search for operators + expr = bot_cmd.bot_cmd_parm_string; + + cmpofs = strstrofs(expr,"=",0); + + if(cmpofs>0) + { + val_a = substring(expr,0,cmpofs); + val_b = substring(expr,cmpofs+1,strlen(expr)); + + if(bot_cmd_eval(this, val_a)==bot_cmd_eval(this, val_b)) + this.bot_cmd_condition_status |= CMD_CONDITION_true; + else + this.bot_cmd_condition_status |= CMD_CONDITION_false; + + return CMD_STATUS_FINISHED; + } + + cmpofs = strstrofs(expr,">",0); + + if(cmpofs>0) + { + val_a = substring(expr,0,cmpofs); + val_b = substring(expr,cmpofs+1,strlen(expr)); + + if(bot_cmd_eval(this, val_a)>bot_cmd_eval(this, val_b)) + this.bot_cmd_condition_status |= CMD_CONDITION_true; + else + this.bot_cmd_condition_status |= CMD_CONDITION_false; + + return CMD_STATUS_FINISHED; + } + + cmpofs = strstrofs(expr,"<",0); + + if(cmpofs>0) + { + val_a = substring(expr,0,cmpofs); + val_b = substring(expr,cmpofs+1,strlen(expr)); + + if(bot_cmd_eval(this, val_a)=this.bot_cmd_aim_endtime) + { + this.bot_cmd_aim_endtime = 0; + return CMD_STATUS_FINISHED; + } + else + return CMD_STATUS_EXECUTING; + } + + // New aiming direction + string parms; + float tokens, step; + + parms = bot_cmd.bot_cmd_parm_string; + + tokens = tokenizebyseparator(parms, " "); + + if(tokens<2||tokens>3) + return CMD_STATUS_ERROR; + + step = (tokens == 3) ? stof(argv(2)) : 0; + + if(step == 0) + { + this.v_angle_x -= stof(argv(1)); + this.v_angle_y += stof(argv(0)); + return CMD_STATUS_FINISHED; + } + + this.bot_cmd_aim_begin = this.v_angle; + + this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1)); + this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0)); + this.bot_cmd_aim_end_z = 0; + + this.bot_cmd_aim_begintime = time; + this.bot_cmd_aim_endtime = time + step; + + return CMD_STATUS_EXECUTING; +} + +float bot_cmd_aimtarget(entity this) +{ + if(this.bot_cmd_aim_endtime) + { + return bot_cmd_aim(this); + } + + entity e; + string parms; + vector v; + float tokens, step; + + parms = bot_cmd.bot_cmd_parm_string; + + tokens = tokenizebyseparator(parms, " "); + + e = bot_getplace(this, argv(0)); + if(!e) + return CMD_STATUS_ERROR; + + v = e.origin + (e.mins + e.maxs) * 0.5; + + if(tokens==1) + { + this.v_angle = vectoangles(v - (this.origin + this.view_ofs)); + this.v_angle_x = -this.v_angle.x; + return CMD_STATUS_FINISHED; + } + + if(tokens<1||tokens>2) + return CMD_STATUS_ERROR; + + step = stof(argv(1)); + + this.bot_cmd_aim_begin = this.v_angle; + this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs)); + this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x; + + this.bot_cmd_aim_begintime = time; + this.bot_cmd_aim_endtime = time + step; + + return CMD_STATUS_EXECUTING; +} + +.int bot_cmd_keys; + +const int BOT_CMD_KEY_NONE = 0; +const int BOT_CMD_KEY_FORWARD = BIT(0); +const int BOT_CMD_KEY_BACKWARD = BIT(1); +const int BOT_CMD_KEY_RIGHT = BIT(2); +const int BOT_CMD_KEY_LEFT = BIT(3); +const int BOT_CMD_KEY_JUMP = BIT(4); +const int BOT_CMD_KEY_ATTACK1 = BIT(5); +const int BOT_CMD_KEY_ATTACK2 = BIT(6); +const int BOT_CMD_KEY_USE = BIT(7); +const int BOT_CMD_KEY_HOOK = BIT(8); +const int BOT_CMD_KEY_CROUCH = BIT(9); +const int BOT_CMD_KEY_CHAT = BIT(10); + +bool bot_presskeys(entity this) +{ + this.movement = '0 0 0'; + PHYS_INPUT_BUTTON_JUMP(this) = false; + PHYS_INPUT_BUTTON_CROUCH(this) = false; + PHYS_INPUT_BUTTON_ATCK(this) = false; + PHYS_INPUT_BUTTON_ATCK2(this) = false; + PHYS_INPUT_BUTTON_USE(this) = false; + PHYS_INPUT_BUTTON_HOOK(this) = false; + PHYS_INPUT_BUTTON_CHAT(this) = false; + + if(this.bot_cmd_keys == BOT_CMD_KEY_NONE) + return false; + + if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD) + this.movement_x = autocvar_sv_maxspeed; + else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD) + this.movement_x = -autocvar_sv_maxspeed; + + if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT) + this.movement_y = autocvar_sv_maxspeed; + else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT) + this.movement_y = -autocvar_sv_maxspeed; + + if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP) + PHYS_INPUT_BUTTON_JUMP(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH) + PHYS_INPUT_BUTTON_CROUCH(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1) + PHYS_INPUT_BUTTON_ATCK(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2) + PHYS_INPUT_BUTTON_ATCK2(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_USE) + PHYS_INPUT_BUTTON_USE(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK) + PHYS_INPUT_BUTTON_HOOK(this) = true; + + if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT) + PHYS_INPUT_BUTTON_CHAT(this) = true; + + return true; +} + + +float bot_cmd_keypress_handler(entity this, string key, float enabled) +{ + switch(key) + { + case "all": + if(enabled) + this.bot_cmd_keys = power2of(20) - 1; // >:) + else + this.bot_cmd_keys = BOT_CMD_KEY_NONE; + case "forward": + if(enabled) + { + this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD; + this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD; + } + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD; + break; + case "backward": + if(enabled) + { + this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD; + this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD; + } + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD; + break; + case "left": + if(enabled) + { + this.bot_cmd_keys |= BOT_CMD_KEY_LEFT; + this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT; + } + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT; + break; + case "right": + if(enabled) + { + this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT; + this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT; + } + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT; + break; + case "jump": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_JUMP; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP; + break; + case "crouch": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH; + break; + case "attack1": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1; + break; + case "attack2": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2; + break; + case "use": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_USE; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_USE; + break; + case "hook": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_HOOK; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK; + break; + case "chat": + if(enabled) + this.bot_cmd_keys |= BOT_CMD_KEY_CHAT; + else + this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT; + break; + default: + break; + } + + return CMD_STATUS_FINISHED; +} + +float bot_cmd_presskey(entity this) +{ + string key; + + key = bot_cmd.bot_cmd_parm_string; + + bot_cmd_keypress_handler(this, key,true); + + return CMD_STATUS_FINISHED; +} + +float bot_cmd_releasekey(entity this) +{ + string key; + + key = bot_cmd.bot_cmd_parm_string; + + return bot_cmd_keypress_handler(this, key,false); +} + +float bot_cmd_pause(entity this) +{ + PHYS_INPUT_BUTTON_DRAG(this) = false; + PHYS_INPUT_BUTTON_USE(this) = false; + PHYS_INPUT_BUTTON_ATCK(this) = false; + PHYS_INPUT_BUTTON_JUMP(this) = false; + PHYS_INPUT_BUTTON_HOOK(this) = false; + PHYS_INPUT_BUTTON_CHAT(this) = false; + PHYS_INPUT_BUTTON_ATCK2(this) = false; + PHYS_INPUT_BUTTON_CROUCH(this) = false; + + this.movement = '0 0 0'; + this.bot_cmd_keys = BOT_CMD_KEY_NONE; + + bot_clear(this); + this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED; + return CMD_STATUS_FINISHED; +} + +float bot_cmd_moveto(entity this) +{ + return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector); +} + +float bot_cmd_movetotarget(entity this) +{ + entity e; + e = bot_getplace(this, bot_cmd.bot_cmd_parm_string); + if(!e) + return CMD_STATUS_ERROR; + return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5); +} + +float bot_cmd_resetgoal(entity this) +{ + return this.cmd_resetgoal(this); +} + + +float bot_cmd_sound(entity this) +{ + string f; + f = bot_cmd.bot_cmd_parm_string; + + float n = tokenizebyseparator(f, " "); + + string sample = f; + float chan = CH_WEAPON_B; + float vol = VOL_BASE; + float atten = ATTEN_MIN; + + if(n >= 1) + sample = argv(n - 1); + if(n >= 2) + chan = stof(argv(0)); + if(n >= 3) + vol = stof(argv(1)); + if(n >= 4) + atten = stof(argv(2)); + + precache_sound(f); + _sound(this, chan, sample, vol, atten); + + return CMD_STATUS_FINISHED; +} + +.entity tuba_note; +float bot_cmd_debug_assert_canfire(entity this) +{ + float f = bot_cmd.bot_cmd_parm_float; + + int slot = 0; // TODO: unhardcode? + .entity weaponentity = weaponentities[slot]; + if(this.(weaponentity).state != WS_READY) + { + if(f) + { + this.colormod = '0 8 8'; + LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n"); + } + } + else if(ATTACK_FINISHED(this, slot) > time) + { + if(f) + { + this.colormod = '8 0 8'; + LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n"); + } + } + else if(this.tuba_note) + { + if(f) + { + this.colormod = '8 0 0'; + LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n"); + } + } + else + { + if(!f) + { + this.colormod = '8 8 0'; + LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n"); + } + } + + return CMD_STATUS_FINISHED; +} + +// + +void bot_command_executed(entity this, bool rm) +{ + entity cmd; + + cmd = bot_cmd; + + if(rm) + bot_dequeuecommand(this, this.bot_cmd_execution_index); + + this.bot_cmd_execution_index++; +} + +void bot_setcurrentcommand(entity this) +{ + bot_cmd = NULL; + + if(!this.bot_cmd_current) + { + this.bot_cmd_current = new_pure(bot_cmd); + } + + bot_cmd = this.bot_cmd_current; + if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0) + { + if(bot_havecommand(this, this.bot_cmd_execution_index)) + { + string cmdstring; + cmdstring = bot_readcommand(this, this.bot_cmd_execution_index); + if(bot_decodecommand(cmdstring)) + { + bot_cmd.owner = this; + bot_cmd.bot_cmd_index = this.bot_cmd_execution_index; + } + else + { + // Invalid command, remove from queue + bot_cmd = NULL; + bot_dequeuecommand(this, this.bot_cmd_execution_index); + this.bot_cmd_execution_index++; + } + } + else + bot_cmd = NULL; + } +} + +void bot_resetqueues() +{ + FOREACH_CLIENT(it.isbot, LAMBDA( + it.bot_cmd_execution_index = 0; + bot_clearqueue(it); + // also, cancel all barriers + 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; + } + it.bot_places_count = 0; + )); + + bot_barriertime = time; +} + +// Here we map commands to functions and deal with complex interactions between commands and execution states +// NOTE: Of course you need to include your commands here too :) +float bot_execute_commands_once(entity this) +{ + float status, ispressingkey; + + // Find command + bot_setcurrentcommand(this); + + // Ignore all commands except continue when the bot is paused + if(!(self.bot_exec_status & BOT_EXEC_STATUS_PAUSED)) + { + // if we have no bot command, better return + // old logic kept pressing previously pressed keys, but that has problems + // (namely, it means you cannot make a bot "normal" ever again) + // to keep a bot walking for a while, use the "wait" bot command + if(bot_cmd == world) + return 0; + } + else if(bot_cmd.bot_cmd_type != BOT_CMD_CONTINUE) + { + if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL) + { + bot_command_executed(this, true); + LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n"); + } + return 1; + } + + // Keep pressing keys raised by the "presskey" command + ispressingkey = boolean(bot_presskeys(this)); + + // Handle conditions + if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)) + if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK) + { + bot_command_executed(this, true); + return -1; + } + else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK) + { + bot_command_executed(this, true); + return -1; + } + + // Map commands to functions + switch(bot_cmd.bot_cmd_type) + { + case BOT_CMD_NULL: + return ispressingkey; + //break; + case BOT_CMD_PAUSE: + status = bot_cmd_pause(this); + break; + case BOT_CMD_CONTINUE: + status = bot_cmd_continue(this); + break; + case BOT_CMD_WAIT: + status = bot_cmd_wait(this); + break; + case BOT_CMD_WAIT_UNTIL: + status = bot_cmd_wait_until(this); + break; + case BOT_CMD_TURN: + status = bot_cmd_turn(this); + break; + case BOT_CMD_MOVETO: + status = bot_cmd_moveto(this); + break; + case BOT_CMD_MOVETOTARGET: + status = bot_cmd_movetotarget(this); + break; + case BOT_CMD_RESETGOAL: + status = bot_cmd_resetgoal(this); + break; + case BOT_CMD_CC: + status = bot_cmd_cc(this); + break; + case BOT_CMD_IF: + status = bot_cmd_if(this); + break; + case BOT_CMD_ELSE: + status = bot_cmd_else(this); + break; + case BOT_CMD_FI: + status = bot_cmd_fi(this); + break; + case BOT_CMD_RESETAIM: + status = bot_cmd_resetaim(this); + break; + case BOT_CMD_AIM: + status = bot_cmd_aim(this); + break; + case BOT_CMD_AIMTARGET: + status = bot_cmd_aimtarget(this); + break; + case BOT_CMD_PRESSKEY: + status = bot_cmd_presskey(this); + break; + case BOT_CMD_RELEASEKEY: + status = bot_cmd_releasekey(this); + break; + case BOT_CMD_SELECTWEAPON: + status = bot_cmd_select_weapon(this); + break; + case BOT_CMD_IMPULSE: + status = bot_cmd_impulse(this); + break; + case BOT_CMD_BARRIER: + status = bot_cmd_barrier(this); + break; + case BOT_CMD_CONSOLE: + localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n")); + status = CMD_STATUS_FINISHED; + break; + case BOT_CMD_SOUND: + status = bot_cmd_sound(this); + break; + case BOT_CMD_DEBUG_ASSERT_CANFIRE: + status = bot_cmd_debug_assert_canfire(this); + break; + default: + LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n")); + return 0; + } + + if (status==CMD_STATUS_ERROR) + LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n")); + + // Move execution pointer + if(status==CMD_STATUS_EXECUTING) + { + return 1; + } + else + { + if(autocvar_g_debug_bot_commands) + { + string parms; + + switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type]) + { + case BOT_CMD_PARAMETER_FLOAT: + parms = ftos(bot_cmd.bot_cmd_parm_float); + break; + case BOT_CMD_PARAMETER_STRING: + parms = bot_cmd.bot_cmd_parm_string; + break; + case BOT_CMD_PARAMETER_VECTOR: + parms = vtos(bot_cmd.bot_cmd_parm_vector); + break; + default: + parms = ""; + break; + } + clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n")); + } + + bot_command_executed(this, true); + } + + if(status == CMD_STATUS_FINISHED) + return -1; + + return CMD_STATUS_ERROR; +} + +// This function should be (the only) called directly from the bot ai loop +int bot_execute_commands(entity this) +{ + int f; + do + { + f = bot_execute_commands_once(this); + } + while(f < 0); + return f; +} diff --git a/qcsrc/server/bot/default/scripting.qh b/qcsrc/server/bot/default/scripting.qh new file mode 100644 index 0000000000..b3f0e90fa3 --- /dev/null +++ b/qcsrc/server/bot/default/scripting.qh @@ -0,0 +1,80 @@ +#pragma once + +#define BOT_EXEC_STATUS_IDLE 0 +#define BOT_EXEC_STATUS_PAUSED 1 +#define BOT_EXEC_STATUS_WAITING 2 + +#define CMD_STATUS_EXECUTING 0 +#define CMD_STATUS_FINISHED 1 +#define CMD_STATUS_ERROR 2 + + +// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER +const int BOT_CMD_NULL = 0; +const int BOT_CMD_PAUSE = 1; +const int BOT_CMD_CONTINUE = 2; +const int BOT_CMD_WAIT = 3; +const int BOT_CMD_TURN = 4; +const int BOT_CMD_MOVETO = 5; +const int BOT_CMD_RESETGOAL = 6; // Not implemented yet +const int BOT_CMD_CC = 7; +const int BOT_CMD_IF = 8; +const int BOT_CMD_ELSE = 9; +const int BOT_CMD_FI = 10; +const int BOT_CMD_RESETAIM = 11; +const int BOT_CMD_AIM = 12; +const int BOT_CMD_PRESSKEY = 13; +const int BOT_CMD_RELEASEKEY = 14; +const int BOT_CMD_SELECTWEAPON = 15; +const int BOT_CMD_IMPULSE = 16; +const int BOT_CMD_WAIT_UNTIL = 17; +const int BOT_CMD_MOVETOTARGET = 18; +const int BOT_CMD_AIMTARGET = 19; +const int BOT_CMD_BARRIER = 20; +const int BOT_CMD_CONSOLE = 21; +const int BOT_CMD_SOUND = 22; +const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23; +const int BOT_CMD_WHILE = 24; // TODO: Not implemented yet +const int BOT_CMD_WEND = 25; // TODO: Not implemented yet +const int BOT_CMD_CHASE = 26; // TODO: Not implemented yet + +const int BOT_CMD_COUNTER = 24; // Update this value if you add/remove a command + +// NOTE: Following commands should be implemented on the bot ai +// If a new command should be handled by the target ai(s) please declare it here +.float(entity, vector) cmd_moveto; +.float(entity) cmd_resetgoal; + +// +const int BOT_CMD_PARAMETER_NONE = 0; +const int BOT_CMD_PARAMETER_FLOAT = 1; +const int BOT_CMD_PARAMETER_STRING = 2; +const int BOT_CMD_PARAMETER_VECTOR = 3; + +float bot_cmds_initialized; +int bot_cmd_parm_type[BOT_CMD_COUNTER]; +string bot_cmd_string[BOT_CMD_COUNTER]; + +// Bots command queue +entity bot_cmd; // global current command +.entity bot_cmd_current; // current command of this bot + +.float bot_cmd_index; // Position of the command in the queue +.int bot_cmd_type; // If of command (see the BOT_CMD_* defines) +.float bot_cmd_parm_float; // Field to store a float parameter +.string bot_cmd_parm_string; // Field to store a string parameter +.vector bot_cmd_parm_vector; // Field to store a vector parameter + +float bot_barriertime; +.float bot_barrier; + +.float bot_cmd_execution_index; // Position in the queue of the command to be executed + + +void bot_resetqueues(); +void bot_queuecommand(entity bot, string cmdstring); +void bot_cmdhelp(string scmd); +void bot_list_commands(); +float bot_execute_commands(entity this); +entity find_bot_by_name(string name); +entity find_bot_by_number(float number); diff --git a/qcsrc/server/bot/default/waypoints.qc b/qcsrc/server/bot/default/waypoints.qc new file mode 100644 index 0000000000..1e03bf5e1b --- /dev/null +++ b/qcsrc/server/bot/default/waypoints.qc @@ -0,0 +1,1097 @@ +#include "waypoints.qh" + +#include "cvars.qh" + +#include "bot.qh" +#include "navigation.qh" + +#include + +#include "../../antilag.qh" + +#include +#include + +#include +#include + +void waypoint_setupmodel(entity wp) +{ + if (autocvar_g_waypointeditor) + { + // TODO: add some sort of visible box in edit mode for box waypoints + vector m1 = wp.mins; + vector m2 = wp.maxs; + setmodel(wp, MDL_WAYPOINT); + setsize(wp, m1, m2); + wp.effects = EF_LOWPRECISION; + if (wp.wpflags & WAYPOINTFLAG_ITEM) + wp.colormod = '1 0 0'; + else if (wp.wpflags & WAYPOINTFLAG_GENERATED) + wp.colormod = '1 1 0'; + else + wp.colormod = '1 1 1'; + } + else + wp.model = ""; +} + +// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link +// them back to it as well +// (suitable for spawnfunc_waypoint editor) +entity waypoint_spawn(vector m1, vector m2, float f) +{ + if(!(f & WAYPOINTFLAG_PERSONAL)) + { + IL_EACH(g_waypoints, boxesoverlap(m1, m2, it.absmin, it.absmax), + { + return it; + }); + } + + entity w = new(waypoint); + IL_PUSH(g_waypoints, w); + w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; + w.wpflags = f; + w.solid = SOLID_TRIGGER; + setorigin(w, (m1 + m2) * 0.5); + setsize(w, m1 - w.origin, m2 - w.origin); + if (w.size) + w.wpisbox = true; + + if(!w.wpisbox) + { + setsize(w, PL_MIN_CONST - '1 1 0', PL_MAX_CONST + '1 1 0'); + if(!move_out_of_solid(w)) + { + if(!(f & WAYPOINTFLAG_GENERATED)) + { + LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin)); + delete(w); + return NULL; + } + else + { + if(autocvar_developer) + { + LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n"); + backtrace("Waypoint stuck"); + } + } + } + setsize(w, '0 0 0', '0 0 0'); + } + + waypoint_clearlinks(w); + //waypoint_schedulerelink(w); + + waypoint_setupmodel(w); + + return w; +} + +// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has +void waypoint_addlink(entity from, entity to) +{ + float c; + + if (from == to) + return; + if (from.wpflags & WAYPOINTFLAG_NORELINK) + return; + + if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return; + if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return; + if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return; + if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return; + if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return; + if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return; + if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return; + if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return; + + if (to.wpisbox || from.wpisbox) + { + // if either is a box we have to find the nearest points on them to + // calculate the distance properly + vector v1, v2, m1, m2; + v1 = from.origin; + m1 = to.absmin; + m2 = to.absmax; + v1_x = bound(m1_x, v1_x, m2_x); + v1_y = bound(m1_y, v1_y, m2_y); + v1_z = bound(m1_z, v1_z, m2_z); + v2 = to.origin; + m1 = from.absmin; + m2 = from.absmax; + v2_x = bound(m1_x, v2_x, m2_x); + v2_y = bound(m1_y, v2_y, m2_y); + v2_z = bound(m1_z, v2_z, m2_z); + v2 = to.origin; + c = vlen(v2 - v1); + } + else + c = vlen(to.origin - from.origin); + + if (from.wp31mincost < c) return; + if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost; + if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost; + if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost; + if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost; + if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost; + if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost; + if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost; + if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost; + if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost; + if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost; + if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost; + if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost; + if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost; + if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost; + if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost; + if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost; + if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost; + if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost; + if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost; + if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost; + if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost; + if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost; + if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost; + if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost; + if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost; + if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost; + if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost; + if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost; + if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost; + if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost; + if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost; + from.wp00 = to;from.wp00mincost = c;return; +} + +// relink this spawnfunc_waypoint +// (precompile a list of all reachable waypoints from this spawnfunc_waypoint) +// (SLOW!) +void waypoint_think(entity this) +{ + vector sv, sm1, sm2, ev, em1, em2, dv; + + bot_calculate_stepheightvec(); + + bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); + + //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n"); + sm1 = this.origin + this.mins; + sm2 = this.origin + this.maxs; + IL_EACH(g_waypoints, true, + { + if (boxesoverlap(this.absmin, this.absmax, it.absmin, it.absmax)) + { + waypoint_addlink(this, it); + waypoint_addlink(it, this); + } + else + { + ++relink_total; + if(!checkpvs(this.origin, it)) + { + ++relink_pvsculled; + continue; + } + sv = it.origin; + sv.x = bound(sm1_x, sv.x, sm2_x); + sv.y = bound(sm1_y, sv.y, sm2_y); + sv.z = bound(sm1_z, sv.z, sm2_z); + ev = this.origin; + em1 = it.origin + it.mins; + em2 = it.origin + it.maxs; + ev.x = bound(em1_x, ev.x, em2_x); + ev.y = bound(em1_y, ev.y, em2_y); + ev.z = bound(em1_z, ev.z, em2_z); + dv = ev - sv; + dv.z = 0; + if(vdist(dv, >=, 1050)) // max search distance in XY + { + ++relink_lengthculled; + continue; + } + navigation_testtracewalk = 0; + if (!this.wpisbox) + { + tracebox(sv - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, sv, false, this); + if (!trace_startsolid) + { + //dprint("sv deviation", vtos(trace_endpos - sv), "\n"); + sv = trace_endpos + '0 0 1'; + } + } + if (!it.wpisbox) + { + tracebox(ev - PL_MIN_CONST.z * '0 0 1', PL_MIN_CONST, PL_MAX_CONST, ev, false, it); + if (!trace_startsolid) + { + //dprint("ev deviation", vtos(trace_endpos - ev), "\n"); + ev = trace_endpos + '0 0 1'; + } + } + //traceline(this.origin, it.origin, false, NULL); + //if (trace_fraction == 1) + if (!this.wpisbox && tracewalk(this, sv, PL_MIN_CONST, PL_MAX_CONST, ev, MOVE_NOMONSTERS)) + waypoint_addlink(this, it); + else + relink_walkculled += 0.5; + if (!it.wpisbox && tracewalk(it, ev, PL_MIN_CONST, PL_MAX_CONST, sv, MOVE_NOMONSTERS)) + waypoint_addlink(it, this); + else + relink_walkculled += 0.5; + } + }); + navigation_testtracewalk = 0; + this.wplinked = true; +} + +void waypoint_clearlinks(entity wp) +{ + // clear links to other waypoints + float f; + f = 10000000; + wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL; + wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL; + wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL; + wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL; + + wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f; + wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f; + wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f; + wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f; + + wp.wplinked = false; +} + +// tell a spawnfunc_waypoint to relink +void waypoint_schedulerelink(entity wp) +{ + if (wp == NULL) + return; + + waypoint_setupmodel(wp); + wp.wpisbox = vdist(wp.size, >, 0); + wp.enemy = NULL; + if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL)) + wp.owner = NULL; + if (!(wp.wpflags & WAYPOINTFLAG_NORELINK)) + waypoint_clearlinks(wp); + // schedule an actual relink on next frame + setthink(wp, waypoint_think); + wp.nextthink = time; + wp.effects = EF_LOWPRECISION; +} + +// spawnfunc_waypoint map entity +spawnfunc(waypoint) +{ + IL_PUSH(g_waypoints, this); + + setorigin(this, this.origin); + // schedule a relink after other waypoints have had a chance to spawn + waypoint_clearlinks(this); + //waypoint_schedulerelink(this); +} + +// remove a spawnfunc_waypoint, and schedule all neighbors to relink +void waypoint_remove(entity e) +{ + // tell all linked waypoints that they need to relink + waypoint_schedulerelink(e.wp00); + waypoint_schedulerelink(e.wp01); + waypoint_schedulerelink(e.wp02); + waypoint_schedulerelink(e.wp03); + waypoint_schedulerelink(e.wp04); + waypoint_schedulerelink(e.wp05); + waypoint_schedulerelink(e.wp06); + waypoint_schedulerelink(e.wp07); + waypoint_schedulerelink(e.wp08); + waypoint_schedulerelink(e.wp09); + waypoint_schedulerelink(e.wp10); + waypoint_schedulerelink(e.wp11); + waypoint_schedulerelink(e.wp12); + waypoint_schedulerelink(e.wp13); + waypoint_schedulerelink(e.wp14); + waypoint_schedulerelink(e.wp15); + waypoint_schedulerelink(e.wp16); + waypoint_schedulerelink(e.wp17); + waypoint_schedulerelink(e.wp18); + waypoint_schedulerelink(e.wp19); + waypoint_schedulerelink(e.wp20); + waypoint_schedulerelink(e.wp21); + waypoint_schedulerelink(e.wp22); + waypoint_schedulerelink(e.wp23); + waypoint_schedulerelink(e.wp24); + waypoint_schedulerelink(e.wp25); + waypoint_schedulerelink(e.wp26); + waypoint_schedulerelink(e.wp27); + waypoint_schedulerelink(e.wp28); + waypoint_schedulerelink(e.wp29); + waypoint_schedulerelink(e.wp30); + waypoint_schedulerelink(e.wp31); + // and now remove the spawnfunc_waypoint + delete(e); +} + +// empties the map of waypoints +void waypoint_removeall() +{ + IL_EACH(g_waypoints, true, + { + delete(it); + }); +} + +// tell all waypoints to relink +// (is this useful at all?) +void waypoint_schedulerelinkall() +{ + relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0; + IL_EACH(g_waypoints, true, + { + waypoint_schedulerelink(it); + }); +} + +// Load waypoint links from file +float waypoint_load_links() +{ + string filename, s; + float file, tokens, c = 0, found; + entity wp_from = NULL, wp_to; + vector wp_to_pos, wp_from_pos; + filename = strcat("maps/", mapname); + filename = strcat(filename, ".waypoints.cache"); + file = fopen(filename, FILE_READ); + + if (file < 0) + { + LOG_TRACE("waypoint links load from "); + LOG_TRACE(filename); + LOG_TRACE(" failed"); + return false; + } + + while ((s = fgets(file))) + { + tokens = tokenizebyseparator(s, "*"); + + if (tokens!=2) + { + // bad file format + fclose(file); + return false; + } + + wp_from_pos = stov(argv(0)); + wp_to_pos = stov(argv(1)); + + // Search "from" waypoint + if(!wp_from || wp_from.origin!=wp_from_pos) + { + wp_from = findradius(wp_from_pos, 1); + found = false; + while(wp_from) + { + if(vdist(wp_from.origin - wp_from_pos, <, 1)) + if(wp_from.classname == "waypoint") + { + found = true; + break; + } + wp_from = wp_from.chain; + } + + if(!found) + { + LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin)); + continue; + } + + } + + // Search "to" waypoint + wp_to = findradius(wp_to_pos, 1); + found = false; + while(wp_to) + { + if(vdist(wp_to.origin - wp_to_pos, <, 1)) + if(wp_to.classname == "waypoint") + { + found = true; + break; + } + wp_to = wp_to.chain; + } + + if(!found) + { + LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin)); + continue; + } + + ++c; + waypoint_addlink(wp_from, wp_to); + } + + fclose(file); + + LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache"); + + botframe_cachedwaypointlinks = true; + return true; +} + +void waypoint_load_links_hardwired() +{ + string filename, s; + float file, tokens, c = 0, found; + entity wp_from = NULL, wp_to; + vector wp_to_pos, wp_from_pos; + filename = strcat("maps/", mapname); + filename = strcat(filename, ".waypoints.hardwired"); + file = fopen(filename, FILE_READ); + + botframe_loadedforcedlinks = true; + + if (file < 0) + { + LOG_TRACE("waypoint links load from ", filename, " failed"); + return; + } + + while ((s = fgets(file))) + { + if(substring(s, 0, 2)=="//") + continue; + + if(substring(s, 0, 1)=="#") + continue; + + tokens = tokenizebyseparator(s, "*"); + + if (tokens!=2) + continue; + + wp_from_pos = stov(argv(0)); + wp_to_pos = stov(argv(1)); + + // Search "from" waypoint + if(!wp_from || wp_from.origin!=wp_from_pos) + { + wp_from = findradius(wp_from_pos, 5); + found = false; + while(wp_from) + { + if(vdist(wp_from.origin - wp_from_pos, <, 5)) + if(wp_from.classname == "waypoint") + { + found = true; + break; + } + wp_from = wp_from.chain; + } + + if(!found) + { + LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n")); + continue; + } + } + + // Search "to" waypoint + wp_to = findradius(wp_to_pos, 5); + found = false; + while(wp_to) + { + if(vdist(wp_to.origin - wp_to_pos, <, 5)) + if(wp_to.classname == "waypoint") + { + found = true; + break; + } + wp_to = wp_to.chain; + } + + if(!found) + { + LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n")); + continue; + } + + ++c; + waypoint_addlink(wp_from, wp_to); + wp_from.wphardwired = true; + wp_to.wphardwired = true; + } + + fclose(file); + + LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired"); +} + +entity waypoint_get_link(entity w, float i) +{ + switch(i) + { + case 0:return w.wp00; + case 1:return w.wp01; + case 2:return w.wp02; + case 3:return w.wp03; + case 4:return w.wp04; + case 5:return w.wp05; + case 6:return w.wp06; + case 7:return w.wp07; + case 8:return w.wp08; + case 9:return w.wp09; + case 10:return w.wp10; + case 11:return w.wp11; + case 12:return w.wp12; + case 13:return w.wp13; + case 14:return w.wp14; + case 15:return w.wp15; + case 16:return w.wp16; + case 17:return w.wp17; + case 18:return w.wp18; + case 19:return w.wp19; + case 20:return w.wp20; + case 21:return w.wp21; + case 22:return w.wp22; + case 23:return w.wp23; + case 24:return w.wp24; + case 25:return w.wp25; + case 26:return w.wp26; + case 27:return w.wp27; + case 28:return w.wp28; + case 29:return w.wp29; + case 30:return w.wp30; + case 31:return w.wp31; + default:return NULL; + } +} + +// Save all waypoint links to a file +void waypoint_save_links() +{ + string filename = sprintf("maps/%s.waypoints.cache", mapname); + int file = fopen(filename, FILE_WRITE); + if (file < 0) + { + LOG_INFOF("waypoint link save to %s failed\n", filename); + return; + } + + int c = 0; + IL_EACH(g_waypoints, true, + { + for(int j = 0; j < 32; ++j) + { + entity link = waypoint_get_link(it, j); + if(link) + { + string s = strcat(vtos(it.origin), "*", vtos(link.origin), "\n"); + fputs(file, s); + ++c; + } + } + }); + fclose(file); + botframe_cachedwaypointlinks = true; + + LOG_INFOF("saved %d waypoint links to maps/%s.waypoints.cache\n", c, mapname); +} + +// save waypoints to gamedir/data/maps/mapname.waypoints +void waypoint_saveall() +{ + string filename = sprintf("maps/%s.waypoints", mapname); + int file = fopen(filename, FILE_WRITE); + if (file < 0) + { + waypoint_save_links(); // save anyway? + botframe_loadedforcedlinks = false; + + LOG_INFOF("waypoint links: save to %s failed\n", filename); + return; + } + + int c = 0; + IL_EACH(g_waypoints, true, + { + if(it.wpflags & WAYPOINTFLAG_GENERATED) + continue; + + for(int j = 0; j < 32; ++j) + { + string s; + s = strcat(vtos(it.origin + it.mins), "\n"); + s = strcat(s, vtos(it.origin + it.maxs)); + s = strcat(s, "\n"); + s = strcat(s, ftos(it.wpflags)); + s = strcat(s, "\n"); + fputs(file, s); + ++c; + } + }); + fclose(file); + waypoint_save_links(); + botframe_loadedforcedlinks = false; + + LOG_INFOF("saved %d waypoints to maps/%s.waypoints\n", c, mapname); +} + +// load waypoints from file +float waypoint_loadall() +{ + string filename, s; + float file, cwp, cwb, fl; + vector m1, m2; + cwp = 0; + cwb = 0; + filename = strcat("maps/", mapname); + filename = strcat(filename, ".waypoints"); + file = fopen(filename, FILE_READ); + if (file >= 0) + { + while ((s = fgets(file))) + { + m1 = stov(s); + s = fgets(file); + if (!s) + break; + m2 = stov(s); + s = fgets(file); + if (!s) + break; + fl = stof(s); + waypoint_spawn(m1, m2, fl); + if (m1 == m2) + cwp = cwp + 1; + else + cwb = cwb + 1; + } + fclose(file); + LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints"); + } + else + { + LOG_TRACE("waypoint load from ", filename, " failed"); + } + return cwp + cwb; +} + +vector waypoint_fixorigin(vector position) +{ + tracebox(position + '0 0 1' * (1 - PL_MIN_CONST.z), PL_MIN_CONST, PL_MAX_CONST, position + '0 0 -512', MOVE_NOMONSTERS, NULL); + if(trace_fraction < 1) + position = trace_endpos; + //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL); + //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n"); + return position; +} + +void waypoint_spawnforitem_force(entity e, vector org) +{ + // Fix the waypoint altitude if necessary + org = waypoint_fixorigin(org); + + // don't spawn an item spawnfunc_waypoint if it already exists + IL_EACH(g_waypoints, true, + { + if(it.wpisbox) + { + if(boxesoverlap(org, org, it.absmin, it.absmax)) + { + e.nearestwaypoint = it; + return; + } + } + else + { + if(vdist(it.origin - org, <, 16)) + { + e.nearestwaypoint = it; + return; + } + } + }); + + e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM); +} + +void waypoint_spawnforitem(entity e) +{ + if(!bot_waypoints_for_items) + return; + + waypoint_spawnforitem_force(e, e.origin); +} + +void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken) +{ + entity w; + entity dw; + w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK); + dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED); + // one way link to the destination + w.wp00 = dw; + w.wp00mincost = timetaken; // this is just for jump pads + // the teleporter's nearest spawnfunc_waypoint is this one + // (teleporters are not goals, so this is probably useless) + e.nearestwaypoint = w; + e.nearestwaypointtimeout = time + 1000000000; +} + +void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) +{ + org = waypoint_fixorigin(org); + destination = waypoint_fixorigin(destination); + waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken); +} + +void waypoint_spawnforteleporter(entity e, vector destination, float timetaken) +{ + destination = waypoint_fixorigin(destination); + waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken); +} + +entity waypoint_spawnpersonal(entity this, vector position) +{ + entity w; + + // drop the waypoint to a proper location: + // first move it up by a player height + // then move it down to hit the floor with player bbox size + position = waypoint_fixorigin(position); + + w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL); + w.nearestwaypoint = NULL; + w.nearestwaypointtimeout = 0; + w.owner = this; + + waypoint_schedulerelink(w); + + return w; +} + +void botframe_showwaypointlinks() +{ + if (time < botframe_waypointeditorlightningtime) + return; + botframe_waypointeditorlightningtime = time + 0.5; + FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot, + { + if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE) + { + //navigation_testtracewalk = true; + entity head = navigation_findnearestwaypoint(it, false); + // print("currently selected WP is ", etos(head), "\n"); + //navigation_testtracewalk = false; + if (head) + { + entity w; + w = head ;if (w) te_lightning2(NULL, w.origin, it.origin); + w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin); + w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin); + } + } + }); +} + +float botframe_autowaypoints_fixdown(vector v) +{ + tracebox(v, PL_MIN_CONST, PL_MAX_CONST, v + '0 0 -64', MOVE_NOMONSTERS, NULL); + if(trace_fraction >= 1) + return 0; + return 1; +} + +float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f) +{ + IL_EACH(g_waypoints, boxesoverlap(v - '32 32 32', v + '32 32 32', it.absmin, it.absmax), + { + // if a matching spawnfunc_waypoint already exists, don't add a duplicate + return 0; + }); + + waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f)); + return 1; +} + +// return value: +// 1 = WP created +// 0 = no action needed +// -1 = temp fail, try from world too +// -2 = permanent fail, do not retry +float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld) +{ + // make it possible to go from p to wp, if we can + // if wp is NULL, nearest is chosen + + entity w; + vector porg; + float t, tmin, tmax; + vector o; + vector save; + + if(!botframe_autowaypoints_fixdown(p.origin)) + return -2; + porg = trace_endpos; + + if(wp) + { + // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w + + // if wp -> porg, then OK + float maxdist; + if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050)) + { + // we may find a better one + maxdist = vlen(wp.origin - porg); + } + else + { + // accept any "good" + maxdist = 2100; + } + + float bestdist = maxdist; + IL_EACH(g_waypoints, it != wp && !(it.wpflags & WAYPOINTFLAG_NORELINK), + { + float d = vlen(wp.origin - it.origin) + vlen(it.origin - porg); + if(d < bestdist) + if(navigation_waypoint_will_link(wp.origin, it.origin, p, walkfromwp, 1050)) + if(navigation_waypoint_will_link(it.origin, porg, p, walkfromwp, 1050)) + { + bestdist = d; + p.(fld) = it; + } + }); + if(bestdist < maxdist) + { + LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n"); + return 0; + } + + if(bestdist < 2100) + { + // we know maxdist < 2100 + // so wp -> porg is still valid + // all is good + p.(fld) = wp; + return 0; + } + + // otherwise, no existing WP can fix our issues + } + else + { + save = p.origin; + setorigin(p, porg); + w = navigation_findnearestwaypoint(p, walkfromwp); + setorigin(p, save); + if(w) + { + p.(fld) = w; + return 0; + } + } + + tmin = 0; + tmax = 1; + for (;;) + { + if(tmax - tmin < 0.001) + { + // did not get a good candidate + return -1; + } + + t = (tmin + tmax) * 0.5; + o = antilag_takebackorigin(p, CS(p), time - t); + if(!botframe_autowaypoints_fixdown(o)) + return -2; + o = trace_endpos; + + if(wp) + { + if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050)) + { + // we cannot walk from wp.origin to o + // get closer to tmax + tmin = t; + continue; + } + } + else + { + save = p.origin; + setorigin(p, o); + w = navigation_findnearestwaypoint(p, walkfromwp); + setorigin(p, save); + if(!w) + { + // we cannot walk from any WP to o + // get closer to tmax + tmin = t; + continue; + } + } + + // if we get here, o is valid regarding waypoints + // check if o is connected right to the player + // we break if it succeeds, as that means o is a good waypoint location + if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050)) + break; + + // o is no good, we need to get closer to the player + tmax = t; + } + + LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n"); + botframe_autowaypoints_createwp(o, p, fld, 0); + return 1; +} + +// automatically create missing waypoints +.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1; +void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld) +{ + float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld); + if(r != -1) + return; + r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld); + if(r != -1) + return; + + LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n"); + if(!botframe_autowaypoints_fixdown(p.origin)) + return; // shouldn't happen, caught above + botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED); +} + +void botframe_deleteuselesswaypoints() +{ + IL_EACH(g_items, it.bot_pickup, + { + // NOTE: this protects waypoints if they're the ONLY nearest + // waypoint. That's the intention. + navigation_findnearestwaypoint(it, false); // Walk TO item. + navigation_findnearestwaypoint(it, true); // Walk FROM item. + }); + IL_EACH(g_waypoints, true, + { + it.wpflags |= WAYPOINTFLAG_DEAD_END; + it.wpflags &= ~WAYPOINTFLAG_USEFUL; + // WP is useful if: + if (it.wpflags & WAYPOINTFLAG_ITEM) + it.wpflags |= WAYPOINTFLAG_USEFUL; + if (it.wpflags & WAYPOINTFLAG_TELEPORT) + it.wpflags |= WAYPOINTFLAG_USEFUL; + if (it.wpflags & WAYPOINTFLAG_PROTECTED) + it.wpflags |= WAYPOINTFLAG_USEFUL; + // b) WP is closest WP for an item/spawnpoint/other entity + // This has been done above by protecting these WPs. + }); + // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2. + IL_EACH(g_waypoints, !(it.wpflags & WAYPOINTFLAG_PERSONAL), + { + for (int m = 0; m < 32; ++m) + { + entity w = waypoint_get_link(it, m); + if (!w) + break; + if (w.wpflags & WAYPOINTFLAG_PERSONAL) + continue; + if (w.wpflags & WAYPOINTFLAG_USEFUL) + continue; + for (int j = 0; j < 32; ++j) + { + entity w2 = waypoint_get_link(w, j); + if (!w2) + break; + if (it == w2) + continue; + if (w2.wpflags & WAYPOINTFLAG_PERSONAL) + continue; + // If we got here, it != w2 exist with it -> w + // and w -> w2. That means the waypoint is not + // a dead end. + w.wpflags &= ~WAYPOINTFLAG_DEAD_END; + for (int k = 0; k < 32; ++k) + { + if (waypoint_get_link(it, k) == w2) + continue; + // IF WE GET HERE, w is proven useful + // to get from it to w2! + w.wpflags |= WAYPOINTFLAG_USEFUL; + goto next; + } + } +LABEL(next) + } + }); + // d) The waypoint is a dead end. Dead end waypoints must be kept as + // they are needed to complete routes while autowaypointing. + + IL_EACH(g_waypoints, !(it.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END)), + { + LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", it.origin); + te_explosion(it.origin); + waypoint_remove(it); + break; + }); + + IL_EACH(g_waypoints, true, + { + it.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag + }); +} + +void botframe_autowaypoints() +{ + FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA( + // going back is broken, so only fix waypoints to walk TO the player + //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0); + botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1); + //te_explosion(p.botframe_autowaypoints_lastwp0.origin); + )); + + if (autocvar_g_waypointeditor_auto >= 2) { + botframe_deleteuselesswaypoints(); + } +} + diff --git a/qcsrc/server/bot/default/waypoints.qh b/qcsrc/server/bot/default/waypoints.qh new file mode 100644 index 0000000000..38d57a04a1 --- /dev/null +++ b/qcsrc/server/bot/default/waypoints.qh @@ -0,0 +1,58 @@ +#pragma once +/* + * Globals and Fields + */ + +// 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; + +float botframe_waypointeditorlightningtime; +float botframe_loadedforcedlinks; +float botframe_cachedwaypointlinks; + +.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15; +.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31; + +// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance +.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost; +.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost; +.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost; +.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost; + +.float wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired; +.int wpflags; + +.vector wpnearestpoint; + +/* + * Functions + */ + +spawnfunc(waypoint); +void waypoint_addlink(entity from, entity to); +void waypoint_think(entity this); +void waypoint_clearlinks(entity wp); +void waypoint_schedulerelink(entity wp); + +void waypoint_remove(entity e); +void waypoint_removeall(); +void waypoint_schedulerelinkall(); +void waypoint_load_links_hardwired(); +void waypoint_save_links(); +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); +void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken); +void botframe_showwaypointlinks(); + +float waypoint_loadall(); +float waypoint_load_links(); + +entity waypoint_spawn(vector m1, vector m2, float f); +entity waypoint_spawnpersonal(entity this, vector position); + +vector waypoint_fixorigin(vector position); + +void botframe_autowaypoints(); diff --git a/qcsrc/server/bot/havocbot/_mod.inc b/qcsrc/server/bot/havocbot/_mod.inc deleted file mode 100644 index a6270bc138..0000000000 --- a/qcsrc/server/bot/havocbot/_mod.inc +++ /dev/null @@ -1,3 +0,0 @@ -// generated file; do not modify -#include -#include diff --git a/qcsrc/server/bot/havocbot/_mod.qh b/qcsrc/server/bot/havocbot/_mod.qh deleted file mode 100644 index 4b62d1b8e6..0000000000 --- a/qcsrc/server/bot/havocbot/_mod.qh +++ /dev/null @@ -1,3 +0,0 @@ -// generated file; do not modify -#include -#include diff --git a/qcsrc/server/bot/havocbot/havocbot.qc b/qcsrc/server/bot/havocbot/havocbot.qc deleted file mode 100644 index f6c0348d28..0000000000 --- a/qcsrc/server/bot/havocbot/havocbot.qc +++ /dev/null @@ -1,1321 +0,0 @@ -#include "havocbot.qh" - -#include "../aim.qh" -#include "../bot.qh" -#include "../navigation.qh" -#include "../scripting.qh" -#include "../waypoints.qh" - -#include -#include -#include -#include - -#include - -#include - -.float speed; - -void havocbot_ai(entity this) -{ - if(this.draggedby) - return; - - if(bot_execute_commands(this)) - return; - - if (bot_strategytoken == this) - if (!bot_strategytoken_taken) - { - if(this.havocbot_blockhead) - { - this.havocbot_blockhead = false; - } - else - { - if (!this.jumppadcount) - this.havocbot_role(this); // little too far down the rabbit hole - } - - // TODO: tracewalk() should take care of this job (better path finding under water) - // if we don't have a goal and we're under water look for a waypoint near the "shore" and push it - if(IS_DEAD(this)) - if(this.goalcurrent==NULL) - if(this.waterlevel==WATERLEVEL_SWIMMING || (this.aistatus & AI_STATUS_OUT_WATER)) - { - // Look for the closest waypoint out of water - entity newgoal, head; - float bestdistance, distance; - - newgoal = NULL; - bestdistance = 10000; - for (head = findchain(classname, "waypoint"); head; head = head.chain) - { - distance = vlen(head.origin - this.origin); - if(distance>10000) - continue; - - if(head.origin.z < this.origin.z) - continue; - - if(head.origin.z - this.origin.z - this.view_ofs.z > 100) - continue; - - if (pointcontents(head.origin + head.maxs + '0 0 1') != CONTENT_EMPTY) - continue; - - traceline(this.origin + this.view_ofs , head.origin, true, head); - - if(trace_fraction<1) - continue; - - if(distance= 2) // bots can only reload the held weapon on purpose past this skill - if(this.clip_load < this.clip_size) - this.impulse = 20; // "press" the reload button, not sure if this is done right - - // if we're not reloading a weapon, switch to any weapon in our invnetory that's not fully loaded to reload it next - // the code above executes next frame, starting the reloading then - if(skill >= 5) // bots can only look for unloaded weapons past this skill - if(this.clip_load >= 0) // only if we're not reloading a weapon already - { - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if((this.weapons & (it.m_wepset)) && (it.spawnflags & WEP_FLAG_RELOADABLE) && (this.weapon_load[it.m_id] < it.reloading_ammo)) - PS(this).m_switchweapon = it; - )); - } - } -} - -void havocbot_keyboard_movement(entity this, vector destorg) -{ - vector keyboard; - float blend, maxspeed; - float sk; - - sk = skill + this.bot_moveskill; - - maxspeed = autocvar_sv_maxspeed; - - if (time < this.havocbot_keyboardtime) - return; - - this.havocbot_keyboardtime = - max( - this.havocbot_keyboardtime - + 0.05/max(1, sk+this.havocbot_keyboardskill) - + random()*0.025/max(0.00025, skill+this.havocbot_keyboardskill) - , time); - keyboard = this.movement * (1.0 / maxspeed); - - float trigger, trigger1; - blend = bound(0,sk*0.1,1); - trigger = autocvar_bot_ai_keyboard_threshold; - trigger1 = 0 - trigger; - - // categorize forward movement - // at skill < 1.5 only forward - // at skill < 2.5 only individual directions - // at skill < 4.5 only individual directions, and forward diagonals - // at skill >= 4.5, all cases allowed - if (keyboard.x > trigger) - { - keyboard.x = 1; - if (sk < 2.5) - keyboard.y = 0; - } - else if (keyboard.x < trigger1 && sk > 1.5) - { - keyboard.x = -1; - if (sk < 4.5) - keyboard.y = 0; - } - else - { - keyboard.x = 0; - if (sk < 1.5) - keyboard.y = 0; - } - if (sk < 4.5) - keyboard.z = 0; - - if (keyboard.y > trigger) - keyboard.y = 1; - else if (keyboard.y < trigger1) - keyboard.y = -1; - else - keyboard.y = 0; - - if (keyboard.z > trigger) - keyboard.z = 1; - else if (keyboard.z < trigger1) - keyboard.z = -1; - else - keyboard.z = 0; - - this.havocbot_keyboard = keyboard * maxspeed; - if (this.havocbot_ducktime>time) PHYS_INPUT_BUTTON_CROUCH(this) = true; - - keyboard = this.havocbot_keyboard; - blend = bound(0,vlen(destorg-this.origin)/autocvar_bot_ai_keyboard_distance,1); // When getting close move with 360 degree - //dprint("movement ", vtos(this.movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); - this.movement = this.movement + (keyboard - this.movement) * blend; -} - -void havocbot_bunnyhop(entity this, vector dir) -{ - float bunnyhopdistance; - vector deviation; - float maxspeed; - vector gco, gno; - - // Don't jump when attacking - if(this.aistatus & AI_STATUS_ATTACKING) - return; - - if(IS_PLAYER(this.goalcurrent)) - return; - - maxspeed = autocvar_sv_maxspeed; - - if(this.aistatus & AI_STATUS_DANGER_AHEAD) - { - this.aistatus &= ~AI_STATUS_RUNNING; - PHYS_INPUT_BUTTON_JUMP(this) = false; - this.bot_canruntogoal = 0; - this.bot_timelastseengoal = 0; - return; - } - - if(this.waterlevel > WATERLEVEL_WETFEET) - { - this.aistatus &= ~AI_STATUS_RUNNING; - return; - } - - if(this.bot_lastseengoal != this.goalcurrent && !(this.aistatus & AI_STATUS_RUNNING)) - { - this.bot_canruntogoal = 0; - this.bot_timelastseengoal = 0; - } - - gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; - bunnyhopdistance = vlen(this.origin - gco); - - // Run only to visible goals - if(IS_ONGROUND(this)) - if(this.speed==maxspeed) - if(checkpvs(this.origin + this.view_ofs, this.goalcurrent)) - { - this.bot_lastseengoal = this.goalcurrent; - - // seen it before - if(this.bot_timelastseengoal) - { - // for a period of time - if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay) - { - float checkdistance; - checkdistance = true; - - // don't run if it is too close - if(this.bot_canruntogoal==0) - { - if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_startdistance) - this.bot_canruntogoal = 1; - else - this.bot_canruntogoal = -1; - } - - if(this.bot_canruntogoal != 1) - return; - - if(this.aistatus & AI_STATUS_ROAMING) - if(this.goalcurrent.classname=="waypoint") - if (!(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)) - if(fabs(gco.z - this.origin.z) < this.maxs.z - this.mins.z) - if(this.goalstack01!=NULL) - { - gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; - deviation = vectoangles(gno - this.origin) - vectoangles(gco - this.origin); - while (deviation.y < -180) deviation.y = deviation.y + 360; - while (deviation.y > 180) deviation.y = deviation.y - 360; - - if(fabs(deviation.y) < 20) - if(bunnyhopdistance < vlen(this.origin - gno)) - if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z) - { - if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance)) - if(checkpvs(this.origin + this.view_ofs, this.goalstack01)) - { - checkdistance = false; - } - } - } - - if(checkdistance) - { - this.aistatus &= ~AI_STATUS_RUNNING; - if(bunnyhopdistance > autocvar_bot_ai_bunnyhop_stopdistance) - PHYS_INPUT_BUTTON_JUMP(this) = true; - } - else - { - this.aistatus |= AI_STATUS_RUNNING; - PHYS_INPUT_BUTTON_JUMP(this) = true; - } - } - } - else - { - this.bot_timelastseengoal = time; - } - } - else - { - this.bot_timelastseengoal = 0; - } - -#if 0 - // Release jump button - if(!cvar("sv_pogostick")) - if((IS_ONGROUND(this)) == 0) - { - if(this.velocity.z < 0 || vlen(this.velocity)maxspeed) - { - deviation = vectoangles(dir) - vectoangles(this.velocity); - while (deviation.y < -180) deviation.y = deviation.y + 360; - while (deviation.y > 180) deviation.y = deviation.y - 360; - - if(fabs(deviation.y)>10) - this.movement_x = 0; - - if(deviation.y>10) - this.movement_y = maxspeed * -1; - else if(deviation.y<10) - this.movement_y = maxspeed; - - } - } -#endif -} - -void havocbot_movetogoal(entity this) -{ - vector destorg; - vector diff; - vector dir; - vector flatdir; - vector m1; - vector m2; - vector evadeobstacle; - vector evadelava; - float s; - float maxspeed; - vector gco; - //float dist; - vector dodge; - //if (this.goalentity) - // te_lightning2(this, this.origin, (this.goalentity.absmin + this.goalentity.absmax) * 0.5); - this.movement = '0 0 0'; - maxspeed = autocvar_sv_maxspeed; - - // Jetpack navigation - if(this.goalcurrent) - if(this.navigation_jetpack_goal) - if(this.goalcurrent==this.navigation_jetpack_goal) - if(this.ammo_fuel) - { - if(autocvar_bot_debug_goalstack) - { - debuggoalstack(this); - te_wizspike(this.navigation_jetpack_point); - } - - // Take off - if (!(this.aistatus & AI_STATUS_JETPACK_FLYING)) - { - // Brake almost completely so it can get a good direction - if(vdist(this.velocity, >, 10)) - return; - this.aistatus |= AI_STATUS_JETPACK_FLYING; - } - - makevectors(this.v_angle.y * '0 1 0'); - dir = normalize(this.navigation_jetpack_point - this.origin); - - // Landing - if(this.aistatus & AI_STATUS_JETPACK_LANDING) - { - // Calculate brake distance in xy - float db, v, d; - vector dxy; - - dxy = this.origin - ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ); dxy.z = 0; - d = vlen(dxy); - v = vlen(this.velocity - this.velocity.z * '0 0 1'); - db = (pow(v,2) / (autocvar_g_jetpack_acceleration_side * 2)) + 100; - // dprint("distance ", ftos(ceil(d)), " velocity ", ftos(ceil(v)), " brake at ", ftos(ceil(db)), "\n"); - if(d < db || d < 500) - { - // Brake - if(fabs(this.velocity.x)>maxspeed*0.3) - { - this.movement_x = dir * v_forward * -maxspeed; - return; - } - // Switch to normal mode - this.navigation_jetpack_goal = NULL; - this.aistatus &= ~AI_STATUS_JETPACK_LANDING; - this.aistatus &= ~AI_STATUS_JETPACK_FLYING; - return; - } - } - else if(checkpvs(this.origin,this.goalcurrent)) - { - // If I can see the goal switch to landing code - this.aistatus &= ~AI_STATUS_JETPACK_FLYING; - this.aistatus |= AI_STATUS_JETPACK_LANDING; - return; - } - - // Flying - PHYS_INPUT_BUTTON_HOOK(this) = true; - if(this.navigation_jetpack_point.z - STAT(PL_MAX, NULL).z + STAT(PL_MIN, NULL).z < this.origin.z) - { - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - } - return; - } - - // Handling of jump pads - if(this.jumppadcount) - { - // If got stuck on the jump pad try to reach the farthest visible waypoint - if(this.aistatus & AI_STATUS_OUT_JUMPPAD) - { - if(fabs(this.velocity.z)<50) - { - entity head, newgoal = NULL; - float distance, bestdistance = 0; - - for (head = findchain(classname, "waypoint"); head; head = head.chain) - { - - distance = vlen(head.origin - this.origin); - if(distance>1000) - continue; - - traceline(this.origin + this.view_ofs , ( ( head.absmin + head.absmax ) * 0.5 ), true, NULL); - - if(trace_fraction<1) - continue; - - if(distance>bestdistance) - { - newgoal = head; - bestdistance = distance; - } - } - - if(newgoal) - { - this.ignoregoal = this.goalcurrent; - this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout; - navigation_clearroute(this); - navigation_routetogoal(this, newgoal, this.origin); - this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; - } - } - else - return; - } - else - { - if(this.velocity.z>0) - { - float threshold; - vector velxy = this.velocity; velxy_z = 0; - threshold = maxspeed * 0.2; - if(vdist(velxy, <, threshold)) - { - LOG_TRACE("Warning: ", this.netname, " got stuck on a jumppad (velocity in xy is ", vtos(velxy), "), trying to get out of it now\n"); - this.aistatus |= AI_STATUS_OUT_JUMPPAD; - } - return; - } - - // Don't chase players while using a jump pad - if(IS_PLAYER(this.goalcurrent) || IS_PLAYER(this.goalstack01)) - return; - } - } - else if(this.aistatus & AI_STATUS_OUT_JUMPPAD) - this.aistatus &= ~AI_STATUS_OUT_JUMPPAD; - - // If there is a trigger_hurt right below try to use the jetpack or make a rocketjump - if(skill>6) - if (!(IS_ONGROUND(this))) - { - tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 -65536', MOVE_NOMONSTERS, this); - if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos )) - if(this.items & IT_JETPACK) - { - tracebox(this.origin, this.mins, this.maxs, this.origin + '0 0 65536', MOVE_NOMONSTERS, this); - if(tracebox_hits_trigger_hurt(this.origin, this.mins, this.maxs, trace_endpos + '0 0 1' )) - { - if(this.velocity.z<0) - { - PHYS_INPUT_BUTTON_HOOK(this) = true; - } - } - else - PHYS_INPUT_BUTTON_HOOK(this) = true; - - // If there is no goal try to move forward - - if(this.goalcurrent==NULL) - dir = v_forward; - else - dir = normalize(( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - this.origin); - - vector xyvelocity = this.velocity; xyvelocity_z = 0; - float xyspeed = xyvelocity * dir; - - if(xyspeed < (maxspeed / 2)) - { - makevectors(this.v_angle.y * '0 1 0'); - tracebox(this.origin, this.mins, this.maxs, this.origin + (dir * maxspeed * 3), MOVE_NOMONSTERS, this); - if(trace_fraction==1) - { - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - if (skill < 10) - havocbot_keyboard_movement(this, this.origin + dir * 100); - } - } - - this.havocbot_blockhead = true; - - return; - } - else if(this.health>WEP_CVAR(devastator, damage)*0.5) - { - if(this.velocity.z < 0) - if(client_hasweapon(this, WEP_DEVASTATOR, true, false)) - { - this.movement_x = maxspeed; - - if(this.rocketjumptime) - { - if(time > this.rocketjumptime) - { - PHYS_INPUT_BUTTON_ATCK2(this) = true; - this.rocketjumptime = 0; - } - return; - } - - PS(this).m_switchweapon = WEP_DEVASTATOR; - this.v_angle_x = 90; - PHYS_INPUT_BUTTON_ATCK(this) = true; - this.rocketjumptime = time + WEP_CVAR(devastator, detonatedelay); - return; - } - } - else - { - // If there is no goal try to move forward - if(this.goalcurrent==NULL) - this.movement_x = maxspeed; - } - } - - // If we are under water with no goals, swim up - if(this.waterlevel) - if(this.goalcurrent==NULL) - { - dir = '0 0 0'; - if(this.waterlevel>WATERLEVEL_SWIMMING) - dir.z = 1; - else if(this.velocity.z >= 0 && !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER)) - PHYS_INPUT_BUTTON_JUMP(this) = true; - else - PHYS_INPUT_BUTTON_JUMP(this) = false; - makevectors(this.v_angle.y * '0 1 0'); - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - this.movement_z = dir * v_up * maxspeed; - } - - // if there is nowhere to go, exit - if (this.goalcurrent == NULL) - return; - - if (this.goalcurrent) - navigation_poptouchedgoals(this); - - // if ran out of goals try to use an alternative goal or get a new strategy asap - if(this.goalcurrent == NULL) - { - this.bot_strategytime = 0; - return; - } - - - if(autocvar_bot_debug_goalstack) - debuggoalstack(this); - - m1 = this.goalcurrent.origin + this.goalcurrent.mins; - m2 = this.goalcurrent.origin + this.goalcurrent.maxs; - destorg = this.origin; - destorg.x = bound(m1_x, destorg.x, m2_x); - destorg.y = bound(m1_y, destorg.y, m2_y); - destorg.z = bound(m1_z, destorg.z, m2_z); - diff = destorg - this.origin; - //dist = vlen(diff); - dir = normalize(diff); - flatdir = diff;flatdir.z = 0; - flatdir = normalize(flatdir); - gco = (this.goalcurrent.absmin + this.goalcurrent.absmax) * 0.5; - - //if (this.bot_dodgevector_time < time) - { - // this.bot_dodgevector_time = time + cvar("bot_ai_dodgeupdateinterval"); - // this.bot_dodgevector_jumpbutton = 1; - evadeobstacle = '0 0 0'; - evadelava = '0 0 0'; - - if (this.waterlevel) - { - if(this.waterlevel>WATERLEVEL_SWIMMING) - { - // flatdir_z = 1; - this.aistatus |= AI_STATUS_OUT_WATER; - } - else - { - if(this.velocity.z >= 0 && !(this.watertype == CONTENT_WATER && gco.z < this.origin.z) && - ( !(this.waterlevel == WATERLEVEL_WETFEET && this.watertype == CONTENT_WATER) || this.aistatus & AI_STATUS_OUT_WATER)) - PHYS_INPUT_BUTTON_JUMP(this) = true; - else - PHYS_INPUT_BUTTON_JUMP(this) = false; - } - dir = normalize(flatdir); - makevectors(this.v_angle.y * '0 1 0'); - } - else - { - if(this.aistatus & AI_STATUS_OUT_WATER) - this.aistatus &= ~AI_STATUS_OUT_WATER; - - // jump if going toward an obstacle that doesn't look like stairs we - // can walk up directly - tracebox(this.origin, this.mins, this.maxs, this.origin + this.velocity * 0.2, false, this); - if (trace_fraction < 1) - if (trace_plane_normal.z < 0.7) - { - s = trace_fraction; - tracebox(this.origin + stepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + stepheightvec, false, this); - if (trace_fraction < s + 0.01) - if (trace_plane_normal.z < 0.7) - { - s = trace_fraction; - tracebox(this.origin + jumpstepheightvec, this.mins, this.maxs, this.origin + this.velocity * 0.2 + jumpstepheightvec, false, this); - if (trace_fraction > s) - PHYS_INPUT_BUTTON_JUMP(this) = true; - } - } - - // avoiding dangers and obstacles - vector dst_ahead, dst_down; - makevectors(this.v_angle.y * '0 1 0'); - dst_ahead = this.origin + this.view_ofs + (this.velocity * 0.4) + (v_forward * 32 * 3); - dst_down = dst_ahead - '0 0 1500'; - - // Look ahead - traceline(this.origin + this.view_ofs, dst_ahead, true, NULL); - - // Check head-banging against walls - if(vdist(this.origin + this.view_ofs - trace_endpos, <, 25) && !(this.aistatus & AI_STATUS_OUT_WATER)) - { - PHYS_INPUT_BUTTON_JUMP(this) = true; - if(this.facingwalltime && time > this.facingwalltime) - { - this.ignoregoal = this.goalcurrent; - this.ignoregoaltime = time + autocvar_bot_ai_ignoregoal_timeout; - this.bot_strategytime = 0; - return; - } - else - { - this.facingwalltime = time + 0.05; - } - } - else - { - this.facingwalltime = 0; - - if(this.ignoregoal != NULL && time > this.ignoregoaltime) - { - this.ignoregoal = NULL; - this.ignoregoaltime = 0; - } - } - - // Check for water/slime/lava and dangerous edges - // (only when the bot is on the ground or jumping intentionally) - this.aistatus &= ~AI_STATUS_DANGER_AHEAD; - - if(trace_fraction == 1 && this.jumppadcount == 0 && !this.goalcurrent.wphardwired ) - if((IS_ONGROUND(this)) || (this.aistatus & AI_STATUS_RUNNING) || PHYS_INPUT_BUTTON_JUMP(this)) - { - // Look downwards - traceline(dst_ahead , dst_down, true, NULL); - // te_lightning2(NULL, this.origin, dst_ahead); // Draw "ahead" look - // te_lightning2(NULL, dst_ahead, dst_down); // Draw "downwards" look - if(trace_endpos.z < this.origin.z + this.mins.z) - { - s = pointcontents(trace_endpos + '0 0 1'); - if (s != CONTENT_SOLID) - if (s == CONTENT_LAVA || s == CONTENT_SLIME) - evadelava = normalize(this.velocity) * -1; - else if (s == CONTENT_SKY) - evadeobstacle = normalize(this.velocity) * -1; - else if (!boxesoverlap(dst_ahead - this.view_ofs + this.mins, dst_ahead - this.view_ofs + this.maxs, - this.goalcurrent.absmin, this.goalcurrent.absmax)) - { - // if ain't a safe goal with "holes" (like the jumpad on soylent) - // and there is a trigger_hurt below - if(tracebox_hits_trigger_hurt(dst_ahead, this.mins, this.maxs, trace_endpos)) - { - // Remove dangerous dynamic goals from stack - LOG_TRACE("bot ", this.netname, " avoided the goal ", this.goalcurrent.classname, " ", etos(this.goalcurrent), " because it led to a dangerous path; goal stack cleared\n"); - navigation_clearroute(this); - return; - } - } - } - } - - dir = flatdir; - evadeobstacle.z = 0; - evadelava.z = 0; - makevectors(this.v_angle.y * '0 1 0'); - - if(evadeobstacle!='0 0 0'||evadelava!='0 0 0') - this.aistatus |= AI_STATUS_DANGER_AHEAD; - } - - dodge = havocbot_dodge(this); - dodge = dodge * bound(0,0.5+(skill+this.bot_dodgeskill)*0.1,1); - 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); - // this.bot_dodgevector = dir; - // this.bot_dodgevector_jumpbutton = PHYS_INPUT_BUTTON_JUMP(this); - } - - 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; - } - else - { - if(this.origin.z + this.mins.z > this.ladder_entity.origin.z + this.ladder_entity.mins.z) - dir.z = -1; - } - } - - //dir = this.bot_dodgevector; - //if (this.bot_dodgevector_jumpbutton) - // PHYS_INPUT_BUTTON_JUMP(this) = true; - this.movement_x = dir * v_forward * maxspeed; - this.movement_y = dir * v_right * maxspeed; - this.movement_z = dir * v_up * maxspeed; - - // Emulate keyboard interface - if (skill < 10) - havocbot_keyboard_movement(this, destorg); - - // Bunnyhop! -// if(this.aistatus & AI_STATUS_ROAMING) - if(this.goalcurrent) - if(skill+this.bot_moveskill >= autocvar_bot_ai_bunnyhop_skilloffset) - havocbot_bunnyhop(this, dir); - - if ((dir * v_up) >= autocvar_sv_jumpvelocity*0.5 && (IS_ONGROUND(this))) PHYS_INPUT_BUTTON_JUMP(this) = true; - if (((dodge * v_up) > 0) && random()*frametime >= 0.2*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) PHYS_INPUT_BUTTON_JUMP(this) = true; - if (((dodge * v_up) < 0) && random()*frametime >= 0.5*bound(0,(10-skill-this.bot_dodgeskill)*0.1,1)) this.havocbot_ducktime=time+0.3/bound(0.1,skill+this.bot_dodgeskill,10); -} - -void havocbot_chooseenemy(entity this) -{ - entity head, best, head2; - float rating, bestrating, hf; - vector eye, v; - if (autocvar_bot_nofire || IS_INDEPENDENT_PLAYER(this)) - { - this.enemy = NULL; - return; - } - if (this.enemy) - { - if (!bot_shouldattack(this, this.enemy)) - { - // enemy died or something, find a new target - this.enemy = NULL; - this.havocbot_chooseenemy_finished = time; - } - else if (this.havocbot_stickenemy) - { - // tracking last chosen enemy - // if enemy is visible - // and not really really far away - // and we're not severely injured - // then keep tracking for a half second into the future - 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) - { - // remain tracking him for a shot while (case he went after a small corner or pilar - this.havocbot_chooseenemy_finished = time + 0.5; - return; - } - // enemy isn't visible, or is far away, or we're injured severely - // so stop preferring this enemy - // (it will still take a half second until a new one is chosen) - this.havocbot_stickenemy = 0; - } - } - if (time < this.havocbot_chooseenemy_finished) - return; - this.havocbot_chooseenemy_finished = time + autocvar_bot_ai_enemydetectioninterval; - eye = this.origin + this.view_ofs; - best = NULL; - bestrating = 100000000; - head = head2 = findchainfloat(bot_attack, true); - - // Backup hit flags - hf = this.dphitcontentsmask; - - // Search for enemies, if no enemy can be seen directly try to look through transparent objects - - this.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_CORPSE; - - bool scan_transparent = false; - bool scan_secondary_targets = false; - bool have_secondary_targets = false; - while(true) - { - scan_secondary_targets = false; -LABEL(scan_targets) - for( ; head; head = head.chain) - { - if(!scan_secondary_targets) - { - if(head.classname == "misc_breakablemodel") - { - have_secondary_targets = true; - continue; - } - } - else - { - if(head.classname != "misc_breakablemodel") - continue; - } - - v = (head.absmin + head.absmax) * 0.5; - rating = vlen(v - eye); - if (rating rating) - if (bot_shouldattack(this, head)) - { - traceline(eye, v, true, this); - if (trace_ent == head || trace_fraction >= 1) - { - best = head; - bestrating = rating; - } - } - } - - if(!best && have_secondary_targets && !scan_secondary_targets) - { - scan_secondary_targets = true; - // restart the loop - head = head2; - bestrating = 100000000; - goto 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 - break; - if(scan_transparent) - break; - - // Set flags to see through transparent objects - this.dphitcontentsmask |= DPCONTENTS_OPAQUE; - - head = head2; - scan_transparent = true; - } - - // Restore hit flags - this.dphitcontentsmask = hf; - - this.enemy = best; - this.havocbot_stickenemy = true; - if(best && best.classname == "misc_breakablemodel") - this.havocbot_stickenemy = false; -} - -float havocbot_chooseweapon_checkreload(entity this, int new_weapon) -{ - // bots under this skill cannot find unloaded weapons to reload idly when not in combat, - // so skip this for them, or they'll never get to reload their weapons at all. - // this also allows bots under this skill to be more stupid, and reload more often during combat :) - if(skill < 5) - return false; - - // if this weapon is scheduled for reloading, don't switch to it during combat - if (this.weapon_load[new_weapon] < 0) - { - bool other_weapon_available = false; - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(it.wr_checkammo1(it, this) + it.wr_checkammo2(it, this)) - other_weapon_available = true; - )); - if(other_weapon_available) - return true; - } - - return false; -} - -void havocbot_chooseweapon(entity this) -{ - int i; - - // ;) - if(g_weaponarena_weapons == WEPSET(TUBA)) - { - PS(this).m_switchweapon = WEP_TUBA; - return; - } - - // TODO: clean this up by moving it to weapon code - if(this.enemy==NULL) - { - // If no weapon was chosen get the first available weapon - if(PS(this).m_weapon==WEP_Null) - FOREACH(Weapons, it != WEP_Null, LAMBDA( - if(client_hasweapon(this, it, true, false)) - { - PS(this).m_switchweapon = it; - return; - } - )); - return; - } - - // Do not change weapon during the next second after a combo - float f = time - this.lastcombotime; - if(f < 1) - return; - - float w; - float distance; distance=bound(10,vlen(this.origin-this.enemy.origin)-200,10000); - - // Should it do a weapon combo? - float af, ct, combo_time, combo; - - af = ATTACK_FINISHED(this, 0); - ct = autocvar_bot_ai_weapon_combo_threshold; - - // Bots with no skill will be 4 times more slower than "godlike" bots when doing weapon combos - // Ideally this 4 should be calculated as longest_weapon_refire / bot_ai_weapon_combo_threshold - combo_time = time + ct + (ct * ((-0.3*(skill+this.bot_weaponskill))+3)); - - combo = false; - - if(autocvar_bot_ai_weapon_combo) - if(PS(this).m_weapon.m_id == this.lastfiredweapon) - if(af > combo_time) - { - combo = true; - this.lastcombotime = time; - } - - distance *= pow(2, this.bot_rangepreference); - - // Custom weapon list based on distance to the enemy - if(bot_custom_weapon){ - - // Choose weapons for far distance - if ( distance > bot_distance_far ) { - for(i=0; i < Weapons_COUNT && bot_weapons_far[i] != -1 ; ++i){ - w = bot_weapons_far[i]; - if ( client_hasweapon(this, Weapons_from(w), true, false) ) - { - if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) - continue; - PS(this).m_switchweapon = Weapons_from(w); - return; - } - } - } - - // Choose weapons for mid distance - if ( distance > bot_distance_close) { - for(i=0; i < Weapons_COUNT && bot_weapons_mid[i] != -1 ; ++i){ - w = bot_weapons_mid[i]; - if ( client_hasweapon(this, Weapons_from(w), true, false) ) - { - if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) - continue; - PS(this).m_switchweapon = Weapons_from(w); - return; - } - } - } - - // Choose weapons for close distance - for(i=0; i < Weapons_COUNT && bot_weapons_close[i] != -1 ; ++i){ - w = bot_weapons_close[i]; - if ( client_hasweapon(this, Weapons_from(w), true, false) ) - { - if ((PS(this).m_weapon.m_id == w && combo) || havocbot_chooseweapon_checkreload(this, w)) - continue; - PS(this).m_switchweapon = Weapons_from(w); - return; - } - } - } -} - -void havocbot_aim(entity this) -{ - vector myvel, enemyvel; -// if(this.flags & FL_INWATER) -// return; - if (time < this.nextaim) - return; - this.nextaim = time + 0.1; - myvel = this.velocity; - if (!this.waterlevel) - myvel.z = 0; - if (this.enemy) - { - enemyvel = this.enemy.velocity; - if (!this.enemy.waterlevel) - enemyvel.z = 0; - lag_additem(this, time + this.ping, 0, 0, this.enemy, this.origin, myvel, (this.enemy.absmin + this.enemy.absmax) * 0.5, enemyvel); - } - else - lag_additem(this, time + this.ping, 0, 0, NULL, this.origin, myvel, ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5, '0 0 0'); -} - -bool havocbot_moveto_refresh_route(entity this) -{ - // Refresh path to goal if necessary - entity wp; - wp = this.havocbot_personal_waypoint; - navigation_goalrating_start(this); - navigation_routerating(this, wp, 10000, 10000); - navigation_goalrating_end(this); - return this.navigation_hasgoals; -} - -float havocbot_moveto(entity this, vector pos) -{ - entity wp; - - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) - { - // Step 4: Move to waypoint - if(this.havocbot_personal_waypoint==NULL) - { - LOG_TRACE("Error: ", this.netname, " trying to walk to a non existent personal waypoint\n"); - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; - return CMD_STATUS_ERROR; - } - - if (!bot_strategytoken_taken) - if(this.havocbot_personal_waypoint_searchtime= 30) - { - LOG_TRACE("Warning: can't walk to the personal waypoint located at ", vtos(this.havocbot_personal_waypoint.origin),"\n"); - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING; - remove(this.havocbot_personal_waypoint); - return CMD_STATUS_ERROR; - } - else - LOG_TRACE(this.netname, " can't walk to its personal waypoint (after ", ftos(this.havocbot_personal_waypoint_failcounter), " failed attempts), trying later\n"); - } - } - - if(autocvar_bot_debug_goalstack) - debuggoalstack(this); - - // Heading - vector dir = ( ( this.goalcurrent.absmin + this.goalcurrent.absmax ) * 0.5 ) - (this.origin + this.view_ofs); - dir.z = 0; - bot_aimdir(this, dir, -1); - - // Go! - havocbot_movetogoal(this); - - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_REACHED) - { - // Step 5: Waypoint reached - LOG_TRACE(this.netname, "'s personal waypoint reached\n"); - remove(this.havocbot_personal_waypoint); - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_REACHED; - return CMD_STATUS_FINISHED; - } - - return CMD_STATUS_EXECUTING; - } - - // Step 2: Linking waypoint - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_LINKING) - { - // Wait until it is linked - if(!this.havocbot_personal_waypoint.wplinked) - { - LOG_TRACE(this.netname, " waiting for personal waypoint to be linked\n"); - return CMD_STATUS_EXECUTING; - } - - this.havocbot_personal_waypoint_searchtime = time; // so we set the route next frame - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_LINKING; - this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_GOING; - - // Step 3: Route to waypoint - LOG_TRACE(this.netname, " walking to its personal waypoint\n"); - - return CMD_STATUS_EXECUTING; - } - - // Step 1: Spawning waypoint - wp = waypoint_spawnpersonal(this, pos); - if(wp==NULL) - { - LOG_TRACE("Error: Can't spawn personal waypoint at ",vtos(pos),"\n"); - return CMD_STATUS_ERROR; - } - - this.havocbot_personal_waypoint = wp; - this.havocbot_personal_waypoint_failcounter = 0; - this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_LINKING; - - // if pos is inside a teleport, then let's mark it as teleport waypoint - entity head; - for(head = NULL; (head = find(head, classname, "trigger_teleport")); ) - { - if(WarpZoneLib_BoxTouchesBrush(pos, pos, head, NULL)) - { - wp.wpflags |= WAYPOINTFLAG_TELEPORT; - this.lastteleporttime = 0; - } - } - -/* - if(wp.wpflags & WAYPOINTFLAG_TELEPORT) - print("routing to a teleporter\n"); - else - print("routing to a non-teleporter\n"); -*/ - - return CMD_STATUS_EXECUTING; -} - -float havocbot_resetgoal(entity this) -{ - navigation_clearroute(this); - return CMD_STATUS_FINISHED; -} - -void havocbot_setupbot(entity this) -{ - this.bot_ai = havocbot_ai; - this.cmd_moveto = havocbot_moveto; - this.cmd_resetgoal = havocbot_resetgoal; - - havocbot_chooserole(this); -} - -vector havocbot_dodge(entity this) -{ - // LordHavoc: disabled because this is too expensive - return '0 0 0'; -#if 0 - entity head; - vector dodge, v, n; - float danger, bestdanger, vl, d; - dodge = '0 0 0'; - bestdanger = -20; - // check for dangerous objects near bot or approaching bot - head = findchainfloat(bot_dodge, true); - while(head) - { - if (head.owner != this) - { - vl = vlen(head.velocity); - if (vl > autocvar_sv_maxspeed * 0.3) - { - n = normalize(head.velocity); - v = this.origin - head.origin; - d = v * n; - if (d > (0 - head.bot_dodgerating)) - if (d < (vl * 0.2 + head.bot_dodgerating)) - { - // calculate direction and distance from the flight path, by removing the forward axis - v = v - (n * (v * n)); - danger = head.bot_dodgerating - vlen(v); - if (bestdanger < danger) - { - bestdanger = danger; - // dodge to the side of the object - dodge = normalize(v); - } - } - } - else - { - danger = head.bot_dodgerating - vlen(head.origin - this.origin); - if (bestdanger < danger) - { - bestdanger = danger; - dodge = normalize(this.origin - head.origin); - } - } - } - head = head.chain; - } - return dodge; -#endif -} diff --git a/qcsrc/server/bot/havocbot/havocbot.qh b/qcsrc/server/bot/havocbot/havocbot.qh deleted file mode 100644 index 4a391b6e7c..0000000000 --- a/qcsrc/server/bot/havocbot/havocbot.qh +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once - -/* - * Globals and Fields - */ - -.float havocbot_keyboardskill; -.float facingwalltime, ignoregoaltime; -.float lastfiredweapon; -.float lastcombotime; -.float havocbot_blockhead; - -.float havocbot_keyboardtime; -.float havocbot_ducktime; -.float bot_timelastseengoal; -.float bot_canruntogoal; -.float bot_chooseweapontime; -.float rocketjumptime; -.float nextaim; -.float havocbot_personal_waypoint_searchtime; -.float havocbot_personal_waypoint_failcounter; -.float havocbot_chooseenemy_finished; -.float havocbot_stickenemy; -.float havocbot_role_timeout; - -.entity ignoregoal; -.entity bot_lastseengoal; -.entity havocbot_personal_waypoint; - -.vector havocbot_keyboard; - -/* - * Functions - */ - -void havocbot_ai(entity this); -void havocbot_aim(entity this); -void havocbot_setupbot(entity this); -void havocbot_movetogoal(entity this); -void havocbot_chooserole(entity this); -void havocbot_chooseenemy(entity this); -void havocbot_chooseweapon(entity this); -void havocbot_bunnyhop(entity this, vector dir); -void havocbot_keyboard_movement(entity this, vector destorg); - -float havocbot_resetgoal(entity this); -float havocbot_moveto(entity this, vector pos); -float havocbot_moveto_refresh_route(entity this); - -vector havocbot_dodge(entity this); - -.void(entity this) havocbot_role; -.void(entity this) havocbot_previous_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; - -/* - * Imports - */ - -.entity draggedby; -.float ladder_time; -.entity ladder_entity; diff --git a/qcsrc/server/bot/havocbot/roles.qc b/qcsrc/server/bot/havocbot/roles.qc deleted file mode 100644 index 034e29fabf..0000000000 --- a/qcsrc/server/bot/havocbot/roles.qc +++ /dev/null @@ -1,228 +0,0 @@ -#include "roles.qh" - -#include "havocbot.qh" - -#include "../bot.qh" -#include "../navigation.qh" - -.float max_armorvalue; -.float havocbot_role_timeout; - -.void(entity this) havocbot_previous_role; -.void(entity this) havocbot_role; - -void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) -{ - entity head; - float rating, d, discard, distance, friend_distance, enemy_distance; - vector o; - ratingscale = ratingscale * 0.0001; // items are rated around 10000 already - head = findchainfloat(bot_pickup, true); - - while (head) - { - o = (head.absmin + head.absmax) * 0.5; - distance = vlen(o - org); - friend_distance = 10000; enemy_distance = 10000; - rating = 0; - - if(!head.solid || distance > sradius || (head == this.ignoregoal && time < this.ignoregoaltime) ) - { - head = head.chain; - continue; - } - - // Check if the item can be picked up safely - if(head.classname == "droppedweapon") - { - traceline(o, o + '0 0 -1500', true, NULL); - - d = pointcontents(trace_endpos + '0 0 1'); - if(d & CONTENT_WATER || d & CONTENT_SLIME || d & CONTENT_LAVA) - { - head = head.chain; - continue; - } - if(tracebox_hits_trigger_hurt(head.origin, head.mins, head.maxs, trace_endpos)) - { - head = head.chain; - continue; - } - } - else - { - // Ignore items under water - traceline(head.origin + head.maxs, head.origin + head.maxs, MOVE_NORMAL, head); - if(trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) - { - head = head.chain; - continue; - } - } - - if(teamplay) - { - discard = false; - - FOREACH_CLIENT(IS_PLAYER(it) && it != this && !IS_DEAD(it), LAMBDA( - d = vlen(it.origin - o); // distance between player and item - - if ( it.team == this.team ) - { - if ( !IS_REAL_CLIENT(it) || discard ) - continue; - - if( d > friend_distance) - continue; - - friend_distance = d; - - discard = true; - - if( head.health && it.health > this.health ) - continue; - - if( head.armorvalue && it.armorvalue > this.armorvalue) - continue; - - if( head.weapons ) - if( head.weapons & ~it.weapons ) - continue; - - if (head.ammo_shells && it.ammo_shells > this.ammo_shells) - continue; - - if (head.ammo_nails && it.ammo_nails > this.ammo_nails) - continue; - - if (head.ammo_rockets && it.ammo_rockets > this.ammo_rockets) - continue; - - if (head.ammo_cells && it.ammo_cells > this.ammo_cells) - continue; - - if (head.ammo_plasma && it.ammo_plasma > this.ammo_plasma) - continue; - - discard = false; - } - else - { - // If enemy only track distances - // TODO: track only if visible ? - if( d < enemy_distance ) - enemy_distance = d; - } - )); - - // Rate the item only if no one needs it, or if an enemy is closer to it - if ( (enemy_distance < friend_distance && distance < enemy_distance) || - (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ) || !discard ) - rating = head.bot_pickupevalfunc(this, head); - - } - else - rating = head.bot_pickupevalfunc(this, head); - - if(rating > 0) - navigation_routerating(this, head, rating * ratingscale, 2000); - head = head.chain; - } -} - -void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius) -{ - entity head; - head = findchain(classname, "dom_controlpoint"); - while (head) - { - if(vdist((((head.absmin + head.absmax) * 0.5) - org), <, sradius)) - { - if(head.cnt > -1) // this is just being fought for - navigation_routerating(this, head, ratingscale, 5000); - else if(head.goalentity.cnt == 0) // unclaimed point - navigation_routerating(this, head, ratingscale * 0.5, 5000); - else if(head.goalentity.team != this.team) // other team's point - navigation_routerating(this, head, ratingscale * 0.2, 5000); - } - head = head.chain; - } -} - -void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) -{ - if (autocvar_bot_nofire) - return; - - // don't chase players if we're under water - if(this.waterlevel>WATERLEVEL_WETFEET) - return; - - int t; - - FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), LAMBDA( - // TODO: Merge this logic with the bot_shouldattack function - if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius)) - continue; - - // rate only visible enemies - /* - traceline(this.origin + this.view_ofs, it.origin, MOVE_NOMONSTERS, this); - if (trace_fraction < 1 || trace_ent != it) - continue; - */ - - if((it.flags & FL_INWATER) || (it.flags & FL_PARTIALGROUND)) - continue; - - // not falling - if((IS_ONGROUND(it)) == 0) - { - traceline(it.origin, it.origin + '0 0 -1500', true, NULL); - t = pointcontents(trace_endpos + '0 0 1'); - if(t != CONTENT_SOLID ) - if(t & CONTENT_WATER || t & CONTENT_SLIME || t & CONTENT_LAVA) - continue; - if(tracebox_hits_trigger_hurt(it.origin, it.mins, it.maxs, trace_endpos)) - continue; - } - - // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis - // adding a player as a goal seems to be quite dangerous, especially on space maps - // remove hack in navigation_poptouchedgoals() after performing this change - - t = (this.health + this.armorvalue ) / (it.health + it.armorvalue ); - navigation_routerating(this, it, t * ratingscale, 2000); - )); -} - -// legacy bot role for standard gamemodes -// go to best items -void havocbot_role_generic(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.bot_strategytime < time) - { - this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - navigation_goalrating_start(this); - havocbot_goalrating_items(this, 10000, this.origin, 10000); - havocbot_goalrating_enemyplayers(this, 20000, this.origin, 10000); - //havocbot_goalrating_waypoints(1, this.origin, 1000); - navigation_goalrating_end(this); - } -} - -void havocbot_chooserole_generic(entity this) -{ - this.havocbot_role = havocbot_role_generic; -} - -void havocbot_chooserole(entity this) -{ - LOG_TRACE("choosing a role...\n"); - this.bot_strategytime = 0; - if(!MUTATOR_CALLHOOK(HavocBot_ChooseRole, this)) - havocbot_chooserole_generic(this); -} diff --git a/qcsrc/server/bot/havocbot/roles.qh b/qcsrc/server/bot/havocbot/roles.qh deleted file mode 100644 index 5b1f2b530d..0000000000 --- a/qcsrc/server/bot/havocbot/roles.qh +++ /dev/null @@ -1,2 +0,0 @@ -#pragma once -void havocbot_goalrating_controlpoints(entity this, float ratingscale, vector org, float sradius); diff --git a/qcsrc/server/bot/havocbot/scripting.qh b/qcsrc/server/bot/havocbot/scripting.qh deleted file mode 100644 index 07cb4d6e60..0000000000 --- a/qcsrc/server/bot/havocbot/scripting.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void bot_clearqueue(entity bot); diff --git a/qcsrc/server/bot/navigation.qc b/qcsrc/server/bot/navigation.qc deleted file mode 100644 index 8deefdb397..0000000000 --- a/qcsrc/server/bot/navigation.qc +++ /dev/null @@ -1,1260 +0,0 @@ -#include "navigation.qh" - -#include "bot.qh" -#include "waypoints.qh" - -#include - -#include - -#include -#include - -.float speed; - -// rough simulation of walking from one point to another to test if a path -// can be traveled, used for waypoint linking and havocbot - -bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) -{ - vector org; - vector move; - vector dir; - float dist; - float totaldist; - float stepdist; - float yaw; - float ignorehazards; - float swimming; - - if(autocvar_bot_debug_tracewalk) - { - debugresetnodes(); - debugnode(e, start); - } - - move = end - start; - move.z = 0; - org = start; - dist = totaldist = vlen(move); - dir = normalize(move); - stepdist = 32; - ignorehazards = false; - swimming = false; - - // Analyze starting point - traceline(start, start, MOVE_NORMAL, e); - if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) - ignorehazards = true; - else - { - traceline( start, start + '0 0 -65536', MOVE_NORMAL, e); - if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) - { - ignorehazards = true; - swimming = true; - } - } - tracebox(start, m1, m2, start, MOVE_NOMONSTERS, e); - if (trace_startsolid) - { - // Bad start - if(autocvar_bot_debug_tracewalk) - debugnodestatus(start, DEBUG_NODE_FAIL); - - //print("tracewalk: ", vtos(start), " is a bad start\n"); - return false; - } - - // Movement loop - yaw = vectoyaw(move); - move = end - org; - for (;;) - { - if (boxesoverlap(end, end, org + m1 + '-1 -1 -1', org + m2 + '1 1 1')) - { - // Succeeded - if(autocvar_bot_debug_tracewalk) - debugnodestatus(org, DEBUG_NODE_SUCCESS); - - //print("tracewalk: ", vtos(start), " can reach ", vtos(end), "\n"); - return true; - } - if(autocvar_bot_debug_tracewalk) - debugnode(e, org); - - if (dist <= 0) - break; - if (stepdist > dist) - stepdist = dist; - dist = dist - stepdist; - traceline(org, org, MOVE_NORMAL, e); - if (!ignorehazards) - { - if (trace_dpstartcontents & (DPCONTENTS_SLIME | DPCONTENTS_LAVA)) - { - // hazards blocking path - if(autocvar_bot_debug_tracewalk) - debugnodestatus(org, DEBUG_NODE_FAIL); - - //print("tracewalk: ", vtos(start), " hits a hazard when trying to reach ", vtos(end), "\n"); - return false; - } - } - if (trace_dpstartcontents & DPCONTENTS_LIQUIDSMASK) - { - move = normalize(end - org); - tracebox(org, m1, m2, org + move * stepdist, movemode, e); - - if(autocvar_bot_debug_tracewalk) - debugnode(e, trace_endpos); - - if (trace_fraction < 1) - { - swimming = true; - org = trace_endpos - normalize(org - trace_endpos) * stepdist; - for (; org.z < end.z + e.maxs.z; org.z += stepdist) - { - if(autocvar_bot_debug_tracewalk) - debugnode(e, org); - - if(pointcontents(org) == CONTENT_EMPTY) - break; - } - - if(pointcontents(org + '0 0 1') != CONTENT_EMPTY) - { - if(autocvar_bot_debug_tracewalk) - debugnodestatus(org, DEBUG_NODE_FAIL); - - return false; - //print("tracewalk: ", vtos(start), " failed under water\n"); - } - continue; - - } - else - org = trace_endpos; - } - else - { - move = dir * stepdist + org; - tracebox(org, m1, m2, move, movemode, e); - - if(autocvar_bot_debug_tracewalk) - debugnode(e, trace_endpos); - - // hit something - if (trace_fraction < 1) - { - // check if we can walk over this obstacle, possibly by jumpstepping - tracebox(org + stepheightvec, m1, m2, move + stepheightvec, movemode, e); - if (trace_fraction < 1 || trace_startsolid) - { - tracebox(org + jumpstepheightvec, m1, m2, move + jumpstepheightvec, movemode, e); - if (trace_fraction < 1 || trace_startsolid) - { - if(autocvar_bot_debug_tracewalk) - debugnodestatus(trace_endpos, DEBUG_NODE_WARNING); - - // check for doors - traceline( org, move, movemode, e); - if ( trace_ent.classname == "door_rotating" || trace_ent.classname == "door") - { - vector nextmove; - move = trace_endpos; - while(trace_ent.classname == "door_rotating" || trace_ent.classname == "door") - { - nextmove = move + (dir * stepdist); - traceline( move, nextmove, movemode, e); - move = nextmove; - } - } - else - { - if(autocvar_bot_debug_tracewalk) - debugnodestatus(trace_endpos, DEBUG_NODE_FAIL); - - //print("tracewalk: ", vtos(start), " hit something when trying to reach ", vtos(end), "\n"); - //te_explosion(trace_endpos); - //print(ftos(e.dphitcontentsmask), "\n"); - return false; // failed - } - } - else - move = trace_endpos; - } - else - move = trace_endpos; - } - else - move = trace_endpos; - - // trace down from stepheight as far as possible and move there, - // if this starts in solid we try again without the stepup, and - // if that also fails we assume it is a wall - // (this is the same logic as the Quake walkmove function used) - tracebox(move, m1, m2, move + '0 0 -65536', movemode, e); - - // moved successfully - if(swimming) - { - float c; - c = pointcontents(org + '0 0 1'); - if (!(c == CONTENT_WATER || c == CONTENT_LAVA || c == CONTENT_SLIME)) - swimming = false; - else - continue; - } - - org = trace_endpos; - } - } - - //print("tracewalk: ", vtos(start), " did not arrive at ", vtos(end), " but at ", vtos(org), "\n"); - - // moved but didn't arrive at the intended destination - if(autocvar_bot_debug_tracewalk) - debugnodestatus(org, DEBUG_NODE_FAIL); - - return false; -} - -///////////////////////////////////////////////////////////////////////////// -// goal stack -///////////////////////////////////////////////////////////////////////////// - -// completely empty the goal stack, used when deciding where to go -void navigation_clearroute(entity this) -{ - //print("bot ", etos(this), " clear\n"); - this.navigation_hasgoals = false; - this.goalcurrent = NULL; - this.goalstack01 = NULL; - this.goalstack02 = NULL; - this.goalstack03 = NULL; - this.goalstack04 = NULL; - this.goalstack05 = NULL; - this.goalstack06 = NULL; - this.goalstack07 = NULL; - this.goalstack08 = NULL; - this.goalstack09 = NULL; - this.goalstack10 = NULL; - this.goalstack11 = NULL; - this.goalstack12 = NULL; - this.goalstack13 = NULL; - this.goalstack14 = NULL; - this.goalstack15 = NULL; - this.goalstack16 = NULL; - this.goalstack17 = NULL; - this.goalstack18 = NULL; - this.goalstack19 = NULL; - this.goalstack20 = NULL; - this.goalstack21 = NULL; - this.goalstack22 = NULL; - this.goalstack23 = NULL; - this.goalstack24 = NULL; - this.goalstack25 = NULL; - this.goalstack26 = NULL; - this.goalstack27 = NULL; - this.goalstack28 = NULL; - this.goalstack29 = NULL; - this.goalstack30 = NULL; - this.goalstack31 = NULL; -} - -// add a new goal at the beginning of the stack -// (in other words: add a new prerequisite before going to the later goals) -// NOTE: when a waypoint is added, the WP gets pushed first, then the -// next-closest WP on the shortest path to the WP -// That means, if the stack overflows, the bot will know how to do the FIRST 32 -// steps to the goal, and then recalculate the path. -void navigation_pushroute(entity this, entity e) -{ - //print("bot ", etos(this), " push ", etos(e), "\n"); - this.goalstack31 = this.goalstack30; - this.goalstack30 = this.goalstack29; - this.goalstack29 = this.goalstack28; - this.goalstack28 = this.goalstack27; - this.goalstack27 = this.goalstack26; - this.goalstack26 = this.goalstack25; - this.goalstack25 = this.goalstack24; - this.goalstack24 = this.goalstack23; - this.goalstack23 = this.goalstack22; - this.goalstack22 = this.goalstack21; - this.goalstack21 = this.goalstack20; - this.goalstack20 = this.goalstack19; - this.goalstack19 = this.goalstack18; - this.goalstack18 = this.goalstack17; - this.goalstack17 = this.goalstack16; - this.goalstack16 = this.goalstack15; - this.goalstack15 = this.goalstack14; - this.goalstack14 = this.goalstack13; - this.goalstack13 = this.goalstack12; - this.goalstack12 = this.goalstack11; - this.goalstack11 = this.goalstack10; - this.goalstack10 = this.goalstack09; - this.goalstack09 = this.goalstack08; - this.goalstack08 = this.goalstack07; - this.goalstack07 = this.goalstack06; - this.goalstack06 = this.goalstack05; - this.goalstack05 = this.goalstack04; - this.goalstack04 = this.goalstack03; - this.goalstack03 = this.goalstack02; - this.goalstack02 = this.goalstack01; - this.goalstack01 = this.goalcurrent; - this.goalcurrent = e; -} - -// remove first goal from stack -// (in other words: remove a prerequisite for reaching the later goals) -// (used when a spawnfunc_waypoint is reached) -void navigation_poproute(entity this) -{ - //print("bot ", etos(this), " pop\n"); - this.goalcurrent = this.goalstack01; - this.goalstack01 = this.goalstack02; - this.goalstack02 = this.goalstack03; - this.goalstack03 = this.goalstack04; - this.goalstack04 = this.goalstack05; - this.goalstack05 = this.goalstack06; - this.goalstack06 = this.goalstack07; - this.goalstack07 = this.goalstack08; - this.goalstack08 = this.goalstack09; - this.goalstack09 = this.goalstack10; - this.goalstack10 = this.goalstack11; - this.goalstack11 = this.goalstack12; - this.goalstack12 = this.goalstack13; - this.goalstack13 = this.goalstack14; - this.goalstack14 = this.goalstack15; - this.goalstack15 = this.goalstack16; - this.goalstack16 = this.goalstack17; - this.goalstack17 = this.goalstack18; - this.goalstack18 = this.goalstack19; - this.goalstack19 = this.goalstack20; - this.goalstack20 = this.goalstack21; - this.goalstack21 = this.goalstack22; - this.goalstack22 = this.goalstack23; - this.goalstack23 = this.goalstack24; - this.goalstack24 = this.goalstack25; - this.goalstack25 = this.goalstack26; - this.goalstack26 = this.goalstack27; - this.goalstack27 = this.goalstack28; - this.goalstack28 = this.goalstack29; - this.goalstack29 = this.goalstack30; - this.goalstack30 = this.goalstack31; - this.goalstack31 = NULL; -} - -float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist) -{ - float dist; - dist = vlen(v - org); - if (bestdist > dist) - { - traceline(v, org, true, ent); - if (trace_fraction == 1) - { - if (walkfromwp) - { - if (tracewalk(ent, v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), org, bot_navigation_movemode)) - return true; - } - else - { - if (tracewalk(ent, org, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v, bot_navigation_movemode)) - return true; - } - } - } - return false; -} - -// find the spawnfunc_waypoint near a dynamic goal such as a dropped weapon -entity navigation_findnearestwaypoint_withdist_except(entity ent, float walkfromwp, float bestdist, entity except) -{ - entity waylist, w, best; - vector v, org, pm1, pm2; - - pm1 = ent.origin + ent.mins; - pm2 = ent.origin + ent.maxs; - waylist = findchain(classname, "waypoint"); - - // do two scans, because box test is cheaper - w = waylist; - while (w) - { - // if object is touching spawnfunc_waypoint - if(w != ent && w != except) - if (boxesoverlap(pm1, pm2, w.absmin, w.absmax)) - return w; - w = w.chain; - } - - org = ent.origin + 0.5 * (ent.mins + ent.maxs); - org.z = ent.origin.z + ent.mins.z - STAT(PL_MIN, NULL).z; // player height - // TODO possibly make other code have the same support for bboxes - if(ent.tag_entity) - org = org + ent.tag_entity.origin; - if (navigation_testtracewalk) - te_plasmaburn(org); - - best = NULL; - - // box check failed, try walk - w = waylist; - while (w) - { - // if object can walk from spawnfunc_waypoint - if(w != ent) - { - if (w.wpisbox) - { - vector wm1, wm2; - wm1 = w.origin + w.mins; - wm2 = w.origin + w.maxs; - v.x = bound(wm1_x, org.x, wm2_x); - v.y = bound(wm1_y, org.y, wm2_y); - v.z = bound(wm1_z, org.z, wm2_z); - } - else - v = w.origin; - if(navigation_waypoint_will_link(v, org, ent, walkfromwp, bestdist)) - { - bestdist = vlen(v - org); - best = w; - } - } - w = w.chain; - } - return best; -} -entity navigation_findnearestwaypoint(entity ent, float walkfromwp) -{ - entity wp = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, NULL); - if (autocvar_g_waypointeditor_auto) - { - entity wp2 = navigation_findnearestwaypoint_withdist_except(ent, walkfromwp, 1050, wp); - if (wp && !wp2) - wp.wpflags |= WAYPOINTFLAG_PROTECTED; - } - return wp; -} - -// finds the waypoints near the bot initiating a navigation query -float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist) -{ - entity head; - vector v, m1, m2, diff; - float c; -// navigation_testtracewalk = true; - c = 0; - head = waylist; - while (head) - { - if (!head.wpconsidered) - { - if (head.wpisbox) - { - m1 = head.origin + head.mins; - m2 = head.origin + head.maxs; - v = this.origin; - v.x = bound(m1_x, v.x, m2_x); - v.y = bound(m1_y, v.y, m2_y); - v.z = bound(m1_z, v.z, m2_z); - } - else - v = head.origin; - diff = v - this.origin; - diff.z = max(0, diff.z); - if(vdist(diff, <, maxdist)) - { - head.wpconsidered = true; - if (tracewalk(this, this.origin, this.mins, this.maxs, v, bot_navigation_movemode)) - { - head.wpnearestpoint = v; - head.wpcost = vlen(v - this.origin) + head.dmg; - head.wpfire = 1; - head.enemy = NULL; - c = c + 1; - } - } - } - head = head.chain; - } - //navigation_testtracewalk = false; - return c; -} - -// updates a path link if a spawnfunc_waypoint link is better than the current one -void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p) -{ - vector m1; - vector m2; - vector v; - if (wp.wpisbox) - { - m1 = wp.absmin; - m2 = wp.absmax; - v.x = bound(m1_x, p.x, m2_x); - v.y = bound(m1_y, p.y, m2_y); - v.z = bound(m1_z, p.z, m2_z); - } - else - v = wp.origin; - cost2 = cost2 + vlen(v - p); - if (wp.wpcost > cost2) - { - wp.wpcost = cost2; - wp.enemy = w; - wp.wpfire = 1; - wp.wpnearestpoint = v; - } -} - -// queries the entire spawnfunc_waypoint network for pathes leading away from the bot -void navigation_markroutes(entity this, entity fixed_source_waypoint) -{ - entity w, wp, waylist; - float searching, cost, cost2; - vector p; - w = waylist = findchain(classname, "waypoint"); - while (w) - { - w.wpconsidered = false; - w.wpnearestpoint = '0 0 0'; - w.wpcost = 10000000; - w.wpfire = 0; - w.enemy = NULL; - w = w.chain; - } - - if(fixed_source_waypoint) - { - fixed_source_waypoint.wpconsidered = true; - fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs); - fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; - fixed_source_waypoint.wpfire = 1; - fixed_source_waypoint.enemy = NULL; - } - else - { - // try a short range search for the nearest waypoints, and expand the search repeatedly if none are found - // as this search is expensive we will use lower values if the bot is on the air - float i, increment, maxdistance; - if(IS_ONGROUND(this)) - { - increment = 750; - maxdistance = 50000; - } - else - { - increment = 500; - maxdistance = 1500; - } - - for(i=increment;!navigation_markroutes_nearestwaypoints(this, waylist, i)&&i cost2 + w.wp00mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp01;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp01mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp02;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp02mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp03;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp03mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp04;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp04mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp05;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp05mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp06;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp06mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp07;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp07mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp08;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp08mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp09;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp09mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp10;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp10mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp11;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp11mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp12;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp12mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp13;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp13mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp14;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp14mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp15;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp15mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp16;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp16mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp17;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp17mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp18;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp18mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp19;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp19mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp20;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp20mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp21;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp21mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp22;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp22mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp23;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp23mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp24;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp24mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp25;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp25mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp26;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp26mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp27;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp27mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp28;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp28mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp29;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp29mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp30;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp30mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - wp = w.wp31;if (wp){cost2 = cost + wp.dmg;if (wp.wpcost > cost2 + w.wp31mincost) navigation_markroutes_checkwaypoint(w, wp, cost2, p); - }}}}}}}}}}}}}}}}}}}}}}}}}}}}}}}} - } - w = w.chain; - } - } -} - -// queries the entire spawnfunc_waypoint network for pathes leading to the bot -void navigation_markroutes_inverted(entity fixed_source_waypoint) -{ - entity w, wp, waylist; - float searching, cost, cost2; - vector p; - w = waylist = findchain(classname, "waypoint"); - while (w) - { - w.wpconsidered = false; - w.wpnearestpoint = '0 0 0'; - w.wpcost = 10000000; - w.wpfire = 0; - w.enemy = NULL; - w = w.chain; - } - - if(fixed_source_waypoint) - { - fixed_source_waypoint.wpconsidered = true; - fixed_source_waypoint.wpnearestpoint = fixed_source_waypoint.origin + 0.5 * (fixed_source_waypoint.mins + fixed_source_waypoint.maxs); - fixed_source_waypoint.wpcost = fixed_source_waypoint.dmg; // the cost to get from X to fixed_source_waypoint - fixed_source_waypoint.wpfire = 1; - fixed_source_waypoint.enemy = NULL; - } - else - { - error("need to start with a waypoint\n"); - } - - searching = true; - while (searching) - { - searching = false; - w = waylist; - while (w) - { - if (w.wpfire) - { - searching = true; - w.wpfire = 0; - cost = w.wpcost; // cost to walk from w to home - p = w.wpnearestpoint; - for(wp = waylist; wp; wp = wp.chain) - { - if(w != wp.wp00) if(w != wp.wp01) if(w != wp.wp02) if(w != wp.wp03) - if(w != wp.wp04) if(w != wp.wp05) if(w != wp.wp06) if(w != wp.wp07) - if(w != wp.wp08) if(w != wp.wp09) if(w != wp.wp10) if(w != wp.wp11) - if(w != wp.wp12) if(w != wp.wp13) if(w != wp.wp14) if(w != wp.wp15) - if(w != wp.wp16) if(w != wp.wp17) if(w != wp.wp18) if(w != wp.wp19) - if(w != wp.wp20) if(w != wp.wp21) if(w != wp.wp22) if(w != wp.wp23) - if(w != wp.wp24) if(w != wp.wp25) if(w != wp.wp26) if(w != wp.wp27) - if(w != wp.wp28) if(w != wp.wp29) if(w != wp.wp30) if(w != wp.wp31) - continue; - cost2 = cost + wp.dmg; - navigation_markroutes_checkwaypoint(w, wp, cost2, p); - } - } - w = w.chain; - } - } -} - -// 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) -{ - entity nwp; - vector o; - if (!e) - return; - - if(e.blacklisted) - return; - - o = (e.absmin + e.absmax) * 0.5; - - //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 - o, >, autocvar_bot_ai_navigation_jetpack_mindistance)) - { - vector pointa, pointb; - - LOG_DEBUG("jetpack ai: evaluating path for ", e.classname, "\n"); - - // Point A - traceline(this.origin, this.origin + '0 0 65535', MOVE_NORMAL, this); - pointa = trace_endpos - '0 0 1'; - - // Point B - traceline(o, o + '0 0 65535', MOVE_NORMAL, e); - pointb = trace_endpos - '0 0 1'; - - // Can I see these two points from the sky? - traceline(pointa, pointb, MOVE_NORMAL, this); - - if(trace_fraction==1) - { - LOG_DEBUG("jetpack ai: can bridge these two points\n"); - - // Lower the altitude of these points as much as possible - float zdistance, xydistance, cost, t, fuel; - vector down, npa, npb; - - down = '0 0 -1' * (STAT(PL_MAX, NULL).z - STAT(PL_MIN, NULL).z) * 10; - - do{ - npa = pointa + down; - npb = pointb + down; - - if(npa.z<=this.absmax.z) - break; - - if(npb.z<=e.absmax.z) - break; - - traceline(npa, npb, MOVE_NORMAL, this); - if(trace_fraction==1) - { - pointa = npa; - pointb = npb; - } - } - while(trace_fraction == 1); - - - // Rough estimation of fuel consumption - // (ignores acceleration and current xyz velocity) - xydistance = vlen(pointa - pointb); - zdistance = fabs(pointa.z - this.origin.z); - - t = zdistance / autocvar_g_jetpack_maxspeed_up; - t += xydistance / autocvar_g_jetpack_maxspeed_side; - fuel = t * autocvar_g_jetpack_fuel * 0.8; - - LOG_DEBUG(strcat("jetpack ai: required fuel ", ftos(fuel), " this.ammo_fuel ", ftos(this.ammo_fuel), "\n")); - - // enough fuel ? - if(this.ammo_fuel>fuel) - { - // Estimate cost - // (as onground costs calculation is mostly based on distances, here we do the same establishing some relationship - // - between air and ground speeds) - - cost = xydistance / (autocvar_g_jetpack_maxspeed_side/autocvar_sv_maxspeed); - cost += zdistance / (autocvar_g_jetpack_maxspeed_up/autocvar_sv_maxspeed); - cost *= 1.5; - - // Compare against other goals - f = f * rangebias / (rangebias + cost); - - if (navigation_bestrating < f) - { - LOG_DEBUG(strcat("jetpack path: added goal ", e.classname, " (with rating ", ftos(f), ")\n")); - navigation_bestrating = f; - navigation_bestgoal = e; - this.navigation_jetpack_goal = e; - this.navigation_jetpack_point = pointb; - } - return; - } - } - } - - //te_wizspike(e.origin); - //bprint(etos(e)); - //bprint("\n"); - // update the cached spawnfunc_waypoint link on a dynamic item entity - if(e.classname == "waypoint" && !(e.wpflags & WAYPOINTFLAG_PERSONAL)) - { - nwp = e; - } - else - { - float search; - - search = true; - - if(e.flags & FL_ITEM) - { - if (!(e.flags & FL_WEAPON)) - if(e.nearestwaypoint) - search = false; - } - else if (e.flags & FL_WEAPON) - { - if(e.classname != "droppedweapon") - if(e.nearestwaypoint) - search = false; - } - - if(search) - if (time > e.nearestwaypointtimeout) - { - nwp = navigation_findnearestwaypoint(e, true); - if(nwp) - e.nearestwaypoint = nwp; - else - { - LOG_DEBUG(strcat("FAILED to find a nearest waypoint to '", e.classname, "' #", etos(e), "\n")); - - if(e.flags & FL_ITEM) - e.blacklisted = true; - else if (e.flags & FL_WEAPON) - { - if(e.classname != "droppedweapon") - e.blacklisted = true; - } - - if(e.blacklisted) - { - LOG_DEBUG(strcat("The entity '", e.classname, "' is going to be excluded from path finding during this match\n")); - return; - } - } - - // TODO: Cleaner solution, probably handling this timeout from ctf.qc - if(e.classname=="item_flag_team") - e.nearestwaypointtimeout = time + 2; - else - e.nearestwaypointtimeout = time + random() * 3 + 5; - } - nwp = e.nearestwaypoint; - } - - LOG_DEBUG(strcat("-- checking ", e.classname, " (with cost ", ftos(nwp.wpcost), ")\n")); - if (nwp) - if (nwp.wpcost < 10000000) - { - //te_wizspike(nwp.wpnearestpoint); - LOG_DEBUG(strcat(e.classname, " ", ftos(f), "/(1+", ftos((nwp.wpcost + vlen(e.origin - nwp.wpnearestpoint))), "/", ftos(rangebias), ") = ")); - f = f * rangebias / (rangebias + (nwp.wpcost + vlen(o - nwp.wpnearestpoint))); - LOG_DEBUG(strcat("considering ", e.classname, " (with rating ", ftos(f), ")\n")); - if (navigation_bestrating < f) - { - LOG_DEBUG(strcat("ground path: added goal ", e.classname, " (with rating ", ftos(f), ")\n")); - navigation_bestrating = f; - navigation_bestgoal = e; - } - } -} - -// adds an item to the the goal stack with the path to a given item -bool navigation_routetogoal(entity this, entity e, vector startposition) -{ - this.goalentity = e; - - // if there is no goal, just exit - if (!e) - return false; - - this.navigation_hasgoals = true; - - // put the entity on the goal stack - //print("routetogoal ", etos(e), "\n"); - navigation_pushroute(this, e); - - if(g_jetpack) - if(e==this.navigation_jetpack_goal) - return true; - - // if it can reach the goal there is nothing more to do - if (tracewalk(this, startposition, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), (e.absmin + e.absmax) * 0.5, bot_navigation_movemode)) - return true; - - // see if there are waypoints describing a path to the item - if(e.classname != "waypoint" || (e.wpflags & WAYPOINTFLAG_PERSONAL)) - e = e.nearestwaypoint; - else - e = e.enemy; // we already have added it, so... - - if(e == NULL) - return false; - - for (;;) - { - // add the spawnfunc_waypoint to the path - navigation_pushroute(this, e); - e = e.enemy; - - if(e==NULL) - break; - } - - return false; -} - -// removes any currently touching waypoints from the goal stack -// (this is how bots detect if they reached a goal) -void navigation_poptouchedgoals(entity this) -{ - vector org, m1, m2; - org = this.origin; - m1 = org + this.mins; - m2 = org + this.maxs; - - if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT) - { - if(this.lastteleporttime>0) - if(time-this.lastteleporttime<(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL)?2:0.15) - { - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) - if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) - { - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; - this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; - } - navigation_poproute(this); - return; - } - } - - // If for some reason the bot is closer to the next goal, pop the current one - if(this.goalstack01) - if(vlen2(this.goalcurrent.origin - this.origin) > vlen2(this.goalstack01.origin - this.origin)) - if(checkpvs(this.origin + this.view_ofs, this.goalstack01)) - if(tracewalk(this, this.origin, this.mins, this.maxs, (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5, bot_navigation_movemode)) - { - LOG_DEBUG(strcat("path optimized for ", this.netname, ", removed a goal from the queue\n")); - navigation_poproute(this); - // TODO this may also be a nice idea to do "early" (e.g. by - // manipulating the vlen() comparisons) to shorten paths in - // general - this would make bots walk more "on rails" than - // "zigzagging" which they currently do with sufficiently - // random-like waypoints, and thus can make a nice bot - // personality property - } - - // HACK: remove players/bots as goals, they can lead a bot to unexpected places (cliffs, lava, etc) - // TODO: rate waypoints near the targetted player at that moment, instead of the player itthis - if(IS_PLAYER(this.goalcurrent)) - navigation_poproute(this); - - // aid for detecting jump pads better (distance based check fails sometimes) - if(this.goalcurrent.wpflags & WAYPOINTFLAG_TELEPORT && this.jumppadcount > 0 ) - navigation_poproute(this); - - // Loose goal touching check when running - if(this.aistatus & AI_STATUS_RUNNING) - if(this.speed >= autocvar_sv_maxspeed) // if -really- running - if(this.goalcurrent.classname=="waypoint") - { - if(vdist(this.origin - this.goalcurrent.origin, <, 150)) - { - traceline(this.origin + this.view_ofs , this.goalcurrent.origin, true, NULL); - if(trace_fraction==1) - { - // Detect personal waypoints - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) - if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) - { - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; - this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; - } - - navigation_poproute(this); - } - } - } - - while (this.goalcurrent && boxesoverlap(m1, m2, this.goalcurrent.absmin, this.goalcurrent.absmax)) - { - // Detect personal waypoints - if(this.aistatus & AI_STATUS_WAYPOINT_PERSONAL_GOING) - if(this.goalcurrent.wpflags & WAYPOINTFLAG_PERSONAL && this.goalcurrent.owner==this) - { - this.aistatus &= ~AI_STATUS_WAYPOINT_PERSONAL_GOING; - this.aistatus |= AI_STATUS_WAYPOINT_PERSONAL_REACHED; - } - - navigation_poproute(this); - } -} - -// begin a goal selection session (queries spawnfunc_waypoint network) -void navigation_goalrating_start(entity this) -{ - if(this.aistatus & AI_STATUS_STUCK) - return; - - this.navigation_jetpack_goal = NULL; - navigation_bestrating = -1; - this.navigation_hasgoals = false; - navigation_clearroute(this); - navigation_bestgoal = NULL; - navigation_markroutes(this, NULL); -} - -// ends a goal selection session (updates goal stack to the best goal) -void navigation_goalrating_end(entity this) -{ - if(this.aistatus & AI_STATUS_STUCK) - return; - - navigation_routetogoal(this, navigation_bestgoal, this.origin); - LOG_DEBUG(strcat("best goal ", this.goalcurrent.classname , "\n")); - - // If the bot got stuck then try to reach the farthest waypoint - if (!this.navigation_hasgoals) - if (autocvar_bot_wander_enable) - { - if (!(this.aistatus & AI_STATUS_STUCK)) - { - LOG_DEBUG(strcat(this.netname, " cannot walk to any goal\n")); - this.aistatus |= AI_STATUS_STUCK; - } - - this.navigation_hasgoals = false; // Reset this value - } -} - -void botframe_updatedangerousobjects(float maxupdate) -{ - entity head, bot_dodgelist; - vector m1, m2, v, o; - float c, d, danger; - c = 0; - bot_dodgelist = findchainfloat(bot_dodge, true); - botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint"); - while (botframe_dangerwaypoint != NULL) - { - danger = 0; - m1 = botframe_dangerwaypoint.mins; - m2 = botframe_dangerwaypoint.maxs; - head = bot_dodgelist; - while (head) - { - v = head.origin; - v.x = bound(m1_x, v.x, m2_x); - v.y = bound(m1_y, v.y, m2_y); - v.z = bound(m1_z, v.z, m2_z); - o = (head.absmin + head.absmax) * 0.5; - d = head.bot_dodgerating - vlen(o - v); - if (d > 0) - { - traceline(o, v, true, NULL); - if (trace_fraction == 1) - danger = danger + d; - } - head = head.chain; - } - botframe_dangerwaypoint.dmg = danger; - c = c + 1; - if (c >= maxupdate) - break; - botframe_dangerwaypoint = find(botframe_dangerwaypoint, classname, "waypoint"); - } -} - -void navigation_unstuck(entity this) -{ - float search_radius = 1000; - - if (!autocvar_bot_wander_enable) - return; - - if (!bot_waypoint_queue_owner) - { - LOG_DEBUG(strcat(this.netname, " sutck, taking over the waypoints queue\n")); - bot_waypoint_queue_owner = this; - bot_waypoint_queue_bestgoal = NULL; - bot_waypoint_queue_bestgoalrating = 0; - } - - if(bot_waypoint_queue_owner!=this) - return; - - if (bot_waypoint_queue_goal) - { - // evaluate the next goal on the queue - float d = vlen(this.origin - bot_waypoint_queue_goal.origin); - LOG_DEBUG(strcat(this.netname, " evaluating ", bot_waypoint_queue_goal.classname, " with distance ", ftos(d), "\n")); - if(tracewalk(bot_waypoint_queue_goal, this.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), bot_waypoint_queue_goal.origin, bot_navigation_movemode)) - { - if( d > bot_waypoint_queue_bestgoalrating) - { - bot_waypoint_queue_bestgoalrating = d; - bot_waypoint_queue_bestgoal = bot_waypoint_queue_goal; - } - } - bot_waypoint_queue_goal = bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal; - - if (!bot_waypoint_queue_goal) - { - if (bot_waypoint_queue_bestgoal) - { - LOG_DEBUG(strcat(this.netname, " stuck, reachable waypoint found, heading to it\n")); - navigation_routetogoal(this, bot_waypoint_queue_bestgoal, this.origin); - this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; - this.aistatus &= ~AI_STATUS_STUCK; - } - else - { - LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n")); - } - - bot_waypoint_queue_owner = NULL; - } - } - else - { - if(bot_strategytoken!=this) - return; - - // build a new queue - LOG_DEBUG(strcat(this.netname, " stuck, scanning reachable waypoints within ", ftos(search_radius)," qu\n")); - - entity head, first; - - first = NULL; - head = findradius(this.origin, search_radius); - - while(head) - { - if(head.classname=="waypoint") - // if(!(head.wpflags & WAYPOINTFLAG_GENERATED)) - { - if(bot_waypoint_queue_goal) - bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = head; - else - first = head; - - bot_waypoint_queue_goal = head; - bot_waypoint_queue_goal.bot_waypoint_queue_nextgoal = NULL; - } - - head = head.chain; - } - - if (first) - bot_waypoint_queue_goal = first; - else - { - LOG_DEBUG(strcat(this.netname, " stuck, cannot walk to any waypoint at all\n")); - bot_waypoint_queue_owner = NULL; - } - } -} - -// Support for debugging tracewalk visually - -void debugresetnodes() -{ - debuglastnode = '0 0 0'; -} - -void debugnode(entity this, vector node) -{ - if (!IS_PLAYER(this)) - return; - - if(debuglastnode=='0 0 0') - { - debuglastnode = node; - return; - } - - te_lightning2(NULL, node, debuglastnode); - debuglastnode = node; -} - -void debugnodestatus(vector position, float status) -{ - vector c; - - switch (status) - { - case DEBUG_NODE_SUCCESS: - c = '0 15 0'; - break; - case DEBUG_NODE_WARNING: - c = '15 15 0'; - break; - case DEBUG_NODE_FAIL: - c = '15 0 0'; - break; - default: - c = '15 15 15'; - } - - te_customflash(position, 40, 2, c); -} - -// Support for debugging the goal stack visually - -.float goalcounter; -.vector lastposition; - -// Debug the goal stack visually -void debuggoalstack(entity this) -{ - entity goal; - vector org, go; - - if(this.goalcounter==0)goal=this.goalcurrent; - else if(this.goalcounter==1)goal=this.goalstack01; - else if(this.goalcounter==2)goal=this.goalstack02; - else if(this.goalcounter==3)goal=this.goalstack03; - else if(this.goalcounter==4)goal=this.goalstack04; - else if(this.goalcounter==5)goal=this.goalstack05; - else if(this.goalcounter==6)goal=this.goalstack06; - else if(this.goalcounter==7)goal=this.goalstack07; - else if(this.goalcounter==8)goal=this.goalstack08; - else if(this.goalcounter==9)goal=this.goalstack09; - else if(this.goalcounter==10)goal=this.goalstack10; - else if(this.goalcounter==11)goal=this.goalstack11; - else if(this.goalcounter==12)goal=this.goalstack12; - else if(this.goalcounter==13)goal=this.goalstack13; - else if(this.goalcounter==14)goal=this.goalstack14; - else if(this.goalcounter==15)goal=this.goalstack15; - else if(this.goalcounter==16)goal=this.goalstack16; - else if(this.goalcounter==17)goal=this.goalstack17; - else if(this.goalcounter==18)goal=this.goalstack18; - else if(this.goalcounter==19)goal=this.goalstack19; - else if(this.goalcounter==20)goal=this.goalstack20; - else if(this.goalcounter==21)goal=this.goalstack21; - else if(this.goalcounter==22)goal=this.goalstack22; - else if(this.goalcounter==23)goal=this.goalstack23; - else if(this.goalcounter==24)goal=this.goalstack24; - else if(this.goalcounter==25)goal=this.goalstack25; - else if(this.goalcounter==26)goal=this.goalstack26; - else if(this.goalcounter==27)goal=this.goalstack27; - else if(this.goalcounter==28)goal=this.goalstack28; - else if(this.goalcounter==29)goal=this.goalstack29; - else if(this.goalcounter==30)goal=this.goalstack30; - else if(this.goalcounter==31)goal=this.goalstack31; - else goal=NULL; - - if(goal==NULL) - { - this.goalcounter = 0; - this.lastposition='0 0 0'; - return; - } - - if(this.lastposition=='0 0 0') - org = this.origin; - else - org = this.lastposition; - - - go = ( goal.absmin + goal.absmax ) * 0.5; - te_lightning2(NULL, org, go); - this.lastposition = go; - - this.goalcounter++; -} diff --git a/qcsrc/server/bot/navigation.qh b/qcsrc/server/bot/navigation.qh deleted file mode 100644 index 7fef6afef3..0000000000 --- a/qcsrc/server/bot/navigation.qh +++ /dev/null @@ -1,78 +0,0 @@ -#pragma once -/* - * Globals and Fields - */ - -float navigation_bestrating; -float bot_navigation_movemode; -float navigation_testtracewalk; - -vector jumpstepheightvec; -vector stepheightvec; - -entity botframe_dangerwaypoint; -entity navigation_bestgoal; - -// stack of current goals (the last one of which may be an item or other -// desirable object, the rest are typically waypoints to reach it) -.entity goalcurrent, goalstack01, goalstack02, goalstack03; -.entity goalstack04, goalstack05, goalstack06, goalstack07; -.entity goalstack08, goalstack09, goalstack10, goalstack11; -.entity goalstack12, goalstack13, goalstack14, goalstack15; -.entity goalstack16, goalstack17, goalstack18, goalstack19; -.entity goalstack20, goalstack21, goalstack22, goalstack23; -.entity goalstack24, goalstack25, goalstack26, goalstack27; -.entity goalstack28, goalstack29, goalstack30, goalstack31; -.entity nearestwaypoint; - -.float nearestwaypointtimeout; -.float navigation_hasgoals; -.float lastteleporttime; - -.float blacklisted; - -.entity navigation_jetpack_goal; -.vector navigation_jetpack_point; - -const float DEBUG_NODE_SUCCESS = 1; -const float DEBUG_NODE_WARNING = 2; -const float DEBUG_NODE_FAIL = 3; -vector debuglastnode; - -entity bot_waypoint_queue_owner; // Owner of the temporary list of goals -entity bot_waypoint_queue_goal; // Head of the temporary list of goals -.entity bot_waypoint_queue_nextgoal; -entity bot_waypoint_queue_bestgoal; -float bot_waypoint_queue_bestgoalrating; - -/* - * Functions - */ - -void debugresetnodes(); -void debugnode(entity this, vector node); -void debugnodestatus(vector position, float status); - -void debuggoalstack(entity this); - -float tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode); - -float navigation_markroutes_nearestwaypoints(entity this, entity waylist, float maxdist); -float navigation_routetogoal(entity this, entity e, vector startposition); - -void navigation_clearroute(entity this); -void navigation_pushroute(entity this, entity e); -void navigation_poproute(entity this); -void navigation_markroutes_checkwaypoint(entity w, entity wp, float cost2, vector p); -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_poptouchedgoals(entity this); -void navigation_goalrating_start(entity this); -void navigation_goalrating_end(entity this); -void navigation_unstuck(entity this); - -void botframe_updatedangerousobjects(float maxupdate); - -entity navigation_findnearestwaypoint(entity ent, float walkfromwp); -float navigation_waypoint_will_link(vector v, vector org, entity ent, float walkfromwp, float bestdist); diff --git a/qcsrc/server/bot/null/_mod.inc b/qcsrc/server/bot/null/_mod.inc new file mode 100644 index 0000000000..d40f4512e9 --- /dev/null +++ b/qcsrc/server/bot/null/_mod.inc @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/server/bot/null/_mod.qh b/qcsrc/server/bot/null/_mod.qh new file mode 100644 index 0000000000..0449a1c43d --- /dev/null +++ b/qcsrc/server/bot/null/_mod.qh @@ -0,0 +1,2 @@ +// generated file; do not modify +#include diff --git a/qcsrc/server/bot/null/bot_null.qc b/qcsrc/server/bot/null/bot_null.qc new file mode 100644 index 0000000000..b4102cb6c0 --- /dev/null +++ b/qcsrc/server/bot/null/bot_null.qc @@ -0,0 +1,42 @@ +#include "bot_null.qh" + +#if 0 +bool bot_aim(entity this, float shotspeed, float shotspeedupward, float maxshottime, float applygravity) { return false; } +void bot_clientconnect(entity this) { } +void bot_clientdisconnect(entity this) { } +void bot_cmdhelp(string scmd) { } +void bot_endgame() { } +bool bot_fixcount() { return true; } +void bot_list_commands() { } +void bot_queuecommand(entity bot, string cmdstring) { } +void bot_relinkplayerlist() { } +void bot_resetqueues() { } +void bot_serverframe() { } +bool bot_shouldattack(entity this, entity e) { return false; } +void bot_think(entity this) { } + +entity find_bot_by_name(string name) { return NULL; } +entity find_bot_by_number(float number) { return NULL; } + +void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org, float sradius) { } +void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { } + +entity navigation_findnearestwaypoint(entity ent, float walkfromwp) { return NULL; } +void navigation_goalrating_end(entity this) { } +void navigation_goalrating_start(entity this) { } +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) { } + +bool tracewalk(entity e, vector start, vector m1, vector m2, vector end, float movemode) { return false; } + +void waypoint_remove(entity e) { } +void waypoint_saveall() { } +void waypoint_schedulerelinkall() { } +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) { } +void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) { } +entity waypoint_spawn(vector m1, vector m2, float f) { return NULL; } +#endif diff --git a/qcsrc/server/bot/null/bot_null.qh b/qcsrc/server/bot/null/bot_null.qh new file mode 100644 index 0000000000..28709a2b2e --- /dev/null +++ b/qcsrc/server/bot/null/bot_null.qh @@ -0,0 +1,3 @@ +#pragma once + +#include "../api.qh" diff --git a/qcsrc/server/bot/scripting.qc b/qcsrc/server/bot/scripting.qc deleted file mode 100644 index 3734ad8501..0000000000 --- a/qcsrc/server/bot/scripting.qc +++ /dev/null @@ -1,1341 +0,0 @@ -#include "scripting.qh" - -#include -#include - -#include "bot.qh" - -.int state; - -.float bot_cmdqueuebuf_allocated; -.float bot_cmdqueuebuf; -.float bot_cmdqueuebuf_start; -.float bot_cmdqueuebuf_end; - -void bot_clearqueue(entity bot) -{ - if(!bot.bot_cmdqueuebuf_allocated) - return; - buf_del(bot.bot_cmdqueuebuf); - bot.bot_cmdqueuebuf_allocated = false; - LOG_TRACE("bot ", bot.netname, " queue cleared\n"); -} - -void bot_queuecommand(entity bot, string cmdstring) -{ - if(!bot.bot_cmdqueuebuf_allocated) - { - bot.bot_cmdqueuebuf = buf_create(); - bot.bot_cmdqueuebuf_allocated = true; - bot.bot_cmdqueuebuf_start = 0; - bot.bot_cmdqueuebuf_end = 0; - } - - bufstr_set(bot.bot_cmdqueuebuf, bot.bot_cmdqueuebuf_end, cmdstring); - - // if the command was a "sound" command, precache the sound NOW - // this prevents lagging! - { - float sp; - string parm; - string cmdstr; - - sp = strstrofs(cmdstring, " ", 0); - if(sp >= 0) - { - parm = substring(cmdstring, sp + 1, -1); - cmdstr = substring(cmdstring, 0, sp); - if(cmdstr == "sound") - { - // find the LAST word - for (;;) - { - sp = strstrofs(parm, " ", 0); - if(sp < 0) - break; - parm = substring(parm, sp + 1, -1); - } - precache_sound(parm); - } - } - } - - bot.bot_cmdqueuebuf_end += 1; -} - -void bot_dequeuecommand(entity bot, float idx) -{ - if(!bot.bot_cmdqueuebuf_allocated) - error("dequeuecommand but no queue allocated"); - if(idx < bot.bot_cmdqueuebuf_start) - error("dequeueing a command in the past"); - if(idx >= bot.bot_cmdqueuebuf_end) - error("dequeueing a command in the future"); - bufstr_set(bot.bot_cmdqueuebuf, idx, ""); - if(idx == bot.bot_cmdqueuebuf_start) - bot.bot_cmdqueuebuf_start += 1; - if(bot.bot_cmdqueuebuf_start >= bot.bot_cmdqueuebuf_end) - bot_clearqueue(bot); -} - -string bot_readcommand(entity bot, float idx) -{ - if(!bot.bot_cmdqueuebuf_allocated) - error("readcommand but no queue allocated"); - if(idx < bot.bot_cmdqueuebuf_start) - error("reading a command in the past"); - if(idx >= bot.bot_cmdqueuebuf_end) - error("reading a command in the future"); - return bufstr_get(bot.bot_cmdqueuebuf, idx); -} - -bool bot_havecommand(entity this, int idx) -{ - if(!this.bot_cmdqueuebuf_allocated) - return false; - if(idx < this.bot_cmdqueuebuf_start) - return false; - if(idx >= this.bot_cmdqueuebuf_end) - return false; - return true; -} - -const int MAX_BOT_PLACES = 4; -.float bot_places_count; -.entity bot_places[MAX_BOT_PLACES]; -.string bot_placenames[MAX_BOT_PLACES]; -entity bot_getplace(entity this, string placename) -{ - entity e; - if(substring(placename, 0, 1) == "@") - { - int i, p; - placename = substring(placename, 1, -1); - string s, s2; - for(i = 0; i < this.bot_places_count; ++i) - if(this.(bot_placenames[i]) == placename) - return this.(bot_places[i]); - // now: i == this.bot_places_count - s = s2 = cvar_string(placename); - p = strstrofs(s2, " ", 0); - if(p >= 0) - { - s = substring(s2, 0, p); - //print("places: ", placename, " -> ", cvar_string(placename), "\n"); - cvar_set(placename, strcat(substring(s2, p+1, -1), " ", s)); - //print("places: ", placename, " := ", cvar_string(placename), "\n"); - } - e = find(NULL, targetname, s); - if(!e) - LOG_INFO("invalid place ", s, "\n"); - if(i < MAX_BOT_PLACES) - { - this.(bot_placenames[i]) = strzone(placename); - this.(bot_places[i]) = e; - this.bot_places_count += 1; - } - return e; - } - else - { - e = find(NULL, targetname, placename); - if(!e) - LOG_INFO("invalid place ", placename, "\n"); - return e; - } -} - - -// Initialize global commands list -// NOTE: New commands should be initialized here -void bot_commands_init() -{ - bot_cmd_string[BOT_CMD_NULL] = ""; - bot_cmd_parm_type[BOT_CMD_NULL] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_PAUSE] = "pause"; - bot_cmd_parm_type[BOT_CMD_PAUSE] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_CONTINUE] = "continue"; - bot_cmd_parm_type[BOT_CMD_CONTINUE] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_WAIT] = "wait"; - bot_cmd_parm_type[BOT_CMD_WAIT] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmd_string[BOT_CMD_TURN] = "turn"; - bot_cmd_parm_type[BOT_CMD_TURN] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmd_string[BOT_CMD_MOVETO] = "moveto"; - bot_cmd_parm_type[BOT_CMD_MOVETO] = BOT_CMD_PARAMETER_VECTOR; - - bot_cmd_string[BOT_CMD_MOVETOTARGET] = "movetotarget"; - bot_cmd_parm_type[BOT_CMD_MOVETOTARGET] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_RESETGOAL] = "resetgoal"; - bot_cmd_parm_type[BOT_CMD_RESETGOAL] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_CC] = "cc"; - bot_cmd_parm_type[BOT_CMD_CC] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_IF] = "if"; - bot_cmd_parm_type[BOT_CMD_IF] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_ELSE] = "else"; - bot_cmd_parm_type[BOT_CMD_ELSE] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_FI] = "fi"; - bot_cmd_parm_type[BOT_CMD_FI] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_RESETAIM] = "resetaim"; - bot_cmd_parm_type[BOT_CMD_RESETAIM] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_AIM] = "aim"; - bot_cmd_parm_type[BOT_CMD_AIM] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_AIMTARGET] = "aimtarget"; - bot_cmd_parm_type[BOT_CMD_AIMTARGET] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_PRESSKEY] = "presskey"; - bot_cmd_parm_type[BOT_CMD_PRESSKEY] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_RELEASEKEY] = "releasekey"; - bot_cmd_parm_type[BOT_CMD_RELEASEKEY] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_SELECTWEAPON] = "selectweapon"; - bot_cmd_parm_type[BOT_CMD_SELECTWEAPON] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmd_string[BOT_CMD_IMPULSE] = "impulse"; - bot_cmd_parm_type[BOT_CMD_IMPULSE] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmd_string[BOT_CMD_WAIT_UNTIL] = "wait_until"; - bot_cmd_parm_type[BOT_CMD_WAIT_UNTIL] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmd_string[BOT_CMD_BARRIER] = "barrier"; - bot_cmd_parm_type[BOT_CMD_BARRIER] = BOT_CMD_PARAMETER_NONE; - - bot_cmd_string[BOT_CMD_CONSOLE] = "console"; - bot_cmd_parm_type[BOT_CMD_CONSOLE] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_SOUND] = "sound"; - bot_cmd_parm_type[BOT_CMD_SOUND] = BOT_CMD_PARAMETER_STRING; - - bot_cmd_string[BOT_CMD_DEBUG_ASSERT_CANFIRE] = "debug_assert_canfire"; - bot_cmd_parm_type[BOT_CMD_DEBUG_ASSERT_CANFIRE] = BOT_CMD_PARAMETER_FLOAT; - - bot_cmds_initialized = true; -} - -// Returns first bot with matching name -entity find_bot_by_name(string name) -{ - entity bot; - - bot = findchainflags(flags, FL_CLIENT); - while (bot) - { - if(IS_BOT_CLIENT(bot)) - if(bot.netname==name) - return bot; - - bot = bot.chain; - } - - return NULL; -} - -// Returns a bot by number on list -entity find_bot_by_number(float number) -{ - entity bot; - float c = 0; - - if(!number) - return NULL; - - bot = findchainflags(flags, FL_CLIENT); - while (bot) - { - if(IS_BOT_CLIENT(bot)) - { - if(++c==number) - return bot; - } - bot = bot.chain; - } - - return NULL; -} - -float bot_decodecommand(string cmdstring) -{ - float cmd_parm_type; - float sp; - string parm; - - sp = strstrofs(cmdstring, " ", 0); - if(sp < 0) - { - parm = ""; - } - else - { - parm = substring(cmdstring, sp + 1, -1); - cmdstring = substring(cmdstring, 0, sp); - } - - if(!bot_cmds_initialized) - bot_commands_init(); - - int i; - for(i=1;i \n")); - - LOG_INFO("Description: "); - switch(i) - { - case BOT_CMD_PAUSE: - LOG_INFO("Stops the bot completely. Any command other than 'continue' will be ignored."); - break; - case BOT_CMD_CONTINUE: - LOG_INFO("Disable paused status"); - break; - case BOT_CMD_WAIT: - LOG_INFO("Pause command parsing and bot ai for N seconds. Pressed key will remain pressed"); - break; - case BOT_CMD_WAIT_UNTIL: - LOG_INFO("Pause command parsing and bot ai until time is N from the last barrier. Pressed key will remain pressed"); - break; - case BOT_CMD_BARRIER: - LOG_INFO("Waits till all bots that have a command queue reach this command. Pressed key will remain pressed"); - break; - case BOT_CMD_TURN: - LOG_INFO("Look to the right or left N degrees. For turning to the left use positive numbers."); - break; - case BOT_CMD_MOVETO: - LOG_INFO("Walk to an specific coordinate on the map. Usage: moveto \"x y z\""); - break; - case BOT_CMD_MOVETOTARGET: - LOG_INFO("Walk to the specific target on the map"); - break; - case BOT_CMD_RESETGOAL: - LOG_INFO("Resets the goal stack"); - break; - case BOT_CMD_CC: - LOG_INFO("Execute client command. Examples: cc \"say something\"; cc god; cc \"name newnickname\"; cc kill;"); - break; - case BOT_CMD_IF: - LOG_INFO("Perform simple conditional execution.\n"); - LOG_INFO("Syntax: \n"); - LOG_INFO(" sv_cmd .. if \"condition\"\n"); - LOG_INFO(" sv_cmd .. \n"); - LOG_INFO(" sv_cmd .. \n"); - LOG_INFO(" sv_cmd .. else\n"); - LOG_INFO(" sv_cmd .. \n"); - LOG_INFO(" sv_cmd .. \n"); - LOG_INFO(" sv_cmd .. fi\n"); - LOG_INFO("Conditions: a=b, a>b, a50; if health>cvar.g_balance_laser_primary_damage; if flagcarrier;"); - break; - case BOT_CMD_RESETAIM: - LOG_INFO("Points the aim to the coordinates x,y 0,0"); - break; - case BOT_CMD_AIM: - LOG_INFO("Move the aim x/y (horizontal/vertical) degrees relatives to the bot\n"); - LOG_INFO("There is a 3rd optional parameter telling in how many seconds the aim has to reach the new position\n"); - LOG_INFO("Examples: aim \"90 0\" // Turn 90 degrees inmediately (positive numbers move to the left/up)\n"); - LOG_INFO(" aim \"0 90 2\" // Will gradually look to the sky in the next two seconds"); - break; - case BOT_CMD_AIMTARGET: - LOG_INFO("Points the aim to given target"); - break; - case BOT_CMD_PRESSKEY: - LOG_INFO("Press one of the following keys: forward, backward, left, right, jump, crouch, attack1, attack2, use\n"); - LOG_INFO("Multiple keys can be pressed at time (with many presskey calls) and it will remain pressed until the command \"releasekey\" is called"); - LOG_INFO("Note: The script will not return the control to the bot ai until all keys are released"); - break; - case BOT_CMD_RELEASEKEY: - LOG_INFO("Release previoulsy used keys. Use the parameter \"all\" to release all keys"); - break; - case BOT_CMD_SOUND: - LOG_INFO("play sound file at bot location"); - break; - case BOT_CMD_DEBUG_ASSERT_CANFIRE: - LOG_INFO("verify the state of the weapon entity"); - break; - default: - LOG_INFO("This command has no description yet."); - break; - } - LOG_INFO("\n"); - } -} - -void bot_list_commands() -{ - int i; - string ptype; - - if(!bot_cmds_initialized) - bot_commands_init(); - - LOG_INFO("List of all available commands:\n"); - LOG_INFO(" Command - Parameter Type\n"); - - for(i=1;i \n")); - } -} - -// Commands code -.int bot_exec_status; - -void SV_ParseClientCommand(string s); -float bot_cmd_cc(entity this) -{ - WITHSELF(this, SV_ParseClientCommand(bot_cmd.bot_cmd_parm_string)); - return CMD_STATUS_FINISHED; -} - -float bot_cmd_impulse(entity this) -{ - this.impulse = bot_cmd.bot_cmd_parm_float; - return CMD_STATUS_FINISHED; -} - -float bot_cmd_continue(entity this) -{ - this.bot_exec_status &= ~BOT_EXEC_STATUS_PAUSED; - return CMD_STATUS_FINISHED; -} - -.float bot_cmd_wait_time; -float bot_cmd_wait(entity this) -{ - if(this.bot_exec_status & BOT_EXEC_STATUS_WAITING) - { - if(time>=this.bot_cmd_wait_time) - { - this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING; - return CMD_STATUS_FINISHED; - } - else - return CMD_STATUS_EXECUTING; - } - - this.bot_cmd_wait_time = time + bot_cmd.bot_cmd_parm_float; - this.bot_exec_status |= BOT_EXEC_STATUS_WAITING; - return CMD_STATUS_EXECUTING; -} - -float bot_cmd_wait_until(entity this) -{ - if(time < bot_cmd.bot_cmd_parm_float + bot_barriertime) - { - this.bot_exec_status |= BOT_EXEC_STATUS_WAITING; - return CMD_STATUS_EXECUTING; - } - this.bot_exec_status &= ~BOT_EXEC_STATUS_WAITING; - return CMD_STATUS_FINISHED; -} - -float bot_cmd_barrier(entity this) -{ - // 0 = no barrier, 1 = waiting, 2 = waiting finished - - if(this.bot_barrier == 0) // initialization - { - this.bot_barrier = 1; - - //this.colormod = '4 4 0'; - } - - if(this.bot_barrier == 1) // find other bots - { - FOREACH_CLIENT(it.isbot, LAMBDA( - if(it.bot_cmdqueuebuf_allocated) - if(it.bot_barrier != 1) - return CMD_STATUS_EXECUTING; // not all are at the barrier yet - )); - - // all bots hit the barrier! - - // acknowledge barrier - FOREACH_CLIENT(it.isbot, LAMBDA(it.bot_barrier = 2)); - - bot_barriertime = time; - } - - // if we get here, the barrier is finished - // so end it... - this.bot_barrier = 0; - //this.colormod = '0 0 0'; - - return CMD_STATUS_FINISHED; -} - -float bot_cmd_turn(entity this) -{ - this.v_angle_y = this.v_angle.y + bot_cmd.bot_cmd_parm_float; - this.v_angle_y = this.v_angle.y - floor(this.v_angle.y / 360) * 360; - return CMD_STATUS_FINISHED; -} - -float bot_cmd_select_weapon(entity this) -{ - float id = bot_cmd.bot_cmd_parm_float; - - if(id < WEP_FIRST || id > WEP_LAST) - return CMD_STATUS_ERROR; - - if(client_hasweapon(this, Weapons_from(id), true, false)) - PS(this).m_switchweapon = Weapons_from(id); - else - return CMD_STATUS_ERROR; - - return CMD_STATUS_FINISHED; -} - -.int bot_cmd_condition_status; - -const int CMD_CONDITION_NONE = 0; -const int CMD_CONDITION_true = 1; -const int CMD_CONDITION_false = 2; -const int CMD_CONDITION_true_BLOCK = 4; -const int CMD_CONDITION_false_BLOCK = 8; - -float bot_cmd_eval(entity this, string expr) -{ - // Search for numbers - if(strstrofs("0123456789", substring(expr, 0, 1), 0) >= 0) - { - return stof(expr); - } - - // Search for cvars - if(substring(expr, 0, 5)=="cvar.") - { - return cvar(substring(expr, 5, strlen(expr))); - } - - // Search for fields - switch(expr) - { - case "health": - return this.health; - case "speed": - return vlen(this.velocity); - case "flagcarrier": - return ((this.flagcarried!=NULL)); - } - - LOG_INFO(strcat("ERROR: Unable to convert the expression '",expr,"' into a numeric value\n")); - return 0; -} - -float bot_cmd_if(entity this) -{ - string expr, val_a, val_b; - float cmpofs; - - if(this.bot_cmd_condition_status != CMD_CONDITION_NONE) - { - // Only one "if" block is allowed at time - LOG_INFO("ERROR: Only one conditional block can be processed at time"); - bot_clearqueue(this); - return CMD_STATUS_ERROR; - } - - this.bot_cmd_condition_status |= CMD_CONDITION_true_BLOCK; - - // search for operators - expr = bot_cmd.bot_cmd_parm_string; - - cmpofs = strstrofs(expr,"=",0); - - if(cmpofs>0) - { - val_a = substring(expr,0,cmpofs); - val_b = substring(expr,cmpofs+1,strlen(expr)); - - if(bot_cmd_eval(this, val_a)==bot_cmd_eval(this, val_b)) - this.bot_cmd_condition_status |= CMD_CONDITION_true; - else - this.bot_cmd_condition_status |= CMD_CONDITION_false; - - return CMD_STATUS_FINISHED; - } - - cmpofs = strstrofs(expr,">",0); - - if(cmpofs>0) - { - val_a = substring(expr,0,cmpofs); - val_b = substring(expr,cmpofs+1,strlen(expr)); - - if(bot_cmd_eval(this, val_a)>bot_cmd_eval(this, val_b)) - this.bot_cmd_condition_status |= CMD_CONDITION_true; - else - this.bot_cmd_condition_status |= CMD_CONDITION_false; - - return CMD_STATUS_FINISHED; - } - - cmpofs = strstrofs(expr,"<",0); - - if(cmpofs>0) - { - val_a = substring(expr,0,cmpofs); - val_b = substring(expr,cmpofs+1,strlen(expr)); - - if(bot_cmd_eval(this, val_a)=this.bot_cmd_aim_endtime) - { - this.bot_cmd_aim_endtime = 0; - return CMD_STATUS_FINISHED; - } - else - return CMD_STATUS_EXECUTING; - } - - // New aiming direction - string parms; - float tokens, step; - - parms = bot_cmd.bot_cmd_parm_string; - - tokens = tokenizebyseparator(parms, " "); - - if(tokens<2||tokens>3) - return CMD_STATUS_ERROR; - - step = (tokens == 3) ? stof(argv(2)) : 0; - - if(step == 0) - { - this.v_angle_x -= stof(argv(1)); - this.v_angle_y += stof(argv(0)); - return CMD_STATUS_FINISHED; - } - - this.bot_cmd_aim_begin = this.v_angle; - - this.bot_cmd_aim_end_x = this.v_angle.x - stof(argv(1)); - this.bot_cmd_aim_end_y = this.v_angle.y + stof(argv(0)); - this.bot_cmd_aim_end_z = 0; - - this.bot_cmd_aim_begintime = time; - this.bot_cmd_aim_endtime = time + step; - - return CMD_STATUS_EXECUTING; -} - -float bot_cmd_aimtarget(entity this) -{ - if(this.bot_cmd_aim_endtime) - { - return bot_cmd_aim(this); - } - - entity e; - string parms; - vector v; - float tokens, step; - - parms = bot_cmd.bot_cmd_parm_string; - - tokens = tokenizebyseparator(parms, " "); - - e = bot_getplace(this, argv(0)); - if(!e) - return CMD_STATUS_ERROR; - - v = e.origin + (e.mins + e.maxs) * 0.5; - - if(tokens==1) - { - this.v_angle = vectoangles(v - (this.origin + this.view_ofs)); - this.v_angle_x = -this.v_angle.x; - return CMD_STATUS_FINISHED; - } - - if(tokens<1||tokens>2) - return CMD_STATUS_ERROR; - - step = stof(argv(1)); - - this.bot_cmd_aim_begin = this.v_angle; - this.bot_cmd_aim_end = vectoangles(v - (this.origin + this.view_ofs)); - this.bot_cmd_aim_end_x = -this.bot_cmd_aim_end.x; - - this.bot_cmd_aim_begintime = time; - this.bot_cmd_aim_endtime = time + step; - - return CMD_STATUS_EXECUTING; -} - -.int bot_cmd_keys; - -const int BOT_CMD_KEY_NONE = 0; -const int BOT_CMD_KEY_FORWARD = BIT(0); -const int BOT_CMD_KEY_BACKWARD = BIT(1); -const int BOT_CMD_KEY_RIGHT = BIT(2); -const int BOT_CMD_KEY_LEFT = BIT(3); -const int BOT_CMD_KEY_JUMP = BIT(4); -const int BOT_CMD_KEY_ATTACK1 = BIT(5); -const int BOT_CMD_KEY_ATTACK2 = BIT(6); -const int BOT_CMD_KEY_USE = BIT(7); -const int BOT_CMD_KEY_HOOK = BIT(8); -const int BOT_CMD_KEY_CROUCH = BIT(9); -const int BOT_CMD_KEY_CHAT = BIT(10); - -bool bot_presskeys(entity this) -{ - this.movement = '0 0 0'; - PHYS_INPUT_BUTTON_JUMP(this) = false; - PHYS_INPUT_BUTTON_CROUCH(this) = false; - PHYS_INPUT_BUTTON_ATCK(this) = false; - PHYS_INPUT_BUTTON_ATCK2(this) = false; - PHYS_INPUT_BUTTON_USE(this) = false; - PHYS_INPUT_BUTTON_HOOK(this) = false; - PHYS_INPUT_BUTTON_CHAT(this) = false; - - if(this.bot_cmd_keys == BOT_CMD_KEY_NONE) - return false; - - if(this.bot_cmd_keys & BOT_CMD_KEY_FORWARD) - this.movement_x = autocvar_sv_maxspeed; - else if(this.bot_cmd_keys & BOT_CMD_KEY_BACKWARD) - this.movement_x = -autocvar_sv_maxspeed; - - if(this.bot_cmd_keys & BOT_CMD_KEY_RIGHT) - this.movement_y = autocvar_sv_maxspeed; - else if(this.bot_cmd_keys & BOT_CMD_KEY_LEFT) - this.movement_y = -autocvar_sv_maxspeed; - - if(this.bot_cmd_keys & BOT_CMD_KEY_JUMP) - PHYS_INPUT_BUTTON_JUMP(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_CROUCH) - PHYS_INPUT_BUTTON_CROUCH(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK1) - PHYS_INPUT_BUTTON_ATCK(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_ATTACK2) - PHYS_INPUT_BUTTON_ATCK2(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_USE) - PHYS_INPUT_BUTTON_USE(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_HOOK) - PHYS_INPUT_BUTTON_HOOK(this) = true; - - if(this.bot_cmd_keys & BOT_CMD_KEY_CHAT) - PHYS_INPUT_BUTTON_CHAT(this) = true; - - return true; -} - - -float bot_cmd_keypress_handler(entity this, string key, float enabled) -{ - switch(key) - { - case "all": - if(enabled) - this.bot_cmd_keys = power2of(20) - 1; // >:) - else - this.bot_cmd_keys = BOT_CMD_KEY_NONE; - case "forward": - if(enabled) - { - this.bot_cmd_keys |= BOT_CMD_KEY_FORWARD; - this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD; - } - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD; - break; - case "backward": - if(enabled) - { - this.bot_cmd_keys |= BOT_CMD_KEY_BACKWARD; - this.bot_cmd_keys &= ~BOT_CMD_KEY_FORWARD; - } - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_BACKWARD; - break; - case "left": - if(enabled) - { - this.bot_cmd_keys |= BOT_CMD_KEY_LEFT; - this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT; - } - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT; - break; - case "right": - if(enabled) - { - this.bot_cmd_keys |= BOT_CMD_KEY_RIGHT; - this.bot_cmd_keys &= ~BOT_CMD_KEY_LEFT; - } - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_RIGHT; - break; - case "jump": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_JUMP; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_JUMP; - break; - case "crouch": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_CROUCH; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_CROUCH; - break; - case "attack1": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK1; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK1; - break; - case "attack2": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_ATTACK2; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_ATTACK2; - break; - case "use": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_USE; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_USE; - break; - case "hook": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_HOOK; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_HOOK; - break; - case "chat": - if(enabled) - this.bot_cmd_keys |= BOT_CMD_KEY_CHAT; - else - this.bot_cmd_keys &= ~BOT_CMD_KEY_CHAT; - break; - default: - break; - } - - return CMD_STATUS_FINISHED; -} - -float bot_cmd_presskey(entity this) -{ - string key; - - key = bot_cmd.bot_cmd_parm_string; - - bot_cmd_keypress_handler(this, key,true); - - return CMD_STATUS_FINISHED; -} - -float bot_cmd_releasekey(entity this) -{ - string key; - - key = bot_cmd.bot_cmd_parm_string; - - return bot_cmd_keypress_handler(this, key,false); -} - -float bot_cmd_pause(entity this) -{ - PHYS_INPUT_BUTTON_DRAG(this) = false; - PHYS_INPUT_BUTTON_USE(this) = false; - PHYS_INPUT_BUTTON_ATCK(this) = false; - PHYS_INPUT_BUTTON_JUMP(this) = false; - PHYS_INPUT_BUTTON_HOOK(this) = false; - PHYS_INPUT_BUTTON_CHAT(this) = false; - PHYS_INPUT_BUTTON_ATCK2(this) = false; - PHYS_INPUT_BUTTON_CROUCH(this) = false; - - this.movement = '0 0 0'; - this.bot_cmd_keys = BOT_CMD_KEY_NONE; - - this.bot_exec_status |= BOT_EXEC_STATUS_PAUSED; - return CMD_STATUS_FINISHED; -} - -float bot_cmd_moveto(entity this) -{ - return this.cmd_moveto(this, bot_cmd.bot_cmd_parm_vector); -} - -float bot_cmd_movetotarget(entity this) -{ - entity e; - e = bot_getplace(this, bot_cmd.bot_cmd_parm_string); - if(!e) - return CMD_STATUS_ERROR; - return this.cmd_moveto(this, e.origin + (e.mins + e.maxs) * 0.5); -} - -float bot_cmd_resetgoal(entity this) -{ - return this.cmd_resetgoal(this); -} - - -float bot_cmd_sound(entity this) -{ - string f; - f = bot_cmd.bot_cmd_parm_string; - - float n = tokenizebyseparator(f, " "); - - string sample = f; - float chan = CH_WEAPON_B; - float vol = VOL_BASE; - float atten = ATTEN_MIN; - - if(n >= 1) - sample = argv(n - 1); - if(n >= 2) - chan = stof(argv(0)); - if(n >= 3) - vol = stof(argv(1)); - if(n >= 4) - atten = stof(argv(2)); - - precache_sound(f); - _sound(this, chan, sample, vol, atten); - - return CMD_STATUS_FINISHED; -} - -.entity tuba_note; -float bot_cmd_debug_assert_canfire(entity this) -{ - float f = bot_cmd.bot_cmd_parm_float; - - int slot = 0; - .entity weaponentity = weaponentities[slot]; - if(this.(weaponentity).state != WS_READY) - { - if(f) - { - this.colormod = '0 8 8'; - LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by weaponentity state\n"); - } - } - else if(ATTACK_FINISHED(this, slot) > time) - { - if(f) - { - this.colormod = '8 0 8'; - LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, inhibited by ATTACK_FINISHED (", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left)\n"); - } - } - else if(this.tuba_note) - { - if(f) - { - this.colormod = '8 0 0'; - LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " wants to fire, bot still has an active tuba note\n"); - } - } - else - { - if(!f) - { - this.colormod = '8 8 0'; - LOG_INFO("Bot ", this.netname, " using ", this.weaponname, " thinks it has fired, but apparently did not; ATTACK_FINISHED says ", ftos(ATTACK_FINISHED(this, slot) - time), " seconds left\n"); - } - } - - return CMD_STATUS_FINISHED; -} - -// - -void bot_command_executed(entity this, bool rm) -{ - entity cmd; - - cmd = bot_cmd; - - if(rm) - bot_dequeuecommand(this, this.bot_cmd_execution_index); - - this.bot_cmd_execution_index++; -} - -void bot_setcurrentcommand(entity this) -{ - bot_cmd = NULL; - - if(!this.bot_cmd_current) - { - this.bot_cmd_current = new_pure(bot_cmd); - this.bot_cmd_current.is_bot_cmd = true; - } - - bot_cmd = this.bot_cmd_current; - if(bot_cmd.bot_cmd_index != this.bot_cmd_execution_index || this.bot_cmd_execution_index == 0) - { - if(bot_havecommand(this, this.bot_cmd_execution_index)) - { - string cmdstring; - cmdstring = bot_readcommand(this, this.bot_cmd_execution_index); - if(bot_decodecommand(cmdstring)) - { - bot_cmd.owner = this; - bot_cmd.bot_cmd_index = this.bot_cmd_execution_index; - } - else - { - // Invalid command, remove from queue - bot_cmd = NULL; - bot_dequeuecommand(this, this.bot_cmd_execution_index); - this.bot_cmd_execution_index++; - } - } - else - bot_cmd = NULL; - } -} - -void bot_resetqueues() -{ - FOREACH_CLIENT(it.isbot, LAMBDA( - it.bot_cmd_execution_index = 0; - bot_clearqueue(it); - // also, cancel all barriers - 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; - } - it.bot_places_count = 0; - )); - - bot_barriertime = time; -} - -// Here we map commands to functions and deal with complex interactions between commands and execution states -// NOTE: Of course you need to include your commands here too :) -float bot_execute_commands_once(entity this) -{ - float status, ispressingkey; - - // Find command - bot_setcurrentcommand(this); - - // if we have no bot command, better return - // old logic kept pressing previously pressed keys, but that has problems - // (namely, it means you cannot make a bot "normal" ever again) - // to keep a bot walking for a while, use the "wait" bot command - if(bot_cmd == NULL) - return false; - - // Ignore all commands except continue when the bot is paused - if(this.bot_exec_status & BOT_EXEC_STATUS_PAUSED) - if(bot_cmd.bot_cmd_type!=BOT_CMD_CONTINUE) - { - if(bot_cmd.bot_cmd_type!=BOT_CMD_NULL) - { - bot_command_executed(this, true); - LOG_INFO( "WARNING: Commands are ignored while the bot is paused. Use the command 'continue' instead.\n"); - } - return 1; - } - - // Keep pressing keys raised by the "presskey" command - ispressingkey = boolean(bot_presskeys(this)); - - // Handle conditions - if (!(bot_cmd.bot_cmd_type==BOT_CMD_FI||bot_cmd.bot_cmd_type==BOT_CMD_ELSE)) - if(this.bot_cmd_condition_status & CMD_CONDITION_true && this.bot_cmd_condition_status & CMD_CONDITION_false_BLOCK) - { - bot_command_executed(this, true); - return -1; - } - else if(this.bot_cmd_condition_status & CMD_CONDITION_false && this.bot_cmd_condition_status & CMD_CONDITION_true_BLOCK) - { - bot_command_executed(this, true); - return -1; - } - - // Map commands to functions - switch(bot_cmd.bot_cmd_type) - { - case BOT_CMD_NULL: - return ispressingkey; - //break; - case BOT_CMD_PAUSE: - status = bot_cmd_pause(this); - break; - case BOT_CMD_CONTINUE: - status = bot_cmd_continue(this); - break; - case BOT_CMD_WAIT: - status = bot_cmd_wait(this); - break; - case BOT_CMD_WAIT_UNTIL: - status = bot_cmd_wait_until(this); - break; - case BOT_CMD_TURN: - status = bot_cmd_turn(this); - break; - case BOT_CMD_MOVETO: - status = bot_cmd_moveto(this); - break; - case BOT_CMD_MOVETOTARGET: - status = bot_cmd_movetotarget(this); - break; - case BOT_CMD_RESETGOAL: - status = bot_cmd_resetgoal(this); - break; - case BOT_CMD_CC: - status = bot_cmd_cc(this); - break; - case BOT_CMD_IF: - status = bot_cmd_if(this); - break; - case BOT_CMD_ELSE: - status = bot_cmd_else(this); - break; - case BOT_CMD_FI: - status = bot_cmd_fi(this); - break; - case BOT_CMD_RESETAIM: - status = bot_cmd_resetaim(this); - break; - case BOT_CMD_AIM: - status = bot_cmd_aim(this); - break; - case BOT_CMD_AIMTARGET: - status = bot_cmd_aimtarget(this); - break; - case BOT_CMD_PRESSKEY: - status = bot_cmd_presskey(this); - break; - case BOT_CMD_RELEASEKEY: - status = bot_cmd_releasekey(this); - break; - case BOT_CMD_SELECTWEAPON: - status = bot_cmd_select_weapon(this); - break; - case BOT_CMD_IMPULSE: - status = bot_cmd_impulse(this); - break; - case BOT_CMD_BARRIER: - status = bot_cmd_barrier(this); - break; - case BOT_CMD_CONSOLE: - localcmd(strcat(bot_cmd.bot_cmd_parm_string, "\n")); - status = CMD_STATUS_FINISHED; - break; - case BOT_CMD_SOUND: - status = bot_cmd_sound(this); - break; - case BOT_CMD_DEBUG_ASSERT_CANFIRE: - status = bot_cmd_debug_assert_canfire(this); - break; - default: - LOG_INFO(strcat("ERROR: Invalid command on queue with id '",ftos(bot_cmd.bot_cmd_type),"'\n")); - return 0; - } - - if (status==CMD_STATUS_ERROR) - LOG_INFO(strcat("ERROR: The command '",bot_cmd_string[bot_cmd.bot_cmd_type],"' returned an error status\n")); - - // Move execution pointer - if(status==CMD_STATUS_EXECUTING) - { - return 1; - } - else - { - if(autocvar_g_debug_bot_commands) - { - string parms; - - switch(bot_cmd_parm_type[bot_cmd.bot_cmd_type]) - { - case BOT_CMD_PARAMETER_FLOAT: - parms = ftos(bot_cmd.bot_cmd_parm_float); - break; - case BOT_CMD_PARAMETER_STRING: - parms = bot_cmd.bot_cmd_parm_string; - break; - case BOT_CMD_PARAMETER_VECTOR: - parms = vtos(bot_cmd.bot_cmd_parm_vector); - break; - default: - parms = ""; - break; - } - clientcommand(this,strcat("say ^7", bot_cmd_string[bot_cmd.bot_cmd_type]," ",parms,"\n")); - } - - bot_command_executed(this, true); - } - - if(status == CMD_STATUS_FINISHED) - return -1; - - return CMD_STATUS_ERROR; -} - -// This function should be (the only) called directly from the bot ai loop -int bot_execute_commands(entity this) -{ - int f; - do - { - f = bot_execute_commands_once(this); - } - while(f < 0); - return f; -} diff --git a/qcsrc/server/bot/scripting.qh b/qcsrc/server/bot/scripting.qh deleted file mode 100644 index cb6cd8731d..0000000000 --- a/qcsrc/server/bot/scripting.qh +++ /dev/null @@ -1,81 +0,0 @@ -#pragma once - -#define BOT_EXEC_STATUS_IDLE 0 -#define BOT_EXEC_STATUS_PAUSED 1 -#define BOT_EXEC_STATUS_WAITING 2 - -#define CMD_STATUS_EXECUTING 0 -#define CMD_STATUS_FINISHED 1 -#define CMD_STATUS_ERROR 2 - - -// NOTE: New commands should be added here. Do not forget to update BOT_CMD_COUNTER -const int BOT_CMD_NULL = 0; -const int BOT_CMD_PAUSE = 1; -const int BOT_CMD_CONTINUE = 2; -const int BOT_CMD_WAIT = 3; -const int BOT_CMD_TURN = 4; -const int BOT_CMD_MOVETO = 5; -const int BOT_CMD_RESETGOAL = 6; // Not implemented yet -const int BOT_CMD_CC = 7; -const int BOT_CMD_IF = 8; -const int BOT_CMD_ELSE = 9; -const int BOT_CMD_FI = 10; -const int BOT_CMD_RESETAIM = 11; -const int BOT_CMD_AIM = 12; -const int BOT_CMD_PRESSKEY = 13; -const int BOT_CMD_RELEASEKEY = 14; -const int BOT_CMD_SELECTWEAPON = 15; -const int BOT_CMD_IMPULSE = 16; -const int BOT_CMD_WAIT_UNTIL = 17; -const int BOT_CMD_MOVETOTARGET = 18; -const int BOT_CMD_AIMTARGET = 19; -const int BOT_CMD_BARRIER = 20; -const int BOT_CMD_CONSOLE = 21; -const int BOT_CMD_SOUND = 22; -const int BOT_CMD_DEBUG_ASSERT_CANFIRE = 23; -const int BOT_CMD_WHILE = 24; // TODO: Not implemented yet -const int BOT_CMD_WEND = 25; // TODO: Not implemented yet -const int BOT_CMD_CHASE = 26; // TODO: Not implemented yet - -const int BOT_CMD_COUNTER = 24; // Update this value if you add/remove a command - -// NOTE: Following commands should be implemented on the bot ai -// If a new command should be handled by the target ai(s) please declare it here -.float(entity, vector) cmd_moveto; -.float(entity) cmd_resetgoal; - -// -const int BOT_CMD_PARAMETER_NONE = 0; -const int BOT_CMD_PARAMETER_FLOAT = 1; -const int BOT_CMD_PARAMETER_STRING = 2; -const int BOT_CMD_PARAMETER_VECTOR = 3; - -float bot_cmds_initialized; -int bot_cmd_parm_type[BOT_CMD_COUNTER]; -string bot_cmd_string[BOT_CMD_COUNTER]; - -// Bots command queue -entity bot_cmd; // global current command -.entity bot_cmd_current; // current command of this bot - -.float is_bot_cmd; // Tells if the entity is a bot command -.float bot_cmd_index; // Position of the command in the queue -.int bot_cmd_type; // If of command (see the BOT_CMD_* defines) -.float bot_cmd_parm_float; // Field to store a float parameter -.string bot_cmd_parm_string; // Field to store a string parameter -.vector bot_cmd_parm_vector; // Field to store a vector parameter - -float bot_barriertime; -.float bot_barrier; - -.float bot_cmd_execution_index; // Position in the queue of the command to be executed - - -void bot_resetqueues(); -void bot_queuecommand(entity bot, string cmdstring); -void bot_cmdhelp(string scmd); -void bot_list_commands(); -float bot_execute_commands(entity this); -entity find_bot_by_name(string name); -entity find_bot_by_number(float number); diff --git a/qcsrc/server/bot/waypoints.qc b/qcsrc/server/bot/waypoints.qc deleted file mode 100644 index e8322c2e0c..0000000000 --- a/qcsrc/server/bot/waypoints.qc +++ /dev/null @@ -1,1146 +0,0 @@ -#include "waypoints.qh" - -#include "bot.qh" -#include "navigation.qh" - -#include - -#include "../antilag.qh" - -#include - -#include -#include - -// create a new spawnfunc_waypoint and automatically link it to other waypoints, and link -// them back to it as well -// (suitable for spawnfunc_waypoint editor) -entity waypoint_spawn(vector m1, vector m2, float f) -{ - entity w; - w = find(NULL, classname, "waypoint"); - - if (!(f & WAYPOINTFLAG_PERSONAL)) - while (w) - { - // if a matching spawnfunc_waypoint already exists, don't add a duplicate - if (boxesoverlap(m1, m2, w.absmin, w.absmax)) - return w; - w = find(w, classname, "waypoint"); - } - - w = new(waypoint); - w.dphitcontentsmask = DPCONTENTS_SOLID | DPCONTENTS_BODY | DPCONTENTS_PLAYERCLIP | DPCONTENTS_BOTCLIP; - w.wpflags = f; - w.solid = SOLID_TRIGGER; - setorigin(w, (m1 + m2) * 0.5); - setsize(w, m1 - w.origin, m2 - w.origin); - if (vlen(w.size) > 0) - w.wpisbox = true; - - if(!w.wpisbox) - { - setsize(w, STAT(PL_MIN, NULL) - '1 1 0', STAT(PL_MAX, NULL) + '1 1 0'); - if(!move_out_of_solid(w)) - { - if(!(f & WAYPOINTFLAG_GENERATED)) - { - LOG_TRACE("Killed a waypoint that was stuck in solid at ", vtos(w.origin), "\n"); - remove(w); - return NULL; - } - else - { - if(autocvar_developer) - { - LOG_INFO("A generated waypoint is stuck in solid at ", vtos(w.origin), "\n"); - backtrace("Waypoint stuck"); - } - } - } - setsize(w, '0 0 0', '0 0 0'); - } - - waypoint_clearlinks(w); - //waypoint_schedulerelink(w); - - if (autocvar_g_waypointeditor) - { - m1 = w.mins; - m2 = w.maxs; - setmodel(w, MDL_WAYPOINT); w.effects = EF_LOWPRECISION; - setsize(w, m1, m2); - if (w.wpflags & WAYPOINTFLAG_ITEM) - w.colormod = '1 0 0'; - else if (w.wpflags & WAYPOINTFLAG_GENERATED) - w.colormod = '1 1 0'; - else - w.colormod = '1 1 1'; - } - else - w.model = ""; - - return w; -} - -// add a new link to the spawnfunc_waypoint, replacing the furthest link it already has -void waypoint_addlink(entity from, entity to) -{ - float c; - - if (from == to) - return; - if (from.wpflags & WAYPOINTFLAG_NORELINK) - return; - - if (from.wp00 == to) return;if (from.wp01 == to) return;if (from.wp02 == to) return;if (from.wp03 == to) return; - if (from.wp04 == to) return;if (from.wp05 == to) return;if (from.wp06 == to) return;if (from.wp07 == to) return; - if (from.wp08 == to) return;if (from.wp09 == to) return;if (from.wp10 == to) return;if (from.wp11 == to) return; - if (from.wp12 == to) return;if (from.wp13 == to) return;if (from.wp14 == to) return;if (from.wp15 == to) return; - if (from.wp16 == to) return;if (from.wp17 == to) return;if (from.wp18 == to) return;if (from.wp19 == to) return; - if (from.wp20 == to) return;if (from.wp21 == to) return;if (from.wp22 == to) return;if (from.wp23 == to) return; - if (from.wp24 == to) return;if (from.wp25 == to) return;if (from.wp26 == to) return;if (from.wp27 == to) return; - if (from.wp28 == to) return;if (from.wp29 == to) return;if (from.wp30 == to) return;if (from.wp31 == to) return; - - if (to.wpisbox || from.wpisbox) - { - // if either is a box we have to find the nearest points on them to - // calculate the distance properly - vector v1, v2, m1, m2; - v1 = from.origin; - m1 = to.absmin; - m2 = to.absmax; - v1_x = bound(m1_x, v1_x, m2_x); - v1_y = bound(m1_y, v1_y, m2_y); - v1_z = bound(m1_z, v1_z, m2_z); - v2 = to.origin; - m1 = from.absmin; - m2 = from.absmax; - v2_x = bound(m1_x, v2_x, m2_x); - v2_y = bound(m1_y, v2_y, m2_y); - v2_z = bound(m1_z, v2_z, m2_z); - v2 = to.origin; - c = vlen(v2 - v1); - } - else - c = vlen(to.origin - from.origin); - - if (from.wp31mincost < c) return; - if (from.wp30mincost < c) {from.wp31 = to;from.wp31mincost = c;return;} from.wp31 = from.wp30;from.wp31mincost = from.wp30mincost; - if (from.wp29mincost < c) {from.wp30 = to;from.wp30mincost = c;return;} from.wp30 = from.wp29;from.wp30mincost = from.wp29mincost; - if (from.wp28mincost < c) {from.wp29 = to;from.wp29mincost = c;return;} from.wp29 = from.wp28;from.wp29mincost = from.wp28mincost; - if (from.wp27mincost < c) {from.wp28 = to;from.wp28mincost = c;return;} from.wp28 = from.wp27;from.wp28mincost = from.wp27mincost; - if (from.wp26mincost < c) {from.wp27 = to;from.wp27mincost = c;return;} from.wp27 = from.wp26;from.wp27mincost = from.wp26mincost; - if (from.wp25mincost < c) {from.wp26 = to;from.wp26mincost = c;return;} from.wp26 = from.wp25;from.wp26mincost = from.wp25mincost; - if (from.wp24mincost < c) {from.wp25 = to;from.wp25mincost = c;return;} from.wp25 = from.wp24;from.wp25mincost = from.wp24mincost; - if (from.wp23mincost < c) {from.wp24 = to;from.wp24mincost = c;return;} from.wp24 = from.wp23;from.wp24mincost = from.wp23mincost; - if (from.wp22mincost < c) {from.wp23 = to;from.wp23mincost = c;return;} from.wp23 = from.wp22;from.wp23mincost = from.wp22mincost; - if (from.wp21mincost < c) {from.wp22 = to;from.wp22mincost = c;return;} from.wp22 = from.wp21;from.wp22mincost = from.wp21mincost; - if (from.wp20mincost < c) {from.wp21 = to;from.wp21mincost = c;return;} from.wp21 = from.wp20;from.wp21mincost = from.wp20mincost; - if (from.wp19mincost < c) {from.wp20 = to;from.wp20mincost = c;return;} from.wp20 = from.wp19;from.wp20mincost = from.wp19mincost; - if (from.wp18mincost < c) {from.wp19 = to;from.wp19mincost = c;return;} from.wp19 = from.wp18;from.wp19mincost = from.wp18mincost; - if (from.wp17mincost < c) {from.wp18 = to;from.wp18mincost = c;return;} from.wp18 = from.wp17;from.wp18mincost = from.wp17mincost; - if (from.wp16mincost < c) {from.wp17 = to;from.wp17mincost = c;return;} from.wp17 = from.wp16;from.wp17mincost = from.wp16mincost; - if (from.wp15mincost < c) {from.wp16 = to;from.wp16mincost = c;return;} from.wp16 = from.wp15;from.wp16mincost = from.wp15mincost; - if (from.wp14mincost < c) {from.wp15 = to;from.wp15mincost = c;return;} from.wp15 = from.wp14;from.wp15mincost = from.wp14mincost; - if (from.wp13mincost < c) {from.wp14 = to;from.wp14mincost = c;return;} from.wp14 = from.wp13;from.wp14mincost = from.wp13mincost; - if (from.wp12mincost < c) {from.wp13 = to;from.wp13mincost = c;return;} from.wp13 = from.wp12;from.wp13mincost = from.wp12mincost; - if (from.wp11mincost < c) {from.wp12 = to;from.wp12mincost = c;return;} from.wp12 = from.wp11;from.wp12mincost = from.wp11mincost; - if (from.wp10mincost < c) {from.wp11 = to;from.wp11mincost = c;return;} from.wp11 = from.wp10;from.wp11mincost = from.wp10mincost; - if (from.wp09mincost < c) {from.wp10 = to;from.wp10mincost = c;return;} from.wp10 = from.wp09;from.wp10mincost = from.wp09mincost; - if (from.wp08mincost < c) {from.wp09 = to;from.wp09mincost = c;return;} from.wp09 = from.wp08;from.wp09mincost = from.wp08mincost; - if (from.wp07mincost < c) {from.wp08 = to;from.wp08mincost = c;return;} from.wp08 = from.wp07;from.wp08mincost = from.wp07mincost; - if (from.wp06mincost < c) {from.wp07 = to;from.wp07mincost = c;return;} from.wp07 = from.wp06;from.wp07mincost = from.wp06mincost; - if (from.wp05mincost < c) {from.wp06 = to;from.wp06mincost = c;return;} from.wp06 = from.wp05;from.wp06mincost = from.wp05mincost; - if (from.wp04mincost < c) {from.wp05 = to;from.wp05mincost = c;return;} from.wp05 = from.wp04;from.wp05mincost = from.wp04mincost; - if (from.wp03mincost < c) {from.wp04 = to;from.wp04mincost = c;return;} from.wp04 = from.wp03;from.wp04mincost = from.wp03mincost; - if (from.wp02mincost < c) {from.wp03 = to;from.wp03mincost = c;return;} from.wp03 = from.wp02;from.wp03mincost = from.wp02mincost; - if (from.wp01mincost < c) {from.wp02 = to;from.wp02mincost = c;return;} from.wp02 = from.wp01;from.wp02mincost = from.wp01mincost; - if (from.wp00mincost < c) {from.wp01 = to;from.wp01mincost = c;return;} from.wp01 = from.wp00;from.wp01mincost = from.wp00mincost; - from.wp00 = to;from.wp00mincost = c;return; -} - -// relink this spawnfunc_waypoint -// (precompile a list of all reachable waypoints from this spawnfunc_waypoint) -// (SLOW!) -void waypoint_think(entity this) -{ - entity e; - vector sv, sm1, sm2, ev, em1, em2, dv; - - bot_calculate_stepheightvec(); - - bot_navigation_movemode = ((autocvar_bot_navigation_ignoreplayers) ? MOVE_NOMONSTERS : MOVE_NORMAL); - - //dprint("waypoint_think wpisbox = ", ftos(this.wpisbox), "\n"); - sm1 = this.origin + this.mins; - sm2 = this.origin + this.maxs; - for(e = NULL; (e = find(e, classname, "waypoint")); ) - { - if (boxesoverlap(this.absmin, this.absmax, e.absmin, e.absmax)) - { - waypoint_addlink(this, e); - waypoint_addlink(e, this); - } - else - { - ++relink_total; - if(!checkpvs(this.origin, e)) - { - ++relink_pvsculled; - continue; - } - sv = e.origin; - sv.x = bound(sm1_x, sv.x, sm2_x); - sv.y = bound(sm1_y, sv.y, sm2_y); - sv.z = bound(sm1_z, sv.z, sm2_z); - ev = this.origin; - em1 = e.origin + e.mins; - em2 = e.origin + e.maxs; - ev.x = bound(em1_x, ev.x, em2_x); - ev.y = bound(em1_y, ev.y, em2_y); - ev.z = bound(em1_z, ev.z, em2_z); - dv = ev - sv; - dv.z = 0; - if(vdist(dv, >=, 1050)) // max search distance in XY - { - ++relink_lengthculled; - continue; - } - navigation_testtracewalk = 0; - if (!this.wpisbox) - { - tracebox(sv - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, false, this); - if (!trace_startsolid) - { - //dprint("sv deviation", vtos(trace_endpos - sv), "\n"); - sv = trace_endpos + '0 0 1'; - } - } - if (!e.wpisbox) - { - tracebox(ev - STAT(PL_MIN, NULL).z * '0 0 1', STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, false, e); - if (!trace_startsolid) - { - //dprint("ev deviation", vtos(trace_endpos - ev), "\n"); - ev = trace_endpos + '0 0 1'; - } - } - //traceline(this.origin, e.origin, false, NULL); - //if (trace_fraction == 1) - if (!this.wpisbox && tracewalk(this, sv, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), ev, MOVE_NOMONSTERS)) - waypoint_addlink(this, e); - else - relink_walkculled += 0.5; - if (!e.wpisbox && tracewalk(e, ev, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), sv, MOVE_NOMONSTERS)) - waypoint_addlink(e, this); - else - relink_walkculled += 0.5; - } - } - navigation_testtracewalk = 0; - this.wplinked = true; -} - -void waypoint_clearlinks(entity wp) -{ - // clear links to other waypoints - float f; - f = 10000000; - wp.wp00 = wp.wp01 = wp.wp02 = wp.wp03 = wp.wp04 = wp.wp05 = wp.wp06 = wp.wp07 = NULL; - wp.wp08 = wp.wp09 = wp.wp10 = wp.wp11 = wp.wp12 = wp.wp13 = wp.wp14 = wp.wp15 = NULL; - wp.wp16 = wp.wp17 = wp.wp18 = wp.wp19 = wp.wp20 = wp.wp21 = wp.wp22 = wp.wp23 = NULL; - wp.wp24 = wp.wp25 = wp.wp26 = wp.wp27 = wp.wp28 = wp.wp29 = wp.wp30 = wp.wp31 = NULL; - - wp.wp00mincost = wp.wp01mincost = wp.wp02mincost = wp.wp03mincost = wp.wp04mincost = wp.wp05mincost = wp.wp06mincost = wp.wp07mincost = f; - wp.wp08mincost = wp.wp09mincost = wp.wp10mincost = wp.wp11mincost = wp.wp12mincost = wp.wp13mincost = wp.wp14mincost = wp.wp15mincost = f; - wp.wp16mincost = wp.wp17mincost = wp.wp18mincost = wp.wp19mincost = wp.wp20mincost = wp.wp21mincost = wp.wp22mincost = wp.wp23mincost = f; - wp.wp24mincost = wp.wp25mincost = wp.wp26mincost = wp.wp27mincost = wp.wp28mincost = wp.wp29mincost = wp.wp30mincost = wp.wp31mincost = f; - - wp.wplinked = false; -} - -// tell a spawnfunc_waypoint to relink -void waypoint_schedulerelink(entity wp) -{ - if (wp == NULL) - return; - // TODO: add some sort of visible box in edit mode for box waypoints - if (autocvar_g_waypointeditor) - { - vector m1, m2; - m1 = wp.mins; - m2 = wp.maxs; - setmodel(wp, MDL_WAYPOINT); wp.effects = EF_LOWPRECISION; - setsize(wp, m1, m2); - if (wp.wpflags & WAYPOINTFLAG_ITEM) - wp.colormod = '1 0 0'; - else if (wp.wpflags & WAYPOINTFLAG_GENERATED) - wp.colormod = '1 1 0'; - else - wp.colormod = '1 1 1'; - } - else - wp.model = ""; - wp.wpisbox = vlen(wp.size) > 0; - wp.enemy = NULL; - if (!(wp.wpflags & WAYPOINTFLAG_PERSONAL)) - wp.owner = NULL; - if (!(wp.wpflags & WAYPOINTFLAG_NORELINK)) - waypoint_clearlinks(wp); - // schedule an actual relink on next frame - setthink(wp, waypoint_think); - wp.nextthink = time; - wp.effects = EF_LOWPRECISION; -} - -// spawnfunc_waypoint map entity -spawnfunc(waypoint) -{ - setorigin(this, this.origin); - // schedule a relink after other waypoints have had a chance to spawn - waypoint_clearlinks(this); - //waypoint_schedulerelink(this); -} - -// remove a spawnfunc_waypoint, and schedule all neighbors to relink -void waypoint_remove(entity e) -{ - // tell all linked waypoints that they need to relink - waypoint_schedulerelink(e.wp00); - waypoint_schedulerelink(e.wp01); - waypoint_schedulerelink(e.wp02); - waypoint_schedulerelink(e.wp03); - waypoint_schedulerelink(e.wp04); - waypoint_schedulerelink(e.wp05); - waypoint_schedulerelink(e.wp06); - waypoint_schedulerelink(e.wp07); - waypoint_schedulerelink(e.wp08); - waypoint_schedulerelink(e.wp09); - waypoint_schedulerelink(e.wp10); - waypoint_schedulerelink(e.wp11); - waypoint_schedulerelink(e.wp12); - waypoint_schedulerelink(e.wp13); - waypoint_schedulerelink(e.wp14); - waypoint_schedulerelink(e.wp15); - waypoint_schedulerelink(e.wp16); - waypoint_schedulerelink(e.wp17); - waypoint_schedulerelink(e.wp18); - waypoint_schedulerelink(e.wp19); - waypoint_schedulerelink(e.wp20); - waypoint_schedulerelink(e.wp21); - waypoint_schedulerelink(e.wp22); - waypoint_schedulerelink(e.wp23); - waypoint_schedulerelink(e.wp24); - waypoint_schedulerelink(e.wp25); - waypoint_schedulerelink(e.wp26); - waypoint_schedulerelink(e.wp27); - waypoint_schedulerelink(e.wp28); - waypoint_schedulerelink(e.wp29); - waypoint_schedulerelink(e.wp30); - waypoint_schedulerelink(e.wp31); - // and now remove the spawnfunc_waypoint - remove(e); -} - -// empties the map of waypoints -void waypoint_removeall() -{ - entity head, next; - head = findchain(classname, "waypoint"); - while (head) - { - next = head.chain; - remove(head); - head = next; - } -} - -// tell all waypoints to relink -// (is this useful at all?) -void waypoint_schedulerelinkall() -{ - entity head; - relink_total = relink_walkculled = relink_pvsculled = relink_lengthculled = 0; - head = findchain(classname, "waypoint"); - while (head) - { - waypoint_schedulerelink(head); - head = head.chain; - } -} - -// Load waypoint links from file -float waypoint_load_links() -{ - string filename, s; - float file, tokens, c = 0, found; - entity wp_from = NULL, wp_to; - vector wp_to_pos, wp_from_pos; - filename = strcat("maps/", mapname); - filename = strcat(filename, ".waypoints.cache"); - file = fopen(filename, FILE_READ); - - if (file < 0) - { - LOG_TRACE("waypoint links load from "); - LOG_TRACE(filename); - LOG_TRACE(" failed\n"); - return false; - } - - while ((s = fgets(file))) - { - tokens = tokenizebyseparator(s, "*"); - - if (tokens!=2) - { - // bad file format - fclose(file); - return false; - } - - wp_from_pos = stov(argv(0)); - wp_to_pos = stov(argv(1)); - - // Search "from" waypoint - if(!wp_from || wp_from.origin!=wp_from_pos) - { - wp_from = findradius(wp_from_pos, 1); - found = false; - while(wp_from) - { - if(vdist(wp_from.origin - wp_from_pos, <, 1)) - if(wp_from.classname == "waypoint") - { - found = true; - break; - } - wp_from = wp_from.chain; - } - - if(!found) - { - LOG_TRACE("waypoint_load_links: couldn't find 'from' waypoint at ", vtos(wp_from.origin),"\n"); - continue; - } - - } - - // Search "to" waypoint - wp_to = findradius(wp_to_pos, 1); - found = false; - while(wp_to) - { - if(vdist(wp_to.origin - wp_to_pos, <, 1)) - if(wp_to.classname == "waypoint") - { - found = true; - break; - } - wp_to = wp_to.chain; - } - - if(!found) - { - LOG_TRACE("waypoint_load_links: couldn't find 'to' waypoint at ", vtos(wp_to.origin),"\n"); - continue; - } - - ++c; - waypoint_addlink(wp_from, wp_to); - } - - fclose(file); - - LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.cache\n"); - - botframe_cachedwaypointlinks = true; - return true; -} - -void waypoint_load_links_hardwired() -{ - string filename, s; - float file, tokens, c = 0, found; - entity wp_from = NULL, wp_to; - vector wp_to_pos, wp_from_pos; - filename = strcat("maps/", mapname); - filename = strcat(filename, ".waypoints.hardwired"); - file = fopen(filename, FILE_READ); - - botframe_loadedforcedlinks = true; - - if (file < 0) - { - LOG_TRACE("waypoint links load from ", filename, " failed\n"); - return; - } - - while ((s = fgets(file))) - { - if(substring(s, 0, 2)=="//") - continue; - - if(substring(s, 0, 1)=="#") - continue; - - tokens = tokenizebyseparator(s, "*"); - - if (tokens!=2) - continue; - - wp_from_pos = stov(argv(0)); - wp_to_pos = stov(argv(1)); - - // Search "from" waypoint - if(!wp_from || wp_from.origin!=wp_from_pos) - { - wp_from = findradius(wp_from_pos, 5); - found = false; - while(wp_from) - { - if(vdist(wp_from.origin - wp_from_pos, <, 5)) - if(wp_from.classname == "waypoint") - { - found = true; - break; - } - wp_from = wp_from.chain; - } - - if(!found) - { - LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_from_pos), ". Path skipped\n")); - continue; - } - } - - // Search "to" waypoint - wp_to = findradius(wp_to_pos, 5); - found = false; - while(wp_to) - { - if(vdist(wp_to.origin - wp_to_pos, <, 5)) - if(wp_to.classname == "waypoint") - { - found = true; - break; - } - wp_to = wp_to.chain; - } - - if(!found) - { - LOG_INFO(strcat("NOTICE: Can not find waypoint at ", vtos(wp_to_pos), ". Path skipped\n")); - continue; - } - - ++c; - waypoint_addlink(wp_from, wp_to); - wp_from.wphardwired = true; - wp_to.wphardwired = true; - } - - fclose(file); - - LOG_TRACE("loaded ", ftos(c), " waypoint links from maps/", mapname, ".waypoints.hardwired\n"); -} - -entity waypoint_get_link(entity w, float i) -{ - switch(i) - { - case 0:return w.wp00; - case 1:return w.wp01; - case 2:return w.wp02; - case 3:return w.wp03; - case 4:return w.wp04; - case 5:return w.wp05; - case 6:return w.wp06; - case 7:return w.wp07; - case 8:return w.wp08; - case 9:return w.wp09; - case 10:return w.wp10; - case 11:return w.wp11; - case 12:return w.wp12; - case 13:return w.wp13; - case 14:return w.wp14; - case 15:return w.wp15; - case 16:return w.wp16; - case 17:return w.wp17; - case 18:return w.wp18; - case 19:return w.wp19; - case 20:return w.wp20; - case 21:return w.wp21; - case 22:return w.wp22; - case 23:return w.wp23; - case 24:return w.wp24; - case 25:return w.wp25; - case 26:return w.wp26; - case 27:return w.wp27; - case 28:return w.wp28; - case 29:return w.wp29; - case 30:return w.wp30; - case 31:return w.wp31; - default:return NULL; - } -} - -// Save all waypoint links to a file -void waypoint_save_links() -{ - string filename, s; - float file, c, i; - entity w, link; - filename = strcat("maps/", mapname); - filename = strcat(filename, ".waypoints.cache"); - file = fopen(filename, FILE_WRITE); - if (file < 0) - { - LOG_INFO("waypoint links save to "); - LOG_INFO(filename); - LOG_INFO(" failed\n"); - } - c = 0; - w = findchain(classname, "waypoint"); - while (w) - { - for(i=0;i<32;++i) - { - // :S - link = waypoint_get_link(w, i); - if(link==NULL) - continue; - - s = strcat(vtos(w.origin), "*", vtos(link.origin), "\n"); - fputs(file, s); - ++c; - } - w = w.chain; - } - fclose(file); - botframe_cachedwaypointlinks = true; - - LOG_INFO("saved "); - LOG_INFO(ftos(c)); - LOG_INFO(" waypoints links to maps/"); - LOG_INFO(mapname); - LOG_INFO(".waypoints.cache\n"); -} - -// save waypoints to gamedir/data/maps/mapname.waypoints -void waypoint_saveall() -{ - string filename, s; - float file, c; - entity w; - filename = strcat("maps/", mapname); - filename = strcat(filename, ".waypoints"); - file = fopen(filename, FILE_WRITE); - if (file >= 0) - { - c = 0; - w = findchain(classname, "waypoint"); - while (w) - { - if (!(w.wpflags & WAYPOINTFLAG_GENERATED)) - { - s = strcat(vtos(w.origin + w.mins), "\n"); - s = strcat(s, vtos(w.origin + w.maxs)); - s = strcat(s, "\n"); - s = strcat(s, ftos(w.wpflags)); - s = strcat(s, "\n"); - fputs(file, s); - c = c + 1; - } - w = w.chain; - } - fclose(file); - bprint("saved "); - bprint(ftos(c)); - bprint(" waypoints to maps/"); - bprint(mapname); - bprint(".waypoints\n"); - } - else - { - bprint("waypoint save to "); - bprint(filename); - bprint(" failed\n"); - } - waypoint_save_links(); - botframe_loadedforcedlinks = false; -} - -// load waypoints from file -float waypoint_loadall() -{ - string filename, s; - float file, cwp, cwb, fl; - vector m1, m2; - cwp = 0; - cwb = 0; - filename = strcat("maps/", mapname); - filename = strcat(filename, ".waypoints"); - file = fopen(filename, FILE_READ); - if (file >= 0) - { - while ((s = fgets(file))) - { - m1 = stov(s); - s = fgets(file); - if (!s) - break; - m2 = stov(s); - s = fgets(file); - if (!s) - break; - fl = stof(s); - waypoint_spawn(m1, m2, fl); - if (m1 == m2) - cwp = cwp + 1; - else - cwb = cwb + 1; - } - fclose(file); - LOG_TRACE("loaded ", ftos(cwp), " waypoints and ", ftos(cwb), " wayboxes from maps/", mapname, ".waypoints\n"); - } - else - { - LOG_TRACE("waypoint load from ", filename, " failed\n"); - } - return cwp + cwb; -} - -vector waypoint_fixorigin(vector position) -{ - tracebox(position + '0 0 1' * (1 - STAT(PL_MIN, NULL).z), STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), position + '0 0 -512', MOVE_NOMONSTERS, NULL); - if(trace_fraction < 1) - position = trace_endpos; - //traceline(position, position + '0 0 -512', MOVE_NOMONSTERS, NULL); - //print("position is ", ftos(trace_endpos_z - position_z), " above solid\n"); - return position; -} - -void waypoint_spawnforitem_force(entity e, vector org) -{ - entity w; - - // Fix the waypoint altitude if necessary - org = waypoint_fixorigin(org); - - // don't spawn an item spawnfunc_waypoint if it already exists - w = findchain(classname, "waypoint"); - while (w) - { - if (w.wpisbox) - { - if (boxesoverlap(org, org, w.absmin, w.absmax)) - { - e.nearestwaypoint = w; - return; - } - } - else - { - if (vlen(w.origin - org) < 16) - { - e.nearestwaypoint = w; - return; - } - } - w = w.chain; - } - e.nearestwaypoint = waypoint_spawn(org, org, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_ITEM); -} - -void waypoint_spawnforitem(entity e) -{ - if(!bot_waypoints_for_items) - return; - - waypoint_spawnforitem_force(e, e.origin); -} - -void waypoint_spawnforteleporter_boxes(entity e, vector org1, vector org2, vector destination1, vector destination2, float timetaken) -{ - entity w; - entity dw; - w = waypoint_spawn(org1, org2, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_TELEPORT | WAYPOINTFLAG_NORELINK); - dw = waypoint_spawn(destination1, destination2, WAYPOINTFLAG_GENERATED); - // one way link to the destination - w.wp00 = dw; - w.wp00mincost = timetaken; // this is just for jump pads - // the teleporter's nearest spawnfunc_waypoint is this one - // (teleporters are not goals, so this is probably useless) - e.nearestwaypoint = w; - e.nearestwaypointtimeout = time + 1000000000; -} - -void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken) -{ - org = waypoint_fixorigin(org); - destination = waypoint_fixorigin(destination); - waypoint_spawnforteleporter_boxes(e, org, org, destination, destination, timetaken); -} - -void waypoint_spawnforteleporter(entity e, vector destination, float timetaken) -{ - destination = waypoint_fixorigin(destination); - waypoint_spawnforteleporter_boxes(e, e.absmin, e.absmax, destination, destination, timetaken); -} - -entity waypoint_spawnpersonal(entity this, vector position) -{ - entity w; - - // drop the waypoint to a proper location: - // first move it up by a player height - // then move it down to hit the floor with player bbox size - position = waypoint_fixorigin(position); - - w = waypoint_spawn(position, position, WAYPOINTFLAG_GENERATED | WAYPOINTFLAG_PERSONAL); - w.nearestwaypoint = NULL; - w.nearestwaypointtimeout = 0; - w.owner = this; - - waypoint_schedulerelink(w); - - return w; -} - -void botframe_showwaypointlinks() -{ - entity head, w; - if (time < botframe_waypointeditorlightningtime) - return; - botframe_waypointeditorlightningtime = time + 0.5; - FOREACH_CLIENT(IS_PLAYER(it) && !it.isbot, LAMBDA( - if(IS_ONGROUND(it) || it.waterlevel > WATERLEVEL_NONE) - { - //navigation_testtracewalk = true; - head = navigation_findnearestwaypoint(it, false); - // print("currently selected WP is ", etos(head), "\n"); - //navigation_testtracewalk = false; - if (head) - { - w = head ;if (w) te_lightning2(NULL, w.origin, it.origin); - w = head.wp00;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp01;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp02;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp03;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp04;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp05;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp06;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp07;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp08;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp09;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp10;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp11;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp12;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp13;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp14;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp15;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp16;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp17;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp18;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp19;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp20;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp21;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp22;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp23;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp24;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp25;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp26;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp27;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp28;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp29;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp30;if (w) te_lightning2(NULL, w.origin, head.origin); - w = head.wp31;if (w) te_lightning2(NULL, w.origin, head.origin); - } - } - )); -} - -float botframe_autowaypoints_fixdown(vector v) -{ - tracebox(v, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), v + '0 0 -64', MOVE_NOMONSTERS, NULL); - if(trace_fraction >= 1) - return 0; - return 1; -} - -float botframe_autowaypoints_createwp(vector v, entity p, .entity fld, float f) -{ - entity w; - - w = find(NULL, classname, "waypoint"); - while (w) - { - // if a matching spawnfunc_waypoint already exists, don't add a duplicate - if (boxesoverlap(v - '32 32 32', v + '32 32 32', w.absmin, w.absmax)) - //if (boxesoverlap(v - '4 4 4', v + '4 4 4', w.absmin, w.absmax)) - return 0; - w = find(w, classname, "waypoint"); - } - - waypoint_schedulerelink(p.(fld) = waypoint_spawn(v, v, f)); - return 1; -} - -// return value: -// 1 = WP created -// 0 = no action needed -// -1 = temp fail, try from world too -// -2 = permanent fail, do not retry -float botframe_autowaypoints_fix_from(entity p, float walkfromwp, entity wp, .entity fld) -{ - // make it possible to go from p to wp, if we can - // if wp is NULL, nearest is chosen - - entity w; - vector porg; - float t, tmin, tmax; - vector o; - vector save; - - if(!botframe_autowaypoints_fixdown(p.origin)) - return -2; - porg = trace_endpos; - - if(wp) - { - // if any WP w fulfills wp -> w -> porg and w is closer than wp, then switch from wp to w - - // if wp -> porg, then OK - float maxdist; - if(navigation_waypoint_will_link(wp.origin, porg, p, walkfromwp, 1050)) - { - // we may find a better one - maxdist = vlen(wp.origin - porg); - } - else - { - // accept any "good" - maxdist = 2100; - } - - float bestdist; - bestdist = maxdist; - w = find(NULL, classname, "waypoint"); - while (w) - { - if(w != wp && !(w.wpflags & WAYPOINTFLAG_NORELINK)) - { - float d; - d = vlen(wp.origin - w.origin) + vlen(w.origin - porg); - if(d < bestdist) - if(navigation_waypoint_will_link(wp.origin, w.origin, p, walkfromwp, 1050)) - if(navigation_waypoint_will_link(w.origin, porg, p, walkfromwp, 1050)) - { - bestdist = d; - p.(fld) = w; - } - } - w = find(w, classname, "waypoint"); - } - if(bestdist < maxdist) - { - LOG_INFO("update chain to new nearest WP ", etos(p.(fld)), "\n"); - return 0; - } - - if(bestdist < 2100) - { - // we know maxdist < 2100 - // so wp -> porg is still valid - // all is good - p.(fld) = wp; - return 0; - } - - // otherwise, no existing WP can fix our issues - } - else - { - save = p.origin; - setorigin(p, porg); - w = navigation_findnearestwaypoint(p, walkfromwp); - setorigin(p, save); - if(w) - { - p.(fld) = w; - return 0; - } - } - - tmin = 0; - tmax = 1; - for (;;) - { - if(tmax - tmin < 0.001) - { - // did not get a good candidate - return -1; - } - - t = (tmin + tmax) * 0.5; - o = antilag_takebackorigin(p, CS(p), time - t); - if(!botframe_autowaypoints_fixdown(o)) - return -2; - o = trace_endpos; - - if(wp) - { - if(!navigation_waypoint_will_link(wp.origin, o, p, walkfromwp, 1050)) - { - // we cannot walk from wp.origin to o - // get closer to tmax - tmin = t; - continue; - } - } - else - { - save = p.origin; - setorigin(p, o); - w = navigation_findnearestwaypoint(p, walkfromwp); - setorigin(p, save); - if(!w) - { - // we cannot walk from any WP to o - // get closer to tmax - tmin = t; - continue; - } - } - - // if we get here, o is valid regarding waypoints - // check if o is connected right to the player - // we break if it succeeds, as that means o is a good waypoint location - if(navigation_waypoint_will_link(o, porg, p, walkfromwp, 1050)) - break; - - // o is no good, we need to get closer to the player - tmax = t; - } - - LOG_INFO("spawning a waypoint for connecting to ", etos(wp), "\n"); - botframe_autowaypoints_createwp(o, p, fld, 0); - return 1; -} - -// automatically create missing waypoints -.entity botframe_autowaypoints_lastwp0, botframe_autowaypoints_lastwp1; -void botframe_autowaypoints_fix(entity p, float walkfromwp, .entity fld) -{ - float r = botframe_autowaypoints_fix_from(p, walkfromwp, p.(fld), fld); - if(r != -1) - return; - r = botframe_autowaypoints_fix_from(p, walkfromwp, NULL, fld); - if(r != -1) - return; - - LOG_INFO("emergency: got no good nearby WP to build a link from, starting a new chain\n"); - if(!botframe_autowaypoints_fixdown(p.origin)) - return; // shouldn't happen, caught above - botframe_autowaypoints_createwp(trace_endpos, p, fld, WAYPOINTFLAG_PROTECTED); -} - -void botframe_deleteuselesswaypoints() -{ - entity w, w1, w2; - float i, j, k; - for (w = NULL; (w = findfloat(w, bot_pickup, true)); ) - { - // NOTE: this protects waypoints if they're the ONLY nearest - // waypoint. That's the intention. - navigation_findnearestwaypoint(w, false); // Walk TO item. - navigation_findnearestwaypoint(w, true); // Walk FROM item. - } - for (w = NULL; (w = find(w, classname, "waypoint")); ) - { - w.wpflags |= WAYPOINTFLAG_DEAD_END; - w.wpflags &= ~WAYPOINTFLAG_USEFUL; - // WP is useful if: - if (w.wpflags & WAYPOINTFLAG_ITEM) - w.wpflags |= WAYPOINTFLAG_USEFUL; - if (w.wpflags & WAYPOINTFLAG_TELEPORT) - w.wpflags |= WAYPOINTFLAG_USEFUL; - if (w.wpflags & WAYPOINTFLAG_PROTECTED) - w.wpflags |= WAYPOINTFLAG_USEFUL; - // b) WP is closest WP for an item/spawnpoint/other entity - // This has been done above by protecting these WPs. - } - // c) There are w1, w, w2 so that w1 -> w, w -> w2 and not w1 -> w2. - for (w1 = NULL; (w1 = find(w1, classname, "waypoint")); ) - { - if (w1.wpflags & WAYPOINTFLAG_PERSONAL) - continue; - for (i = 0; i < 32; ++i) - { - w = waypoint_get_link(w1, i); - if (!w) - break; - if (w.wpflags & WAYPOINTFLAG_PERSONAL) - continue; - if (w.wpflags & WAYPOINTFLAG_USEFUL) - continue; - for (j = 0; j < 32; ++j) - { - w2 = waypoint_get_link(w, j); - if (!w2) - break; - if (w1 == w2) - continue; - if (w2.wpflags & WAYPOINTFLAG_PERSONAL) - continue; - // If we got here, w1 != w2 exist with w1 -> w - // and w -> w2. That means the waypoint is not - // a dead end. - w.wpflags &= ~WAYPOINTFLAG_DEAD_END; - for (k = 0; k < 32; ++k) - { - if (waypoint_get_link(w1, k) == w2) - continue; - // IF WE GET HERE, w is proven useful - // to get from w1 to w2! - w.wpflags |= WAYPOINTFLAG_USEFUL; - goto next; - } - } -LABEL(next) - } - } - // d) The waypoint is a dead end. Dead end waypoints must be kept as - // they are needed to complete routes while autowaypointing. - - for (w = NULL; (w = find(w, classname, "waypoint")); ) - { - if (!(w.wpflags & (WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END))) - { - LOG_INFOF("Removed a waypoint at %v. Try again for more!\n", w.origin); - te_explosion(w.origin); - waypoint_remove(w); - break; - } - } - for (w = NULL; (w = find(w, classname, "waypoint")); ) - w.wpflags &= ~(WAYPOINTFLAG_USEFUL | WAYPOINTFLAG_DEAD_END); // temp flag -} - -void botframe_autowaypoints() -{ - FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it) && !IS_DEAD(it), LAMBDA( - // going back is broken, so only fix waypoints to walk TO the player - //botframe_autowaypoints_fix(p, false, botframe_autowaypoints_lastwp0); - botframe_autowaypoints_fix(it, true, botframe_autowaypoints_lastwp1); - //te_explosion(p.botframe_autowaypoints_lastwp0.origin); - )); - - if (autocvar_g_waypointeditor_auto >= 2) { - botframe_deleteuselesswaypoints(); - } -} - diff --git a/qcsrc/server/bot/waypoints.qh b/qcsrc/server/bot/waypoints.qh deleted file mode 100644 index 23c0fa63c3..0000000000 --- a/qcsrc/server/bot/waypoints.qh +++ /dev/null @@ -1,67 +0,0 @@ -#pragma once -/* - * Globals and Fields - */ - -const int WAYPOINTFLAG_GENERATED = BIT(23); -const int WAYPOINTFLAG_ITEM = BIT(22); -const int WAYPOINTFLAG_TELEPORT = BIT(21); -const int WAYPOINTFLAG_NORELINK = BIT(20); -const int WAYPOINTFLAG_PERSONAL = BIT(19); -const int WAYPOINTFLAG_PROTECTED = BIT(18); // Useless WP detection never kills these. -const int WAYPOINTFLAG_USEFUL = BIT(17); // Useless WP detection temporary flag. -const int WAYPOINTFLAG_DEAD_END = BIT(16); // Useless WP detection temporary flag. - -// 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; - -float botframe_waypointeditorlightningtime; -float botframe_loadedforcedlinks; -float botframe_cachedwaypointlinks; - -.entity wp00, wp01, wp02, wp03, wp04, wp05, wp06, wp07, wp08, wp09, wp10, wp11, wp12, wp13, wp14, wp15; -.entity wp16, wp17, wp18, wp19, wp20, wp21, wp22, wp23, wp24, wp25, wp26, wp27, wp28, wp29, wp30, wp31; - -// itemscore = (howmuchmoreIwant / howmuchIcanwant) / itemdistance -.float wp00mincost, wp01mincost, wp02mincost, wp03mincost, wp04mincost, wp05mincost, wp06mincost, wp07mincost; -.float wp08mincost, wp09mincost, wp10mincost, wp11mincost, wp12mincost, wp13mincost, wp14mincost, wp15mincost; -.float wp16mincost, wp17mincost, wp18mincost, wp19mincost, wp20mincost, wp21mincost, wp22mincost, wp23mincost; -.float wp24mincost, wp25mincost, wp26mincost, wp27mincost, wp28mincost, wp29mincost, wp30mincost, wp31mincost; - -.float wpfire, wpcost, wpconsidered, wpisbox, wplinked, wphardwired; -.int wpflags; - -.vector wpnearestpoint; - -/* - * Functions - */ - -spawnfunc(waypoint); -void waypoint_addlink(entity from, entity to); -void waypoint_think(entity this); -void waypoint_clearlinks(entity wp); -void waypoint_schedulerelink(entity wp); - -void waypoint_remove(entity e); -void waypoint_removeall(); -void waypoint_schedulerelinkall(); -void waypoint_load_links_hardwired(); -void waypoint_save_links(); -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); -void waypoint_spawnforteleporter_v(entity e, vector org, vector destination, float timetaken); -void botframe_showwaypointlinks(); - -float waypoint_loadall(); -float waypoint_load_links(); - -entity waypoint_spawn(vector m1, vector m2, float f); -entity waypoint_spawnpersonal(entity this, vector position); - -vector waypoint_fixorigin(vector position); - -void botframe_autowaypoints(); diff --git a/qcsrc/server/campaign.qc b/qcsrc/server/campaign.qc index 5dcd0ad935..5097bd8a14 100644 --- a/qcsrc/server/campaign.qc +++ b/qcsrc/server/campaign.qc @@ -73,21 +73,25 @@ void CampaignPreInit() { float baseskill; string title; - if(autocvar_sv_cheats) - { - CampaignBailout("JOLLY CHEATS AHAHAHAHAHAHAH))"); - return; - } campaign_level = autocvar__campaign_index; campaign_name = strzone(autocvar__campaign_name); campaign_index_var = strzone(strcat("g_campaign", campaign_name, "_index")); CampaignFile_Load(campaign_level, 2); + if(campaign_entries < 1) { CampaignBailout("unknown map"); return; } + if(autocvar_sv_cheats) + { + MapInfo_SwitchGameType(MapInfo_Type_FromString(campaign_gametype[0])); + CampaignFile_Unload(); + CampaignBailout("JOLLY CHEATS AHAHAHAHAHAHAH))"); + return; + } + baseskill = autocvar_g_campaign_skill; baseskill = baseskill + campaign_botskill[0]; if(baseskill < 0) @@ -256,7 +260,7 @@ void CampaignPostIntermission() if(campaign_won && campaign_entries < 2) { // last map won! - LOG_DEBUG("^2test run: campaign looks GOOD\n"); + LOG_DEBUG("^2test run: campaign looks GOOD"); localcmd("togglemenu 1\n"); CampaignFile_Unload(); return; diff --git a/qcsrc/server/cheats.qc b/qcsrc/server/cheats.qc index 9706a2f159..3d569222cd 100644 --- a/qcsrc/server/cheats.qc +++ b/qcsrc/server/cheats.qc @@ -4,7 +4,7 @@ #include "race.qh" #include "../common/triggers/teleporters.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "weapons/tracing.qh" @@ -14,9 +14,9 @@ #include -#include "../common/monsters/all.qh" +#include "../common/monsters/_mod.qh" -#include "../common/weapons/all.qh" +#include #include "../common/triggers/subs.qh" @@ -164,7 +164,7 @@ float CheatImpulse(entity this, int imp) this.personal.strength_finished = this.strength_finished; this.personal.invincible_finished = this.invincible_finished; this.personal.teleport_time = time; - break; // this part itthis doesn't cheat, so let's not count this + break; // this part itself doesn't cheat, so let's not count this case CHIMPULSE_CLONE_MOVING.impulse: IS_CHEAT(this, imp, 0, 0); makevectors (this.v_angle); @@ -232,7 +232,7 @@ float CheatImpulse(entity this, int imp) break; case CHIMPULSE_TELEPORT.impulse: IS_CHEAT(this, imp, 0, 0); - if(this.movetype == MOVETYPE_NOCLIP) + if(this.move_movetype == MOVETYPE_NOCLIP) { e = find(NULL, classname, "info_autoscreenshot"); if(e) @@ -240,7 +240,7 @@ float CheatImpulse(entity this, int imp) sprint(this, "Emergency teleport used info_autoscreenshot location\n"); setorigin(this, e.origin); this.angles = e.angles; - remove(e); + delete(e); // should we? this.angles_x = -this.angles_x; this.fixangle = true; this.velocity = '0 0 0'; @@ -262,7 +262,7 @@ float CheatImpulse(entity this, int imp) case CHIMPULSE_R00T.impulse: IS_CHEAT(this, imp, 0, 0); RandomSelection_Init(); - FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it) && DIFF_TEAM(it, this), LAMBDA(RandomSelection_Add(it, 0, string_null, 1, 1))); + FOREACH_CLIENT(IS_PLAYER(it) && !IS_DEAD(it) && DIFF_TEAM(it, this), LAMBDA(RandomSelection_AddEnt(it, 1, 1))); if(RandomSelection_chosen_ent) e = RandomSelection_chosen_ent; else @@ -274,7 +274,7 @@ float CheatImpulse(entity this, int imp) e2 = spawn(); setorigin(e2, e.origin); RadiusDamage(e2, this, 1000, 0, 128, NULL, NULL, 500, DEATH_CHEAT.m_id, e); - remove(e2); + delete(e2); LOG_INFO("404 Sportsmanship not found.\n"); DID_CHEAT(); @@ -323,7 +323,7 @@ float CheatCommand(entity this, int argc) // arguments: // effectname effectnum = _particleeffectnum(argv(1)); - W_SetupShot(this, false, false, SND_Null, CH_WEAPON_A, 0); + W_SetupShot(this, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0); traceline(w_shotorg, w_shotorg + w_shotdir * MAX_SHOT_DISTANCE, MOVE_NORMAL, this); __trailparticles(this, effectnum, w_shotorg, trace_endpos); DID_CHEAT(); @@ -338,7 +338,7 @@ float CheatCommand(entity this, int argc) // arguments: // modelname mode f = stof(argv(2)); - W_SetupShot(this, false, false, SND_Null, CH_WEAPON_A, 0); + W_SetupShot(this, weaponentities[0], false, false, SND_Null, CH_WEAPON_A, 0); traceline(w_shotorg, w_shotorg + w_shotdir * 2048, MOVE_NORMAL, this); if((trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) || trace_fraction == 1) { @@ -364,7 +364,7 @@ float CheatCommand(entity this, int argc) tracebox(e.origin, e.mins, e.maxs, e.origin, MOVE_NORMAL, e); if(trace_startsolid) { - remove(e); + delete(e); sprint(this, "cannot make stuff there (no space)\n"); } else @@ -444,7 +444,7 @@ float CheatCommand(entity this, int argc) e.nextthink = time; e.solid = 0; // nothing special setmodel(e, MDL_MARKER); - setsize(e, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); + setsize(e, STAT(PL_MIN, this), STAT(PL_MAX, this)); e.skin = 2; if(argc == 3) e.cnt = stof(argv(1)); @@ -475,18 +475,18 @@ float CheatCommand(entity this, int argc) RandomSelection_Init(); crosshair_trace(this); for(entity e = NULL; (e = find(e, classname, "dragbox_box")); ) - RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); + RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); for(entity e = NULL; (e = find(e, classname, "dragpoint")); ) - RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); + RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); if(RandomSelection_chosen_ent) { - remove(RandomSelection_chosen_ent.killindicator.killindicator); - remove(RandomSelection_chosen_ent.killindicator); + delete(RandomSelection_chosen_ent.killindicator.killindicator); + delete(RandomSelection_chosen_ent.killindicator); if(RandomSelection_chosen_ent.aiment) - remove(RandomSelection_chosen_ent.aiment); + delete(RandomSelection_chosen_ent.aiment); if(RandomSelection_chosen_ent.enemy) - remove(RandomSelection_chosen_ent.enemy); - remove(RandomSelection_chosen_ent); + delete(RandomSelection_chosen_ent.enemy); + delete(RandomSelection_chosen_ent); } DID_CHEAT(); break; @@ -497,9 +497,9 @@ float CheatCommand(entity this, int argc) RandomSelection_Init(); crosshair_trace(this); for(entity e = NULL; (e = find(e, classname, "dragbox_box")); ) - RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); + RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); for(entity e = NULL; (e = find(e, classname, "dragpoint")); ) - RandomSelection_Add(e, 0, string_null, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); + RandomSelection_AddEnt(e, 1, 1 / vlen(e.origin + (e.mins + e.maxs) * 0.5 - trace_endpos)); if(RandomSelection_chosen_ent) { if(substring(argv(1), 0, 1) == "*") @@ -604,15 +604,15 @@ float CheatCommand(entity this, int argc) case "drag_clear": IS_CHEAT(this, 0, argc, 0); for(entity e = NULL; (e = find(e, classname, "dragbox_box")); ) - remove(e); + delete(e); for(entity e = NULL; (e = find(e, classname, "dragbox_corner_1")); ) - remove(e); + delete(e); for(entity e = NULL; (e = find(e, classname, "dragbox_corner_2")); ) - remove(e); + delete(e); for(entity e = NULL; (e = find(e, classname, "dragpoint")); ) - remove(e); + delete(e); for(entity e = NULL; (e = find(e, classname, "drag_digit")); ) - remove(e); + delete(e); DID_CHEAT(); break; case "god": @@ -639,29 +639,29 @@ float CheatCommand(entity this, int argc) break; case "noclip": IS_CHEAT(this, 0, argc, 0); - if(this.movetype != MOVETYPE_NOCLIP) + if(this.move_movetype != MOVETYPE_NOCLIP) { - this.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); sprint(this, "noclip ON\n"); DID_CHEAT(); } else { - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); sprint(this, "noclip OFF\n"); } break; case "fly": IS_CHEAT(this, 0, argc, 0); - if(this.movetype != MOVETYPE_FLY) + if(this.move_movetype != MOVETYPE_FLY) { - this.movetype = MOVETYPE_FLY; + set_movetype(this, MOVETYPE_FLY); sprint(this, "flymode ON\n"); DID_CHEAT(); } else { - this.movetype = MOVETYPE_WALK; + set_movetype(this, MOVETYPE_WALK); sprint(this, "flymode OFF\n"); } break; @@ -675,7 +675,7 @@ float CheatCommand(entity this, int argc) entity e = spawn(); e.target = argv(1); SUB_UseTargets(e, this, NULL); - remove(e); + delete(e); DID_CHEAT(); break; case "killtarget": @@ -683,7 +683,7 @@ float CheatCommand(entity this, int argc) entity e2 = spawn(); e2.killtarget = argv(1); SUB_UseTargets(e2, this, NULL); - remove(e2); + delete(e2); DID_CHEAT(); break; case "teleporttotarget": @@ -695,7 +695,7 @@ float CheatCommand(entity this, int argc) if(!wasfreed(ent)) { Simple_TeleportPlayer(ent, this); - remove(ent); + delete(ent); DID_CHEAT(); } break; @@ -733,7 +733,8 @@ float CheatFrame(entity this) if(this.maycheat || (gamestart_sv_cheats && autocvar_sv_cheats)) { // use cheat dragging if cheats are enabled - crosshair_trace_plusvisibletriggers(this); + //if(Drag_IsDragging(this)) + //crosshair_trace_plusvisibletriggers(this); Drag(this, true, true); } else @@ -867,9 +868,9 @@ void Drag_Begin(entity dragger, entity draggee, vector touchpoint) { float tagscale; - draggee.dragmovetype = draggee.movetype; + draggee.dragmovetype = draggee.move_movetype; draggee.draggravity = draggee.gravity; - draggee.movetype = MOVETYPE_WALK; + set_movetype(draggee, MOVETYPE_WALK); draggee.gravity = 0.00001; UNSET_ONGROUND(draggee); draggee.draggedby = dragger; @@ -894,10 +895,10 @@ void Drag_Finish(entity dragger) if(dragger) dragger.dragentity = NULL; draggee.draggedby = NULL; - draggee.movetype = draggee.dragmovetype; + set_movetype(draggee, draggee.dragmovetype); draggee.gravity = draggee.draggravity; - switch(draggee.movetype) + switch(draggee.move_movetype) { case MOVETYPE_TOSS: case MOVETYPE_WALK: @@ -912,7 +913,7 @@ void Drag_Finish(entity dragger) break; } - if((draggee.flags & FL_ITEM) && (vlen(draggee.velocity) < 32)) + if((draggee.flags & FL_ITEM) && (vdist(draggee.velocity, <, 32))) { draggee.velocity = '0 0 0'; SET_ONGROUND(draggee); // floating items are FUN diff --git a/qcsrc/server/cl_client.qc b/qcsrc/server/cl_client.qc deleted file mode 100644 index 28d9c6c1a0..0000000000 --- a/qcsrc/server/cl_client.qc +++ /dev/null @@ -1,2475 +0,0 @@ -#include "cl_client.qh" - -#include "anticheat.qh" -#include "cl_impulse.qh" -#include "cl_player.qh" -#include "ipban.qh" -#include "miscfunctions.qh" -#include "portals.qh" -#include "teamplay.qh" -#include "playerdemo.qh" -#include "spawnpoints.qh" -#include "g_damage.qh" -#include "g_hook.qh" -#include "command/common.qh" -#include "cheats.qh" -#include "g_world.qh" -#include "race.qh" -#include "antilag.qh" -#include "campaign.qh" -#include "command/common.qh" - -#include "bot/bot.qh" -#include "bot/navigation.qh" - -#include "../common/ent_cs.qh" -#include - -#include - -#include "../common/triggers/teleporters.qh" - -#include "../common/vehicles/all.qh" - -#include "weapons/hitplot.qh" -#include "weapons/weaponsystem.qh" - -#include "../common/net_notice.qh" -#include "../common/physics/player.qh" - -#include "../common/items/all.qc" - -#include "../common/mutators/mutator/waypoints/all.qh" - -#include "../common/triggers/subs.qh" -#include "../common/triggers/triggers.qh" -#include "../common/triggers/trigger/secret.qh" - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/items/inventory.qh" - -#include "../common/monsters/sv_monsters.qh" - -#include "../lib/warpzone/server.qh" - -STATIC_METHOD(Client, Add, void(Client this, int _team)) -{ - WITHSELF(this, ClientConnect()); - TRANSMUTE(Player, this); - this.frame = 12; // 7 - this.team = _team; - WITHSELF(this, PutClientInServer()); -} - -void PutObserverInServer(entity this); -void ClientDisconnect(); - -STATIC_METHOD(Client, Remove, void(Client this)) -{ - TRANSMUTE(Observer, this); - WITHSELF(this, PutClientInServer()); - WITHSELF(this, ClientDisconnect()); -} - -void send_CSQC_teamnagger() { - WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); -} - -bool ClientData_Send(entity this, entity to, int sf) -{ - assert(to == this.owner, return false); - - entity e = to; - if (IS_SPEC(e)) e = e.enemy; - - sf = 0; - if (e.race_completed) sf |= 1; // forced scoreboard - if (to.spectatee_status) sf |= 2; // spectator ent number follows - if (e.zoomstate) sf |= 4; // zoomed - if (e.porto_v_angle_held) sf |= 8; // angles held - - WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); - WriteByte(MSG_ENTITY, sf); - - if (sf & 2) - { - WriteByte(MSG_ENTITY, to.spectatee_status); - } - if (sf & 8) - { - WriteAngle(MSG_ENTITY, e.v_angle.x); - WriteAngle(MSG_ENTITY, e.v_angle.y); - } - return true; -} - -void ClientData_Attach(entity this) -{ - Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send); - this.clientdata.drawonlytoclient = this; - this.clientdata.owner = this; -} - -void ClientData_Detach(entity this) -{ - remove(this.clientdata); - this.clientdata = NULL; -} - -void ClientData_Touch(entity e) -{ - e.clientdata.SendFlags = 1; - - // make it spectatable - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1)); -} - -.string netname_previous; - -void SetSpectatee(entity player, entity spectatee); - - -/* -============= -CheckPlayerModel - -Checks if the argument string can be a valid playermodel. -Returns a valid one in doubt. -============= -*/ -string FallbackPlayerModel; -string CheckPlayerModel(string plyermodel) { - if(FallbackPlayerModel != cvar_defstring("_cl_playermodel")) - { - // note: we cannot summon Don Strunzone here, some player may - // still have the model string set. In case anyone manages how - // to change a cvar default, we'll have a small leak here. - FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel")); - } - // only in right path - if( substring(plyermodel,0,14) != "models/player/") - return FallbackPlayerModel; - // only good file extensions - if(substring(plyermodel,-4,4) != ".zym") - if(substring(plyermodel,-4,4) != ".dpm") - if(substring(plyermodel,-4,4) != ".iqm") - if(substring(plyermodel,-4,4) != ".md3") - if(substring(plyermodel,-4,4) != ".psk") - return FallbackPlayerModel; - // forbid the LOD models - if(substring(plyermodel, -9,5) == "_lod1") - return FallbackPlayerModel; - if(substring(plyermodel, -9,5) == "_lod2") - return FallbackPlayerModel; - if(plyermodel != strtolower(plyermodel)) - return FallbackPlayerModel; - // also, restrict to server models - if(autocvar_sv_servermodelsonly) - { - if(!fexists(plyermodel)) - return FallbackPlayerModel; - } - return plyermodel; -} - -void setplayermodel(entity e, string modelname) -{ - precache_model(modelname); - _setmodel(e, modelname); - player_setupanimsformodel(e); - if(!autocvar_g_debug_globalsounds) - UpdatePlayerSounds(e); -} - -void FixPlayermodel(entity player); -/** putting a client as observer in the server */ -void PutObserverInServer(entity this) -{ - bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this); - PlayerState_detach(this); - - if (IS_PLAYER(this) && this.health >= 1) { - // despawn effect - Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); - } - - { - entity spot = SelectSpawnPoint(this, true); - if (!spot) LOG_FATAL("No spawnpoints for observers?!?"); - this.angles = spot.angles; - this.angles_z = 0; - this.fixangle = true; - // offset it so that the spectator spawns higher off the ground, looks better this way - setorigin(this, spot.origin + STAT(PL_VIEW_OFS, NULL)); - this.prevorigin = this.origin; - if (IS_REAL_CLIENT(this)) - { - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this); - } - // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY - // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS" - if(!autocvar_g_debug_globalsounds) - { - // needed for player sounds - this.model = ""; - FixPlayermodel(this); - } - setmodel(this, MDL_Null); - setsize(this, STAT(PL_CROUCH_MIN, NULL), STAT(PL_CROUCH_MAX, NULL)); - this.view_ofs = '0 0 0'; - } - - RemoveGrapplingHook(this); - Portal_ClearAll(this); - Unfreeze(this); - - if (this.alivetime) - { - if (!warmup_stage) - PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); - this.alivetime = 0; - } - - if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); - - WaypointSprite_PlayerDead(this); - - if (mutator_returnvalue) { - // mutator prevents resetting teams+score - } else { - this.team = -1; // move this as it is needed to log the player spectating in eventlog - this.frags = FRAGS_SPECTATOR; - PlayerScore_Clear(this); // clear scores when needed - } - - if (this.killcount != FRAGS_SPECTATOR) - { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname); - if(!intermission_running) - if(autocvar_g_chat_nospectators == 1 || (!(warmup_stage || gameover) && autocvar_g_chat_nospectators == 2)) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_CHAT_NOSPECTATORS); - - if(this.just_joined == false) { - LogTeamchange(this.playerid, -1, 4); - } else - this.just_joined = false; - } - - accuracy_resend(this); - - this.spectatortime = time; - this.bot_attack = false; - this.hud = HUD_NORMAL; - TRANSMUTE(Observer, this); - this.iscreature = false; - this.teleportable = TELEPORT_SIMPLE; - this.damagedbycontents = false; - this.health = FRAGS_SPECTATOR; - this.takedamage = DAMAGE_NO; - this.solid = SOLID_NOT; - this.movetype = 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; - this.pauserotarmor_finished = 0; - this.pauserothealth_finished = 0; - this.pauseregen_finished = 0; - this.damageforcescale = 0; - this.death_time = 0; - this.respawn_flags = 0; - this.respawn_time = 0; - this.stat_respawn_time = 0; - this.alpha = 0; - this.scale = 0; - this.fade_time = 0; - this.pain_frame = 0; - this.pain_finished = 0; - this.strength_finished = 0; - this.invincible_finished = 0; - this.superweapons_finished = 0; - this.pushltime = 0; - this.istypefrag = 0; - setthink(this, func_null); - this.nextthink = 0; - this.hook_time = 0; - this.deadflag = DEAD_NO; - this.crouch = false; - this.revival_time = 0; - - this.items = 0; - this.weapons = '0 0 0'; - this.drawonlytoclient = this; - - this.weaponname = ""; - this.weaponmodel = ""; - for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - this.weaponentities[slot] = NULL; - } - this.exteriorweaponentity = NULL; - this.killcount = FRAGS_SPECTATOR; - this.velocity = '0 0 0'; - this.avelocity = '0 0 0'; - this.punchangle = '0 0 0'; - this.punchvector = '0 0 0'; - this.oldvelocity = this.velocity; - this.fire_endtime = -1; - this.event_damage = func_null; -} - -int player_getspecies(entity this) -{ - get_model_parameters(this.model, this.skin); - int s = get_model_parameters_species; - get_model_parameters(string_null, 0); - if (s < 0) return SPECIES_HUMAN; - return s; -} - -.float model_randomizer; -void FixPlayermodel(entity player) -{ - string defaultmodel = ""; - int defaultskin = 0; - if(autocvar_sv_defaultcharacter) - { - if(teamplay) - { - string s = Static_Team_ColorName_Lower(player.team); - if (s != "neutral") - { - defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s)); - defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); - } - } - - if(defaultmodel == "") - { - defaultmodel = autocvar_sv_defaultplayermodel; - defaultskin = autocvar_sv_defaultplayerskin; - } - - int n = tokenize_console(defaultmodel); - if(n > 0) - { - defaultmodel = argv(floor(n * player.model_randomizer)); - // However, do NOT randomize if the player-selected model is in the list. - for (int i = 0; i < n; ++i) - if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin)) - defaultmodel = argv(i); - } - - int i = strstrofs(defaultmodel, ":", 0); - if(i >= 0) - { - defaultskin = stof(substring(defaultmodel, i+1, -1)); - defaultmodel = substring(defaultmodel, 0, i); - } - } - if(autocvar_sv_defaultcharacterskin && !defaultskin) - { - if(teamplay) - { - string s = Static_Team_ColorName_Lower(player.team); - if (s != "neutral") - defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); - } - - if(!defaultskin) - defaultskin = autocvar_sv_defaultplayerskin; - } - - MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player); - defaultmodel = M_ARGV(0, string); - defaultskin = M_ARGV(1, int); - - bool chmdl = false; - int oldskin; - if(defaultmodel != "") - { - if (defaultmodel != player.model) - { - vector m1 = player.mins; - vector m2 = player.maxs; - setplayermodel (player, defaultmodel); - setsize (player, m1, m2); - chmdl = true; - } - - oldskin = player.skin; - player.skin = defaultskin; - } else { - if (player.playermodel != player.model || player.playermodel == "") - { - player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop - vector m1 = player.mins; - vector m2 = player.maxs; - setplayermodel (player, player.playermodel); - setsize (player, m1, m2); - chmdl = true; - } - - if(!autocvar_sv_defaultcharacterskin) - { - oldskin = player.skin; - player.skin = stof(player.playerskin); - } - else - { - oldskin = player.skin; - player.skin = defaultskin; - } - } - - if(chmdl || oldskin != player.skin) // model or skin has changed - { - player.species = player_getspecies(player); // update species - if(!autocvar_g_debug_globalsounds) - UpdatePlayerSounds(player); // update skin sounds - } - - if(!teamplay) - if(strlen(autocvar_sv_defaultplayercolors)) - if(player.clientcolors != stof(autocvar_sv_defaultplayercolors)) - setcolor(player, stof(autocvar_sv_defaultplayercolors)); -} - - -/** Called when a client spawns in the server */ -void PutClientInServer() -{ENGINE_EVENT(); - if (IS_BOT_CLIENT(this)) { - TRANSMUTE(Player, this); - } else if (IS_REAL_CLIENT(this)) { - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this); - } - if (gameover) { - TRANSMUTE(Observer, this); - } - - SetSpectatee(this, NULL); - - // reset player keys - this.itemkeys = 0; - - MUTATOR_CALLHOOK(PutClientInServer, this); - - if (IS_OBSERVER(this)) { - PutObserverInServer(this); - } else if (IS_PLAYER(this)) { - PlayerState_attach(this); - accuracy_resend(this); - - if (this.team < 0) - JoinBestTeam(this, false, true); - - entity spot = SelectSpawnPoint(this, false); - if (!spot) { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS); - return; // spawn failed - } - - TRANSMUTE(Player, this); - this.wasplayer = true; - this.iscreature = true; - this.teleportable = TELEPORT_NORMAL; - this.damagedbycontents = true; - this.movetype = MOVETYPE_WALK; - this.solid = SOLID_SLIDEBOX; - this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; - if (autocvar_g_playerclip_collisions) - this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; - if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions) - this.dphitcontentsmask |= DPCONTENTS_BOTCLIP; - this.frags = FRAGS_PLAYER; - if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this); - this.flags = FL_CLIENT | FL_PICKUPITEMS; - if (autocvar__notarget) - this.flags |= FL_NOTARGET; - this.takedamage = DAMAGE_AIM; - this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; - this.dmg = 2; // WTF - - if (warmup_stage) { - this.ammo_shells = warmup_start_ammo_shells; - this.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; - } 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; - } - - this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; - - this.items = start_items; - - this.spawnshieldtime = time + autocvar_g_spawnshieldtime; - this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn; - this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn; - this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn; - this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn; - // extend the pause of rotting if client was reset at the beginning of the countdown - if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted? - float f = game_starttime - time; - this.spawnshieldtime += f; - this.pauserotarmor_finished += f; - this.pauserothealth_finished += f; - this.pauseregen_finished += f; - } - this.damageforcescale = 2; - this.death_time = 0; - this.respawn_flags = 0; - this.respawn_time = 0; - this.stat_respawn_time = 0; - this.scale = autocvar_sv_player_scale; - this.fade_time = 0; - this.pain_frame = 0; - this.pain_finished = 0; - this.pushltime = 0; - setthink(this, func_null); // players have no think function - this.nextthink = 0; - this.dmg_team = 0; - this.ballistics_density = autocvar_g_ballistics_density_player; - - this.deadflag = DEAD_NO; - - this.angles = spot.angles; - this.angles_z = 0; // never spawn tilted even if the spot says to - if (IS_BOT_CLIENT(this)) - this.v_angle = this.angles; - this.fixangle = true; // turn this way immediately - this.oldvelocity = this.velocity = '0 0 0'; - this.avelocity = '0 0 0'; - this.punchangle = '0 0 0'; - this.punchvector = '0 0 0'; - - this.strength_finished = 0; - this.invincible_finished = 0; - this.fire_endtime = -1; - this.revival_time = 0; - this.air_finished = time + 12; - - entity spawnevent = new_pure(spawnevent); - spawnevent.owner = this; - Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send); - - // Cut off any still running player sounds. - stopsound(this, CH_PLAYER_SINGLE); - - this.model = ""; - FixPlayermodel(this); - this.drawonlytoclient = NULL; - - this.crouch = false; - this.view_ofs = STAT(PL_VIEW_OFS, NULL); - setsize(this, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL)); - this.spawnorigin = spot.origin; - setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24)); - // don't reset back to last position, even if new position is stuck in solid - this.oldorigin = this.origin; - this.prevorigin = this.origin; - this.lastteleporttime = time; // prevent insane speeds due to changing origin - this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player - this.hud = HUD_NORMAL; - - this.event_damage = PlayerDamage; - - this.bot_attack = true; - this.monster_attack = true; - - PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; - - if (this.killcount == FRAGS_SPECTATOR) { - PlayerScore_Clear(this); - this.killcount = 0; - } - - for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - CL_SpawnWeaponentity(this, weaponentities[slot]); - } - this.alpha = default_player_alpha; - this.colormod = '1 1 1' * autocvar_g_player_brightness; - this.exteriorweaponentity.alpha = default_weapon_alpha; - - this.speedrunning = false; - - target_voicescript_clear(this); - - // reset fields the weapons may use - FOREACH(Weapons, true, LAMBDA( - it.wr_resetplayer(it, this); - // reload all reloadable weapons - if (it.spawnflags & WEP_FLAG_RELOADABLE) { - this.weapon_load[it.m_id] = it.reloading_ammo; - } - )); - - { - string s = spot.target; - spot.target = string_null; - SUB_UseTargets(spot, this, NULL); - spot.target = s; - } - - Unfreeze(this); - - MUTATOR_CALLHOOK(PlayerSpawn, this, spot); - - if (autocvar_spawn_debug) - { - sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n")); - remove(spot); // usefull for checking if there are spawnpoints, that let drop through the floor - } - - PS(this).m_switchweapon = w_getbestweapon(this); - this.cnt = -1; // W_LastWeapon will not complain - PS(this).m_weapon = WEP_Null; - this.weaponname = ""; - PS(this).m_switchingweapon = WEP_Null; - - if (!warmup_stage && !this.alivetime) - this.alivetime = time; - - antilag_clear(this, CS(this)); - } -} - -void ClientInit_misc(entity this); - -.float ebouncefactor, ebouncestop; // electro's values -// 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) -{ - WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); - return = true; - msg_entity = to; - // MSG_INIT replacement - // TODO: make easier to use - Registry_send_all(); - W_PROP_reload(MSG_ONE, to); - ClientInit_misc(this); - MUTATOR_CALLHOOK(Ent_Init); -} -void ClientInit_misc(entity this) -{ - int channel = MSG_ONE; - WriteHeader(channel, ENT_CLIENT_INIT); - WriteByte(channel, g_nexball_meter_period * 32); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2])); - WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2])); - WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3])); - - if(sv_foginterval && world.fog != "") - WriteString(channel, world.fog); - else - WriteString(channel, ""); - WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent - WriteByte(channel, serverflags); // client has to know if it should zoom or not - WriteCoord(channel, autocvar_g_trueaim_minrange); -} - -void ClientInit_CheckUpdate(entity this) -{ - this.nextthink = time; - if(this.count != autocvar_g_balance_armor_blockpercent) - { - this.count = autocvar_g_balance_armor_blockpercent; - this.SendFlags |= 1; - } -} - -void ClientInit_Spawn() -{ - entity e = new_pure(clientinit); - setthink(e, ClientInit_CheckUpdate); - Net_LinkEntity(e, false, 0, ClientInit_SendEntity); - - ClientInit_CheckUpdate(e); -} - -/* -============= -SetNewParms -============= -*/ -void SetNewParms () -{ - // initialize parms for a new player - parm1 = -(86400 * 366); - - MUTATOR_CALLHOOK(SetNewParms); -} - -/* -============= -SetChangeParms -============= -*/ -void SetChangeParms () -{ENGINE_EVENT(); - // save parms for level change - parm1 = this.parm_idlesince - time; - - MUTATOR_CALLHOOK(SetChangeParms); -} - -/* -============= -DecodeLevelParms -============= -*/ -void DecodeLevelParms(entity this) -{ - // load parms - this.parm_idlesince = parm1; - if (this.parm_idlesince == -(86400 * 366)) - this.parm_idlesince = time; - - // whatever happens, allow 60 seconds of idling directly after connect for map loading - this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60); - - 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, false, 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 - WITHSELF(this, SV_ChangeTeam(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, this.origin, '0 0 0'); - } - } - - if(this.killindicator && !wasfreed(this.killindicator)) - remove(this.killindicator); - - this.killindicator = NULL; - - if(this.killindicator_teamchange) - ClientKill_Now_TeamChange(this); - - if(IS_PLAYER(this)) - Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0'); - - // now I am sure the player IS dead -} -void KillIndicator_Think(entity this) -{ - if (gameover) - { - this.owner.killindicator = NULL; - remove(this); - return; - } - - if (this.owner.alpha < 0 && !this.owner.vehicle) - { - this.owner.killindicator = NULL; - remove(this); - return; - } - - if(this.cnt <= 0) - { - ClientKill_Now(this.owner); - return; - } - else if(g_cts && 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; - entity e; - - if (gameover) - return; - - killtime = autocvar_g_balance_kill_delay; - - if(g_race_qualifying || g_cts) - killtime = 0; - - if(MUTATOR_CALLHOOK(ClientKill, this, killtime)) - return; - - 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")); - - for(e = NULL; (e = find(e, classname, "body")) != NULL; ) - { - if(e.enemy != this) - continue; - e.killindicator = spawn(); - e.killindicator.owner = e; - e.killindicator.scale = 0.5; - setattachment(e.killindicator, e, ""); - setorigin(e.killindicator, '0 0 52'); - setthink(e.killindicator, KillIndicator_Think); - e.killindicator.nextthink = starttime + (e.lip) * 0.05; - clientkilltime = max(clientkilltime, e.killindicator.nextthink + 0.05); - e.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 () -{ENGINE_EVENT(); - if(gameover) 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 - stuffcmd(e, "\nin_bindmap 0 0\n"); - if(autocvar_g_antilag == 3) // client side hitscan - stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n"); - if(autocvar_sv_gentle) - stuffcmd(e, "cl_cmd settemp cl_gentle 1\n"); - - MUTATOR_CALLHOOK(FixClientCvars, e); -} - -float PlayerInIDList(entity p, string idlist) -{ - float n, i; - string s; - - // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this - if (!p.crypto_idfp) - return 0; - - // this function allows abbreviated player IDs too! - n = tokenize_console(idlist); - for(i = 0; i < n; ++i) - { - s = argv(i); - if(s == substring(p.crypto_idfp, 0, strlen(s))) - return 1; - } - - return 0; -} - -#ifdef DP_EXT_PRECONNECT -/* -============= -ClientPreConnect - -Called once (not at each match start) when a client begins a connection to the server -============= -*/ -void ClientPreConnect () -{ENGINE_EVENT(); - if(autocvar_sv_eventlog) - { - GameLogEcho(sprintf(":connect:%d:%d:%s", - this.playerid, - etof(this), - ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot") - )); - } -} -#endif - -/** -============= -ClientConnect - -Called when a client connects to the server -============= -*/ -void ClientConnect() -{ENGINE_EVENT(); - if (Ban_MaybeEnforceBanOnce(this)) return; - assert(!IS_CLIENT(this), return); - this.flags |= FL_CLIENT; - assert(player_count >= 0, player_count = 0); - -#ifdef WATERMARK - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK); -#endif - this.version_nagtime = time + 10 + random() * 10; - TRANSMUTE(Client, this); - - // 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 (PlayerInIDList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; - else if (PlayerInIDList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; - else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; - else if (PlayerInIDList(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; - - { - int id = this.playerid; - this.playerid = 0; // silent - JoinBestTeam(this, false, false); // if the team number is valid, keep it - this.playerid = id; - } - - 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 - } - } - - PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); - - // always track bots, don't ask for cl_allow_uidtracking - if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this); - - if (autocvar_sv_eventlog) - GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname)); - - LogTeamchange(this.playerid, this.team, 1); - - this.just_joined = true; // stop spamming the eventlog with additional lines when the client connects - - this.netname_previous = strzone(this.netname); - - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && IS_PLAYER(this)) ? APP_TEAM_ENT(this, INFO_JOIN_CONNECT_TEAM) : INFO_JOIN_CONNECT), this.netname); - - stuffcmd(this, clientstuff, "\n"); - stuffcmd(this, "cl_particles_reloadeffects\n"); // TODO do we still need this? - - FixClientCvars(this); - - // get version info from player - stuffcmd(this, "cmd clientversion $gameversion\n"); - - // 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); - stuffcmd(this, sprintf("set _teams_available %d\n", t)); - } - else - { - stuffcmd(this, "set _teams_available 0\n"); - } - - bot_relinkplayerlist(); - - this.spectatortime = time; - if (blockSpectators) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); - } - - this.jointime = time; - this.allowed_timeouts = autocvar_sv_timeout_number; - - if (IS_REAL_CLIENT(this)) - { - if (!autocvar_g_campaign) - { - this.motd_actived_time = -1; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); - } - - if (g_weaponarena_weapons == WEPSET(TUBA)) - stuffcmd(this, "cl_cmd settemp chase_active 1\n"); - } - - if (!sv_foginterval && world.fog != "") - stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); - - if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && (c3==-1 && c4==-1))) - if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts - send_CSQC_teamnagger(); - - CSQCMODEL_AUTOINIT(this); - - this.model_randomizer = random(); - - if (IS_REAL_CLIENT(this)) - sv_notice_join(this); - - FOREACH_ENTITY_FLOAT(init_for_player_needed, true, { - it.init_for_player(it, this); - }); - - MUTATOR_CALLHOOK(ClientConnect, this); -} -/* -============= -ClientDisconnect - -Called when a client disconnects from the server -============= -*/ -.entity chatbubbleentity; -void ReadyCount(); -void ClientDisconnect() -{ENGINE_EVENT(); - assert(IS_CLIENT(this), return); - - PlayerStats_GameReport_FinalizePlayer(this); - if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); - if (this.active_minigame) part_minigame(this); - if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); - - if (autocvar_sv_eventlog) - GameLogEcho(strcat(":part:", ftos(this.playerid))); - - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname); - - MUTATOR_CALLHOOK(ClientDisconnect, this); - - ClientState_detach(this); - - Portal_ClearAll(this); - - Unfreeze(this); - - RemoveGrapplingHook(this); - - // Here, everything has been done that requires this player to be a client. - - this.flags &= ~FL_CLIENT; - - if (this.chatbubbleentity) remove(this.chatbubbleentity); - if (this.killindicator) remove(this.killindicator); - - WaypointSprite_PlayerGone(this); - - bot_relinkplayerlist(); - - if (this.netname_previous) strunzone(this.netname_previous); - if (this.clientstatus) strunzone(this.clientstatus); - if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse); - if (this.personal) remove(this.personal); - - this.playerid = 0; - ReadyCount(); - if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false); -} - -void ChatBubbleThink(entity this) -{ - this.nextthink = time; - if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this) - { - if(this.owner) // but why can that ever be NULL? - this.owner.chatbubbleentity = NULL; - remove(this); - return; - } - - this.mdl = ""; - - if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) - { - if ( this.owner.active_minigame ) - this.mdl = "models/sprites/minigame_busy.iqm"; - else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) - this.mdl = "models/misc/chatbubble.spr"; - } - - if ( this.model != this.mdl ) - _setmodel(this, this.mdl); - -} - -void UpdateChatBubble(entity this) -{ - if (this.alpha < 0) - return; - // spawn a chatbubble entity if needed - if (!this.chatbubbleentity) - { - this.chatbubbleentity = new(chatbubbleentity); - this.chatbubbleentity.owner = this; - this.chatbubbleentity.exteriormodeltoclient = this; - setthink(this.chatbubbleentity, ChatBubbleThink); - this.chatbubbleentity.nextthink = time; - setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below - //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1'); - setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1'); - setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth - this.chatbubbleentity.mdl = this.chatbubbleentity.model; - //this.chatbubbleentity.model = ""; - this.chatbubbleentity.effects = EF_LOWPRECISION; - } -} - - -// LordHavoc: this hack will be removed when proper _pants/_shirt layers are -// added to the model skins -/*void UpdateColorModHack() -{ - float c; - c = this.clientcolors & 15; - // LordHavoc: only bothering to support white, green, red, yellow, blue - if (!teamplay) this.colormod = '0 0 0'; - else if (c == 0) this.colormod = '1.00 1.00 1.00'; - else if (c == 3) this.colormod = '0.10 1.73 0.10'; - else if (c == 4) this.colormod = '1.73 0.10 0.10'; - else if (c == 12) this.colormod = '1.22 1.22 0.10'; - else if (c == 13) this.colormod = '0.10 0.10 1.73'; - else this.colormod = '1 1 1'; -}*/ - -void respawn(entity this) -{ - if(this.alpha >= 0 && autocvar_g_respawn_ghosts) - { - this.solid = SOLID_NOT; - this.takedamage = DAMAGE_NO; - this.movetype = MOVETYPE_FLY; - this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed; - this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3; - this.effects |= CSQCMODEL_EF_RESPAWNGHOST; - Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1); - if(autocvar_g_respawn_ghosts_maxtime) - SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5); - } - - CopyBody(this, 1); - - this.effects |= EF_NODRAW; // prevent another CopyBody - WITHSELF(this, PutClientInServer()); -} - -void play_countdown(entity this, float finished, Sound samp) -{ - TC(Sound, samp); - if(IS_REAL_CLIENT(this)) - if(floor(finished - time - frametime) != floor(finished - time)) - if(finished - time < 6) - sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM); -} - -void player_powerups(entity this) -{ - // add a way to see what the items were BEFORE all of these checks for the mutator hook - int items_prev = this.items; - - if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover) - this.modelflags |= MF_ROCKET; - else - this.modelflags &= ~MF_ROCKET; - - this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST); - - if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed - return; - - Fire_ApplyDamage(this); - Fire_ApplyEffect(this); - - if (!g_instagib) - { - if (this.items & ITEM_Strength.m_itemid) - { - play_countdown(this, this.strength_finished, SND_POWEROFF); - this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT); - if (time > this.strength_finished) - { - this.items = this.items - (this.items & ITEM_Strength.m_itemid); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH); - } - } - else - { - if (time < this.strength_finished) - { - this.items = this.items | ITEM_Strength.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH); - } - } - if (this.items & ITEM_Shield.m_itemid) - { - play_countdown(this, this.invincible_finished, SND_POWEROFF); - this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT); - if (time > this.invincible_finished) - { - this.items = this.items - (this.items & ITEM_Shield.m_itemid); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD); - } - } - else - { - if (time < this.invincible_finished) - { - this.items = this.items | ITEM_Shield.m_itemid; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD); - } - } - if (this.items & IT_SUPERWEAPON) - { - if (!(this.weapons & WEPSET_SUPERWEAPONS)) - { - this.superweapons_finished = 0; - this.items = this.items - (this.items & IT_SUPERWEAPON); - //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname); - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST); - } - else if (this.items & IT_UNLIMITED_SUPERWEAPONS) - { - // don't let them run out - } - else - { - play_countdown(this, this.superweapons_finished, SND_POWEROFF); - if (time > this.superweapons_finished) - { - this.items = this.items - (this.items & IT_SUPERWEAPON); - this.weapons &= ~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) - { - if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS)) - { - this.items = this.items | IT_SUPERWEAPON; - 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; - } - } - else - { - this.superweapons_finished = 0; - } - } - - if(autocvar_g_nodepthtestplayers) - this.effects = this.effects | EF_NODEPTHTEST; - - if(autocvar_g_fullbrightplayers) - this.effects = this.effects | EF_FULLBRIGHT; - - if (time >= game_starttime) - if (time < this.spawnshieldtime) - this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT); - - MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev); -} - -float CalcRegen(float current, float stable, float regenfactor, float regenframetime) -{ - if(current > stable) - return current; - else if(current > stable - 0.25) // when close enough, "snap" - return stable; - else - return min(stable, current + (stable - current) * regenfactor * regenframetime); -} - -float CalcRot(float current, float stable, float rotfactor, float rotframetime) -{ - if(current < stable) - return current; - else if(current < stable + 0.25) // when close enough, "snap" - return stable; - else - return max(stable, current + (stable - current) * rotfactor * rotframetime); -} - -float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit) -{ - if(current > rotstable) - { - if(rotframetime > 0) - { - current = CalcRot(current, rotstable, rotfactor, rotframetime); - current = max(rotstable, current - rotlinear * rotframetime); - } - } - else if(current < regenstable) - { - if(regenframetime > 0) - { - current = CalcRegen(current, regenstable, regenfactor, regenframetime); - current = min(regenstable, current + regenlinear * regenframetime); - } - } - - if(current > limit) - current = limit; - - return current; -} - -void player_regen(entity this) -{ - float max_mod, regen_mod, rot_mod, limit_mod; - max_mod = regen_mod = rot_mod = limit_mod = 1; - - float regen_health = autocvar_g_balance_health_regen; - float regen_health_linear = autocvar_g_balance_health_regenlinear; - float regen_health_rot = autocvar_g_balance_health_rot; - float regen_health_rotlinear = autocvar_g_balance_health_rotlinear; - float regen_health_stable = autocvar_g_balance_health_regenstable; - float regen_health_rotstable = autocvar_g_balance_health_rotstable; - bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot, - regen_health_rotlinear, regen_health_stable, regen_health_rotstable); - max_mod = M_ARGV(1, float); - regen_mod = M_ARGV(2, float); - rot_mod = M_ARGV(3, float); - limit_mod = M_ARGV(4, float); - regen_health = M_ARGV(5, float); - regen_health_linear = M_ARGV(6, float); - regen_health_rot = M_ARGV(7, float); - regen_health_rotlinear = M_ARGV(8, float); - regen_health_stable = M_ARGV(9, float); - regen_health_rotstable = M_ARGV(10, float); - - - if(!mutator_returnvalue) - if(!STAT(FROZEN, this)) - { - float mina, maxa, limith, limita; - maxa = autocvar_g_balance_armor_rotstable; - mina = autocvar_g_balance_armor_regenstable; - limith = autocvar_g_balance_health_limit; - limita = autocvar_g_balance_armor_limit; - - regen_health_rotstable = regen_health_rotstable * max_mod; - regen_health_stable = regen_health_stable * max_mod; - 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); - } - - // 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(this.vehicle) - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(this.event_damage) - this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0'); - } - - if (!(this.items & IT_UNLIMITED_WEAPON_AMMO)) - { - float minf, maxf, limitf; - - maxf = autocvar_g_balance_fuel_rotstable; - minf = autocvar_g_balance_fuel_regenstable; - limitf = autocvar_g_balance_fuel_limit; - - 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); - } -} - -bool zoomstate_set; -void SetZoomState(entity this, float z) -{ - if(z != this.zoomstate) - { - this.zoomstate = z; - ClientData_Touch(this); - } - zoomstate_set = true; -} - -void GetPressedKeys(entity this) -{ - MUTATOR_CALLHOOK(GetPressedKeys, this); - int keys = this.pressedkeys; - keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); - keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); - keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); - keys = BITSET(keys, KEY_LEFT, 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_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); - keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); - this.pressedkeys = keys; -} - -/* -====================== -spectate mode routines -====================== -*/ - -void SpectateCopy(entity this, entity 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; - this.clip_load = spectatee.clip_load; - this.clip_size = spectatee.clip_size; - this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance - this.health = spectatee.health; - this.impulse = 0; - this.items = spectatee.items; - this.last_pickup = spectatee.last_pickup; - this.hit_time = spectatee.hit_time; - this.strength_finished = spectatee.strength_finished; - this.invincible_finished = spectatee.invincible_finished; - this.pressedkeys = spectatee.pressedkeys; - this.weapons = spectatee.weapons; - this.vortex_charge = spectatee.vortex_charge; - this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; - this.hagar_load = spectatee.hagar_load; - this.arc_heat_percent = spectatee.arc_heat_percent; - this.minelayer_mines = spectatee.minelayer_mines; - this.punchangle = spectatee.punchangle; - this.view_ofs = spectatee.view_ofs; - this.velocity = spectatee.velocity; - this.dmg_take = spectatee.dmg_take; - this.dmg_save = spectatee.dmg_save; - this.dmg_inflictor = spectatee.dmg_inflictor; - this.v_angle = spectatee.v_angle; - this.angles = spectatee.v_angle; - STAT(FROZEN, this) = STAT(FROZEN, spectatee); - this.revive_progress = spectatee.revive_progress; - if(!PHYS_INPUT_BUTTON_USE(this)) - this.fixangle = true; - setorigin(this, spectatee.origin); - setsize(this, spectatee.mins, spectatee.maxs); - SetZoomState(this, spectatee.zoomstate); - - anticheat_spectatecopy(this, spectatee); - this.hud = spectatee.hud; - if(spectatee.vehicle) - { - this.fixangle = false; - //this.velocity = spectatee.vehicle.velocity; - this.vehicle_health = spectatee.vehicle_health; - this.vehicle_shield = spectatee.vehicle_shield; - this.vehicle_energy = spectatee.vehicle_energy; - this.vehicle_ammo1 = spectatee.vehicle_ammo1; - this.vehicle_ammo2 = spectatee.vehicle_ammo2; - this.vehicle_reload1 = spectatee.vehicle_reload1; - this.vehicle_reload2 = spectatee.vehicle_reload2; - - msg_entity = this; - - WriteByte (MSG_ONE, SVC_SETVIEWANGLES); - WriteAngle(MSG_ONE, spectatee.v_angle.x); - WriteAngle(MSG_ONE, spectatee.v_angle.y); - WriteAngle(MSG_ONE, spectatee.v_angle.z); - - //WriteByte (MSG_ONE, SVC_SETVIEW); - // WriteEntity(MSG_ONE, this); - //makevectors(spectatee.v_angle); - //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/ - } -} - -bool SpectateUpdate(entity this) -{ - if(!this.enemy) - return false; - - if(!IS_PLAYER(this.enemy) || this == this.enemy) - { - SetSpectatee(this, NULL); - return false; - } - - SpectateCopy(this, this.enemy); - - return true; -} - -bool SpectateSet(entity this) -{ - if(!IS_PLAYER(this.enemy)) - return false; - - msg_entity = this; - WriteByte(MSG_ONE, SVC_SETVIEW); - WriteEntity(MSG_ONE, this.enemy); - this.movetype = MOVETYPE_NONE; - accuracy_resend(this); - - if(!SpectateUpdate(this)) - PutObserverInServer(this); - - return true; -} - -void SetSpectatee(entity this, entity spectatee) -{ - entity old_spectatee = this.enemy; - - this.enemy = spectatee; - - // WEAPONTODO - // these are required to fix the spectator bug with arc - if(old_spectatee && old_spectatee.arc_beam) { old_spectatee.arc_beam.SendFlags |= ARC_SF_SETTINGS; } - if(this.enemy && this.enemy.arc_beam) { this.enemy.arc_beam.SendFlags |= ARC_SF_SETTINGS; } -} - -bool Spectate(entity this, entity pl) -{ - if(MUTATOR_CALLHOOK(SpectateSet, this, pl)) - return false; - pl = M_ARGV(1, entity); - - SetSpectatee(this, pl); - return SpectateSet(this); -} - -bool SpectateNext(entity this) -{ - other = find(this.enemy, classname, STR_PLAYER); - - if (MUTATOR_CALLHOOK(SpectateNext, this, other)) - other = M_ARGV(1, entity); - else if (!other) - other = find(other, classname, STR_PLAYER); - - if(other) { SetSpectatee(this, other); } - - return SpectateSet(this); -} - -bool SpectatePrev(entity this) -{ - // NOTE: chain order is from the highest to the lower entnum (unlike find) - other = findchain(classname, STR_PLAYER); - if (!other) // no player - return false; - - entity first = other; - // skip players until current spectated player - if(this.enemy) - while(other && other != this.enemy) - other = other.chain; - - switch (MUTATOR_CALLHOOK(SpectatePrev, this, other, first)) - { - case MUT_SPECPREV_FOUND: - other = M_ARGV(1, entity); - break; - case MUT_SPECPREV_RETURN: - other = M_ARGV(1, entity); - return true; - case MUT_SPECPREV_CONTINUE: - default: - { - if(other.chain) - other = other.chain; - else - other = first; - break; - } - } - - SetSpectatee(this, other); - return SpectateSet(this); -} - -/* -============= -ShowRespawnCountdown() - -Update a respawn countdown display. -============= -*/ -void ShowRespawnCountdown(entity this) -{ - float number; - if(!IS_DEAD(this)) // just respawned? - return; - else - { - number = ceil(this.respawn_time - time); - if(number <= 0) - return; - if(number <= this.respawn_countdown) - { - this.respawn_countdown = number - 1; - if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds - { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); } - } - } -} - -void LeaveSpectatorMode(entity this) -{ - if(this.caplayer) - return; - if(nJoinAllowed(this, this)) - { - if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0) - { - TRANSMUTE(Player, this); - - if(autocvar_g_campaign || autocvar_g_balance_teams) - { JoinBestTeam(this, false, true); } - - if(autocvar_g_campaign) - { campaign_bots_may_start = true; } - - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN); - - WITHSELF(this, PutClientInServer()); - - if(IS_PLAYER(this)) { Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && this.team != -1) ? APP_TEAM_ENT(this, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), this.netname); } - } - else - stuffcmd(this, "menu_showteamselect\n"); - } - else - { - // Player may not join because g_maxplayers is set - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT); - } -} - -/** - * 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 - * it checks whether the number of currently playing players exceeds g_maxplayers. - * @return int number of free slots for players, 0 if none - */ -bool nJoinAllowed(entity this, entity ignore) -{ - if(!ignore) - // this is called that way when checking if anyone may be able to join (to build qcstatus) - // so report 0 free slots if restricted - { - if(autocvar_g_forced_team_otherwise == "spectate") - return false; - if(autocvar_g_forced_team_otherwise == "spectator") - return false; - } - - if(this.team_forced < 0) - return false; // forced spectators can never join - - // TODO simplify this - int totalClients = 0; - int currentlyPlaying = 0; - FOREACH_CLIENT(true, LAMBDA( - if(it != ignore) - ++totalClients; - if(IS_REAL_CLIENT(it)) - if(IS_PLAYER(it) || it.caplayer) - ++currentlyPlaying; - )); - - if (!autocvar_g_maxplayers) - return maxclients - totalClients; - - if(currentlyPlaying < autocvar_g_maxplayers) - return min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); - - return false; -} - -/** - * Checks whether the client is an observer or spectator, if so, he will get kicked after - * g_maxplayers_spectator_blocktime seconds - */ -void checkSpectatorBlock(entity this) -{ - if(IS_SPEC(this) || IS_OBSERVER(this)) - if(!this.caplayer) - if(IS_REAL_CLIENT(this)) - { - if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); - dropclient(this); - } - } -} - -void PrintWelcomeMessage(entity this) -{ - if(this.motd_actived_time == 0) - { - if (autocvar_g_campaign) { - if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) { - this.motd_actived_time = time; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message); - } - } else { - if (PHYS_INPUT_BUTTON_INFO(this)) { - this.motd_actived_time = time; - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); - } - } - } - else if(this.motd_actived_time > 0) // showing MOTD or campaign message - { - if (autocvar_g_campaign) { - if (PHYS_INPUT_BUTTON_INFO(this)) - this.motd_actived_time = time; - else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } else { - if (PHYS_INPUT_BUTTON_INFO(this)) - this.motd_actived_time = time; - else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } - } - else //if(this.motd_actived_time < 0) // just connected, motd is active - { - if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD - this.motd_actived_time = -2; // wait until BUTTON_INFO gets released - else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this)) - { - // instanctly hide MOTD - this.motd_actived_time = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); - } - } -} - -void ObserverThink(entity this) -{ - if ( this.impulse ) - { - MinigameImpulse(this, this.impulse); - this.impulse = 0; - } - float prefered_movetype; - if (this.flags & FL_JUMPRELEASED) { - if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - this.flags |= FL_SPAWNING; - } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectateNext(this)) { - TRANSMUTE(Spectator, this); - } - } else { - prefered_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); - if (this.movetype != prefered_movetype) - this.movetype = prefered_movetype; - } - } else { - if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) { - this.flags |= FL_JUMPRELEASED; - if(this.flags & FL_SPAWNING) - { - this.flags &= ~FL_SPAWNING; - LeaveSpectatorMode(this); - return; - } - } - } -} - -void SpectatorThink(entity this) -{ - if ( this.impulse ) - { - if(MinigameImpulse(this, this.impulse)) - this.impulse = 0; - } - if (this.flags & FL_JUMPRELEASED) { - if (PHYS_INPUT_BUTTON_JUMP(this) && !this.version_mismatch) { - this.flags &= ~FL_JUMPRELEASED; - this.flags |= FL_SPAWNING; - } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectateNext(this)) { - TRANSMUTE(Spectator, this); - } else { - TRANSMUTE(Observer, this); - WITHSELF(this, PutClientInServer()); - } - this.impulse = 0; - } else if(this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) { - this.flags &= ~FL_JUMPRELEASED; - if(SpectatePrev(this)) { - TRANSMUTE(Spectator, this); - } else { - TRANSMUTE(Observer, this); - WITHSELF(this, PutClientInServer()); - } - this.impulse = 0; - } else if (PHYS_INPUT_BUTTON_ATCK2(this)) { - this.flags &= ~FL_JUMPRELEASED; - TRANSMUTE(Observer, this); - WITHSELF(this, PutClientInServer()); - } else { - if(!SpectateUpdate(this)) - PutObserverInServer(this); - } - } else { - if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) { - this.flags |= FL_JUMPRELEASED; - if(this.flags & FL_SPAWNING) - { - this.flags &= ~FL_SPAWNING; - LeaveSpectatorMode(this); - return; - } - } - if(!SpectateUpdate(this)) - PutObserverInServer(this); - } - - this.flags |= FL_CLIENT | FL_NOTARGET; -} - -void vehicles_enter (entity pl, entity veh); -void PlayerUseKey(entity this) -{ - if (!IS_PLAYER(this)) - return; - - if(this.vehicle) - { - if(!gameover) - { - vehicles_exit(this.vehicle, VHEF_NORMAL); - return; - } - } - else if(autocvar_g_vehicles_enter) - { - if(!STAT(FROZEN, this)) - if(!IS_DEAD(this)) - if(!gameover) - { - entity head, closest_target = NULL; - head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true); - - while(head) // find the closest acceptable target to enter - { - if(head.vehicle_flags & VHF_ISVEHICLE) - if(!IS_DEAD(head)) - if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this))) - if(head.takedamage != DAMAGE_NO) - { - if(closest_target) - { - if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin)) - { closest_target = head; } - } - else { closest_target = head; } - } - - head = head.chain; - } - - if(closest_target) { vehicles_enter(this, closest_target); return; } - } - } - - // a use key was pressed; call handlers - MUTATOR_CALLHOOK(PlayerUseKey, this); -} - - -/* -============= -PlayerPreThink - -Called every frame for each client before the physics are run -============= -*/ -.float usekeypressed; -.float last_vehiclecheck; -.int items_added; -void PlayerPreThink () -{ENGINE_EVENT(); - WarpZone_PlayerPhysics_FixVAngle(this); - - STAT(GAMESTARTTIME, this) = game_starttime; - STAT(ROUNDSTARTTIME, this) = round_starttime; - STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam; - STAT(LEADLIMIT, this) = autocvar_leadlimit; - - STAT(WEAPONSINMAP, this) = weaponsInMap; - - if (frametime) { - // physics frames: update anticheat stuff - anticheat_prethink(this); - } - - if (blockSpectators && frametime) { - // WORKAROUND: only use dropclient in server frames (frametime set). - // Never use it in cl_movement frames (frametime zero). - checkSpectatorBlock(this); - } - - zoomstate_set = false; - - // Check for nameless players - if (isInvisibleString(this.netname)) { - this.netname = strzone(sprintf("Player#%d", this.playerid)); - // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? - } - if (this.netname != this.netname_previous) { - if (autocvar_sv_eventlog) { - GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname)); - } - if (this.netname_previous) strunzone(this.netname_previous); - this.netname_previous = strzone(this.netname); - } - - // version nagging - if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) { - this.version_nagtime = 0; - if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git client - } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) { - // git server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } else { - int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion); - if (r < 0) { // old client - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } else if (r > 0) { // old server - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); - } - } - } - - // GOD MODE info - if (!(this.flags & FL_GODMODE) && this.max_armorvalue) - { - Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); - this.max_armorvalue = 0; - } - - if (STAT(FROZEN, this) == 2) - { - this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1); - this.health = max(1, this.revive_progress * start_health); - this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1); - - if (this.revive_progress >= 1) - Unfreeze(this); - } - else if (STAT(FROZEN, this) == 3) - { - this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1); - this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress ); - - if (this.health < 1) - { - if (this.vehicle) - vehicles_exit(this.vehicle, VHEF_RELEASE); - if(this.event_damage) - this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0'); - } - else if (this.revive_progress <= 0) - Unfreeze(this); - } - - MUTATOR_CALLHOOK(PlayerPreThink, this); - - if(autocvar_g_vehicles_enter) - if(time > this.last_vehiclecheck) - if(IS_PLAYER(this)) - if(!gameover) - if(!STAT(FROZEN, this)) - if(!this.vehicle) - if(!IS_DEAD(this)) - { - entity veh; - for(veh = NULL; (veh = findflags(veh, vehicle_flags, VHF_ISVEHICLE)); ) - if(vdist(veh.origin - this.origin, <, autocvar_g_vehicles_enter_radius)) - if(!IS_DEAD(veh)) - if(veh.takedamage != DAMAGE_NO) - if((veh.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(veh.owner, this)) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); - else if(!veh.owner) - if(!veh.team || SAME_TEAM(this, veh)) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); - else if(autocvar_g_vehicles_steal) - Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); - - this.last_vehiclecheck = time + 1; - } - - if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button - { - if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed) - PlayerUseKey(this); - this.usekeypressed = PHYS_INPUT_BUTTON_USE(this); - } - - if (IS_REAL_CLIENT(this)) - PrintWelcomeMessage(this); - - if (IS_PLAYER(this)) { - CheckRules_Player(this); - - if (intermission_running) { - IntermissionThink(this); - return; - } - - if (timeout_status == TIMEOUT_ACTIVE) { - // don't allow the player to turn around while game is paused - // FIXME turn this into CSQC stuff - this.v_angle = this.lastV_angle; - this.angles = this.lastV_angle; - this.fixangle = true; - } - - if (frametime) player_powerups(this); - - if (IS_DEAD(this)) { - if (this.personal && g_race_qualifying) { - if (time > this.respawn_time) { - STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second - respawn(this); - this.impulse = CHIMPULSE_SPEEDRUN.impulse; - } - } else { - if (frametime) player_anim(this); - bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)); - - if (this.deadflag == DEAD_DYING) { - if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) { - this.deadflag = DEAD_RESPAWNING; - } else if (!button_pressed) { - this.deadflag = DEAD_DEAD; - } - } else if (this.deadflag == DEAD_DEAD) { - if (button_pressed) { - this.deadflag = DEAD_RESPAWNABLE; - } else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) { - this.deadflag = DEAD_RESPAWNING; - } - } else if (this.deadflag == DEAD_RESPAWNABLE) { - if (!button_pressed) { - this.deadflag = DEAD_RESPAWNING; - } - } else if (this.deadflag == DEAD_RESPAWNING) { - if (time > this.respawn_time) { - this.respawn_time = time + 1; // only retry once a second - this.respawn_time_max = this.respawn_time; - respawn(this); - } - } - - ShowRespawnCountdown(this); - - if (this.respawn_flags & RESPAWN_SILENT) - STAT(RESPAWN_TIME, this) = 0; - else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max) - { - if (time < this.respawn_time) - STAT(RESPAWN_TIME, this) = this.respawn_time; - else if (this.deadflag != DEAD_RESPAWNING) - STAT(RESPAWN_TIME, this) = -this.respawn_time_max; - } - else - STAT(RESPAWN_TIME, this) = this.respawn_time; - } - - // if respawning, invert stat_respawn_time to indicate this, the client translates it - if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0) - STAT(RESPAWN_TIME, this) *= -1; - - return; - } - - this.prevorigin = this.origin; - - bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - if (this.hook.state) { - do_crouch = false; - } else if (this.vehicle) { - do_crouch = false; - } else if (STAT(FROZEN, this)) { - do_crouch = false; - } else if ((PS(this).m_weapon == WEP_SHOTGUN || PS(this).m_weapon == WEP_SHOCKWAVE) && this.(weaponentity).wframe == WFRAME_FIRE2 && time < this.(weaponentity).weapon_nextthink) { - // WEAPONTODO: predict - 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); - - // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers - //if(frametime) - { - this.items &= ~this.items_added; - - W_WeaponFrame(this); - - this.items_added = 0; - if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01)) - this.items_added |= IT_FUEL; - - this.items |= this.items_added; - } - - player_regen(this); - - // WEAPONTODO: Add a weapon request for this - // rot vortex charge to the charge limit - if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time) - this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1); - - if (frametime) player_anim(this); - - // secret status - secrets_setstatus(this); - - // monsters status - monsters_setstatus(this); - - this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime); - } - else if (gameover) { - if (intermission_running) IntermissionThink(this); - return; - } - else if (IS_OBSERVER(this)) { - ObserverThink(this); - } - else if (IS_SPEC(this)) { - SpectatorThink(this); - } - - // WEAPONTODO: Add weapon request for this - if (!zoomstate_set) { - SetZoomState(this, - PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) - || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX) - || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0) - ); - } - - int oldspectatee_status = this.spectatee_status; - if (IS_SPEC(this)) { - this.spectatee_status = etof(this.enemy); - } else if (IS_OBSERVER(this)) { - this.spectatee_status = etof(this); - } else { - this.spectatee_status = 0; - } - if (this.spectatee_status != oldspectatee_status) { - ClientData_Touch(this); - if (g_race || g_cts) race_InitSpectator(); - } - - if (this.teamkill_soundtime && time > this.teamkill_soundtime) - { - this.teamkill_soundtime = 0; - - entity e = this.teamkill_soundsource; - entity oldpusher = e.pusher; - e.pusher = this; - PlayerSound(e, playersound_teamshoot, CH_VOICE, VOICETYPE_LASTATTACKER_ONLY); - e.pusher = oldpusher; - } - - if (this.taunt_soundtime && time > this.taunt_soundtime) { - this.taunt_soundtime = 0; - PlayerSound(this, playersound_taunt, CH_VOICE, VOICETYPE_AUTOTAUNT); - } - - target_voicescript_next(this); - - // WEAPONTODO: Move into weaponsystem somehow - // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring - if (PS(this).m_weapon == WEP_Null) - this.clip_load = this.clip_size = 0; -} - -void DrownPlayer(entity this) -{ - if(IS_DEAD(this)) - return; - - if (this.waterlevel != WATERLEVEL_SUBMERGED) - { - if(this.air_finished < time) - PlayerSound(this, playersound_gasp, CH_PLAYER, VOICETYPE_PLAYERSOUND); - this.air_finished = time + autocvar_g_balance_contents_drowndelay; - this.dmg = 2; - } - else if (this.air_finished < time) - { // drown! - if (this.pain_finished < time) - { - Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0'); - this.pain_finished = time + 0.5; - } - } -} - -/* -============= -PlayerPostThink - -Called every frame for each client after the physics are run -============= -*/ -.float idlekick_lasttimeleft; -void PlayerPostThink () -{ENGINE_EVENT(); - if (sv_maxidle > 0) - if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). - if (IS_REAL_CLIENT(this)) - if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle) - { - if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10 - { - if (this.idlekick_lasttimeleft) - { - this.idlekick_lasttimeleft = 0; - Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING); - } - } - else - { - float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince)); - if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10 - if (!this.idlekick_lasttimeleft) - Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft); - } - if (timeleft <= 0) { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname); - dropclient(this); - return; - } - else if (timeleft <= 10) { - if (timeleft != this.idlekick_lasttimeleft) { - Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft)); - } - this.idlekick_lasttimeleft = timeleft; - } - } - } - - CheatFrame(this); - - //CheckPlayerJump(); - - if (IS_PLAYER(this)) { - DrownPlayer(this); - CheckRules_Player(this); - UpdateChatBubble(this); - if (this.impulse) ImpulseCommands(this); - if (intermission_running) return; // intermission or finale - GetPressedKeys(this); - } - - if (this.waypointsprite_attachedforcarrier) { - vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); - WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); - } - - playerdemo_write(this); - - CSQCMODEL_AUTOUPDATE(this); -} diff --git a/qcsrc/server/cl_client.qh b/qcsrc/server/cl_client.qh deleted file mode 100644 index d540bb16ac..0000000000 --- a/qcsrc/server/cl_client.qh +++ /dev/null @@ -1,109 +0,0 @@ -#pragma once - -void ClientState_attach(entity this); - -CLASS(Client, Object) - /** Client name */ - ATTRIB(Client, netname, string, this.netname) - ATTRIB(Client, colormap, int, this.colormap) - ATTRIB(Client, team, int, this.team) - ATTRIB(Client, clientcolors, int, this.clientcolors) - /** Client IP */ - ATTRIB(Client, netaddress, string, this.netaddress) - ATTRIB(Client, playermodel, string, this.playermodel) - ATTRIB(Client, playerskin, int, this.playerskin) - - /** fingerprint of CA key the player used to authenticate */ - ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp) - /** fingerprint of CA key the server used to authenticate to the player */ - ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp) - /** fingerprint of ID used by the player entity, or string_null if not identified */ - ATTRIB(Client, crypto_idfp, string, this.crypto_idfp) - /** set if the player's ID has been signed */ - ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed) - /** the string "AES128" if encrypting, and string_null if plaintext */ - ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod) - /** the string "HMAC-SHA256" if signing, and string_null if plaintext */ - ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod) - - // custom - - ATTRIB(Client, playerid, int, this.playerid) - - METHOD(Client, m_unwind, bool(Client this)); - - STATIC_METHOD(Client, Add, void(Client this, int _team)); - STATIC_METHOD(Client, Remove, void(Client this)); - - INIT(Client) { - if (this.m_unwind(this)) return this; - make_impure(this); - this.classname = "player_joining"; - static int playerid_last; - this.playerid = ++playerid_last; - ClientState_attach(this); - } - DESTRUCTOR(Client) { - Client_Remove(this); - } - CONSTRUCTOR(Client, string name) { - CONSTRUCT(Client); - this.netname = name; - this.netaddress = "local"; - this.playermodel = "models/player/megaerebus.iqm"; - } -ENDCLASS(Client) - -CLASS(Observer, Client) - INIT(Observer) { - this.classname = STR_OBSERVER; - } - DESTRUCTOR(Observer) { } -ENDCLASS(Observer) - -CLASS(Spectator, Client) - INIT(Spectator) { - this.classname = STR_SPECTATOR; - } - DESTRUCTOR(Spectator) { } -ENDCLASS(Spectator) - -CLASS(Player, Client) - INIT(Player) { - this.classname = STR_PLAYER; - } - DESTRUCTOR(Player) { } -ENDCLASS(Player) - -METHOD(Client, m_unwind, bool(Client this)) -{ - TC(Client, this); - #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END - switch (this.classname) { - case "Observer": - UNWIND(Spectator); - UNWIND(Player); - return true; - case "Spectator": - UNWIND(Observer); - UNWIND(Player); - return true; - case "Player": - UNWIND(Observer); - UNWIND(Spectator); - return true; - } - #undef UNWIND - return false; -} - -float c1, c2, c3, c4; - -void play_countdown(entity this, float finished, Sound samp); - -float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit); - -bool Spectate(entity this, entity pl); - -#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee) -#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } diff --git a/qcsrc/server/cl_impulse.qc b/qcsrc/server/cl_impulse.qc deleted file mode 100644 index 517ee37961..0000000000 --- a/qcsrc/server/cl_impulse.qc +++ /dev/null @@ -1,607 +0,0 @@ -#include "cl_impulse.qh" -#include "round_handler.qh" - -#include "bot/waypoints.qh" - -#include "weapons/throwing.qh" -#include "command/common.qh" -#include "cheats.qh" -#include "bot/navigation.qh" -#include "weapons/selection.qh" -#include "weapons/tracing.qh" -#include "weapons/weaponsystem.qh" - -#include - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/weapons/all.qh" -#include "../common/vehicles/sv_vehicles.qh" - -#include "../common/mutators/mutator/waypoints/waypointsprites.qh" - -.entity vehicle; - -#define IMPULSE(id) _IMPULSE(IMP_##id) -#define _IMPULSE(id) \ - void id##_handle(entity this); \ - STATIC_INIT_LATE(id) \ - { \ - id.impulse_handle = id##_handle; \ - } \ - void id##_handle(entity this) - -/** - * Impulse map: - * - * 0 reserved (no input) - * - * 99: loaded - * - * 140: moving clone - * 141: ctf speedrun - * 142: fixed clone - * 143: emergency teleport - * 148: unfairly eliminate - * - * TODO: - * 200 to 209: prev weapon shortcuts - * 210 to 219: best weapon shortcuts - * 220 to 229: next weapon shortcuts - * 230 to 253: individual weapons (up to 24) - */ - -// weapon switching impulses - -#define X(slot) \ - IMPULSE(weapon_group_##slot) \ - { \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_group_##slot.impulse; \ - return; \ - } \ - W_NextWeaponOnImpulse(this, slot); \ - } -X(1) -X(2) -X(3) -X(4) -X(5) -X(6) -X(7) -X(8) -X(9) -X(0) -#undef X - -// custom order weapon cycling - -#define X(slot, dir) \ - IMPULSE(weapon_priority_##slot##_##dir) \ - { \ - if (this.vehicle) return; \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \ - return; \ - } \ - noref int prev = -1; \ - noref int best = 0; \ - noref int next = +1; \ - W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \ - } -X(0, prev) -X(1, prev) -X(2, prev) -X(3, prev) -X(4, prev) -X(5, prev) -X(6, prev) -X(7, prev) -X(8, prev) -X(9, prev) - -X(0, best) -X(1, best) -X(2, best) -X(3, best) -X(4, best) -X(5, best) -X(6, best) -X(7, best) -X(8, best) -X(9, best) - -X(0, next) -X(1, next) -X(2, next) -X(3, next) -X(4, next) -X(5, next) -X(6, next) -X(7, next) -X(8, next) -X(9, next) -#undef X - -// direct weapons - -#define X(i) \ - IMPULSE(weapon_byid_##i) \ - { \ - if (this.vehicle) return; \ - if (IS_DEAD(this)) \ - { \ - this.impulse = IMP_weapon_byid_##i.impulse; \ - return; \ - } \ - W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \ - } -X(0) -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) -X(18) -X(19) -X(20) -X(21) -X(22) -X(23) -#undef X - -IMPULSE(weapon_next_byid) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_byid.impulse; - return; - } - W_NextWeapon(this, 0); -} - -IMPULSE(weapon_prev_byid) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_byid.impulse; - return; - } - W_PreviousWeapon(this, 0); -} - -IMPULSE(weapon_next_bygroup) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_bygroup.impulse; - return; - } - W_NextWeapon(this, 1); -} - -IMPULSE(weapon_prev_bygroup) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_bygroup.impulse; - return; - } - W_PreviousWeapon(this, 1); -} - -IMPULSE(weapon_next_bypriority) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_next_bypriority.impulse; - return; - } - W_NextWeapon(this, 2); -} - -IMPULSE(weapon_prev_bypriority) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) - { - this.impulse = IMP_weapon_prev_bypriority.impulse; - return; - } - W_PreviousWeapon(this, 2); -} - -IMPULSE(weapon_last) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_LastWeapon(this); -} - -IMPULSE(weapon_best) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_SwitchWeapon(this, w_getbestweapon(this)); -} - -IMPULSE(weapon_drop) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - W_ThrowWeapon(this, W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true); -} - -IMPULSE(weapon_reload) -{ - if (this.vehicle) return; - if (IS_DEAD(this)) return; - if (forbidWeaponUse(this)) return; - Weapon w = PS(this).m_weapon; - entity actor = this; - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - w.wr_reload(w, actor, weaponentity); -} - -void ImpulseCommands(entity this) -{ - if (gameover) return; - - int imp = this.impulse; - if (!imp) return; - this.impulse = 0; - - if (MinigameImpulse(this, imp)) return; - - if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused - - // allow only weapon change impulses when not in round time - if (round_handler_IsActive() && !round_handler_IsRoundStarted()) - { - #define X(id) case IMP_##id.impulse: - switch (imp) - { - X(weapon_group_0) - X(weapon_group_1) - X(weapon_group_2) - X(weapon_group_3) - X(weapon_group_4) - X(weapon_group_5) - X(weapon_group_6) - X(weapon_group_7) - X(weapon_group_8) - X(weapon_group_9) - X(weapon_next_byid) - X(weapon_prev_byid) - X(weapon_next_bygroup) - X(weapon_prev_bygroup) - X(weapon_next_bypriority) - X(weapon_prev_bypriority) - X(weapon_last) - X(weapon_best) - X(weapon_reload) - X(weapon_priority_0_prev) - X(weapon_priority_1_prev) - X(weapon_priority_2_prev) - X(weapon_priority_3_prev) - X(weapon_priority_4_prev) - X(weapon_priority_5_prev) - X(weapon_priority_6_prev) - X(weapon_priority_7_prev) - X(weapon_priority_8_prev) - X(weapon_priority_9_prev) - X(weapon_priority_0_next) - X(weapon_priority_1_next) - X(weapon_priority_2_next) - X(weapon_priority_3_next) - X(weapon_priority_4_next) - X(weapon_priority_5_next) - X(weapon_priority_6_next) - X(weapon_priority_7_next) - X(weapon_priority_8_next) - X(weapon_priority_9_next) - X(weapon_priority_0_best) - X(weapon_priority_1_best) - X(weapon_priority_2_best) - X(weapon_priority_3_best) - X(weapon_priority_4_best) - X(weapon_priority_5_best) - X(weapon_priority_6_best) - X(weapon_priority_7_best) - X(weapon_priority_8_best) - X(weapon_priority_9_best) - X(weapon_byid_0) - X(weapon_byid_1) - X(weapon_byid_2) - X(weapon_byid_3) - X(weapon_byid_4) - X(weapon_byid_5) - X(weapon_byid_6) - X(weapon_byid_7) - X(weapon_byid_8) - X(weapon_byid_9) - X(weapon_byid_10) - X(weapon_byid_11) - X(weapon_byid_12) - X(weapon_byid_13) - X(weapon_byid_14) - X(weapon_byid_15) - X(weapon_byid_16) - X(weapon_byid_17) - X(weapon_byid_18) - X(weapon_byid_19) - X(weapon_byid_20) - X(weapon_byid_21) - X(weapon_byid_22) - X(weapon_byid_23) - break; - default: return; - } -#undef X - } - - if (vehicle_impulse(this, imp)) return; - - if (CheatImpulse(this, imp)) return; - - FOREACH(IMPULSES, it.impulse == imp, { - void(entity) f = it.impulse_handle; - if (!f) continue; - f(this); - return; - }); -} - -IMPULSE(use) -{ - PlayerUseKey(this); -} - -IMPULSE(waypoint_personal_here) -{ - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at location\n"); -} - -IMPULSE(waypoint_personal_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at crosshair\n"); -} - -IMPULSE(waypoint_personal_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "personal waypoint spawned at death location\n"); -} - -IMPULSE(waypoint_here_follow) -{ - if (!teamplay) return; - if (IS_DEAD(this)) return; - if (!MUTATOR_CALLHOOK(HelpMePing, this)) - { - entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME); - if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier); - else WaypointSprite_Ping(wp); - } - sprint(this, "HELP ME attached\n"); -} - -IMPULSE(waypoint_here_here) -{ - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at location\n"); -} - -IMPULSE(waypoint_here_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at crosshair\n"); -} - -IMPULSE(waypoint_here_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "HERE spawned at death location\n"); -} - -IMPULSE(waypoint_danger_here) -{ - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at location\n"); -} - -IMPULSE(waypoint_danger_crosshair) -{ - WarpZone_crosshair_trace(this); - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at crosshair\n"); -} - -IMPULSE(waypoint_danger_death) -{ - if (!this.death_origin) return; - entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER); - if (wp) WaypointSprite_Ping(wp); - sprint(this, "DANGER spawned at death location\n"); -} - -IMPULSE(waypoint_clear_personal) -{ - WaypointSprite_ClearPersonal(this); - if (this.personal) - { - remove(this.personal); - this.personal = NULL; - } - sprint(this, "personal waypoint cleared\n"); -} - -IMPULSE(waypoint_clear) -{ - WaypointSprite_ClearOwned(this); - if (this.personal) - { - remove(this.personal); - this.personal = NULL; - } - sprint(this, "all waypoints cleared\n"); -} - -IMPULSE(navwaypoint_spawn) -{ - if (!autocvar_g_waypointeditor) return; - waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0)); - bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n")); -} - -IMPULSE(navwaypoint_remove) -{ - if (!autocvar_g_waypointeditor) return; - entity e = navigation_findnearestwaypoint(this, false); - if (!e) return; - if (e.wpflags & WAYPOINTFLAG_GENERATED) return; - bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n")); - waypoint_remove(e); -} - -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; - for (entity e = findchain(classname, "waypoint"); e; e = e.chain) - { - e.colormod = '0.5 0.5 0.5'; - e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); - } - entity e2 = navigation_findnearestwaypoint(this, false); - navigation_markroutes(this, e2); - - int i, m; - - i = 0; - m = 0; - for (entity e = findchain(classname, "waypoint"); e; e = e.chain) - { - if (e.wpcost < 10000000) continue; - LOG_INFO("unreachable: ", etos(e), " ", vtos(e.origin), "\n"); - e.colormod_z = 8; - e.effects |= EF_NODEPTHTEST | EF_BLUE; - ++i; - ++m; - } - if (i) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", i); - navigation_markroutes_inverted(e2); - - i = 0; - for (entity e = findchain(classname, "waypoint"); e; e = e.chain) - { - if (e.wpcost < 10000000) continue; - LOG_INFO("cannot reach me: ", etos(e), " ", vtos(e.origin), "\n"); - e.colormod_x = 8; - if (!(e.effects & EF_NODEPTHTEST)) // not already reported before - ++m; - e.effects |= EF_NODEPTHTEST | EF_RED; - ++i; - } - if (i) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", i); - if (m) LOG_INFOF("%d waypoints have been marked total\n", m); - - i = 0; - for (entity e = findchain(classname, "info_player_deathmatch"); e; e = e.chain) - { - vector org = e.origin; - tracebox(e.origin, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), e.origin - '0 0 512', MOVE_NOMONSTERS, NULL); - setorigin(e, trace_endpos); - if (navigation_findnearestwaypoint(e, false)) - { - setorigin(e, org); - e.effects &= ~EF_NODEPTHTEST; - e.model = ""; - } - else - { - setorigin(e, org); - LOG_INFO("spawn without waypoint: ", etos(e), " ", vtos(e.origin), "\n"); - e.effects |= EF_NODEPTHTEST; - _setmodel(e, this.model); - e.frame = this.frame; - e.skin = this.skin; - e.colormod = '8 0.5 8'; - setsize(e, '0 0 0', '0 0 0'); - ++i; - } - } - if (i) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", i); - - i = 0; - entity start = findchainflags(flags, FL_ITEM); - for (entity e = start; e; e = e.chain) - { - e.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); - e.colormod = '0.5 0.5 0.5'; - } - for (entity e = start; e; e = e.chain) - { - if (navigation_findnearestwaypoint(e, false)) continue; - LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n"); - e.effects |= EF_NODEPTHTEST | EF_RED; - e.colormod_x = 8; - ++i; - } - if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", i); - - i = 0; - for (entity e = start; e; e = e.chain) - { - if (navigation_findnearestwaypoint(e, true)) continue; - LOG_INFO("item without waypoint: ", etos(e), " ", vtos(e.origin), "\n"); - e.effects |= EF_NODEPTHTEST | EF_BLUE; - e.colormod_z = 8; - ++i; - } - if (i) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", i); -} diff --git a/qcsrc/server/cl_impulse.qh b/qcsrc/server/cl_impulse.qh deleted file mode 100644 index 50edc2c9c5..0000000000 --- a/qcsrc/server/cl_impulse.qh +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void ImpulseCommands(entity this); diff --git a/qcsrc/server/cl_player.qc b/qcsrc/server/cl_player.qc deleted file mode 100644 index 4c54dddaa6..0000000000 --- a/qcsrc/server/cl_player.qc +++ /dev/null @@ -1,952 +0,0 @@ -#include "cl_player.qh" - -#include "bot/bot.qh" -#include "cheats.qh" -#include "g_damage.qh" -#include "g_subs.qh" -#include "miscfunctions.qh" -#include "portals.qh" -#include "teamplay.qh" -#include "weapons/throwing.qh" -#include "command/common.qh" -#include "../common/state.qh" -#include "../common/anim.qh" -#include "../common/animdecide.qh" -#include "../common/csqcmodel_settings.qh" -#include "../common/deathtypes/all.qh" -#include "../common/triggers/subs.qh" -#include "../common/playerstats.qh" -#include "../lib/csqcmodel/sv_model.qh" - -#include "../common/minigames/sv_minigames.qh" - -#include "../common/physics/player.qh" -#include "../common/effects/qc/all.qh" -#include "../common/mutators/mutator/waypoints/waypointsprites.qh" -#include "../common/triggers/include.qh" - -#include "weapons/weaponstats.qh" - -#include "../common/animdecide.qh" - -void Drop_Special_Items(entity player) -{ - // called when the player has become stuck or frozen - // so objective items aren't stuck with the player - - MUTATOR_CALLHOOK(DropSpecialItems, player); -} - -void CopyBody_Think(entity this) -{ - if(this.CopyBody_nextthink && time > this.CopyBody_nextthink) - { - this.CopyBody_think(this); - if(wasfreed(this)) - return; - this.CopyBody_nextthink = this.nextthink; - this.CopyBody_think = getthink(this); - setthink(this, CopyBody_Think); - } - CSQCMODEL_AUTOUPDATE(this); - this.nextthink = time; -} -void CopyBody(entity this, float keepvelocity) -{ - if (this.effects & EF_NODRAW) - return; - entity clone = new(body); - clone.enemy = this; - clone.lip = this.lip; - clone.colormap = this.colormap; - clone.iscreature = this.iscreature; - clone.teleportable = this.teleportable; - clone.damagedbycontents = this.damagedbycontents; - clone.angles = this.angles; - clone.v_angle = this.v_angle; - clone.avelocity = this.avelocity; - clone.damageforcescale = this.damageforcescale; - clone.effects = this.effects; - clone.glowmod = this.glowmod; - clone.event_damage = this.event_damage; - clone.anim_state = this.anim_state; - clone.anim_time = this.anim_time; - clone.anim_lower_action = this.anim_lower_action; - clone.anim_lower_time = this.anim_lower_time; - clone.anim_upper_action = this.anim_upper_action; - clone.anim_upper_time = this.anim_upper_time; - clone.anim_implicit_state = this.anim_implicit_state; - clone.anim_implicit_time = this.anim_implicit_time; - clone.anim_lower_implicit_action = this.anim_lower_implicit_action; - clone.anim_lower_implicit_time = this.anim_lower_implicit_time; - clone.anim_upper_implicit_action = this.anim_upper_implicit_action; - clone.anim_upper_implicit_time = this.anim_upper_implicit_time; - clone.dphitcontentsmask = this.dphitcontentsmask; - clone.death_time = this.death_time; - clone.pain_finished = this.pain_finished; - clone.health = this.health; - clone.armorvalue = this.armorvalue; - clone.armortype = this.armortype; - clone.model = this.model; - clone.modelindex = this.modelindex; - clone.skin = this.skin; - clone.species = this.species; - clone.movetype = this.movetype; - clone.solid = this.solid; - clone.ballistics_density = this.ballistics_density; - clone.takedamage = this.takedamage; - setcefc(clone, getcefc(this)); - clone.uncustomizeentityforclient = this.uncustomizeentityforclient; - clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set; - if (keepvelocity == 1) - clone.velocity = this.velocity; - clone.oldvelocity = clone.velocity; - clone.alpha = this.alpha; - clone.fade_time = this.fade_time; - clone.fade_rate = this.fade_rate; - //clone.weapon = this.weapon; - setorigin(clone, this.origin); - setsize(clone, this.mins, this.maxs); - clone.prevorigin = this.origin; - clone.reset = SUB_Remove; - clone._ps = this._ps; - - Drag_MoveDrag(this, clone); - - if(clone.colormap <= maxclients && clone.colormap > 0) - clone.colormap = 1024 + this.clientcolors; - - CSQCMODEL_AUTOINIT(clone); - clone.CopyBody_nextthink = this.nextthink; - clone.CopyBody_think = getthink(this); - clone.nextthink = time; - setthink(clone, CopyBody_Think); - // "bake" the current animation frame for clones (they don't get clientside animation) - animdecide_load_if_needed(clone); - animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time); -} - -void player_setupanimsformodel(entity this) -{ - // load animation info - animdecide_load_if_needed(this); - animdecide_setstate(this, 0, false); -} - -void player_anim(entity this) -{ - int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); - if(IS_DEAD(this)) { - if (!deadbits) { - // Decide on which death animation to use. - if(random() < 0.5) - deadbits = ANIMSTATE_DEAD1; - else - deadbits = ANIMSTATE_DEAD2; - } - } else { - // Clear a previous death animation. - deadbits = 0; - } - int animbits = deadbits; - if(STAT(FROZEN, this)) - animbits |= ANIMSTATE_FROZEN; - if(this.movetype == MOVETYPE_FOLLOW) - animbits |= ANIMSTATE_FOLLOW; - if(this.crouch) - animbits |= ANIMSTATE_DUCK; - animdecide_setstate(this, animbits, false); - animdecide_setimplicitstate(this, IS_ONGROUND(this)); -} - -void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - float take, save; - vector v; - Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); - - // damage resistance (ignore most of the damage from a bullet or similar) - damage = max(damage - 5, 1); - - v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); - take = v.x; - save = v.y; - - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if (save > 10) - sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); - else if (take > 30) - sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); - else if (take > 10) - sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); - } - - if (take > 50) - Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); - if (take > 100) - Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; - // pause regeneration for 5 seconds - this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); - - this.dmg_save = this.dmg_save + save;//max(save - 10, 0); - this.dmg_take = this.dmg_take + take;//max(take - 10, 0); - this.dmg_inflictor = inflictor; - - if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) - { - // don't use any animations as a gib - this.frame = 0; - // view just above the floor - this.view_ofs = '0 0 4'; - - Violence_GibSplash(this, 1, 1, attacker); - this.alpha = -1; - this.solid = SOLID_NOT; // restore later - this.takedamage = DAMAGE_NO; // restore later - this.damagedbycontents = false; - } -} - -void calculate_player_respawn_time(entity this) -{ - if(g_ca) - return; - - float gametype_setting_tmp; - float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); - float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); - float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large); - float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count); - float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count); - float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves); - - float pcount = 1; // Include myself whether or not team is already set right and I'm a "player". - if (teamplay) - { - FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( - if(it.team == this.team) - ++pcount; - )); - if (sdelay_small_count == 0) - sdelay_small_count = 1; - if (sdelay_large_count == 0) - sdelay_large_count = 1; - } - else - { - FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( - ++pcount; - )); - if (sdelay_small_count == 0) - { - if (g_cts) - { - // Players play independently. No point in requiring enemies. - sdelay_small_count = 1; - } - else - { - // Players play AGAINST each other. Enemies required. - sdelay_small_count = 2; - } - } - if (sdelay_large_count == 0) - { - if (g_cts) - { - // Players play independently. No point in requiring enemies. - sdelay_large_count = 1; - } - else - { - // Players play AGAINST each other. Enemies required. - sdelay_large_count = 2; - } - } - } - - float sdelay; - - if (pcount <= sdelay_small_count) - sdelay = sdelay_small; - else if (pcount >= sdelay_large_count) - sdelay = sdelay_large; - else // NOTE: this case implies sdelay_large_count > sdelay_small_count. - sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count); - - if(waves) - this.respawn_time = ceil((time + sdelay) / waves) * waves; - else - this.respawn_time = time + sdelay; - - if(sdelay < sdelay_max) - this.respawn_time_max = time + sdelay_max; - else - this.respawn_time_max = this.respawn_time; - - if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75)) - this.respawn_countdown = 10; // first number to count down from is 10 - else - this.respawn_countdown = -1; // do not count down - - if(autocvar_g_forced_respawn) - this.respawn_flags = this.respawn_flags | RESPAWN_FORCE; -} - -void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) -{ - float take, save, dh, da; - vector v; - float valid_damage_for_weaponstats; - float excess; - - dh = max(this.health, 0); - da = max(this.armorvalue, 0); - - if(!DEATH_ISSPECIAL(deathtype)) - { - damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0)); - if(this != attacker) - damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0)); - } - - if(DEATH_ISWEAPON(deathtype, WEP_TUBA)) - { - // tuba causes blood to come out of the ears - vector ear1, ear2; - vector d; - float f; - ear1 = this.origin; - ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8 - ear2 = ear1; - makevectors(this.angles); - ear1 += v_right * -10; - ear2 += v_right * +10; - d = inflictor.origin - this.origin; - if (d) - f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right! - else - f = 0; // Assum ecenter. - force = v_right * vlen(force); - Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker); - Violence_GibSplash_At(ear2, force, 2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker); - if(f > 0) - { - hitloc = ear1; - force = force * -1; - } - else - { - hitloc = ear2; - // force is already good - } - } - 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); - take = v.x; - save = v.y; - - if(attacker == this) - { - // don't reset pushltime for this damage as it may be an attempt to - // escape a lava pit or similar - //this.pushltime = 0; - this.istypefrag = 0; - } - else if(IS_PLAYER(attacker)) - { - this.pusher = attacker; - this.pushltime = time + autocvar_g_maxpushtime; - this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this); - } - else if(time < this.pushltime) - { - attacker = this.pusher; - this.pushltime = max(this.pushltime, time + 0.6); - } - else - { - this.pushltime = 0; - this.istypefrag = 0; - } - - if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1) - { - vector v = healtharmor_applydamage(this.armorvalue, max(0, autocvar_g_spawnshield_blockdamage), deathtype, damage); - take = v.x; - save = v.y; - } - - 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); - excess = max(0, damage - take - save); - - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if (save > 10) - sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); - else if (take > 30) - sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); - else if (take > 10) - sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them? - } - - if (take > 50) - Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); - if (take > 100) - Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); - - if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1) - { - if (!(this.flags & FL_GODMODE)) - { - this.armorvalue = this.armorvalue - save; - this.health = this.health - take; - // pause regeneration for 5 seconds - if(take) - this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); - - if (time > this.pain_finished) //Don't switch pain sequences like crazy - { - this.pain_finished = time + 0.5; //Supajoe - - if(autocvar_sv_gentle < 1) { - if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models - { - if (!this.animstate_override) - { - if (random() > 0.5) - animdecide_setaction(this, ANIMACTION_PAIN1, true); - else - animdecide_setaction(this, ANIMACTION_PAIN2, true); - } - } - - if(sound_allowed(MSG_BROADCAST, attacker)) - if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser - 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, VOICETYPE_PLAYERSOUND); - else if(this.health > 75) - PlayerSound(this, playersound_pain100, CH_PAIN, VOICETYPE_PLAYERSOUND); - else if(this.health > 50) - PlayerSound(this, playersound_pain75, CH_PAIN, VOICETYPE_PLAYERSOUND); - else if(this.health > 25) - PlayerSound(this, playersound_pain50, CH_PAIN, VOICETYPE_PLAYERSOUND); - else - PlayerSound(this, playersound_pain25, CH_PAIN, VOICETYPE_PLAYERSOUND); - } - } - } - - // throw off bot aim temporarily - float shake; - if(IS_BOT_CLIENT(this) && this.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); - } - } - else - this.max_armorvalue += (save + take); - } - this.dmg_save = this.dmg_save + save;//max(save - 10, 0); - 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)) { - PlayerScore_Add(attacker, SP_DMG, realdmg); - } - if (IS_PLAYER(this)) { - PlayerScore_Add(this, SP_DMGTAKEN, realdmg); - } - } - - bool abot = (IS_BOT_CLIENT(attacker)); - bool vbot = (IS_BOT_CLIENT(this)); - - valid_damage_for_weaponstats = 0; - Weapon awep = WEP_Null; - - if(vbot || IS_REAL_CLIENT(this)) - if(abot || IS_REAL_CLIENT(attacker)) - if(attacker && this != attacker) - if(DIFF_TEAM(this, attacker)) - { - if(DEATH_ISSPECIAL(deathtype)) - awep = PS(attacker).m_weapon; - else - awep = DEATH_WEAPONOF(deathtype); - valid_damage_for_weaponstats = 1; - } - - if(valid_damage_for_weaponstats) - { - dh = dh - max(this.health, 0); - da = da - max(this.armorvalue, 0); - WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da); - MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype); - } - - if (this.health < 1) - { - float defer_ClientKill_Now_TeamChange; - defer_ClientKill_Now_TeamChange = false; - - if(this.alivetime) - { - PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); - this.alivetime = 0; - } - - if(valid_damage_for_weaponstats) - WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot); - - if(autocvar_sv_gentle < 1) - if(sound_allowed(MSG_BROADCAST, attacker)) - { - if(deathtype == DEATH_DROWN.m_id) - PlayerSound(this, playersound_drown, CH_PAIN, VOICETYPE_PLAYERSOUND); - else - PlayerSound(this, playersound_death, CH_PAIN, VOICETYPE_PLAYERSOUND); - } - - // get rid of kill indicator - if(this.killindicator) - { - remove(this.killindicator); - this.killindicator = NULL; - if(this.killindicator_teamchange) - defer_ClientKill_Now_TeamChange = true; - - if(this.classname == "body") - if(deathtype == DEATH_KILL.m_id) - { - // for the lemmings fans, a small harmless explosion - Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); - } - } - - // print an obituary message - if(this.classname != "body") - Obituary (attacker, inflictor, this, deathtype); - - // increment frag counter for used weapon type - Weapon w = DEATH_WEAPONOF(deathtype); - if(w != WEP_Null) - if(accuracy_isgooddamage(attacker, this)) - attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1; - - MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage); - excess = M_ARGV(4, float); - - Weapon wep = PS(this).m_weapon; - wep.wr_playerdeath(wep, this); - - RemoveGrapplingHook(this); - - Portal_ClearAllLater(this); - - this.fixangle = true; - - if(defer_ClientKill_Now_TeamChange) - ClientKill_Now_TeamChange(this); // can turn player into spectator - - // 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")) - return; - - // 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 - - // clear waypoints - WaypointSprite_PlayerDead(this); - // throw a weapon - SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id); - - // become fully visible - this.alpha = default_player_alpha; - // make the corpse upright (not tilted) - this.angles_x = 0; - this.angles_z = 0; - // don't spin - this.avelocity = '0 0 0'; - // view from the floor - this.view_ofs = '0 0 -8'; - // toss the corpse - this.movetype = MOVETYPE_TOSS; - // shootable corpse - this.solid = SOLID_CORPSE; - this.ballistics_density = autocvar_g_ballistics_density_corpse; - // don't stick to the floor - UNSET_ONGROUND(this); - // dying animation - this.deadflag = DEAD_DYING; - - // when to allow respawn - calculate_player_respawn_time(this); - - this.death_time = time; - if (random() < 0.5) - animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true); - else - animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true); - if (this.maxs.z > 5) - { - this.maxs_z = 5; - setsize(this, this.mins, this.maxs); - } - // set damage function to corpse damage - this.event_damage = PlayerCorpseDamage; - // call the corpse damage function just in case it wants to gib - this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force); - - // set up to fade out later - SUB_SetFade (this, time + 6 + random (), 1); - // reset body think wrapper broken by SUB_SetFade - if(this.classname == "body" && getthink(this) != CopyBody_Think) { - this.CopyBody_think = getthink(this); - this.CopyBody_nextthink = this.nextthink; - setthink(this, CopyBody_Think); - this.nextthink = time; - } - - if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") { - // remove corpse - // clones don't run any animation code any more, so we must gib them when they die :( - PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force); - } - - // reset fields the weapons may use just in case - FOREACH(Weapons, it != WEP_Null, LAMBDA( - it.wr_resetplayer(it, this); - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0; - } - )); - } -} - -void MoveToTeam(entity client, int team_colour, int type) -{ - int lockteams_backup = lockteams; // backup any team lock - lockteams = 0; // disable locked teams - TeamchangeFrags(client); // move the players frags - SetPlayerColors(client, team_colour - 1); // set the players colour - Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player - lockteams = lockteams_backup; // restore the team lock - LogTeamchange(client.playerid, client.team, type); -} - -/** print(), but only print if the server is not local */ -void dedicated_print(string input) -{ - if (server_is_dedicated) print(input); -} - -/** - * 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) if (substring(msgin, 0, 1) == " ") - msgin = substring(msgin, 1, -1); // work around DP say bug (say_team does not have this!) - - 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(intermission_running) - 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 = autocvar_g_chat_teamcolors ? playername(source) : source.netname; - - 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); - if(autocvar_g_chat_teamcolors) - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7"); - else - privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^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) - { - 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 (!intermission_running) - if(teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !(warmup_stage || gameover))) - teamsay = -1; // spectators - } - - if(flood) - LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n"); - - // 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.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(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); - sprint(privatesay, msgstr); - if (!autocvar_g_chat_tellprivacy) { dedicated_print(msgstr); } // send to server console too if "tellprivacy" is disabled - if(cmsgstr != "") - centerprint(privatesay, cmsgstr); - } - else if ( teamsay && source.active_minigame ) - { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == source.active_minigame, 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, { - 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, sprint(it, msgstr)); - } - else - { - if (source) { - sprint(source, sourcemsgstr); - dedicated_print(msgstr); // send to server console too - MX_Say(strcat(playername(source), "^7: ", msgin)); - } - FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source, sprint(it, msgstr)); - } - } - - return ret; -} diff --git a/qcsrc/server/cl_player.qh b/qcsrc/server/cl_player.qh deleted file mode 100644 index b5f8ca07c8..0000000000 --- a/qcsrc/server/cl_player.qh +++ /dev/null @@ -1,39 +0,0 @@ -#pragma once - -.entity pusher; -.float pushltime; -.float istypefrag; - -.float CopyBody_nextthink; -.void(entity this) CopyBody_think; -void CopyBody_Think(entity this); -void CopyBody(entity this, float keepvelocity); - -void player_setupanimsformodel(entity this); - -void player_anim(entity this); - -void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); - -// g__str: -// If 0, default is used. -// If <0, 0 is used. -// Otherwise, g_str (default value) is used. -// For consistency, negative values there are mapped to zero too. -#define GAMETYPE_DEFAULTED_SETTING(str) \ - ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ - (gametype_setting_tmp < 0) ? 0 \ - : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \ - : gametype_setting_tmp) - -void calculate_player_respawn_time(entity this); - -void ClientKill_Now_TeamChange(entity this); - -void MoveToTeam(entity client, float team_colour, float type); - -void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); - -/** to be used by `prvm_edictset server playernumber muted 1` */ -.float muted; -int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc new file mode 100644 index 0000000000..d4a3e9febf --- /dev/null +++ b/qcsrc/server/client.qc @@ -0,0 +1,2648 @@ +#include "client.qh" + +#include "anticheat.qh" +#include "impulse.qh" +#include "player.qh" +#include "ipban.qh" +#include "miscfunctions.qh" +#include "portals.qh" +#include "teamplay.qh" +#include "playerdemo.qh" +#include "spawnpoints.qh" +#include "g_damage.qh" +#include "g_hook.qh" +#include "command/common.qh" +#include "cheats.qh" +#include "g_world.qh" +#include "race.qh" +#include "antilag.qh" +#include "campaign.qh" +#include "command/common.qh" +#include "scores_rules.qh" + +#include "bot/api.qh" + +#include "../common/ent_cs.qh" +#include + +#include + +#include "../common/triggers/func/conveyor.qh" +#include "../common/triggers/teleporters.qh" + +#include "../common/vehicles/all.qh" + +#include "weapons/hitplot.qh" +#include "weapons/weaponsystem.qh" + +#include "../common/net_notice.qh" +#include "../common/net_linked.qh" +#include "../common/physics/player.qh" + +#include "../common/items/_mod.qh" + +#include "../common/mutators/mutator/waypoints/all.qh" + +#include "../common/triggers/subs.qh" +#include "../common/triggers/triggers.qh" +#include "../common/triggers/trigger/secret.qh" + +#include "../common/minigames/sv_minigames.qh" + +#include "../common/items/inventory.qh" + +#include "../common/monsters/sv_monsters.qh" + +#include "../lib/warpzone/server.qh" + +STATIC_METHOD(Client, Add, void(Client this, int _team)) +{ + ClientConnect(this); + TRANSMUTE(Player, this); + this.frame = 12; // 7 + this.team = _team; + PutClientInServer(this); +} + +void PutObserverInServer(entity this); + +STATIC_METHOD(Client, Remove, void(Client this)) +{ + TRANSMUTE(Observer, this); + PutClientInServer(this); + ClientDisconnect(this); +} + +void send_CSQC_teamnagger() { + WriteHeader(MSG_BROADCAST, TE_CSQC_TEAMNAGGER); +} + +int CountSpectators(entity player, entity to) +{ + if(!player) { return 0; } // not sure how, but best to be safe + + int spec_count = 0; + + FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, + { + spec_count++; + }); + + return spec_count; +} + +void WriteSpectators(entity player, entity to) +{ + if(!player) { return; } // not sure how, but best to be safe + + FOREACH_CLIENT(IS_REAL_CLIENT(it) && IS_SPEC(it) && it != to && it.enemy == player, + { + WriteByte(MSG_ENTITY, num_for_edict(it)); + }); +} + +bool ClientData_Send(entity this, entity to, int sf) +{ + assert(to == this.owner, return false); + + entity e = to; + if (IS_SPEC(e)) e = e.enemy; + + sf = 0; + if (e.race_completed) sf |= 1; // forced scoreboard + if (to.spectatee_status) sf |= 2; // spectator ent number follows + if (e.zoomstate) sf |= 4; // zoomed + if (e.porto_v_angle_held) sf |= 8; // angles held + if (autocvar_sv_showspectators) sf |= 16; // show spectators + + WriteHeader(MSG_ENTITY, ENT_CLIENT_CLIENTDATA); + WriteByte(MSG_ENTITY, sf); + + if (sf & 2) + { + WriteByte(MSG_ENTITY, to.spectatee_status); + } + if (sf & 8) + { + WriteAngle(MSG_ENTITY, e.v_angle.x); + WriteAngle(MSG_ENTITY, e.v_angle.y); + } + + if(sf & 16) + { + float specs = CountSpectators(e, to); + WriteByte(MSG_ENTITY, specs); + WriteSpectators(e, to); + } + + return true; +} + +void ClientData_Attach(entity this) +{ + Net_LinkEntity(this.clientdata = new_pure(clientdata), false, 0, ClientData_Send); + this.clientdata.drawonlytoclient = this; + this.clientdata.owner = this; +} + +void ClientData_Detach(entity this) +{ + delete(this.clientdata); + this.clientdata = NULL; +} + +void ClientData_Touch(entity e) +{ + e.clientdata.SendFlags = 1; + + // make it spectatable + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != e && IS_SPEC(it) && it.enemy == e, LAMBDA(it.clientdata.SendFlags = 1)); +} + +.string netname_previous; + +void SetSpectatee(entity this, entity spectatee); +void SetSpectatee_status(entity this, int spectatee_num); + + +/* +============= +CheckPlayerModel + +Checks if the argument string can be a valid playermodel. +Returns a valid one in doubt. +============= +*/ +string FallbackPlayerModel; +string CheckPlayerModel(string plyermodel) { + if(FallbackPlayerModel != cvar_defstring("_cl_playermodel")) + { + // note: we cannot summon Don Strunzone here, some player may + // still have the model string set. In case anyone manages how + // to change a cvar default, we'll have a small leak here. + FallbackPlayerModel = strzone(cvar_defstring("_cl_playermodel")); + } + // only in right path + if( substring(plyermodel,0,14) != "models/player/") + return FallbackPlayerModel; + // only good file extensions + if(substring(plyermodel,-4,4) != ".zym") + if(substring(plyermodel,-4,4) != ".dpm") + if(substring(plyermodel,-4,4) != ".iqm") + if(substring(plyermodel,-4,4) != ".md3") + if(substring(plyermodel,-4,4) != ".psk") + return FallbackPlayerModel; + // forbid the LOD models + if(substring(plyermodel, -9,5) == "_lod1") + return FallbackPlayerModel; + if(substring(plyermodel, -9,5) == "_lod2") + return FallbackPlayerModel; + if(plyermodel != strtolower(plyermodel)) + return FallbackPlayerModel; + // also, restrict to server models + if(autocvar_sv_servermodelsonly) + { + if(!fexists(plyermodel)) + return FallbackPlayerModel; + } + return plyermodel; +} + +void setplayermodel(entity e, string modelname) +{ + precache_model(modelname); + _setmodel(e, modelname); + player_setupanimsformodel(e); + if(!autocvar_g_debug_globalsounds) + UpdatePlayerSounds(e); +} + +void FixPlayermodel(entity player); +/** putting a client as observer in the server */ +void PutObserverInServer(entity this) +{ + bool mutator_returnvalue = MUTATOR_CALLHOOK(MakePlayerObserver, this); + PlayerState_detach(this); + + if (IS_PLAYER(this) && this.health >= 1) { + // despawn effect + Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); + } + + { + entity spot = SelectSpawnPoint(this, true); + if (!spot) LOG_FATAL("No spawnpoints for observers?!?"); + this.angles = spot.angles; + this.angles_z = 0; + this.fixangle = true; + // offset it so that the spectator spawns higher off the ground, looks better this way + setorigin(this, spot.origin + STAT(PL_VIEW_OFS, this)); + this.prevorigin = this.origin; + if (IS_REAL_CLIENT(this)) + { + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this); + } + // give the spectator some space between walls for MOVETYPE_FLY_WORLDONLY + // so that your view doesn't go into the ceiling with MOVETYPE_FLY_WORLDONLY, previously "PL_VIEW_OFS" + if(!autocvar_g_debug_globalsounds) + { + // needed for player sounds + this.model = ""; + FixPlayermodel(this); + } + setmodel(this, MDL_Null); + setsize(this, STAT(PL_CROUCH_MIN, this), STAT(PL_CROUCH_MAX, this)); + this.view_ofs = '0 0 0'; + } + + RemoveGrapplingHook(this); + Portal_ClearAll(this); + Unfreeze(this); + SetSpectatee(this, NULL); + + if (this.alivetime) + { + if (!warmup_stage) + PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); + this.alivetime = 0; + } + + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + + WaypointSprite_PlayerDead(this); + + if (mutator_returnvalue) { + // mutator prevents resetting teams+score + } else { + this.team = -1; // move this as it is needed to log the player spectating in eventlog + this.frags = FRAGS_SPECTATOR; + PlayerScore_Clear(this); // clear scores when needed + } + + if (this.killcount != FRAGS_SPECTATOR) + { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_SPECTATE, this.netname); + if(!gameover) + 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(this.just_joined == false) { + LogTeamchange(this.playerid, -1, 4); + } else + this.just_joined = false; + } + + accuracy_resend(this); + + this.spectatortime = time; + if(this.bot_attack) + IL_REMOVE(g_bot_targets, this); + this.bot_attack = false; + this.hud = HUD_NORMAL; + TRANSMUTE(Observer, this); + this.iscreature = false; + this.teleportable = TELEPORT_SIMPLE; + if(this.damagedbycontents) + IL_REMOVE(g_damagedbycontents, this); + this.damagedbycontents = false; + this.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; + this.pauserotarmor_finished = 0; + this.pauserothealth_finished = 0; + this.pauseregen_finished = 0; + this.damageforcescale = 0; + this.death_time = 0; + this.respawn_flags = 0; + this.respawn_time = 0; + this.stat_respawn_time = 0; + this.alpha = 0; + this.scale = 0; + this.fade_time = 0; + this.pain_frame = 0; + this.pain_finished = 0; + this.strength_finished = 0; + this.invincible_finished = 0; + this.superweapons_finished = 0; + this.pushltime = 0; + this.istypefrag = 0; + setthink(this, func_null); + this.nextthink = 0; + this.hook_time = 0; + this.deadflag = DEAD_NO; + this.crouch = false; + this.revive_progress = 0; + this.revival_time = 0; + + this.items = 0; + this.weapons = '0 0 0'; + this.drawonlytoclient = this; + + this.weaponname = ""; + this.weaponmodel = ""; + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + this.weaponentities[slot] = NULL; + } + this.exteriorweaponentity = NULL; + this.killcount = FRAGS_SPECTATOR; + this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; + this.punchangle = '0 0 0'; + this.punchvector = '0 0 0'; + this.oldvelocity = this.velocity; + this.fire_endtime = -1; + this.event_damage = func_null; + + STAT(ACTIVEWEAPON, this) = WEP_Null.m_id; + STAT(SWITCHINGWEAPON, this) = WEP_Null.m_id; + STAT(SWITCHWEAPON, this) = WEP_Null.m_id; +} + +int player_getspecies(entity this) +{ + get_model_parameters(this.model, this.skin); + int s = get_model_parameters_species; + get_model_parameters(string_null, 0); + if (s < 0) return SPECIES_HUMAN; + return s; +} + +.float model_randomizer; +void FixPlayermodel(entity player) +{ + string defaultmodel = ""; + int defaultskin = 0; + if(autocvar_sv_defaultcharacter) + { + if(teamplay) + { + string s = Static_Team_ColorName_Lower(player.team); + if (s != "neutral") + { + defaultmodel = cvar_string(strcat("sv_defaultplayermodel_", s)); + defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); + } + } + + if(defaultmodel == "") + { + defaultmodel = autocvar_sv_defaultplayermodel; + defaultskin = autocvar_sv_defaultplayerskin; + } + + int n = tokenize_console(defaultmodel); + if(n > 0) + { + defaultmodel = argv(floor(n * player.model_randomizer)); + // However, do NOT randomize if the player-selected model is in the list. + for (int i = 0; i < n; ++i) + if ((argv(i) == player.playermodel && defaultskin == stof(player.playerskin)) || argv(i) == strcat(player.playermodel, ":", player.playerskin)) + defaultmodel = argv(i); + } + + int i = strstrofs(defaultmodel, ":", 0); + if(i >= 0) + { + defaultskin = stof(substring(defaultmodel, i+1, -1)); + defaultmodel = substring(defaultmodel, 0, i); + } + } + if(autocvar_sv_defaultcharacterskin && !defaultskin) + { + if(teamplay) + { + string s = Static_Team_ColorName_Lower(player.team); + if (s != "neutral") + defaultskin = cvar(strcat("sv_defaultplayerskin_", s)); + } + + if(!defaultskin) + defaultskin = autocvar_sv_defaultplayerskin; + } + + MUTATOR_CALLHOOK(FixPlayermodel, defaultmodel, defaultskin, player); + defaultmodel = M_ARGV(0, string); + defaultskin = M_ARGV(1, int); + + bool chmdl = false; + int oldskin; + if(defaultmodel != "") + { + if (defaultmodel != player.model) + { + vector m1 = player.mins; + vector m2 = player.maxs; + setplayermodel (player, defaultmodel); + setsize (player, m1, m2); + chmdl = true; + } + + oldskin = player.skin; + player.skin = defaultskin; + } else { + if (player.playermodel != player.model || player.playermodel == "") + { + player.playermodel = CheckPlayerModel(player.playermodel); // this is never "", so no endless loop + vector m1 = player.mins; + vector m2 = player.maxs; + setplayermodel (player, player.playermodel); + setsize (player, m1, m2); + chmdl = true; + } + + if(!autocvar_sv_defaultcharacterskin) + { + oldskin = player.skin; + player.skin = stof(player.playerskin); + } + else + { + oldskin = player.skin; + player.skin = defaultskin; + } + } + + if(chmdl || oldskin != player.skin) // model or skin has changed + { + player.species = player_getspecies(player); // update species + if(!autocvar_g_debug_globalsounds) + UpdatePlayerSounds(player); // update skin sounds + } + + if(!teamplay) + if(strlen(autocvar_sv_defaultplayercolors)) + if(player.clientcolors != stof(autocvar_sv_defaultplayercolors)) + setcolor(player, stof(autocvar_sv_defaultplayercolors)); +} + + +/** Called when a client spawns in the server */ +void PutClientInServer(entity this) +{ + if (IS_BOT_CLIENT(this)) { + TRANSMUTE(Player, this); + } else if (IS_REAL_CLIENT(this)) { + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this); + } + if (gameover) { + TRANSMUTE(Observer, this); + } + + SetSpectatee(this, NULL); + + // reset player keys + this.itemkeys = 0; + + MUTATOR_CALLHOOK(PutClientInServer, this); + + if (IS_OBSERVER(this)) { + PutObserverInServer(this); + } else if (IS_PLAYER(this)) { + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + + PlayerState_attach(this); + accuracy_resend(this); + + if (this.team < 0) + JoinBestTeam(this, false, true); + + entity spot = SelectSpawnPoint(this, false); + if (!spot) { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_NOSPAWNS); + return; // spawn failed + } + + TRANSMUTE(Player, this); + + this.wasplayer = true; + this.iscreature = true; + this.teleportable = TELEPORT_NORMAL; + if(!this.damagedbycontents) + IL_PUSH(g_damagedbycontents, this); + this.damagedbycontents = true; + set_movetype(this, MOVETYPE_WALK); + this.solid = SOLID_SLIDEBOX; + this.dphitcontentsmask = DPCONTENTS_BODY | DPCONTENTS_SOLID; + if (autocvar_g_playerclip_collisions) + this.dphitcontentsmask |= DPCONTENTS_PLAYERCLIP; + if (IS_BOT_CLIENT(this) && autocvar_g_botclip_collisions) + this.dphitcontentsmask |= DPCONTENTS_BOTCLIP; + this.frags = FRAGS_PLAYER; + if (INDEPENDENT_PLAYERS) MAKE_INDEPENDENT_PLAYER(this); + this.flags = FL_CLIENT | FL_PICKUPITEMS; + if (autocvar__notarget) + this.flags |= FL_NOTARGET; + this.takedamage = DAMAGE_AIM; + this.effects = EF_TELEPORT_BIT | EF_RESTARTANIM_BIT; + this.dmg = 2; // WTF + + if (warmup_stage) { + this.ammo_shells = warmup_start_ammo_shells; + this.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; + } 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; + } + SetSpectatee_status(this, 0); + + this.superweapons_finished = (this.weapons & WEPSET_SUPERWEAPONS) ? time + autocvar_g_balance_superweapons_time : 0; + + this.items = start_items; + + this.spawnshieldtime = time + autocvar_g_spawnshieldtime; + this.pauserotarmor_finished = time + autocvar_g_balance_pause_armor_rot_spawn; + this.pauserothealth_finished = time + autocvar_g_balance_pause_health_rot_spawn; + this.pauserotfuel_finished = time + autocvar_g_balance_pause_fuel_rot_spawn; + this.pauseregen_finished = time + autocvar_g_balance_pause_health_regen_spawn; + // extend the pause of rotting if client was reset at the beginning of the countdown + if (!autocvar_sv_ready_restart_after_countdown && time < game_starttime) { // TODO why is this cvar NOTted? + float f = game_starttime - time; + this.spawnshieldtime += f; + this.pauserotarmor_finished += f; + this.pauserothealth_finished += f; + this.pauseregen_finished += f; + } + this.damageforcescale = 2; + this.death_time = 0; + this.respawn_flags = 0; + this.respawn_time = 0; + this.stat_respawn_time = 0; + this.scale = autocvar_sv_player_scale; + this.fade_time = 0; + this.pain_frame = 0; + this.pain_finished = 0; + this.pushltime = 0; + setthink(this, func_null); // players have no think function + this.nextthink = 0; + this.dmg_team = 0; + this.ballistics_density = autocvar_g_ballistics_density_player; + + this.deadflag = DEAD_NO; + + this.angles = spot.angles; + this.angles_z = 0; // never spawn tilted even if the spot says to + if (IS_BOT_CLIENT(this)) + this.v_angle = this.angles; + this.fixangle = true; // turn this way immediately + this.oldvelocity = this.velocity = '0 0 0'; + this.avelocity = '0 0 0'; + this.punchangle = '0 0 0'; + this.punchvector = '0 0 0'; + + this.strength_finished = 0; + this.invincible_finished = 0; + this.fire_endtime = -1; + this.revive_progress = 0; + this.revival_time = 0; + this.air_finished = time + 12; + + entity spawnevent = new_pure(spawnevent); + spawnevent.owner = this; + Net_LinkEntity(spawnevent, false, 0.5, SpawnEvent_Send); + + // Cut off any still running player sounds. + stopsound(this, CH_PLAYER_SINGLE); + + this.model = ""; + FixPlayermodel(this); + this.drawonlytoclient = NULL; + + this.viewloc = NULL; + + this.crouch = false; + this.view_ofs = STAT(PL_VIEW_OFS, this); + setsize(this, STAT(PL_MIN, this), STAT(PL_MAX, this)); + this.spawnorigin = spot.origin; + setorigin(this, spot.origin + '0 0 1' * (1 - this.mins.z - 24)); + // don't reset back to last position, even if new position is stuck in solid + this.oldorigin = this.origin; + this.prevorigin = this.origin; + this.lastteleporttime = time; // prevent insane speeds due to changing origin + if(this.conveyor) + IL_REMOVE(g_conveyed, this); + this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player + this.hud = HUD_NORMAL; + + this.event_damage = PlayerDamage; + + if(!this.bot_attack) + IL_PUSH(g_bot_targets, this); + this.bot_attack = true; + this.monster_attack = true; + + PHYS_INPUT_BUTTON_ATCK(this) = PHYS_INPUT_BUTTON_JUMP(this) = PHYS_INPUT_BUTTON_ATCK2(this) = false; + + if (this.killcount == FRAGS_SPECTATOR) { + PlayerScore_Clear(this); + this.killcount = 0; + } + + for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + CL_SpawnWeaponentity(this, weaponentities[slot]); + } + this.alpha = default_player_alpha; + this.colormod = '1 1 1' * autocvar_g_player_brightness; + this.exteriorweaponentity.alpha = default_weapon_alpha; + + this.speedrunning = false; + + target_voicescript_clear(this); + + // reset fields the weapons may use + FOREACH(Weapons, true, LAMBDA( + it.wr_resetplayer(it, this); + // reload all reloadable weapons + if (it.spawnflags & WEP_FLAG_RELOADABLE) { + this.weapon_load[it.m_id] = it.reloading_ammo; + } + )); + + { + string s = spot.target; + spot.target = string_null; + SUB_UseTargets(spot, this, NULL); + spot.target = s; + } + + Unfreeze(this); + + MUTATOR_CALLHOOK(PlayerSpawn, this, spot); + + if (autocvar_spawn_debug) + { + sprint(this, strcat("spawnpoint origin: ", vtos(spot.origin), "\n")); + delete(spot); // usefull for checking if there are spawnpoints, that let drop through the floor + } + + PS(this).m_switchweapon = w_getbestweapon(this); + this.cnt = -1; // W_LastWeapon will not complain + PS(this).m_weapon = WEP_Null; + this.weaponname = ""; + PS(this).m_switchingweapon = WEP_Null; + + if (!warmup_stage && !this.alivetime) + this.alivetime = time; + + antilag_clear(this, CS(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) +{ + WriteHeader(MSG_ENTITY, _ENT_CLIENT_INIT); + return = true; + msg_entity = to; + // MSG_INIT replacement + // TODO: make easier to use + Registry_send_all(); + W_PROP_reload(MSG_ONE, to); + ClientInit_misc(this); + MUTATOR_CALLHOOK(Ent_Init); +} +void ClientInit_misc(entity this) +{ + int channel = MSG_ONE; + WriteHeader(channel, ENT_CLIENT_INIT); + WriteByte(channel, g_nexball_meter_period * 32); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(hook_shotorigin[3])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[0])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[1])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[2])); + WriteInt24_t(channel, compressShotOrigin(arc_shotorigin[3])); + + if(sv_foginterval && world.fog != "") + WriteString(channel, world.fog); + else + WriteString(channel, ""); + WriteByte(channel, this.count * 255.0); // g_balance_armor_blockpercent + WriteByte(channel, serverflags); + WriteCoord(channel, autocvar_g_trueaim_minrange); +} + +void ClientInit_CheckUpdate(entity this) +{ + this.nextthink = time; + if(this.count != autocvar_g_balance_armor_blockpercent) + { + this.count = autocvar_g_balance_armor_blockpercent; + this.SendFlags |= 1; + } +} + +void ClientInit_Spawn() +{ + entity e = new_pure(clientinit); + setthink(e, ClientInit_CheckUpdate); + Net_LinkEntity(e, false, 0, ClientInit_SendEntity); + + ClientInit_CheckUpdate(e); +} + +/* +============= +SetNewParms +============= +*/ +void SetNewParms () +{ + // initialize parms for a new player + parm1 = -(86400 * 366); + + MUTATOR_CALLHOOK(SetNewParms); +} + +/* +============= +SetChangeParms +============= +*/ +void SetChangeParms (entity this) +{ + // save parms for level change + parm1 = this.parm_idlesince - time; + + MUTATOR_CALLHOOK(SetChangeParms); +} + +/* +============= +DecodeLevelParms +============= +*/ +void DecodeLevelParms(entity this) +{ + // load parms + this.parm_idlesince = parm1; + if (this.parm_idlesince == -(86400 * 366)) + this.parm_idlesince = time; + + // whatever happens, allow 60 seconds of idling directly after connect for map loading + this.parm_idlesince = max(this.parm_idlesince, time - sv_maxidle + 60); + + 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, false, 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, 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)) + Damage(this, this, this, 100000, DEATH_KILL.m_id, this.origin, '0 0 0'); + + // now I am sure the player IS dead +} +void KillIndicator_Think(entity this) +{ + if (gameover) + { + 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(g_cts && 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 (gameover) + return; + + killtime = autocvar_g_balance_kill_delay; + + if(g_race_qualifying || g_cts) + killtime = 0; + + if(MUTATOR_CALLHOOK(ClientKill, this, killtime)) + return; + + 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(gameover) 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 + stuffcmd(e, "\nin_bindmap 0 0\n"); + if(autocvar_g_antilag == 3) // client side hitscan + stuffcmd(e, "cl_cmd settemp cl_prydoncursor_notrace 0\n"); + if(autocvar_sv_gentle) + stuffcmd(e, "cl_cmd settemp cl_gentle 1\n"); + + MUTATOR_CALLHOOK(FixClientCvars, e); +} + +float PlayerInIDList(entity p, string idlist) +{ + float n, i; + string s; + + // NOTE: we do NOT check crypto_idfp_signed here, an unsigned ID is fine too for this + if (!p.crypto_idfp) + return 0; + + // this function allows abbreviated player IDs too! + n = tokenize_console(idlist); + for(i = 0; i < n; ++i) + { + s = argv(i); + if(s == substring(p.crypto_idfp, 0, strlen(s))) + return 1; + } + + return 0; +} + +#ifdef DP_EXT_PRECONNECT +/* +============= +ClientPreConnect + +Called once (not at each match start) when a client begins a connection to the server +============= +*/ +void ClientPreConnect(entity this) +{ + if(autocvar_sv_eventlog) + { + GameLogEcho(sprintf(":connect:%d:%d:%s", + this.playerid, + etof(this), + ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot") + )); + } +} +#endif + +/** +============= +ClientConnect + +Called when a client connects to the server +============= +*/ +void ClientConnect(entity this) +{ + if (Ban_MaybeEnforceBanOnce(this)) return; + assert(!IS_CLIENT(this), return); + this.flags |= FL_CLIENT; + assert(player_count >= 0, player_count = 0); + +#ifdef WATERMARK + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_WATERMARK, WATERMARK); +#endif + this.version_nagtime = time + 10 + random() * 10; + TRANSMUTE(Client, this); + + // 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 (PlayerInIDList(this, autocvar_g_forced_team_red)) this.team_forced = NUM_TEAM_1; + else if (PlayerInIDList(this, autocvar_g_forced_team_blue)) this.team_forced = NUM_TEAM_2; + else if (PlayerInIDList(this, autocvar_g_forced_team_yellow)) this.team_forced = NUM_TEAM_3; + else if (PlayerInIDList(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; + + { + int id = this.playerid; + this.playerid = 0; // silent + JoinBestTeam(this, false, false); // if the team number is valid, keep it + this.playerid = id; + } + + 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 + } + } + + PlayerStats_GameReport_AddEvent(sprintf("kills-%d", this.playerid)); + + // always track bots, don't ask for cl_allow_uidtracking + if (IS_BOT_CLIENT(this)) PlayerStats_GameReport_AddPlayer(this); + + if (autocvar_sv_eventlog) + GameLogEcho(strcat(":join:", ftos(this.playerid), ":", ftos(etof(this)), ":", ((IS_REAL_CLIENT(this)) ? this.netaddress : "bot"), ":", this.netname)); + + LogTeamchange(this.playerid, this.team, 1); + + this.just_joined = true; // stop spamming the eventlog with additional lines when the client connects + + this.netname_previous = strzone(this.netname); + + if(teamplay && IS_PLAYER(this)) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_JOIN_CONNECT_TEAM), this.netname); + else + 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? + + FixClientCvars(this); + + // get version info from player + stuffcmd(this, "cmd clientversion $gameversion\n"); + + // 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); + stuffcmd(this, sprintf("set _teams_available %d\n", t)); + } + else + { + stuffcmd(this, "set _teams_available 0\n"); + } + + bot_relinkplayerlist(); + + this.spectatortime = time; + if (blockSpectators) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_SPECTATE_WARNING, autocvar_g_maxplayers_spectator_blocktime); + } + + this.jointime = time; + this.allowed_timeouts = autocvar_sv_timeout_number; + + if (IS_REAL_CLIENT(this)) + { + if (g_weaponarena_weapons == WEPSET(TUBA)) + stuffcmd(this, "cl_cmd settemp chase_active 1\n"); + } + + if (!sv_foginterval && world.fog != "") + stuffcmd(this, strcat("\nfog ", world.fog, "\nr_fog_exp2 0\nr_drawfog 1\n")); + + if (autocvar_sv_teamnagger && !(autocvar_bot_vs_human && AvailableTeams() == 2)) + if (!g_ca && !g_cts && !g_race) // teamnagger is currently bad for ca, race & cts + send_CSQC_teamnagger(); + + CSQCMODEL_AUTOINIT(this); + + this.model_randomizer = random(); + + if (IS_REAL_CLIENT(this)) + sv_notice_join(this); + + // update physics stats (players can spawn before physics runs) + Physics_UpdateStats(this, PHYS_HIGHSPEED(this)); + + IL_EACH(g_initforplayer, it.init_for_player, { + it.init_for_player(it, this); + }); + + MUTATOR_CALLHOOK(ClientConnect, this); + + if (IS_REAL_CLIENT(this)) + { + if (!autocvar_g_campaign && !IS_PLAYER(this)) + { + this.motd_actived_time = -1; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); + } + } +} +/* +============= +ClientDisconnect + +Called when a client disconnects from the server +============= +*/ +.entity chatbubbleentity; +void ReadyCount(); +void ClientDisconnect(entity this) +{ + assert(IS_CLIENT(this), return); + + PlayerStats_GameReport_FinalizePlayer(this); + if (this.vehicle) vehicles_exit(this.vehicle, VHEF_RELEASE); + if (this.active_minigame) part_minigame(this); + if (IS_PLAYER(this)) Send_Effect(EFFECT_SPAWN_NEUTRAL, this.origin, '0 0 0', 1); + + if (autocvar_sv_eventlog) + GameLogEcho(strcat(":part:", ftos(this.playerid))); + + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_DISCONNECT, this.netname); + + SetSpectatee(this, NULL); + + MUTATOR_CALLHOOK(ClientDisconnect, this); + + ClientState_detach(this); + + Portal_ClearAll(this); + + Unfreeze(this); + + RemoveGrapplingHook(this); + + // Here, everything has been done that requires this player to be a client. + + this.flags &= ~FL_CLIENT; + + if (this.chatbubbleentity) delete(this.chatbubbleentity); + if (this.killindicator) delete(this.killindicator); + + WaypointSprite_PlayerGone(this); + + bot_relinkplayerlist(); + + if (this.netname_previous) strunzone(this.netname_previous); + if (this.clientstatus) strunzone(this.clientstatus); + if (this.weaponorder_byimpulse) strunzone(this.weaponorder_byimpulse); + if (this.personal) delete(this.personal); + + this.playerid = 0; + ReadyCount(); + if (vote_called && IS_REAL_CLIENT(this)) VoteCount(false); + + ONREMOVE(this); +} + +void ChatBubbleThink(entity this) +{ + this.nextthink = time; + if ((this.owner.alpha < 0) || this.owner.chatbubbleentity != this) + { + if(this.owner) // but why can that ever be NULL? + this.owner.chatbubbleentity = NULL; + delete(this); + return; + } + + this.mdl = ""; + + if ( !IS_DEAD(this.owner) && IS_PLAYER(this.owner) ) + { + if ( this.owner.active_minigame ) + this.mdl = "models/sprites/minigame_busy.iqm"; + else if (PHYS_INPUT_BUTTON_CHAT(this.owner)) + this.mdl = "models/misc/chatbubble.spr"; + } + + if ( this.model != this.mdl ) + _setmodel(this, this.mdl); + +} + +void UpdateChatBubble(entity this) +{ + if (this.alpha < 0) + return; + // spawn a chatbubble entity if needed + if (!this.chatbubbleentity) + { + this.chatbubbleentity = new(chatbubbleentity); + this.chatbubbleentity.owner = this; + this.chatbubbleentity.exteriormodeltoclient = this; + setthink(this.chatbubbleentity, ChatBubbleThink); + this.chatbubbleentity.nextthink = time; + setmodel(this.chatbubbleentity, MDL_CHAT); // precision set below + //setorigin(this.chatbubbleentity, this.origin + '0 0 15' + this.maxs_z * '0 0 1'); + setorigin(this.chatbubbleentity, '0 0 15' + this.maxs_z * '0 0 1'); + setattachment(this.chatbubbleentity, this, ""); // sticks to moving player better, also conserves bandwidth + this.chatbubbleentity.mdl = this.chatbubbleentity.model; + //this.chatbubbleentity.model = ""; + this.chatbubbleentity.effects = EF_LOWPRECISION; + } +} + + +// LordHavoc: this hack will be removed when proper _pants/_shirt layers are +// added to the model skins +/*void UpdateColorModHack() +{ + float c; + c = this.clientcolors & 15; + // LordHavoc: only bothering to support white, green, red, yellow, blue + if (!teamplay) this.colormod = '0 0 0'; + else if (c == 0) this.colormod = '1.00 1.00 1.00'; + else if (c == 3) this.colormod = '0.10 1.73 0.10'; + else if (c == 4) this.colormod = '1.73 0.10 0.10'; + else if (c == 12) this.colormod = '1.22 1.22 0.10'; + else if (c == 13) this.colormod = '0.10 0.10 1.73'; + else this.colormod = '1 1 1'; +}*/ + +void respawn(entity this) +{ + if(this.alpha >= 0 && autocvar_g_respawn_ghosts) + { + this.solid = SOLID_NOT; + this.takedamage = DAMAGE_NO; + set_movetype(this, MOVETYPE_FLY); + this.velocity = '0 0 1' * autocvar_g_respawn_ghosts_speed; + this.avelocity = randomvec() * autocvar_g_respawn_ghosts_speed * 3 - randomvec() * autocvar_g_respawn_ghosts_speed * 3; + this.effects |= CSQCMODEL_EF_RESPAWNGHOST; + Send_Effect(EFFECT_RESPAWN_GHOST, this.origin, '0 0 0', 1); + if(autocvar_g_respawn_ghosts_maxtime) + SUB_SetFade (this, time + autocvar_g_respawn_ghosts_maxtime / 2 + random () * (autocvar_g_respawn_ghosts_maxtime - autocvar_g_respawn_ghosts_maxtime / 2), 1.5); + } + + CopyBody(this, 1); + + this.effects |= EF_NODRAW; // prevent another CopyBody + PutClientInServer(this); +} + +void play_countdown(entity this, float finished, Sound samp) +{ + TC(Sound, samp); + if(IS_REAL_CLIENT(this)) + if(floor(finished - time - frametime) != floor(finished - time)) + if(finished - time < 6) + sound (this, CH_INFO, samp, VOL_BASE, ATTEN_NORM); +} + +void player_powerups(entity this) +{ + // add a way to see what the items were BEFORE all of these checks for the mutator hook + int items_prev = this.items; + + if((this.items & IT_USING_JETPACK) && !IS_DEAD(this) && !gameover) + this.modelflags |= MF_ROCKET; + else + this.modelflags &= ~MF_ROCKET; + + this.effects &= ~(EF_RED | EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT | EF_FLAME | EF_NODEPTHTEST); + + if((this.alpha < 0 || IS_DEAD(this)) && !this.vehicle) // don't apply the flags if the player is gibbed + return; + + Fire_ApplyDamage(this); + Fire_ApplyEffect(this); + + if (!g_instagib) + { + if (this.items & ITEM_Strength.m_itemid) + { + play_countdown(this, this.strength_finished, SND_POWEROFF); + this.effects = this.effects | (EF_BLUE | EF_ADDITIVE | EF_FULLBRIGHT); + if (time > this.strength_finished) + { + this.items = this.items - (this.items & ITEM_Strength.m_itemid); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_STRENGTH, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_STRENGTH); + } + } + else + { + if (time < this.strength_finished) + { + this.items = this.items | ITEM_Strength.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_STRENGTH, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_STRENGTH); + } + } + if (this.items & ITEM_Shield.m_itemid) + { + play_countdown(this, this.invincible_finished, SND_POWEROFF); + this.effects = this.effects | (EF_RED | EF_ADDITIVE | EF_FULLBRIGHT); + if (time > this.invincible_finished) + { + this.items = this.items - (this.items & ITEM_Shield.m_itemid); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERDOWN_SHIELD, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERDOWN_SHIELD); + } + } + else + { + if (time < this.invincible_finished) + { + this.items = this.items | ITEM_Shield.m_itemid; + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_POWERUP_SHIELD, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_POWERUP_SHIELD); + } + } + if (this.items & IT_SUPERWEAPON) + { + if (!(this.weapons & WEPSET_SUPERWEAPONS)) + { + this.superweapons_finished = 0; + this.items = this.items - (this.items & IT_SUPERWEAPON); + //Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_SUPERWEAPON_LOST, this.netname); + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_SUPERWEAPON_LOST); + } + else if (this.items & IT_UNLIMITED_SUPERWEAPONS) + { + // don't let them run out + } + else + { + play_countdown(this, this.superweapons_finished, SND_POWEROFF); + if (time > this.superweapons_finished) + { + this.items = this.items - (this.items & IT_SUPERWEAPON); + this.weapons &= ~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) + { + if (time < this.superweapons_finished || (this.items & IT_UNLIMITED_SUPERWEAPONS)) + { + this.items = this.items | IT_SUPERWEAPON; + 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; + } + } + else + { + this.superweapons_finished = 0; + } + } + + if(autocvar_g_nodepthtestplayers) + this.effects = this.effects | EF_NODEPTHTEST; + + if(autocvar_g_fullbrightplayers) + this.effects = this.effects | EF_FULLBRIGHT; + + if (time >= game_starttime) + if (time < this.spawnshieldtime) + this.effects = this.effects | (EF_ADDITIVE | EF_FULLBRIGHT); + + MUTATOR_CALLHOOK(PlayerPowerups, this, items_prev); +} + +float CalcRegen(float current, float stable, float regenfactor, float regenframetime) +{ + if(current > stable) + return current; + else if(current > stable - 0.25) // when close enough, "snap" + return stable; + else + return min(stable, current + (stable - current) * regenfactor * regenframetime); +} + +float CalcRot(float current, float stable, float rotfactor, float rotframetime) +{ + if(current < stable) + return current; + else if(current < stable + 0.25) // when close enough, "snap" + return stable; + else + return max(stable, current + (stable - current) * rotfactor * rotframetime); +} + +float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit) +{ + if(current > rotstable) + { + if(rotframetime > 0) + { + current = CalcRot(current, rotstable, rotfactor, rotframetime); + current = max(rotstable, current - rotlinear * rotframetime); + } + } + else if(current < regenstable) + { + if(regenframetime > 0) + { + current = CalcRegen(current, regenstable, regenfactor, regenframetime); + current = min(regenstable, current + regenlinear * regenframetime); + } + } + + if(current > limit) + current = limit; + + return current; +} + +void player_regen(entity this) +{ + float max_mod, regen_mod, rot_mod, limit_mod; + max_mod = regen_mod = rot_mod = limit_mod = 1; + + float regen_health = autocvar_g_balance_health_regen; + float regen_health_linear = autocvar_g_balance_health_regenlinear; + float regen_health_rot = autocvar_g_balance_health_rot; + float regen_health_rotlinear = autocvar_g_balance_health_rotlinear; + float regen_health_stable = autocvar_g_balance_health_regenstable; + float regen_health_rotstable = autocvar_g_balance_health_rotstable; + bool mutator_returnvalue = MUTATOR_CALLHOOK(PlayerRegen, this, max_mod, regen_mod, rot_mod, limit_mod, regen_health, regen_health_linear, regen_health_rot, + regen_health_rotlinear, regen_health_stable, regen_health_rotstable); + max_mod = M_ARGV(1, float); + regen_mod = M_ARGV(2, float); + rot_mod = M_ARGV(3, float); + limit_mod = M_ARGV(4, float); + regen_health = M_ARGV(5, float); + regen_health_linear = M_ARGV(6, float); + regen_health_rot = M_ARGV(7, float); + regen_health_rotlinear = M_ARGV(8, float); + regen_health_stable = M_ARGV(9, float); + regen_health_rotstable = M_ARGV(10, float); + + + if(!mutator_returnvalue) + if(!STAT(FROZEN, this)) + { + float mina, maxa, limith, limita; + maxa = autocvar_g_balance_armor_rotstable; + mina = autocvar_g_balance_armor_regenstable; + limith = autocvar_g_balance_health_limit; + limita = autocvar_g_balance_armor_limit; + + regen_health_rotstable = regen_health_rotstable * max_mod; + regen_health_stable = regen_health_stable * max_mod; + 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); + } + + // 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(this.vehicle) + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(this.event_damage) + this.event_damage(this, this, this, 1, DEATH_ROT.m_id, this.origin, '0 0 0'); + } + + if (!(this.items & IT_UNLIMITED_WEAPON_AMMO)) + { + float minf, maxf, limitf; + + maxf = autocvar_g_balance_fuel_rotstable; + minf = autocvar_g_balance_fuel_regenstable; + limitf = autocvar_g_balance_fuel_limit; + + 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); + } +} + +bool zoomstate_set; +void SetZoomState(entity this, float newzoom) +{ + if(newzoom != this.zoomstate) + { + this.zoomstate = newzoom; + ClientData_Touch(this); + } + zoomstate_set = true; +} + +void GetPressedKeys(entity this) +{ + MUTATOR_CALLHOOK(GetPressedKeys, this); + int keys = STAT(PRESSED_KEYS, this); + keys = BITSET(keys, KEY_FORWARD, this.movement.x > 0); + keys = BITSET(keys, KEY_BACKWARD, this.movement.x < 0); + keys = BITSET(keys, KEY_RIGHT, this.movement.y > 0); + keys = BITSET(keys, KEY_LEFT, 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_ATCK, PHYS_INPUT_BUTTON_ATCK(this)); + keys = BITSET(keys, KEY_ATCK2, PHYS_INPUT_BUTTON_ATCK2(this)); + this.pressedkeys = keys; // store for other users + + STAT(PRESSED_KEYS, this) = keys; +} + +/* +====================== +spectate mode routines +====================== +*/ + +void SpectateCopy(entity this, entity 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; + this.clip_load = spectatee.clip_load; + this.clip_size = spectatee.clip_size; + this.effects = spectatee.effects & EFMASK_CHEAP; // eat performance + this.health = spectatee.health; + this.impulse = 0; + this.items = spectatee.items; + this.last_pickup = spectatee.last_pickup; + this.hit_time = spectatee.hit_time; + this.strength_finished = spectatee.strength_finished; + this.invincible_finished = spectatee.invincible_finished; + STAT(PRESSED_KEYS, this) = STAT(PRESSED_KEYS, spectatee); + this.weapons = spectatee.weapons; + this.vortex_charge = spectatee.vortex_charge; + this.vortex_chargepool_ammo = spectatee.vortex_chargepool_ammo; + this.hagar_load = spectatee.hagar_load; + this.arc_heat_percent = spectatee.arc_heat_percent; + this.minelayer_mines = spectatee.minelayer_mines; + this.punchangle = spectatee.punchangle; + this.view_ofs = spectatee.view_ofs; + this.velocity = spectatee.velocity; + this.dmg_take = spectatee.dmg_take; + this.dmg_save = spectatee.dmg_save; + this.dmg_inflictor = spectatee.dmg_inflictor; + this.v_angle = spectatee.v_angle; + this.angles = spectatee.v_angle; + STAT(FROZEN, this) = STAT(FROZEN, spectatee); + this.revive_progress = spectatee.revive_progress; + this.viewloc = spectatee.viewloc; + if(!PHYS_INPUT_BUTTON_USE(this) && STAT(CAMERA_SPECTATOR, this) != 2) + this.fixangle = true; + setorigin(this, spectatee.origin); + setsize(this, spectatee.mins, spectatee.maxs); + SetZoomState(this, spectatee.zoomstate); + + anticheat_spectatecopy(this, spectatee); + this.hud = spectatee.hud; + if(spectatee.vehicle) + { + this.angles = spectatee.v_angle; + + //this.fixangle = false; + //this.velocity = spectatee.vehicle.velocity; + this.vehicle_health = spectatee.vehicle_health; + this.vehicle_shield = spectatee.vehicle_shield; + this.vehicle_energy = spectatee.vehicle_energy; + this.vehicle_ammo1 = spectatee.vehicle_ammo1; + this.vehicle_ammo2 = spectatee.vehicle_ammo2; + this.vehicle_reload1 = spectatee.vehicle_reload1; + this.vehicle_reload2 = spectatee.vehicle_reload2; + + //msg_entity = this; + + // WriteByte (MSG_ONE, SVC_SETVIEWANGLES); + //WriteAngle(MSG_ONE, spectatee.v_angle.x); + // WriteAngle(MSG_ONE, spectatee.v_angle.y); + // WriteAngle(MSG_ONE, spectatee.v_angle.z); + + //WriteByte (MSG_ONE, SVC_SETVIEW); + // WriteEntity(MSG_ONE, this); + //makevectors(spectatee.v_angle); + //setorigin(this, spectatee.origin - v_forward * 400 + v_up * 300);*/ + } +} + +bool SpectateUpdate(entity this) +{ + if(!this.enemy) + return false; + + if(!IS_PLAYER(this.enemy) || this == this.enemy) + { + SetSpectatee(this, NULL); + return false; + } + + SpectateCopy(this, this.enemy); + + return true; +} + +bool SpectateSet(entity this) +{ + if(!IS_PLAYER(this.enemy)) + return false; + + ClientData_Touch(this.enemy); + + msg_entity = this; + WriteByte(MSG_ONE, SVC_SETVIEW); + WriteEntity(MSG_ONE, this.enemy); + set_movetype(this, MOVETYPE_NONE); + accuracy_resend(this); + + if(!SpectateUpdate(this)) + PutObserverInServer(this); + + return true; +} + +void SetSpectatee_status(entity this, int spectatee_num) +{ + int oldspectatee_status = this.spectatee_status; + this.spectatee_status = spectatee_num; + + if (this.spectatee_status != oldspectatee_status) + { + ClientData_Touch(this); + if (g_race || g_cts) race_InitSpectator(); + } +} + +void SetSpectatee(entity this, entity spectatee) +{ + entity old_spectatee = this.enemy; + + this.enemy = spectatee; + + // WEAPONTODO + // these are required to fix the spectator bug with arc + if(old_spectatee) + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(old_spectatee.(weaponentity).arc_beam) + old_spectatee.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; + } + } + if(this.enemy) + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + if(this.enemy.(weaponentity).arc_beam) + this.enemy.(weaponentity).arc_beam.SendFlags |= ARC_SF_SETTINGS; + } + } + + if (this.enemy) + SetSpectatee_status(this, etof(this.enemy)); + + // needed to update spectator list + if(old_spectatee) { ClientData_Touch(old_spectatee); } +} + +bool Spectate(entity this, entity pl) +{ + if(MUTATOR_CALLHOOK(SpectateSet, this, pl)) + return false; + pl = M_ARGV(1, entity); + + SetSpectatee(this, pl); + return SpectateSet(this); +} + +bool SpectateNext(entity this) +{ + entity ent = find(this.enemy, classname, STR_PLAYER); + + if (MUTATOR_CALLHOOK(SpectateNext, this, ent)) + ent = M_ARGV(1, entity); + else if (!ent) + ent = find(ent, classname, STR_PLAYER); + + if(ent) { SetSpectatee(this, ent); } + + return SpectateSet(this); +} + +bool SpectatePrev(entity this) +{ + // NOTE: chain order is from the highest to the lower entnum (unlike find) + entity ent = findchain(classname, STR_PLAYER); + if (!ent) // no player + return false; + + entity first = ent; + // skip players until current spectated player + if(this.enemy) + while(ent && ent != this.enemy) + ent = ent.chain; + + switch (MUTATOR_CALLHOOK(SpectatePrev, this, ent, first)) + { + case MUT_SPECPREV_FOUND: + ent = M_ARGV(1, entity); + break; + case MUT_SPECPREV_RETURN: + return true; + case MUT_SPECPREV_CONTINUE: + default: + { + if(ent.chain) + ent = ent.chain; + else + ent = first; + break; + } + } + + SetSpectatee(this, ent); + return SpectateSet(this); +} + +/* +============= +ShowRespawnCountdown() + +Update a respawn countdown display. +============= +*/ +void ShowRespawnCountdown(entity this) +{ + float number; + if(!IS_DEAD(this)) // just respawned? + return; + else + { + number = ceil(this.respawn_time - time); + if(number <= 0) + return; + if(number <= this.respawn_countdown) + { + this.respawn_countdown = number - 1; + if(ceil(this.respawn_time - (time + 0.5)) == number) // only say it if it is the same number even in 0.5s; to prevent overlapping sounds + { Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_RESPAWN, number)); } + } + } +} + +.bool team_selected; +bool ShowTeamSelection(entity this) +{ + if(!teamplay || autocvar_g_campaign || autocvar_g_balance_teams || this.team_selected || (this.wasplayer && autocvar_g_changeteam_banned) || this.team_forced > 0) + return false; + stuffcmd(this, "menu_showteamselect\n"); + return true; +} +void Join(entity this) +{ + TRANSMUTE(Player, this); + + if(!this.team_selected) + if(autocvar_g_campaign || autocvar_g_balance_teams) + JoinBestTeam(this, false, true); + + if(autocvar_g_campaign) + campaign_bots_may_start = true; + + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_PREVENT_JOIN); + + PutClientInServer(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; +} + +/** + * 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 + * it checks whether the number of currently playing players exceeds g_maxplayers. + * @return int number of free slots for players, 0 if none + */ +int nJoinAllowed(entity this, entity ignore) +{ + if(!ignore) + // this is called that way when checking if anyone may be able to join (to build qcstatus) + // so report 0 free slots if restricted + { + if(autocvar_g_forced_team_otherwise == "spectate") + return 0; + if(autocvar_g_forced_team_otherwise == "spectator") + return 0; + } + + if(this && this.team_forced < 0) + return 0; // forced spectators can never join + + // TODO simplify this + int totalClients = 0; + int currentlyPlaying = 0; + FOREACH_CLIENT(true, LAMBDA( + if(it != ignore) + ++totalClients; + if(IS_REAL_CLIENT(it)) + if(IS_PLAYER(it) || it.caplayer) + ++currentlyPlaying; + )); + + float free_slots = 0; + if (!autocvar_g_maxplayers) + free_slots = maxclients - totalClients; + else if(currentlyPlaying < autocvar_g_maxplayers) + free_slots = min(maxclients - totalClients, autocvar_g_maxplayers - currentlyPlaying); + + static float join_prevent_msg_time = 0; + if(this && ignore && !free_slots && time > join_prevent_msg_time) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_JOIN_PREVENT); + join_prevent_msg_time = time + 3; + } + + return free_slots; +} + +/** + * Checks whether the client is an observer or spectator, if so, he will get kicked after + * g_maxplayers_spectator_blocktime seconds + */ +void checkSpectatorBlock(entity this) +{ + if(IS_SPEC(this) || IS_OBSERVER(this)) + if(!this.caplayer) + if(IS_REAL_CLIENT(this)) + { + if( time > (this.spectatortime + autocvar_g_maxplayers_spectator_blocktime) ) { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_QUIT_KICK_SPECTATING); + dropclient(this); + } + } +} + +void PrintWelcomeMessage(entity this) +{ + if(this.motd_actived_time == 0) + { + if (autocvar_g_campaign) { + if ((IS_PLAYER(this) && PHYS_INPUT_BUTTON_INFO(this)) || (!IS_PLAYER(this))) { + this.motd_actived_time = time; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, campaign_message); + } + } else { + if (PHYS_INPUT_BUTTON_INFO(this)) { + this.motd_actived_time = time; + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_MOTD, getwelcomemessage(this)); + } + } + } + else if(this.motd_actived_time > 0) // showing MOTD or campaign message + { + if (autocvar_g_campaign) { + if (PHYS_INPUT_BUTTON_INFO(this)) + this.motd_actived_time = time; + else if ((time - this.motd_actived_time > 2) && IS_PLAYER(this)) { // hide it some seconds after BUTTON_INFO has been released + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } else { + if (PHYS_INPUT_BUTTON_INFO(this)) + this.motd_actived_time = time; + else if (time - this.motd_actived_time > 2) { // hide it some seconds after BUTTON_INFO has been released + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } + } + else //if(this.motd_actived_time < 0) // just connected, motd is active + { + if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD + this.motd_actived_time = -2; // wait until BUTTON_INFO gets released + else if(this.motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this)) + { + // instanctly hide MOTD + this.motd_actived_time = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); + } + } +} + +bool joinAllowed(entity this) +{ + if (this.version_mismatch) 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; + return true; +} + +void ObserverThink(entity this) +{ + if ( this.impulse ) + { + MinigameImpulse(this, this.impulse); + this.impulse = 0; + } + + if (this.flags & FL_JUMPRELEASED) { + if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) { + this.flags &= ~FL_JUMPRELEASED; + this.flags |= FL_SPAWNING; + } else if(PHYS_INPUT_BUTTON_ATCK(this) && !this.version_mismatch) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectateNext(this)) { + TRANSMUTE(Spectator, this); + } + } else { + int preferred_movetype = ((!PHYS_INPUT_BUTTON_USE(this) ? this.cvar_cl_clippedspectating : !this.cvar_cl_clippedspectating) ? MOVETYPE_FLY_WORLDONLY : MOVETYPE_NOCLIP); + set_movetype(this, preferred_movetype); + } + } else { + if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this))) { + this.flags |= FL_JUMPRELEASED; + if(this.flags & FL_SPAWNING) + { + this.flags &= ~FL_SPAWNING; + Join(this); + return; + } + } + } +} + +void SpectatorThink(entity this) +{ + if ( this.impulse ) + { + if(MinigameImpulse(this, this.impulse)) + this.impulse = 0; + + if (this.impulse == IMP_weapon_drop.impulse) + { + STAT(CAMERA_SPECTATOR, this) = (STAT(CAMERA_SPECTATOR, this) + 1) % 3; + this.impulse = 0; + return; + } + } + + if (this.flags & FL_JUMPRELEASED) { + if (PHYS_INPUT_BUTTON_JUMP(this) && joinAllowed(this)) { + this.flags &= ~FL_JUMPRELEASED; + this.flags |= FL_SPAWNING; + } else if(PHYS_INPUT_BUTTON_ATCK(this) || this.impulse == 10 || this.impulse == 15 || this.impulse == 18 || (this.impulse >= 200 && this.impulse <= 209)) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectateNext(this)) { + TRANSMUTE(Spectator, this); + } else { + TRANSMUTE(Observer, this); + PutClientInServer(this); + } + this.impulse = 0; + } else if(this.impulse == 12 || this.impulse == 16 || this.impulse == 19 || (this.impulse >= 220 && this.impulse <= 229)) { + this.flags &= ~FL_JUMPRELEASED; + if(SpectatePrev(this)) { + TRANSMUTE(Spectator, this); + } else { + TRANSMUTE(Observer, this); + PutClientInServer(this); + } + this.impulse = 0; + } else if (PHYS_INPUT_BUTTON_ATCK2(this)) { + this.flags &= ~FL_JUMPRELEASED; + TRANSMUTE(Observer, this); + PutClientInServer(this); + } else { + if(!SpectateUpdate(this)) + PutObserverInServer(this); + } + } else { + if (!(PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_ATCK2(this))) { + this.flags |= FL_JUMPRELEASED; + if(this.flags & FL_SPAWNING) + { + this.flags &= ~FL_SPAWNING; + Join(this); + return; + } + } + if(!SpectateUpdate(this)) + PutObserverInServer(this); + } + + this.flags |= FL_CLIENT | FL_NOTARGET; +} + +void vehicles_enter (entity pl, entity veh); +void PlayerUseKey(entity this) +{ + if (!IS_PLAYER(this)) + return; + + if(this.vehicle) + { + if(!gameover) + { + vehicles_exit(this.vehicle, VHEF_NORMAL); + return; + } + } + else if(autocvar_g_vehicles_enter) + { + if(!STAT(FROZEN, this)) + if(!IS_DEAD(this)) + if(!gameover) + { + entity head, closest_target = NULL; + head = WarpZone_FindRadius(this.origin, autocvar_g_vehicles_enter_radius, true); + + while(head) // find the closest acceptable target to enter + { + if(IS_VEHICLE(head)) + if(!IS_DEAD(head)) + if(!head.owner || ((head.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(head.owner, this))) + if(head.takedamage != DAMAGE_NO) + { + if(closest_target) + { + if(vlen2(this.origin - head.origin) < vlen2(this.origin - closest_target.origin)) + { closest_target = head; } + } + else { closest_target = head; } + } + + head = head.chain; + } + + if(closest_target) { vehicles_enter(this, closest_target); return; } + } + } + + // a use key was pressed; call handlers + MUTATOR_CALLHOOK(PlayerUseKey, this); +} + + +/* +============= +PlayerPreThink + +Called every frame for each client before the physics are run +============= +*/ +.float usekeypressed; +.float last_vehiclecheck; +.int items_added; +void PlayerPreThink (entity this) +{ + WarpZone_PlayerPhysics_FixVAngle(this); + + STAT(GAMESTARTTIME, this) = game_starttime; + STAT(ROUNDSTARTTIME, this) = round_starttime; + STAT(ALLOW_OLDVORTEXBEAM, this) = autocvar_g_allow_oldvortexbeam; + STAT(LEADLIMIT, this) = autocvar_leadlimit; + + STAT(WEAPONSINMAP, this) = weaponsInMap; + + if (frametime) { + // physics frames: update anticheat stuff + anticheat_prethink(this); + } + + if (blockSpectators && frametime) { + // WORKAROUND: only use dropclient in server frames (frametime set). + // Never use it in cl_movement frames (frametime zero). + checkSpectatorBlock(this); + } + + zoomstate_set = false; + + // Check for nameless players + if (isInvisibleString(this.netname)) { + this.netname = strzone(sprintf("Player#%d", this.playerid)); + // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? + } + if (this.netname != this.netname_previous) { + if (autocvar_sv_eventlog) { + GameLogEcho(strcat(":name:", ftos(this.playerid), ":", this.netname)); + } + if (this.netname_previous) strunzone(this.netname_previous); + this.netname_previous = strzone(this.netname); + } + + // version nagging + if (this.version_nagtime && this.cvar_g_xonoticversion && time > this.version_nagtime) { + this.version_nagtime = 0; + if (strstrofs(this.cvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(this.cvar_g_xonoticversion, "autobuild", 0) >= 0) { + // git client + } else if (strstrofs(autocvar_g_xonoticversion, "git", 0) >= 0 || strstrofs(autocvar_g_xonoticversion, "autobuild", 0) >= 0) { + // git server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_BETA, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } else { + int r = vercmp(this.cvar_g_xonoticversion, autocvar_g_xonoticversion); + if (r < 0) { // old client + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OUTDATED, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } else if (r > 0) { // old server + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_VERSION_OLD, autocvar_g_xonoticversion, this.cvar_g_xonoticversion); + } + } + } + + // GOD MODE info + if (!(this.flags & FL_GODMODE) && this.max_armorvalue) + { + Send_Notification(NOTIF_ONE_ONLY, this, MSG_INFO, INFO_GODMODE_OFF, this.max_armorvalue); + this.max_armorvalue = 0; + } + + if (STAT(FROZEN, this) == 2) + { + this.revive_progress = bound(0, this.revive_progress + frametime * this.revive_speed, 1); + this.health = max(1, this.revive_progress * start_health); + this.iceblock.alpha = bound(0.2, 1 - this.revive_progress, 1); + + if (this.revive_progress >= 1) + Unfreeze(this); + } + else if (STAT(FROZEN, this) == 3) + { + this.revive_progress = bound(0, this.revive_progress - frametime * this.revive_speed, 1); + this.health = max(0, autocvar_g_nades_ice_health + (start_health-autocvar_g_nades_ice_health) * this.revive_progress ); + + if (this.health < 1) + { + if (this.vehicle) + vehicles_exit(this.vehicle, VHEF_RELEASE); + if(this.event_damage) + this.event_damage(this, this, this.frozen_by, 1, DEATH_NADE_ICE_FREEZE.m_id, this.origin, '0 0 0'); + } + else if (this.revive_progress <= 0) + Unfreeze(this); + } + + MUTATOR_CALLHOOK(PlayerPreThink, this); + + if(autocvar_g_vehicles_enter && (time > this.last_vehiclecheck) && !gameover && !this.vehicle) + if(IS_PLAYER(this) && !STAT(FROZEN, this) && !IS_DEAD(this)) + { + FOREACH_ENTITY_RADIUS(this.origin, autocvar_g_vehicles_enter_radius, IS_VEHICLE(it), + { + if(!IS_DEAD(it) && it.takedamage != DAMAGE_NO) + if((it.vehicle_flags & VHF_MULTISLOT) && SAME_TEAM(it.owner, this)) + { + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_GUNNER); + } + else if(!it.owner) + { + if(!it.team || SAME_TEAM(this, it)) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER); + else if(autocvar_g_vehicles_steal) + Send_Notification(NOTIF_ONE, this, MSG_CENTER, CENTER_VEHICLE_ENTER_STEAL); + } + }); + + this.last_vehiclecheck = time + 1; + } + + if(!this.cvar_cl_newusekeysupported) // FIXME remove this - it was a stupid idea to begin with, we can JUST use the button + { + if(PHYS_INPUT_BUTTON_USE(this) && !this.usekeypressed) + PlayerUseKey(this); + this.usekeypressed = PHYS_INPUT_BUTTON_USE(this); + } + + if (IS_REAL_CLIENT(this)) + PrintWelcomeMessage(this); + + if (IS_PLAYER(this)) { + CheckRules_Player(this); + + if (gameover || intermission_running) { + if(intermission_running) + IntermissionThink(this); + return; + } + + if (timeout_status == TIMEOUT_ACTIVE) { + // don't allow the player to turn around while game is paused + // FIXME turn this into CSQC stuff + this.v_angle = this.lastV_angle; + this.angles = this.lastV_angle; + this.fixangle = true; + } + + if (frametime) player_powerups(this); + + if (IS_DEAD(this)) { + if (this.personal && g_race_qualifying) { + if (time > this.respawn_time) { + STAT(RESPAWN_TIME, this) = this.respawn_time = time + 1; // only retry once a second + respawn(this); + this.impulse = CHIMPULSE_SPEEDRUN.impulse; + } + } else { + if (frametime) player_anim(this); + bool button_pressed = (PHYS_INPUT_BUTTON_ATCK(this) || PHYS_INPUT_BUTTON_JUMP(this) || PHYS_INPUT_BUTTON_ATCK2(this) || PHYS_INPUT_BUTTON_HOOK(this) || PHYS_INPUT_BUTTON_USE(this)); + + switch(this.deadflag) + { + case DEAD_DYING: + { + if ((this.respawn_flags & RESPAWN_FORCE) && !(this.respawn_time < this.respawn_time_max)) + this.deadflag = DEAD_RESPAWNING; + else if (!button_pressed || (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE))) + this.deadflag = DEAD_DEAD; + break; + } + case DEAD_DEAD: + { + if (button_pressed) + this.deadflag = DEAD_RESPAWNABLE; + else if (time >= this.respawn_time_max && (this.respawn_flags & RESPAWN_FORCE)) + this.deadflag = DEAD_RESPAWNING; + break; + } + case DEAD_RESPAWNABLE: + { + if (!button_pressed || (this.respawn_flags & RESPAWN_FORCE)) + this.deadflag = DEAD_RESPAWNING; + break; + } + case DEAD_RESPAWNING: + { + if (time > this.respawn_time) + { + this.respawn_time = time + 1; // only retry once a second + this.respawn_time_max = this.respawn_time; + respawn(this); + } + break; + } + } + + ShowRespawnCountdown(this); + + if (this.respawn_flags & RESPAWN_SILENT) + STAT(RESPAWN_TIME, this) = 0; + else if ((this.respawn_flags & RESPAWN_FORCE) && this.respawn_time < this.respawn_time_max) + { + if (time < this.respawn_time) + STAT(RESPAWN_TIME, this) = this.respawn_time; + else if (this.deadflag != DEAD_RESPAWNING) + STAT(RESPAWN_TIME, this) = -this.respawn_time_max; + } + else + STAT(RESPAWN_TIME, this) = this.respawn_time; + } + + // if respawning, invert stat_respawn_time to indicate this, the client translates it + if (this.deadflag == DEAD_RESPAWNING && STAT(RESPAWN_TIME, this) > 0) + STAT(RESPAWN_TIME, this) *= -1; + + return; + } + + this.prevorigin = this.origin; + + bool do_crouch = PHYS_INPUT_BUTTON_CROUCH(this); + if (this.hook.state) { + 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); + + // LordHavoc: allow firing on move frames (sub-ticrate), this gives better timing on slow servers + //if(frametime) + { + this.items &= ~this.items_added; + + //for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + //{ + //.entity weaponentity = weaponentities[slot]; + //W_WeaponFrame(this, weaponentity); + //} + .entity weaponentity = weaponentities[0]; // TODO + W_WeaponFrame(this, weaponentity); + + this.items_added = 0; + if (this.items & ITEM_Jetpack.m_itemid && (this.items & ITEM_JetpackRegen.m_itemid || this.ammo_fuel >= 0.01)) + this.items_added |= IT_FUEL; + + this.items |= this.items_added; + } + + player_regen(this); + + // WEAPONTODO: Add a weapon request for this + // rot vortex charge to the charge limit + if (WEP_CVAR(vortex, charge_rot_rate) && this.vortex_charge > WEP_CVAR(vortex, charge_limit) && this.vortex_charge_rottime < time) + this.vortex_charge = bound(WEP_CVAR(vortex, charge_limit), this.vortex_charge - WEP_CVAR(vortex, charge_rot_rate) * frametime / W_TICSPERFRAME, 1); + + if (frametime) player_anim(this); + + // secret status + secrets_setstatus(this); + + // monsters status + monsters_setstatus(this); + + this.dmg_team = max(0, this.dmg_team - autocvar_g_teamdamage_resetspeed * frametime); + } + else if (gameover || intermission_running) { + if(intermission_running) + IntermissionThink(this); + return; + } + else if (IS_OBSERVER(this)) { + ObserverThink(this); + } + else if (IS_SPEC(this)) { + SpectatorThink(this); + } + + // WEAPONTODO: Add weapon request for this + if (!zoomstate_set) { + SetZoomState(this, + PHYS_INPUT_BUTTON_ZOOM(this) || PHYS_INPUT_BUTTON_ZOOMSCRIPT(this) + || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_VORTEX) + || (PHYS_INPUT_BUTTON_ATCK2(this) && PS(this).m_weapon == WEP_RIFLE && WEP_CVAR(rifle, secondary) == 0) + ); + } + + if (this.teamkill_soundtime && time > this.teamkill_soundtime) + { + this.teamkill_soundtime = 0; + + entity e = this.teamkill_soundsource; + entity oldpusher = e.pusher; + e.pusher = this; + PlayerSound(e, playersound_teamshoot, CH_VOICE, VOL_BASEVOICE, VOICETYPE_LASTATTACKER_ONLY); + e.pusher = oldpusher; + } + + if (this.taunt_soundtime && time > this.taunt_soundtime) { + this.taunt_soundtime = 0; + PlayerSound(this, playersound_taunt, CH_VOICE, VOL_BASEVOICE, VOICETYPE_AUTOTAUNT); + } + + target_voicescript_next(this); + + // WEAPONTODO: Move into weaponsystem somehow + // if a player goes unarmed after holding a loaded weapon, empty his clip size and remove the crosshair ammo ring + if (PS(this).m_weapon == WEP_Null) + this.clip_load = this.clip_size = 0; +} + +void DrownPlayer(entity this) +{ + if(IS_DEAD(this)) + return; + + if (this.waterlevel != WATERLEVEL_SUBMERGED || this.vehicle) + { + if(this.air_finished < time) + PlayerSound(this, playersound_gasp, CH_PLAYER, VOL_BASE, VOICETYPE_PLAYERSOUND); + this.air_finished = time + autocvar_g_balance_contents_drowndelay; + this.dmg = 2; + } + else if (this.air_finished < time) + { // drown! + if (this.pain_finished < time) + { + Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_drowning * autocvar_g_balance_contents_damagerate, DEATH_DROWN.m_id, this.origin, '0 0 0'); + this.pain_finished = time + 0.5; + } + } +} + +.bool move_qcphysics; + +void Player_Physics(entity this) +{ + set_movetype(this, this.move_movetype); + + if(!this.move_qcphysics) + return; + + if(!frametime && !this.pm_frametime) + return; + + Movetype_Physics_NoMatchTicrate(this, this.pm_frametime, true); + + this.pm_frametime = 0; +} + +/* +============= +PlayerPostThink + +Called every frame for each client after the physics are run +============= +*/ +.float idlekick_lasttimeleft; +void PlayerPostThink (entity this) +{ + Player_Physics(this); + + if (sv_maxidle > 0) + if (frametime) // WORKAROUND: only use dropclient in server frames (frametime set). Never use it in cl_movement frames (frametime zero). + if (IS_REAL_CLIENT(this)) + if (IS_PLAYER(this) || sv_maxidle_spectatorsareidle) + { + int totalClients = 0; + if(sv_maxidle_slots > 0) + { + FOREACH_CLIENT(IS_REAL_CLIENT(it) || sv_maxidle_slots_countbots, + { + ++totalClients; + }); + } + + if (sv_maxidle_slots > 0 && (maxclients - totalClients) > sv_maxidle_slots) + { /* do nothing */ } + else if (time - this.parm_idlesince < 1) // instead of (time == this.parm_idlesince) to support sv_maxidle <= 10 + { + if (this.idlekick_lasttimeleft) + { + this.idlekick_lasttimeleft = 0; + Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_IDLING); + } + } + else + { + float timeleft = ceil(sv_maxidle - (time - this.parm_idlesince)); + if (timeleft == min(10, sv_maxidle - 1)) { // - 1 to support sv_maxidle <= 10 + if (!this.idlekick_lasttimeleft) + Send_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CENTER_DISCONNECT_IDLING, timeleft); + } + if (timeleft <= 0) { + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_QUIT_KICK_IDLING, this.netname); + dropclient(this); + return; + } + else if (timeleft <= 10) { + if (timeleft != this.idlekick_lasttimeleft) { + Send_Notification(NOTIF_ONE, this, MSG_ANNCE, Announcer_PickNumber(CNT_IDLE, timeleft)); + } + this.idlekick_lasttimeleft = timeleft; + } + } + } + + CheatFrame(this); + + //CheckPlayerJump(); + if (gameover) + { + this.solid = SOLID_NOT; + this.takedamage = DAMAGE_NO; + set_movetype(this, MOVETYPE_NONE); + } + + if (IS_PLAYER(this)) { + DrownPlayer(this); + CheckRules_Player(this); + UpdateChatBubble(this); + if (this.impulse) ImpulseCommands(this); + if (gameover) + { + CSQCMODEL_AUTOUPDATE(this); + return; + } + GetPressedKeys(this); + } + + if (this.waypointsprite_attachedforcarrier) { + vector v = healtharmor_maxdamage(this.health, this.armorvalue, autocvar_g_balance_armor_blockpercent, DEATH_WEAPON.m_id); + WaypointSprite_UpdateHealth(this.waypointsprite_attachedforcarrier, '1 0 0' * v); + } + + playerdemo_write(this); + + CSQCMODEL_AUTOUPDATE(this); +} diff --git a/qcsrc/server/client.qh b/qcsrc/server/client.qh new file mode 100644 index 0000000000..c2317aff02 --- /dev/null +++ b/qcsrc/server/client.qh @@ -0,0 +1,115 @@ +#pragma once + +void ClientState_attach(entity this); + +IntrusiveList g_players; +STATIC_INIT(g_players) { g_players = IL_NEW(); } + +CLASS(Client, Object) + /** Client name */ + ATTRIB(Client, netname, string, this.netname); + ATTRIB(Client, colormap, int, this.colormap); + ATTRIB(Client, team, int, this.team); + ATTRIB(Client, clientcolors, int, this.clientcolors); + /** Client IP */ + ATTRIB(Client, netaddress, string, this.netaddress); + ATTRIB(Client, playermodel, string, this.playermodel); + ATTRIB(Client, playerskin, int, this.playerskin); + + /** fingerprint of CA key the player used to authenticate */ + ATTRIB(Client, crypto_keyfp, string, this.crypto_keyfp); + /** fingerprint of CA key the server used to authenticate to the player */ + ATTRIB(Client, crypto_mykeyfp, string, this.crypto_mykeyfp); + /** fingerprint of ID used by the player entity, or string_null if not identified */ + ATTRIB(Client, crypto_idfp, string, this.crypto_idfp); + /** set if the player's ID has been signed */ + ATTRIB(Client, crypto_idfp_signed, bool, this.crypto_idfp_signed); + /** the string "AES128" if encrypting, and string_null if plaintext */ + ATTRIB(Client, crypto_encryptmethod, string, this.crypto_encryptmethod); + /** the string "HMAC-SHA256" if signing, and string_null if plaintext */ + ATTRIB(Client, crypto_signmethod, string, this.crypto_signmethod); + + // custom + + ATTRIB(Client, playerid, int, this.playerid); + + METHOD(Client, m_unwind, bool(Client this)); + + STATIC_METHOD(Client, Add, void(Client this, int _team)); + STATIC_METHOD(Client, Remove, void(Client this)); + + INIT(Client) { + if (this.m_unwind(this)) return this; + make_impure(this); + this.classname = "player_joining"; + static int playerid_last; + this.playerid = ++playerid_last; + ClientState_attach(this); + } + DESTRUCTOR(Client) { + Client_Remove(this); + } + CONSTRUCTOR(Client, string name) { + CONSTRUCT(Client); + this.netname = name; + this.netaddress = "local"; + this.playermodel = "models/player/megaerebus.iqm"; + } +ENDCLASS(Client) + +CLASS(Observer, Client) + INIT(Observer) { + this.classname = STR_OBSERVER; + } + DESTRUCTOR(Observer) { } +ENDCLASS(Observer) + +CLASS(Spectator, Client) + INIT(Spectator) { + this.classname = STR_SPECTATOR; + } + DESTRUCTOR(Spectator) { } +ENDCLASS(Spectator) + +CLASS(Player, Client) + INIT(Player) { + this.classname = STR_PLAYER; + IL_PUSH(g_players, this); + } + DESTRUCTOR(Player) { + IL_REMOVE(g_players, this); + } +ENDCLASS(Player) + +METHOD(Client, m_unwind, bool(Client this)) +{ + TC(Client, this); + #define UNWIND(class) MACRO_BEGIN if (this.instanceOf##class) { METHOD_REFERENCE(class, dtorimpl)(this); } MACRO_END + switch (this.classname) { + case "Observer": + UNWIND(Spectator); + UNWIND(Player); + return true; + case "Spectator": + UNWIND(Observer); + UNWIND(Player); + return true; + case "Player": + UNWIND(Observer); + UNWIND(Spectator); + return true; + } + #undef UNWIND + return false; +} + +float c1, c2, c3, c4; + +void play_countdown(entity this, float finished, Sound samp); + +float CalcRotRegen(float current, float regenstable, float regenfactor, float regenlinear, float regenframetime, float rotstable, float rotfactor, float rotlinear, float rotframetime, float limit); + +bool Spectate(entity this, entity pl); + +#define SPECTATE_COPY() [[accumulate]] void SpectateCopy(entity this, entity spectatee) +#define SPECTATE_COPYFIELD(fld) SPECTATE_COPY() { this.(fld) = spectatee.(fld); } diff --git a/qcsrc/server/command/_mod.inc b/qcsrc/server/command/_mod.inc index fa15432311..2cabd69c7f 100644 --- a/qcsrc/server/command/_mod.inc +++ b/qcsrc/server/command/_mod.inc @@ -1,5 +1,4 @@ // generated file; do not modify -#include #include #include #ifdef SVQC @@ -8,4 +7,5 @@ #include #include #include +#include #include diff --git a/qcsrc/server/command/_mod.qh b/qcsrc/server/command/_mod.qh index 60e34ffe52..6a3b175a4b 100644 --- a/qcsrc/server/command/_mod.qh +++ b/qcsrc/server/command/_mod.qh @@ -1,8 +1,11 @@ // generated file; do not modify -#include #include #include +#ifdef SVQC + #include +#endif #include #include #include +#include #include diff --git a/qcsrc/server/command/all.qc b/qcsrc/server/command/all.qc deleted file mode 100644 index bc15eeb689..0000000000 --- a/qcsrc/server/command/all.qc +++ /dev/null @@ -1,2 +0,0 @@ -#include "all.qh" -#include diff --git a/qcsrc/server/command/all.qh b/qcsrc/server/command/all.qh deleted file mode 100644 index cde5ef367a..0000000000 --- a/qcsrc/server/command/all.qh +++ /dev/null @@ -1,28 +0,0 @@ -#pragma once - -#include -REGISTRY(SERVER_COMMANDS, BITS(7)) -#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL) -REGISTER_REGISTRY(SERVER_COMMANDS) -REGISTRY_SORT(SERVER_COMMANDS) - -#define SERVER_COMMAND(id, description) \ - CLASS(servercommand_##id, Command) \ - ATTRIB(servercommand_##id, m_name, string, #id); \ - ATTRIB(servercommand_##id, m_description, string, description); \ - ENDCLASS(servercommand_##id) \ - REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \ - METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command)) - -STATIC_INIT(SERVER_COMMANDS_aliases) { - FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv")))); -} - -#include "sv_cmd.qh" - -#include "banning.qh" -#include "cmd.qh" -#include "common.qh" -#include "getreplies.qh" -#include "radarmap.qh" -#include "vote.qh" diff --git a/qcsrc/server/command/banning.qc b/qcsrc/server/command/banning.qc index d6968c262d..8a35bec292 100644 --- a/qcsrc/server/command/banning.qc +++ b/qcsrc/server/command/banning.qc @@ -1,10 +1,10 @@ #include "banning.qh" -#include +#include #include "banning.qh" #include "common.qh" -#include "../cl_player.qh" +#include "../player.qh" #include "../ipban.qh" #include diff --git a/qcsrc/server/command/cmd.qc b/qcsrc/server/command/cmd.qc index 2ede443b36..ea9610b160 100644 --- a/qcsrc/server/command/cmd.qc +++ b/qcsrc/server/command/cmd.qc @@ -1,18 +1,18 @@ #include "cmd.qh" -#include +#include #include "common.qh" #include "vote.qh" #include "../campaign.qh" #include "../cheats.qh" -#include "../cl_player.qh" +#include "../player.qh" #include "../ipban.qh" #include "../mapvoting.qh" #include "../scores.qh" #include "../teamplay.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #ifdef SVQC #include @@ -30,8 +30,8 @@ #include -#include -#include +#include +#include #include #include @@ -159,33 +159,19 @@ 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) { switch (request) { case CMD_REQUEST_COMMAND: { - if (IS_CLIENT(caller)) - { - if (!IS_PLAYER(caller) && !lockteams && !gameover) - { - if (caller.caplayer) return; - if (nJoinAllowed(caller, caller)) - { - if (autocvar_g_campaign) campaign_bots_may_start = true; - TRANSMUTE(Player, caller); - PlayerScore_Clear(caller); - Kill_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CPID_PREVENT_JOIN); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((teamplay && caller.team != -1) ? APP_TEAM_ENT(caller, INFO_JOIN_PLAY_TEAM) : INFO_JOIN_PLAY), caller.netname); - WITHSELF(caller, PutClientInServer()); - } - else - { - // player may not join because of g_maxplayers is set - Send_Notification(NOTIF_ONE_ONLY, caller, MSG_CENTER, CENTER_JOIN_PREVENT); - } - } - } + if (!gameover) + if (IS_CLIENT(caller) && !IS_PLAYER(caller)) + if (joinAllowed(caller)) + Join(caller); + return; // never fall through to usage } @@ -326,6 +312,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) { switch (request) @@ -363,7 +350,7 @@ void ClientCommand_selectteam(entity caller, float request, float argc) if (selection) { - if (caller.team == selection && !IS_DEAD(caller)) + if (caller.team == selection && selection != -1 && !IS_DEAD(caller)) { sprint(caller, "^7You already are on that team.\n"); } @@ -385,6 +372,8 @@ void ClientCommand_selectteam(entity caller, float request, float argc) } ClientKill_TeamChange(caller, selection); } + if(!IS_PLAYER(caller)) + caller.team_selected = true; // avoids asking again for team selection on join } } else @@ -488,9 +477,11 @@ void ClientCommand_spectate(entity caller, float request) if (mutator_returnvalue == MUT_SPECCMD_RETURN) return; - if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE) && autocvar_sv_spectate == 1) ClientKill_TeamChange(caller, -2); // observe + if ((IS_PLAYER(caller) || mutator_returnvalue == MUT_SPECCMD_FORCE)) + if (autocvar_sv_spectate == 1) + ClientKill_TeamChange(caller, -2); // observe } - return; // never fall through to usage + return; // never fall through to usage } default: @@ -704,8 +695,8 @@ void ClientCommand_macro_write_aliases(float fh) // ====================================== // If this function exists, server game code parses clientcommand before the engine code gets it. -void SV_ParseClientCommand(string command) -{ENGINE_EVENT(); +void SV_ParseClientCommand(entity this, string command) +{ // If invalid UTF-8, don't even parse it string command2 = ""; float len = strlen(command); diff --git a/qcsrc/server/command/cmd.qh b/qcsrc/server/command/cmd.qh index 5f2c86e408..3f8e4c47af 100644 --- a/qcsrc/server/command/cmd.qh +++ b/qcsrc/server/command/cmd.qh @@ -2,7 +2,6 @@ .float cmd_floodtime; .float cmd_floodcount; -.float lms_spectate_warning; string MapVote_Suggest(entity this, string m); diff --git a/qcsrc/server/command/common.qc b/qcsrc/server/command/common.qc index 3c97737e54..5b01f13494 100644 --- a/qcsrc/server/command/common.qc +++ b/qcsrc/server/command/common.qc @@ -1,10 +1,10 @@ #include "common.qh" -#include +#include #include "common.qh" #include "../scores.qh" -#include +#include #include #include @@ -189,7 +189,7 @@ void timeout_handler_reset(entity this) timeout_time = 0; timeout_leadtime = 0; - remove(this); + delete(this); } void timeout_handler_think(entity this) @@ -210,6 +210,7 @@ void timeout_handler_think(entity this) } else // time to end the timeout { + Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_TIMEIN); timeout_status = TIMEOUT_INACTIVE; // reset the slowmo value back to normal @@ -364,10 +365,10 @@ void CommonCommand_editmob(int request, entity caller, int argc) if (arg_lower == "list") { print_to(caller, monsterlist_reply); return; } - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA( - if(it.realowner == caller) - ++tmp_moncount; - )); + IL_EACH(g_monsters, it.realowner == caller, + { + ++tmp_moncount; + }); if (!autocvar_g_monsters) { print_to(caller, "Monsters are disabled"); return; } if (autocvar_g_monsters_max <= 0 || autocvar_g_monsters_max_perplayer <= 0) { print_to(caller, "Monster spawning is disabled"); return; } @@ -380,17 +381,17 @@ void CommonCommand_editmob(int request, entity caller, int argc) if (tmp_moncount >= autocvar_g_monsters_max_perplayer) { print_to(caller, "You can't spawn any more monsters"); return; } bool found = false; - for (int i = MON_FIRST; i <= MON_LAST; ++i) + FOREACH(Monsters, it != MON_Null && it.netname == arg_lower, { - mon = get_monsterinfo(i); - if (mon.netname == arg_lower) { found = true; break; } - } + found = true; + break; + }); if (!found && arg_lower != "random") { 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); - mon = spawnmonster(arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag); + mon = spawnmonster(spawn(), arg_lower, 0, caller, caller, trace_endpos, false, false, moveflag); print_to(caller, strcat("Spawned ", mon.monster_name)); return; } @@ -436,10 +437,12 @@ void CommonCommand_editmob(int request, entity caller, int argc) int tmp_remcount = 0; - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA( + IL_EACH(g_monsters, true, + { Monster_Remove(it); ++tmp_remcount; - )); + }); + IL_CLEAR(g_monsters); monsters_total = monsters_killed = totalspawned = 0; diff --git a/qcsrc/server/command/common.qh b/qcsrc/server/command/common.qh index 63b1c708f2..978a92b14f 100644 --- a/qcsrc/server/command/common.qh +++ b/qcsrc/server/command/common.qh @@ -1,6 +1,6 @@ #pragma once -#include +#include REGISTRY(COMMON_COMMANDS, BITS(7)) #define COMMON_COMMANDS_from(i) _COMMON_COMMANDS_from(i, NULL) REGISTER_REGISTRY(COMMON_COMMANDS) @@ -19,10 +19,9 @@ STATIC_INIT(COMMON_COMMANDS_aliases) { } #include "vote.qh" -#include +#include -#include -#include +#include // ============================================================ // Shared declarations for server commands, written by Samual diff --git a/qcsrc/server/command/getreplies.qc b/qcsrc/server/command/getreplies.qc index 93ce85a61a..d01448aad8 100644 --- a/qcsrc/server/command/getreplies.qc +++ b/qcsrc/server/command/getreplies.qc @@ -1,14 +1,15 @@ #include "getreplies.qh" -#include +#include #include "getreplies.qh" #include "../race.qh" #include +#include #include #include -#include +#include // ========================================================= // Reply messages for common commands, re-worked by Samual @@ -241,16 +242,22 @@ string getmaplist() return sprintf("^7Maps in list: %s\n", maplist); } - +const int LSMAPS_MAX = 250; string getlsmaps() { string lsmaps = "", col; - float i, newmaps = 0; + bool newmaps = false; + int added = 0; - for (i = 0; i < MapInfo_count; ++i) + for (int i = 0; i < MapInfo_count; ++i) { if ((MapInfo_Get_ByID(i)) && !(MapInfo_Map_flags & MapInfo_ForbiddenFlags())) { + ++added; + + if(added > LSMAPS_MAX) + continue; // we still get the added count, but skip the actual processing + // todo: Check by play count of maps for other game types? if ( (g_race && !stof(db_get(ServerProgsDB, strcat(MapInfo_Map_bspname, RACE_RECORD, "time")))) @@ -270,19 +277,22 @@ string getlsmaps() } } + if(added > LSMAPS_MAX) + lsmaps = sprintf("%s^7(%d not listed)", lsmaps, added - LSMAPS_MAX); + MapInfo_ClearTemps(); - return sprintf("^7Maps available (%d)%s: %s\n", tokenize_console(lsmaps), (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps); + return sprintf("^7Maps available (%d)%s: %s\n", added, (newmaps ? " (New maps have asterisks marked in blue)" : ""), lsmaps); } string getmonsterlist() { - string monsterlist = "", col; + string monsterlist = ""; - for (int i = MON_FIRST; i <= MON_LAST; ++i) + FOREACH(Monsters, it != MON_Null, { - if (i % 2) col = "^2"; else col = "^3"; - monsterlist = sprintf("%s%s%s ", monsterlist, col, (get_monsterinfo(i)).netname); - } + string col = ((i % 2) ? "^2" : "^3"); + monsterlist = sprintf("%s%s%s ", monsterlist, col, it.netname); + }); return sprintf("^7Monsters available: %s\n", monsterlist); } diff --git a/qcsrc/server/command/radarmap.qc b/qcsrc/server/command/radarmap.qc index b12977823e..ad86afb23c 100644 --- a/qcsrc/server/command/radarmap.qc +++ b/qcsrc/server/command/radarmap.qc @@ -1,5 +1,5 @@ #include "radarmap.qh" -#include +#include #include "radarmap.qh" #include "../g_world.qh" @@ -26,7 +26,7 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b) float n, m; n = m = 0; - while (vlen(c - b) > 1) + while (vdist(c - b, >, 1)) { ++m; @@ -45,22 +45,22 @@ float FullTraceFraction(vector a, vector mi, vector ma, vector b) c = trace_endpos; } - if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations\n"); + if (n > 200) LOG_TRACE("HOLY SHIT! FullTraceFraction: ", ftos(n), " total traces, ", ftos(m), " iterations"); return white / (black + white); } -float RadarMapAtPoint_Trace(float x, float y, float w, float h, float zmin, float zsize, float q) +float RadarMapAtPoint_Trace(float e, float f, float w, float h, float zmin, float zsize, float q) { vector a, b, mi, ma; mi = '0 0 0'; ma = '1 0 0' * w + '0 1 0' * h; - a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; - b = '1 0 0' * x + '0 1 0' * y + '0 0 1' * (zsize + zmin); + a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin; + b = '1 0 0' * e + '0 1 0' * f + '0 0 1' * (zsize + zmin); return FullTraceFraction(a, mi, ma, b); } -float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, float zsize, float q) +float RadarMapAtPoint_LineBlock(float e, float f, float w, float h, float zmin, float zsize, float q) { vector o, mi, ma; float i, r; @@ -72,12 +72,12 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, mi = '0 0 0'; dz = (zsize / q) * '0 0 1'; ma = '1 0 0' * w + '0 1 0' * h + dz; - o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; + o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin; - if (x < world.absmin.x - w) return 0; - if (y < world.absmin.y - h) return 0; - if (x > world.absmax.x) return 0; - if (y > world.absmax.y) return 0; + if (e < world.absmin.x - w) return 0; + if (f < world.absmin.y - h) return 0; + if (e > world.absmax.x) return 0; + if (f > world.absmax.y) return 0; r = 0; for (i = 0; i < q; ++i) @@ -95,7 +95,7 @@ float RadarMapAtPoint_LineBlock(float x, float y, float w, float h, float zmin, } return r / q; } -float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, float zsize, float q) +float RadarMapAtPoint_Block(float e, float f, float w, float h, float zmin, float zsize, float q) { vector o, mi, ma; float i, r; @@ -107,12 +107,12 @@ float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, floa mi = '0 0 0'; dz = (zsize / q) * '0 0 1'; ma = '1 0 0' * w + '0 1 0' * h + dz; - o = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; + o = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin; - if (x < world.absmin.x - w) return 0; - if (y < world.absmin.y - h) return 0; - if (x > world.absmax.x) return 0; - if (y > world.absmax.y) return 0; + if (e < world.absmin.x - w) return 0; + if (f < world.absmin.y - h) return 0; + if (e > world.absmax.x) return 0; + if (f > world.absmax.y) return 0; r = 0; for (i = 0; i < q; ++i) @@ -122,7 +122,7 @@ float RadarMapAtPoint_Block(float x, float y, float w, float h, float zmin, floa } return r / q; } -float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, float zsize, float q) +float RadarMapAtPoint_Sample(float e, float f, float w, float h, float zmin, float zsize, float q) { vector a, b, mi, ma; @@ -133,7 +133,7 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo mi = '0 0 0'; ma = '1 0 0' * w + '0 1 0' * h; - a = '1 0 0' * x + '0 1 0' * y + '0 0 1' * zmin; + a = '1 0 0' * e + '0 1 0' * f + '0 0 1' * zmin; b = '1 0 0' * w + '0 1 0' * h + '0 0 1' * zsize; float c, i; @@ -151,31 +151,31 @@ float RadarMapAtPoint_Sample(float x, float y, float w, float h, float zmin, flo return c / q; } -void sharpen_set(int x, float v) +void sharpen_set(int b, float v) { - sharpen_buffer[x + 2 * RADAR_WIDTH_MAX] = v; + sharpen_buffer[b + 2 * RADAR_WIDTH_MAX] = v; } -float sharpen_getpixel(int x, int y) +float sharpen_getpixel(int b, int c) { - if (x < 0) return 0; - if (x >= RADAR_WIDTH_MAX) return 0; - if (y < 0) return 0; - if (y > 2) return 0; - return sharpen_buffer[x + y * RADAR_WIDTH_MAX]; + if (b < 0) return 0; + if (b >= RADAR_WIDTH_MAX) return 0; + if (c < 0) return 0; + if (c > 2) return 0; + return sharpen_buffer[b + c * RADAR_WIDTH_MAX]; } -float sharpen_get(float x, float a) +float sharpen_get(float b, float a) { - float sum = sharpen_getpixel(x, 1); + float sum = sharpen_getpixel(b, 1); if (a == 0) return sum; sum *= (8 + 1 / a); - sum -= sharpen_getpixel(x - 1, 0); - sum -= sharpen_getpixel(x - 1, 1); - sum -= sharpen_getpixel(x - 1, 2); - sum -= sharpen_getpixel(x + 1, 0); - sum -= sharpen_getpixel(x + 1, 1); - sum -= sharpen_getpixel(x + 1, 2); - sum -= sharpen_getpixel(x, 0); - sum -= sharpen_getpixel(x, 2); + sum -= sharpen_getpixel(b - 1, 0); + sum -= sharpen_getpixel(b - 1, 1); + sum -= sharpen_getpixel(b - 1, 2); + sum -= sharpen_getpixel(b + 1, 0); + sum -= sharpen_getpixel(b + 1, 1); + sum -= sharpen_getpixel(b + 1, 2); + sum -= sharpen_getpixel(b, 0); + sum -= sharpen_getpixel(b, 2); return bound(0, sum * a, 1); } void sharpen_shift(int w) @@ -207,7 +207,7 @@ void RadarMap_Next() localcmd(strcat("defer 1 \"sv_cmd radarmap --flags ", ftos(radarmapper.count), strcat(" --res ", ftos(radarmapper.size.x), " ", ftos(radarmapper.size.y), " --sharpen ", ftos(radarmapper.ltime), " --qual ", ftos(radarmapper.size.z)), "\"\n")); GotoNextMap(0); } - remove(radarmapper); + delete(radarmapper); radarmapper = NULL; } void RadarMap_Think(entity this) @@ -253,7 +253,7 @@ void RadarMap_Think(entity this) if (this.cnt < 0) { LOG_INFO("Error writing ", this.netname, "\n"); - remove(this); + delete(this); radarmapper = NULL; return; } @@ -439,7 +439,7 @@ float RadarMap_Make(float argc) default: i = argc; - remove(radarmapper); + delete(radarmapper); radarmapper = NULL; break; } diff --git a/qcsrc/server/command/reg.qc b/qcsrc/server/command/reg.qc new file mode 100644 index 0000000000..c0af5b5e09 --- /dev/null +++ b/qcsrc/server/command/reg.qc @@ -0,0 +1 @@ +#include "reg.qh" diff --git a/qcsrc/server/command/reg.qh b/qcsrc/server/command/reg.qh new file mode 100644 index 0000000000..b135c04601 --- /dev/null +++ b/qcsrc/server/command/reg.qh @@ -0,0 +1,18 @@ +#pragma once + +REGISTRY(SERVER_COMMANDS, BITS(7)) +#define SERVER_COMMANDS_from(i) _SERVER_COMMANDS_from(i, NULL) +REGISTER_REGISTRY(SERVER_COMMANDS) +REGISTRY_SORT(SERVER_COMMANDS) + +#define SERVER_COMMAND(id, description) \ + CLASS(servercommand_##id, Command) \ + ATTRIB(servercommand_##id, m_name, string, #id); \ + ATTRIB(servercommand_##id, m_description, string, description); \ + ENDCLASS(servercommand_##id) \ + REGISTER(SERVER_COMMANDS, CMD_SV, id, m_id, NEW(servercommand_##id)); \ + METHOD(servercommand_##id, m_invokecmd, void(servercommand_##id this, int request, entity caller, int arguments, string command)) + +STATIC_INIT(SERVER_COMMANDS_aliases) { + FOREACH(SERVER_COMMANDS, true, LAMBDA(localcmd(sprintf("alias %1$s \"%2$s %1$s ${* ?}\"\n", it.m_name, "qc_cmd_sv")))); +} diff --git a/qcsrc/server/command/sv_cmd.qc b/qcsrc/server/command/sv_cmd.qc index 895318affc..55879fd35b 100644 --- a/qcsrc/server/command/sv_cmd.qc +++ b/qcsrc/server/command/sv_cmd.qc @@ -1,5 +1,5 @@ #include "sv_cmd.qh" -#include "all.qh" +#include "_mod.qh" #include "banning.qh" #include "cmd.qh" @@ -9,20 +9,19 @@ #include "../anticheat.qh" #include "../campaign.qh" -#include "../cl_client.qh" -#include "../cl_player.qh" +#include "../client.qh" +#include "../player.qh" #include "../g_world.qh" #include "../ipban.qh" #include "../playerdemo.qh" #include "../teamplay.qh" -#include "../bot/bot.qh" -#include "../bot/navigation.qh" -#include "../bot/scripting.qh" +#include "../bot/api.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include +#include #include #include #include @@ -41,11 +40,11 @@ void PutObserverInServer(entity this); // used by GameCommand_make_mapinfo() void make_mapinfo_Think(entity this) { - if (MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) + if (_MapInfo_FilterGametype(MAPINFO_TYPE_ALL, 0, 0, 0, 1)) { LOG_INFO("Done rebuiling mapinfos.\n"); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); - remove(this); + delete(this); } else { @@ -141,7 +140,7 @@ void GameCommand_adminmsg(float request, float argc) } successful = strcat(successful, (successful ? ", " : ""), client.netname); - LOG_TRACE("Message sent to ", client.netname, "\n"); + LOG_TRACE("Message sent to ", client.netname); continue; } @@ -227,7 +226,7 @@ void GameCommand_anticheat(float request, float argc) if (accepted > 0) { - anticheat_report(client); + anticheat_report_to_eventlog(client); return; } else @@ -384,10 +383,16 @@ void GameCommand_bot_cmd(float request, float argc, string command) } else { - // let's start at token 2 so we can skip sv_cmd bot_cmd - bot = find_bot_by_number(stof(argv(2))); - if (bot == NULL) bot = find_bot_by_name(argv(2)); - if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1)); + if(argv(2) == "*" || argv(2) == "all") + FOREACH_CLIENT(IS_BOT_CLIENT(it), { + bot_queuecommand(it, substring(s, argv_start_index(3), -1)); + }); + else + { + bot = find_bot_by_number(stof(argv(2))); + if (bot == NULL) bot = find_bot_by_name(argv(2)); + if (bot) bot_queuecommand(bot, substring(s, argv_start_index(3), -1)); + } } } else @@ -409,17 +414,31 @@ void GameCommand_bot_cmd(float request, float argc, string command) } else if (argc >= 3) // this comes last { - bot = find_bot_by_number(stof(argv(1))); - if (bot == NULL) bot = find_bot_by_name(argv(1)); - if (bot) + if(argv(1) == "*" || argv(1) == "all") { - LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n")); - bot_queuecommand(bot, substring(command, argv_start_index(2), -1)); + int bot_num = 0; + FOREACH_CLIENT(IS_BOT_CLIENT(it), { + bot_queuecommand(it, substring(command, argv_start_index(2), -1)); + bot_num++; + }); + if(bot_num) + LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to all bots (", ftos(bot_num), ")\n")); return; } else { - LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n")); // don't return so that usage is shown + bot = find_bot_by_number(stof(argv(1))); + if (bot == NULL) bot = find_bot_by_name(argv(1)); + if (bot) + { + LOG_INFO(strcat("Command '", substring(command, argv_start_index(2), -1), "' sent to bot ", bot.netname, "\n")); + bot_queuecommand(bot, substring(command, argv_start_index(2), -1)); + return; + } + else + { + LOG_INFO(strcat("Error: Can't find bot with the name or id '", argv(1), "' - Did you mistype the command?\n")); // don't return so that usage is shown + } } } } @@ -429,10 +448,12 @@ void GameCommand_bot_cmd(float request, float argc, string command) case CMD_REQUEST_USAGE: { LOG_INFO("\nUsage:^3 sv_cmd bot_cmd client command [argument]\n"); - LOG_INFO(" 'client' can be either the name or entity id of the bot\n"); + LOG_INFO(" 'client' can be either the name of the bot or a progressive number (not the entity number!)\n"); + LOG_INFO(" can also be '*' or 'all' to allow sending the command to all the bots\n"); LOG_INFO(" For full list of commands, see bot_cmd help [command].\n"); - LOG_INFO("Examples: sv_cmd bot_cmd client cc \"say something\"\n"); - LOG_INFO(" sv_cmd bot_cmd client presskey jump\n"); + LOG_INFO("Examples: sv_cmd bot_cmd 1 cc \"say something\"\n"); + LOG_INFO(" sv_cmd bot_cmd 1 presskey jump\n"); + LOG_INFO(" sv_cmd bot_cmd * pause\n"); return; } } @@ -742,7 +763,7 @@ void GameCommand_gametype(float request, float argc) if (argv(1) != "") { string s = argv(1); - float t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype(); + Gametype t = MapInfo_Type_FromString(s), tsave = MapInfo_CurrentGametype(); if (t) { @@ -830,7 +851,7 @@ void GameCommand_gettaginfo(float request, float argc) LOG_INFO("bone not found\n"); } - remove(tmp_entity); + delete(tmp_entity); return; } } @@ -889,7 +910,7 @@ void GameCommand_animbench(float request, float argc) LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f1), " animtime ", ftos(n / t1), "/s\n"); LOG_INFO("model ", tmp_entity.model, " frame ", ftos(f2), " animtime ", ftos(n / t2), "/s\n"); - remove(tmp_entity); + delete(tmp_entity); return; } } @@ -1329,7 +1350,7 @@ void GameCommand_shuffleteams(float request) { if (teamplay) { - float x, t_teams, t_players, team_color; + float t_teams, t_players, team_color; // count the total amount of players and total amount of teams t_players = 0; @@ -1367,15 +1388,15 @@ void GameCommand_shuffleteams(float request) for (int i = 1; i <= t_teams; ++i) { // find out how many players to assign to this team - x = (t_players / t_teams); - x = ((i == 1) ? ceil(x) : floor(x)); + int pnum = (t_players / t_teams); + pnum = ((i == 1) ? ceil(pnum) : floor(pnum)); team_color = Team_NumberToTeam(i); // sort through the random list of players made earlier for (int z = 1; z <= maxclients; ++z) { - if (!(shuffleteams_teams[i] >= x)) + if (!(shuffleteams_teams[i] >= pnum)) { if (!(shuffleteams_players[z])) continue; // not a player, move on to next random slot @@ -1507,15 +1528,15 @@ void GameCommand_trace(float request, float argc) start = stov(vtos(start)); end = stov(vtos(end)); - tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL); + tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL); if (!trace_startsolid && trace_fraction < 1) { p = trace_endpos; - tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), p, MOVE_NOMONSTERS, NULL); + tracebox(p, PL_MIN_CONST, PL_MAX_CONST, p, MOVE_NOMONSTERS, NULL); if (trace_startsolid) { rint(42); // do an engine breakpoint on VM_rint so you can get the trace that errnoeously returns startsolid - tracebox(start, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), end, MOVE_NOMONSTERS, NULL); + tracebox(start, PL_MIN_CONST, PL_MAX_CONST, end, MOVE_NOMONSTERS, NULL); // how much do we need to back off? safe = 1; @@ -1523,7 +1544,7 @@ void GameCommand_trace(float request, float argc) for ( ; ; ) { pos = p * (1 - (safe + unsafe) * 0.5) + start * ((safe + unsafe) * 0.5); - tracebox(pos, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), pos, MOVE_NOMONSTERS, NULL); + tracebox(pos, PL_MIN_CONST, PL_MAX_CONST, pos, MOVE_NOMONSTERS, NULL); if (trace_startsolid) { if ((safe + unsafe) * 0.5 == unsafe) break; @@ -1539,7 +1560,7 @@ void GameCommand_trace(float request, float argc) LOG_INFO("safe distance to back off: ", ftos(safe * vlen(p - start)), "qu\n"); LOG_INFO("unsafe distance to back off: ", ftos(unsafe * vlen(p - start)), "qu\n"); - tracebox(p, STAT(PL_MIN, NULL) + '0.1 0.1 0.1', STAT(PL_MAX, NULL) - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL); + tracebox(p, PL_MIN_CONST + '0.1 0.1 0.1', PL_MAX_CONST - '0.1 0.1 0.1', p, MOVE_NOMONSTERS, NULL); if (trace_startsolid) LOG_INFO("trace_endpos much in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); else LOG_INFO("trace_endpos just in solid when tracing from ", vtos(start), " to ", vtos(end), " endpos ", vtos(p), "\n"); if (++hitcount >= 10) break; @@ -1553,7 +1574,7 @@ void GameCommand_trace(float request, float argc) { q = p + normalize(end - p) * (dq + dqf); if (q == q0) break; - tracebox(p, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), q, MOVE_NOMONSTERS, NULL); + tracebox(p, PL_MIN_CONST, PL_MAX_CONST, q, MOVE_NOMONSTERS, NULL); if (trace_startsolid) error("THIS ONE cannot happen"); if (trace_fraction > 0) dq += dqf * trace_fraction; dqf *= 0.5; diff --git a/qcsrc/server/command/vote.qc b/qcsrc/server/command/vote.qc index 337b428c55..b91a8f4df3 100644 --- a/qcsrc/server/command/vote.qc +++ b/qcsrc/server/command/vote.qc @@ -1,5 +1,5 @@ #include "vote.qh" -#include +#include #include "vote.qh" #include "common.qh" @@ -10,9 +10,10 @@ #include "../round_handler.qh" #include "../scores.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include +#include #include #include #include @@ -333,7 +334,13 @@ void VoteThink() // Resets the state of all clients, items, weapons, waypoints, ... of the map. void reset_map(bool dorespawn) { - if (time <= game_starttime && round_handler_IsActive()) round_handler_Reset(game_starttime); + if (time <= game_starttime) + { + if (gameover) + return; + if (round_handler_IsActive()) + round_handler_Reset(game_starttime); + } MUTATOR_CALLHOOK(reset_map_global); @@ -344,7 +351,7 @@ void reset_map(bool dorespawn) continue; } if (it.team_saved) it.team = it.team_saved; - if (it.flags & FL_PROJECTILE) remove(it); // remove any projectiles left + if (it.flags & FL_PROJECTILE) delete(it); // remove any projectiles left }); // Waypoints and assault start come LAST @@ -378,7 +385,7 @@ void reset_map(bool dorespawn) it.velocity = '0 0 0'; it.avelocity = '0 0 0'; it.movement = '0 0 0'; - WITHSELF(it, PutClientInServer()); + PutClientInServer(it); } } )); @@ -392,12 +399,15 @@ void ReadyRestart_think(entity this) restart_mapalreadyrestarted = true; reset_map(true); Score_ClearAll(); - remove(this); + delete(this); } // Forces a restart of the game without actually reloading the map // this is a mess... void ReadyRestart_force() { + if (time <= game_starttime && gameover) + return; + bprint("^1Server is restarting...\n"); VoteReset(); @@ -454,8 +464,7 @@ void ReadyRestart_force() void ReadyRestart() { - // no assault support yet... - if (g_assault | gameover | intermission_running | race_completing) localcmd("restart\n"); + if (MUTATOR_CALLHOOK(ReadyRestart_Deny) || gameover || race_completing) localcmd("restart\n"); else localcmd("\nsv_hook_gamerestart\n"); // Reset ALL scores, but only do that at the beginning of the countdown if sv_ready_restart_after_countdown is off! diff --git a/qcsrc/server/compat/_mod.inc b/qcsrc/server/compat/_mod.inc new file mode 100644 index 0000000000..69d031eaf3 --- /dev/null +++ b/qcsrc/server/compat/_mod.inc @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#include +#include +#include diff --git a/qcsrc/server/compat/_mod.qh b/qcsrc/server/compat/_mod.qh new file mode 100644 index 0000000000..bd658580e1 --- /dev/null +++ b/qcsrc/server/compat/_mod.qh @@ -0,0 +1,5 @@ +// generated file; do not modify +#include +#include +#include +#include diff --git a/qcsrc/server/compat/halflife.qc b/qcsrc/server/compat/halflife.qc new file mode 100644 index 0000000000..74e50137fd --- /dev/null +++ b/qcsrc/server/compat/halflife.qc @@ -0,0 +1,35 @@ +#include "halflife.qh" +.float roomtype; +.float radius; +.float pitch; +.float renderamt; +.float rendermode; +.vector rendercolor; + +spawnfunc(weapon_crossbow) {} +spawnfunc(weapon_handgrenade) {} +spawnfunc(ammo_crossbow) {} +spawnfunc(ammo_9mmclip) {} +spawnfunc(ammo_gaussclip) {} +spawnfunc(weapon_rpg) {} +spawnfunc(weapon_357) {} +void ammo_ARgrenades() {} +spawnfunc(item_battery) {} +spawnfunc(ammo_rpgclip) {} +void weapon_9mmAR() {} +spawnfunc(weapon_tripmine) {} +spawnfunc(weapon_snark) {} +spawnfunc(ammo_buckshot) {} +void ammo_9mmAR() {} +spawnfunc(ammo_357) {} +spawnfunc(weapon_gauss) {} +spawnfunc(weapon_hornetgun) {} +//spawnfunc(weapon_shotgun) {} +spawnfunc(item_healthkit) {} +spawnfunc(item_longjump) {} +spawnfunc(item_antidote) {} +spawnfunc(func_recharge) {} +spawnfunc(info_node) {} +spawnfunc(env_sound) {} +spawnfunc(light_spot) {} +spawnfunc(func_healthcharger) {} diff --git a/qcsrc/server/compat/halflife.qh b/qcsrc/server/compat/halflife.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/compat/halflife.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/compat/quake.qc b/qcsrc/server/compat/quake.qc new file mode 100644 index 0000000000..539042d636 --- /dev/null +++ b/qcsrc/server/compat/quake.qc @@ -0,0 +1,30 @@ +#include "quake.qh" + +#include + +spawnfunc(weapon_electro); +spawnfunc(weapon_hagar); +spawnfunc(weapon_machinegun); +spawnfunc(item_bullets); +spawnfunc(item_armor_mega); +spawnfunc(item_health_mega); +spawnfunc(item_health_medium); + +//*********************** +//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons +//*********************** +spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);} +spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);} +spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);} + +spawnfunc(item_spikes) {spawnfunc_item_bullets(this);} +//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);} // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard +spawnfunc(item_armor2) {spawnfunc_item_armor_mega(this);} +spawnfunc(item_armorInv) {spawnfunc_item_armor_mega(this);} // TODO: make sure we actually want this +spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);} + +//spawnfunc_item_spikes +//spawnfunc_item_health + + + diff --git a/qcsrc/server/compat/quake.qh b/qcsrc/server/compat/quake.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/compat/quake.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/compat/quake3.qc b/qcsrc/server/compat/quake3.qc new file mode 100644 index 0000000000..1c53de31ef --- /dev/null +++ b/qcsrc/server/compat/quake3.qc @@ -0,0 +1,208 @@ +#include "quake3.qh" + +#include + +spawnfunc(weapon_crylink); +spawnfunc(weapon_electro); +spawnfunc(weapon_hagar); +spawnfunc(weapon_machinegun); +spawnfunc(weapon_vortex); + +spawnfunc(target_items); + +spawnfunc(item_bullets); +spawnfunc(item_cells); +spawnfunc(item_rockets); +spawnfunc(item_shells); + +spawnfunc(item_jetpack); + +spawnfunc(item_armor_big); +spawnfunc(item_armor_mega); +spawnfunc(item_armor_small); + +spawnfunc(item_health_medium); +spawnfunc(item_health_mega); + +//*********************** +//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons +//*********************** + +// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG + +// SG -> SG +spawnfunc(ammo_shells) { spawnfunc_item_shells(this); } + +// MG -> MG +spawnfunc(ammo_bullets) { spawnfunc_item_bullets(this); } + +// GL -> Mortar +spawnfunc(ammo_grenades) { spawnfunc_item_rockets(this); } + +// LG -> Lightning +spawnfunc(weapon_lightning) { spawnfunc_weapon_electro(this); } +spawnfunc(ammo_lightning) { spawnfunc_item_cells(this); } + +// Plasma -> Hagar +spawnfunc(weapon_plasmagun) { spawnfunc_weapon_hagar(this); } +spawnfunc(ammo_cells) { spawnfunc_item_rockets(this); } + +// Rail -> Vortex +spawnfunc(weapon_railgun) { spawnfunc_weapon_vortex(this); } +spawnfunc(ammo_slugs) { spawnfunc_item_cells(this); } + +// BFG -> Crylink +spawnfunc(weapon_bfg) { spawnfunc_weapon_crylink(this); } +spawnfunc(ammo_bfg) { spawnfunc_item_cells(this); } + +// RL -> RL +spawnfunc(ammo_rockets) { spawnfunc_item_rockets(this); } + +// Armor +spawnfunc(item_armor_body) { spawnfunc_item_armor_mega(this); } +spawnfunc(item_armor_combat) { spawnfunc_item_armor_big(this); } +spawnfunc(item_armor_shard) { spawnfunc_item_armor_small(this); } +spawnfunc(item_enviro) { spawnfunc_item_invincible(this); } + +.float wait; +.float delay; + +// weapon remove ent from df +void target_init_verify(entity this) +{ + entity trigger, targ; + for(trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) + for(targ = NULL; (targ = find(targ, targetname, trigger.target)); ) + if (targ.classname == "target_init" || targ.classname == "target_give" || targ.classname == "target_items") + { + trigger.wait = 0; + trigger.delay = 0; + targ.wait = 0; + targ.delay = 0; + + //setsize(targ, trigger.mins, trigger.maxs); + //setorigin(targ, trigger.origin); + //remove(trigger); + } +} + +spawnfunc(target_init) +{ + this.spawnflags = 0; // remove all weapons except the ones listed below + this.netname = "shotgun"; // keep these weapons through the remove trigger + spawnfunc_target_items(this); + InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET); +} + +// weapon give ent from defrag +void target_give_init(entity this) +{ + IL_EACH(g_items, it.targetname == this.target, + { + if (it.classname == "weapon_rocketlauncher" || it.classname == "weapon_devastator") { + this.ammo_rockets += it.count * WEP_CVAR(devastator, ammo); + this.netname = "devastator"; + } + else if (it.classname == "weapon_plasmagun") { + this.ammo_rockets += it.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO + if(this.netname == "") + this.netname = "hagar"; + else + this.netname = strcat(this.netname, " hagar"); + } + else if (it.classname == "weapon_bfg") { + this.ammo_cells += it.count * WEP_CVAR_PRI(crylink, ammo); + if(this.netname == "") + this.netname = "crylink"; + else + this.netname = strcat(this.netname, " crylink"); + } + else if (it.classname == "weapon_grenadelauncher" || it.classname == "weapon_mortar") { + this.ammo_rockets += it.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO + if(this.netname == "") + this.netname = "mortar"; + else + this.netname = strcat(this.netname, " mortar"); + } + else if (it.classname == "item_armor_body") + this.armorvalue = 100; + else if (it.classname == "item_health_mega") + this.health = 200; + //remove(it); // removing ents in init functions causes havoc, workaround: + setthink(it, SUB_Remove); + it.nextthink = time; + }); + this.spawnflags = 2; + spawnfunc_target_items(this); + InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET); +} + +spawnfunc(target_give) +{ + InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET); +} + +//spawnfunc(item_flight) /* handled by jetpack */ +//spawnfunc(item_haste) /* handled by buffs mutator */ +//spawnfunc(item_health) /* handled in t_quake.qc */ +//spawnfunc(item_health_large) /* handled in t_items.qc */ +//spawnfunc(item_health_small) /* handled in t_items.qc */ +//spawnfunc(item_health_mega) /* handled in t_items.qc */ +//spawnfunc(item_invis) /* handled by buffs mutator */ +//spawnfunc(item_regen) /* handled by buffs mutator */ + +// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now + +spawnfunc(item_flight) +{ + spawnfunc_item_jetpack(this); +} + +.float notteam; +.float notsingle; +.float notfree; +.float notq3a; +.float notta; +.string gametype; +bool DoesQ3ARemoveThisEntity(entity this) +{ + // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY) + + if(this.notq3a) + if(!teamplay || g_tdm || g_ctf) + return true; + + if(this.notta) + if (!(!teamplay || g_tdm || g_ctf)) + return true; + + if(this.notsingle) + if(maxclients == 1) + return true; + + if(this.notteam) + if(teamplay) + return true; + + if(this.notfree) + if(!teamplay) + return true; + + if(this.gametype) + { + string gametypename; + // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"} + gametypename = "ffa"; + if(teamplay) + gametypename = "team"; + if(g_ctf) + gametypename = "ctf"; + if(maxclients == 1) + gametypename = "single"; + // we do not have the other types (oneflag, obelisk, harvester, teamtournament) + if(strstrofs(this.gametype, gametypename, 0) < 0) + return true; + } + + return false; +} diff --git a/qcsrc/server/compat/quake3.qh b/qcsrc/server/compat/quake3.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/compat/quake3.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/compat/wop.qc b/qcsrc/server/compat/wop.qc new file mode 100644 index 0000000000..6d53e18efb --- /dev/null +++ b/qcsrc/server/compat/wop.qc @@ -0,0 +1,62 @@ +#include "wop.qh" + +#include +// #include + +spawnfunc(weapon_arc); +spawnfunc(weapon_crylink); +spawnfunc(weapon_electro); +spawnfunc(weapon_mortar); +spawnfunc(weapon_hagar); +spawnfunc(weapon_machinegun); +spawnfunc(weapon_devastator); +spawnfunc(weapon_shotgun); +spawnfunc(weapon_vortex); + +spawnfunc(item_armor_big); +spawnfunc(item_armor_mega); +spawnfunc(item_armor_small); + +spawnfunc(item_bullets); +spawnfunc(item_cells); +spawnfunc(item_quad); +spawnfunc(item_rockets); +spawnfunc(item_shells); + +spawnfunc(item_jetpack); + +spawnfunc(item_haste); +spawnfunc(item_health_medium); +spawnfunc(item_health_mega); +spawnfunc(item_invis); +spawnfunc(item_medic); + +//*********************** +//WORD OF PADMAN ENTITIES - So people can play wop maps with the xonotic weapons +//*********************** + +spawnfunc(weapon_punchy) { spawnfunc_weapon_arc(this); } +spawnfunc(weapon_nipper) { spawnfunc_weapon_machinegun(this); } +spawnfunc(weapon_pumper) { spawnfunc_weapon_shotgun(this); } +spawnfunc(weapon_boaster) { spawnfunc_weapon_electro(this); } +spawnfunc(weapon_splasher) { spawnfunc_weapon_vortex(this); } +spawnfunc(weapon_bubbleg) { spawnfunc_weapon_hagar(this); } +spawnfunc(weapon_balloony) { spawnfunc_weapon_mortar(this); } +spawnfunc(weapon_betty) { spawnfunc_weapon_devastator(this); } +spawnfunc(weapon_imperius) { spawnfunc_weapon_crylink(this); } + +spawnfunc(ammo_pumper) { spawnfunc_item_shells(this); } +spawnfunc(ammo_nipper) { spawnfunc_item_bullets(this); } +spawnfunc(ammo_balloony) { spawnfunc_item_rockets(this); } +spawnfunc(ammo_bubbleg) { spawnfunc_item_rockets(this); } +spawnfunc(ammo_boaster) { spawnfunc_item_cells(this); } +spawnfunc(ammo_betty) { spawnfunc_item_rockets(this); } +spawnfunc(ammo_imperius) { spawnfunc_item_cells(this); } + +spawnfunc(item_padpower) { spawnfunc_item_quad(this); } +spawnfunc(item_climber) { spawnfunc_item_invincible(this); } +spawnfunc(item_speedy) { spawnfunc_item_haste(this); } +spawnfunc(item_jump) { spawnfunc_item_jetpack(this); } +spawnfunc(item_visionless) { spawnfunc_item_invis(this); } +spawnfunc(item_revival) { spawnfunc_item_medic(this); } +spawnfunc(item_armor_padshield) { spawnfunc_item_armor_mega(this); } diff --git a/qcsrc/server/compat/wop.qh b/qcsrc/server/compat/wop.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/compat/wop.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 8eea0dc203..fedd5eb1ab 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -1,7 +1,7 @@ #pragma once float warmup_limit; -#include +#include #include #define INDEPENDENT_ATTACK_FINISHED 1 @@ -12,20 +12,6 @@ float g_footsteps, g_grappling_hook, g_instagib; float g_warmup_allguns; float g_warmup_allow_timeout; float warmup_stage; -PROPERTY(float, g_pickup_respawntime_weapon) -PROPERTY(float, g_pickup_respawntime_superweapon) -PROPERTY(float, g_pickup_respawntime_ammo) -PROPERTY(float, g_pickup_respawntime_short) -PROPERTY(float, g_pickup_respawntime_medium) -PROPERTY(float, g_pickup_respawntime_long) -PROPERTY(float, g_pickup_respawntime_powerup) -PROPERTY(float, g_pickup_respawntimejitter_weapon) -PROPERTY(float, g_pickup_respawntimejitter_superweapon) -PROPERTY(float, g_pickup_respawntimejitter_ammo) -PROPERTY(float, g_pickup_respawntimejitter_short) -PROPERTY(float, g_pickup_respawntimejitter_medium) -PROPERTY(float, g_pickup_respawntimejitter_long) -PROPERTY(float, g_pickup_respawntimejitter_powerup) float g_jetpack; float sv_clones; @@ -35,7 +21,7 @@ float player_count; float currentbots; float bots_would_leave; -void UpdateFrags(entity player, float f); +void UpdateFrags(entity player, int f); .float totalfrags; float team1_score, team2_score, team3_score, team4_score; @@ -143,7 +129,6 @@ const int W_TICSPERFRAME = 2; void weapon_defaultspawnfunc(entity this, Weapon e); -float gameover; float intermission_running; float intermission_exittime; float alreadychangedlevel; @@ -174,7 +159,6 @@ bool nJoinAllowed(entity this, entity ignore); .float noalign; // if set to 1, the item or spawnpoint won't be dropped to the floor .vector death_origin; -.vector killer_origin; float default_player_alpha; float default_weapon_alpha; @@ -253,6 +237,8 @@ float lockteams; .float parm_idlesince; float sv_maxidle; float sv_maxidle_spectatorsareidle; +int sv_maxidle_slots; +bool sv_maxidle_slots_countbots; float tracebox_hits_trigger_hurt(vector start, vector mi, vector ma, vector end); @@ -273,7 +259,7 @@ bool independent_players; string clientstuff; .float phase; -.int pressedkeys = _STAT(PRESSED_KEYS); +.int pressedkeys; .string fog; @@ -393,7 +379,6 @@ const float ACTIVE_TOGGLE = 3; .float player_blocked; .float weapon_blocked; // weapon use disabled -.float frozen = _STAT(FROZEN); // for freeze attacks .float revive_progress = _STAT(REVIVE_PROGRESS); .float revival_time; // time at which player was last revived .float revive_speed; // NOTE: multiplier (anything above 1 is instaheal) @@ -429,16 +414,56 @@ const int MIF_GUIDED_CONFUSABLE = MIF_GUIDED_HEAT | MIF_GUIDED_AI; #define MISSILE_IS_GUIDED(m) ((m.missile_flags & MIF_GUIDED_ALL) ? true : false) #define MISSILE_IS_TRACKING(m) ((m.missile_flags & MIF_GUIDED_TRACKING) ? true : false) - //// -.entity player_stats; -//.float playerid; -.string playernick; -.float elos; -.float ranks; - .string cvar_cl_physics; -.bool init_for_player_needed; .void(entity this, entity player) init_for_player; + +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(); } diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 4fbccc7b02..a604a2bade 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -1,15 +1,15 @@ #include "g_damage.qh" -#include "bot/bot.qh" +#include "bot/api.qh" #include "g_hook.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "scores.qh" #include "spawnpoints.qh" #include "../common/state.qh" #include "../common/physics/player.qh" #include "../common/t_items.qh" #include "../common/vehicles/all.qh" -#include "../common/items/all.qc" +#include "../common/items/_mod.qh" #include "../common/mutators/mutator/waypoints/waypointsprites.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" @@ -21,11 +21,11 @@ #include "../common/playerstats.qh" #include "../common/teams.qh" #include "../common/util.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/common.qh" -void UpdateFrags(entity player, float f) +void UpdateFrags(entity player, int f) { PlayerTeamScore_AddScore(player, f); } @@ -111,6 +111,8 @@ void GiveFrags (entity attacker, entity targ, float f, int deathtype) UpdateFrags(attacker, f); } +.entity kh_next; + string AppendItemcodes(string s, entity player) { int w = PS(player).m_weapon.m_id; @@ -262,6 +264,18 @@ float Obituary_WeaponDeath( return false; } +bool frag_centermessage_override(entity attacker, entity targ, int deathtype, int kill_count_to_attacker, int kill_count_to_target) +{ + if(deathtype == DEATH_FIRE.m_id) + { + Send_Notification(NOTIF_ONE, attacker, MSG_CHOICE, CHOICE_FRAG_FIRE, targ.netname, kill_count_to_attacker, (IS_BOT_CLIENT(targ) ? -1 : targ.ping)); + Send_Notification(NOTIF_ONE, targ, MSG_CHOICE, CHOICE_FRAGGED_FIRE, attacker.netname, kill_count_to_target, attacker.health, attacker.armorvalue, (IS_BOT_CLIENT(attacker) ? -1 : attacker.ping)); + return true; + } + + return MUTATOR_CALLHOOK(FragCenterMessage, attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target); +} + .int buffs = _STAT(BUFFS); // TODO: remove entity buff_FirstFromFlags(int _buffs); void Obituary(entity attacker, entity inflictor, entity targ, int deathtype) @@ -275,7 +289,6 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype) // Set final information for the death targ.death_origin = targ.origin; - if(targ != attacker) { targ.killer_origin = attacker.origin; } string deathlocation = (autocvar_notification_server_allows_location ? NearestLocation(targ.death_origin) : ""); #ifdef NOTIFICATIONS_DEBUG @@ -411,7 +424,7 @@ void Obituary(entity attacker, entity inflictor, entity targ, int deathtype) (IS_BOT_CLIENT(attacker) ? -1 : attacker.ping) ); } - else + else if(!frag_centermessage_override(attacker, targ, deathtype, kill_count_to_attacker, kill_count_to_target)) { Send_Notification( NOTIF_ONE, @@ -503,7 +516,7 @@ void Ice_Think(entity this) { if(!STAT(FROZEN, this.owner) || this.owner.iceblock != this) { - remove(this); + delete(this); return; } setorigin(this, this.owner.origin - '0 0 16'); @@ -524,6 +537,8 @@ void Freeze (entity targ, float freeze_time, float frozen_type, float show_waypo targ.revive_progress = ((frozen_type == 3) ? 1 : 0); targ.health = ((frozen_type == 3) ? targ_maxhealth : 1); targ.revive_speed = freeze_time; + if(targ.bot_attack) + IL_REMOVE(g_bot_targets, targ); targ.bot_attack = false; entity ice = new(ice); @@ -556,11 +571,16 @@ void Unfreeze (entity targ) return; if(STAT(FROZEN, targ) && STAT(FROZEN, targ) != 3) // only reset health if target was frozen + { targ.health = ((IS_PLAYER(targ)) ? start_health : targ.max_health); + targ.pauseregen_finished = time + autocvar_g_balance_pause_health_regen; + } STAT(FROZEN, targ) = 0; targ.revive_progress = 0; targ.revival_time = time; + if(!targ.bot_attack) + IL_PUSH(g_bot_targets, targ); targ.bot_attack = true; WaypointSprite_Kill(targ.waypointsprite_attached); @@ -569,7 +589,7 @@ void Unfreeze (entity targ) // remove the ice block if(targ.iceblock) - remove(targ.iceblock); + delete(targ.iceblock); targ.iceblock = NULL; } @@ -788,7 +808,7 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d else victim = targ; - if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim)) + if(IS_PLAYER(victim) || (IS_TURRET(victim) && victim.active == ACTIVE_ACTIVE) || IS_MONSTER(victim) || MUTATOR_CALLHOOK(PlayHitsound, victim, attacker)) { if(DIFF_TEAM(victim, attacker) && !STAT(FROZEN, victim)) { @@ -833,11 +853,11 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d // apply push if (targ.damageforcescale) - if (vlen(force)) + if (force) if (!IS_PLAYER(targ) || time >= targ.spawnshieldtime || targ == attacker) { vector farce = damage_explosion_calcpush(targ.damageforcescale * force, targ.velocity, autocvar_g_balance_damagepush_speedfactor); - if(targ.movetype == MOVETYPE_PHYSICS) + if(targ.move_movetype == MOVETYPE_PHYSICS) { entity farcent = new(farce); farcent.enemy = targ; @@ -852,14 +872,12 @@ void Damage (entity targ, entity inflictor, entity attacker, float damage, int d else { targ.velocity = targ.velocity + farce; - targ.move_velocity = targ.velocity; } UNSET_ONGROUND(targ); - targ.move_flags &= ~FL_ONGROUND; UpdateCSQCProjectile(targ); } // apply damage - if (damage != 0 || (targ.damageforcescale && vlen(force))) + if (damage != 0 || (targ.damageforcescale && force)) if (targ.event_damage) targ.event_damage (targ, inflictor, attacker, damage, deathtype, hitloc, force); @@ -1034,7 +1052,7 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in // print(" finaldmg ", ftos(finaldmg), " force ", vtos(force)); // print(" (", ftos(a), ")\n"); //} - if(finaldmg || vlen(force)) + if(finaldmg || force) { if(targ.iscreature) { @@ -1253,14 +1271,14 @@ void fireburner_think(entity this) // for players, this is done in the regular loop if(wasfreed(this.owner)) { - remove(this); + delete(this); return; } Fire_ApplyEffect(this.owner); if(!Fire_IsBurning(this.owner)) { this.owner.fire_burner = NULL; - remove(this); + delete(this); return; } Fire_ApplyDamage(this.owner); diff --git a/qcsrc/server/g_damage.qh b/qcsrc/server/g_damage.qh index e2c5ea2b35..019c8fc9a8 100644 --- a/qcsrc/server/g_damage.qh +++ b/qcsrc/server/g_damage.qh @@ -7,7 +7,7 @@ #include #include #include - #include + #include #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" @@ -17,7 +17,7 @@ #include "defs.qh" #include #include - #include "mutators/all.qh" + #include "mutators/_mod.qh" #include #include #include @@ -52,7 +52,7 @@ float damage_gooddamage; float IsFlying(entity a); -void UpdateFrags(entity player, float f); +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); diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index b22a81e042..cccba42bb4 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -5,7 +5,7 @@ #include "weapons/weaponsystem.qh" #include "weapons/selection.qh" #include "weapons/tracing.qh" -#include "cl_player.qh" +#include "player.qh" #include "command/common.qh" #include "round_handler.qh" #include "../common/state.qh" @@ -13,7 +13,8 @@ #include "../common/vehicles/all.qh" #include "../common/constants.qh" #include "../common/util.qh" -#include "../common/weapons/all.qh" +#include +#include #include "../lib/warpzone/common.qh" #include "../lib/warpzone/server.qh" @@ -74,10 +75,10 @@ void RemoveGrapplingHook(entity pl) { if(pl.hook == NULL) return; - remove(pl.hook); + delete(pl.hook); pl.hook = NULL; - if(pl.movetype == MOVETYPE_FLY) - pl.movetype = MOVETYPE_WALK; + if(pl.move_movetype == MOVETYPE_FLY) + set_movetype(pl, MOVETYPE_WALK); //pl.disableclientprediction = false; } @@ -87,7 +88,7 @@ void GrapplingHookReset(entity this) if(this.realowner.hook == this) RemoveGrapplingHook(this.owner); else // in any case: - remove(this); + delete(this); } void GrapplingHookThink(entity this); @@ -101,7 +102,7 @@ void GrapplingHook_Stop(entity this) this.nextthink = time; settouch(this, func_null); this.velocity = '0 0 0'; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.hook_length = -1; } @@ -143,7 +144,7 @@ void GrapplingHookThink(entity this) error("Owner lost the hook!\n"); return; } - if(LostMovetypeFollow(this) || intermission_running || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade")) + if(LostMovetypeFollow(this) || gameover || (round_handler_IsActive() && !round_handler_IsRoundStarted()) || ((this.aiment.flags & FL_PROJECTILE) && this.aiment.classname != "nade")) { RemoveGrapplingHook(this.realowner); return; @@ -220,8 +221,8 @@ void GrapplingHookThink(entity this) this.hook_length = newlength; } - if(pull_entity.movetype == MOVETYPE_FLY) - pull_entity.movetype = MOVETYPE_WALK; + if(pull_entity.move_movetype == MOVETYPE_FLY) + set_movetype(pull_entity, MOVETYPE_WALK); if(this.realowner.hook_state & HOOK_RELEASING) { @@ -238,7 +239,7 @@ void GrapplingHookThink(entity this) dv = ((v - v0) * dir) * dir; if(tarzan >= 2) { - if(this.aiment.movetype == MOVETYPE_WALK || this.aiment.classname == "nade") + if(this.aiment.move_movetype == MOVETYPE_WALK || this.aiment.classname == "nade") { entity aim_ent = ((IS_VEHICLE(this.aiment) && this.aiment.owner) ? this.aiment.owner : this.aiment); v = v - dv * 0.5; @@ -280,7 +281,7 @@ void GrapplingHookThink(entity this) if(spd < 50) spd = 0; this.realowner.velocity = dir*spd; - this.realowner.movetype = MOVETYPE_FLY; + set_movetype(this.realowner, MOVETYPE_FLY); UNSET_ONGROUND(this.realowner); } @@ -301,18 +302,18 @@ void GrapplingHookThink(entity this) } } -void GrapplingHookTouch (entity this) +void GrapplingHookTouch(entity this, entity toucher) { - if(other.movetype == MOVETYPE_FOLLOW) + if(toucher.move_movetype == MOVETYPE_FOLLOW) return; - PROJECTILE_TOUCH(this); + PROJECTILE_TOUCH(this, toucher); GrapplingHook_Stop(this); - if(other) - if(other.movetype != MOVETYPE_NONE) + if(toucher) + if(toucher.move_movetype != MOVETYPE_NONE) { - SetMovetypeFollow(this, other); + SetMovetypeFollow(this, toucher); WarpZone_RefSys_BeginAddingIncrementally(this, this.aiment); } @@ -370,8 +371,10 @@ void FireGrapplingHook(entity actor) missile.reset = GrapplingHookReset; missile.classname = "grapplinghook"; missile.flags = FL_PROJECTILE; + IL_PUSH(g_projectiles, missile); + IL_PUSH(g_bot_dodge, missile); - missile.movetype = ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY); + set_movetype(missile, ((autocvar_g_balance_grapplehook_gravity) ? MOVETYPE_TOSS : MOVETYPE_FLY)); PROJECTILE_MAKETRIGGER(missile); //setmodel (missile, MDL_HOOK); // precision set below @@ -396,6 +399,8 @@ void FireGrapplingHook(entity actor) missile.takedamage = DAMAGE_AIM; missile.damageforcescale = 0; missile.damagedbycontents = (autocvar_g_balance_grapplehook_damagedbycontents); + if(missile.damagedbycontents) + IL_PUSH(g_damagedbycontents, missile); missile.hook_start = missile.hook_end = missile.origin; diff --git a/qcsrc/server/g_lights.qc b/qcsrc/server/g_lights.qc index 41366f5ea2..425716d2da 100644 --- a/qcsrc/server/g_lights.qc +++ b/qcsrc/server/g_lights.qc @@ -37,7 +37,7 @@ flags: void dynlight_think(entity this) { if(!this.owner) - remove(this); + delete(this); this.nextthink = time + 0.1; } @@ -48,7 +48,7 @@ void dynlight_find_aiment(entity this) objerror (this, "dynlight: no target to follow"); targ = find(NULL, targetname, this.target); - this.movetype = MOVETYPE_FOLLOW; + set_movetype(this, MOVETYPE_FOLLOW); this.aiment = targ; this.owner = targ; this.punchangle = targ.angles; @@ -123,7 +123,7 @@ spawnfunc(dynlight) if (this.target) // if (!(this.spawnflags & DFOLLOW)) { - this.movetype = MOVETYPE_NOCLIP; + set_movetype(this, MOVETYPE_NOCLIP); if (!this.speed) this.speed = 100; InitializeEntity(this, dynlight_find_path, INITPRIO_FINDTARGET); diff --git a/qcsrc/server/g_models.qc b/qcsrc/server/g_models.qc index 38e72c7329..3ca062b784 100644 --- a/qcsrc/server/g_models.qc +++ b/qcsrc/server/g_models.qc @@ -1,6 +1,7 @@ #include "g_models.qh" #include "g_subs.qh" +#include #include "../common/triggers/subs.qh" #include "../common/triggers/triggers.qh" @@ -152,11 +153,11 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf) WriteCoord(MSG_ENTITY, this.movedir.z); WriteByte(MSG_ENTITY, floor(this.lip * 255)); } - WriteShort(MSG_ENTITY, this.fade_start); - WriteShort(MSG_ENTITY, this.fade_end); - WriteShort(MSG_ENTITY, this.alpha_max); - WriteShort(MSG_ENTITY, this.alpha_min); - WriteShort(MSG_ENTITY, this.inactive); + 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); } @@ -165,7 +166,7 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf) #define G_MODEL_INIT(ent,sol) \ - if(ent.geomtype) if(autocvar_physics_ode && checkextension("DP_PHYSICS_ODE")) ent.movetype = MOVETYPE_PHYSICS; \ + 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; \ @@ -173,7 +174,7 @@ bool g_clientmodel_genericsendentity(entity this, entity to, int sf) 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")) ent.movetype = MOVETYPE_PHYSICS; \ + 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; \ diff --git a/qcsrc/server/g_subs.qc b/qcsrc/server/g_subs.qc index 1b12174b99..e8e50c7184 100644 --- a/qcsrc/server/g_subs.qc +++ b/qcsrc/server/g_subs.qc @@ -8,7 +8,7 @@ spawnfunc(info_null) { - remove(this); + delete(this); // if anything breaks, tell the mapper to fix his map! info_null is meant to remove itself immediately. } @@ -51,9 +51,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, { // take players back into the past FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_takeback(it, CS(it), time - lag)); - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { - if(it != forent) - antilag_takeback(it, it, time - lag); + IL_EACH(g_monsters, it != forent, + { + antilag_takeback(it, it, time - lag); }); } @@ -67,9 +67,9 @@ void tracebox_antilag_force_wz (entity source, vector v1, vector mi, vector ma, if (lag) { FOREACH_CLIENT(IS_PLAYER(it) && it != forent, antilag_restore(it, CS(it))); - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { - if (it != forent) - antilag_restore(it, it); + IL_EACH(g_monsters, it != forent, + { + antilag_restore(it, it); }); } @@ -141,10 +141,10 @@ float tracebox_inverted (vector v1, vector mi, vector ma, vector v2, float nomon if(c == 50) { - LOG_TRACE("HOLY SHIT! When tracing from ", vtos(v1), " to ", vtos(v2), "\n"); - LOG_TRACE(" Nudging gets us nowhere at ", vtos(pos), "\n"); - LOG_TRACE(" trace_endpos is ", vtos(trace_endpos), "\n"); - LOG_TRACE(" trace distance is ", ftos(vlen(pos - trace_endpos)), "\n"); + 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; @@ -234,7 +234,7 @@ vector findbetterlocation (vector org, float mindist) return org; } -float LOD_customize(entity this) +bool LOD_customize(entity this, entity client) { if(autocvar_loddebug) { @@ -249,10 +249,10 @@ float LOD_customize(entity this) } // TODO csqc network this so it only gets sent once - vector near_point = NearestPointOnBox(this, other.origin); - if(vdist(near_point - other.origin, <, this.loddistance1)) + 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 - other.origin, <, this.loddistance2)) + else if(!this.lodmodelindex2 || vdist(near_point - client.origin, <, this.loddistance2)) this.modelindex = this.lodmodelindex1; else this.modelindex = this.lodmodelindex2; @@ -281,7 +281,7 @@ void LODmodel_attach(entity this) if(e) { this.lodmodel1 = e.model; - remove(e); + delete(e); } } if(this.lodtarget2 != "") @@ -290,7 +290,7 @@ void LODmodel_attach(entity this) if(e) { this.lodmodel2 = e.model; - remove(e); + delete(e); } } @@ -417,7 +417,7 @@ void InitTrigger(entity this) SetMovedir(this); this.solid = SOLID_TRIGGER; SetBrushEntityModel(this); - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.modelindex = 0; this.model = ""; } @@ -429,7 +429,7 @@ void InitSolidBSPTrigger(entity this) SetMovedir(this); this.solid = SOLID_BSP; SetBrushEntityModel(this); - this.movetype = MOVETYPE_NONE; // why was this PUSH? -div0 + set_movetype(this, MOVETYPE_NONE); // why was this PUSH? -div0 // this.modelindex = 0; this.model = ""; } @@ -440,7 +440,7 @@ bool InitMovingBrushTrigger(entity this) // to mean no restrictions, so use a yaw of 360 instead. this.solid = SOLID_BSP; SetBrushEntityModel(this); - this.movetype = MOVETYPE_PUSH; + set_movetype(this, MOVETYPE_PUSH); if(this.modelindex == 0) { objerror(this, "InitMovingBrushTrigger: no brushes found!"); diff --git a/qcsrc/server/g_subs.qh b/qcsrc/server/g_subs.qh index 4f9fc471f5..1f5537cea5 100644 --- a/qcsrc/server/g_subs.qh +++ b/qcsrc/server/g_subs.qh @@ -138,7 +138,7 @@ float angc (float a1, float a2); .float loddistance1; .float loddistance2; -float LOD_customize(entity this); +bool LOD_customize(entity this, entity client); void LOD_uncustomize(entity this); diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 56bc0b34d8..4ca9ef77c8 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -2,10 +2,10 @@ #include "anticheat.qh" #include "antilag.qh" -#include "bot/bot.qh" +#include "bot/api.qh" #include "campaign.qh" #include "cheats.qh" -#include "cl_client.qh" +#include "client.qh" #include "command/common.qh" #include "command/getreplies.qh" #include "command/sv_cmd.qh" @@ -13,15 +13,16 @@ #include "g_hook.qh" #include "ipban.qh" #include "mapvoting.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "race.qh" #include "scores.qh" #include "teamplay.qh" #include "weapons/weaponstats.qh" #include "../common/constants.qh" +#include #include "../common/deathtypes/all.qh" #include "../common/mapinfo.qh" -#include "../common/monsters/all.qh" +#include "../common/monsters/_mod.qh" #include "../common/monsters/sv_monsters.qh" #include "../common/vehicles/all.qh" #include "../common/notifications/all.qh" @@ -32,8 +33,8 @@ #include "../common/triggers/trigger/secret.qh" #include "../common/triggers/target/music.qh" #include "../common/util.qh" -#include "../common/items/all.qh" -#include "../common/weapons/all.qh" +#include "../common/items/_mod.qh" +#include #include "../common/state.qh" const float LATENCY_THINKRATE = 10; @@ -56,9 +57,9 @@ void PingPLReport_Think(entity this) { WriteHeader(MSG_BROADCAST, TE_CSQC_PINGPLREPORT); WriteByte(MSG_BROADCAST, this.cnt); - WriteShort(MSG_BROADCAST, max(1, e.ping)); - WriteByte(MSG_BROADCAST, ceil(e.ping_packetloss * 255)); - WriteByte(MSG_BROADCAST, ceil(e.ping_movementloss * 255)); + WriteShort(MSG_BROADCAST, bound(1, e.ping, 65535)); + WriteByte(MSG_BROADCAST, min(ceil(e.ping_packetloss * 255), 255)); + WriteByte(MSG_BROADCAST, min(ceil(e.ping_movementloss * 255), 255)); // record latency times for clients throughout the match so we can report it to playerstats if(time > (e.latency_time + LATENCY_THINKRATE)) @@ -320,17 +321,20 @@ void cvar_changes_init() BADCVAR("g_invasion_point_limit"); BADCVAR("g_keyhunt_point_leadlimit"); BADCVAR("g_nexball_goalleadlimit"); + BADCVAR("g_new_toys_use_pickupsound"); + BADCVAR("g_physics_predictall"); + BADCVAR("g_piggyback"); BADCVAR("g_tdm_point_leadlimit"); BADCVAR("g_tdm_point_limit"); BADCVAR("leadlimit_and_fraglimit"); BADCVAR("leadlimit_override"); BADCVAR("pausable"); - BADCVAR("sv_allow_fullbright"); BADCVAR("sv_checkforpacketsduringsleep"); BADCVAR("sv_intermission_cdtrack"); BADCVAR("sv_minigames"); BADCVAR("sv_namechangetimer"); BADCVAR("sv_precacheplayermodels"); + BADCVAR("sv_stepheight"); BADCVAR("sv_timeout"); BADPREFIX("crypto_"); BADPREFIX("gameversion_"); @@ -342,9 +346,11 @@ void cvar_changes_init() BADPREFIX("net_"); BADPREFIX("prvm_"); BADPREFIX("skill_"); + BADPREFIX("sv_allow_"); BADPREFIX("sv_cullentities_"); BADPREFIX("sv_maxidle_"); BADPREFIX("sv_minigames_"); + BADPREFIX("sv_radio_"); BADPREFIX("sv_timeout_"); BADPREFIX("sv_vote_"); BADPREFIX("timelimit_"); @@ -369,6 +375,7 @@ void cvar_changes_init() BADCVAR("g_balance_teams_scorefactor"); BADCVAR("g_ban_sync_trusted_servers"); BADCVAR("g_ban_sync_uri"); + BADCVAR("g_buffs"); BADCVAR("g_ca_teams_override"); BADCVAR("g_ctf_ignore_frags"); BADCVAR("g_domination_point_limit"); @@ -391,6 +398,8 @@ void cvar_changes_init() BADCVAR("g_maxplayers"); BADCVAR("g_mirrordamage"); BADCVAR("g_nexball_goallimit"); + BADCVAR("g_norecoil"); + BADCVAR("g_physics_clientselect"); BADCVAR("g_powerups"); BADCVAR("g_spawnshieldtime"); BADCVAR("g_start_delay"); @@ -443,6 +452,7 @@ void cvar_changes_init() BADCVAR("g_grappling_hook"); BADCVAR("g_jetpack"); +#undef BADPRESUFFIX #undef BADPREFIX #undef BADCVAR @@ -540,7 +550,7 @@ spawnfunc(__init_dedicated_server) cvar_string = cvar_string_normal; cvar_set = cvar_set_normal; - remove = remove_unsafely; + delete_fn = remove_unsafely; entity e = spawn(); setthink(e, GotoFirstMap); @@ -555,6 +565,8 @@ spawnfunc(__init_dedicated_server) static_init_late(); static_init_precache(); + IL_PUSH(g_spawnpoints, e); // just incase + MapInfo_Enumerate(); MapInfo_FilterGametype(MapInfo_CurrentGametype(), MapInfo_CurrentFeatures(), MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); } @@ -646,9 +658,6 @@ spawnfunc(worldspawn) } } - float fd, l; - string s; - cvar = cvar_normal; cvar_string = cvar_string_normal; cvar_set = cvar_set_normal; @@ -657,12 +666,10 @@ spawnfunc(worldspawn) error("world already spawned - you may have EXACTLY ONE worldspawn!"); world_already_spawned = true; - remove = remove_safely; // during spawning, watch what you remove! + delete_fn = remove_safely; // during spawning, watch what you remove! cvar_changes_init(); // do this very early now so it REALLY matches the server config - compressShortVector_init(); - maxclients = 0; for (entity head = nextent(NULL); head; head = nextent(head)) { @@ -753,7 +760,7 @@ spawnfunc(worldspawn) // character set: ASCII 33-126 without the following characters: : ; ' " \ $ if(autocvar_sv_eventlog) { - s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000)); + string s = sprintf("%d.%s.%06d", itos(autocvar_sv_eventlog_files_counter), strftime(false, "%s"), floor(random() * 1000000)); matchid = strzone(s); GameLogEcho(strcat(":gamestart:", GetGametype(), "_", GetMapname(), ":", s)); @@ -806,12 +813,13 @@ spawnfunc(worldspawn) if(whichpack(strcat("maps/", mapname, ".cfg")) != "") { - fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ); + int fd = fopen(strcat("maps/", mapname, ".cfg"), FILE_READ); if(fd != -1) { + string s; while((s = fgets(fd))) { - l = tokenize_console(s); + int l = tokenize_console(s); if(l < 2) continue; if(argv(0) == "cd") @@ -853,7 +861,7 @@ spawnfunc(worldspawn) monsterlist_reply = strzone(getmonsterlist()); for(int i = 0; i < 10; ++i) { - s = getrecords(i); + string s = getrecords(i); if (s) records_reply[i] = strzone(s); } @@ -872,7 +880,7 @@ spawnfunc(worldspawn) // fill sv_curl_serverpackages from .serverpackage files if (autocvar_sv_curl_serverpackages_auto) { - s = "csprogs-" WATERMARK ".txt"; + string s = "csprogs-" WATERMARK ".txt"; // remove automatically managed files from the list to prevent duplicates for (int i = 0, n = tokenize_console(cvar_string("sv_curl_serverpackages")); i < n; ++i) { @@ -884,7 +892,7 @@ spawnfunc(worldspawn) } // add automatically managed files to the list #define X(match) MACRO_BEGIN { \ - fd = search_begin(match, true, false); \ + int fd = search_begin(match, true, false); \ if (fd >= 0) \ { \ for (int i = 0, j = search_getsize(fd); i < j; ++i) \ @@ -910,7 +918,8 @@ spawnfunc(worldspawn) if(cvar_string("g_mod_config") != cvar_defstring("g_mod_config")) modname = cvar_string("g_mod_config"); // extra mutators that deserve to count as mod - MUTATOR_CALLHOOK(SetModname); + MUTATOR_CALLHOOK(SetModname, modname); + modname = M_ARGV(0, string); // save it for later modname = strzone(modname); @@ -923,7 +932,7 @@ spawnfunc(worldspawn) spawnfunc(light) { //makestatic (this); // Who the f___ did that? - remove(this); + delete(this); } string GetGametype() @@ -970,10 +979,10 @@ float MapHasRightSize(string map) LOG_TRACE("checkwp "); LOG_TRACE(map); if(!fexists(strcat("maps/", map, ".waypoints"))) { - LOG_TRACE(": no waypoints\n"); + LOG_TRACE(": no waypoints"); return false; } - LOG_TRACE(": has waypoints\n"); + LOG_TRACE(": has waypoints"); } // open map size restriction file @@ -988,18 +997,18 @@ float MapHasRightSize(string map) fclose(fh); if(player_count < mapmin) { - LOG_TRACE("not enough\n"); + LOG_TRACE("not enough"); return false; } if(player_count > mapmax) { - LOG_TRACE("too many\n"); + LOG_TRACE("too many"); return false; } - LOG_TRACE("right size\n"); + LOG_TRACE("right size"); return true; } - LOG_TRACE(": not found\n"); + LOG_TRACE(": not found"); return true; } @@ -1038,7 +1047,7 @@ float Map_Check(float position, float pass) return 0; } else - LOG_TRACE( "Couldn't select '", filename, "'..\n" ); + LOG_DEBUG( "Couldn't select '", filename, "'..." ); return 0; } @@ -1071,7 +1080,7 @@ float() MaplistMethod_Iterate = // usual method { float pass, i; - LOG_TRACE("Trying MaplistMethod_Iterate\n"); + LOG_TRACE("Trying MaplistMethod_Iterate"); for(pass = 1; pass <= 2; ++pass) { @@ -1088,7 +1097,7 @@ float() MaplistMethod_Iterate = // usual method float() MaplistMethod_Repeat = // fallback method { - LOG_TRACE("Trying MaplistMethod_Repeat\n"); + LOG_TRACE("Trying MaplistMethod_Repeat"); if(Map_Check(Map_Current, 2)) return Map_Current; @@ -1099,7 +1108,7 @@ float() MaplistMethod_Random = // random map selection { float i, imax; - LOG_TRACE("Trying MaplistMethod_Random\n"); + LOG_TRACE("Trying MaplistMethod_Random"); imax = 42; @@ -1119,7 +1128,7 @@ float(float exponent) MaplistMethod_Shuffle = // more clever shuffling { float i, j, imax, insertpos; - LOG_TRACE("Trying MaplistMethod_Shuffle\n"); + LOG_TRACE("Trying MaplistMethod_Shuffle"); imax = 42; @@ -1131,7 +1140,7 @@ float(float exponent) MaplistMethod_Shuffle = // more clever shuffling insertpos = pow(random(), 1 / exponent); // ]0, 1] insertpos = insertpos * (Map_Count - 1); // ]0, Map_Count - 1] insertpos = ceil(insertpos) + 1; // {2, 3, 4, ..., Map_Count} - LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos), "\n"); + LOG_TRACE("SHUFFLE: insert pos = ", ftos(insertpos)); // insert the current map there newlist = ""; @@ -1335,32 +1344,32 @@ entity FindIntermission() local float cyc; // look for info_intermission first - spot = find (NULL, classname, "info_intermission"); + spot = find(NULL, classname, "info_intermission"); if (spot) { // pick a random one cyc = random() * 4; while (cyc > 1) { - spot = find (spot, classname, "info_intermission"); + spot = find(spot, classname, "info_intermission"); if (!spot) - spot = find (spot, classname, "info_intermission"); + spot = find(spot, classname, "info_intermission"); cyc = cyc - 1; } return spot; } // then look for the start position - spot = find (NULL, classname, "info_player_start"); + spot = find(NULL, classname, "info_player_start"); if (spot) return spot; // testinfo_player_start is only found in regioned levels - spot = find (NULL, classname, "testplayerstart"); + spot = find(NULL, classname, "testplayerstart"); if (spot) return spot; // then look for the start position - spot = find (NULL, classname, "info_player_deathmatch"); + spot = find(NULL, classname, "info_player_deathmatch"); if (spot) return spot; @@ -1486,9 +1495,6 @@ void FixIntermissionClient(entity e) e.autoscreenshot = time + 0.8; // used for autoscreenshot e.health = -2342; // first intermission phase; voting phase has positive health (used to decide whether to send SVC_FINALE or not) - e.solid = SOLID_NOT; - e.movetype = MOVETYPE_NONE; - e.takedamage = DAMAGE_NO; for (int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { .entity weaponentity = weaponentities[slot]; @@ -1504,7 +1510,7 @@ void FixIntermissionClient(entity e) stuffcmd(e, "\nscr_printspeed 1000000\n"); RandomSelection_Init(); FOREACH_WORD(autocvar_sv_intermission_cdtrack, true, LAMBDA( - RandomSelection_Add(NULL, 0, it, 1, 1); + RandomSelection_AddString(it, 1, 1); )); if (RandomSelection_chosen_string != "") { @@ -1526,7 +1532,7 @@ void NextLevel() intermission_running = 1; -// enforce a wait time before allowing changelevel + // enforce a wait time before allowing changelevel if(player_count > 0) intermission_exittime = time + autocvar_sv_mapchange_delay; else @@ -1771,7 +1777,8 @@ float WinningCondition_RanOutOfSpawns() } )); - FOREACH_ENTITY_CLASS("info_player_deathmatch", true, LAMBDA( + IL_EACH(g_spawnpoints, true, + { switch(it.team) { case NUM_TEAM_1: team1_score = 1; break; @@ -1779,7 +1786,7 @@ float WinningCondition_RanOutOfSpawns() case NUM_TEAM_3: team3_score = 1; break; case NUM_TEAM_4: team4_score = 1; break; } - )); + }); ClearWinners(); if(team1_score + team2_score + team3_score + team4_score == 0) @@ -1832,7 +1839,7 @@ void CheckRules_World() SetDefaultAlpha(); - if (gameover) // someone else quit the game already + if (intermission_running) // someone else quit the game already { if(player_count == 0) // Nobody there? Then let's go to the next map MapVote_Start(); @@ -1983,11 +1990,80 @@ string GotoMap(string m) return "Map switch will happen after scoreboard."; } +bool autocvar_sv_gameplayfix_multiplethinksperframe; +void RunThink(entity this) +{ + // don't let things stay in the past. + // it is possible to start that way by a trigger with a local time. + if(this.nextthink <= 0 || this.nextthink > time + frametime) + return; + + float oldtime = time; // do we need to save this? + + for (int iterations = 0; iterations < 128 && !wasfreed(this); iterations++) + { + time = max(oldtime, this.nextthink); + this.nextthink = 0; + + if(getthink(this)) + getthink(this)(this); + // mods often set nextthink to time to cause a think every frame, + // we don't want to loop in that case, so exit if the new nextthink is + // <= the time the qc was told, also exit if it is past the end of the + // frame + if(this.nextthink <= time || this.nextthink > oldtime + frametime || !autocvar_sv_gameplayfix_multiplethinksperframe) + break; + } + + time = oldtime; +} + +bool autocvar_sv_freezenonclients; +bool autocvar_sv_gameplayfix_delayprojectiles; +void Physics_Frame() +{ + if(autocvar_sv_freezenonclients) + return; + + FOREACH_ENTITY_FLOAT(pure_data, false, + { + if(IS_CLIENT(it) || it.classname == "" || it.move_movetype == MOVETYPE_PUSH || it.move_movetype == MOVETYPE_FAKEPUSH || it.move_movetype == MOVETYPE_PHYSICS) + continue; + + set_movetype(it, it.move_movetype); + + if(it.move_movetype == MOVETYPE_NONE) + continue; + + if(it.move_qcphysics) + Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false); + + if(it.movetype >= MOVETYPE_USER_FIRST && it.movetype <= MOVETYPE_USER_LAST) // these cases have no think handling + { + // handle thinking here + if (getthink(it) && it.nextthink > 0 && it.nextthink <= time + frametime) + RunThink(it); + } + }); + + if(autocvar_sv_gameplayfix_delayprojectiles >= 0) + return; + + FOREACH_ENTITY_FLOAT(move_qcphysics, true, + { + if(IS_CLIENT(it) || is_pure(it) || it.classname == "" || it.move_movetype == MOVETYPE_NONE) + continue; + Movetype_Physics_NoMatchTicrate(it, PHYS_INPUT_TIMELENGTH, false); + }); +} +void systems_update(); void EndFrame() { anticheat_endframe(); + Physics_Frame(); + FOREACH_CLIENT(IS_REAL_CLIENT(it), { entity e = IS_SPEC(it) ? it.enemy : it; if (e.typehitsound) { @@ -2008,13 +2084,16 @@ void EndFrame() it.damage_dealt = 0; antilag_record(it, CS(it), altime); }); - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { + IL_EACH(g_monsters, true, + { antilag_record(it, it, altime); }); FOREACH_CLIENT(PS(it), { PlayerState s = PS(it); s.ps_push(s, it); }); + systems_update(); + IL_ENDFRAME(); } @@ -2090,7 +2169,7 @@ void Shutdown() if(world_initialized > 0) { world_initialized = 0; - LOG_TRACE("Saving persistent data...\n"); + LOG_TRACE("Saving persistent data..."); Ban_SaveBans(); // playerstats with unfinished match @@ -2113,7 +2192,7 @@ void Shutdown() CheatShutdown(); // must be after cheatcount check db_close(ServerProgsDB); db_close(TemporaryDB); - LOG_TRACE("Saving persistent data... done!\n"); + LOG_TRACE("Saving persistent data... done!"); // tell the bot system the game is ending now bot_endgame(); diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc new file mode 100644 index 0000000000..9b36010fec --- /dev/null +++ b/qcsrc/server/impulse.qc @@ -0,0 +1,606 @@ +#include "impulse.qh" +#include "round_handler.qh" + +#include "bot/api.qh" + +#include "weapons/throwing.qh" +#include "command/common.qh" +#include "cheats.qh" +#include "weapons/selection.qh" +#include "weapons/tracing.qh" +#include "weapons/weaponsystem.qh" + +#include + +#include "../common/minigames/sv_minigames.qh" + +#include +#include "../common/vehicles/sv_vehicles.qh" + +#include "../common/mutators/mutator/waypoints/waypointsprites.qh" + +.entity vehicle; + +#define IMPULSE(id) _IMPULSE(IMP_##id) +#define _IMPULSE(id) \ + void id##_handle(entity this); \ + STATIC_INIT_LATE(id) \ + { \ + id.impulse_handle = id##_handle; \ + } \ + void id##_handle(entity this) + +/** + * Impulse map: + * + * 0 reserved (no input) + * + * 99: loaded + * + * 140: moving clone + * 141: ctf speedrun + * 142: fixed clone + * 143: emergency teleport + * 148: unfairly eliminate + * + * TODO: + * 200 to 209: prev weapon shortcuts + * 210 to 219: best weapon shortcuts + * 220 to 229: next weapon shortcuts + * 230 to 253: individual weapons (up to 24) + */ + +// weapon switching impulses + +#define X(slot) \ + IMPULSE(weapon_group_##slot) \ + { \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_group_##slot.impulse; \ + return; \ + } \ + W_NextWeaponOnImpulse(this, slot); \ + } +X(1) +X(2) +X(3) +X(4) +X(5) +X(6) +X(7) +X(8) +X(9) +X(0) +#undef X + +// custom order weapon cycling + +#define X(slot, dir) \ + IMPULSE(weapon_priority_##slot##_##dir) \ + { \ + if (this.vehicle) return; \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_priority_##slot##_##dir.impulse; \ + return; \ + } \ + noref int prev = -1; \ + noref int best = 0; \ + noref int next = +1; \ + W_CycleWeapon(this, this.cvar_cl_weaponpriorities[slot], dir); \ + } +X(0, prev) +X(1, prev) +X(2, prev) +X(3, prev) +X(4, prev) +X(5, prev) +X(6, prev) +X(7, prev) +X(8, prev) +X(9, prev) + +X(0, best) +X(1, best) +X(2, best) +X(3, best) +X(4, best) +X(5, best) +X(6, best) +X(7, best) +X(8, best) +X(9, best) + +X(0, next) +X(1, next) +X(2, next) +X(3, next) +X(4, next) +X(5, next) +X(6, next) +X(7, next) +X(8, next) +X(9, next) +#undef X + +// direct weapons + +#define X(i) \ + IMPULSE(weapon_byid_##i) \ + { \ + if (this.vehicle) return; \ + if (IS_DEAD(this)) \ + { \ + this.impulse = IMP_weapon_byid_##i.impulse; \ + return; \ + } \ + W_SwitchWeapon(this, Weapons_from(WEP_FIRST + i)); \ + } +X(0) +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) +X(18) +X(19) +X(20) +X(21) +X(22) +X(23) +#undef X + +IMPULSE(weapon_next_byid) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_byid.impulse; + return; + } + W_NextWeapon(this, 0); +} + +IMPULSE(weapon_prev_byid) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_byid.impulse; + return; + } + W_PreviousWeapon(this, 0); +} + +IMPULSE(weapon_next_bygroup) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_bygroup.impulse; + return; + } + W_NextWeapon(this, 1); +} + +IMPULSE(weapon_prev_bygroup) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_bygroup.impulse; + return; + } + W_PreviousWeapon(this, 1); +} + +IMPULSE(weapon_next_bypriority) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_next_bypriority.impulse; + return; + } + W_NextWeapon(this, 2); +} + +IMPULSE(weapon_prev_bypriority) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) + { + this.impulse = IMP_weapon_prev_bypriority.impulse; + return; + } + W_PreviousWeapon(this, 2); +} + +IMPULSE(weapon_last) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_LastWeapon(this); +} + +IMPULSE(weapon_best) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_SwitchWeapon(this, w_getbestweapon(this)); +} + +IMPULSE(weapon_drop) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + W_ThrowWeapon(this, weaponentities[0], W_CalculateProjectileVelocity(this, this.velocity, v_forward * 750, false), '0 0 0', true); +} + +IMPULSE(weapon_reload) +{ + if (this.vehicle) return; + if (IS_DEAD(this)) return; + if (forbidWeaponUse(this)) return; + Weapon w = PS(this).m_weapon; + entity actor = this; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + w.wr_reload(w, actor, weaponentity); + } +} + +void ImpulseCommands(entity this) +{ + if (gameover) return; + + int imp = this.impulse; + if (!imp) return; + this.impulse = 0; + + if (MinigameImpulse(this, imp)) return; + + if (timeout_status == TIMEOUT_ACTIVE) return; // don't allow any impulses while the game is paused + + // allow only weapon change impulses when not in round time + if (round_handler_IsActive() && !round_handler_IsRoundStarted()) + { + #define X(id) case IMP_##id.impulse: + switch (imp) + { + X(weapon_group_0) + X(weapon_group_1) + X(weapon_group_2) + X(weapon_group_3) + X(weapon_group_4) + X(weapon_group_5) + X(weapon_group_6) + X(weapon_group_7) + X(weapon_group_8) + X(weapon_group_9) + X(weapon_next_byid) + X(weapon_prev_byid) + X(weapon_next_bygroup) + X(weapon_prev_bygroup) + X(weapon_next_bypriority) + X(weapon_prev_bypriority) + X(weapon_last) + X(weapon_best) + X(weapon_reload) + X(weapon_priority_0_prev) + X(weapon_priority_1_prev) + X(weapon_priority_2_prev) + X(weapon_priority_3_prev) + X(weapon_priority_4_prev) + X(weapon_priority_5_prev) + X(weapon_priority_6_prev) + X(weapon_priority_7_prev) + X(weapon_priority_8_prev) + X(weapon_priority_9_prev) + X(weapon_priority_0_next) + X(weapon_priority_1_next) + X(weapon_priority_2_next) + X(weapon_priority_3_next) + X(weapon_priority_4_next) + X(weapon_priority_5_next) + X(weapon_priority_6_next) + X(weapon_priority_7_next) + X(weapon_priority_8_next) + X(weapon_priority_9_next) + X(weapon_priority_0_best) + X(weapon_priority_1_best) + X(weapon_priority_2_best) + X(weapon_priority_3_best) + X(weapon_priority_4_best) + X(weapon_priority_5_best) + X(weapon_priority_6_best) + X(weapon_priority_7_best) + X(weapon_priority_8_best) + X(weapon_priority_9_best) + X(weapon_byid_0) + X(weapon_byid_1) + X(weapon_byid_2) + X(weapon_byid_3) + X(weapon_byid_4) + X(weapon_byid_5) + X(weapon_byid_6) + X(weapon_byid_7) + X(weapon_byid_8) + X(weapon_byid_9) + X(weapon_byid_10) + X(weapon_byid_11) + X(weapon_byid_12) + X(weapon_byid_13) + X(weapon_byid_14) + X(weapon_byid_15) + X(weapon_byid_16) + X(weapon_byid_17) + X(weapon_byid_18) + X(weapon_byid_19) + X(weapon_byid_20) + X(weapon_byid_21) + X(weapon_byid_22) + X(weapon_byid_23) + break; + default: return; + } +#undef X + } + + if (vehicle_impulse(this, imp)) return; + + if (CheatImpulse(this, imp)) return; + + FOREACH(IMPULSES, it.impulse == imp, { + void(entity) f = it.impulse_handle; + if (!f) continue; + f(this); + return; + }); +} + +IMPULSE(use) +{ + PlayerUseKey(this); +} + +IMPULSE(waypoint_personal_here) +{ + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.origin, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at location\n"); +} + +IMPULSE(waypoint_personal_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, trace_endpos, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at crosshair\n"); +} + +IMPULSE(waypoint_personal_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployPersonal(WP_Waypoint, this, this.death_origin, RADARICON_WAYPOINT); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "personal waypoint spawned at death location\n"); +} + +IMPULSE(waypoint_here_follow) +{ + if (!teamplay) return; + if (IS_DEAD(this)) return; + if (!MUTATOR_CALLHOOK(HelpMePing, this)) + { + entity wp = WaypointSprite_Attach(WP_Helpme, this, true, RADARICON_HELPME); + if (!wp) WaypointSprite_HelpMePing(this.waypointsprite_attachedforcarrier); + else WaypointSprite_Ping(wp); + } + sprint(this, "HELP ME attached\n"); +} + +IMPULSE(waypoint_here_here) +{ + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.origin, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at location\n"); +} + +IMPULSE(waypoint_here_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, trace_endpos, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at crosshair\n"); +} + +IMPULSE(waypoint_here_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployFixed(WP_Here, false, this, this.death_origin, RADARICON_HERE); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "HERE spawned at death location\n"); +} + +IMPULSE(waypoint_danger_here) +{ + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.origin, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at location\n"); +} + +IMPULSE(waypoint_danger_crosshair) +{ + WarpZone_crosshair_trace(this); + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, trace_endpos, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at crosshair\n"); +} + +IMPULSE(waypoint_danger_death) +{ + if (!this.death_origin) return; + entity wp = WaypointSprite_DeployFixed(WP_Danger, false, this, this.death_origin, RADARICON_DANGER); + if (wp) WaypointSprite_Ping(wp); + sprint(this, "DANGER spawned at death location\n"); +} + +IMPULSE(waypoint_clear_personal) +{ + WaypointSprite_ClearPersonal(this); + if (this.personal) + { + delete(this.personal); + this.personal = NULL; + } + sprint(this, "personal waypoint cleared\n"); +} + +IMPULSE(waypoint_clear) +{ + WaypointSprite_ClearOwned(this); + if (this.personal) + { + delete(this.personal); + this.personal = NULL; + } + sprint(this, "all waypoints cleared\n"); +} + +IMPULSE(navwaypoint_spawn) +{ + if (!autocvar_g_waypointeditor) return; + waypoint_schedulerelink(waypoint_spawn(this.origin, this.origin, 0)); + bprint(strcat("Waypoint spawned at ", vtos(this.origin), "\n")); +} + +IMPULSE(navwaypoint_remove) +{ + if (!autocvar_g_waypointeditor) return; + entity e = navigation_findnearestwaypoint(this, false); + if (!e) return; + if (e.wpflags & WAYPOINTFLAG_GENERATED) return; + bprint(strcat("Waypoint removed at ", vtos(e.origin), "\n")); + waypoint_remove(e); +} + +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; + IL_EACH(g_waypoints, true, + { + it.colormod = '0.5 0.5 0.5'; + it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); + }); + entity e2 = navigation_findnearestwaypoint(this, false); + navigation_markroutes(this, e2); + + int j, m; + + j = 0; + m = 0; + IL_EACH(g_waypoints, it.wpcost >= 10000000, + { + LOG_INFO("unreachable: ", etos(it), " ", vtos(it.origin), "\n"); + it.colormod_z = 8; + it.effects |= EF_NODEPTHTEST | EF_BLUE; + ++j; + ++m; + }); + if (j) LOG_INFOF("%d waypoints cannot be reached from here in any way (marked with blue light)\n", j); + navigation_markroutes_inverted(e2); + + j = 0; + IL_EACH(g_waypoints, it.wpcost >= 10000000, + { + LOG_INFO("cannot reach me: ", etos(it), " ", vtos(it.origin), "\n"); + it.colormod_x = 8; + if (!(it.effects & EF_NODEPTHTEST)) // not already reported before + ++m; + it.effects |= EF_NODEPTHTEST | EF_RED; + ++j; + }); + if (j) LOG_INFOF("%d waypoints cannot walk to here in any way (marked with red light)\n", j); + if (m) LOG_INFOF("%d waypoints have been marked total\n", m); + + j = 0; + IL_EACH(g_spawnpoints, true, + { + vector org = it.origin; + tracebox(it.origin, PL_MIN_CONST, PL_MAX_CONST, it.origin - '0 0 512', MOVE_NOMONSTERS, NULL); + setorigin(it, trace_endpos); + if (navigation_findnearestwaypoint(it, false)) + { + setorigin(it, org); + it.effects &= ~EF_NODEPTHTEST; + it.model = ""; + } + else + { + setorigin(it, org); + LOG_INFO("spawn without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST; + _setmodel(it, this.model); + it.frame = this.frame; + it.skin = this.skin; + it.colormod = '8 0.5 8'; + setsize(it, '0 0 0', '0 0 0'); + ++j; + } + }); + if (j) LOG_INFOF("%d spawnpoints have no nearest waypoint (marked by player model)\n", j); + + j = 0; + IL_EACH(g_items, true, + { + it.effects &= ~(EF_NODEPTHTEST | EF_RED | EF_BLUE); + it.colormod = '0.5 0.5 0.5'; + }); + IL_EACH(g_items, true, + { + if (navigation_findnearestwaypoint(it, false)) continue; + LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST | EF_RED; + it.colormod_x = 8; + ++j; + }); + if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked away from (marked with red light)\n", j); + + j = 0; + IL_EACH(g_items, true, + { + if (navigation_findnearestwaypoint(it, true)) continue; + LOG_INFO("item without waypoint: ", etos(it), " ", vtos(it.origin), "\n"); + it.effects |= EF_NODEPTHTEST | EF_BLUE; + it.colormod_z = 8; + ++j; + }); + if (j) LOG_INFOF("%d items have no nearest waypoint and cannot be walked to (marked with blue light)\n", j); +} diff --git a/qcsrc/server/impulse.qh b/qcsrc/server/impulse.qh new file mode 100644 index 0000000000..50edc2c9c5 --- /dev/null +++ b/qcsrc/server/impulse.qh @@ -0,0 +1,3 @@ +#pragma once + +void ImpulseCommands(entity this); diff --git a/qcsrc/server/ipban.qc b/qcsrc/server/ipban.qc index 8cc3759793..50c258f927 100644 --- a/qcsrc/server/ipban.qc +++ b/qcsrc/server/ipban.qc @@ -163,7 +163,7 @@ void OnlineBanList_URI_Get_Callback(float id, float status, string data) LOG_TRACE("received ban list item ", ftos(i / 4), ": ip=", ip); LOG_TRACE(" timeleft=", ftos(timeleft), " reason=", reason); - LOG_TRACE(" serverip=", serverip, "\n"); + LOG_TRACE(" serverip=", serverip); timeleft -= 1.5 * autocvar_g_ban_sync_timeout; if(timeleft < 0) @@ -243,7 +243,7 @@ void OnlineBanList_Think(entity this) return; LABEL(killme) - remove(this); + delete(this); } const float BAN_MAX = 256; @@ -507,10 +507,10 @@ float Ban_Insert(string ip, float bantime, string reason, float dosync) if(time + bantime > ban_expire[i]) { ban_expire[i] = time + bantime; - LOG_TRACE(ip, "'s ban has been prolonged to ", ftos(bantime), " seconds from now\n"); + LOG_TRACE(ip, "'s ban has been prolonged to ", ftos(bantime), " seconds from now"); } else - LOG_TRACE(ip, "'s ban is still active until ", ftos(ban_expire[i] - time), " seconds from now\n"); + LOG_TRACE(ip, "'s ban is still active until ", ftos(ban_expire[i] - time), " seconds from now"); // and enforce reason = Ban_Enforce(i, reason); @@ -552,7 +552,7 @@ float Ban_Insert(string ip, float bantime, string reason, float dosync) } // okay, insert our new victim as i Ban_Delete(i); - LOG_TRACE(ip, " has been banned for ", ftos(bantime), " seconds\n"); + LOG_TRACE(ip, " has been banned for ", ftos(bantime), " seconds"); ban_expire[i] = time + bantime; ban_ip[i] = strzone(ip); ban_count = max(ban_count, i + 1); diff --git a/qcsrc/server/item_key.qc b/qcsrc/server/item_key.qc index 317b897b0c..d33fe87c2e 100644 --- a/qcsrc/server/item_key.qc +++ b/qcsrc/server/item_key.qc @@ -1,7 +1,7 @@ #include "item_key.qh" #include "../common/triggers/subs.qh" -#include "../common/monsters/all.qh" +#include "../common/monsters/_mod.qh" #include "../common/notifications/all.qh" #include "../common/util.qh" #include "../lib/warpzone/util_server.qh" @@ -68,23 +68,23 @@ item_key /** * Key touch handler. */ -void item_key_touch(entity this) +void item_key_touch(entity this, entity toucher) { - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; // player already picked up this key - if (other.itemkeys & this.itemkeys) + if (toucher.itemkeys & this.itemkeys) return; - other.itemkeys |= this.itemkeys; - play2(other, this.noise); + toucher.itemkeys |= this.itemkeys; + play2(toucher, this.noise); - centerprint(other, this.message); + centerprint(toucher, this.message); string oldmsg = this.message; this.message = ""; - SUB_UseTargets(this, other, other); // TODO: should we be using other for the trigger here? + SUB_UseTargets(this, toucher, toucher); // TODO: should we be using toucher for the trigger here? this.message = oldmsg; }; @@ -99,9 +99,9 @@ void spawn_item_key(entity this) this.noalign = 1; if (this.noalign) - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); else - this.movetype = MOVETYPE_TOSS; + set_movetype(this, MOVETYPE_TOSS); precache_sound(this.noise); @@ -160,7 +160,7 @@ spawnfunc(item_key) // 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!"); - remove(this); + delete(this); return; } @@ -202,7 +202,7 @@ spawnfunc(item_key) if (this.netname == "") { objerror(this, "item_key doesn't have a default name for this key and a custom one was not specified!"); - remove(this); + delete(this); return; } break; @@ -217,7 +217,7 @@ spawnfunc(item_key) _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!"); - remove(this); + delete(this); return; } diff --git a/qcsrc/server/mapvoting.qc b/qcsrc/server/mapvoting.qc index b3a496f41e..b5a6014462 100644 --- a/qcsrc/server/mapvoting.qc +++ b/qcsrc/server/mapvoting.qc @@ -4,6 +4,7 @@ #include "command/cmd.qh" #include "command/getreplies.qh" #include "../common/constants.qh" +#include #include "../common/mapinfo.qh" #include "../common/playerstats.qh" #include "../common/util.qh" @@ -14,7 +15,6 @@ float mapvote_nextthink; float mapvote_keeptwotime; float mapvote_timeout; -string mapvote_message; const float MAPVOTE_SCREENSHOT_DIRS_COUNT = 4; string mapvote_screenshot_dirs[MAPVOTE_SCREENSHOT_DIRS_COUNT]; float mapvote_screenshot_dirs_count; @@ -41,10 +41,10 @@ entity mapvote_ent; * Returns the gamtype ID from its name, if type_name isn't a real gametype it * checks for sv_vote_gametype_(type_name)_type */ -float GameTypeVote_Type_FromString(string type_name) +Gametype GameTypeVote_Type_FromString(string type_name) { - float type = MapInfo_Type_FromString(type_name); - if ( type == 0 ) + Gametype type = MapInfo_Type_FromString(type_name); + if (type == NULL) type = MapInfo_Type_FromString(cvar_string( strcat("sv_vote_gametype_",type_name,"_type"))); return type; @@ -54,22 +54,22 @@ int GameTypeVote_AvailabilityStatus(string type_name) { int flag = GTV_FORBIDDEN; - float type = MapInfo_Type_FromString(type_name); - if ( type == 0 ) + Gametype type = MapInfo_Type_FromString(type_name); + if ( type == NULL ) { type = MapInfo_Type_FromString(cvar_string( strcat("sv_vote_gametype_",type_name,"_type"))); flag |= GTV_CUSTOM; } - if( type == 0 ) + if( type == NULL ) return flag; if ( autocvar_nextmap != "" ) { - if ( !MapInfo_Get_ByName(autocvar_nextmap, false, 0) ) + if ( !MapInfo_Get_ByName(autocvar_nextmap, false, NULL) ) return flag; - if (!(MapInfo_Map_supportedGametypes & type)) + if (!(MapInfo_Map_supportedGametypes & type.m_flags)) return flag; } @@ -83,7 +83,7 @@ float GameTypeVote_GetMask() n = min(MAPVOTE_COUNT, n); gametype_mask = 0; for(j = 0; j < n; ++j) - gametype_mask |= GameTypeVote_Type_FromString(argv(j)); + gametype_mask |= GameTypeVote_Type_FromString(argv(j)).m_flags; return gametype_mask; } @@ -92,7 +92,7 @@ string GameTypeVote_MapInfo_FixName(string m) if ( autocvar_sv_vote_gametype ) { MapInfo_Enumerate(); - MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); + _MapInfo_FilterGametype(GameTypeVote_GetMask(), 0, MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags(), 0); } return MapInfo_FixName(m); } @@ -255,7 +255,6 @@ void MapVote_Init() mapvote_timeout = time + autocvar_g_maplist_votable_timeout; if(mapvote_count_real < 3 || mapvote_keeptwotime <= time) mapvote_keeptwotime = 0; - mapvote_message = "Choose a map and press its key!"; MapVote_Spawn(); } @@ -271,14 +270,14 @@ void MapVote_SendPicture(entity to, int id) void MapVote_WriteMask() { - float i; if ( mapvote_count < 24 ) { - float mask,power; - mask = 0; - for(i = 0, power = 1; i < mapvote_count; ++i, power *= 2) - if(mapvote_maps_flags[i] & GTV_AVAILABLE ) - mask |= power; + int mask = 0; + for(int j = 0; j < mapvote_count; ++j) + { + if(mapvote_maps_flags[j] & GTV_AVAILABLE) + mask |= BIT(j); + } if(mapvote_count < 8) WriteByte(MSG_ENTITY, mask); @@ -289,8 +288,8 @@ void MapVote_WriteMask() } else { - for ( i = 0; i < mapvote_count; ++i ) - WriteByte(MSG_ENTITY, mapvote_maps_flags[i]); + for (int j = 0; j < mapvote_count; ++j) + WriteByte(MSG_ENTITY, mapvote_maps_flags[j]); } } @@ -336,6 +335,8 @@ void GameTypeVote_SendOption(int i) strcat("sv_vote_gametype_",type_name,"_name"))); WriteString(MSG_ENTITY, cvar_string( strcat("sv_vote_gametype_",type_name,"_description"))); + WriteString(MSG_ENTITY, cvar_string( + strcat("sv_vote_gametype_",type_name,"_type"))); } } } @@ -522,7 +523,7 @@ float MapVote_CheckRules_2() for(i = 0; i < mapvote_count_real; ++i) if ( mapvote_maps_flags[i] & GTV_AVAILABLE ) { - RandomSelection_Add(NULL, i, string_null, 1, mapvote_selections[i]); + RandomSelection_AddFloat(i, 1, mapvote_selections[i]); if ( gametypevote && mapvote_maps[i] == MapInfo_Type_ToString(MapInfo_CurrentGametype()) ) { currentVotes = mapvote_selections[i]; @@ -542,7 +543,7 @@ float MapVote_CheckRules_2() for(i = 0; i < mapvote_count_real; ++i) if(i != firstPlace) if ( mapvote_maps_flags[i] & GTV_AVAILABLE ) - RandomSelection_Add(NULL, i, string_null, 1, mapvote_selections[i]); + RandomSelection_AddFloat(i, 1, mapvote_selections[i]); secondPlace = RandomSelection_chosen_float; secondPlaceVotes = RandomSelection_best_priority; //dprint("Second place: ", ftos(secondPlace), "\n"); @@ -559,7 +560,6 @@ float MapVote_CheckRules_2() { float didntvote; MapVote_TouchMask(); - mapvote_message = "Now decide between the TOP TWO!"; mapvote_keeptwotime = 0; result = strcat(":vote:keeptwo:", mapvote_maps[firstPlace]); result = strcat(result, ":", ftos(firstPlaceVotes)); @@ -590,10 +590,8 @@ float MapVote_CheckRules_2() void MapVote_Tick() { - float keeptwo; float totalvotes; - keeptwo = mapvote_keeptwotime; MapVote_CheckRules_1(); // count if(MapVote_CheckRules_2()) // decide return; @@ -695,12 +693,12 @@ void MapVote_Think() MapVote_Tick(); } -float GameTypeVote_SetGametype(float type) +float GameTypeVote_SetGametype(Gametype type) { if (MapInfo_CurrentGametype() == type) return true; - float tsave = MapInfo_CurrentGametype(); + Gametype tsave = MapInfo_CurrentGametype(); MapInfo_SwitchGameType(type); @@ -752,7 +750,7 @@ float GameTypeVote_Finished(float pos) float GameTypeVote_AddVotable(string nextMode) { float j; - if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == 0 ) + if ( nextMode == "" || GameTypeVote_Type_FromString(nextMode) == NULL ) return false; for(j = 0; j < mapvote_count; ++j) if(mapvote_maps[j] == nextMode) diff --git a/qcsrc/server/matrix.qc b/qcsrc/server/matrix.qc index b7d26decf5..4d235da694 100644 --- a/qcsrc/server/matrix.qc +++ b/qcsrc/server/matrix.qc @@ -1,6 +1,6 @@ #include "matrix.qh" -#include "cl_player.qh" +#include "player.qh" var void MX_Handle(int buf, string ancestor) { diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index 2c206cf1a7..39c42a5569 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -4,13 +4,14 @@ #include "constants.qh" #include "g_hook.qh" #include "ipban.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/t_items.qh" #include "weapons/accuracy.qh" #include "weapons/csqcprojectile.qh" #include "weapons/selection.qh" -#include "../common/command/generic.qh" +#include "../common/command/_mod.qh" #include "../common/constants.qh" +#include #include "../common/deathtypes/all.qh" #include "../common/mapinfo.qh" #include "../common/notifications/all.qh" @@ -19,10 +20,10 @@ #include "../common/triggers/subs.qh" #include "../common/util.qh" #include "../common/turrets/sv_turrets.qh" -#include "../common/weapons/all.qh" +#include #include "../common/vehicles/sv_vehicles.qh" #include "../common/vehicles/vehicle.qh" -#include "../common/items/all.qc" +#include "../common/items/_mod.qh" #include "../common/state.qh" #include "../common/effects/qc/globalsound.qh" #include "../lib/csqcmodel/sv_model.qh" @@ -33,20 +34,25 @@ void crosshair_trace(entity pl) { traceline_antilag(pl, pl.cursor_trace_start, pl.cursor_trace_start + normalize(pl.cursor_trace_endpos - pl.cursor_trace_start) * MAX_SHOT_DISTANCE, MOVE_NORMAL, pl, ANTILAG_LATENCY(pl)); } +.bool ctrace_solidchanged; void crosshair_trace_plusvisibletriggers(entity pl) { - entity first; - entity e; - first = findchainfloat(solid, SOLID_TRIGGER); - - for (e = first; e; e = e.chain) - if (e.model != "") - e.solid = SOLID_BSP; + FOREACH_ENTITY_FLOAT(solid, SOLID_TRIGGER, + { + if(it.model != "") + { + it.solid = SOLID_BSP; + it.ctrace_solidchanged = true; + } + }); crosshair_trace(pl); - for (e = first; e; e = e.chain) - e.solid = SOLID_TRIGGER; + FOREACH_ENTITY_FLOAT(ctrace_solidchanged, true, + { + it.solid = SOLID_TRIGGER; + it.ctrace_solidchanged = false; + }); } void WarpZone_crosshair_trace(entity pl) { @@ -92,7 +98,7 @@ void GameLogEcho(string s) } if (autocvar_sv_eventlog_console) { - LOG_INFO(s, "\n"); + dedicated_print(strcat(s, "\n")); } } @@ -167,7 +173,7 @@ entity findnearest(vector point, .string field, string value, vector axismod) { LOG_TRACE("Nearest point ("); LOG_TRACE(nearest_entity[0].netname); - LOG_TRACE(") is not visible, using a visible one.\n"); + LOG_TRACE(") is not visible, using a visible one."); } return nearest_entity[i]; } @@ -176,7 +182,7 @@ entity findnearest(vector point, .string field, string value, vector axismod) if (num_nearest == 0) return NULL; - LOG_TRACE("Not seeing any location point, using nearest as fallback.\n"); + LOG_TRACE("Not seeing any location point, using nearest as fallback."); /* DEBUGGING CODE: dprint("Candidates were: "); for(j = 0; j < num_nearest; ++j) @@ -223,15 +229,23 @@ string formatmessage(entity this, string msg) n = 7; ammoitems = "batteries"; - if(this.items & ITEM_Plasma.m_itemid) ammoitems = ITEM_Plasma.m_name; - if(this.items & ITEM_Cells.m_itemid) ammoitems = ITEM_Cells.m_name; - if(this.items & ITEM_Rockets.m_itemid) ammoitems = ITEM_Rockets.m_name; - if(this.items & ITEM_Shells.m_itemid) ammoitems = ITEM_Shells.m_name; + switch((PS(this).m_weapon).ammo_field) + { + case ammo_shells: ammoitems = ITEM_Shells.m_name; break; + case ammo_nails: ammoitems = ITEM_Bullets.m_name; break; + case ammo_rockets: ammoitems = ITEM_Rockets.m_name; break; + case ammo_cells: ammoitems = ITEM_Cells.m_name; break; + case ammo_plasma: ammoitems = ITEM_Plasma.m_name; break; + case ammo_fuel: ammoitems = ITEM_JetpackFuel.m_name; break; + } WarpZone_crosshair_trace(this); cursor = trace_endpos; cursor_ent = trace_ent; + MUTATOR_CALLHOOK(PreFormatMessage, this, msg); + msg = M_ARGV(1, string); + while (1) { if (n < 1) break; // too many replacements @@ -452,7 +466,7 @@ void GetCvars(entity this, int f) string playername(entity p) { string t; - if (teamplay && !intermission_running && IS_PLAYER(p)) + if (teamplay && !gameover && IS_PLAYER(p)) { t = Team_ColorCode(p.team); return strcat(t, strdecolorize(p.netname)); @@ -881,7 +895,7 @@ void InitializeEntitiesRun() { entity startoflist = initialize_entity_first; initialize_entity_first = NULL; - remove = remove_except_protected; + delete_fn = remove_except_protected; for (entity e = startoflist; e; e = e.initialize_entity_next) { e.remove_except_protected_forbidden = 1; @@ -912,7 +926,7 @@ void InitializeEntitiesRun() } e = next; } - remove = remove_unsafely; + delete_fn = remove_unsafely; } .float(entity) isEliminated; @@ -1031,8 +1045,7 @@ bool SUB_NoImpactCheck(entity this, entity toucher) // these stop the projectile from moving, so... if(trace_dphitcontents == 0) { - //dprint("A hit happened with zero hit contents... DEBUG THIS, this should never happen for projectiles! Projectile will self-destruct.\n"); - 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: %d, classname: %s, origin: %s)\n", etof(this), this.classname, vtos(this.origin)); + 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); } if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) @@ -1045,11 +1058,11 @@ bool SUB_NoImpactCheck(entity this, entity toucher) traceline(this.origin - tic, this.origin + tic, MOVE_NORMAL, this); if (trace_fraction >= 1) { - LOG_TRACE("Odd... did not hit...?\n"); + LOG_TRACE("Odd... did not hit...?"); } else if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) { - LOG_TRACE("Detected and prevented the sky-grapple bug.\n"); + LOG_TRACE("Detected and prevented the sky-grapple bug."); return true; } } @@ -1073,10 +1086,10 @@ bool WarpZone_Projectile_Touch_ImpactFilter_Callback(entity this, entity toucher else if(this.classname == "spike") { W_Crylink_Dequeue(this); - remove(this); + delete(this); } else - remove(this); + delete(this); return true; } if(trace_ent && trace_ent.solid > SOLID_TRIGGER) @@ -1140,7 +1153,6 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma { float m, i; vector start, org, delta, end, enddown, mstart; - entity sp; m = e.dphitcontentsmask; e.dphitcontentsmask = goodcontents | badcontents; @@ -1196,16 +1208,26 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma continue; // rule 4: we must "see" some spawnpoint or item - for(sp = NULL; (sp = find(sp, classname, "info_player_deathmatch")); ) - if(checkpvs(mstart, sp)) - if((traceline(mstart, sp.origin, MOVE_NORMAL, e), trace_fraction) >= 1) - break; + entity sp = NULL; + IL_EACH(g_spawnpoints, checkpvs(mstart, it), + { + if((traceline(mstart, it.origin, MOVE_NORMAL, e), trace_fraction) >= 1) + { + sp = it; + break; + } + }); if(!sp) { - for(sp = NULL; (sp = findflags(sp, flags, FL_ITEM)); ) - if(checkpvs(mstart, sp)) - if((traceline(mstart, sp.origin + (sp.mins + sp.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1) - break; + IL_EACH(g_items, checkpvs(mstart, it), + { + if((traceline(mstart, it.origin + (it.mins + it.maxs) * 0.5, MOVE_NORMAL, e), trace_fraction) >= 1) + { + sp = it; + break; + } + }); + if(!sp) continue; } @@ -1240,7 +1262,7 @@ float MoveToRandomLocationWithinBounds(entity e, vector boundmin, vector boundma { setorigin(e, start); e.angles = vectoangles(end - start); - LOG_TRACE("Needed ", ftos(i + 1), " attempts\n"); + LOG_TRACE("Needed ", ftos(i + 1), " attempts"); return true; } else @@ -1319,7 +1341,7 @@ void detach_sameorigin(entity e) void follow_sameorigin(entity e, entity to) { - e.movetype = MOVETYPE_FOLLOW; // make the hole follow + set_movetype(e, MOVETYPE_FOLLOW); // make the hole follow e.aiment = to; // make the hole follow bmodel e.punchangle = to.angles; // the original angles of bmodel e.view_ofs = e.origin - to.origin; // relative origin @@ -1328,7 +1350,7 @@ void follow_sameorigin(entity e, entity to) void unfollow_sameorigin(entity e) { - e.movetype = MOVETYPE_NONE; + set_movetype(e, MOVETYPE_NONE); } entity gettaginfo_relative_ent; @@ -1350,7 +1372,7 @@ vector gettaginfo_relative(entity e, float tag) void SetMovetypeFollow(entity ent, entity e) { // FIXME this may not be warpzone aware - ent.movetype = MOVETYPE_FOLLOW; // make the hole follow + set_movetype(ent, MOVETYPE_FOLLOW); // make the hole follow ent.solid = SOLID_NOT; // MOVETYPE_FOLLOW is always non-solid - this means this cannot be teleported by warpzones any more! Instead, we must notice when our owner gets teleported. ent.aiment = e; // make the hole follow bmodel ent.punchangle = e.angles; // the original angles of bmodel @@ -1361,14 +1383,14 @@ void SetMovetypeFollow(entity ent, entity e) } void UnsetMovetypeFollow(entity ent) { - ent.movetype = MOVETYPE_FLY; + set_movetype(ent, MOVETYPE_FLY); PROJECTILE_MAKETRIGGER(ent); ent.aiment = NULL; } float LostMovetypeFollow(entity ent) { /* - if(ent.movetype != MOVETYPE_FOLLOW) + if(ent.move_movetype != MOVETYPE_FOLLOW) if(ent.aiment) error("???"); */ diff --git a/qcsrc/server/miscfunctions.qh b/qcsrc/server/miscfunctions.qh index a8304b0e3a..eb0f3a910a 100644 --- a/qcsrc/server/miscfunctions.qh +++ b/qcsrc/server/miscfunctions.qh @@ -121,7 +121,7 @@ void WarpZone_traceline_antilag (entity source, vector v1, vector v2, float nomo #define ITEM_TOUCH_NEEDKILL() (((trace_dpstartcontents | trace_dphitcontents) & DPCONTENTS_NODROP) || (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY)) #define ITEM_DAMAGE_NEEDKILL(dt) (((dt) == DEATH_HURTTRIGGER.m_id) || ((dt) == DEATH_SLIME.m_id) || ((dt) == DEATH_LAVA.m_id) || ((dt) == DEATH_SWAMP.m_id)) -#define PROJECTILE_TOUCH(this) MACRO_BEGIN if (WarpZone_Projectile_Touch(this)) return; MACRO_END +#define PROJECTILE_TOUCH(e,t) MACRO_BEGIN if (WarpZone_Projectile_Touch(e,t)) return; MACRO_END #define CENTER_OR_VIEWOFS(ent) (ent.origin + (IS_PLAYER(ent) ? ent.view_ofs : ((ent.mins + ent.maxs) * 0.5))) @@ -182,18 +182,18 @@ float g_pickup_armormedium_anyway; float g_pickup_armorbig; float g_pickup_armorbig_max; float g_pickup_armorbig_anyway; -float g_pickup_armorlarge; -float g_pickup_armorlarge_max; -float g_pickup_armorlarge_anyway; +float g_pickup_armormega; +float g_pickup_armormega_max; +float g_pickup_armormega_anyway; float g_pickup_healthsmall; float g_pickup_healthsmall_max; float g_pickup_healthsmall_anyway; float g_pickup_healthmedium; float g_pickup_healthmedium_max; float g_pickup_healthmedium_anyway; -float g_pickup_healthlarge; -float g_pickup_healthlarge_max; -float g_pickup_healthlarge_anyway; +float g_pickup_healthbig; +float g_pickup_healthbig_max; +float g_pickup_healthbig_anyway; float g_pickup_healthmega; float g_pickup_healthmega_max; float g_pickup_healthmega_anyway; @@ -256,6 +256,8 @@ void readlevelcvars() g_jetpack = cvar("g_jetpack"); sv_maxidle = cvar("sv_maxidle"); sv_maxidle_spectatorsareidle = cvar("sv_maxidle_spectatorsareidle"); + sv_maxidle_slots = cvar("sv_maxidle_slots"); + sv_maxidle_slots_countbots = cvar("sv_maxidle_slots_countbots"); sv_autotaunt = cvar("sv_autotaunt"); sv_taunt = cvar("sv_taunt"); @@ -310,18 +312,18 @@ void readlevelcvars() g_pickup_armorbig = cvar("g_pickup_armorbig"); g_pickup_armorbig_max = cvar("g_pickup_armorbig_max"); g_pickup_armorbig_anyway = cvar("g_pickup_armorbig_anyway"); - g_pickup_armorlarge = cvar("g_pickup_armorlarge"); - g_pickup_armorlarge_max = cvar("g_pickup_armorlarge_max"); - g_pickup_armorlarge_anyway = cvar("g_pickup_armorlarge_anyway"); + g_pickup_armormega = cvar("g_pickup_armormega"); + g_pickup_armormega_max = cvar("g_pickup_armormega_max"); + g_pickup_armormega_anyway = cvar("g_pickup_armormega_anyway"); g_pickup_healthsmall = cvar("g_pickup_healthsmall"); g_pickup_healthsmall_max = cvar("g_pickup_healthsmall_max"); g_pickup_healthsmall_anyway = cvar("g_pickup_healthsmall_anyway"); g_pickup_healthmedium = cvar("g_pickup_healthmedium"); g_pickup_healthmedium_max = cvar("g_pickup_healthmedium_max"); g_pickup_healthmedium_anyway = cvar("g_pickup_healthmedium_anyway"); - g_pickup_healthlarge = cvar("g_pickup_healthlarge"); - g_pickup_healthlarge_max = cvar("g_pickup_healthlarge_max"); - g_pickup_healthlarge_anyway = cvar("g_pickup_healthlarge_anyway"); + g_pickup_healthbig = cvar("g_pickup_healthbig"); + g_pickup_healthbig_max = cvar("g_pickup_healthbig_max"); + g_pickup_healthbig_anyway = cvar("g_pickup_healthbig_anyway"); g_pickup_healthmega = cvar("g_pickup_healthmega"); g_pickup_healthmega_max = cvar("g_pickup_healthmega_max"); g_pickup_healthmega_anyway = cvar("g_pickup_healthmega_anyway"); diff --git a/qcsrc/server/mutators/_mod.inc b/qcsrc/server/mutators/_mod.inc index 3d2321896c..f0108dec37 100644 --- a/qcsrc/server/mutators/_mod.inc +++ b/qcsrc/server/mutators/_mod.inc @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#include + +#include diff --git a/qcsrc/server/mutators/_mod.qh b/qcsrc/server/mutators/_mod.qh index 8feb1f37d8..9888c94666 100644 --- a/qcsrc/server/mutators/_mod.qh +++ b/qcsrc/server/mutators/_mod.qh @@ -1,2 +1,4 @@ // generated file; do not modify -#include +#include + +#include diff --git a/qcsrc/server/mutators/all.inc b/qcsrc/server/mutators/all.inc deleted file mode 100644 index 1a80b44094..0000000000 --- a/qcsrc/server/mutators/all.inc +++ /dev/null @@ -1,13 +0,0 @@ -#include "mutator/gamemode_assault.qc" -#include "mutator/gamemode_ca.qc" -#include "mutator/gamemode_ctf.qc" -#include "mutator/gamemode_cts.qc" -#include "mutator/gamemode_deathmatch.qc" -#include "mutator/gamemode_domination.qc" -#include "mutator/gamemode_freezetag.qc" -#include "mutator/gamemode_invasion.qc" -#include "mutator/gamemode_keepaway.qc" -#include "mutator/gamemode_keyhunt.qc" -#include "mutator/gamemode_lms.qc" -#include "mutator/gamemode_race.qc" -#include "mutator/gamemode_tdm.qc" diff --git a/qcsrc/server/mutators/all.qc b/qcsrc/server/mutators/all.qc deleted file mode 100644 index d742c77af1..0000000000 --- a/qcsrc/server/mutators/all.qc +++ /dev/null @@ -1,88 +0,0 @@ -#include "all.qh" -#if defined(CSQC) -#elif defined(MENUQC) -#elif defined(SVQC) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include "../weapons/accuracy.qh" - #include "../weapons/common.qh" - #include "../weapons/csqcprojectile.qh" - #include "../weapons/hitplot.qh" - #include "../weapons/selection.qh" - #include "../weapons/spawning.qh" - #include "../weapons/throwing.qh" - #include "../weapons/tracing.qh" - #include "../weapons/weaponstats.qh" - #include "../weapons/weaponsystem.qh" - #include - #include "../autocvars.qh" - #include "../constants.qh" - #include "../defs.qh" - #include - #include - #include "all.qh" - #include - #include - #include "../campaign.qh" - #include - #include - #include "../command/common.qh" - #include "../command/banning.qh" - #include "../command/radarmap.qh" - #include "../command/vote.qh" - #include "../command/getreplies.qh" - #include "../command/cmd.qh" - #include "../command/sv_cmd.qh" - #include - #include - #include - #include "../anticheat.qh" - #include "../cheats.qh" - #include - #include "../portals.qh" - #include "../g_hook.qh" - #include "../scores.qh" - #include "../spawnpoints.qh" - #include "../mapvoting.qh" - #include "../ipban.qh" - #include "../race.qh" - #include "../antilag.qh" - #include "../playerdemo.qh" - #include "../round_handler.qh" - #include "../item_key.qh" - #include "../pathlib/pathlib.qh" - #include -#endif - -#include "all.qh" - -STATIC_INIT_LATE(Gametype) { - Gametype g = MapInfo_Type(MapInfo_CurrentGametype()); - if (g) { - for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) { - string s = car(_s); - FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break)); - } - } -} - -#define IMPLEMENTATION -#include "all.inc" -#undef IMPLEMENTATION diff --git a/qcsrc/server/mutators/all.qh b/qcsrc/server/mutators/all.qh deleted file mode 100644 index 4a1f2b3533..0000000000 --- a/qcsrc/server/mutators/all.qh +++ /dev/null @@ -1,6 +0,0 @@ -#pragma once - -#include "mutator.qh" -#include "gamemode.qh" - -#include "all.inc" diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index 6fbce15b95..09e1cf70bd 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -16,6 +16,14 @@ MUTATOR_HOOKABLE(MakePlayerObserver, EV_MakePlayerObserver) /**/ MUTATOR_HOOKABLE(PutClientInServer, EV_PutClientInServer); +/** + * return true to prevent a spectator/observer to spawn as player + */ + #define EV_ForbidSpawn(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(ForbidSpawn, EV_ForbidSpawn); + /** called when a player spawns as player, after shared setup, before his weapon is chosen (so items may be changed in here) */ #define EV_PlayerSpawn(i, o) \ /** player spawning */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -55,9 +63,20 @@ MUTATOR_HOOKABLE(ClientDisconnect, EV_ClientDisconnect); /**/ MUTATOR_HOOKABLE(PlayerDies, EV_PlayerDies); +/** allows overriding the frag centerprint messages */ +#define EV_FragCenterMessage(i, o) \ + /** attacker */ i(entity, MUTATOR_ARGV_0_entity) \ + /** target */ i(entity, MUTATOR_ARGV_1_entity) \ + /** deathtype */ i(float, MUTATOR_ARGV_2_float) \ + /** attacker kcount*/ i(int, MUTATOR_ARGV_3_int) \ + /** targ killcount */ i(int, MUTATOR_ARGV_4_int) \ + /**/ +MUTATOR_HOOKABLE(FragCenterMessage, EV_FragCenterMessage); + /** called when a player dies to e.g. remove stuff he was carrying */ #define EV_PlayHitsound(i, o) \ /** victim */ i(entity, MUTATOR_ARGV_0_entity) \ + /** attacker */ i(entity, MUTATOR_ARGV_1_entity) \ /**/ MUTATOR_HOOKABLE(PlayHitsound, EV_PlayHitsound); @@ -77,6 +96,14 @@ MUTATOR_HOOKABLE(WeaponSound, EV_WeaponSound); /**/ MUTATOR_HOOKABLE(ItemModel, EV_ItemModel); +/** called when an item sound is about to be played, allows custom paths etc. */ +#define EV_ItemSound(i, o) \ + /** sound */ i(string, MUTATOR_ARGV_0_string) \ + /** output */ i(string, MUTATOR_ARGV_1_string) \ + /**/ o(string, MUTATOR_ARGV_1_string) \ + /**/ +MUTATOR_HOOKABLE(ItemSound, EV_ItemSound); + /** called when someone was fragged by "self", and is expected to change frag_score to adjust scoring for the kill */ #define EV_GiveFragsForKill(i, o) \ /** attacker */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -89,14 +116,14 @@ MUTATOR_HOOKABLE(GiveFragsForKill, EV_GiveFragsForKill); /** called when the match ends */ MUTATOR_HOOKABLE(MatchEnd, EV_NO_ARGS); -/** should adjust number to contain the team count */ -#define EV_GetTeamCount(i, o) \ - /** number of teams */ i(float, MUTATOR_ARGV_0_float) \ +/** allows adjusting allowed teams */ +#define EV_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) \ /**/ -MUTATOR_HOOKABLE(GetTeamCount, EV_GetTeamCount); +MUTATOR_HOOKABLE(CheckAllowedTeams, EV_CheckAllowedTeams); /** copies variables for spectating "spectatee" to "this" */ #define EV_SpectateCopy(i, o) \ @@ -115,6 +142,14 @@ MUTATOR_HOOKABLE(SpectateCopy, EV_SpectateCopy); /**/ MUTATOR_HOOKABLE(FormatMessage, EV_FormatMessage); +/** called before any formatting is applied, handy for tweaking the message before scripts get ahold of it */ +#define EV_PreFormatMessage(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** message */ i(string, MUTATOR_ARGV_1_string) \ + /**/ o(string, MUTATOR_ARGV_1_string) \ + /**/ +MUTATOR_HOOKABLE(PreFormatMessage, EV_PreFormatMessage); + /** returns true if throwing the current weapon shall not be allowed */ #define EV_ForbidThrowCurrentWeapon(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -271,6 +306,15 @@ MUTATOR_HOOKABLE(MonsterMove, EV_MonsterMove); /** called when a monster looks for another target */ MUTATOR_HOOKABLE(MonsterFindTarget, EV_NO_ARGS); +/** + * called when validating a monster's target + */ +#define EV_MonsterValidTarget(i, o) \ + /** monster */ i(entity, MUTATOR_ARGV_0_entity) \ + /** target */ i(entity, MUTATOR_ARGV_1_entity) \ + /**/ +MUTATOR_HOOKABLE(MonsterValidTarget, EV_MonsterValidTarget); + /** called to change a random monster to a miniboss */ #define EV_MonsterCheckBossFlag(i, o) \ /** monster */ i(entity, MUTATOR_ARGV_0_entity) \ @@ -327,10 +371,11 @@ MUTATOR_HOOKABLE(PlayerDamage_Calculate, EV_PlayerDamage_Calculate); #define EV_PlayerDamaged(i, o) \ /** attacker */ i(entity, MUTATOR_ARGV_0_entity) \ /** target */ i(entity, MUTATOR_ARGV_1_entity) \ - /** health */ i(int, MUTATOR_ARGV_2_int) \ - /** armor */ i(int, MUTATOR_ARGV_3_int) \ + /** health */ i(float, MUTATOR_ARGV_2_float) \ + /** armor */ i(float, MUTATOR_ARGV_3_float) \ /** location */ i(vector, MUTATOR_ARGV_4_vector) \ /** deathtype */ i(int, MUTATOR_ARGV_5_int) \ + /** potential_damage */ i(float, MUTATOR_ARGV_6_float) \ /**/ MUTATOR_HOOKABLE(PlayerDamaged, EV_PlayerDamaged); @@ -350,7 +395,7 @@ MUTATOR_HOOKABLE(W_DecreaseAmmo, EV_W_DecreaseAmmo); /**/ MUTATOR_HOOKABLE(W_Reload, EV_W_Reload); -/** called at the end of player_powerups() in cl_client.qc, used for manipulating the values which are set by powerup items. */ +/** called at the end of player_powerups() in client.qc, used for manipulating the values which are set by powerup items. */ #define EV_PlayerPowerups(i, o) \ /** player */ i(entity, MUTATOR_ARGV_0_entity) \ /** old items */ i(int, MUTATOR_ARGV_1_int) \ @@ -452,6 +497,7 @@ MUTATOR_HOOKABLE(SV_StartFrame, EV_NO_ARGS); #define EV_SetModname(i, o) \ /** name of the mutator/mod if it warrants showing as such in the server browser */ \ + /**/ i(string, MUTATOR_ARGV_0_string) \ /**/ o(string, MUTATOR_ARGV_0_string) \ /**/ MUTATOR_HOOKABLE(SetModname, EV_SetModname); @@ -499,7 +545,7 @@ MUTATOR_HOOKABLE(BotShouldAttack, EV_BotShouldAttack); MUTATOR_HOOKABLE(PortalTeleport, EV_PortalTeleport); /** - * called whenever a player uses impulse 33 (help me) in cl_impulse.qc + * called whenever a player uses impulse 33 (help me) in impulse.qc * normally help me ping uses .waypointsprite_attachedforcarrier, * but if your mutator uses something different then you can handle it * in a special manner using this hook @@ -513,10 +559,10 @@ MUTATOR_HOOKABLE(HelpMePing, EV_HelpMePing); * called when a vehicle initializes * return true to remove the vehicle */ -#define EV_VehicleSpawn(i, o) \ +#define EV_VehicleInit(i, o) \ /** vehicle */ i(entity, MUTATOR_ARGV_0_entity) \ /**/ -MUTATOR_HOOKABLE(VehicleSpawn, EV_VehicleSpawn); +MUTATOR_HOOKABLE(VehicleInit, EV_VehicleInit); /** * called when a player enters a vehicle @@ -656,7 +702,7 @@ MUTATOR_HOOKABLE(SetChangeParms, EV_NO_ARGS); MUTATOR_HOOKABLE(DecodeLevelParms, EV_NO_ARGS); #define EV_GetRecords(i, o) \ - /** page */ i(int, MUTATOR_ARGV_1_int) \ + /** page */ i(int, MUTATOR_ARGV_0_int) \ /** record list */ i(string, MUTATOR_ARGV_1_string) \ /**/ o(string, MUTATOR_ARGV_1_string) \ /**/ @@ -748,7 +794,7 @@ MUTATOR_HOOKABLE(CheckRules_World, EV_CheckRules_World); MUTATOR_HOOKABLE(WantWeapon, EV_WantWeapon); #define EV_AddPlayerScore(i, o) \ - /** score field */ i(int, MUTATOR_ARGV_0_int) \ + /** score field */ i(PlayerScoreField, MUTATOR_ARGV_0_entity) \ /** score */ i(float, MUTATOR_ARGV_1_float) \ /**/ o(float, MUTATOR_ARGV_1_float) \ /** player */ i(entity, MUTATOR_ARGV_2_entity) \ @@ -837,3 +883,37 @@ MUTATOR_HOOKABLE(Player_ChangeTeam, EV_Player_ChangeTeam); /** data */ i(string, MUTATOR_ARGV_2_string) \ /**/ MUTATOR_HOOKABLE(URI_GetCallback, EV_URI_GetCallback); + +/** + * return true to prevent weapon use for a player + */ + #define EV_ForbidWeaponUse(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /**/ +MUTATOR_HOOKABLE(ForbidWeaponUse, EV_ForbidWeaponUse); + +/** called when creating a clone of the player (usually for corpses that stay after the player has re-spawned) */ +#define EV_CopyBody(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** newly created clone */ i(entity, MUTATOR_ARGV_1_entity) \ + /** keepvelocity? */ i(bool, MUTATOR_ARGV_2_bool) \ + /**/ +MUTATOR_HOOKABLE(CopyBody, EV_CopyBody); + +/** called when sending a chat message, ret argument can be changed to prevent the message */ +#define EV_ChatMessage(i, o) \ + /** sender */ i(entity, MUTATOR_ARGV_0_entity) \ + /** ret */ i(int, MUTATOR_ARGV_1_int) \ + /**/ o(int, MUTATOR_ARGV_1_int) \ + /**/ +MUTATOR_HOOKABLE(ChatMessage, EV_ChatMessage); + +/** return true to prevent sending a chat (private, team or regular) message from reaching a certain player */ +#define EV_ChatMessageTo(i, o) \ + /** destination player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** sender */ i(entity, MUTATOR_ARGV_1_entity) \ + /**/ +MUTATOR_HOOKABLE(ChatMessageTo, EV_ChatMessageTo); + +/** return true to just restart the match, for modes that don't support readyrestart */ +MUTATOR_HOOKABLE(ReadyRestart_Deny, EV_NO_ARGS); diff --git a/qcsrc/server/mutators/gamemode.qh b/qcsrc/server/mutators/gamemode.qh index ee80b44b06..9a473affdf 100644 --- a/qcsrc/server/mutators/gamemode.qh +++ b/qcsrc/server/mutators/gamemode.qh @@ -1,28 +1,87 @@ #pragma once -#include -#include -#include -#include -#include #include #include #include #include #include -#include -#include -#include -#include +#include "mutator.qh" -#include +// TODO: trim +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include -#include +#include -#include +#include + +#include #include #include diff --git a/qcsrc/server/mutators/loader.qc b/qcsrc/server/mutators/loader.qc new file mode 100644 index 0000000000..1784e72ecd --- /dev/null +++ b/qcsrc/server/mutators/loader.qc @@ -0,0 +1,11 @@ +#include "loader.qh" + +STATIC_INIT_LATE(Gametype) { + Gametype g = MapInfo_CurrentGametype(); + if (g) { + for (string _s = g.m_mutators; _s != ""; _s = cdr(_s)) { + string s = car(_s); + FOREACH(Mutators, it.m_name == s, LAMBDA(Mutator_Add(it); break)); + } + } +} diff --git a/qcsrc/server/mutators/loader.qh b/qcsrc/server/mutators/loader.qh new file mode 100644 index 0000000000..6f70f09bee --- /dev/null +++ b/qcsrc/server/mutators/loader.qh @@ -0,0 +1 @@ +#pragma once diff --git a/qcsrc/server/mutators/mutator.qh b/qcsrc/server/mutators/mutator.qh index 40fad29b34..e051cd6978 100644 --- a/qcsrc/server/mutators/mutator.qh +++ b/qcsrc/server/mutators/mutator.qh @@ -2,24 +2,18 @@ #include -#include -#include -#include +#include +#include +#include #include #include #include #include #include -#include -#include -#include +#include -#include -#include - -#include -#include +#include #include #include @@ -33,7 +27,7 @@ #include #include -#include +#include #include #include diff --git a/qcsrc/server/mutators/mutator/gamemode_assault.qc b/qcsrc/server/mutators/mutator/gamemode_assault.qc index 938cdd748b..3998741434 100644 --- a/qcsrc/server/mutators/mutator/gamemode_assault.qc +++ b/qcsrc/server/mutators/mutator/gamemode_assault.qc @@ -1,72 +1,7 @@ #include "gamemode_assault.qh" -#ifndef GAMEMODE_ASSAULT_H -#define GAMEMODE_ASSAULT_H -void assault_ScoreRules(); -void ActivateTeamplay(); - -REGISTER_MUTATOR(as, false) -{ - ActivateTeamplay(); - have_team_spawns = -1; // request team spawns - - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - assault_ScoreRules(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back assault_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - 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; -.void(entity this) havocbot_previous_role; - -void(entity this) havocbot_role_ast_defense; -void(entity this) havocbot_role_ast_offense; -.entity havocbot_ast_target; - -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; - -// scoreboard stuff -const float ST_ASSAULT_OBJECTIVES = 1; -const float SP_ASSAULT_OBJECTIVES = 4; - -// predefined spawnfuncs -void target_objective_decrease_activate(entity this); -#endif - -#ifdef IMPLEMENTATION .entity sprite; +#define AS_ROUND_DELAY 5 // random functions void assault_objective_use(entity this, entity actor, entity trigger) @@ -76,13 +11,10 @@ void assault_objective_use(entity this, entity actor, entity trigger) //print("^2Activated objective ", this.targetname, "=", etos(this), "\n"); //print("Activator is ", actor.classname, "\n"); - for (entity e = NULL; (e = find(e, target, this.targetname)); ) + IL_EACH(g_assault_objectivedecreasers, it.target == this.targetname, { - if (e.classname == "target_objective_decrease") - { - target_objective_decrease_activate(e); - } - } + target_objective_decrease_activate(it); + }); } vector target_objective_spawn_evalfunc(entity this, entity player, entity spot, vector current) @@ -140,18 +72,14 @@ void assault_objective_decrease_use(entity this, entity actor, entity trigger) void assault_setenemytoobjective(entity this) { - entity objective; - for(objective = NULL; (objective = find(objective, targetname, this.target)); ) + IL_EACH(g_assault_objectives, it.targetname == this.target, { - if(objective.classname == "target_objective") - { - if(this.enemy == NULL) - this.enemy = objective; - else - objerror(this, "more than one objective as target - fix the map!"); - break; - } - } + 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!"); @@ -167,32 +95,32 @@ bool assault_decreaser_sprite_visible(entity this, entity player, entity view) void target_objective_decrease_activate(entity this) { - entity ent, spr; + entity spr; this.owner = NULL; - for(ent = NULL; (ent = find(ent, target, this.targetname)); ) + FOREACH_ENTITY_STRING(target, this.targetname, { - if(ent.assault_sprite != NULL) + if(it.assault_sprite != NULL) { - WaypointSprite_Disown(ent.assault_sprite, waypointsprite_deadlifetime); - if(ent.classname == "func_assault_destructible") - ent.sprite = NULL; // TODO: just unsetting it?! + WaypointSprite_Disown(it.assault_sprite, waypointsprite_deadlifetime); + if(it.classname == "func_assault_destructible") + it.sprite = NULL; // TODO: just unsetting it?! } - spr = WaypointSprite_SpawnFixed(WP_Assault, 0.5 * (ent.absmin + ent.absmax), ent, assault_sprite, RADARICON_OBJECTIVE); + 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(ent.classname == "func_assault_destructible") + if(it.classname == "func_assault_destructible") { WaypointSprite_UpdateSprites(spr, WP_AssaultDefend, WP_AssaultDestroy, WP_AssaultDestroy); - WaypointSprite_UpdateMaxHealth(spr, ent.max_health); - WaypointSprite_UpdateHealth(spr, ent.health); - ent.sprite = spr; + 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) @@ -217,7 +145,8 @@ void assault_roundstart_use(entity this, entity actor, entity trigger) SUB_UseTargets(this, this, trigger); //(Re)spawn all turrets - FOREACH_ENTITY_CLASS("turret_main", true, LAMBDA( + IL_EACH(g_turrets, true, + { // Swap turret teams if(it.team == NUM_TEAM_1) it.team = NUM_TEAM_2; @@ -226,7 +155,7 @@ void assault_roundstart_use(entity this, entity actor, entity trigger) // Doubles as teamchange turret_respawn(it); - )); + }); } void assault_roundstart_use_this(entity this) { @@ -251,20 +180,10 @@ void assault_wall_think(entity this) // trigger new round // reset objectives, toggle spawnpoints, reset triggers, ... -void vehicles_clearreturn(entity veh); -void vehicles_spawn(entity this); void assault_new_round(entity this) { //bprint("ASSAULT: new round\n"); - // Eject players from vehicles - FOREACH_CLIENT(IS_PLAYER(it) && it.vehicle, vehicles_exit(it.vehicle, VHEF_RELEASE)); - - FOREACH_ENTITY_FLAGS(vehicle_flags, VHF_ISVEHICLE, LAMBDA( - vehicles_clearreturn(it); - vehicles_spawn(it); - )); - // up round counter this.winning = this.winning + 1; @@ -274,20 +193,37 @@ void assault_new_round(entity this) else assault_attacker_team = NUM_TEAM_1; - FOREACH_ENTITY(IS_NOT_A_CLIENT(it), LAMBDA( + FOREACH_ENTITY_FLOAT(pure_data, false, + { + if(IS_CLIENT(it)) + continue; + 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 - game_starttime) / 60)); + 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() +{ + gameover = 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; @@ -307,7 +243,7 @@ int WinningCondition_Assault() { if(ent.winning) // round end has been triggered by attacking team { - bprint("ASSAULT: round completed...\n"); + 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)); @@ -318,7 +254,17 @@ int WinningCondition_Assault() } else { - assault_new_round(ent); + 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; + gameover = 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)); } } } @@ -329,7 +275,7 @@ int WinningCondition_Assault() // spawnfuncs spawnfunc(info_player_attacker) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } this.team = NUM_TEAM_1; // red, gets swapped every round spawnfunc_info_player_deathmatch(this); @@ -337,7 +283,7 @@ spawnfunc(info_player_attacker) spawnfunc(info_player_defender) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } this.team = NUM_TEAM_2; // blue, gets swapped every round spawnfunc_info_player_deathmatch(this); @@ -345,9 +291,10 @@ spawnfunc(info_player_defender) spawnfunc(target_objective) { - if (!g_assault) { remove(this); return; } + 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); @@ -356,9 +303,10 @@ spawnfunc(target_objective) spawnfunc(target_objective_decrease) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } this.classname = "target_objective_decrease"; + IL_PUSH(g_assault_objectivedecreasers, this); if(!this.dmg) this.dmg = 101; @@ -375,10 +323,11 @@ spawnfunc(target_objective_decrease) spawnfunc(func_breakable); spawnfunc(func_assault_destructible) { - if (!g_assault) { remove(this); return; } + 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; @@ -390,7 +339,7 @@ spawnfunc(func_assault_destructible) spawnfunc(func_assault_wall) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } this.classname = "func_assault_wall"; this.mdl = this.model; @@ -403,7 +352,7 @@ spawnfunc(func_assault_wall) spawnfunc(target_assault_roundend) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } this.winning = 0; // round not yet won by attackers this.classname = "target_assault_roundend"; @@ -414,7 +363,7 @@ spawnfunc(target_assault_roundend) spawnfunc(target_assault_roundstart) { - if (!g_assault) { remove(this); return; } + if (!g_assault) { delete(this); return; } assault_attacker_team = NUM_TEAM_1; this.classname = "target_assault_roundstart"; @@ -426,67 +375,46 @@ spawnfunc(target_assault_roundstart) // legacy bot code void havocbot_goalrating_ast_targets(entity this, float ratingscale) { - entity ad, best, wp, tod; - float radius, found, bestvalue; - vector p; - - ad = findchain(classname, "func_assault_destructible"); - - for (; ad; ad = ad.chain) + IL_EACH(g_assault_destructibles, it.bot_attack, { - if (ad.target == "") - continue; - - if (!ad.bot_attack) + if (it.target == "") continue; - found = false; - for(tod = NULL; (tod = find(tod, targetname, ad.target)); ) + bool found = false; + entity destr = it; + IL_EACH(g_assault_objectivedecreasers, it.targetname == destr.target, { - if(tod.classname == "target_objective_decrease") + if(it.enemy.health > 0 && it.enemy.health < ASSAULT_VALUE_INACTIVE) { - if(tod.enemy.health > 0 && tod.enemy.health < ASSAULT_VALUE_INACTIVE) - { - // dprint(etos(ad),"\n"); - found = true; - break; - } + found = true; + break; } - } + }); if(!found) - { - /// dprint("target not found\n"); continue; - } - /// dprint("target #", etos(ad), " found\n"); - - p = 0.5 * (ad.absmin + ad.absmax); - // dprint(vtos(ad.origin), " ", vtos(ad.absmin), " ", vtos(ad.absmax),"\n"); - // te_knightspike(p); - // te_lightning2(NULL, '0 0 0', p); + vector p = 0.5 * (it.absmin + it.absmax); // Find and rate waypoints around it found = false; - best = NULL; - bestvalue = 99999999999; - for(radius=0; radius<1500 && !found; radius+=500) + entity best = NULL; + float bestvalue = 99999999999; + entity des = it; + for(float radius = 0; radius < 1500 && !found; radius += 500) { - for(wp=findradius(p, radius); wp; wp=wp.chain) + FOREACH_ENTITY_RADIUS(p, radius, it.classname == "waypoint" && !(it.wpflags & WAYPOINTFLAG_GENERATED), { - if(!(wp.wpflags & WAYPOINTFLAG_GENERATED)) - if(wp.classname=="waypoint") - if(checkpvs(wp.origin, ad)) + if(checkpvs(it.origin, des)) { found = true; - if(wp.cnt 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + assault_ScoreRules(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back assault_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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; +.void(entity this) havocbot_previous_role; + +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; + +// scoreboard stuff +const float ST_ASSAULT_OBJECTIVES = 1; + +// 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 index 901a04f2a7..561a30d222 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ca.qc @@ -1,71 +1,5 @@ #include "gamemode_ca.qh" -#ifndef GAMEMODE_CA_H -#define GAMEMODE_CA_H -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); - -void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override); - -REGISTER_MUTATOR(ca, false) -{ - MUTATOR_ONADD - { - // game loads at time 1 - if (time > 1) error("This is a game type and it cannot be added at runtime."); - - allowed_to_spawn = true; - - ca_teams = autocvar_g_ca_teams_override; - if (ca_teams < 2) ca_teams = autocvar_g_ca_teams; - ca_teams = bound(2, ca_teams, 4); - - ScoreRules_basics(ca_teams, SFL_SORT_PRIO_PRIMARY, 0, true); - ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); - ScoreRules_basics_end(); - - 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); - - ActivateTeamplay(); - SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1); - - if (autocvar_g_ca_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - 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 -#endif - -#ifdef IMPLEMENTATION float autocvar_g_ca_damage2score_multiplier; bool autocvar_g_ca_spectate_enemies; @@ -117,7 +51,7 @@ float CA_GetWinnerTeam() 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() == ca_teams) +#define CA_ALIVE_TEAMS_OK() (CA_ALIVE_TEAMS() == NumTeams(ca_teams)) float CA_CheckWinner() { if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) @@ -179,9 +113,15 @@ bool CA_CheckTeams() prev_missing_teams_mask = -1; return false; } - int missing_teams_mask = (!redalive) + (!bluealive) * 2; - if(ca_teams >= 3) missing_teams_mask += (!yellowalive) * 4; - if(ca_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + 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); @@ -226,6 +166,18 @@ MUTATOR_HOOKFUNCTION(ca, PlayerSpawn) 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); @@ -254,9 +206,10 @@ MUTATOR_HOOKFUNCTION(ca, reset_map_players) { TRANSMUTE(Player, it); it.caplayer = 1; - WITHSELF(it, PutClientInServer()); + PutClientInServer(it); } }); + bot_relinkplayerlist(); return true; } @@ -274,7 +227,7 @@ MUTATOR_HOOKFUNCTION(ca, reset_map_global) return true; } -MUTATOR_HOOKFUNCTION(ca, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(ca, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(0, float) = ca_teams; } @@ -307,12 +260,14 @@ void ca_LastPlayerForTeam_Notify(entity this) 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; if (!warmup_stage) eliminatedPlayers.SendFlags |= 1; + if(IS_BOT_CLIENT(frag_target)) + bot_clear(frag_target); return true; } @@ -325,23 +280,20 @@ MUTATOR_HOOKFUNCTION(ca, ClientDisconnect) return true; } -MUTATOR_HOOKFUNCTION(ca, ForbidPlayerScore_Clear) -{ - 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) + 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 } @@ -439,11 +391,11 @@ MUTATOR_HOOKFUNCTION(ca, SpectateSet) MUTATOR_HOOKFUNCTION(ca, SpectateNext) { entity client = M_ARGV(0, entity); - entity targ = M_ARGV(1, entity); if (!autocvar_g_ca_spectate_enemies && client.caplayer) { - targ = CA_SpectateNext(client, targ); + entity targ = M_ARGV(1, entity); + M_ARGV(1, entity) = CA_SpectateNext(client, targ); return true; } } @@ -468,6 +420,8 @@ MUTATOR_HOOKFUNCTION(ca, SpectatePrev) } } + M_ARGV(1, entity) = targ; + return MUT_SPECPREV_FOUND; } @@ -513,5 +467,3 @@ MUTATOR_HOOKFUNCTION(ca, SetWeaponArena) // most weapons arena if (M_ARGV(0, string) == "0" || M_ARGV(0, string) == "") M_ARGV(0, string) = "most"; } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_ca.qh b/qcsrc/server/mutators/mutator/gamemode_ca.qh index 399830dad2..5aba748fd5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ca.qh +++ b/qcsrc/server/mutators/mutator/gamemode_ca.qh @@ -1,3 +1,74 @@ #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); + +void SetLimits(int fraglimit_override, int leadlimit_override, float timelimit_override, float qualifying_override); + +REGISTER_MUTATOR(ca, false) +{ + MUTATOR_ONADD + { + // game loads at time 1 + if (time > 1) error("This is a game type and it cannot be added at runtime."); + + allowed_to_spawn = true; + + 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 = bound(2, ca_teams, 4); + + int teams = 0; + if(ca_teams >= 1) teams |= BIT(0); + if(ca_teams >= 2) teams |= BIT(1); + if(ca_teams >= 3) teams |= BIT(2); + if(ca_teams >= 4) teams |= BIT(3); + + ca_teams = teams; // now set it? + + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); + ScoreInfo_SetLabel_TeamScore(ST_CA_ROUNDS, "rounds", SFL_SORT_PRIO_PRIMARY); + ScoreRules_basics_end(); + + 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); + + ActivateTeamplay(); + SetLimits(autocvar_g_ca_point_limit, autocvar_g_ca_point_leadlimit, autocvar_timelimit_override, -1); + + if (autocvar_g_ca_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index f9872e7490..d1c97bd285 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qc +++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qc @@ -1,6 +1,5 @@ #include "gamemode_ctf.qh" -#ifdef IMPLEMENTATION #ifndef CSQC void ctf_Initialize(); @@ -136,10 +135,14 @@ void ctf_CaptureRecord(entity flag, entity player) 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_ENT(flag, CHOICE_CTF_CAPTURE_TIME), player.netname, (cap_time * 100)); } - else if(cap_time < cap_record) { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_BROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); } - else { Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); } + 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, (cap_time * 100)); + 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, (cap_time * 100), (cap_record * 100)); + else + Send_Notification(NOTIF_ALL, NULL, MSG_CHOICE, APP_TEAM_NUM(flag.team, CHOICE_CTF_CAPTURE_UNBROKEN), player.netname, refername, (cap_time * 100), (cap_record * 100)); // write that shit in the database if(!ctf_oneflag) // but not in 1-flag mode @@ -152,12 +155,49 @@ void ctf_CaptureRecord(entity flag, entity player) } } +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) @@ -233,7 +273,7 @@ bool ctf_CaptureShield_CheckStatus(entity p) if(ctf_captureshield_max_ratio <= 0) return false; - s = PlayerScore_Add(p, SP_CTF_CAPS, 0); + s = PlayerScore_Add(p, SP_CTF_CAPS, 0); s2 = PlayerScore_Add(p, SP_CTF_PICKUPS, 0); s3 = PlayerScore_Add(p, SP_CTF_RETURNS, 0); s4 = PlayerScore_Add(p, SP_CTF_FCKILLS, 0); @@ -247,7 +287,7 @@ bool ctf_CaptureShield_CheckStatus(entity p) FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( if(DIFF_TEAM(it, p)) continue; - se = PlayerScore_Add(it, SP_CTF_CAPS, 0); + se = PlayerScore_Add(it, SP_CTF_CAPS, 0); se2 = PlayerScore_Add(it, SP_CTF_PICKUPS, 0); se3 = PlayerScore_Add(it, SP_CTF_RETURNS, 0); se4 = PlayerScore_Add(it, SP_CTF_FCKILLS, 0); @@ -278,24 +318,24 @@ void ctf_CaptureShield_Update(entity player, bool wanted_status) } } -bool ctf_CaptureShield_Customize(entity this) +bool ctf_CaptureShield_Customize(entity this, entity client) { - if(!other.ctf_captureshielded) { return false; } - if(CTF_SAMETEAM(this, other)) { return false; } + if(!client.ctf_captureshielded) { return false; } + if(CTF_SAMETEAM(this, client)) { return false; } return true; } -void ctf_CaptureShield_Touch(entity this) +void ctf_CaptureShield_Touch(entity this, entity toucher) { - if(!other.ctf_captureshielded) { return; } - if(CTF_SAMETEAM(this, other)) { return; } + if(!toucher.ctf_captureshielded) { return; } + if(CTF_SAMETEAM(this, toucher)) { return; } vector mymid = (this.absmin + this.absmax) * 0.5; - vector othermid = (other.absmin + other.absmax) * 0.5; + vector theirmid = (toucher.absmin + toucher.absmax) * 0.5; - Damage(other, this, this, 0, DEATH_HURTTRIGGER.m_id, mymid, normalize(othermid - mymid) * ctf_captureshield_force); - if(IS_REAL_CLIENT(other)) { Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_CTF_CAPTURESHIELD_SHIELDED); } + Damage(toucher, this, this, 0, DEATH_HURTTRIGGER.m_id, 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) @@ -307,7 +347,7 @@ void ctf_CaptureShield_Spawn(entity flag) settouch(shield, ctf_CaptureShield_Touch); setcefc(shield, ctf_CaptureShield_Customize); shield.effects = EF_ADDITIVE; - shield.movetype = MOVETYPE_NOCLIP; + set_movetype(shield, MOVETYPE_NOCLIP); shield.solid = SOLID_TRIGGER; shield.avelocity = '7 0 11'; shield.scale = 0.5; @@ -328,7 +368,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) player = (player ? player : flag.pass_sender); // main - flag.movetype = MOVETYPE_TOSS; + set_movetype(flag, MOVETYPE_TOSS); flag.takedamage = DAMAGE_YES; flag.angles = '0 0 0'; flag.health = flag.max_flag_health; @@ -337,7 +377,7 @@ void ctf_Handle_Drop(entity flag, entity player, int droptype) flag.ctf_status = FLAG_DROPPED; // messages and sounds - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_LOST) : INFO_CTF_LOST_NEUTRAL), player.netname); + 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); @@ -387,7 +427,7 @@ void ctf_Handle_Retrieve(entity flag, entity player) setattachment(flag, player, ""); setorigin(flag, FLAG_CARRY_OFFSET); } - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; flag.angles = '0 0 0'; @@ -399,11 +439,11 @@ void ctf_Handle_Retrieve(entity flag, entity player) FOREACH_CLIENT(IS_PLAYER(it) && IS_REAL_CLIENT(it), LAMBDA( if(it == sender) - Send_Notification(NOTIF_ONE, it, MSG_CENTER, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_SENT) : CENTER_CTF_PASS_SENT_NEUTRAL), player.netname); + 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, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_RECEIVED) : CENTER_CTF_PASS_RECEIVED_NEUTRAL), sender.netname); + 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, ((flag.team) ? APP_TEAM_ENT(flag, CENTER_CTF_PASS_OTHER) : CENTER_CTF_PASS_OTHER_NEUTRAL), sender.netname, player.netname); + Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_NUM(flag.team, CENTER_CTF_PASS_OTHER), sender.netname, player.netname); )); // create new waypoint @@ -453,7 +493,7 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype) ctf_CalculatePassVelocity(flag, targ_origin, player.origin, false); // main - flag.movetype = MOVETYPE_FLY; + set_movetype(flag, MOVETYPE_FLY); flag.takedamage = DAMAGE_NO; flag.pass_sender = player; flag.pass_target = receiver; @@ -498,6 +538,9 @@ void ctf_Handle_Throw(entity player, entity receiver, int droptype) 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 } @@ -537,7 +580,7 @@ void ctf_Handle_Capture(entity flag, entity toucher, int capturetype) player.throw_count = 0; // messages and sounds - Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((enemy_flag.team) ? APP_TEAM_ENT(enemy_flag, CENTER_CTF_CAPTURE) : CENTER_CTF_CAPTURE_NEUTRAL)); + 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); @@ -581,12 +624,12 @@ void ctf_Handle_Return(entity flag, entity player) // messages and sounds if(IS_MONSTER(player)) { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN_MONSTER), player.monster_name); + 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_ENT(flag, CENTER_CTF_RETURN)); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(flag, INFO_CTF_RETURN), player.netname); + 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); @@ -638,7 +681,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) } // flag setup - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; flag.angles = '0 0 0'; @@ -652,13 +695,17 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) } // messages and sounds - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_PICKUP) : INFO_CTF_PICKUP_NEUTRAL), 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_ENT(flag, CENTER_CTF_PICKUP)); } - else { Send_Notification(NOTIF_ONE, player, MSG_CENTER, ((SAME_TEAM(player, flag)) ? CENTER_CTF_PICKUP_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); } + 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_TEAM : CENTER_CTF_PICKUP_TEAM_ENEMY), Team_ColorCode(flag.team)); - Send_Notification(NOTIF_TEAM_EXCEPT, player, MSG_CHOICE, ((flag.team) ? APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM) : CHOICE_CTF_PICKUP_TEAM_NEUTRAL), Team_ColorCode(player.team), player.netname); + 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), LAMBDA(Send_Notification(NOTIF_ONE, it, MSG_CHOICE, CHOICE_CTF_PICKUP_ENEMY_NEUTRAL, Team_ColorCode(player.team), player.netname))); @@ -667,7 +714,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) FOREACH_CLIENT(IS_PLAYER(it) && it != player, LAMBDA( if(CTF_SAMETEAM(flag, it)) if(SAME_TEAM(player, it)) - Send_Notification(NOTIF_ONE, it, MSG_CHOICE, APP_TEAM_ENT(flag, CHOICE_CTF_PICKUP_TEAM), Team_ColorCode(player.team), player.netname); + 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); )); @@ -690,7 +737,7 @@ void ctf_Handle_Pickup(entity flag, entity player, int pickuptype) { 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), "\n"); + LOG_TRACE("pickup_dropped_score is ", ftos(pickup_dropped_score)); PlayerTeamScore_AddScore(player, pickup_dropped_score); ctf_EventLog("pickup", flag.team, player); break; @@ -731,14 +778,17 @@ void ctf_CheckFlagReturn(entity flag, int returntype) { switch(returntype) { - case RETURN_DROPPED: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DROPPED) : INFO_CTF_FLAGRETURN_DROPPED_NEUTRAL)); break; - case RETURN_DAMAGE: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_DAMAGED) : INFO_CTF_FLAGRETURN_DAMAGED_NEUTRAL)); break; - case RETURN_SPEEDRUN: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_SPEEDRUN) : INFO_CTF_FLAGRETURN_SPEEDRUN_NEUTRAL), ctf_captimerecord); break; - case RETURN_NEEDKILL: Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_NEEDKILL) : INFO_CTF_FLAGRETURN_NEEDKILL_NEUTRAL)); break; - + 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), 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, ((flag.team) ? APP_TEAM_ENT(flag, INFO_CTF_FLAGRETURN_TIMEOUT) : INFO_CTF_FLAGRETURN_TIMEOUT_NEUTRAL)); break; } + 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); @@ -747,15 +797,14 @@ void ctf_CheckFlagReturn(entity flag, int returntype) } } -bool ctf_Stalemate_Customize(entity this) +bool ctf_Stalemate_Customize(entity this, entity client) { // make spectators see what the player would see - entity e, wp_owner; - e = WaypointSprite_getviewentity(other); - wp_owner = this.owner; + entity e = WaypointSprite_getviewentity(client); + entity wp_owner = this.owner; // team waypoints - if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; } + //if(CTF_SAMETEAM(wp_owner.flagcarried, wp_owner)) { return false; } if(SAME_TEAM(wp_owner, e)) { return false; } if(!IS_PLAYER(e)) { return false; } @@ -832,9 +881,7 @@ void ctf_FlagDamage(entity this, entity inflictor, entity attacker, float damage if(ITEM_DAMAGE_NEEDKILL(deathtype)) { if(autocvar_g_ctf_flag_return_damage_delay) - { - this.ctf_flagdamaged = true; - } + this.ctf_flagdamaged_byworld = true; else { this.health = 0; @@ -864,20 +911,10 @@ void ctf_FlagThink(entity this) // sanity checks if(this.mins != CTF_FLAG.m_mins || this.maxs != CTF_FLAG.m_maxs) { // reset the flag boundaries in case it got squished - LOG_TRACE("wtf the flag got squashed?\n"); + LOG_TRACE("wtf the flag got squashed?"); tracebox(this.origin, CTF_FLAG.m_mins, CTF_FLAG.m_maxs, this.origin, MOVE_NOMONSTERS, this); if(!trace_startsolid || this.noalign) // can we resize it without getting stuck? - setsize(this, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); } - - switch(this.ctf_status) // reset flag angles in case warpzones adjust it - { - case FLAG_DROPPED: - { - this.angles = '0 0 0'; - break; - } - - default: break; + setsize(this, CTF_FLAG.m_mins, CTF_FLAG.m_maxs); } // main think method @@ -898,6 +935,8 @@ void ctf_FlagThink(entity this) 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); @@ -908,9 +947,9 @@ void ctf_FlagThink(entity this) if(pointcontents(midpoint + FLAG_FLOAT_OFFSET) == CONTENT_WATER) { this.velocity_z = autocvar_g_ctf_flag_dropped_floatinwater; } else - { this.movetype = MOVETYPE_FLY; } + { set_movetype(this, MOVETYPE_FLY); } } - else if(this.movetype == MOVETYPE_FLY) { this.movetype = MOVETYPE_TOSS; } + else if(this.move_movetype == MOVETYPE_FLY) { set_movetype(this, MOVETYPE_TOSS); } } if(autocvar_g_ctf_flag_return_dropped) { @@ -921,7 +960,7 @@ void ctf_FlagThink(entity this) return; } } - if(this.ctf_flagdamaged) + 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); @@ -973,7 +1012,7 @@ void ctf_FlagThink(entity 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)) + || (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)) { @@ -990,7 +1029,7 @@ void ctf_FlagThink(entity this) default: // this should never happen { - LOG_TRACE("ctf_FlagThink(): Flag exists with no status?\n"); + LOG_TRACE("ctf_FlagThink(): Flag exists with no status?"); return; } } @@ -1012,12 +1051,9 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) flag.health = 0; ctf_CheckFlagReturn(flag, RETURN_NEEDKILL); } - if(!flag.ctf_flagdamaged) { return; } + if(!flag.ctf_flagdamaged_byworld) { return; } } - int num_perteam = 0; - FOREACH_CLIENT(IS_PLAYER(it) && SAME_TEAM(toucher, it), LAMBDA(++num_perteam)); - // special touch behaviors if(STAT(FROZEN, toucher)) { return; } else if(IS_VEHICLE(toucher)) @@ -1069,7 +1105,7 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) case FLAG_DROPPED: { - if(CTF_SAMETEAM(toucher, flag) && (autocvar_g_ctf_flag_return || num_perteam <= 1 || (autocvar_g_ctf_flag_return_carrying && toucher.flagcarried)) && flag.team) // automatically return if there's only 1 player on the team + 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 @@ -1078,7 +1114,7 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) case FLAG_CARRY: { - LOG_TRACE("Someone touched a flag even though it was being carried?\n"); + LOG_TRACE("Someone touched a flag even though it was being carried?"); break; } @@ -1087,7 +1123,12 @@ METHOD(Flag, giveTo, bool(Flag this, entity flag, entity toucher)) if((IS_PLAYER(toucher)) && !IS_DEAD(toucher) && (toucher != flag.pass_sender)) { if(DIFF_TEAM(toucher, flag.pass_sender)) - ctf_Handle_Return(flag, toucher); + { + 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 ctf_Handle_Retrieve(flag, toucher); } @@ -1109,6 +1150,7 @@ void ctf_RespawnFlag(entity flag) 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; @@ -1127,7 +1169,7 @@ void ctf_RespawnFlag(entity flag) setattachment(flag, NULL, ""); setorigin(flag, flag.ctf_spawnorigin); - flag.movetype = ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS); + set_movetype(flag, ((flag.noalign) ? MOVETYPE_NONE : MOVETYPE_TOSS)); flag.takedamage = DAMAGE_NO; flag.health = flag.max_flag_health; flag.solid = SOLID_TRIGGER; @@ -1143,7 +1185,7 @@ void ctf_RespawnFlag(entity flag) flag.ctf_dropper = NULL; flag.ctf_pickuptime = 0; flag.ctf_droptime = 0; - flag.ctf_flagdamaged = 0; + flag.ctf_flagdamaged_byworld = false; ctf_CheckStalemate(); } @@ -1156,6 +1198,13 @@ void ctf_Reset(entity this) ctf_RespawnFlag(this); } +bool ctf_FlagBase_Customize(entity this, entity client) +{ + if(client.flagcarried && CTF_SAMETEAM(client, client.flagcarried)) + return false; + return true; +} + void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map by ctf_FlagSetup() { // bot waypoints @@ -1177,6 +1226,7 @@ void ctf_DelayedFlagSetup(entity this) // called after a flag is placed on a map 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); @@ -1197,6 +1247,7 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e 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; @@ -1208,6 +1259,8 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e 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; @@ -1280,13 +1333,13 @@ void ctf_FlagSetup(int teamnumber, entity flag) // called when spawning a flag e { flag.dropped_origin = flag.origin; flag.noalign = true; - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); } else // drop to floor, automatically find a platform and set that as spawn origin { flag.noalign = false; droptofloor(flag); - flag.movetype = MOVETYPE_TOSS; + set_movetype(flag, MOVETYPE_NONE); } InitializeEntity(flag, ctf_DelayedFlagSetup, INITPRIO_SETLOCATION); @@ -1490,23 +1543,19 @@ void havocbot_goalrating_ctf_droppedflags(entity this, float ratingscale, vector void havocbot_goalrating_ctf_carrieritems(entity this, float ratingscale, vector org, float sradius) { - entity head; - float t; - head = findchainfloat(bot_pickup, true); - while (head) + IL_EACH(g_items, it.bot_pickup, { // gather health and armor only - if (head.solid) - if (head.health || head.armorvalue) - if (vdist(head.origin - org, <, sradius)) + if (it.solid) + if (it.health || it.armorvalue) + if (vdist(it.origin - org, <, sradius)) { // get the value of the item - t = head.bot_pickupevalfunc(this, head) * 0.0001; + float t = it.bot_pickupevalfunc(this, it) * 0.0001; if (t > 0) - navigation_routerating(this, head, t * ratingscale, 500); + navigation_routerating(this, it, t * ratingscale, 500); } - head = head.chain; - } + }); } void havocbot_ctf_reset_role(entity this) @@ -1776,6 +1825,11 @@ void havocbot_role_ctf_retriever(entity this) mf = havocbot_ctf_find_flag(this); if(mf.ctf_status==FLAG_BASE) { + if(this.goalcurrent == mf) + { + navigation_clearroute(this); + this.bot_strategytime = 0; + } havocbot_ctf_reset_role(this); return; } @@ -1927,47 +1981,47 @@ void havocbot_role_ctf_defense(entity this) void havocbot_role_ctf_setrole(entity bot, int role) { - LOG_TRACE(strcat(bot.netname," switched to ")); + string s = "(null)"; switch(role) { case HAVOCBOT_CTF_ROLE_CARRIER: - LOG_TRACE("carrier"); + s = "carrier"; bot.havocbot_role = havocbot_role_ctf_carrier; bot.havocbot_role_timeout = 0; bot.havocbot_cantfindflag = time + 10; bot.bot_strategytime = 0; break; case HAVOCBOT_CTF_ROLE_DEFENSE: - LOG_TRACE("defense"); + s = "defense"; bot.havocbot_role = havocbot_role_ctf_defense; bot.havocbot_role_timeout = 0; break; case HAVOCBOT_CTF_ROLE_MIDDLE: - LOG_TRACE("middle"); + s = "middle"; bot.havocbot_role = havocbot_role_ctf_middle; bot.havocbot_role_timeout = 0; break; case HAVOCBOT_CTF_ROLE_OFFENSE: - LOG_TRACE("offense"); + s = "offense"; bot.havocbot_role = havocbot_role_ctf_offense; bot.havocbot_role_timeout = 0; break; case HAVOCBOT_CTF_ROLE_RETRIEVER: - LOG_TRACE("retriever"); + s = "retriever"; bot.havocbot_previous_role = bot.havocbot_role; bot.havocbot_role = havocbot_role_ctf_retriever; bot.havocbot_role_timeout = time + 10; bot.bot_strategytime = 0; break; case HAVOCBOT_CTF_ROLE_ESCORT: - LOG_TRACE("escort"); + s = "escort"; bot.havocbot_previous_role = bot.havocbot_role; bot.havocbot_role = havocbot_role_ctf_escort; bot.havocbot_role_timeout = time + 30; bot.bot_strategytime = 0; break; } - LOG_TRACE("\n"); + LOG_TRACE(bot.netname, " switched to ", s); } @@ -1987,7 +2041,7 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink) | 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_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) @@ -2021,6 +2075,9 @@ MUTATOR_HOOKFUNCTION(ctf, PlayerPreThink) if(player.ctf_captureshielded) player.ctf_flagstatus |= CTF_SHIELDED; + if(ctf_stalemate) + player.ctf_flagstatus |= 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)); @@ -2238,14 +2295,13 @@ MUTATOR_HOOKFUNCTION(ctf, VehicleEnter) if(player.flagcarried) { - player.flagcarried.nodrawtoclient = player; // hide the flag from the driver - 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; @@ -2276,7 +2332,7 @@ MUTATOR_HOOKFUNCTION(ctf, AbortSpeedrun) if(player.flagcarried) { - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, ((player.flagcarried.team) ? APP_TEAM_ENT(player.flagcarried, INFO_CTF_FLAGRETURN_ABORTRUN) : INFO_CTF_FLAGRETURN_ABORTRUN_NEUTRAL)); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_NUM(player.flagcarried.team, INFO_CTF_FLAGRETURN_ABORTRUN)); ctf_RespawnFlag(player.flagcarried); return true; } @@ -2294,7 +2350,7 @@ MUTATOR_HOOKFUNCTION(ctf, MatchEnd) case FLAG_PASSING: { // lock the flag, game is over - flag.movetype = MOVETYPE_NONE; + set_movetype(flag, MOVETYPE_NONE); flag.takedamage = DAMAGE_NO; flag.solid = SOLID_NOT; flag.nextthink = false; // stop thinking @@ -2322,7 +2378,7 @@ MUTATOR_HOOKFUNCTION(ctf, HavocBot_ChooseRole) return true; } -MUTATOR_HOOKFUNCTION(ctf, GetTeamCount) +MUTATOR_HOOKFUNCTION(ctf, CheckAllowedTeams) { //M_ARGV(0, float) = ctf_teams; M_ARGV(1, string) = "ctf_team"; @@ -2382,10 +2438,10 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) { switch(argv(1)) { - case "red": _team = NUM_TEAM_1; break; - case "blue": _team = NUM_TEAM_2; break; - case "yellow": if(ctf_teams >= 3) _team = NUM_TEAM_3; break; - case "pink": if(ctf_teams >= 4) _team = NUM_TEAM_4; break; + 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; } } @@ -2408,7 +2464,7 @@ MUTATOR_HOOKFUNCTION(ctf, SV_ParseClientCommand) MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - + if(frag_target.flagcarried) ctf_Handle_Throw(frag_target, NULL, DROP_THROW); } @@ -2431,7 +2487,7 @@ Keys: "noise5" sound played when flag touches the ground... */ spawnfunc(item_flag_team1) { - if(!g_ctf) { remove(this); return; } + if(!g_ctf) { delete(this); return; } ctf_FlagSetup(NUM_TEAM_1, this); } @@ -2449,7 +2505,7 @@ Keys: "noise5" sound played when flag touches the ground... */ spawnfunc(item_flag_team2) { - if(!g_ctf) { remove(this); return; } + if(!g_ctf) { delete(this); return; } ctf_FlagSetup(NUM_TEAM_2, this); } @@ -2467,7 +2523,7 @@ Keys: "noise5" sound played when flag touches the ground... */ spawnfunc(item_flag_team3) { - if(!g_ctf) { remove(this); return; } + if(!g_ctf) { delete(this); return; } ctf_FlagSetup(NUM_TEAM_3, this); } @@ -2485,7 +2541,7 @@ Keys: "noise5" sound played when flag touches the ground... */ spawnfunc(item_flag_team4) { - if(!g_ctf) { remove(this); return; } + if(!g_ctf) { delete(this); return; } ctf_FlagSetup(NUM_TEAM_4, this); } @@ -2503,8 +2559,8 @@ Keys: "noise5" sound played when flag touches the ground... */ spawnfunc(item_flag_neutral) { - if(!g_ctf) { remove(this); return; } - if(!cvar("g_ctf_oneflag")) { remove(this); return; } + if(!g_ctf) { delete(this); return; } + if(!cvar("g_ctf_oneflag")) { delete(this); return; } ctf_FlagSetup(0, this); } @@ -2517,7 +2573,7 @@ Keys: "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ spawnfunc(ctf_team) { - if(!g_ctf) { remove(this); return; } + if(!g_ctf) { delete(this); return; } this.classname = "ctf_team"; this.team = this.cnt + 1; @@ -2536,6 +2592,14 @@ 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 @@ -2547,12 +2611,12 @@ void ctf_ScoreRules(int teams) CheckAllowedTeams(NULL); ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, 0, true); ScoreInfo_SetLabel_TeamScore (ST_CTF_CAPS, "caps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns", 0); - ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_CAPTIME, "captime", SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_FCKILLS, "fckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_RETURNS, "returns", 0); + ScoreInfo_SetLabel_PlayerScore(SP_CTF_DROPS, "drops", SFL_LOWER_IS_BETTER); ScoreRules_basics_end(); } @@ -2568,27 +2632,44 @@ void ctf_SpawnTeam (string teamname, int teamcolor) void ctf_DelayedInit(entity this) // Do this check with a delay so we can wait for teams to be set up. { - ctf_teams = 2; + 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); } + //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; } } - ctf_teams = bound(2, ctf_teams, 4); + 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.\n"); - ctf_SpawnTeam("Red", NUM_TEAM_1); - ctf_SpawnTeam("Blue", NUM_TEAM_2); - if(ctf_teams >= 3) + 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 >= 4) + if(ctf_teams & BIT(3)) ctf_SpawnTeam("Pink", NUM_TEAM_4); } @@ -2605,5 +2686,3 @@ void ctf_Initialize() InitializeEntity(NULL, ctf_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_ctf.qh b/qcsrc/server/mutators/mutator/gamemode_ctf.qh index 95e0bcd424..593b0bec34 100644 --- a/qcsrc/server/mutators/mutator/gamemode_ctf.qh +++ b/qcsrc/server/mutators/mutator/gamemode_ctf.qh @@ -8,19 +8,13 @@ void ctf_RespawnFlag(entity flag); // score rule declarations const int ST_CTF_CAPS = 1; -const int SP_CTF_CAPS = 4; -const int SP_CTF_CAPTIME = 5; -const int SP_CTF_PICKUPS = 6; -const int SP_CTF_DROPS = 7; -const int SP_CTF_FCKILLS = 8; -const int SP_CTF_RETURNS = 9; CLASS(Flag, Pickup) - ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13') - ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13') + ATTRIB(Flag, m_mins, vector, PL_MIN_CONST + '0 0 -13'); + ATTRIB(Flag, m_maxs, vector, PL_MAX_CONST + '0 0 -13'); ENDCLASS(Flag) Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } -void ctf_FlagTouch(entity this) { ITEM_HANDLE(Pickup, CTF_FLAG, this, other); } +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? @@ -70,6 +64,7 @@ entity ctf_worldflaglist; .entity wps_flagbase; .entity wps_flagcarrier; .entity wps_flagdropped; +.entity wps_flagreturn; .entity wps_enemyflagcarrier; .float wps_helpme_time; bool wpforenemy_announced; @@ -98,6 +93,8 @@ 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 @@ -110,7 +107,7 @@ float ctf_captimerecord; // record time for capturing the flag .entity ctf_dropper; // don't allow spam of dropping the flag .int max_flag_health; .float next_take_time; -.bool ctf_flagdamaged; +.bool ctf_flagdamaged_byworld; int ctf_teams; // passing/throwing properties @@ -171,3 +168,4 @@ 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 index bfddc30b01..f83eb6b649 100644 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qc +++ b/qcsrc/server/mutators/mutator/gamemode_cts.qc @@ -1,50 +1,6 @@ #include "gamemode_cts.qh" #include -#ifndef GAMEMODE_CTS_H -#define GAMEMODE_CTS_H - -void cts_Initialize(); - -REGISTER_MUTATOR(cts, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - - g_race_qualifying = true; - independent_players = 1; - SetLimits(0, 0, autocvar_timelimit_override, -1); - - cts_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back cts_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// scores -const float ST_CTS_LAPS = 1; -const float SP_CTS_LAPS = 4; -const float SP_CTS_TIME = 5; -const float SP_CTS_FASTEST = 6; -#endif - -#ifdef IMPLEMENTATION - #include float autocvar_g_cts_finish_kill_delay; @@ -62,7 +18,7 @@ void havocbot_role_cts(entity this) this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; navigation_goalrating_start(this); - FOREACH_ENTITY_CLASS("trigger_race_checkpoint", true, + IL_EACH(g_racecheckpoints, true, { if(it.cnt == this.race_checkpoint) navigation_routerating(this, it, 1000000, 5000); @@ -79,13 +35,13 @@ void cts_ScoreRules() ScoreRules_basics(0, 0, 0, false); if(g_race_qualifying) { - ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_SORT_PRIO_PRIMARY | SFL_LOWER_IS_BETTER | SFL_TIME); } else { - ScoreInfo_SetLabel_PlayerScore(SP_CTS_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); - ScoreInfo_SetLabel_PlayerScore(SP_CTS_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); - ScoreInfo_SetLabel_PlayerScore(SP_CTS_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); + ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); } ScoreRules_basics_end(); } @@ -111,8 +67,9 @@ void CTS_ClientKill(entity e) // silent version of ClientKill, used when player MUTATOR_HOOKFUNCTION(cts, PlayerPhysics) { entity player = M_ARGV(0, entity); + float dt = M_ARGV(1, float); - player.race_movetime_frac += PHYS_INPUT_TIMELENGTH; + player.race_movetime_frac += dt; float f = floor(player.race_movetime_frac); player.race_movetime_frac -= f; player.race_movetime_count += f; @@ -127,7 +84,7 @@ MUTATOR_HOOKFUNCTION(cts, PlayerPhysics) if(player.race_penalty) { player.velocity = '0 0 0'; - player.movetype = MOVETYPE_NONE; + set_movetype(player, MOVETYPE_NONE); player.disableclientprediction = 2; } } @@ -285,7 +242,7 @@ MUTATOR_HOOKFUNCTION(cts, PutClientInServer) MUTATOR_HOOKFUNCTION(cts, PlayerDies) { entity frag_target = M_ARGV(2, entity); - + frag_target.respawn_flags |= RESPAWN_FORCE; race_AbandonRaceCheck(frag_target); } @@ -406,7 +363,7 @@ MUTATOR_HOOKFUNCTION(cts, ClientKill) if(player.killindicator && player.killindicator.health == 1) // player.killindicator.health == 1 means that the kill indicator was spawned by CTS_ClientKill { - remove(player.killindicator); + delete(player.killindicator); player.killindicator = NULL; ClientKill_Now(player); // allow instant kill in this case @@ -440,5 +397,3 @@ void cts_Initialize() { cts_ScoreRules(); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_cts.qh b/qcsrc/server/mutators/mutator/gamemode_cts.qh index 399830dad2..2a18fe915d 100644 --- a/qcsrc/server/mutators/mutator/gamemode_cts.qh +++ b/qcsrc/server/mutators/mutator/gamemode_cts.qh @@ -1,3 +1,39 @@ #pragma once #include "../gamemode.qh" +#include + +void cts_Initialize(); + +REGISTER_MUTATOR(cts, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + + g_race_qualifying = true; + independent_players = 1; + SetLimits(0, 0, autocvar_timelimit_override, -1); + + cts_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back cts_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index 8663703513..9590027d3f 100644 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc +++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qc @@ -1,37 +1,7 @@ #include "gamemode_deathmatch.qh" -#ifndef GAMEMODE_DEATHMATCH_H -#define GAMEMODE_DEATHMATCH_H -REGISTER_MUTATOR(dm, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back dm_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - error("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION MUTATOR_HOOKFUNCTION(dm, Scores_CountFragsRemaining) { // announce remaining frags return true; } -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh index 399830dad2..d3cc197eaf 100644 --- a/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh +++ b/qcsrc/server/mutators/mutator/gamemode_deathmatch.qh @@ -1,3 +1,27 @@ #pragma once #include "../gamemode.qh" + +REGISTER_MUTATOR(dm, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back dm_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + error("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qc b/qcsrc/server/mutators/mutator/gamemode_domination.qc index 6a217133a7..7f98ed289c 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qc +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qc @@ -1,70 +1,4 @@ #include "gamemode_domination.qh" -#ifndef GAMEMODE_DOMINATION_H -#define GAMEMODE_DOMINATION_H - -#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_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - dom_Initialize(); - - 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; - - ActivateTeamplay(); - SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1); - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// score rule declarations -const float ST_DOM_TICKS = 1; -const float SP_DOM_TICKS = 4; -const float SP_DOM_TAKES = 5; -const float ST_DOM_CAPS = 1; -const float SP_DOM_CAPS = 4; - -// pps: points per second -.float dom_total_pps = _STAT(DOM_TOTAL_PPS); -.float dom_pps_red = _STAT(DOM_PPS_RED); -.float dom_pps_blue = _STAT(DOM_PPS_BLUE); -.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW); -.float dom_pps_pink = _STAT(DOM_PPS_PINK); -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; -#endif - -#ifdef IMPLEMENTATION #include @@ -165,8 +99,8 @@ void dompoint_captured(entity this) WaypointSprite_UpdateSprites(this.sprite, msg, WP_Null, WP_Null); total_pps = 0, pps_red = 0, pps_blue = 0, pps_yellow = 0, pps_pink = 0; - for(head = NULL; (head = find(head, classname, "dom_controlpoint")) != NULL; ) - FOREACH_ENTITY_CLASS("dom_controlpoint", true, LAMBDA( + IL_EACH(g_dompoints, true, + { if (autocvar_g_domination_point_amt) points = autocvar_g_domination_point_amt; else @@ -183,7 +117,7 @@ void dompoint_captured(entity this) 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); @@ -250,11 +184,11 @@ void dompointthink(entity this) } } -void dompointtouch(entity this) +void dompointtouch(entity this, entity toucher) { - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; - if (other.health < 1) + if (toucher.health < 1) return; if(round_handler_IsActive() && !round_handler_IsRoundStarted()) @@ -265,7 +199,7 @@ void dompointtouch(entity this) // only valid teams can claim it entity head = find(NULL, classname, "dom_team"); - while (head && head.team != other.team) + while (head && head.team != toucher.team) head = find(head, classname, "dom_team"); if (!head || head.netname == "" || head == this.goalentity) return; @@ -274,9 +208,9 @@ void dompointtouch(entity this) this.team = this.goalentity.team; // this stores the PREVIOUS team! - this.cnt = other.team; + this.cnt = toucher.team; this.owner = head; // team to switch to after the delay - this.dmg_inflictor = other; + this.dmg_inflictor = toucher; // this.state = 1; // this.delay = time + cvar("g_domination_point_capturetime"); @@ -299,8 +233,8 @@ void dompointtouch(entity this) this.modelindex = head.dmg; this.skin = head.skin; - this.enemy = other; // individual player scoring - this.enemy_playerid = other.playerid; + this.enemy = toucher; // individual player scoring + this.enemy_playerid = toucher.playerid; dompoint_captured(this); } @@ -350,6 +284,8 @@ void dom_controlpoint_setup(entity this) 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'); @@ -362,16 +298,15 @@ void dom_controlpoint_setup(entity this) float total_controlpoints; void Domination_count_controlpoints() { - entity e; total_controlpoints = redowned = blueowned = yellowowned = pinkowned = 0; - for(e = NULL; (e = find(e, classname, "dom_controlpoint")) != NULL; ) + IL_EACH(g_dompoints, true, { ++total_controlpoints; - redowned += (e.goalentity.team == NUM_TEAM_1); - blueowned += (e.goalentity.team == NUM_TEAM_2); - yellowowned += (e.goalentity.team == NUM_TEAM_3); - pinkowned += (e.goalentity.team == NUM_TEAM_4); - } + 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() @@ -446,6 +381,19 @@ void Domination_RoundStart() } //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)) @@ -463,7 +411,7 @@ void havocbot_role_dom(entity this) } } -MUTATOR_HOOKFUNCTION(dom, GetTeamCount) +MUTATOR_HOOKFUNCTION(dom, CheckAllowedTeams) { // fallback? M_ARGV(0, float) = domination_teams; @@ -495,7 +443,7 @@ 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), LAMBDA( - WITHSELF(it, PutClientInServer()); + PutClientInServer(it); if(domination_roundbased) it.player_blocked = 1; if(IS_REAL_CLIENT(it)) @@ -537,7 +485,7 @@ spawnfunc(dom_controlpoint) { if(!g_domination) { - remove(this); + delete(this); return; } setthink(this, dom_controlpoint_setup); @@ -550,6 +498,8 @@ spawnfunc(dom_controlpoint) 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) @@ -582,7 +532,7 @@ spawnfunc(dom_team) { if(!g_domination || autocvar_g_domination_teams_override >= 2) { - remove(this); + delete(this); return; } precache_model(this.model); @@ -602,7 +552,7 @@ spawnfunc(dom_team) } // scoreboard setup -void ScoreRules_dom(float teams) +void ScoreRules_dom(int teams) { if(domination_roundbased) { @@ -680,13 +630,23 @@ void dom_DelayedInit(entity this) // Do this check with a delay so we can wait f // 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.\n"); - domination_teams = bound(2, ((autocvar_g_domination_teams_override < 2) ? autocvar_g_domination_default_teams : autocvar_g_domination_teams_override), 4); + 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); + //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; @@ -704,5 +664,3 @@ void dom_Initialize() g_domination = true; InitializeEntity(NULL, dom_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_domination.qh b/qcsrc/server/mutators/mutator/gamemode_domination.qh index 399830dad2..609fcfd0d9 100644 --- a/qcsrc/server/mutators/mutator/gamemode_domination.qh +++ b/qcsrc/server/mutators/mutator/gamemode_domination.qh @@ -1,3 +1,66 @@ #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_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + dom_Initialize(); + + 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; + + ActivateTeamplay(); + SetLimits(fraglimit_override, autocvar_g_domination_point_leadlimit, autocvar_timelimit_override, -1); + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} + +// score rule declarations +const float ST_DOM_TICKS = 1; +const float ST_DOM_CAPS = 1; + +// pps: points per second +.float dom_total_pps = _STAT(DOM_TOTAL_PPS); +.float dom_pps_red = _STAT(DOM_PPS_RED); +.float dom_pps_blue = _STAT(DOM_PPS_BLUE); +.float dom_pps_yellow = _STAT(DOM_PPS_YELLOW); +.float dom_pps_pink = _STAT(DOM_PPS_PINK); +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 index 6db1db73d4..cec7d950e9 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qc +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qc @@ -1,68 +1,13 @@ #include "gamemode_freezetag.qh" -#ifndef GAMEMODE_FREEZETAG_H -#define GAMEMODE_FREEZETAG_H - -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_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - freezetag_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1); - - if (autocvar_g_freezetag_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back freezetag_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - 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; - -#endif -#ifdef IMPLEMENTATION 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; int autocvar_g_freezetag_teams_override; float autocvar_g_freezetag_warmup; -const float SP_FREEZETAG_REVIVALS = 4; -void freezetag_ScoreRules(float teams) +void freezetag_ScoreRules(int teams) { ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); // SFL_SORT_PRIO_PRIMARY ScoreInfo_SetLabel_PlayerScore(SP_FREEZETAG_REVIVALS, "revivals", 0); @@ -91,7 +36,7 @@ void freezetag_count_alive_players() eliminatedPlayers.SendFlags |= 1; } #define FREEZETAG_ALIVE_TEAMS() ((redalive > 0) + (bluealive > 0) + (yellowalive > 0) + (pinkalive > 0)) -#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == freezetag_teams) +#define FREEZETAG_ALIVE_TEAMS_OK() (FREEZETAG_ALIVE_TEAMS() == NumTeams(freezetag_teams)) float freezetag_CheckTeams() { @@ -110,9 +55,15 @@ float freezetag_CheckTeams() prev_missing_teams_mask = -1; return 0; } - float missing_teams_mask = (!redalive) + (!bluealive) * 2; - if(freezetag_teams >= 3) missing_teams_mask += (!yellowalive) * 4; - if(freezetag_teams >= 4) missing_teams_mask += (!pinkalive) * 8; + 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); @@ -146,6 +97,9 @@ float freezetag_getWinnerTeam() 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) @@ -299,7 +253,7 @@ void havocbot_role_ft_offense(entity this) // 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\n"); + LOG_TRACE("changing role to freeing"); this.havocbot_role = havocbot_role_ft_freeing; this.havocbot_role_timeout = 0; return; @@ -328,7 +282,7 @@ void havocbot_role_ft_freeing(entity this) if (time > this.havocbot_role_timeout) { - LOG_TRACE("changing role to offense\n"); + LOG_TRACE("changing role to offense"); this.havocbot_role = havocbot_role_ft_offense; this.havocbot_role_timeout = 0; return; @@ -380,7 +334,7 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies) { entity frag_attacker = M_ARGV(1, entity); entity frag_target = M_ARGV(2, entity); - float frag_deathtype = M_ARGV(2, float); + float frag_deathtype = M_ARGV(3, float); if(round_handler_IsActive()) if(round_handler_CountdownRunning()) @@ -424,10 +378,6 @@ MUTATOR_HOOKFUNCTION(ft, PlayerDies) } else { - if(IS_PLAYER(frag_target)) - Send_Notification(NOTIF_ONE, frag_target, MSG_CENTER, CENTER_FREEZETAG_FROZEN, frag_attacker.netname); - if(IS_PLAYER(frag_attacker)) - Send_Notification(NOTIF_ONE, frag_attacker, MSG_CENTER, CENTER_FREEZETAG_FREEZE, frag_target.netname); Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_FREEZETAG_FREEZE, frag_target.netname, frag_attacker.netname); } @@ -464,7 +414,7 @@ MUTATOR_HOOKFUNCTION(ft, reset_map_players) FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( it.killcount = 0; it.freezetag_frozen_timeout = -1; - WITHSELF(it, PutClientInServer()); + PutClientInServer(it); it.freezetag_frozen_timeout = 0; )); freezetag_count_alive_players(); @@ -482,22 +432,13 @@ MUTATOR_HOOKFUNCTION(ft, PlayerPreThink, CBC_ORDER_FIRST) if(gameover) return true; - entity player = M_ARGV(0, entity); - - if(STAT(FROZEN, player) == 1) - { - // keep health = 1 - player.pauseregen_finished = time + autocvar_g_balance_pause_health_regen; - } - if(round_handler_IsActive()) if(!round_handler_IsRoundStarted()) return true; int n; - - entity o; - o = NULL; + 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); @@ -599,7 +540,7 @@ MUTATOR_HOOKFUNCTION(ft, HavocBot_ChooseRole) return true; } -MUTATOR_HOOKFUNCTION(ft, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(ft, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(0, float) = freezetag_teams; } @@ -611,12 +552,37 @@ MUTATOR_HOOKFUNCTION(ft, SetWeaponArena) 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 : 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 : frag_attacker.ping)); + + return true; +} + void freezetag_Initialize() { freezetag_teams = autocvar_g_freezetag_teams_override; if(freezetag_teams < 2) - freezetag_teams = autocvar_g_freezetag_teams; + freezetag_teams = cvar("g_freezetag_teams"); // read the cvar directly as it gets written earlier in the same frame freezetag_teams = bound(2, freezetag_teams, 4); + + int teams = 0; + if(freezetag_teams >= 1) teams |= BIT(0); + if(freezetag_teams >= 2) teams |= BIT(1); + if(freezetag_teams >= 3) teams |= BIT(2); + if(freezetag_teams >= 4) teams |= BIT(3); + + freezetag_teams = teams; // now set it? freezetag_ScoreRules(freezetag_teams); round_handler_Spawn(freezetag_CheckTeams, freezetag_CheckWinner, func_null); @@ -624,5 +590,3 @@ void freezetag_Initialize() EliminatedPlayers_Init(freezetag_isEliminated); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh b/qcsrc/server/mutators/mutator/gamemode_freezetag.qh index 399830dad2..fda0737dd9 100644 --- a/qcsrc/server/mutators/mutator/gamemode_freezetag.qh +++ b/qcsrc/server/mutators/mutator/gamemode_freezetag.qh @@ -1,3 +1,52 @@ #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_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + freezetag_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_freezetag_point_limit, autocvar_g_freezetag_point_leadlimit, autocvar_timelimit_override, -1); + + if (autocvar_g_freezetag_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back freezetag_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index 7c949d8663..1496feec59 100644 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qc +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qc @@ -1,72 +1,12 @@ #include "gamemode_invasion.qh" -#ifndef GAMEMODE_INVASION_H -#define GAMEMODE_INVASION_H -#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") -int autocvar_g_invasion_teams; -bool autocvar_g_invasion_team_spawns; -bool g_invasion; -void invasion_Initialize(); - -REGISTER_MUTATOR(inv, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - g_invasion = true; - invasion_Initialize(); - - cvar_settemp("g_monsters", "1"); - - SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); - if (autocvar_g_invasion_teams >= 2) - { - ActivateTeamplay(); - if (autocvar_g_invasion_team_spawns) - have_team_spawns = -1; // request team spawns - } - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back invasion_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - 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; -#endif - -#ifdef IMPLEMENTATION - -#include +#include #include #include +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; @@ -75,57 +15,46 @@ int autocvar_g_invasion_monster_count; bool autocvar_g_invasion_zombies_only; float autocvar_g_invasion_spawn_delay; +.string spawnmob; + spawnfunc(invasion_spawnpoint) { - if(!g_invasion) { remove(this); return; } + if(!g_invasion) { delete(this); return; } this.classname = "invasion_spawnpoint"; - - if(autocvar_g_invasion_zombies_only) // precache only if it hasn't been already - if(this.monsterid) { - Monster mon = get_monsterinfo(this.monsterid); - mon.mr_precache(mon); - } + IL_PUSH(g_invasion_spawns, this); } -float invasion_PickMonster(float supermonster_count) +Monster invasion_PickMonster(int supermonster_count) { - if(autocvar_g_invasion_zombies_only) - return MON_ZOMBIE.monsterid; - - float i; - entity mon; - RandomSelection_Init(); - for(i = MON_FIRST; i <= MON_LAST; ++i) + FOREACH(Monsters, it != MON_Null, { - mon = get_monsterinfo(i); - if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM) || ((mon.spawnflags & MON_FLAG_SUPERMONSTER) && supermonster_count >= 1)) - continue; // flying/swimming monsters not yet supported - - RandomSelection_Add(NULL, i, string_null, 1, 1); - } + if((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_float; + return RandomSelection_chosen_ent; } entity invasion_PickSpawn() { - entity e; - RandomSelection_Init(); - for(e = NULL;(e = find(e, classname, "invasion_spawnpoint")); ) + IL_EACH(g_invasion_spawns, true, { - RandomSelection_Add(e, 0, string_null, 1, ((time >= e.spawnshieldtime) ? 0.2 : 1)); // give recently used spawnpoints a very low rating - e.spawnshieldtime = time + autocvar_g_invasion_spawnpoint_spawn_delay; - } + 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; } -void invasion_SpawnChosenMonster(float mon) +void invasion_SpawnChosenMonster(Monster mon) { entity spawn_point, monster; @@ -133,19 +62,23 @@ void invasion_SpawnChosenMonster(float mon) if(spawn_point == NULL) { - LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations\n"); + LOG_TRACE("Warning: couldn't find any invasion_spawnpoint spawnpoints, attempting to spawn monsters in random locations"); entity e = spawn(); - setsize(e, (get_monsterinfo(mon)).mins, (get_monsterinfo(mon)).maxs); + setsize(e, mon.mins, mon.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("", mon, NULL, NULL, e.origin, false, false, 2); - else return; - - setthink(e, SUB_Remove); - e.nextthink = time + 0.1; + monster = spawnmonster(e, "", mon.monsterid, NULL, NULL, e.origin, false, false, 2); + else + { + delete(e); + return; + } } - else - monster = spawnmonster("", ((spawn_point.monsterid) ? spawn_point.monsterid : mon), spawn_point, spawn_point, spawn_point.origin, false, false, 2); + else // if spawnmob field falls through (unset), fallback to mon (relying on spawnmonster for that behaviour) + monster = spawnmonster(spawn(), spawn_point.spawnmob, mon.monsterid, spawn_point, spawn_point, spawn_point.origin, false, false, 2); + + if(!monster) + return; if(spawn_point) monster.target2 = spawn_point.target2; monster.spawnshieldtime = time; @@ -157,10 +90,10 @@ void invasion_SpawnChosenMonster(float mon) else { RandomSelection_Init(); - if(inv_monsters_perteam[NUM_TEAM_1] > 0) RandomSelection_Add(NULL, NUM_TEAM_1, string_null, 1, 1); - if(inv_monsters_perteam[NUM_TEAM_2] > 0) RandomSelection_Add(NULL, NUM_TEAM_2, string_null, 1, 1); - if(invasion_teams >= 3) if(inv_monsters_perteam[NUM_TEAM_3] > 0) { RandomSelection_Add(NULL, NUM_TEAM_3, string_null, 1, 1); } - if(invasion_teams >= 4) if(inv_monsters_perteam[NUM_TEAM_4] > 0) { RandomSelection_Add(NULL, NUM_TEAM_4, string_null, 1, 1); } + 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; } @@ -184,18 +117,22 @@ void invasion_SpawnChosenMonster(float mon) monster.spawnflags |= MONSTERFLAG_MINIBOSS; // last round spawns minibosses } -void invasion_SpawnMonsters(float supermonster_count) +void invasion_SpawnMonsters(int supermonster_count) { - float chosen_monster = invasion_PickMonster(supermonster_count); + Monster chosen_monster = invasion_PickMonster(supermonster_count); invasion_SpawnChosenMonster(chosen_monster); } -float Invasion_CheckWinner() +bool Invasion_CheckWinner() { if(round_handler_GetEndTime() > 0 && round_handler_GetEndTime() - time <= 0) { - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it))); + 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); @@ -205,23 +142,21 @@ float Invasion_CheckWinner() float total_alive_monsters = 0, supermonster_count = 0, red_alive = 0, blue_alive = 0, yellow_alive = 0, pink_alive = 0; - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA( - if(it.health > 0) - { - if((get_monsterinfo(it.monsterid)).spawnflags & MON_FLAG_SUPERMONSTER) - ++supermonster_count; - ++total_alive_monsters; + 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(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) { @@ -274,7 +209,11 @@ float Invasion_CheckWinner() )); } - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, LAMBDA(Monster_Remove(it))); + IL_EACH(g_monsters, true, + { + Monster_Remove(it); + }); + IL_CLEAR(g_monsters); if(teamplay) { @@ -398,6 +337,8 @@ MUTATOR_HOOKFUNCTION(inv, PlayerSpawn) { entity player = M_ARGV(0, entity); + if(player.bot_attack) + IL_REMOVE(g_bot_targets, player); player.bot_attack = false; } @@ -470,7 +411,7 @@ MUTATOR_HOOKFUNCTION(inv, AllowMobSpawning) return true; } -MUTATOR_HOOKFUNCTION(inv, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(inv, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(0, float) = invasion_teams; } @@ -481,7 +422,7 @@ MUTATOR_HOOKFUNCTION(inv, AllowMobButcher) return true; } -void invasion_ScoreRules(float inv_teams) +void invasion_ScoreRules(int inv_teams) { if(inv_teams) { CheckAllowedTeams(NULL); } ScoreRules_basics(inv_teams, 0, 0, false); @@ -493,7 +434,16 @@ void invasion_ScoreRules(float inv_teams) 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_teams) + { invasion_teams = bound(2, autocvar_g_invasion_teams, 4); + int teams = 0; + if(invasion_teams >= 1) teams |= BIT(0); + if(invasion_teams >= 2) teams |= BIT(1); + if(invasion_teams >= 3) teams |= BIT(2); + if(invasion_teams >= 4) teams |= BIT(3); + + invasion_teams = teams; // now set it? + } else invasion_teams = 0; @@ -512,24 +462,5 @@ void invasion_DelayedInit(entity this) // Do this check with a delay so we can w void invasion_Initialize() { - if(autocvar_g_invasion_zombies_only) { - Monster mon = MON_ZOMBIE; - mon.mr_precache(mon); - } else - { - float i; - entity mon; - for(i = MON_FIRST; i <= MON_LAST; ++i) - { - mon = get_monsterinfo(i); - if((mon.spawnflags & MONSTER_TYPE_FLY) || (mon.spawnflags & MONSTER_TYPE_SWIM)) - continue; // flying/swimming monsters not yet supported - - mon.mr_precache(mon); - } - } - InitializeEntity(NULL, invasion_DelayedInit, INITPRIO_GAMETYPE); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_invasion.qh b/qcsrc/server/mutators/mutator/gamemode_invasion.qh index 399830dad2..e934f8745a 100644 --- a/qcsrc/server/mutators/mutator/gamemode_invasion.qh +++ b/qcsrc/server/mutators/mutator/gamemode_invasion.qh @@ -1,3 +1,60 @@ #pragma once #include "../gamemode.qh" + +#define autocvar_g_invasion_point_limit cvar("g_invasion_point_limit") +int autocvar_g_invasion_teams; +bool autocvar_g_invasion_team_spawns; +bool g_invasion; +void invasion_Initialize(); + +REGISTER_MUTATOR(inv, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + g_invasion = true; + invasion_Initialize(); + + cvar_settemp("g_monsters", "1"); + + SetLimits(autocvar_g_invasion_point_limit, autocvar_leadlimit_override, autocvar_timelimit_override, -1); + if (autocvar_g_invasion_teams >= 2) + { + ActivateTeamplay(); + if (autocvar_g_invasion_team_spawns) + have_team_spawns = -1; // request team spawns + } + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back invasion_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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; diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc index 454e2f168f..6fa76d6c5c 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qc @@ -1,48 +1,4 @@ #include "gamemode_keepaway.qh" -#ifndef GAMEMODE_KEEPAWAY_H -#define GAMEMODE_KEEPAWAY_H - -void ka_Initialize(); - -REGISTER_MUTATOR(ka, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - ka_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back ka_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return false; -} - - -entity ka_ball; - -const float SP_KEEPAWAY_PICKUPS = 4; -const float SP_KEEPAWAY_CARRIERKILLS = 5; -const float SP_KEEPAWAY_BCTIME = 6; - -void(entity this) havocbot_role_ka_carrier; -void(entity this) havocbot_role_ka_collector; - -void ka_DropEvent(entity plyr); -#endif - -#ifdef IMPLEMENTATION int autocvar_g_keepaway_ballcarrier_effects; float autocvar_g_keepaway_ballcarrier_damage; @@ -81,7 +37,7 @@ void ka_EventLog(string mode, entity actor) // use an alias for easy changing an GameLogEcho(strcat(":ka:", mode, ((actor != NULL) ? (strcat(":", ftos(actor.playerid))) : ""))); } -void ka_TouchEvent(entity this); +void ka_TouchEvent(entity this, entity toucher); void ka_RespawnBall(entity this); void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated { @@ -96,7 +52,7 @@ void ka_RespawnBall(entity this) // runs whenever the ball needs to be relocated } makevectors(this.angles); - this.movetype = MOVETYPE_BOUNCE; + set_movetype(this, MOVETYPE_BOUNCE); this.velocity = '0 0 200'; this.angles = '0 0 0'; this.effects = autocvar_g_keepawayball_effects; @@ -125,7 +81,7 @@ void ka_TimeScoring(entity this) } } -void ka_TouchEvent(entity this) // runs any time that the ball comes in contact with something +void ka_TouchEvent(entity this, entity toucher) // runs any time that the ball comes in contact with something { if(gameover) { return; } if(!this) { return; } @@ -134,9 +90,9 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact ka_RespawnBall(this); return; } - if(IS_DEAD(other)) { return; } - if(STAT(FROZEN, other)) { return; } - if (!IS_PLAYER(other)) + 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); @@ -145,14 +101,14 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact else if(this.wait > time) { return; } // attach the ball to the player - this.owner = other; - other.ballcarried = this; - setattachment(this, other, ""); + this.owner = toucher; + toucher.ballcarried = this; + 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'; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); this.effects |= EF_NODRAW; settouch(this, func_null); setthink(this, ka_TimeScoring); @@ -160,25 +116,25 @@ void ka_TouchEvent(entity this) // runs any time that the ball comes in contact this.takedamage = DAMAGE_NO; // apply effects to player - other.glow_color = autocvar_g_keepawayball_trail_color; - other.glow_trail = true; - other.effects |= autocvar_g_keepaway_ballcarrier_effects; + 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", other); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, INFO_KEEPAWAY_PICKUP, other.netname); - Send_Notification(NOTIF_ALL_EXCEPT, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP, other.netname); - Send_Notification(NOTIF_ONE, other, MSG_CENTER, CENTER_KEEPAWAY_PICKUP_SELF); + 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 - PlayerScore_Add(other, SP_KEEPAWAY_PICKUPS, 1); + PlayerScore_Add(toucher, SP_KEEPAWAY_PICKUPS, 1); // waypoints - WaypointSprite_AttachCarrier(WP_KaBallCarrier, other, RADARICON_FLAGCARRIER); - other.waypointsprite_attachedforcarrier.waypointsprite_visible_for_player = ka_ballcarrier_waypointsprite_visible_for_player; - WaypointSprite_UpdateRule(other.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); - WaypointSprite_Ping(other.waypointsprite_attachedforcarrier); + 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); } @@ -191,7 +147,7 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los // reset the ball setattachment(ball, NULL, ""); - ball.movetype = MOVETYPE_BOUNCE; + set_movetype(ball, MOVETYPE_BOUNCE); ball.wait = time + 1; settouch(ball, ka_TouchEvent); setthink(ball, ka_RespawnBall); @@ -211,7 +167,7 @@ void ka_DropEvent(entity plyr) // runs any time that a player is supposed to los 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(other, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) + sound(NULL, CH_TRIGGER, SND_KA_DROPPED, VOL_BASE, ATTEN_NONE); // ATTEN_NONE (it's a sound intended to be heard anywhere) // scoring // PlayerScore_Add(plyr, SP_KEEPAWAY_DROPS, 1); Not anymore, this is 100% the same as pickups and is useless. @@ -470,11 +426,12 @@ MUTATOR_HOOKFUNCTION(ka, HavocBot_ChooseRole) MUTATOR_HOOKFUNCTION(ka, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - + if(frag_target.ballcarried) ka_DropEvent(frag_target); } +.bool pushable; // ============== // Initialization @@ -490,10 +447,11 @@ void ka_SpawnBall() // loads various values for the ball, runs only once at star e.damageforcescale = autocvar_g_keepawayball_damageforcescale; e.takedamage = DAMAGE_YES; e.solid = SOLID_TRIGGER; - e.movetype = MOVETYPE_BOUNCE; + 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); @@ -506,9 +464,9 @@ void ka_SpawnBall() // loads various values for the ball, runs only once at star void ka_ScoreRules() { ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, 0, true); // SFL_SORT_PRIO_PRIMARY - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS, "pickups", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); - ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_PICKUPS, "pickups", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_CARRIERKILLS, "bckills", 0); + ScoreInfo_SetLabel_PlayerScore(SP_KEEPAWAY_BCTIME, "bctime", SFL_SORT_PRIO_SECONDARY); ScoreRules_basics_end(); } @@ -517,5 +475,3 @@ void ka_Initialize() // run at the start of a match, initiates game mode ka_ScoreRules(); ka_SpawnBall(); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh b/qcsrc/server/mutators/mutator/gamemode_keepaway.qh index 399830dad2..a13ab83a55 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keepaway.qh +++ b/qcsrc/server/mutators/mutator/gamemode_keepaway.qh @@ -1,3 +1,38 @@ #pragma once #include "../gamemode.qh" + +void ka_Initialize(); + +REGISTER_MUTATOR(ka, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + ka_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back ka_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index 448a2bbc78..5bfbe676e6 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qc @@ -1,60 +1,4 @@ #include "gamemode_keyhunt.qh" -#ifndef GAMEMODE_KEYHUNT_H -#define GAMEMODE_KEYHUNT_H - -#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_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - kh_Initialize(); - - ActivateTeamplay(); - SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1); - if (autocvar_g_keyhunt_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back kh_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - 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: -float kh_tracking_enabled; -.entity kh_next; -float kh_Key_AllOwnedByWhichTeam(); - -USING(kh_Think_t, void()); -void kh_StartRound(); -void kh_Controller_SetThink(float t, kh_Think_t func); - -entity kh_worldkeylist; -.entity kh_worldkeynext; -#endif - -#ifdef IMPLEMENTATION float autocvar_g_balance_keyhunt_damageforcescale; float autocvar_g_balance_keyhunt_delay_collect; @@ -73,7 +17,7 @@ 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; int autocvar_g_keyhunt_teams_override; // #define KH_PLAYER_USE_ATTACHMENT @@ -96,24 +40,24 @@ const vector KH_KEY_MIN = '-10 -10 -46'; const vector KH_KEY_MAX = '10 10 3'; const float KH_KEY_BRIGHTNESS = 2; -float kh_no_radar_circles; +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 kh_state = _STAT(KH_KEYS); +.int kh_state = _STAT(KH_KEYS); .float siren_time; // time delay the siren //.float stuff_time; // time delay to stuffcmd a cvar -float kh_keystatus[17]; +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"; -float kh_Team_ByID(float t) +int kh_Team_ByID(int t) { if(t == 0) return NUM_TEAM_1; if(t == 1) return NUM_TEAM_2; @@ -125,26 +69,22 @@ float kh_Team_ByID(float t) //entity kh_worldkeylist; .entity kh_worldkeynext; entity kh_controller; -//float kh_tracking_enabled; -float kh_teams; -float kh_interferemsg_time, kh_interferemsg_team; +//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; -.float kh_dropperteam; +.int kh_dropperteam; .entity kh_previous_owner; -.float kh_previous_owner_playerid; -.float kh_cp_duration; +.int kh_previous_owner_playerid; + +int kh_key_dropped, kh_key_carried; -float kh_key_dropped, kh_key_carried; +int kh_Key_AllOwnedByWhichTeam(); const float ST_KH_CAPS = 1; -const float SP_KH_CAPS = 4; -const float SP_KH_PUSHES = 5; -const float SP_KH_DESTROYS = 6; -const float SP_KH_PICKUPS = 7; -const float SP_KH_KCKILLS = 8; -const float SP_KH_LOSSES = 9; -void kh_ScoreRules(float teams) +void kh_ScoreRules(int teams) { ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); ScoreInfo_SetLabel_TeamScore( ST_KH_CAPS, "caps", SFL_SORT_PRIO_SECONDARY); @@ -180,10 +120,8 @@ bool kh_Key_waypointsprite_visible_for_player(entity this, entity player, entity void kh_update_state() { entity key; - float s; - float f; - - s = 0; + int f; + int s = 0; FOR_EACH_KH_KEY(key) { if(key.owner) @@ -217,10 +155,13 @@ void kh_Controller_SetThink(float t, kh_Think_t func) // runs occasionaly void kh_WaitForPlayers(); void kh_Controller_Think(entity this) // called a lot { - if(intermission_running) + if(gameover) return; if(this.cnt > 0) - { if(getthink(this) != kh_WaitForPlayers) { this.cnt -= 1; } } + { + if(getthink(this) != kh_WaitForPlayers) + this.cnt -= 1; + } else if(this.cnt == 0) { this.cnt -= 1; @@ -234,7 +175,7 @@ void kh_Controller_Think(entity this) // called a lot 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(intermission_running) + if(gameover) return; if(frags_player) @@ -275,8 +216,7 @@ vector kh_AttachedOrigin(entity e) // runs when a team captures the flag, it ca 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; - first = key.owner.kh_next; + entity first = key.owner.kh_next; if(key == first) { setattachment(key, key.owner, KH_PLAYER_ATTACHMENT_BONE); @@ -306,8 +246,9 @@ void kh_Key_Attach(entity key) // runs when a player picks up a key and several key.angles_y -= key.owner.angles.y; #endif key.flags = 0; + IL_REMOVE(g_items, key); key.solid = SOLID_NOT; - key.movetype = MOVETYPE_NONE; + set_movetype(key, MOVETYPE_NONE); key.team = key.owner.team; key.nextthink = time; key.damageforcescale = 0; @@ -318,8 +259,7 @@ void kh_Key_Attach(entity key) // runs when a player picks up a key and several 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; - first = key.owner.kh_next; + entity first = key.owner.kh_next; if(key == first) { if(key.kh_next) @@ -337,7 +277,7 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs } // in any case: setattachment(key, NULL, ""); - setorigin(key, key.owner.origin + '0 0 1' * (STAT(PL_MIN, NULL).z - KH_KEY_MIN_z)); + 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'); @@ -345,8 +285,9 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs key.angles_y += key.owner.angles.y; #endif key.flags = FL_ITEM; + IL_PUSH(g_items, key); key.solid = SOLID_TRIGGER; - key.movetype = MOVETYPE_TOSS; + 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; @@ -358,12 +299,10 @@ void kh_Key_Detach(entity key) // runs every time a key is dropped or lost. Runs void kh_Key_AssignTo(entity key, entity player) // runs every time a key is picked up or assigned. Runs prior to kh_key_attach { - entity k; - float ownerteam0, ownerteam; if(key.owner == player) return; - ownerteam0 = kh_Key_AllOwnedByWhichTeam(); + int ownerteam0 = kh_Key_AllOwnedByWhichTeam(); if(key.owner) { @@ -435,15 +374,16 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic key.pusher = NULL; - ownerteam = kh_Key_AllOwnedByWhichTeam(); + 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 + // audit all key carrier sprites, update them to "Run here" FOR_EACH_KH_KEY(k) { if (!k.owner) continue; @@ -458,7 +398,7 @@ void kh_Key_AssignTo(entity key, entity player) // runs every time a key is pic { kh_interferemsg_time = 0; - // audit all key carrier sprites, update them to RUN HERE + // audit all key carrier sprites, update them to "Key Carrier" FOR_EACH_KH_KEY(k) { if (!k.owner) continue; @@ -483,7 +423,7 @@ void kh_Key_Damage(entity this, entity inflictor, entity attacker, float damage, // immediately return is bad // maybe start a shorter countdown? } - if(vlen(force) <= 0) + if(force == '0 0 0') return; if(time > this.pushltime) if(IS_PLAYER(attacker)) @@ -500,14 +440,15 @@ void kh_Key_Collect(entity key, entity player) //a player picks up a dropped ke PlayerScore_Add(player, SP_KH_PICKUPS, 1); } key.kh_dropperteam = 0; - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_PICKUP), player.netname); + 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) // runs many, many times when a key has been dropped and can be picked up +void kh_Key_Touch(entity this, entity toucher) // runs many, many times when a key has been dropped and can be picked up { - if(intermission_running) + if(gameover) return; if(this.owner) // already carried @@ -521,20 +462,19 @@ void kh_Key_Touch(entity this) // runs many, many times when a key has been dro // maybe start a shorter countdown? } - if (!IS_PLAYER(other)) + if (!IS_PLAYER(toucher)) return; - if(IS_DEAD(other)) + if(IS_DEAD(toucher)) return; - if(other == this.enemy) + if(toucher == this.enemy) if(time < this.kh_droptime + autocvar_g_balance_keyhunt_delay_collect) return; // you just dropped it! - kh_Key_Collect(this, other); + 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; - o = key.owner; + entity o = key.owner; kh_Key_AssignTo(key, NULL); if(o) // it was attached WaypointSprite_Kill(key.waypointsprite_attachedforcarrier); @@ -558,7 +498,7 @@ void kh_Key_Remove(entity key) // runs after when all the keys have been collec } } - remove(key); + delete(key); kh_update_state(); } @@ -578,27 +518,25 @@ void kh_FinishRound() // runs when a team captures the keys kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round, kh_StartRound); } -void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TEEM?!?! what the fuck is wrong with you people +void nades_GiveBonus(entity player, float score); + +void kh_WinnerTeam(int winner_team) // runs when a team wins { // all key carriers get some points - vector firstorigin, lastorigin, midpoint; - float first; entity key; - float score; - score = (kh_teams - 1) * autocvar_g_balance_keyhunt_score_capture; - DistributeEvenly_Init(score, kh_teams); + 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; - f = DistributeEvenly_Get(1); + float f = DistributeEvenly_Get(1); kh_Scores_Event(key.owner, key, "capture", f, 0); PlayerTeamScore_Add(key.owner, SP_KH_CAPS, ST_KH_CAPS, 1); nades_GiveBonus(key.owner, autocvar_g_nades_bonus_score_high); } - first = true; + bool first = true; string keyowner = ""; FOR_EACH_KH_KEY(key) if(key.owner.kh_next == key) @@ -609,17 +547,13 @@ void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TE first = false; } - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(teem, INFO_KEYHUNT_CAPTURE), keyowner); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(winner_team, INFO_KEYHUNT_CAPTURE), keyowner); first = true; - midpoint = '0 0 0'; - firstorigin = '0 0 0'; - lastorigin = '0 0 0'; + vector firstorigin = '0 0 0', lastorigin = '0 0 0', midpoint = '0 0 0'; FOR_EACH_KH_KEY(key) { - vector thisorigin; - - thisorigin = kh_AttachedOrigin(key); + vector thisorigin = kh_AttachedOrigin(key); //dprint("Key origin: ", vtos(thisorigin), "\n"); midpoint += thisorigin; @@ -630,32 +564,26 @@ void kh_WinnerTeam(float teem) // runs when a team wins // Samual: Teem?.... TE firstorigin = thisorigin; first = false; } - if(kh_teams > 2) + if(NumTeams(kh_teams) > 2) { te_lightning2(NULL, lastorigin, firstorigin); } - midpoint = midpoint * (1 / kh_teams); - te_customflash(midpoint, 1000, 1, Team_ColorRGB(teem) * 0.5 + '0.5 0.5 0.5'); // make the color >=0.5 in each component + 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(float teem, entity lostkey) // runs when a player pushes a flag carrier off the map +void kh_LoserTeam(int loser_team, entity lostkey) // runs when a player pushes a flag carrier off the map { - entity key, attacker; - float players; - float keys; float f; - - attacker = NULL; + entity attacker = NULL; if(lostkey.pusher) - if(lostkey.pusher.team != teem) + if(lostkey.pusher.team != loser_team) if(IS_PLAYER(lostkey.pusher)) attacker = lostkey.pusher; - players = keys = 0; - if(attacker) { if(lostkey.kh_previous_owner) @@ -667,13 +595,15 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl } else { - float of, fragsleft, i, j, thisteam; - of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; + int players = 0; + float of = autocvar_g_balance_keyhunt_score_destroyed_ownfactor; - FOREACH_CLIENT(IS_PLAYER(it) && it.team != teem, LAMBDA(++players)); + FOREACH_CLIENT(IS_PLAYER(it) && it.team != loser_team, LAMBDA(++players)); + entity key; + int keys = 0; FOR_EACH_KH_KEY(key) - if(key.owner && key.team != teem) + if(key.owner && key.team != loser_team) ++keys; if(lostkey.kh_previous_owner) @@ -686,20 +616,20 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl DistributeEvenly_Init(autocvar_g_balance_keyhunt_score_destroyed, keys * of + players); FOR_EACH_KH_KEY(key) - if(key.owner && key.team != teem) + if(key.owner && key.team != loser_team) { f = DistributeEvenly_Get(of); kh_Scores_Event(key.owner, NULL, "destroyed_holdingkey", f, 0); } - fragsleft = DistributeEvenly_Get(players); + int fragsleft = DistributeEvenly_Get(players); // Now distribute these among all other teams... - j = kh_teams - 1; - for(i = 0; i < kh_teams; ++i) + int j = NumTeams(kh_teams) - 1; + for(int i = 0; i < NumTeams(kh_teams); ++i) { - thisteam = kh_Team_ByID(i); - if(thisteam == teem) // bad boy, no cookie - this WILL happen + int thisteam = kh_Team_ByID(i); + if(thisteam == loser_team) // bad boy, no cookie - this WILL happen continue; players = 0; @@ -718,7 +648,8 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl } } - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(lostkey, INFO_KEYHUNT_LOST), lostkey.kh_previous_owner.netname); + int realteam = kh_Team_ByID(lostkey.count); + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(realteam, INFO_KEYHUNT_LOST), lostkey.kh_previous_owner.netname); play2all(SND(KH_DESTROY)); te_tarexplosion(lostkey.origin); @@ -728,7 +659,7 @@ void kh_LoserTeam(float teem, entity lostkey) // runs when a player pushes a fl void kh_Key_Think(entity this) // runs all the time { - if(intermission_running) + if(gameover) return; if(this.owner) @@ -754,8 +685,7 @@ void kh_Key_Think(entity this) // runs all the time } entity key; - vector p; - p = this.owner.origin; + vector p = this.owner.origin; FOR_EACH_KH_KEY(key) if(vdist(key.owner.origin - p, >, autocvar_g_balance_keyhunt_maxdist)) goto not_winning; @@ -840,14 +770,11 @@ void kh_Key_Spawn(entity initial_owner, float _angle, float i) // runs every ti } // -1 when no team completely owns all keys yet -float kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team +int kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all the keys are owned by the same team { entity key; - float teem; - float keys; - - teem = -1; - keys = kh_teams; + int teem = -1; + int keys = NumTeams(kh_teams); FOR_EACH_KH_KEY(key) { if(!key.owner) @@ -866,15 +793,15 @@ float kh_Key_AllOwnedByWhichTeam() // constantly called. check to see if all th void kh_Key_DropOne(entity key) { // prevent collecting this one for some time - entity player; - player = key.owner; + entity player = key.owner; key.kh_droptime = time; key.enemy = player; kh_Scores_Event(player, key, "dropkey", 0, 0); PlayerScore_Add(player, SP_KH_LOSSES, 1); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_DROP), player.netname); + 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); @@ -888,19 +815,20 @@ void kh_Key_DropOne(entity key) void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies { - entity key; - entity mypusher; if(player.kh_next) { - mypusher = NULL; + 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); PlayerScore_Add(player, SP_KH_LOSSES, 1); - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_ENT(key, INFO_KEYHUNT_LOST), player.netname); + 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); @@ -913,24 +841,23 @@ void kh_Key_DropAll(entity player, float suicide) // runs whenever a player dies } } -float kh_CheckPlayers(float num) +int kh_GetMissingTeams() { - if(num < kh_teams) + int missing_teams = 0; + for(int i = 0; i < NumTeams(kh_teams); ++i) { - float t_team = kh_Team_ByID(num); - float players = 0; + int teem = kh_Team_ByID(i); + int players = 0; FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( - if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == t_team) + if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) ++players; )); - - if (!players) { return t_team; } + if (!players) + missing_teams |= pow(2, i); } - return 0; + return missing_teams; } -#define KH_READY_TEAMS() (!p1 + !p2 + ((kh_teams >= 3) ? !p3 : p3) + ((kh_teams >= 4) ? !p4 : p4)) -#define KH_READY_TEAMS_OK() (KH_READY_TEAMS() == kh_teams) void kh_WaitForPlayers() // delay start of the round until enough players are present { if(time < game_starttime) @@ -939,9 +866,9 @@ void kh_WaitForPlayers() // delay start of the round until enough players are p return; } - static float prev_missing_teams_mask; - float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3); - if(KH_READY_TEAMS_OK()) + static int prev_missing_teams_mask; + 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); @@ -959,9 +886,6 @@ void kh_WaitForPlayers() // delay start of the round until enough players are p } else { - float missing_teams_mask = boolean(p1) + boolean(p2) * 2; - if(kh_teams >= 3) missing_teams_mask += boolean(p3) * 4; - if(kh_teams >= 4) missing_teams_mask += boolean(p4) * 8; if(prev_missing_teams_mask != missing_teams_mask) { Send_Notification(NOTIF_ALL, NULL, MSG_CENTER, CENTER_MISSING_TEAMS, missing_teams_mask); @@ -982,16 +906,13 @@ void kh_EnableTrackingDevice() // runs after each round void kh_StartRound() // runs at the start of each round { - float i, players, teem; - if(time < game_starttime) { kh_Controller_SetThink(game_starttime - time + 0.1, kh_WaitForPlayers); return; } - float p1 = kh_CheckPlayers(0), p2 = kh_CheckPlayers(1), p3 = kh_CheckPlayers(2), p4 = kh_CheckPlayers(3); - if(!KH_READY_TEAMS_OK()) + if(kh_GetMissingTeams()) { kh_Controller_SetThink(1, kh_WaitForPlayers); return; @@ -1000,10 +921,10 @@ void kh_StartRound() // runs at the start of each round Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT); Kill_Notification(NOTIF_ALL, NULL, MSG_CENTER, CPID_KEYHUNT_OTHER); - for(i = 0; i < kh_teams; ++i) + for(int i = 0; i < NumTeams(kh_teams); ++i) { - teem = kh_Team_ByID(i); - players = 0; + int teem = kh_Team_ByID(i); + int players = 0; entity my_player = NULL; FOREACH_CLIENT(IS_PLAYER(it), LAMBDA( if(!IS_DEAD(it) && !PHYS_INPUT_BUTTON_CHAT(it) && it.team == teem) @@ -1013,7 +934,7 @@ void kh_StartRound() // runs at the start of each round my_player = it; } )); - kh_Key_Spawn(my_player, 360 * i / kh_teams, i); + kh_Key_Spawn(my_player, 360 * i / NumTeams(kh_teams), i); } kh_tracking_enabled = false; @@ -1030,10 +951,8 @@ float kh_HandleFrags(entity attacker, entity targ, float f) // adds to the play { if(attacker.team == targ.team) { - entity k; - float nk; - nk = 0; - for(k = targ.kh_next; k != NULL; k = k.kh_next) + 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); } @@ -1053,9 +972,17 @@ void kh_Initialize() // sets up th KH environment // setup variables kh_teams = autocvar_g_keyhunt_teams_override; if(kh_teams < 2) - kh_teams = autocvar_g_keyhunt_teams; + kh_teams = cvar("g_keyhunt_teams"); // read the cvar directly as it gets written earlier in the same frame kh_teams = bound(2, kh_teams, 4); + int teams = 0; + if(kh_teams >= 1) teams |= BIT(0); + if(kh_teams >= 2) teams |= BIT(1); + if(kh_teams >= 3) teams |= BIT(2); + if(kh_teams >= 4) teams |= BIT(3); + + kh_teams = teams; // now set it? + // make a KH entity for controlling the game kh_controller = spawn(); setthink(kh_controller, kh_Controller_Think); @@ -1085,7 +1012,7 @@ void kh_finalize() { // to be called before intermission kh_FinishRound(); - remove(kh_controller); + delete(kh_controller); kh_controller = NULL; } @@ -1133,7 +1060,7 @@ void havocbot_role_kh_carrier(entity this) if (!(this.kh_next)) { - LOG_TRACE("changing role to freelancer\n"); + LOG_TRACE("changing role to freelancer"); this.havocbot_role = havocbot_role_kh_freelancer; this.havocbot_role_timeout = 0; return; @@ -1160,7 +1087,7 @@ void havocbot_role_kh_defense(entity this) if (this.kh_next) { - LOG_TRACE("changing role to carrier\n"); + LOG_TRACE("changing role to carrier"); this.havocbot_role = havocbot_role_kh_carrier; this.havocbot_role_timeout = 0; return; @@ -1170,7 +1097,7 @@ void havocbot_role_kh_defense(entity this) this.havocbot_role_timeout = time + random() * 10 + 20; if (time > this.havocbot_role_timeout) { - LOG_TRACE("changing role to freelancer\n"); + LOG_TRACE("changing role to freelancer"); this.havocbot_role = havocbot_role_kh_freelancer; this.havocbot_role_timeout = 0; return; @@ -1201,7 +1128,7 @@ void havocbot_role_kh_offense(entity this) if (this.kh_next) { - LOG_TRACE("changing role to carrier\n"); + LOG_TRACE("changing role to carrier"); this.havocbot_role = havocbot_role_kh_carrier; this.havocbot_role_timeout = 0; return; @@ -1211,7 +1138,7 @@ void havocbot_role_kh_offense(entity this) this.havocbot_role_timeout = time + random() * 10 + 20; if (time > this.havocbot_role_timeout) { - LOG_TRACE("changing role to freelancer\n"); + LOG_TRACE("changing role to freelancer"); this.havocbot_role = havocbot_role_kh_freelancer; this.havocbot_role_timeout = 0; return; @@ -1243,7 +1170,7 @@ void havocbot_role_kh_freelancer(entity this) if (this.kh_next) { - LOG_TRACE("changing role to carrier\n"); + LOG_TRACE("changing role to carrier"); this.havocbot_role = havocbot_role_kh_carrier; this.havocbot_role_timeout = 0; return; @@ -1255,12 +1182,12 @@ void havocbot_role_kh_freelancer(entity this) { if (random() < 0.5) { - LOG_TRACE("changing role to offense\n"); + LOG_TRACE("changing role to offense"); this.havocbot_role = havocbot_role_kh_offense; } else { - LOG_TRACE("changing role to defense\n"); + LOG_TRACE("changing role to defense"); this.havocbot_role = havocbot_role_kh_defense; } this.havocbot_role_timeout = 0; @@ -1269,12 +1196,10 @@ void havocbot_role_kh_freelancer(entity this) if (this.bot_strategytime < time) { - float key_owner_team; - this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; navigation_goalrating_start(this); - key_owner_team = kh_Key_AllOwnedByWhichTeam(); + 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) @@ -1329,7 +1254,7 @@ MUTATOR_HOOKFUNCTION(kh, MatchEnd) kh_finalize(); } -MUTATOR_HOOKFUNCTION(kh, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(kh, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(0, float) = kh_teams; } @@ -1378,7 +1303,7 @@ MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole) MUTATOR_HOOKFUNCTION(kh, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); - + kh_Key_DropAll(frag_target, false); } @@ -1386,5 +1311,3 @@ MUTATOR_HOOKFUNCTION(kh, reset_map_global) { kh_Controller_SetThink(autocvar_g_balance_keyhunt_delay_round + (game_starttime - time), kh_StartRound); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh index 399830dad2..d0fb5f95bc 100644 --- a/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh +++ b/qcsrc/server/mutators/mutator/gamemode_keyhunt.qh @@ -1,3 +1,50 @@ #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_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + kh_Initialize(); + + ActivateTeamplay(); + SetLimits(autocvar_g_keyhunt_point_limit, autocvar_g_keyhunt_point_leadlimit, autocvar_timelimit_override, -1); + if (autocvar_g_keyhunt_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back kh_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index adfd6eb6a7..25f6d3e0ab 100644 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qc +++ b/qcsrc/server/mutators/mutator/gamemode_lms.qc @@ -1,53 +1,8 @@ #include "gamemode_lms.qh" -#ifndef GAMEMODE_LMS_H -#define GAMEMODE_LMS_H - -#define autocvar_g_lms_lives_override cvar("g_lms_lives_override") -void lms_Initialize(); - -REGISTER_MUTATOR(lms, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - lms_Initialize(); - - SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back lms_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -// scoreboard stuff -const float SP_LMS_LIVES = 4; -const float SP_LMS_RANK = 5; - -// lives related defs -float lms_lowest_lives; -float lms_next_place; -float LMS_NewPlayerLives(); - -#endif - -#ifdef IMPLEMENTATION #include #include -#include +#include int autocvar_g_lms_extra_lives; bool autocvar_g_lms_join_anytime; @@ -117,7 +72,10 @@ int WinningCondition_LMS() // a winner! // and assign him his first place PlayerScore_Add(head, SP_LMS_RANK, 1); - return WINNING_YES; + if(warmup_stage) + return WINNING_NO; + else + return WINNING_YES; } } } @@ -132,7 +90,7 @@ int WinningCondition_LMS() { // SNAFU (maybe a draw game?) ClearWinners(); - LOG_TRACE("No players, ending game.\n"); + LOG_TRACE("No players, ending game."); return WINNING_YES; } } @@ -155,42 +113,104 @@ int WinningCondition_LMS() MUTATOR_HOOKFUNCTION(lms, reset_map_global) { lms_lowest_lives = 999; - lms_next_place = player_count; } MUTATOR_HOOKFUNCTION(lms, reset_map_players) { - if(restart_mapalreadyrestarted || (time < game_starttime)) - FOREACH_CLIENT(IS_PLAYER(it), LAMBDA(PlayerScore_Add(it, SP_LMS_LIVES, LMS_NewPlayerLives()))); + FOREACH_CLIENT(true, { + TRANSMUTE(Player, it); + it.frags = FRAGS_PLAYER; + PlayerScore_Add(it, SP_LMS_LIVES, LMS_NewPlayerLives()); + PutClientInServer(it); + }); } MUTATOR_HOOKFUNCTION(lms, PutClientInServer) { entity player = M_ARGV(0, entity); - // player is dead and becomes observer - // FIXME fix LMS scoring for new system - if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0) - { + if(player.frags == FRAGS_SPECTATOR) TRANSMUTE(Observer, player); + else + { + float tl = PlayerScore_Add(player, SP_LMS_LIVES, 0); + if(tl < lms_lowest_lives) + lms_lowest_lives = tl; + if(tl <= 0) + TRANSMUTE(Observer, player); + if(warmup_stage) + PlayerScore_Add(player, SP_LMS_RANK, -PlayerScore_Add(player, SP_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(PlayerScore_Add(player, SP_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) { - // Only if the player cannot play at all - if(PlayerScore_Add(player, SP_LMS_RANK, 0) == 666) - player.frags = FRAGS_SPECTATOR; - else - player.frags = FRAGS_LMS_LOSER; + static int quitters = 0; + float player_rank = PlayerScore_Add(player, SP_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; + PlayerScore_Add(player, SP_LMS_RANK, pl_cnt + 1); + } + else + { + lms_lowest_lives = 999; + FOREACH_CLIENT(true, { + if (it.frags == FRAGS_LMS_LOSER) + { + float it_rank = PlayerScore_Add(it, SP_LMS_RANK, 0); + if (it_rank > player_rank && it_rank <= 256) + PlayerScore_Add(it, SP_LMS_RANK, -1); + lms_lowest_lives = 0; + } + else if (it.frags != FRAGS_SPECTATOR) + { + float tl = PlayerScore_Add(it, SP_LMS_LIVES, 0); + if(tl < lms_lowest_lives) + lms_lowest_lives = tl; + } + }); + PlayerScore_Add(player, SP_LMS_RANK, 665 - quitters); // different from 666 + if(!warmup_stage) + { + PlayerScore_Add(player, SP_LMS_LIVES, -PlayerScore_Add(player, SP_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(player.killcount != FRAGS_SPECTATOR) if(PlayerScore_Add(player, SP_LMS_RANK, 0) > 0 && player.lms_spectate_warning != 2) @@ -223,7 +243,7 @@ MUTATOR_HOOKFUNCTION(lms, ClientConnect) if(PlayerScore_Add(player, SP_LMS_LIVES, LMS_NewPlayerLives()) <= 0) { - PlayerScore_Add(player, SP_LMS_RANK, 666); + PlayerScore_Add(player, SP_LMS_RANK, 666); // mark as forced spectator for the hud code player.frags = FRAGS_SPECTATOR; } } @@ -253,21 +273,23 @@ MUTATOR_HOOKFUNCTION(lms, GiveFragsForKill) { entity frag_target = M_ARGV(1, entity); - // remove a life - float tl; - tl = PlayerScore_Add(frag_target, SP_LMS_LIVES, -1); - if(tl < lms_lowest_lives) - lms_lowest_lives = tl; - if(tl <= 0) + if (!warmup_stage) { - if(!lms_next_place) - lms_next_place = player_count; - else - lms_next_place = min(lms_next_place, player_count); - PlayerScore_Add(frag_target, SP_LMS_RANK, lms_next_place); // won't ever spawn again - --lms_next_place; + // remove a life + int tl = PlayerScore_Add(frag_target, SP_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; + PlayerScore_Add(frag_target, SP_LMS_RANK, pl_cnt); + } } - M_ARGV(2, float) = 0; + M_ARGV(2, float) = 0; // frag score return true; } @@ -346,8 +368,8 @@ MUTATOR_HOOKFUNCTION(lms, ItemTouch) MUTATOR_HOOKFUNCTION(lms, Bot_FixCount, CBC_ORDER_EXCLUSIVE) { FOREACH_CLIENT(IS_REAL_CLIENT(it), LAMBDA( - ++M_ARGV(0, int); - ++M_ARGV(1, int); + ++M_ARGV(0, int); // activerealplayers + ++M_ARGV(1, int); // realplayers )); return true; @@ -357,17 +379,18 @@ MUTATOR_HOOKFUNCTION(lms, ClientCommand_Spectate) { entity player = M_ARGV(0, entity); - if(player.lms_spectate_warning) + if(warmup_stage || player.lms_spectate_warning) { // for the forfeit message... player.lms_spectate_warning = 2; - // mark player as spectator - PlayerScore_Add(player, SP_LMS_RANK, 666 - PlayerScore_Add(player, SP_LMS_RANK, 0)); } else { - 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"); + 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; @@ -392,7 +415,7 @@ MUTATOR_HOOKFUNCTION(lms, GetPlayerStatus) MUTATOR_HOOKFUNCTION(lms, AddPlayerScore) { if(gameover) - if(M_ARGV(0, int) == SP_LMS_RANK) // score field + 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 } @@ -408,10 +431,6 @@ void lms_ScoreRules() void lms_Initialize() { lms_lowest_lives = 9999; - lms_next_place = 0; lms_ScoreRules(); } - - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_lms.qh b/qcsrc/server/mutators/mutator/gamemode_lms.qh index 399830dad2..1f1d2d4c34 100644 --- a/qcsrc/server/mutators/mutator/gamemode_lms.qh +++ b/qcsrc/server/mutators/mutator/gamemode_lms.qh @@ -1,3 +1,38 @@ #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_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + lms_Initialize(); + + SetLimits(((!autocvar_g_lms_lives_override) ? -1 : autocvar_g_lms_lives_override), 0, autocvar_timelimit_override, -1); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back lms_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + 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 index 132eba1620..abe6b846e7 100644 --- a/qcsrc/server/mutators/mutator/gamemode_race.qc +++ b/qcsrc/server/mutators/mutator/gamemode_race.qc @@ -1,42 +1,5 @@ #include "gamemode_race.qh" -#ifndef GAMEMODE_RACE_H -#define GAMEMODE_RACE_H - -void rc_SetLimits(); -void race_Initialize(); - -REGISTER_MUTATOR(rc, false) -{ - MUTATOR_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - - rc_SetLimits(); - race_Initialize(); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back race_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION - #include #define autocvar_g_race_laps_limit cvar("g_race_laps_limit") @@ -51,23 +14,22 @@ void havocbot_role_race(entity this) if(IS_DEAD(this)) return; - entity e; if (this.bot_strategytime < time) { this.bot_strategytime = time + autocvar_bot_ai_strategyinterval; navigation_goalrating_start(this); - for(e = NULL; (e = find(e, classname, "trigger_race_checkpoint")) != NULL; ) + IL_EACH(g_racecheckpoints, true, { - if(e.cnt == this.race_checkpoint) + if(it.cnt == this.race_checkpoint) { - navigation_routerating(this, e, 1000000, 5000); + navigation_routerating(this, it, 1000000, 5000); } else if(this.race_checkpoint == -1) { - navigation_routerating(this, e, 1000000, 5000); + navigation_routerating(this, it, 1000000, 5000); } - } + }); navigation_goalrating_end(this); } @@ -78,7 +40,7 @@ void race_ScoreRules() ScoreRules_basics(race_teams, 0, 0, false); if(race_teams) { - ScoreInfo_SetLabel_TeamScore( ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); + ScoreInfo_SetLabel_TeamScore( ST_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); ScoreInfo_SetLabel_PlayerScore(SP_RACE_LAPS, "laps", SFL_SORT_PRIO_PRIMARY); ScoreInfo_SetLabel_PlayerScore(SP_RACE_TIME, "time", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER | SFL_TIME); ScoreInfo_SetLabel_PlayerScore(SP_RACE_FASTEST, "fastest", SFL_LOWER_IS_BETTER | SFL_TIME); @@ -143,8 +105,9 @@ float WinningCondition_QualifyingThenRace(float limit) MUTATOR_HOOKFUNCTION(rc, PlayerPhysics) { entity player = M_ARGV(0, entity); + float dt = M_ARGV(1, float); - player.race_movetime_frac += PHYS_INPUT_TIMELENGTH; + player.race_movetime_frac += dt; float f = floor(player.race_movetime_frac); player.race_movetime_frac -= f; player.race_movetime_count += f; @@ -159,7 +122,7 @@ MUTATOR_HOOKFUNCTION(rc, PlayerPhysics) if(player.race_penalty) { player.velocity = '0 0 0'; - player.movetype = MOVETYPE_NONE; + set_movetype(player, MOVETYPE_NONE); player.disableclientprediction = 2; } } @@ -318,7 +281,7 @@ MUTATOR_HOOKFUNCTION(rc, PutClientInServer) MUTATOR_HOOKFUNCTION(rc, PlayerDies) { entity frag_target = M_ARGV(2, entity); - + frag_target.respawn_flags |= RESPAWN_FORCE; race_AbandonRaceCheck(frag_target); } @@ -380,7 +343,7 @@ MUTATOR_HOOKFUNCTION(rc, ForbidPlayerScore_Clear) return true; // in qualifying, you don't lose score by observing } -MUTATOR_HOOKFUNCTION(rc, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(rc, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(0, float) = race_teams; } @@ -463,6 +426,14 @@ void rc_SetLimits() { ActivateTeamplay(); race_teams = bound(2, autocvar_g_race_teams, 4); + int teams = 0; + if(race_teams >= 1) teams |= BIT(0); + if(race_teams >= 2) teams |= BIT(1); + if(race_teams >= 3) teams |= BIT(2); + if(race_teams >= 4) teams |= BIT(3); + + race_teams = teams; // now set it? + have_team_spawns = -1; // request team spawns } else @@ -496,5 +467,3 @@ void rc_SetLimits() g_race_qualifying = 0; SetLimits(fraglimit_override, leadlimit_override, timelimit_override, qualifying_override); } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_race.qh b/qcsrc/server/mutators/mutator/gamemode_race.qh index 399830dad2..ec71a62d17 100644 --- a/qcsrc/server/mutators/mutator/gamemode_race.qh +++ b/qcsrc/server/mutators/mutator/gamemode_race.qh @@ -1,3 +1,33 @@ #pragma once #include "../gamemode.qh" + +void rc_SetLimits(); +void race_Initialize(); + +REGISTER_MUTATOR(rc, false) +{ + MUTATOR_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + + rc_SetLimits(); + race_Initialize(); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back race_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qc b/qcsrc/server/mutators/mutator/gamemode_tdm.qc index 7874129085..101f57fcc4 100644 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qc +++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qc @@ -1,45 +1,5 @@ #include "gamemode_tdm.qh" -#ifndef GAMEMODE_TDM_H -#define GAMEMODE_TDM_H -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_ONADD - { - if (time > 1) // game loads at time 1 - error("This is a game type and it cannot be added at runtime."); - InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); - - ActivateTeamplay(); - SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1); - if (autocvar_g_tdm_team_spawns) - have_team_spawns = -1; // request team spawns - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - // we actually cannot roll back tdm_Initialize here - // BUT: we don't need to! If this gets called, adding always - // succeeds. - } - - MUTATOR_ONREMOVE - { - LOG_INFO("This is a game type and it cannot be removed at runtime."); - return -1; - } - - return 0; -} - -#endif - -#ifdef IMPLEMENTATION int autocvar_g_tdm_teams; int autocvar_g_tdm_teams_override; @@ -51,20 +11,21 @@ Keys: "cnt" Scoreboard color of the team (for example 4 is red and 13 is blue)... */ spawnfunc(tdm_team) { - if(!g_tdm || !this.cnt) { remove(this); return; } + 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, float teamcolor) +void tdm_SpawnTeam (string teamname, int teamcolor) { - entity this = new(tdm_team); + entity this = new_pure(tdm_team); this.netname = teamname; - this.cnt = teamcolor; + this.cnt = teamcolor - 1; + this.team = teamcolor; this.spawnfunc_checked = true; - spawnfunc_tdm_team(this); + //spawnfunc_tdm_team(this); } void tdm_DelayedInit(entity this) @@ -72,20 +33,31 @@ 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.\n"); + LOG_TRACE("No \"tdm_team\" entities found on this map, creating them anyway."); - int numteams = min(4, autocvar_g_tdm_teams_override); + int numteams = autocvar_g_tdm_teams_override; if(numteams < 2) { numteams = autocvar_g_tdm_teams; } numteams = bound(2, numteams, 4); - float i; - for(i = 1; i <= numteams; ++i) - tdm_SpawnTeam(Team_ColorName(Team_NumberToTeam(i)), Team_NumberToTeam(i) - 1); + int teams = 0; + if(numteams >= 1) teams |= BIT(0); + if(numteams >= 2) teams |= BIT(1); + if(numteams >= 3) teams |= BIT(2); + if(numteams >= 4) teams |= BIT(3); + + 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, GetTeamCount, CBC_ORDER_EXCLUSIVE) +MUTATOR_HOOKFUNCTION(tdm, CheckAllowedTeams, CBC_ORDER_EXCLUSIVE) { M_ARGV(1, string) = "tdm_team"; return true; @@ -96,5 +68,3 @@ MUTATOR_HOOKFUNCTION(tdm, Scores_CountFragsRemaining) // announce remaining frags return true; } - -#endif diff --git a/qcsrc/server/mutators/mutator/gamemode_tdm.qh b/qcsrc/server/mutators/mutator/gamemode_tdm.qh index 399830dad2..e7efbae7f5 100644 --- a/qcsrc/server/mutators/mutator/gamemode_tdm.qh +++ b/qcsrc/server/mutators/mutator/gamemode_tdm.qh @@ -1,3 +1,38 @@ #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_ONADD + { + if (time > 1) // game loads at time 1 + error("This is a game type and it cannot be added at runtime."); + InitializeEntity(NULL, tdm_DelayedInit, INITPRIO_GAMETYPE); + + ActivateTeamplay(); + SetLimits(autocvar_g_tdm_point_limit, autocvar_g_tdm_point_leadlimit, autocvar_timelimit_override, -1); + if (autocvar_g_tdm_team_spawns) + have_team_spawns = -1; // request team spawns + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + // we actually cannot roll back tdm_Initialize here + // BUT: we don't need to! If this gets called, adding always + // succeeds. + } + + MUTATOR_ONREMOVE + { + LOG_INFO("This is a game type and it cannot be removed at runtime."); + return -1; + } + + return 0; +} diff --git a/qcsrc/server/pathlib/_all.inc b/qcsrc/server/pathlib/_all.inc deleted file mode 100644 index 7a06615bf3..0000000000 --- a/qcsrc/server/pathlib/_all.inc +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef DEBUGPATHING - #define DEBUGPATHING 0 -#endif - -#include "costs.qc" -#include "expandnode.qc" -#include "main.qc" -#include "movenode.qc" -#include "path_waypoint.qc" -#include "utility.qc" -#if DEBUGPATHING - #include "debug.qc" -#endif diff --git a/qcsrc/server/pathlib/costs.qc b/qcsrc/server/pathlib/costs.qc index 6b89bb6402..fdb95d2f60 100644 --- a/qcsrc/server/pathlib/costs.qc +++ b/qcsrc/server/pathlib/costs.qc @@ -1,5 +1,4 @@ #include "costs.qh" -#include "pathlib.qh" float pathlib_g_static(entity parent,vector to, float static_cost) { diff --git a/qcsrc/server/pathlib/costs.qh b/qcsrc/server/pathlib/costs.qh index 6f70f09bee..811c031aff 100644 --- a/qcsrc/server/pathlib/costs.qh +++ b/qcsrc/server/pathlib/costs.qh @@ -1 +1,2 @@ #pragma once +#include "pathlib.qh" diff --git a/qcsrc/server/pathlib/debug.qc b/qcsrc/server/pathlib/debug.qc index 0a350df2c2..b84ae6414e 100644 --- a/qcsrc/server/pathlib/debug.qc +++ b/qcsrc/server/pathlib/debug.qc @@ -1,5 +1,6 @@ #include "debug.qh" -#include "pathlib.qh" + +#if DEBUGPATHING MODEL(SQUARE, "models/pathlib/square.md3"); MODEL(SQUARE_GOOD, "models/pathlib/goodsquare.md3"); @@ -119,3 +120,5 @@ void pathlib_showedge(vector where,float _lifetime,float rot) //e.angles_x += 90; } + +#endif diff --git a/qcsrc/server/pathlib/debug.qh b/qcsrc/server/pathlib/debug.qh index 6f70f09bee..811c031aff 100644 --- a/qcsrc/server/pathlib/debug.qh +++ b/qcsrc/server/pathlib/debug.qh @@ -1 +1,2 @@ #pragma once +#include "pathlib.qh" diff --git a/qcsrc/server/pathlib/main.qc b/qcsrc/server/pathlib/main.qc index b6e1350789..36c7c1d314 100644 --- a/qcsrc/server/pathlib/main.qc +++ b/qcsrc/server/pathlib/main.qc @@ -6,15 +6,11 @@ void pathlib_deletepath(entity start) { - entity e; - - e = findchainentity(owner, start); - while(e) + FOREACH_ENTITY_ENT(owner, start, { - setthink(e, SUB_Remove); - e.nextthink = time; - e = e.chain; - } + setthink(it, SUB_Remove); + it.nextthink = time; + }); } //#define PATHLIB_NODEEXPIRE 0.05 @@ -59,7 +55,6 @@ entity pathlib_mknode(vector where,entity parent) setsize(node, '0 0 0', '0 0 0'); setorigin(node, where); - node.medium = pointcontents(where); #if DEBUGPATHING pathlib_showsquare(where, 1 ,15); #endif @@ -79,7 +74,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go if(inwater(parent.origin)) { - LOG_TRACE("FromWater\n"); + LOG_TRACE("FromWater"); pathlib_expandnode = pathlib_expandnode_box; pathlib_movenode = pathlib_swimnode; } @@ -87,13 +82,13 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go { if(inwater(to)) { - LOG_TRACE("ToWater\n"); + LOG_TRACE("ToWater"); pathlib_expandnode = pathlib_expandnode_box; pathlib_movenode = pathlib_walknode; } else { - LOG_TRACE("LandToLoand\n"); + LOG_TRACE("LandToLoand"); //if(edge_check(parent.origin)) // return 0; @@ -106,7 +101,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go node = pathlib_nodeatpoint(to); if(node) { - LOG_TRACE("NodeAtPoint\n"); + LOG_TRACE("NodeAtPoint"); ++pathlib_merge_cnt; if(node.owner == openlist) @@ -139,7 +134,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go { //pathlib_showsquare(where, 0 ,30); //pathlib_showsquare(parent.origin, 1 ,30); - LOG_TRACE("pathlib_movenode_goodnode = 0\n"); + LOG_TRACE("pathlib_movenode_goodnode = 0"); return 0; } @@ -147,9 +142,9 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go if(pathlib_nodeatpoint(where)) { - LOG_TRACE("NAP WHERE :",vtos(where),"\n"); - LOG_TRACE("not NAP TO:",vtos(to),"\n"); - LOG_TRACE("NAP-NNAP:",ftos(vlen(to-where)),"\n\n"); + LOG_TRACE("NAP WHERE :",vtos(where)); + LOG_TRACE("not NAP TO:",vtos(to)); + LOG_TRACE("NAP-NNAP:",ftos(vlen(to-where))); return 0; } @@ -157,7 +152,7 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go if(doedge) if (!tile_check(parent, where)) { - LOG_TRACE("tile_check fail\n"); + LOG_TRACE("tile_check fail"); #if DEBUGPATHING pathlib_showsquare(where, 0 ,30); #endif @@ -218,9 +213,6 @@ float pathlib_makenode_adaptive(entity parent,vector start, vector to, vector go entity pathlib_getbestopen() { - entity node; - entity bestnode; - if(best_open_node) { ++pathlib_bestcash_hits; @@ -229,19 +221,14 @@ entity pathlib_getbestopen() return best_open_node; } - node = findchainentity(owner,openlist); - if(!node) - return NULL; - - bestnode = node; - while(node) + entity bestnode = NULL; + FOREACH_ENTITY_ENT(owner, openlist, { - ++pathlib_bestopen_seached; - if(node.pathlib_node_f < bestnode.pathlib_node_f) - bestnode = node; + ++pathlib_bestopen_searched; - node = node.chain; - } + if(!bestnode || it.pathlib_node_f < bestnode.pathlib_node_f) + bestnode = it; + }); return bestnode; } @@ -251,7 +238,7 @@ void pathlib_close_node(entity node,vector goal) if(node.owner == closedlist) { - LOG_TRACE("Pathlib: Tried to close a closed node!\n"); + LOG_TRACE("Pathlib: Tried to close a closed node!"); return; } @@ -300,21 +287,21 @@ void pathlib_cleanup() } if(openlist) - remove(openlist); + delete(openlist); if(closedlist) - remove(closedlist); + delete(closedlist); openlist = NULL; closedlist = NULL; } -float Cosine_Interpolate(float a, float b, float x) +float Cosine_Interpolate(float a, float b, float c) { float ft,f; - ft = x * 3.1415927; + ft = c * 3.1415927; f = (1 - cos(ft)) * 0.5; return a*(1-f) + b*f; @@ -432,7 +419,7 @@ entity pathlib_astar(entity this, vector from,vector to) pathlib_made_cnt = 0; pathlib_merge_cnt = 0; pathlib_searched_cnt = 0; - pathlib_bestopen_seached = 0; + pathlib_bestopen_searched = 0; pathlib_bestcash_hits = 0; pathlib_bestcash_saved = 0; @@ -467,12 +454,12 @@ entity pathlib_astar(entity this, vector from,vector to) to.y = fsnap(to.y, pathlib_gridsize); //to_z += 32; - LOG_TRACE("AStar init\n"); + LOG_TRACE("AStar init"); path = pathlib_mknode(from, NULL); pathlib_close_node(path, to); if(pathlib_foundgoal) { - LOG_TRACE("AStar: Goal found on first node!\n"); + LOG_TRACE("AStar: Goal found on first node!"); open = new(path_end); open.owner = open; @@ -485,7 +472,7 @@ entity pathlib_astar(entity this, vector from,vector to) if(pathlib_expandnode(path, from, to) <= 0) { - LOG_TRACE("AStar path fail.\n"); + LOG_TRACE("AStar path fail."); pathlib_cleanup(); return NULL; @@ -503,11 +490,11 @@ entity pathlib_astar(entity this, vector from,vector to) { if((gettime(GETTIME_REALTIME) - pathlib_starttime) > pathlib_maxtime) { - LOG_TRACE("Path took to long to compute!\n"); - LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); - LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); - LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); - LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); + LOG_TRACE("Path took to long to compute!"); + LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt)); + LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt)); + LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt)); + LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt)); pathlib_cleanup(); return NULL; @@ -524,7 +511,7 @@ entity pathlib_astar(entity this, vector from,vector to) if(pathlib_foundgoal) { - LOG_TRACE("Target found. Rebuilding and filtering path...\n"); + LOG_TRACE("Target found. Rebuilding and filtering path..."); ftime = gettime(GETTIME_REALTIME); ptime = ftime - ptime; @@ -551,26 +538,26 @@ entity pathlib_astar(entity this, vector from,vector to) #if DEBUGPATHING pathlib_showpath2(start); - LOG_TRACE("Time used - pathfinding: ", ftos(ptime),"\n"); - LOG_TRACE("Time used - rebuild & filter: ", ftos(ftime),"\n"); - LOG_TRACE("Time used - cleanup: ", ftos(ctime),"\n"); - LOG_TRACE("Time used - total: ", ftos(ptime + ftime + ctime),"\n"); - LOG_TRACE("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime)),"\n\n"); - LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt),"\n"); - LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt),"\n"); - LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt),"\n"); - LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt),"\n"); - LOG_TRACE("Nodes - searched: ", ftos(pathlib_searched_cnt),"\n"); - LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_seached),"\n"); - LOG_TRACE("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits),"\n"); - LOG_TRACE("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved),"\n"); - LOG_TRACE("AStar done.\n"); + LOG_TRACE("Time used - pathfinding: ", ftos(ptime)); + LOG_TRACE("Time used - rebuild & filter: ", ftos(ftime)); + LOG_TRACE("Time used - cleanup: ", ftos(ctime)); + LOG_TRACE("Time used - total: ", ftos(ptime + ftime + ctime)); + LOG_TRACE("Time used - # frames: ", ftos(ceil((ptime + ftime + ctime) / sys_frametime))); + LOG_TRACE("Nodes - created: ", ftos(pathlib_made_cnt)); + LOG_TRACE("Nodes - open: ", ftos(pathlib_open_cnt)); + LOG_TRACE("Nodes - merged: ", ftos(pathlib_merge_cnt)); + LOG_TRACE("Nodes - closed: ", ftos(pathlib_closed_cnt)); + LOG_TRACE("Nodes - searched: ", ftos(pathlib_searched_cnt)); + LOG_TRACE("Nodes bestopen searched: ", ftos(pathlib_bestopen_searched)); + LOG_TRACE("Nodes bestcash - hits: ", ftos(pathlib_bestcash_hits)); + LOG_TRACE("Nodes bestcash - save: ", ftos(pathlib_bestcash_saved)); + LOG_TRACE("AStar done."); #endif return start; } } - LOG_TRACE("A* Faild to find a path! Try a smaller gridsize.\n"); + LOG_TRACE("A* Faild to find a path! Try a smaller gridsize."); pathlib_cleanup(); diff --git a/qcsrc/server/pathlib/movenode.qc b/qcsrc/server/pathlib/movenode.qc index 3059021d12..4a3bfe35c5 100644 --- a/qcsrc/server/pathlib/movenode.qc +++ b/qcsrc/server/pathlib/movenode.qc @@ -77,7 +77,7 @@ void a_think(entity this) { te_lightning1(this,this.origin, this.pos1); if(this.cnt < time) - remove(this); + delete(this); else this.nextthink = time + 0.2; } @@ -87,7 +87,7 @@ vector pathlib_walknode(entity this, vector start, vector end, float doedge) vector direction,point,last_point,s,e; float steps, distance, i; - LOG_TRACE("Walking node from ", vtos(start), " to ", vtos(end), "\n"); + LOG_TRACE("Walking node from ", vtos(start), " to ", vtos(end)); pathlib_movenode_goodnode = 0; @@ -109,7 +109,7 @@ vector pathlib_walknode(entity this, vector start, vector end, float doedge) //start - movenode_maxdrop a.cnt = time + 10; - LOG_TRACE("I cant walk on air!\n"); + LOG_TRACE("I cant walk on air!"); return trace_endpos; } diff --git a/qcsrc/server/pathlib/path_waypoint.qc b/qcsrc/server/pathlib/path_waypoint.qc index 35dcce5b25..906ebc7a54 100644 --- a/qcsrc/server/pathlib/path_waypoint.qc +++ b/qcsrc/server/pathlib/path_waypoint.qc @@ -1,5 +1,5 @@ #include "path_waypoint.qh" -#include "../bot/waypoints.qh" +#include "../bot/api.qh" #include "pathlib.qh" #include "main.qh" @@ -114,20 +114,16 @@ float pathlib_wpp_expand(entity wp) entity pathlib_wpp_bestopen() { - entity n, best; - if(best_open_node) return best_open_node; - n = findchainentity(pathlib_list, openlist); - best = n; - while(n) - { - if(n.pathlib_node_f < best.pathlib_node_f) - best = n; + entity best = NULL; - n = n.chain; - } + FOREACH_ENTITY_ENT(pathlib_list, openlist, + { + if(!best || it.pathlib_node_f < best.pathlib_node_f) + best = it; + }); return best; @@ -135,7 +131,6 @@ entity pathlib_wpp_bestopen() entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback) { - entity n; float ptime; ptime = gettime(GETTIME_REALTIME); @@ -164,32 +159,30 @@ entity pathlib_waypointpath(entity wp_from, entity wp_to, float callback) pathlib_searched_cnt = 0; pathlib_foundgoal = false; - LOG_TRACE("pathlib_waypointpath init\n"); + LOG_TRACE("pathlib_waypointpath init"); // Initialize waypoint grid - // FIXME! presisted chain for better preformance - for(n = findchain(classname, "waypoint"); n; n = n.chain) + IL_EACH(g_waypoints, true, { - n.pathlib_list = NULL; - n.pathlib_node_g = 0; - n.pathlib_node_f = 0; - n.pathlib_node_h = 0; - - //setmodel(n, "models/runematch/rune.mdl"); - //n.effects = EF_LOWPRECISION; - //n.colormod = '0 0 0'; - //n.scale = 1; + it.pathlib_list = NULL; + it.pathlib_node_g = 0; + it.pathlib_node_f = 0; + it.pathlib_node_h = 0; - } + //setmodel(it, "models/runematch/rune.mdl"); + //it.effects = EF_LOWPRECISION; + //it.colormod = '0 0 0'; + //it.scale = 1; + }); goal_node = wp_to; start_node = wp_from; start_node.pathlib_list = closedlist; - LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(start_node))," links\n"); + LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(start_node))," links"); if(pathlib_open_cnt <= 0) { - LOG_TRACE("pathlib_waypointpath: Start waypoint not linked! aborting.\n"); + LOG_TRACE("pathlib_waypointpath: Start waypoint not linked! aborting."); return NULL; } @@ -203,17 +196,17 @@ entity pathlib_waypointpath_step() n = pathlib_wpp_bestopen(); if(!n) { - LOG_TRACE("Cannot find best open node, abort.\n"); + LOG_TRACE("Cannot find best open node, abort."); return NULL; } pathlib_wpp_close(n); - LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(n))," links\n"); + LOG_TRACE("Expanding ",ftos(pathlib_wpp_expand(n))," links"); if(pathlib_foundgoal) { entity start, end, open, ln; - LOG_TRACE("Target found. Rebuilding and filtering path...\n"); + LOG_TRACE("Target found. Rebuilding and filtering path..."); buildpath_nodefilter = buildpath_nodefilter_none; start = path_build(NULL, start_node.origin, NULL, NULL); diff --git a/qcsrc/server/pathlib/pathlib.qh b/qcsrc/server/pathlib/pathlib.qh index a5f36da4a3..028a2c7e6e 100644 --- a/qcsrc/server/pathlib/pathlib.qh +++ b/qcsrc/server/pathlib/pathlib.qh @@ -1,11 +1,14 @@ #pragma once +#ifndef DEBUGPATHING + #define DEBUGPATHING 0 +#endif + .entity pathlib_list; .entity path_next; .entity path_prev; #define inwater(point) (pointcontents(point) == CONTENT_WATER) -.int medium; const vector PLIB_FORWARD = '0 1 0'; //#define PLIB_BACK '0 -1 0' @@ -46,7 +49,7 @@ float pathlib_closed_cnt; float pathlib_made_cnt; float pathlib_merge_cnt; float pathlib_searched_cnt; -float pathlib_bestopen_seached; +float pathlib_bestopen_searched; float pathlib_bestcash_hits; float pathlib_bestcash_saved; float pathlib_gridsize; diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc new file mode 100644 index 0000000000..9cccbe887a --- /dev/null +++ b/qcsrc/server/player.qc @@ -0,0 +1,970 @@ +#include "player.qh" + +#include "bot/api.qh" +#include "cheats.qh" +#include "g_damage.qh" +#include "g_subs.qh" +#include "miscfunctions.qh" +#include "portals.qh" +#include "teamplay.qh" +#include "weapons/throwing.qh" +#include "command/common.qh" +#include "../common/state.qh" +#include "../common/anim.qh" +#include "../common/animdecide.qh" +#include "../common/csqcmodel_settings.qh" +#include "../common/deathtypes/all.qh" +#include "../common/triggers/subs.qh" +#include "../common/playerstats.qh" +#include "../lib/csqcmodel/sv_model.qh" + +#include "../common/minigames/sv_minigames.qh" + +#include "../common/physics/player.qh" +#include "../common/effects/qc/all.qh" +#include "../common/mutators/mutator/waypoints/waypointsprites.qh" +#include "../common/triggers/include.qh" + +#include "weapons/weaponstats.qh" + +#include "../common/animdecide.qh" + +void Drop_Special_Items(entity player) +{ + // called when the player has become stuck or frozen + // so objective items aren't stuck with the player + + MUTATOR_CALLHOOK(DropSpecialItems, player); +} + +void CopyBody_Think(entity this) +{ + if(this.CopyBody_nextthink && time > this.CopyBody_nextthink) + { + this.CopyBody_think(this); + if(wasfreed(this)) + return; + this.CopyBody_nextthink = this.nextthink; + this.CopyBody_think = getthink(this); + setthink(this, CopyBody_Think); + } + CSQCMODEL_AUTOUPDATE(this); + this.nextthink = time; +} +void CopyBody(entity this, float keepvelocity) +{ + if (this.effects & EF_NODRAW) + return; + entity clone = new(body); + clone.enemy = this; + clone.lip = this.lip; + clone.colormap = this.colormap; + clone.iscreature = this.iscreature; + clone.teleportable = this.teleportable; + clone.damagedbycontents = this.damagedbycontents; + if(clone.damagedbycontents) + IL_PUSH(g_damagedbycontents, clone); + clone.angles = this.angles; + clone.v_angle = this.v_angle; + clone.avelocity = this.avelocity; + clone.damageforcescale = this.damageforcescale; + clone.effects = this.effects; + clone.glowmod = this.glowmod; + clone.event_damage = this.event_damage; + clone.anim_state = this.anim_state; + clone.anim_time = this.anim_time; + clone.anim_lower_action = this.anim_lower_action; + clone.anim_lower_time = this.anim_lower_time; + clone.anim_upper_action = this.anim_upper_action; + clone.anim_upper_time = this.anim_upper_time; + clone.anim_implicit_state = this.anim_implicit_state; + clone.anim_implicit_time = this.anim_implicit_time; + clone.anim_lower_implicit_action = this.anim_lower_implicit_action; + clone.anim_lower_implicit_time = this.anim_lower_implicit_time; + clone.anim_upper_implicit_action = this.anim_upper_implicit_action; + clone.anim_upper_implicit_time = this.anim_upper_implicit_time; + clone.dphitcontentsmask = this.dphitcontentsmask; + clone.death_time = this.death_time; + clone.pain_finished = this.pain_finished; + clone.health = this.health; + clone.armorvalue = this.armorvalue; + clone.armortype = this.armortype; + clone.model = this.model; + clone.modelindex = this.modelindex; + clone.skin = this.skin; + clone.species = this.species; + clone.move_qcphysics = false; // don't run gamecode logic on clones, too many + set_movetype(clone, this.move_movetype); + clone.solid = this.solid; + clone.ballistics_density = this.ballistics_density; + clone.takedamage = this.takedamage; + setcefc(clone, getcefc(this)); + clone.uncustomizeentityforclient = this.uncustomizeentityforclient; + clone.uncustomizeentityforclient_set = this.uncustomizeentityforclient_set; + if (keepvelocity == 1) + clone.velocity = this.velocity; + clone.oldvelocity = clone.velocity; + clone.alpha = this.alpha; + clone.fade_time = this.fade_time; + clone.fade_rate = this.fade_rate; + //clone.weapon = this.weapon; + setorigin(clone, this.origin); + setsize(clone, this.mins, this.maxs); + clone.prevorigin = this.origin; + clone.reset = SUB_Remove; + clone._ps = this._ps; + + Drag_MoveDrag(this, clone); + + if(clone.colormap <= maxclients && clone.colormap > 0) + clone.colormap = 1024 + this.clientcolors; + + CSQCMODEL_AUTOINIT(clone); + clone.CopyBody_nextthink = this.nextthink; + clone.CopyBody_think = getthink(this); + clone.nextthink = time; + setthink(clone, CopyBody_Think); + // "bake" the current animation frame for clones (they don't get clientside animation) + animdecide_load_if_needed(clone); + animdecide_setframes(clone, false, frame, frame1time, frame2, frame2time); + + IL_PUSH(g_clones, clone); + + MUTATOR_CALLHOOK(CopyBody, this, clone, keepvelocity); +} + +void player_setupanimsformodel(entity this) +{ + // load animation info + animdecide_load_if_needed(this); + animdecide_setstate(this, 0, false); +} + +void player_anim(entity this) +{ + int deadbits = (this.anim_state & (ANIMSTATE_DEAD1 | ANIMSTATE_DEAD2)); + if(IS_DEAD(this)) { + if (!deadbits) { + // Decide on which death animation to use. + if(random() < 0.5) + deadbits = ANIMSTATE_DEAD1; + else + deadbits = ANIMSTATE_DEAD2; + } + } else { + // Clear a previous death animation. + deadbits = 0; + } + int animbits = deadbits; + if(STAT(FROZEN, this)) + animbits |= ANIMSTATE_FROZEN; + if(this.move_movetype == MOVETYPE_FOLLOW) + animbits |= ANIMSTATE_FOLLOW; + if(this.crouch) + animbits |= ANIMSTATE_DUCK; + animdecide_setstate(this, animbits, false); + animdecide_setimplicitstate(this, IS_ONGROUND(this)); +} + +void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + float take, save; + vector v; + Violence_GibSplash_At(hitloc, force, 2, bound(0, damage, 200) / 16, this, attacker); + + // damage resistance (ignore most of the damage from a bullet or similar) + damage = max(damage - 5, 1); + + v = healtharmor_applydamage(this.armorvalue, autocvar_g_balance_armor_blockpercent, deathtype, damage); + take = v.x; + save = v.y; + + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if (save > 10) + sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); + else if (take > 30) + sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); + else if (take > 10) + sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); + } + + if (take > 50) + Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); + if (take > 100) + Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); + + this.armorvalue = this.armorvalue - save; + this.health = this.health - take; + // pause regeneration for 5 seconds + this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + + this.dmg_save = this.dmg_save + save;//max(save - 10, 0); + this.dmg_take = this.dmg_take + take;//max(take - 10, 0); + this.dmg_inflictor = inflictor; + + if (this.health <= -autocvar_sv_gibhealth && this.alpha >= 0) + { + // don't use any animations as a gib + this.frame = 0; + // view just above the floor + this.view_ofs = '0 0 4'; + + Violence_GibSplash(this, 1, 1, attacker); + this.alpha = -1; + this.solid = SOLID_NOT; // restore later + this.takedamage = DAMAGE_NO; // restore later + if(this.damagedbycontents) + IL_REMOVE(g_damagedbycontents, this); + this.damagedbycontents = false; + } +} + +void calculate_player_respawn_time(entity this) +{ + if(g_ca) + return; + + float gametype_setting_tmp; + float sdelay_max = GAMETYPE_DEFAULTED_SETTING(respawn_delay_max); + float sdelay_small = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small); + float sdelay_large = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large); + float sdelay_small_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_small_count); + float sdelay_large_count = GAMETYPE_DEFAULTED_SETTING(respawn_delay_large_count); + float waves = GAMETYPE_DEFAULTED_SETTING(respawn_waves); + + float pcount = 1; // Include myself whether or not team is already set right and I'm a "player". + if (teamplay) + { + FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( + if(it.team == this.team) + ++pcount; + )); + if (sdelay_small_count == 0) + sdelay_small_count = 1; + if (sdelay_large_count == 0) + sdelay_large_count = 1; + } + else + { + FOREACH_CLIENT(IS_PLAYER(it) && it != this, LAMBDA( + ++pcount; + )); + if (sdelay_small_count == 0) + { + if (g_cts) + { + // Players play independently. No point in requiring enemies. + sdelay_small_count = 1; + } + else + { + // Players play AGAINST each other. Enemies required. + sdelay_small_count = 2; + } + } + if (sdelay_large_count == 0) + { + if (g_cts) + { + // Players play independently. No point in requiring enemies. + sdelay_large_count = 1; + } + else + { + // Players play AGAINST each other. Enemies required. + sdelay_large_count = 2; + } + } + } + + float sdelay; + + if (pcount <= sdelay_small_count) + sdelay = sdelay_small; + else if (pcount >= sdelay_large_count) + sdelay = sdelay_large; + else // NOTE: this case implies sdelay_large_count > sdelay_small_count. + sdelay = sdelay_small + (sdelay_large - sdelay_small) * (pcount - sdelay_small_count) / (sdelay_large_count - sdelay_small_count); + + if(waves) + this.respawn_time = ceil((time + sdelay) / waves) * waves; + else + this.respawn_time = time + sdelay; + + if(sdelay < sdelay_max) + this.respawn_time_max = time + sdelay_max; + else + this.respawn_time_max = this.respawn_time; + + if((sdelay + waves >= 5.0) && (this.respawn_time - time > 1.75)) + this.respawn_countdown = 10; // first number to count down from is 10 + else + this.respawn_countdown = -1; // do not count down + + if(autocvar_g_forced_respawn) + this.respawn_flags = this.respawn_flags | RESPAWN_FORCE; +} + +void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force) +{ + float take, save, dh, da; + vector v; + float valid_damage_for_weaponstats; + float excess; + + dh = max(this.health, 0); + da = max(this.armorvalue, 0); + + if(!DEATH_ISSPECIAL(deathtype)) + { + damage *= sqrt(bound(1.0, this.cvar_cl_handicap, 100.0)); + if(this != attacker) + damage /= sqrt(bound(1.0, attacker.cvar_cl_handicap, 100.0)); + } + + if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1) + damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage); + + if(DEATH_ISWEAPON(deathtype, WEP_TUBA)) + { + // tuba causes blood to come out of the ears + vector ear1, ear2; + vector d; + float f; + ear1 = this.origin; + ear1_z += 0.125 * this.view_ofs.z + 0.875 * this.maxs.z; // 7/8 + ear2 = ear1; + makevectors(this.angles); + ear1 += v_right * -10; + ear2 += v_right * +10; + d = inflictor.origin - this.origin; + if (d) + f = (d * v_right) / vlen(d); // this is cos of angle of d and v_right! + else + f = 0; // Assum ecenter. + force = v_right * vlen(force); + Violence_GibSplash_At(ear1, force * -1, 2, bound(0, damage, 25) / 2 * (0.5 - 0.5 * f), this, attacker); + Violence_GibSplash_At(ear2, force, 2, bound(0, damage, 25) / 2 * (0.5 + 0.5 * f), this, attacker); + if(f > 0) + { + hitloc = ear1; + force = force * -1; + } + else + { + hitloc = ear2; + // force is already good + } + } + 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); + take = v.x; + save = v.y; + + if(attacker == this) + { + // don't reset pushltime for this damage as it may be an attempt to + // escape a lava pit or similar + //this.pushltime = 0; + this.istypefrag = 0; + } + else if(IS_PLAYER(attacker)) + { + this.pusher = attacker; + this.pushltime = time + autocvar_g_maxpushtime; + this.istypefrag = PHYS_INPUT_BUTTON_CHAT(this); + } + else if(time < this.pushltime) + { + attacker = this.pusher; + this.pushltime = max(this.pushltime, time + 0.6); + } + else + { + this.pushltime = 0; + this.istypefrag = 0; + } + + 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); + excess = max(0, damage - take - save); + + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if (save > 10) + sound (this, CH_SHOTS, SND_ARMORIMPACT, VOL_BASE, ATTEN_NORM); + else if (take > 30) + sound (this, CH_SHOTS, SND_BODYIMPACT2, VOL_BASE, ATTEN_NORM); + else if (take > 10) + sound (this, CH_SHOTS, SND_BODYIMPACT1, VOL_BASE, ATTEN_NORM); // FIXME possibly remove them? + } + + if (take > 50) + Violence_GibSplash_At(hitloc, force * -0.1, 3, 1, this, attacker); + if (take > 100) + Violence_GibSplash_At(hitloc, force * -0.2, 3, 1, this, attacker); + + if (time >= this.spawnshieldtime || autocvar_g_spawnshield_blockdamage < 1) + { + if (!(this.flags & FL_GODMODE)) + { + this.armorvalue = this.armorvalue - save; + this.health = this.health - take; + // pause regeneration for 5 seconds + if(take) + this.pauseregen_finished = max(this.pauseregen_finished, time + autocvar_g_balance_pause_health_regen); + + if (time > this.pain_finished) //Don't switch pain sequences like crazy + { + this.pain_finished = time + 0.5; //Supajoe + + if(autocvar_sv_gentle < 1) { + if(this.classname != "body") // pain anim is BORKED on our ZYMs, FIXME remove this once we have good models + { + if (!this.animstate_override) + { + if (random() > 0.5) + animdecide_setaction(this, ANIMACTION_PAIN1, true); + else + animdecide_setaction(this, ANIMACTION_PAIN2, true); + } + } + + if(sound_allowed(MSG_BROADCAST, attacker)) + if((this.health < 2 * WEP_CVAR_PRI(blaster, damage) * autocvar_g_balance_selfdamagepercent + 1) || !(DEATH_WEAPONOF(deathtype).spawnflags & WEP_FLAG_CANCLIMB) || attacker != this) // WEAPONTODO: create separate limit for pain notification with laser + 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) + PlayerSound(this, playersound_pain100, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else if(this.health > 50) + PlayerSound(this, playersound_pain75, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else if(this.health > 25) + PlayerSound(this, playersound_pain50, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else + PlayerSound(this, playersound_pain25, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + } + } + } + + // throw off bot aim temporarily + float shake; + if(IS_BOT_CLIENT(this) && this.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); + } + } + else + this.max_armorvalue += (save + take); + } + this.dmg_save = this.dmg_save + save;//max(save - 10, 0); + 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)) { + PlayerScore_Add(attacker, SP_DMG, realdmg); + } + if (IS_PLAYER(this)) { + PlayerScore_Add(this, SP_DMGTAKEN, realdmg); + } + } + + bool abot = (IS_BOT_CLIENT(attacker)); + bool vbot = (IS_BOT_CLIENT(this)); + + valid_damage_for_weaponstats = 0; + Weapon awep = WEP_Null; + + if(vbot || IS_REAL_CLIENT(this)) + if(abot || IS_REAL_CLIENT(attacker)) + if(attacker && this != attacker) + if(DIFF_TEAM(this, attacker)) + { + if(DEATH_ISSPECIAL(deathtype)) + awep = PS(attacker).m_weapon; + else + awep = DEATH_WEAPONOF(deathtype); + valid_damage_for_weaponstats = 1; + } + + dh = dh - max(this.health, 0); + da = da - max(this.armorvalue, 0); + if(valid_damage_for_weaponstats) + { + WeaponStats_LogDamage(awep.m_id, abot, PS(this).m_weapon.m_id, vbot, dh + da); + } + if (damage) + { + MUTATOR_CALLHOOK(PlayerDamaged, attacker, this, dh, da, hitloc, deathtype, damage); + } + + if (this.health < 1) + { + float defer_ClientKill_Now_TeamChange; + defer_ClientKill_Now_TeamChange = false; + + if(this.alivetime) + { + PS_GR_P_ADDVAL(this, PLAYERSTATS_ALIVETIME, time - this.alivetime); + this.alivetime = 0; + } + + if(valid_damage_for_weaponstats) + WeaponStats_LogKill(awep.m_id, abot, PS(this).m_weapon.m_id, vbot); + + if(autocvar_sv_gentle < 1) + if(sound_allowed(MSG_BROADCAST, attacker)) + { + if(deathtype == DEATH_DROWN.m_id) + PlayerSound(this, playersound_drown, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + else + PlayerSound(this, playersound_death, CH_PAIN, VOL_BASE, VOICETYPE_PLAYERSOUND); + } + + // get rid of kill indicator + if(this.killindicator) + { + delete(this.killindicator); + this.killindicator = NULL; + if(this.killindicator_teamchange) + defer_ClientKill_Now_TeamChange = true; + + if(this.classname == "body") + if(deathtype == DEATH_KILL.m_id) + { + // for the lemmings fans, a small harmless explosion + Send_Effect(EFFECT_ROCKET_EXPLODE, this.origin, '0 0 0', 1); + } + } + + // print an obituary message + if(this.classname != "body") + Obituary (attacker, inflictor, this, deathtype); + + // increment frag counter for used weapon type + Weapon w = DEATH_WEAPONOF(deathtype); + if(w != WEP_Null && accuracy_isgooddamage(attacker, this)) + attacker.accuracy.(accuracy_frags[w.m_id-1]) += 1; + + MUTATOR_CALLHOOK(PlayerDies, inflictor, attacker, this, deathtype, damage); + excess = M_ARGV(4, float); + + Weapon wep = PS(this).m_weapon; + /*for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + wep.wr_playerdeath(wep, this, weaponentity); + }*/ + .entity weaponentity = weaponentities[0]; // TODO: unhardcode + wep.wr_playerdeath(wep, this, weaponentity); + + RemoveGrapplingHook(this); + + Portal_ClearAllLater(this); + + this.fixangle = true; + + if(defer_ClientKill_Now_TeamChange) + ClientKill_Now_TeamChange(this); // can turn player into spectator + + // 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")) + return; + + // 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 + + // clear waypoints + WaypointSprite_PlayerDead(this); + // throw a weapon + SpawnThrownWeapon(this, this.origin + (this.mins + this.maxs) * 0.5, PS(this).m_switchweapon.m_id); + + // become fully visible + this.alpha = default_player_alpha; + // make the corpse upright (not tilted) + this.angles_x = 0; + this.angles_z = 0; + // don't spin + this.avelocity = '0 0 0'; + // view from the floor + this.view_ofs = '0 0 -8'; + // toss the corpse + set_movetype(this, MOVETYPE_TOSS); + // shootable corpse + this.solid = SOLID_CORPSE; + this.ballistics_density = autocvar_g_ballistics_density_corpse; + // don't stick to the floor + UNSET_ONGROUND(this); + // dying animation + this.deadflag = DEAD_DYING; + + // when to allow respawn + calculate_player_respawn_time(this); + + this.death_time = time; + if (random() < 0.5) + animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD1, true); + else + animdecide_setstate(this, this.anim_state | ANIMSTATE_DEAD2, true); + if (this.maxs.z > 5) + { + this.maxs_z = 5; + setsize(this, this.mins, this.maxs); + } + // set damage function to corpse damage + this.event_damage = PlayerCorpseDamage; + // call the corpse damage function just in case it wants to gib + this.event_damage(this, inflictor, attacker, excess, deathtype, hitloc, force); + + // set up to fade out later + SUB_SetFade (this, time + 6 + random (), 1); + // reset body think wrapper broken by SUB_SetFade + if(this.classname == "body" && getthink(this) != CopyBody_Think) { + this.CopyBody_think = getthink(this); + this.CopyBody_nextthink = this.nextthink; + setthink(this, CopyBody_Think); + this.nextthink = time; + } + + if(autocvar_sv_gentle > 0 || autocvar_ekg || this.classname == "body") { + // remove corpse + // clones don't run any animation code any more, so we must gib them when they die :( + PlayerCorpseDamage(this, inflictor, attacker, autocvar_sv_gibhealth+1.0, deathtype, hitloc, force); + } + + // reset fields the weapons may use just in case + FOREACH(Weapons, it != WEP_Null, LAMBDA( + it.wr_resetplayer(it, this); + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + ATTACK_FINISHED_FOR(this, it.m_id, slot) = 0; + } + )); + } +} + +void MoveToTeam(entity client, int team_colour, int type) +{ + int lockteams_backup = lockteams; // backup any team lock + lockteams = 0; // disable locked teams + TeamchangeFrags(client); // move the players frags + SetPlayerColors(client, team_colour - 1); // set the players colour + Damage(client, client, client, 100000, DEATH_AUTOTEAMCHANGE.m_id, client.origin, '0 0 0'); // kill the player + lockteams = lockteams_backup; // restore the team lock + LogTeamchange(client.playerid, client.team, type); +} + +/** print(), but only print if the server is not local */ +void dedicated_print(string input) +{ + if (server_is_dedicated) print(input); +} + +/** + * 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!) + + 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(gameover) + 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 = autocvar_g_chat_teamcolors ? playername(source) : source.netname; + + 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); + if(autocvar_g_chat_teamcolors) + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", playername(privatesay), ": ^7"); + else + privatemsgprefix = strcat("\{1}\{13}* ^3You tell ", privatesay.netname, ": ^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) + { + 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 (!gameover) + if (teamsay || (autocvar_g_chat_nospectators == 1) || (autocvar_g_chat_nospectators == 2 && !warmup_stage)) + teamsay = -1; // spectators + } + + if(flood) + LOG_INFO("NOTE: ", playername(source), "^7 is flooding.\n"); + + // 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.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; + } + + 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 && source.active_minigame ) + { + sprint(source, sourcemsgstr); + dedicated_print(msgstr); // send to server console too + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && it.active_minigame == 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), "^7: ", msgin)); + } + FOREACH_CLIENT(IS_REAL_CLIENT(it) && it != source && !MUTATOR_CALLHOOK(ChatMessageTo, it, source), sprint(it, msgstr)); + } + } + + return ret; +} diff --git a/qcsrc/server/player.qh b/qcsrc/server/player.qh new file mode 100644 index 0000000000..3e2b860162 --- /dev/null +++ b/qcsrc/server/player.qh @@ -0,0 +1,41 @@ +#pragma once + +.entity pusher; +.float pushltime; +.float 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); + +void player_setupanimsformodel(entity this); + +void player_anim(entity this); + +void PlayerCorpseDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +// g__str: +// If 0, default is used. +// If <0, 0 is used. +// Otherwise, g_str (default value) is used. +// For consistency, negative values there are mapped to zero too. +#define GAMETYPE_DEFAULTED_SETTING(str) \ + ((gametype_setting_tmp = cvar(strcat("g_", GetGametype(), "_" #str))), \ + (gametype_setting_tmp < 0) ? 0 \ + : (gametype_setting_tmp == 0 || autocvar_g_respawn_delay_forced) ? max(0, autocvar_g_##str) \ + : gametype_setting_tmp) + +void calculate_player_respawn_time(entity this); + +void ClientKill_Now_TeamChange(entity this); + +void MoveToTeam(entity client, float team_colour, float type); + +void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, int deathtype, vector hitloc, vector force); + +/** to be used by `prvm_edictset server playernumber muted 1` */ +.float muted; +int Say(entity source, float teamsay, entity privatesay, string msgin, float floodcontrol); diff --git a/qcsrc/server/playerdemo.qc b/qcsrc/server/playerdemo.qc index 61104ebe2f..34c886fa10 100644 --- a/qcsrc/server/playerdemo.qc +++ b/qcsrc/server/playerdemo.qc @@ -34,7 +34,7 @@ void playerdemo_open_read(entity this, string f) this.playerdemo_starttime = time - 1; this.playerdemo_time = stof(fgets(this.playerdemo_fh)); this.playerdemo_time += this.playerdemo_starttime; - this.movetype = MOVETYPE_NONE; + set_movetype(this, MOVETYPE_NONE); LOG_INFO("playerdemo: ", this.netname, " reading from ", f, "\n"); } void playerdemo_open_write(entity this, string f) @@ -147,9 +147,9 @@ float playerdemo_read(entity this) PLAYERDEMO_FIELDS(this, playerdemo_read_) { time = this.playerdemo_time; - WITHSELF(this, PlayerPreThink()); + PlayerPreThink(this); // not running physics though... this is just so we can run weapon stuff - WITHSELF(this, PlayerPostThink()); + PlayerPostThink(this); } this.playerdemo_time = stof(fgets(this.playerdemo_fh)); if(this.playerdemo_time == 0) diff --git a/qcsrc/server/portals.qc b/qcsrc/server/portals.qc index f6502dfd81..1656b1400e 100644 --- a/qcsrc/server/portals.qc +++ b/qcsrc/server/portals.qc @@ -1,14 +1,14 @@ #include "portals.qh" #include "g_hook.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #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/util.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" #include "../lib/warpzone/anglestransform.qh" #include "../lib/warpzone/util_server.qh" @@ -208,8 +208,8 @@ float Portal_FindSafeOrigin(entity portal) { vector o; o = portal.origin; - portal.mins = STAT(PL_MIN, NULL) - SAFERNUDGE; - portal.maxs = STAT(PL_MAX, NULL) + SAFERNUDGE; + portal.mins = PL_MIN_CONST - SAFERNUDGE; + portal.maxs = PL_MAX_CONST + SAFERNUDGE; fixedmakevectors(portal.mangle); portal.origin += 16 * v_forward; if(!move_out_of_solid(portal)) @@ -249,7 +249,7 @@ float Portal_WillHitPlane(vector eorg, vector emins, vector emaxs, vector evel, #endif } -void Portal_Touch(entity this) +void Portal_Touch(entity this, entity toucher) { vector g; @@ -258,18 +258,18 @@ void Portal_Touch(entity this) if(this.solid != SOLID_TRIGGER) return; // possibly engine bug - if(IS_PLAYER(other)) + if(IS_PLAYER(toucher)) return; // handled by think #endif - if(other.classname == "item_flag_team") + if(toucher.classname == "item_flag_team") return; // never portal these - if(other.classname == "grapplinghook") + if(toucher.classname == "grapplinghook") return; // handled by think if(!autocvar_g_vehicles_teleportable) - if(other.vehicle_flags & VHF_ISVEHICLE) + if(IS_VEHICLE(toucher)) return; // no teleporting vehicles? if(!this.enemy) @@ -283,43 +283,43 @@ void Portal_Touch(entity this) return; // only handle impacts #endif - if(other.classname == "porto") + if(toucher.classname == "porto") { - if(other.portal_id == this.portal_id) + if(toucher.portal_id == this.portal_id) return; } if(time < this.portal_activatetime) - if(other == this.aiment) + if(toucher == this.aiment) { this.portal_activatetime = time + 0.1; return; } - if(other != this.aiment) - if(IS_PLAYER(other)) - if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(this.aiment)) + if(toucher != this.aiment) + if(IS_PLAYER(toucher)) + if(IS_INDEPENDENT_PLAYER(toucher) || IS_INDEPENDENT_PLAYER(this.aiment)) return; // cannot go through someone else's portal - if(other.aiment != this.aiment) - if(IS_PLAYER(other.aiment)) - if(IS_INDEPENDENT_PLAYER(other.aiment) || IS_INDEPENDENT_PLAYER(this.aiment)) + if(toucher.aiment != this.aiment) + if(IS_PLAYER(toucher.aiment)) + if(IS_INDEPENDENT_PLAYER(toucher.aiment) || IS_INDEPENDENT_PLAYER(this.aiment)) return; // cannot go through someone else's portal fixedmakevectors(this.mangle); g = frametime * '0 0 -1' * autocvar_sv_gravity; - if(!Portal_WillHitPlane(other.origin, other.mins, other.maxs, other.velocity + g, this.origin, v_forward, this.maxs.x)) + if(!Portal_WillHitPlane(toucher.origin, toucher.mins, toucher.maxs, toucher.velocity + g, this.origin, v_forward, this.maxs.x)) return; /* - if(other.mins_x < PL_MIN.x || other.mins_y < PL_MIN.y || other.mins_z < PL_MIN.z - || other.maxs_x > PL_MAX.x || other.maxs_y > PL_MAX.y || other.maxs_z > PL_MAX.z) + if(toucher.mins_x < PL_MIN.x || toucher.mins_y < PL_MIN.y || toucher.mins_z < PL_MIN.z + || toucher.maxs_x > PL_MAX.x || toucher.maxs_y > PL_MAX.y || toucher.maxs_z > PL_MAX.z) { // can't teleport this return; } */ - if(Portal_TeleportPlayer(this, other)) - if(other.classname == "porto") - if(other.effects & EF_RED) - other.effects += EF_BLUE - EF_RED; + if(Portal_TeleportPlayer(this, toucher)) + if(toucher.classname == "porto") + if(toucher.effects & EF_RED) + toucher.effects += EF_BLUE - EF_RED; } void Portal_Think(entity this); @@ -418,7 +418,7 @@ void Portal_Remove(entity portal, float killed) fixedmakevectors(portal.mangle); sound(portal, CH_SHOTS, SND_PORTO_EXPLODE, VOL_BASE, ATTEN_NORM); Send_Effect(EFFECT_ROCKET_EXPLODE, portal.origin + v_forward * 16, v_forward * 1024, 4); - remove(portal); + delete(portal); } else { @@ -494,15 +494,15 @@ void Portal_Think(entity this) Portal_Remove(this, 0); } -float Portal_Customize(entity this) +bool Portal_Customize(entity this, entity client) { - if(IS_SPEC(other)) - other = other.enemy; - if(other == this.aiment) + if(IS_SPEC(client)) + client = client.enemy; + if(client == this.aiment) { this.modelindex = this.savemodelindex; } - else if(IS_INDEPENDENT_PLAYER(other) || IS_INDEPENDENT_PLAYER(this.aiment)) + else if(IS_INDEPENDENT_PLAYER(client) || IS_INDEPENDENT_PLAYER(this.aiment)) { this.modelindex = 0; } @@ -642,7 +642,7 @@ entity Portal_Spawn(entity own, vector org, vector ang) if(!Portal_FindSafeOrigin(portal)) { - remove(portal); + delete(portal); return NULL; } diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index 0396dee6cd..1a8ada45fe 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -1,28 +1,11 @@ -#ifndef DEBUGPATHING - #define DEBUGPATHING 0 -#endif - #include -#include "_all.qh" - -#include "../server/_mod.inc" -#include "bot/_mod.inc" -#include "bot/havocbot/_mod.inc" -#include "command/_mod.inc" -#include "mutators/_mod.inc" -#include "pathlib/_all.inc" -#include "weapons/_mod.inc" -#include -#include +#if XONOTIC +#include -#include - -#include -#include -#include -#include +#include +#endif -#if BUILD_MOD -#include "../../mod/server/progs.inc" +#ifdef BUILD_MOD +#include #endif diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index c4e94cd13d..a3cbc685f2 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -1,15 +1,15 @@ #include "race.qh" -#include "cl_client.qh" +#include "client.qh" #include "portals.qh" #include "scores.qh" #include "spawnpoints.qh" -#include "bot/waypoints.qh" -#include "bot/navigation.qh" +#include "bot/api.qh" #include "command/getreplies.qh" #include "../common/deathtypes/all.qh" #include "../common/notifications/all.qh" #include "../common/mapinfo.qh" +#include #include "../common/triggers/subs.qh" #include "../lib/warpzone/util_server.qh" #include "../lib/warpzone/common.qh" @@ -698,10 +698,10 @@ void checkpoint_passed(entity this, entity player) } } -void checkpoint_touch(entity this) +void checkpoint_touch(entity this, entity toucher) { - EXACTTRIGGER_TOUCH; - checkpoint_passed(this, other); + EXACTTRIGGER_TOUCH(this, toucher); + checkpoint_passed(this, toucher); } void checkpoint_use(entity this, entity actor, entity trigger) @@ -833,15 +833,14 @@ void trigger_race_checkpoint_verify(entity this) race_timed_checkpoint = 255; } } else { - for (entity cp = NULL; (cp = find(cp, classname, "trigger_race_checkpoint")); ) { - if (cp.sprite) { - if (cp.race_checkpoint == 0) { - WaypointSprite_UpdateSprites(cp.sprite, WP_RaceStart, WP_Null, WP_Null); - } else if (cp.race_checkpoint == race_timed_checkpoint) { - WaypointSprite_UpdateSprites(cp.sprite, WP_RaceFinish, WP_Null, WP_Null); - } + IL_EACH(g_racecheckpoints, it.sprite, + { + if (it.race_checkpoint == 0) { + WaypointSprite_UpdateSprites(it.sprite, WP_RaceStart, WP_Null, WP_Null); + } else if (it.race_checkpoint == race_timed_checkpoint) { + WaypointSprite_UpdateSprites(it.sprite, WP_RaceFinish, WP_Null, WP_Null); } - } + }); } } @@ -905,7 +904,7 @@ vector trigger_race_checkpoint_spawn_evalfunc(entity this, entity player, entity spawnfunc(trigger_race_checkpoint) { vector o; - if(!g_race && !g_cts) { remove(this); return; } + if(!g_race && !g_cts) { delete(this); return; } EXACTTRIGGER_INIT; @@ -914,7 +913,7 @@ spawnfunc(trigger_race_checkpoint) settouch(this, checkpoint_touch); o = (this.absmin + this.absmax) * 0.5; - tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this); + tracebox(o, PL_MIN_CONST, PL_MAX_CONST, o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this); waypoint_spawnforitem_force(this, trace_endpos); this.nearestwaypointtimeout = time + 1000000000; @@ -947,25 +946,36 @@ spawnfunc(trigger_race_checkpoint) this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player; this.spawn_evalfunc = trigger_race_checkpoint_spawn_evalfunc; + IL_PUSH(g_racecheckpoints, this); + InitializeEntity(this, trigger_race_checkpoint_verify, INITPRIO_FINDTARGET); } spawnfunc(target_checkpoint) // defrag entity { - vector o; - if(!g_race && !g_cts) { remove(this); return; } + if(!g_race && !g_cts) { delete(this); return; } defrag_ents = 1; - EXACTTRIGGER_INIT; + // if this is targeted, then it probably isn't a trigger + bool is_trigger = !boolean(!this.nottargeted && this.targetname != ""); + + if(is_trigger) + EXACTTRIGGER_INIT; this.use = checkpoint_use; - if (!(this.spawnflags & 1)) + if (is_trigger && !(this.spawnflags & 1)) settouch(this, checkpoint_touch); - o = (this.absmin + this.absmax) * 0.5; - tracebox(o, STAT(PL_MIN, NULL), STAT(PL_MAX, NULL), o - '0 0 1' * (o.z - this.absmin.z), MOVE_NORMAL, this); - waypoint_spawnforitem_force(this, trace_endpos); - this.nearestwaypointtimeout = time + 1000000000; + vector org = this.origin; + + // bots should only pathfind to this if it is a valid touchable trigger + if(is_trigger) + { + org = (this.absmin + this.absmax) * 0.5; + tracebox(org, PL_MIN_CONST, PL_MAX_CONST, org - '0 0 1' * (org.z - this.absmin.z), MOVE_NORMAL, this); + waypoint_spawnforitem_force(this, trace_endpos); + this.nearestwaypointtimeout = time + 1000000000; + } if(this.message == "") this.message = "went backwards"; @@ -982,9 +992,9 @@ spawnfunc(target_checkpoint) // defrag entity race_timed_checkpoint = 1; if(this.race_checkpoint == 0) - WaypointSprite_SpawnFixed(WP_RaceStart, o, this, sprite, RADARICON_NONE); + WaypointSprite_SpawnFixed(WP_RaceStart, org, this, sprite, RADARICON_NONE); else - WaypointSprite_SpawnFixed(WP_RaceCheckpoint, o, this, sprite, RADARICON_NONE); + WaypointSprite_SpawnFixed(WP_RaceCheckpoint, org, this, sprite, RADARICON_NONE); this.sprite.waypointsprite_visible_for_player = race_waypointsprite_visible_for_player; @@ -1031,7 +1041,7 @@ void race_RetractPlayer(entity this) spawnfunc(info_player_race) { - if(!g_race && !g_cts) { remove(this); return; } + if(!g_race && !g_cts) { delete(this); return; } ++race_spawns; spawnfunc_info_player_deathmatch(this); @@ -1092,13 +1102,13 @@ void race_ImposePenaltyTime(entity pl, float penalty, string reason) } } -void penalty_touch(entity this) +void penalty_touch(entity this, entity toucher) { - EXACTTRIGGER_TOUCH; - if(other.race_lastpenalty != this) + EXACTTRIGGER_TOUCH(this, toucher); + if(toucher.race_lastpenalty != this) { - other.race_lastpenalty = this; - race_ImposePenaltyTime(other, this.race_penalty, this.race_penalty_reason); + toucher.race_lastpenalty = this; + race_ImposePenaltyTime(toucher, this.race_penalty, this.race_penalty_reason); } } @@ -1143,7 +1153,7 @@ float race_GetFractionalLapCount(entity e) vector o0, o1; float bestfraction, fraction; - entity lastcp, cp0, cp1; + entity lastcp; float nextcpindex, lastcpindex; nextcpindex = max(e.race_checkpoint, 0); @@ -1154,26 +1164,26 @@ float race_GetFractionalLapCount(entity e) return l; // finish bestfraction = 1; - for(cp0 = NULL; (cp0 = find(cp0, classname, "trigger_race_checkpoint")); ) + IL_EACH(g_racecheckpoints, true, { - if(cp0.race_checkpoint != lastcpindex) + if(it.race_checkpoint != lastcpindex) continue; if(lastcp) - if(cp0 != lastcp) + if(it != lastcp) continue; - o0 = (cp0.absmin + cp0.absmax) * 0.5; - for(cp1 = NULL; (cp1 = find(cp1, classname, "trigger_race_checkpoint")); ) + o0 = (it.absmin + it.absmax) * 0.5; + IL_EACH(g_racecheckpoints, true, { - if(cp1.race_checkpoint != nextcpindex) + if(it.race_checkpoint != nextcpindex) continue; - o1 = (cp1.absmin + cp1.absmax) * 0.5; + o1 = (it.absmin + it.absmax) * 0.5; if(o0 == o1) continue; fraction = bound(0.0001, vlen(e.origin - o1) / vlen(o0 - o1), 1); if(fraction < bestfraction) bestfraction = fraction; - } - } + }); + }); // we are at CP "nextcpindex - bestfraction" // race_timed_checkpoint == 4: then nextcp==4 means 0.9999x, nextcp==0 means 0.0000x diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh index b69b0006a9..6419afb737 100644 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@ -4,9 +4,6 @@ float race_teams; // scores const float ST_RACE_LAPS = 1; -const float SP_RACE_LAPS = 4; -const float SP_RACE_TIME = 5; -const float SP_RACE_FASTEST = 6; bool g_race_qualifying; @@ -32,6 +29,9 @@ float race_completing; .float race_respawn_checkpoint; .entity race_respawn_spotref; // try THIS spawn in case you respawn +IntrusiveList g_racecheckpoints; +STATIC_INIT(g_racecheckpoints) { g_racecheckpoints = IL_NEW(); } + // definitions for functions used outside race.qc float race_PreviousCheckpoint(float f); float race_NextCheckpoint(float f); diff --git a/qcsrc/server/round_handler.qc b/qcsrc/server/round_handler.qc index d1302a84d7..063c12abb0 100644 --- a/qcsrc/server/round_handler.qc +++ b/qcsrc/server/round_handler.qc @@ -1,5 +1,6 @@ #include "round_handler.qh" +#include "campaign.qh" #include "command/vote.qh" #include "../common/util.qh" @@ -12,6 +13,9 @@ void round_handler_Think(entity this) } if (gameover) + gameover = false; + + if (intermission_running) { round_handler_Reset(0); round_handler_Remove(); @@ -28,7 +32,7 @@ void round_handler_Think(entity this) if (this.cnt > 0) // countdown running { - if (this.canRoundStart()) + if (this.canRoundStart() && !(autocvar_g_campaign && !campaign_bots_may_start)) { if (this.cnt == this.count + 1) round_starttime = time + this.count; int f = this.cnt - 1; @@ -55,6 +59,7 @@ void round_handler_Think(entity this) // schedule a new round this.wait = true; this.nextthink = time + this.delay; + gameover = true; } else { @@ -111,6 +116,6 @@ void round_handler_Reset(float next_think) void round_handler_Remove() { - remove(round_handler); + delete(round_handler); round_handler = NULL; } diff --git a/qcsrc/server/scores.qc b/qcsrc/server/scores.qc index aa108dd985..e6264b8693 100644 --- a/qcsrc/server/scores.qc +++ b/qcsrc/server/scores.qc @@ -1,16 +1,13 @@ #include "scores.qh" #include "command/common.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" +#include #include "../common/playerstats.qh" #include "../common/teams.qh" .entity scorekeeper; entity teamscorekeepers[16]; -string scores_label[MAX_SCORE]; -float scores_flags[MAX_SCORE]; -string teamscores_label[MAX_TEAMSCORE]; -float teamscores_flags[MAX_TEAMSCORE]; float teamscores_entities_count; var .float scores_primary; var .float teamscores_primary; @@ -65,7 +62,7 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags) longflags = 0; for(i = 0, p = 1; i < MAX_TEAMSCORE; ++i, p *= 2) - if(this.(teamscores[i]) > 127 || this.(teamscores[i]) <= -128) + if(this.(teamscores(i)) > 127 || this.(teamscores(i)) <= -128) longflags |= p; #if MAX_TEAMSCORE <= 8 @@ -79,9 +76,9 @@ bool TeamScore_SendEntity(entity this, entity to, float sendflags) if(sendflags & p) { if(longflags & p) - WriteInt24_t(MSG_ENTITY, this.(teamscores[i])); + WriteInt24_t(MSG_ENTITY, this.(teamscores(i))); else - WriteChar(MSG_ENTITY, this.(teamscores[i])); + WriteChar(MSG_ENTITY, this.(teamscores(i))); } return true; @@ -120,9 +117,9 @@ float TeamScore_AddToTeam(float t, float scorefield, float score) error("Adding score to unknown team!"); } if(score) - if(teamscores_label[scorefield] != "") + if(teamscores_label(scorefield) != "") s.SendFlags |= pow(2, scorefield); - return (s.(teamscores[scorefield]) += score); + return (s.(teamscores(scorefield)) += score); } float TeamScore_Add(entity player, float scorefield, float score) @@ -139,8 +136,8 @@ float TeamScore_Compare(entity t1, entity t2, float strict) for(i = 0; i < MAX_TEAMSCORE; ++i) { var .float f; - f = teamscores[i]; - result = ScoreField_Compare(t1, t2, f, teamscores_flags[i], result, strict); + f = teamscores(i); + result = ScoreField_Compare(t1, t2, f, teamscores_flags(i), result, strict); } if (result.x == 0 && strict) @@ -153,13 +150,13 @@ float TeamScore_Compare(entity t1, entity t2, float strict) * the scoreinfo entity */ -void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags) +void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags) { - scores_label[i] = label; - scores_flags[i] = scoreflags; + scores_label(i) = label; + scores_flags(i) = scoreflags; if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) { - scores_primary = scores[i]; + scores_primary = scores(i); scores_flags_primary = scoreflags; } if(label != "") @@ -171,11 +168,11 @@ void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags) void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags) { - teamscores_label[i] = label; - teamscores_flags[i] = scoreflags; + teamscores_label(i) = label; + teamscores_flags(i) = scoreflags; if((scoreflags & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) { - teamscores_primary = teamscores[i]; + teamscores_primary = teamscores(i); teamscores_flags_primary = scoreflags; } if(label != "") @@ -189,21 +186,20 @@ bool ScoreInfo_SendEntity(entity this, entity to, int sf) { float i; WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES_INFO); - WriteInt24_t(MSG_ENTITY, MapInfo_LoadedGametype); - for(i = 0; i < MAX_SCORE; ++i) - { - WriteString(MSG_ENTITY, scores_label[i]); - WriteByte(MSG_ENTITY, scores_flags[i]); - } + WriteRegistered(Gametypes, MSG_ENTITY, MapInfo_LoadedGametype); + FOREACH(Scores, true, { + WriteString(MSG_ENTITY, scores_label(it)); + WriteByte(MSG_ENTITY, scores_flags(it)); + }); for(i = 0; i < MAX_TEAMSCORE; ++i) { - WriteString(MSG_ENTITY, teamscores_label[i]); - WriteByte(MSG_ENTITY, teamscores_flags[i]); + WriteString(MSG_ENTITY, teamscores_label(i)); + WriteByte(MSG_ENTITY, teamscores_flags(i)); } return true; } -void ScoreInfo_Init(float teams) +void ScoreInfo_Init(int teams) { if(scores_initialized) { @@ -214,13 +210,13 @@ void ScoreInfo_Init(float teams) scores_initialized = new_pure(ent_client_scoreinfo); Net_LinkEntity(scores_initialized, false, 0, ScoreInfo_SendEntity); } - if(teams >= 1) + if(teams & BIT(0)) TeamScore_Spawn(NUM_TEAM_1, "Red"); - if(teams >= 2) + if(teams & BIT(1)) TeamScore_Spawn(NUM_TEAM_2, "Blue"); - if(teams >= 3) + if(teams & BIT(2)) TeamScore_Spawn(NUM_TEAM_3, "Yellow"); - if(teams >= 4) + if(teams & BIT(3)) TeamScore_Spawn(NUM_TEAM_4, "Pink"); } @@ -230,31 +226,28 @@ void ScoreInfo_Init(float teams) bool PlayerScore_SendEntity(entity this, entity to, float sendflags) { - float i, p, longflags; - WriteHeader(MSG_ENTITY, ENT_CLIENT_SCORES); WriteByte(MSG_ENTITY, etof(this.owner)); - longflags = 0; - for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) - if(this.(scores[i]) > 127 || this.(scores[i]) <= -128) + int longflags = 0; + FOREACH(Scores, true, { + int p = 1 << (i % 16); + if (this.(scores(it)) > 127 || this.(scores(it)) <= -128) longflags |= p; + }); -#if MAX_SCORE <= 8 - WriteByte(MSG_ENTITY, sendflags); - WriteByte(MSG_ENTITY, longflags); -#else WriteShort(MSG_ENTITY, sendflags); WriteShort(MSG_ENTITY, longflags); -#endif - for(i = 0, p = 1; i < MAX_SCORE; ++i, p *= 2) - if(sendflags & p) + FOREACH(Scores, true, { + int p = 1 << (i % 16); + if (sendflags & p) { if(longflags & p) - WriteInt24_t(MSG_ENTITY, this.(scores[i])); + WriteInt24_t(MSG_ENTITY, this.(scores(it))); else - WriteChar(MSG_ENTITY, this.(scores[i])); + WriteChar(MSG_ENTITY, this.(scores(it))); } + }); return true; } @@ -262,7 +255,6 @@ bool PlayerScore_SendEntity(entity this, entity to, float sendflags) float PlayerScore_Clear(entity player) { entity sk; - float i; if(teamscores_entities_count) return 0; @@ -270,13 +262,12 @@ float PlayerScore_Clear(entity player) if(MUTATOR_CALLHOOK(ForbidPlayerScore_Clear)) return 0; sk = player.scorekeeper; - for(i = 0; i < MAX_SCORE; ++i) - { - if(sk.(scores[i]) != 0) - if(scores_label[i] != "") - sk.SendFlags |= pow(2, i); - sk.(scores[i]) = 0; - } + FOREACH(Scores, true, { + if(sk.(scores(it)) != 0) + if(scores_label(it) != "") + sk.SendFlags |= pow(2, i % 16); + sk.(scores(it)) = 0; + }); return 1; } @@ -285,18 +276,15 @@ void Score_ClearAll() { entity sk; float t; - FOREACH_CLIENTSLOT(true, - { + FOREACH_CLIENTSLOT(true, { sk = it.scorekeeper; - if(!sk) - continue; - for(int j = 0; j < MAX_SCORE; ++j) - { - if(sk.(scores[j]) != 0) - if(scores_label[j] != "") - sk.SendFlags |= pow(2, j); - sk.(scores[j]) = 0; - } + if (!sk) continue; + FOREACH(Scores, true, { + if(sk.(scores(it)) != 0) + if(scores_label(it) != "") + sk.SendFlags |= pow(2, i % 16); + sk.(scores(it)) = 0; + }); }); for(t = 0; t < 16; ++t) { @@ -305,10 +293,10 @@ void Score_ClearAll() continue; for(int j = 0; j < MAX_TEAMSCORE; ++j) { - if(sk.(teamscores[j]) != 0) - if(teamscores_label[j] != "") + if(sk.(teamscores(j)) != 0) + if(teamscores_label(j) != "") sk.SendFlags |= pow(2, j); - sk.(teamscores[j]) = 0; + sk.(teamscores(j)) = 0; } } } @@ -327,11 +315,11 @@ void PlayerScore_Detach(entity player) { if(!player.scorekeeper) error("player has no scorekeeper"); - remove(player.scorekeeper); + delete(player.scorekeeper); player.scorekeeper = NULL; } -float PlayerScore_Add(entity player, float scorefield, float score) +float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score) { bool mutator_returnvalue = MUTATOR_CALLHOOK(AddPlayerScore, scorefield, score, player); score = M_ARGV(1, float); @@ -346,18 +334,18 @@ float PlayerScore_Add(entity player, float scorefield, float score) { if(gameover) return 0; - LOG_WARNING("Adding score to unknown player!"); + LOG_WARN("Adding score to unknown player!"); return 0; } if(score) - if(scores_label[scorefield] != "") - s.SendFlags |= pow(2, scorefield); + if(scores_label(scorefield) != "") + s.SendFlags |= pow(2, scorefield.m_id % 16); if(!warmup_stage) - PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label[scorefield]), score); - return (s.(scores[scorefield]) += score); + PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_TOTAL, scores_label(scorefield)), score); + return (s.(scores(scorefield)) += score); } -float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score) +float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score) { float r; r = PlayerScore_Add(player, pscorefield, score); @@ -371,13 +359,10 @@ float PlayerScore_Compare(entity t1, entity t2, float strict) if(!t1 || !t2) return (!t2) - !t1; vector result = '0 0 0'; - float i; - for(i = 0; i < MAX_SCORE; ++i) - { - var .float f; - f = scores[i]; - result = ScoreField_Compare(t1, t2, f, scores_flags[i], result, strict); - } + FOREACH(Scores, true, { + var .float f = scores(it); + result = ScoreField_Compare(t1, t2, f, scores_flags(it), result, strict); + }); if (result.x == 0 && strict) result.x = etof(t1.owner) - etof(t2.owner); @@ -562,53 +547,59 @@ string GetPlayerScoreString(entity pl, float shortString) { string out; entity sk; - float i, f; + float f; string l; out = ""; if(!pl) { // label - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) + FOREACH(Scores, true, { + if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) { - f = scores_flags[i]; - l = scores_label[i]; + f = scores_flags(it); + l = scores_label(it); out = strcat(out, GetScoreLogLabel(l, f), ","); } + }); if(shortString < 2) - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) + FOREACH(Scores, true, { + if((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) { - f = scores_flags[i]; - l = scores_label[i]; + f = scores_flags(it); + l = scores_label(it); out = strcat(out, GetScoreLogLabel(l, f), ","); } + }); if(shortString < 1) - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) + FOREACH(Scores, true, { + if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) + if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) { - f = scores_flags[i]; - l = scores_label[i]; + f = scores_flags(it); + l = scores_label(it); out = strcat(out, GetScoreLogLabel(l, f), ","); } + }); out = substring(out, 0, strlen(out) - 1); } else if((sk = pl.scorekeeper)) { - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) - out = strcat(out, ftos(sk.(scores[i])), ","); + FOREACH(Scores, true, { + if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) + out = strcat(out, ftos(sk.(scores(it))), ","); + }); if(shortString < 2) - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) - out = strcat(out, ftos(sk.(scores[i])), ","); + FOREACH(Scores, true, { + if ((scores_flags(it) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) + out = strcat(out, ftos(sk.(scores(it))), ","); + }); if(shortString < 1) - for(i = 0; i < MAX_SCORE; ++i) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) - if((scores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) - out = strcat(out, ftos(sk.(scores[i])), ","); + FOREACH(Scores, true, { + if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) + if((scores_flags(it) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) + out = strcat(out, ftos(sk.(scores(it))), ","); + }); out = substring(out, 0, strlen(out) - 1); } return out; @@ -626,27 +617,27 @@ string GetTeamScoreString(float tm, float shortString) { // label for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) { - f = teamscores_flags[i]; - l = teamscores_label[i]; + f = teamscores_flags(i); + l = teamscores_label(i); out = strcat(out, GetScoreLogLabel(l, f), ","); } if(shortString < 2) for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) { - f = teamscores_flags[i]; - l = teamscores_label[i]; + f = teamscores_flags(i); + l = teamscores_label(i); out = strcat(out, GetScoreLogLabel(l, f), ","); } if(shortString < 1) for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) { - f = teamscores_flags[i]; - l = teamscores_label[i]; + f = teamscores_flags(i); + l = teamscores_label(i); out = strcat(out, GetScoreLogLabel(l, f), ","); } out = substring(out, 0, strlen(out) - 1); @@ -654,17 +645,17 @@ string GetTeamScoreString(float tm, float shortString) else if((sk = teamscorekeepers[tm - 1])) { for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) - out = strcat(out, ftos(sk.(teamscores[i])), ","); + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_PRIMARY) + out = strcat(out, ftos(sk.(teamscores(i))), ","); if(shortString < 2) for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) - out = strcat(out, ftos(sk.(teamscores[i])), ","); + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) == SFL_SORT_PRIO_SECONDARY) + out = strcat(out, ftos(sk.(teamscores(i))), ","); if(shortString < 1) for(i = 0; i < MAX_TEAMSCORE; ++i) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) - if((teamscores_flags[i] & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) - out = strcat(out, ftos(sk.(teamscores[i])), ","); + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_PRIMARY) + if((teamscores_flags(i) & SFL_SORT_PRIO_MASK) != SFL_SORT_PRIO_SECONDARY) + out = strcat(out, ftos(sk.(teamscores(i))), ","); out = substring(out, 0, strlen(out) - 1); } return out; @@ -799,10 +790,10 @@ void Score_NicePrint_Team(entity to, float t, float w) { s = strcat(s, Team_ColoredFullName(t)); for(i = 0; i < MAX_TEAMSCORE; ++i) - if(teamscores_label[i] != "") + if(teamscores_label(i) != "") { - fl = teamscores_flags[i]; - sc = sk.(teamscores[i]); + fl = teamscores_flags(i); + sc = sk.(teamscores(i)); s = strcat(s, " ", Score_NicePrint_ItemColor(fl), ScoreString(fl, sc)); } } @@ -811,13 +802,14 @@ void Score_NicePrint_Team(entity to, float t, float w) s = strcat(s, strpad(max(0, NAMEWIDTH - strlennocol(s)), "")); - for(i = 0; i < MAX_SCORE; ++i) - if(scores_label[i] != "") + FOREACH(Scores, true, { + if(scores_label(it) != "") { - fl = scores_flags[i]; - s2 = scores_label[i]; + fl = scores_flags(it); + s2 = scores_label(it); s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, substring(s2, 0, w))); } + }); print_to(to, s); } @@ -845,13 +837,14 @@ void Score_NicePrint_Player(entity to, entity p, float w) } } - for(i = 0; i < MAX_SCORE; ++i) - if(scores_label[i] != "") + FOREACH(Scores, true, { + if(scores_label(it) != "") { - fl = scores_flags[i]; - sc = sk.(scores[i]); + fl = scores_flags(it); + sc = sk.(scores(it)); s = strcat(s, " ", Score_NicePrint_ItemColor(fl), strpad(-w, ScoreString(fl, sc))); } + }); print_to(to, s); } @@ -870,13 +863,13 @@ void Score_NicePrint_Spectator(entity to, entity p) void Score_NicePrint(entity to) { entity p; - float i; float w; int t = 0; - for(i = 0; i < MAX_SCORE; ++i) - if(scores_label[i] != "") + FOREACH(Scores, true, { + if(scores_label(it) != "") ++t; + }); w = bound(6, floor(SCORESWIDTH / t - 1), 9); p = PlayerScore_Sort(score_dummyfield, 1, 1, 0); @@ -905,14 +898,12 @@ void Score_NicePrint(entity to) void PlayerScore_PlayerStats(entity p) { - entity s; - float i; - s = p.scorekeeper; - - for(i = 0; i < MAX_SCORE; ++i) - if(s.(scores[i]) != 0) - if(scores_label[i] != "") - PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label[i]), s.(scores[i])); + entity s = p.scorekeeper; + FOREACH(Scores, true, { + if(s.(scores(it)) != 0) + if(scores_label(it) != "") + PS_GR_P_ADDVAL(s.owner, strcat(PLAYERSTATS_SCOREBOARD, scores_label(it)), s.(scores(it))); + }); } void PlayerScore_TeamStats() @@ -925,9 +916,9 @@ void PlayerScore_TeamStats() if(!sk) continue; for(i = 0; i < MAX_TEAMSCORE; ++i) - if(sk.(teamscores[i]) != 0) - if(teamscores_label[i] != "") + if(sk.(teamscores(i)) != 0) + if(teamscores_label(i) != "") // the +1 is important here! - PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label[i]), sk.(teamscores[i])); + PS_GR_T_ADDVAL(t+1, strcat(PLAYERSTATS_SCOREBOARD, teamscores_label(i)), sk.(teamscores(i))); } } diff --git a/qcsrc/server/scores.qh b/qcsrc/server/scores.qh index a462bde92c..cf76765905 100644 --- a/qcsrc/server/scores.qh +++ b/qcsrc/server/scores.qh @@ -3,8 +3,6 @@ #include entity scores_initialized; // non-NULL when scores labels/rules have been set -.float scores[MAX_SCORE]; -.float teamscores[MAX_TEAMSCORE]; .float scoreboard_pos; /** @@ -24,7 +22,7 @@ void PlayerScore_Detach(entity player); * Means: FIXME make players unable to join the game when not called ClientConnect yet. * Returns the new score. */ -float PlayerScore_Add(entity player, float scorefield, float score); +float PlayerScore_Add(entity player, PlayerScoreField scorefield, float score); /** * Initialize the score of this player if needed. @@ -57,12 +55,12 @@ float TeamScore_GetCompareValue(float t); * Adds a score to both the player and the team. Returns the team score if * possible, otherwise the player score. */ -float PlayerTeamScore_Add(entity player, float pscorefield, float tscorefield, float score); +float PlayerTeamScore_Add(entity player, PlayerScoreField pscorefield, float tscorefield, float score); /** * Adds to the generic score fields for both the player and the team. */ -#define PlayerTeamScore_AddScore(p,s) PlayerTeamScore_Add(p, SP_SCORE, ST_SCORE, s) +#define PlayerTeamScore_AddScore(p, s) PlayerTeamScore_Add(p, SP_SCORE, ST_SCORE, s) /** * Set the label of a team score item, as well as the scoring flags. @@ -72,7 +70,7 @@ void ScoreInfo_SetLabel_TeamScore(float i, string label, float scoreflags); /** * Set the label of a player score item, as well as the scoring flags. */ -void ScoreInfo_SetLabel_PlayerScore(float i, string label, float scoreflags); +void ScoreInfo_SetLabel_PlayerScore(PlayerScoreField i, string label, float scoreflags); /** * Initialize the scores info for the given number of teams. diff --git a/qcsrc/server/scores_rules.qc b/qcsrc/server/scores_rules.qc index 72eeb9a1cc..9c416472f4 100644 --- a/qcsrc/server/scores_rules.qc +++ b/qcsrc/server/scores_rules.qc @@ -1,19 +1,32 @@ #include "scores_rules.qh" -#include "cl_client.qh" +#include "client.qh" #include "scores.qh" +int ScoreRules_teams; + void CheckAllowedTeams (entity for_whom); -// NOTE: SP_ constants may not be >= MAX_SCORE; ST_constants may not be >= MAX_TEAMSCORE +int NumTeams(int teams) +{ + return boolean(teams & BIT(0)) + boolean(teams & BIT(1)) + boolean(teams & BIT(2)) + boolean(teams & BIT(3)); +} + +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 // scores that should be in all modes: -float ScoreRules_teams; -void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled) +void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled) { - float i; - for(i = 0; i < MAX_SCORE; ++i) - ScoreInfo_SetLabel_PlayerScore(i, "", 0); - for(i = 0; i < MAX_TEAMSCORE; ++i) + FOREACH(Scores, true, { + ScoreInfo_SetLabel_PlayerScore(it, "", 0); + }); + for(int i = 0; i < MAX_TEAMSCORE; ++i) ScoreInfo_SetLabel_TeamScore(i, "", 0); ScoreRules_teams = teams; @@ -32,8 +45,9 @@ void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabl if(score_enabled) ScoreInfo_SetLabel_PlayerScore(SP_SCORE, "score", sprio); - ScoreInfo_SetLabel_PlayerScore(SP_DMG, "damage", 0); - ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "damagetaken", SFL_LOWER_IS_BETTER); + ScoreInfo_SetLabel_PlayerScore(SP_DMG, "dmg", 0); + ScoreInfo_SetLabel_PlayerScore(SP_DMGTAKEN, "dmgtaken", SFL_LOWER_IS_BETTER); + ScoreInfo_SetLabel_PlayerScore(SP_ELO, "elo", 0); } void ScoreRules_basics_end() { @@ -44,7 +58,12 @@ void ScoreRules_generic() if(teamplay) { CheckAllowedTeams(NULL); - ScoreRules_basics(((c4>=0) ? 4 : (c3>=0) ? 3 : 2), SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); + 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); + ScoreRules_basics(teams, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); } else ScoreRules_basics(0, SFL_SORT_PRIO_PRIMARY, SFL_SORT_PRIO_PRIMARY, true); diff --git a/qcsrc/server/scores_rules.qh b/qcsrc/server/scores_rules.qh index 1d2646bb83..9d1caf5395 100644 --- a/qcsrc/server/scores_rules.qh +++ b/qcsrc/server/scores_rules.qh @@ -1,5 +1,7 @@ #pragma once -void ScoreRules_basics(float teams, float sprio, float stprio, float score_enabled); +int NumTeams(int teams); +int AvailableTeams(); +void ScoreRules_basics(int teams, float sprio, float stprio, float score_enabled); void ScoreRules_basics_end(); void ScoreRules_generic(); diff --git a/qcsrc/server/spawnpoints.qc b/qcsrc/server/spawnpoints.qc index f55fb1122a..517bc1892c 100644 --- a/qcsrc/server/spawnpoints.qc +++ b/qcsrc/server/spawnpoints.qc @@ -1,9 +1,10 @@ #include "spawnpoints.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "g_world.qh" #include "race.qh" #include "../common/constants.qh" +#include #include "../common/teams.qh" #include "../common/triggers/subs.qh" #include "../common/util.qh" @@ -166,6 +167,7 @@ spawnfunc(info_player_start) spawnfunc(info_player_deathmatch) { this.classname = "info_player_deathmatch"; + IL_PUSH(g_spawnpoints, this); relocate_spawnpoint(this); } @@ -249,17 +251,15 @@ 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 != "") { - entity ent; - float found; - - found = 0; - for(ent = NULL; (ent = find(ent, targetname, spot.target)); ) + if(spot.target != "") + { + int found = 0; + for(entity targ = findchain(targetname, spot.target); targ; targ = targ.chain) { ++found; - if(ent.spawn_evalfunc) + if(targ.spawn_evalfunc) { - spawn_score = ent.spawn_evalfunc(ent, this, spot, spawn_score); + spawn_score = targ.spawn_evalfunc(targ, this, spot, spawn_score); if(spawn_score.x < 0) return spawn_score; } @@ -267,7 +267,7 @@ vector Spawn_Score(entity this, entity spot, float mindist, float teamcheck) if(!found) { - LOG_TRACE("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target, "\n"); + LOG_TRACE("WARNING: spawnpoint at ", vtos(spot.origin), " could not find its target ", spot.target); return '-1 0 0'; } } @@ -318,7 +318,7 @@ entity Spawn_WeightedPoint(entity firstspot, float lower, float upper, float exp RandomSelection_Init(); for(spot = firstspot; spot; spot = spot.chain) - RandomSelection_Add(spot, 0, string_null, pow(bound(lower, spot.spawnpoint_score.y, upper), exponent) * spot.cnt, (spot.spawnpoint_score.y >= lower) * 0.5 + spot.spawnpoint_score.x); + RandomSelection_AddEnt(spot, pow(bound(lower, spot.spawnpoint_score.y, upper), exponent) * spot.cnt, (spot.spawnpoint_score.y >= lower) * 0.5 + spot.spawnpoint_score.x); return RandomSelection_chosen_ent; } @@ -335,7 +335,7 @@ entity SelectSpawnPoint(entity this, bool anypoint) float teamcheck; entity spot, firstspot; - spot = find (NULL, classname, "testplayerstart"); + spot = find(NULL, classname, "testplayerstart"); if (spot) return spot; diff --git a/qcsrc/server/steerlib.qc b/qcsrc/server/steerlib.qc index bf0a955515..1544fa384a 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -7,10 +7,11 @@ /** Uniform pull towards a point **/ -vector steerlib_pull(entity this, vector point) +#define steerlib_pull(ent,point) normalize(point - (ent).origin) +/*vector steerlib_pull(entity this, vector point) { return normalize(point - this.origin); -} +}*/ /** Uniform push from a point @@ -143,7 +144,7 @@ vector steerlib_wander(entity this, float range, float tresh, vector oldpoint) vector wander_point; wander_point = v_forward - oldpoint; - if (vlen(wander_point) > tresh) + if (vdist(wander_point, >, tresh)) return oldpoint; range = bound(0,range,1); @@ -339,24 +340,30 @@ vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector v return normalize(leftwish + rightwish + frontwish); } -float beamsweep_badpoint(vector point,float waterok) +bool beamsweep_badpoint(vector point, bool waterok) { - float pc,pc2; - if(trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) - return 1; - - pc = pointcontents(point); - pc2 = pointcontents(point - '0 0 1'); - - switch(pc) + return true; + + int pc = pointcontents(point); + int pc2 = pointcontents(point - '0 0 1'); + + if(pc == CONTENT_EMPTY && pc2 == CONTENT_SOLID) + return false; + if(pc == CONTENT_EMPTY && pc2 == CONTENT_WATER && waterok) + return false; + if(pc == CONTENT_WATER && waterok) + return false; + return true; + + /*switch(pc) { case CONTENT_SOLID: break; case CONTENT_SLIME: break; case CONTENT_LAVA: break; case CONTENT_SKY: - return 1; + return true; case CONTENT_EMPTY: if (pc2 == CONTENT_SOLID) @@ -375,14 +382,14 @@ float beamsweep_badpoint(vector point,float waterok) break; } - return 1; + return true;*/ } //#define BEAMSTEER_VISUAL float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down) { float i; - vector a,b,u,d; + vector a, b, u, d; u = '0 0 1' * step_up; d = '0 0 1' * step_down; @@ -464,8 +471,8 @@ vector steerlib_beamsteer(entity this, vector dir, float length, float step, flo if(bm_left + bm_right < 0.15) { - vr = normalize((v_forward*-1) + v_right * 0.75); - vl = normalize((v_forward*-1) - v_right * 0.75); + vr = normalize((v_forward*-1) + v_right * 0.90); + vl = normalize((v_forward*-1) - v_right * 0.90); bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); @@ -556,7 +563,7 @@ void spawn_flocker(entity this) setthink(flocker, flocker_think); flocker.nextthink = time + random() * 5; PROJECTILE_MAKETRIGGER(flocker); - flocker.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(flocker, MOVETYPE_BOUNCEMISSILE); flocker.effects = EF_LOWPRECISION; flocker.velocity = randomvec() * 300; flocker.angles = vectoangles(flocker.velocity); @@ -579,8 +586,7 @@ void flockerspawn_think(entity this) void flocker_hunter_think(entity this) { vector dodgemove,attractmove,newmove; - entity e,ee; - float d,bd; + entity ee; this.angles_x = this.angles.x * -1; makevectors(this.angles); @@ -597,20 +603,14 @@ void flocker_hunter_think(entity this) if(!this.enemy) { - e = findchainfloat(flock_id,this.flock_id); - while(e) + FOREACH_ENTITY_FLOAT(flock_id, this.flock_id, { - d = vlen(this.origin - e.origin); - - if(e != this.owner) - if(e != ee) - if(d > bd) - { - this.enemy = e; - bd = d; - } - e = e.chain; - } + 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) @@ -624,7 +624,6 @@ void flocker_hunter_think(entity this) 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; } @@ -649,7 +648,7 @@ spawnfunc(flockerspawn) this.enemy.scale = 3; this.enemy.effects = EF_LOWPRECISION; - this.enemy.movetype = MOVETYPE_BOUNCEMISSILE; + set_movetype(this.enemy, MOVETYPE_BOUNCEMISSILE); PROJECTILE_MAKETRIGGER(this.enemy); setthink(this.enemy, flocker_hunter_think); this.enemy.nextthink = time + 10; diff --git a/qcsrc/server/steerlib.qh b/qcsrc/server/steerlib.qh index fa21610f04..4beb69f632 100644 --- a/qcsrc/server/steerlib.qh +++ b/qcsrc/server/steerlib.qh @@ -4,4 +4,4 @@ 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); +//vector steerlib_pull(entity this, vector point); diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index aac9c497d1..0c36a77c5b 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -4,12 +4,11 @@ #include "g_hook.qh" #include "g_world.qh" -#include "bot/bot.qh" -#include "bot/waypoints.qh" +#include "bot/api.qh" #include "command/common.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "weapons/csqcprojectile.qh" #include "../common/constants.qh" @@ -19,7 +18,7 @@ #include "../common/util.qh" #include "../common/vehicles/all.qh" -#include "../common/weapons/all.qh" +#include #include "../lib/csqcmodel/sv_model.qh" @@ -96,6 +95,7 @@ void CreatureFrame_Liquids(entity this) void CreatureFrame_FallDamage(entity this) { if(!IS_VEHICLE(this) && !(this.flags & FL_PROJECTILE)) // vehicles don't get falling damage + if(this.velocity || this.oldvelocity) // moving or has moved { // check for falling damage float velocity_len = vlen(this.velocity); @@ -117,8 +117,9 @@ void CreatureFrame_FallDamage(entity this) void CreatureFrame_All() { - FOREACH_ENTITY_FLOAT(damagedbycontents, true, { - if (it.movetype == MOVETYPE_NOCLIP) continue; + IL_EACH(g_damagedbycontents, it.damagedbycontents, + { + if (it.move_movetype == MOVETYPE_NOCLIP) continue; CreatureFrame_Liquids(it); CreatureFrame_FallDamage(it); it.oldvelocity = it.velocity; @@ -152,19 +153,20 @@ Called before each frame by the server float game_delay; float game_delay_last; -bool autocvar_sv_autopause = true; +bool autocvar_sv_autopause = false; float RedirectionThink(); -void PM_Main(Client this); +void systems_update(); +void sys_phys_update(entity this, float dt); void StartFrame() { // TODO: if move is more than 50ms, split it into two moves (this matches QWSV behavior and the client prediction) - FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), PM_Main(it)); - FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), WITHSELF(it, PlayerPreThink())); + IL_EACH(g_players, IS_FAKE_CLIENT(it), sys_phys_update(it, frametime)); + IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPreThink(it)); execute_next_frame(); if (autocvar_sv_autopause && !server_is_dedicated) Pause_TryPause(true); - remove = remove_unsafely; // not during spawning! + delete_fn = remove_unsafely; // not during spawning! serverprevtime = servertime; servertime = time; serverframetime = frametime; @@ -190,7 +192,7 @@ void StartFrame() } #endif - FOREACH_ENTITY_FLOAT(csqcprojectile_clientanimate, true, CSQCProjectile_Check(it)); + IL_EACH(g_projectiles, it.csqcprojectile_clientanimate, CSQCProjectile_Check(it)); if (RedirectionThink()) return; @@ -228,7 +230,7 @@ void StartFrame() MUTATOR_CALLHOOK(SV_StartFrame); FOREACH_CLIENT(true, GlobalStats_update(it)); - FOREACH_ENTITY_CLASS(STR_PLAYER, IS_FAKE_CLIENT(it), WITHSELF(it, PlayerPostThink())); + IL_EACH(g_players, IS_FAKE_CLIENT(it), PlayerPostThink(it)); } .vector originjitter; @@ -237,15 +239,15 @@ void StartFrame() .string gametypefilter; .string cvarfilter; bool DoesQ3ARemoveThisEntity(entity this); -void SV_OnEntityPreSpawnFunction() -{ENGINE_EVENT(); +void SV_OnEntityPreSpawnFunction(entity this) +{ __spawnfunc_expecting = true; __spawnfunc_expect = this; if (this) if (this.gametypefilter != "") if (!isGametypeInFilter(MapInfo_LoadedGametype, teamplay, have_team_spawns, this.gametypefilter)) { - remove(this); + delete(this); __spawnfunc_expecting = false; return; } @@ -354,7 +356,7 @@ LABEL(cvar_fail) if (!inv) { //print("cvarfilter fail\n"); - remove(this); + delete(this); __spawnfunc_expecting = false; return; } @@ -362,11 +364,13 @@ LABEL(cvar_fail) if(DoesQ3ARemoveThisEntity(this)) { - remove(this); + delete(this); __spawnfunc_expecting = false; return; } + set_movetype(this, this.movetype); + // support special -1 and -2 angle from radiant if (this.angles == '0 -1 0') this.angles = '-90 0 0'; @@ -390,7 +394,7 @@ LABEL(cvar_fail) if(MUTATOR_CALLHOOK(OnEntityPreSpawn, this)) { - remove(this); + delete(this); __spawnfunc_expecting = false; return; } @@ -399,8 +403,8 @@ LABEL(cvar_fail) void WarpZone_PostInitialize_Callback() { // create waypoint links for warpzones - entity e; - for(e = NULL; (e = find(e, classname, "trigger_warpzone")); ) + //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; diff --git a/qcsrc/server/sys-post.qh b/qcsrc/server/sys-post.qh index e24a790a14..1ccf6695a1 100644 --- a/qcsrc/server/sys-post.qh +++ b/qcsrc/server/sys-post.qh @@ -2,7 +2,6 @@ #undef droptofloor #undef sound -#undef remove #undef cvar_set #undef cvar_string #undef cvar @@ -10,7 +9,7 @@ var float(string name) cvar; var string(string name) cvar_string; var void(string name, string value) cvar_set; -var void remove(entity e); +var void delete_fn(entity e); #undef IT_SHOTGUN #undef IT_SUPER_SHOTGUN diff --git a/qcsrc/server/sys-pre.qh b/qcsrc/server/sys-pre.qh index 7ab3f7b9f6..9799a087ea 100644 --- a/qcsrc/server/sys-pre.qh +++ b/qcsrc/server/sys-pre.qh @@ -1,7 +1,6 @@ #pragma once #define droptofloor builtin_droptofloor -#define remove builtin_remove #define cvar_set builtin_cvar_set #define cvar_string builtin_cvar_string #define cvar builtin_cvar diff --git a/qcsrc/server/t_halflife.qc b/qcsrc/server/t_halflife.qc deleted file mode 100644 index 7719542459..0000000000 --- a/qcsrc/server/t_halflife.qc +++ /dev/null @@ -1,35 +0,0 @@ -#include "t_halflife.qh" -.float roomtype; -.float radius; -.float pitch; -.float renderamt; -.float rendermode; -.vector rendercolor; - -spawnfunc(weapon_crossbow) {} -spawnfunc(weapon_handgrenade) {} -spawnfunc(ammo_crossbow) {} -spawnfunc(ammo_9mmclip) {} -spawnfunc(ammo_gaussclip) {} -spawnfunc(weapon_rpg) {} -spawnfunc(weapon_357) {} -void ammo_ARgrenades() {} -spawnfunc(item_battery) {} -spawnfunc(ammo_rpgclip) {} -void weapon_9mmAR() {} -spawnfunc(weapon_tripmine) {} -spawnfunc(weapon_snark) {} -spawnfunc(ammo_buckshot) {} -void ammo_9mmAR() {} -spawnfunc(ammo_357) {} -spawnfunc(weapon_gauss) {} -spawnfunc(weapon_hornetgun) {} -//spawnfunc(weapon_shotgun) {} -spawnfunc(item_healthkit) {} -spawnfunc(item_longjump) {} -spawnfunc(item_antidote) {} -spawnfunc(func_recharge) {} -spawnfunc(info_node) {} -spawnfunc(env_sound) {} -spawnfunc(light_spot) {} -spawnfunc(func_healthcharger) {} diff --git a/qcsrc/server/t_halflife.qh b/qcsrc/server/t_halflife.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/server/t_halflife.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/server/t_quake.qc b/qcsrc/server/t_quake.qc deleted file mode 100644 index 8589ecc7df..0000000000 --- a/qcsrc/server/t_quake.qc +++ /dev/null @@ -1,31 +0,0 @@ -#include "t_quake.qh" - -#include "../common/weapons/all.qh" - -spawnfunc(weapon_electro); -spawnfunc(weapon_hagar); -spawnfunc(weapon_machinegun); -spawnfunc(item_bullets); -spawnfunc(item_armor_large); -spawnfunc(item_armor_large); -spawnfunc(item_health_mega); -spawnfunc(item_health_medium); - -//*********************** -//QUAKE 1 ENTITIES - So people can play quake1 maps with the xonotic weapons -//*********************** -spawnfunc(weapon_nailgun) {spawnfunc_weapon_electro(this);} -spawnfunc(weapon_supernailgun) {spawnfunc_weapon_hagar(this);} -spawnfunc(weapon_supershotgun) {spawnfunc_weapon_machinegun(this);} - -spawnfunc(item_spikes) {spawnfunc_item_bullets(this);} -//spawnfunc(item_armor1) {spawnfunc_item_armor_medium(this);} // FIXME: in Quake this is green armor, in Xonotic maps it is an armor shard -spawnfunc(item_armor2) {spawnfunc_item_armor_large(this);} -spawnfunc(item_armorInv) {spawnfunc_item_armor_large(this);} // TODO: make sure we actually want this -spawnfunc(item_health) {if (this.spawnflags & 2) spawnfunc_item_health_mega(this);else spawnfunc_item_health_medium(this);} - -//spawnfunc_item_spikes -//spawnfunc_item_health - - - diff --git a/qcsrc/server/t_quake.qh b/qcsrc/server/t_quake.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/server/t_quake.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/server/t_quake3.qc b/qcsrc/server/t_quake3.qc deleted file mode 100644 index 1bdb49765d..0000000000 --- a/qcsrc/server/t_quake3.qc +++ /dev/null @@ -1,208 +0,0 @@ -#include "t_quake3.qh" - -#include "../common/weapons/all.qh" - -spawnfunc(weapon_crylink); -spawnfunc(weapon_electro); -spawnfunc(weapon_hagar); -spawnfunc(weapon_machinegun); -spawnfunc(weapon_vortex); - -spawnfunc(target_items); - -spawnfunc(item_bullets); -spawnfunc(item_cells); -spawnfunc(item_rockets); -spawnfunc(item_shells); - -spawnfunc(item_jetpack); - -spawnfunc(item_armor_big); -spawnfunc(item_armor_large); -spawnfunc(item_armor_small); - -spawnfunc(item_health_medium); -spawnfunc(item_health_mega); - -//*********************** -//QUAKE 3 ENTITIES - So people can play quake3 maps with the xonotic weapons -//*********************** - -// NOTE: for best experience, you need to swap MGs with SGs in the map or it won't have a MG - -// SG -> SG -spawnfunc(ammo_shells) { spawnfunc_item_shells(this); } - -// MG -> MG -spawnfunc(ammo_bullets) { spawnfunc_item_bullets(this); } - -// GL -> Mortar -spawnfunc(ammo_grenades) { spawnfunc_item_rockets(this); } - -// LG -> Lightning -spawnfunc(weapon_lightning) { spawnfunc_weapon_electro(this); } -spawnfunc(ammo_lightning) { spawnfunc_item_cells(this); } - -// Plasma -> Hagar -spawnfunc(weapon_plasmagun) { spawnfunc_weapon_hagar(this); } -spawnfunc(ammo_cells) { spawnfunc_item_rockets(this); } - -// Rail -> Vortex -spawnfunc(weapon_railgun) { spawnfunc_weapon_vortex(this); } -spawnfunc(ammo_slugs) { spawnfunc_item_cells(this); } - -// BFG -> Crylink -spawnfunc(weapon_bfg) { spawnfunc_weapon_crylink(this); } -spawnfunc(ammo_bfg) { spawnfunc_item_cells(this); } - -// RL -> RL -spawnfunc(ammo_rockets) { spawnfunc_item_rockets(this); } - -// Armor -spawnfunc(item_armor_body) { spawnfunc_item_armor_large(this); } -spawnfunc(item_armor_combat) { spawnfunc_item_armor_big(this); } -spawnfunc(item_armor_shard) { spawnfunc_item_armor_small(this); } -spawnfunc(item_enviro) { spawnfunc_item_invincible(this); } - -.float wait; -.float delay; - -// weapon remove ent from df -void target_init_verify(entity this) -{ - entity trigger, targ; - for(trigger = NULL; (trigger = find(trigger, classname, "trigger_multiple")); ) - for(targ = NULL; (targ = find(targ, targetname, trigger.target)); ) - if (targ.classname == "target_init" || targ.classname == "target_give" || targ.classname == "target_items") - { - trigger.wait = 0; - trigger.delay = 0; - targ.wait = 0; - targ.delay = 0; - - //setsize(targ, trigger.mins, trigger.maxs); - //setorigin(targ, trigger.origin); - //remove(trigger); - } -} - -spawnfunc(target_init) -{ - this.spawnflags = 0; // remove all weapons except the ones listed below - this.netname = "shotgun"; // keep these weapons through the remove trigger - spawnfunc_target_items(this); - InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET); -} - -// weapon give ent from defrag -void target_give_init(entity this) -{ - entity targ; - for (targ = NULL; (targ = find(targ, targetname, this.target)); ) { - if (targ.classname == "weapon_rocketlauncher" || targ.classname == "weapon_devastator") { - this.ammo_rockets += targ.count * WEP_CVAR(devastator, ammo); - this.netname = "devastator"; - } - else if (targ.classname == "weapon_plasmagun") { - this.ammo_rockets += targ.count * WEP_CVAR_PRI(hagar, ammo); // WEAPONTODO - if(this.netname == "") - this.netname = "hagar"; - else - this.netname = strcat(this.netname, " hagar"); - } - else if (targ.classname == "weapon_bfg") { - this.ammo_cells += targ.count * WEP_CVAR_PRI(crylink, ammo); - if(this.netname == "") - this.netname = "crylink"; - else - this.netname = strcat(this.netname, " crylink"); - } - else if (targ.classname == "weapon_grenadelauncher" || targ.classname == "weapon_mortar") { - this.ammo_rockets += targ.count * WEP_CVAR_PRI(mortar, ammo); // WEAPONTODO - if(this.netname == "") - this.netname = "mortar"; - else - this.netname = strcat(this.netname, " mortar"); - } - else if (targ.classname == "item_armor_body") - this.armorvalue = 100; - else if (targ.classname == "item_health_mega") - this.health = 200; - //remove(targ); // removing ents in init functions causes havoc, workaround: - setthink(targ, SUB_Remove); - targ.nextthink = time; - } - this.spawnflags = 2; - spawnfunc_target_items(this); - InitializeEntity(this, target_init_verify, INITPRIO_FINDTARGET); -} - -spawnfunc(target_give) -{ - InitializeEntity(this, target_give_init, INITPRIO_FINDTARGET); -} - -//spawnfunc(item_flight) /* handled by jetpack */ -//spawnfunc(item_haste) /* handled by buffs mutator */ -//spawnfunc(item_health) /* handled in t_quake.qc */ -//spawnfunc(item_health_large) /* handled in t_items.qc */ -//spawnfunc(item_health_small) /* handled in t_items.qc */ -//spawnfunc(item_health_mega) /* handled in t_items.qc */ -//spawnfunc(item_invis) /* handled by buffs mutator */ -//spawnfunc(item_regen) /* handled by buffs mutator */ - -// CTF spawnfuncs handled in mutators/gamemode_ctf.qc now - -spawnfunc(item_flight) -{ - spawnfunc_item_jetpack(this); -} - -.float notteam; -.float notsingle; -.float notfree; -.float notq3a; -.float notta; -.string gametype; -bool DoesQ3ARemoveThisEntity(entity this) -{ - // Q3 style filters (DO NOT USE, THIS IS COMPAT ONLY) - - if(this.notq3a) - if(!teamplay || g_tdm || g_ctf) - return true; - - if(this.notta) - if (!(!teamplay || g_tdm || g_ctf)) - return true; - - if(this.notsingle) - if(maxclients == 1) - return true; - - if(this.notteam) - if(teamplay) - return true; - - if(this.notfree) - if(!teamplay) - return true; - - if(this.gametype) - { - string gametypename; - // static char *gametypeNames[] = {"ffa", "tournament", "single", "team", "ctf", "oneflag", "obelisk", "harvester", "teamtournament"} - gametypename = "ffa"; - if(teamplay) - gametypename = "team"; - if(g_ctf) - gametypename = "ctf"; - if(maxclients == 1) - gametypename = "single"; - // we do not have the other types (oneflag, obelisk, harvester, teamtournament) - if(strstrofs(this.gametype, gametypename, 0) < 0) - return true; - } - - return false; -} diff --git a/qcsrc/server/t_quake3.qh b/qcsrc/server/t_quake3.qh deleted file mode 100644 index 6f70f09bee..0000000000 --- a/qcsrc/server/t_quake3.qh +++ /dev/null @@ -1 +0,0 @@ -#pragma once diff --git a/qcsrc/server/teamplay.qc b/qcsrc/server/teamplay.qc index 8709a6aae6..0918130159 100644 --- a/qcsrc/server/teamplay.qc +++ b/qcsrc/server/teamplay.qc @@ -1,18 +1,18 @@ #include "teamplay.qh" -#include "cl_client.qh" +#include "client.qh" #include "race.qh" #include "scores.qh" #include "scores_rules.qh" -#include "bot/bot.qh" +#include "bot/api.qh" #include "command/vote.qh" -#include "mutators/all.qh" +#include "mutators/_mod.qh" #include "../common/deathtypes/all.qh" -#include "../common/gamemodes/all.qh" +#include "../common/gamemodes/_mod.qh" #include "../common/teams.qh" void TeamchangeFrags(entity e) @@ -109,7 +109,7 @@ string getwelcomemessage(entity this) else modifications = strcat(modifications, ", ", g_weaponarena_list, " Arena"); } - else if(cvar("g_balance_blaster_weaponstart") == 0) + 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"); @@ -158,6 +158,16 @@ string getwelcomemessage(entity this) return s; } +void setcolor(entity this, int clr) +{ +#if 0 + this.clientcolors = clr; + this.team = (clr & 15) + 1; +#else + builtin_setcolor(this, clr); +#endif +} + void SetPlayerColors(entity pl, float _color) { /*string s; @@ -206,25 +216,23 @@ void SetPlayerTeam(entity pl, float t, float s, float noprint) // set c1...c4 to show what teams are allowed void CheckAllowedTeams (entity for_whom) { - int dm = 0; + int teams_mask = 0; c1 = c2 = c3 = c4 = -1; cb1 = cb2 = cb3 = cb4 = 0; string teament_name = string_null; - bool mutator_returnvalue = MUTATOR_CALLHOOK(GetTeamCount, dm, teament_name); - dm = M_ARGV(0, float); + bool mutator_returnvalue = MUTATOR_CALLHOOK(CheckAllowedTeams, teams_mask, teament_name); + teams_mask = M_ARGV(0, float); teament_name = M_ARGV(1, string); if(!mutator_returnvalue) { - if(dm >= 4) - c1 = c2 = c3 = c4 = 0; - else if(dm >= 3) - c1 = c2 = c3 = 0; - else - c1 = c2 = 0; + 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; } // find out what teams are allowed if necessary @@ -246,24 +254,46 @@ void CheckAllowedTeams (entity for_whom) } // TODO: Balance quantity of bots across > 2 teams when bot_vs_human is set (and remove next line) - if(c3==-1 && c4==-1) + if(AvailableTeams() == 2) if(autocvar_bot_vs_human && for_whom) { if(autocvar_bot_vs_human > 0) { - // bots are all blue + // find last team available + if(IS_BOT_CLIENT(for_whom)) - c1 = c3 = c4 = -1; + { + if(c4 >= 0) { c3 = c2 = c1 = -1; } + else if(c3 >= 0) { c4 = c2 = c1 = -1; } + else { c4 = c3 = c1 = -1; } + // no further cases, we know at least 2 teams exist + } else - c2 = -1; + { + if(c1 >= 0) { c2 = c3 = c4 = -1; } + else if(c2 >= 0) { c1 = c3 = c4 = -1; } + else { c1 = c2 = c4 = -1; } + // no further cases, bots have one of the teams + } } else { - // bots are all red + // find first team available + if(IS_BOT_CLIENT(for_whom)) - c2 = c3 = c4 = -1; + { + if(c1 >= 0) { c2 = c3 = c4 = -1; } + else if(c2 >= 0) { c1 = c3 = c4 = -1; } + else { c1 = c2 = c4 = -1; } + // no further cases, we know at least 2 teams exist + } else - c1 = -1; + { + if(c4 >= 0) { c3 = c2 = c1 = -1; } + else if(c3 >= 0) { c4 = c2 = c1 = -1; } + else { c4 = c3 = c1 = -1; } + // no further cases, bots have one of the teams + } } } @@ -426,8 +456,12 @@ float TeamSmallerEqThanTeam(float ta, float tb, entity e) // NOTE: Assumes CheckAllowedTeams has already been called! float FindSmallestTeam(entity pl, float ignore_pl) { - float totalteams, t; - totalteams = 0; + int totalteams = 0; + int t = 1; // initialize with a random team? + if(c4 >= 0) t = 4; + if(c3 >= 0) t = 3; + if(c2 >= 0) t = 2; + if(c1 >= 0) t = 1; // find out what teams are available //CheckAllowedTeams(); @@ -449,8 +483,10 @@ float FindSmallestTeam(entity pl, float ignore_pl) { if(autocvar_g_campaign && pl && IS_REAL_CLIENT(pl)) return 1; // special case for campaign and player joining - else - error(sprintf("Too few teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype()))); + else if(totalteams == 1) // single team + LOG_TRACEF("Only 1 team available for %s, you may need to fix your map", MapInfo_Type_ToString(MapInfo_CurrentGametype())); + else // no teams, major no no + error(sprintf("No teams available for %s\n", MapInfo_Type_ToString(MapInfo_CurrentGametype()))); } // count how many players are in each team @@ -461,7 +497,8 @@ float FindSmallestTeam(entity pl, float ignore_pl) RandomSelection_Init(); - t = 1; + if(TeamSmallerEqThanTeam(1, t, pl)) + t = 1; if(TeamSmallerEqThanTeam(2, t, pl)) t = 2; if(TeamSmallerEqThanTeam(3, t, pl)) @@ -471,13 +508,13 @@ float FindSmallestTeam(entity pl, float ignore_pl) // now t is the minimum, or A minimum! if(t == 1 || TeamSmallerEqThanTeam(1, t, pl)) - RandomSelection_Add(NULL, 1, string_null, 1, 1); + RandomSelection_AddFloat(1, 1, 1); if(t == 2 || TeamSmallerEqThanTeam(2, t, pl)) - RandomSelection_Add(NULL, 2, string_null, 1, 1); + RandomSelection_AddFloat(2, 1, 1); if(t == 3 || TeamSmallerEqThanTeam(3, t, pl)) - RandomSelection_Add(NULL, 3, string_null, 1, 1); + RandomSelection_AddFloat(3, 1, 1); if(t == 4 || TeamSmallerEqThanTeam(4, t, pl)) - RandomSelection_Add(NULL, 4, string_null, 1, 1); + RandomSelection_AddFloat(4, 1, 1); return RandomSelection_chosen_float; } @@ -515,7 +552,7 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam) SetPlayerColors(this, selectedteam - 1); // when JoinBestTeam is called by client.qc/ClientKill_Now_TeamChange the players team is -1 and thus skipped - // when JoinBestTeam is called by cl_client.qc/ClientConnect the player_id is 0 the log attempt is rejected + // when JoinBestTeam is called by client.qc/ClientConnect the player_id is 0 the log attempt is rejected LogTeamchange(this.playerid, this.team, 99); } return selectedteam; @@ -559,8 +596,8 @@ int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam) } //void() ctf_playerchanged; -void SV_ChangeTeam(float _color) -{ENGINE_EVENT(); +void SV_ChangeTeam(entity this, float _color) +{ float scolor, dcolor, steam, dteam; //, dbotcount, scount, dcount; // in normal deathmatch we can just apply the color and we're done diff --git a/qcsrc/server/teamplay.qh b/qcsrc/server/teamplay.qh index c9713984e8..127ac7a6d3 100644 --- a/qcsrc/server/teamplay.qh +++ b/qcsrc/server/teamplay.qh @@ -48,6 +48,7 @@ float FindSmallestTeam(entity pl, float ignore_pl); int JoinBestTeam(entity this, bool only_return_best, bool forcebestteam); //void() ctf_playerchanged; -void SV_ChangeTeam(float _color); void ShufflePlayerOutOfTeam (float source_team); + +void setcolor(entity this, int clr); diff --git a/qcsrc/server/tests.qc b/qcsrc/server/tests.qc index 7bbf00dc51..96674db702 100644 --- a/qcsrc/server/tests.qc +++ b/qcsrc/server/tests.qc @@ -3,8 +3,8 @@ void test_weapons_hurt(entity this) { EXPECT_NE(100, this.health); - remove(this.enemy); - remove(this); + delete(this.enemy); + delete(this); } TEST(Weapons, Hurt) diff --git a/qcsrc/server/tests.qh b/qcsrc/server/tests.qh index be6445b48e..e6d6f66a0d 100644 --- a/qcsrc/server/tests.qh +++ b/qcsrc/server/tests.qh @@ -1,11 +1,11 @@ #pragma once #include "autocvars.qh" -#include "cl_client.qh" -#include "command/all.qh" +#include "client.qh" +#include "command/_mod.qh" #include "weapons/common.qh" #include "weapons/selection.qh" #include #include -#include +#include #include diff --git a/qcsrc/server/weapons/accuracy.qc b/qcsrc/server/weapons/accuracy.qc index abb41ad06a..7cc06da3e6 100644 --- a/qcsrc/server/weapons/accuracy.qc +++ b/qcsrc/server/weapons/accuracy.qc @@ -1,10 +1,11 @@ #include "accuracy.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include +#include #include #include -#include +#include int accuracy_byte(float n, float d) { @@ -47,7 +48,7 @@ void accuracy_init(entity e) void accuracy_free(entity e) { - remove(e.accuracy); + delete(e.accuracy); } // force a resend of a player's accuracy stats diff --git a/qcsrc/server/weapons/common.qc b/qcsrc/server/weapons/common.qc index 954f82564f..6d16375506 100644 --- a/qcsrc/server/weapons/common.qc +++ b/qcsrc/server/weapons/common.qc @@ -2,11 +2,12 @@ #include #include +#include #include #include #include -#include -#include +#include +#include void W_GiveWeapon(entity e, int wep) { diff --git a/qcsrc/server/weapons/csqcprojectile.qc b/qcsrc/server/weapons/csqcprojectile.qc index 7e7478e9b8..9fcfd34c20 100644 --- a/qcsrc/server/weapons/csqcprojectile.qc +++ b/qcsrc/server/weapons/csqcprojectile.qc @@ -5,7 +5,8 @@ #include "../command/common.qh" #include -#include +#include +#include .float csqcprojectile_type; @@ -83,7 +84,7 @@ void CSQCProjectile(entity e, float clientanimate, int type, float docull) e.csqcprojectile_clientanimate = clientanimate; - if(e.movetype == MOVETYPE_TOSS || e.movetype == MOVETYPE_BOUNCE) + if(e.move_movetype == MOVETYPE_TOSS || e.move_movetype == MOVETYPE_BOUNCE) { if(e.gravity == 0) e.gravity = 1; diff --git a/qcsrc/server/weapons/hitplot.qc b/qcsrc/server/weapons/hitplot.qc index ec1fd089b1..372f7357b4 100644 --- a/qcsrc/server/weapons/hitplot.qc +++ b/qcsrc/server/weapons/hitplot.qc @@ -2,7 +2,7 @@ #include "../antilag.qh" #include "../g_subs.qh" -#include +#include #include vector W_HitPlotUnnormalizedUntransform(vector screenforward, vector screenright, vector screenup, vector v) diff --git a/qcsrc/server/weapons/selection.qc b/qcsrc/server/weapons/selection.qc index 70284a8f4b..cdb337d145 100644 --- a/qcsrc/server/weapons/selection.qc +++ b/qcsrc/server/weapons/selection.qc @@ -3,9 +3,10 @@ #include "weaponsystem.qh" #include #include +#include #include #include -#include +#include #include #include @@ -21,15 +22,13 @@ void Send_WeaponComplain(entity e, float wpn, float type) void Weapon_whereis(Weapon this, entity cl) { if (!autocvar_g_showweaponspawns) return; - for (entity it = NULL; (it = findfloat(it, weapon, this.m_id)); ) + IL_EACH(g_items, it.weapon == this.m_id && (!it.team || (it.ItemStatus & ITS_AVAILABLE)), { if (it.classname == "droppedweapon" && autocvar_g_showweaponspawns < 2) continue; - if (!(it.flags & FL_ITEM)) - continue; entity wp = WaypointSprite_Spawn( WP_Weapon, - 1, 0, + -2, 0, NULL, it.origin + ('0 0 1' * it.maxs.z) * 1.2, cl, 0, NULL, enemy, @@ -37,7 +36,7 @@ void Weapon_whereis(Weapon this, entity cl) RADARICON_NONE ); wp.wp_extra = this.m_id; - } + }); } bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain) @@ -75,7 +74,7 @@ bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain) // always allow selecting the Mine Layer if we placed mines, so that we can detonate them if(wpn == WEP_MINE_LAYER) - FOREACH_ENTITY_CLASS("mine", it.owner == this, + IL_EACH(g_mines, it.owner == this, { f = 1; break; // no need to continue @@ -101,7 +100,15 @@ bool client_hasweapon(entity this, Weapon wpn, float andammo, bool complain) if (weaponsInMap & WepSet_FromWeapon(wpn)) { Send_WeaponComplain(this, wpn.m_id, 1); - Weapon_whereis(wpn, this); + if(autocvar_g_showweaponspawns < 3) + Weapon_whereis(wpn, this); + else + { + FOREACH(Weapons, it.impulse == wpn.impulse, + { + Weapon_whereis(it, this); + }); + } } else { @@ -264,8 +271,11 @@ void W_SwitchWeapon(entity this, Weapon w) } else if(!forbidWeaponUse(this)) { entity actor = this; - .entity weaponentity = weaponentities[0]; // TODO: unhardcode - w.wr_reload(w, actor, weaponentity); + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + w.wr_reload(w, actor, weaponentity); + } } } diff --git a/qcsrc/server/weapons/spawning.qc b/qcsrc/server/weapons/spawning.qc index 748f434eb3..00bc0e5d12 100644 --- a/qcsrc/server/weapons/spawning.qc +++ b/qcsrc/server/weapons/spawning.qc @@ -1,9 +1,9 @@ #include "spawning.qh" #include "weaponsystem.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include -#include +#include string W_Apply_Weaponreplace(string in) { @@ -29,7 +29,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e) { if (e.spawnflags & WEP_FLAG_MUTATORBLOCKED) { - LOG_MAPWARNF("Attempted to spawn a mutator-blocked weapon rejected: prvm_edict server %i", this); + LOG_WARNF("Attempted to spawn a mutator-blocked weapon rejected: prvm_edict server %i", this); startitem_failed = true; return; } @@ -39,7 +39,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e) s = M_ARGV(2, string); if (s == "") { - remove(this); + delete(this); startitem_failed = true; return; } @@ -76,7 +76,7 @@ void weapon_defaultspawnfunc(entity this, Weapon e) } if (wpn == WEP_Null) { - remove(this); + delete(this); startitem_failed = true; return; } diff --git a/qcsrc/server/weapons/throwing.qc b/qcsrc/server/weapons/throwing.qc index c69b8e9a9c..30b700098d 100644 --- a/qcsrc/server/weapons/throwing.qc +++ b/qcsrc/server/weapons/throwing.qc @@ -1,7 +1,7 @@ #include "throwing.qh" #include "weaponsystem.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include #include "../g_damage.qh" #include @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include void thrown_wep_think(entity this) @@ -19,7 +19,10 @@ void thrown_wep_think(entity this) { this.SendFlags |= ISF_LOCATION; this.oldorigin = this.origin; + this.bot_pickup = false; } + else + this.bot_pickup = true; this.owner = NULL; float timeleft = this.savenextthink - time; if(timeleft > 1) @@ -45,6 +48,7 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto wep.owner = wep.enemy = own; wep.flags |= FL_TOSSED; wep.colormap = own.colormap; + wep.glowmod = weaponentity_glowmod(info, own, own.clientcolors); W_DropEvent(wr_drop,own,wpn,wep); @@ -79,7 +83,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto weapon_defaultspawnfunc(wep, info); if(startitem_failed) return string_null; - wep.glowmod = weaponentity_glowmod(info, own.clientcolors); setthink(wep, thrown_wep_think); wep.savenextthink = wep.nextthink; wep.nextthink = min(wep.nextthink, time + 0.5); @@ -165,7 +168,7 @@ bool W_IsWeaponThrowable(entity this, int w) } // toss current weapon -void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce) +void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce) { Weapon w = PS(this).m_weapon; if (w == WEP_Null) @@ -174,7 +177,6 @@ void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce) return; if(!autocvar_g_weapon_throwable) return; - .entity weaponentity = weaponentities[0]; // TODO: unhardcode if(this.(weaponentity).state != WS_READY) return; if(!W_IsWeaponThrowable(this, w.m_id)) diff --git a/qcsrc/server/weapons/throwing.qh b/qcsrc/server/weapons/throwing.qh index 5a27025bc8..a03968083e 100644 --- a/qcsrc/server/weapons/throwing.qh +++ b/qcsrc/server/weapons/throwing.qh @@ -9,6 +9,6 @@ string W_ThrowNewWeapon(entity own, float wpn, float doreduce, vector org, vecto bool W_IsWeaponThrowable(entity this, int w); // toss current weapon -void W_ThrowWeapon(entity this, vector velo, vector delta, float doreduce); +void W_ThrowWeapon(entity this, .entity weaponentity, vector velo, vector delta, float doreduce); void SpawnThrownWeapon(entity this, vector org, float w); diff --git a/qcsrc/server/weapons/tracing.qc b/qcsrc/server/weapons/tracing.qc index 5c64a0ec64..b49ed88f59 100644 --- a/qcsrc/server/weapons/tracing.qc +++ b/qcsrc/server/weapons/tracing.qc @@ -10,9 +10,10 @@ #include "../antilag.qh" #include +#include #include -#include +#include #include #include @@ -20,9 +21,9 @@ // this function calculates w_shotorg and w_shotdir based on the weapon model // offset, trueaim and antilag, and won't put w_shotorg inside a wall. // make sure you call makevectors first (FIXME?) -void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range) +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) { - TC(Sound, snd); + 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; vector vecs, dv; @@ -58,7 +59,6 @@ void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector m if(IS_PLAYER(ent)) W_HitPlotAnalysis(ent, v_forward, v_right, v_up); - .entity weaponentity = weaponentities[0]; // TODO: unhardcode vector md = ent.(weaponentity).movedir; if(md.x > 0) vecs = md; @@ -300,7 +300,7 @@ void FireRailgunBullet (entity this, vector start, vector end, float bdamage, fl )); if(pseudoprojectile) - remove(pseudoprojectile); + delete(pseudoprojectile); } // find all the entities the railgun hit and hurt them @@ -377,9 +377,9 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s if(lag) { FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_takeback(it, CS(it), time - lag)); - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { - if (it != this) - antilag_takeback(it, it, time - lag); + IL_EACH(g_monsters, it != this, + { + antilag_takeback(it, it, time - lag); }); } @@ -400,7 +400,7 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s entity hit = trace_ent; // When hitting sky, stop. - if (pointcontents(start) == CONTENT_SKY) + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SKY) break; // can't use noimpact, as we need to pass through walls @@ -446,7 +446,7 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s } } - if (is_weapclip) + if (is_weapclip && !autocvar_g_ballistics_penetrate_clips) break; // go through solid! @@ -476,7 +476,10 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s break; float dist_taken = max(autocvar_g_ballistics_mindistance, vlen(trace_endpos - start)); - solid_penetration_left *= (dist_taken / maxdist); + // fraction_used_of_what_is_left = dist_taken / maxdist + // solid_penetration_left = solid_penetration_left - solid_penetration_left * fraction_used_of_what_is_left + solid_penetration_left *= 1 - dist_taken / maxdist; + solid_penetration_left = max(solid_penetration_left, 0); // Only show effect when going through a player (invisible otherwise) if (hit && (hit.solid != SOLID_BSP)) @@ -492,9 +495,9 @@ void fireBullet(entity this, vector start, vector dir, float spread, float max_s if(lag) { FOREACH_CLIENT(IS_PLAYER(it) && it != this, antilag_restore(it, CS(it))); - FOREACH_ENTITY_FLAGS(flags, FL_MONSTER, { - if (it != this) - antilag_restore(it, it); + IL_EACH(g_monsters, it != this, + { + antilag_restore(it, it); }); } diff --git a/qcsrc/server/weapons/tracing.qh b/qcsrc/server/weapons/tracing.qh index 52e5bd840e..21f12c1e4c 100644 --- a/qcsrc/server/weapons/tracing.qh +++ b/qcsrc/server/weapons/tracing.qh @@ -7,13 +7,13 @@ vector w_shotend; // this function calculates w_shotorg and w_shotdir based on the weapon model // offset, trueaim and antilag, and won't put w_shotorg inside a wall. // make sure you call makevectors first (FIXME?) -void W_SetupShot_Dir_ProjectileSize_Range(entity ent, vector s_forward, vector mi, vector ma, float antilag, float recoil, Sound snd, float chan, float maxdamage, float range); +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); -#define W_SetupShot_Dir_ProjectileSize(ent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE) -#define W_SetupShot_ProjectileSize(ent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot_Dir(ent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot(ent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) -#define W_SetupShot_Range(ent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range) +#define W_SetupShot_Dir_ProjectileSize(ent,wepent,s_forward,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, s_forward, mi, ma, antilag, recoil, snd, chan, maxdamage, MAX_SHOT_DISTANCE) +#define W_SetupShot_ProjectileSize(ent,wepent,mi,ma,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, v_forward, mi, ma, antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot_Dir(ent,wepent,s_forward,antilag,recoil,snd,chan,maxdamage) W_SetupShot_Dir_ProjectileSize(ent, wepent, s_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot(ent,wepent,antilag,recoil,snd,chan,maxdamage) W_SetupShot_ProjectileSize(ent, wepent, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage) +#define W_SetupShot_Range(ent,wepent,antilag,recoil,snd,chan,maxdamage,range) W_SetupShot_Dir_ProjectileSize_Range(ent, wepent, v_forward, '0 0 0', '0 0 0', antilag, recoil, snd, chan, maxdamage, range) vector W_CalculateProjectileVelocity(entity actor, vector pvelocity, vector mvelocity, float forceAbsolute); diff --git a/qcsrc/server/weapons/weaponstats.qc b/qcsrc/server/weapons/weaponstats.qc index 2b4be7e917..2ffb1c1ec8 100644 --- a/qcsrc/server/weapons/weaponstats.qc +++ b/qcsrc/server/weapons/weaponstats.qc @@ -2,7 +2,7 @@ #include "../g_world.qh" -#include +#include void WeaponStats_Init() { diff --git a/qcsrc/server/weapons/weaponsystem.qc b/qcsrc/server/weapons/weaponsystem.qc index 21057e4a0b..ad479824ec 100644 --- a/qcsrc/server/weapons/weaponsystem.qc +++ b/qcsrc/server/weapons/weaponsystem.qc @@ -3,15 +3,16 @@ #include "selection.qh" #include "../command/common.qh" -#include "../mutators/all.qh" +#include "../mutators/_mod.qh" #include "../round_handler.qh" #include #include #include -#include +#include +#include #include #include -#include +#include #include #include @@ -21,7 +22,9 @@ float W_WeaponRateFactor(entity this) { - float t = 1.0 / g_weaponratefactor; + float t = 1; + if(g_weaponratefactor > 0) + t = 1.0 / g_weaponratefactor; MUTATOR_CALLHOOK(WeaponRateFactor, t, this); t = M_ARGV(0, float); @@ -40,10 +43,10 @@ float W_WeaponSpeedFactor(entity this) } -bool CL_Weaponentity_CustomizeEntityForClient(entity this) +bool CL_Weaponentity_CustomizeEntityForClient(entity this, entity client) { this.viewmodelforclient = this.owner; - if (IS_SPEC(other) && other.enemy == this.owner) this.viewmodelforclient = other; + if (IS_SPEC(client) && client.enemy == this.owner) this.viewmodelforclient = client; return true; } @@ -54,7 +57,7 @@ vector CL_Weapon_GetShotOrg(int wpn) CL_WeaponEntity_SetModel(e, wi.mdl, false); vector ret = e.movedir; CL_WeaponEntity_SetModel(e, "", false); - remove(e); + delete(e); return ret; } @@ -64,13 +67,13 @@ vector CL_Weapon_GetShotOrg(int wpn) void CL_Weaponentity_Think(entity this) { this.nextthink = time; - if (intermission_running) this.frame = this.anim_idle.x; + if (gameover) this.frame = this.anim_idle.x; .entity weaponentity = this.weaponentity_fld; if (this.owner.(weaponentity) != this) { // owner has new gun; remove old one - if (this.weaponchild) remove(this.weaponchild); - remove(this); + if (this.weaponchild) delete(this.weaponchild); + delete(this); return; } if (IS_DEAD(this.owner)) @@ -110,7 +113,7 @@ void CL_ExteriorWeaponentity_Think(entity this) this.nextthink = time; if (this.owner.exteriorweaponentity != this) { - remove(this); + delete(this); return; } if (IS_DEAD(this.owner)) @@ -150,7 +153,7 @@ void CL_ExteriorWeaponentity_Think(entity this) else this.alpha = 1; Weapon wep = PS(this.owner).m_weapon; - if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner.clientcolors); + if (wep) this.glowmod = weaponentity_glowmod(wep, this.owner, this.owner.clientcolors); this.colormap = this.owner.colormap; CSQCMODEL_AUTOUPDATE(this); @@ -214,8 +217,12 @@ bool weapon_prepareattack_checkammo(Weapon thiswep, entity actor, bool secondary if (ammo) return true; // always keep the Mine Layer if we placed mines, so that we can detonate them if (thiswep == WEP_MINE_LAYER) - for (entity mine; (mine = find(mine, classname, "mine")); ) - if (mine.owner == actor) return false; + { + IL_EACH(g_mines, it.owner == actor, + { + return false; + }); + } if (thiswep == WEP_SHOTGUN) if (!secondary && WEP_CVAR(shotgun, secondary) == 1) return false; // no clicking, just allow @@ -386,7 +393,9 @@ void weapon_thinkf(entity actor, .entity weaponentity, WFRAME fr, float t, void( if ((fr == WFRAME_FIRE1 || fr == WFRAME_FIRE2) && t) { - int act = (fr == WFRAME_FIRE2 && (PS(actor).m_weapon == WEP_SHOCKWAVE || PS(actor).m_weapon == WEP_SHOTGUN)) + bool primary_melee = boolean(fr == WFRAME_FIRE1 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_PRI)); + bool secondary_melee = boolean(fr == WFRAME_FIRE2 && (PS(actor).m_weapon.spawnflags & WEP_TYPE_MELEE_SEC)); + int act = (primary_melee || secondary_melee) ? ANIMACTION_MELEE : ANIMACTION_SHOOT ; @@ -403,18 +412,19 @@ bool forbidWeaponUse(entity player) if (time < game_starttime && !autocvar_sv_ready_restart_after_countdown) return true; if (round_handler_IsActive() && !round_handler_IsRoundStarted()) return true; if (player.player_blocked) return true; + if (gameover) return true; if (STAT(FROZEN, player)) return true; if (player.weapon_blocked) return true; + if (MUTATOR_CALLHOOK(ForbidWeaponUse, player)) return true; return false; } .bool hook_switchweapon; -void W_WeaponFrame(Player actor) +void W_WeaponFrame(Player actor, .entity weaponentity) { TC(Player, actor); TC(PlayerState, PS(actor)); - .entity weaponentity = weaponentities[0]; // TODO: unhardcode entity this = actor.(weaponentity); if (frametime) actor.weapon_frametime = frametime; @@ -437,7 +447,6 @@ void W_WeaponFrame(Player actor) PS(actor).m_switchingweapon = WEP_Null; this.state = WS_CLEAR; actor.weaponname = ""; - // actor.items &= ~IT_AMMO; return; } @@ -452,7 +461,7 @@ void W_WeaponFrame(Player actor) switch (this.state) { default: - LOG_WARNINGF("unhandled weaponentity (%i) state for player (%i): %d\n", this, actor, this.state); + LOG_WARNF("unhandled weaponentity (%i) state for player (%i): %d", this, actor, this.state); break; case WS_INUSE: case WS_RAISE: @@ -563,7 +572,7 @@ void W_WeaponFrame(Player actor) } else if (e) { - e.wr_gonethink(e, actor); + e.wr_gonethink(e, actor, weaponentity); } } @@ -586,9 +595,8 @@ void W_WeaponFrame(Player actor) } } -void W_AttachToShotorg(entity actor, entity flash, vector offset) +void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset) { - .entity weaponentity = weaponentities[0]; flash.owner = actor; flash.angles_z = random() * 360; @@ -683,10 +691,9 @@ void W_ReloadedAndReady(Weapon thiswep, entity actor, .entity weaponentity, int w_ready(wpn, actor, weaponentity, PHYS_INPUT_BUTTON_ATCK(actor) | (PHYS_INPUT_BUTTON_ATCK2(actor) << 1)); } -void W_Reload(entity actor, float sent_ammo_min, Sound sent_sound) +void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sent_sound) { TC(Sound, sent_sound); - .entity weaponentity = weaponentities[0]; // set global values to work with Weapon e = PS(actor).m_weapon; diff --git a/qcsrc/server/weapons/weaponsystem.qh b/qcsrc/server/weapons/weaponsystem.qh index 2aeca53811..b7ca597046 100644 --- a/qcsrc/server/weapons/weaponsystem.qh +++ b/qcsrc/server/weapons/weaponsystem.qh @@ -8,17 +8,17 @@ void CL_SpawnWeaponentity(entity e, .entity weaponentity); vector CL_Weapon_GetShotOrg(float wpn); -float forbidWeaponUse(entity player); +bool forbidWeaponUse(entity player); -void W_AttachToShotorg(entity actor, entity flash, vector offset); +void W_AttachToShotorg(entity actor, .entity weaponentity, entity flash, vector offset); void W_DecreaseAmmo(Weapon wep, entity actor, float ammo_use); void W_DropEvent(.void(Weapon, entity actor) event, entity player, float weapon_type, entity weapon_item); -void W_Reload(entity actor, float sent_ammo_min, Sound sent_sound); +void W_Reload(entity actor, .entity weaponentity, float sent_ammo_min, Sound sent_sound); -void W_WeaponFrame(Player actor); +void W_WeaponFrame(Player actor, .entity weaponentity); float W_WeaponRateFactor(entity this); diff --git a/qcsrc/tools/auto-super.pl b/qcsrc/tools/auto-super.pl new file mode 100644 index 0000000000..00926d0617 --- /dev/null +++ b/qcsrc/tools/auto-super.pl @@ -0,0 +1,101 @@ +my %classoffile = (); +my %classes = (); +my %baseclass = (); +my %methods = (); +my %attrs = (); +my %methodnames = (); +my %old2new = (); + +print STDERR "Scanning...\n"; +for my $f(@ARGV) +{ + open my $fh, '<', $f; + while(<$fh>) + { + if(/^CLASS\(([^)]*)\)(?:\s*EXTENDS\(([^)]*)\))?/) + { + $classes{$1} = defined($2) ? $2 : "Object"; + $classoffile{$f} = $1; + } + if(/^\s*METHOD\(([^),]*),\s*([^),]*)/) + { + $methods{$1}{$2} = $1; + $methodnames{"$1"."_"."$2"} = $f; + $old2new{"$2$1"} = "$1"."_"."$2"; + } + if(/^\s*ATTRIB(?:ARRAY)?\(([^),]*),\s*([^),]*)/) + { + $attrs{$1}{$2} = $1; + } + } + close $fh; +} + +# propagate down methods etc. +print STDERR "Propagating...\n"; +for my $class(keys %classes) +{ + print STDERR "$class"; + my $base = $class; + for(;;) + { + $base = $classes{$base}; + last if not defined $base; + print STDERR " -> $base"; + while(my ($method, $definingclass) = each %{$methods{$base}}) + { + $methods{$class}{$method} = $definingclass + if not defined $methods{$class}{$method}; + } + while(my ($attr, $definingclass) = each %{$attrs{$base}}) + { + $attrs{$class}{$attr} = $definingclass + if not defined $attrs{$class}{$attr}; + } + } + print STDERR "\n"; +} + +# change all calls to base method to super, complain about skipping +print STDERR "Fixing...\n"; +for my $f(@ARGV) +{ + open my $fh, '<', $f; + my $s = do { undef local $/; <$fh>; }; + my $s0 = $s; + close $fh; + + my $class = $classoffile{$f}; + my $base = $classes{$class}; + next if not defined $base; + + for(keys %old2new) + { + $s =~ s/\b$_\b/$old2new{$_}/g; + } + + my @methods_super = map { [ $methods{$base}{$_} . "_" . $_, "SUPER($class).$_" ]; } keys %{$methods{$base}}; + for(@methods_super) + { + my ($search, $replace) = @$_; + my $n = ($s =~ s/\b$search\b/$replace/g); + print STDERR "[$f] $search -> $replace... $n replacements\n" + if $n; + } + + for(grep { $methodnames{$_} ne $f } keys %methodnames) + { + if($s =~ /\b$_\b/) + { + print STDERR "[$f] calls non-super external method directly: $_\n"; + } + } + + if($s ne $s0) + { + print STDERR "Rewriting $f...\n"; + open my $fh, '>', $f; + print $fh $s; + close $fh; + } +} diff --git a/qcsrc/tools/cloc.txt b/qcsrc/tools/cloc.txt new file mode 100644 index 0000000000..9b1b3af3e0 --- /dev/null +++ b/qcsrc/tools/cloc.txt @@ -0,0 +1,13 @@ +QC + filter call_regexp_common C++ + filter remove_inline //.*$ + extension qc + extension inc + 3rd_gen_scale 0.5 + end_of_line_continuation \\$ +QC Header + filter call_regexp_common C++ + filter remove_inline //.*$ + extension qh + 3rd_gen_scale 0.5 + end_of_line_continuation \\$ diff --git a/qcsrc/tools/compilationunits.sh b/qcsrc/tools/compilationunits.sh index 3c794e93fb..ebb232105f 100755 --- a/qcsrc/tools/compilationunits.sh +++ b/qcsrc/tools/compilationunits.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -cd "$(dirname "$0")" +cd ${0%/*} WORKDIR=../.tmp @@ -9,8 +9,8 @@ CPP="cc -xc -E" declare -a QCCDEFS=( -DNDEBUG=1 + -DXONOTIC=1 -DWATERMARK="\"$(git describe --tags --dirty='~')\"" - -DDEBUGPATHING=0 ) QCCDEFS="${QCCDEFS[@]}" @@ -34,23 +34,22 @@ QCCFLAGS="${QCCFLAGS[@]} ${NOWARN[@]}" cd .. function check1() { - declare -l base="${1}" - MODE=${2} - declare -l file="${3}" + declare -l prog="${1}" + declare -l file="${2}" + MODE=${prog} qpp ${file} test.dat \ - -include lib/_all.inc -include ${base}/_all.qh \ - -I. ${QCCIDENT} ${QCCDEFS} -D${MODE} > ${WORKDIR}/${MODE}.qc - qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${MODE}.qc >/dev/null + -include lib/_all.inc -include ${prog}/_all.qh \ + -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${prog}.qc + qcc ${QCCFLAGS} -o ../${WORKDIR}/test.dat ../${WORKDIR}/${prog}.qc >/dev/null } function check() { - declare -l base="${1}" - MODE=${2} - find ${base} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do - check1 ${base} ${MODE} ${file} + declare -l prog="${1}" + find ${prog} -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do + check1 ${prog} ${file} done } -check client CSQC -check server SVQC -check menu MENUQC +check client +check server +check menu diff --git a/qcsrc/tools/genmod.sh b/qcsrc/tools/genmod.sh index b4c4fc3048..9a3ba10987 100755 --- a/qcsrc/tools/genmod.sh +++ b/qcsrc/tools/genmod.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -cd "$(dirname "$0")" +cd ${0%/*} cd .. ROOT=$PWD/ @@ -12,26 +12,42 @@ function genmod() { echo '// generated file; do not modify' > ${MOD}.inc echo '// generated file; do not modify' > ${MOD}.qh for f in $(ls | sort -k 1,1 -t .); do - if [[ "$f" == cl_* ]]; then if [[ -f "${f#cl_}" ]]; then continue; fi; fi - if [[ "$f" == sv_* ]]; then if [[ -f "${f#sv_}" ]]; then continue; fi; fi - if [[ "$f" == ui_* ]]; then if [[ -f "${f#ui_}" ]]; then continue; fi; fi + if [[ "$f" == cl_* ]]; then f="${f#cl_}"; if [[ -f "$f" ]]; then continue; fi + elif [[ "$f" == sv_* ]]; then f="${f#sv_}"; if [[ -f "$f" ]]; then continue; fi + elif [[ "$f" == ui_* ]]; then f="${f#ui_}"; if [[ -f "$f" ]]; then continue; fi + fi if [[ "$f" == *.qc ]]; then - echo "#include <${CTX}$f>" >> ${MOD}.inc - echo "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh + if [[ -f "$f" ]]; then echo -e "#include <${CTX}$f>" >> ${MOD}.inc; fi + if [[ -f "${f%.qc}.qh" ]]; then echo -e "#include <${CTX}${f%.qc}.qh>" >> ${MOD}.qh; fi if [[ -f "cl_$f" ]]; then echo -e "#ifdef CSQC\n #include <${CTX}cl_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "cl_${f%.qc}.qh" ]]; then echo -e "#ifdef CSQC\n #include <${CTX}cl_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi if [[ -f "sv_$f" ]]; then echo -e "#ifdef SVQC\n #include <${CTX}sv_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "sv_${f%.qc}.qh" ]]; then echo -e "#ifdef SVQC\n #include <${CTX}sv_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi if [[ -f "ui_$f" ]]; then echo -e "#ifdef MENUQC\n #include <${CTX}ui_$f>\n#endif" >> ${MOD}.inc; fi + if [[ -f "ui_${f%.qc}.qh" ]]; then echo -e "#ifdef MENUQC\n #include <${CTX}ui_${f%.qc}.qh>\n#endif" >> ${MOD}.qh; fi fi done - # echo >> ${MOD} + declare -l rec=1 + if [[ -f "_all.inc" ]]; then rec=0; fi for f in *; do if [ -d "$f" ]; then (cd -- "$f" && genmod) - # echo "#include \"$f/MOD\"" >> ${MOD} + if [[ $rec == 1 ]]; then + rec=2 + echo >> ${MOD}.inc + echo >> ${MOD}.qh + fi + if [[ $rec != 0 ]]; then + declare -l mod=_mod + if [[ -f "$f/_all.inc" ]]; then mod=_all; fi + echo "#include <${CTX}$f/${mod}.inc>" >> ${MOD}.inc + echo "#include <${CTX}$f/${mod}.qh>" >> ${MOD}.qh + fi fi; done } (cd lib; genmod) (cd common; genmod) +(cd ecs; genmod) (cd client; genmod) (cd server; genmod) (cd menu; genmod) diff --git a/qcsrc/tools/headerstyle.sh b/qcsrc/tools/headerstyle.sh index ef17315926..924083166f 100755 --- a/qcsrc/tools/headerstyle.sh +++ b/qcsrc/tools/headerstyle.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -cd "$(dirname "$0")" +cd ${0%/*} cd .. function startswith() { @@ -17,14 +17,14 @@ function check() { find "$base" -type f -name '*.qc' -print0 | sort -z | while read -r -d '' file; do echo "$file" declare -l file_h="${file%.qc}.qh" - if [ ! -f "$file_h" ]; then echo "#pragma once" > "$file_h"; fi + if [[ ! -f "$file_h" ]]; then echo "#pragma once" > "$file_h"; fi include=$(basename "$file") include="${include%.qc}.qh" include="#include \"${include}\"" startswith "$file" "$include" done - find "$base" -type f -name '*.qh' -print0 | sort -z | while read -r -d '' file; do + find "$base" -type f -name '*.qh' -a \! -name '_mod.qh' -print0 | sort -z | while read -r -d '' file; do echo "$file" startswith "$file" "#pragma once" done @@ -33,3 +33,4 @@ function check() { check client check server check menu +check common diff --git a/qcsrc/tools/qcc.sh b/qcsrc/tools/qcc.sh index b115c196ec..9951ec2fd7 100755 --- a/qcsrc/tools/qcc.sh +++ b/qcsrc/tools/qcc.sh @@ -12,15 +12,23 @@ QCCFLAGS=${QCCFLAGS} function qpp() { IN=$1 OUT=$2 - >&2 echo + ${CPP} ${@:3} ${IN} + case ${MODE} in + client) DEFS="-DGAMEQC -DCSQC" + ;; + menu) DEFS="-DMENUQC" + ;; + server) DEFS="-DGAMEQC -DSVQC" + ;; + esac + >&2 echo + ${CPP} ${@:3} ${DEFS} ${IN} set +e # additional information - ${CPP} ${@:3} \ + ${CPP} ${@:3} ${DEFS} \ -dM 1>${WORKDIR}/${MODE}_macros.txt \ -H 2>${WORKDIR}/${MODE}_includes.txt \ ${IN} # main step - ${CPP} ${@:3} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt + ${CPP} ${@:3} ${DEFS} -MMD -MP -MT ${OUT} -Wall -Wundef -Werror ${IN} -o ${WORKDIR}/${MODE}.txt err=$? set -e if [ ${err} -ne 0 ]; then return ${err}; fi @@ -37,16 +45,6 @@ $(return >/dev/null 2>&1) || { MODE=$1 OUT=$2 IN=$3 - - case ${MODE} in - client) PROG=CSQC - ;; - menu) PROG=MENUQC - ;; - server) PROG=SVQC - ;; - esac - - qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} -D${PROG} > ${WORKDIR}/${MODE}.qc + qpp ${IN} ${OUT} -I. ${QCCIDENT} ${QCCDEFS} > ${WORKDIR}/${MODE}.qc qcc ${QCCFLAGS} -o ${OUT} ../${WORKDIR}/${MODE}.qc } diff --git a/qcsrc/tools/whitespace.sh b/qcsrc/tools/whitespace.sh index 5662337411..594c60ea29 100755 --- a/qcsrc/tools/whitespace.sh +++ b/qcsrc/tools/whitespace.sh @@ -1,6 +1,6 @@ -#!/bin/bash +#!/usr/bin/env bash set -eu -cd "$(dirname "$0")" +cd ${0%/*} cd .. function check() { @@ -14,6 +14,7 @@ function check() { check lib check common +check ecs check client check server check menu diff --git a/qcsrc/uncrustify.cfg b/qcsrc/uncrustify.cfg index 98cdab9dd5..b598727223 100644 --- a/qcsrc/uncrustify.cfg +++ b/qcsrc/uncrustify.cfg @@ -418,7 +418,7 @@ sp_angle_shift = ignore # ignore/add/remove/force sp_permit_cpp11_shift = false # false/true # Add or remove space before '(' of 'if', 'for', 'switch', and 'while' -sp_before_sparen = add # ignore/add/remove/force #force +sp_before_sparen = force # ignore/add/remove/force #force # Add or remove space inside if-condition '(' and ')' # NOTE: is 68 worse than ignore @@ -431,10 +431,10 @@ sp_inside_sparen_close = ignore # ignore/add/remove/force #f sp_inside_sparen_open = ignore # ignore/add/remove/force #force # Add or remove space after ')' of 'if', 'for', 'switch', and 'while' -sp_after_sparen = add # ignore/add/remove/force +sp_after_sparen = force # ignore/add/remove/force # Add or remove space between ')' and '{' of 'if', 'for', 'switch', and 'while' -sp_sparen_brace = add # ignore/add/remove/force +sp_sparen_brace = force # ignore/add/remove/force # Add or remove space between 'invariant' and '(' in the D language. sp_invariant_paren = ignore # ignore/add/remove/force #ignore @@ -630,11 +630,11 @@ sp_macro = remove # ignore/add/remove/force #f sp_macro_func = remove # ignore/add/remove/force #force # Add or remove space between 'else' and '{' if on the same line -sp_else_brace = add # ignore/add/remove/force +sp_else_brace = force # ignore/add/remove/force # Add or remove space between '}' and 'else' if on the same line # WARNING: Code doesn't seem to use this feature - delete from the config? -sp_brace_else = ignore # ignore/add/remove/force +sp_brace_else = force # ignore/add/remove/force # Add or remove space between '}' and the name of a typedef on the same line sp_brace_typedef = add # ignore/add/remove/force @@ -1116,17 +1116,17 @@ nl_union_brace = ignore # ignore/add/remove/force # Add or remove newline between 'if' and '{' # NOTE: is 136 worse than ignore -nl_if_brace = add # ignore/add/remove/force +nl_if_brace = remove # ignore/add/remove/force # Add or remove newline between '}' and 'else' -nl_brace_else = add # ignore/add/remove/force +nl_brace_else = remove # ignore/add/remove/force # Add or remove newline between 'else if' and '{' # If set to ignore, nl_if_brace is used instead -nl_elseif_brace = add # ignore/add/remove/force +nl_elseif_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and '{' -nl_else_brace = add # ignore/add/remove/force +nl_else_brace = remove # ignore/add/remove/force # Add or remove newline between 'else' and 'if' nl_else_if = remove # ignore/add/remove/force @@ -1137,7 +1137,7 @@ nl_brace_finally = ignore # ignore/add/remove/force # Add or remove newline between 'finally' and '{' # WARNING: Code doesn't seem to use this feature - delete from the config? -nl_finally_brace = ignore # ignore/add/remove/force +nl_finally_brace = remove # ignore/add/remove/force # Add or remove newline between 'try' and '{' # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1149,7 +1149,7 @@ nl_getset_brace = ignore # ignore/add/remove/force # Add or remove newline between 'for' and '{' # NOTE: is 109 worse than ignore -nl_for_brace = add # ignore/add/remove/force +nl_for_brace = remove # ignore/add/remove/force # Add or remove newline between 'catch' and '{' # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1161,7 +1161,7 @@ nl_brace_catch = ignore # ignore/add/remove/force # Add or remove newline between 'while' and '{' # NOTE: is 22 worse than ignore -nl_while_brace = add # ignore/add/remove/force +nl_while_brace = remove # ignore/add/remove/force # Add or remove newline between 'scope (x)' and '{' (D) # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1570,27 +1570,27 @@ nl_between_annotation = ignore # ignore/add/remove/force # # Add or remove braces on single-line 'do' statement -mod_full_brace_do = add # ignore/add/remove/force +mod_full_brace_do = force # ignore/add/remove/force # Add or remove braces on single-line 'for' statement # NOTE: is 3 worse than ignore -mod_full_brace_for = remove # ignore/add/remove/force +mod_full_brace_for = force # ignore/add/remove/force # Add or remove braces on single-line function definitions. (Pawn) mod_full_brace_function = ignore # ignore/add/remove/force #ignore # Add or remove braces on single-line 'if' statement. Will not remove the braces if they contain an 'else'. -mod_full_brace_if = add # ignore/add/remove/force +mod_full_brace_if = force # ignore/add/remove/force # Make all if/elseif/else statements in a chain be braced or not. Overrides mod_full_brace_if. # If any must be braced, they are all braced. If all can be unbraced, then the braces are removed. -mod_full_brace_if_chain = true # false/true #force +mod_full_brace_if_chain = false # false/true #force # Don't remove braces around statements that span N newlines mod_full_brace_nl = 2 # number #force # Add or remove braces on single-line 'while' statement -mod_full_brace_while = remove # ignore/add/remove/force +mod_full_brace_while = force # ignore/add/remove/force # Add or remove braces on single-line 'using ()' statement # WARNING: Code doesn't seem to use this feature - delete from the config? @@ -1608,7 +1608,7 @@ mod_pawn_semicolon = false # false/true mod_full_paren_if_bool = false # false/true # Whether to remove superfluous semicolons -mod_remove_extra_semicolon = false # false/true +mod_remove_extra_semicolon = true # false/true # If a function body exceeds the specified number of newlines and doesn't have a comment after # the close brace, a comment will be added. diff --git a/scripts/fireball.shader b/scripts/fireball.shader index 822be77f72..8df3e2a558 100644 --- a/scripts/fireball.shader +++ b/scripts/fireball.shader @@ -1,10 +1,7 @@ -fireball +fireball_plasma { - { - map textures/fireball - tcgen environment - } - { - map $lightmap - } + { + map textures/fireball_plasma.tga + tcMod scroll 0.03 0.001 + } } diff --git a/scripts/luma.shader b/scripts/luma.shader index ab2d0c6112..b6ee3cd8ae 100644 --- a/scripts/luma.shader +++ b/scripts/luma.shader @@ -423,3 +423,25 @@ g_uzi_luma blendfunc blend } } + +g_ok_hmg_luma +{ + deformVertexes autosprite + cull none + nopicmip + { + map models/weapons/g_ok_hmg_luma + blendfunc blend + } +} + +g_ok_rl_luma +{ + deformVertexes autosprite + cull none + nopicmip + { + map models/weapons/g_ok_rl_luma + blendfunc blend + } +} diff --git a/scripts/simpleitems.shader b/scripts/simpleitems.shader index 92e9e5affd..3a7d11eafe 100644 --- a/scripts/simpleitems.shader +++ b/scripts/simpleitems.shader @@ -300,6 +300,32 @@ g_tuba_simple // tuba } } +g_ok_hmg_simple // heavy machinegun +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_ok_hmg_simple + blendfunc blend + + } +} + +g_ok_rl_simple // rocket propelled chainsaw +{ + deformVertexes autosprite + cull none + nopicmip + + { + map models/weapons/g_ok_rl_simple + blendfunc blend + + } +} + ////////// ARMOR + HEALTH ITEMS ////////// ///// ARMOR ///// diff --git a/textures/fireball.tga b/textures/fireball.tga index 53b47a192c..c631e8c880 100644 Binary files a/textures/fireball.tga and b/textures/fireball.tga differ diff --git a/textures/fireball_gloss.tga b/textures/fireball_gloss.tga index 8cd8b25bdc..eaf50d0a2e 100644 Binary files a/textures/fireball_gloss.tga and b/textures/fireball_gloss.tga differ diff --git a/textures/fireball_glow.tga b/textures/fireball_glow.tga index c949f92a47..358784d8fd 100644 Binary files a/textures/fireball_glow.tga and b/textures/fireball_glow.tga differ diff --git a/textures/fireball_norm.tga b/textures/fireball_norm.tga new file mode 100644 index 0000000000..b0bd3f93cc Binary files /dev/null and b/textures/fireball_norm.tga differ diff --git a/textures/fireball_pants.tga b/textures/fireball_pants.tga new file mode 100644 index 0000000000..2fbc09a4ed Binary files /dev/null and b/textures/fireball_pants.tga differ diff --git a/textures/fireball_shirt.tga b/textures/fireball_shirt.tga new file mode 100644 index 0000000000..3d93916836 Binary files /dev/null and b/textures/fireball_shirt.tga differ diff --git a/textures/items/a_rocket_bottom.jpg b/textures/items/a_rocket_bottom.jpg new file mode 100644 index 0000000000..b2db59520c Binary files /dev/null and b/textures/items/a_rocket_bottom.jpg differ