From: Mario Date: Sun, 11 Aug 2019 10:34:55 +0000 (+0000) Subject: Merge branch 'martin-t/whoosh' into 'master' X-Git-Tag: xonotic-v0.8.5~1406 X-Git-Url: https://de.git.xonotic.org/?p=xonotic%2Fxonotic-data.pk3dir.git;a=commitdiff_plain;h=97ead695d25a6f24ee9d9837664e05456692af9d;hp=17659a0446f9789974a93b5544f7bf85317c8363 Merge branch 'martin-t/whoosh' into 'master' Fixes for nex whoosh See merge request xonotic/xonotic-data.pk3dir!660 --- diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 81af16137d..dd18ca4a70 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -29,7 +29,7 @@ test_sv_game: - wget -O data/maps/stormkeep.waypoints https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints - wget -O data/maps/stormkeep.waypoints.cache https://gitlab.com/xonotic/xonotic-maps.pk3dir/raw/master/maps/stormkeep.waypoints.cache - make - - EXPECT=6e591fe90a6b90e62fdfd531636dd6eb + - EXPECT=0bff07a368ae61253bf725b0a7e794e7 - HASH=$(${ENGINE} -noconfig -nohome +exec serverbench.cfg | tee /dev/stderr | grep '^:' diff --git a/.tx/merge-base b/.tx/merge-base index c0a2b39ecd..27d0db5ace 100644 --- a/.tx/merge-base +++ b/.tx/merge-base @@ -1 +1 @@ -Mon Jul 15 07:24:31 CEST 2019 +Tue Jul 30 07:24:47 CEST 2019 diff --git a/_hud_common.cfg b/_hud_common.cfg index 9c29d18921..c860be99ce 100644 --- a/_hud_common.cfg +++ b/_hud_common.cfg @@ -117,7 +117,7 @@ seta hud_panel_scoreboard_minwidth 0.6 "minimum width of the scoreboard" seta hud_panel_scoreboard_accuracy_showdelay 2 "how long to delay displaying accuracy below the scoreboard if it's too far down" seta hud_panel_scoreboard_accuracy_showdelay_minpos 0.75 "delay displaying the accuracy panel only if its position is lower than this percentage of the screen height from the top" -seta hud_panel_scoreboard_team_size_position 0 "where to show the team size (0 = do not show, 1 = left of scoreboard, 2 = right of scoreboard, and move team scores to the right)" +seta hud_panel_scoreboard_team_size_position 0 "where to show the team size (0 = do not show, 1 = left of scoreboard, 2 = right of scoreboard), will move team scores to the other side if necessary" // hud panel aliases alias quickmenu "cl_cmd hud quickmenu ${* ?}" diff --git a/bal-wep-mario.cfg b/bal-wep-mario.cfg index 45a75349be..c838e00586 100644 --- a/bal-wep-mario.cfg +++ b/bal-wep-mario.cfg @@ -71,7 +71,7 @@ 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_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 diff --git a/bal-wep-nexuiz25.cfg b/bal-wep-nexuiz25.cfg index b1a83000f6..a27427305c 100644 --- a/bal-wep-nexuiz25.cfg +++ b/bal-wep-nexuiz25.cfg @@ -71,7 +71,7 @@ set g_balance_machinegun_burst_ammo 3 set g_balance_machinegun_burst_animtime 0.75 set g_balance_machinegun_burst_refire 0.05 set g_balance_machinegun_burst_refire2 0.75 -set g_balance_machinegun_burst_speed 0 +set g_balance_machinegun_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 30 diff --git a/bal-wep-samual.cfg b/bal-wep-samual.cfg index 1816f3f2e9..4a5827d2b0 100644 --- a/bal-wep-samual.cfg +++ b/bal-wep-samual.cfg @@ -86,7 +86,7 @@ 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_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 diff --git a/bal-wep-xdf.cfg b/bal-wep-xdf.cfg index 13bdc529a9..a9592ac1f5 100644 --- a/bal-wep-xdf.cfg +++ b/bal-wep-xdf.cfg @@ -71,7 +71,7 @@ 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_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 diff --git a/bal-wep-xonotic.cfg b/bal-wep-xonotic.cfg index 5c6ace7abd..c730ebc1ed 100644 --- a/bal-wep-xonotic.cfg +++ b/bal-wep-xonotic.cfg @@ -71,7 +71,7 @@ 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_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 diff --git a/bal-wep-xpm.cfg b/bal-wep-xpm.cfg index a5438d1b7c..6dea1ed6ea 100644 --- a/bal-wep-xpm.cfg +++ b/bal-wep-xpm.cfg @@ -71,7 +71,7 @@ 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_burst_spread 0 set g_balance_machinegun_first 1 set g_balance_machinegun_first_ammo 1 set g_balance_machinegun_first_damage 14 diff --git a/common.zh_CN.po b/common.zh_CN.po index e7d502a567..5ba567a63f 100644 --- a/common.zh_CN.po +++ b/common.zh_CN.po @@ -18,8 +18,8 @@ msgstr "" "Project-Id-Version: Xonotic\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2019-05-19 07:23+0200\n" -"PO-Revision-Date: 2019-06-14 06:43+0000\n" -"Last-Translator: Liang Liu \n" +"PO-Revision-Date: 2019-07-30 05:15+0000\n" +"Last-Translator: 杜茂森 \n" "Language-Team: Chinese (China) (http://www.transifex.com/team-xonotic/" "xonotic/language/zh_CN/)\n" "Language: zh_CN\n" @@ -394,11 +394,11 @@ msgstr "QMCMD^掉落武器, icon" #: qcsrc/client/hud/panel/quickmenu.qc:806 msgid "QMCMD^dropped weapon %w^7 (l:%l^7)" -msgstr "" +msgstr "QMCMD^掉落武器 %w^7 (l:%l^7)" #: qcsrc/client/hud/panel/quickmenu.qc:807 msgid "QMCMD^drop flag/key, icon" -msgstr "" +msgstr "QMCMD^掉落旗帜/钥匙, icon" #: qcsrc/client/hud/panel/quickmenu.qc:807 msgid "QMCMD^dropped flag/key %w^7 (l:%l^7)" diff --git a/qcsrc/client/autocvars.qh b/qcsrc/client/autocvars.qh index 9300293408..59ecb04d23 100644 --- a/qcsrc/client/autocvars.qh +++ b/qcsrc/client/autocvars.qh @@ -268,7 +268,6 @@ 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; int autocvar_hud_panel_modicons_freezetag_layout; bool autocvar_hud_panel_notify; float autocvar_hud_panel_notify_fadetime; diff --git a/qcsrc/client/hud/panel/modicons.qc b/qcsrc/client/hud/panel/modicons.qc index 306bb6f8a3..18ec37d47e 100644 --- a/qcsrc/client/hud/panel/modicons.qc +++ b/qcsrc/client/hud/panel/modicons.qc @@ -9,8 +9,6 @@ // Mod icons (#10) -bool mod_active; // is there any active mod icon? - void DrawCAItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) { TC(int, layout); TC(int, i); @@ -84,405 +82,6 @@ void HUD_Mod_CA(vector myPos, vector mySize) } } -// CTF HUD modicon section -int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame -int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status -float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed - -void HUD_Mod_CTF_Reset() -{ - redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0; - redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0; - 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; - vector flag_size; - float f; // every function should have that - - 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; - pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3; - neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3; - - 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 (nteams & BIT(2)) - yellowflag = 2; - if (nteams & BIT(3)) - pinkflag = 3; - ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor? - } - - // when status CHANGES, set old status into prevstatus and current status into status - #define X(team) MACRO_BEGIN \ - if (team##flag != team##flag_prevframe) { \ - team##flag_statuschange_time = time; \ - team##flag_prevstatus = team##flag_prevframe; \ - team##flag_prevframe = team##flag; \ - } \ - team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time; \ - MACRO_END - X(red); - X(blue); - X(yellow); - X(pink); - X(neutral); - #undef X - - const float BLINK_FACTOR = 0.15; - const float BLINK_BASE = 0.85; - // note: - // RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2) - // thus - // BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2) - // ensure RMS == 1 - const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz - - #define X(team, cond) \ - string team##_icon = string_null, team##_icon_prevstatus = string_null; \ - int team##_alpha, team##_alpha_prevstatus; \ - team##_alpha = team##_alpha_prevstatus = 1; \ - MACRO_BEGIN \ - switch (team##flag) { \ - case 1: team##_icon = "flag_" #team "_taken"; break; \ - case 2: team##_icon = "flag_" #team "_lost"; break; \ - case 3: team##_icon = "flag_" #team "_carrying"; team##_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \ - default: \ - if ((stat_items & CTF_SHIELDED) && (cond)) { \ - team##_icon = "flag_" #team "_shielded"; \ - } else { \ - team##_icon = string_null; \ - } \ - break; \ - } \ - switch (team##flag_prevstatus) { \ - case 1: team##_icon_prevstatus = "flag_" #team "_taken"; break; \ - case 2: team##_icon_prevstatus = "flag_" #team "_lost"; break; \ - case 3: team##_icon_prevstatus = "flag_" #team "_carrying"; team##_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \ - default: \ - if (team##flag == 3) { \ - team##_icon_prevstatus = "flag_" #team "_carrying"; /* make it more visible */\ - } else if((stat_items & CTF_SHIELDED) && (cond)) { \ - team##_icon_prevstatus = "flag_" #team "_shielded"; \ - } else { \ - team##_icon_prevstatus = string_null; \ - } \ - break; \ - } \ - MACRO_END - 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 (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; - case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break; - } - - if (mySize_x > mySize_y) { - size1 = mySize_x; - size2 = mySize_y; - e1 = eX; - e2 = eY; - } else { - size1 = mySize_y; - size2 = mySize_x; - e1 = eY; - e2 = eX; - } - - switch (myteam) { - default: - case NUM_TEAM_1: { - redflag_pos = pos; - blueflag_pos = pos + eX * fs2 * size1; - yellowflag_pos = pos - eX * fs2 * size1; - pinkflag_pos = pos + eX * fs3 * size1; - break; - } - case NUM_TEAM_2: { - redflag_pos = pos + eX * fs2 * size1; - blueflag_pos = pos; - yellowflag_pos = pos - eX * fs2 * size1; - pinkflag_pos = pos + eX * fs3 * size1; - break; - } - case NUM_TEAM_3: { - redflag_pos = pos + eX * fs3 * size1; - blueflag_pos = pos - eX * fs2 * size1; - yellowflag_pos = pos; - pinkflag_pos = pos + eX * fs2 * size1; - break; - } - case NUM_TEAM_4: { - redflag_pos = pos - eX * fs2 * size1; - blueflag_pos = pos + eX * fs3 * size1; - yellowflag_pos = pos + eX * fs2 * size1; - pinkflag_pos = pos; - break; - } - } - neutralflag_pos = pos; - flag_size = e1 * fs * size1 + e2 * size2; - - #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) \ - drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \ - MACRO_END - X(red); - X(blue); - X(yellow); - X(pink); - X(neutral); - #undef X -} - -// Keyhunt HUD modicon section -vector KH_SLOTS[4]; - -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; - - for(i = 0; i < 4; ++i) - { - key_state = (bitshift(state, i * -5) & 31) - 1; - - if(key_state == -1) - continue; - - if(key_state == 30) - { - ++carrying_keys; - key_state = myteam; - } - - switch(key_state) - { - case NUM_TEAM_1: ++team1_keys; break; - case NUM_TEAM_2: ++team2_keys; break; - case NUM_TEAM_3: ++team3_keys; break; - case NUM_TEAM_4: ++team4_keys; break; - case 29: ++dropped_keys; break; - } - - ++all_keys; - } - - // Calculate slot measurements - vector slot_size; - if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x) - { - // Quadratic arrangement - slot_size = vec2(mySize.x * 0.5, mySize.y * 0.5); - KH_SLOTS[0] = pos; - KH_SLOTS[1] = pos + eX * slot_size.x; - KH_SLOTS[2] = pos + eY * slot_size.y; - KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y; - } - else - { - if(mySize.x > mySize.y) - { - // Horizontal arrangement - slot_size = vec2(mySize.x / all_keys, mySize.y); - for(i = 0; i < all_keys; ++i) - KH_SLOTS[i] = pos + eX * slot_size.x * i; - } - else - { - // Vertical arrangement - slot_size = vec2(mySize.x, mySize.y / all_keys); - for(i = 0; i < all_keys; ++i) - KH_SLOTS[i] = pos + eY * slot_size.y * i; - } - } - - // Make icons blink in case of RUN HERE - - 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; - case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break; - 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 - - i = 0; - - while(team1_keys--) - if(myteam == NUM_TEAM_1 && carrying_keys) - { - drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - --carrying_keys; - } - else - drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - - while(team2_keys--) - if(myteam == NUM_TEAM_2 && carrying_keys) - { - drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - --carrying_keys; - } - else - drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - - while(team3_keys--) - if(myteam == NUM_TEAM_3 && carrying_keys) - { - drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - --carrying_keys; - } - else - drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - - while(team4_keys--) - if(myteam == NUM_TEAM_4 && carrying_keys) - { - drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - --carrying_keys; - } - else - drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); - - while(dropped_keys--) - drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); -} - -// Keepaway HUD mod icon -int kaball_prevstatus; // last remembered status -float kaball_statuschange_time; // time when the status changed - -// we don't need to reset for keepaway since it immediately -// autocorrects prevstatus as to if the player has the ball or not - -void HUD_Mod_Keepaway(vector pos, vector mySize) -{ - mod_active = 1; // keepaway should always show the mod HUD - - float BLINK_FACTOR = 0.15; - float BLINK_BASE = 0.85; - float BLINK_FREQ = 5; - float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); - - int stat_items = STAT(ITEMS); - int kaball = (stat_items/IT_KEY1) & 1; - - if(kaball != kaball_prevstatus) - { - kaball_statuschange_time = time; - kaball_prevstatus = kaball; - } - - vector kaball_pos, kaball_size; - - if(mySize.x > mySize.y) { - kaball_pos = pos + eX * 0.25 * mySize.x; - kaball_size = vec2(0.5 * mySize.x, mySize.y); - } else { - kaball_pos = pos + eY * 0.25 * mySize.y; - kaball_size = vec2(mySize.x, 0.5 * mySize.y); - } - - float kaball_statuschange_elapsedtime = time - kaball_statuschange_time; - float f = bound(0, kaball_statuschange_elapsedtime*2, 1); - - if(kaball_prevstatus && f < 1) - drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f); - - if(kaball) - drawpic_aspect_skin(pos, "keepawayball_carrying", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL); -} - - -// Nexball HUD mod icon -void HUD_Mod_NexBall(vector pos, vector mySize) -{ - float nb_pb_starttime, dt, p; - int stat_items; - - stat_items = STAT(ITEMS); - nb_pb_starttime = STAT(NB_METERSTART); - - if (stat_items & IT_KEY1) - mod_active = 1; - else - mod_active = 0; - - //Manage the progress bar if any - if (nb_pb_starttime > 0) - { - dt = (time - nb_pb_starttime) % nb_pb_period; - // one period of positive triangle - p = 2 * dt / nb_pb_period; - if (p > 1) - p = 2 - p; - - HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); - } - - if (stat_items & IT_KEY1) - drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); -} - // Race/CTS HUD mod icons float crecordtime_prev; // last remembered crecordtime float crecordtime_change_time; // time when crecordtime last changed @@ -629,87 +228,6 @@ void HUD_Mod_Race(vector pos, vector mySize) } } -void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) -{ - TC(int, layout); TC(int, i); - float stat = -1; - string pic = ""; - 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; - default: - case 3: stat = STAT(DOM_PPS_PINK); pic = "dom_icon_pink"; color = '1 0 1'; break; - } - float pps_ratio = 0; - if(STAT(DOM_TOTAL_PPS)) - pps_ratio = stat / STAT(DOM_TOTAL_PPS); - - if(mySize.x/mySize.y > aspect_ratio) - { - i = aspect_ratio * mySize.y; - myPos.x = myPos.x + (mySize.x - i) / 2; - mySize.x = i; - } - else - { - i = 1/aspect_ratio * mySize.x; - myPos.y = myPos.y + (mySize.y - i) / 2; - mySize.y = i; - } - - if (layout) // show text too - { - //draw the text - color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max - if (layout == 2) // average pps - drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL); - else // percentage of average pps - drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL); - } - - //draw the icon - drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - if (stat > 0) - { - drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio); - drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); - drawresetcliparea(); - } -} - -void HUD_Mod_Dom(vector myPos, vector mySize) -{ - mod_active = 1; // required in each mod function that always shows something - - int layout = autocvar_hud_panel_modicons_dom_layout; - int rows, columns; - float aspect_ratio; - aspect_ratio = (layout) ? 3 : 1; - rows = HUD_GetRowCount(team_count, mySize, aspect_ratio); - columns = ceil(team_count/rows); - - int i; - float row = 0, column = 0; - vector pos, itemSize; - itemSize = vec2(mySize.x / columns, mySize.y / rows); - for(i=0; i= rows) - { - row = 0; - ++column; - } - } -} - void HUD_ModIcons_SetFunc() { HUD_ModIcons_GameType = gametype.m_modicons; diff --git a/qcsrc/client/hud/panel/modicons.qh b/qcsrc/client/hud/panel/modicons.qh index 6db88c68b3..c90c0991f5 100644 --- a/qcsrc/client/hud/panel/modicons.qh +++ b/qcsrc/client/hud/panel/modicons.qh @@ -1,2 +1,4 @@ #pragma once #include "../panel.qh" + +bool mod_active; // is there any active mod icon? diff --git a/qcsrc/client/main.qh b/qcsrc/client/main.qh index f0f8f1d4be..87951ff894 100644 --- a/qcsrc/client/main.qh +++ b/qcsrc/client/main.qh @@ -45,20 +45,24 @@ entity teamslots[17]; // 17 teams (including "spectator team") .bool ready; .bool 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(); } +IntrusiveList g_radarlinks; +IntrusiveList g_radaricons; +STATIC_INIT(main) +{ + g_drawables = IL_NEW(); + g_drawables_2d = IL_NEW(); + g_radarlinks = IL_NEW(); + g_radaricons = IL_NEW(); +} + +.void(entity) draw; +.void(entity) draw2d; .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; diff --git a/qcsrc/client/view.qc b/qcsrc/client/view.qc index 322abb87e0..60ae3f18a2 100644 --- a/qcsrc/client/view.qc +++ b/qcsrc/client/view.qc @@ -392,86 +392,6 @@ STATIC_INIT(fpscounter_init) showfps_prevfps_time = currentTime; // we must initialize it to avoid an instant low frame sending } -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; -} - -const int polyline_length = 16; -.vector polyline[polyline_length]; -void Porto_Draw(entity this) -{ - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - entity wepent = viewmodels[slot]; - - if (wepent.activeweapon != WEP_PORTO) continue; - if (spectatee_status) continue; - if (WEP_CVAR(porto, secondary)) continue; - if (intermission == 1) continue; - if (intermission == 2) continue; - if (STAT(HEALTH) <= 0) continue; - - vector pos = view_origin; - vector dir = view_forward; - makevectors(((autocvar_chase_active) ? warpzone_save_view_angles : view_angles)); - pos += v_right * -wepent.movedir.y - + v_up * wepent.movedir.z; - - if (wepent.angles_held_status) - { - makevectors(wepent.angles_held); - dir = v_forward; - } - - wepent.polyline[0] = pos; - - int portal_number = 0, portal1_idx = 1, portal_max = 2; - int n = 1 + 2; // 2 lines == 3 points - for (int idx = 0; idx < n && idx < polyline_length - 1; ) - { - traceline(pos, pos + 65536 * dir, true, this); - dir = reflect(dir, trace_plane_normal); - pos = trace_endpos; - wepent.polyline[++idx] = pos; - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) - { - n += 1; - continue; - } - if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) - { - n = max(2, idx); - break; - } - // check size - { - vector ang = vectoangles2(trace_plane_normal, dir); - ang.x = -ang.x; - makevectors(ang); - if (!CheckWireframeBox(this, pos - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) - { - n = max(2, idx); - break; - } - } - portal_number += 1; - if (portal_number >= portal_max) break; - if (portal_number == 1) portal1_idx = idx; - } - for (int idx = 0; idx < n - 1; ++idx) - { - vector p = wepent.polyline[idx], q = wepent.polyline[idx + 1]; - if (idx == 0) p -= view_up * 16; // line from player - vector rgb = (idx < portal1_idx) ? '1 0 0' : '0 0 1'; - Draw_CylindricLine(p, q, 4, "", 1, 0, rgb, 0.5, DRAWFLAG_NORMAL, view_origin); - } - } -} - float drawtime; float avgspeed; vector GetCurrentFov(float fov) @@ -679,6 +599,9 @@ float EnemyHitCheck() float TrueAimCheck(entity wepent) { + if(wepent.activeweapon.spawnflags & WEP_FLAG_NOTRUEAIM) + return SHOTTYPE_HITWORLD; + 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? vector vecs, trueaimpoint, w_shotorg; vector mi, ma, dv; @@ -692,12 +615,6 @@ float TrueAimCheck(entity wepent) switch(wepent.activeweapon) // WEAPONTODO { - case WEP_TUBA: // no aim - case WEP_PORTO: // shoots from eye - case WEP_NEXBALL: // shoots from eye - case WEP_HOOK: // no trueaim - case WEP_MORTAR: // toss curve - return SHOTTYPE_HITWORLD; case WEP_VORTEX: case WEP_OVERKILL_NEX: case WEP_VAPORIZER: diff --git a/qcsrc/client/view.qh b/qcsrc/client/view.qh index 12fd6eb614..f3c1f4139f 100644 --- a/qcsrc/client/view.qh +++ b/qcsrc/client/view.qh @@ -6,8 +6,6 @@ vector crosshair_getcolor(entity this, float health_stat); void calc_followmodel_ofs(entity view); -void Porto_Draw(entity this); - void CSQC_Demo_Camera(); void TrueAim_Init(); diff --git a/qcsrc/common/deathtypes/all.qh b/qcsrc/common/deathtypes/all.qh index beb8e3e912..2169c8bd76 100644 --- a/qcsrc/common/deathtypes/all.qh +++ b/qcsrc/common/deathtypes/all.qh @@ -26,9 +26,8 @@ const int HITTYPE_SECONDARY = BITS(1) << 8; const int HITTYPE_SPLASH = BITS(1) << 9; const int HITTYPE_BOUNCE = BITS(1) << 10; const int HITTYPE_ARMORPIERCE = BITS(1) << 11; -// unused yet -const int HITTYPE_RESERVED = BITS(1) << 12; -const int DEATH_HITTYPEMASK = HITTYPE_SECONDARY | HITTYPE_SPLASH | HITTYPE_BOUNCE | HITTYPE_ARMORPIERCE | HITTYPE_RESERVED; +const int HITTYPE_SOUND = BITS(1) << 12; +const int DEATH_HITTYPEMASK = HITTYPE_SECONDARY | HITTYPE_SPLASH | HITTYPE_BOUNCE | HITTYPE_ARMORPIERCE | HITTYPE_SOUND; // normal deaths begin const int DT_FIRST = BIT(13); diff --git a/qcsrc/common/effects/qc/damageeffects.qc b/qcsrc/common/effects/qc/damageeffects.qc index fe509e3d49..17f48136a6 100644 --- a/qcsrc/common/effects/qc/damageeffects.qc +++ b/qcsrc/common/effects/qc/damageeffects.qc @@ -174,7 +174,6 @@ void DamageEffect(entity this, vector hitorg, float thedamage, int type, int spe NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) { - const float ATTEN_LOW = 0.2; float thedamage, rad, edge, thisdmg; bool hitplayer = false; int species, forcemul; @@ -393,7 +392,7 @@ NET_HANDLE(ENT_CLIENT_DAMAGEINFO, bool isNew) w_random = prandom(); traceline(w_org - normalize(force) * 16, w_org + normalize(force) * 16, MOVE_NOMONSTERS, NULL); - if(trace_fraction < 1 && hitwep != WEP_VORTEX && hitwep != WEP_VAPORIZER) + if(trace_fraction < 1 && !(hitwep.spawnflags & WEP_TYPE_HITSCAN)) w_backoff = trace_plane_normal; else w_backoff = -1 * normalize(force); diff --git a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc index 835073d07d..9a9a654de9 100644 --- a/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc +++ b/qcsrc/common/gamemodes/gamemode/assault/sv_assault.qc @@ -1,5 +1,7 @@ #include "sv_assault.qh" +#include + .entity sprite; #define AS_ROUND_DELAY 5 @@ -347,7 +349,6 @@ bool destructible_heal(entity targ, entity inflictor, float amount, float limit) return true; } -spawnfunc(func_breakable); spawnfunc(func_assault_destructible) { if (!g_assault) { delete(this); return; } diff --git a/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc b/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc index 7bc5a9679c..c6d756fbb8 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/ctf/_mod.inc @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh b/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh index e7fcea7bcf..9a1d181193 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/ctf/_mod.qh @@ -1,5 +1,8 @@ // generated file; do not modify #include +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qc new file mode 100644 index 0000000000..c82e0bce93 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qc @@ -0,0 +1,198 @@ +#include "cl_ctf.qh" + +#include + +// CTF HUD modicon section +int redflag_prevframe, blueflag_prevframe, yellowflag_prevframe, pinkflag_prevframe, neutralflag_prevframe; // status during previous frame +int redflag_prevstatus, blueflag_prevstatus, yellowflag_prevstatus, pinkflag_prevstatus, neutralflag_prevstatus; // last remembered status +float redflag_statuschange_time, blueflag_statuschange_time, yellowflag_statuschange_time, pinkflag_statuschange_time, neutralflag_statuschange_time; // time when the status changed + +void HUD_Mod_CTF_Reset() +{ + redflag_prevstatus = blueflag_prevstatus = yellowflag_prevstatus = pinkflag_prevstatus = neutralflag_prevstatus = 0; + redflag_prevframe = blueflag_prevframe = yellowflag_prevframe = pinkflag_prevframe = neutralflag_prevframe = 0; + 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; + vector flag_size; + float f; // every function should have that + + 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; + pinkflag = (stat_items/CTF_PINK_FLAG_TAKEN) & 3; + neutralflag = (stat_items/CTF_NEUTRAL_FLAG_TAKEN) & 3; + + 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 (nteams & BIT(2)) + yellowflag = 2; + if (nteams & BIT(3)) + pinkflag = 3; + ctf_oneflag = neutralflag = 0; // disable neutral flag in hud editor? + } + + // when status CHANGES, set old status into prevstatus and current status into status + #define X(team) MACRO_BEGIN \ + if (team##flag != team##flag_prevframe) { \ + team##flag_statuschange_time = time; \ + team##flag_prevstatus = team##flag_prevframe; \ + team##flag_prevframe = team##flag; \ + } \ + team##flag_statuschange_elapsedtime = time - team##flag_statuschange_time; \ + MACRO_END + X(red); + X(blue); + X(yellow); + X(pink); + X(neutral); + #undef X + + const float BLINK_FACTOR = 0.15; + const float BLINK_BASE = 0.85; + // note: + // RMS = sqrt(BLINK_BASE^2 + 0.5 * BLINK_FACTOR^2) + // thus + // BLINK_BASE = sqrt(RMS^2 - 0.5 * BLINK_FACTOR^2) + // ensure RMS == 1 + const float BLINK_FREQ = 5; // circle frequency, = 2*pi*frequency in hertz + + #define X(team, cond) \ + string team##_icon = string_null, team##_icon_prevstatus = string_null; \ + int team##_alpha, team##_alpha_prevstatus; \ + team##_alpha = team##_alpha_prevstatus = 1; \ + MACRO_BEGIN \ + switch (team##flag) { \ + case 1: team##_icon = "flag_" #team "_taken"; break; \ + case 2: team##_icon = "flag_" #team "_lost"; break; \ + case 3: team##_icon = "flag_" #team "_carrying"; team##_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \ + default: \ + if ((stat_items & CTF_SHIELDED) && (cond)) { \ + team##_icon = "flag_" #team "_shielded"; \ + } else { \ + team##_icon = string_null; \ + } \ + break; \ + } \ + switch (team##flag_prevstatus) { \ + case 1: team##_icon_prevstatus = "flag_" #team "_taken"; break; \ + case 2: team##_icon_prevstatus = "flag_" #team "_lost"; break; \ + case 3: team##_icon_prevstatus = "flag_" #team "_carrying"; team##_alpha_prevstatus = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); break; \ + default: \ + if (team##flag == 3) { \ + team##_icon_prevstatus = "flag_" #team "_carrying"; /* make it more visible */\ + } else if((stat_items & CTF_SHIELDED) && (cond)) { \ + team##_icon_prevstatus = "flag_" #team "_shielded"; \ + } else { \ + team##_icon_prevstatus = string_null; \ + } \ + break; \ + } \ + MACRO_END + 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 (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; + case 4: fs = 0.75; fs2 = 0.25; fs3 = 0.5; break; + } + + if (mySize_x > mySize_y) { + size1 = mySize_x; + size2 = mySize_y; + e1 = eX; + e2 = eY; + } else { + size1 = mySize_y; + size2 = mySize_x; + e1 = eY; + e2 = eX; + } + + switch (myteam) { + default: + case NUM_TEAM_1: { + redflag_pos = pos; + blueflag_pos = pos + eX * fs2 * size1; + yellowflag_pos = pos - eX * fs2 * size1; + pinkflag_pos = pos + eX * fs3 * size1; + break; + } + case NUM_TEAM_2: { + redflag_pos = pos + eX * fs2 * size1; + blueflag_pos = pos; + yellowflag_pos = pos - eX * fs2 * size1; + pinkflag_pos = pos + eX * fs3 * size1; + break; + } + case NUM_TEAM_3: { + redflag_pos = pos + eX * fs3 * size1; + blueflag_pos = pos - eX * fs2 * size1; + yellowflag_pos = pos; + pinkflag_pos = pos + eX * fs2 * size1; + break; + } + case NUM_TEAM_4: { + redflag_pos = pos - eX * fs2 * size1; + blueflag_pos = pos + eX * fs3 * size1; + yellowflag_pos = pos + eX * fs2 * size1; + pinkflag_pos = pos; + break; + } + } + neutralflag_pos = pos; + flag_size = e1 * fs * size1 + e2 * size2; + + #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) \ + drawpic_aspect_skin(team##flag_pos, team##_icon, flag_size, '1 1 1', panel_fg_alpha * team##_alpha * f, DRAWFLAG_NORMAL); \ + MACRO_END + X(red); + X(blue); + X(yellow); + X(pink); + X(neutral); + #undef X +} diff --git a/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qh new file mode 100644 index 0000000000..1cf4bcfc26 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/ctf/cl_ctf.qh @@ -0,0 +1,6 @@ +#pragma once + +#include "ctf.qh" + +void HUD_Mod_CTF_Reset(); +void HUD_Mod_CTF(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc index 2484240a26..939c70e29e 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qc @@ -2593,6 +2593,13 @@ MUTATOR_HOOKFUNCTION(ctf, DropSpecialItems) ctf_Handle_Throw(frag_target, NULL, DROP_THROW); } +MUTATOR_HOOKFUNCTION(ctf, LogDeath_AppendItemCodes) +{ + entity player = M_ARGV(0, entity); + if(player.flagcarried) + M_ARGV(1, string) = strcat(M_ARGV(1, string), "F"); // item codes +} + // ========== // Spawnfuncs diff --git a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh index 679dd85025..b1518bd4fc 100644 --- a/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh +++ b/qcsrc/common/gamemodes/gamemode/ctf/sv_ctf.qh @@ -28,7 +28,8 @@ CLASS(Flag, Pickup) ATTRIB(Flag, m_mins, vector, (PL_MIN_CONST + '0 0 -13') * 1.4); // scaling be damned ATTRIB(Flag, m_maxs, vector, (PL_MAX_CONST + '0 0 -13') * 1.4); ENDCLASS(Flag) -Flag CTF_FLAG; STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } +Flag CTF_FLAG; +STATIC_INIT(Flag) { CTF_FLAG = NEW(Flag); } void ctf_FlagTouch(entity this, entity toucher) { ITEM_HANDLE(Pickup, CTF_FLAG, this, toucher); } // flag constants // for most of these, there is just one question to be asked: WHYYYYY? diff --git a/qcsrc/common/gamemodes/gamemode/domination/_mod.inc b/qcsrc/common/gamemodes/gamemode/domination/_mod.inc index ff9bc11f88..ccaa150df7 100644 --- a/qcsrc/common/gamemodes/gamemode/domination/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/domination/_mod.inc @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/domination/_mod.qh b/qcsrc/common/gamemodes/gamemode/domination/_mod.qh index 0c5e841214..b48a0a9147 100644 --- a/qcsrc/common/gamemodes/gamemode/domination/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/domination/_mod.qh @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qc b/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qc new file mode 100644 index 0000000000..418a843d9b --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qc @@ -0,0 +1,86 @@ +#include "cl_domination.qh" + +#include + +int autocvar_hud_panel_modicons_dom_layout; + +void DrawDomItem(vector myPos, vector mySize, float aspect_ratio, int layout, int i) +{ + TC(int, layout); TC(int, i); + float stat = -1; + string pic = ""; + 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; + default: + case 3: stat = STAT(DOM_PPS_PINK); pic = "dom_icon_pink"; color = '1 0 1'; break; + } + float pps_ratio = 0; + if(STAT(DOM_TOTAL_PPS)) + pps_ratio = stat / STAT(DOM_TOTAL_PPS); + + if(mySize.x/mySize.y > aspect_ratio) + { + i = aspect_ratio * mySize.y; + myPos.x = myPos.x + (mySize.x - i) / 2; + mySize.x = i; + } + else + { + i = 1/aspect_ratio * mySize.x; + myPos.y = myPos.y + (mySize.y - i) / 2; + mySize.y = i; + } + + if (layout) // show text too + { + //draw the text + color *= 0.5 + pps_ratio * (1 - 0.5); // half saturated color at min, full saturated at max + if (layout == 2) // average pps + drawstring_aspect(myPos + eX * mySize.y, ftos_decimals(stat, 2), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL); + else // percentage of average pps + drawstring_aspect(myPos + eX * mySize.y, strcat( ftos(floor(pps_ratio*100 + 0.5)), "%" ), vec2((2/3) * mySize.x, mySize.y), color, panel_fg_alpha, DRAWFLAG_NORMAL); + } + + //draw the icon + drawpic_aspect_skin(myPos, pic, '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + if (stat > 0) + { + drawsetcliparea(myPos.x, myPos.y + mySize.y * (1 - pps_ratio), mySize.y, mySize.y * pps_ratio); + drawpic_aspect_skin(myPos, strcat(pic, "-highlighted"), '1 1 0' * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); + drawresetcliparea(); + } +} + +void HUD_Mod_Dom(vector myPos, vector mySize) +{ + mod_active = 1; // required in each mod function that always shows something + + int layout = autocvar_hud_panel_modicons_dom_layout; + int rows, columns; + float aspect_ratio; + aspect_ratio = (layout) ? 3 : 1; + rows = HUD_GetRowCount(team_count, mySize, aspect_ratio); + columns = ceil(team_count/rows); + + int i; + float row = 0, column = 0; + vector pos, itemSize; + itemSize = vec2(mySize.x / columns, mySize.y / rows); + for(i=0; i= rows) + { + row = 0; + ++column; + } + } +} diff --git a/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qh b/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qh new file mode 100644 index 0000000000..11ab3daca7 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/domination/cl_domination.qh @@ -0,0 +1,3 @@ +#pragma once + +void HUD_Mod_Dom(vector myPos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc index 420f7af78a..2ce6f4493b 100644 --- a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.inc @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh index 145ca49f0a..8536675f3f 100644 --- a/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/keepaway/_mod.qh @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qc b/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qc new file mode 100644 index 0000000000..b2d0874285 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qc @@ -0,0 +1,48 @@ +#include "cl_keepaway.qh" + +#include + +// Keepaway HUD mod icon +int kaball_prevstatus; // last remembered status +float kaball_statuschange_time; // time when the status changed + +// we don't need to reset for keepaway since it immediately +// autocorrects prevstatus as to if the player has the ball or not + +void HUD_Mod_Keepaway(vector pos, vector mySize) +{ + mod_active = 1; // keepaway should always show the mod HUD + + float BLINK_FACTOR = 0.15; + float BLINK_BASE = 0.85; + float BLINK_FREQ = 5; + float kaball_alpha = BLINK_BASE + BLINK_FACTOR * cos(time * BLINK_FREQ); + + int stat_items = STAT(ITEMS); + int kaball = (stat_items/IT_KEY1) & 1; + + if(kaball != kaball_prevstatus) + { + kaball_statuschange_time = time; + kaball_prevstatus = kaball; + } + + vector kaball_pos, kaball_size; + + if(mySize.x > mySize.y) { + kaball_pos = pos + eX * 0.25 * mySize.x; + kaball_size = vec2(0.5 * mySize.x, mySize.y); + } else { + kaball_pos = pos + eY * 0.25 * mySize.y; + kaball_size = vec2(mySize.x, 0.5 * mySize.y); + } + + float kaball_statuschange_elapsedtime = time - kaball_statuschange_time; + float f = bound(0, kaball_statuschange_elapsedtime*2, 1); + + if(kaball_prevstatus && f < 1) + drawpic_aspect_skin_expanding(kaball_pos, "keepawayball_carrying", kaball_size, '1 1 1', panel_fg_alpha * kaball_alpha, DRAWFLAG_NORMAL, f); + + if(kaball) + drawpic_aspect_skin(pos, "keepawayball_carrying", vec2(mySize.x, mySize.y), '1 1 1', panel_fg_alpha * kaball_alpha * f, DRAWFLAG_NORMAL); +} diff --git a/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qh b/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qh new file mode 100644 index 0000000000..7bcc289cd9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keepaway/cl_keepaway.qh @@ -0,0 +1,3 @@ +#pragma once + +void HUD_Mod_Keepaway(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc index 4f44840c41..14802494b9 100644 --- a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.inc @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh index e4143fc84c..defda74978 100644 --- a/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/_mod.qh @@ -1,4 +1,7 @@ // generated file; do not modify +#ifdef CSQC + #include +#endif #ifdef SVQC #include #endif diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qc b/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qc new file mode 100644 index 0000000000..e95275eba9 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qc @@ -0,0 +1,131 @@ +#include "cl_keyhunt.qh" + +#include + +// Keyhunt HUD modicon section +vector KH_SLOTS[4]; + +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; + + for(i = 0; i < 4; ++i) + { + key_state = (bitshift(state, i * -5) & 31) - 1; + + if(key_state == -1) + continue; + + if(key_state == 30) + { + ++carrying_keys; + key_state = myteam; + } + + switch(key_state) + { + case NUM_TEAM_1: ++team1_keys; break; + case NUM_TEAM_2: ++team2_keys; break; + case NUM_TEAM_3: ++team3_keys; break; + case NUM_TEAM_4: ++team4_keys; break; + case 29: ++dropped_keys; break; + } + + ++all_keys; + } + + // Calculate slot measurements + vector slot_size; + if(all_keys == 4 && mySize.x * 0.5 < mySize.y && mySize.y * 0.5 < mySize.x) + { + // Quadratic arrangement + slot_size = vec2(mySize.x * 0.5, mySize.y * 0.5); + KH_SLOTS[0] = pos; + KH_SLOTS[1] = pos + eX * slot_size.x; + KH_SLOTS[2] = pos + eY * slot_size.y; + KH_SLOTS[3] = pos + eX * slot_size.x + eY * slot_size.y; + } + else + { + if(mySize.x > mySize.y) + { + // Horizontal arrangement + slot_size = vec2(mySize.x / all_keys, mySize.y); + for(i = 0; i < all_keys; ++i) + KH_SLOTS[i] = pos + eX * slot_size.x * i; + } + else + { + // Vertical arrangement + slot_size = vec2(mySize.x, mySize.y / all_keys); + for(i = 0; i < all_keys; ++i) + KH_SLOTS[i] = pos + eY * slot_size.y * i; + } + } + + // Make icons blink in case of RUN HERE + + 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; + case NUM_TEAM_2: if(team2_keys == all_keys) alpha = blink; break; + 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 + + i = 0; + + while(team1_keys--) + if(myteam == NUM_TEAM_1 && carrying_keys) + { + drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + --carrying_keys; + } + else + drawpic_aspect_skin(KH_SLOTS[i++], "kh_red_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + + while(team2_keys--) + if(myteam == NUM_TEAM_2 && carrying_keys) + { + drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + --carrying_keys; + } + else + drawpic_aspect_skin(KH_SLOTS[i++], "kh_blue_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + + while(team3_keys--) + if(myteam == NUM_TEAM_3 && carrying_keys) + { + drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + --carrying_keys; + } + else + drawpic_aspect_skin(KH_SLOTS[i++], "kh_yellow_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + + while(team4_keys--) + if(myteam == NUM_TEAM_4 && carrying_keys) + { + drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_carrying", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + --carrying_keys; + } + else + drawpic_aspect_skin(KH_SLOTS[i++], "kh_pink_taken", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); + + while(dropped_keys--) + drawpic_aspect_skin(KH_SLOTS[i++], "kh_dropped", slot_size, '1 1 1', alpha, DRAWFLAG_NORMAL); +} diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qh b/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qh new file mode 100644 index 0000000000..8d650f11d3 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/cl_keyhunt.qh @@ -0,0 +1,3 @@ +#pragma once + +void HUD_Mod_KH(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc index d33696ad37..21d9208bf9 100644 --- a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qc @@ -23,6 +23,7 @@ float autocvar_g_balance_keyhunt_throwvelocity; int autocvar_g_keyhunt_teams_override; // #define KH_PLAYER_USE_ATTACHMENT +// TODO? no model exists for this // #define KH_PLAYER_USE_CARRIEDMODEL #ifdef KH_PLAYER_USE_ATTACHMENT @@ -61,8 +62,6 @@ int kh_Team_ByID(int t) return 0; } -//entity kh_worldkeylist; -.entity kh_worldkeynext; entity kh_controller; //bool kh_tracking_enabled; int kh_teams; @@ -1293,6 +1292,13 @@ MUTATOR_HOOKFUNCTION(kh, HavocBot_ChooseRole) return true; } +MUTATOR_HOOKFUNCTION(kh, LogDeath_AppendItemCodes) +{ + entity player = M_ARGV(0, entity); + if(player.kh_next) + M_ARGV(1, string) = strcat(M_ARGV(1, string), "K"); // item codes +} + MUTATOR_HOOKFUNCTION(kh, DropSpecialItems) { entity frag_target = M_ARGV(0, entity); diff --git a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh index 345a3d166e..66321c3d9d 100644 --- a/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh +++ b/qcsrc/common/gamemodes/gamemode/keyhunt/sv_keyhunt.qh @@ -21,6 +21,9 @@ REGISTER_MUTATOR(kh, false) return 0; } +entity kh_worldkeylist; +.entity kh_worldkeynext; + #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 diff --git a/qcsrc/common/gamemodes/gamemode/nexball/_mod.inc b/qcsrc/common/gamemodes/gamemode/nexball/_mod.inc index 7872767563..b229dcdf50 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/_mod.inc +++ b/qcsrc/common/gamemodes/gamemode/nexball/_mod.inc @@ -1,5 +1,10 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include #ifdef SVQC #include diff --git a/qcsrc/common/gamemodes/gamemode/nexball/_mod.qh b/qcsrc/common/gamemodes/gamemode/nexball/_mod.qh index a1cf544371..0a3d1e2383 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/_mod.qh +++ b/qcsrc/common/gamemodes/gamemode/nexball/_mod.qh @@ -1,5 +1,10 @@ // generated file; do not modify -#include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include #ifdef SVQC #include diff --git a/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qc new file mode 100644 index 0000000000..5a6b42dc46 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qc @@ -0,0 +1,44 @@ +#include "cl_nexball.qh" + +#include + +// Nexball HUD mod icon +void HUD_Mod_NexBall(vector pos, vector mySize) +{ + float nb_pb_starttime, dt, p; + int stat_items; + + stat_items = STAT(ITEMS); + nb_pb_starttime = STAT(NB_METERSTART); + + if (stat_items & IT_KEY1) + mod_active = 1; + else + mod_active = 0; + + //Manage the progress bar if any + if (nb_pb_starttime > 0) + { + dt = (time - nb_pb_starttime) % nb_pb_period; + // one period of positive triangle + p = 2 * dt / nb_pb_period; + if (p > 1) + p = 2 - p; + + HUD_Panel_DrawProgressBar(pos, mySize, "progressbar", p, (mySize.x <= mySize.y), 0, autocvar_hud_progressbar_nexball_color, autocvar_hud_progressbar_alpha * panel_fg_alpha, DRAWFLAG_NORMAL); + } + + if (stat_items & IT_KEY1) + drawpic_aspect_skin(pos, "nexball_carrying", eX * mySize.x + eY * mySize.y, '1 1 1', panel_fg_alpha, DRAWFLAG_NORMAL); +} + +int autocvar_cl_eventchase_nexball = 1; + +REGISTER_MUTATOR(cl_nb, true); + +MUTATOR_HOOKFUNCTION(cl_nb, WantEventchase) +{ + if(autocvar_cl_eventchase_nexball && ISGAMETYPE(NEXBALL) && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) + return true; + return false; +} diff --git a/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qh new file mode 100644 index 0000000000..d0b3731bc5 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/cl_nexball.qh @@ -0,0 +1,3 @@ +#pragma once + +void HUD_Mod_NexBall(vector pos, vector mySize); diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc deleted file mode 100644 index c841079fb7..0000000000 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qc +++ /dev/null @@ -1,984 +0,0 @@ -#include "nexball.qh" - -#ifdef CSQC -int autocvar_cl_eventchase_nexball = 1; - -REGISTER_MUTATOR(cl_nb, true); - -MUTATOR_HOOKFUNCTION(cl_nb, WantEventchase) -{ - if(autocvar_cl_eventchase_nexball && ISGAMETYPE(NEXBALL) && !(WepSet_GetFromStat() & WEPSET(NEXBALL))) - return true; - return false; -} -#endif -#ifdef SVQC -.entity ballcarried; - -int autocvar_g_nexball_goalleadlimit; -#define autocvar_g_nexball_goallimit cvar("g_nexball_goallimit") - -bool autocvar_g_nexball_basketball_jumppad = true; -float autocvar_g_nexball_basketball_bouncefactor; -float autocvar_g_nexball_basketball_bouncestop; -float autocvar_g_nexball_basketball_carrier_highspeed; -bool autocvar_g_nexball_basketball_meter; -float autocvar_g_nexball_basketball_meter_maxpower; -float autocvar_g_nexball_basketball_meter_minpower; -float autocvar_g_nexball_delay_collect; -float autocvar_g_nexball_delay_goal; -float autocvar_g_nexball_delay_start; -bool autocvar_g_nexball_football_jumppad = true; -float autocvar_g_nexball_football_bouncefactor; -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; -float autocvar_g_nexball_safepass_holdtime; -float autocvar_g_nexball_viewmodel_scale; -float autocvar_g_nexball_tackling; -vector autocvar_g_nexball_viewmodel_offset; - -float autocvar_g_balance_nexball_primary_animtime; -float autocvar_g_balance_nexball_primary_refire; -float autocvar_g_balance_nexball_primary_speed; -float autocvar_g_balance_nexball_secondary_animtime; -float autocvar_g_balance_nexball_secondary_force; -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, entity toucher); -void football_touch(entity this, entity toucher); -void ResetBall(entity this); -const int NBM_NONE = 0; -const int NBM_FOOTBALL = 2; -const int NBM_BASKETBALL = 4; -float nexball_mode; - -float OtherTeam(float t) //works only if there are two teams on the map! -{ - entity e; - e = find(NULL, classname, "nexball_team"); - if(e.team == t) - e = find(e, classname, "nexball_team"); - return e.team; -} - -const int ST_NEXBALL_GOALS = 1; -void nb_ScoreRules(int teams) -{ - GameRules_scoring(teams, 0, 0, { - field_team(ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - field(SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); - field(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER); - }); -} - -void LogNB(string mode, entity actor) -{ - string s; - if(!autocvar_sv_eventlog) - return; - s = strcat(":nexball:", mode); - if(actor != NULL) - s = strcat(s, ":", ftos(actor.playerid)); - GameLogEcho(s); -} - -void ball_restart(entity this) -{ - if(this.owner) - DropBall(this, this.owner.origin, '0 0 0'); - ResetBall(this); -} - -void nexball_setstatus(entity this) -{ - this.items &= ~IT_KEY1; - if(this.ballcarried) - { - if(this.ballcarried.teamtime && (this.ballcarried.teamtime < time)) - { - 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 - this.items |= IT_KEY1; - } -} - -void relocate_nexball(entity this) -{ - tracebox(this.origin, BALL_MINS, BALL_MAXS, this.origin, true, this); - if(trace_startsolid) - { - vector o = this.origin; - if (!move_out_of_solid(this)) { - objerror(this, "could not get out of solid at all!"); - } - LOG_INFOF( - "^1NOTE: this map needs FIXING. %s at %s needs to be moved out of solid, e.g. by %s", - this.classname, - vtos(o - '0 0 1'), - vtos(this.origin - o) - ); - this.origin = o; - } -} - -void DropOwner(entity this) -{ - entity ownr; - ownr = this.owner; - DropBall(this, ownr.origin, ownr.velocity); - makevectors(ownr.v_angle.y * '0 1 0'); - ownr.velocity += ('0 0 0.75' - v_forward) * 1000; - UNSET_ONGROUND(ownr); -} - -void GiveBall(entity plyr, entity ball) -{ - .entity weaponentity = weaponentities[0]; // TODO: find ballstealer - entity ownr = ball.owner; - if(ownr) - { - ownr.effects &= ~autocvar_g_nexball_basketball_effects_default; - ownr.ballcarried = NULL; - GameRules_scoring_vip(ownr, false); - if(STAT(NB_METERSTART, ownr)) - { - STAT(NB_METERSTART, ownr) = 0; - ownr.(weaponentity).state = WS_READY; - } - WaypointSprite_Kill(ownr.waypointsprite_attachedforcarrier); - } - else - { - WaypointSprite_Kill(ball.waypointsprite_attachedforcarrier); - } - - //setattachment(ball, plyr, ""); - setorigin(ball, plyr.origin + plyr.view_ofs); - - if(ball.team != plyr.team) - ball.teamtime = time + autocvar_g_nexball_basketball_delay_hold_forteam; - - ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it - ball.weaponentity_fld = weaponentity; - ball.team = plyr.team; - plyr.ballcarried = ball; - GameRules_scoring_vip(plyr, true); - ball.nb_dropper = plyr; - - plyr.effects |= autocvar_g_nexball_basketball_effects_default; - ball.effects &= ~autocvar_g_nexball_basketball_effects_default; - - ball.velocity = '0 0 0'; - set_movetype(ball, MOVETYPE_NONE); - settouch(ball, func_null); - ball.effects |= EF_NOSHADOW; - ball.scale = 1; // scale down. - - WaypointSprite_AttachCarrier(WP_NbBall, plyr, RADARICON_FLAGCARRIER); - WaypointSprite_UpdateRule(plyr.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); - - if(autocvar_g_nexball_basketball_delay_hold) - { - setthink(ball, DropOwner); - ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold; - } - - STAT(WEAPONS, plyr.(weaponentity)) = STAT(WEAPONS, plyr); - plyr.m_switchweapon = plyr.(weaponentity).m_weapon; - STAT(WEAPONS, plyr) = WEPSET(NEXBALL); - Weapon w = WEP_NEXBALL; - w.wr_resetplayer(w, plyr); - plyr.(weaponentity).m_switchweapon = WEP_NEXBALL; - W_SwitchWeapon(plyr, WEP_NEXBALL, weaponentity); -} - -void DropBall(entity ball, vector org, vector vel) -{ - ball.effects |= autocvar_g_nexball_basketball_effects_default; - ball.effects &= ~EF_NOSHADOW; - ball.owner.effects &= ~autocvar_g_nexball_basketball_effects_default; - - setattachment(ball, NULL, ""); - setorigin(ball, org); - set_movetype(ball, MOVETYPE_BOUNCE); - UNSET_ONGROUND(ball); - ball.scale = ball_scale; - ball.velocity = vel; - ball.nb_droptime = time; - settouch(ball, basketball_touch); - setthink(ball, ResetBall); - ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime); - - if(STAT(NB_METERSTART, ball.owner)) - { - STAT(NB_METERSTART, ball.owner) = 0; - .entity weaponentity = ball.weaponentity_fld; - ball.owner.(weaponentity).state = WS_READY; - } - - WaypointSprite_Kill(ball.owner.waypointsprite_attachedforcarrier); - WaypointSprite_Spawn(WP_NbBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); // no health bar please - WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); - - entity e = ball.owner; ball.owner = NULL; - e.ballcarried = NULL; - GameRules_scoring_vip(e, false); -} - -void InitBall(entity this) -{ - if(game_stopped) return; - UNSET_ONGROUND(this); - set_movetype(this, MOVETYPE_BOUNCE); - if(this.classname == "nexball_basketball") - settouch(this, basketball_touch); - else if(this.classname == "nexball_football") - settouch(this, football_touch); - this.cnt = 0; - setthink(this, ResetBall); - this.nextthink = time + autocvar_g_nexball_delay_idle + 3; - this.teamtime = 0; - this.pusher = NULL; - this.team = false; - _sound(this, CH_TRIGGER, this.noise1, VOL_BASE, ATTEN_NORM); - WaypointSprite_Ping(this.waypointsprite_attachedforcarrier); - LogNB("init", NULL); -} - -void ResetBall(entity this) -{ - if(this.cnt < 2) // step 1 - { - if(time == this.teamtime) - Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD)); - - settouch(this, func_null); - set_movetype(this, MOVETYPE_NOCLIP); - this.velocity = '0 0 0'; // just in case? - if(!this.cnt) - LogNB("resetidle", NULL); - this.cnt = 2; - this.nextthink = time; - } - else if(this.cnt < 4) // step 2 and 3 - { -// dprint("Step ", ftos(this.cnt), ": Calculated velocity: ", vtos(this.spawnorigin - this.origin), ", time: ", ftos(time), "\n"); - this.velocity = (this.spawnorigin - this.origin) * (this.cnt - 1); // 1 or 0.5 second movement - this.nextthink = time + 0.5; - this.cnt += 1; - } - else // step 4 - { -// dprint("Step 4: time: ", ftos(time), "\n"); - if(vdist(this.origin - this.spawnorigin, >, 10)) // should not happen anymore - LOG_TRACE("The ball moved too far away from its spawn origin.\nOffset: ", - 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 - set_movetype(this, MOVETYPE_NONE); - setthink(this, InitBall); - this.nextthink = max(time, game_starttime) + autocvar_g_nexball_delay_start; - } -} - -void football_touch(entity this, entity toucher) -{ - if(toucher.solid == SOLID_BSP) - { - if(time > this.lastground + 0.1) - { - _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - this.lastground = time; - } - if(this.velocity && !this.cnt) - this.nextthink = time + autocvar_g_nexball_delay_idle; - return; - } - if (!IS_PLAYER(toucher) && !IS_VEHICLE(toucher)) - return; - if(GetResource(toucher, RES_HEALTH) < 1) - return; - if(!this.cnt) - this.nextthink = time + autocvar_g_nexball_delay_idle; - - this.pusher = toucher; - this.team = toucher.team; - - if(autocvar_g_nexball_football_physics == -1) // MrBougo try 1, before decompiling Rev's original - { - 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(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(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(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, entity toucher) -{ - if(toucher.ballcarried) - { - football_touch(this, toucher); - return; - } - 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(GetResource(toucher, RES_HEALTH) < 1) - return; - LogNB("caught", toucher); - GiveBall(toucher, this); - } - else if(toucher.solid == SOLID_BSP) - { - _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); - if(this.velocity && !this.cnt) - this.nextthink = min(time + autocvar_g_nexball_delay_idle, this.teamtime); - } -} - -void GoalTouch(entity this, entity toucher) -{ - entity ball; - float isclient, pscore, otherteam; - string pname; - - if(game_stopped) return; - if((this.spawnflags & GOAL_TOUCHPLAYER) && toucher.ballcarried) - ball = toucher.ballcarried; - else - 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(this, toucher); - - - if(NumTeams(nb_teams) == 2) - otherteam = OtherTeam(ball.team); - else - otherteam = 0; - - if((isclient = IS_CLIENT(ball.pusher))) - pname = ball.pusher.netname; - else - pname = "Someone (?)"; - - if(ball.team == this.team) //owngoal (regular goals) - { - LogNB("owngoal", ball.pusher); - bprint("Boo! ", pname, "^7 scored a goal against their own team!\n"); - pscore = -1; - } - else if(this.team == GOAL_FAULT) - { - LogNB("fault", ball.pusher); - 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"); - pscore = -1; - } - else if(this.team == GOAL_OUT) - { - LogNB("out", ball.pusher); - if((this.spawnflags & GOAL_TOUCHPLAYER) && ball.owner) - bprint(pname, "^7 went out of bounds.\n"); - else - bprint("The ball was returned.\n"); - pscore = 0; - } - else //score - { - LogNB(strcat("goal:", ftos(this.team)), ball.pusher); - bprint("Goaaaaal! ", pname, "^7 scored a point for the ", Team_ColoredFullName(ball.team), ".\n"); - pscore = 1; - } - - _sound(ball, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NONE); - - if(ball.team && pscore) - { - if(NumTeams(nb_teams) == 2 && pscore < 0) - TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore); - else - TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore); - } - if(isclient) - { - if(pscore > 0) - GameRules_scoring_add(ball.pusher, NEXBALL_GOALS, pscore); - else if(pscore < 0) - GameRules_scoring_add(ball.pusher, NEXBALL_FAULTS, -pscore); - } - - if(ball.owner) // Happens on spawnflag GOAL_TOUCHPLAYER - DropBall(ball, ball.owner.origin, ball.owner.velocity); - - WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); - - ball.cnt = 1; - setthink(ball, ResetBall); - if(ball.classname == "nexball_basketball") - settouch(ball, football_touch); // better than func_null: football control until the ball gets reset - ball.nextthink = time + autocvar_g_nexball_delay_goal * (this.team != GOAL_OUT); -} - -//=======================// -// team ents // -//=======================// -spawnfunc(nexball_team) -{ - if(!g_nexball) - { - delete(this); - return; - } - this.team = this.cnt + 1; -} - -void nb_spawnteam(string teamname, float teamcolor) -{ - LOG_TRACE("^2spawned team ", teamname); - entity e = new(nexball_team); - e.netname = teamname; - e.cnt = teamcolor; - e.team = e.cnt + 1; - //nb_teams += 1; -} - -void nb_spawnteams() -{ - bool t_red = false, t_blue = false, t_yellow = false, t_pink = false; - entity e; - for(e = NULL; (e = find(e, classname, "nexball_goal"));) - { - switch(e.team) - { - case NUM_TEAM_1: - if(!t_red) - { - nb_spawnteam("Red", e.team-1) ; - nb_teams |= BIT(0); - t_red = true; - } - break; - case NUM_TEAM_2: - if(!t_blue) - { - nb_spawnteam("Blue", e.team-1) ; - t_blue = true; - nb_teams |= BIT(1); - } - break; - case NUM_TEAM_3: - if(!t_yellow) - { - nb_spawnteam("Yellow", e.team-1); - t_yellow = true; - nb_teams |= BIT(2); - } - break; - case NUM_TEAM_4: - if(!t_pink) - { - nb_spawnteam("Pink", e.team-1) ; - t_pink = true; - nb_teams |= BIT(3); - } - break; - } - } -} - -void nb_delayedinit(entity this) -{ - if(find(NULL, classname, "nexball_team") == NULL) - nb_spawnteams(); - nb_ScoreRules(nb_teams); -} - - -//=======================// -// spawnfuncs // -//=======================// - -void SpawnBall(entity this) -{ - 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 - - if(this.model == "") - { - this.model = "models/nexball/ball.md3"; - this.scale = 1.3; - } - - precache_model(this.model); - _setmodel(this, this.model); - setsize(this, BALL_MINS, BALL_MAXS); - ball_scale = this.scale; - - relocate_nexball(this); - this.spawnorigin = this.origin; - - this.effects = this.effects | EF_LOWPRECISION; - - if(cvar(strcat("g_", this.classname, "_trail"))) //nexball_basketball :p - { - this.glow_color = autocvar_g_nexball_trail_color; - this.glow_trail = true; - } - - 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 = ""; - else if(this.noise == "") - this.noise = strzone(SND(NB_BOUNCE)); - //bounce sound placeholder (FIXME) - if(this.noise1 == "") - this.noise1 = strzone(SND(NB_DROP)); - //ball drop sound placeholder (FIXME) - if(this.noise2 == "") - this.noise2 = strzone(SND(NB_STEAL)); - //stealing sound placeholder (FIXME) - if(this.noise) precache_sound(this.noise); - precache_sound(this.noise1); - precache_sound(this.noise2); - - WaypointSprite_AttachCarrier(WP_NbBall, this, RADARICON_FLAGCARRIER); // the ball's team is not set yet, no rule update needed - - this.reset = ball_restart; - setthink(this, InitBall); - this.nextthink = game_starttime + autocvar_g_nexball_delay_start; -} - -spawnfunc(nexball_basketball) -{ - nexball_mode |= NBM_BASKETBALL; - this.classname = "nexball_basketball"; - if (!(balls & BALL_BASKET)) - { - /* - CVTOV(g_nexball_basketball_effects_default); - CVTOV(g_nexball_basketball_delay_hold); - CVTOV(g_nexball_basketball_delay_hold_forteam); - CVTOV(g_nexball_basketball_teamsteal); - */ - autocvar_g_nexball_basketball_effects_default = autocvar_g_nexball_basketball_effects_default & BALL_EFFECTMASK; - } - if(!this.effects) - this.effects = autocvar_g_nexball_basketball_effects_default; - this.solid = SOLID_TRIGGER; - this.pushable = autocvar_g_nexball_basketball_jumppad; - balls |= BALL_BASKET; - this.bouncefactor = autocvar_g_nexball_basketball_bouncefactor; - this.bouncestop = autocvar_g_nexball_basketball_bouncestop; - SpawnBall(this); -} - -spawnfunc(nexball_football) -{ - nexball_mode |= NBM_FOOTBALL; - this.classname = "nexball_football"; - this.solid = SOLID_TRIGGER; - balls |= BALL_FOOT; - this.pushable = autocvar_g_nexball_football_jumppad; - this.bouncefactor = autocvar_g_nexball_football_bouncefactor; - this.bouncestop = autocvar_g_nexball_football_bouncestop; - SpawnBall(this); -} - -bool nb_Goal_Customize(entity this, entity client) -{ - entity e = WaypointSprite_getviewentity(client); - entity wp_owner = this.owner; - if(SAME_TEAM(e, wp_owner)) { return false; } - - return true; -} - -void SpawnGoal(entity this) -{ - if(!g_nexball) { delete(this); return; } - - EXACTTRIGGER_INIT; - - if(this.team != GOAL_OUT && Team_IsValidTeam(this.team)) - { - entity wp = WaypointSprite_SpawnFixed(WP_NbGoal, (this.absmin + this.absmax) * 0.5, this, sprite, RADARICON_NONE); - wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0.5 0'); - setcefc(this.sprite, nb_Goal_Customize); - } - - this.classname = "nexball_goal"; - if(this.noise == "") - this.noise = "ctf/respawn.wav"; - precache_sound(this.noise); - settouch(this, GoalTouch); -} - -spawnfunc(nexball_redgoal) -{ - this.team = NUM_TEAM_1; - SpawnGoal(this); -} -spawnfunc(nexball_bluegoal) -{ - this.team = NUM_TEAM_2; - SpawnGoal(this); -} -spawnfunc(nexball_yellowgoal) -{ - this.team = NUM_TEAM_3; - SpawnGoal(this); -} -spawnfunc(nexball_pinkgoal) -{ - this.team = NUM_TEAM_4; - SpawnGoal(this); -} - -spawnfunc(nexball_fault) -{ - this.team = GOAL_FAULT; - if(this.noise == "") - this.noise = strzone(SND(TYPEHIT)); - SpawnGoal(this); -} - -spawnfunc(nexball_out) -{ - this.team = GOAL_OUT; - if(this.noise == "") - this.noise = strzone(SND(TYPEHIT)); - SpawnGoal(this); -} - -// -//Spawnfuncs preserved for compatibility -// - -spawnfunc(ball) -{ - spawnfunc_nexball_football(this); -} -spawnfunc(ball_football) -{ - spawnfunc_nexball_football(this); -} -spawnfunc(ball_basketball) -{ - spawnfunc_nexball_basketball(this); -} -// The "red goal" is defended by blue team. A ball in there counts as a point for red. -spawnfunc(ball_redgoal) -{ - spawnfunc_nexball_bluegoal(this); // I blame Revenant -} -spawnfunc(ball_bluegoal) -{ - spawnfunc_nexball_redgoal(this); // but he didn't mean to cause trouble :p -} -spawnfunc(ball_fault) -{ - spawnfunc_nexball_fault(this); -} -spawnfunc(ball_bound) -{ - spawnfunc_nexball_out(this); -} - -bool ball_customize(entity this, entity client) -{ - if(!this.owner) - { - this.effects &= ~EF_FLAME; - this.scale = 1; - setcefc(this, func_null); - return true; - } - - if(client == this.owner) - { - this.scale = autocvar_g_nexball_viewmodel_scale; - if(this.enemy) - this.effects |= EF_FLAME; - else - this.effects &= ~EF_FLAME; - } - else - { - this.effects &= ~EF_FLAME; - this.scale = 1; - } - - return true; -} - -void nb_DropBall(entity player) -{ - if(player.ballcarried && g_nexball) - DropBall(player.ballcarried, player.origin, player.velocity); -} - -MUTATOR_HOOKFUNCTION(nb, ClientDisconnect) -{ - entity player = M_ARGV(0, entity); - - nb_DropBall(player); -} - -MUTATOR_HOOKFUNCTION(nb, PlayerDies) -{ - entity frag_target = M_ARGV(2, entity); - - nb_DropBall(frag_target); -} - -MUTATOR_HOOKFUNCTION(nb, MakePlayerObserver) -{ - entity player = M_ARGV(0, entity); - - nb_DropBall(player); - return false; -} - -MUTATOR_HOOKFUNCTION(nb, PlayerPreThink) -{ - entity player = M_ARGV(0, entity); - - makevectors(player.v_angle); - if(nexball_mode & NBM_BASKETBALL) - { - if(player.ballcarried) - { - // 'view ball' - player.ballcarried.velocity = player.velocity; - setcefc(player.ballcarried, ball_customize); - - vector org = player.origin + player.view_ofs + - v_forward * autocvar_g_nexball_viewmodel_offset.x + - v_right * autocvar_g_nexball_viewmodel_offset.y + - v_up * autocvar_g_nexball_viewmodel_offset.z; - setorigin(player.ballcarried, org); - - // 'safe passing' - if(autocvar_g_nexball_safepass_maxdist) - { - if(player.ballcarried.wait < time && player.ballcarried.enemy) - { - //centerprint(player, sprintf("Lost lock on %s", player.ballcarried.enemy.netname)); - player.ballcarried.enemy = NULL; - } - - - //tracebox(player.origin + player.view_ofs, '-2 -2 -2', '2 2 2', player.origin + player.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist); - crosshair_trace(player); - if( trace_ent && - IS_CLIENT(trace_ent) && - !IS_DEAD(trace_ent) && - trace_ent.team == player.team && - vdist(trace_ent.origin - player.origin, <=, autocvar_g_nexball_safepass_maxdist) ) - { - - //if(player.ballcarried.enemy != trace_ent) - // centerprint(player, sprintf("Locked to %s", trace_ent.netname)); - player.ballcarried.enemy = trace_ent; - player.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime; - - - } - } - } - else - { - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - - if(STAT(WEAPONS, player.(weaponentity))) - { - STAT(WEAPONS, player) = STAT(WEAPONS, player.(weaponentity)); - Weapon w = WEP_NEXBALL; - w.wr_resetplayer(w, player); - player.(weaponentity).m_switchweapon = player.m_switchweapon; - W_SwitchWeapon(player, player.(weaponentity).m_switchweapon, weaponentity); - - STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; - } - } - } - - } - - nexball_setstatus(player); -} - -MUTATOR_HOOKFUNCTION(nb, SpectateCopy) -{ - entity spectatee = M_ARGV(0, entity); - entity client = M_ARGV(1, entity); - - STAT(NB_METERSTART, client) = STAT(NB_METERSTART, spectatee); -} - -MUTATOR_HOOKFUNCTION(nb, PlayerSpawn) -{ - entity player = M_ARGV(0, entity); - - STAT(NB_METERSTART, player) = 0; - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) - { - .entity weaponentity = weaponentities[slot]; - STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; - } - - if (nexball_mode & NBM_BASKETBALL) - STAT(WEAPONS, player) |= WEPSET(NEXBALL); - else - STAT(WEAPONS, player) = '0 0 0'; - - return false; -} - -MUTATOR_HOOKFUNCTION(nb, PlayerPhysics_UpdateStats) -{ - entity player = M_ARGV(0, entity); - // these automatically reset, no need to worry - - if(player.ballcarried) - STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nexball_basketball_carrier_highspeed; -} - -MUTATOR_HOOKFUNCTION(nb, ForbidThrowCurrentWeapon) -{ - //entity player = M_ARGV(0, entity); - entity wepent = M_ARGV(1, entity); - - return wepent.m_weapon == WEP_NEXBALL; -} - -MUTATOR_HOOKFUNCTION(nb, ForbidDropCurrentWeapon) -{ - //entity player = M_ARGV(0, entity); - int wep = M_ARGV(1, int); - - return wep == WEP_MORTAR.m_id; // TODO: what is this for? -} - -MUTATOR_HOOKFUNCTION(nb, FilterItem) -{ - entity item = M_ARGV(0, entity); - - if(Item_IsLoot(item)) - if(item.weapon == WEP_NEXBALL.m_id) - return true; - - return false; -} - -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, TeamBalance_CheckAllowedTeams) -{ - M_ARGV(1, string) = "nexball_team"; - return true; -} - -MUTATOR_HOOKFUNCTION(nb, WantWeapon) -{ - M_ARGV(1, float) = 0; // weapon is set a few lines later, apparently - return true; -} - -MUTATOR_HOOKFUNCTION(nb, DropSpecialItems) -{ - entity frag_target = M_ARGV(0, entity); - - if(frag_target.ballcarried) - DropBall(frag_target.ballcarried, frag_target.origin, frag_target.velocity); - - return false; -} - -MUTATOR_HOOKFUNCTION(nb, SendWaypoint) -{ - M_ARGV(2, int) &= ~0x80; -} - -REGISTER_MUTATOR(nb, g_nexball) -{ - MUTATOR_STATIC(); - MUTATOR_ONADD - { - g_nexball_meter_period = autocvar_g_nexball_meter_period; - if(g_nexball_meter_period <= 0) - g_nexball_meter_period = 2; // avoid division by zero etc. due to silly users - g_nexball_meter_period = rint(g_nexball_meter_period * 32) / 32; //Round to 1/32ths to send as a byte multiplied by 32 - - // General settings - /* - CVTOV(g_nexball_football_boost_forward); //100 - CVTOV(g_nexball_football_boost_up); //200 - CVTOV(g_nexball_delay_idle); //10 - CVTOV(g_nexball_football_physics); //0 - */ - radar_showennemies = autocvar_g_nexball_radar_showallplayers; - - InitializeEntity(NULL, nb_delayedinit, INITPRIO_GAMETYPE); - WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; - - GameRules_teams(true); - GameRules_limit_score(autocvar_g_nexball_goallimit); - GameRules_limit_lead(autocvar_g_nexball_goalleadlimit); - } - - MUTATOR_ONROLLBACK_OR_REMOVE - { - WEP_NEXBALL.spawnflags |= WEP_FLAG_MUTATORBLOCKED; - } - return 0; -} - -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh deleted file mode 100644 index 53797d2bc6..0000000000 --- a/qcsrc/common/gamemodes/gamemode/nexball/nexball.qh +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#ifdef SVQC -//EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME -const float BALL_EFFECTMASK = 1229; -const vector BALL_MINS = '-16 -16 -16'; // The model is 24*24*24 -const vector BALL_MAXS = '16 16 16'; -const vector BALL_ATTACHORG = '3 0 16'; -const float BALL_FOOT = 1; -const float BALL_BASKET = 2; -//spawnflags -const float GOAL_TOUCHPLAYER = 1; -//goal types -const float GOAL_FAULT = -1; -const float GOAL_OUT = -2; - -void DropBall(entity ball, vector org, vector vel); -float autocvar_g_nexball_football_boost_forward; -float autocvar_g_nexball_football_boost_up; -float autocvar_g_nexball_football_physics; -float autocvar_g_nexball_delay_idle; -float autocvar_g_nexball_basketball_delay_hold; -float autocvar_g_nexball_basketball_delay_hold_forteam; -float autocvar_g_nexball_basketball_effects_default; -float autocvar_g_nexball_basketball_teamsteal; -float autocvar_g_nexball_meter_period; - -float balls; -float ball_scale; -float nb_teams; - -.entity nb_dropper; -.float nb_droptime; - -.float teamtime; -#endif diff --git a/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qc b/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qc new file mode 100644 index 0000000000..38b7a7f530 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qc @@ -0,0 +1,969 @@ +#include "sv_nexball.qh" + +.entity ballcarried; + +int autocvar_g_nexball_goalleadlimit; +#define autocvar_g_nexball_goallimit cvar("g_nexball_goallimit") + +bool autocvar_g_nexball_basketball_jumppad = true; +float autocvar_g_nexball_basketball_bouncefactor; +float autocvar_g_nexball_basketball_bouncestop; +float autocvar_g_nexball_basketball_carrier_highspeed; +bool autocvar_g_nexball_basketball_meter; +float autocvar_g_nexball_basketball_meter_maxpower; +float autocvar_g_nexball_basketball_meter_minpower; +float autocvar_g_nexball_delay_collect; +float autocvar_g_nexball_delay_goal; +float autocvar_g_nexball_delay_start; +bool autocvar_g_nexball_football_jumppad = true; +float autocvar_g_nexball_football_bouncefactor; +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; +float autocvar_g_nexball_safepass_holdtime; +float autocvar_g_nexball_viewmodel_scale; +float autocvar_g_nexball_tackling; +vector autocvar_g_nexball_viewmodel_offset; + +float autocvar_g_balance_nexball_primary_animtime; +float autocvar_g_balance_nexball_primary_refire; +float autocvar_g_balance_nexball_primary_speed; +float autocvar_g_balance_nexball_secondary_animtime; +float autocvar_g_balance_nexball_secondary_force; +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, entity toucher); +void football_touch(entity this, entity toucher); +void ResetBall(entity this); +const int NBM_NONE = 0; +const int NBM_FOOTBALL = 2; +const int NBM_BASKETBALL = 4; +float nexball_mode; + +float OtherTeam(float t) //works only if there are two teams on the map! +{ + entity e; + e = find(NULL, classname, "nexball_team"); + if(e.team == t) + e = find(e, classname, "nexball_team"); + return e.team; +} + +const int ST_NEXBALL_GOALS = 1; +void nb_ScoreRules(int teams) +{ + GameRules_scoring(teams, 0, 0, { + field_team(ST_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); + field(SP_NEXBALL_GOALS, "goals", SFL_SORT_PRIO_PRIMARY); + field(SP_NEXBALL_FAULTS, "faults", SFL_SORT_PRIO_SECONDARY | SFL_LOWER_IS_BETTER); + }); +} + +void LogNB(string mode, entity actor) +{ + string s; + if(!autocvar_sv_eventlog) + return; + s = strcat(":nexball:", mode); + if(actor != NULL) + s = strcat(s, ":", ftos(actor.playerid)); + GameLogEcho(s); +} + +void ball_restart(entity this) +{ + if(this.owner) + DropBall(this, this.owner.origin, '0 0 0'); + ResetBall(this); +} + +void nexball_setstatus(entity this) +{ + this.items &= ~IT_KEY1; + if(this.ballcarried) + { + if(this.ballcarried.teamtime && (this.ballcarried.teamtime < time)) + { + 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 + this.items |= IT_KEY1; + } +} + +void relocate_nexball(entity this) +{ + tracebox(this.origin, BALL_MINS, BALL_MAXS, this.origin, true, this); + if(trace_startsolid) + { + vector o = this.origin; + if (!move_out_of_solid(this)) { + objerror(this, "could not get out of solid at all!"); + } + LOG_INFOF( + "^1NOTE: this map needs FIXING. %s at %s needs to be moved out of solid, e.g. by %s", + this.classname, + vtos(o - '0 0 1'), + vtos(this.origin - o) + ); + this.origin = o; + } +} + +void DropOwner(entity this) +{ + entity ownr; + ownr = this.owner; + DropBall(this, ownr.origin, ownr.velocity); + makevectors(ownr.v_angle.y * '0 1 0'); + ownr.velocity += ('0 0 0.75' - v_forward) * 1000; + UNSET_ONGROUND(ownr); +} + +void GiveBall(entity plyr, entity ball) +{ + .entity weaponentity = weaponentities[0]; // TODO: find ballstealer + entity ownr = ball.owner; + if(ownr) + { + ownr.effects &= ~autocvar_g_nexball_basketball_effects_default; + ownr.ballcarried = NULL; + GameRules_scoring_vip(ownr, false); + if(STAT(NB_METERSTART, ownr)) + { + STAT(NB_METERSTART, ownr) = 0; + ownr.(weaponentity).state = WS_READY; + } + WaypointSprite_Kill(ownr.waypointsprite_attachedforcarrier); + } + else + { + WaypointSprite_Kill(ball.waypointsprite_attachedforcarrier); + } + + //setattachment(ball, plyr, ""); + setorigin(ball, plyr.origin + plyr.view_ofs); + + if(ball.team != plyr.team) + ball.teamtime = time + autocvar_g_nexball_basketball_delay_hold_forteam; + + ball.owner = ball.pusher = plyr; //"owner" is set to the player carrying, "pusher" to the last player who touched it + ball.weaponentity_fld = weaponentity; + ball.team = plyr.team; + plyr.ballcarried = ball; + GameRules_scoring_vip(plyr, true); + ball.nb_dropper = plyr; + + plyr.effects |= autocvar_g_nexball_basketball_effects_default; + ball.effects &= ~autocvar_g_nexball_basketball_effects_default; + + ball.velocity = '0 0 0'; + set_movetype(ball, MOVETYPE_NONE); + settouch(ball, func_null); + ball.effects |= EF_NOSHADOW; + ball.scale = 1; // scale down. + + WaypointSprite_AttachCarrier(WP_NbBall, plyr, RADARICON_FLAGCARRIER); + WaypointSprite_UpdateRule(plyr.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); + + if(autocvar_g_nexball_basketball_delay_hold) + { + setthink(ball, DropOwner); + ball.nextthink = time + autocvar_g_nexball_basketball_delay_hold; + } + + STAT(WEAPONS, plyr.(weaponentity)) = STAT(WEAPONS, plyr); + plyr.m_switchweapon = plyr.(weaponentity).m_weapon; + STAT(WEAPONS, plyr) = WEPSET(NEXBALL); + Weapon w = WEP_NEXBALL; + w.wr_resetplayer(w, plyr); + plyr.(weaponentity).m_switchweapon = WEP_NEXBALL; + W_SwitchWeapon(plyr, WEP_NEXBALL, weaponentity); +} + +void DropBall(entity ball, vector org, vector vel) +{ + ball.effects |= autocvar_g_nexball_basketball_effects_default; + ball.effects &= ~EF_NOSHADOW; + ball.owner.effects &= ~autocvar_g_nexball_basketball_effects_default; + + setattachment(ball, NULL, ""); + setorigin(ball, org); + set_movetype(ball, MOVETYPE_BOUNCE); + UNSET_ONGROUND(ball); + ball.scale = ball_scale; + ball.velocity = vel; + ball.nb_droptime = time; + settouch(ball, basketball_touch); + setthink(ball, ResetBall); + ball.nextthink = min(time + autocvar_g_nexball_delay_idle, ball.teamtime); + + if(STAT(NB_METERSTART, ball.owner)) + { + STAT(NB_METERSTART, ball.owner) = 0; + .entity weaponentity = ball.weaponentity_fld; + ball.owner.(weaponentity).state = WS_READY; + } + + WaypointSprite_Kill(ball.owner.waypointsprite_attachedforcarrier); + WaypointSprite_Spawn(WP_NbBall, 0, 0, ball, '0 0 64', NULL, ball.team, ball, waypointsprite_attachedforcarrier, false, RADARICON_FLAGCARRIER); // no health bar please + WaypointSprite_UpdateRule(ball.waypointsprite_attachedforcarrier, 0, SPRITERULE_DEFAULT); + + entity e = ball.owner; ball.owner = NULL; + e.ballcarried = NULL; + GameRules_scoring_vip(e, false); +} + +void InitBall(entity this) +{ + if(game_stopped) return; + UNSET_ONGROUND(this); + set_movetype(this, MOVETYPE_BOUNCE); + if(this.classname == "nexball_basketball") + settouch(this, basketball_touch); + else if(this.classname == "nexball_football") + settouch(this, football_touch); + this.cnt = 0; + setthink(this, ResetBall); + this.nextthink = time + autocvar_g_nexball_delay_idle + 3; + this.teamtime = 0; + this.pusher = NULL; + this.team = false; + _sound(this, CH_TRIGGER, this.noise1, VOL_BASE, ATTEN_NORM); + WaypointSprite_Ping(this.waypointsprite_attachedforcarrier); + LogNB("init", NULL); +} + +void ResetBall(entity this) +{ + if(this.cnt < 2) // step 1 + { + if(time == this.teamtime) + Send_Notification(NOTIF_ALL, NULL, MSG_INFO, APP_TEAM_NUM(this.team, INFO_NEXBALL_RETURN_HELD)); + + settouch(this, func_null); + set_movetype(this, MOVETYPE_NOCLIP); + this.velocity = '0 0 0'; // just in case? + if(!this.cnt) + LogNB("resetidle", NULL); + this.cnt = 2; + this.nextthink = time; + } + else if(this.cnt < 4) // step 2 and 3 + { +// dprint("Step ", ftos(this.cnt), ": Calculated velocity: ", vtos(this.spawnorigin - this.origin), ", time: ", ftos(time), "\n"); + this.velocity = (this.spawnorigin - this.origin) * (this.cnt - 1); // 1 or 0.5 second movement + this.nextthink = time + 0.5; + this.cnt += 1; + } + else // step 4 + { +// dprint("Step 4: time: ", ftos(time), "\n"); + if(vdist(this.origin - this.spawnorigin, >, 10)) // should not happen anymore + LOG_TRACE("The ball moved too far away from its spawn origin.\nOffset: ", + 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 + set_movetype(this, MOVETYPE_NONE); + setthink(this, InitBall); + this.nextthink = max(time, game_starttime) + autocvar_g_nexball_delay_start; + } +} + +void football_touch(entity this, entity toucher) +{ + if(toucher.solid == SOLID_BSP) + { + if(time > this.lastground + 0.1) + { + _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + this.lastground = time; + } + if(this.velocity && !this.cnt) + this.nextthink = time + autocvar_g_nexball_delay_idle; + return; + } + if (!IS_PLAYER(toucher) && !IS_VEHICLE(toucher)) + return; + if(GetResource(toucher, RES_HEALTH) < 1) + return; + if(!this.cnt) + this.nextthink = time + autocvar_g_nexball_delay_idle; + + this.pusher = toucher; + this.team = toucher.team; + + if(autocvar_g_nexball_football_physics == -1) // MrBougo try 1, before decompiling Rev's original + { + 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(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(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(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, entity toucher) +{ + if(toucher.ballcarried) + { + football_touch(this, toucher); + return; + } + 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(GetResource(toucher, RES_HEALTH) < 1) + return; + LogNB("caught", toucher); + GiveBall(toucher, this); + } + else if(toucher.solid == SOLID_BSP) + { + _sound(this, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); + if(this.velocity && !this.cnt) + this.nextthink = min(time + autocvar_g_nexball_delay_idle, this.teamtime); + } +} + +void GoalTouch(entity this, entity toucher) +{ + entity ball; + float isclient, pscore, otherteam; + string pname; + + if(game_stopped) return; + if((this.spawnflags & GOAL_TOUCHPLAYER) && toucher.ballcarried) + ball = toucher.ballcarried; + else + 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(this, toucher); + + + if(NumTeams(nb_teams) == 2) + otherteam = OtherTeam(ball.team); + else + otherteam = 0; + + if((isclient = IS_CLIENT(ball.pusher))) + pname = ball.pusher.netname; + else + pname = "Someone (?)"; + + if(ball.team == this.team) //owngoal (regular goals) + { + LogNB("owngoal", ball.pusher); + bprint("Boo! ", pname, "^7 scored a goal against their own team!\n"); + pscore = -1; + } + else if(this.team == GOAL_FAULT) + { + LogNB("fault", ball.pusher); + 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"); + pscore = -1; + } + else if(this.team == GOAL_OUT) + { + LogNB("out", ball.pusher); + if((this.spawnflags & GOAL_TOUCHPLAYER) && ball.owner) + bprint(pname, "^7 went out of bounds.\n"); + else + bprint("The ball was returned.\n"); + pscore = 0; + } + else //score + { + LogNB(strcat("goal:", ftos(this.team)), ball.pusher); + bprint("Goaaaaal! ", pname, "^7 scored a point for the ", Team_ColoredFullName(ball.team), ".\n"); + pscore = 1; + } + + _sound(ball, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NONE); + + if(ball.team && pscore) + { + if(NumTeams(nb_teams) == 2 && pscore < 0) + TeamScore_AddToTeam(otherteam, ST_NEXBALL_GOALS, -pscore); + else + TeamScore_AddToTeam(ball.team, ST_NEXBALL_GOALS, pscore); + } + if(isclient) + { + if(pscore > 0) + GameRules_scoring_add(ball.pusher, NEXBALL_GOALS, pscore); + else if(pscore < 0) + GameRules_scoring_add(ball.pusher, NEXBALL_FAULTS, -pscore); + } + + if(ball.owner) // Happens on spawnflag GOAL_TOUCHPLAYER + DropBall(ball, ball.owner.origin, ball.owner.velocity); + + WaypointSprite_Ping(ball.waypointsprite_attachedforcarrier); + + ball.cnt = 1; + setthink(ball, ResetBall); + if(ball.classname == "nexball_basketball") + settouch(ball, football_touch); // better than func_null: football control until the ball gets reset + ball.nextthink = time + autocvar_g_nexball_delay_goal * (this.team != GOAL_OUT); +} + +//=======================// +// team ents // +//=======================// +spawnfunc(nexball_team) +{ + if(!g_nexball) + { + delete(this); + return; + } + this.team = this.cnt + 1; +} + +void nb_spawnteam(string teamname, float teamcolor) +{ + LOG_TRACE("^2spawned team ", teamname); + entity e = new(nexball_team); + e.netname = teamname; + e.cnt = teamcolor; + e.team = e.cnt + 1; + //nb_teams += 1; +} + +void nb_spawnteams() +{ + bool t_red = false, t_blue = false, t_yellow = false, t_pink = false; + entity e; + for(e = NULL; (e = find(e, classname, "nexball_goal"));) + { + switch(e.team) + { + case NUM_TEAM_1: + if(!t_red) + { + nb_spawnteam("Red", e.team-1) ; + nb_teams |= BIT(0); + t_red = true; + } + break; + case NUM_TEAM_2: + if(!t_blue) + { + nb_spawnteam("Blue", e.team-1) ; + t_blue = true; + nb_teams |= BIT(1); + } + break; + case NUM_TEAM_3: + if(!t_yellow) + { + nb_spawnteam("Yellow", e.team-1); + t_yellow = true; + nb_teams |= BIT(2); + } + break; + case NUM_TEAM_4: + if(!t_pink) + { + nb_spawnteam("Pink", e.team-1) ; + t_pink = true; + nb_teams |= BIT(3); + } + break; + } + } +} + +void nb_delayedinit(entity this) +{ + if(find(NULL, classname, "nexball_team") == NULL) + nb_spawnteams(); + nb_ScoreRules(nb_teams); +} + + +//=======================// +// spawnfuncs // +//=======================// + +void SpawnBall(entity this) +{ + 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 + + if(this.model == "") + { + this.model = "models/nexball/ball.md3"; + this.scale = 1.3; + } + + precache_model(this.model); + _setmodel(this, this.model); + setsize(this, BALL_MINS, BALL_MAXS); + ball_scale = this.scale; + + relocate_nexball(this); + this.spawnorigin = this.origin; + + this.effects = this.effects | EF_LOWPRECISION; + + if(cvar(strcat("g_", this.classname, "_trail"))) //nexball_basketball :p + { + this.glow_color = autocvar_g_nexball_trail_color; + this.glow_trail = true; + } + + 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 = ""; + else if(this.noise == "") + this.noise = strzone(SND(NB_BOUNCE)); + //bounce sound placeholder (FIXME) + if(this.noise1 == "") + this.noise1 = strzone(SND(NB_DROP)); + //ball drop sound placeholder (FIXME) + if(this.noise2 == "") + this.noise2 = strzone(SND(NB_STEAL)); + //stealing sound placeholder (FIXME) + if(this.noise) precache_sound(this.noise); + precache_sound(this.noise1); + precache_sound(this.noise2); + + WaypointSprite_AttachCarrier(WP_NbBall, this, RADARICON_FLAGCARRIER); // the ball's team is not set yet, no rule update needed + + this.reset = ball_restart; + setthink(this, InitBall); + this.nextthink = game_starttime + autocvar_g_nexball_delay_start; +} + +spawnfunc(nexball_basketball) +{ + nexball_mode |= NBM_BASKETBALL; + this.classname = "nexball_basketball"; + if (!(balls & BALL_BASKET)) + { + /* + CVTOV(g_nexball_basketball_effects_default); + CVTOV(g_nexball_basketball_delay_hold); + CVTOV(g_nexball_basketball_delay_hold_forteam); + CVTOV(g_nexball_basketball_teamsteal); + */ + autocvar_g_nexball_basketball_effects_default = autocvar_g_nexball_basketball_effects_default & BALL_EFFECTMASK; + } + if(!this.effects) + this.effects = autocvar_g_nexball_basketball_effects_default; + this.solid = SOLID_TRIGGER; + this.pushable = autocvar_g_nexball_basketball_jumppad; + balls |= BALL_BASKET; + this.bouncefactor = autocvar_g_nexball_basketball_bouncefactor; + this.bouncestop = autocvar_g_nexball_basketball_bouncestop; + SpawnBall(this); +} + +spawnfunc(nexball_football) +{ + nexball_mode |= NBM_FOOTBALL; + this.classname = "nexball_football"; + this.solid = SOLID_TRIGGER; + balls |= BALL_FOOT; + this.pushable = autocvar_g_nexball_football_jumppad; + this.bouncefactor = autocvar_g_nexball_football_bouncefactor; + this.bouncestop = autocvar_g_nexball_football_bouncestop; + SpawnBall(this); +} + +bool nb_Goal_Customize(entity this, entity client) +{ + entity e = WaypointSprite_getviewentity(client); + entity wp_owner = this.owner; + if(SAME_TEAM(e, wp_owner)) { return false; } + + return true; +} + +void SpawnGoal(entity this) +{ + if(!g_nexball) { delete(this); return; } + + EXACTTRIGGER_INIT; + + if(this.team != GOAL_OUT && Team_IsValidTeam(this.team)) + { + entity wp = WaypointSprite_SpawnFixed(WP_NbGoal, (this.absmin + this.absmax) * 0.5, this, sprite, RADARICON_NONE); + wp.colormod = ((this.team) ? Team_ColorRGB(this.team) : '1 0.5 0'); + setcefc(this.sprite, nb_Goal_Customize); + } + + this.classname = "nexball_goal"; + if(this.noise == "") + this.noise = "ctf/respawn.wav"; + precache_sound(this.noise); + settouch(this, GoalTouch); +} + +spawnfunc(nexball_redgoal) +{ + this.team = NUM_TEAM_1; + SpawnGoal(this); +} +spawnfunc(nexball_bluegoal) +{ + this.team = NUM_TEAM_2; + SpawnGoal(this); +} +spawnfunc(nexball_yellowgoal) +{ + this.team = NUM_TEAM_3; + SpawnGoal(this); +} +spawnfunc(nexball_pinkgoal) +{ + this.team = NUM_TEAM_4; + SpawnGoal(this); +} + +spawnfunc(nexball_fault) +{ + this.team = GOAL_FAULT; + if(this.noise == "") + this.noise = strzone(SND(TYPEHIT)); + SpawnGoal(this); +} + +spawnfunc(nexball_out) +{ + this.team = GOAL_OUT; + if(this.noise == "") + this.noise = strzone(SND(TYPEHIT)); + SpawnGoal(this); +} + +// +//Spawnfuncs preserved for compatibility +// + +spawnfunc(ball) +{ + spawnfunc_nexball_football(this); +} +spawnfunc(ball_football) +{ + spawnfunc_nexball_football(this); +} +spawnfunc(ball_basketball) +{ + spawnfunc_nexball_basketball(this); +} +// The "red goal" is defended by blue team. A ball in there counts as a point for red. +spawnfunc(ball_redgoal) +{ + spawnfunc_nexball_bluegoal(this); // I blame Revenant +} +spawnfunc(ball_bluegoal) +{ + spawnfunc_nexball_redgoal(this); // but he didn't mean to cause trouble :p +} +spawnfunc(ball_fault) +{ + spawnfunc_nexball_fault(this); +} +spawnfunc(ball_bound) +{ + spawnfunc_nexball_out(this); +} + +bool ball_customize(entity this, entity client) +{ + if(!this.owner) + { + this.effects &= ~EF_FLAME; + this.scale = 1; + setcefc(this, func_null); + return true; + } + + if(client == this.owner) + { + this.scale = autocvar_g_nexball_viewmodel_scale; + if(this.enemy) + this.effects |= EF_FLAME; + else + this.effects &= ~EF_FLAME; + } + else + { + this.effects &= ~EF_FLAME; + this.scale = 1; + } + + return true; +} + +void nb_DropBall(entity player) +{ + if(player.ballcarried && g_nexball) + DropBall(player.ballcarried, player.origin, player.velocity); +} + +MUTATOR_HOOKFUNCTION(nb, ClientDisconnect) +{ + entity player = M_ARGV(0, entity); + + nb_DropBall(player); +} + +MUTATOR_HOOKFUNCTION(nb, PlayerDies) +{ + entity frag_target = M_ARGV(2, entity); + + nb_DropBall(frag_target); +} + +MUTATOR_HOOKFUNCTION(nb, MakePlayerObserver) +{ + entity player = M_ARGV(0, entity); + + nb_DropBall(player); + return false; +} + +MUTATOR_HOOKFUNCTION(nb, PlayerPreThink) +{ + entity player = M_ARGV(0, entity); + + makevectors(player.v_angle); + if(nexball_mode & NBM_BASKETBALL) + { + if(player.ballcarried) + { + // 'view ball' + player.ballcarried.velocity = player.velocity; + setcefc(player.ballcarried, ball_customize); + + vector org = player.origin + player.view_ofs + + v_forward * autocvar_g_nexball_viewmodel_offset.x + + v_right * autocvar_g_nexball_viewmodel_offset.y + + v_up * autocvar_g_nexball_viewmodel_offset.z; + setorigin(player.ballcarried, org); + + // 'safe passing' + if(autocvar_g_nexball_safepass_maxdist) + { + if(player.ballcarried.wait < time && player.ballcarried.enemy) + { + //centerprint(player, sprintf("Lost lock on %s", player.ballcarried.enemy.netname)); + player.ballcarried.enemy = NULL; + } + + + //tracebox(player.origin + player.view_ofs, '-2 -2 -2', '2 2 2', player.origin + player.view_ofs + v_forward * autocvar_g_nexball_safepass_maxdist); + crosshair_trace(player); + if( trace_ent && + IS_CLIENT(trace_ent) && + !IS_DEAD(trace_ent) && + trace_ent.team == player.team && + vdist(trace_ent.origin - player.origin, <=, autocvar_g_nexball_safepass_maxdist) ) + { + + //if(player.ballcarried.enemy != trace_ent) + // centerprint(player, sprintf("Locked to %s", trace_ent.netname)); + player.ballcarried.enemy = trace_ent; + player.ballcarried.wait = time + autocvar_g_nexball_safepass_holdtime; + + + } + } + } + else + { + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + + if(STAT(WEAPONS, player.(weaponentity))) + { + STAT(WEAPONS, player) = STAT(WEAPONS, player.(weaponentity)); + Weapon w = WEP_NEXBALL; + w.wr_resetplayer(w, player); + player.(weaponentity).m_switchweapon = player.m_switchweapon; + W_SwitchWeapon(player, player.(weaponentity).m_switchweapon, weaponentity); + + STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; + } + } + } + + } + + nexball_setstatus(player); +} + +MUTATOR_HOOKFUNCTION(nb, SpectateCopy) +{ + entity spectatee = M_ARGV(0, entity); + entity client = M_ARGV(1, entity); + + STAT(NB_METERSTART, client) = STAT(NB_METERSTART, spectatee); +} + +MUTATOR_HOOKFUNCTION(nb, PlayerSpawn) +{ + entity player = M_ARGV(0, entity); + + STAT(NB_METERSTART, player) = 0; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + .entity weaponentity = weaponentities[slot]; + STAT(WEAPONS, player.(weaponentity)) = '0 0 0'; + } + + if (nexball_mode & NBM_BASKETBALL) + STAT(WEAPONS, player) |= WEPSET(NEXBALL); + else + STAT(WEAPONS, player) = '0 0 0'; + + return false; +} + +MUTATOR_HOOKFUNCTION(nb, PlayerPhysics_UpdateStats) +{ + entity player = M_ARGV(0, entity); + // these automatically reset, no need to worry + + if(player.ballcarried) + STAT(MOVEVARS_HIGHSPEED, player) *= autocvar_g_nexball_basketball_carrier_highspeed; +} + +MUTATOR_HOOKFUNCTION(nb, ForbidThrowCurrentWeapon) +{ + //entity player = M_ARGV(0, entity); + entity wepent = M_ARGV(1, entity); + + return wepent.m_weapon == WEP_NEXBALL; +} + +MUTATOR_HOOKFUNCTION(nb, ForbidDropCurrentWeapon) +{ + //entity player = M_ARGV(0, entity); + int wep = M_ARGV(1, int); + + return wep == WEP_MORTAR.m_id; // TODO: what is this for? +} + +MUTATOR_HOOKFUNCTION(nb, FilterItem) +{ + entity item = M_ARGV(0, entity); + + if(Item_IsLoot(item)) + if(item.weapon == WEP_NEXBALL.m_id) + return true; + + return false; +} + +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, TeamBalance_CheckAllowedTeams) +{ + M_ARGV(1, string) = "nexball_team"; + return true; +} + +MUTATOR_HOOKFUNCTION(nb, WantWeapon) +{ + M_ARGV(1, float) = 0; // weapon is set a few lines later, apparently + return true; +} + +MUTATOR_HOOKFUNCTION(nb, DropSpecialItems) +{ + entity frag_target = M_ARGV(0, entity); + + if(frag_target.ballcarried) + DropBall(frag_target.ballcarried, frag_target.origin, frag_target.velocity); + + return false; +} + +MUTATOR_HOOKFUNCTION(nb, SendWaypoint) +{ + M_ARGV(2, int) &= ~0x80; +} + +REGISTER_MUTATOR(nb, false) +{ + MUTATOR_STATIC(); + MUTATOR_ONADD + { + g_nexball_meter_period = autocvar_g_nexball_meter_period; + if(g_nexball_meter_period <= 0) + g_nexball_meter_period = 2; // avoid division by zero etc. due to silly users + g_nexball_meter_period = rint(g_nexball_meter_period * 32) / 32; //Round to 1/32ths to send as a byte multiplied by 32 + + // General settings + /* + CVTOV(g_nexball_football_boost_forward); //100 + CVTOV(g_nexball_football_boost_up); //200 + CVTOV(g_nexball_delay_idle); //10 + CVTOV(g_nexball_football_physics); //0 + */ + radar_showennemies = autocvar_g_nexball_radar_showallplayers; + + InitializeEntity(NULL, nb_delayedinit, INITPRIO_GAMETYPE); + WEP_NEXBALL.spawnflags &= ~WEP_FLAG_MUTATORBLOCKED; + + GameRules_teams(true); + GameRules_limit_score(autocvar_g_nexball_goallimit); + GameRules_limit_lead(autocvar_g_nexball_goalleadlimit); + } + + MUTATOR_ONROLLBACK_OR_REMOVE + { + WEP_NEXBALL.spawnflags |= WEP_FLAG_MUTATORBLOCKED; + } + return 0; +} diff --git a/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qh b/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qh new file mode 100644 index 0000000000..d7cde3a734 --- /dev/null +++ b/qcsrc/common/gamemodes/gamemode/nexball/sv_nexball.qh @@ -0,0 +1,34 @@ +#pragma once + +//EF_BRIGHTFIELD|EF_BRIGHTLIGHT|EF_DIMLIGHT|EF_BLUE|EF_RED|EF_FLAME +const float BALL_EFFECTMASK = 1229; +const vector BALL_MINS = '-16 -16 -16'; // The model is 24*24*24 +const vector BALL_MAXS = '16 16 16'; +const vector BALL_ATTACHORG = '3 0 16'; +const float BALL_FOOT = 1; +const float BALL_BASKET = 2; +//spawnflags +const float GOAL_TOUCHPLAYER = 1; +//goal types +const float GOAL_FAULT = -1; +const float GOAL_OUT = -2; + +void DropBall(entity ball, vector org, vector vel); +float autocvar_g_nexball_football_boost_forward; +float autocvar_g_nexball_football_boost_up; +float autocvar_g_nexball_football_physics; +float autocvar_g_nexball_delay_idle; +float autocvar_g_nexball_basketball_delay_hold; +float autocvar_g_nexball_basketball_delay_hold_forteam; +float autocvar_g_nexball_basketball_effects_default; +float autocvar_g_nexball_basketball_teamsteal; +float autocvar_g_nexball_meter_period; + +float balls; +float ball_scale; +float nb_teams; + +.entity nb_dropper; +.float nb_droptime; + +.float teamtime; diff --git a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh index 73b887260c..7790663ace 100644 --- a/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh +++ b/qcsrc/common/gamemodes/gamemode/nexball/weapon.qh @@ -1,7 +1,7 @@ #pragma once CLASS(BallStealer, PortoLaunch) -/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED); +/* flags */ ATTRIB(BallStealer, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_HIDDEN | WEP_FLAG_MUTATORBLOCKED | WEP_FLAG_NOTRUEAIM); /* impulse */ ATTRIB(BallStealer, impulse, int, 0); /* refname */ ATTRIB(BallStealer, netname, string, "ballstealer"); /* wepname */ ATTRIB(BallStealer, m_name, string, _("Ball Stealer")); diff --git a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc index d4303efbd9..3128bae266 100644 --- a/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc +++ b/qcsrc/common/gamemodes/gamemode/onslaught/sv_onslaught.qc @@ -988,7 +988,7 @@ void ons_GeneratorThink(entity this) { Send_Notification(NOTIF_ONE, it, MSG_CENTER, CENTER_ONS_NOTSHIELDED_TEAM); msg_entity = it; - soundto(MSG_ONE, this, CHAN_AUTO, SND(KH_ALARM), VOL_BASE, ATTEN_NONE); // FIXME: unique sound? + soundto(MSG_ONE, this, CHAN_AUTO, SND(ONS_GENERATOR_ALARM), VOL_BASE, ATTEN_NONE); } else Send_Notification(NOTIF_ONE, it, MSG_CENTER, APP_TEAM_NUM(this.team, CENTER_ONS_NOTSHIELDED)); diff --git a/qcsrc/common/mapobjects/misc/corner.qc b/qcsrc/common/mapobjects/misc/corner.qc index a0f67b759b..729f7e236e 100644 --- a/qcsrc/common/mapobjects/misc/corner.qc +++ b/qcsrc/common/mapobjects/misc/corner.qc @@ -10,12 +10,26 @@ bool corner_send(entity this, entity to, int sf) WriteVector(MSG_ENTITY, this.origin); - WriteString(MSG_ENTITY, this.target); - WriteString(MSG_ENTITY, this.target2); - WriteString(MSG_ENTITY, this.target3); - WriteString(MSG_ENTITY, this.target4); - WriteString(MSG_ENTITY, this.targetname); - WriteByte(MSG_ENTITY, this.target_random); + sf = 0; + sf = BITSET(sf, BIT(0), this.target_random); + + sf = BITSET(sf, BIT(1), this.target && this.target != ""); + sf = BITSET(sf, BIT(2), this.target2 && this.target2 != ""); + sf = BITSET(sf, BIT(3), this.target3 && this.target3 != ""); + sf = BITSET(sf, BIT(4), this.target4 && this.target4 != ""); + sf = BITSET(sf, BIT(5), this.targetname && this.targetname != ""); + + WriteByte(MSG_ENTITY, sf); + if(sf & BIT(1)) + WriteString(MSG_ENTITY, this.target); + if(sf & BIT(2)) + WriteString(MSG_ENTITY, this.target2); + if(sf & BIT(3)) + WriteString(MSG_ENTITY, this.target3); + if(sf & BIT(4)) + WriteString(MSG_ENTITY, this.target4); + if(sf & BIT(5)) + WriteString(MSG_ENTITY, this.targetname); WriteByte(MSG_ENTITY, this.wait); @@ -54,12 +68,14 @@ NET_HANDLE(ENT_CLIENT_CORNER, bool isnew) this.origin = ReadVector(); setorigin(this, this.origin); - this.target = strzone(ReadString()); - this.target2 = strzone(ReadString()); - this.target3 = strzone(ReadString()); - this.target4 = strzone(ReadString()); - this.targetname = strzone(ReadString()); - this.target_random = ReadByte(); + int targbits = ReadByte(); + this.target_random = (targbits & BIT(0)); + + this.target = ((targbits & BIT(1)) ? strzone(ReadString()) : string_null); + this.target2 = ((targbits & BIT(2)) ? strzone(ReadString()) : string_null); + this.target3 = ((targbits & BIT(3)) ? strzone(ReadString()) : string_null); + this.target4 = ((targbits & BIT(4)) ? strzone(ReadString()) : string_null); + this.targetname = ((targbits & BIT(5)) ? strzone(ReadString()) : string_null); this.wait = ReadByte(); diff --git a/qcsrc/common/mapobjects/misc/dynlight.qc b/qcsrc/common/mapobjects/misc/dynlight.qc index 7c70b8444e..6e9f02b874 100644 --- a/qcsrc/common/mapobjects/misc/dynlight.qc +++ b/qcsrc/common/mapobjects/misc/dynlight.qc @@ -46,7 +46,7 @@ void dynlight_think(entity this) void dynlight_find_aiment(entity this) { entity targ; - if (!this.target) + if (!this.target || this.target == "") objerror (this, "dynlight: no target to follow"); targ = find(NULL, targetname, this.target); @@ -62,7 +62,7 @@ void dynlight_find_aiment(entity this) void dynlight_find_path(entity this) { entity targ; - if (!this.target) + if (!this.target || this.target == "") objerror (this, "dynlight: no target to follow"); targ = find(NULL, targetname, this.target); @@ -74,7 +74,7 @@ void dynlight_find_path(entity this) void dynlight_find_target(entity this) { entity targ; - if (!this.target) + if (!this.target || this.target == "") objerror (this, "dynlight: no target to follow"); targ = find(NULL, targetname, this.target); diff --git a/qcsrc/common/mapobjects/target/music.qc b/qcsrc/common/mapobjects/target/music.qc index 5a63872dbd..55ccf41fbf 100644 --- a/qcsrc/common/mapobjects/target/music.qc +++ b/qcsrc/common/mapobjects/target/music.qc @@ -272,7 +272,7 @@ void Net_TargetMusic() void Ent_TriggerMusic_Think(entity this) { - if(this.active == ACTIVE_NOT) + if(this.active == ACTIVE_NOT || intermission) { return; } diff --git a/qcsrc/common/mapobjects/trigger/counter.qc b/qcsrc/common/mapobjects/trigger/counter.qc index 9156439f99..db255ebb7a 100644 --- a/qcsrc/common/mapobjects/trigger/counter.qc +++ b/qcsrc/common/mapobjects/trigger/counter.qc @@ -5,7 +5,7 @@ void counter_reset(entity this); void counter_use(entity this, entity actor, entity trigger) { entity store = this; - if(this.spawnflags & COUNTER_PER_PLAYER) + if(this.spawnflags & COUNTER_PER_PLAYER) // FIXME: multiple counters in the map will not function correctly, and upon trigger reset the player won't be able to use it again! { if(!IS_PLAYER(actor)) return; diff --git a/qcsrc/common/mapobjects/trigger/heal.qc b/qcsrc/common/mapobjects/trigger/heal.qc index 94abd4041d..b110d9913f 100644 --- a/qcsrc/common/mapobjects/trigger/heal.qc +++ b/qcsrc/common/mapobjects/trigger/heal.qc @@ -18,7 +18,7 @@ void trigger_heal_touch(entity this, entity toucher) toucher.triggerhealtime = time + this.delay; bool playthesound = (this.spawnflags & HEAL_SOUND_ALWAYS); - bool healed = Heal(toucher, this, GetResource(this, RES_HEALTH), this.max_health); + bool healed = Heal(toucher, this, this.health, this.max_health); if(playthesound || healed) _sound (toucher, CH_TRIGGER, this.noise, VOL_BASE, ATTEN_NORM); @@ -36,8 +36,8 @@ void trigger_heal_init(entity this) this.active = ACTIVE_ACTIVE; if(!this.delay) this.delay = 1; - if(!GetResource(this, RES_HEALTH)) - SetResourceExplicit(this, RES_HEALTH, 10); // TODO: use a special field for this, it doesn't have actual health! + if(!this.health) + this.health = 10; if(!this.max_health) this.max_health = 200; // max health topoff for field if(this.noise == "") diff --git a/qcsrc/common/mapobjects/trigger/magicear.qc b/qcsrc/common/mapobjects/trigger/magicear.qc index 16118cb9d6..7321fe23f9 100644 --- a/qcsrc/common/mapobjects/trigger/magicear.qc +++ b/qcsrc/common/mapobjects/trigger/magicear.qc @@ -1,7 +1,7 @@ #include "magicear.qh" #ifdef SVQC -float magicear_matched; -float W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, float instrument, float ignorepitch, float mintempo, float maxtempo); +#include +bool magicear_matched; string trigger_magicear_processmessage(entity ear, entity source, float teamsay, entity privatesay, string msgin) { float domatch, dotrigger, matchstart, l; diff --git a/qcsrc/common/mapobjects/trigger/swamp.qc b/qcsrc/common/mapobjects/trigger/swamp.qc index 0717f4bead..f7d9df2900 100644 --- a/qcsrc/common/mapobjects/trigger/swamp.qc +++ b/qcsrc/common/mapobjects/trigger/swamp.qc @@ -10,23 +10,12 @@ /* * t_swamp.c -* Adds spawnfunc_trigger_swamp and suppoart routines for xonotic 1.2.1+ +* Adds spawnfunc_trigger_swamp and support routines for nexuiz 1.2.1+ and xonotic * Author tZork (Jakob MG) * jakob@games43.se * 2005 11 29 */ -.float swamp_interval; //Hurt players in swamp with this interval -.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) -.float swamp_lifetime; // holds the points remaining until slug dies (not quite health!) -.entity swampslug; - -#ifdef SVQC -spawnfunc(trigger_swamp); -#endif -void swamp_touch(entity this, entity toucher); -void swampslug_think(entity this); - /* * Uses a entity calld swampslug to handle players in the swamp @@ -46,7 +35,11 @@ void swampslug_think(entity this) //Slug dead? then remove curses. if(GetResource(this, RES_HEALTH) <= 0) { - this.owner.in_swamp = 0; + if(this.owner.swampslug == this) + { + this.owner.in_swamp = false; + this.owner.swampslug = NULL; + } delete(this); //centerprint(this.owner,"Killing slug...\n"); return; @@ -72,11 +65,12 @@ void swamp_touch(entity this, entity toucher) EXACTTRIGGER_TOUCH(this, toucher); // Chech if player alredy got a swampslug. - if(toucher.in_swamp != 1) + if(!toucher.in_swamp) { // If not attach one. //centerprint(toucher,"Entering swamp!\n"); - toucher.swampslug = spawn(); + if(!toucher.swampslug) // just incase + toucher.swampslug = spawn(); toucher.swampslug.swamp_lifetime = 2; setthink(toucher.swampslug, swampslug_think); toucher.swampslug.nextthink = time; @@ -84,11 +78,11 @@ void swamp_touch(entity this, entity toucher) toucher.swampslug.dmg = this.dmg; toucher.swampslug.swamp_interval = this.swamp_interval; toucher.swamp_slowdown = this.swamp_slowdown; - toucher.in_swamp = 1; + toucher.in_swamp = true; return; } - //toucher.in_swamp = 1; + //toucher.in_swamp = true; //Revitalize players swampslug toucher.swampslug.swamp_lifetime = 2; @@ -116,8 +110,8 @@ void swamp_link(entity this) } /*QUAKED spawnfunc_trigger_swamp (.5 .5 .5) ? -Players gettin into the swamp will -get slowd down and damaged +Players in the swamp will be +slowed down and damaged over time */ spawnfunc(trigger_swamp) { diff --git a/qcsrc/common/mapobjects/trigger/swamp.qh b/qcsrc/common/mapobjects/trigger/swamp.qh index f4df98378d..bfe860ed03 100644 --- a/qcsrc/common/mapobjects/trigger/swamp.qh +++ b/qcsrc/common/mapobjects/trigger/swamp.qh @@ -2,7 +2,16 @@ .float swamp_interval; //Hurt players in swamp with this interval .float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) -.entity swampslug; -.float in_swamp; // bool +.bool in_swamp; .entity swampslug; // Uses this to release from swamp ("untouch" fix) + +.float swamp_interval; //Hurt players in swamp with this interval +.float swamp_slowdown; //Players in swamp get slowd down by this mutch 0-1 is slowdown 1-~ is speedup (!?) +.float swamp_lifetime; // holds the points remaining until slug dies (not quite health!) + +#ifdef SVQC +spawnfunc(trigger_swamp); +#endif +void swamp_touch(entity this, entity toucher); +void swampslug_think(entity this); diff --git a/qcsrc/common/models/model.qh b/qcsrc/common/models/model.qh index 38aa4e33ad..c6a1c2199b 100644 --- a/qcsrc/common/models/model.qh +++ b/qcsrc/common/models/model.qh @@ -17,7 +17,7 @@ CLASS(Model, Object) LOG_WARNF("Missing model: \"%s\"", s); return; } - profile(sprintf("precache_model(\"%s\")", s)); + //profile(sprintf("precache_model(\"%s\")", s)); precache_model(s); strcpy(this.model_str_, s); } diff --git a/qcsrc/common/monsters/monster/mage.qc b/qcsrc/common/monsters/monster/mage.qc index 86e780e0a4..efdd836d21 100644 --- a/qcsrc/common/monsters/monster/mage.qc +++ b/qcsrc/common/monsters/monster/mage.qc @@ -36,7 +36,8 @@ CLASS(OffhandMageTeleport, OffhandWeapon) player.OffhandMageTeleport_key_pressed = key_pressed; } ENDCLASS(OffhandMageTeleport) -OffhandMageTeleport OFFHAND_MAGE_TELEPORT; STATIC_INIT(OFFHAND_MAGE_TELEPORT) { OFFHAND_MAGE_TELEPORT = NEW(OffhandMageTeleport); } +OffhandMageTeleport OFFHAND_MAGE_TELEPORT; +STATIC_INIT(OFFHAND_MAGE_TELEPORT) { OFFHAND_MAGE_TELEPORT = NEW(OffhandMageTeleport); } float autocvar_g_monster_mage_health; float autocvar_g_monster_mage_damageforcescale = 0.5; diff --git a/qcsrc/common/monsters/sv_monsters.qc b/qcsrc/common/monsters/sv_monsters.qc index 97a68b5f8f..c707f8c38e 100644 --- a/qcsrc/common/monsters/sv_monsters.qc +++ b/qcsrc/common/monsters/sv_monsters.qc @@ -1095,15 +1095,12 @@ void Monster_Move_2D(entity this, float mspeed, bool allow_jumpoff) if(trace_ent && IS_MONSTER(trace_ent)) reverse = true; - // TODO: fix this... tracing is broken if the floor is thin - /* - if(!allow_jumpoff) + if(!allow_jumpoff && IS_ONGROUND(this)) { - a = b - '0 0 32'; - traceline(b, a, MOVE_WORLDONLY, this); + traceline(b, b - '0 0 32', MOVE_NORMAL, this); if(trace_fraction == 1.0) reverse = true; - } */ + } if(reverse) { diff --git a/qcsrc/common/monsters/sv_spawner.qc b/qcsrc/common/monsters/sv_spawner.qc index d3f4ee740a..13c98e93fa 100644 --- a/qcsrc/common/monsters/sv_spawner.qc +++ b/qcsrc/common/monsters/sv_spawner.qc @@ -16,6 +16,7 @@ void spawner_use(entity this, entity actor, entity trigger) e.noalign = this.noalign; e.angles = this.angles; e.monster_skill = this.monster_skill; + e.skin = this.skin; e = spawnmonster(e, this.spawnmob, 0, this, this, this.origin, false, true, this.monster_moveflags); } diff --git a/qcsrc/common/mutators/base.qh b/qcsrc/common/mutators/base.qh index c1d658576e..cc4cc01295 100644 --- a/qcsrc/common/mutators/base.qh +++ b/qcsrc/common/mutators/base.qh @@ -171,7 +171,8 @@ void Mutator_Remove(Mutator mut); bool mutator_log = false; .bool m_added; -#define MUTATOR_IS_ENABLED(this) MUTATOR_##this.mutatorcheck() +#define _MUTATOR_IS_ENABLED(this) this.mutatorcheck() +#define MUTATOR_IS_ENABLED(this) _MUTATOR_IS_ENABLED(MUTATOR_##this) #ifdef GAMEQC /** server mutators activate corresponding client mutators for all clients */ @@ -276,7 +277,7 @@ STATIC_INIT(Mutators) { } STATIC_INIT_LATE(Mutators) { - FOREACH(Mutators, it.mutatorcheck(), Mutator_Add(it)); + FOREACH(Mutators, _MUTATOR_IS_ENABLED(it), Mutator_Add(it)); } #define MUTATOR_ONADD if (mode == MUTATOR_ADDING) diff --git a/qcsrc/common/mutators/mutator/buffs/buffs.qh b/qcsrc/common/mutators/mutator/buffs/buffs.qh index 7d4e583675..5f626bee95 100644 --- a/qcsrc/common/mutators/mutator/buffs/buffs.qh +++ b/qcsrc/common/mutators/mutator/buffs/buffs.qh @@ -35,10 +35,10 @@ CLASS(Buff, Pickup) ENDCLASS(Buff) STATIC_INIT(REGISTER_BUFFS) { - FOREACH(Buffs, true, { - it.m_itemid = BIT(it.m_id - 1); \ - it.m_sprite = strzone(strcat("buff-", it.netname)); \ - }); + FOREACH(Buffs, true, { + it.m_itemid = BIT(it.m_id - 1); + it.m_sprite = strzone(strcat("buff-", it.netname)); + }); } #ifdef SVQC diff --git a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc index fc571678b2..63f3182ea6 100644 --- a/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc +++ b/qcsrc/common/mutators/mutator/instagib/sv_instagib.qc @@ -18,7 +18,7 @@ int autocvar_g_instagib_extralives; float autocvar_g_instagib_speed_highspeed; IntrusiveList g_instagib_items; -STATIC_INIT() +STATIC_INIT(instagib) { g_instagib_items = IL_NEW(); IL_PUSH(g_instagib_items, ITEM_VaporizerCells); diff --git a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc index 1379d586f1..8d2bb318f0 100644 --- a/qcsrc/common/mutators/mutator/itemstime/itemstime.qc +++ b/qcsrc/common/mutators/mutator/itemstime/itemstime.qc @@ -28,16 +28,12 @@ NET_HANDLE(itemstime, bool isNew) #endif #ifdef CSQC -void Item_ItemsTime_Init() -{ - FOREACH(Items, true, { - ItemsTime_time[it.m_id] = -1; - }); - ItemsTime_time[Items_MAX] = -1; -} STATIC_INIT(ItemsTime_Init) { - Item_ItemsTime_Init(); + FOREACH(Items, true, { + ItemsTime_time[it.m_id] = -1; + }); + ItemsTime_time[Items_MAX] = -1; } int autocvar_hud_panel_itemstime = 2; @@ -77,17 +73,11 @@ bool Item_ItemsTime_Allow(GameItem it) // reserve one more spot for superweapons time float it_times[Items_MAX + 1]; -void Item_ItemsTime_Init() -{ - FOREACH(Items, Item_ItemsTime_Allow(it), { - it_times[it.m_id] = -1; - }); - it_times[Items_MAX] = -1; -} - STATIC_INIT(ItemsTime_Init) { - // items time - Item_ItemsTime_Init(); + FOREACH(Items, Item_ItemsTime_Allow(it), { + it_times[it.m_id] = -1; + }); + it_times[Items_MAX] = -1; } void Item_ItemsTime_ResetTimes() diff --git a/qcsrc/common/mutators/mutator/nades/nades.qc b/qcsrc/common/mutators/mutator/nades/nades.qc index 484aaddcda..8dc82dbcf9 100644 --- a/qcsrc/common/mutators/mutator/nades/nades.qc +++ b/qcsrc/common/mutators/mutator/nades/nades.qc @@ -891,15 +891,18 @@ void nade_damage(entity this, entity inflictor, entity attacker, float damage, i damage = this.max_health * 0.1; else if(DEATH_ISWEAPON(deathtype, WEP_SHOCKWAVE) || DEATH_ISWEAPON(deathtype, WEP_SHOTGUN)) // WEAPONTODO { - if(deathtype & HITTYPE_SECONDARY) - { - damage = this.max_health * 0.1; - force *= 10; - } - else + if(!(deathtype & HITTYPE_SECONDARY)) damage = this.max_health * 1.15; } + // melee slaps + entity death_weapon = DEATH_WEAPONOF(deathtype); + if(((deathtype & HITTYPE_SECONDARY) ? (death_weapon.spawnflags & WEP_TYPE_MELEE_SEC) : (death_weapon.spawnflags & WEP_TYPE_MELEE_PRI))) + { + damage = this.max_health * 0.1; + force *= 10; + } + this.velocity += force; UpdateCSQCProjectile(this); @@ -1030,7 +1033,7 @@ void nades_GiveBonus(entity player, float score) if ( STAT(NADE_BONUS_SCORE, player) >= 1 ) { Send_Notification(NOTIF_ONE, player, MSG_CENTER, CENTER_NADE_BONUS); - play2(player, SND(KH_ALARM)); + play2(player, SND(NADE_BONUS)); STAT(NADE_BONUS, player)++; STAT(NADE_BONUS_SCORE, player) -= 1; } @@ -1247,7 +1250,8 @@ CLASS(NadeOffhand, OffhandWeapon) } } ENDCLASS(NadeOffhand) -NadeOffhand OFFHAND_NADE; STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } +NadeOffhand OFFHAND_NADE; +STATIC_INIT(OFFHAND_NADE) { OFFHAND_NADE = NEW(NadeOffhand); } MUTATOR_HOOKFUNCTION(nades, ForbidThrowCurrentWeapon, CBC_ORDER_LAST) { @@ -1444,7 +1448,8 @@ MUTATOR_HOOKFUNCTION(nades, PlayerDies, CBC_ORDER_LAST) if(IS_PLAYER(frag_attacker)) { - float killcount_bonus = ((CS(frag_attacker).killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * CS(frag_attacker).killcount, autocvar_g_nades_bonus_score_medium) : autocvar_g_nades_bonus_score_minor); + float killcount_bonus = ((CS(frag_attacker).killcount >= 1) ? bound(0, autocvar_g_nades_bonus_score_minor * CS(frag_attacker).killcount, autocvar_g_nades_bonus_score_medium) + : autocvar_g_nades_bonus_score_minor); if (SAME_TEAM(frag_attacker, frag_target) || frag_attacker == frag_target) nades_RemoveBonus(frag_attacker); diff --git a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc index 15a2fc2e0c..41ba2da62b 100644 --- a/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc +++ b/qcsrc/common/mutators/mutator/overkill/sv_overkill.qc @@ -11,7 +11,7 @@ bool autocvar_g_overkill_itemwaypoints = true; .Weapon ok_lastwep[MAX_WEAPONSLOTS]; IntrusiveList g_overkill_items; -STATIC_INIT() +STATIC_INIT(overkill) { g_overkill_items = IL_NEW(); IL_PUSH(g_overkill_items, ITEM_HealthMega); diff --git a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh index e2190b66a2..f08e2eceee 100644 --- a/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh +++ b/qcsrc/common/mutators/mutator/waypoints/waypointsprites.qh @@ -53,6 +53,7 @@ float autocvar_g_waypointsprite_timealphaexponent; bool autocvar_g_waypointsprite_turrets = true; float autocvar_g_waypointsprite_turrets_maxdist = 5000; bool autocvar_g_waypointsprite_turrets_text = false; +bool autocvar_g_waypointsprite_turrets_onlyhurt = false; bool autocvar_g_waypointsprite_uppercase; bool autocvar_g_waypointsprite_text; float autocvar_g_waypointsprite_iconsize = 32; diff --git a/qcsrc/common/sounds/all.inc b/qcsrc/common/sounds/all.inc index 31b5ed4873..29f2946b05 100644 --- a/qcsrc/common/sounds/all.inc +++ b/qcsrc/common/sounds/all.inc @@ -198,6 +198,7 @@ SOUND(ONS_CONTROLPOINT_BUILT, "onslaught/controlpoint_built"); SOUND(ONS_CONTROLPOINT_UNDERATTACK, "onslaught/controlpoint_underattack"); SOUND(ONS_DAMAGEBLOCKEDBYSHIELD, "onslaught/damageblockedbyshield"); SOUND(ONS_ELECTRICITY_EXPLODE, "onslaught/electricity_explode"); +SOUND(ONS_GENERATOR_ALARM, "kh/alarm"); // FIXME: unique sound SOUND(ONS_GENERATOR_DECAY, "onslaught/generator_decay"); SOUND(ONS_GENERATOR_UNDERATTACK, "onslaught/generator_underattack"); SOUND(ONS_HIT1, "onslaught/ons_hit1"); @@ -236,6 +237,7 @@ SOUND(VEH_SPIDERBOT_STRAFE, "vehicles/spiderbot_strafe"); SOUND(VEH_SPIDERBOT_WALK, "vehicles/spiderbot_walk"); SOUND(NADE_BEEP, "overkill/grenadebip"); +SOUND(NADE_BONUS, "kh/alarm"); // FIXME: unique sound SOUND(BUFF_LOST, "relics/relic_effect"); diff --git a/qcsrc/common/sounds/sound.qh b/qcsrc/common/sounds/sound.qh index b3eb1ea861..45cd41f15e 100644 --- a/qcsrc/common/sounds/sound.qh +++ b/qcsrc/common/sounds/sound.qh @@ -26,6 +26,7 @@ const int CH_AMBIENT_SINGLE = 9; const float ATTEN_NONE = 0; const float ATTEN_MIN = 0.015625; +const float ATTEN_LOW = 0.2; const float ATTEN_NORM = 0.5; const float ATTEN_LARGE = 1; const float ATTEN_IDLE = 2; @@ -129,7 +130,7 @@ CLASS(Sound, Object) TC(Sound, this); string s = _Sound_fixpath(this.sound_str()); if (!s) return; - profile(sprintf("precache_sound(\"%s\")", s)); + //profile(sprintf("precache_sound(\"%s\")", s)); precache_sound(s); strcpy(this.sound_str_, s); } diff --git a/qcsrc/common/turrets/checkpoint.qc b/qcsrc/common/turrets/checkpoint.qc index f5ba714779..25c61b4b67 100644 --- a/qcsrc/common/turrets/checkpoint.qc +++ b/qcsrc/common/turrets/checkpoint.qc @@ -2,43 +2,6 @@ #ifdef SVQC -/** - turret_checkpoint -**/ - - -//.entity checkpoint_target; - -/* -#define checkpoint_cache_who flagcarried -#define checkpoint_cache_from lastrocket -#define checkpoint_cache_to selected_player -*/ - -/* -entity path_makeorcache(entity forwho,entity start, entity end) -{ - entity pth; - - //pth = pathlib_makepath(forwho, start.origin,end.origin,PFL_GROUNDSNAP,500,1.5,PT_QUICKSTAR); - - return pth; -} -*/ - -void turret_checkpoint_use() -{ -} - -#if 0 -void turret_checkpoint_think(entity this) -{ - if(this.enemy) - te_lightning1(this,this.origin, this.enemy.origin); - - this.nextthink = time + 0.25; -} -#endif /*QUAKED turret_checkpoint (1 0 1) (-32 -32 -32) (32 32 32) -----------KEYS------------ target: .targetname of next waypoint in chain. @@ -46,30 +9,25 @@ wait: Pause at this point # seconds. -----------SPAWNFLAGS----------- ---------NOTES---------- If a loop is of targets are formed, any unit entering this loop will patrol it indefinitly. -If the checkpoint chain in not looped, the unit will go "Roaming" when the last point is reached. +If the checkpoint chain is not looped, the unit will go "Roaming" when the last point is reached. */ -//float tc_acum; void turret_checkpoint_init(entity this) { traceline(this.origin + '0 0 16', this.origin - '0 0 1024', MOVE_WORLDONLY, this); setorigin(this, trace_endpos + '0 0 32'); - if(this.target != "") + if(this.target && this.target != "") { this.enemy = find(NULL, targetname, this.target); - if(this.enemy == NULL) - LOG_TRACE("A turret_checkpoint faild to find its target!"); + if(!this.enemy) + LOG_TRACE("A turret_checkpoint failed to find its target!"); } - //setthink(this, turret_checkpoint_think); - //this.nextthink = time + tc_acum + 0.25; - //tc_acum += 0.25; } spawnfunc(turret_checkpoint) { setorigin(this, this.origin); - setthink(this, turret_checkpoint_init); - this.nextthink = time + 0.2; + InitializeEntity(this, turret_checkpoint_init, INITPRIO_FINDTARGET); } // Compat. diff --git a/qcsrc/common/turrets/cl_turrets.qc b/qcsrc/common/turrets/cl_turrets.qc index f9374497c8..df20e1ad2f 100644 --- a/qcsrc/common/turrets/cl_turrets.qc +++ b/qcsrc/common/turrets/cl_turrets.qc @@ -175,28 +175,37 @@ void turret_draw2d(entity this) t = t * (1 - (1 - waypointsprite_crosshairfadescale) * (1 - bound(0, crosshairdistance/waypointsprite_crosshairfadedistance, 1))); } - o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); - if(autocvar_g_waypointsprite_turrets_text) + bool draw_healthbar = ((this.helpme && time < this.helpme) || !autocvar_g_waypointsprite_turrets_onlyhurt || hud != HUD_NORMAL); + bool draw_text = autocvar_g_waypointsprite_turrets_text; + + if(draw_healthbar || draw_text) // make sure it's actually being drawn + { + o = drawspritearrow(o, M_PI, rgb, a, SPRITE_ARROW_SCALE * t); + } + if(draw_text) { o = drawsprite_TextOrIcon(true, o, M_PI, (SPRITE_HEALTHBAR_WIDTH + 2 * SPRITE_HEALTHBAR_BORDER) * t, rgb, a, waypointsprite_fontsize * '1 1 0', txt); } - drawhealthbar( - o, - 0, - GetResource(this, RES_HEALTH) / 255, - '0 0 0', - '0 0 0', - 0.5 * SPRITE_HEALTHBAR_WIDTH * t, - 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, - SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, - SPRITE_HEALTHBAR_BORDER * t, - 0, - rgb, - a * SPRITE_HEALTHBAR_BORDERALPHA, - rgb, - a * SPRITE_HEALTHBAR_HEALTHALPHA, - DRAWFLAG_NORMAL - ); + if(draw_healthbar) + { + drawhealthbar( + o, + 0, + GetResource(this, RES_HEALTH) / 255, + '0 0 0', + '0 0 0', + 0.5 * SPRITE_HEALTHBAR_WIDTH * t, + 0.5 * SPRITE_HEALTHBAR_HEIGHT * t, + SPRITE_HEALTHBAR_MARGIN * t + 0.5 * waypointsprite_fontsize, + SPRITE_HEALTHBAR_BORDER * t, + 0, + rgb, + a * SPRITE_HEALTHBAR_BORDERALPHA, + rgb, + a * SPRITE_HEALTHBAR_HEALTHALPHA, + DRAWFLAG_NORMAL + ); + } } void turret_construct(entity this, bool isnew) diff --git a/qcsrc/common/weapons/weapon.qh b/qcsrc/common/weapons/weapon.qh index a2623ca201..8c025cb19f 100644 --- a/qcsrc/common/weapons/weapon.qh +++ b/qcsrc/common/weapons/weapon.qh @@ -198,6 +198,7 @@ const int WEP_FLAG_DUALWIELD = BIT(11); // weapon can be dual wielded const int WEP_FLAG_NODUAL = BIT(12); // weapon doesn't work well with dual wielding (fireball etc just explode on fire), doesn't currently prevent anything const int WEP_FLAG_PENETRATEWALLS = BIT(13); // weapon has high calibur bullets that can penetrate thick walls (WEAPONTODO) const int WEP_FLAG_BLEED = BIT(14); // weapon pierces and causes bleeding (used for damage effects) +const int WEP_FLAG_NOTRUEAIM = BIT(15); // weapon doesn't aim directly at targets // variables: string weaponorder_byid; diff --git a/qcsrc/common/weapons/weapon/blaster.qh b/qcsrc/common/weapons/weapon/blaster.qh index c9825adf0b..6297bdf37b 100644 --- a/qcsrc/common/weapons/weapon/blaster.qh +++ b/qcsrc/common/weapons/weapon/blaster.qh @@ -50,7 +50,8 @@ SPAWNFUNC_WEAPON(weapon_laser, WEP_BLASTER) CLASS(OffhandBlaster, OffhandWeapon) ENDCLASS(OffhandBlaster) -OffhandBlaster OFFHAND_BLASTER; STATIC_INIT(OFFHAND_BLASTER) { OFFHAND_BLASTER = NEW(OffhandBlaster); } +OffhandBlaster OFFHAND_BLASTER; +STATIC_INIT(OFFHAND_BLASTER) { OFFHAND_BLASTER = NEW(OffhandBlaster); } #ifdef SVQC .float blaster_damage; diff --git a/qcsrc/common/weapons/weapon/hook.qh b/qcsrc/common/weapons/weapon/hook.qh index 1d15d448ec..bff975e005 100644 --- a/qcsrc/common/weapons/weapon/hook.qh +++ b/qcsrc/common/weapons/weapon/hook.qh @@ -4,7 +4,7 @@ CLASS(Hook, Weapon) /* spawnfunc */ ATTRIB(Hook, m_canonical_spawnfunc, string, "weapon_hook"); /* ammotype */ ATTRIB(Hook, ammo_type, int, RES_FUEL); /* impulse */ ATTRIB(Hook, impulse, int, 0); -/* flags */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); +/* flags */ ATTRIB(Hook, spawnflags, int, WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH | WEP_FLAG_NOTRUEAIM); /* rating */ ATTRIB(Hook, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(Hook, wpcolor, vector, '0 0.5 0'); /* modelname */ ATTRIB(Hook, mdl, string, "hookgun"); @@ -62,7 +62,8 @@ CLASS(OffhandHook, OffhandWeapon) } #endif ENDCLASS(OffhandHook) -OffhandHook OFFHAND_HOOK; STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); } +OffhandHook OFFHAND_HOOK; +STATIC_INIT(OFFHAND_HOOK) { OFFHAND_HOOK = NEW(OffhandHook); } #ifdef SVQC diff --git a/qcsrc/common/weapons/weapon/machinegun.qc b/qcsrc/common/weapons/weapon/machinegun.qc index 6679f2135f..2d88a9deb2 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qc +++ b/qcsrc/common/weapons/weapon/machinegun.qc @@ -164,7 +164,7 @@ void W_MachineGun_Attack_Burst(Weapon thiswep, entity actor, .entity weaponentit actor.punchangle_y = random() - 0.5; } - fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_speed), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET); + fireBullet(actor, weaponentity, w_shotorg, w_shotdir, WEP_CVAR(machinegun, burst_spread), WEP_CVAR(machinegun, solidpenetration), WEP_CVAR(machinegun, sustained_damage), WEP_CVAR(machinegun, sustained_force), thiswep.m_id, EFFECT_BULLET); Send_Effect(EFFECT_MACHINEGUN_MUZZLEFLASH, w_shotorg, w_shotdir * 1000, 1); diff --git a/qcsrc/common/weapons/weapon/machinegun.qh b/qcsrc/common/weapons/weapon/machinegun.qh index a53d17a8d9..19bba63a2b 100644 --- a/qcsrc/common/weapons/weapon/machinegun.qh +++ b/qcsrc/common/weapons/weapon/machinegun.qh @@ -24,7 +24,7 @@ CLASS(MachineGun, Weapon) P(class, prefix, burst_animtime, float, NONE) \ P(class, prefix, burst_refire2, float, NONE) \ P(class, prefix, burst_refire, float, NONE) \ - P(class, prefix, burst_speed, float, NONE) \ + P(class, prefix, burst_spread, float, NONE) \ P(class, prefix, first, float, NONE) \ P(class, prefix, first_ammo, float, NONE) \ P(class, prefix, first_damage, float, NONE) \ diff --git a/qcsrc/common/weapons/weapon/mortar.qh b/qcsrc/common/weapons/weapon/mortar.qh index d83fe59dc6..4593442379 100644 --- a/qcsrc/common/weapons/weapon/mortar.qh +++ b/qcsrc/common/weapons/weapon/mortar.qh @@ -4,7 +4,7 @@ CLASS(Mortar, Weapon) /* spawnfunc */ ATTRIB(Mortar, m_canonical_spawnfunc, string, "weapon_mortar"); /* ammotype */ ATTRIB(Mortar, ammo_type, int, RES_ROCKETS); /* impulse */ ATTRIB(Mortar, impulse, int, 4); -/* flags */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH); +/* flags */ ATTRIB(Mortar, spawnflags, int, WEP_FLAG_NORMAL | WEP_FLAG_RELOADABLE | WEP_FLAG_CANCLIMB | WEP_TYPE_SPLASH | WEP_FLAG_NOTRUEAIM); /* rating */ ATTRIB(Mortar, bot_pickupbasevalue, float, 7000); /* color */ ATTRIB(Mortar, wpcolor, vector, '1 0 0'); /* modelname */ ATTRIB(Mortar, mdl, string, "gl"); diff --git a/qcsrc/common/weapons/weapon/porto.qc b/qcsrc/common/weapons/weapon/porto.qc index 722171b958..0e482d8ae8 100644 --- a/qcsrc/common/weapons/weapon/porto.qc +++ b/qcsrc/common/weapons/weapon/porto.qc @@ -1,5 +1,84 @@ #include "porto.qh" +#ifdef CSQC +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; +} + +const int polyline_length = 16; +.vector polyline[polyline_length]; +void Porto_Draw(entity this) +{ + if (spectatee_status || intermission == 1 || intermission == 2 || STAT(HEALTH) <= 0 || WEP_CVAR(porto, secondary)) return; + + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + { + entity wepent = viewmodels[slot]; + + if (wepent.activeweapon != WEP_PORTO) continue; + + vector pos = view_origin; + vector dir = view_forward; + makevectors(((autocvar_chase_active) ? warpzone_save_view_angles : view_angles)); + pos += v_right * -wepent.movedir.y + + v_up * wepent.movedir.z; + + if (wepent.angles_held_status) + { + makevectors(wepent.angles_held); + dir = v_forward; + } + + wepent.polyline[0] = pos; + + int portal_number = 0, portal1_idx = 1, portal_max = 2; + int n = 1 + 2; // 2 lines == 3 points + for (int idx = 0; idx < n && idx < polyline_length - 1; ) + { + traceline(pos, pos + 65536 * dir, true, this); + dir = reflect(dir, trace_plane_normal); + pos = trace_endpos; + wepent.polyline[++idx] = pos; + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_SLICK || trace_dphitcontents & DPCONTENTS_PLAYERCLIP) + { + n += 1; + continue; + } + if (trace_dphitq3surfaceflags & Q3SURFACEFLAG_NOIMPACT) + { + n = max(2, idx); + break; + } + // check size + { + vector ang = vectoangles2(trace_plane_normal, dir); + ang.x = -ang.x; + makevectors(ang); + if (!CheckWireframeBox(this, pos - 48 * v_right - 48 * v_up + 16 * v_forward, 96 * v_right, 96 * v_up, 96 * v_forward)) + { + n = max(2, idx); + break; + } + } + portal_number += 1; + if (portal_number >= portal_max) break; + if (portal_number == 1) portal1_idx = idx; + } + for (int idx = 0; idx < n - 1; ++idx) + { + vector p = wepent.polyline[idx], q = wepent.polyline[idx + 1]; + if (idx == 0) p -= view_up * 16; // line from player + vector rgb = (idx < portal1_idx) ? '1 0 0' : '0 0 1'; + Draw_CylindricLine(p, q, 4, "", 1, 0, rgb, 0.5, DRAWFLAG_NORMAL, view_origin); + } + } +} +#endif + #ifdef SVQC #include #include diff --git a/qcsrc/common/weapons/weapon/porto.qh b/qcsrc/common/weapons/weapon/porto.qh index 735426feb6..29820ef06b 100644 --- a/qcsrc/common/weapons/weapon/porto.qh +++ b/qcsrc/common/weapons/weapon/porto.qh @@ -4,7 +4,7 @@ CLASS(PortoLaunch, Weapon) /* spawnfunc */ ATTRIB(PortoLaunch, m_canonical_spawnfunc, string, "weapon_porto"); /* ammotype */ ATTRIB(PortoLaunch, ammo_type, int, RES_NONE); /* impulse */ ATTRIB(PortoLaunch, impulse, int, 0); -/* flags */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON | WEP_FLAG_NODUAL); +/* flags */ ATTRIB(PortoLaunch, spawnflags, int, WEP_TYPE_OTHER | WEP_FLAG_SUPERWEAPON | WEP_FLAG_NODUAL | WEP_FLAG_NOTRUEAIM); /* rating */ ATTRIB(PortoLaunch, bot_pickupbasevalue, float, 0); /* color */ ATTRIB(PortoLaunch, wpcolor, vector, '0.5 0.5 0.5'); /* modelname */ ATTRIB(PortoLaunch, mdl, string, "porto"); @@ -38,6 +38,10 @@ REGISTER_WEAPON(PORTO, porto, NEW(PortoLaunch)); SPAWNFUNC_WEAPON(weapon_porto, WEP_PORTO) +#ifdef CSQC +void Porto_Draw(entity this); +#endif + #ifdef SVQC .entity porto_current; .vector porto_v_angle; // holds "held" view angles diff --git a/qcsrc/common/weapons/weapon/tuba.qc b/qcsrc/common/weapons/weapon/tuba.qc index eb78080893..bf5fa0c98f 100644 --- a/qcsrc/common/weapons/weapon/tuba.qc +++ b/qcsrc/common/weapons/weapon/tuba.qc @@ -268,7 +268,7 @@ void W_Tuba_NoteOn(entity actor, .entity weaponentity, float hittype) vector o; float n = W_Tuba_GetNote(actor, hittype); - hittype = 0; + hittype = HITTYPE_SOUND; if(actor.(weaponentity).tuba_instrument & 1) hittype |= HITTYPE_SECONDARY; if(actor.(weaponentity).tuba_instrument & 2) diff --git a/qcsrc/common/weapons/weapon/tuba.qh b/qcsrc/common/weapons/weapon/tuba.qh index d932d98bd1..335326514d 100644 --- a/qcsrc/common/weapons/weapon/tuba.qh +++ b/qcsrc/common/weapons/weapon/tuba.qh @@ -3,7 +3,7 @@ CLASS(Tuba, Weapon) /* spawnfunc */ ATTRIB(Tuba, m_canonical_spawnfunc, string, "weapon_tuba"); /* impulse */ ATTRIB(Tuba, impulse, int, 1); -/* flags */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL); +/* flags */ ATTRIB(Tuba, spawnflags, int, WEP_FLAG_HIDDEN | WEP_TYPE_SPLASH | WEP_FLAG_NODUAL | WEP_FLAG_NOTRUEAIM); /* rating */ ATTRIB(Tuba, bot_pickupbasevalue, float, 2000); /* color */ ATTRIB(Tuba, wpcolor, vector, '0 1 0'); /* modelname */ ATTRIB(Tuba, mdl, string, "tuba"); @@ -51,3 +51,7 @@ classfield(Tuba) .float tuba_volume; classfield(Tuba) .float tuba_volume_initial; classfield(Tuba) .int tuba_instrument; #endif + +#ifdef SVQC +bool W_Tuba_HasPlayed(entity pl, .entity weaponentity, string melody, int instrument, bool ignorepitch, float mintempo, float maxtempo); +#endif diff --git a/qcsrc/common/wepent.qc b/qcsrc/common/wepent.qc index 6b1797c664..9193c4ef16 100644 --- a/qcsrc/common/wepent.qc +++ b/qcsrc/common/wepent.qc @@ -36,10 +36,10 @@ MACRO_END \ PROP(false, porto_v_angle_held, WEPENT_SET_NORMAL, \ { WriteByte(chan, this.porto_v_angle_held); if(this.porto_v_angle_held) { \ - WriteAngle(chan, this.porto_v_angle.x); WriteAngle(chan, this.porto_v_angle.y); \ + WriteAngle(chan, this.owner.porto_v_angle.x); WriteAngle(chan, this.owner.porto_v_angle.y); \ } }, \ { (viewmodels[this.m_wepent_slot]).angles_held_status = ReadByte(); if((viewmodels[this.m_wepent_slot]).angles_held_status) { \ - (viewmodels[this.m_wepent_slot]).angles_held_x = ReadAngle(); (viewmodels[this.m_wepent_slot]).angles_held_y = ReadAngle(); (viewmodels[this.m_wepent_slot]).angles_held_z = 0; } \ + (viewmodels[this.m_wepent_slot]).angles_held = vec2(ReadAngle(), ReadAngle()); } \ else { (viewmodels[this.m_wepent_slot]).angles_held = '0 0 0'; } }) \ \ PROP(false, tuba_instrument, WEPENT_SET_NORMAL, \ diff --git a/qcsrc/lib/registry.qh b/qcsrc/lib/registry.qh index 4e8e09083f..a48e83ac1a 100644 --- a/qcsrc/lib/registry.qh +++ b/qcsrc/lib/registry.qh @@ -155,7 +155,7 @@ MACRO_END #define REGISTRY_HASH(id) Registry_hash_##id ERASEABLE -ACCUMULATE void Registry_check(string r, string server) { } +ACCUMULATE void Registry_check(string r, string sv) { } ERASEABLE ACCUMULATE void Registry_send_all() { } @@ -170,9 +170,9 @@ void Registry_send(string id, string hash); STATIC_INIT(Registry_check_##id) \ { \ /* Note: SHA256 isn't always available, use MD4 instead */ \ - string s = "", join = ":"; \ - FOREACH(id, true, s = strcat(s, join, it.registered_id)); \ - s = substring(s, strlen(join), -1); \ + string s = ""; \ + FOREACH(id, true, s = strcat(s, ":", it.registered_id)); \ + s = substring(s, 1, -1); /* remove initial ":" */ \ string h = REGISTRY_HASH(id) = strzone(digest_hex("MD4", s)); \ LOG_DEBUGF(#id ": %s\n[%s]", h, s); \ } \ diff --git a/qcsrc/lib/spawnfunc.qh b/qcsrc/lib/spawnfunc.qh index e2a25cbc76..244665cc5f 100644 --- a/qcsrc/lib/spawnfunc.qh +++ b/qcsrc/lib/spawnfunc.qh @@ -146,12 +146,14 @@ noref bool require_spawnfunc_prefix; FIELD_SCALAR(fld, health) \ FIELD_SCALAR(fld, height) \ FIELD_SCALAR(fld, impulse) \ + FIELD_SCALAR(fld, item_pickupsound) \ FIELD_SCALAR(fld, killtarget) \ FIELD_SCALAR(fld, lerpfrac) \ FIELD_SCALAR(fld, light_lev) \ FIELD_SCALAR(fld, lip) \ FIELD_SCALAR(fld, loddistance1) \ FIELD_SCALAR(fld, lodmodel1) \ + FIELD_SCALAR(fld, lodmodel2) \ FIELD_SCALAR(fld, ltime) \ FIELD_SCALAR(fld, map) \ FIELD_SCALAR(fld, max_health) \ diff --git a/qcsrc/server/autocvars.qh b/qcsrc/server/autocvars.qh index a763b97267..4b71e28e8b 100644 --- a/qcsrc/server/autocvars.qh +++ b/qcsrc/server/autocvars.qh @@ -156,6 +156,7 @@ string autocvar_g_maplist_votable_screenshot_dir; bool autocvar_g_maplist_votable_suggestions; bool autocvar_g_maplist_votable_suggestions_override_mostrecent; float autocvar_g_maplist_votable_timeout; +bool autocvar_g_maplist_ignore_sizes; int autocvar_g_maxplayers; float autocvar_g_maxplayers_spectator_blocktime; float autocvar_g_maxpushtime; @@ -179,6 +180,7 @@ float autocvar_g_items_maxdist; int autocvar_g_pickup_items; float autocvar_g_player_alpha; float autocvar_g_player_brightness; +float autocvar_g_player_damageforcescale = 2; bool autocvar_g_playerclip_collisions; int autocvar_g_powerups; int autocvar_g_projectiles_damage; @@ -252,7 +254,6 @@ int autocvar_leadlimit_and_fraglimit; int autocvar_leadlimit_override; int autocvar_minplayers; int autocvar_minplayers_per_team; -int autocvar_name_maxlength; string autocvar_nextmap; string autocvar_quit_and_redirect; float autocvar_quit_and_redirect_timer; @@ -315,6 +316,7 @@ float autocvar_sv_mapchange_delay; float autocvar_sv_maxairspeed; float autocvar_sv_maxspeed; string autocvar_sv_motd; +int autocvar_sv_name_maxlength = 64; bool autocvar_sv_precacheplayermodels; bool autocvar_sv_q3acompat_machineshotgunswap; bool autocvar_sv_servermodelsonly; diff --git a/qcsrc/server/bot/api.qh b/qcsrc/server/bot/api.qh index 3f434dbecc..35b52e3d91 100644 --- a/qcsrc/server/bot/api.qh +++ b/qcsrc/server/bot/api.qh @@ -15,9 +15,6 @@ const int WAYPOINTFLAG_DEAD_END = BIT(16); // Useless WP detection temporary fl const int WAYPOINTFLAG_LADDER = BIT(15); const int WAYPOINTFLAG_JUMP = BIT(14); -entity kh_worldkeylist; -.entity kh_worldkeynext; - float bot_custom_weapon; float bot_weapons_close[Weapons_MAX]; float bot_weapons_far[Weapons_MAX]; diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qc b/qcsrc/server/bot/default/havocbot/havocbot.qc index 57b4c310e3..2b2dfbf3cf 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qc +++ b/qcsrc/server/bot/default/havocbot/havocbot.qc @@ -1,5 +1,7 @@ #include "havocbot.qh" +#include "roles.qh" + #include #include #include "../cvars.qh" @@ -24,8 +26,6 @@ #include -.float speed; - void havocbot_ai(entity this) { if(this.draggedby) @@ -193,82 +193,76 @@ void havocbot_ai(entity this) void havocbot_keyboard_movement(entity this, vector destorg) { - vector keyboard; + if(time <= this.havocbot_keyboardtime) + return; - if (time > this.havocbot_keyboardtime) + float sk = skill + this.bot_moveskill; + 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); + vector keyboard = CS(this).movement / autocvar_sv_maxspeed; + + float trigger = autocvar_bot_ai_keyboard_threshold; + + // 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) { - float sk = skill + this.bot_moveskill; - 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 = CS(this).movement / autocvar_sv_maxspeed; - - float trigger = autocvar_bot_ai_keyboard_threshold; - - // 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 < -trigger && sk > 1.5) - { - keyboard.x = -1; - if (sk < 4.5) - keyboard.y = 0; - } - else - { - keyboard.x = 0; - if (sk < 1.5) - keyboard.y = 0; - } + keyboard.x = 1; + if (sk < 2.5) + keyboard.y = 0; + } + else if (keyboard.x < -trigger && sk > 1.5) + { + keyboard.x = -1; if (sk < 4.5) - keyboard.z = 0; - - if (keyboard.y > trigger) - keyboard.y = 1; - else if (keyboard.y < -trigger) - keyboard.y = -1; - else keyboard.y = 0; + } + else + { + keyboard.x = 0; + if (sk < 1.5) + keyboard.y = 0; + } + if (sk < 4.5) + keyboard.z = 0; - if (keyboard.z > trigger) - keyboard.z = 1; - else if (keyboard.z < -trigger) - keyboard.z = -1; - else - keyboard.z = 0; + if (keyboard.y > trigger) + keyboard.y = 1; + else if (keyboard.y < -trigger) + keyboard.y = -1; + else + keyboard.y = 0; - // make sure bots don't get stuck if havocbot_keyboardtime is very high - if (keyboard == '0 0 0') - this.havocbot_keyboardtime = min(this.havocbot_keyboardtime, time + 0.2); + if (keyboard.z > trigger) + keyboard.z = 1; + else if (keyboard.z < -trigger) + keyboard.z = -1; + else + keyboard.z = 0; - this.havocbot_keyboard = keyboard * autocvar_sv_maxspeed; - if (this.havocbot_ducktime > time) - PHYS_INPUT_BUTTON_CROUCH(this) = true; + // make sure bots don't get stuck if havocbot_keyboardtime is very high + if (keyboard == '0 0 0') + this.havocbot_keyboardtime = min(this.havocbot_keyboardtime, time + 0.2); - keyboard = this.havocbot_keyboard; - float blend = bound(0, vlen(destorg - this.origin) / autocvar_bot_ai_keyboard_distance, 1); // When getting close move with 360 degree - //dprint("movement ", vtos(CS(this).movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); - CS(this).movement = CS(this).movement + (keyboard - CS(this).movement) * blend; - } + this.havocbot_keyboard = keyboard * autocvar_sv_maxspeed; + if (this.havocbot_ducktime > time) + PHYS_INPUT_BUTTON_CROUCH(this) = true; + + keyboard = this.havocbot_keyboard; + float blend = bound(0, vlen(destorg - this.origin) / autocvar_bot_ai_keyboard_distance, 1); // When getting close move with 360 degree + //dprint("movement ", vtos(CS(this).movement), " keyboard ", vtos(keyboard), " blend ", ftos(blend), "\n"); + CS(this).movement = CS(this).movement + (keyboard - CS(this).movement) * blend; } void havocbot_bunnyhop(entity this, vector dir) { - float bunnyhopdistance; - vector deviation; - float maxspeed; - // Don't jump when attacking if(this.aistatus & AI_STATUS_ATTACKING) return; @@ -276,10 +270,8 @@ void havocbot_bunnyhop(entity this, vector dir) if(IS_PLAYER(this.goalcurrent)) return; - maxspeed = autocvar_sv_maxspeed; - - if(this.aistatus & AI_STATUS_RUNNING && vdist(this.velocity, <, autocvar_sv_maxspeed * 0.75) - || this.aistatus & AI_STATUS_DANGER_AHEAD) + if((this.aistatus & AI_STATUS_RUNNING) && vdist(this.velocity, <, autocvar_sv_maxspeed * 0.75) + || (this.aistatus & AI_STATUS_DANGER_AHEAD)) { this.aistatus &= ~AI_STATUS_RUNNING; PHYS_INPUT_BUTTON_JUMP(this) = false; @@ -301,110 +293,80 @@ void havocbot_bunnyhop(entity this, vector dir) } vector gco = get_closer_dest(this.goalcurrent, this.origin); - bunnyhopdistance = vlen(this.origin - gco); // Run only to visible goals if(IS_ONGROUND(this)) - if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed)) // if -really- running + if(vdist(vec2(this.velocity), >=, autocvar_sv_maxspeed)) if(checkpvs(this.origin + this.view_ofs, this.goalcurrent)) { - this.bot_lastseengoal = this.goalcurrent; + this.bot_lastseengoal = this.goalcurrent; - // seen it before - if(this.bot_timelastseengoal) + // seen it before + if(this.bot_timelastseengoal) + { + // for a period of time + if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay) { - // for a period of time - if(time - this.bot_timelastseengoal > autocvar_bot_ai_bunnyhop_firstjumpdelay) - { - float checkdistance; - checkdistance = true; + bool 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; - } + // don't run if it is too close + if(this.bot_canruntogoal==0) + { + if(vdist(this.origin - gco, >, autocvar_bot_ai_bunnyhop_startdistance)) + this.bot_canruntogoal = 1; + else + this.bot_canruntogoal = -1; + } - if(this.bot_canruntogoal != 1) - return; + 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)) + 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)) + { + vector gno = (this.goalstack01.absmin + this.goalstack01.absmax) * 0.5; + vector 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(vlen2(this.origin - gco) < vlen2(this.origin - gno)) + if(fabs(gno.z - gco.z) < this.maxs.z - this.mins.z) { - vector 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)) { - if(vdist(gco - gno, >, autocvar_bot_ai_bunnyhop_startdistance)) - if(checkpvs(this.origin + this.view_ofs, this.goalstack01)) - { - checkdistance = false; - } + 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; + if(checkdistance) + { + this.aistatus &= ~AI_STATUS_RUNNING; + // increase stop distance in case the goal is on a slope or a lower platform + if(vdist(this.origin - gco, >, 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 = 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) - CS(this).movement_x = 0; - - if(deviation.y>10) - CS(this).movement_y = maxspeed * -1; - else if(deviation.y<10) - CS(this).movement_y = maxspeed; - - } - } -#endif } // return true when bot isn't getting closer to the current goal diff --git a/qcsrc/server/bot/default/havocbot/havocbot.qh b/qcsrc/server/bot/default/havocbot/havocbot.qh index 2ec6316917..d1a36a1172 100644 --- a/qcsrc/server/bot/default/havocbot/havocbot.qh +++ b/qcsrc/server/bot/default/havocbot/havocbot.qh @@ -51,9 +51,6 @@ 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; diff --git a/qcsrc/server/bot/default/havocbot/roles.qc b/qcsrc/server/bot/default/havocbot/roles.qc index b72153c95c..84c597329e 100644 --- a/qcsrc/server/bot/default/havocbot/roles.qc +++ b/qcsrc/server/bot/default/havocbot/roles.qc @@ -11,14 +11,6 @@ #include "../bot.qh" #include "../navigation.qh" -.float bot_ratingscale; -.float bot_ratingscale_time; -.float max_armorvalue; -.float havocbot_role_timeout; - -.void(entity this) havocbot_previous_role; -.void(entity this) havocbot_role; - void havocbot_goalrating_waypoints(entity this, float ratingscale, vector org, float sradius) { // rate waypoints only if there's no alternative goal @@ -68,10 +60,10 @@ bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, en if(!teamplay) return true; - // actually these variables hold the squared distances in order to optimize code - float friend_distance = FLOAT_MAX; - float enemy_distance = FLOAT_MAX; - float dist; + // these variables hold squared distances in order to optimize code + float friend_dist2 = FLOAT_MAX; + float enemy_dist2 = FLOAT_MAX; + float dist2; FOREACH_CLIENT(IS_PLAYER(it) && it != this && !(IS_DEAD(it) || STAT(FROZEN, it)), { @@ -80,13 +72,13 @@ bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, en if (!IS_REAL_CLIENT(it)) continue; - dist = vlen2(it.origin - item_org); - if(dist > friend_distance) + dist2 = vlen2(it.origin - item_org); + if (dist2 > friend_dist2) continue; if(havocbot_goalrating_item_can_be_left_to_teammate(this, it, item)) { - friend_distance = dist; + friend_dist2 = dist2; continue; } } @@ -94,25 +86,23 @@ bool havocbot_goalrating_item_pickable_check_players(entity this, vector org, en { // If enemy only track distances // TODO: track only if visible ? - dist = vlen2(it.origin - item_org); - if(dist < enemy_distance) - enemy_distance = dist; + dist2 = vlen2(it.origin - item_org); + if (dist2 < enemy_dist2) + enemy_dist2 = dist2; } }); // Rate the item only if no one needs it, or if an enemy is closer to it - dist = vlen2(item_org - org); - if ((enemy_distance < friend_distance && dist < enemy_distance) || - (friend_distance > autocvar_bot_ai_friends_aware_pickup_radius ** 2) || - (dist < friend_distance && dist < 200 ** 2)) + dist2 = vlen2(item_org - org); + if ((enemy_dist2 < friend_dist2 && dist2 < enemy_dist2) + || (friend_dist2 > autocvar_bot_ai_friends_aware_pickup_radius ** 2) + || (dist2 < friend_dist2 && dist2 < 200 ** 2)) return true; return false; }; void havocbot_goalrating_items(entity this, float ratingscale, vector org, float sradius) { - float rating; - vector o; ratingscale = ratingscale * 0.0001; IL_EACH(g_items, it.bot_pickup, @@ -144,7 +134,7 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float it.bot_pickup_respawning = true; } - o = (it.absmin + it.absmax) * 0.5; + vector o = (it.absmin + it.absmax) * 0.5; if(vdist(o - org, >, sradius) || (it == this.ignoregoal && time < this.ignoregoaltime) ) continue; @@ -175,25 +165,23 @@ void havocbot_goalrating_items(entity this, float ratingscale, vector org, float it.bot_ratingscale_time = time; it.bot_ratingscale = ratingscale; - rating = it.bot_pickupevalfunc(this, it); + float rating = it.bot_pickupevalfunc(this, it); if(rating > 0) navigation_routerating(this, it, rating * ratingscale, 2000); }); } -#define BOT_RATING_ENEMY 2500 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) + if(this.waterlevel > WATERLEVEL_WETFEET) return; ratingscale = ratingscale * 0.0001; - float t; FOREACH_CLIENT(IS_PLAYER(it) && bot_shouldattack(this, it), { // TODO: Merge this logic with the bot_shouldattack function if(vdist(it.origin - org, <, 100) || vdist(it.origin - org, >, sradius)) @@ -208,7 +196,7 @@ void havocbot_goalrating_enemyplayers(entity this, float ratingscale, vector org continue; */ - t = ((GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR)) - (GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR))) / 150; + float t = ((GetResource(this, RES_HEALTH) + GetResource(this, RES_ARMOR)) - (GetResource(it, RES_HEALTH) + GetResource(it, RES_ARMOR))) / 150; t = bound(0, 1 + t, 3); if (skill > 3) { diff --git a/qcsrc/server/bot/default/havocbot/roles.qh b/qcsrc/server/bot/default/havocbot/roles.qh index 6f70f09bee..23a23759a9 100644 --- a/qcsrc/server/bot/default/havocbot/roles.qh +++ b/qcsrc/server/bot/default/havocbot/roles.qh @@ -1 +1,10 @@ #pragma once + +const float BOT_RATING_ENEMY = 2500; + +.float bot_ratingscale; +.float bot_ratingscale_time; +.float havocbot_role_timeout; + +.void(entity this) havocbot_previous_role; +.void(entity this) havocbot_role; diff --git a/qcsrc/server/client.qc b/qcsrc/server/client.qc index 639717a901..209958389b 100644 --- a/qcsrc/server/client.qc +++ b/qcsrc/server/client.qc @@ -40,6 +40,7 @@ #include "../common/mapobjects/teleporters.qh" #include "../common/mapobjects/target/spawnpoint.qh" #include +#include #include "../common/vehicles/all.qh" @@ -604,7 +605,7 @@ void PutPlayerInServer(entity this) this.pauseregen_finished += f; } - this.damageforcescale = 2; + this.damageforcescale = autocvar_g_player_damageforcescale; this.death_time = 0; this.respawn_flags = 0; this.respawn_time = 0; @@ -678,6 +679,9 @@ void PutPlayerInServer(entity this) if(this.conveyor) IL_REMOVE(g_conveyed, this); this.conveyor = NULL; // prevent conveyors at the previous location from moving a freshly spawned player + if(this.swampslug) + delete(this.swampslug); + this.in_swamp = false; STAT(HUD, this) = HUD_NORMAL; this.event_damage = PlayerDamage; @@ -1034,7 +1038,6 @@ string getwelcomemessage(entity this) string versionmessage = GetClientVersionMessage(this); string s = strcat(versionmessage, "^8\n^8\nmatch type is ^1", gamemode_name, "^8\n"); - s = strcat(s, "^8\nCurrent map: ^2", GetMapname(), "^8\n"); if(modifications != "") s = strcat(s, "^8\nactive modifications: ^3", modifications, "^8\n"); @@ -2045,12 +2048,21 @@ void PrintWelcomeMessage(entity this) { if(PHYS_INPUT_BUTTON_INFO(this)) // BUTTON_INFO hides initial MOTD CS(this).motd_actived_time = -2; // wait until BUTTON_INFO gets released - else if(CS(this).motd_actived_time == -2 || IS_PLAYER(this) || IS_SPEC(this)) + else if (CS(this).motd_actived_time == -2) { // instantly hide MOTD CS(this).motd_actived_time = 0; Kill_Notification(NOTIF_ONE_ONLY, this, MSG_CENTER, CPID_MOTD); } + else if (IS_PLAYER(this) || IS_SPEC(this)) + { + // FIXME occasionally for some reason MOTD never goes away + // delay MOTD removal a little bit in the hope it fixes this bug + if (CS(this).motd_actived_time == -1) // MOTD marked to fade away as soon as client becomes player or spectator + CS(this).motd_actived_time = -(5 + floor(random() * 10)); // add small delay + else //if (CS(this).motd_actived_time < -2) + CS(this).motd_actived_time++; + } } } @@ -2393,11 +2405,11 @@ void PlayerPreThink (entity this) if (this.netname == "" || this.netname != CS(this).netname_previous) { bool assume_unchanged = (CS(this).netname_previous == ""); - if (autocvar_name_maxlength > 0 && strlennocol(this.netname) > autocvar_name_maxlength) + if (autocvar_sv_name_maxlength > 0 && strlennocol(this.netname) > autocvar_sv_name_maxlength) { - int new_length = textLengthUpToLength(this.netname, autocvar_name_maxlength, strlennocol); + int new_length = textLengthUpToLength(this.netname, autocvar_sv_name_maxlength, strlennocol); this.netname = strzone(strcat(substring(this.netname, 0, new_length), "^7")); - sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_name_maxlength)); + sprint(this, sprintf("Warning: your name is longer than %d characters, it has been truncated.\n", autocvar_sv_name_maxlength)); assume_unchanged = false; // stuffcmd(this, strcat("name ", this.netname, "\n")); // maybe? } diff --git a/qcsrc/server/defs.qh b/qcsrc/server/defs.qh index 28a61cf1cd..b52649a34e 100644 --- a/qcsrc/server/defs.qh +++ b/qcsrc/server/defs.qh @@ -298,11 +298,6 @@ string matchid; bool radar_showennemies; -#ifdef PROFILING -float client_cefc_accumulator; -float client_cefc_accumulatortime; -#endif - .float weapon_load[Weapons_MAX]; .int ammo_none; // used by the reloading system, must always be 0 .int clip_load; diff --git a/qcsrc/server/g_damage.qc b/qcsrc/server/g_damage.qc index 0901ab4478..1688f506c7 100644 --- a/qcsrc/server/g_damage.qc +++ b/qcsrc/server/g_damage.qc @@ -74,8 +74,6 @@ void GiveFrags(entity attacker, entity targ, float f, int deathtype, .entity wea UpdateFrags(attacker, f); } -.entity kh_next; - string AppendItemcodes(string s, entity player) { for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) @@ -91,12 +89,11 @@ string AppendItemcodes(string s, entity player) s = strcat(s, "S"); if(time < player.invincible_finished) s = strcat(s, "I"); - if(player.flagcarried != NULL) - s = strcat(s, "F"); if(PHYS_INPUT_BUTTON_CHAT(player)) s = strcat(s, "T"); - if(player.kh_next) - s = strcat(s, "K"); + // TODO: include these codes as a flag on the item itself + MUTATOR_CALLHOOK(LogDeath_AppendItemCodes, player, s); + s = M_ARGV(1, string); return s; } @@ -953,7 +950,7 @@ float RadiusDamageForSource (entity inflictor, vector inflictororigin, vector in hitloc = nearest; if(deathtype & WEP_BLASTER.m_id) - force *= WEP_CVAR_BOTH(blaster, !(deathtype & HITTYPE_SECONDARY), force_zscale); + force.z *= WEP_CVAR_BOTH(blaster, !(deathtype & HITTYPE_SECONDARY), force_zscale); if(targ != directhitentity) { diff --git a/qcsrc/server/g_hook.qc b/qcsrc/server/g_hook.qc index 453cfc1e70..b2b91bc02b 100644 --- a/qcsrc/server/g_hook.qc +++ b/qcsrc/server/g_hook.qc @@ -21,8 +21,6 @@ #include "../lib/warpzone/common.qh" #include "../lib/warpzone/server.qh" -.int state; - /*============================================ Wazat's Xonotic Grappling Hook @@ -72,8 +70,6 @@ And you should be done! ============================================*/ -.float hook_length; - void RemoveGrapplingHooks(entity pl) { if(pl.move_movetype == MOVETYPE_FLY) diff --git a/qcsrc/server/g_hook.qh b/qcsrc/server/g_hook.qh index c0df31662a..1ed78e2745 100644 --- a/qcsrc/server/g_hook.qh +++ b/qcsrc/server/g_hook.qh @@ -8,12 +8,16 @@ void RemoveHook(entity this); // (note: you can change the hook impulse #'s to whatever you please) .float hook_time; +.float hook_length; + const float HOOK_FIRING = BIT(0); const float HOOK_REMOVING = BIT(1); const float HOOK_PULLING = BIT(2); const float HOOK_RELEASING = BIT(3); const float HOOK_WAITING_FOR_RELEASE = BIT(4); .float hook_state; +.int state; void GrappleHookInit(); vector hook_shotorigin[4]; + diff --git a/qcsrc/server/g_world.qc b/qcsrc/server/g_world.qc index 9ef72ae832..8680b83a58 100644 --- a/qcsrc/server/g_world.qc +++ b/qcsrc/server/g_world.qc @@ -860,8 +860,14 @@ spawnfunc(worldspawn) continue; if(argv(0) == "cd") { + string trackname = argv(2); LOG_INFO("Found ^1UNSUPPORTED^7 cd loop command in .cfg file; put this line in mapinfo instead:"); - LOG_INFO(" cdtrack ", argv(2)); + LOG_INFO(" cdtrack ", trackname); + if (cvar_value_issafe(trackname)) + { + string newstuff = strcat(clientstuff, "cd loop \"", trackname, "\"\n"); + strcpy(clientstuff, newstuff); + } } else if(argv(0) == "fog") { @@ -984,22 +990,27 @@ float Map_Count, Map_Current; string Map_Current_Name; // NOTE: this now expects the map list to be already tokenized and the count in Map_Count -float GetMaplistPosition() +int GetMaplistPosition() { - float pos, idx; - string map; - - map = GetMapname(); - idx = autocvar_g_maplist_index; + string map = GetMapname(); + int idx = autocvar_g_maplist_index; if(idx >= 0) + { if(idx < Map_Count) + { if(map == argv(idx)) + { return idx; + } + } + } - for(pos = 0; pos < Map_Count; ++pos) + for(int pos = 0; pos < Map_Count; ++pos) + { if(map == argv(pos)) return pos; + } // resume normal maplist rotation if current map is not in g_maplist return idx; @@ -1022,6 +1033,9 @@ bool MapHasRightSize(string map) LOG_TRACE(checkwp_msg, ": has waypoints"); } + if(autocvar_g_maplist_ignore_sizes) + return true; + // open map size restriction file string opensize_msg = strcat("opensize ", map); float fh = fopen(strcat("maps/", map, ".sizes"), FILE_READ); @@ -1199,18 +1213,25 @@ float MaplistMethod_Shuffle(float exponent) // more clever shuffling void Maplist_Init() { - Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); - float i; - for (i = 0; i < Map_Count; ++i) - if (Map_Check(i, 2)) - break; + float i = Map_Count = 0; + if(autocvar_g_maplist != "") + { + Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); + for (i = 0; i < Map_Count; ++i) + { + if (Map_Check(i, 2)) + break; + } + } + if (i == Map_Count) { bprint( "Maplist contains no usable maps! Resetting it to default map list.\n" ); cvar_set("g_maplist", MapInfo_ListAllAllowedMaps(MapInfo_RequiredFlags(), MapInfo_ForbiddenFlags() | MAPINFO_FLAG_NOAUTOMAPLIST)); if(autocvar_g_maplist_shuffle) ShuffleMaplist(); - localcmd("\nmenu_cmd sync\n"); + if(!server_is_dedicated) + localcmd("\nmenu_cmd sync\n"); Map_Count = tokenizebyseparator(autocvar_g_maplist, " "); } if(Map_Count == 0) @@ -1224,10 +1245,8 @@ void Maplist_Init() string GetNextMap() { - float nextMap; - Maplist_Init(); - nextMap = -1; + float nextMap = -1; if(nextMap == -1) if(autocvar_g_maplist_shuffle > 0) @@ -1322,9 +1341,7 @@ void GotoNextMap(float reinit) return; alreadychangedlevel = true; - string nextMap; - - nextMap = GetNextMap(); + string nextMap = GetNextMap(); if(nextMap == "") error("Everything is broken - cannot find a next map. Please report this to the developers."); Map_Goto(reinit); diff --git a/qcsrc/server/impulse.qc b/qcsrc/server/impulse.qc index 2320eb830e..49685d49d7 100644 --- a/qcsrc/server/impulse.qc +++ b/qcsrc/server/impulse.qc @@ -507,7 +507,7 @@ IMPULSE(waypoint_here_here) IMPULSE(waypoint_here_crosshair) { - WarpZone_crosshair_trace(this); + WarpZone_crosshair_trace_plusvisibletriggers(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"); diff --git a/qcsrc/server/miscfunctions.qc b/qcsrc/server/miscfunctions.qc index bc0b49ca02..167a75ae2c 100644 --- a/qcsrc/server/miscfunctions.qc +++ b/qcsrc/server/miscfunctions.qc @@ -499,7 +499,7 @@ float want_weapon(entity weaponinfo, float allguns) // WEAPONTODO: what still ne allow_mutatorblocked = M_ARGV(3, bool); if(allguns) - d = boolean(weaponinfo.spawnflags & WEP_FLAG_NORMAL); + d = boolean((weaponinfo.spawnflags & WEP_FLAG_NORMAL) && !(weaponinfo.spawnflags & WEP_FLAG_HIDDEN)); else if(!mutator_returnvalue) d = !(!weaponinfo.weaponstart); diff --git a/qcsrc/server/mutators/events.qh b/qcsrc/server/mutators/events.qh index f0237b27ee..cd09b1defd 100644 --- a/qcsrc/server/mutators/events.qh +++ b/qcsrc/server/mutators/events.qh @@ -1222,3 +1222,11 @@ MUTATOR_HOOKABLE(Unfreeze, EV_Unfreeze); /**/ o(int, MUTATOR_ARGV_0_int) \ /**/ MUTATOR_HOOKABLE(GetPlayerLimit, EV_GetPlayerLimit); + +/** include special item codes for a death to the game log */ +#define EV_LogDeath_AppendItemCodes(i, o) \ + /** player */ i(entity, MUTATOR_ARGV_0_entity) \ + /** item codes */ i(string, MUTATOR_ARGV_1_string) \ + /**/ o(string, MUTATOR_ARGV_1_string) \ + /**/ +MUTATOR_HOOKABLE(LogDeath_AppendItemCodes, EV_LogDeath_AppendItemCodes); diff --git a/qcsrc/server/player.qc b/qcsrc/server/player.qc index b8d3c903c5..c39f59e721 100644 --- a/qcsrc/server/player.qc +++ b/qcsrc/server/player.qc @@ -329,9 +329,8 @@ void PlayerDamage(entity this, entity inflictor, entity attacker, float damage, if (time < this.spawnshieldtime && autocvar_g_spawnshield_blockdamage < 1) damage *= 1 - max(0, autocvar_g_spawnshield_blockdamage); - if(DEATH_ISWEAPON(deathtype, WEP_TUBA)) + if(deathtype & HITTYPE_SOUND) // sound based attacks cause bleeding from the ears { - // tuba causes blood to come out of the ears vector ear1, ear2; vector d; float f; diff --git a/qcsrc/server/race.qc b/qcsrc/server/race.qc index b3241838b0..9a12a04866 100644 --- a/qcsrc/server/race.qc +++ b/qcsrc/server/race.qc @@ -23,7 +23,12 @@ #include "../common/mutators/mutator/waypoints/waypointsprites.qh" IntrusiveList g_race_targets; -STATIC_INIT(g_race_targets) { g_race_targets = IL_NEW(); } +IntrusiveList g_racecheckpoints; +STATIC_INIT(g_race) +{ + g_race_targets = IL_NEW(); + g_racecheckpoints = IL_NEW(); +} void race_InitSpectator() { diff --git a/qcsrc/server/race.qh b/qcsrc/server/race.qh index 4402e22568..4dafbcf637 100644 --- a/qcsrc/server/race.qh +++ b/qcsrc/server/race.qh @@ -31,9 +31,6 @@ 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/steerlib.qc b/qcsrc/server/steerlib.qc index 92c918f00c..310835c5ce 100644 --- a/qcsrc/server/steerlib.qc +++ b/qcsrc/server/steerlib.qc @@ -17,7 +17,7 @@ /** Uniform push from a point **/ -#define steerlib_push(ent,point) normalize(ent.origin - point) +#define steerlib_push(ent,point) normalize((ent).origin - point) /* vector steerlib_push(entity this, vector point) { @@ -29,7 +29,7 @@ vector steerlib_push(entity this, vector point) **/ vector steerlib_arrive(entity this, vector point, float maximal_distance) { - float distance = bound(0.001,vlen(this.origin - point),maximal_distance); + float distance = bound(0.001, vlen(this.origin - point), maximal_distance); vector direction = normalize(point - this.origin); return direction * (distance / maximal_distance); } @@ -39,21 +39,21 @@ vector steerlib_arrive(entity this, vector point, float maximal_distance) **/ vector steerlib_attract(entity this, vector point, float maximal_distance) { - float distance = bound(0.001,vlen(this.origin - point),maximal_distance); + float distance = bound(0.001, vlen(this.origin - point), maximal_distance); vector direction = normalize(point - this.origin); - return direction * (1-(distance / maximal_distance)); + return direction * (1 - (distance / maximal_distance)); } -vector steerlib_attract2(entity this, vector point, float min_influense,float max_distance,float max_influense) +vector steerlib_attract2(entity this, vector point, float min_influense, float max_distance, float max_influense) { - float distance = bound(0.00001,vlen(this.origin - point),max_distance); + float distance = bound(0.00001, vlen(this.origin - point), max_distance); vector direction = normalize(point - this.origin); float influense = 1 - (distance / max_distance); influense = min_influense + (influense * (max_influense - min_influense)); - return direction * influense; + return direction * influense; } /* @@ -89,27 +89,21 @@ vector steerlib_attract2(vector point, float maximal_distance,float min_influens /** Move away from a point. **/ -vector steerlib_repell(entity this, vector point,float maximal_distance) +vector steerlib_repel(entity this, vector point, float maximal_distance) { - float distance; - vector direction; - - distance = bound(0.001,vlen(this.origin - point),maximal_distance); - direction = normalize(this.origin - point); + float distance = bound(0.001, vlen(this.origin - point), maximal_distance); + vector direction = normalize(this.origin - point); - return direction * (1-(distance / maximal_distance)); + return direction * (1 - (distance / maximal_distance)); } /** Try to keep at ideal_distance away from point **/ -vector steerlib_standoff(entity this, vector point,float ideal_distance) +vector steerlib_standoff(entity this, vector point, float ideal_distance) { - float distance; vector direction; - - distance = vlen(this.origin - point); - + float distance = vlen(this.origin - point); if(distance < ideal_distance) { @@ -123,22 +117,22 @@ vector steerlib_standoff(entity this, vector point,float ideal_distance) } /** - A random heading in a forward halfcicrle + A random heading in a forward semicircle - use like: - this.target = steerlib_wander(256,32,this.target) + usage: + this.target = steerlib_wander(256, 32, this.target) - where range is the cicrle radius and tresh is how close we need to be to pick a new heading. + where range is the circle radius and threshold is how close we need to be to pick a new heading. + Assumes v_forward is set by makevectors **/ -vector steerlib_wander(entity this, float range, float tresh, vector oldpoint) +vector steerlib_wander(entity this, float range, float threshold, vector oldpoint) { - vector wander_point; - wander_point = v_forward - oldpoint; + vector wander_point = v_forward - oldpoint; - if (vdist(wander_point, >, tresh)) + if (vdist(wander_point, >, threshold)) return oldpoint; - range = bound(0,range,1); + range = bound(0, range, 1); wander_point = this.origin + v_forward * 128; wander_point = wander_point + randomvec() * (range * 128) - randomvec() * (range * 128); @@ -147,17 +141,15 @@ vector steerlib_wander(entity this, float range, float tresh, vector oldpoint) } /** - Dodge a point. dont work to well. + Dodge a point NOTE: doesn't work well **/ vector steerlib_dodge(entity this, vector point, vector dodge_dir, float min_distance) { - float distance; - - distance = max(vlen(this.origin - point),min_distance); + float distance = max(vlen(this.origin - point), min_distance); if (min_distance < distance) return '0 0 0'; - return dodge_dir * (min_distance/distance); + return dodge_dir * (min_distance / distance); } /** @@ -165,20 +157,19 @@ vector steerlib_dodge(entity this, vector point, vector dodge_dir, float min_dis Group will move towards the unified direction while keeping close to eachother. **/ .float flock_id; -vector steerlib_flock(entity this, float _radius, float standoff,float separation_force,float flock_force) +vector steerlib_flock(entity this, float _radius, float standoff, float separation_force, float flock_force) { - entity flock_member; vector push = '0 0 0', pull = '0 0 0'; - float ccount = 0; + int ccount = 0; - flock_member = findradius(this.origin, _radius); + entity flock_member = findradius(this.origin, _radius); while(flock_member) { if(flock_member != this) if(flock_member.flock_id == this.flock_id) { ++ccount; - push = push + (steerlib_repell(this, flock_member.origin,standoff) * separation_force); + push = push + (steerlib_repel(this, flock_member.origin,standoff) * separation_force); pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force); } flock_member = flock_member.chain; @@ -191,20 +182,19 @@ vector steerlib_flock(entity this, float _radius, float standoff,float separatio Group will move towards the unified direction while keeping close to eachother. xy only version (for ground movers). **/ -vector steerlib_flock2d(entity this, float _radius, float standoff,float separation_force,float flock_force) +vector steerlib_flock2d(entity this, float _radius, float standoff, float separation_force, float flock_force) { - entity flock_member; vector push = '0 0 0', pull = '0 0 0'; - float ccount = 0; + int ccount = 0; - flock_member = findradius(this.origin,_radius); + entity flock_member = findradius(this.origin,_radius); while(flock_member) { if(flock_member != this) if(flock_member.flock_id == this.flock_id) { ++ccount; - push = push + (steerlib_repell(this, flock_member.origin, standoff) * separation_force); + push = push + (steerlib_repel(this, flock_member.origin, standoff) * separation_force); pull = pull + (steerlib_arrive(this, flock_member.origin + flock_member.velocity, _radius) * flock_force); } flock_member = flock_member.chain; @@ -218,25 +208,23 @@ vector steerlib_flock2d(entity this, float _radius, float standoff,float separat /** All members want to be in the center, and keep away from eachother. - The furtehr form the center the more they want to be there. + The further from the center the more they want to be there. This results in a aligned movement (?!) much like flocking. **/ -vector steerlib_swarm(entity this, float _radius, float standoff,float separation_force,float swarm_force) +vector steerlib_swarm(entity this, float _radius, float standoff, float separation_force, float swarm_force) { - entity swarm_member; vector force = '0 0 0', center = '0 0 0'; - float ccount = 0; - - swarm_member = findradius(this.origin,_radius); + int ccount = 0; + entity swarm_member = findradius(this.origin,_radius); while(swarm_member) { if(swarm_member.flock_id == this.flock_id) { ++ccount; center = center + swarm_member.origin; - force = force + (steerlib_repell(this, swarm_member.origin,standoff) * separation_force); + force = force + (steerlib_repel(this, swarm_member.origin,standoff) * separation_force); } swarm_member = swarm_member.chain; } @@ -252,93 +240,80 @@ vector steerlib_swarm(entity this, float _radius, float standoff,float separatio Run four tracelines in a forward funnel, bias each diretion negative if something is found there. You need to call makevectors() (or equivalent) before this function to set v_forward and v_right **/ -vector steerlib_traceavoid(entity this, float pitch,float length) +vector steerlib_traceavoid(entity this, float pitch, float length) { - vector vup_left,vup_right,vdown_left,vdown_right; - float fup_left,fup_right,fdown_left,fdown_right; - vector upwish,downwish,leftwish,rightwish; - vector v_left,v_down; - + vector v_left = v_right * -1; + vector v_down = v_up * -1; - v_left = v_right * -1; - v_down = v_up * -1; - - vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length; - traceline(this.origin, this.origin + vup_left,MOVE_NOMONSTERS,this); - fup_left = trace_fraction; + vector vup_left = (v_forward + (v_left * pitch + v_up * pitch)) * length; + traceline(this.origin, this.origin + vup_left, MOVE_NOMONSTERS, this); + float fup_left = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length; - traceline(this.origin,this.origin + vup_right ,MOVE_NOMONSTERS,this); - fup_right = trace_fraction; + vector vup_right = (v_forward + (v_right * pitch + v_up * pitch)) * length; + traceline(this.origin, this.origin + vup_right, MOVE_NOMONSTERS, this); + float fup_right = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length; - traceline(this.origin,this.origin + vdown_left,MOVE_NOMONSTERS,this); - fdown_left = trace_fraction; + vector vdown_left = (v_forward + (v_left * pitch + v_down * pitch)) * length; + traceline(this.origin, this.origin + vdown_left, MOVE_NOMONSTERS, this); + float fdown_left = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length; - traceline(this.origin,this.origin + vdown_right,MOVE_NOMONSTERS,this); - fdown_right = trace_fraction; + vector vdown_right = (v_forward + (v_right * pitch + v_down * pitch)) * length; + traceline(this.origin, this.origin + vdown_right, MOVE_NOMONSTERS, this); + float fdown_right = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - upwish = v_up * (fup_left + fup_right); - downwish = v_down * (fdown_left + fdown_right); - leftwish = v_left * (fup_left + fdown_left); - rightwish = v_right * (fup_right + fdown_right); + vector upwish = v_up * (fup_left + fup_right); + vector downwish = v_down * (fdown_left + fdown_right); + vector leftwish = v_left * (fup_left + fdown_left); + vector rightwish = v_right * (fup_right + fdown_right); - return (upwish+leftwish+downwish+rightwish) * 0.25; + return (upwish + leftwish + downwish + rightwish) * 0.25; } /** Steer towards the direction least obstructed. Run tracelines in a forward trident, bias each direction negative if something is found there. + You need to call makevectors() (or equivalent) before this function to set v_forward and v_right **/ vector steerlib_traceavoid_flat(entity this, float pitch, float length, vector vofs) { - vector vt_left, vt_right,vt_front; - float f_left, f_right,f_front; - vector leftwish, rightwish,frontwish, v_left; + vector v_left = v_right * -1; - v_left = v_right * -1; - - - vt_front = v_forward * length; + vector vt_front = v_forward * length; traceline(this.origin + vofs, this.origin + vofs + vt_front,MOVE_NOMONSTERS,this); - f_front = trace_fraction; + float f_front = trace_fraction; - vt_left = (v_forward + (v_left * pitch)) * length; + vector vt_left = (v_forward + (v_left * pitch)) * length; traceline(this.origin + vofs, this.origin + vofs + vt_left,MOVE_NOMONSTERS,this); - f_left = trace_fraction; + float f_left = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - vt_right = (v_forward + (v_right * pitch)) * length; + vector vt_right = (v_forward + (v_right * pitch)) * length; traceline(this.origin + vofs, this.origin + vofs + vt_right ,MOVE_NOMONSTERS,this); - f_right = trace_fraction; + float f_right = trace_fraction; //te_lightning1(NULL,this.origin, trace_endpos); - leftwish = v_left * f_left; - rightwish = v_right * f_right; - frontwish = v_forward * f_front; + vector leftwish = v_left * f_left; + vector rightwish = v_right * f_right; + vector frontwish = v_forward * f_front; return normalize(leftwish + rightwish + frontwish); } //#define BEAMSTEER_VISUAL -float beamsweep(entity this, vector from, vector dir,float length, float step,float step_up, float step_down) +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; - - u = '0 0 1' * step_up; - d = '0 0 1' * step_down; + vector u = '0 0 1' * step_up; + vector d = '0 0 1' * step_down; traceline(from + u, from - d,MOVE_NORMAL,this); if(trace_fraction == 1.0) @@ -347,11 +322,11 @@ float beamsweep(entity this, vector from, vector dir,float length, float step,fl if(!location_isok(trace_endpos, false, false)) return 0; - a = trace_endpos; - for(i = 0; i < length; i += step) + vector a = trace_endpos; + for(int i = 0; i < length; i += step) { - b = a + dir * step; + vector b = a + dir * step; tracebox(a + u,'-4 -4 -4','4 4 4', b + u,MOVE_NORMAL,this); if(trace_fraction != 1.0) return i / length; @@ -374,31 +349,27 @@ float beamsweep(entity this, vector from, vector dir,float length, float step,fl vector steerlib_beamsteer(entity this, vector dir, float length, float step, float step_up, float step_down) { - float bm_forward, bm_right, bm_left,p; - vector vr,vl; - dir.z *= 0.15; - vr = vectoangles(dir); - //vr_x *= -1; + vector vr = vectoangles(dir); + //vr.x *= -1; tracebox(this.origin + '0 0 1' * step_up, this.mins, this.maxs, ('0 0 1' * step_up) + this.origin + (dir * length), MOVE_NOMONSTERS, this); if(trace_fraction == 1.0) { - //te_lightning1(this,this.origin,this.origin + (dir * length)); + //te_lightning1(this,this.origin,this.origin + (dir * length)); return dir; } makevectors(vr); - bm_forward = beamsweep(this, this.origin, v_forward, length, step, step_up, step_down); + float bm_forward = beamsweep(this, this.origin, v_forward, length, step, step_up, step_down); vr = normalize(v_forward + v_right * 0.125); - vl = normalize(v_forward - v_right * 0.125); + vector vl = normalize(v_forward - v_right * 0.125); - 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); + float bm_right = beamsweep(this, this.origin, vr, length, step, step_up, step_down); + float bm_left = beamsweep(this, this.origin, vl, length, step, step_up, step_down); - - p = bm_left + bm_right; + float p = bm_left + bm_right; if(p == 2) { //te_lightning1(this,this.origin + '0 0 32',this.origin + '0 0 32' + vr * length); @@ -435,5 +406,4 @@ vector steerlib_beamsteer(entity this, vector dir, float length, float step, flo vl = vl * bm_left; return normalize(vr + vl); - } diff --git a/qcsrc/server/sv_main.qc b/qcsrc/server/sv_main.qc index 9be3c18f60..a281e9054b 100644 --- a/qcsrc/server/sv_main.qc +++ b/qcsrc/server/sv_main.qc @@ -28,44 +28,43 @@ #include "../lib/warpzone/common.qh" #include "../lib/warpzone/server.qh" -.float lastground; -.int state; - void CreatureFrame_hotliquids(entity this) { - if (this.dmgtime < time) + if (this.dmgtime > time) { - this.dmgtime = time + autocvar_g_balance_contents_damagerate; + return; + } - if (this.flags & FL_PROJECTILE) - { - if (this.watertype == CONTENT_LAVA) - Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0'); - else if (this.watertype == CONTENT_SLIME) - Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0'); - } - else + this.dmgtime = time + autocvar_g_balance_contents_damagerate; + + if (this.flags & FL_PROJECTILE) + { + if (this.watertype == CONTENT_LAVA) + Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0'); + else if (this.watertype == CONTENT_SLIME) + Damage (this, NULL, NULL, autocvar_g_balance_contents_projectiledamage * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0'); + } + else + { + if (this.watertype == CONTENT_LAVA) { - if (this.watertype == CONTENT_LAVA) + if (this.watersound_finished < time) { - if (this.watersound_finished < time) - { - this.watersound_finished = time + 0.5; - sound (this, CH_PLAYER_SINGLE, SND_LAVA, VOL_BASE, ATTEN_NORM); - } - Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_lava * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0'); - if(autocvar_g_balance_contents_playerdamage_lava_burn) - Fire_AddDamage(this, NULL, autocvar_g_balance_contents_playerdamage_lava_burn * this.waterlevel, autocvar_g_balance_contents_playerdamage_lava_burn_time * this.waterlevel, DEATH_LAVA.m_id); + this.watersound_finished = time + 0.5; + sound (this, CH_PLAYER_SINGLE, SND_LAVA, VOL_BASE, ATTEN_NORM); } - else if (this.watertype == CONTENT_SLIME) + Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_lava * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_LAVA.m_id, DMG_NOWEP, this.origin, '0 0 0'); + if(autocvar_g_balance_contents_playerdamage_lava_burn) + Fire_AddDamage(this, NULL, autocvar_g_balance_contents_playerdamage_lava_burn * this.waterlevel, autocvar_g_balance_contents_playerdamage_lava_burn_time * this.waterlevel, DEATH_LAVA.m_id); + } + else if (this.watertype == CONTENT_SLIME) + { + if (this.watersound_finished < time) { - if (this.watersound_finished < time) - { - this.watersound_finished = time + 0.5; - sound (this, CH_PLAYER_SINGLE, SND_SLIME, VOL_BASE, ATTEN_NORM); - } - Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_slime * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0'); + this.watersound_finished = time + 0.5; + sound (this, CH_PLAYER_SINGLE, SND_SLIME, VOL_BASE, ATTEN_NORM); } + Damage (this, NULL, NULL, autocvar_g_balance_contents_playerdamage_slime * autocvar_g_balance_contents_damagerate * this.waterlevel, DEATH_SLIME.m_id, DMG_NOWEP, this.origin, '0 0 0'); } } } @@ -96,35 +95,36 @@ 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 + if(IS_VEHICLE(this) || (this.flags & FL_PROJECTILE)) + return; // vehicles and projectiles don't receive fall damage + if(!(this.velocity || this.oldvelocity)) + return; // if the entity hasn't moved and isn't moving, then don't do anything + + // check for falling damage + float velocity_len = vlen(this.velocity); + bool have_hook = false; + for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) { - // check for falling damage - float velocity_len = vlen(this.velocity); - bool have_hook = false; - for(int slot = 0; slot < MAX_WEAPONSLOTS; ++slot) + .entity weaponentity = weaponentities[slot]; + if(this.(weaponentity).hook && this.(weaponentity).hook.state) { - .entity weaponentity = weaponentities[slot]; - if(this.(weaponentity).hook && this.(weaponentity).hook.state) - { - have_hook = true; - break; - } + have_hook = true; + break; } - if(!have_hook) - { - float dm = vlen(this.oldvelocity) - velocity_len; // dm is now the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage. - if (IS_DEAD(this)) - dm = (dm - autocvar_g_balance_falldamage_deadminspeed) * autocvar_g_balance_falldamage_factor; - else - dm = min((dm - autocvar_g_balance_falldamage_minspeed) * autocvar_g_balance_falldamage_factor, autocvar_g_balance_falldamage_maxdamage); - if (dm > 0) - Damage (this, NULL, NULL, dm, DEATH_FALL.m_id, DMG_NOWEP, this.origin, '0 0 0'); - } - - if(autocvar_g_maxspeed > 0 && velocity_len > autocvar_g_maxspeed) - Damage (this, NULL, NULL, 100000, DEATH_SHOOTING_STAR.m_id, DMG_NOWEP, this.origin, '0 0 0'); } + if(!have_hook) + { + float dm = vlen(this.oldvelocity) - velocity_len; // dm is now the velocity DECREASE. Velocity INCREASE should never cause a sound or any damage. + if (IS_DEAD(this)) + dm = (dm - autocvar_g_balance_falldamage_deadminspeed) * autocvar_g_balance_falldamage_factor; + else + dm = min((dm - autocvar_g_balance_falldamage_minspeed) * autocvar_g_balance_falldamage_factor, autocvar_g_balance_falldamage_maxdamage); + if (dm > 0) + Damage (this, NULL, NULL, dm, DEATH_FALL.m_id, DMG_NOWEP, this.origin, '0 0 0'); + } + + if(autocvar_g_maxspeed > 0 && velocity_len > autocvar_g_maxspeed) + Damage (this, NULL, NULL, 100000, DEATH_SHOOTING_STAR.m_id, DMG_NOWEP, this.origin, '0 0 0'); } void CreatureFrame_All() diff --git a/qcsrc/server/sv_main.qh b/qcsrc/server/sv_main.qh index 93480cf282..46284fde2e 100644 --- a/qcsrc/server/sv_main.qh +++ b/qcsrc/server/sv_main.qh @@ -2,6 +2,11 @@ bool expr_evaluate(string s); +#ifdef PROFILING +float client_cefc_accumulator; +float client_cefc_accumulatortime; +#endif + /* ================== main diff --git a/xonotic-client.cfg b/xonotic-client.cfg index 61651320af..c9a49f924a 100644 --- a/xonotic-client.cfg +++ b/xonotic-client.cfg @@ -396,6 +396,7 @@ set g_waypointsprite_timealphaexponent 1 seta g_waypointsprite_turrets 1 "disable turret waypoints" seta g_waypointsprite_turrets_maxdist 5000 "max distance for turret waypoints" seta g_waypointsprite_turrets_text 0 "show the turret's name in the waypoint" +seta g_waypointsprite_turrets_onlyhurt 0 "only show the turret waypoint for a short period after being hurt" seta g_waypointsprite_uppercase 1 seta g_waypointsprite_text 0 "Always show text instead of icons, setting this to 0 will still use text if the icon is unavailable" seta g_waypointsprite_iconsize 32 diff --git a/xonotic-common.cfg b/xonotic-common.cfg index 3a01784c7c..f44d9c96f9 100644 --- a/xonotic-common.cfg +++ b/xonotic-common.cfg @@ -1,3 +1,10 @@ +// most common prefixes (currently not used consistently - especially g_ and sv_): +// cl_ client +// g_ gameplay +// r_ renderer +// sv_ server administration, should not affect gameplay +// _ temporary, private or internal cvar - should not be changed manually, sometimes used in scripts + // Xonotic version (formatted for machines) // used to determine if a client version is compatible // this doesn't have to be bumped with every release diff --git a/xonotic-server.cfg b/xonotic-server.cfg index 463df7224e..d261e262ea 100644 --- a/xonotic-server.cfg +++ b/xonotic-server.cfg @@ -2,7 +2,7 @@ // note that it doesn't reset all server cvars, // some are shared with the client and so are left in xonotic-common.cfg -set name_maxlength 64 "max player name length (not counting color codes) allowed by the server" +set sv_name_maxlength 64 "max player name length (not counting color codes) allowed by the server" // taunts and voices set sv_taunt 1 "allow taunts on the server" @@ -212,6 +212,7 @@ set g_maplist_index 0 "this is used internally for saving position in maplist cy set g_maplist_selectrandom 0 "if 1, a random map will be chosen as next map - DEPRECATED in favor of g_maplist_shuffle" set g_maplist_shuffle 1 "new randomization method: like selectrandom, but avoid playing the same maps in short succession. This works by taking out the first element and inserting it into g_maplist with a bias to the end of the list" set g_maplist_check_waypoints 0 "when 1, maps are skipped if there currently are bots, but the map has no waypoints" +set g_maplist_ignore_sizes 0 "when 1, all maps are shown in the map list regardless of player count" set g_items_mindist 4000 "starting distance for the fading of items" set g_items_maxdist 4500 "maximum distance at which an item can be viewed, after which it will be invisible" @@ -221,6 +222,7 @@ set g_grab_range 200 "distance at which dragable objects can be grabbed" set g_cloaked 0 "display all players mostly invisible" set g_player_alpha 1 "default opacity of players" set g_player_brightness 0 "set to 2 for brighter players" +set g_player_damageforcescale 2 "push multiplier of attacks against players" set g_balance_cloaked_alpha 0.25 "opacity of cloaked players" set g_playerclip_collisions 1 "0 = disable collision testing against playerclips, might be useful on some defrag maps"